typeprof 0.32.0 → 0.33.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.
@@ -256,9 +256,20 @@ module TypeProf::Core
256
256
  vtx = @expr.install(genv)
257
257
 
258
258
  a_args = ActualArguments.new([], [], nil, nil)
259
- to_a_vtx = @changes.add_method_call_box(genv, vtx, :to_a, a_args, false, suppress_errors: true).ret
260
-
261
- @changes.add_splat_box(genv, to_a_vtx, nil, vtx).ret
259
+ # Receiver types with no `to_a` are collected here, so that each union
260
+ # member can independently fall back to wrapping itself as `[x]`.
261
+ unresolved_recv = Vertex.new(self)
262
+ to_a_box = @changes.add_method_call_box(
263
+ genv,
264
+ vtx,
265
+ :to_a,
266
+ a_args,
267
+ false,
268
+ suppress_errors: true,
269
+ unresolved_recv: unresolved_recv,
270
+ )
271
+
272
+ @changes.add_splat_box(genv, to_a_box.ret, nil, unresolved_recv).ret
262
273
  end
263
274
  end
264
275
 
@@ -332,8 +343,7 @@ module TypeProf::Core
332
343
  def subnodes = { value:, pat: }
333
344
 
334
345
  def install0(genv)
335
- @value.install(genv)
336
- @pat.install(genv)
346
+ @pat.install_pattern(genv, @value.install(genv))
337
347
  Source.new(genv.nil_type)
338
348
  end
339
349
  end
@@ -350,8 +360,7 @@ module TypeProf::Core
350
360
  def subnodes = { value:, pat: }
351
361
 
352
362
  def install0(genv)
353
- @value.install(genv)
354
- @pat.install(genv)
363
+ @pat.install_pattern(genv, @value.install(genv))
355
364
  Source.new(genv.true_type, genv.false_type)
356
365
  end
357
366
  end
@@ -18,11 +18,15 @@ module TypeProf::Core
18
18
  @body = nil
19
19
  end
20
20
 
21
- @cname_code_range = meta ? nil : lenv.code_range_from_node(raw_node.constant_path)
21
+ @cname_code_range_loc = meta ? nil : raw_node.constant_path
22
22
  @mod_cdef = nil
23
23
  end
24
24
 
25
- attr_reader :tbl, :cpath, :static_cpath, :cname_code_range, :body
25
+ attr_reader :tbl, :cpath, :static_cpath, :body
26
+
27
+ def cname_code_range
28
+ @cname_code_range ||= @lenv.code_range_from_node(@cname_code_range_loc) if @cname_code_range_loc
29
+ end
26
30
 
27
31
  def subnodes = { cpath:, body: }
28
32
  def attrs = { static_cpath:, tbl: }
@@ -22,14 +22,19 @@ module TypeProf::Core
22
22
  def attrs = { rest: }
23
23
  def subnodes = { requireds:, rest_pattern:, posts: }
24
24
 
25
- def install0(genv)
26
- @requireds.each do |pat|
27
- pat.install(genv)
25
+ def install_pattern0(genv, subject)
26
+ @requireds.each_with_index do |pat, i|
27
+ pat.install_pattern(genv, @changes.add_splat_box(genv, subject, i).ret)
28
+ end
29
+ if @rest_pattern
30
+ elem = @changes.add_splat_box(genv, subject).ret
31
+ @rest_pattern.install_pattern(genv, Source.new(genv.gen_ary_type(elem)))
28
32
  end
29
- @rest_pattern.install(genv) if @rest_pattern
30
33
  @posts.each do |pat|
31
- pat.install(genv)
34
+ # TODO: precise indices for post elements (those after `*rest`)
35
+ pat.install_pattern(genv, @changes.add_splat_box(genv, subject).ret)
32
36
  end
37
+ subject
33
38
  end
34
39
  end
35
40
 
@@ -39,7 +44,14 @@ module TypeProf::Core
39
44
  @keys = raw_node.elements.map {|raw_assoc| raw_assoc.key.value.to_sym }
40
45
  @values = raw_node.elements.map {|raw_assoc| AST.create_pattern_node(raw_assoc.value, lenv) }
