typeprof 0.3.0 → 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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/doc/demo.md +398 -0
  4. data/doc/doc.ja.md +4 -0
  5. data/doc/doc.md +4 -0
  6. data/lib/typeprof.rb +8 -0
  7. data/lib/typeprof/analyzer.rb +229 -245
  8. data/lib/typeprof/arguments.rb +397 -0
  9. data/lib/typeprof/block.rb +133 -0
  10. data/lib/typeprof/builtin.rb +14 -10
  11. data/lib/typeprof/container-type.rb +94 -17
  12. data/lib/typeprof/export.rb +185 -108
  13. data/lib/typeprof/import.rb +76 -54
  14. data/lib/typeprof/iseq.rb +27 -2
  15. data/lib/typeprof/method.rb +87 -73
  16. data/lib/typeprof/type.rb +125 -309
  17. data/lib/typeprof/version.rb +1 -1
  18. data/smoke/arguments2.rb +1 -1
  19. data/smoke/array-each3.rb +1 -4
  20. data/smoke/array12.rb +1 -1
  21. data/smoke/array6.rb +1 -0
  22. data/smoke/block-ambiguous.rb +36 -0
  23. data/smoke/block-args1-rest.rb +62 -0
  24. data/smoke/block-args1.rb +59 -0
  25. data/smoke/block-args2-rest.rb +62 -0
  26. data/smoke/block-args2.rb +59 -0
  27. data/smoke/block-args3-rest.rb +73 -0
  28. data/smoke/block-args3.rb +70 -0
  29. data/smoke/block-blockarg.rb +27 -0
  30. data/smoke/block-kwarg.rb +52 -0
  31. data/smoke/block11.rb +1 -1
  32. data/smoke/block14.rb +17 -0
  33. data/smoke/block4.rb +2 -2
  34. data/smoke/block5.rb +1 -0
  35. data/smoke/block6.rb +1 -1
  36. data/smoke/block7.rb +0 -2
  37. data/smoke/block8.rb +2 -2
  38. data/smoke/block9.rb +1 -1
  39. data/smoke/blown.rb +1 -1
  40. data/smoke/class-hierarchy.rb +54 -0
  41. data/smoke/class-hierarchy2.rb +27 -0
  42. data/smoke/constant1.rb +11 -6
  43. data/smoke/constant2.rb +2 -0
  44. data/smoke/cvar.rb +1 -0
  45. data/smoke/demo10.rb +1 -1
  46. data/smoke/demo8.rb +2 -2
  47. data/smoke/demo9.rb +1 -3
  48. data/smoke/flow7.rb +1 -7
  49. data/smoke/flow8.rb +13 -0
  50. data/smoke/instance_eval.rb +1 -1
  51. data/smoke/int_times.rb +1 -1
  52. data/smoke/multiple-superclass.rb +4 -0
  53. data/smoke/next2.rb +1 -1
  54. data/smoke/optional3.rb +10 -0
  55. data/smoke/proc4.rb +1 -1
  56. data/smoke/rbs-proc1.rb +9 -0
  57. data/smoke/rbs-proc1.rbs +3 -0
  58. data/smoke/rbs-proc2.rb +20 -0
  59. data/smoke/rbs-proc2.rbs +3 -0
  60. data/smoke/rbs-proc3.rb +13 -0
  61. data/smoke/rbs-proc3.rbs +4 -0
  62. data/smoke/rbs-record.rb +17 -0
  63. data/smoke/rbs-record.rbs +4 -0
  64. data/smoke/rbs-tyvar3.rb +25 -0
  65. data/smoke/rbs-tyvar3.rbs +4 -0
  66. data/smoke/rest2.rb +1 -1
  67. data/smoke/rest5.rb +1 -1
  68. data/smoke/return.rb +1 -1
  69. data/smoke/singleton_method.rb +3 -0
  70. data/smoke/struct.rb +4 -3
  71. data/smoke/struct3.rb +14 -0
  72. data/smoke/symbol-proc.rb +24 -0
  73. metadata +31 -3
  74. data/smoke/variadic1.rb.notyet +0 -5
