typeprof 0.4.2 → 0.5.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.
@@ -210,7 +210,7 @@ module TypeProf
210
210
  # * superclasses and modules appear earlier than their subclasses (Object is earlier than String)
211
211
  # * namespace module appers earlier than its children (Process is earlier than Process::Status)
212
212
  visited = {}
213
- queue = @cur_env.class_decls.keys.map {|name| [:visit, name] }
213
+ queue = @cur_env.class_decls.keys.map {|name| [:visit, name] }.reverse
214
214
  until queue.empty?
215
215
  event, name = queue.pop
216
216
  case event
@@ -365,10 +365,15 @@ module TypeProf
365
365
  raise if ty.args.size != 2
366
366
  [:array, [:Enumerator], [], conv_type(ty.args.first)]
367
367
  else
368
- [:instance, klass]
368
+ if ty.args.empty?
369
+ [:instance, klass]
370
+ else
371
+ [:cell, [:instance, klass], ty.args.map {|ty| conv_type(ty) }]
372
+ end
369
373
  end
370
374
  when RBS::Types::Bases::Bool then [:bool]
371
375
  when RBS::Types::Bases::Any then [:any]
376
+ when RBS::Types::Bases::Top then [:any]
372
377
  when RBS::Types::Bases::Void then [:void]
373
378
  when RBS::Types::Bases::Self then [:self]
374
379
  when RBS::Types::Bases::Nil then [:nil]
@@ -499,24 +504,25 @@ module TypeProf
499
504
  end
500
505
 
501
506
  ivars.each do |ivar_name, ty|
502
- ty = conv_type(ty)
507
+ ty = conv_type(ty).remove_type_vars
503
508
  @scratch.add_ivar_write!(Type::Instance.new(klass), ivar_name, ty, nil)
504
509
  end
505
510
 
506
511
  cvars.each do |ivar_name, ty|
507
- ty = conv_type(ty)
512
+ ty = conv_type(ty).remove_type_vars
508
513
  @scratch.add_cvar_write!(klass, ivar_name, ty, nil)
509
514
  end
510
515
  end
511
516
 
512
517
  @json[:constants].each do |classpath, value|
513
518
  base_klass = path_to_klass(classpath[0..-2])
514
- value = conv_type(value)
519
+ value = conv_type(value).remove_type_vars
515
520
  @scratch.add_constant(base_klass, classpath[-1], value, nil)
516
521
  end
517
522
 
518
- @json[:globals].each do |name, value|
519
- @scratch.add_gvar_write!(name, conv_type(value), nil)
523
+ @json[:globals].each do |name, ty|
524
+ ty = conv_type(ty).remove_type_vars
525
+ @scratch.add_gvar_write!(name, ty, nil)
520
526
  end
521
527
 
522
528
  true
@@ -574,6 +580,8 @@ module TypeProf
574
580
  case ty.first
575
581
  when :class then path_to_klass(ty[1])
576
582
  when :instance then Type::Instance.new(path_to_klass(ty[1]))
583
+ when :cell
584
+ Type::Cell.new(Type::Cell::Elements.new(ty[2].map {|ty| conv_type(ty) }), conv_type(ty[1]))
577
585
  when :any then Type.any
578
586
  when :void then Type::Void.new
579
587
  when :nil then Type.nil
@@ -609,7 +617,7 @@ module TypeProf
609
617
  end
610
618
  when :union
611
619
  tys = ty[1]
612
- Type::Union.new(Utils::Set[*tys.map {|ty2| conv_type(ty2) }], nil) # XXX: Array and Hash support
620
+ Type::Union.new(Utils::Set[*tys.map {|ty2| conv_type(ty2) }], nil).normalize # XXX: Array and Hash support
613
621
  when :var
614
622
  Type::Var.new(ty[1])
615
623
  when :proc
@@ -11,6 +11,7 @@ module TypeProf
11
11
  end
12
12
 
13
13
  def do_send(recv, mid, aargs, caller_ep, caller_env, scratch, &ctn)
14
+ recv = recv.base_type while recv.respond_to?(:base_type)
14
15
  recv = scratch.globalize_type(recv, caller_env, caller_ep)