41
46
  @rest = !!raw_node.rest
42
- @rest_pattern = raw_node.rest && raw_node.rest.value ? AST.create_pattern_node(raw_node.rest.value, lenv) : nil
47
+ @rest_pattern = case raw_node.rest
48
+ when Prism::AssocSplatNode
49
+ AST.create_pattern_node(raw_node.rest.value, lenv) if raw_node.rest.value
50
+ when Prism::NoKeywordsParameterNode, nil
51
+ nil
52
+ else
53
+ raise
54
+ end
43
55
  end
44
56
 
45
57
  attr_reader :keys, :values, :rest, :rest_pattern
@@ -47,32 +59,37 @@ module TypeProf::Core
47
59
  def attrs = { keys:, rest: }
48
60
  def subnodes = { values:, rest_pattern: }
49
61
 
50
- def install0(genv)
62
+ def install_pattern0(genv, subject)
63
+ # TODO: extract each key's value type from `subject` (captures stay untyped for now)
51
64
  @values.each do |pat|
52
- pat.install(genv)
65
+ pat.install_pattern(genv, Vertex.new(self))
53
66
  end
54
- @rest_pattern.install(genv) if @rest_pattern
67
+ @rest_pattern.install_pattern(genv, Vertex.new(self)) if @rest_pattern
68
+ subject
55
69
  end
56
70
  end
57
71
 
58
72
  class FindPatternNode < Node
59
73
  def initialize(raw_node, lenv)
60
74
  super(raw_node, lenv)
61
- @left = raw_node.left ? AST.create_pattern_node(raw_node.left.expression, lenv) : nil
75
+ @left = raw_node.left.expression ? AST.create_pattern_node(raw_node.left.expression, lenv) : nil
62
76
  @requireds = raw_node.requireds.map {|raw_elem| AST.create_pattern_node(raw_elem, lenv) }
63
- @right = raw_node.right ? AST.create_pattern_node(raw_node.right.expression, lenv) : nil
77
+ @right = raw_node.right.expression ? AST.create_pattern_node(raw_node.right.expression, lenv) : nil
64
78
  end
65
79
 
66
80
  attr_reader :left, :requireds, :right
67
81
 
68
82
  def subnodes = { left:, requireds:, right: }
69
83
 
70
- def install0(genv)
71
- @left.install(genv) if @left
84
+ def install_pattern0(genv, subject)
85
+ elem = @changes.add_splat_box(genv, subject).ret
86
+ rest_ary = Source.new(genv.gen_ary_type(elem))
87
+ @left.install_pattern(genv, rest_ary) if @left
72
88
  @requireds.each do |pat|
73
- pat.install(genv)
89
+ pat.install_pattern(genv, elem)
74
90
  end
75
- @right.install(genv) if @right
91
+ @right.install_pattern(genv, rest_ary) if @right
92
+ subject
76
93
  end
77
94
  end
78
95
 
@@ -87,9 +104,10 @@ module TypeProf::Core
87
104
 
88
105
  def subnodes = { left:, right: }
89
106
 
90
- def install0(genv)
91
- @left.install(genv)
92
- @right.install(genv)
107
+ def install_pattern0(genv, subject)
108
+ @left.install_pattern(genv, subject)
109
+ @right.install_pattern(genv, subject)
110
+ subject
93
111
  end
94
112
  end
95
113
 
@@ -104,9 +122,18 @@ module TypeProf::Core
104
122
 
105
123
  def subnodes = { value:, target: }
106
124
 
107
- def install0(genv)
108
- @value.install(genv)
109
- @target.install(genv)
125
+ def install_pattern0(genv, subject)
126
+ @value.install_pattern(genv, subject)
127
+ # For `Const => var`, narrow the capture by the class, as `when Const` does
128
+ narrowed =
129
+ if @value.is_a?(ConstantReadNode) && @value.static_ret
130
+ filtered = subject.new_vertex(genv, self)
131
+ IsAFilter.new(genv, self, filtered, false, @value.static_ret).next_vtx
132
+ else
133
+ subject
134
+ end
135
+ @target.install_pattern(genv, narrowed)
136
+ subject
110
137
  end
111
138
  end
112
139
 
