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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d2ffa092b1a33661649a9a3b48347d78baf5d9bac0ba4020f72a122442d4d287
4
- data.tar.gz: fbe985ca3547bd831564f5921f38edf65160c86133bb0219596aaa4e5962066a
3
+ metadata.gz: c1cf845328dbfd7b8dc14113c986be27a28457153d4e3b470ca45341035bdff7
4
+ data.tar.gz: 7e1aa96c7623832dc63e0d60573c28deeb0112c31921d63752553dcbf8127fde
5
5
  SHA512:
6
- metadata.gz: b260426c710befa65825fc41998643ced8ef3b43d4edc16b5eb9a2f00cbaf865c832f80ee3443c6a8e9f725e5def2d6bd47a1109e97dffaab8d17771035ae08a
7
- data.tar.gz: 47cf9f339601864c16464859d92aa9b904e51c471da84c2f2997519947edbc9c647b1762129cc0d677264fe0c0372de56acf04e98968a7b6e1c372d43af8a543
6
+ metadata.gz: 40e5f8da50d3b31dc96f2b010275083d9907c7510d16afc1f86b23114196a30c8170b9fc77cb8d8e1de0a7ba0b14a5deaf143450f3541e007a56a9170ce7ed93
7
+ data.tar.gz: 3fd01d2edc108f2ce262af023886fa04c8e2cbc6e98647dad238954428a188aa4637913c2e7f13c983deeb4ac6ff7b3dc3489acc740effcbddbd128519135662
@@ -17,9 +17,8 @@ jobs:
17
17
  uses: ruby/setup-ruby@v1
18
18
  with:
19
19
  ruby-version: ${{ matrix.ruby-version }}
20
- - name: Set up RBS and bundle install
20
+ - name: Bundle install
21
21
  run: |
22
- cd rbs && bundle install && bundle exec rake parser && cd ..
23
22
  bundle install
24
23
  - name: Run the test suite
25
24
  run: |
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- typeprof (0.4.1)
4
+ typeprof (0.4.2)
5
5
  rbs (>= 0.16.0)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -33,3 +33,9 @@ end
33
33
  ## Documentation
34
34
 
35
35
  [English](doc/doc.md) / [日本語](doc/doc.ja.md)
36
+
37
+ ## Playground
38
+
39
+ You can try typeprof gem on the Web via the URL below.
40
+
41
+ https://mame.github.io/typeprof-playground/
@@ -87,6 +87,11 @@ module TypeProf
87
87
  @recv_ty = recv_ty
88
88
  @blk_ty = blk_ty
89
89
  @mod_func = mod_func
90
+
91
+ return if recv_ty == :top #OK
92
+ recv_ty.each_child_global do |ty|
93
+ raise ty.inspect if !ty.is_a?(Type::Instance) && !ty.is_a?(Type::Class) && ty != Type.any
94
+ end
90
95
  end
91
96
 
92
97
  attr_reader :recv_ty, :blk_ty, :mod_func
@@ -129,11 +134,7 @@ module TypeProf
129
134
  elems2 = type_params[id]
130
135
  if elems2
131
136
  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
+ type_params[id] = elems.union(elems2)
137
138
  end
138
139
  else
139
140
  type_params[id] = elems
@@ -151,6 +152,8 @@ module TypeProf
151
152
  tys.each do |ty|
152
153
  raise "nil cannot be pushed to the stack" if ty.nil?
153
154
  ty.each_child do |ty|
155
+ raise if ty.is_a?(Type::Var)
156
+ #raise if ty.is_a?(Type::Instance) && ty.klass.type_params.size > 1
154
157
  raise "Array cannot be pushed to the stack" if ty.is_a?(Type::Array)
155
158
  raise "Hash cannot be pushed to the stack" if ty.is_a?(Type::Hash)
156
159
  end
@@ -572,9 +575,7 @@ module TypeProf
572
575
 
573
576
  ret_ty = @return_values[callee_ctx] ||= Type.bot
574
577
  if ret_ty != Type.bot
575
- @callsites[callee_ctx].each do |caller_ep, ctn|
576
- ctn[ret_ty, caller_ep, @return_envs[caller_ep]]
577
- end
578
+ ctn[ret_ty, caller_ep, @return_envs[caller_ep]]
578
579
  end
579
580
  end
580
581
 
