typeprof 0.5.3 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (208) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +6 -4
  3. data/doc/doc.ja.md +3 -4
  4. data/doc/doc.md +3 -4
  5. data/lib/typeprof/analyzer.rb +248 -166
  6. data/lib/typeprof/arguments.rb +12 -6
  7. data/lib/typeprof/builtin.rb +123 -23
  8. data/lib/typeprof/cli.rb +33 -34
  9. data/lib/typeprof/config.rb +6 -4
  10. data/lib/typeprof/container-type.rb +175 -112
  11. data/lib/typeprof/export.rb +23 -17
  12. data/lib/typeprof/import.rb +58 -53
  13. data/lib/typeprof/method.rb +59 -125
  14. data/lib/typeprof/type.rb +26 -14
  15. data/lib/typeprof/version.rb +1 -1
  16. data/smoke/alias.rb +1 -0
  17. data/smoke/any1.rb +1 -0
  18. data/smoke/any2.rb +1 -0
  19. data/smoke/arguments.rb +1 -0
  20. data/smoke/arguments2.rb +1 -0
  21. data/smoke/array-each.rb +1 -0
  22. data/smoke/array-each2.rb +1 -0
  23. data/smoke/array-each3.rb +2 -1
  24. data/smoke/array-ltlt.rb +1 -0
  25. data/smoke/array-ltlt2.rb +1 -0
  26. data/smoke/array-map.rb +1 -0
  27. data/smoke/array-map2.rb +1 -0
  28. data/smoke/array-map3.rb +1 -0
  29. data/smoke/array-mul.rb +1 -0
  30. data/smoke/array-plus1.rb +1 -0
  31. data/smoke/array-pop.rb +1 -0
  32. data/smoke/array-range-aref.rb +71 -0
  33. data/smoke/array-replace.rb +1 -0
  34. data/smoke/array-s-aref.rb +1 -0
  35. data/smoke/array1.rb +1 -0
  36. data/smoke/array10.rb +1 -0
  37. data/smoke/array11.rb +2 -1
  38. data/smoke/array12.rb +1 -0
  39. data/smoke/array13.rb +1 -0
  40. data/smoke/array14.rb +1 -0
  41. data/smoke/array2.rb +1 -0
  42. data/smoke/array4.rb +1 -0
  43. data/smoke/array5.rb +1 -0
  44. data/smoke/array6.rb +3 -2
  45. data/smoke/array7.rb +1 -0
  46. data/smoke/array8.rb +1 -1
  47. data/smoke/array9.rb +1 -0
  48. data/smoke/autoload.rb +14 -0
  49. data/smoke/backtrace.rb +1 -0
  50. data/smoke/block-ambiguous.rb +1 -0
  51. data/smoke/block-args1-rest.rb +1 -0
  52. data/smoke/block-args1.rb +1 -0
  53. data/smoke/block-args2-rest.rb +1 -0
  54. data/smoke/block-args2.rb +4 -3
  55. data/smoke/block-args3-rest.rb +1 -0
  56. data/smoke/block-args3.rb +5 -4
  57. data/smoke/block-blockarg.rb +2 -1
  58. data/smoke/block-kwarg.rb +1 -0
  59. data/smoke/block1.rb +1 -0
  60. data/smoke/block10.rb +1 -0
  61. data/smoke/block11.rb +1 -0
  62. data/smoke/block12.rb +1 -0
  63. data/smoke/block14.rb +1 -0
  64. data/smoke/block2.rb +1 -0
  65. data/smoke/block4.rb +1 -0
  66. data/smoke/block5.rb +2 -1
  67. data/smoke/block6.rb +1 -0
  68. data/smoke/block7.rb +1 -0
  69. data/smoke/block8.rb +1 -0
  70. data/smoke/block9.rb +1 -0
  71. data/smoke/blown.rb +1 -0
  72. data/smoke/break1.rb +1 -0
  73. data/smoke/break2.rb +1 -0
  74. data/smoke/case.rb +1 -0
  75. data/smoke/case2.rb +1 -0
  76. data/smoke/class_method3.rb +2 -0
  77. data/smoke/constant2.rb +2 -2
  78. data/smoke/constant3.rb +1 -0
  79. data/smoke/constant4.rb +1 -0
  80. data/smoke/context-sensitive1.rb +1 -0
  81. data/smoke/cvar.rb +1 -0
  82. data/smoke/define_method.rb +16 -0
  83. data/smoke/define_method2.rb +18 -0
  84. data/smoke/demo.rb +1 -0
  85. data/smoke/demo1.rb +1 -0
  86. data/smoke/demo10.rb +1 -0
  87. data/smoke/demo11.rb +1 -0
  88. data/smoke/demo2.rb +1 -0
  89. data/smoke/demo3.rb +1 -0
  90. data/smoke/demo5.rb +1 -1
  91. data/smoke/demo7.rb +1 -0
  92. data/smoke/demo8.rb +1 -0
  93. data/smoke/demo9.rb +2 -1
  94. data/smoke/dummy-execution1.rb +1 -0
  95. data/smoke/ensure1.rb +1 -0
  96. data/smoke/enumerator.rb +1 -0
  97. data/smoke/expandarray1.rb +1 -0
  98. data/smoke/expandarray2.rb +1 -0
  99. data/smoke/flow1.rb +1 -0
  100. data/smoke/flow2.rb +1 -0
  101. data/smoke/flow3.rb +1 -0
  102. data/smoke/flow5.rb +1 -0
  103. data/smoke/flow6.rb +1 -0
  104. data/smoke/flow7.rb +1 -0
  105. data/smoke/flow8.rb +1 -0
  106. data/smoke/freeze.rb +1 -0
  107. data/smoke/function.rb +1 -0
  108. data/smoke/gvar.rb +1 -0
  109. data/smoke/gvar2.rb +1 -0
  110. data/smoke/hash-fetch.rb +1 -0
  111. data/smoke/hash-merge-bang.rb +1 -0
  112. data/smoke/hash1.rb +3 -1
  113. data/smoke/hash2.rb +1 -0
  114. data/smoke/hash3.rb +1 -0
  115. data/smoke/hash4.rb +2 -1
  116. data/smoke/inheritance2.rb +2 -2
  117. data/smoke/initialize.rb +1 -0
  118. data/smoke/int_times.rb +1 -0
  119. data/smoke/integer.rb +1 -0
  120. data/smoke/ivar.rb +1 -0
  121. data/smoke/ivar2.rb +1 -1
  122. data/smoke/kernel-class.rb +2 -1
  123. data/smoke/keyword1.rb +1 -0
  124. data/smoke/keyword2.rb +1 -0
  125. data/smoke/keyword3.rb +1 -0
  126. data/smoke/keyword4.rb +1 -0
  127. data/smoke/keyword5.rb +1 -0
  128. data/smoke/kwrest.rb +12 -0
  129. data/smoke/kwrest.rbs +3 -0
  130. data/smoke/kwsplat1.rb +2 -1
  131. data/smoke/kwsplat2.rb +1 -0
  132. data/smoke/manual-rbs.rb +1 -0
  133. data/smoke/manual-rbs2.rb +1 -0
  134. data/smoke/masgn1.rb +1 -0
  135. data/smoke/masgn2.rb +1 -0
  136. data/smoke/masgn3.rb +1 -0
  137. data/smoke/method_in_branch.rb +1 -0
  138. data/smoke/method_missing.rb +28 -0
  139. data/smoke/multiple-superclass.rb +1 -1
  140. data/smoke/next1.rb +1 -0
  141. data/smoke/next2.rb +1 -0
  142. data/smoke/object-send1.rb +1 -0
  143. data/smoke/once.rb +1 -0
  144. data/smoke/optional1.rb +1 -0
  145. data/smoke/optional2.rb +1 -0
  146. data/smoke/optional3.rb +1 -0
  147. data/smoke/parameterizedd-self.rb +3 -2
  148. data/smoke/parameterizedd-self2.rb +15 -0
  149. data/smoke/pathname1.rb +1 -0
  150. data/smoke/pathname2.rb +1 -0
  151. data/smoke/pattern-match1.rb +1 -0
  152. data/smoke/pattern-match2.rb +1 -0
  153. data/smoke/proc.rb +1 -0
  154. data/smoke/proc2.rb +1 -0
  155. data/smoke/proc3.rb +1 -0
  156. data/smoke/proc4.rb +1 -0
  157. data/smoke/range.rb +1 -0
  158. data/smoke/rbs-alias.rb +1 -0
  159. data/smoke/rbs-attr.rb +3 -2
  160. data/smoke/rbs-attr2.rb +11 -0
  161. data/smoke/rbs-attr2.rbs +3 -0
  162. data/smoke/rbs-extend.rb +1 -0
  163. data/smoke/rbs-interface.rb +1 -0
  164. data/smoke/rbs-proc1.rb +1 -0
  165. data/smoke/rbs-proc2.rb +1 -0
  166. data/smoke/rbs-proc3.rb +1 -0
  167. data/smoke/rbs-record.rb +1 -0
  168. data/smoke/rbs-tyvar.rb +1 -0
  169. data/smoke/rbs-tyvar2.rb +1 -0
  170. data/smoke/rbs-tyvar3.rb +1 -0
  171. data/smoke/rbs-tyvar5.rb +1 -0
  172. data/smoke/rbs-tyvar6.rb +18 -0
  173. data/smoke/rbs-tyvar6.rbs +12 -0
  174. data/smoke/rbs-tyvar7.rb +12 -0
  175. data/smoke/rbs-tyvar7.rbs +7 -0
  176. data/smoke/rbs-vars.rb +1 -2
  177. data/smoke/redo1.rb +1 -0
  178. data/smoke/redo2.rb +1 -0
  179. data/smoke/req-keyword.rb +1 -0
  180. data/smoke/rescue1.rb +1 -0
  181. data/smoke/rescue2.rb +1 -0
  182. data/smoke/respond_to.rb +1 -0
  183. data/smoke/rest-farg.rb +1 -0
  184. data/smoke/rest1.rb +1 -0
  185. data/smoke/rest2.rb +1 -0
  186. data/smoke/rest3.rb +1 -0
  187. data/smoke/rest4.rb +1 -0
  188. data/smoke/rest5.rb +1 -0
  189. data/smoke/rest6.rb +1 -0
  190. data/smoke/retry1.rb +1 -0
  191. data/smoke/return.rb +1 -0
  192. data/smoke/step.rb +1 -0
  193. data/smoke/string-split.rb +1 -0
  194. data/smoke/struct.rb +2 -2
  195. data/smoke/struct2.rb +1 -0
  196. data/smoke/super1.rb +1 -0
  197. data/smoke/super4.rb +43 -0
  198. data/smoke/super5.rb +36 -0
  199. data/smoke/svar1.rb +1 -0
  200. data/smoke/symbol-proc.rb +1 -0
  201. data/smoke/tap1.rb +1 -0
  202. data/smoke/toplevel.rb +1 -0
  203. data/smoke/two-map.rb +1 -0
  204. data/smoke/type_var.rb +1 -0
  205. data/smoke/typed_method.rb +1 -0
  206. data/smoke/uninitialize-var.rb +1 -0
  207. data/smoke/union-recv.rb +2 -2
  208. metadata +18 -2
