typeprof 0.33.1 → 0.33.2

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.
@@ -16,6 +16,8 @@ module TypeProf::Core
16
16
 
17
17
  attr_reader :stmts
18
18
 
19
+ def ret_code_range = @stmts.last.code_range
20
+
19
21
  def subnodes = { stmts: }
20
22
 
21
23
  def install0(genv)
@@ -23,8 +25,13 @@ module TypeProf::Core
23
25
 
24
26
  post_stmts = []
25
27
 
28
+ # BEGIN runs before the surrounding statements, END after them
29
+ @stmts.each do |stmt|
30
+ stmt.install(genv) if stmt.is_a?(PreExecutionNode)
31
+ end
32
+
26
33
  @stmts.each do |stmt|
27
- next if stmt.nil?
34
+ next if stmt.is_a?(PreExecutionNode)
28
35
 
29
36
  if stmt.is_a?(PostExecutionNode)
30
37
  post_stmts << stmt
@@ -126,7 +133,7 @@ module TypeProf::Core
126
133
  if @rights
127
134
  @rights.each {|lhs| lhs.install(genv) }
128
135
  @rights.each {|lhs| lhs.rhs.ret || raise(lhs.rhs.inspect) }
129
- rights = @rights.map {|rhs| rhs.ret }
136
+ rights = @rights.map {|lhs| lhs.rhs.ret }
130
137
  end
131
138
 
132
139
  box = @changes.add_masgn_box(genv, value, lefts, rest_elem, rights)
@@ -144,7 +144,8 @@ module TypeProf::Core
144
144
  raise if raw_node.statements.type != :statements_node
145
145
  raise if raw_node.statements.body.size != 1
146
146
  @body = AST.create_pattern_node(raw_node.statements.body[0], lenv)
147
- raise if raw_node.subsequent
147
+ raise if raw_node.type == :if_node && raw_node.subsequent
148
+ raise if raw_node.type == :unless_node && raw_node.else_clause
148
149
  end
149
150
 
150
151
  attr_reader :cond, :body
@@ -152,8 +153,9 @@ module TypeProf::Core
152
153
  def subnodes = { cond:, body: }
153
154
 
154
155
  def install_pattern0(genv, subject)
155
- @cond.install(genv)
156
+ # The guard is evaluated after the pattern binds its variables
156
157
  @body.install_pattern(genv, subject)
158
+ @cond.install(genv)
157
159
  subject
158
160
  end
159
161
  end
@@ -39,7 +39,7 @@ module TypeProf::Core
39
39
  if prep_decl.is_a?(AST::SigPrependNode) && prep_mod.type_params
40
40
  prep_ty = genv.get_instance_type(prep_mod, prep_decl.args, changes, {}, a_ty)
41
41
  else
42
- type_params = prep_mod.type_params.map {|(_name, _default_ty)| Source.new() } # TODO: better support
42
+ type_params = prep_mod.type_params.map {|(_name, _default_ty)| genv.untyped_arg } # TODO: better support
43
43
  prep_ty = Type::Instance.new(genv, prep_mod, type_params)
44
44
  end
45
45
  if prep_ty.mod == f_mod
@@ -64,7 +64,7 @@ module TypeProf::Core
64
64
  if inc_decl.is_a?(AST::SigIncludeNode) && inc_mod.type_params
65
65
  inc_ty = genv.get_instance_type(inc_mod, inc_decl.args, changes, {}, a_ty)
66
66
  else
67
- type_params = inc_mod.type_params.map {|(_name, _default_ty)| Source.new() } # TODO: better support
67
+ type_params = inc_mod.type_params.map {|(_name, _default_ty)| genv.untyped_arg } # TODO: better support
68
68
  inc_ty = Type::Instance.new(genv, inc_mod, type_params)
69
69
  end
70
70
  if inc_ty.mod == f_mod
@@ -89,7 +89,7 @@ module TypeProf::Core
89
89
  if ext_decl.is_a?(AST::SigExtendNode) && ext_mod.type_params
90
90
  ext_ty = genv.get_instance_type(ext_mod, ext_decl.args, changes, {}, a_ty)
91
91
  else