@@ -632,7 +633,7 @@ module TypeProf
632
633
  entry = @tbl[site] ||= Entry.new(!ep, {}, Type.bot, Utils::MutableSet.new)
633
634
  if ep
634
635
  if entry.rbs_declared
635
- if !entry.type.consistent?(ty, {})
636
+ unless Type.match?(ty, entry.type)
636
637
  scratch.warn(ep, "inconsistent assignment to RBS-declared global variable")
637
638
  return
638
639
  end
@@ -651,6 +652,7 @@ module TypeProf
651
652
  end
652
653
 
653
654
  def get_ivar(recv)
655
+ recv = recv.base_type while recv.respond_to?(:base_type)
654
656
  case recv
655
657
  when Type::Class
656
658
  [@class_defs[recv.idx], true]
@@ -884,26 +886,41 @@ module TypeProf
884
886
  def pend_method_execution(iseq, meth, recv, mid, cref)
885
887
  ctx = Context.new(iseq, cref, mid)
886
888
  ep = ExecutionPoint.new(ctx, 0, nil)
887
- locals = [Type.any] * iseq.locals.size
888
-
889
- keyword = iseq.fargs_format[:keyword]
889
+ locals = [Type.nil] * iseq.locals.size
890
+
891
+ fargs_format = iseq.fargs_format
892
+ lead_num = fargs_format[:lead_num] || 0
893
+ post_num = fargs_format[:post_num] || 0
894
+ post_index = fargs_format[:post_start]
895
+ rest_index = fargs_format[:rest_start]
896
+ keyword = fargs_format[:keyword]
897
+ kw_index = fargs_format[:kwbits] - keyword.size if keyword
898
+ kwrest_index = fargs_format[:kwrest]
899
+ block_index = fargs_format[:block_start]
900
+ opt = fargs_format[:opt] || [0]
901
+
902
+ (lead_num + opt.size - 1).times {|i| locals[i] = Type.any }
903
+ post_num.times {|i| locals[i + post_index] = Type.any } if post_index
904
+ locals[rest_index] = Type.any if rest_index
890
905
  if keyword
891
- kw_start = iseq.fargs_format[:kwbits]
892
- kw_start -= iseq.fargs_format[:keyword].size if kw_start
893
906
  keyword.each_with_index do |kw, i|
894
907
  case
895
908
  when kw.is_a?(Symbol) # required keyword
909
+ locals[kw_index + i] = Type.any
896
910
  when kw.size == 2 # optional keyword (default value is a literal)
897
911
  _key, default_ty = *kw
898
912
  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)
913
+ default_ty = default_ty.base_type if default_ty.is_a?(Type::Literal)
914
+ locals[kw_index + i] = default_ty.union(Type.any)
901
915
  else # optional keyword (default value is an expression)
916
+ locals[kw_index + i] = Type.any
902
917
  end
903
918
  end
904
919
  end
920
+ locals[kwrest_index] = Type.any if kwrest_index
921
+ locals[block_index] = Type.nil if block_index
905
922
 
906
- env = Env.new(StaticEnv.new(recv, Type.any, false), locals, [], Utils::HashWrapper.new({}))
923
+ env = Env.new(StaticEnv.new(recv, Type.nil, false), locals, [], Utils::HashWrapper.new({}))
907
924
 
908
925
  @pending_execution[iseq] ||= [:method, [meth, ep, env]]
909
926
  end
@@ -986,7 +1003,7 @@ module TypeProf
986
1003
  when kw.size == 2 # optional keyword (default value is a literal)
987
1004
  key, default_ty = *kw
988
1005
  default_ty = Type.guess_literal_type(default_ty)
989
- default_ty = default_ty.type if default_ty.is_a?(Type::Literal)
1006
+ default_ty = default_ty.base_type if default_ty.is_a?(Type::Literal)
990
1007
  req = false
991
1008
  else # optional keyword (default value is an expression)
992
1009
  key, = kw
@@ -1029,7 +1046,17 @@ module TypeProf
1029
1046
  ty = Type::Literal.new(str, Type::Instance.new(Type::Builtin[:str]))
1030
1047
  env = env.push(ty)
1031
1048
  when :putself