@@ -10,6 +10,10 @@ module TypeProf
10
10
  raise unless blk_ty
11
11
  end
12
12
 
13
+ def for_method_missing(mid)
14
+ ActualArguments.new([mid] + @lead_tys, @rest_ty, @kw_tys, @blk_ty)
15
+ end
16
+
13
17
  attr_reader :lead_tys, :rest_ty, :kw_tys, :blk_ty
14
18
 
15
19
  def globalize(caller_env, visited, depth)
@@ -148,12 +152,14 @@ module TypeProf
148
152
  lead_tys = ty.elems.lead_tys
149
153
  rest_ty = ty.elems.rest_ty
150
154
  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
155
+ if ty.elems
156
+ other_elems = {}
157
+ ty.elems.each do |(container_kind, base_type), elems|
158
+ if container_kind == Type::Array
159
+ rest_ty = rest_ty ? rest_ty.union(elems.squash) : elems.squash
160
+ else
161
+ other_elems[[container_kind, base_type]] = elems
162
+ end
157
163
  end
158
164
  end
159
165
  lead_tys = [Type::Union.new(ty.types, other_elems)]
@@ -33,10 +33,10 @@ module TypeProf
33
33
  h2 = aargs.lead_tys[1]
34
34
  elems = nil
35
35
  h1.each_child do |h1|