92
- type_params = ext_mod.type_params.map {|(_name, _default_ty)| Source.new() } # TODO: better support
92
+ type_params = ext_mod.type_params.map {|(_name, _default_ty)| genv.untyped_arg } # TODO: better support
93
93
  ext_ty = Type::Instance.new(genv, ext_mod, type_params)
94
94
  end
95
95
  if ext_ty.mod == f_mod
@@ -705,6 +705,10 @@ module TypeProf::Core
705
705
  return unless cpath
706
706
  mod = genv.resolve_cpath(cpath)
707
707
  args = @args.map {|arg| arg.covariant_vertex(genv, changes, subst) }
708
+ # Omitted type arguments (e.g., `Foo` for `Foo[T]`) default to untyped
709
+ mod.type_params.drop(args.size).each do |_, default_ty|
710
+ args << (default_ty ? default_ty.covariant_vertex(genv, changes, subst) : genv.untyped_arg)
711
+ end
708
712
  changes.add_edge(genv, Source.new(Type::Instance.new(genv, mod, args)), vtx)
709
713
  end
710
714
 
@@ -716,7 +720,7 @@ module TypeProf::Core
716
720
  # TODO: report error for wrong type arguments
717
721
  # TODO: support default type args
718
722
  args = mod.type_params.zip(@args).map do |_, arg|
719
- arg ? arg.contravariant_vertex(genv, changes, subst) : Source.new
723
+ arg ? arg.contravariant_vertex(genv, changes, subst) : genv.untyped_arg
720
724
  end
721
725
  changes.add_edge(genv, Source.new(Type::Instance.new(genv, mod, args)), vtx)
722
726
  end
@@ -1095,6 +1099,10 @@ module TypeProf::Core
1095
1099
  return unless cpath
1096
1100
  mod = genv.resolve_cpath(cpath)
1097
1101
  args = @args.map {|arg| arg.covariant_vertex(genv, changes, subst) }
1102
+ # Omitted type arguments (e.g., `Foo` for `Foo[T]`) default to untyped
1103
+ mod.type_params.drop(args.size).each do |_, default_ty|
1104
+ args << (default_ty ? default_ty.covariant_vertex(genv, changes, subst) : genv.untyped_arg)
1105
+ end
1098
1106
  changes.add_edge(genv, Source.new(Type::Instance.new(genv, mod, args)), vtx)
1099
1107
  end
1100
1108
 
@@ -1104,6 +1112,7 @@ module TypeProf::Core
1104
1112
  return unless cpath
1105
1113
  mod = genv.resolve_cpath(cpath)
1106
1114
  args = @args.map {|arg| arg.contravariant_vertex(genv, changes, subst) }
1115
+ mod.type_params.drop(args.size).each { args << genv.untyped_arg }
1107
1116
  changes.add_edge(genv, Source.new(Type::Instance.new(genv, mod, args)), vtx)
1108
1117
  end
1109
1118
 
@@ -326,10 +326,59 @@ module TypeProf::Core
326
326
  end
327
327
  end
328
328
 
329
- class LambdaNode < Node
330
- def install0(genv)
331
- Source.new(genv.proc_type)
329
+ # Prism::LambdaNode has the same locals/parameters/body as Prism::BlockNode.
330
+ # A lambda literal is a block without a call: `->` never dispatches to a
331
+ # user-defined `lambda` method.
332
+ class LambdaNode < BlockNode
333
+ def lambda? = true
334
+
335
+ # A diagnostic on the `===` call below, such as wrong arity, points at the
336
+ # whole lambda.
337
+ def mid_code_range = nil
338
+
339
+ # `in ->(x) { ... }` matches by calling the lambda with the subject.
340
+ def install_pattern0(genv, subject)
341
+ recv = install0(genv)
342
+ a_args = ActualArguments.new([subject], [false], nil, nil)
343
+ @changes.add_method_call_box(genv, recv, :===, a_args, false)
344
+ recv
332
345
  end