@@ -124,9 +151,10 @@ module TypeProf::Core
124
151
 
125
152
  def subnodes = { cond:, body: }
126
153
 
127
- def install0(genv)
154
+ def install_pattern0(genv, subject)
128
155
  @cond.install(genv)
129
- @body.install(genv)
156
+ @body.install_pattern(genv, subject)
157
+ subject
130
158
  end
131
159
  end
132
160
 
@@ -190,7 +190,7 @@ module TypeProf::Core
190
190
  def initialize(raw_decl, lenv)
191
191
  super(raw_decl, lenv)
192
192
  @mid = raw_decl.name
193
- @mid_code_range = lenv.code_range_from_node(raw_decl.location[:name])
193
+ @mid_code_range_loc = raw_decl.location[:name]
194
194
  @singleton = raw_decl.singleton?
195
195
  @instance = raw_decl.instance?
196
196
  @method_types = OverloadSet.new(raw_decl.overloads.map do |overload|
@@ -200,12 +200,16 @@ module TypeProf::Core
200
200
  @overloading = raw_decl.overloading
201
201
  end
202
202
 
203
- attr_reader :mid, :singleton, :instance, :method_types, :overloading, :mid_code_range
203
+ attr_reader :mid, :singleton, :instance, :method_types, :overloading
204
+
205
+ def mid_code_range
206
+ @mid_code_range ||= @lenv.code_range_from_node(@mid_code_range_loc) if @mid_code_range_loc
207
+ end
204
208
 
205
209
  def subnodes = { method_types: @method_types.to_a }
206
- def attrs = { mid:, mid_code_range:, singleton:, instance:, overloading: }
210
+ def attrs = { mid:, singleton:, instance:, overloading: }
207
211
 
208
- def mname_code_range(_name) = @mid_code_range
212
+ def mname_code_range(_name) = mid_code_range
209
213
 
210
214
  def install0(genv)
211
215
  [[@singleton, true], [@instance, false]].each do |enabled, singleton|
@@ -314,6 +318,55 @@ module TypeProf::Core
314
318
  end
315
319
  end
316
320
 
321
+ class SigExtendNode < Node
322
+ def initialize(raw_decl, lenv)
323
+ super(raw_decl, lenv)
324
+ name = raw_decl.name
325
+ @cpath = name.namespace.path + [name.name]
326
+ @toplevel = name.namespace.absolute?
327
+ @args = raw_decl.args.map {|arg| AST.create_rbs_type(arg, lenv) }
328
+ end
329
+
330
+ attr_reader :cpath, :toplevel, :args
331
+ def subnodes = { args: }
332
+ def attrs = { cpath:, toplevel: }
333
+
334
+ def define0(genv)
335
+ @args.each {|arg| arg.define(genv) }
336
+ const_reads = []
337
+ const_read = BaseConstRead.new(genv, @cpath.first, @toplevel ? CRef::Toplevel : @lenv.cref, true)
338
+ const_reads << const_read
339
+ @cpath[1..].each do |cname|
340
+ const_read = ScopedConstRead.new(cname, const_read, true)
341
+ const_reads << const_read
342
+ end
343
+ mod = genv.resolve_cpath(@lenv.cref.cpath)
344
+ const_read.followers << mod
345
+ mod.add_extend_decl(genv, self)
346
+ const_reads
347
+ end
348
+
349
+ def define_copy(genv)
350
+ mod = genv.resolve_cpath(@lenv.cref.cpath)
351
+ mod.add_extend_decl(genv, self)
352
+ mod.remove_extend_decl(genv, @prev_node)
353
+ super(genv)
354
+ end
355
+
356
+ def undefine0(genv)
357
+ mod = genv.resolve_cpath(@lenv.cref.cpath)
358
+ mod.remove_extend_decl(genv, self)
359
+ @static_ret.each do |const_read|
360
+ const_read.destroy(genv)
361
+ end
362
+ @args.each {|arg| arg.undefine(genv) }
363
+ end
364
+
365
+ def install0(genv)
366
+ Source.new
367
+ end
368
+ end
369
+
317
370
  class SigAliasNode < Node
318
371
  def initialize(raw_decl, lenv)
319
372
  super(raw_decl, lenv)
@@ -23,6 +23,9 @@ module TypeProf::Core
23
23
  if f_mod.module?
24
24
  return true if typecheck_for_prepended_modules(genv, changes, ty, f_mod, f_args, subst)
25
25
  return true if typecheck_for_included_modules(genv, changes, ty, f_mod, f_args, subst)
26
+ if ty.is_a?(Type::Singleton)
27
+ return true if typecheck_for_extended_modules(genv, changes, ty, f_mod, f_args, subst)
28
+ end
26
29
  end
27
30
 
28
31
  ty = genv.get_superclass_type(ty, changes, {})
@@ -81,6 +84,32 @@ module TypeProf::Core
81
84
  return false
82
85
  end
83
86
 
87
+ def self.typecheck_for_extended_modules(genv, changes, a_ty, f_mod, f_args, subst)
88
+ a_ty.mod.extended_modules.each do |ext_decl, ext_mod|
89
+ if ext_decl.is_a?(AST::SigExtendNode) && ext_mod.type_params
90
+ ext_ty = genv.get_instance_type(ext_mod, ext_decl.args, changes, {}, a_ty)
91
+ else
92
+ type_params = ext_mod.type_params.map {|(_name, _default_ty)| Source.new() } # TODO: better support
93
+ ext_ty = Type::Instance.new(genv, ext_mod, type_params)
94
+ end
95
+ if ext_ty.mod == f_mod
96
+ args_all_match = true
97
+ f_args.zip(ext_ty.args) do |f_arg_node, a_arg_vtx|
98
+ unless f_arg_node.typecheck(genv, changes, a_arg_vtx, subst)
99
+ args_all_match = false
100
+ break
101
+ end
102
+ end
103
+ return true if args_all_match
104
+ end
105
+ changes.add_depended_superclass(ext_ty.mod)
106
+
107
+ # The extended module may itself include other modules
108
+ return true if typecheck_for_included_modules(genv, changes, ext_ty, f_mod, f_args, subst)
109
+ end
110
+ return false
111
+ end
112
+
84
113
  class SigFuncType < Node
85
114
  def initialize(raw_decl, raw_type_params, raw_block, lenv)
86
115
  super(raw_decl, lenv)
@@ -465,7 +494,13 @@ module TypeProf::Core
465
494
  decl = tae.decls.each {|decl| break decl }
466
495
  subst0 = subst.dup
467
496
  decl.params.zip(@args) do |param, arg|
468
- subst0[param] = arg.covariant_vertex(genv, changes, subst0)
497
+ if arg.is_a?(SigTyVarNode) && subst[arg.var]
498
+ # Share the vertex so that the types inferred in the alias body flow
499
+ # back into the variable, which makes `U` of `(array[U])` inferrable
500
+ subst0[param] = subst[arg.var]
501
+ else
502
+ subst0[param] = arg.covariant_vertex(genv, changes, subst0)
503
+ end
469
504
  end
470
505
  tae.type.typecheck(genv, changes, vtx, subst0)
471
506
  end
@@ -63,6 +63,12 @@ module TypeProf::Core
63
63
  val
64
64
  end
65
65
 
66
+ def install_pattern0(genv, subject)
67
+ install0(genv)
68
+ @changes.add_edge(genv, subject, @rhs.ret)
69
+ subject
70
+ end
71
+
66
72
  def retrieve_at(pos, &blk)
67
73
  yield self if @var_code_range && @var_code_range.include?(pos)
68
74
  super(pos, &blk)
@@ -283,6 +283,8 @@ module TypeProf::Core
283
283
  case raw_node.name
284
284
  when :include
285
285
  return IncludeMetaNode.new(raw_node, lenv)
286
+ when :extend
287
+ return ExtendMetaNode.new(raw_node, lenv)
286
288
  when :attr_reader
287
289
  return AttrReaderMetaNode.new(raw_node, lenv)
288
290
  when :attr_writer
@@ -460,6 +462,7 @@ module TypeProf::Core
460
462
  when RBS::AST::Members::Prepend
461
463
  SigPrependNode.new(raw_decl, lenv)
462
464
  when RBS::AST::Members::Extend
465
+ SigExtendNode.new(raw_decl, lenv)
463
466
  when RBS::AST::Members::Public
464
467
  when RBS::AST::Members::Private
465
468
  when RBS::AST::Members::Alias
@@ -48,10 +48,33 @@ module TypeProf::Core
48
48
  @positionals + [@keywords],
49
49
  @splat_flags + [false],
50
50
  nil,
51
- @block
51
+ @block,
52
52
  )
