typeprof 0.1.2 → 0.4.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.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/Gemfile +2 -2
  4. data/Gemfile.lock +10 -21
  5. data/LICENSE +21 -0
  6. data/README.md +1 -1
  7. data/doc/demo.md +398 -0
  8. data/doc/doc.ja.md +11 -1
  9. data/doc/doc.md +16 -7
  10. data/exe/typeprof +2 -1
  11. data/lib/typeprof.rb +9 -0
  12. data/lib/typeprof/analyzer.rb +455 -364
  13. data/lib/typeprof/arguments.rb +397 -0
  14. data/lib/typeprof/block.rb +133 -0
  15. data/lib/typeprof/builtin.rb +125 -116
  16. data/lib/typeprof/cli.rb +62 -71
  17. data/lib/typeprof/config.rb +114 -0
  18. data/lib/typeprof/container-type.rb +208 -27
  19. data/lib/typeprof/export.rb +201 -96
  20. data/lib/typeprof/import.rb +451 -365
  21. data/lib/typeprof/iseq.rb +43 -2
  22. data/lib/typeprof/method.rb +139 -100
  23. data/lib/typeprof/type.rb +138 -297
  24. data/lib/typeprof/utils.rb +4 -18
  25. data/lib/typeprof/version.rb +3 -0
  26. data/smoke/arguments2.rb +55 -0
  27. data/smoke/array-each3.rb +1 -4
  28. data/smoke/array12.rb +1 -1
  29. data/smoke/array6.rb +1 -0
  30. data/smoke/block-ambiguous.rb +36 -0
  31. data/smoke/block-args1-rest.rb +62 -0
  32. data/smoke/block-args1.rb +59 -0
  33. data/smoke/block-args2-rest.rb +62 -0
  34. data/smoke/block-args2.rb +59 -0
  35. data/smoke/block-args3-rest.rb +73 -0
  36. data/smoke/block-args3.rb +70 -0
  37. data/smoke/block-blockarg.rb +27 -0
  38. data/smoke/block-kwarg.rb +52 -0
  39. data/smoke/block11.rb +1 -1
  40. data/smoke/block13.rb +9 -0
  41. data/smoke/block13.rbs +3 -0
  42. data/smoke/block14.rb +17 -0
  43. data/smoke/block4.rb +2 -2
  44. data/smoke/block5.rb +1 -0
  45. data/smoke/block6.rb +1 -1
  46. data/smoke/block7.rb +0 -2
  47. data/smoke/block8.rb +2 -2
  48. data/smoke/block9.rb +1 -1
  49. data/smoke/blown.rb +1 -1
  50. data/smoke/class-hierarchy.rb +54 -0
  51. data/smoke/class-hierarchy2.rb +27 -0
  52. data/smoke/class.rb +2 -0
  53. data/smoke/constant1.rb +13 -5
  54. data/smoke/constant2.rb +2 -0
  55. data/smoke/cvar.rb +1 -0
  56. data/smoke/demo10.rb +1 -1
  57. data/smoke/demo5.rb +3 -0
  58. data/smoke/demo8.rb +2 -2
  59. data/smoke/demo9.rb +1 -3
  60. data/smoke/flow7.rb +1 -7
  61. data/smoke/flow8.rb +13 -0
  62. data/smoke/gvar.rb +1 -1
  63. data/smoke/gvar2.rb +17 -0
  64. data/smoke/gvar2.rbs +1 -0
  65. data/smoke/hash4.rb +1 -1
  66. data/smoke/inheritance2.rb +6 -0
  67. data/smoke/instance_eval.rb +1 -1
  68. data/smoke/int_times.rb +1 -1
  69. data/smoke/ivar3.rb +16 -0
  70. data/smoke/ivar3.rbs +3 -0
  71. data/smoke/keyword3.rb +1 -2
  72. data/smoke/keyword4.rb +1 -1
  73. data/smoke/manual-rbs2.rb +1 -1
  74. data/smoke/manual-rbs3.rb +12 -0
  75. data/smoke/manual-rbs3.rbs +3 -0
  76. data/smoke/module4.rb +5 -0
  77. data/smoke/multiple-superclass.rb +12 -0
  78. data/smoke/next2.rb +1 -1
  79. data/smoke/optional1.rb +1 -1
  80. data/smoke/optional2.rb +1 -1
  81. data/smoke/optional3.rb +10 -0
  82. data/smoke/proc4.rb +1 -1
  83. data/smoke/rbs-alias.rb +9 -0
  84. data/smoke/rbs-alias.rbs +4 -0
  85. data/smoke/rbs-attr.rb +26 -0
  86. data/smoke/rbs-attr.rbs +5 -0
  87. data/smoke/rbs-extend.rb +9 -0
  88. data/smoke/rbs-extend.rbs +7 -0
  89. data/smoke/rbs-interface.rb +24 -0
  90. data/smoke/rbs-interface.rbs +12 -0
  91. data/smoke/rbs-proc1.rb +9 -0
  92. data/smoke/rbs-proc1.rbs +3 -0
  93. data/smoke/rbs-proc2.rb +20 -0
  94. data/smoke/rbs-proc2.rbs +3 -0
  95. data/smoke/rbs-proc3.rb +13 -0
  96. data/smoke/rbs-proc3.rbs +4 -0
  97. data/smoke/rbs-record.rb +17 -0
  98. data/smoke/rbs-record.rbs +4 -0
  99. data/smoke/rbs-tyvar.rb +18 -0
  100. data/smoke/rbs-tyvar.rbs +5 -0
  101. data/smoke/rbs-tyvar2.rb +20 -0
  102. data/smoke/rbs-tyvar2.rbs +9 -0
  103. data/smoke/rbs-tyvar3.rb +25 -0
  104. data/smoke/rbs-tyvar3.rbs +4 -0
  105. data/smoke/rbs-vars.rb +39 -0
  106. data/smoke/rbs-vars.rbs +7 -0
  107. data/smoke/rest1.rb +1 -1
  108. data/smoke/rest2.rb +1 -1
  109. data/smoke/rest3.rb +1 -1
  110. data/smoke/rest5.rb +1 -1
  111. data/smoke/rest6.rb +1 -1
  112. data/smoke/retry1.rb +1 -1
  113. data/smoke/return.rb +1 -1
  114. data/smoke/singleton_method.rb +3 -0
  115. data/smoke/step.rb +1 -1
  116. data/smoke/struct.rb +6 -2
  117. data/smoke/struct3.rb +14 -0
  118. data/smoke/super1.rb +18 -0
  119. data/smoke/symbol-proc.rb +24 -0
  120. data/smoke/union-recv.rb +6 -0
  121. data/smoke/user-demo.rb +15 -0
  122. data/smoke/wrong-extend.rb +1 -0
  123. data/tools/setup-insns-def.rb +1 -1
  124. data/tools/stackprof-wrapper.rb +1 -1
  125. data/typeprof.gemspec +12 -4
  126. metadata +68 -10
  127. data/.gitmodules +0 -6
  128. data/run.sh +0 -3
  129. data/smoke/variadic1.rb.notyet +0 -5