346
+
347
+ # A lambda is entered like a method, so every parameter kind binds, not just
348
+ # the positionals a block is handed.
349
+ def build_formals(genv, blenv, f_args)
350
+ # f_args is the block-shaped list, required then optional; numbered and `it`
351
+ # parameters put names there that @params does not carry.
352
+ req_count = f_args.size - @opt_positional_defaults.size
353
+ rest = new_formal(blenv, @params[:rest_positionals])
354
+ post = @params[:post_positionals].map {|v| blenv.new_var(v, self) }
355
+ req_keywords = self.req_keywords.map {|v| blenv.new_var(v, self) }
356
+ opt_keywords = self.opt_keywords.map {|v| blenv.new_var(v, self) }
357
+ rest_keywords = new_formal(blenv, self.rest_keywords)
358
+ block = new_formal(blenv, @params[:block])
359
+
360
+ if rest
361
+ @changes.add_edge(genv, Source.new(genv.gen_ary_type(Vertex.new(self))), rest)
362
+ end
363
+ # Only the anonymous `**` needs seeding, as it does for a method: a named
364
+ # one takes its type from what the call passes.
365
+ if self.rest_keywords == :"**anonymous_keyword"
366
+ @changes.add_edge(genv, Source.new(genv.gen_hash_type(Vertex.new(self), Vertex.new(self))), rest_keywords)
367
+ end
368
+ install_multi_targets(genv, @params[:post_multi_targets], post, blenv)
369
+ opt_keyword_defaults.zip(opt_keywords) do |expr, vtx|
370
+ @changes.add_edge(genv, expr.install(genv), vtx)
371
+ end
372
+
373
+ FormalArguments.new(
374
+ f_args[0, req_count], f_args[req_count..], rest, post,
375
+ req_keywords, opt_keywords, rest_keywords, block,
376
+ )
377
+ end
378
+
379
+ private
380
+
381
+ def new_formal(blenv, name) = name ? blenv.new_var(name, self) : nil
333
382
  end
334
383
  end
335
384
  end
@@ -257,7 +257,7 @@ module TypeProf::Core
257
257
  end
258
258
  end
259
259
 
260
- class PostExecutionNode < Node
260
+ class ExecutionBaseNode < Node
261
261
  def initialize(raw_node, lenv)
262
262
  super(raw_node, lenv)
263
263
  @body = raw_node.statements ? AST.create_node(raw_node.statements, lenv) : DummyNilNode.new(TypeProf::CodeRange.new(code_range.last, code_range.last), lenv)
@@ -273,6 +273,12 @@ module TypeProf::Core
273
273
  end
274
274
  end
275
275
 
276
+ class PreExecutionNode < ExecutionBaseNode
277
+ end
278
+
279
+ class PostExecutionNode < ExecutionBaseNode
280
+ end
281
+
276
282
  class ClassVariableWriteNode < Node
277
283
  def initialize(raw_node, rhs, lenv)
278
284
  super(raw_node, lenv)
@@ -278,6 +278,7 @@ module TypeProf::Core
278
278
  when :splat_node then SplatNode.new(raw_node, lenv)
279
279
  when :for_node then ForNode.new(raw_node, lenv)
280
280
  when :alias_global_variable_node then AliasGlobalVariableNode.new(raw_node, lenv)
281
+ when :pre_execution_node then PreExecutionNode.new(raw_node, lenv)
281
282
  when :post_execution_node then PostExecutionNode.new(raw_node, lenv)
282
283
  when :flip_flop_node then FlipFlopNode.new(raw_node, lenv)
283
284
  when :shareable_constant_node then create_node(raw_node.write, lenv)
@@ -362,7 +363,7 @@ module TypeProf::Core
362
363
 
363
364
  when :capture_pattern_node then CapturePatternNode.new(raw_node, lenv)
364
365
 
365
- when :if_node then IfPatternNode.new(raw_node, lenv)
366
+ when :if_node, :unless_node then IfPatternNode.new(raw_node, lenv)
366
367
 
367
368
  when :pinned_variable_node then PinnedPatternNode.new(raw_node, lenv)
368
369
  when :pinned_expression_node then PinnedPatternNode.new(raw_node, lenv)
@@ -396,6 +397,7 @@ module TypeProf::Core
396
397
 
397
398
  when :array_node then ArrayNode.new(raw_node, lenv) # for %w[foo bar]
398
399
  when :range_node then RangeNode.new(raw_node, lenv) # TODO: support range pattern correctly
400
+ when :lambda_node then LambdaNode.new(raw_node, lenv)
399
401
 
400
402
  else
401
403
  raise "unknown pattern node type: #{ raw_node.type }"
@@ -25,7 +25,7 @@ module TypeProf::Core
25
25
  def proc_call(changes, node, ty, a_args, ret)