15
16
  aargs = scratch.globalize_type(aargs, caller_env, caller_ep)
16
17
 
@@ -175,8 +176,10 @@ module TypeProf
175
176
  ncaller_env = caller_env
176
177
  #pp [mid, aargs, msig]
177
178
  # XXX: support self type in msig
178
- subst = { Type::Var.new(:self) => recv }
179
- next unless aargs.consistent_with_method_signature?(msig, subst)
179
+ subst = aargs.consistent_with_method_signature?(msig)
180
+ next unless subst
181
+ # need to check self tyvar?
182
+ subst[Type::Var.new(:self)] = recv
180
183
  case
181
184
  when recv.is_a?(Type::Cell) && recv_orig.is_a?(Type::LocalCell)
182
185
  tyvars = recv.base_type.klass.type_params.map {|name,| Type::Var.new(name) }
@@ -205,22 +208,34 @@ module TypeProf
205
208
  elems.update(nil, ty)
206
209
  end
207
210
  end
208
- subst.merge!({ tyvar_elem => recv.elems.squash_or_any })
211
+ subst.merge!({ tyvar_elem => recv.elems.squash })
209
212
  when recv.is_a?(Type::Hash) && recv_orig.is_a?(Type::LocalHash)
210
213
  tyvar_k = Type::Var.new(:K)
211
214
  tyvar_v = Type::Var.new(:V)
212
- # XXX: need to support destructive operation
213
- k_ty, v_ty = recv.elems.squash
215
+ k_ty0, v_ty0 = recv.elems.squash
216
+ if subst[tyvar_k] && subst[tyvar_v]
217
+ k_ty = subst[tyvar_k]
218
+ v_ty = subst[tyvar_v]
219
+ k_ty0 = k_ty0.union(k_ty)
220
+ v_ty0 = v_ty0.union(v_ty)
221
+ alloc_site = AllocationSite.new(caller_ep)
222
+ ncaller_env, k_ty = scratch.localize_type(k_ty, ncaller_env, caller_ep, alloc_site.add_id(:k))
223
+ ncaller_env, v_ty = scratch.localize_type(v_ty, ncaller_env, caller_ep, alloc_site.add_id(:v))
224
+ ncaller_env = scratch.update_container_elem_types(ncaller_env, caller_ep, recv_orig.id, recv_orig.base_type) do |elems|
225
+ elems.update(k_ty, v_ty)
226
+ end
227
+ end
214
228
  # XXX: need to heuristically replace ret type Hash[K, V] with self, instead of conversative type?
215
- subst.merge!({ tyvar_k => k_ty, tyvar_v => v_ty })
229
+ subst.merge!({ tyvar_k => k_ty0, tyvar_v => v_ty0 })
216
230
  end
217
- ret_ty = ret_ty.substitute(subst, Config.options[:type_depth_limit])
218
231
  found = true
219
232
  if aargs.blk_ty.is_a?(Type::Proc)
220
233
  #raise NotImplementedError unless aargs.blk_ty.block_body.is_a?(ISeqBlock) # XXX
221
234
  dummy_ctx = TypedContext.new(caller_ep, mid)
222
235
  dummy_ep = ExecutionPoint.new(dummy_ctx, -1, caller_ep)
223
- dummy_env = Env.new(StaticEnv.new(recv, msig.blk_ty, false), [], [], Utils::HashWrapper.new({}))
236
+ s_recv = recv
237
+ s_recv = s_recv.base_type while s_recv.respond_to?(:base_type)
238
+ dummy_env = Env.new(StaticEnv.new(s_recv, msig.blk_ty, false), [], [], Utils::HashWrapper.new({}))
224
239
  if msig.blk_ty.is_a?(Type::Proc)
225
240
  scratch.add_callsite!(dummy_ctx, caller_ep, ncaller_env, &ctn)
226
241
  nfargs = msig.blk_ty.block_body.msig
@@ -240,13 +255,55 @@ module TypeProf
240
255
  0.upto(nfargs.opt_tys.size) do |n|