36
- if h1.is_a?(Type::LocalHash)
36
+ if h1.is_a?(Type::Local) && h1.kind == Type::Hash
37
37
  h1_elems = scratch.get_container_elem_types(env, ep, h1.id)
38
38
  h2.each_child do |h2|
39
- if h2.is_a?(Type::LocalHash)
39
+ if h2.is_a?(Type::Local) && h2.kind == Type::Hash
40
40
  h2_elems = scratch.get_container_elem_types(env, ep, h2.id)
41
41
  elems0 = h1_elems.union(h2_elems)
42
42
  if elems
@@ -100,7 +100,8 @@ module TypeProf
100
100
  raise unless aargs.lead_tys.size != 0
101
101
  sym = get_sym("respond_to?", aargs.lead_tys[0], ep, scratch)
102
102
  if sym
103
- if recv.get_method(sym, scratch)
103
+ klass, singleton = recv.method_dispatch_info
104
+ if scratch.get_method(klass, singleton, sym)
104
105
  true_val = Type::Instance.new(Type::Builtin[:true])
105
106
  ctn[true_val, ep, env]
106
107
  else
@@ -142,8 +143,8 @@ module TypeProf
142
143
 
143
144
  def object_instance_eval(recv, mid, aargs, ep, env, scratch, &ctn)
144
145
  if aargs.lead_tys.size >= 1
145
- scratch.warn(ep, "instance_eval with arguments are ignored")
146
- ctn[type.any, ep, env]
146
+ scratch.warn(ep, "instance_eval with arguments is ignored")
147
+ ctn[Type.any, ep, env]
147
148
  return
148
149
  end
149
150
  naargs = ActualArguments.new([recv], nil, {}, Type.nil)
@@ -153,20 +154,30 @@ module TypeProf
153
154
  end
154
155
 
155
156
  def module_include(recv, mid, aargs, ep, env, scratch, &ctn)
157
+ if aargs.lead_tys.size != 1
158
+ scratch.warn(ep, "Module#include without an argument is ignored")
159
+ ctn[Type.any, ep, env]
160
+ return
161
+ end
156
162
  arg = aargs.lead_tys[0]
157
163
  arg.each_child do |arg|
158
164
  if arg.is_a?(Type::Class)
159
- scratch.include_module(recv, arg, false, ep.ctx.iseq.absolute_path)
165
+ scratch.include_module(recv, arg, nil, false, ep.ctx.iseq.absolute_path)
160
166
  end
161
167
  end
162
168
  ctn[recv, ep, env]
163
169
  end
164
170
 
165
171
  def module_extend(recv, mid, aargs, ep, env, scratch, &ctn)
172
+ if aargs.lead_tys.size != 1
173
+ scratch.warn(ep, "Module#extend without an argument is ignored")
174
+ ctn[Type.any, ep, env]
175
+ return
176
+ end
166
177
  arg = aargs.lead_tys[0]
167
178
  arg.each_child do |arg|
168
179
  if arg.is_a?(Type::Class)
169
- scratch.include_module(recv, arg, true, ep.ctx.iseq.absolute_path)
180
+ scratch.include_module(recv, arg, nil, true, ep.ctx.iseq.absolute_path)
170
181
  end
171
182
  end
172
183
  ctn[recv, ep, env]
@@ -178,7 +189,7 @@ module TypeProf
178
189
  else
179
190
  aargs.lead_tys.each do |aarg|
180
191
  sym = get_sym("module_function", aarg, ep, scratch) or next
181
- meths = Type::Instance.new(recv).get_method(sym, scratch)
192
+ meths = scratch.get_method(recv, false, sym)
182
193
  meths.each do |mdef|
183
194
  scratch.add_method(recv, sym, true, mdef)
184
195
  end
@@ -187,6 +198,67 @@ module TypeProf
187
198
  end
188
199
  end
189
200
 
201
+ def module_public(recv, mid, aargs, ep, env, scratch, &ctn)
202
+ if aargs.lead_tys.empty?
203
+ ctn[recv, ep, env.method_public_set(true)]
204
+ else
205
+ aargs.lead_tys.each do |aarg|
206
+ sym = get_sym("public", aarg, ep, scratch) or next
207
+ meths = scratch.get_method(recv, false, sym)
208
+ next unless meths
209
+ meths.each do |mdef|
210
+ mdef.pub_meth = true
211
+ end
212
+ end
213
+ ctn[recv, ep, env]
214
+ end
215
+ end
216
+
217
+ def module_private(recv, mid, aargs, ep, env, scratch, &ctn)
218
+ if aargs.lead_tys.empty?
219
+ ctn[recv, ep, env.method_public_set(false)]
220
+ else
221
+ aargs.lead_tys.each do |aarg|
222
+ sym = get_sym("private", aarg, ep, scratch) or next
223
+ meths = scratch.get_method(recv, false, sym)
224
+ next unless meths
225
+ meths.each do |mdef|
226
+ mdef.pub_meth = false
227
+ end
228
+ end
229
+ ctn[recv, ep, env]
230
+ end
231
+ end
232
+
233
+ def module_define_method(recv, mid, aargs, ep, env, scratch, &ctn)
234
+ if aargs.lead_tys.size != 1
235
+ scratch.warn(ep, "Module#define with #{ aargs.lead_tys.size } argument is ignored")
236
+ ctn[Type.any, ep, env]
237
+ return
238
+ end
239
+
240
+ mid, = aargs.lead_tys
241
+ if mid.is_a?(Type::Symbol)
242
+ mid = mid.sym
243
+ aargs.blk_ty.each_child do |blk_ty|
244
+ if blk_ty.is_a?(Type::Proc)
245
+ blk = blk_ty.block_body
246
+ case blk
247
+ when ISeqBlock
248
+ scratch.do_define_iseq_method(ep, env, mid, blk.iseq, ep)
249
+ else
250
+ # XXX: what to do?
251
+ end
252
+ else
253
+ # XXX: what to do?
254
+ end
255
+ end
256
+ else
257
+ # XXX: what to do?
258
+ end
259
+ ctn[Type.any, ep, env]
260
+ end
261
+
190
262
  def module_attr_accessor(recv, mid, aargs, ep, env, scratch, &ctn)
191
263
  aargs.lead_tys.each do |aarg|
192
264
  sym = get_sym("attr_accessor", aarg, ep, scratch) or next
@@ -222,21 +294,16 @@ module TypeProf
222
294
  end
223
295
 
224
296
  def array_aref(recv, mid, aargs, ep, env, scratch, &ctn)