@@ -32,6 +32,23 @@ module TypeProf
32
32
  @name, @path, @absolute_path, @start_lineno, @type,
33
33
  @locals, @fargs_format, catch_table, insns = *iseq
34
34
 
35
+ case @type
36
+ when :method, :block
37
+ if @fargs_format[:opt]
38
+ label = @fargs_format[:opt].last
39
+ i = insns.index(label) + 1
40
+ else
41
+ i = insns.find_index {|insn| insn.is_a?(Array) }
42
+ end
43
+ # skip keyword initialization
44
+ while insns[i][0] == :checkkeyword
45
+ raise if insns[i + 1][0] != :branchif
46
+ label = insns[i + 1][1]
47
+ i = insns.index(label) + 1
48
+ end
49
+ insns[i, 0] = [[:_iseq_body_start]]
50
+ end
51
+
35
52
  @insns = []
36
53
  @linenos = []
37
54
 
@@ -79,7 +96,7 @@ module TypeProf
79
96
  nil
80
97
  when Array
81
98
  insn, *operands = e
82
- operands = INSN_TABLE[insn].zip(operands).map do |type, operand|
99
+ operands = (INSN_TABLE[insn] || []).zip(operands).map do |type, operand|
83
100
  case type
84
101
  when "ISEQ"
85
102
  operand && ISeq.new(operand)
@@ -125,7 +142,7 @@ module TypeProf
125
142
  "#{ @path }:#{ @linenos[pc] }"
126
143
  end
127
144
 
128
- attr_reader :name, :path, :abolute_path, :start_lineno, :type, :locals, :fargs_format, :catch_table, :insns, :linenos
145
+ attr_reader :name, :path, :absolute_path, :start_lineno, :type, :locals, :fargs_format, :catch_table, :insns, :linenos
129
146
  attr_reader :id
130
147
 
131
148
  def pretty_print(q)
@@ -200,6 +217,30 @@ module TypeProf
200
217
  @insns[j + 1] = [:send_branch, [getlocal_operands, send_operands, branch_operands]]
201
218
  end
202
219
 
220
+ # find a pattern: getlocal, dup, branch
221
+ (@insns.size - 2).times do |i|
222
+ next if branch_targets[i + 1] || branch_targets[i + 2]
223
+ insn0, getlocal_operands = @insns[i]
224
+ insn1, dup_operands = @insns[i + 1]
225
+ insn2, branch_operands = @insns[i + 2]
226
+ if insn0 == :getlocal && insn1 == :dup && insn2 == :branch && getlocal_operands[1] == 0
227
+ @insns[i ] = [:nop]
228
+ @insns[i + 1] = [:nop]
229
+ @insns[i + 2] = [:getlocal_dup_branch, [getlocal_operands, dup_operands, branch_operands]]
230
+ end
231
+ end
232
+
233
+ # find a pattern: dup, branch
234
+ (@insns.size - 1).times do |i|
235
+ next if branch_targets[i + 1]
236
+ insn0, dup_operands = @insns[i]
237
+ insn1, branch_operands = @insns[i + 1]
238
+ if insn0 == :dup && insn1 == :branch
239
+ @insns[i ] = [:nop]
240
+ @insns[i + 1] = [:dup_branch, [dup_operands, branch_operands]]
241
+ end
242
+ end
243
+
203
244
  # find a pattern: getlocal, branch
204
245
  (@insns.size - 1).times do |i|
205
246
  next if branch_targets[i + 1]
@@ -14,108 +14,128 @@ module TypeProf
14
14
  recv = scratch.globalize_type(recv, caller_env, caller_ep)
15
15
  aargs = scratch.globalize_type(aargs, caller_env, caller_ep)
16
16
 
17
- aargs.each_formal_arguments(@iseq.fargs_format) do |fargs, start_pc|
18
- if fargs.is_a?(String)
19
- scratch.error(caller_ep, fargs)
20
- ctn[Type.any, caller_ep, caller_env]
21
- next
22
- end
17
+ locals = [Type.nil] * @iseq.locals.size
23
18
 
24
- callee_ep = do_send_core(fargs, start_pc, recv, mid, scratch)
19
+ blk_ty, start_pcs = aargs.setup_formal_arguments(:method, locals, @iseq.fargs_format)
20
+ if blk_ty.is_a?(String)
21
+ scratch.error(caller_ep, blk_ty)
22
+ ctn[Type.any, caller_ep, caller_env]
23
+ return
24
+ end
25
+
26
+ nctx = Context.new(@iseq, @cref, mid)
27
+ callee_ep = ExecutionPoint.new(nctx, 0, nil)
28
+ nenv = Env.new(StaticEnv.new(recv, blk_ty, false), locals, [], Utils::HashWrapper.new({}))
29
+ alloc_site = AllocationSite.new(callee_ep)
30
+ locals.each_with_index do |ty, i|
31
+ alloc_site2 = alloc_site.add_id(i)
32
+ # nenv is top-level, so it is okay to call Type#localize directly
33
+ nenv, ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
34
+ nenv = nenv.local_update(i, ty)
35
+ end
25
36
 
26
- scratch.add_iseq_method_call!(self, callee_ep.ctx)
27
- scratch.add_callsite!(callee_ep.ctx, fargs, caller_ep, caller_env, &ctn)
37
+ start_pcs.each do |start_pc|
38
+ scratch.merge_env(ExecutionPoint.new(nctx, start_pc, @outer_ep), nenv)
28
39
  end
40
+
41
+ scratch.add_iseq_method_call!(self, nctx)
42
+ scratch.add_callsite!(nctx, caller_ep, caller_env, &ctn)
29
43
  end
30
44
 
31
- def do_send_core(fargs, start_pc, recv, mid, scratch)
45
+ def do_check_send(msig, recv, mid, ep, scratch)
46
+ lead_num = @iseq.fargs_format[:lead_num] || 0
47
+ post_num = @iseq.fargs_format[:post_num] || 0
48
+ rest_start = @iseq.fargs_format[:rest_start]
49
+ opt = @iseq.fargs_format[:opt] || [0]
50
+
51
+ # TODO: check keywords
52
+ if rest_start
53
+ # almost ok
54
+ else
55
+ if msig.lead_tys.size + msig.post_tys.size < lead_num + post_num
56
+ scratch.error(ep, "RBS says that the arity may be %d, but the method definition requires at least %d arguments" % [msig.lead_tys.size + msig.post_tys.size, lead_num + post_num])
57
+ return
58
+ end
59
+ if msig.lead_tys.size + msig.opt_tys.size + msig.post_tys.size > lead_num + opt.size - 1 + post_num
60
+ scratch.error(ep, "RBS says that the arity may be %d, but the method definition requires at most %d arguments" % [msig.lead_tys.size + msig.opt_tys.size + msig.post_tys.size, lead_num + opt.size - 1 + post_num])
61
+ return
62
+ end
63
+ end
64
+
32
65
  lead_num = @iseq.fargs_format[:lead_num] || 0