241
256
  naargs = ActualArguments.new(nlead_tys[0, nfargs.lead_tys.size + n], nil, {}, Type.nil) # XXX: support block to block?
242
257
  scratch.do_invoke_block(aargs.blk_ty, naargs, dummy_ep, dummy_env) do |blk_ret_ty, _ep, _env|
243
- subst2 = {}
244
- if blk_ret_ty.consistent?(msig.blk_ty.block_body.ret_ty, subst2)
245
- if recv.is_a?(Type::Array) && recv_orig.is_a?(Type::LocalArray)
258
+ subst2 = Type.match?(blk_ret_ty, msig.blk_ty.block_body.ret_ty)
259
+ if subst2
260
+ subst2 = Type.merge_substitution(subst, subst2)
261
+ case
262
+ when recv.is_a?(Type::Cell) && recv_orig.is_a?(Type::LocalCell)
263
+ tyvars = recv.base_type.klass.type_params.map {|name,| Type::Var.new(name) }
264
+ tyvars.each_with_index do |tyvar, idx|
265
+ ty = subst2[tyvar]
266
+ if ty
267
+ ncaller_env, ty = scratch.localize_type(ty, ncaller_env, caller_ep)
268
+ ncaller_env = scratch.update_container_elem_types(ncaller_env, caller_ep, recv_orig.id, recv_orig.base_type) do |elems|
269
+ elems.update(idx, ty)
270
+ end
271
+ scratch.merge_return_env(caller_ep) {|env| env ? env.merge(ncaller_env) : ncaller_env }
272
+ end
273
+ end
274
+ tyvars.zip(recv.elems.elems) do |tyvar, elem|
275
+ if subst2[tyvar]
276
+ subst2[tyvar] = subst2[tyvar].union(elem)
277
+ else
278
+ subst2[tyvar] = elem
279
+ end
280
+ end
281
+ ret_ty = ret_ty.substitute(subst2, Config.options[:type_depth_limit])
282
+ when recv.is_a?(Type::Array) && recv_orig.is_a?(Type::LocalArray)
246
283
  tyvar_elem = Type::Var.new(:Elem)
247
284
  if subst2[tyvar_elem]
285
+ ty = subst2[tyvar_elem]
286
+ ncaller_env, ty = scratch.localize_type(ty, ncaller_env, caller_ep)
287
+ ncaller_env = scratch.update_container_elem_types(ncaller_env, caller_ep, recv_orig.id, recv_orig.base_type) do |elems|
288
+ elems.update(nil, ty)
289
+ end
290
+ scratch.merge_return_env(caller_ep) {|env| env ? env.merge(ncaller_env) : ncaller_env }
291
+ end
292
+ ret_ty = ret_ty.substitute(subst2, Config.options[:type_depth_limit])
293
+ when recv.is_a?(Type::Hash) && recv_orig.is_a?(Type::LocalHash)
294
+ tyvar_k = Type::Var.new(:K)
295
+ tyvar_v = Type::Var.new(:V)
296
+ k_ty0, v_ty0 = recv.elems.squash
297
+ if subst2[tyvar_k] && subst2[tyvar_v]
298
+ k_ty = subst2[tyvar_k]
299
+ v_ty = subst2[tyvar_v]
300
+ k_ty0 = k_ty0.union(k_ty)
301
+ v_ty0 = v_ty0.union(v_ty)
302
+ alloc_site = AllocationSite.new(caller_ep)
303
+ ncaller_env, k_ty = scratch.localize_type(k_ty, ncaller_env, caller_ep, alloc_site.add_id(:k))
304
+ ncaller_env, v_ty = scratch.localize_type(v_ty, ncaller_env, caller_ep, alloc_site.add_id(:v))
248
305
  ncaller_env = scratch.update_container_elem_types(ncaller_env, caller_ep, recv_orig.id, recv_orig.base_type) do |elems|
249
- elems.update(nil, subst2[tyvar_elem])
306
+ elems.update(k_ty, v_ty)
250
307
  end
251
308
  scratch.merge_return_env(caller_ep) {|env| env ? env.merge(ncaller_env) : ncaller_env }
252
309
  end