53
53
  end
54
54
 
55
+ def prepend_positionals(positionals, splat_flags)
56
+ return self if positionals.empty?
57
+
58
+ ActualArguments.new(positionals + @positionals, splat_flags + @splat_flags, @keywords, @block)
59
+ end
60
+
61
+ def with_keywords(keywords)
62
+ ActualArguments.new(@positionals, @splat_flags, keywords, @block)
63
+ end
64
+
65
+ def with_block(block, omittable: false)
66
+ ActualArguments.new(@positionals, @splat_flags, @keywords, block)
67
+ end
68
+
69
+ def add_box_edges(genv, box)
70
+ @keywords.add_edge(genv, box) if @keywords
71
+ @block.add_edge(genv, box) if @block
72
+ end
73
+
74
+ def normalize_for_method_call(_genv)
75
+ self
76
+ end
77
+
55
78
  def get_rest_args(genv, changes, start_rest, end_rest)
56
79
  vtxs = []
57
80
 
@@ -95,45 +118,175 @@ module TypeProf::Core
95
118
  end
96
119
  end
97
120
 
121
+ class ForwardingActualArguments < ActualArguments
122
+ def initialize(positionals, splat_flags, keywords, block, positionals_omittable, keywords_omittable, block_omittable, activation, activation_required)
123
+ super(positionals, splat_flags, keywords, block)
124
+ @positionals_omittable = positionals_omittable
125
+ @keywords_omittable = keywords_omittable
126
+ @block_omittable = block_omittable
127
+ @activation = activation
128
+ @activation_required = activation_required
129
+ end
130
+
131
+ attr_reader :positionals_omittable, :keywords_omittable, :block_omittable
132
+
133
+ def new_vertexes(genv, node)
134
+ positionals = @positionals.map {|arg| arg.new_vertex(genv, node) }
135
+ splat_flags = @splat_flags
136
+ keywords = @keywords ? @keywords.new_vertex(genv, node) : nil
137
+ block = @block ? @block.new_vertex(genv, node) : nil
138
+ activation = @activation.new_vertex(genv, node)
139
+ ForwardingActualArguments.new(positionals, splat_flags, keywords, block, @positionals_omittable, @keywords_omittable, @block_omittable, activation, @activation_required)
140
+ end
141
+
142
+ def with_keywords_as_last_positional_hash
143
+ return self unless @keywords
144
+
145
+ ForwardingActualArguments.new(
146
+ @positionals + [@keywords],
147
+ @splat_flags + [false],
148
+ nil,
149
+ @block,
150
+ @positionals_omittable + [false],
151
+ false,
152
+ @block_omittable,
153
+ @activation,
154
+ @activation_required,
155
+ )
156
+ end
157
+
158
+ def prepend_positionals(positionals, splat_flags)
159
+ return self if positionals.empty?
160
+
161
+ ForwardingActualArguments.new(
162
+ positionals + @positionals,
163
+ splat_flags + @splat_flags,
164
+ @keywords,
165
+ @block,
166
+ ::Array.new(positionals.size, false) + @positionals_omittable,
167
+ @keywords_omittable,
168
+ @block_omittable,
169
+ @activation,
170
+ @activation_required,
171
+ )
172
+ end
173
+
174
+ def with_keywords(keywords, keywords_omittable: false)
175
+ ForwardingActualArguments.new(
176
+ @positionals,
177
+ @splat_flags,
178
+ keywords,
179
+ @block,
180
+ @positionals_omittable,
181
+ keywords_omittable,
182
+ @block_omittable,
183
+ @activation,
184
+ @activation_required,
185
+ )
186
+ end
187
+
188
+ def with_block(block, omittable: false)
189
+ ForwardingActualArguments.new(
190
+ @positionals,
191
+ @splat_flags,
192
+ @keywords,
193
+ block,
194
+ @positionals_omittable,
195
+ @keywords_omittable,
196
+ omittable,
197
+ @activation,
198
+ @activation_required,
199
+ )
200
+ end
201
+
202
+ def add_box_edges(genv, box)
203
+ @activation.add_edge(genv, box)
204
+ super
205
+ end
206
+
207
+ def normalize_for_method_call(genv)
208
+ if @activation.types.empty?
209
+ return nil if @activation_required
210
+ return self
211
+ end
212
+
213
+ positionals = []
214
+ splat_flags = []
215
+
216
+ @positionals.each_with_index do |arg, i|
217
+ unless @positionals_omittable[i] && @splat_flags[i] && empty_omittable_splat_argument?(genv, arg)
218
+ positionals << arg
219
+ splat_flags << @splat_flags[i]
220
+ end
221
+ end
222
+
223
+ keywords = @keywords
224
+ keywords = nil if @keywords_omittable && keywords && keywords.types.empty?
225
+
226
+ block = @block
227
+ block = nil if @block_omittable && block && block.types.empty?
228
+
229
+ ActualArguments.new(positionals, splat_flags, keywords, block)
230
+ end
231
+
232
+ private
233
+
234
+ def empty_omittable_splat_argument?(genv, arg)
235
+ empty = true
236
+ arg.each_type do |ty|
237
+ ty = ty.base_type(genv)
238
+ unless ty.is_a?(Type::Instance) && ty.mod == genv.mod_ary && ty.args[0] && ty.args[0].types.empty?
239
+ empty = false
240
+ break
241
+ end
242
+ end
243
+ empty
244
+ end
245
+ end
246
+
98
247
  class ForwardingArguments