26
26
  case ty
27
27
  when Type::Proc
28
- ty.block.accept_args(@genv, changes, a_args.positionals)
28
+ ty.block.pass_arguments(@genv, changes, a_args)
29
29
  ty.block.add_ret(@genv, changes, ret)
30
30
  true
31
31
  else
@@ -21,6 +21,136 @@ module TypeProf::Core
21
21
  attr_reader :opt_keywords
22
22
  attr_reader :rest_keywords
23
23
  attr_reader :block
24
+
25
+ # Binds a call's actual arguments to these formals. `node` supplies the
26
+ # keyword names, which live on the AST node rather than in the vertices.
27
+ def pass_arguments(changes, genv, a_args, node)
28
+ if a_args.splat_flags.any?
29
+ # there is at least one splat actual argument
30
+
31
+ lower = @req_positionals.size + @post_positionals.size
32
+ upper = @rest_positionals ? nil : lower + @opt_positionals.size
33
+ if upper && upper < a_args.positionals.size
34
+ meth = changes.node.mid_code_range ? :mid_code_range : :code_range
35
+ err = "#{ a_args.positionals.size } for #{ lower }#{ upper ? lower < upper ? "...#{ upper }" : "" : "+" }"
36
+ changes.add_diagnostic(meth, "wrong number of arguments (#{ err })")
37
+ return false
38
+ end
39
+
40
+ start_rest = [a_args.splat_flags.index(true), @req_positionals.size + @opt_positionals.size].min
41
+ end_rest = [a_args.splat_flags.rindex(true) + 1, a_args.positionals.size - @post_positionals.size].max
42
+ rest_vtxs = a_args.get_rest_args(genv, changes, start_rest, end_rest)
43
+
44
+ @req_positionals.each_with_index do |f_vtx, i|
45
+ if i < start_rest
46
+ changes.add_edge(genv, a_args.positionals[i], f_vtx)
47
+ else
48
+ rest_vtxs.each do |vtx|
49
+ changes.add_edge(genv, vtx, f_vtx)
50
+ end
51
+ end
52
+ end
53
+ @opt_positionals.each_with_index do |f_vtx, i|
54
+ i += @req_positionals.size
55
+ if i < start_rest
56
+ changes.add_edge(genv, a_args.positionals[i], f_vtx)
57
+ else
58
+ rest_vtxs.each do |vtx|
59
+ changes.add_edge(genv, vtx, f_vtx)
60
+ end
61
+ end
62
+ end
63
+ @post_positionals.each_with_index do |f_vtx, i|
64
+ i += a_args.positionals.size - @post_positionals.size
65
+ if end_rest <= i
66
+ changes.add_edge(genv, a_args.positionals[i], f_vtx)
67
+ else
68
+ rest_vtxs.each do |vtx|
69
+ changes.add_edge(genv, vtx, f_vtx)
70
+ end
71
+ end
72
+ end
73
+
74
+ if @rest_positionals
75
+ rest_vtxs.each do |vtx|
76
+ @rest_positionals.each_type do |ty|
77
+ if ty.is_a?(Type::Instance) && ty.mod == genv.mod_ary && ty.args[0]
78
+ changes.add_edge(genv, vtx, ty.args[0])
79
+ end
80
+ end
81
+ end
82
+ end
83
+ else
84
+ # there is no splat actual argument
85
+
86
+ lower = @req_positionals.size + @post_positionals.size
87
+ upper = @rest_positionals ? nil : lower + @opt_positionals.size
88
+ if a_args.positionals.size < lower || (upper && upper < a_args.positionals.size)
89
+ meth = changes.node.mid_code_range ? :mid_code_range : :code_range
90
+ err = "#{ a_args.positionals.size } for #{ lower }#{ upper ? lower < upper ? "...#{ upper }" : "" : "+" }"
91
+ changes.add_diagnostic(meth, "wrong number of arguments (#{ err })")
92
+ return false
93
+ end
94
+
95
+ @req_positionals.each_with_index do |f_vtx, i|
96
+ changes.add_edge(genv, a_args.positionals[i], f_vtx)
97
+ end
98
+ @post_positionals.each_with_index do |f_vtx, i|
99
+ i -= @post_positionals.size
100
+ changes.add_edge(genv, a_args.positionals[i], f_vtx)
101
+ end
102
+ start_rest = @req_positionals.size
103
+ end_rest = a_args.positionals.size - @post_positionals.size
104
+ i = 0
105
+ while i < @opt_positionals.size && start_rest < end_rest
106
+ f_arg = @opt_positionals[i]
107
+ changes.add_edge(genv, a_args.positionals[start_rest], f_arg)
108
+ i += 1
109
+ start_rest += 1
110
+ end
111
+
112
+ if start_rest < end_rest
113
+ if @rest_positionals
114
+ (start_rest..end_rest-1).each do |i|
115
+ @rest_positionals.each_type do |ty|
116
+ if ty.is_a?(Type::Instance) && ty.mod == genv.mod_ary && ty.args[0]
117
+ changes.add_edge(genv, a_args.positionals[i], ty.args[0])
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ if a_args.keywords
126
+ # TODO: support diagnostics
127
+ node.req_keywords.zip(@req_keywords) do |name, f_vtx|
128
+ changes.add_edge(genv, a_args.get_keyword_arg(genv, changes, name), f_vtx)
129
+ end
130
+
131
+ node.opt_keywords.zip(@opt_keywords).each do |name, f_vtx|
132
+ changes.add_edge(genv, a_args.get_keyword_arg(genv, changes, name), f_vtx)
133
+ end
134
+
135
+ if node.rest_keywords
136
+ named_keys = node.req_keywords + node.opt_keywords
137
+ a_args.keywords.each_type do |kw_ty|
138
+ case kw_ty
139
+ when Type::Record
140
+ rest_fields = kw_ty.fields.reject {|key, _| named_keys.include?(key) }
141
+ base = kw_ty.base_type(genv)
142
+ rest_record = Type::Record.new(genv, rest_fields, base)
143
+ changes.add_edge(genv, Source.new(rest_record), @rest_keywords)
144
+ when Type::Hash, Type::Instance
145
+ changes.add_edge(genv, Source.new(kw_ty), @rest_keywords)
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ return true
152
+ end
153
+
24
154
  end