33
66
  post_start = @iseq.fargs_format[:post_start]
34
67
  rest_start = @iseq.fargs_format[:rest_start]
35
68
  kw_start = @iseq.fargs_format[:kwbits]
36
69
  kw_start -= @iseq.fargs_format[:keyword].size if kw_start
70
+ kw_rest = @iseq.fargs_format[:kwrest]
37
71
  block_start = @iseq.fargs_format[:block_start]
38
72
 
39
- # XXX: need to check .rbs fargs and .rb fargs
73
+ # XXX: need to check .rbs msig and .rb fargs
40
74
 
41
- ctx = Context.new(@iseq, @cref, mid) # XXX: to support opts, rest, etc
42
- callee_ep = ExecutionPoint.new(ctx, start_pc, nil)
75
+ ctx = Context.new(@iseq, @cref, mid)
76
+ callee_ep = ExecutionPoint.new(ctx, 0, nil)
43
77
 
44
78
  locals = [Type.nil] * @iseq.locals.size
45
- nenv = Env.new(StaticEnv.new(recv, fargs.blk_ty, false), locals, [], Utils::HashWrapper.new({}))
79
+ nenv = Env.new(StaticEnv.new(recv, msig.blk_ty, false), locals, [], Utils::HashWrapper.new({}))
46
80
  alloc_site = AllocationSite.new(callee_ep)
47
81
  idx = 0
48
- fargs.lead_tys.each_with_index do |ty, i|
82
+ msig.lead_tys.each_with_index do |ty, i|
49
83
  alloc_site2 = alloc_site.add_id(idx += 1)
50
84
  # nenv is top-level, so it is okay to call Type#localize directly
51
85
  nenv, ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
52
86
  nenv = nenv.local_update(i, ty)
53
87
  end
54
- if fargs.opt_tys
55
- fargs.opt_tys.each_with_index do |ty, i|
88
+ if msig.opt_tys
89
+ msig.opt_tys.each_with_index do |ty, i|
56
90
  alloc_site2 = alloc_site.add_id(idx += 1)
57
91
  nenv, ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
58
92
  nenv = nenv.local_update(lead_num + i, ty)
59
93
  end
60
94
  end
61
- if fargs.rest_ty
95
+ if msig.rest_ty
62
96
  alloc_site2 = alloc_site.add_id(idx += 1)
63
- ty = Type::Array.new(Type::Array::Elements.new([], fargs.rest_ty), Type::Instance.new(Type::Builtin[:ary]))
97
+ ty = Type::Array.new(Type::Array::Elements.new([], msig.rest_ty), Type::Instance.new(Type::Builtin[:ary]))
64
98
  nenv, rest_ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
65
99
  nenv = nenv.local_update(rest_start, rest_ty)
66
100
  end
67
- if fargs.post_tys
68
- fargs.post_tys.each_with_index do |ty, i|
101
+ if msig.post_tys
102
+ msig.post_tys.each_with_index do |ty, i|
69
103
  alloc_site2 = alloc_site.add_id(idx += 1)
70
104
  nenv, ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
71
105
  nenv = nenv.local_update(post_start + i, ty)
72
106
  end
73
107
  end
74
- if fargs.kw_tys
75
- fargs.kw_tys.each_with_index do |(_, _, ty), i|
76
- alloc_site2 = alloc_site.add_id(idx += 1)
108
+ if msig.kw_tys
109
+ msig.kw_tys.each_with_index do |(_, key, ty), i|
110
+ alloc_site2 = alloc_site.add_id(key)
77
111
  nenv, ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
78
112
  nenv = nenv.local_update(kw_start + i, ty)
79
113
  end
80
114
  end
81
- # kwrest
82
- nenv = nenv.local_update(block_start, fargs.blk_ty) if block_start
83
-
84
- scratch.merge_env(callee_ep, nenv)
85
-
86
- callee_ep
87
- end
88
-
89
- def do_check_send_core(fargs, recv, mid, ep, scratch)
90
- lead_num = @iseq.fargs_format[:lead_num] || 0
91
- post_num = @iseq.fargs_format[:post_num] || 0
92
- rest_start = @iseq.fargs_format[:rest_start]
93
- opt = @iseq.fargs_format[:opt] || [0]
115
+ if msig.kw_rest_ty
116
+ ty = msig.kw_rest_ty
117
+ alloc_site2 = alloc_site.add_id(:**)
118
+ nenv, ty = ty.localize(nenv, alloc_site2, Config.options[:type_depth_limit])
119
+ nenv = nenv.local_update(kw_rest, ty)
120
+ end
121
+ nenv = nenv.local_update(block_start, msig.blk_ty) if block_start
94
122
 
95
- # TODO: check keywords
96
- if rest_start
97
- # almost ok
98
- else
99
- if fargs.lead_tys.size + fargs.post_tys.size < lead_num + post_num
100
- scratch.error(ep, "RBS says that the arity may be %d, but the method definition requires at least %d arguments" % [fargs.lead_tys.size + fargs.post_tys.size, lead_num + post_num])
101
- return
102
- end
103
- if fargs.lead_tys.size + fargs.opt_tys.size + fargs.post_tys.size > lead_num + opt.size - 1 + post_num
104
- scratch.error(ep, "RBS says that the arity may be %d, but the method definition requires at most %d arguments" % [fargs.lead_tys.size + fargs.opt_tys.size + fargs.post_tys.size, lead_num + opt.size - 1 + post_num])
105
- return
106
- end
123
+ opt.each do |start_pc|
124
+ scratch.merge_env(ExecutionPoint.new(ctx, start_pc, nil), nenv)
107
125
  end
108
- do_send_core(fargs, 0, recv, mid, scratch)
126
+
127
+ ctx
109
128
  end
110
129
  end
111
130
 
112
131
  class AttrMethodDef < MethodDef
113
- def initialize(ivar, kind)
132
+ def initialize(ivar, kind, absolute_path)
114
133
  @ivar = ivar
115
134
  @kind = kind # :reader | :writer
135
+ @absolute_path = absolute_path
116
136
  end
117
137
 
118
- attr_reader :ivar, :kind
138
+ attr_reader :ivar, :kind, :absolute_path
119
139
 
120
140
  def do_send(recv, mid, aargs, caller_ep, caller_env, scratch, &ctn)
121
141
  case @kind
@@ -140,8 +160,8 @@ module TypeProf
140
160
  end
141
161
 
142
162
  class TypedMethodDef < MethodDef
143
- def initialize(sigs, rbs_source) # sigs: Array<[FormalArguments, (return)Type]>
144
- @sigs = sigs
163
+ def initialize(sig_rets, rbs_source) # sig_rets: Array<[MethodSignature, (return)Type]>
164
+ @sig_rets = sig_rets
145
165
  @rbs_source = rbs_source
