typeprof 0.1.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/Gemfile +2 -2
  4. data/Gemfile.lock +10 -21
  5. data/LICENSE +21 -0
  6. data/README.md +1 -1
  7. data/doc/demo.md +398 -0
  8. data/doc/doc.ja.md +11 -1
  9. data/doc/doc.md +16 -7
  10. data/exe/typeprof +2 -1
  11. data/lib/typeprof.rb +9 -0
  12. data/lib/typeprof/analyzer.rb +455 -364
  13. data/lib/typeprof/arguments.rb +397 -0
  14. data/lib/typeprof/block.rb +133 -0
  15. data/lib/typeprof/builtin.rb +125 -116
  16. data/lib/typeprof/cli.rb +62 -71
  17. data/lib/typeprof/config.rb +114 -0
  18. data/lib/typeprof/container-type.rb +208 -27
  19. data/lib/typeprof/export.rb +201 -96
  20. data/lib/typeprof/import.rb +451 -365
  21. data/lib/typeprof/iseq.rb +43 -2
  22. data/lib/typeprof/method.rb +139 -100
  23. data/lib/typeprof/type.rb +138 -297
  24. data/lib/typeprof/utils.rb +4 -18
  25. data/lib/typeprof/version.rb +3 -0
  26. data/smoke/arguments2.rb +55 -0
  27. data/smoke/array-each3.rb +1 -4
  28. data/smoke/array12.rb +1 -1
  29. data/smoke/array6.rb +1 -0
  30. data/smoke/block-ambiguous.rb +36 -0
  31. data/smoke/block-args1-rest.rb +62 -0
  32. data/smoke/block-args1.rb +59 -0
  33. data/smoke/block-args2-rest.rb +62 -0
  34. data/smoke/block-args2.rb +59 -0
  35. data/smoke/block-args3-rest.rb +73 -0
  36. data/smoke/block-args3.rb +70 -0
  37. data/smoke/block-blockarg.rb +27 -0
  38. data/smoke/block-kwarg.rb +52 -0
  39. data/smoke/block11.rb +1 -1
  40. data/smoke/block13.rb +9 -0
  41. data/smoke/block13.rbs +3 -0
  42. data/smoke/block14.rb +17 -0
  43. data/smoke/block4.rb +2 -2
  44. data/smoke/block5.rb +1 -0
  45. data/smoke/block6.rb +1 -1
  46. data/smoke/block7.rb +0 -2
  47. data/smoke/block8.rb +2 -2
  48. data/smoke/block9.rb +1 -1
  49. data/smoke/blown.rb +1 -1
  50. data/smoke/class-hierarchy.rb +54 -0
  51. data/smoke/class-hierarchy2.rb +27 -0
  52. data/smoke/class.rb +2 -0
  53. data/smoke/constant1.rb +13 -5
  54. data/smoke/constant2.rb +2 -0
  55. data/smoke/cvar.rb +1 -0
  56. data/smoke/demo10.rb +1 -1
  57. data/smoke/demo5.rb +3 -0
  58. data/smoke/demo8.rb +2 -2
  59. data/smoke/demo9.rb +1 -3
  60. data/smoke/flow7.rb +1 -7
  61. data/smoke/flow8.rb +13 -0
  62. data/smoke/gvar.rb +1 -1
  63. data/smoke/gvar2.rb +17 -0
  64. data/smoke/gvar2.rbs +1 -0
  65. data/smoke/hash4.rb +1 -1
  66. data/smoke/inheritance2.rb +6 -0
  67. data/smoke/instance_eval.rb +1 -1
  68. data/smoke/int_times.rb +1 -1
  69. data/smoke/ivar3.rb +16 -0
  70. data/smoke/ivar3.rbs +3 -0
  71. data/smoke/keyword3.rb +1 -2
  72. data/smoke/keyword4.rb +1 -1
  73. data/smoke/manual-rbs2.rb +1 -1
  74. data/smoke/manual-rbs3.rb +12 -0
  75. data/smoke/manual-rbs3.rbs +3 -0
  76. data/smoke/module4.rb +5 -0
  77. data/smoke/multiple-superclass.rb +12 -0
  78. data/smoke/next2.rb +1 -1
  79. data/smoke/optional1.rb +1 -1
  80. data/smoke/optional2.rb +1 -1
  81. data/smoke/optional3.rb +10 -0
  82. data/smoke/proc4.rb +1 -1
  83. data/smoke/rbs-alias.rb +9 -0
  84. data/smoke/rbs-alias.rbs +4 -0
  85. data/smoke/rbs-attr.rb +26 -0
  86. data/smoke/rbs-attr.rbs +5 -0
  87. data/smoke/rbs-extend.rb +9 -0
  88. data/smoke/rbs-extend.rbs +7 -0
  89. data/smoke/rbs-interface.rb +24 -0
  90. data/smoke/rbs-interface.rbs +12 -0
  91. data/smoke/rbs-proc1.rb +9 -0
  92. data/smoke/rbs-proc1.rbs +3 -0
  93. data/smoke/rbs-proc2.rb +20 -0
  94. data/smoke/rbs-proc2.rbs +3 -0
  95. data/smoke/rbs-proc3.rb +13 -0
  96. data/smoke/rbs-proc3.rbs +4 -0
  97. data/smoke/rbs-record.rb +17 -0
  98. data/smoke/rbs-record.rbs +4 -0
  99. data/smoke/rbs-tyvar.rb +18 -0
  100. data/smoke/rbs-tyvar.rbs +5 -0
  101. data/smoke/rbs-tyvar2.rb +20 -0
  102. data/smoke/rbs-tyvar2.rbs +9 -0
  103. data/smoke/rbs-tyvar3.rb +25 -0
  104. data/smoke/rbs-tyvar3.rbs +4 -0
  105. data/smoke/rbs-vars.rb +39 -0
  106. data/smoke/rbs-vars.rbs +7 -0
  107. data/smoke/rest1.rb +1 -1
  108. data/smoke/rest2.rb +1 -1
  109. data/smoke/rest3.rb +1 -1
  110. data/smoke/rest5.rb +1 -1
  111. data/smoke/rest6.rb +1 -1
  112. data/smoke/retry1.rb +1 -1
  113. data/smoke/return.rb +1 -1
  114. data/smoke/singleton_method.rb +3 -0
  115. data/smoke/step.rb +1 -1
  116. data/smoke/struct.rb +6 -2
  117. data/smoke/struct3.rb +14 -0
  118. data/smoke/super1.rb +18 -0
  119. data/smoke/symbol-proc.rb +24 -0
  120. data/smoke/union-recv.rb +6 -0
  121. data/smoke/user-demo.rb +15 -0
  122. data/smoke/wrong-extend.rb +1 -0
  123. data/tools/setup-insns-def.rb +1 -1
  124. data/tools/stackprof-wrapper.rb +1 -1
  125. data/typeprof.gemspec +12 -4
  126. metadata +68 -10
  127. data/.gitmodules +0 -6
  128. data/run.sh +0 -3
  129. data/smoke/variadic1.rb.notyet +0 -5
@@ -1,5 +1,9 @@
1
1
  # TypeProf: 抽象解釈に基づくRubyコードの型解析ツール
2
2
 
3
+ ## とりあえずデモ
4
+
5
+ [demo.md](demo.md) を参照。
6
+
3
7
  ## TypeProfの使い方
4
8
 
5
9
  app.rb を解析する。
@@ -24,7 +28,13 @@ $ typeprof sig/app.rbs app.rb -o sig/app.gen.rbs
24
28
 
25
29
  * `-o OUTFILE`: 標準出力ではなく、指定ファイル名に出力する
26
30
  * `-q`: 解析の進捗を表示しない
27
- * `-v`: 解析の詳細ログを表示する(現状ではデバッグ用出力に近い)
31
+ * `-v`: `-fshow-errors`の別名
32
+ * `-d`: 解析の詳細ログを表示する(現状ではデバッグ用出力に近い)
33
+ * `-I DIR`: `require`のファイル探索ディレクトリを追加する
34
+ * `-r GEMNAME`: `GEMNAME`に対応するRBSをロードする
35
+ * `--exclude-dir DIR`: `DIR`以下のファイルの解析結果を出力から省略する。後に指定されているほうが優先される(`--include-dir foo --exclude-dir foo/bar`の場合う、foo/bar/baz.rbの結果は出力されず、foo/baz.rbの結果は出力される)。
36
+ * `--include-dir DIR`: `DIR`以下のファイルの解析結果を出力に含める。後に指定されているほうが優先される(`--exclude-dir foo --include-dir foo/bar`の場合、
37
+ foo/bar/baz.rbの結果は出力されるが、foo/baz.rbの結果は出力されない)。
28
38
  * `-fshow-errors`: 実行中に見つけたバグの可能性を出力します(多くの場合、大量のfalse positiveが出ます)。
29
39
  * `-fpedantic-output`: デフォルトでは`A | untyped`と推定されたところを単に`A`と出力しますが、より生の出力、つまり`A | untyped`と出力します。
30
40
  * `-fshow-container-raw-elements`: (後で書く)
data/doc/doc.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # TypeProf: A type analysis tool for Ruby code based on abstract interpretation
2
2
 
3
+ ## Show me demo first
4
+
5
+ See [demo.md](demo.md).
6
+
3
7
  ## How to use TypeProf
4
8
 
5
9
  Analyze app.rb:
@@ -24,7 +28,12 @@ Here is a list of currently avaiable options:
24
28
 
25
29
  * `-o OUTFILE`: Write the analyze result to OUTFILE instead of standard output
26
30
  * `-q`: Hide the progress indicator
27
- * `-v`: Show the analysis log (Currently, the log is just for debugging and may become very huge)
31
+ * `-v`: Alias to `-fshow-errors`
32
+ * `-d`: Show the analysis log (Currently, the log is just for debugging and may become very huge)
33
+ * `-I DIR`: Add `DIR` to the file search path of `require`
34
+ * `-r GEMNAME`: Load the RBS files of `GEMNAME`
35
+ * `--exclude-dir DIR`: Omit the result of files that are placed under the directory `DIR`. If there are some directory specifications, the latter one is stronger. (Assuming that `--include-dir foo --exclude-dir foo/bar` is specified, the analysis result of foo/bar/baz.rb is omitted, but foo/baz.rb is shown.)
36
+ * `--include-dir DIR`: Show the result of files that are placed under the directory `DIR`. If there are some directory specifications, the latter one is stronger. (Assuming that `--exclude-dir foo --include-dir foo/bar` is specified, the analysis result of foo/bar/baz.rb is shown, but foo/baz.rb is omitted.)
28
37
  * `-fshow-errors`: Prints out possible bugs found during execution (often a lot of false positives).