25
155
 
26
156
  class ActualArguments
@@ -52,6 +182,15 @@ module TypeProf::Core
52
182
  )
53
183
  end
54
184
 
185
+ # Keywords passed to formals that take none are the last positional hash.
186
+ def with_keywords_normalized_for(node)
187
+ return self unless keywords
188
+ return self if node.no_keywords || node.rest_keywords
189
+ return self unless node.req_keywords.empty? && node.opt_keywords.empty?
190
+
191
+ with_keywords_as_last_positional_hash
192
+ end
193
+
55
194
  def prepend_positionals(positionals, splat_flags)
56
195
  return self if positionals.empty?
57
196
 
@@ -245,7 +384,7 @@ module TypeProf::Core
245
384
  end
246
385
 
247
386
  class ForwardingArguments
248
- def initialize(req_positionals, opt_positionals, rest_positionals, post_positionals, req_keyword_pairs, opt_keyword_pairs, rest_keywords, block, activation)
387
+ def initialize(req_positionals, opt_positionals, rest_positionals, post_positionals, req_keyword_pairs, opt_keyword_pairs, rest_keywords, block, activation, param_names)
249
388
  @req_positionals = req_positionals
250
389
  @opt_positionals = opt_positionals
251
390
  @rest_positionals = rest_positionals
@@ -255,33 +394,68 @@ module TypeProf::Core
255
394
  @rest_keywords = rest_keywords
256
395
  @block = block
257
396
  @activation = activation
397
+ @param_names = param_names
258
398
  end
259
399
 
260
- def to_actual_arguments(genv, changes, node, include_leading_positionals: true, activation_required: false)
261
- positionals = include_leading_positionals ? @req_positionals.dup : []
262
- splat_flags = ::Array.new(positionals.size, false)
263
- positionals_omittable = ::Array.new(positionals.size, false)
400
+ # The arguments that `...` forwards: only the ones the caller passed to it.
401
+ # The parameters before `...` (`def foo(a, b = 1, ...)`) are not forwarded,
402
+ # and none can follow it.
403
+ def to_actual_arguments(genv, changes, node)
404
+ positionals = []
405
+ splat_flags = []
406
+ positionals_omittable = []
264
407
 