146
166
  end
147
167
 
@@ -151,25 +171,42 @@ module TypeProf
151
171
  recv = scratch.globalize_type(recv_orig, caller_env, caller_ep)
152
172
  found = false
153
173
  aargs = scratch.globalize_type(aargs, caller_env, caller_ep)
154
- @sigs.each do |fargs, ret_ty|
174
+ @sig_rets.each do |msig, ret_ty|
155
175
  ncaller_env = caller_env
156
- # XXX: need to interpret args more correctly
157
- #pp [mid, aargs, fargs]
158
- # XXX: support self type in fargs
176
+ #pp [mid, aargs, msig]
177
+ # XXX: support self type in msig
159
178
  subst = { Type::Var.new(:self) => recv }
160
- next unless aargs.consistent_with_formal_arguments?(fargs, subst)
161
- if recv.is_a?(Type::Array) && recv_orig.is_a?(Type::LocalArray)
179
+ next unless aargs.consistent_with_method_signature?(msig, subst)
180
+ case
181
+ when recv.is_a?(Type::Cell) && recv_orig.is_a?(Type::LocalCell)
182
+ tyvars = recv.base_type.klass.type_params.map {|name,| Type::Var.new(name) }
183
+ tyvars.each_with_index do |tyvar, idx|
184
+ ty = subst[tyvar]
185
+ if ty
186
+ ncaller_env, ty = scratch.localize_type(ty, ncaller_env, caller_ep)
187
+ ncaller_env = scratch.update_container_elem_types(ncaller_env, caller_ep, recv_orig.id, recv_orig.base_type) do |elems|
188
+ elems.update(idx, ty)
189
+ end
190
+ end
191
+ end
192
+ tyvars.zip(recv.elems.elems) do |tyvar, elem|
193
+ if subst[tyvar]
194
+ subst[tyvar] = subst[tyvar].union(elem)
195
+ else
196
+ subst[tyvar] = elem
197
+ end
198
+ end
199
+ when recv.is_a?(Type::Array) && recv_orig.is_a?(Type::LocalArray)
162
200
  tyvar_elem = Type::Var.new(:Elem)
163
201
  if subst[tyvar_elem]
164
202
  ty = subst[tyvar_elem]
165
- alloc_site = AllocationSite.new(caller_ep).add_id(self)
166
203
  ncaller_env, ty = scratch.localize_type(ty, ncaller_env, caller_ep)
167
- ncaller_env = scratch.update_container_elem_types(ncaller_env, caller_ep, recv_orig.id) do |elems|
204
+ ncaller_env = scratch.update_container_elem_types(ncaller_env, caller_ep, recv_orig.id, recv_orig.base_type) do |elems|
168
205
  elems.update(nil, ty)
169
206
  end
170
207
  end
171
- subst.merge!({ tyvar_elem => recv.elems.squash })
172
- elsif recv.is_a?(Type::Hash) && recv_orig.is_a?(Type::LocalHash)
208
+ subst.merge!({ tyvar_elem => recv.elems.squash_or_any })
209
+ when recv.is_a?(Type::Hash) && recv_orig.is_a?(Type::LocalHash)
173
210
  tyvar_k = Type::Var.new(:K)
174
211
  tyvar_v = Type::Var.new(:V)
175
212
  # XXX: need to support destructive operation
@@ -179,54 +216,57 @@ module TypeProf
179
216
  end
180
217
  ret_ty = ret_ty.substitute(subst, Config.options[:type_depth_limit])
181
218
  found = true
182
- if aargs.blk_ty.is_a?(Type::ISeqProc)
219
+ if aargs.blk_ty.is_a?(Type::Proc)
220
+ #raise NotImplementedError unless aargs.blk_ty.block_body.is_a?(ISeqBlock) # XXX
183
221
  dummy_ctx = TypedContext.new(caller_ep, mid)
184
222
  dummy_ep = ExecutionPoint.new(dummy_ctx, -1, caller_ep)
185
- dummy_env = Env.new(StaticEnv.new(recv, fargs.blk_ty, false), [], [], Utils::HashWrapper.new({}))
186
- if fargs.blk_ty.is_a?(Type::TypedProc)
187
- scratch.add_callsite!(dummy_ctx, nil, caller_ep, ncaller_env, &ctn)
188
- nfargs = fargs.blk_ty.fargs
223
+ dummy_env = Env.new(StaticEnv.new(recv, msig.blk_ty, false), [], [], Utils::HashWrapper.new({}))
224
+ if msig.blk_ty.is_a?(Type::Proc)
225
+ scratch.add_callsite!(dummy_ctx, caller_ep, ncaller_env, &ctn)
226
+ nfargs = msig.blk_ty.block_body.msig
189
227
  alloc_site = AllocationSite.new(caller_ep).add_id(self)
190
- nfargs = nfargs.map.with_index do |nfarg, i|
228
+ nlead_tys = (nfargs.lead_tys + nfargs.opt_tys).map.with_index do |ty, i|
191
229
  if recv.is_a?(Type::Array)
192
230
  tyvar_elem = Type::Var.new(:Elem)
193
- nfarg = nfarg.substitute(subst.merge({ tyvar_elem => recv.elems.squash }), Config.options[:type_depth_limit])
231
+ ty = ty.substitute(subst.merge({ tyvar_elem => recv.elems.squash_or_any }), Config.options[:type_depth_limit])
194
232
  else
195
- nfarg = nfarg.substitute(subst, Config.options[:type_depth_limit])
233
+ ty = ty.substitute(subst, Config.options[:type_depth_limit])
196
234
  end
197
- nfarg = nfarg.remove_type_vars
235
+ ty = ty.remove_type_vars
198
236
  alloc_site2 = alloc_site.add_id(i)
199
- dummy_env, nfarg = scratch.localize_type(nfarg, dummy_env, dummy_ep, alloc_site2)
200
- nfarg
237
+ dummy_env, ty = scratch.localize_type(ty, dummy_env, dummy_ep, alloc_site2)
238
+ ty
201
239
  end
202
- naargs = ActualArguments.new(nfargs, nil, nil, Type.nil) # XXX: support block to block?
203
- scratch.do_invoke_block(false, aargs.blk_ty, naargs, dummy_ep, dummy_env) do |blk_ret_ty, _ep, _env|
204
- subst2 = {}
205
- if blk_ret_ty.consistent?(fargs.blk_ty.ret_ty, subst2)
206
- if recv.is_a?(Type::Array) && recv_orig.is_a?(Type::LocalArray)
207
- tyvar_elem = Type::Var.new(:Elem)
208
- if subst2[tyvar_elem]
209
- ncaller_env = scratch.update_container_elem_types(ncaller_env, caller_ep, recv_orig.id) do |elems|
210
- elems.update(nil, subst2[tyvar_elem])
240
+ 0.upto(nfargs.opt_tys.size) do |n|
241
+ naargs = ActualArguments.new(nlead_tys[0, nfargs.lead_tys.size + n], nil, {}, Type.nil) # XXX: support block to block?
242
+ 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)
246
+ tyvar_elem = Type::Var.new(:Elem)
247
+ if subst2[tyvar_elem]
248
+ 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])
250
+ end
251
+ scratch.merge_return_env(caller_ep) {|env| env ? env.merge(ncaller_env) : ncaller_env }
211
252
  end
