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