@@ -0,0 +1,397 @@
1
+ module TypeProf
2
+ # Arguments from caller side
3
+ class ActualArguments
4
+ def initialize(lead_tys, rest_ty, kw_tys, blk_ty)
5
+ @lead_tys = lead_tys
6
+ @rest_ty = rest_ty
7
+ @kw_tys = kw_tys # kw_tys should be {:key1 => Type, :key2 => Type, ...} or {nil => Type}
8
+ raise if !kw_tys.is_a?(::Hash)
9
+ @blk_ty = blk_ty
10
+ raise unless blk_ty
11
+ end
12
+
13
+ attr_reader :lead_tys, :rest_ty, :kw_tys, :blk_ty
14
+
15
+ def globalize(caller_env, visited, depth)
16
+ lead_tys = @lead_tys.map {|ty| ty.globalize(caller_env, visited, depth) }
17
+ rest_ty = @rest_ty.globalize(caller_env, visited, depth) if @rest_ty
18
+ kw_tys = @kw_tys.to_h do |key, ty|
19
+ [key, ty.globalize(caller_env, visited, depth)]
20
+ end
21
+ ActualArguments.new(lead_tys, rest_ty, kw_tys, @blk_ty)
22
+ end
23
+
24
+ def limit_size(limit)
25
+ self
26
+ end
27
+
28
+ def consistent_with_method_signature?(msig, subst)
29
+ aargs = @lead_tys.dup
30
+
31
+ # aargs: lead_tys, rest_ty
32
+ # msig: lead_tys, opt_tys, rest_ty, post_tys
33
+ if @rest_ty
34
+ lower_bound = [0, msig.lead_tys.size + msig.post_tys.size - aargs.size].max
35
+ upper_bound = [0, lower_bound + msig.opt_tys.size].max
36
+ (lower_bound..upper_bound).each do |n|
37
+ # BUG: @rest_ty is an Array, so need to squash
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
42
+ end
43
+ return false
44
+ end
45
+
46
+ if msig.rest_ty
47
+ return false if aargs.size < msig.lead_tys.size + msig.post_tys.size
48
+ aargs.shift(msig.lead_tys.size).zip(msig.lead_tys) do |aarg, farg|
49
+ return false unless aarg.consistent?(farg, subst)
50
+ end
51
+ aargs.pop(msig.post_tys.size).zip(msig.post_tys) do |aarg, farg|
52
+ return false unless aarg.consistent?(farg, subst)
53
+ end
54
+ msig.opt_tys.each do |farg|
55
+ aarg = aargs.shift
56
+ return false unless aarg.consistent?(farg, subst)
57
+ end
58
+ aargs.each do |aarg|
59
+ return false unless aarg.consistent?(msig.rest_ty, subst)
60
+ end
61
+ 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
64
+ aargs.shift(msig.lead_tys.size).zip(msig.lead_tys) do |aarg, farg|
65
+ return false unless aarg.consistent?(farg, subst)
66
+ end
67
+ aargs.pop(msig.post_tys.size).zip(msig.post_tys) do |aarg, farg|
68
+ return false unless aarg.consistent?(farg, subst)
69
+ end
70
+ aargs.zip(msig.opt_tys) do |aarg, farg|
71
+ return false unless aarg.consistent?(farg, subst)
72
+ end
73
+ end
74
+ # XXX: msig.keyword_tys
75
+
76
+ case msig.blk_ty
77
+ when Type::Proc
78
+ return false if @blk_ty == Type.nil
79
+ when Type.nil
80
+ return false if @blk_ty != Type.nil
81
+ when Type::Any
82
+ else
83
+ raise "unknown type of formal block signature"
84
+ end
85
+ true
86
+ end
87
+
88
+ def argument_error(given, exp_lower, exp_upper)
89
+ case
90
+ when !exp_upper then exp = "#{ exp_lower }+"
91
+ when exp_lower == exp_upper then exp = "#{ exp_lower }"
92
+ else exp = "#{ exp_lower }..#{ exp_upper }"
93
+ end
94
+ "wrong number of arguments (given #{ given }, expected #{ exp })"
95
+ end
96
+
97
+ def to_block_signature
98
+ if @rest_ty
99
+ rest_ty = Type.bot
100
+ @rest_ty.each_child_global do |ty|
101
+ if ty.is_a?(Type::Array)
102
+ rest_ty = rest_ty.union(ty.elems.squash)
103
+ else
104
+ # XXX: to_ary?
105
+ rest_ty = rest_ty.union(ty)
106
+ end
107
+ end
108
+ end
109
+ BlockSignature.new(@lead_tys, [], rest_ty, @blk_ty)
110
+ end
111
+
112
+ def setup_formal_arguments(kind, locals, fargs_format)
113
+ lead_num = fargs_format[:lead_num] || 0
114
+ post_num = fargs_format[:post_num] || 0
115
+ post_index = fargs_format[:post_start]
116
+ rest_index = fargs_format[:rest_start]
117
+ keyword = fargs_format[:keyword]
118
+ kw_index = fargs_format[:kwbits] - keyword.size if keyword
119
+ kwrest_index = fargs_format[:kwrest]
120
+ block_index = fargs_format[:block_start]
121
+ opt = fargs_format[:opt] || [0]
122
+ ambiguous_param0 = fargs_format[:ambiguous_param0]
123
+
124
+ lead_tys = @lead_tys
125
+ rest_ty = @rest_ty
126
+
127
+ if kind == :block
128
+ # The rule of passing arguments to block:
129
+ #
130
+ # Let A is actual arguments and F is formal arguments.
131
+ # If F is NOT ambiguous_param0, and if length(A) == 1, and if A[0] is an Array,
132
+ # then replace A with A[0]. And then, F[i] = A[i] for all 0 <= i < length(F).
133
+
134
+ # Handling the special case
135
+ if !ambiguous_param0
136
+ if lead_tys.size == 1 && !rest_ty && @kw_tys.empty? # length(A) == 1
137
+ ty = lead_tys[0]
138
+ case ty
139
+ when Type::Array
140
+ lead_tys = ty.elems.lead_tys
141
+ rest_ty = ty.elems.rest_ty
142
+ when Type::Union
143
+ other_elems = nil
144
+ ty.elems&.each do |(container_kind, base_type), elems|
145
+ if container_kind == Type::Array
146
+ rest_ty = rest_ty ? rest_ty.union(elems.squash) : elems.squash
147
+ else
148
+ other_elems = other_elems ? other_elems.union(elems) : elems
149
+ end
150
+ end
151
+ lead_tys = [Type::Union.new(ty.types, other_elems)]
152
+ end
153
+ end
154
+ end
155
+ end
156
+
157
+ # Normal case: copy actual args to formal args
158
+ if rest_ty
159
+ ty = Type.bot
160
+ additional_lead_size = nil
161
+ rest_ty.each_child_global do |ty0|
162
+ if ty0.is_a?(Type::Array)
163
+ additional_lead_size = [additional_lead_size, ty0.elems.lead_tys.size].compact.min
164
+ else
165
+ additional_lead_size = 0
166
+ end
167
+ end
168
+ additional_lead_tys = [Type.bot] * (additional_lead_size || 0)
169
+ rest_ty.each_child_global do |ty0|
170
+ if ty0.is_a?(Type::Array)
171
+ tys, new_rest_ty = ty0.elems.take_first(additional_lead_size)
172
+ tys.each_with_index do |ty00, i|
173
+ additional_lead_tys[i] = additional_lead_tys[i].union(ty00)
174
+ end
175
+ ty = ty.union(new_rest_ty.elems.squash)
176
+ else
177
+ # XXX: to_ary?
178
+ ty = ty.union(ty0)
179
+ end
180
+ end
181
+ lead_tys += additional_lead_tys
182
+ rest_ty = ty
183
+
184
+ # XXX: Strictly speaking, this is needed, but it brings false positives. Which is better?
185
+ #rest_ty = rest_ty.union(Type.nil)
186
+
187
+ if rest_index
188
+ # foo(a0, a1, a2, ...(rest_ty)) -->
189
+ # def foo(l0, l1, o0=, o1=, *rest, p0, p1)
190
+ # lead_ty argc == 0: - - - - - - -
191
+ # lead_ty argc == 1: a0 - - - - - -
192
+ # lead_ty argc == 2: a0 a1 - - - - -
193
+ # lead_ty argc == 3: a0 a1 - - - a2 -
194
+ # lead_ty argc == 4: a0 a1 - - - a2 a3
195
+ # lead_ty argc == 5: a0 a1 a2 - - a3 a4
196
+ # lead_ty argc == 6: a0 a1 a2 a3 - a4 a5
197
+ # lead_ty argc == 7: a0 a1 a2 a3 a4 a5 a6
198
+ # lead_ty argc == 8: a0 a1 a2 a3 a4|a5 a6 a7
199
+ #
200
+ # l0 = a0
201
+ # l1 = a1
202
+ # o0 = a2
203
+ # o1 = a3
204
+ # rest = a4|a5|...|rest_ty (= cum_lead_tys[4])
205
+ # p0 = a2|a3|...|rest_ty (= cum_lead_tys[2])
206
+ # p1 = a3|a4|...|rest_ty (= cum_lead_tys[3])
207
+
208
+ cum_lead_tys = []
209
+ ty = rest_ty
210
+ lead_tys.reverse_each do |ty0|
211
+ cum_lead_tys.unshift(ty = ty.union(ty0))
212
+ end
213
+
214
+ # l1, l2, o1, o2
215
+ (lead_num + opt.size - 1).times {|i| locals[i] = lead_tys[i] || rest_ty }
216
+ opt_count = opt.size - 1
217
+
218
+ # rest
219
+ ty = cum_lead_tys[lead_num + opt.size - 1] || rest_ty
220
+ locals[rest_index] = Type::Array.new(Type::Array::Elements.new([], ty), Type::Instance.new(Type::Builtin[:ary]))
221
+
222
+ # p0, p1
223
+ off = [lead_num, lead_tys.size - post_num].max
224
+ post_num.times {|i| locals[post_index + i] = cum_lead_tys[off + i] || rest_ty }
225
+ else
226
+ # foo(a0, a1, a2, ...(rest_ty)) -->
227
+ # def foo(l0, l1, o0=, o1=, p0, p1)
228
+ # lead_ty argc == 0: - - - - - -
229
+ # lead_ty argc == 1: a0 - - - - -
230
+ # lead_ty argc == 2: a0 a1 - - - -
231
+ # lead_ty argc == 3: a0 a1 - - a2 -
232
+ # lead_ty argc == 4: a0 a1 - - a2 a3
233
+ # lead_ty argc == 5: a0 a1 a2 - a3 a4
234
+ # lead_ty argc == 6: a0 a1 a2 a3 a4 a5
235
+ # lead_ty argc == 7: a0 a1 a2 a3 a4 a5 (if there is a6, report error if kind is method, or ignore if kind is block)
236
+ #
237
+ # l0 = a0
238
+ # l1 = a1
239
+ # o0 = a2
240
+ # o1 = a3
241
+ # p0 = a2|a3|a4
242
+ # p1 = a3|a4|a5
243
+
244
+ if kind == :method && lead_num + opt.size - 1 + post_num < lead_tys.size
245
+ return argument_error(lead_tys.size, lead_num + post_num, lead_num + post_num + opt.size - 1)
246
+ end
247
+
248
+ # l1, l2, o1, o2
249
+ (lead_num + opt.size - 1).times {|i| locals[i] = lead_tys[i] || rest_ty }
250
+ opt_count = opt.size - 1
251
+
252
+ # p0, p1
253
+ post_num.times do |i|
254
+ candidates = lead_tys[lead_num, opt.size] || []
255
+ candidates << rest_ty if candidates.size < opt.size
256
+ locals[post_index + i] = candidates.inject(&:union)
257
+ end
258
+ end
259
+ else
260
+ if rest_index
261
+ # foo(a0, a1, a2) -->
262
+ # def foo(l0, l1, o0=, o1=, *rest, p0, p1)
263
+ # lead_ty argc == 0: - - - - - - - (error if kind is method)
264
+ # lead_ty argc == 1: a0 - - - - - - (error if kind is method)
265
+ # lead_ty argc == 2: a0 a1 - - - - - (error if kind is method)
266
+ # lead_ty argc == 3: a0 a1 - - - a2 - (error if kind is method)
267
+ # lead_ty argc == 4: a0 a1 - - - a2 a3
268
+ # lead_ty argc == 5: a0 a1 a2 - - a3 a4
269
+ # lead_ty argc == 6: a0 a1 a2 a3 - a4 a5
270
+ # lead_ty argc == 7: a0 a1 a2 a3 a4 a5 a6
271
+ # lead_ty argc == 8: a0 a1 a2 a3 a4|a5 a6 a7
272
+ #
273
+ # len(a) < 4 -> error
274
+ #
275
+ # l0 = a0
276
+ # l1 = a1
277
+ # o0 = a2
278
+ # o1 = a3
279
+ # rest = a4|a5|...|a[[4,len(a)-3].max]
280
+ # p0 = a[[2,len(a)-2].max]
281
+ # p1 = a[[3,len(a)-1].max]
282
+
283
+ if kind == :method && lead_tys.size < lead_num + post_num
284
+ return argument_error(lead_tys.size, lead_num + post_num, nil)
285
+ end
286
+
287
+ # l0, l1
288
+ lead_num.times {|i| locals[i] = lead_tys[i] || Type.nil }
289
+
290
+ # o0, o1
291
+ opt_count = (lead_tys.size - lead_num - post_num).clamp(0, opt.size - 1)
292
+ (opt.size - 1).times {|i| locals[lead_num + i] = i < opt_count ? lead_tys[lead_num + i] : Type.nil }
293
+
294
+ # rest
295
+ rest_b = lead_num + opt_count
296
+ rest_e = [0, lead_tys.size - post_num].max
297
+ locals[rest_index] = Type::Array.new(Type::Array::Elements.new(lead_tys[rest_b...rest_e] || [], Type.bot), Type::Instance.new(Type::Builtin[:ary]))
298
+
299
+ # p0, p1
300
+ off = [lead_num, lead_tys.size - post_num].max
301
+ post_num.times {|i| locals[post_index + i] = lead_tys[off + i] || Type.nil }
302
+ else
303
+ # yield a0, a1, a2 -->
304
+ # do |l0, l1, o0=, o1=, p0, p1|
305
+ # lead_ty argc == 0: - - - - - - (error if kind is method)
306
+ # lead_ty argc == 1: a0 - - - - - (error if kind is method)
307
+ # lead_ty argc == 2: a0 a1 - - - - (error if kind is method)
308
+ # lead_ty argc == 3: a0 a1 - - a2 - (error if kind is method)
309
+ # lead_ty argc == 4: a0 a1 - - a2 a3
310
+ # lead_ty argc == 5: a0 a1 a2 - a3 a4
311
+ # lead_ty argc == 6: a0 a1 a2 a3 a4 a5
312
+ # lead_ty argc == 7: a0 a1 a2 a3 a4 a5 (if there is a6, report error if kind is method, or ignore if kind is block)
313
+ #
314
+ # l0 = a0
315
+ # l1 = a1
316
+ # o0 = a2
317
+ # o1 = a3
318
+ # p0 = a2|a3|a4
319
+ # p1 = a3|a4|a5
320
+
321
+ if kind == :method && (lead_tys.size < lead_num + post_num || lead_num + opt.size - 1 + post_num < lead_tys.size)
322
+ return argument_error(lead_tys.size, lead_num + post_num, lead_num + post_num + opt.size - 1)
323
+ end
324
+
325
+ # l0, l1
326
+ lead_num.times {|i| locals[i] = lead_tys[i] || Type.nil }
327
+
328
+ # o0, o1
329
+ opt_count = (lead_tys.size - lead_num - post_num).clamp(0, opt.size - 1)
330
+ (opt.size - 1).times {|i| locals[lead_num + i] = i < opt_count ? lead_tys[lead_num + i] : Type.nil }
331
+
332
+ # p0, p1
333
+ off = lead_num + opt_count
334
+ post_num.times {|i| locals[post_index + i] = lead_tys[off + i] || Type.nil }
335
+ end
336
+ end
337
+
338
+ kw_tys = @kw_tys.dup
339
+ if keyword
340
+ keyword.each_with_index do |kw, i|
341
+ case
342
+ when kw.is_a?(Symbol) # required keyword
343
+ key = kw
344
+ req = true
345
+ when kw.size == 2 # optional keyword (default value is a literal)
346
+ key, default_ty = *kw
347
+ default_ty = Type.guess_literal_type(default_ty)
348
+ default_ty = default_ty.type if default_ty.is_a?(Type::Literal)
349
+ req = false
350
+ else # optional keyword (default value is an expression)
351
+ key, = kw
352
+ req = false
353
+ end
354
+
355
+ if kw_tys.key?(key)
356
+ ty = kw_tys.delete(key)
357
+ else
358
+ ty = kw_tys[nil] || Type.bot
359
+ end
360
+
361
+ if ty == Type.bot && req
362
+ return "no argument for required keywords"
363
+ end
364
+
365
+ ty = ty.union(default_ty) if default_ty
366
+ locals[kw_index + i] = ty
367
+ end
368
+ end
369
+
370
+ if kwrest_index
371
+ if kw_tys.key?(nil)
372
+ kw_rest_ty = Type.gen_hash {|h| h[Type.any] = kw_tys[nil] }
373
+ else
374
+ kw_rest_ty = Type.gen_hash do |h|
375
+ kw_tys.each do |key, ty|
376
+ sym = Type::Symbol.new(key, Type::Instance.new(Type::Builtin[:sym]))
377
+ h[sym] = ty
378
+ end
379
+ end
380
+ end
381
+ locals[kwrest_index] = kw_rest_ty
382
+ else
383
+ if !kw_tys.empty?
384
+ return "unknown keyword: #{ kw_tys.keys.join(", ") }"
385
+ end
386
+ end
387
+
388
+ if block_index
389
+ locals[block_index] = @blk_ty
390
+ end
391
+
392
+ start_pcs = opt[0..opt_count]
393
+
394
+ return @blk_ty, start_pcs
395
+ end
396
+ end
397
+ end
@@ -0,0 +1,133 @@
1
+ module TypeProf
2
+ class Block
3
+ include Utils::StructuralEquality
4
+ end
5
+
6
+ class ISeqBlock < Block
7
+ def initialize(iseq, ep)
8
+ @iseq = iseq
9
+ @outer_ep = ep
10
+ end
11
+
12
+ attr_reader :iseq, :outer_ep
13
+
14
+ def inspect
15
+ "#<ISeqBlock: #{ @outer_ep.source_location }>"
16
+ end
17
+
18
+ def consistent?(other)
19
+ if other.is_a?(ISeqBlock)
20
+ self == other
21
+ else
22
+ true # XXX
23
+ end
24
+ end
25
+
26
+ def substitute(_subst, _depth)
27
+ self
28
+ end
29
+
30
+ def do_call(aargs, caller_ep, caller_env, scratch, replace_recv_ty:, &ctn)
31
+ blk_env = scratch.return_envs[@outer_ep]
32
+ blk_env = blk_env.replace_recv_ty(replace_recv_ty) if replace_recv_ty
33
+ aargs = scratch.globalize_type(aargs, caller_env, caller_ep)
34
+
35
+ scratch.add_block_signature!(self, aargs.to_block_signature)
36
+
37
+ locals = [Type.nil] * @iseq.locals.size
38
+
39
+ blk_ty, start_pcs = aargs.setup_formal_arguments(:block, locals, @iseq.fargs_format)
40
+ if blk_ty.is_a?(String)
41
+ scratch.error(caller_ep, blk_ty)
42
+ ctn[Type.any, caller_ep, caller_env]
43
+ return
44
+ end
45
+
46
+ nctx = Context.new(@iseq, @outer_ep.ctx.cref, nil)
47
+ callee_ep = ExecutionPoint.new(nctx, 0, @outer_ep)
48
+ nenv = Env.new(blk_env.static_env, locals, [], nil)
49
+ alloc_site = AllocationSite.new(callee_ep)
50
+ locals.each_with_index do |ty, i|
51
+ alloc_site2 = alloc_site.add_id(i)
52
+ nenv, ty = scratch.localize_type(ty, nenv, callee_ep, alloc_site2)
53
+ nenv = nenv.local_update(i, ty)
54
+ end
55
+
56
+ start_pcs.each do |start_pc|
57
+ scratch.merge_env(ExecutionPoint.new(nctx, start_pc, @outer_ep), nenv)
58
+ end
59
+
60
+ scratch.add_block_to_ctx!(self, callee_ep.ctx)
61
+ scratch.add_callsite!(callee_ep.ctx, caller_ep, caller_env, &ctn)
62
+ end
63
+ end
64
+
65
+ class TypedBlock < Block
66
+ def initialize(msig, ret_ty)
67
+ @msig = msig
68
+ @ret_ty = ret_ty
69
+ end
70
+
71
+ attr_reader :msig, :ret_ty
72
+
73
+ def consistent?(other)
74
+ if other.is_a?(ISeqBlock)
75
+ raise "assert false"
76
+ else
77
+ self == other
78
+ end
79
+ end
80
+
81
+ def substitute(subst, depth)
82
+ msig = @msig.substitute(subst, depth)
83
+ ret_ty = @ret_ty.substitute(subst, depth)
84
+ TypedBlock.new(msig, ret_ty)
85
+ end
86
+
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)
90
+ scratch.warn(caller_ep, "The arguments is not compatibile to RBS block")
91
+ end
92
+ # XXX: Update type vars
93
+ ctn[@ret_ty, caller_ep, caller_env]
94
+ end
95
+ end
96
+
97
+ class SymbolBlock < Block
98
+ def initialize(sym)
99
+ @sym = sym
100
+ end
101
+
102
+ attr_reader :iseq, :outer_ep
103
+
104
+ def inspect
105
+ "#<SymbolBlock: #{ @sym }>"
106
+ end
107
+
108
+ def consistent?(other)
109
+ true # XXX
110
+ end
111
+
112
+ def substitute(_subst, _depth)
113
+ self
114
+ end
115
+
116
+ def do_call(aargs, caller_ep, caller_env, scratch, replace_recv_ty:, &ctn)
117
+ if aargs.lead_tys.size >= 1
118
+ recv = aargs.lead_tys[0]
119
+ aargs = ActualArguments.new(aargs.lead_tys[1..], aargs.rest_ty, aargs.kw_tys, aargs.blk_ty)
120
+ elsif aargs.rest_ty
121
+ recv = aargs.rest_ty.elems.squash_or_any # XXX: need to shift
122
+ else
123
+ raise
124
+ end
125
+
126
+ scratch.add_block_signature!(self, aargs.to_block_signature)
127
+
128
+ recv.each_child do |recv|
129
+ scratch.do_send(recv, @sym, aargs, caller_ep, caller_env, &ctn)
130
+ end
131
+ end
132
+ end
133
+ end