typeprof 0.31.1 → 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.
- checksums.yaml +4 -4
- data/README.md +2 -1
- data/doc/report_guide.md +88 -0
- data/lib/typeprof/cli/cli.rb +9 -3
- data/lib/typeprof/code_range.rb +7 -5
- data/lib/typeprof/core/ast/base.rb +31 -6
- data/lib/typeprof/core/ast/call.rb +120 -39
- data/lib/typeprof/core/ast/const.rb +22 -11
- data/lib/typeprof/core/ast/control.rb +62 -32
- data/lib/typeprof/core/ast/meta.rb +279 -14
- data/lib/typeprof/core/ast/method.rb +82 -24
- data/lib/typeprof/core/ast/misc.rb +41 -12
- data/lib/typeprof/core/ast/module.rb +38 -4
- data/lib/typeprof/core/ast/pattern.rb +51 -23
- data/lib/typeprof/core/ast/sig_decl.rb +141 -27
- data/lib/typeprof/core/ast/sig_type.rb +113 -32
- data/lib/typeprof/core/ast/value.rb +14 -6
- data/lib/typeprof/core/ast/variable.rb +17 -4
- data/lib/typeprof/core/ast.rb +98 -14
- data/lib/typeprof/core/builtin.rb +184 -12
- data/lib/typeprof/core/env/method.rb +343 -6
- data/lib/typeprof/core/env/method_entity.rb +18 -15
- data/lib/typeprof/core/env/module_entity.rb +116 -18
- data/lib/typeprof/core/env/static_read.rb +4 -4
- data/lib/typeprof/core/env/type_alias_entity.rb +1 -1
- data/lib/typeprof/core/env/value_entity.rb +25 -3
- data/lib/typeprof/core/env.rb +113 -23
- data/lib/typeprof/core/graph/box.rb +508 -63
- data/lib/typeprof/core/graph/change_set.rb +64 -46
- data/lib/typeprof/core/graph/filter.rb +8 -5
- data/lib/typeprof/core/graph/vertex.rb +20 -19
- data/lib/typeprof/core/service.rb +340 -24
- data/lib/typeprof/core/type.rb +41 -7
- data/lib/typeprof/core/util.rb +6 -0
- data/lib/typeprof/lsp/messages.rb +5 -0
- data/lib/typeprof/lsp/server.rb +35 -4
- data/lib/typeprof/version.rb +1 -1
- metadata +3 -2
|
@@ -17,6 +17,7 @@ module TypeProf::Core
|
|
|
17
17
|
$box_counts[self.class] -= 1
|
|
18
18
|
$box_counts[Box] -= 1
|
|
19
19
|
@destroyed = true
|
|
20
|
+
destroy_symbol_proc_call_boxes(genv)
|
|
20
21
|
@changes.reinstall(genv) # rollback all changes
|
|
21
22
|
end
|
|
22
23
|
|
|
@@ -43,6 +44,24 @@ module TypeProf::Core
|
|
|
43
44
|
raise NotImplementedError
|
|
44
45
|
end
|
|
45
46
|
|
|
47
|
+
def add_symbol_proc_call_box(_changes, genv, sym, caller_positionals, caller_keywords = nil)
|
|
48
|
+
return if caller_positionals.empty?
|
|
49
|
+
|
|
50
|
+
recv = caller_positionals.first
|
|
51
|
+
positionals = caller_positionals[1..]
|
|
52
|
+
@symbol_proc_call_boxes ||= {}
|
|
53
|
+
@symbol_proc_call_boxes[[recv, sym, *positionals, caller_keywords]] ||= begin
|
|
54
|
+
a_args = ActualArguments.new(positionals, ::Array.new(positionals.size, false), caller_keywords, nil)
|
|
55
|
+
MethodCallBox.new(@node, genv, recv, sym, a_args, false)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def destroy_symbol_proc_call_boxes(genv)
|
|
60
|
+
return unless @symbol_proc_call_boxes
|
|
61
|
+
@symbol_proc_call_boxes.each_value { |box| box.destroy(genv) }
|
|
62
|
+
@symbol_proc_call_boxes = nil
|
|
63
|
+
end
|
|
64
|
+
|
|
46
65
|
def to_s
|
|
47
66
|
"#{ self.class.to_s.split("::").last }#{ @id ||= $new_id += 1 }"
|
|
48
67
|
end
|
|
@@ -87,7 +106,7 @@ module TypeProf::Core
|
|
|
87
106
|
mod = genv.resolve_cpath(@node.cpath)
|
|
88
107
|
if mod.type_params && !mod.type_params.empty?
|
|
89
108
|
# Create a substitution map where each type parameter maps to a type variable vertex
|
|
90
|
-
subst = mod.type_params.to_h do |param|
|
|
109
|
+
subst = mod.type_params.to_h do |param, _default_ty|
|
|
91
110
|
type_var_vtx = Vertex.new(@node)
|
|
92
111
|
[param, type_var_vtx]
|
|
93
112
|
end
|
|
@@ -99,6 +118,147 @@ module TypeProf::Core
|
|
|
99
118
|
end
|
|
100
119
|
end
|
|
101
120
|
|
|
121
|
+
class OverloadSet
|
|
122
|
+
include Enumerable
|
|
123
|
+
|
|
124
|
+
def initialize(method_types)
|
|
125
|
+
@method_types = method_types
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def each(&blk) = @method_types.each(&blk)
|
|
129
|
+
def map(&blk) = @method_types.map(&blk)
|
|
130
|
+
def first = @method_types.first
|
|
131
|
+
def size = @method_types.size
|
|
132
|
+
def to_a = @method_types
|
|
133
|
+
|
|
134
|
+
# lazy cache: combination(2) for all-pair comparison
|
|
135
|
+
def overloads_differ_in_args?
|
|
136
|
+
return @overloads_differ_in_args if defined?(@overloads_differ_in_args)
|
|
137
|
+
@overloads_differ_in_args = !@method_types.combination(2).all? { |a, b|
|
|
138
|
+
positionals_match?(a, b) && keywords_match?(a, b)
|
|
139
|
+
}
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def overloads_differ_at_top_level?
|
|
143
|
+
return @overloads_differ_at_top_level if defined?(@overloads_differ_at_top_level)
|
|
144
|
+
@overloads_differ_at_top_level = !@method_types.combination(2).all? { |a, b|
|
|
145
|
+
positionals_match_shallow?(a, b) && keywords_match_shallow?(a, b)
|
|
146
|
+
}
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# lazy cache: overloads split into fixed-arity ones and rest-positional ones
|
|
150
|
+
def partition_by_rest_positionals
|
|
151
|
+
@partition_by_rest_positionals ||=
|
|
152
|
+
@method_types.partition {|method_type| !method_type.rest_positionals }
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
private
|
|
156
|
+
|
|
157
|
+
# Check if two method types have structurally identical positional
|
|
158
|
+
# parameter types (req, opt, rest).
|
|
159
|
+
def positionals_match?(mt1, mt2)
|
|
160
|
+
return false unless mt1.req_positionals.size == mt2.req_positionals.size
|
|
161
|
+
return false unless mt1.opt_positionals.size == mt2.opt_positionals.size
|
|
162
|
+
return false unless mt1.rest_positionals.nil? == mt2.rest_positionals.nil?
|
|
163
|
+
mt1.req_positionals.zip(mt2.req_positionals).all? {|a, b| sig_types_match?(a, b) } &&
|
|
164
|
+
mt1.opt_positionals.zip(mt2.opt_positionals).all? {|a, b| sig_types_match?(a, b) } &&
|
|
165
|
+
(mt1.rest_positionals.nil? || sig_types_match?(mt1.rest_positionals, mt2.rest_positionals))
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Check if two method types have identical positional parameter
|
|
169
|
+
# types at the top level (ignoring type parameter contents).
|
|
170
|
+
def positionals_match_shallow?(mt1, mt2)
|
|
171
|
+
return false unless mt1.req_positionals.size == mt2.req_positionals.size
|
|
172
|
+
return false unless mt1.opt_positionals.size == mt2.opt_positionals.size
|
|
173
|
+
return false unless mt1.rest_positionals.nil? == mt2.rest_positionals.nil?
|
|
174
|
+
mt1.req_positionals.zip(mt2.req_positionals).all? {|a, b| sig_types_match_shallow?(a, b) } &&
|
|
175
|
+
mt1.opt_positionals.zip(mt2.opt_positionals).all? {|a, b| sig_types_match_shallow?(a, b) } &&
|
|
176
|
+
(mt1.rest_positionals.nil? || sig_types_match_shallow?(mt1.rest_positionals, mt2.rest_positionals))
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Check if two method types have structurally identical keyword
|
|
180
|
+
# parameter types (req, opt, rest).
|
|
181
|
+
def keywords_match?(mt1, mt2)
|
|
182
|
+
return false unless mt1.req_keyword_keys == mt2.req_keyword_keys
|
|
183
|
+
return false unless mt1.opt_keyword_keys == mt2.opt_keyword_keys
|
|
184
|
+
return false unless mt1.rest_keywords.nil? == mt2.rest_keywords.nil?
|
|
185
|
+
mt1.req_keyword_values.zip(mt2.req_keyword_values).all? {|a, b| sig_types_match?(a, b) } &&
|
|
186
|
+
mt1.opt_keyword_values.zip(mt2.opt_keyword_values).all? {|a, b| sig_types_match?(a, b) } &&
|
|
187
|
+
(mt1.rest_keywords.nil? || sig_types_match?(mt1.rest_keywords, mt2.rest_keywords))
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Shallow version: compare keyword keys and structure, but use
|
|
191
|
+
# shallow type comparison for values.
|
|
192
|
+
def keywords_match_shallow?(mt1, mt2)
|
|
193
|
+
return false unless mt1.req_keyword_keys == mt2.req_keyword_keys
|
|
194
|
+
return false unless mt1.opt_keyword_keys == mt2.opt_keyword_keys
|
|
195
|
+
return false unless mt1.rest_keywords.nil? == mt2.rest_keywords.nil?
|
|
196
|
+
mt1.req_keyword_values.zip(mt2.req_keyword_values).all? {|a, b| sig_types_match_shallow?(a, b) } &&
|
|
197
|
+
mt1.opt_keyword_values.zip(mt2.opt_keyword_values).all? {|a, b| sig_types_match_shallow?(a, b) } &&
|
|
198
|
+
(mt1.rest_keywords.nil? || sig_types_match_shallow?(mt1.rest_keywords, mt2.rest_keywords))
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Structural equality check for two SigTyNode objects.
|
|
202
|
+
def sig_types_match?(a, b)
|
|
203
|
+
return false unless a.class == b.class
|
|
204
|
+
case a
|
|
205
|
+
when AST::SigTyInstanceNode, AST::SigTyInterfaceNode
|
|
206
|
+
a.cpath == b.cpath &&
|
|
207
|
+
a.args.size == b.args.size &&
|
|
208
|
+
a.args.zip(b.args).all? {|x, y| sig_types_match?(x, y) }
|
|
209
|
+
when AST::SigTySingletonNode
|
|
210
|
+
a.cpath == b.cpath
|
|
211
|
+
when AST::SigTyTupleNode, AST::SigTyUnionNode, AST::SigTyIntersectionNode
|
|
212
|
+
a.types.size == b.types.size &&
|
|
213
|
+
a.types.zip(b.types).all? {|x, y| sig_types_match?(x, y) }
|
|
214
|
+
when AST::SigTyRecordNode
|
|
215
|
+
a.fields.size == b.fields.size &&
|
|
216
|
+
a.fields.all? {|k, v| b.fields[k] && sig_types_match?(v, b.fields[k]) }
|
|
217
|
+
when AST::SigTyOptionalNode, AST::SigTyProcNode
|
|
218
|
+
sig_types_match?(a.type, b.type)
|
|
219
|
+
when AST::SigTyVarNode
|
|
220
|
+
a.var == b.var
|
|
221
|
+
when AST::SigTyLiteralNode
|
|
222
|
+
a.lit == b.lit
|
|
223
|
+
when AST::SigTyAliasNode
|
|
224
|
+
a.cpath == b.cpath && a.name == b.name &&
|
|
225
|
+
a.args.size == b.args.size &&
|
|
226
|
+
a.args.zip(b.args).all? {|x, y| sig_types_match?(x, y) }
|
|
227
|
+
else
|
|
228
|
+
true # Leaf types (bool, nil, self, void, untyped, etc.)
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Shallow structural equality: compare only the top-level type
|
|
233
|
+
# identity without recursing into type parameters.
|
|
234
|
+
def sig_types_match_shallow?(a, b)
|
|
235
|
+
return false unless a.class == b.class
|
|
236
|
+
case a
|
|
237
|
+
when AST::SigTyInstanceNode, AST::SigTyInterfaceNode
|
|
238
|
+
a.cpath == b.cpath
|
|
239
|
+
when AST::SigTySingletonNode
|
|
240
|
+
a.cpath == b.cpath
|
|
241
|
+
when AST::SigTyTupleNode
|
|
242
|
+
a.types.size == b.types.size
|
|
243
|
+
when AST::SigTyUnionNode, AST::SigTyIntersectionNode
|
|
244
|
+
a.types.size == b.types.size &&
|
|
245
|
+
a.types.zip(b.types).all? {|x, y| sig_types_match_shallow?(x, y) }
|
|
246
|
+
when AST::SigTyRecordNode
|
|
247
|
+
a.fields.keys.sort == b.fields.keys.sort
|
|
248
|
+
when AST::SigTyOptionalNode, AST::SigTyProcNode
|
|
249
|
+
true
|
|
250
|
+
when AST::SigTyVarNode
|
|
251
|
+
a.var == b.var
|
|
252
|
+
when AST::SigTyLiteralNode
|
|
253
|
+
a.lit == b.lit
|
|
254
|
+
when AST::SigTyAliasNode
|
|
255
|
+
a.cpath == b.cpath && a.name == b.name
|
|
256
|
+
else
|
|
257
|
+
true
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
102
262
|
class MethodDeclBox < Box
|
|
103
263
|
def initialize(node, genv, cpath, singleton, mid, method_types, overloading)
|
|
104
264
|
super(node)
|
|
@@ -123,6 +283,7 @@ module TypeProf::Core
|
|
|
123
283
|
me = genv.resolve_method(@cpath, @singleton, @mid)
|
|
124
284
|
me.remove_decl(self)
|
|
125
285
|
me.add_run_all_method_call_boxes(genv)
|
|
286
|
+
destroy_symbol_proc_call_boxes(genv)
|
|
126
287
|
end
|
|
127
288
|
|
|
128
289
|
def match_arguments?(genv, changes, param_map, a_args, method_type)
|
|
@@ -179,14 +340,30 @@ module TypeProf::Core
|
|
|
179
340
|
end
|
|
180
341
|
end
|
|
181
342
|
|
|
343
|
+
# Check keyword arguments by inspecting the keywords vertex types
|
|
344
|
+
# directly. We avoid get_keyword_arg here because it creates a fresh
|
|
345
|
+
# Vertex each call, which would destabilize the change-set edges and
|
|
346
|
+
# cause oscillation when match_arguments? runs on every box re-eval.
|
|
347
|
+
if a_args.keywords
|
|
348
|
+
method_type.req_keyword_keys.zip(method_type.req_keyword_values) do |key, ty|
|
|
349
|
+
return false unless keyword_arg_typecheck?(genv, changes, a_args.keywords, key, ty, param_map)
|
|
350
|
+
end
|
|
351
|
+
method_type.opt_keyword_keys.zip(method_type.opt_keyword_values) do |key, ty|
|
|
352
|
+
return false unless keyword_arg_typecheck?(genv, changes, a_args.keywords, key, ty, param_map)
|
|
353
|
+
end
|
|
354
|
+
if method_type.rest_keywords
|
|
355
|
+
return false unless rest_keyword_args_typecheck?(genv, changes, a_args.keywords, method_type, param_map)
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
|
|
182
359
|
return true
|
|
183
360
|
end
|
|
184
361
|
|
|
185
362
|
def resolve_overload(changes, genv, method_type, node, param_map, a_args, ret, force)
|
|
186
363
|
param_map0 = param_map.dup
|
|
187
364
|
if method_type.type_params
|
|
188
|
-
method_type.type_params.zip(yield(method_type)) do |var, vtx|
|
|
189
|
-
param_map0[var] = vtx
|
|
365
|
+
method_type.type_params.zip(yield(method_type)) do |(var, _default_ty), vtx|
|
|
366
|
+
param_map0[var] = vtx # TODO: default_ty?
|
|
190
367
|
end
|
|
191
368
|
end
|
|
192
369
|
|
|
@@ -213,6 +390,7 @@ module TypeProf::Core
|
|
|
213
390
|
end
|
|
214
391
|
return false
|
|
215
392
|
end
|
|
393
|
+
|
|
216
394
|
if rbs_blk && a_args.block
|
|
217
395
|
# rbs_blk_func.optional_keywords, ...
|
|
218
396
|
blk_a_args = rbs_blk.req_positionals.map do |blk_a_arg|
|
|
@@ -230,6 +408,8 @@ module TypeProf::Core
|
|
|
230
408
|
end
|
|
231
409
|
end
|
|
232
410
|
end
|
|
411
|
+
when Type::Symbol
|
|
412
|
+
resolve_symbol_proc(changes, genv, ty.sym, blk_a_args, rbs_blk, param_map0)
|
|
233
413
|
end
|
|
234
414
|
end
|
|
235
415
|
end
|
|
@@ -244,6 +424,13 @@ module TypeProf::Core
|
|
|
244
424
|
end
|
|
245
425
|
end
|
|
246
426
|
|
|
427
|
+
def resolve_symbol_proc(changes, genv, sym, blk_a_args, rbs_blk, param_map)
|
|
428
|
+
box = add_symbol_proc_call_box(changes, genv, sym, blk_a_args)
|
|
429
|
+
return unless box
|
|
430
|
+
|
|
431
|
+
rbs_blk.return_type.typecheck(genv, changes, box.ret, param_map)
|
|
432
|
+
end
|
|
433
|
+
|
|
247
434
|
def resolve_overloads(changes, genv, node, param_map, a_args, ret, &blk)
|
|
248
435
|
if @method_types.size == 1
|
|
249
436
|
method_type = @method_types.first
|
|
@@ -251,11 +438,67 @@ module TypeProf::Core
|
|
|
251
438
|
return
|
|
252
439
|
end
|
|
253
440
|
|
|
441
|
+
# If any positional argument has no type information, we cannot
|
|
442
|
+
# determine which overload to select. Return silently (untyped)
|
|
443
|
+
# rather than attempting to match. This prevents oscillation in
|
|
444
|
+
# cyclic cases and avoids false "failed to resolve overloads"
|
|
445
|
+
# diagnostics for untyped arguments.
|
|
446
|
+
#
|
|
447
|
+
# We check at two levels:
|
|
448
|
+
# 1. Top-level empty vertices are always uninformative.
|
|
449
|
+
# 2. Empty type parameter vertices (e.g., Array[T] where T is
|
|
450
|
+
# empty) are only uninformative when overloads differ solely
|
|
451
|
+
# in their type parameters (e.g., Array[Integer] vs
|
|
452
|
+
# Array[String]). When overloads differ at the top level
|
|
453
|
+
# (e.g., Integer vs Float), the type parameter contents are
|
|
454
|
+
# irrelevant for overload selection and should not trigger
|
|
455
|
+
# bail-out.
|
|
456
|
+
has_uninformative_args = if @method_types.overloads_differ_in_args?
|
|
457
|
+
# Check whether overloads also differ at the top level (e.g.,
|
|
458
|
+
# Integer vs Float) or only in their type parameters (e.g.,
|
|
459
|
+
# Array[Integer] vs Array[String]).
|
|
460
|
+
if @method_types.overloads_differ_at_top_level?
|
|
461
|
+
# Overloads are distinguished by top-level types.
|
|
462
|
+
# Only top-level empty vertices matter; empty type parameters
|
|
463
|
+
# are irrelevant for overload selection.
|
|
464
|
+
# However, splatted arguments have their elements extracted
|
|
465
|
+
# during matching, so also check splat element vertices.
|
|
466
|
+
a_args.positionals.any? {|vtx| vtx.types.empty? } ||
|
|
467
|
+
splat_elements_uninformative?(genv, a_args) ||
|
|
468
|
+
(a_args.keywords && a_args.keywords.types.empty?)
|
|
469
|
+
else
|
|
470
|
+
# Overloads differ only in type parameters (e.g.,
|
|
471
|
+
# Array[Integer] vs Array[String]). Empty type parameter
|
|
472
|
+
# vertices can cause oscillation, so check recursively.
|
|
473
|
+
a_args.positionals.any? {|vtx| vertex_uninformative?(genv, vtx) } ||
|
|
474
|
+
(a_args.keywords && vertex_uninformative?(genv, a_args.keywords))
|
|
475
|
+
end
|
|
476
|
+
else
|
|
477
|
+
a_args.positionals.any? {|vtx| vtx.types.empty? } ||
|
|
478
|
+
(a_args.keywords && a_args.keywords.types.empty?)
|
|
479
|
+
end
|
|
480
|
+
if has_uninformative_args
|
|
481
|
+
a_args.positionals.each do |vtx|
|
|
482
|
+
changes.add_edge(genv, vtx, changes.target)
|
|
483
|
+
end
|
|
484
|
+
# Note: keywords already have a permanent edge to the box
|
|
485
|
+
# (established in MethodCallBox#initialize), so no extra edge needed.
|
|
486
|
+
return
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
# A splatted call can match only a rest-positional overload; otherwise prefer
|
|
490
|
+
# the fixed-arity ones, as a rest-positional one is usually a catch-all
|
|
491
|
+
overload_groups =
|
|
492
|
+
a_args.splat_flags.any? ? [@method_types] : @method_types.partition_by_rest_positionals
|
|
493
|
+
|
|
254
494
|
match_any_overload = false
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
495
|
+
overload_groups.each do |method_types|
|
|
496
|
+
method_types.each do |method_type|
|
|
497
|
+
if resolve_overload(changes, genv, method_type, node, param_map, a_args, ret, false, &blk)
|
|
498
|
+
match_any_overload = true
|
|
499
|
+
end
|
|
258
500
|
end
|
|
501
|
+
break if match_any_overload
|
|
259
502
|
end
|
|
260
503
|
unless match_any_overload
|
|
261
504
|
meth = node.mid_code_range ? :mid_code_range : :code_range
|
|
@@ -263,6 +506,76 @@ module TypeProf::Core
|
|
|
263
506
|
end
|
|
264
507
|
end
|
|
265
508
|
|
|
509
|
+
# Check if any splatted argument has an Array element vertex
|
|
510
|
+
# that is empty. Splat expansion extracts elements during
|
|
511
|
+
# overload matching, so empty element types can cause oscillation
|
|
512
|
+
# even when the top-level Array type is present.
|
|
513
|
+
def splat_elements_uninformative?(genv, a_args)
|
|
514
|
+
a_args.positionals.each_with_index do |vtx, i|
|
|
515
|
+
next unless a_args.splat_flags[i]
|
|
516
|
+
vtx.each_type do |ty|
|
|
517
|
+
base = ty.base_type(genv)
|
|
518
|
+
if base.is_a?(Type::Instance) && base.mod == genv.mod_ary && base.args[0]
|
|
519
|
+
return true if base.args[0].types.empty?
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
end
|
|
523
|
+
false
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
def vertex_uninformative?(genv, vtx, depth = 0)
|
|
527
|
+
return true if vtx.types.empty?
|
|
528
|
+
return false if depth > 3
|
|
529
|
+
vtx.each_type do |ty|
|
|
530
|
+
base = ty.base_type(genv)
|
|
531
|
+
next unless base.is_a?(Type::Instance) && !base.args.empty?
|
|
532
|
+
base.args.each do |arg_vtx|
|
|
533
|
+
return true if arg_vtx && vertex_uninformative?(genv, arg_vtx, depth + 1)
|
|
534
|
+
end
|
|
535
|
+
end
|
|
536
|
+
false
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
# Typecheck a single keyword argument value against the expected type
|
|
540
|
+
# by directly inspecting the pre-existing value vertices in the
|
|
541
|
+
# keywords vertex's types (Record, Hash, Instance).
|
|
542
|
+
def keyword_arg_typecheck?(genv, changes, keywords_vtx, key, expected_ty, param_map)
|
|
543
|
+
keywords_vtx.each_type do |kw_ty|
|
|
544
|
+
val_vtx = case kw_ty
|
|
545
|
+
when Type::Hash then kw_ty.get_value(key)
|
|
546
|
+
when Type::Record then kw_ty.get_value(key)
|
|
547
|
+
when Type::Instance then kw_ty.mod == genv.mod_hash ? kw_ty.args[1] : nil
|
|
548
|
+
else nil
|
|
549
|
+
end
|
|
550
|
+
return false if val_vtx && !expected_ty.typecheck(genv, changes, val_vtx, param_map)
|
|
551
|
+
end
|
|
552
|
+
true
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
# Typecheck rest keyword argument values (those not consumed by named
|
|
556
|
+
# keywords) against the method type's rest_keywords type.
|
|
557
|
+
def rest_keyword_args_typecheck?(genv, changes, keywords_vtx, method_type, param_map)
|
|
558
|
+
named_keys = method_type.req_keyword_keys + method_type.opt_keyword_keys
|
|
559
|
+
rest_ty = method_type.rest_keywords
|
|
560
|
+
keywords_vtx.each_type do |kw_ty|
|
|
561
|
+
case kw_ty
|
|
562
|
+
when Type::Record
|
|
563
|
+
kw_ty.fields.each do |key, val_vtx|
|
|
564
|
+
next if named_keys.include?(key)
|
|
565
|
+
return false unless rest_ty.typecheck(genv, changes, val_vtx, param_map)
|
|
566
|
+
end
|
|
567
|
+
when Type::Hash
|
|
568
|
+
val_vtx = kw_ty.base_type(genv).args[1]
|
|
569
|
+
return false if val_vtx && !rest_ty.typecheck(genv, changes, val_vtx, param_map)
|
|
570
|
+
when Type::Instance
|
|
571
|
+
if kw_ty.mod == genv.mod_hash && kw_ty.args[1]
|
|
572
|
+
return false unless rest_ty.typecheck(genv, changes, kw_ty.args[1], param_map)
|
|
573
|
+
end
|
|
574
|
+
end
|
|
575
|
+
end
|
|
576
|
+
true
|
|
577
|
+
end
|
|
578
|
+
|
|
266
579
|
def show
|
|
267
580
|
@method_types.map do |method_type|
|
|
268
581
|
args = []
|
|
@@ -279,10 +592,10 @@ module TypeProf::Core
|
|
|
279
592
|
args << arg.show
|
|
280
593
|
end
|
|
281
594
|
|
|
282
|
-
method_type.
|
|
595
|
+
method_type.req_keyword_keys.zip(method_type.req_keyword_values) do |key, arg|
|
|
283
596
|
args << "#{ key }: #{arg.show}"
|
|
284
597
|
end
|
|
285
|
-
method_type.
|
|
598
|
+
method_type.opt_keyword_keys.zip(method_type.opt_keyword_values) do |key, arg|
|
|
286
599
|
args << "?#{ key }: #{arg.show}"
|
|
287
600
|
end
|
|
288
601
|
if method_type.rest_keywords
|
|
@@ -311,7 +624,6 @@ module TypeProf::Core
|
|
|
311
624
|
|
|
312
625
|
def wrong_return_type(f_ret_show, changes)
|
|
313
626
|
actual_ty = @a_ret.show
|
|
314
|
-
return if actual_ty == "untyped" # XXX: too ad-hoc?
|
|
315
627
|
msg = "expected: #{ f_ret_show }; actual: #{ actual_ty }"
|
|
316
628
|
case @node
|
|
317
629
|
when AST::ReturnNode
|
|
@@ -331,11 +643,13 @@ module TypeProf::Core
|
|
|
331
643
|
end
|
|
332
644
|
|
|
333
645
|
class SplatBox < Box
|
|
334
|
-
def initialize(node, genv, ary, idx)
|
|
646
|
+
def initialize(node, genv, ary, idx, unresolved_recv = nil)
|
|
335
647
|
super(node)
|
|
336
648
|
@ary = ary
|
|
337
649
|
@idx = idx
|
|
650
|
+
@unresolved_recv = unresolved_recv
|
|
338
651
|
@ary.add_edge(genv, self)
|
|
652
|
+
@unresolved_recv.add_edge(genv, self) if @unresolved_recv
|
|
339
653
|
@ret = Vertex.new(node)
|
|
340
654
|
end
|
|
341
655
|
|
|
@@ -360,6 +674,39 @@ module TypeProf::Core
|
|
|
360
674
|
"???"
|
|
361
675
|
end
|
|
362
676
|
end
|
|
677
|
+
# For types where to_a is not defined, [*x] wraps x as [x]
|
|
678
|
+
if @unresolved_recv
|
|
679
|
+
@unresolved_recv.each_type do |ty|
|
|
680
|
+
changes.add_edge(genv, Source.new(ty), @ret)
|
|
681
|
+
end
|
|
682
|
+
end
|
|
683
|
+
end
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
# Merges the keywords being forwarded into the `**rest` hash while keeping the
|
|
687
|
+
# rest's own fields, so that the callee can still tell the two apart.
|
|
688
|
+
class KeywordMergeBox < Box
|
|
689
|
+
def initialize(node, genv, rest, literal_pairs, fallback)
|
|
690
|
+
super(node)
|
|
691
|
+
@rest = rest
|
|
692
|
+
@literal_pairs = literal_pairs
|
|
693
|
+
@fallback = fallback
|
|
694
|
+
@rest.add_edge(genv, self)
|
|
695
|
+
@ret = Vertex.new(node)
|
|
696
|
+
end
|
|
697
|
+
|
|
698
|
+
attr_reader :ret
|
|
699
|
+
|
|
700
|
+
def run0(genv, changes)
|
|
701
|
+
merged = false
|
|
702
|
+
@rest.each_type do |ty|
|
|
703
|
+
if ty.is_a?(Type::Record)
|
|
704
|
+
fields = ty.fields.merge(@literal_pairs)
|
|
705
|
+
changes.add_edge(genv, Source.new(Type::Record.new(genv, fields, ty.base_type(genv))), @ret)
|
|
706
|
+
merged = true
|
|
707
|
+
end
|
|
708
|
+
end
|
|
709
|
+
changes.add_edge(genv, @fallback, @ret) unless merged
|
|
363
710
|
end
|
|
364
711
|
end
|
|
365
712
|
|
|
@@ -418,7 +765,7 @@ module TypeProf::Core
|
|
|
418
765
|
|
|
419
766
|
attr_accessor :node
|
|
420
767
|
|
|
421
|
-
attr_reader :cpath, :singleton, :mid, :f_args, :ret
|
|
768
|
+
attr_reader :cpath, :singleton, :mid, :f_args, :ret, :record_block
|
|
422
769
|
|
|
423
770
|
def destroy(genv)
|
|
424
771
|
me = genv.resolve_method(@cpath, @singleton, @mid)
|
|
@@ -443,17 +790,17 @@ module TypeProf::Core
|
|
|
443
790
|
ty = Type::Singleton.new(genv, mod)
|
|
444
791
|
param_map0 = Type.default_param_map(genv, ty)
|
|
445
792
|
else
|
|
446
|
-
type_params = mod.type_params.map {|
|
|
793
|
+
type_params = mod.type_params.map {|(_name, _default_ty)| Source.new() } # TODO: better support
|
|
447
794
|
ty = Type::Instance.new(genv, mod, type_params)
|
|
448
795
|
param_map0 = Type.default_param_map(genv, ty)
|
|
449
796
|
if ty.is_a?(Type::Instance)
|
|
450
|
-
ty.mod.type_params.zip(ty.args) do |
|
|
451
|
-
param_map0[
|
|
797
|
+
ty.mod.type_params.zip(ty.args) do |(name, _default_ty), arg|
|
|
798
|
+
param_map0[name] = arg
|
|
452
799
|
end
|
|
453
800
|
end
|
|
454
801
|
end
|
|
455
|
-
method_type.type_params.each do |
|
|
456
|
-
param_map0[
|
|
802
|
+
method_type.type_params.each do |name, _default_ty|
|
|
803
|
+
param_map0[name] = Source.new()
|
|
457
804
|
end
|
|
458
805
|
|
|
459
806
|
positional_args = []
|
|
@@ -517,7 +864,7 @@ module TypeProf::Core
|
|
|
517
864
|
end
|
|
518
865
|
end
|
|
519
866
|
@f_args.opt_positionals.each_with_index do |f_vtx, i|
|
|
520
|
-
i += @f_args.
|
|
867
|
+
i += @f_args.req_positionals.size
|
|
521
868
|
if i < start_rest
|
|
522
869
|
changes.add_edge(genv, a_args.positionals[i], f_vtx)
|
|
523
870
|
else
|
|
@@ -599,16 +946,39 @@ module TypeProf::Core
|
|
|
599
946
|
end
|
|
600
947
|
|
|
601
948
|
if @node.rest_keywords
|
|
602
|
-
|
|
603
|
-
|
|
949
|
+
named_keys = @node.req_keywords + @node.opt_keywords
|
|
950
|
+
a_args.keywords.each_type do |kw_ty|
|
|
951
|
+
case kw_ty
|
|
952
|
+
when Type::Record
|
|
953
|
+
rest_fields = kw_ty.fields.reject {|key, _| named_keys.include?(key) }
|
|
954
|
+
base = kw_ty.base_type(genv)
|
|
955
|
+
rest_record = Type::Record.new(genv, rest_fields, base)
|
|
956
|
+
changes.add_edge(genv, Source.new(rest_record), @f_args.rest_keywords)
|
|
957
|
+
when Type::Hash, Type::Instance
|
|
958
|
+
changes.add_edge(genv, Source.new(kw_ty), @f_args.rest_keywords)
|
|
959
|
+
end
|
|
960
|
+
end
|
|
604
961
|
end
|
|
605
962
|
end
|
|
606
963
|
|
|
607
964
|
return true
|
|
608
965
|
end
|
|
609
966
|
|
|
967
|
+
def normalize_keyword_hash_argument_for_def(a_args)
|
|
968
|
+
return a_args unless a_args.keywords
|
|
969
|
+
return a_args if @node.no_keywords
|
|
970
|
+
return a_args if @node.rest_keywords
|
|
971
|
+
return a_args unless @node.req_keywords.empty? && @node.opt_keywords.empty?
|
|
972
|
+
|
|
973
|
+
a_args.with_keywords_as_last_positional_hash
|
|
974
|
+
end
|
|
975
|
+
|
|
610
976
|
def call(changes, genv, a_args, ret)
|
|
977
|
+
a_args = normalize_keyword_hash_argument_for_def(a_args)
|
|
611
978
|
if pass_arguments(changes, genv, a_args)
|
|
979
|
+
if @node.is_a?(AST::DefNode)
|
|
980
|
+
@node.body.lenv.forward_args&.accept_actual_arguments(genv, changes, a_args)
|
|
981
|
+
end
|
|
612
982
|
changes.add_edge(genv, a_args.block, @f_args.block) if @f_args.block && a_args.block
|
|
613
983
|
|
|
614
984
|
changes.add_edge(genv, @ret, ret)
|
|
@@ -635,7 +1005,9 @@ module TypeProf::Core
|
|
|
635
1005
|
@f_args.post_positionals.each do |var|
|
|
636
1006
|
args << Type.strip_parens(var.show)
|
|
637
1007
|
end
|
|
638
|
-
if @node.
|
|
1008
|
+
if @node.respond_to?(:req_keywords) &&
|
|
1009
|
+
@node.req_keywords.size == @f_args.req_keywords.size &&
|
|
1010
|
+
@node.opt_keywords.size == @f_args.opt_keywords.size
|
|
639
1011
|
@node.req_keywords.zip(@f_args.req_keywords) do |name, f_vtx|
|
|
640
1012
|
args << "#{ name }: #{Type.strip_parens(f_vtx.show)}"
|
|
641
1013
|
end
|
|
@@ -651,11 +1023,11 @@ module TypeProf::Core
|
|
|
651
1023
|
names = []
|
|
652
1024
|
names.concat(@node.req_positionals)
|
|
653
1025
|
names.concat(@node.opt_positionals)
|
|
654
|
-
names
|
|
1026
|
+
names << @node.rest_positionals if @node.rest_positionals
|
|
655
1027
|
names.concat(@node.post_positionals)
|
|
656
1028
|
names.concat(@node.req_keywords)
|
|
657
1029
|
names.concat(@node.opt_keywords)
|
|
658
|
-
names
|
|
1030
|
+
names << @node.rest_keywords if @node.rest_keywords
|
|
659
1031
|
args = args.zip(names).map do |arg, name|
|
|
660
1032
|
name ? "#{ arg } #{ name }" : arg
|
|
661
1033
|
end
|
|
@@ -702,35 +1074,50 @@ module TypeProf::Core
|
|
|
702
1074
|
end
|
|
703
1075
|
|
|
704
1076
|
class MethodCallBox < Box
|
|
705
|
-
|
|
1077
|
+
# `unresolved_recv`, when given, collects the receiver types for which no
|
|
1078
|
+
# method entity was found. Without it those types only become "undefined
|
|
1079
|
+
# method" diagnostics; with it the caller can handle them itself, as `[*x]`
|
|
1080
|
+
# does to wrap a receiver that has no `to_a`.
|
|
1081
|
+
def initialize(node, genv, recv, mid, a_args, subclasses, suppress_errors: false, unresolved_recv: nil)
|
|
706
1082
|
raise mid.to_s unless mid
|
|
707
1083
|
super(node)
|
|
708
1084
|
@recv = recv.new_vertex(genv, node)
|
|
709
1085
|
@recv.add_edge(genv, self)
|
|
710
1086
|
@mid = mid
|
|
711
1087
|
@a_args = a_args.new_vertexes(genv, node)
|
|
712
|
-
@a_args.
|
|
713
|
-
@a_args.block.add_edge(genv, self) if @a_args.block
|
|
1088
|
+
@a_args.add_box_edges(genv, self)
|
|
714
1089
|
@ret = Vertex.new(node)
|
|
715
1090
|
@subclasses = subclasses
|
|
1091
|
+
@suppress_errors = suppress_errors
|
|
1092
|
+
@unresolved_recv = unresolved_recv
|
|
716
1093
|
@generics = {}
|
|
717
1094
|
end
|
|
718
1095
|
|
|
719
1096
|
attr_reader :recv, :mid, :ret
|
|
720
1097
|
|
|
721
1098
|
def run0(genv, changes)
|
|
722
|
-
|
|
723
|
-
|
|
1099
|
+
a_args = @a_args.normalize_for_method_call(genv)
|
|
1100
|
+
return unless a_args
|
|
1101
|
+
|
|
1102
|
+
edges = Set.empty
|
|
1103
|
+
called_mdefs = Set.empty
|
|
724
1104
|
error_count = 0
|
|
725
1105
|
resolve(genv, changes) do |me, ty, mid, orig_ty|
|
|
726
|
-
if
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
1106
|
+
if @node.is_a?(AST::YieldNode) && mid == :call && orig_ty.is_a?(Type::Symbol)
|
|
1107
|
+
box = add_symbol_proc_call_box(changes, genv, orig_ty.sym, a_args.positionals, a_args.keywords)
|
|
1108
|
+
changes.add_edge(genv, box.ret, @ret) if box
|
|
1109
|
+
elsif !me
|
|
1110
|
+
if @unresolved_recv
|
|
1111
|
+
changes.add_edge(genv, Source.new(orig_ty), @unresolved_recv)
|
|
1112
|
+
end
|
|
1113
|
+
unless @suppress_errors
|
|
1114
|
+
if error_count < 3
|
|
1115
|
+
meth = @node.mid_code_range ? :mid_code_range : :code_range
|
|
1116
|
+
changes.add_diagnostic(meth, "undefined method: #{ orig_ty.show }##{ mid }")
|
|
1117
|
+
end
|
|
731
1118
|
end
|
|
732
1119
|
error_count += 1
|
|
733
|
-
elsif me.builtin && me.builtin[changes, @node, orig_ty,
|
|
1120
|
+
elsif me.builtin && me.builtin[changes, @node, orig_ty, a_args, @ret]
|
|
734
1121
|
# do nothing
|
|
735
1122
|
elsif !me.decls.empty?
|
|
736
1123
|
# TODO: support "| ..."
|
|
@@ -739,19 +1126,19 @@ module TypeProf::Core
|
|
|
739
1126
|
# TODO: add_depended_method_entity for types used to resolve overloads
|
|
740
1127
|
ty_env = Type.default_param_map(genv, orig_ty)
|
|
741
1128
|
if ty.is_a?(Type::Instance)
|
|
742
|
-
ty.mod.type_params.zip(ty.args) do |param, arg|
|
|
743
|
-
ty_env[param] = arg
|
|
1129
|
+
ty.mod.type_params.zip(ty.args) do |(param, default_ty), arg|
|
|
1130
|
+
ty_env[param] = arg || (default_ty ? default_ty.covariant_vertex(genv, changes, ty_env) : Source.new)
|
|
744
1131
|
end
|
|
745
1132
|
end
|
|
746
|
-
mdecl.resolve_overloads(changes, genv, @node, ty_env,
|
|
747
|
-
@generics[method_type] ||= method_type.type_params.map {
|
|
1133
|
+
mdecl.resolve_overloads(changes, genv, @node, ty_env, a_args, @ret) do |method_type|
|
|
1134
|
+
@generics[method_type] ||= method_type.type_params.map { Vertex.new(@node) }
|
|
748
1135
|
end
|
|
749
1136
|
end
|
|
750
1137
|
elsif !me.defs.empty?
|
|
751
1138
|
me.defs.each do |mdef|
|
|
752
1139
|
next if called_mdefs.include?(mdef)
|
|
753
1140
|
called_mdefs << mdef
|
|
754
|
-
mdef.call(changes, genv,
|
|
1141
|
+
mdef.call(changes, genv, a_args, @ret)
|
|
755
1142
|
end
|
|
756
1143
|
else
|
|
757
1144
|
pp me
|
|
@@ -764,7 +1151,7 @@ module TypeProf::Core
|
|
|
764
1151
|
me.defs.each do |mdef|
|
|
765
1152
|
next if called_mdefs.include?(mdef)
|
|
766
1153
|
called_mdefs << mdef
|
|
767
|
-
mdef.call(changes, genv,
|
|
1154
|
+
mdef.call(changes, genv, a_args, @ret)
|
|
768
1155
|
end
|
|
769
1156
|
end
|
|
770
1157
|
end
|
|
@@ -772,7 +1159,7 @@ module TypeProf::Core
|
|
|
772
1159
|
edges.each do |src, dst|
|
|
773
1160
|
changes.add_edge(genv, src, dst)
|
|
774
1161
|
end
|
|
775
|
-
if error_count > 3
|
|
1162
|
+
if error_count > 3 && !@suppress_errors
|
|
776
1163
|
meth = @node.mid_code_range ? :mid_code_range : :code_range
|
|
777
1164
|
changes.add_diagnostic(meth, "... and other #{ error_count - 3 } errors")
|
|
778
1165
|
end
|
|
@@ -780,7 +1167,7 @@ module TypeProf::Core
|
|
|
780
1167
|
|
|
781
1168
|
def resolve(genv, changes, &blk)
|
|
782
1169
|
@recv.each_type do |orig_ty|
|
|
783
|
-
next if orig_ty ==
|
|
1170
|
+
next if orig_ty == genv.bot_type
|
|
784
1171
|
if @mid == :"*super"
|
|
785
1172
|
mid = @node.lenv.cref.mid
|
|
786
1173
|
skip = true
|
|
@@ -822,7 +1209,10 @@ module TypeProf::Core
|
|
|
822
1209
|
skip = false
|
|
823
1210
|
|
|
824
1211
|
if ty.is_a?(Type::Singleton)
|
|
825
|
-
#
|
|
1212
|
+
# Check extended modules (their instance methods are singleton methods here)
|
|
1213
|
+
break if resolve_extended_modules(genv, changes, base_ty_env, ty, mid) do |me, ty, mid|
|
|
1214
|
+
yield me, ty, mid, orig_ty
|
|
1215
|
+
end
|
|
826
1216
|
else
|
|
827
1217
|
# Finally check included modules
|
|
828
1218
|
break if resolve_included_modules(genv, changes, base_ty_env, ty, mid) do |me, ty, mid|
|
|
@@ -846,7 +1236,7 @@ module TypeProf::Core
|
|
|
846
1236
|
if prep_decl.is_a?(AST::SigPrependNode) && prep_mod.type_params
|
|
847
1237
|
prep_ty = genv.get_instance_type(prep_mod, prep_decl.args, changes, base_ty_env, ty)
|
|
848
1238
|
else
|
|
849
|
-
type_params = prep_mod.type_params.map {
|
|
1239
|
+
type_params = prep_mod.type_params.map { Source.new() } # TODO: better support
|
|
850
1240
|
prep_ty = Type::Instance.new(genv, prep_mod, type_params)
|
|
851
1241
|
end
|
|
852
1242
|
|
|
@@ -901,7 +1291,7 @@ module TypeProf::Core
|
|
|
901
1291
|
if inc_decl.is_a?(AST::SigIncludeNode) && inc_mod.type_params
|
|
902
1292
|
inc_ty = genv.get_instance_type(inc_mod, inc_decl.args, changes, base_ty_env, ty)
|
|
903
1293
|
else
|
|
904
|
-
type_params = inc_mod.type_params.map {
|
|
1294
|
+
type_params = inc_mod.type_params.map { Source.new() } # TODO: better support
|
|
905
1295
|
inc_ty = Type::Instance.new(genv, inc_mod, type_params)
|
|
906
1296
|
end
|
|
907
1297
|
|
|
@@ -922,10 +1312,42 @@ module TypeProf::Core
|
|
|
922
1312
|
found
|
|
923
1313
|
end
|
|
924
1314
|
|
|
1315
|
+
def resolve_extended_modules(genv, changes, base_ty_env, ty, mid, &blk)
|
|
1316
|
+
found = false
|
|
1317
|
+
|
|
1318
|
+
alias_limit = 0
|
|
1319
|
+
# An extended module's instance methods are resolved as the receiver's
|
|
1320
|
+
# singleton methods, so look them up with singleton = false.
|
|
1321
|
+
ty.mod.extended_modules.each do |ext_decl, ext_mod|
|
|
1322
|
+
if ext_decl.is_a?(AST::SigExtendNode) && ext_mod.type_params
|
|
1323
|
+
ext_ty = genv.get_instance_type(ext_mod, ext_decl.args, changes, base_ty_env, ty)
|
|
1324
|
+
else
|
|
1325
|
+
type_params = ext_mod.type_params.map { Source.new() } # TODO: better support
|
|
1326
|
+
ext_ty = Type::Instance.new(genv, ext_mod, type_params)
|
|
1327
|
+
end
|
|
1328
|
+
|
|
1329
|
+
me = ext_ty.mod.get_method(false, mid)
|
|
1330
|
+
changes.add_depended_method_entity(me) if changes
|
|
1331
|
+
if !me.aliases.empty?
|
|
1332
|
+
mid = me.aliases.values.first
|
|
1333
|
+
alias_limit += 1
|
|
1334
|
+
redo if alias_limit < 5
|
|
1335
|
+
end
|
|
1336
|
+
if me.exist?
|
|
1337
|
+
found = true
|
|
1338
|
+
yield me, ext_ty, mid
|
|
1339
|
+
else
|
|
1340
|
+
# The extended module may itself include other modules.
|
|
1341
|
+
found ||= resolve_included_modules(genv, changes, base_ty_env, ext_ty, mid, &blk)
|
|
1342
|
+
end
|
|
1343
|
+
end
|
|
1344
|
+
found
|
|
1345
|
+
end
|
|
1346
|
+
|
|
925
1347
|
def resolve_subclasses(genv, changes)
|
|
926
1348
|
# TODO: This does not follow new subclasses
|
|
927
1349
|
@recv.each_type do |ty|
|
|
928
|
-
next if ty ==
|
|
1350
|
+
next if ty == genv.bot_type
|
|
929
1351
|
base_ty = ty.base_type(genv)
|
|
930
1352
|
singleton = base_ty.is_a?(Type::Singleton)
|
|
931
1353
|
mod = base_ty.mod
|
|
@@ -980,23 +1402,44 @@ module TypeProf::Core
|
|
|
980
1402
|
singleton = @singleton
|
|
981
1403
|
cur_ive = mod.get_ivar(singleton, @name)
|
|
982
1404
|
target_vtx = nil
|
|
1405
|
+
target_decls = nil
|
|
983
1406
|
genv.each_direct_superclass(mod, singleton) do |mod, singleton|
|
|
984
1407
|
ive = mod.get_ivar(singleton, @name)
|
|
1408
|
+
# Subscribe to every visited ive so that, if one later acquires an
|
|
1409
|
+
# RBS declaration, this box is re-run and switches to the declared
|
|
1410
|
+
# type instead of the inferred one.
|
|
1411
|
+
changes.add_depended_value_entity(ive)
|
|
985
1412
|
if ive.exist?
|
|
986
1413
|
target_vtx = ive.vtx
|
|
1414
|
+
target_decls = ive.decls unless ive.decls.empty?
|
|
1415
|
+
break if target_decls
|
|
987
1416
|
end
|
|
988
1417
|
end
|
|
989
|
-
|
|
990
|
-
if
|
|
1418
|
+
|
|
1419
|
+
if target_decls
|
|
1420
|
+
# When declarations exist, return declared types instead of assigned types
|
|
1421
|
+
target_decls.each do |decl|
|
|
1422
|
+
subst = {}
|
|
1423
|
+
if decl.cpath
|
|
1424
|
+
decl_mod = genv.resolve_cpath(decl.cpath)
|
|
1425
|
+
if decl_mod.type_params && !decl_mod.type_params.empty?
|
|
1426
|
+
subst = decl_mod.type_params.to_h do |param, _default_ty|
|
|
1427
|
+
[param, Vertex.new(@node)]
|
|
1428
|
+
end
|
|
1429
|
+
end
|
|
1430
|
+
end
|
|
1431
|
+
vtx = decl.type.covariant_vertex(genv, changes, subst)
|
|
1432
|
+
changes.add_edge(genv, vtx, @ret)
|
|
1433
|
+
end
|
|
1434
|
+
elsif target_vtx
|
|
1435
|
+
edges = []
|
|
991
1436
|
if target_vtx != cur_ive.vtx
|
|
992
1437
|
edges << [cur_ive.vtx, @proxy] << [@proxy, target_vtx]
|
|
993
1438
|
end
|
|
994
1439
|
edges << [target_vtx, @ret]
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
edges.each do |src, dst|
|
|
999
|
-
changes.add_edge(genv, src, dst)
|
|
1440
|
+
edges.each do |src, dst|
|
|
1441
|
+
changes.add_edge(genv, src, dst)
|
|
1442
|
+
end
|
|
1000
1443
|
end
|
|
1001
1444
|
end
|
|
1002
1445
|
end
|
|
@@ -1064,25 +1507,27 @@ module TypeProf::Core
|
|
|
1064
1507
|
def ret = @rhs
|
|
1065
1508
|
|
|
1066
1509
|
def run0(genv, changes)
|
|
1067
|
-
edges = []
|
|
1068
1510
|
@value.each_type do |ty|
|
|
1069
1511
|
# TODO: call to_ary?
|
|
1070
1512
|
case ty
|
|
1071
1513
|
when Type::Array
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1514
|
+
ty.splat_assign(genv, @lefts, @rest_elem, @rights).each do |src, dst|
|
|
1515
|
+
changes.add_edge(genv, src, dst)
|
|
1516
|
+
end
|
|
1517
|
+
when Type::Instance
|
|
1518
|
+
if ty.mod == genv.mod_ary && (elem_vtx = ty.args[0])
|
|
1519
|
+
@lefts.each {|lhs| changes.add_edge(genv, elem_vtx, lhs) }
|
|
1520
|
+
changes.add_edge(genv, elem_vtx, @rest_elem) if @rest_elem
|
|
1521
|
+
@rights&.each {|rhs| changes.add_edge(genv, elem_vtx, rhs) }
|
|
1078
1522
|
else
|
|
1079
|
-
|
|
1523
|
+
lhs = @lefts[0] || (@rights && @rights[0]) || @rest_elem
|
|
1524
|
+
changes.add_edge(genv, Source.new(ty), lhs) if lhs
|
|
1080
1525
|
end
|
|
1526
|
+
else
|
|
1527
|
+
lhs = @lefts[0] || (@rights && @rights[0]) || @rest_elem
|
|
1528
|
+
changes.add_edge(genv, Source.new(ty), lhs) if lhs
|
|
1081
1529
|
end
|
|
1082
1530
|
end
|
|
1083
|
-
edges.each do |src, dst|
|
|
1084
|
-
changes.add_edge(genv, src, dst)
|
|
1085
|
-
end
|
|
1086
1531
|
end
|
|
1087
1532
|
end
|
|
1088
1533
|
|