@@ -274,6 +331,7 @@ module TypeProf
274
331
  ctn[ret_ty, caller_ep, ncaller_env]
275
332
  end
276
333
  else
334
+ ret_ty = ret_ty.substitute(subst, Config.options[:type_depth_limit])
277
335
  ret_ty = ret_ty.remove_type_vars
278
336
  ctn[ret_ty, caller_ep, ncaller_env]
279
337
  end
@@ -20,16 +20,69 @@ module TypeProf
20
20
  self
21
21
  end
22
22
 
23
- def consistent?(other, subst)
24
- case other
25
- when Type::Any then true
26
- when Type::Var then other.add_subst!(self, subst)
23
+ def self.match?(ty1, ty2)
24
+ # both ty1 and ty2 should be global
25
+ # ty1 is always concrete; it should not have type variables
26
+ # ty2 might be abstract; it may have type variables
27
+ case ty2
28
+ when Type::Var
29
+ { ty2 => ty1 }
30
+ when Type::Any
31
+ {}
27
32
  when Type::Union
28
- other.types.each do |ty2|
29
- return true if consistent?(ty2, subst)
33
+ subst = nil
34
+ ty2.each_child_global do |ty2|
35
+ # this is very conservative to create subst:
36
+ # Type.match?( int | str, int | X) creates { X => int | str } but should be { X => str }???
37
+ subst2 = Type.match?(ty1, ty2)
38
+ next unless subst2
39
+ subst = Type.merge_substitution(subst, subst2)
40
+ end
41
+ subst
42
+ else
43
+ case ty1
44
+ when Type::Var then raise "should not occur"
45
+ when Type::Any
46
+ subst = {}
47
+ ty2.each_free_type_variable do |tyvar|
48
+ subst[tyvar] = Type.any
49
+ end
50
+ subst
51
+ when Type::Union
52
+ subst = nil
53
+ ty1.each_child_global do |ty1|
54
+ subst2 = Type.match?(ty1, ty2)
55
+ next unless subst2
56
+ subst = Type.merge_substitution(subst, subst2)
57
+ end
58
+ subst
59
+ else
60
+ if ty2.is_a?(Type::ContainerType)
61
+ # ty2 may have type variables
62
+ return nil if ty1.class != ty2.class
63
+ ty1.match?(ty2)
64
+ elsif ty1.is_a?(Type::ContainerType)
65
+ nil
66
+ else
67
+ ty1.consistent?(ty2) ? {} : nil
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ def self.merge_substitution(subst1, subst2)
74
+ if subst1
75
+ subst1 = subst1.dup
76
+ subst2.each do |tyvar, ty|
77
+ if subst1[tyvar]
78
+ subst1[tyvar] = subst1[tyvar].union(ty)
79
+ else
80
+ subst1[tyvar] = ty
81
+ end
30
82
  end
83
+ subst1
31
84
  else
32
- self == other
85
+ subst2
33
86
  end
34
87
  end
35
88
 
@@ -41,6 +94,9 @@ module TypeProf
41
94
  yield self
42
95
  end
43
96
 
97
+ def each_free_type_variable
98
+ end
99
+
44
100
  def union(other)
45
101
  return self if self == other # fastpath
46
102
 
@@ -118,10 +174,8 @@ module TypeProf
118
174
  nil
119
175
  end
120
176
 
121
- def consistent?(other, subst)
122
- # need to create a type assignment if other is Var
123
- other.add_subst!(self, subst) if other.is_a?(Type::Var)
124
- true
177
+ def consistent?(_other)
178
+ raise "should not be called"
125
179
  end
126
180
 
127
181
  def substitute(_subst, _depth)
@@ -156,6 +210,12 @@ module TypeProf
156
210
  @elems = elems
157
211
  end
158
212
 
213
+ def each_free_type_variable(&blk)
214
+ each_child_global do |ty|
215
+ ty.each_free_type_variable(&blk)
216
+ end
217
+ end
218
+
159
219
  def limit_size(limit)
160
220
  return Type.any if limit <= 0
161
221
  tys = Utils::Set[]
@@ -271,48 +331,20 @@ module TypeProf
271
331
  def localize(env, alloc_site, depth)
