typeprof 0.4.1 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)
@@ -318,14 +324,24 @@ module TypeProf
318
324
  end
319
325
  end
320
326
 
321
- def consistent?(other, subst)
327
+ def match?(other)
322
328
  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
329
  rest_ty1 = @lead_tys[n..].inject(@rest_ty) {|ty1, ty2| ty1.union(ty2) }
327
330
  rest_ty2 = other.lead_tys[n..].inject(other.rest_ty) {|ty1, ty2| ty1.union(ty2) }
328
- rest_ty1.consistent?(rest_ty2, subst)
331
+ subst = nil
332
+ (@lead_tys[0, n] + [rest_ty1]).zip(other.lead_tys[0, n] + [rest_ty2]) do |ty0, ty1|
333
+ subst2 = Type.match?(ty0, ty1)
334
+ return nil unless subst2
335
+ subst = Type.merge_substitution(subst, subst2)
336
+ end
337
+ subst
338
+ end
339
+
340
+ def each_free_type_variable(&blk)
341
+ @lead_tys.each do |ty|
342
+ ty.each_free_type_variable(&blk)
343
+ end
344
+ @rest_ty&.each_free_type_variable(&blk)
329
345
  end
330
346
 
331
347
  def substitute(subst, depth)
@@ -476,7 +492,7 @@ module TypeProf
476
492
  end
477
493
 
478
494
  # Do not insert Array type to local environment, stack, etc.
479
- class LocalArray < Type
495
+ class LocalArray < ContainerType
480
496
  def initialize(id, base_type)
481
497
  @id = id
482
498
  raise unless base_type
@@ -506,6 +522,7 @@ module TypeProf
506
522
  # TODO: currently out-of-scope array cannot be accessed
507
523
  elems = Array::Elements.new([], Type.any)
508
524
  end
525
+ visited.delete(self)
509
526
  Array.new(elems, @base_type)
510
527
  end
511
528
  end
@@ -513,14 +530,10 @@ module TypeProf
513
530
  def get_method(mid, scratch)
514
531
  @base_type.get_method(mid, scratch)
515
532
  end
516
-
517
- def consistent?(other, subst)
518
- raise "must not be used"
519
- end
520
533
  end
521
534
 
522
535
 
523
- class Hash < Type
536
+ class Hash < ContainerType
524
537
  def initialize(elems, base_type)
525
538
  @elems = elems
526
539
  raise unless elems
@@ -539,7 +552,7 @@ module TypeProf
539
552
 
540
553
  def localize(env, alloc_site, depth)
541
554
  return env, Type.any if depth <= 0
542
- alloc_site = alloc_site.add_id(:hash)
555
+ alloc_site = alloc_site.add_id(:hash).add_id(@base_type)
543
556
  env, elems = @elems.localize(env, alloc_site, depth - 1)
544
557
  env.deploy_type(LocalHash, alloc_site, elems, @base_type)
545
558
  end
@@ -553,29 +566,13 @@ module TypeProf
553
566
  raise
554
567
  end
555
568
 
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
569
  def substitute(subst, depth)
573
570
  return Type.any if depth <= 0
574
571
  elems = @elems.substitute(subst, depth - 1)
575
572
  Hash.new(elems, @base_type)
576
573
  end
577
574
 
578
- class Elements
575
+ class Elements # Hash
579
576
  include Utils::StructuralEquality
580
577
 
581
578
  def initialize(map_tys)
@@ -635,9 +632,13 @@ module TypeProf
635
632
 
636
633
  def screen_name(scratch)
637
634
  s = @map_tys.map do |k_ty, v_ty|
638
- k = k_ty.screen_name(scratch)
639
635
  v = v_ty.screen_name(scratch)
640
- "#{ k }=>#{ v }"
636
+ if k_ty.is_a?(Type::Symbol)
637
+ "#{ k_ty.sym }: #{ v }"
638
+ else
639
+ k = k_ty.screen_name(scratch)
640
+ "#{ k }=>#{ v }"
641
+ end
641
642
  end.join(", ")
642
643
  "{#{ s }}"
643
644
  end
@@ -657,22 +658,31 @@ module TypeProf
657
658
  end
658
659
  end
659
660
 
660
- def consistent?(other, subst)
661
- subst2 = subst.dup
661
+ def match?(other)
662
+ subst = nil
662
663
  other.map_tys.each do |k1, v1|
663
- found = false
664
+ subst2 = nil
664
665
  @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
666
+ subst3 = Type.match?(k0, k1)
667
+ if subst3
668
+ subst4 = Type.match?(v0, v1)
669
+ if subst4
670
+ subst2 = Type.merge_substitution(subst2, subst3)
671
+ subst2 = Type.merge_substitution(subst2, subst4)
672
+ end
670
673
  end
671
674
  end