212
- scratch.merge_return_env(caller_ep) {|env| env ? env.merge(ncaller_env) : ncaller_env }
253
+ ret_ty = ret_ty.substitute(subst2, Config.options[:type_depth_limit])
254
+ else
255
+ ret_ty = ret_ty.substitute(subst2, Config.options[:type_depth_limit])
213
256
  end
214
- ret_ty = ret_ty.substitute(subst2, Config.options[:type_depth_limit])
215
257
  else
216
- ret_ty = ret_ty.substitute(subst2, Config.options[:type_depth_limit])
258
+ # raise "???"
259
+ # XXX: need warning
260
+ ret_ty = Type.any
217
261
  end
218
- else
219
- # raise "???"
220
- # XXX: need warning
221
- ret_ty = Type.any
262
+ ret_ty = ret_ty.remove_type_vars
263
+ # XXX: check the return type from the block
264
+ # sig.blk_ty.block_body.ret_ty.eql?(_ret_ty) ???
265
+ scratch.add_return_value!(dummy_ctx, ret_ty)
222
266
  end
223
- ret_ty = ret_ty.remove_type_vars
224
- # XXX: check the return type from the block
225
- # sig.blk_ty.ret_ty.eql?(_ret_ty) ???
226
- scratch.add_return_type!(dummy_ctx, ret_ty)
267
+ # scratch.add_return_value!(dummy_ctx, ret_ty) ?
268
+ # This makes `def foo; 1.times { return "str" }; end` return Integer|String
227
269
  end
228
- # scratch.add_return_type!(dummy_ctx, ret_ty) ?
229
- # This makes `def foo; 1.times { return "str" }; end` return Integer|String
230
270
  else
231
271
  # XXX: a block is passed to a method that does not accept block.
232
272
  # Should we call the passed block with any arguments?
@@ -247,8 +287,8 @@ module TypeProf
247
287
 
248
288
  def do_match_iseq_mdef(iseq_mdef, recv, mid, env, ep, scratch)
249
289
  recv = scratch.globalize_type(recv, env, ep)
250
- @sigs.each do |fargs, _ret_ty|
251
- iseq_mdef.do_check_send_core(fargs, recv, mid, ep, scratch)
290
+ @sig_rets.each do |msig, _ret_ty|
291
+ iseq_mdef.do_check_send(msig, recv, mid, ep, scratch)
252
292
  end
253
293
  end
254
294
  end
@@ -259,7 +299,6 @@ module TypeProf
259
299
  end
260
300
 
261
301
  def do_send(recv, mid, aargs, caller_ep, caller_env, scratch, &ctn)
262
- # XXX: ctn?
263
302
  scratch.merge_return_env(caller_ep) {|env| env ? env.merge(caller_env) : caller_env } # for Kernel#lambda
264
303
  @impl[recv, mid, aargs, caller_ep, caller_env, scratch, &ctn]
265
304
  end
@@ -3,7 +3,7 @@ module TypeProf
3
3
  include Utils::StructuralEquality
4
4
 
5
5
  def initialize
6
- raise "cannot instanciate abstract type"
6
+ raise "cannot instantiate abstract type"
7
7
  end
8
8
 
9
9
  Builtin = {}
@@ -129,6 +129,17 @@ module TypeProf
129
129
  end
130
130
  end
131
131
 
132
+ class Void < Any
133
+ def inspect
134
+ "Type::Void"
135
+ end
136
+
137
+ def screen_name(scratch)
138
+ "void"
139
+ end
140
+ end
141
+
142
+
132
143
  class Union < Type
133
144
  def initialize(tys, elems)
134
145
  raise unless tys.is_a?(Utils::Set)
@@ -137,7 +148,7 @@ module TypeProf
137
148
  # invariant check
138
149
  local = nil
139
150
  tys.each do |ty|
140
- raise unless ty.is_a?(Type)
151
+ raise ty.inspect unless ty.is_a?(Type)
141
152
  local = true if ty.is_a?(LocalArray) || ty.is_a?(LocalHash)
142
153
  end
143
154
  raise if local && elems
@@ -215,12 +226,15 @@ module TypeProf
215
226
  bool = true
216
227
  end
217
228
  types.delete(Type.any) unless Config.options[:pedantic_output]
229
+ proc_tys, types = types.partition {|ty| ty.is_a?(Proc) }
218
230
  types = types.map {|ty| ty.screen_name(scratch) }
231
+ types << scratch.show_proc_signature(proc_tys) unless proc_tys.empty?
219
232
  types << "bool" if bool
220
233
  types = types.sort
221
234
  if optional
222
- if types.size == 1
223
- types.first + "?"
235
+ case types.size
236
+ when 0 then "nil"
237
+ when 1 then types.first + "?"
224
238
  else
225
239
  "(#{ types.join (" | ") })?"
226
240
  end
@@ -505,58 +519,51 @@ module TypeProf
505
519
  end
506
520
  end
507
521
 
508
- class ISeqProc < Type
509
- def initialize(iseq, ep, type)
510
- @iseq = iseq
511
- @ep = ep
512
- @type = type
522
+ class Proc < Type
523
+ def initialize(block_body, type)
524
+ @block_body, @type = block_body, type
513
525
  end
514
526
 
515
- attr_reader :iseq, :ep, :type
516
-
517
- def inspect
518
- "#<ISeqProc>"
519
- end
527
+ attr_reader :block_body, :type
520
528
 
521
- def screen_name(scratch)
522
- "Proc[#{ scratch.proc_screen_name(self) }]" # TODO: use RBS syntax
529
+ def consistent?(other, subst)
530
+ 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
+ when Type::Proc
538
+ @block_body.consistent?(other.block_body)
539
+ else
540
+ self == other
541
+ end
523
542
  end
524
543
 
525
544
  def get_method(mid, scratch)
526
545
  @type.get_method(mid, scratch)
527
546
  end
528
547
 
529
- def substitute(_subst, _depth)
530
- self # XXX
531
- end
532
- end
533
-
534
- class TypedProc < Type
535
- def initialize(fargs, ret_ty, type)
536
- # XXX: need to receive blk_ty?
537
- # XXX: may refactor "arguments = arg_tys * blk_ty" out
538
- @fargs = fargs
539
- @ret_ty = ret_ty
540
- @type = type
548
+ def substitute(subst, depth)
549
+ Proc.new(@block_body.substitute(subst, depth), @type)
541
550
  end
542
551
 
543
- attr_reader :fargs, :ret_ty
544
-
545
552
  def screen_name(scratch)
