typeprof 0.4.2 → 0.5.4

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +1 -2
  3. data/Gemfile.lock +3 -3
  4. data/README.md +6 -0
  5. data/lib/typeprof/analyzer.rb +97 -84
  6. data/lib/typeprof/arguments.rb +27 -19
  7. data/lib/typeprof/block.rb +5 -2
  8. data/lib/typeprof/builtin.rb +14 -4
  9. data/lib/typeprof/config.rb +10 -6
  10. data/lib/typeprof/container-type.rb +117 -101
  11. data/lib/typeprof/export.rb +1 -1
  12. data/lib/typeprof/import.rb +44 -22
  13. data/lib/typeprof/method.rb +70 -12
  14. data/lib/typeprof/type.rb +108 -89
  15. data/lib/typeprof/version.rb +1 -1
  16. data/smoke/arguments2.rb +1 -1
  17. data/smoke/array11.rb +1 -1
  18. data/smoke/array6.rb +2 -2
  19. data/smoke/array8.rb +1 -1
  20. data/smoke/block-args2.rb +3 -3
  21. data/smoke/block-args3.rb +4 -4
  22. data/smoke/block-blockarg.rb +1 -1
  23. data/smoke/block-kwarg.rb +1 -1
  24. data/smoke/block10.rb +1 -1
  25. data/smoke/block5.rb +1 -1
  26. data/smoke/constant2.rb +1 -2
  27. data/smoke/demo5.rb +1 -1
  28. data/smoke/demo9.rb +1 -1
  29. data/smoke/hash-fetch.rb +3 -3
  30. data/smoke/hash-merge-bang.rb +11 -0
  31. data/smoke/hash1.rb +3 -2
  32. data/smoke/hash3.rb +1 -1
  33. data/smoke/inheritance2.rb +2 -2
  34. data/smoke/ivar2.rb +2 -2
  35. data/smoke/kernel-class.rb +1 -1
  36. data/smoke/keyword4.rb +1 -1
  37. data/smoke/kwsplat1.rb +2 -2
  38. data/smoke/kwsplat2.rb +1 -1
  39. data/smoke/multiple-superclass.rb +1 -1
  40. data/smoke/parameterizedd-self.rb +2 -2
  41. data/smoke/pattern-match1.rb +23 -0
  42. data/smoke/pattern-match2.rb +15 -0
  43. data/smoke/rbs-tyvar3.rb +11 -19
  44. data/smoke/rbs-tyvar3.rbs +4 -3
  45. data/smoke/rbs-tyvar4.rb +36 -0
  46. data/smoke/rbs-tyvar5.rb +12 -0
  47. data/smoke/rbs-tyvar5.rbs +8 -0
  48. data/smoke/struct.rb +2 -2
  49. data/smoke/uninitialize-var.rb +12 -0
  50. data/smoke/union-recv.rb +2 -2
  51. data/typeprof.gemspec +1 -1
  52. metadata +11 -4
@@ -85,10 +85,13 @@ module TypeProf
85
85
  end
86
86
 
87
87
  def do_call(aargs, caller_ep, caller_env, scratch, replace_recv_ty:, &ctn)
88
- subst = { Type::Var.new(:self) => caller_env.static_env.recv_ty } # XXX: support other type variables
89
- unless aargs.consistent_with_method_signature?(@msig, subst)
88
+ aargs = scratch.globalize_type(aargs, caller_env, caller_ep)
89
+ subst = aargs.consistent_with_method_signature?(@msig)
90
+ unless subst
90
91
  scratch.warn(caller_ep, "The arguments is not compatibile to RBS block")
91
92
  end
93
+ # check?
94
+ #subst = { Type::Var.new(:self) => caller_env.static_env.recv_ty }
92
95
  # XXX: Update type vars
93
96
  ctn[@ret_ty, caller_ep, caller_env]
94
97
  end
@@ -54,6 +54,10 @@ module TypeProf
54
54
  ctn[ret_ty, ep, env]
55
55
  end
56
56
 
