typeprof 0.4.2 → 0.5.0

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