1032
- env, ty = localize_type(env.static_env.recv_ty, env, ep)
1049
+ ty = env.static_env.recv_ty
1050
+ if ty.is_a?(Type::Instance)
1051
+ klass = ty.klass
1052
+ if klass.type_params.size >= 1
1053
+ ty = Type::ContainerType.create_empty_instance(klass)
1054
+ env, ty = localize_type(ty, env, ep, AllocationSite.new(ep))
1055
+ else
1056
+ ty = Type::Instance.new(klass)
1057
+ end
1058
+ env, ty = localize_type(ty, env, ep)
1059
+ end
1033
1060
  env = env.push(ty)
1034
1061
  when :newarray, :newarraykwsplat
1035
1062
  len, = operands
@@ -1700,15 +1727,27 @@ module TypeProf
1700
1727
  end
1701
1728
 
1702
1729
  when :checktype
1703
- type, = operands
1704
- raise NotImplementedError if type != 5 # T_STRING
1705
- # XXX: is_a?
1706
- env, (val,) = env.pop(1)
1707
- res = globalize_type(val, env, ep) == Type::Instance.new(Type::Builtin[:str])
1708
- if res
1709
- ty = Type::Instance.new(Type::Builtin[:true])
1730
+ kind, = operands
1731
+ case kind
1732
+ when 5 then klass = :str # T_STRING
1733
+ when 7 then klass = :ary # T_ARRAY
1734
+ when 8 then klass = :hash # T_HASH
1710
1735
  else
1711
- ty = Type::Instance.new(Type::Builtin[:false])
1736
+ raise NotImplementedError
1737
+ end
1738
+ env, (val,) = env.pop(1)
1739
+ ty = Type.bot
1740
+ val.each_child do |val|
1741
+ #globalize_type(val, env, ep).each_child_global do |val|
1742
+ val = val.base_type while val.respond_to?(:base_type)
1743
+ case val
1744
+ when Type::Instance.new(Type::Builtin[klass])
1745
+ ty = ty.union(Type::Instance.new(Type::Builtin[:true]))
1746
+ when Type.any
1747
+ ty = Type.bool
1748
+ else
1749
+ ty = ty.union(Type::Instance.new(Type::Builtin[:false]))
1750
+ end
1712
1751
  end
1713
1752
  env = env.push(ty)
1714
1753
  else
@@ -1946,7 +1985,7 @@ module TypeProf
1946
1985
  end
1947
1986
  end
1948
1987
 
1949
- @block_to_ctx[blk.block_body].each do |blk_ctx|
1988
+ @block_to_ctx[blk.block_body]&.each do |blk_ctx|
1950
1989
  ret_ty = ret_ty.union(@return_values[blk_ctx]) if @return_values[blk_ctx]
1951
1990
  end
1952
1991
  end
@@ -25,7 +25,7 @@ module TypeProf
25
25
  self
26
26
  end
27
27
 
28
- def consistent_with_method_signature?(msig, subst)
28
+ def consistent_with_method_signature?(msig)
29
29
  aargs = @lead_tys.dup
30
30
 
31
31
  # aargs: lead_tys, rest_ty
@@ -36,53 +36,61 @@ module TypeProf
36
36
  (lower_bound..upper_bound).each do |n|
37
37
  # BUG: @rest_ty is an Array, so need to squash
38
38
  tmp_aargs = ActualArguments.new(@lead_tys + [@rest_ty] * n, nil, @kw_tys, @blk_ty)
39
- if tmp_aargs.consistent_with_method_signature?(msig, subst) # XXX: wrong subst handling in the loop!
40
- return true
41
- end
39
+ subst = tmp_aargs.consistent_with_method_signature?(msig) # XXX: wrong subst handling in the loop!
40
+ return subst if subst
42
41
  end
43
- return false
42
+ return nil
44
43
  end
45
44
 
45
+ subst = {}
46
46
  if msig.rest_ty
47
- return false if aargs.size < msig.lead_tys.size + msig.post_tys.size
47
+ return nil if aargs.size < msig.lead_tys.size + msig.post_tys.size
48
48
  aargs.shift(msig.lead_tys.size).zip(msig.lead_tys) do |aarg, farg|
49
- return false unless aarg.consistent?(farg, subst)
49
+ return nil unless subst2 = Type.match?(aarg, farg)
50
+ subst = Type.merge_substitution(subst, subst2)
50
51
  end
51
52
  aargs.pop(msig.post_tys.size).zip(msig.post_tys) do |aarg, farg|