546
- "TypedProc[...]" # TODO: use RBS syntax
553
+ scratch.show_proc_signature([self])
547
554
  end
548
555
  end
549
556
 
550
557
  class Symbol < Type
551
- def initialize(sym, type)
558
+ def initialize(sym, base_type)
552
559
  @sym = sym
553
- @type = type
560
+ @base_type = base_type
554
561
  end
555
562
 
556
- attr_reader :sym, :type
563
+ attr_reader :sym, :base_type
557
564
 
558
565
  def inspect
559
- "Type::Symbol[#{ @sym ? @sym.inspect : "(dynamic symbol)" }, #{ @type.inspect }]"
566
+ "Type::Symbol[#{ @sym ? @sym.inspect : "(dynamic symbol)" }, #{ @base_type.inspect }]"
560
567
  end
561
568
 
562
569
  def consistent?(other, subst)
@@ -566,7 +573,7 @@ module TypeProf
566
573
  when Symbol
567
574
  @sym == other.sym
568
575
  else
569
- @type.consistent?(other, subst)
576
+ @base_type.consistent?(other, subst)
570
577
  end
571
578
  end
572
579
 
@@ -574,12 +581,12 @@ module TypeProf
574
581
  if @sym
575
582
  @sym.inspect
576
583
  else
577
- @type.screen_name(scratch)
584
+ @base_type.screen_name(scratch)
578
585
  end
579
586
  end
580
587
 
581
588
  def get_method(mid, scratch)
582
- @type.get_method(mid, scratch)
589
+ @base_type.get_method(mid, scratch)
583
590
  end
584
591
 
585
592
  def substitute(_subst, _depth)
@@ -639,10 +646,9 @@ module TypeProf
639
646
  end
640
647
  end
641
648
 
642
- def self.gen_hash
649
+ def self.gen_hash(base_ty = Type::Instance.new(Type::Builtin[:hash]))
643
650
  hg = HashGenerator.new
644
651
  yield hg
645
- base_ty = Type::Instance.new(Type::Builtin[:hash])
646
652
  Type::Hash.new(Type::Hash::Elements.new(hg.map_tys), base_ty)
647
653
  end
648
654
 
@@ -739,66 +745,74 @@ module TypeProf
739
745
  end
740
746
  end
741
747
 
742
- # Arguments for callee side
743
- class FormalArguments
748
+ class Signature
744
749
  include Utils::StructuralEquality
745
750
 
746
- def initialize(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, blk_ty)
747
- @lead_tys = lead_tys
748
- @opt_tys = opt_tys
749
- @rest_ty = rest_ty
750
- @post_tys = post_tys
751
- @kw_tys = kw_tys
752
- kw_tys.each {|a| raise if a.size != 3 } if kw_tys
753
- @kw_rest_ty = kw_rest_ty
754
- @blk_ty = blk_ty
755
- end
756
-
757
- attr_reader :lead_tys, :opt_tys, :rest_ty, :post_tys, :kw_tys, :kw_rest_ty, :blk_ty
758
-
759
- def consistent?(fargs, subst)
760
- warn "used?"
761
- return false if @lead_tys.size != fargs.lead_tys.size
762
- return false unless @lead_tys.zip(fargs.lead_tys).all? {|ty1, ty2| ty1.consistent?(ty2, subst) }
763
- return false if (@opt_tys || []) != (fargs.opt_tys || []) # ??
764
- if @rest_ty
765
- return false unless @rest_ty.consistent?(fargs.rest_ty, subst)
766
- end
767
- if @post_tys
768
- return false if @post_tys.size != fargs.post_tys.size
769
- return false unless @post_tys.zip(fargs.post_tys).all? {|ty1, ty2| ty1.consistent?(ty2, subst) }
770
- end
771
- return false if @kw_tys.size != fargs.kw_tys.size
772
- return false unless @kw_tys.zip(fargs.kw_tys).all? {|(_, ty1), (_, ty2)| ty1.consistent?(ty2, subst) }
773
- if @kw_rest_ty
774
- return false unless @kw_rest_ty.consistent?(fargs.kw_rest_ty, subst)
775
- end
776
- # intentionally skip blk_ty
777
- true
778
- end
779
-
780
751
  def screen_name(scratch)
781
- fargs = @lead_tys.map {|ty| ty.screen_name(scratch) }
752
+ str = @lead_tys.map {|ty| ty.screen_name(scratch) }
782
753
  if @opt_tys
783
- fargs += @opt_tys.map {|ty| "?" + ty.screen_name(scratch) }
754
+ str += @opt_tys.map {|ty| "?" + ty.screen_name(scratch) }
784
755
  end
785
756
  if @rest_ty
786
- fargs << ("*" + @rest_ty.screen_name(scratch))
757
+ str << ("*" + @rest_ty.screen_name(scratch))
787
758
  end
788
759
  if @post_tys
789
- fargs += @post_tys.map {|ty| ty.screen_name(scratch) }
760
+ str += @post_tys.map {|ty| ty.screen_name(scratch) }
790
761
  end
791
762
  if @kw_tys
792
763
  @kw_tys.each do |req, sym, ty|
793
764
  opt = req ? "" : "?"
794
- fargs << "#{ opt }#{ sym }: #{ ty.screen_name(scratch) }"
765
+ str << "#{ opt }#{ sym }: #{ ty.screen_name(scratch) }"
795
766
  end
796
767
  end
797
768
  if @kw_rest_ty
798
- fargs << ("**" + @kw_rest_ty.screen_name(scratch))
769
+ str << ("**" + @kw_rest_ty.screen_name(scratch))
770
+ end
771
+ str = str.empty? ? "" : "(#{ str.join(", ") })"
772
+
773
+ optional = false
774
+ blks = []
775
+ @blk_ty.each_child_global do |ty|
776
+ if ty.is_a?(Type::Proc)
777
+ blks << ty
778
+ else
779
+ # XXX: how should we handle types other than Type.nil
780
+ optional = true
781
+ end
782
+ end
783
+ if blks != []
784
+ str << " " if str != ""
785
+ str << "?" if optional
786
+ str << scratch.show_block_signature(blks)
799
787
  end