57
+ def vmcore_raise(recv, mid, aargs, ep, env, scratch, &ctn)
58
+ # no-op
59
+ end
60
+
57
61
  def lambda(recv, mid, aargs, ep, env, scratch, &ctn)
58
62
  ctn[aargs.blk_ty, ep, env]
59
63
  end
@@ -63,10 +67,11 @@ module TypeProf
63
67
  end
64
68
 
65
69
  def object_s_new(recv, mid, aargs, ep, env, scratch, &ctn)
66
- ty = Type::Instance.new(recv)
67
70
  if recv.type_params.size >= 1
68
- ty = Type::Cell.new(Type::Cell::Elements.new([Type.bot] * recv.type_params.size), ty)
71
+ ty = Type::ContainerType.create_empty_instance(recv)
69
72
  env, ty = scratch.localize_type(ty, env, ep, AllocationSite.new(ep).add_id(:object_s_new))
73
+ else
74
+ ty = Type::Instance.new(recv)
70
75
  end
71
76
  meths = scratch.get_method(recv, false, :initialize)
72
77
  meths.flat_map do |meth|
@@ -149,7 +154,11 @@ module TypeProf
149
154
 
150
155
  def module_include(recv, mid, aargs, ep, env, scratch, &ctn)
151
156
  arg = aargs.lead_tys[0]
152
- scratch.include_module(recv, arg, ep.ctx.iseq.absolute_path)
157
+ arg.each_child do |arg|
158
+ if arg.is_a?(Type::Class)
159
+ scratch.include_module(recv, arg, false, ep.ctx.iseq.absolute_path)
160
+ end
161
+ end
153
162
  ctn[recv, ep, env]
154
163
  end
155
164
 
@@ -157,7 +166,7 @@ module TypeProf
157
166
  arg = aargs.lead_tys[0]
158
167
  arg.each_child do |arg|
159
168
  if arg.is_a?(Type::Class)
160
- scratch.extend_module(recv, arg, ep.ctx.iseq.absolute_path)
169
+ scratch.include_module(recv, arg, true, ep.ctx.iseq.absolute_path)
161
170
  end
162
171
  end
163
172
  ctn[recv, ep, env]
@@ -520,6 +529,7 @@ module TypeProf
520
529
  scratch.set_custom_method(klass_vmcore, :"core#set_method_alias", Builtin.method(:vmcore_set_method_alias))
521
530
  scratch.set_custom_method(klass_vmcore, :"core#undef_method", Builtin.method(:vmcore_undef_method))
522
531
  scratch.set_custom_method(klass_vmcore, :"core#hash_merge_kwd", Builtin.method(:vmcore_hash_merge_kwd))
532
+ scratch.set_custom_method(klass_vmcore, :"core#raise", Builtin.method(:vmcore_raise))
523
533
  scratch.set_custom_method(klass_vmcore, :lambda, Builtin.method(:lambda))
524
534
  scratch.set_singleton_custom_method(klass_obj, :"new", Builtin.method(:object_s_new))
525
535
  scratch.set_singleton_custom_method(klass_obj, :"attr_accessor", Builtin.method(:module_attr_accessor))
@@ -69,19 +69,23 @@ module TypeProf
69
69
  prologue_ep = ExecutionPoint.new(prologue_ctx, -1, nil)
70
70
  prologue_env = Env.new(StaticEnv.new(:top, Type.nil, false), [], [], Utils::HashWrapper.new({}))
71
71
 
72
- Config.rb_files.each do |file|
73
- if file.respond_to?(:read)
74
- iseq = ISeq.compile_str(file.read, file.to_s)
72
+ Config.rb_files.each do |rb|
73
+ if rb.is_a?(Array) # [String name, String content]
74
+ iseq = ISeq.compile_str(*rb.reverse)
75
75
  else
76
- iseq = ISeq.compile(file)
76
+ iseq = ISeq.compile(rb)
77
77
  end
78
78
  ep, env = TypeProf.starting_state(iseq)
79
79
  scratch.merge_env(ep, env)
80
80
  scratch.add_callsite!(ep.ctx, prologue_ep, prologue_env) {|ty, ep| }
81
81
  end