52
- return false unless aarg.consistent?(farg, subst)
53
+ return nil unless subst2 = Type.match?(aarg, farg)
54
+ subst = Type.merge_substitution(subst, subst2)
53
55
  end
54
56
  msig.opt_tys.each do |farg|
55
57
  aarg = aargs.shift
56
- return false unless aarg.consistent?(farg, subst)
58
+ return nil unless subst2 = Type.match?(aarg, farg)
59
+ subst = Type.merge_substitution(subst, subst2)
57
60
  end
58
61
  aargs.each do |aarg|
59
- return false unless aarg.consistent?(msig.rest_ty, subst)
62
+ return nil unless subst2 = Type.match?(aarg, msig.rest_ty)
63
+ subst = Type.merge_substitution(subst, subst2)
60
64
  end
61
65
  else
62
- return false if aargs.size < msig.lead_tys.size + msig.post_tys.size
63
- return false if aargs.size > msig.lead_tys.size + msig.post_tys.size + msig.opt_tys.size
66
+ return nil if aargs.size < msig.lead_tys.size + msig.post_tys.size
67
+ return nil if aargs.size > msig.lead_tys.size + msig.post_tys.size + msig.opt_tys.size
64
68
  aargs.shift(msig.lead_tys.size).zip(msig.lead_tys) do |aarg, farg|
65
- return false unless aarg.consistent?(farg, subst)
69
+ return nil unless subst2 = Type.match?(aarg, farg)
70
+ subst = Type.merge_substitution(subst, subst2)
66
71
  end
67
72
  aargs.pop(msig.post_tys.size).zip(msig.post_tys) do |aarg, farg|
68
- return false unless aarg.consistent?(farg, subst)
73
+ return nil unless subst2 = Type.match?(aarg, farg)
74
+ subst = Type.merge_substitution(subst, subst2)
69
75
  end
70
76
  aargs.zip(msig.opt_tys) do |aarg, farg|
71
- return false unless aarg.consistent?(farg, subst)
77
+ return nil unless subst2 = Type.match?(aarg, farg)
78
+ subst = Type.merge_substitution(subst, subst2)
72
79
  end
73
80
  end
74
81
  # XXX: msig.keyword_tys
75
82
 
76
83
  case msig.blk_ty
77
84
  when Type::Proc
78
- return false if @blk_ty == Type.nil
85
+ return nil if @blk_ty == Type.nil
79
86
  when Type.nil
80
- return false if @blk_ty != Type.nil
87
+ return nil if @blk_ty != Type.nil
81
88
  when Type::Any
82
89
  else
83
90
  raise "unknown type of formal block signature"
84
91
  end
85
- true
92
+
93
+ subst
86
94
  end
87
95
 
88
96
  def argument_error(given, exp_lower, exp_upper)
@@ -345,7 +353,7 @@ module TypeProf
345
353
  when kw.size == 2 # optional keyword (default value is a literal)
346
354
  key, default_ty = *kw
347
355
  default_ty = Type.guess_literal_type(default_ty)
348
- default_ty = default_ty.type if default_ty.is_a?(Type::Literal)
356
+ default_ty = default_ty.base_type if default_ty.is_a?(Type::Literal)
349
357
  req = false
350
358
  else # optional keyword (default value is an expression)
351
359
  key, = kw
@@ -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|
@@ -520,6 +525,7 @@ module TypeProf
520
525
  scratch.set_custom_method(klass_vmcore, :"core#set_method_alias", Builtin.method(:vmcore_set_method_alias))
521
526
  scratch.set_custom_method(klass_vmcore, :"core#undef_method", Builtin.method(:vmcore_undef_method))
522
527
  scratch.set_custom_method(klass_vmcore, :"core#hash_merge_kwd", Builtin.method(:vmcore_hash_merge_kwd))
528
+ scratch.set_custom_method(klass_vmcore, :"core#raise", Builtin.method(:vmcore_raise))
523
529
  scratch.set_custom_method(klass_vmcore, :lambda, Builtin.method(:lambda))
524
530
  scratch.set_singleton_custom_method(klass_obj, :"new", Builtin.method(:object_s_new))
525
531
  scratch.set_singleton_custom_method(klass_obj, :"attr_accessor", Builtin.method(:module_attr_accessor))
@@ -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