225
- return ctn[Type.any, ep, env] unless recv.is_a?(Type::LocalArray)
297
+ return ctn[Type.any, ep, env] unless recv.is_a?(Type::Local) && recv.kind == Type::Array
226
298
 
227
299
  case aargs.lead_tys.size
228
300
  when 1
229
301
  idx = aargs.lead_tys.first
230
302
  if idx.is_a?(Type::Literal)
231
303
  idx = idx.lit
232
- if idx.is_a?(Range)
233
- ty = scratch.get_array_elem_type(env, ep, recv.id)
234
- base_ty = Type::Instance.new(Type::Builtin[:ary])
235
- ret_ty = Type::Array.new(Type::Array::Elements.new([], ty), base_ty)
236
- ctn[ret_ty, ep, env]
237
- return
238
- end
239
- idx = nil if !idx.is_a?(Integer)
304
+ idx = nil if !idx.is_a?(Integer) && !idx.is_a?(Range)
305
+ elsif idx == Type::Instance.new(Type::Builtin[:range])
306
+ idx = (nil..nil)
240
307
  else
241
308
  idx = nil
242
309
  end
@@ -253,7 +320,7 @@ module TypeProf
253
320
  end
254
321
 
255
322
  def array_aset(recv, mid, aargs, ep, env, scratch, &ctn)
256
- return ctn[Type.any, ep, env] unless recv.is_a?(Type::LocalArray)
323
+ return ctn[Type.any, ep, env] unless recv.is_a?(Type::Local) && recv.kind == Type::Array
257
324
 
258
325
  if aargs.lead_tys.size != 2
259
326
  #raise NotImplementedError # XXX
@@ -278,7 +345,7 @@ module TypeProf
278
345
  end
279
346
 
280
347
  def array_pop(recv, mid, aargs, ep, env, scratch, &ctn)
281
- return ctn[Type.any, ep, env] unless recv.is_a?(Type::LocalArray)
348
+ return ctn[Type.any, ep, env] unless recv.is_a?(Type::Local) && recv.kind == Type::Array
282
349
 
283
350
  if aargs.lead_tys.size != 0
284
351
  ctn[Type.any, ep, env]
@@ -290,7 +357,7 @@ module TypeProf
290
357
  end
291
358
 
292
359
  def hash_aref(recv, mid, aargs, ep, env, scratch, &ctn)
293
- return ctn[Type.any, ep, env] unless recv.is_a?(Type::LocalHash)
360
+ return ctn[Type.any, ep, env] unless recv.is_a?(Type::Local) && recv.kind == Type::Hash
294
361
 
295
362
  if aargs.lead_tys.size != 1
296
363
  ctn[Type.any, ep, env]
@@ -298,7 +365,7 @@ module TypeProf
298
365
  end
299
366
  idx = aargs.lead_tys.first
300
367
  recv.each_child do |recv|
301
- if recv.is_a?(Type::LocalHash)
368
+ if recv.is_a?(Type::Local) && recv.kind == Type::Hash
302
369
  ty = scratch.get_hash_elem_type(env, ep, recv.id, idx)
303
370
  else
304
371
  ty = Type.any
@@ -308,7 +375,7 @@ module TypeProf
308
375
  end
309
376
 
310
377
  def hash_aset(recv, mid, aargs, ep, env, scratch, &ctn)
311
- return ctn[Type.any, ep, env] unless recv.is_a?(Type::LocalHash)
378
+ return ctn[Type.any, ep, env] unless recv.is_a?(Type::Local) && recv.kind == Type::Hash
312
379
 
313
380
  raise NotImplementedError if aargs.lead_tys.size != 2
314
381
 
@@ -316,7 +383,7 @@ module TypeProf
316
383
  idx = scratch.globalize_type(idx, env, ep)
317
384
  ty = aargs.lead_tys.last
318
385
 
319
- unless recv.is_a?(Type::LocalHash)
386
+ unless recv.is_a?(Type::Local) && recv.kind == Type::Hash
320
387
  # to ignore: class OptionMap < Hash
321
388
  return ctn[ty, ep, env]
322
389
  end
@@ -473,6 +540,31 @@ module TypeProf
473
540
  ctn[result, ep, env]
474
541
  end
475
542
 
543
+ def kernel_autoload(recv, mid, aargs, ep, env, scratch, &ctn)
544
+ raise NotImplementedError if aargs.lead_tys.size != 2
545
+ feature = aargs.lead_tys[1]
546
+ if feature.is_a?(Type::Literal)
547
+ feature = feature.lit
548
+
549
+ action, arg = Builtin.file_require(feature, scratch)
550
+ case action
551
+ when :do
552
+ Builtin.file_load(arg, ep, env, scratch, &ctn)
553
+ when :done
554
+ when :error
555
+ scratch.warn(ep, arg)
556
+ end
557
+ ctn[Type.nil, ep, env]
558
+ else
559
+ scratch.warn(ep, "autoload target cannot be identified statically")
560
+ ctn[Type.nil, ep, env]
561
+ end
562
+ end
563
+
564
+ def module_autoload(recv, mid, aargs, ep, env, scratch, &ctn)
565
+ kernel_autoload(recv, mid, aargs, ep, env, scratch, &ctn)
566
+ end
567
+
476
568
  def kernel_Array(recv, mid, aargs, ep, env, scratch, &ctn)
477
569
  raise NotImplementedError if aargs.lead_tys.size != 1
478
570
  ty = aargs.lead_tys.first
@@ -546,6 +638,9 @@ module TypeProf
546
638
  scratch.set_custom_method(klass_module, :include, Builtin.method(:module_include))
547
639
  scratch.set_custom_method(klass_module, :extend, Builtin.method(:module_extend))
548
640
  scratch.set_custom_method(klass_module, :module_function, Builtin.method(:module_module_function))
641
+ scratch.set_custom_method(klass_module, :public, Builtin.method(:module_public))
642
+ scratch.set_custom_method(klass_module, :private, Builtin.method(:module_private))
643
+ scratch.set_custom_method(klass_module, :define_method, Builtin.method(:module_define_method))
549
644
 
550
645
  scratch.set_custom_method(klass_proc, :[], Builtin.method(:proc_call))
551
646
  scratch.set_custom_method(klass_proc, :call, Builtin.method(:proc_call))