82
82
 
83
- Config.rbs_files.each do |path|
84
- Import.import_rbs_file(scratch, path)
83
+ Config.rbs_files.each do |rbs|
84
+ if rbs.is_a?(Array) # [String name, String content]
85
+ Import.import_rbs_code(scratch, *rbs)
86
+ else
87
+ Import.import_rbs_file(scratch, rbs)
88
+ end
85
89
  end
86
90
 
87
91
  result = scratch.type_profile
@@ -19,13 +19,44 @@ module TypeProf
19
19
  # Cell, Array, and Hash are types for global interface, e.g., TypedISeq.
20
20
  # Do not push such types to local environment, stack, etc.
21
21
 
22
+ class ContainerType < Type
23
+ def match?(other)
24
+ return nil if self.class != other.class
25
+ return nil unless @base_type.consistent?(other.base_type)
26
+ @elems.match?(other.elems)
27
+ end
28
+
29
+ def each_free_type_variable(&blk)
30
+ @elems.each_free_type_variable(&blk)
31
+ end
32
+
33
+ def consistent?(other)
34
+ raise "must not be used"
35
+ end
36
+
37
+ def self.create_empty_instance(klass)
38
+ base_type = Type::Instance.new(klass)
39
+ case klass
40
+ when Type::Builtin[:ary] # XXX: check inheritance...
41
+ Type::Array.new(Type::Array::Elements.new([], Type.bot), base_type)
42
+ when Type::Builtin[:hash]
43
+ Type.gen_hash(base_type) {|h| }
44
+ else
45
+ Type::Cell.new(Type::Cell::Elements.new([Type.bot] * klass.type_params.size), base_type)
46
+ end
47
+ end
48
+ end
49
+
22
50
  # The most basic container type for default type parameter class
23
- class Cell < Type
51
+ class Cell < ContainerType
24
52
  def initialize(elems, base_type)
25
53
  raise if !elems.is_a?(Cell::Elements)
26
54
  @elems = elems # Cell::Elements
27
55
  raise unless base_type
28
56
  @base_type = base_type
57
+ if base_type.klass.type_params.size != elems.elems.size
58
+ raise
59
+ end
29
60
  end
30
61
 
31
62
  attr_reader :elems, :base_type
@@ -44,7 +75,7 @@ module TypeProf
44
75
 
45
76
  def localize(env, alloc_site, depth)
46
77
  return env, Type.any if depth <= 0
47
- alloc_site = alloc_site.add_id(:cell)
78
+ alloc_site = alloc_site.add_id(:cell).add_id(@base_type)
48
79
  env, elems = @elems.localize(env, alloc_site, depth)
49
80
  env.deploy_type(LocalCell, alloc_site, elems, @base_type)
50
81
  end
@@ -58,31 +89,13 @@ module TypeProf
58
89
  raise
59
90
  end
60
91
 
61
- def consistent?(other, subst)
62
- case other
63
- when Type::Any then true
64
- when Type::Var then other.add_subst!(self, subst)
65
- when Type::Union
66
- other.types.each do |ty2|
67
- return true if consistent?(ty2, subst)
68
- end
69
- return false
70
- when Type::Cell
71
- @elems.size == other.elems.size &&
72
- @base_type.consistent?(other.base_type, subst) &&
73
- @elems.zip(other.elems).all? {|elem1, elem2| elem1..consistent?(elem2, subst) }
74
- else
75
- self == other
76
- end
77
- end
78
-
79
92
  def substitute(subst, depth)
80
93
  return Type.any if depth <= 0
81
94
  elems = @elems.substitute(subst, depth)
82
95
  Cell.new(elems, @base_type)
83
96
  end
84
97
 
85
- class Elements
98
+ class Elements # Cell
86
99
  include Utils::StructuralEquality
87
100
 
88
101
  def initialize(elems)
@@ -124,12 +137,21 @@ module TypeProf
124
137
  end
125
138
  end
126
139
 
127
- def consistent?(other, subst)
128
- false if @elems.size != other.elems.size
140
+ def match?(other)
141
+ return nil if @elems.size != other.elems.size
142
+ subst = nil
129
143
  @elems.zip(other.elems) do |ty0, ty1|