272
332
  return env, Type.any if depth <= 0
273
333
  tys = @types.map do |ty|
274
- alloc_site2 = alloc_site.add_id(ty)
275
- env, ty2 = ty.localize(env, alloc_site2, depth - 1)
334
+ env, ty2 = ty.localize(env, alloc_site, depth - 1)
276
335
  ty2
277
336
  end
278
337
  @elems&.each do |(container_kind, base_type), elems|
279
338
  ty = container_kind.new(elems, base_type)
280
- alloc_site2 = alloc_site.add_id(container_kind.name.to_sym).add_id(base_type)
281
- env, ty = ty.localize(env, alloc_site2, depth - 1)
339
+ env, ty = ty.localize(env, alloc_site, depth - 1)
282
340
  tys = tys.add(ty)
283
341
  end
284
342
  ty = Union.new(tys, nil).normalize
285
343
  return env, ty
286
344
  end
287
345
 
288
- def consistent?(other, subst)
289
- case other
290
- when Type::Any then true
291
- when Type::Var then other.add_subst!(self, subst)
292
- when Type::Union
293
- # this is very conservative to create subst:
294
- # consistent?( int | str, int | X) creates { X => int | str } but should be { X => str }???
295
- @types.each do |ty1|
296
- other.types.each do |ty2|
297
- subst2 = subst.dup
298
- if ty1.consistent?(ty2, subst2)
299
- subst.replace(subst2)
300
- # XXX: need to check other pairs to create conservative substitution??
301
- # consistent?( X | :foo, str | int ) may return { X => str } or { X => int } but should be { X => str | int }?
302
- return true
303
- end
304
- end
305
- end
306
- return true if @types.size == 0 && other.types.size == 0 # XXX: is this okay?
307
- # TODO: array argument?
308
- return false
309
- else
310
- @types.each do |ty1|
311
- return true if ty1.consistent?(other, subst)
312
- end
313
- # TODO: array argument?
314
- return false
315
- end
346
+ def consistent?(_other)
347
+ raise "should not be called"
316
348
  end
317
349
 
318
350
  def substitute(subst, depth)
@@ -331,7 +363,7 @@ module TypeProf
331
363
  elems = @elems&.to_h do |(container_kind, base_type), elems|
332
364
  [[container_kind, base_type], elems.substitute(subst, depth - 1)]
333
365
  end
334
- ty = Union.new(tys, elems)
366
+ ty = Union.new(tys, elems).normalize
335
367
  unions.each do |ty0|
336
368
  ty = ty.union(ty0)
337
369
  end
@@ -371,6 +403,10 @@ module TypeProf
371
403
  "Var[#{ @name }]"
372
404
  end
373
405
 
406
+ def each_free_type_variable
407
+ yield self
408
+ end
409
+
374
410
  def substitute(subst, depth)
375
411
  if subst[self]
376
412
  subst[self].limit_size(depth)
@@ -379,7 +415,7 @@ module TypeProf
379
415
  end
380
416
  end
381
417
 
382
- def consistent?(other, subst)
418
+ def consistent?(_other)
383
419
  raise "should not be called: #{ self }"
384
420
  end
385
421
 
@@ -420,15 +456,8 @@ module TypeProf
420
456
  scratch.get_method(self, true, mid)
421
457
  end
422
458
 
423
- def consistent?(other, subst)
459
+ def consistent?(other)
424
460
  case other
425
- when Type::Any then true
426
- when Type::Var then other.add_subst!(self, subst)
427
- when Type::Union
428
- other.types.each do |ty|
429
- return true if consistent?(ty, subst)
430
- end
431
- return false
432
461
  when Type::Class
433
462
  ty = self
434
463
  loop do
@@ -479,17 +508,10 @@ module TypeProf
479
508
  scratch.get_method(@klass, false, mid)
480
509
  end
481
510
 
482
- def consistent?(other, subst)
511
+ def consistent?(other)
483
512
  case other
484
- when Type::Any then true
485
- when Type::Var then other.add_subst!(self, subst)
486
- when Type::Union
487
- other.types.each do |ty|
488
- return true if consistent?(ty, subst)
489
- end
490
- return false
491
513
  when Type::Instance