99
- def initialize(req_positionals, opt_positionals, opt_positional_elems, rest_positionals, post_positionals, req_keyword_pairs, opt_keyword_pairs, rest_keywords, block)
248
+ def initialize(req_positionals, opt_positionals, rest_positionals, post_positionals, req_keyword_pairs, opt_keyword_pairs, rest_keywords, block, activation)
100
249
  @req_positionals = req_positionals
101
250
  @opt_positionals = opt_positionals
102
- @opt_positional_elems = opt_positional_elems
103
251
  @rest_positionals = rest_positionals
104
252
  @post_positionals = post_positionals
105
253
  @req_keyword_pairs = req_keyword_pairs
106
254
  @opt_keyword_pairs = opt_keyword_pairs
107
255
  @rest_keywords = rest_keywords
108
256
  @block = block
257
+ @activation = activation
109
258
  end
110
259
 
111
- attr_reader :block
112
-
113
- def to_actual_arguments(genv, changes, node)
114
- positionals = @req_positionals.dup
260
+ def to_actual_arguments(genv, changes, node, include_leading_positionals: true, activation_required: false)
261
+ positionals = include_leading_positionals ? @req_positionals.dup : []
115
262
  splat_flags = ::Array.new(positionals.size, false)
263
+ positionals_omittable = ::Array.new(positionals.size, false)
116
264
 