800
- # intentionally skip blk_ty
801
- fargs
788
+
789
+ str
790
+ end
791
+ end
792
+
793
+ class MethodSignature < Signature
794
+ def initialize(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, blk_ty)
795
+ @lead_tys = lead_tys
796
+ @opt_tys = opt_tys
797
+ @rest_ty = rest_ty
798
+ @post_tys = post_tys
799
+ @kw_tys = kw_tys
800
+ kw_tys.each {|a| raise if a.size != 3 } if kw_tys
801
+ @kw_rest_ty = kw_rest_ty
802
+ @blk_ty = blk_ty
803
+ end
804
+
805
+ attr_reader :lead_tys, :opt_tys, :rest_ty, :post_tys, :kw_tys, :kw_rest_ty, :blk_ty
806
+
807
+ def substitute(subst, depth)
808
+ lead_tys = @lead_tys.map {|ty| ty.substitute(subst, depth - 1) }
809
+ opt_tys = @opt_tys.map {|ty| ty.substitute(subst, depth - 1) }
810
+ rest_ty = @rest_ty&.substitute(subst, depth - 1)
811
+ post_tys = @post_tys.map {|ty| ty.substitute(subst, depth - 1) }
812
+ kw_tys = @kw_tys.map {|req, key, ty| [req, key, ty.substitute(subst, depth - 1)] }
813
+ kw_rest_ty = @kw_rest_ty&.substitute(subst, depth - 1)
814
+ blk_ty = @blk_ty.substitute(subst, depth - 1)
815
+ MethodSignature.new(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, blk_ty)
802
816
  end
803
817
 
804
818
  def merge(other)
@@ -813,8 +827,6 @@ module TypeProf
813
827
  raise if !!kws1[kw] != !!kws2[kw]
814
828
  end
815
829
  elsif @kw_tys || other.kw_tys
816
- puts
817
- p self, other
818
830
  (@kw_tys || other.kw_tys).each do |req,|
819
831
  raise if req
820
832
  end
@@ -862,231 +874,60 @@ module TypeProf
862
874
  end
863
875
  end
864
876
  blk_ty = @blk_ty.union(other.blk_ty) if @blk_ty
865
- FormalArguments.new(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, blk_ty)
877
+ MethodSignature.new(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, blk_ty)
866
878
  end
867
879
  end
868
880
 
869
- # Arguments from caller side
870
- class ActualArguments
871
- def initialize(lead_tys, rest_ty, kw_ty, blk_ty)
881
+ class BlockSignature < Signature
882
+ def initialize(lead_tys, opt_tys, rest_ty, blk_ty)
872
883
  @lead_tys = lead_tys
884
+ @opt_tys = opt_tys
873
885
  @rest_ty = rest_ty
874
- @kw_ty = kw_ty
875
886
  @blk_ty = blk_ty
876
- raise unless blk_ty
877
- end
878
-
879
- attr_reader :lead_tys, :rest_ty, :kw_ty, :blk_ty
880
-
881
- def merge(aargs)
882
- len = [@lead_tys.size, aargs.lead_tys.size].min
883
- lead_tys = @lead_tys[0, len].zip(aargs.lead_tys[0, len]).map do |ty1, ty2|
884
- ty1.union(ty2)
885
- end
886
- rest_ty = @rest_ty || Type.bot
887
- rest_ty = rest_ty.union(aargs.rest_ty) if aargs.rest_ty
888
- (@lead_tys[len..] + aargs.lead_tys[len..]).each do |ty|
889
- rest_ty = rest_ty.union(ty)
890
- end
891
- rest_ty = nil if rest_ty == Type.bot
892
- #kw_ty = @kw_ty.union(aargs.kw_ty) # TODO
893
- blk_ty = @blk_ty.union(aargs.blk_ty)
894
- ActualArguments.new(lead_tys, rest_ty, kw_ty, blk_ty)
895
- end
896
-
897
- def globalize(caller_env, visited, depth)
898
- lead_tys = @lead_tys.map {|ty| ty.globalize(caller_env, visited, depth) }
899
- rest_ty = @rest_ty.globalize(caller_env, visited, depth) if @rest_ty
900
- kw_ty = @kw_ty.globalize(caller_env, visited, depth) if @kw_ty
901
- ActualArguments.new(lead_tys, rest_ty, kw_ty, @blk_ty)
902
- end
903
-
904
- def limit_size(limit)
905
- self
906
- end
907
-
908
- def each_formal_arguments(fargs_format)
909
- lead_num = fargs_format[:lead_num] || 0
910
- post_num = fargs_format[:post_num] || 0
911
- rest_acceptable = !!fargs_format[:rest_start]
912
- keyword = fargs_format[:keyword]
913
- kw_rest_acceptable = !!fargs_format[:kwrest]
914
- opt = fargs_format[:opt]
915
- #p fargs_format
916
-
917
- # TODO: expand tuples to normal arguments
918
-
919
- # check number of arguments
920
- if !@rest_ty && lead_num + post_num > @lead_tys.size
921
- # too less
922
- yield "wrong number of arguments (given #{ @lead_tys.size }, expected #{ lead_num + post_num })"
923
- return
924
- end
925
- if !rest_acceptable
926
- # too many
927
- if opt
928
- if lead_num + post_num + opt.size - 1 < @lead_tys.size
929
- yield "wrong number of arguments (given #{ @lead_tys.size }, expected #{ lead_num + post_num }..#{ lead_num + post_num + opt.size - 1})"
930
- return
931
- end
932
- else
933
- if lead_num + post_num < @lead_tys.size
934
- yield "wrong number of arguments (given #{ @lead_tys.size }, expected #{ lead_num + post_num })"
935
- return
887
+ # TODO: kw_tys
888
+ end
889
+
890
+ attr_reader :lead_tys, :opt_tys, :rest_ty, :blk_ty
891
+
892
+ def merge(bsig)
893
+ if @rest_ty && bsig.rest_ty
894
+ rest_ty = @rest_ty.union(bsig.rest_ty)
895
+ BlockSignature.new(@lead_tys, [], rest_ty, @blk_ty.union(bsig.blk_ty))
896
+ elsif @rest_ty || bsig.rest_ty
897
+ rest_ty = @rest_ty || bsig.rest_ty
898
+ rest_ty = @opt_tys.inject(rest_ty, &:union)
899
+ rest_ty = bsig.opt_tys.inject(rest_ty, &:union)
900
+
901
+ lead_tys = []
902
+ [@lead_tys.size, bsig.lead_tys.size].max.times do |i|
903
+ ty1 = @lead_tys[i]
904
+ ty2 = bsig.lead_tys[i]
905
+ if ty1 && ty2
906
+ lead_tys << ty1.union(ty2)
907
+ else
908
+ rest_ty = rest_ty.union(ty1 || ty2)
936
909
  end
937
910
  end
938
- end
939
911
 
940
- if @rest_ty
941
- lower_bound = [lead_num + post_num - @lead_tys.size, 0].max
942
- upper_bound = lead_num + post_num - @lead_tys.size + (opt ? opt.size - 1 : 0) + (rest_acceptable ? 1 : 0)
943
- rest_elem = @rest_ty.is_a?(Type::Array) ? @rest_ty.elems.squash : Type.any
912
+ BlockSignature.new(lead_tys, [], rest_ty, @blk_ty.union(bsig.blk_ty))
944
913
  else