@@ -563,6 +658,11 @@ module TypeProf
563
658
  scratch.set_custom_method(klass_obj, :require, Builtin.method(:kernel_require))
564
659
  scratch.set_custom_method(klass_obj, :require_relative, Builtin.method(:kernel_require_relative))
565
660
  scratch.set_custom_method(klass_obj, :Array, Builtin.method(:kernel_Array))
661
+ scratch.set_custom_method(klass_obj, :autoload, Builtin.method(:kernel_autoload))
662
+ scratch.set_custom_method(klass_module, :autoload, Builtin.method(:module_autoload))
663
+
664
+ # remove BasicObject#method_missing
665
+ scratch.set_method(klass_basic_obj, :method_missing, false, nil)
566
666
 
567
667
  # ENV: Hash[String, String]
568
668
  str_ty = Type::Instance.new(Type::Builtin[:str])
@@ -7,60 +7,59 @@ module TypeProf
7
7
  def parse(argv)
8
8
  opt = OptionParser.new
9
9
 
10
+ opt.banner = "Usage: #{ opt.program_name } [options] files..."
11
+
10
12
  output = nil
11
13
 
12
14
  # Verbose level:
13
- # * 0: no output
14
- # * 1: show indicator
15
- # * 2: debug print
15
+ # * 0: none
16
+ # * 1: default level
17
+ # * 2: debugging level
16
18
  verbose = 1
17
19
 
18
20
  options = {}
19
21
  dir_filter = nil
20
22
  gem_rbs_features = []
21
- version = false
23
+ show_version = false
22
24
  max_sec = max_iter = nil
23
25
 
24
- opt.on("-o OUTFILE") {|v| output = v }
25
- opt.on("-q", "--quiet") { verbose = 0 }
26
- opt.on("-v", "--verbose") { options[:show_errors] = true }
27
- opt.on("--version") { version = true }
28
- opt.on("-d", "--debug") { verbose = 2 }
29
- opt.on("-I DIR") {|v| $LOAD_PATH << v }
30
- opt.on("-r FEATURE") {|v| gem_rbs_features << v }
31
- opt.on("--max-second SECOND", Float) {|v| max_sec = v }
32
- opt.on("--max-iteration TIMES", Integer) {|v| max_iter = v }
26
+ opt.separator ""
27
+ opt.separator "Options:"
28
+ opt.on("-o OUTFILE", "Output to OUTFILE instead of stdout") {|v| output = v }
29
+ opt.on("-q", "--quiet", "Do not display progress indicator") { options[:show_indicator] = false }
30
+ opt.on("-v", "--verbose", "Alias to --show-errors") { options[:show_errors] = true }
31
+ opt.on("--version", "Display typeprof version") { show_version = true }
32
+ opt.on("-I DIR", "Add DIR to the load/require path") {|v| $LOAD_PATH << v }
33
+ opt.on("-r FEATURE", "Require RBS of the FEATURE gem") {|v| gem_rbs_features << v }
33
34
 
34
- opt.on("--include-dir DIR") do |dir|
35
+ opt.separator ""
36
+ opt.separator "Analysis output options:"
37
+ opt.on("--include-dir DIR", "Include the analysis result of .rb file in DIR") do |dir|
35
38
  # When `--include-dir` option is specified as the first directory option,
36
39
  # typeprof will exclude any files by default unless a file path matches the explicit option
37
40
  dir_filter ||= [[:exclude]]
38
41
  dir_filter << [:include, File.expand_path(dir)]
39
42
  end
40
- opt.on("--exclude-dir DIR") do |dir|
43
+ opt.on("--exclude-dir DIR", "Exclude the analysis result of .rb file in DIR") do |dir|
41
44
  # When `--exclude-dir` option is specified as the first directory option,
42
45
  # typeprof will include any files by default, except Ruby's install directory and Gem directories
43
46
  dir_filter ||= ConfigData::DEFAULT_DIR_FILTER
44
47
  dir_filter << [:exclude, File.expand_path(dir)]
45
48
  end
49
+ opt.on("--[no-]show-errors", "Display possible errors found during the analysis") {|v| options[:show_errors] = v }
50
+ opt.on("--[no-]show-untyped", "Display \"Foo | untyped\" instead of \"Foo\"") {|v| options[:show_untyped] = v }
46
51
 
47
- opt.on("-f OPTION") do |v|
48
- key, args = v.split("=", 2)
49
- case key
50
- when "type-depth-limit"
51
- options[:type_depth_limit] = Integer(args)
52
- when "pedantic-output"
53
- options[:pedantic_output] = true
54
- when "show-errors"
55
- options[:show_errors] = true
56
- when "show-container-raw-elements"
57
- options[:show_container_raw_elements] = true
58
- when "stackprof"
59
- options[:stackprof] = args ? args.to_sym : :cpu
60
- else
61
- raise OptionParser::InvalidOption.new("unknown option: #{ key }")
62
- end
63
- end
52
+ opt.separator ""
53
+ opt.separator "Analysis limit options:"
54
+ opt.on("--max-second SECOND", Float, "Limit the maxium time of analysis (in second)") {|v| max_sec = v }
55
+ opt.on("--max-iteration TIMES", Integer, "Limit the maxium instruction count of analysis") {|v| max_iter = v }
56
+
57
+ opt.separator ""
58
+ opt.separator "Advanced options:"
59
+ opt.on("--[no-]stub-execution", "Force to call all unreachable methods with \"untyped\" arguments") {|v| options[:stub_execution] = v }
60
+ opt.on("--type-depth-limit DEPTH", Integer, "Limit the maximum depth of nested types") {|v| options[:type_depth_limit] = v }
61
+ opt.on("--debug", "Display analysis log (for debugging purpose)") { verbose = 2 }
62
+ opt.on("--[no-]stackprof MODE", /\Acpu|wall|object\z/, "Enable stackprof (for debugging purpose)") {|v| options[:stackprof] = v.to_sym }
64
63
 
65
64
  opt.parse!(argv)
66
65
 
@@ -75,9 +74,9 @@ module TypeProf
75
74
  end
76
75
  end
77
76
 
78
- puts "typeprof #{ VERSION }" if version
77
+ puts "typeprof #{ VERSION }" if show_version
79
78
  if rb_files.empty?
80
- exit if version
79
+ exit if show_version
81
80
  raise OptionParser::InvalidOption.new("no input files")
82
81
  end
83
82
 
@@ -22,9 +22,11 @@ module TypeProf
22
22
  opt[:verbose] ||= 0