117
- @opt_positionals.each do |arg|
118
- positionals << arg
265
+ @opt_positionals.each do |elem_vtx|
266
+ positionals << Source.new(genv.gen_ary_type(elem_vtx))
119
267
  splat_flags << true
268
+ positionals_omittable << true
120
269
  end
121
270
 
122
271
  if @rest_positionals
123
- positionals << @rest_positionals
272
+ positionals << Source.new(genv.gen_ary_type(@rest_positionals))
124
273
  splat_flags << true
274
+ positionals_omittable << true
125
275
  end
126
276
 
127
277
  @post_positionals.each do |arg|
128
278
  positionals << arg
129
279
  splat_flags << false
280
+ positionals_omittable << false
130
281
  end
131
282
 
132
- keywords = build_keyword_args(genv, changes, node)
133
- ActualArguments.new(positionals, splat_flags, keywords, @block)
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)
134
285
  end
135
286
 
136
287
  def accept_actual_arguments(genv, changes, a_args)
288
+ changes.add_edge(genv, Source.new(genv.true_type), @activation)
289
+
137
290
  if a_args.splat_flags.any?
138
291
  start_rest = [a_args.splat_flags.index(true), @req_positionals.size + @opt_positionals.size].min
139
292
  end_rest = [a_args.splat_flags.rindex(true) + 1, a_args.positionals.size - @post_positionals.size].max