945
- lower_bound = upper_bound = 0
946
- end
947
-
948
- if keyword
949
- kw_tys = []
950
- keyword.each do |kw|
951
- case
952
- when kw.is_a?(Symbol) # required keyword
953
- key = kw
954
- req = true
955
- when kw.size == 2 # optional keyword (default value is a literal)
956
- key, default_ty = *kw
957
- default_ty = Type.guess_literal_type(default_ty)
958
- default_ty = default_ty.type if default_ty.is_a?(Type::Literal)
959
- req = false
960
- else # optional keyword (default value is an expression)
961
- key, = kw
962
- req = false
963
- end
964
-
965
- sym = Type::Symbol.new(key, Type::Instance.new(Type::Builtin[:sym]))
966
- ty = Type.bot
967
- if @kw_ty.is_a?(Type::Hash)
968
- # XXX: consider Union
969
- ty = @kw_ty.elems[sym]
970
- # XXX: remove the key
971
- end
972
- if ty == Type.bot
973
- yield "no argument for required keywords"
974
- return
975
- end
976
- ty = ty.union(default_ty) if default_ty
977
- kw_tys << [req, key, ty]
914
+ lead_tys = []
915
+ n = [@lead_tys.size, bsig.lead_tys.size].min
916
+ n.times do |i|
917
+ lead_tys << @lead_tys[i].union(bsig.lead_tys[i])
978
918
  end
979
- end
980
- if kw_rest_acceptable
981
- kw_rest_ty = @kw_ty
982
- if kw_rest_ty == Type.any
983
- kw_rest_ty = Type.gen_hash {|h| h[Type.any] = Type.any }
984
- end
985
- end
986
- #if @kw_ty
987
- # yield "passed a keyword to non-keyword method"
988
- #end
989
-
990
- (lower_bound .. upper_bound).each do |rest_len|
991
- aargs = @lead_tys + [rest_elem] * rest_len
992
- lead_tys = aargs.shift(lead_num)
993
- lead_tys << rest_elem until lead_tys.size == lead_num
994
- post_tys = aargs.pop(post_num)
995
- post_tys.unshift(rest_elem) until post_tys.size == post_num
996
- start_pc = 0
997
- if opt
998
- tmp_opt = opt[1..]
999
- opt_tys = []
1000
- until aargs.empty? || tmp_opt.empty?
1001
- opt_tys << aargs.shift
1002
- start_pc = tmp_opt.shift
1003
- end
1004
- end
1005
- if rest_acceptable
1006
- acc = aargs.inject {|acc, ty| acc.union(ty) }
1007
- acc = acc ? acc.union(rest_elem) : rest_elem if rest_elem
1008
- acc ||= Type.bot
1009
- rest_ty = acc
1010
- aargs.clear
1011
- end
1012
- if !aargs.empty?
1013
- yield "wrong number of arguments (given #{ @lead_tys.size }, expected #{ lead_num + post_num })"
1014
- return
1015
- end
1016
- yield FormalArguments.new(lead_tys, opt_tys, rest_ty, post_tys, kw_tys, kw_rest_ty, @blk_ty), start_pc
1017
- end
1018
- end
1019
-
1020
- def consistent_with_formal_arguments?(fargs, subst)
1021
- aargs = @lead_tys.dup
1022
-
1023
- # aargs: lead_tys, rest_ty
1024
- # fargs: lead_tys, opt_tys, rest_ty, post_tys
1025
- if @rest_ty
1026
- lower_bound = [0, fargs.lead_tys.size + fargs.post_tys.size - aargs.size].max
1027
- upper_bound = [0, lower_bound + fargs.opt_tys.size].max
1028
- (lower_bound..upper_bound).each do |n|
1029
- tmp_aargs = ActualArguments.new(@lead_tys + [@rest_ty] * n, nil, @kw_ty, @blk_ty)
1030
- if tmp_aargs.consistent_with_formal_arguments?(fargs, subst)
1031
- return true
919
+ opt_tys1 = @lead_tys[n..] + @opt_tys
920
+ opt_tys2 = bsig.lead_tys[n..] + bsig.opt_tys
921
+ opt_tys = []
922
+ [opt_tys1.size, opt_tys2.size].max.times do |i|
923
+ if opt_tys1[i] && opt_tys2[i]
924
+ opt_tys << opt_tys1[i].union(opt_tys2[i])
925
+ else
926
+ opt_tys << (opt_tys1[i] || opt_tys2[i])
1032
927
  end
1033
928
  end
1034
- return false
1035
- end
1036
-
1037
- if fargs.rest_ty
1038
- return false if aargs.size < fargs.lead_tys.size + fargs.post_tys.size
1039
- aargs.shift(fargs.lead_tys.size).zip(fargs.lead_tys) do |aarg, farg|
1040
- return false unless aarg.consistent?(farg, subst)
1041
- end
1042
- aargs.pop(fargs.post_tys.size).zip(fargs.post_tys) do |aarg, farg|
1043
- return false unless aarg.consistent?(farg, subst)
1044
- end
1045
- fargs.opt_tys.each do |farg|
1046
- aarg = aargs.shift
1047
- return false unless aarg.consistent?(farg, subst)
1048
- end
1049
- aargs.each do |aarg|
1050
- return false unless aarg.consistent?(fargs.rest_ty, subst)
1051
- end
1052
- else
1053
- return false if aargs.size < fargs.lead_tys.size + fargs.post_tys.size
1054
- return false if aargs.size > fargs.lead_tys.size + fargs.post_tys.size + fargs.opt_tys.size
1055
- aargs.shift(fargs.lead_tys.size).zip(fargs.lead_tys) do |aarg, farg|
1056
- return false unless aarg.consistent?(farg, subst)
1057
- end
1058
- aargs.pop(fargs.post_tys.size).zip(fargs.post_tys) do |aarg, farg|
1059
- return false unless aarg.consistent?(farg, subst)
1060
- end
1061
- aargs.zip(fargs.opt_tys) do |aarg, farg|
1062
- return false unless aarg.consistent?(farg, subst)
1063
- end
1064
- end
1065
- # XXX: fargs.keyword_tys
1066
-
1067
- case fargs.blk_ty
1068
- when Type::TypedProc
1069
- return false if @blk_ty == Type.nil
1070
- when Type.nil
1071
- return false if @blk_ty != Type.nil
1072
- when Type::Any
1073
- else
1074
- raise "unknown typo of formal block signature"
1075
- end
1076
- true
1077
- end
1078
-
1079
- def screen_name(scratch)
1080
- aargs = @lead_tys.map {|ty| ty.screen_name(scratch) }
1081
- if @rest_ty
1082
- aargs << ("*" + @rest_ty.screen_name(scratch))
1083
- end
1084
- if @kw_ty
1085
- aargs << ("**" + @kw_ty.screen_name(scratch)) # TODO: Hash notation -> keyword notation
929
+ BlockSignature.new(lead_tys, opt_tys, nil, @blk_ty.union(bsig.blk_ty))
1086
930
  end
1087
- s = "(#{ aargs.join(", ") })"
1088
- s << " { #{ scratch.proc_screen_name(@blk_ty) } }" if @blk_ty != Type.nil
1089
- s
1090
931
  end
1091
932
  end
1092
933
  end