23
23
  opt[:options] ||= {}
24
24
  opt[:options] = {
25
- type_depth_limit: 5,
26
- pedantic_output: false,
25
+ show_indicator: true,
26
+ show_untyped: false,
27
27
  show_errors: false,
28
+ stub_execution: true,
29
+ type_depth_limit: 5,
28
30
  stackprof: nil,
29
31
  }.merge(opt[:options])
30
32
  super(**opt)
@@ -67,7 +69,7 @@ module TypeProf
67
69
 
68
70
  prologue_ctx = Context.new(nil, nil, nil)
69
71
  prologue_ep = ExecutionPoint.new(prologue_ctx, -1, nil)
70
- prologue_env = Env.new(StaticEnv.new(:top, Type.nil, false), [], [], Utils::HashWrapper.new({}))
72
+ prologue_env = Env.new(StaticEnv.new(:top, Type.nil, false, true), [], [], Utils::HashWrapper.new({}))
71
73
 
72
74
  Config.rb_files.each do |rb|
73
75
  if rb.is_a?(Array) # [String name, String content]
@@ -111,7 +113,7 @@ module TypeProf
111
113
  ctx = Context.new(iseq, cref, nil)
112
114
  ep = ExecutionPoint.new(ctx, 0, nil)
113
115
  locals = [Type.nil] * iseq.locals.size
114
- env = Env.new(StaticEnv.new(recv, Type.nil, false), locals, [], Utils::HashWrapper.new({}))
116
+ env = Env.new(StaticEnv.new(recv, Type.nil, false, false), locals, [], Utils::HashWrapper.new({}))
115
117
 
116
118
  return ep, env
117
119
  end
@@ -77,7 +77,9 @@ module TypeProf
77
77
  return env, Type.any if depth <= 0
78
78
  alloc_site = alloc_site.add_id(:cell).add_id(@base_type)
79
79
  env, elems = @elems.localize(env, alloc_site, depth)
80
- env.deploy_type(LocalCell, alloc_site, elems, @base_type)
80
+ ty = Local.new(Cell, alloc_site, @base_type)
81
+ env = env.deploy_type(alloc_site, elems)
82
+ return env, ty
81
83
  end
82
84
 
83
85
  def limit_size(limit)
@@ -85,7 +87,7 @@ module TypeProf
85
87
  Cell.new(@elems.limit_size(limit - 1), @base_type)
86
88
  end
87
89
 
88
- def get_method(mid, scratch)
90
+ def method_dispatch_info
89
91
  raise
90
92
  end
91
93
 
@@ -95,6 +97,19 @@ module TypeProf
95
97
  Cell.new(elems, @base_type)
96
98
  end
97
99
 
100
+ def generate_substitution
101
+ subst = {}
102
+ tyvars = @base_type.klass.type_params.map {|name,| Type::Var.new(name) }
103
+ tyvars.zip(@elems.elems) do |tyvar, elem|
104
+ if subst[tyvar]
105
+ subst[tyvar] = subst[tyvar].union(elem)
106
+ else
107
+ subst[tyvar] = elem
108
+ end
109
+ end
110
+ subst
111
+ end
112
+
98
113
  class Elements # Cell
99
114
  include Utils::StructuralEquality
100
115
 
@@ -102,10 +117,14 @@ module TypeProf
102
117
  @elems = elems
103
118
  end
104
119
 
120
+ def self.dummy_elements
121
+ Elements.new([]) # XXX
122
+ end
123
+
105
124
  attr_reader :elems
106
125
 
107
126
  def to_local_type(id, base_ty)
108
- Type::LocalCell.new(id, base_ty)
127
+ Type::Local.new(Cell, id, base_ty)
109
128
  end
110
129
 
111
130
  def globalize(env, visited, depth)
@@ -180,45 +199,6 @@ module TypeProf
180
199
  end
181
200
  end
182
201
 
183
- class LocalCell < ContainerType
184
- def initialize(id, base_type)
185
- @id = id
186
- raise unless base_type
187
- @base_type = base_type
188
- end
189
-
190
- attr_reader :id, :base_type
191
-
192
- def inspect
193
- "Type::LocalCell[#{ @id }, base_type: #{ @base_type.inspect }]"
194
- end
195
-
196
- def screen_name(scratch)
197
- #raise "LocalArray must not be included in signature"
198
- "LocalCell!"
199
- end
200
-
201
- def globalize(env, visited, depth)
202
- if visited[self] || depth <= 0
203
- Type.any
204
- else
205
- visited[self] = true
206
- elems = env.get_container_elem_types(@id)
207
- if elems
208
- elems = elems.globalize(env, visited, depth - 1)
209
- else
210
- elems = Cell::Elements.new([]) # XXX
211
- end
212
- visited.delete(self)
213
- Cell.new(elems, @base_type)
214
- end
215
- end
216
-
217
- def get_method(mid, scratch)
218
- @base_type.get_method(mid, scratch)
219
- end
220
- end
221
-
222
202
  # Do not insert Array type to local environment, stack, etc.
223
203
  class Array < ContainerType
224
204
  def initialize(elems, base_type)
@@ -228,6 +208,10 @@ module TypeProf
228
208
  @base_type = base_type
229
209
  end
230
210
 
211
+ def self.dummy_elements
212
+ Elements.new([], Type.any)
213
+ end
214
+
231
215
  attr_reader :elems, :base_type
232
216
 
233
217
  def inspect
@@ -247,7 +231,9 @@ module TypeProf
247
231
  return env, Type.any if depth <= 0
248
232
  alloc_site = alloc_site.add_id(:ary).add_id(@base_type)
249
233
  env, elems = @elems.localize(env, alloc_site, depth - 1)
250
- env.deploy_type(LocalArray, alloc_site, elems, @base_type)
234
+ ty = Local.new(Array, alloc_site, @base_type)
235
+ env = env.deploy_type(alloc_site, elems)
236
+ return env, ty
251
237
  end
252
238
 
253
239
  def limit_size(limit)
@@ -255,7 +241,7 @@ module TypeProf
255
241
  Array.new(@elems.limit_size(limit - 1), @base_type)
256
242
  end
257
243
 
258
- def get_method(mid, scratch)
244
+ def method_dispatch_info
259
245
  raise
260
246
  end
261
247
 
@@ -265,6 +251,10 @@ module TypeProf
265
251
  Array.new(elems, @base_type)