492
- @klass.consistent?(other.klass, subst)
514
+ @klass.consistent?(other.klass)
493
515
  when Type::Class
494
516
  return true if @klass == Type::Builtin[:obj] || @klass == Type::Builtin[:class] || @klass == Type::Builtin[:module]
495
517
  return false
@@ -503,6 +525,7 @@ module TypeProf
503
525
  end
504
526
  end
505
527
 
528
+ # This is an internal object in MRI, so a user program cannot create this object explicitly
506
529
  class ISeq < Type
507
530
  def initialize(iseq)
508
531
  @iseq = iseq
@@ -520,20 +543,14 @@ module TypeProf
520
543
  end
521
544
 
522
545
  class Proc < Type
523
- def initialize(block_body, type)
524
- @block_body, @type = block_body, type
546
+ def initialize(block_body, base_type)
547
+ @block_body, @base_type = block_body, base_type
525
548
  end
526
549
 
527
- attr_reader :block_body, :type
550
+ attr_reader :block_body, :base_type
528
551
 
529
- def consistent?(other, subst)
552
+ def consistent?(other)
530
553
  case other
531
- when Type::Any then true
532
- when Type::Var then other.add_subst!(self, subst)
533
- when Type::Union
534
- other.types.each do |ty2|
535
- return true if consistent?(ty2, subst)
536
- end
537
554
  when Type::Proc
538
555
  @block_body.consistent?(other.block_body)
539
556
  else
@@ -542,11 +559,11 @@ module TypeProf
542
559
  end
543
560
 
544
561
  def get_method(mid, scratch)
545
- @type.get_method(mid, scratch)
562
+ @base_type.get_method(mid, scratch)
546
563
  end
547
564
 
548
565
  def substitute(subst, depth)
549
- Proc.new(@block_body.substitute(subst, depth), @type)
566
+ Proc.new(@block_body.substitute(subst, depth), @base_type)
550
567
  end
551
568
 
552
569
  def screen_name(scratch)
@@ -566,14 +583,12 @@ module TypeProf
566
583
  "Type::Symbol[#{ @sym ? @sym.inspect : "(dynamic symbol)" }, #{ @base_type.inspect }]"
567
584
  end
568
585
 
569
- def consistent?(other, subst)
586
+ def consistent?(other)
570
587
  case other
571
- when Var
572
- other.add_subst!(self, subst)
573
588
  when Symbol
574
589
  @sym == other.sym
575
590
  else
576
- @base_type.consistent?(other, subst)
591
+ @base_type.consistent?(other)
577
592
  end
578
593
  end
579
594
 
@@ -594,33 +609,33 @@ module TypeProf
594
609
  end
595
610
  end
596
611
 
597
- # local info
612
+ # A local type
598
613
  class Literal < Type
599
- def initialize(lit, type)
614
+ def initialize(lit, base_type)
600
615
  @lit = lit
601
- @type = type
616
+ @base_type = base_type
602
617
  end
603
618
 
604
- attr_reader :lit, :type
619
+ attr_reader :lit, :base_type
605
620
 
606
621
  def inspect
607
- "Type::Literal[#{ @lit.inspect }, #{ @type.inspect }]"
622
+ "Type::Literal[#{ @lit.inspect }, #{ @base_type.inspect }]"
608
623
  end
609
624
 
610
625
  def screen_name(scratch)
611
- @type.screen_name(scratch) + "<#{ @lit.inspect }>"
626
+ @base_type.screen_name(scratch) + "<#{ @lit.inspect }>"
612
627
  end
613
628
 
614
629
  def globalize(_env, _visited, _depth)
615
- @type
630
+ @base_type
616
631
  end
617
632
 
618
633
  def get_method(mid, scratch)
619
- @type.get_method(mid, scratch)
634
+ @base_type.get_method(mid, scratch)
620
635
  end
621
636
 
622
- def consistent?(other, subst)
623
- @type.consistent?(other, subst)
637
+ def consistent?(_other)
638
+ raise "should not called"
624
639
  end
625
640
  end
626
641