29
38
  * `-fpedantic-output`: When TypeProf inferred a type `A | untyped`, it simply outputs `A` by default. But this option forces it to output `A | untyped`.
30
39
  * `-fshow-container-raw-elements`: (undocumented yet)
@@ -50,7 +59,7 @@ p foo(42) #=> String
50
59
  The analysis results of TypeProf are as follows.
51
60
 
52
61
  ```
53
- $ ruby exe/type-profiler test.rb
62
+ $ ruby exe/typeprof test.rb
54
63
  # Revealed types
55
64
  # test.rb:2 #=> Integer
56
65
  # test.rb:6 #=> String
@@ -84,7 +93,7 @@ p Foo.new.a #=> Integer | String
84
93
  ```
85
94
 
86
95
  ```
87
- $ ruby exe/type-profiler test.rb
96
+ $ ruby exe/typeprof test.rb
88
97
  # Revealed types
89
98
  # test.rb:11 #=> Integer | String
90
99
 
@@ -230,7 +239,7 @@ If a method returns different abstract values, it can lead to retrospective exec
230
239
  Even after TypeProf traced all programs as possible, there may be methods or blocks that aren't executed.
231
240
  For example, a method is not executed if it is called from nowhere; this is typical for library method that has no test.
232
241
  (Basically, when you use TypeProf, it is recommended to invoke all methods with supposed argument types.)
233
- TypeProf forcibly calls these unreachable methods and blocks with `untyped` as argumetns.
242
+ TypeProf forcibly calls these unreachable methods and blocks with `untyped` as arguments.
234
243
 
235
244
  ```
236
245
  def foo(n)
@@ -299,7 +308,7 @@ class Object
299
308
  def foo: (Integer) -> Integer | (String) -> String
300
309
  end
301
310
  ```
302
-
311
+
303
312
  ```
304
313
  # test.rb
305
314
  def foo(n)