266
252
  end
267
253
 
254
+ def generate_substitution
255
+ { Type::Var.new(:Elem) => @elems.squash }
256
+ end
257
+
268
258
  class Elements # Array
269
259
  include Utils::StructuralEquality
270
260
 
@@ -277,7 +267,7 @@ module TypeProf
277
267
  attr_reader :lead_tys, :rest_ty
278
268
 
279
269
  def to_local_type(id, base_ty)
280
- Type::LocalArray.new(id, base_ty)
270
+ Type::Local.new(Array, id, base_ty)
281
271
  end
282
272
 
283
273
  def globalize(env, visited, depth)
@@ -305,7 +295,10 @@ module TypeProf
305
295
  end
306
296
 
307
297
  def screen_name(scratch)
308
- if Config.options[:show_container_raw_elements] || @rest_ty == Type.bot
298
+ if @rest_ty == Type.bot
299
+ if @lead_tys.empty?
300
+ return "Array[bot]" # RBS does not allow an empty tuple "[]"
301
+ end
309
302
  s = @lead_tys.map do |ty|
310
303
  ty.screen_name(scratch)
311
304
  end
@@ -360,7 +353,57 @@ module TypeProf
360
353
  end
361
354
 
362
355
  def [](idx)
363
- if idx >= 0
356
+ if idx.is_a?(Range)
357
+ if @rest_ty == Type.bot
358
+ lead_tys = @lead_tys[idx]
359
+ if lead_tys
360
+ rest_ty = Type.bot
361
+ else
362
+ return Type.nil
363
+ end
364
+ else
365
+ b, e = idx.begin, idx.end
366
+ b = 0 if !b
367
+ if !e
368
+ lead_tys = @lead_tys[idx] || []
369
+ rest_ty = @rest_ty
370
+ elsif b >= 0
371
+ if e >= 0
372
+ if b <= e
373
+ if e < @lead_tys.size
374
+ lead_tys = @lead_tys[idx]
375
+ rest_ty = Type.bot
376
+ else
377
+ lead_tys = @lead_tys[idx] || []
378
+ rest_ty = @rest_ty
379
+ end
380
+ else
381
+ return Type.nil
382
+ end
383
+ else
384
+ lead_tys = @lead_tys[idx] || []
385
+ e = idx.exclude_end? ? e : e == -1 ? @lead_tys.size : e + 1
386
+ rest_ty = (@lead_tys[e + 1..] || []).inject(@rest_ty) {|ty0, ty1| ty0.union(ty1) }
387
+ end
388
+ else
389
+ lead_tys = []
390
+ if e >= 0
391
+ rest_ty = e < @lead_tys.size ? Type.bot : @rest_ty
392
+ range = [0, @lead_tys.size + b].max .. (idx.exclude_end? ? e - 1 : e)
393
+ rest_ty = @lead_tys[range].inject(rest_ty) {|ty0, ty1| ty0.union(ty1) }
394
+ else
395
+ if b <= e
396
+ range = [0, @lead_tys.size + b].max .. (idx.exclude_end? ? e - 1 : e)
397
+ rest_ty = @lead_tys[range].inject(@rest_ty) {|ty0, ty1| ty0.union(ty1) }
398
+ else
399
+ return Type.nil
400
+ end
401
+ end
402
+ end
403
+ end
404
+ base_ty = Type::Instance.new(Type::Builtin[:ary])
405
+ Array.new(Elements.new(lead_tys, rest_ty), base_ty)
406
+ elsif idx >= 0
364
407
  if idx < @lead_tys.size
365
408
  @lead_tys[idx]
366
409
  elsif @rest_ty == Type.bot
@@ -491,48 +534,6 @@ module TypeProf
491
534
  end
492
535
  end
493
536
 
494
- # Do not insert Array type to local environment, stack, etc.
495
- class LocalArray < ContainerType
496
- def initialize(id, base_type)
497
- @id = id
498
- raise unless base_type
499
- @base_type = base_type
500
- end
501
-
502
- attr_reader :id, :base_type
503
-
504
- def inspect
505
- "Type::LocalArray[#{ @id }, base_type: #{ @base_type.inspect }]"
506
- end
507
-
508
- def screen_name(scratch)
509
- #raise "LocalArray must not be included in signature"
510
- "LocalArray!"
511
- end
512
-
513
- def globalize(env, visited, depth)
514
- if visited[self] || depth <= 0
515
- Type.any
516
- else
517
- visited[self] = true
518
- elems = env.get_container_elem_types(@id)
519
- if elems
520
- elems = elems.globalize(env, visited, depth - 1)
521
- else
522
- # TODO: currently out-of-scope array cannot be accessed
523
- elems = Array::Elements.new([], Type.any)
524
- end
525
- visited.delete(self)
526
- Array.new(elems, @base_type)
527
- end
528
- end
529
-
530
- def get_method(mid, scratch)
531
- @base_type.get_method(mid, scratch)
532
- end
533
- end
534
-
535
-
536
537
  class Hash < ContainerType
537
538
  def initialize(elems, base_type)
538
539
  @elems = elems
@@ -554,7 +555,9 @@ module TypeProf
554
555
  return env, Type.any if depth <= 0
555
556
  alloc_site = alloc_site.add_id(:hash).add_id(@base_type)
556
557
  env, elems = @elems.localize(env, alloc_site, depth - 1)
557
- env.deploy_type(LocalHash, alloc_site, elems, @base_type)
558
+ ty = Local.new(Hash, alloc_site, @base_type)
559
+ env = env.deploy_type(alloc_site, elems)
560
+ return env, ty
558
561
  end
559
562
 
560
563
  def limit_size(limit)
@@ -562,7 +565,7 @@ module TypeProf
562
565
  Hash.new(@elems.limit_size(limit - 1), @base_type)
563
566
  end
564
567
 
565
- def get_method(mid, scratch)
568
+ def method_dispatch_info
566
569
  raise
567
570
  end
568
571
 
@@ -572,6 +575,14 @@ module TypeProf
572
575
  Hash.new(elems, @base_type)
573
576
  end
574
577
 
578
+ def generate_substitution
579
+ tyvar_k = Type::Var.new(:K)
580
+ tyvar_v = Type::Var.new(:V)
581
+ k_ty0, v_ty0 = @elems.squash
582
+ # XXX: need to heuristically replace ret type Hash[K, V] with self, instead of conversative type?
583
+ { tyvar_k => k_ty0, tyvar_v => v_ty0 }
584
+ end
585
+
575
586
  class Elements # Hash