672
- return false unless found
675
+ return nil unless subst2
676
+ subst = Type.merge_substitution(subst, subst2)
677
+ end
678
+ subst
679
+ end
680
+
681
+ def each_free_type_variable(&blk)
682
+ @map_tys.each do |k, v|
683
+ k.each_free_type_variable(&blk)
684
+ v.each_free_type_variable(&blk)
673
685
  end
674
- subst.replace(subst2)
675
- true
676
686
  end
677
687
 
678
688
  def substitute(subst, depth)
@@ -706,7 +716,7 @@ module TypeProf
706
716
  def [](key_ty)
707
717
  val_ty = Type.bot
708
718
  @map_tys.each do |k_ty, v_ty|
709
- if k_ty.consistent?(key_ty, {})
719
+ if Type.match?(k_ty, key_ty)
710
720
  val_ty = val_ty.union(v_ty)
711
721
  end
712
722
  end
@@ -763,7 +773,7 @@ module TypeProf
763
773
  end
764
774
  end
765
775
 
766
- class LocalHash < Type
776
+ class LocalHash < ContainerType
767
777
  def initialize(id, base_type)
768
778
  @id = id
769
779
  @base_type = base_type
@@ -791,6 +801,7 @@ module TypeProf
791
801
  else
792
802
  elems = Hash::Elements.new({Type.any => Type.any})
793
803
  end
804
+ visited.delete(self)
794
805
  Hash.new(elems, @base_type)
795
806
  end
796
807
  end
@@ -798,10 +809,6 @@ module TypeProf
798
809
  def get_method(mid, scratch)
799
810
  @base_type.get_method(mid, scratch)
800
811
  end
801
-
802
- def consistent?(other, subst)
803
- raise "must not be used"
804
- end
805
812
  end
806
813
  end
807
814
  end
@@ -174,7 +174,7 @@ module TypeProf
174
174
  cvars = cvars.map do |var, entry|
175
175
  next if entry.absolute_paths.all? {|path| Config.check_dir_filter(path) == :exclude }
176
176
  [var, entry.type.screen_name(@scratch), entry.rbs_declared]
177
- end
177
+ end.compact
178
178
 
179
179
  if !class_def.absolute_path || Config.check_dir_filter(class_def.absolute_path) == :exclude
180
180
  return nil if consts.empty? && included_mods.empty? && extended_mods.empty? && ivars.empty? && cvars.empty? && iseq_methods.empty? && attr_methods.empty? && inner_classes.empty?
@@ -10,7 +10,10 @@ module TypeProf
10
10
  def self.get_builtin_env
11
11
  unless @builtin_env
12
12
  @builtin_env = RBS::Environment.new
13
- @builtin_env_json = load_rbs(@builtin_env, builtin: true)
13
+
14
+ loader = RBS::EnvironmentLoader.new
15
+ new_decls = loader.load(env: @builtin_env).map {|decl,| decl }
16
+ @builtin_env_json = load_rbs(@builtin_env, new_decls)
14
17
  end
15
18
 
16
19
  return @builtin_env.dup, @builtin_env_json
@@ -21,27 +24,34 @@ module TypeProf
21
24
  end
22
25
 
23
26
  def load_library(lib)
24
- RBSReader.load_rbs(@env, library: lib)
27
+ loader = RBS::EnvironmentLoader.new(core_root: nil)
28
+ loader.add(library: lib)
29
+ new_decls = loader.load(env: @env).map {|decl,| decl }
30
+ RBSReader.load_rbs(@env, new_decls)
25
31
  end
26
32
 
27
33
  def load_path(path)
28
- RBSReader.load_rbs(@env, path: path)
34
+ loader = RBS::EnvironmentLoader.new(core_root: nil)
35
+ loader.add(path: path)
36
+ new_decls = loader.load(env: @env).map {|decl,| decl }
37
+ RBSReader.load_rbs(@env, new_decls)
29
38
  end
30
39
 
31
- def self.load_rbs(env, builtin: false, **opt)
32
- if builtin
33
- loader = RBS::EnvironmentLoader.new
34
- else
35
- loader = RBS::EnvironmentLoader.new(core_root: nil)
36
- loader.add(**opt)
40
+ def load_rbs_string(name, content)
41
+ buffer = RBS::Buffer.new(name: name, content: content)
42
+ new_decls = []
43
+ RBS::Parser.parse_signature(buffer).each do |decl|
44
+ @env << decl
45
+ new_decls << decl
37
46
  end
38
- new_decls = loader.load(env: env)
47
+ RBSReader.load_rbs(@env, new_decls)
48
+ end
39
49
 
50
+ def self.load_rbs(env, new_decls)
40
51
  all_env = env.resolve_type_names
41
-
42
52
  resolver = RBS::TypeNameResolver.from_env(all_env)
43
53
  cur_env = RBS::Environment.new
44
- new_decls.each do |decl,|
54
+ new_decls.each do |decl|
45
55
  cur_env << env.resolve_declaration(resolver, decl, outer: [], prefix: RBS::Namespace.root)
46
56
  end
47
57
 