130
- return false unless ty0.consistent?(ty1, subst)
144
+ subst2 = Type.match?(ty0, ty1)
145
+ return nil unless subst2
146
+ subst = Type.merge_substitution(subst, subst2)
147
+ end
148
+ subst
149
+ end
150
+
151
+ def each_free_type_variable(&blk)
152
+ @elems.each do |ty|
153
+ ty.each_free_type_variable(&blk)
131
154
  end
132
- return true
133
155
  end
134
156
 
135
157
  def substitute(subst, depth)
@@ -146,6 +168,9 @@ module TypeProf
146
168
 
147
169
  def union(other)
148
170
  return self if self == other
171
+ if @elems.size != other.elems.size
172
+ raise "#{ @elems.size } != #{ other.elems.size }"
173
+ end
149
174
  elems = []
150
175
  @elems.zip(other.elems) do |ty0, ty1|
151
176
  elems << ty0.union(ty1)
@@ -155,7 +180,7 @@ module TypeProf
155
180
  end
156
181
  end
157
182
 
158
- class LocalCell < Type
183
+ class LocalCell < ContainerType
159
184
  def initialize(id, base_type)
160
185
  @id = id
161
186
  raise unless base_type
@@ -184,6 +209,7 @@ module TypeProf
184
209
  else
185
210
  elems = Cell::Elements.new([]) # XXX
186
211
  end
212
+ visited.delete(self)
187
213
  Cell.new(elems, @base_type)
188
214
  end
189
215
  end
@@ -191,14 +217,10 @@ module TypeProf
191
217
  def get_method(mid, scratch)
192
218
  @base_type.get_method(mid, scratch)
193
219
  end
194
-
195
- def consistent?(other, subst)
196
- raise "must not be used"
197
- end
198
220
  end
199
221
 
200
222
  # Do not insert Array type to local environment, stack, etc.
201
- class Array < Type
223
+ class Array < ContainerType
202
224
  def initialize(elems, base_type)
203
225
  raise unless elems.is_a?(Array::Elements)
204
226
  @elems = elems # Array::Elements
@@ -223,7 +245,7 @@ module TypeProf
223
245
 
224
246
  def localize(env, alloc_site, depth)
225
247
  return env, Type.any if depth <= 0
226
- alloc_site = alloc_site.add_id(:ary)
248
+ alloc_site = alloc_site.add_id(:ary).add_id(@base_type)
227
249
  env, elems = @elems.localize(env, alloc_site, depth - 1)
228
250
  env.deploy_type(LocalArray, alloc_site, elems, @base_type)
229
251
  end
@@ -237,29 +259,13 @@ module TypeProf
237
259
  raise
238
260
  end
239
261
 
240
- def consistent?(other, subst)
241
- case other
242
- when Type::Any then true
243
- when Type::Var then other.add_subst!(self, subst)
244
- when Type::Union
245
- other.types.each do |ty2|
246
- return true if consistent?(ty2, subst)
247
- end
248
- return false
249
- when Type::Array
250
- @base_type.consistent?(other.base_type, subst) && @elems.consistent?(other.elems, subst)
251
- else
252
- self == other
253
- end
254
- end
255
-
256
262
  def substitute(subst, depth)
257
263
  return Type.any if depth <= 0
258
264
  elems = @elems.substitute(subst, depth - 1)
259
265
  Array.new(elems, @base_type)
260
266
  end
261
267
 
262
- class Elements
268
+ class Elements # Array
263
269
  include Utils::StructuralEquality
264
270
 
265
271
  def initialize(lead_tys, rest_ty = Type.bot)
@@ -300,6 +306,9 @@ module TypeProf
300
306
 
301
307
  def screen_name(scratch)
302
308
  if Config.options[:show_container_raw_elements] || @rest_ty == Type.bot
309
+ if @lead_tys.empty?
310
+ return "Array[bot]" # RBS does not allow an empty tuple "[]"
311
+ end
303
312
  s = @lead_tys.map do |ty|