576
587
  include Utils::StructuralEquality
577
588
 
@@ -580,18 +591,21 @@ module TypeProf
580
591
  raise unless k_ty.is_a?(Type)
581
592
  raise unless v_ty.is_a?(Type)
582
593
  raise if k_ty.is_a?(Type::Union)
583
- raise if k_ty.is_a?(Type::LocalArray)
584
- raise if k_ty.is_a?(Type::LocalHash)
594
+ raise if k_ty.is_a?(Type::Local)
585
595
  raise if k_ty.is_a?(Type::Array)
586
596
  raise if k_ty.is_a?(Type::Hash)
587
597
  end
588
598
  @map_tys = map_tys
589
599
  end
590
600
 
601
+ def self.dummy_elements
602
+ Elements.new({Type.any => Type.any})
603
+ end
604
+
591
605
  attr_reader :map_tys
592
606
 
593
607
  def to_local_type(id, base_ty)
594
- Type::LocalHash.new(id, base_ty)
608
+ Type::Local.new(Hash, id, base_ty)
595
609
  end
596
610
 
597
611
  def globalize(env, visited, depth)
@@ -631,16 +645,22 @@ module TypeProf
631
645
  end
632
646
 
633
647
  def screen_name(scratch)
634
- s = @map_tys.map do |k_ty, v_ty|
635
- v = v_ty.screen_name(scratch)
636
- if k_ty.is_a?(Type::Symbol)
648
+ if !@map_tys.empty? && @map_tys.all? {|k_ty,| k_ty.is_a?(Type::Symbol) }
649
+ s = @map_tys.map do |k_ty, v_ty|
650
+ v = v_ty.screen_name(scratch)
637
651
  "#{ k_ty.sym }: #{ v }"
638
- else
639
- k = k_ty.screen_name(scratch)
640
- "#{ k }=>#{ v }"
652
+ end.join(", ")
653
+ "{#{ s }}"
654
+ else
655
+ k_ty = v_ty = Type.bot
656
+ @map_tys.each do |k, v|
657
+ k_ty = k_ty.union(k)
658
+ v_ty = v_ty.union(v)
641
659
  end
642
- end.join(", ")
643
- "{#{ s }}"
660
+ k_ty = k_ty.screen_name(scratch)
661
+ v_ty = v_ty.screen_name(scratch)
662
+ "Hash[#{ k_ty }, #{ v_ty }]"
663
+ end
644
664
  end
645
665
 
646
666
  def pretty_print(q)
@@ -773,21 +793,23 @@ module TypeProf
773
793
  end
774
794
  end
775
795
 
776
- class LocalHash < ContainerType
777
- def initialize(id, base_type)
796
+ class Local < ContainerType
797
+ def initialize(kind, id, base_type)
798
+ @kind = kind
778
799
  @id = id
800
+ raise unless base_type
779
801
  @base_type = base_type
780
802
  end
781
803
 
782
- attr_reader :id, :base_type
804
+ attr_reader :kind, :id, :base_type
783
805
 
784
806
  def inspect
785
- "Type::LocalHash[#{ @id }]"
807
+ "Type::Local[#{ @kind }, #{ @id }, base_type: #{ @base_type.inspect }]"
786
808
  end
787
809
 
788
810
  def screen_name(scratch)
789
- #raise "LocalHash must not be included in signature"
790
- "LocalHash!"
811
+ #raise "Local type must not be included in signature"
812
+ "Local[#{ @kind }]"
791
813
  end
792
814
 
793
815
  def globalize(env, visited, depth)
@@ -799,15 +821,56 @@ module TypeProf
799
821
  if elems
800
822
  elems = elems.globalize(env, visited, depth - 1)
801
823
  else
802
- elems = Hash::Elements.new({Type.any => Type.any})
824
+ # TODO: currently out-of-scope array cannot be accessed
825
+ elems = @kind::Elements.dummy_elements
803
826
  end
804
827
  visited.delete(self)
805
- Hash.new(elems, @base_type)
828
+ @kind.new(elems, @base_type)
806
829
  end
807
830
  end
808
831
 
809
- def get_method(mid, scratch)
810
- @base_type.get_method(mid, scratch)
832
+ def method_dispatch_info
833
+ @base_type.method_dispatch_info
834
+ end
835
+
836
+ def update_container_elem_type(subst, env, caller_ep, scratch)
837
+ case
838
+ when @kind == Cell
839
+ tyvars = @base_type.klass.type_params.map {|name,| Type::Var.new(name) }
840
+ # XXX: This should be skipped when the called methods belongs to superclass
841
+ tyvars.each_with_index do |tyvar, idx|
842
+ ty = subst[tyvar]
843
+ if ty
844
+ env, ty = scratch.localize_type(ty, env, caller_ep)
845
+ env = scratch.update_container_elem_types(env, caller_ep, @id, @base_type) do |elems|
846
+ elems.update(idx, ty)
847
+ end
848
+ end
849
+ end
850
+ when @kind == Array
851
+ tyvar_elem = Type::Var.new(:Elem)
852
+ if subst[tyvar_elem]
853
+ ty = subst[tyvar_elem]
854
+ env, ty = scratch.localize_type(ty, env, caller_ep)
855
+ env = scratch.update_container_elem_types(env, caller_ep, @id, @base_type) do |elems|
856
+ elems.update(nil, ty)
857
+ end
858
+ end
859
+ when @kind == Hash
860
+ tyvar_k = Type::Var.new(:K)
861
+ tyvar_v = Type::Var.new(:V)
862
+ if subst[tyvar_k] && subst[tyvar_v]
863
+ k_ty = subst[tyvar_k]
864
+ v_ty = subst[tyvar_v]
865
+ alloc_site = AllocationSite.new(caller_ep)
866
+ env, k_ty = scratch.localize_type(k_ty, env, caller_ep, alloc_site.add_id(:k))
867
+ env, v_ty = scratch.localize_type(v_ty, env, caller_ep, alloc_site.add_id(:v))
868
+ env = scratch.update_container_elem_types(env, caller_ep, @id, @base_type) do |elems|
869
+ elems.update(k_ty, v_ty)
870
+ end
871
+ end
872
+ end
873
+ env
811
874
  end
812
875
  end
813
876
  end