@@ -210,7 +220,7 @@ module TypeProf
210
220
  # * superclasses and modules appear earlier than their subclasses (Object is earlier than String)
211
221
  # * namespace module appers earlier than its children (Process is earlier than Process::Status)
212
222
  visited = {}
213
- queue = @cur_env.class_decls.keys.map {|name| [:visit, name] }
223
+ queue = @cur_env.class_decls.keys.map {|name| [:visit, name] }.reverse
214
224
  until queue.empty?
215
225
  event, name = queue.pop
216
226
  case event
@@ -365,10 +375,15 @@ module TypeProf
365
375
  raise if ty.args.size != 2
366
376
  [:array, [:Enumerator], [], conv_type(ty.args.first)]
367
377
  else
368
- [:instance, klass]
378
+ if ty.args.empty?
379
+ [:instance, klass]
380
+ else
381
+ [:cell, [:instance, klass], ty.args.map {|ty| conv_type(ty) }]
382
+ end
369
383
  end
370
384
  when RBS::Types::Bases::Bool then [:bool]
371
385
  when RBS::Types::Bases::Any then [:any]
386
+ when RBS::Types::Bases::Top then [:any]
372
387
  when RBS::Types::Bases::Void then [:void]
373
388
  when RBS::Types::Bases::Self then [:self]
374
389
  when RBS::Types::Bases::Nil then [:nil]
@@ -440,6 +455,10 @@ module TypeProf
440
455
  Import.new(scratch, scratch.rbs_reader.load_path(rbs_path)).import(true)
441
456
  end
442
457
 
458
+ def self.import_rbs_code(scratch, rbs_name, rbs_code)
459
+ Import.new(scratch, scratch.rbs_reader.load_rbs_string(rbs_name, rbs_code)).import(true)
460
+ end
461
+
443
462
  def initialize(scratch, json)
444
463
  @scratch = scratch
445
464
  @json = json
@@ -485,11 +504,11 @@ module TypeProf
485
504
  rbs_sources = members[:rbs_sources]
486
505
 
487
506
  included_modules.each do |mod|
488
- @scratch.include_module(klass, path_to_klass(mod), nil)
507
+ @scratch.include_module(klass, path_to_klass(mod), false, nil)
489
508
  end
490
509
 
491
510
  extended_modules.each do |mod|
492
- @scratch.extend_module(klass, path_to_klass(mod), nil)
511
+ @scratch.include_module(klass, path_to_klass(mod), true, nil)
493
512
  end
494
513
 
495
514
  methods.each do |(singleton, method_name), mdef|
@@ -499,24 +518,25 @@ module TypeProf
499
518
  end
500
519
 
501
520
  ivars.each do |ivar_name, ty|
502
- ty = conv_type(ty)
521
+ ty = conv_type(ty).remove_type_vars
503
522
  @scratch.add_ivar_write!(Type::Instance.new(klass), ivar_name, ty, nil)
504
523
  end
505
524
 
506
525
  cvars.each do |ivar_name, ty|
507
- ty = conv_type(ty)
526
+ ty = conv_type(ty).remove_type_vars
508
527
  @scratch.add_cvar_write!(klass, ivar_name, ty, nil)
509
528
  end
510
529
  end
511
530
 
512
531
  @json[:constants].each do |classpath, value|
513
532
  base_klass = path_to_klass(classpath[0..-2])
514
- value = conv_type(value)
533
+ value = conv_type(value).remove_type_vars
515
534
  @scratch.add_constant(base_klass, classpath[-1], value, nil)
516
535
  end
517
536
 
518
- @json[:globals].each do |name, value|
519
- @scratch.add_gvar_write!(name, conv_type(value), nil)
537
+ @json[:globals].each do |name, ty|
538
+ ty = conv_type(ty).remove_type_vars
539
+ @scratch.add_gvar_write!(name, ty, nil)
520
540
  end
521
541
 
522
542
  true
@@ -574,6 +594,8 @@ module TypeProf
574
594
  case ty.first
575
595
  when :class then path_to_klass(ty[1])
576
596
  when :instance then Type::Instance.new(path_to_klass(ty[1]))
597
+ when :cell
598
+ Type::Cell.new(Type::Cell::Elements.new(ty[2].map {|ty| conv_type(ty) }), conv_type(ty[1]))
577
599
  when :any then Type.any
578
600
  when :void then Type::Void.new
579
601
  when :nil then Type.nil
@@ -609,7 +631,7 @@ module TypeProf
609
631
  end
610
632
  when :union
611
633
  tys = ty[1]
612
- Type::Union.new(Utils::Set[*tys.map {|ty2| conv_type(ty2) }], nil) # XXX: Array and Hash support
634
+ Type::Union.new(Utils::Set[*tys.map {|ty2| conv_type(ty2) }], nil).normalize # XXX: Array and Hash support
613
635
  when :var
614
636
  Type::Var.new(ty[1])
615
637
  when :proc