@@ -149,7 +302,7 @@ module TypeProf::Core
149
302
  end
150
303
  end
151
304
 
152
- @opt_positional_elems.each_with_index do |elem_vtx, i|
305
+ @opt_positionals.each_with_index do |elem_vtx, i|
153
306
  i += @req_positionals.size
154
307
  if i < start_rest
155
308
  changes.add_edge(genv, a_args.positionals[i], elem_vtx)
@@ -171,6 +324,12 @@ module TypeProf::Core
171
324
  end
172
325
  end
173
326
 
327
+ if @rest_positionals
328
+ rest_vtxs.each do |vtx|
329
+ changes.add_edge(genv, vtx, @rest_positionals)
330
+ end
331
+ end
332
+
174
333
  else
175
334
  @req_positionals.each_with_index do |f_vtx, i|
176
335
  changes.add_edge(genv, a_args.positionals[i], f_vtx)
@@ -184,11 +343,17 @@ module TypeProf::Core
184
343
  start_rest = @req_positionals.size
185
344
  end_rest = a_args.positionals.size - @post_positionals.size
186
345
  i = 0
187
- while i < @opt_positional_elems.size && start_rest < end_rest
188
- changes.add_edge(genv, a_args.positionals[start_rest], @opt_positional_elems[i])
346
+ while i < @opt_positionals.size && start_rest < end_rest
347
+ changes.add_edge(genv, a_args.positionals[start_rest], @opt_positionals[i])
189
348
  i += 1
190
349
  start_rest += 1
191
350
  end
351
+
352
+ if @rest_positionals
353
+ start_rest.upto(end_rest - 1) do |i|
354
+ changes.add_edge(genv, a_args.positionals[i], @rest_positionals)
355
+ end
356
+ end
192
357
  end
193
358
 
194
359
  changes.add_edge(genv, a_args.block, @block) if @block && a_args.block
@@ -222,8 +387,11 @@ module TypeProf::Core
222
387
  private
223
388
 
224
389
  def build_keyword_args(genv, changes, node)
225
- return nil if @req_keyword_pairs.empty? && @opt_keyword_pairs.empty? && !@rest_keywords
226
- return @rest_keywords if @req_keyword_pairs.empty? && @opt_keyword_pairs.empty?
390
+ opt_keyword_pairs = @opt_keyword_pairs
391
+
392
+ if @req_keyword_pairs.empty? && opt_keyword_pairs.empty?
393
+ return @rest_keywords, !!@rest_keywords
394
+ end
227
395
 
228
396
  unified_key = Vertex.new(node)
229
397
  unified_val = Vertex.new(node)
@@ -235,18 +403,22 @@ module TypeProf::Core
235
403
  literal_pairs[name] = vtx
236
404
  end
237
405
 
238
- @opt_keyword_pairs.each do |name, vtx|
406
+ opt_keyword_pairs.each do |name, vtx|
239
407
  changes.add_edge(genv, Source.new(Type::Symbol.new(genv, name)), unified_key)
240
408
  changes.add_edge(genv, vtx, unified_val)
409
+ literal_pairs[name] = vtx
241
410
  end
242
411
 
243
412
  base_hash_type = genv.gen_hash_type(unified_key, unified_val)
244
413
  changes.add_hash_splat_box(genv, @rest_keywords, unified_key, unified_val) if @rest_keywords
245
414
 
246
415
  if literal_pairs.empty?
247
- Source.new(base_hash_type)
416
+ [Source.new(base_hash_type), false]
417
+ elsif @rest_keywords
418
+ 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]
248
420
  else
249
- Source.new(Type::Record.new(genv, literal_pairs, base_hash_type))
421
+ [Source.new(Type::Record.new(genv, literal_pairs, base_hash_type)), false]
250
422
  end
251
423
  end
252
424
  end