typeprof 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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