265
- @opt_positionals.each do |elem_vtx|
266
- positionals << Source.new(genv.gen_ary_type(elem_vtx))
408
+ if @rest_positionals
409
+ positionals << Source.new(genv.gen_ary_type(@rest_positionals))
267
410
  splat_flags << true
268
411
  positionals_omittable << true
269
412
  end
270
413
 
271
- if @rest_positionals
414
+ # `...` cannot follow keyword parameters, so only the rest keywords remain
415
+ ForwardingActualArguments.new(positionals, splat_flags, @rest_keywords, @block, positionals_omittable, !!@rest_keywords, true, @activation, true)
416
+ end
417
+
418
+ # A bare `super` passes the current values of the parameters, which
419
+ # include the defaults of omitted ones and any reassignment.
420
+ def to_implicit_super_arguments(genv, changes, node, lenv)
421
+ positionals = []
422
+ splat_flags = []
423
+ positionals_omittable = []
424
+
425
+ # A destructured parameter has no name, so it is passed as it was given
426
+ @param_names[:req_positionals].zip(@req_positionals) do |name, vtx|
427
+ positionals << (name ? lenv.get_method_var(name) : vtx)
428
+ splat_flags << false
429
+ positionals_omittable << false
430
+ end
431
+
432
+ @param_names[:opt_positionals].each do |name|
433
+ positionals << lenv.get_method_var(name)
434
+ splat_flags << false
435
+ positionals_omittable << false
436
+ end
437
+
438
+ if @param_names[:rest_positionals]
439
+ positionals << lenv.get_method_var(@param_names[:rest_positionals])
440
+ splat_flags << true
441
+ positionals_omittable << true
442
+ elsif @rest_positionals
272
443
  positionals << Source.new(genv.gen_ary_type(@rest_positionals))
273
444
  splat_flags << true
274
445
  positionals_omittable << true
275
446
  end
276
447
 
277
- @post_positionals.each do |arg|
278
- positionals << arg
448
+ @param_names[:post_positionals].zip(@post_positionals) do |name, vtx|
449
+ positionals << (name ? lenv.get_method_var(name) : vtx)
279
450
  splat_flags << false
280
451
  positionals_omittable << false
281
452
  end
282
453
 
283
- keywords, keywords_omittable = build_keyword_args(genv, changes, node)
284
- ForwardingActualArguments.new(positionals, splat_flags, keywords, @block, positionals_omittable, keywords_omittable, true, @activation, activation_required)
454
+ req_keyword_pairs = @param_names[:req_keywords].map {|name| [name, lenv.get_method_var(name)] }
455
+ opt_keyword_pairs = @param_names[:opt_keywords].map {|name| [name, lenv.get_method_var(name)] }
456
+ rest_keywords = @param_names[:rest_keywords] ? lenv.get_method_var(@param_names[:rest_keywords]) : @rest_keywords
457
+ keywords, keywords_omittable = build_keyword_args(genv, changes, node, req_keyword_pairs, opt_keyword_pairs, rest_keywords)
458
+ ForwardingActualArguments.new(positionals, splat_flags, keywords, @block, positionals_omittable, keywords_omittable, true, @activation, false)
285
459
  end
286
460
 
287
461
  def accept_actual_arguments(genv, changes, a_args)
@@ -386,18 +560,16 @@ module TypeProf::Core
386
560
 
387
561
  private
388
562
 
389
- def build_keyword_args(genv, changes, node)
390
- opt_keyword_pairs = @opt_keyword_pairs
391
-
392
- if @req_keyword_pairs.empty? && opt_keyword_pairs.empty?
393
- return @rest_keywords, !!@rest_keywords
563
+ def build_keyword_args(genv, changes, node, req_keyword_pairs, opt_keyword_pairs, rest_keywords)
564
+ if req_keyword_pairs.empty? && opt_keyword_pairs.empty?
565
+ return rest_keywords, !!rest_keywords
394
566
  end
395
567
 
396
568
  unified_key = Vertex.new(node)
397
569
  unified_val = Vertex.new(node)
398
570
  literal_pairs = {}
399
571
 
400
- @req_keyword_pairs.each do |name, vtx|
572
+ req_keyword_pairs.each do |name, vtx|
401
573
  changes.add_edge(genv, Source.new(Type::Symbol.new(genv, name)), unified_key)