304
313
  ty.screen_name(scratch)
305
314
  end
@@ -318,14 +327,24 @@ module TypeProf
318
327
  end
319
328
  end
320
329
 
321
- def consistent?(other, subst)
330
+ def match?(other)
322
331
  n = [@lead_tys.size, other.lead_tys.size].min
323
- n.times do |i|
324
- return false unless @lead_tys[i].consistent?(other.lead_tys[i], subst)
325
- end
326
332
  rest_ty1 = @lead_tys[n..].inject(@rest_ty) {|ty1, ty2| ty1.union(ty2) }
327
333
  rest_ty2 = other.lead_tys[n..].inject(other.rest_ty) {|ty1, ty2| ty1.union(ty2) }
328
- rest_ty1.consistent?(rest_ty2, subst)
334
+ subst = nil
335
+ (@lead_tys[0, n] + [rest_ty1]).zip(other.lead_tys[0, n] + [rest_ty2]) do |ty0, ty1|
336
+ subst2 = Type.match?(ty0, ty1)
337
+ return nil unless subst2
338
+ subst = Type.merge_substitution(subst, subst2)
339
+ end
340
+ subst
341
+ end
342
+
343
+ def each_free_type_variable(&blk)
344
+ @lead_tys.each do |ty|
345
+ ty.each_free_type_variable(&blk)
346
+ end
347
+ @rest_ty&.each_free_type_variable(&blk)
329
348
  end
330
349
 
331
350
  def substitute(subst, depth)
@@ -476,7 +495,7 @@ module TypeProf
476
495
  end
477
496
 
478
497
  # Do not insert Array type to local environment, stack, etc.
479
- class LocalArray < Type
498
+ class LocalArray < ContainerType
480
499
  def initialize(id, base_type)
481
500
  @id = id
482
501
  raise unless base_type
@@ -506,6 +525,7 @@ module TypeProf
506
525
  # TODO: currently out-of-scope array cannot be accessed
507
526
  elems = Array::Elements.new([], Type.any)
508
527
  end
528
+ visited.delete(self)
509
529
  Array.new(elems, @base_type)
510
530
  end
511
531
  end
@@ -513,14 +533,10 @@ module TypeProf
513
533
  def get_method(mid, scratch)
514
534
  @base_type.get_method(mid, scratch)
515
535
  end
516
-
517
- def consistent?(other, subst)
518
- raise "must not be used"
519
- end
520
536
  end
521
537
 
522
538
 
523
- class Hash < Type
539
+ class Hash < ContainerType
524
540
  def initialize(elems, base_type)
525
541
  @elems = elems
526
542
  raise unless elems
@@ -539,7 +555,7 @@ module TypeProf
539
555
 
540
556
  def localize(env, alloc_site, depth)
541
557
  return env, Type.any if depth <= 0
542
- alloc_site = alloc_site.add_id(:hash)
558
+ alloc_site = alloc_site.add_id(:hash).add_id(@base_type)
543
559
  env, elems = @elems.localize(env, alloc_site, depth - 1)
544
560
  env.deploy_type(LocalHash, alloc_site, elems, @base_type)
545
561
  end
@@ -553,29 +569,13 @@ module TypeProf
553
569
  raise
554
570
  end
555
571
 
556
- def consistent?(other, subst)
557
- case other
558
- when Type::Any then true
559
- when Type::Var then other.add_subst!(self, subst)
560
- when Type::Union
561
- other.types.each do |ty2|
562
- return true if consistent?(ty2, subst)
563
- end
564
- return false
565
- when Type::Hash
566
- @base_type.consistent?(other.base_type, subst) && @elems.consistent?(other.elems, subst)
567
- else
568
- self == other
569
- end
570
- end
571
-
572
572
  def substitute(subst, depth)
573
573
  return Type.any if depth <= 0
574
574
  elems = @elems.substitute(subst, depth - 1)
575
575
  Hash.new(elems, @base_type)
576
576
  end
577
577
 
578
- class Elements
578
+ class Elements # Hash
579
579
  include Utils::StructuralEquality