@@ -386,9 +395,9 @@ You can update the types; this allows the following code to initialize the array
386
395
  ```
387
396
  def foo
388
397
  a = []
389
-
398
+
390
399
  100.times {|n| a << n.to_s}
391
-
400
+
392
401
  a
393
402
  end
394
403
 
@@ -2,4 +2,5 @@
2
2
 
3
3
  require_relative "../lib/typeprof"
4
4
 
5
- TypeProf::CLI.new(ARGV).run
5
+ config = TypeProf::CLI.parse(ARGV)
6
+ TypeProf.analyze(config)
@@ -1,11 +1,20 @@
1
+ unless defined?(RubyVM::InstructionSequence)
2
+ puts "Currently, TypeProf can work on a Ruby implementation that supports RubyVM::InstructionSequence, such as CRuby."
3
+ exit 1
4
+ end
5
+
1
6
  module TypeProf end
2
7
 
8
+ require_relative "typeprof/version"
9
+ require_relative "typeprof/config"
3
10
  require_relative "typeprof/insns-def"
4
11
  require_relative "typeprof/utils"
5
12
  require_relative "typeprof/type"
6
13
  require_relative "typeprof/container-type"
7
14
  require_relative "typeprof/method"
15
+ require_relative "typeprof/block"
8
16
  require_relative "typeprof/iseq"
17
+ require_relative "typeprof/arguments"
9
18
  require_relative "typeprof/analyzer"
10
19
  require_relative "typeprof/import"
11
20
  require_relative "typeprof/export"
@@ -128,7 +128,13 @@ module TypeProf
128
128
  other.type_params.internal_hash.each do |id, elems|
129
129
  elems2 = type_params[id]
130
130
  if elems2
131
- type_params[id] = elems.union(elems2) if elems != elems2
131
+ if elems != elems2
132
+ if elems.is_a?(Array) # should be refactored as Cell::Elements
133
+ type_params[id] = elems.zip(elems2).map {|elem1, elem2| elem1.union(elem2) }
134
+ else
135
+ type_params[id] = elems.union(elems2)
136
+ end
137
+ end
132
138
  else
133
139
  type_params[id] = elems
134
140
  end
@@ -176,15 +182,8 @@ module TypeProf
176
182
  Env.new(@static_env, Utils.array_update(@locals, idx, ty), @stack, @type_params)
177
183
  end
178
184
 
179
- def deploy_array_type(alloc_site, elems, base_ty)
180
- local_ty = Type::LocalArray.new(alloc_site, base_ty)
181
- type_params = Utils::HashWrapper.new(@type_params.internal_hash.merge({ alloc_site => elems }))
182
- nenv = Env.new(@static_env, @locals, @stack, type_params)
183
- return nenv, local_ty
184
- end
185
-
186
- def deploy_hash_type(alloc_site, elems, base_ty)
187
- local_ty = Type::LocalHash.new(alloc_site, base_ty)
185
+ def deploy_type(klass, alloc_site, elems, base_ty)
186
+ local_ty = klass.new(alloc_site, base_ty)
188
187
  type_params = Utils::HashWrapper.new(@type_params.internal_hash.merge({ alloc_site => elems }))
189
188
  nenv = Env.new(@static_env, @locals, @stack, type_params)
190
189
  return nenv, local_ty
@@ -231,12 +230,13 @@ module TypeProf
231
230
 
232
231
  @alloc_site_to_global_id = {}
233
232
 
234
- @callsites, @return_envs, @sig_fargs, @sig_ret, @yields = {}, {}, {}, {}, {}
233
+ @callsites, @return_envs = {}, {}
235
234
  @block_to_ctx = {}
235
+ @method_signatures = {}
236
+ @block_signatures = {}
237
+ @return_values = {}
236
238
  @gvar_table = VarTable.new
237
239
 
238
- @include_relations = {}
239
-
240
240
  @errors = []
241
241
  @reveal_types = {}
242
242
  @backward_edges = {}
@@ -247,6 +247,8 @@ module TypeProf
247
247
  @loaded_features = {}
248
248
 
249
249
  @rbs_reader = RBSReader.new
250
+
251
+ @terminated = false
250
252
  end
251
253
 
252
254
  attr_reader :return_envs, :loaded_features, :rbs_reader
@@ -261,7 +263,7 @@ module TypeProf
261
263
  env2 = @ep2env[ep]
262
264
  if env2
263
265
  nenv = env2.merge(env)
264
- if !nenv.eql?(env2) && !@worklist.member?(ep)
266
+ if nenv != env2 && !@worklist.member?(ep)
265
267
  @worklist.insert(ep.key, ep)
266
268
  end
267
269
  @ep2env[ep] = nenv
@@ -274,48 +276,56 @@ module TypeProf
274
276
  attr_reader :class_defs
275
277
 
276
278
  class ClassDef # or ModuleDef
277
- def initialize(kind, name, superclass)
279
+ def initialize(kind, name, superclass, absolute_path)
280
+ raise unless name.is_a?(Array)
278
281
  @kind = kind
279
282
  @superclass = superclass
280
- @modules = { true => [], false => [] }
283
+ @modules = { true => {}, false => {} }
281
284
  @name = name
282
285
  @consts = {}
283
286
  @methods = {}
284
287
  @ivars = VarTable.new
285
288
  @cvars = VarTable.new
289
+ @absolute_path = absolute_path
290
+ @namespace = nil
286
291
  end
287
292
 
288
- attr_reader :kind, :modules, :methods, :superclass, :ivars, :cvars
293
+ attr_reader :kind, :superclass, :modules, :consts, :methods, :ivars, :cvars, :absolute_path
289
294
  attr_accessor :name, :klass_obj
290
295
 
291
- def include_module(mod, visible)
296
+ def include_module(mod, absolute_path)
292
297
  # XXX: need to check if mod is already included by the ancestors?
293
- unless @modules[false].include?([visible, mod])
294
- @modules[false] << [visible, mod]
298
+ absolute_paths = @modules[false][mod]
299
+ unless absolute_paths
300
+ @modules[false][mod] = absolute_paths = Utils::MutableSet.new
295
301
  end
302
+ absolute_paths << absolute_path
296
303
  end
297
304
 
298
- def extend_module(mod, visible)
305
+ def extend_module(mod, absolute_path)
299
306
  # XXX: need to check if mod is already included by the ancestors?
300
- unless @modules[true].include?([visible, mod])
301
- @modules[true] << [visible, mod]
307
+ absolute_paths = @modules[true][mod]
308
+ unless absolute_paths
309
+ @modules[true][mod] = absolute_paths = Utils::MutableSet.new
302
310
  end
311
+ absolute_paths << absolute_path
303
312
  end
304
313
 
305
314
  def get_constant(name)
306
- @consts[name] || Type.any # XXX: warn?
315
+ ty, = @consts[name]
316
+ ty || Type.any # XXX: warn?
307
317
  end
308
318
 
309
- def add_constant(name, ty)
319
+ def add_constant(name, ty, absolute_path)
310
320
  if @consts[name]
311
321
  # XXX: warn!
312
322
  end
313
- @consts[name] = ty
323
+ @consts[name] = [ty, absolute_path]
314
324
  end
315
325
 
316
326
  def get_method(mid, singleton)
317
327
  @methods[[singleton, mid]] || begin
318
- @modules[singleton].reverse_each do |_visible, mod|
328
+ @modules[singleton].each_key do |mod|
319
329
  meth = mod.get_method(mid, false)
320
330
  return meth if meth
321
331
  end
@@ -336,22 +346,22 @@ module TypeProf
336
346
  @methods[[singleton, mid]] << mdef
337
347
  # Need to restart...?
338
348
  end
349
+
350
+ def set_method(mid, singleton, mdef)
351
+ @methods[[singleton, mid]] = Utils::MutableSet.new
352
+ @methods[[singleton, mid]] << mdef
353
+ end
339
354
  end
340
355
 
341
- def include_module(including_mod, included_mod, visible = true)
356
+ def include_module(including_mod, included_mod, absolute_path)
342
357
  return if included_mod == Type.any
343
358
 
344
- if visible
345
- @include_relations[including_mod] ||= Utils::MutableSet.new
346
- @include_relations[including_mod] << included_mod
347
- end
348
-
349
359
  including_mod = @class_defs[including_mod.idx]
350
360
  included_mod.each_child do |included_mod|
351
361
  if included_mod.is_a?(Type::Class)
352
362
  included_mod = @class_defs[included_mod.idx]
353
363
  if included_mod && included_mod.kind == :module
354
- including_mod.include_module(included_mod, visible)
364
+ including_mod.include_module(included_mod, absolute_path)
355
365
  else
356
366
  warn "including something that is not a module"
357
367
  end
@@ -359,13 +369,13 @@ module TypeProf
359
369
  end
360
370
  end
361
371
 
362
- def extend_module(extending_mod, extended_mod, visible = true)
372
+ def extend_module(extending_mod, extended_mod, absolute_path)
363
373
  extending_mod = @class_defs[extending_mod.idx]
364
374
  extended_mod.each_child do |extended_mod|
365
375
  if extended_mod.is_a?(Type::Class)
366
376
  extended_mod = @class_defs[extended_mod.idx]
367
377
  if extended_mod && extended_mod.kind == :module
368
- extending_mod.extend_module(extended_mod, visible)
378
+ extending_mod.extend_module(extended_mod, absolute_path)
369
379
  else
370
380
  warn "extending something that is not a module"
371
381
  end
@@ -373,12 +383,12 @@ module TypeProf
373
383
  end
374
384
  end
375
385
 
376
- def new_class(cbase, name, type_params, superclass)
377
- if cbase && cbase.idx != 0
378
- show_name = "#{ @class_defs[cbase.idx].name }::#{ name }"
379
- else
380
- show_name = name.to_s
381
- end
386
+ def cbase_path(cbase)
387
+ cbase && cbase.idx != 1 ? @class_defs[cbase.idx].name : []
388
+ end
389
+
390
+ def new_class(cbase, name, type_params, superclass, absolute_path)
391
+ show_name = cbase_path(cbase) + [name]
382
392
  idx = @class_defs.size
383
393
  if superclass
384
394
  if superclass == :__root__
@@ -386,18 +396,18 @@ module TypeProf
386
396
  else
387
397
  superclass_idx = superclass.idx
388
398
  end
389
- @class_defs[idx] = ClassDef.new(:class, show_name, superclass_idx)
399
+ @class_defs[idx] = ClassDef.new(:class, show_name, superclass_idx, absolute_path)
390
400
  klass = Type::Class.new(:class, idx, type_params, superclass, show_name)
391
401
  @class_defs[idx].klass_obj = klass
392
402
  cbase ||= klass # for bootstrap
393
- add_constant(cbase, name, klass)
403
+ add_constant(cbase, name, klass, absolute_path)
394
404
  return klass
395
405
  else
396
406
  # module
397
- @class_defs[idx] = ClassDef.new(:module, show_name, nil)
407
+ @class_defs[idx] = ClassDef.new(:module, show_name, nil, absolute_path)
398
408
  mod = Type::Class.new(:module, idx, type_params, nil, show_name)
399
409
  @class_defs[idx].klass_obj = mod
400
- add_constant(cbase, name, mod)
410
+ add_constant(cbase, name, mod, absolute_path)
401
411
  return mod
402
412
  end
403
413
  end
@@ -407,8 +417,8 @@ module TypeProf
407
417
 
408
418
  idx = @class_defs.size
409
419
  superclass = Type::Builtin[:struct]
410
- @class_defs[idx] = ClassDef.new(:class, "(Struct)", superclass.idx)
411
- klass = Type::Class.new(:class, idx, [], superclass, "(Struct)")
420
+ @class_defs[idx] = ClassDef.new(:class, ["(Anonymous Struct)"], superclass.idx, ep.ctx.iseq.absolute_path)
421
+ klass = Type::Class.new(:class, idx, [], superclass, "(Anonymous Struct)")
412
422
  @class_defs[idx].klass_obj = klass
413
423
 
414
424
  @struct_defs[ep] = klass
@@ -416,11 +426,25 @@ module TypeProf
416
426
  klass
417
427
  end
418
428
 
429
+ attr_accessor :namespace
430
+
419
431
  def get_class_name(klass)
420
432
  if klass == Type.any
421
433
  "???"
422
434
  else
423
- @class_defs[klass.idx].name
435
+ path = @class_defs[klass.idx].name
436
+ if @namespace
437
+ i = 0
438
+ i += 1 while @namespace[i] && @namespace[i] == path[i]
439
+ if path[i]
440
+ path[i..].join("::")
441
+ else
442
+ path.last.to_s
443
+ end
444
+ else
445
+ #"::" + path.join("::")
446
+ path.join("::")
447
+ end
424
448
  end
425
449
  end
426
450
 
@@ -470,11 +494,11 @@ module TypeProf
470
494
  Type.any
471
495
  end
472
496
 
473
- def add_constant(klass, name, value)
497
+ def add_constant(klass, name, value, user_defined)
474
498
  if klass == Type.any
475
499
  self
476
500
  else
477
- @class_defs[klass.idx].add_constant(name, value)
501
+ @class_defs[klass.idx].add_constant(name, value, user_defined)
478
502
  end
479
503
  end
480
504
 
@@ -487,12 +511,17 @@ module TypeProf
487
511
  mdef
488
512
  end
489
513
 
490
- def add_attr_method(klass, mid, ivar, kind)
514
+ def set_method(klass, mid, singleton, mdef)
515
+ @class_defs[klass.idx].set_method(mid, singleton, mdef)
516
+ mdef
517
+ end
518
+
519
+ def add_attr_method(klass, absolute_path, mid, ivar, kind)
491
520
  if kind == :reader || kind == :accessor
492
- add_method(klass, mid, false, AttrMethodDef.new(ivar, :reader))
521
+ add_method(klass, mid, false, AttrMethodDef.new(ivar, :reader, absolute_path))
493
522
  end
494
523
  if kind == :writer || kind == :accessor
495
- add_method(klass, :"#{ mid }=", false, AttrMethodDef.new(ivar, :writer))
524
+ add_method(klass, :"#{ mid }=", false, AttrMethodDef.new(ivar, :writer, absolute_path))
496
525
  end
497
526
  end
498
527
 
@@ -504,20 +533,12 @@ module TypeProf
504
533
  add_method(klass, mid, true, ISeqMethodDef.new(iseq, cref))
505
534
  end
506
535
 
507
- def add_typed_method(recv_ty, mid, fargs, ret_ty)
508
- add_method(recv_ty.klass, mid, false, TypedMethodDef.new([[fargs, ret_ty]]))
509
- end
510
-
511
- def add_singleton_typed_method(recv_ty, mid, fargs, ret_ty)
512
- add_method(recv_ty.klass, mid, true, TypedMethodDef.new([[fargs, ret_ty]]))
513
- end
514
-
515
- def add_custom_method(klass, mid, impl)
516
- add_method(klass, mid, false, CustomMethodDef.new(impl))
536
+ def set_custom_method(klass, mid, impl)
537
+ set_method(klass, mid, false, CustomMethodDef.new(impl))
517
538
  end
518
539
 
519
- def add_singleton_custom_method(klass, mid, impl)
520
- add_method(klass, mid, true, CustomMethodDef.new(impl))
540
+ def set_singleton_custom_method(klass, mid, impl)
541
+ set_method(klass, mid, true, CustomMethodDef.new(impl))
521
542
  end
522
543
 
523
544
  def alias_method(klass, singleton, new, old)
@@ -542,33 +563,36 @@ module TypeProf
542
563
  @iseq_method_to_ctxs[iseq_mdef] << ctx
543
564
  end
544
565
 
545
- def add_callsite!(callee_ctx, fargs, caller_ep, caller_env, &ctn)
566
+ def add_callsite!(callee_ctx, caller_ep, caller_env, &ctn)
546
567
  @executed_iseqs << callee_ctx.iseq if callee_ctx.is_a?(Context)
547
568
 
548
569
  @callsites[callee_ctx] ||= {}
549
570
  @callsites[callee_ctx][caller_ep] = ctn
550
571
  merge_return_env(caller_ep) {|env| env ? env.merge(caller_env) : caller_env }
551
572
 
552
- if @sig_fargs[callee_ctx]
553
- @sig_fargs[callee_ctx] = @sig_fargs[callee_ctx].merge(fargs)
554
- else
555
- @sig_fargs[callee_ctx] = fargs
556
- end
557
- ret_ty = @sig_ret[callee_ctx] ||= Type.bot
558
- unless ret_ty.eql?(Type.bot)
573
+ ret_ty = @return_values[callee_ctx] ||= Type.bot
574
+ if ret_ty != Type.bot
559
575
  @callsites[callee_ctx].each do |caller_ep, ctn|
560
576
  ctn[ret_ty, caller_ep, @return_envs[caller_ep]]
561
577
  end
562
578
  end
563
579
  end
564
580
 
581
+ def add_method_signature!(callee_ctx, msig)
582
+ if @method_signatures[callee_ctx]
583
+ @method_signatures[callee_ctx] = @method_signatures[callee_ctx].merge(msig)
584
+ else
585
+ @method_signatures[callee_ctx] = msig
586
+ end
587
+ end
588
+
565
589
  def merge_return_env(caller_ep)
566
590
  @return_envs[caller_ep] = yield @return_envs[caller_ep]
567
591
  end
568
592
 
569
- def add_return_type!(callee_ctx, ret_ty)
570
- @sig_ret[callee_ctx] ||= Type.bot
571
- @sig_ret[callee_ctx] = @sig_ret[callee_ctx].union(ret_ty)
593
+ def add_return_value!(callee_ctx, ret_ty)
594
+ @return_values[callee_ctx] ||= Type.bot
595
+ @return_values[callee_ctx] = @return_values[callee_ctx].union(ret_ty)
572
596
 
573
597
  @callsites[callee_ctx] ||= {}
574
598
  @callsites[callee_ctx].each do |caller_ep, ctn|
@@ -576,43 +600,54 @@ module TypeProf
576
600
  end
577
601
  end
578
602
 
579
- def add_yield!(caller_ctx, aargs, blk_ctx)
580
- aargs_acc, = @yields[caller_ctx]
581
- if aargs_acc
582
- @yields[caller_ctx][0] = aargs_acc.merge(aargs)
583
- else
584
- @yields[caller_ctx] = [aargs, Utils::MutableSet.new]
585
- end
586
- @yields[caller_ctx][1] << blk_ctx
603
+ def add_block_to_ctx!(block_body, ctx)
604
+ raise if !block_body.is_a?(Block)
605
+ @block_to_ctx[block_body] ||= Utils::MutableSet.new
606
+ @block_to_ctx[block_body] << ctx
587
607
  end
588
608
 
589
- def add_block_to_ctx!(blk, ctx)
590
- @block_to_ctx[blk] ||= Utils::MutableSet.new
591
- @block_to_ctx[blk] << ctx
609
+ def add_block_signature!(block_body, bsig)
610
+ if @block_signatures[block_body]
611
+ @block_signatures[block_body] = @block_signatures[block_body].merge(bsig)
612
+ else
613
+ @block_signatures[block_body] = bsig
614
+ end
592
615
  end
593
616
 
594
617
  class VarTable
618
+ Entry = Struct.new(:rbs_declared, :read_continuations, :type, :absolute_paths)
619
+
595
620
  def initialize
596
- @read, @write = {}, {}
621
+ @tbl = {}
597
622
  end
598
623
 
599
- attr_reader :write
600
-
601
624
  def add_read!(site, ep, &ctn)
602
- @read[site] ||= {}
603
- @read[site][ep] = ctn
604
- @write[site] ||= Type.bot
605
- ctn[@write[site], ep]
625
+ entry = @tbl[site] ||= Entry.new(false, {}, Type.bot, Utils::MutableSet.new)
626
+ entry.read_continuations[ep] = ctn
627
+ entry.absolute_paths << ep.ctx.iseq.absolute_path
628
+ ctn[entry.type, ep]
606
629
  end
607
630
 
608
- def add_write!(site, ty, &ctn)
609
- @write[site] ||= Type.bot
610
- @write[site] = @write[site].union(ty)
611
- @read[site] ||= {}
612
- @read[site].each do |ep, ctn|
631
+ def add_write!(site, ty, ep, scratch)
632
+ entry = @tbl[site] ||= Entry.new(!ep, {}, Type.bot, Utils::MutableSet.new)
633
+ if ep
634
+ if entry.rbs_declared
635
+ if !entry.type.consistent?(ty, {})
636
+ scratch.warn(ep, "inconsistent assignment to RBS-declared global variable")
637
+ return
638
+ end
639
+ end
640
+ entry.absolute_paths << ep.ctx.iseq.absolute_path
641
+ end
642
+ entry.type = entry.type.union(ty)
643
+ entry.read_continuations.each do |ep, ctn|
613
644
  ctn[ty, ep]
614
645
  end
615
646
  end
647
+
648
+ def dump
649
+ @tbl
650
+ end
616
651
  end
617
652
 
618
653
  def get_ivar(recv)
@@ -637,11 +672,11 @@ module TypeProf
637
672
  end
638
673
  end
639
674
 
640
- def add_ivar_write!(recv, var, ty, &ctn)
675
+ def add_ivar_write!(recv, var, ty, ep)
641
676
  recv.each_child do |recv|
642
677
  class_def, singleton = get_ivar(recv)
643
678
  next unless class_def
644
- class_def.ivars.add_write!([singleton, var], ty, &ctn)
679
+ class_def.ivars.add_write!([singleton, var], ty, ep, self)
645
680
  end
646
681
  end
647
682
 
@@ -653,11 +688,11 @@ module TypeProf
653
688
  end
654
689
  end
655
690
 
656
- def add_cvar_write!(klass, var, ty, &ctn)
691
+ def add_cvar_write!(klass, var, ty, ep)
657
692
  klass.each_child do |klass|
658
693
  class_def = @class_defs[klass.idx]
659
694
  next unless class_def
660
- class_def.cvars.add_write!(var, ty, &ctn)
695
+ class_def.cvars.add_write!(var, ty, ep, self)
661
696
  end
662
697
  end
663
698
 
@@ -665,8 +700,8 @@ module TypeProf
665
700
  @gvar_table.add_read!(var, ep, &ctn)
666
701
  end
667
702
 
668
- def add_gvar_write!(var, ty, &ctn)
669
- @gvar_table.add_write!(var, ty, &ctn)
703
+ def add_gvar_write!(var, ty, ep)
704
+ @gvar_table.add_write!(var, ty, ep, self)
670
705
  end
671
706
 
672
707
  def error(ep, msg)
@@ -698,7 +733,7 @@ module TypeProf
698
733
  env.get_container_elem_types(id)
699
734
  end
700
735
 
701
- def update_container_elem_types(env, ep, id)
736
+ def update_container_elem_types(env, ep, id, base_type)
702
737
  if ep.outer
703
738
  tmp_ep = ep
704
739
  tmp_ep = tmp_ep.outer while tmp_ep.outer
@@ -708,8 +743,8 @@ module TypeProf
708
743
  menv = menv.update_container_elem_types(id, elems)
709
744
  gid = @alloc_site_to_global_id[id]
710
745
  if gid
711
- ty = globalize_type(elems.to_local_type(id), env, ep)
712
- add_ivar_write!(*gid, ty)
746
+ ty = globalize_type(elems.to_local_type(id, base_type), env, ep)
747
+ add_ivar_write!(*gid, ty, ep)
713
748
  end
714
749
  menv
715
750
  end
@@ -720,8 +755,8 @@ module TypeProf
720
755
  env = env.update_container_elem_types(id, elems)
721
756
  gid = @alloc_site_to_global_id[id]
722
757
  if gid
723
- ty = globalize_type(elems.to_local_type(id), env, ep)
724
- add_ivar_write!(*gid, ty)
758
+ ty = globalize_type(elems.to_local_type(id, base_type), env, ep)
759
+ add_ivar_write!(*gid, ty, ep)
725
760
  end
726
761
  env
727
762
  end
@@ -732,7 +767,7 @@ module TypeProf
732
767
 
733
768
  if elems
734
769
  return elems[idx] || Type.nil if idx
735
- return elems.squash
770
+ return elems.squash_or_any
736
771
  else
737
772
  Type.any
738
773
  end
@@ -749,20 +784,35 @@ module TypeProf
749
784
  end
750
785
 
751
786
  def type_profile
752
- counter = 0
787
+ start_time = tick = Time.now
788
+ iter_counter = 0
753
789
  stat_eps = Utils::MutableSet.new
790
+
754
791
  while true
755
792
  until @worklist.empty?
756
- counter += 1
757
- if counter % 1000 == 0 && Config.verbose >= 1
758
- puts "iter %d, remain: %d" % [counter, @worklist.size]
759
- #exit if counter == 20000
793
+ ep = @worklist.deletemin
794
+
795
+ iter_counter += 1
796
+ if Config.verbose >= 1
797
+ tick2 = Time.now
798
+ if tick2 - tick >= 1
799
+ tick = tick2
800
+ $stderr << "\rType Profiling... (%d steps @ %s)\e[K" % [iter_counter, ep.source_location]
801
+ $stderr.flush
802
+ end
803
+ end
804
+
805
+ if (Config.max_sec && Time.now - start_time >= Config.max_sec) || (Config.max_iter && Config.max_iter <= iter_counter)
806
+ @terminated = true
807
+ break
760
808
  end
761
- @ep = @worklist.deletemin
762
- stat_eps << @ep
763
- step(@ep) # TODO: deletemin
809
+
810
+ stat_eps << ep
811
+ step(ep)
764
812
  end
765
813
 
814
+ break if @terminated
815
+
766
816
  # XXX: it would be good to provide no-dummy-execution mode.
767
817
  # It should work as a bit smarter "rbs prototype rb";
768
818
  # show all method definitions as "untyped" arguments and return values
@@ -782,61 +832,29 @@ module TypeProf
782
832
  merge_env(ep, env)
783
833
  add_iseq_method_call!(meth, ep.ctx)
784
834
 
785
- fargs_format = iseq.fargs_format
786
- lead_tys = [Type.any] * (fargs_format[:lead_num] || 0)
787
- opt_tys = fargs_format[:opt] ? [] : nil
788
- post_tys = [Type.any] * (fargs_format[:post_num] || 0)
789
- if fargs_format[:kwbits]
790
- kw_tys = []
791
- fargs_format[:keyword].each do |kw|
792
- case
793
- when kw.is_a?(Symbol) # required keyword
794
- key = kw
795
- req = true
796
- ty = Type.any
797
- when kw.size == 2 # optional keyword (default value is a literal)
798
- key, ty = *kw
799
- ty = Type.guess_literal_type(ty)
800
- ty = ty.type if ty.is_a?(Type::Literal)
801
- else # optional keyword
802
- key, = kw
803
- req = false
804
- ty = Type.any
805
- end
806
- kw_tys << [req, key, ty]
807
- end
808
- else
809
- kw_tys = nil
810
- end
811
- fargs = FormalArguments.new(lead_tys, opt_tys, nil, post_tys, kw_tys, nil, nil)
812
- add_callsite!(ep.ctx, fargs, nil, nil) do |_ret_ty, _ep, _env|
813
- # ignore
814
- end
815
-
816
835
  when :block
817
- epenvs = dummy_continuation
836
+ blk, epenvs = dummy_continuation
818
837
  epenvs.each do |ep, env|
819
838
  merge_env(ep, env)
839
+ add_block_to_ctx!(blk.block_body, ep.ctx)
820
840
  end
821
841
  end
822
842
  end
843
+ $stderr.print "\r\e[K" if Config.verbose >= 1
823
844
 
824
845
  stat_eps
825
846
  end
826
847
 
827
848
  def report(stat_eps, output)
849
+ Reporters.show_message(@terminated, output)
850
+
828
851
  Reporters.show_error(@errors, @backward_edges, output)
829
852
 
830
853
  Reporters.show_reveal_types(self, @reveal_types, output)
831
854
 
832
- Reporters.show_gvars(self, @gvar_table.write, output)
833
-
834
- #RubySignatureExporter2.new(
835
- # self, @include_relations, @ivar_table.write, @cvar_table.write, @class_defs
836
- #).show
855
+ Reporters.show_gvars(self, @gvar_table, output)
837
856
 
838
- #return
839
- RubySignatureExporter.new(self, @class_defs, @iseq_method_to_ctxs, @sig_fargs, @sig_ret, @yields).show(stat_eps, output)
857
+ RubySignatureExporter.new(self, @class_defs, @iseq_method_to_ctxs).show(stat_eps, output)
840
858
  end
841
859
 
842
860
  def globalize_type(ty, env, ep)
@@ -867,17 +885,35 @@ module TypeProf
867
885
  ctx = Context.new(iseq, cref, mid)
868
886
  ep = ExecutionPoint.new(ctx, 0, nil)
869
887
  locals = [Type.any] * iseq.locals.size
888
+
889
+ keyword = iseq.fargs_format[:keyword]
890
+ if keyword
891
+ kw_start = iseq.fargs_format[:kwbits]
892
+ kw_start -= iseq.fargs_format[:keyword].size if kw_start
893
+ keyword.each_with_index do |kw, i|
894
+ case
895
+ when kw.is_a?(Symbol) # required keyword
896
+ when kw.size == 2 # optional keyword (default value is a literal)
897
+ _key, default_ty = *kw
898
+ default_ty = Type.guess_literal_type(default_ty)
899
+ default_ty = default_ty.type if default_ty.is_a?(Type::Literal)
900
+ locals[kw_start + i] = default_ty.union(Type.any)
901
+ else # optional keyword (default value is an expression)
902
+ end
903
+ end
904
+ end
905
+
870
906
  env = Env.new(StaticEnv.new(recv, Type.any, false), locals, [], Utils::HashWrapper.new({}))
871
907
 
872
908
  @pending_execution[iseq] ||= [:method, [meth, ep, env]]
873
909
  end
874
910
 
875
- def pend_block_dummy_execution(iseq, nep, nenv)
876
- @pending_execution[iseq] ||= [:block, {}]
877
- if @pending_execution[iseq][1][nep]
878
- @pending_execution[iseq][1][nep] = @pending_execution[iseq][1][nep].merge(nenv)
911
+ def pend_block_dummy_execution(blk, iseq, nep, nenv)
912
+ @pending_execution[iseq] ||= [:block, [blk, {}]]
913
+ if @pending_execution[iseq][1][1][nep]
914
+ @pending_execution[iseq][1][1][nep] = @pending_execution[iseq][1][1][nep].merge(nenv)
879
915
  else
880
- @pending_execution[iseq][1][nep] = nenv
916
+ @pending_execution[iseq][1][1][nep] = nenv
881
917
  end
882
918
  end
883
919
 
@@ -886,7 +922,7 @@ module TypeProf
886
922
  alloc_site = AllocationSite.new(ep)
887
923
  nenv, ty = localize_type(ty, env, ep, alloc_site)
888
924
  case ty
889
- when Type::LocalArray, Type::LocalHash
925
+ when Type::LocalCell, Type::LocalArray, Type::LocalHash
890
926
  @alloc_site_to_global_id[ty.id] = [recv, var] # need overwrite check??
891
927
  end
892
928
  yield ty, nenv
@@ -895,7 +931,7 @@ module TypeProf
895
931
 
896
932
  def set_instance_variable(recv, var, ty, ep, env)
897
933
  ty = globalize_type(ty, env, ep)
898
- add_ivar_write!(recv, var, ty)
934
+ add_ivar_write!(recv, var, ty, ep)
899
935
  end
900
936
 
901
937
  def step(ep)
@@ -911,6 +947,67 @@ module TypeProf
911
947
  end
912
948
 
913
949
  case insn
950
+ when :_iseq_body_start
951
+ # XXX: reconstruct and record the method signature
952
+ iseq = ep.ctx.iseq
953
+ lead_num = iseq.fargs_format[:lead_num] || 0
954
+ opt = iseq.fargs_format[:opt] || [0]
955
+ rest_start = iseq.fargs_format[:rest_start]
956
+ post_start = iseq.fargs_format[:post_start]
957
+ post_num = iseq.fargs_format[:post_num] || 0
958
+ kw_start = iseq.fargs_format[:kwbits]
959
+ keyword = iseq.fargs_format[:keyword]
960
+ kw_start -= keyword.size if kw_start
961
+ kw_rest = iseq.fargs_format[:kwrest]
962
+ block_start = iseq.fargs_format[:block_start]
963
+
964
+ lead_tys = env.locals[0, lead_num].map {|ty| globalize_type(ty, env, ep) }
965
+ opt_tys = opt.size > 1 ? env.locals[lead_num, opt.size - 1].map {|ty| globalize_type(ty, env, ep) } : nil
966
+ if rest_start # XXX:squash
967
+ ty = globalize_type(env.locals[lead_num + opt.size - 1], env, ep)
968
+ rest_ty = Type.bot
969
+ ty.each_child_global do |ty|
970
+ if ty.is_a?(Type::Array)
971
+ rest_ty = rest_ty.union(ty.elems.squash)
972
+ else
973
+ # XXX: to_ary?
974
+ rest_ty = rest_ty.union(ty)
975
+ end
976
+ end
977
+ end
978
+ post_tys = (post_start ? env.locals[post_start, post_num] : []).map {|ty| globalize_type(ty, env, ep) }
979
+ if keyword
980
+ kw_tys = []
981
+ keyword.each_with_index do |kw, i|
982
+ case
983
+ when kw.is_a?(Symbol) # required keyword
984
+ key = kw
985
+ req = true
986
+ when kw.size == 2 # optional keyword (default value is a literal)
987
+ key, default_ty = *kw
988
+ default_ty = Type.guess_literal_type(default_ty)
989
+ default_ty = default_ty.type if default_ty.is_a?(Type::Literal)
990
+ req = false
991
+ else # optional keyword (default value is an expression)
992
+ key, = kw
993
+ req = false
994
+ end
995
+ ty = env.locals[kw_start + i]
996
+ ty = ty.union(default_ty) if default_ty
997
+ ty = globalize_type(ty, env, ep)
998
+ kw_tys << [req, key, ty]
999
+ end
1000
+ end
1001
+ kw_rest_ty = globalize_type(env.locals[kw_rest], env, ep) if kw_rest
1002
+ if block_start
1003
+ blk_ty = globalize_type(env.locals[block_start], env, ep)
1004
+ elsif iseq.type == :method
1005
+ blk_ty = env.static_env.blk_ty
1006
+ else
1007
+ blk_ty = Type.nil
1008
+ end
1009
+ msig = MethodSignature.new(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, blk_ty)
1010
+ add_method_signature!(ep.ctx, msig)
914
1011
  when :putspecialobject
915
1012
  kind, = operands
916
1013
  ty = case kind
@@ -985,6 +1082,7 @@ module TypeProf
985
1082
  recv = env.static_env.recv_ty
986
1083
  if cref.klass.is_a?(Type::Class)
987
1084
  typed_mdef = check_typed_method(cref.klass, mid, ep.ctx.cref.singleton)
1085
+ recv = Type::Instance.new(recv) if recv.is_a?(Type::Class)
988
1086
  if typed_mdef
989
1087
  mdef = ISeqMethodDef.new(iseq, cref)
990
1088
  typed_mdef.each do |typed_mdef|
@@ -1000,7 +1098,6 @@ module TypeProf
1000
1098
  end
1001
1099
  end
1002
1100
 
1003
- recv = Type::Instance.new(recv) if recv.is_a?(Type::Class)
1004
1101
  pend_method_execution(iseq, meth, recv, mid, ep.ctx.cref)
1005
1102
  end
1006
1103
  else
@@ -1043,7 +1140,7 @@ module TypeProf
1043
1140
  elsif superclass == Type.any
1044
1141
  warn(ep, "superclass is any; Object is used instead")
1045
1142
  superclass = Type::Builtin[:obj]
1046
- elsif superclass.eql?(Type.nil)
1143
+ elsif superclass == Type.nil
1047
1144
  superclass = Type::Builtin[:obj]
1048
1145
  elsif superclass.is_a?(Type::Instance)
1049
1146
  warn(ep, "superclass is an instance; Object is used instead")
@@ -1058,7 +1155,7 @@ module TypeProf
1058
1155
  if cbase == Type.any
1059
1156
  klass = Type.any
1060
1157
  else
1061
- klass = new_class(cbase, id, [], superclass)
1158
+ klass = new_class(cbase, id, [], superclass, ep.ctx.iseq.absolute_path)
1062
1159
  end
1063
1160
  end
1064
1161
  end
@@ -1083,14 +1180,14 @@ module TypeProf
1083
1180
  locals = [Type.nil] * iseq.locals.size
1084
1181
  nenv = Env.new(StaticEnv.new(recv, blk, false), locals, [], Utils::HashWrapper.new({}))
1085
1182
  merge_env(nep, nenv)
1086
- add_callsite!(nep.ctx, nil, ep, env) do |ret_ty, ep, env|
1183
+ add_callsite!(nep.ctx, ep, env) do |ret_ty, ep, env|
1087
1184
  nenv, ret_ty = localize_type(ret_ty, env, ep)
1088
1185
  nenv = nenv.push(ret_ty)
1089
1186
  merge_env(ep.next, nenv)
1090
1187
  end
1091
1188
  return
1092
1189
  when :send
1093
- env, recvs, mid, aargs = setup_actual_arguments(operands, ep, env)
1190
+ env, recvs, mid, aargs = setup_actual_arguments(:method, operands, ep, env)
1094
1191
  recvs = Type.any if recvs == Type.bot
1095
1192
  recvs.each_child do |recv|
1096
1193
  do_send(recv, mid, aargs, ep, env) do |ret_ty, ep, env|
@@ -1102,7 +1199,7 @@ module TypeProf
1102
1199
  return
1103
1200
  when :send_branch
1104
1201
  getlocal_operands, send_operands, branch_operands = operands
1105
- env, recvs, mid, aargs = setup_actual_arguments(send_operands, ep, env)
1202
+ env, recvs, mid, aargs = setup_actual_arguments(:method, send_operands, ep, env)
1106
1203
  recvs = Type.any if recvs == Type.bot
1107
1204
  recvs.each_child do |recv|
1108
1205
  do_send(recv, mid, aargs, ep, env) do |ret_ty, ep, env|
@@ -1129,23 +1226,16 @@ module TypeProf
1129
1226
  end
1130
1227
  return
1131
1228
  when :invokeblock
1132
- # XXX: need block parameter, unknown block, etc. Use setup_actual_arguments
1133
- opt, = operands
1134
- _flags = opt[:flag]
1135
- orig_argc = opt[:orig_argc]
1136
- env, aargs = env.pop(orig_argc)
1229
+ env, recvs, mid, aargs = setup_actual_arguments(:block, operands, ep, env)
1137
1230
  blk = env.static_env.blk_ty
1138
1231
  case
1139
- when blk.eql?(Type.nil)
1232
+ when blk == Type.nil
1140
1233
  env = env.push(Type.any)
1141
- when blk.eql?(Type.any)
1234
+ when blk == Type.any
1142
1235
  #warn(ep, "block is any")
1143
1236
  env = env.push(Type.any)
1144
1237
  else # Proc
1145
- blk_nil = Type.nil
1146
- #
1147
- aargs = ActualArguments.new(aargs, nil, nil, blk_nil)
1148
- do_invoke_block(true, env.static_env.blk_ty, aargs, ep, env) do |ret_ty, ep, env|
1238
+ do_invoke_block(blk, aargs, ep, env) do |ret_ty, ep, env|
1149
1239
  nenv, ret_ty, = localize_type(ret_ty, env, ep)
1150
1240
  nenv = nenv.push(ret_ty)
1151
1241
  merge_env(ep.next, nenv)
@@ -1153,7 +1243,7 @@ module TypeProf
1153
1243
  return
1154
1244
  end
1155
1245
  when :invokesuper
1156
- env, recv, _, aargs = setup_actual_arguments(operands, ep, env)
1246
+ env, recv, _, aargs = setup_actual_arguments(:method, operands, ep, env)
1157
1247
 
1158
1248
  env, recv = localize_type(env.static_env.recv_ty, env, ep)
1159
1249
  mid = ep.ctx.mid
@@ -1186,7 +1276,7 @@ module TypeProf
1186
1276
  end
1187
1277
  env, (ty,) = env.pop(1)
1188
1278
  ty = globalize_type(ty, env, ep)
1189
- add_return_type!(ep.ctx, ty)
1279
+ add_return_value!(ep.ctx, ty)
1190
1280
  return
1191
1281
  when :throw
1192
1282
  throwtype, = operands
@@ -1200,7 +1290,7 @@ module TypeProf
1200
1290
  ty = globalize_type(ty, env, ep)
1201
1291
  tmp_ep = ep
1202
1292
  tmp_ep = tmp_ep.outer while tmp_ep.outer
1203
- add_return_type!(tmp_ep.ctx, ty)
1293
+ add_return_value!(tmp_ep.ctx, ty)
1204
1294
  return
1205
1295
  when :break
1206
1296
  tmp_ep = ep
@@ -1238,7 +1328,7 @@ module TypeProf
1238
1328
  raise if iseq.locals != []
1239
1329
  nenv = Env.new(env.static_env, [], [], nil)
1240
1330
  merge_env(nep, nenv)
1241
- add_callsite!(nep.ctx, nil, ep, env) do |ret_ty, ep, env|
1331
+ add_callsite!(nep.ctx, ep, env) do |ret_ty, ep, env|
1242
1332
  nenv, ret_ty = localize_type(ret_ty, env, ep)
1243
1333
  nenv = nenv.push(ret_ty)
1244
1334
  merge_env(ep.next, nenv)
@@ -1254,7 +1344,7 @@ module TypeProf
1254
1344
 
1255
1345
  # TODO: it works for only simple cases: `x = nil; x || 1`
1256
1346
  # It would be good to merge "dup; branchif" to make it context-sensitive-like
1257
- falsy = ty.eql?(Type.nil)
1347
+ falsy = ty == Type.nil
1258
1348
 
1259
1349
  merge_env(ep_then, env)
1260
1350
  merge_env(ep_else, env) unless branchtype == :if && falsy
@@ -1284,7 +1374,7 @@ module TypeProf
1284
1374
  cbase = ep.ctx.cref.klass
1285
1375
  ty = globalize_type(ty, env, ep)
1286
1376
  # TODO: if superclass has the variable, it should be updated
1287
- add_cvar_write!(cbase, var, ty)
1377
+ add_cvar_write!(cbase, var, ty, ep)
1288
1378
 
1289
1379
  when :getclassvariable
1290
1380
  var, = operands
@@ -1300,7 +1390,7 @@ module TypeProf
1300
1390
  var, = operands
1301
1391
  env, (ty,) = env.pop(1)
1302
1392
  ty = globalize_type(ty, env, ep)
1303
- add_gvar_write!(var, ty)
1393
+ add_gvar_write!(var, ty, ep)
1304
1394
 
1305
1395
  when :getglobal
1306
1396
  var, = operands
@@ -1311,7 +1401,6 @@ module TypeProf
1311
1401
  env = env.push(ty)
1312
1402
  else
1313
1403
  add_gvar_read!(var, ep) do |ty, ep|
1314
- ty = Type.nil if ty == Type.bot # HACK
1315
1404
  nenv, ty = localize_type(ty, env, ep)
1316
1405
  merge_env(ep.next, nenv.push(ty))
1317
1406
  end
@@ -1347,8 +1436,37 @@ module TypeProf
1347
1436
  flow_env = env.local_update(-var_idx+2, ret_ty)
1348
1437
  case ret_ty
1349
1438
  when Type.any
1350
- merge_env(ep_then, env)
1351
- merge_env(ep_else, env)
1439
+ merge_env(ep_then, flow_env)
1440
+ merge_env(ep_else, flow_env)
1441
+ when Type::Instance.new(Type::Builtin[:false]), Type.nil
1442
+ merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
1443
+ else
1444
+ merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
1445
+ end
1446
+ end
1447
+ return
1448
+ when :getlocal_dup_branch
1449
+ getlocal_operands, _dup_operands, branch_operands = operands
1450
+ var_idx, _scope_idx, _escaped = getlocal_operands
1451
+ ret_ty = env.get_local(-var_idx+2)
1452
+ unless ret_ty
1453
+ p env.locals
1454
+ raise
1455
+ end
1456
+
1457
+ branchtype, target, = branch_operands
1458
+ # branchtype: :if or :unless or :nil
1459
+ ep_then = ep.next
1460
+ ep_else = ep.jump(target)
1461
+
1462
+ var_idx, _scope_idx, _escaped = getlocal_operands
1463
+
1464
+ ret_ty.each_child do |ret_ty|
1465
+ flow_env = env.local_update(-var_idx+2, ret_ty).push(ret_ty)
1466
+ case ret_ty
1467
+ when Type.any
1468
+ merge_env(ep_then, flow_env)
1469
+ merge_env(ep_else, flow_env)
1352
1470
  when Type::Instance.new(Type::Builtin[:false]), Type.nil
1353
1471
  merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
1354
1472
  else
@@ -1372,6 +1490,10 @@ module TypeProf
1372
1490
 
1373
1491
  ret_ty.each_child do |ret_ty|
1374
1492
  flow_env = env.local_update(-var_idx+2, ret_ty)
1493
+ ret_ty = ret_ty.base_type if ret_ty.is_a?(Type::Symbol)
1494
+ ret_ty = ret_ty.base_type if ret_ty.is_a?(Type::LocalCell)
1495
+ ret_ty = ret_ty.base_type if ret_ty.is_a?(Type::LocalArray)
1496
+ ret_ty = ret_ty.base_type if ret_ty.is_a?(Type::LocalHash)
1375
1497
  if ret_ty.is_a?(Type::Instance)
1376
1498
  if ret_ty.klass == pattern_ty # XXX: inheritance
1377
1499
  merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
@@ -1379,8 +1501,8 @@ module TypeProf
1379
1501
  merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
1380
1502
  end
1381
1503
  else
1382
- merge_env(ep_then, env)
1383
- merge_env(ep_else, env)
1504
+ merge_env(ep_then, flow_env)
1505
+ merge_env(ep_else, flow_env)
1384
1506
  end
1385
1507
  end
1386
1508
  return
@@ -1401,11 +1523,11 @@ module TypeProf
1401
1523
  when :getconstant
1402
1524
  name, = operands
1403
1525
  env, (cbase, _allow_nil,) = env.pop(2)
1404
- if cbase.eql?(Type.nil)
1526
+ if cbase == Type.nil
1405
1527
  ty = search_constant(ep.ctx.cref, name)
1406
1528
  env, ty = localize_type(ty, env, ep)
1407
1529
  env = env.push(ty)
1408
- elsif cbase.eql?(Type.any)
1530
+ elsif cbase == Type.any
1409
1531
  env = env.push(Type.any) # XXX: warning needed?
1410
1532
  else
1411
1533
  ty = get_constant(cbase, name)
@@ -1421,10 +1543,10 @@ module TypeProf
1421
1543
  end
1422
1544
  ty.each_child do |ty|
1423
1545
  if ty.is_a?(Type::Class) && ty.superclass == Type::Builtin[:struct]
1424
- @class_defs[ty.idx].name = name.to_s
1546
+ @class_defs[ty.idx].name = cbase_path(cbase) + [name]
1425
1547
  end
1426
1548
  end
1427
- add_constant(cbase, name, globalize_type(ty, env, ep))
1549
+ add_constant(cbase, name, globalize_type(ty, env, ep), ep.ctx.iseq.absolute_path)
1428
1550
 
1429
1551
  when :getspecial
1430
1552
  key, type = operands
@@ -1453,6 +1575,28 @@ module TypeProf
1453
1575
  when :dup
1454
1576
  env, (ty,) = env.pop(1)
1455
1577
  env = env.push(ty).push(ty)
1578
+ when :dup_branch
1579
+ _dup_operands, branch_operands = operands
1580
+ env, (ty,) = env.pop(1)
1581
+
1582
+ branchtype, target, = branch_operands
1583
+ # branchtype: :if or :unless or :nil
1584
+ ep_then = ep.next
1585
+ ep_else = ep.jump(target)
1586
+
1587
+ ty.each_child do |ty|
1588
+ flow_env = env.push(ty)
1589
+ case ty
1590
+ when Type.any
1591
+ merge_env(ep_then, flow_env)
1592
+ merge_env(ep_else, flow_env)
1593
+ when Type::Instance.new(Type::Builtin[:false]), Type.nil
1594
+ merge_env(branchtype == :if ? ep_then : ep_else, flow_env)
1595
+ else
1596
+ merge_env(branchtype == :if ? ep_else : ep_then, flow_env)
1597
+ end
1598
+ end
1599
+ return
1456
1600
  when :duphash
1457
1601
  raw_hash, = operands
1458
1602
  ty = Type.guess_literal_type(raw_hash)
@@ -1542,11 +1686,11 @@ module TypeProf
1542
1686
  if ary2.is_a?(Type::LocalArray)
1543
1687
  elems2 = get_container_elem_types(env, ep, ary2.id)
1544
1688
  elems = Type::Array::Elements.new([], elems1.squash.union(elems2.squash))
1545
- env = update_container_elem_types(env, ep, ary1.id) { elems }
1689
+ env = update_container_elem_types(env, ep, ary1.id, ary1.base_type) { elems }
1546
1690
  env = env.push(ary1)
1547
1691
  else
1548
1692
  elems = Type::Array::Elements.new([], Type.any)
1549
- env = update_container_elem_types(env, ep, ary1.id) { elems }
1693
+ env = update_container_elem_types(env, ep, ary1.id, ary1.base_type) { elems }
1550
1694
  env = env.push(ary1)
1551
1695
  end
1552
1696
  else
@@ -1585,7 +1729,7 @@ module TypeProf
1585
1729
  locals = [Type.nil] * iseq.locals.size
1586
1730
  nenv = Env.new(env.static_env, locals, [], Utils::HashWrapper.new({}))
1587
1731
  merge_env(nep, nenv)
1588
- add_callsite!(nep.ctx, nil, cont_ep, cont_env) do |ret_ty, ep, env|
1732
+ add_callsite!(nep.ctx, cont_ep, cont_env) do |ret_ty, ep, env|
1589
1733
  nenv, ret_ty = localize_type(ret_ty, env, ep)
1590
1734
  nenv = nenv.push(ret_ty)
1591
1735
  merge_env(ep.jump(cont), nenv)
@@ -1617,13 +1761,13 @@ module TypeProf
1617
1761
  merge_env(ep.next, env)
1618
1762
  end
1619
1763
 
1620
- private def setup_actual_arguments(operands, ep, env)
1764
+ private def setup_actual_arguments(kind, operands, ep, env)
1621
1765
  opt, blk_iseq = operands
1622
1766
  flags = opt[:flag]
1623
1767
  mid = opt[:mid]
1624
1768
  kw_arg = opt[:kw_arg]
1625
1769
  argc = opt[:orig_argc]
1626
- argc += 1 # receiver
1770
+ argc += 1 if kind == :method # for the receiver
1627
1771
  argc += kw_arg.size if kw_arg
1628
1772
 
1629
1773
  flag_args_splat = flags[ 0] != 0
@@ -1638,29 +1782,36 @@ module TypeProf
1638
1782
  _flag_super = flags[ 9] != 0
1639
1783
  _flag_zsuper = flags[10] != 0
1640
1784
 
1785
+ argc += 1 if flag_args_blockarg
1786
+
1787
+ env, aargs = env.pop(argc)
1788
+
1789
+ recv = aargs.shift if kind == :method
1790
+
1641
1791
  if flag_args_blockarg
1642
- env, (recv, *aargs, blk_ty) = env.pop(argc + 1)
1643
- raise "both block arg and actual block given" if blk_iseq
1792
+ blk_ty = aargs.pop
1793
+ elsif blk_iseq
1794
+ blk_ty = Type::Proc.new(ISeqBlock.new(blk_iseq, ep), Type::Instance.new(Type::Builtin[:proc]))
1644
1795
  else
1645
- env, (recv, *aargs) = env.pop(argc)
1646
- if blk_iseq
1647
- # check
1648
- blk_ty = Type::ISeqProc.new(blk_iseq, ep, Type::Instance.new(Type::Builtin[:proc]))
1649
- else
1650
- blk_ty = Type.nil
1651
- end
1796
+ blk_ty = Type.nil
1652
1797
  end
1653
1798
 
1799
+ new_blk_ty = Type.bot
1654
1800
  blk_ty.each_child do |blk_ty|
1655
1801
  case blk_ty
1656
1802
  when Type.nil
1657
1803
  when Type.any
1658
- when Type::ISeqProc
1804
+ when Type::Proc
1805
+ when Type::Symbol
1806
+ blk_ty = Type::Proc.new(SymbolBlock.new(blk_ty.sym), Type::Instance.new(Type::Builtin[:proc]))
1659
1807
  else
1808
+ # XXX: attempt to call to_proc
1660
1809
  error(ep, "wrong argument type #{ blk_ty.screen_name(self) } (expected Proc)")
1661
1810
  blk_ty = Type.any
1662
1811
  end
1812
+ new_blk_ty = new_blk_ty.union(blk_ty)
1663
1813
  end
1814
+ blk_ty = new_blk_ty
1664
1815
 
1665
1816
  if flag_args_splat
1666
1817
  # assert !flag_args_kwarg
@@ -1672,33 +1823,38 @@ module TypeProf
1672
1823
  _, (ty,) = ty.elems.take_last(1)
1673
1824
  case ty
1674
1825
  when Type::Hash
1675
- kw_ty = ty
1826
+ kw_tys = ty.elems.to_keywords
1676
1827
  when Type::Union
1677
1828
  hash_elems = nil
1678
1829
  ty.elems&.each do |(container_kind, base_type), elems|
1679
1830
  if container_kind == Type::Hash
1831
+ elems.to_keywords
1680
1832
  hash_elems = hash_elems ? hash_elems.union(elems) : elems
1681
1833
  end
1682
1834
  end
1683
- hash_elems ||= Type::Hash::Elements.new({Type.any => Type.any})
1684
- kw_ty = Type::Hash.new(hash_elems, Type::Instance.new(Type::Builtin[:hash]))
1835
+ if hash_elems
1836
+ kw_tys = hash_elems.to_keywords
1837
+ else
1838
+ kw_tys = { nil => Type.any }
1839
+ end
1685
1840
  else
1686
1841
  warn(ep, "non hash is passed to **kwarg?") unless ty == Type.any
1687
- kw_ty = nil
1842
+ kw_tys = { nil => Type.any }
1688
1843
  end
1689
1844
  else
1690
1845
  raise NotImplementedError
1691
1846
  end
1692
- # XXX: should we remove kw_ty from rest_ty?
1847
+ else
1848
+ kw_tys = {}
1693
1849
  end
1694
- aargs = ActualArguments.new(aargs, rest_ty, kw_ty, blk_ty)
1850
+ aargs = ActualArguments.new(aargs, rest_ty, kw_tys, blk_ty)
1695
1851
  elsif flag_args_kw_splat
1696
1852
  last = aargs.last
1697
1853
  ty = globalize_type(last, env, ep)
1698
1854
  case ty
1699
1855
  when Type::Hash
1700
1856
  aargs = aargs[0..-2]
1701
- kw_ty = ty
1857
+ kw_tys = ty.elems.to_keywords
1702
1858
  when Type::Union
1703
1859
  hash_elems = nil
1704
1860
  ty.elems.each do |(container_kind, base_type), elems|
@@ -1706,31 +1862,30 @@ module TypeProf
1706
1862
  hash_elems = hash_elems ? hash_elems.union(elems) : elems
1707
1863
  end
1708
1864
  end
1709
- hash_elems ||= Type::Hash::Elements.new({Type.any => Type.any})
1710
- kw_ty = Type::Hash.new(hash_elems, Type::Instance.new(Type::Builtin[:hash]))
1865
+ if hash_elems
1866
+ kw_tys = hash_elems.to_keywords
1867
+ else
1868
+ kw_tys = { nil => Type.any }
1869
+ end
1711
1870
  when Type::Any
1712
1871
  aargs = aargs[0..-2]
1713
- kw_ty = ty
1872
+ kw_tys = { nil => Type.any }
1714
1873
  else
1715
1874
  warn(ep, "non hash is passed to **kwarg?")
1716
- kw_ty = nil
1875
+ kw_tys = { nil => Type.any }
1717
1876
  end
1718
- aargs = ActualArguments.new(aargs, nil, kw_ty, blk_ty)
1877
+ aargs = ActualArguments.new(aargs, nil, kw_tys, blk_ty)
1719
1878
  elsif flag_args_kwarg
1720
1879
  kw_vals = aargs.pop(kw_arg.size)
1721
1880
 
1722
- kw_ty = Type.gen_hash do |h|
1723
- kw_arg.zip(kw_vals) do |key, v_ty|
1724
- k_ty = Type::Symbol.new(key, Type::Instance.new(Type::Builtin[:sym]))
1725
- h[k_ty] = v_ty
1726
- end
1881
+ kw_tys = {}
1882
+ kw_arg.zip(kw_vals) do |key, v_ty|
1883
+ kw_tys[key] = v_ty
1727
1884
  end
1728
1885
 
1729
- # kw_ty is Type::Hash, but we don't have to localize it, maybe?
1730
-
1731
- aargs = ActualArguments.new(aargs, nil, kw_ty, blk_ty)
1886
+ aargs = ActualArguments.new(aargs, nil, kw_tys, blk_ty)
1732
1887
  else
1733
- aargs = ActualArguments.new(aargs, nil, nil, blk_ty)
1888
+ aargs = ActualArguments.new(aargs, nil, {}, blk_ty)
1734
1889
  end
1735
1890
 
1736
1891
  if blk_iseq
@@ -1740,7 +1895,7 @@ module TypeProf
1740
1895
  nlocals = [Type.any] * blk_iseq.locals.size
1741
1896
  nsenv = StaticEnv.new(env.static_env.recv_ty, Type.any, env.static_env.mod_func)
1742
1897
  nenv = Env.new(nsenv, nlocals, [], nil)
1743
- pend_block_dummy_execution(blk_iseq, nep, nenv)
1898
+ pend_block_dummy_execution(blk_ty, blk_iseq, nep, nenv)
1744
1899
  merge_return_env(ep) {|tenv| tenv ? tenv.merge(env) : env }
1745
1900
  end
1746
1901
 
@@ -1754,158 +1909,94 @@ module TypeProf
1754
1909
  meth.do_send(recv, mid, aargs, ep, env, self, &ctn)
1755
1910
  end
1756
1911
  else
1757
- if recv != Type.any # XXX: should be configurable
1912
+ case recv
1913
+ when Type::Void
1914
+ error(ep, "void's method is called: #{ globalize_type(recv, env, ep).screen_name(self) }##{ mid }")
1915
+ when Type::Any
1916
+ else
1758
1917
  error(ep, "undefined method: #{ globalize_type(recv, env, ep).screen_name(self) }##{ mid }")
1759
1918
  end
1760
1919
  ctn[Type.any, ep, env]
1761
1920
  end
1762
1921
  end
1763
1922
 
1764
- def do_invoke_block(given_block, blk, aargs, ep, env, replace_recv_ty: nil, &ctn)
1923
+ def do_invoke_block(blk, aargs, ep, env, replace_recv_ty: nil, &ctn)
1765
1924
  blk.each_child do |blk|
1766
- unless blk.is_a?(Type::ISeqProc)
1767
- warn(ep, "non-iseq-proc is passed as a block")
1768
- next
1769
- end
1770
- blk_iseq = blk.iseq
1771
- blk_ep = blk.ep
1772
- blk_env = @return_envs[blk_ep]
1773
- blk_env = blk_env.replace_recv_ty(replace_recv_ty) if replace_recv_ty
1774
- arg_blk = aargs.blk_ty
1775
- aargs_ = aargs.lead_tys.map {|aarg| globalize_type(aarg, env, ep) }
1776
- argc = blk_iseq.fargs_format[:lead_num] || 0
1777
- # actual argc == 1, not array, formal argc == 1: yield 42 => do |x| : x=42
1778
- # actual argc == 1, array, formal argc == 1: yield [42,43,44] => do |x| : x=[42,43,44]
1779
- # actual argc >= 2, formal argc == 1: yield 42,43,44 => do |x| : x=42
1780
- # actual argc == 1, not array, formal argc >= 2: yield 42 => do |x,y| : x,y=42,nil
1781
- # actual argc == 1, array, formal argc >= 2: yield [42,43,44] => do |x,y| : x,y=42,43
1782
- # actual argc >= 2, formal argc >= 2: yield 42,43,44 => do |x,y| : x,y=42,43
1783
- if aargs_.size >= 2 || argc == 0
1784
- aargs_.pop while argc < aargs_.size
1785
- aargs_ << Type.nil while argc > aargs_.size
1925
+ if blk.is_a?(Type::Proc)
1926
+ blk.block_body.do_call(aargs, ep, env, self, replace_recv_ty: replace_recv_ty, &ctn)
1786
1927
  else
1787
- aarg_ty, = aargs_
1788
- if argc == 1
1789
- aargs_ = [aarg_ty || Type.nil]
1790
- else # actual argc == 1 && formal argc >= 2
1791
- ary_elems = nil
1792
- any_ty = nil
1793
- case aarg_ty
1794
- when Type::Union
1795
- ary_elems = nil
1796
- other_elems = nil
1797
- aarg_ty.elems&.each do |(container_kind, base_type), elems|
1798
- if container_kind == Type::Array
1799
- ary_elems = ary_elems ? ary_elems.union(elems) : elems
1800
- else
1801
- other_elems = other_elems ? other_elems.union(elems) : elems
1802
- end
1803
- end
1804
- aarg_ty = Type::Union.new(aarg_ty.types, other_elems)
1805
- any_ty = Type.any if aarg_ty.types.include?(Type.any)
1806
- when Type::Array
1807
- ary_elems = aarg_ty.elems
1808
- aarg_ty = nil
1809
- when Type::Any
1810
- any_ty = Type.any
1811
- end
1812
- aargs_ = [Type.bot] * argc
1813
- aargs_[0] = aargs_[0].union(aarg_ty) if aarg_ty
1814
- argc.times do |i|
1815
- ty = aargs_[i]
1816
- ty = ty.union(ary_elems[i]) if ary_elems
1817
- ty = ty.union(Type.any) if any_ty
1818
- ty = ty.union(Type.nil) if i >= 1 && aarg_ty
1819
- aargs_[i] = ty
1820
- end
1821
- end
1822
- end
1823
- locals = [Type.nil] * blk_iseq.locals.size
1824
- locals[blk_iseq.fargs_format[:block_start]] = arg_blk if blk_iseq.fargs_format[:block_start]
1825
- env_blk = blk_env.static_env.blk_ty
1826
- nfargs = FormalArguments.new(aargs_, [], nil, [], nil, nil, env_blk) # XXX: aargs_ -> fargs
1827
- nctx = Context.new(blk_iseq, blk_ep.ctx.cref, nil)
1828
- nep = ExecutionPoint.new(nctx, 0, blk_ep)
1829
- nenv = Env.new(blk_env.static_env, locals, [], nil)
1830
- alloc_site = AllocationSite.new(nep)
1831
- aargs_.each_with_index do |ty, i|
1832
- alloc_site2 = alloc_site.add_id(i)
1833
- nenv, ty = localize_type(ty, nenv, nep, alloc_site2) # Use Scratch#localize_type?
1834
- nenv = nenv.local_update(i, ty)
1928
+ warn(ep, "non-proc is passed as a block")
1929
+ ctn[Type.any, ep, env]
1835
1930
  end
1836
-
1837
- merge_env(nep, nenv)
1838
-
1839
- # caution: given_block flag is not complete
1840
- #
1841
- # def foo
1842
- # bar do |&blk|
1843
- # yield
1844
- # blk.call
1845
- # end
1846
- # end
1847
- #
1848
- # yield and blk.call call different blocks.
1849
- # So, a context can have two blocks.
1850
- # given_block is calculated by comparing "context's block (yield target)" and "blk", but it is not a correct result
1851
-
1852
- add_yield!(ep.ctx, globalize_type(aargs, env, ep), nep.ctx) if given_block
1853
- add_block_to_ctx!(blk, nep.ctx)
1854
- add_callsite!(nep.ctx, nfargs, ep, env, &ctn)
1855
1931
  end
1856
1932
  end
1857
1933
 
1858
- def proc_screen_name(blk)
1859
- blk_ctxs = []
1860
- blk.each_child_global do |blk|
1861
- if @block_to_ctx[blk]
1862
- @block_to_ctx[blk].each do |ctx|
1863
- blk_ctxs << [ctx, @sig_fargs[ctx]]
1934
+ def show_block_signature(blks)
1935
+ bsig = nil
1936
+ ret_ty = Type.bot
1937
+
1938
+ blks.each do |blk|
1939
+ blk.each_child_global do |blk|
1940
+ bsig0 = @block_signatures[blk.block_body]
1941
+ if bsig0
1942
+ if bsig
1943
+ bsig = bsig.merge(bsig0)
1944
+ else
1945
+ bsig = bsig0
1946
+ end
1947
+ end
1948
+
1949
+ @block_to_ctx[blk.block_body].each do |blk_ctx|
1950
+ ret_ty = ret_ty.union(@return_values[blk_ctx]) if @return_values[blk_ctx]
1864
1951
  end
1865
- else
1866
- # uncalled proc? dummy execution doesn't work?
1867
- #p blk
1868
1952
  end
1869
1953
  end
1870
- show_block_signature(blk_ctxs)
1954
+
1955
+ bsig ||= BlockSignature.new([], [], nil, Type.nil)
1956
+
1957
+ bsig = bsig.screen_name(self)#, block: true)
1958
+ ret_ty = ret_ty.screen_name(self)
1959
+ ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
1960
+
1961
+ bsig = bsig + " " if bsig != ""
1962
+ "{ #{ bsig }-> #{ ret_ty } }"
1871
1963
  end
1872
1964
 
1873
- def show_block_signature(blk_ctxs)
1874
- all_farg_tys = all_ret_tys = nil
1875
- blk_ctxs.each do |blk_ctx, farg_tys|
1876
- if all_farg_tys
1877
- all_farg_tys = all_farg_tys.merge(farg_tys)
1878
- else
1879
- all_farg_tys = farg_tys
1880
- end
1965
+ def show_proc_signature(blks)
1966
+ farg_tys, ret_ty = nil, Type.bot
1881
1967
 
1882
- if all_ret_tys
1883
- all_ret_tys = all_ret_tys.union(@sig_ret[blk_ctx])
1884
- else
1885
- all_ret_tys = @sig_ret[blk_ctx]
1968
+ blks.each do |blk|
1969
+ blk.each_child_global do |blk|
1970
+ next if blk.block_body.is_a?(TypedBlock) # XXX: Support TypedBlock
1971
+ next unless @block_to_ctx[blk.block_body] # this occurs when screen_name is called before type-profiling finished (e.g., error message)
1972
+ @block_to_ctx[blk.block_body].each do |blk_ctx|
1973
+ if farg_tys
1974
+ farg_tys = farg_tys.merge(@method_signatures[blk_ctx])
1975
+ else
1976
+ farg_tys = @method_signatures[blk_ctx]
1977
+ end
1978
+
1979
+ ret_ty = ret_ty.union(@return_values[blk_ctx]) if @return_values[blk_ctx]
1980
+ end
1886
1981
  end
1887
1982
  end
1888
- return "" if !all_farg_tys
1889
- # XXX: should support @yields[blk_ctx] (block's block)
1890
- show_signature(all_farg_tys, nil, all_ret_tys)
1983
+
1984
+ farg_tys = farg_tys ? farg_tys.screen_name(self) : "(unknown)"
1985
+ ret_ty = ret_ty.screen_name(self)
1986
+ ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
1987
+
1988
+ farg_tys = farg_tys + " " if farg_tys != ""
1989
+ "^#{ farg_tys }-> #{ ret_ty }"
1891
1990
  end
1892
1991
 
1893
- def show_signature(farg_tys, yield_data, ret_ty)
1992
+ def show_method_signature(ctx)
1993
+ farg_tys = @method_signatures[ctx]
1994
+ ret_ty = @return_values[ctx] || Type.bot
1995
+
1894
1996
  farg_tys = farg_tys.screen_name(self)
1895
1997
  ret_ty = ret_ty.screen_name(self)
1896
- s = farg_tys.empty? ? "" : "(#{ farg_tys.join(", ") }) "
1897
- if yield_data
1898
- aargs, blk_ctxs = yield_data
1899
- all_blk_ret_ty = Type.bot
1900
- blk_ctxs.each do |blk_ctx|
1901
- all_blk_ret_ty = all_blk_ret_ty.union(@sig_ret[blk_ctx])
1902
- end
1903
- all_blk_ret_ty = all_blk_ret_ty.screen_name(self)
1904
- all_blk_ret_ty = all_blk_ret_ty.include?("|") ? "(#{ all_blk_ret_ty })" : all_blk_ret_ty
1905
- s << "{ #{ aargs.screen_name(self) } -> #{ all_blk_ret_ty } } " if aargs
1906
- end
1907
- s << "-> "
1908
- s << (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty)
1998
+ ret_ty = (ret_ty.include?("|") ? "(#{ ret_ty })" : ret_ty) # XXX?
1999
+ "#{ (farg_tys.empty? ? "" : "#{ farg_tys } ") }-> #{ ret_ty }"
1909
2000
  end
1910
2001
  end
1911
2002
  end