402
574
  changes.add_edge(genv, vtx, unified_val)
403
575
  literal_pairs[name] = vtx
@@ -410,13 +582,13 @@ module TypeProf::Core
410
582
  end
411
583
 
412
584
  base_hash_type = genv.gen_hash_type(unified_key, unified_val)
413
- changes.add_hash_splat_box(genv, @rest_keywords, unified_key, unified_val) if @rest_keywords
585
+ changes.add_hash_splat_box(genv, rest_keywords, unified_key, unified_val) if rest_keywords
414
586
 
415
587
  if literal_pairs.empty?
416
588
  [Source.new(base_hash_type), false]
417
- elsif @rest_keywords
589
+ elsif rest_keywords
418
590
  fallback = Source.new(Type::Record.new(genv, literal_pairs, base_hash_type))
419
- [changes.add_keyword_merge_box(genv, @rest_keywords, literal_pairs, fallback).ret, false]
591
+ [changes.add_keyword_merge_box(genv, rest_keywords, literal_pairs, fallback).ret, false]
420
592
  else
421
593
  [Source.new(Type::Record.new(genv, literal_pairs, base_hash_type)), false]
422
594
  end
@@ -424,18 +596,34 @@ module TypeProf::Core
424
596
  end
425
597
 
426
598
  class Block
427
- #: (AST::CallBaseNode, Vertex, Array[Vertex], Array[EscapeBox]) -> void
428
- def initialize(node, f_ary_arg, f_args, next_boxes)
599
+ #: (AST::BlockNode, Vertex, Array[Vertex], Array[EscapeBox], FormalArguments?) -> void
600
+ def initialize(node, f_ary_arg, f_args, next_boxes, formals = nil)
429
601
  @node = node
430
602
  @f_ary_arg = f_ary_arg
431
603
  @f_args = f_args
432
604
  @next_boxes = next_boxes
605
+ # Set when the body is entered like a method rather than yielded to, which
606
+ # is to say for a lambda: the full formals then bind the call's arguments.
607
+ @formals = formals
433
608
  end
434
609
 
435
610
  attr_reader :node, :f_args, :next_boxes
436
611
 
612
+ # The arguments of a call that enters this body directly, as Proc#call does.
613
+ def pass_arguments(genv, changes, a_args)
614
+ if @formals
615
+ a_args = a_args.with_keywords_normalized_for(@node)
616
+ @formals.pass_arguments(changes, genv, a_args, @node)
617
+ changes.add_edge(genv, a_args.block, @formals.block) if @formals.block && a_args.block
618
+ else
619
+ accept_args(genv, changes, a_args.positionals)
620
+ end
621
+ end
622
+
437
623
  def accept_args(genv, changes, caller_positionals)
438
- if caller_positionals.size == 1 && @f_args.size >= 2
624
+ if caller_positionals.size == 1 && @f_args.size >= 2 && !@formals
625
+ # A block deconstructs a sole array argument over its parameters; a
626
+ # lambda takes it as the one argument it is.
439
627
  changes.add_edge(genv, caller_positionals[0], @f_ary_arg)
440
628
  else
441
629
  caller_positionals.zip(@f_args) do |a_arg, f_arg|
@@ -452,6 +640,10 @@ module TypeProf::Core
452
640
  end
453
641
 
454
642
  class RecordBlock
643
+ def pass_arguments(genv, changes, a_args)
644
+ accept_args(genv, changes, a_args.positionals)
645
+ end
646
+
455
647
  def initialize(node)
456
648
  @node = node
457
649
  @used = false
@@ -42,6 +42,11 @@ module TypeProf::Core
42
42
 
43
43
  @bot_type = Type::Bot.new(self)
44
44
 
45
+ # The vertex for a type argument that is not given. This must be shared:
46
+ # a fresh vertex makes a different Instance type and a different edge on
47
+ # every run, so a box that uses it is re-run forever.
48
+ @untyped_arg = Source.new
49
+
45
50
  @run_count = 0
46
51
  end
47
52
 
@@ -52,7 +57,7 @@ module TypeProf::Core
52
57
  attr_reader :obj_type, :nil_type, :true_type, :false_type, :str_type
53
58
  attr_reader :int_type, :float_type, :rational_type, :complex_type