580
580
 
581
581
  def initialize(map_tys)
@@ -634,12 +634,22 @@ module TypeProf
634
634
  end
635
635
 
636
636
  def screen_name(scratch)
637
- s = @map_tys.map do |k_ty, v_ty|
638
- k = k_ty.screen_name(scratch)
639
- v = v_ty.screen_name(scratch)
640
- "#{ k }=>#{ v }"
641
- end.join(", ")
642
- "{#{ s }}"
637
+ if !@map_tys.empty? && @map_tys.all? {|k_ty,| k_ty.is_a?(Type::Symbol) }
638
+ s = @map_tys.map do |k_ty, v_ty|
639
+ v = v_ty.screen_name(scratch)
640
+ "#{ k_ty.sym }: #{ v }"
641
+ end.join(", ")
642
+ "{#{ s }}"
643
+ else
644
+ k_ty = v_ty = Type.bot
645
+ @map_tys.each do |k, v|
646
+ k_ty = k_ty.union(k)
647
+ v_ty = v_ty.union(v)
648
+ end
649
+ k_ty = k_ty.screen_name(scratch)
650
+ v_ty = v_ty.screen_name(scratch)
651
+ "Hash[#{ k_ty }, #{ v_ty }]"
652
+ end
643
653
  end
644
654
 
645
655
  def pretty_print(q)
@@ -657,22 +667,31 @@ module TypeProf
657
667
  end
658
668
  end
659
669
 
660
- def consistent?(other, subst)
661
- subst2 = subst.dup
670
+ def match?(other)
671
+ subst = nil
662
672
  other.map_tys.each do |k1, v1|
663
- found = false
673
+ subst2 = nil
664
674
  @map_tys.each do |k0, v0|
665
- subst3 = subst2.dup
666
- if k0.consistent?(k1, subst3) && v0.consistent?(v1, subst3)
667
- subst2.replace(subst3)
668
- found = true
669
- break
675
+ subst3 = Type.match?(k0, k1)
676
+ if subst3
677
+ subst4 = Type.match?(v0, v1)
678
+ if subst4
679
+ subst2 = Type.merge_substitution(subst2, subst3)
680
+ subst2 = Type.merge_substitution(subst2, subst4)
681
+ end
670
682
  end
671
683
  end
672
- return false unless found
684
+ return nil unless subst2
685
+ subst = Type.merge_substitution(subst, subst2)
686
+ end
687
+ subst
688
+ end
689
+
690
+ def each_free_type_variable(&blk)
691
+ @map_tys.each do |k, v|
692
+ k.each_free_type_variable(&blk)
693
+ v.each_free_type_variable(&blk)
673
694
  end
674
- subst.replace(subst2)
675
- true
676
695
  end
677
696
 
678
697
  def substitute(subst, depth)
@@ -706,7 +725,7 @@ module TypeProf
706
725
  def [](key_ty)
707
726
  val_ty = Type.bot
708
727
  @map_tys.each do |k_ty, v_ty|
709
- if k_ty.consistent?(key_ty, {})
728
+ if Type.match?(k_ty, key_ty)
710
729
  val_ty = val_ty.union(v_ty)
711
730
  end
712
731
  end
@@ -763,7 +782,7 @@ module TypeProf
763
782
  end
764
783
  end
765
784
 
766
- class LocalHash < Type
785
+ class LocalHash < ContainerType
767
786
  def initialize(id, base_type)
768
787
  @id = id
769
788
  @base_type = base_type
@@ -791,6 +810,7 @@ module TypeProf
791
810
  else
792
811
  elems = Hash::Elements.new({Type.any => Type.any})
793
812
  end
813
+ visited.delete(self)
794
814
  Hash.new(elems, @base_type)
795
815
  end
796
816
  end
@@ -798,10 +818,6 @@ module TypeProf
798
818
  def get_method(mid, scratch)
799
819
  @base_type.get_method(mid, scratch)
800
820
  end
801
-
802
- def consistent?(other, subst)
803
- raise "must not be used"
804
- end
805
821
  end
806
822
  end
807
823
  end