54
59
  attr_reader :proc_type, :symbol_type, :method_type, :set_type, :regexp_type
55
- attr_reader :bot_type
60
+ attr_reader :bot_type, :untyped_arg
56
61
 
57
62
  def gen_ary_type(elem_vtx)
58
63
  Type::Instance.new(self, @mod_ary, [elem_vtx])
@@ -130,18 +135,18 @@ module TypeProf::Core
130
135
  ty_env = base_ty_env.dup
131
136
  if base_ty.is_a?(Type::Instance)
132
137
  base_ty.mod.type_params.zip(base_ty.args) do |(param, default_ty), arg|
133
- ty_env[param] = arg || (default_ty ? default_ty.covariant_vertex(self, changes, ty_env) : Source.new)
138
+ ty_env[param] = arg || (default_ty ? default_ty.covariant_vertex(self, changes, ty_env) : @untyped_arg)
134
139
  end
135
140
  elsif base_ty.is_a?(Type::Singleton)
136
141
  base_ty.mod.type_params&.each do |(param, default_ty)|
137
- ty_env[param] = default_ty ? default_ty.covariant_vertex(self, changes, ty_env) : Source.new
142
+ ty_env[param] = default_ty ? default_ty.covariant_vertex(self, changes, ty_env) : @untyped_arg
138
143
  end
139
144
  end
140
145
  args = mod.type_params.zip(type_args).map do |(param, default_ty), arg|
141
146
  if changes
142
- (arg || default_ty)&.covariant_vertex(self, changes, ty_env) || Source.new
147
+ (arg || default_ty)&.covariant_vertex(self, changes, ty_env) || @untyped_arg
143
148
  else
144
- Source.new
149
+ @untyped_arg
145
150
  end
146
151
  end
147
152
  Type::Instance.new(self, mod, args)
@@ -374,12 +379,13 @@ module TypeProf::Core
374
379
  @ivar_narrowings = {}
375
380
  @strict_const_scope = false
376
381
  @forward_args = forward_args
382
+ @shadowed_vars = {}
377
383
  # [cpath, names] of the type parameters of the enclosing RBS declaration
378
384
  @sig_type_params = sig_type_params
379
385
  end
380
386
 
381
387
  attr_reader :file_context, :cref, :locals, :return_boxes, :break_vtx, :next_boxes, :strict_const_scope, :sig_type_params
382
- attr_accessor :module_function, :forward_args
388
+ attr_accessor :module_function, :forward_args, :shadowed_vars
383
389
 
384
390
  def path = @file_context&.path
385
391
  def code_range_from_node(node)
@@ -398,6 +404,12 @@ module TypeProf::Core
398
404
  @locals[name] || raise("#{ name }")
399
405
  end
400
406
 
407
+ # A bare `super` passes the variables of the method, not the block
408
+ # parameters that shadow them
409
+ def get_method_var(name)
410
+ @shadowed_vars[name] || get_var(name)
411
+ end
412
+
401
413
  def exist_var?(name)
402
414
  !!@locals[name]
403
415
  end
@@ -443,20 +455,23 @@ module TypeProf::Core
443
455
  end
444
456
 
445
457
  class CRef
446
- def initialize(cpath, scope_level, mid, outer)
458
+ # in_method: whether this is inside a method body, which a define_method
459
+ # block is even though its method name (mid) is not known
460
+ def initialize(cpath, scope_level, mid, outer, in_method: !mid.nil?)
447
461
  @cpath = cpath
448
462
  @scope_level = scope_level
449
463
  @mid = mid
450
464
  @outer = outer
465
+ @in_method = in_method
451
466
  end
452
467
 
453
- attr_reader :cpath, :scope_level, :mid, :outer
468
+ attr_reader :cpath, :scope_level, :mid, :outer, :in_method
454
469
 
455
470
  def get_self(genv)
456
471
  case @scope_level
457
472
  when :instance
458
473
  mod = genv.resolve_cpath(@cpath || [])
459
- type_params = mod.type_params.map {|(_name, _default_ty)| Source.new() } # TODO: better support
474
+ type_params = mod.type_params.map {|(_name, _default_ty)| genv.untyped_arg } # TODO: better support
460
475
  ty = Type::Instance.new(genv, mod, type_params)
461
476
  Source.new(ty)
462
477
  when :class