@@ -51,14 +51,24 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
51
51
end
52
52
53
53
(; valid_worlds, applicable, info) = matches
54
- update_valid_age! (sv, valid_worlds)
54
+ update_valid_age! (sv, valid_worlds) # need to record the negative world now, since even if we don't generate any useful information, inlining might want to add an invoke edge and it won't have this information anymore
55
+ if bail_out_toplevel_call (interp, sv)
56
+ napplicable = length (applicable)
57
+ for i = 1 : napplicable
58
+ sig = applicable[i]. match. spec_types
59
+ if ! isdispatchtuple (sig)
60
+ # only infer fully concrete call sites in top-level expressions (ignoring even isa_compileable_sig matches)
61
+ add_remark! (interp, sv, " Refusing to infer non-concrete call site in top-level expression" )
62
+ return Future (CallMeta (Any, Any, Effects (), NoCallInfo ()))
63
+ end
64
+ end
65
+ end
55
66
56
67
# final result
57
68
gfresult = Future {CallMeta} ()
58
69
# intermediate work for computing gfresult
59
70
rettype = exctype = Bottom
60
71
conditionals = nothing # keeps refinement information of call argument types when the return type is boolean
61
- seenall = true
62
72
const_results = nothing # or const_results::Vector{Union{Nothing,ConstResult}} if any const results are available
63
73
fargs = arginfo. fargs
64
74
all_effects = EFFECTS_TOTAL
@@ -69,16 +79,14 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
69
79
f = Core. Box (f)
70
80
atype = Core. Box (atype)
71
81
function infercalls (interp, sv)
72
- napplicable = length (applicable)
73
- multiple_matches = napplicable > 1
82
+ local napplicable = length (applicable)
83
+ local multiple_matches = napplicable > 1
74
84
while i <= napplicable
75
85
(; match, edges, edge_idx) = applicable[i]
76
86
method = match. method
77
87
sig = match. spec_types
78
- if bail_out_toplevel_call (interp, InferenceLoopState (sig, rettype, all_effects), sv)
79
- # only infer concrete call sites in top-level expressions
80
- add_remark! (interp, sv, " Refusing to infer non-concrete call site in top-level expression" )
81
- seenall = false
88
+ if bail_out_call (interp, InferenceLoopState (rettype, all_effects), sv)
89
+ add_remark! (interp, sv, " Call inference reached maximally imprecise information: bailing on doing more abstract inference." )
82
90
break
83
91
end
84
92
# TODO : this is unmaintained now as it didn't seem to improve things, though it does avoid hard-coding the union split at the higher level,
@@ -162,17 +170,13 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
162
170
Any[Bottom for _ in 1 : length (argtypes)]
163
171
end
164
172
for i = 1 : length (argtypes)
165
- cnd = conditional_argtype (𝕃ᵢ, this_conditional, sig , argtypes, i)
173
+ cnd = conditional_argtype (𝕃ᵢ, this_conditional, match . spec_types , argtypes, i)
166
174
conditionals[1 ][i] = conditionals[1 ][i] ⊔ ᵢ cnd. thentype
167
175
conditionals[2 ][i] = conditionals[2 ][i] ⊔ ᵢ cnd. elsetype
168
176
end
169
177
end
170
178
edges[edge_idx] = edge
171
- if i < napplicable && bail_out_call (interp, InferenceLoopState (sig, rettype, all_effects), sv)
172
- add_remark! (interp, sv, " Call inference reached maximally imprecise information. Bailing on." )
173
- seenall = false
174
- i = napplicable # break in outer function
175
- end
179
+
176
180
i += 1
177
181
return true
178
182
end # function handle1
@@ -184,12 +188,12 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
184
188
end
185
189
end # while
186
190
187
- if const_results != = nothing
188
- @assert napplicable == nmatches (info) == length (const_results)
189
- info = ConstCallInfo (info, const_results)
190
- end
191
-
192
- if seenall
191
+ seenall = i > napplicable
192
+ if seenall # small optimization to skip some work that is already implied
193
+ if const_results != = nothing
194
+ @assert napplicable == nmatches (info) == length (const_results)
195
+ info = ConstCallInfo (info, const_results)
196
+ end
193
197
if ! fully_covering (matches) || any_ambig (matches)
194
198
# Account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature.
195
199
all_effects = Effects (all_effects; nothrow= false )
@@ -198,51 +202,67 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
198
202
if sv isa InferenceState && fargs != = nothing
199
203
slotrefinements = collect_slot_refinements (𝕃ᵢ, applicable, argtypes, fargs, sv)
200
204
end
205
+ rettype = from_interprocedural! (interp, rettype, sv, arginfo, conditionals)
206
+ if call_result_unused (si) && ! (rettype === Bottom)
207
+ add_remark! (interp, sv, " Call result type was widened because the return value is unused" )
208
+ # We're mainly only here because the optimizer might want this code,
209
+ # but we ourselves locally don't typically care about it locally
210
+ # (beyond checking if it always throws).
211
+ # So avoid adding an edge, since we don't want to bother attempting
212
+ # to improve our result even if it does change (to always throw),
213
+ # and avoid keeping track of a more complex result type.
214
+ rettype = Any
215
+ end
216
+ # if from_interprocedural added any pclimitations to the set inherited from the arguments,
217
+ # some of those may be part of our cycles, so those can be deleted now
218
+ # TODO : and those might need to be deleted later too if the cycle grows to include them?
219
+ if isa (sv, InferenceState)
220
+ # TODO (#48913) implement a proper recursion handling for irinterp:
221
+ # This works just because currently the `:terminate` condition guarantees that
222
+ # irinterp doesn't fail into unresolved cycles, but it's not a good solution.
223
+ # We should revisit this once we have a better story for handling cycles in irinterp.
224
+ if ! isempty (sv. pclimitations) # remove self, if present
225
+ delete! (sv. pclimitations, sv)
226
+ for caller in callers_in_cycle (sv)
227
+ delete! (sv. pclimitations, caller)
228
+ end
229
+ end
230
+ end
201
231
else
202
232
# there is unanalyzed candidate, widen type and effects to the top
203
233
rettype = exctype = Any
204
234
all_effects = Effects ()
235
+ const_results = nothing
205
236
end
206
237
207
- rettype = from_interprocedural! (interp, rettype, sv, arginfo, conditionals)
208
-
209
238
# Also considering inferring the compilation signature for this method, so
210
- # it is available to the compiler, unless it should not end up needing it (for an invoke).
211
- if (isa (sv, InferenceState) && infer_compilation_signature (interp) &&
212
- (seenall && 1 == napplicable) && (! is_removable_if_unused (all_effects) || ! call_result_unused (si)))
213
- (; match) = applicable[1 ]
214
- method = match. method
215
- sig = match. spec_types
216
- mi = specialize_method (match; preexisting= true )
217
- if mi === nothing || ! const_prop_methodinstance_heuristic (interp, mi, arginfo, sv)
218
- csig = get_compileable_sig (method, sig, match. sparams)
219
- if csig != = nothing && csig != = sig
220
- abstract_call_method (interp, method, csig, match. sparams, multiple_matches, StmtInfo (false ), sv):: Future
221
- end
222
- end
223
- end
224
-
225
- if call_result_unused (si) && ! (rettype === Bottom)
226
- add_remark! (interp, sv, " Call result type was widened because the return value is unused" )
227
- # We're mainly only here because the optimizer might want this code,
228
- # but we ourselves locally don't typically care about it locally
229
- # (beyond checking if it always throws).
230
- # So avoid adding an edge, since we don't want to bother attempting
231
- # to improve our result even if it does change (to always throw),
232
- # and avoid keeping track of a more complex result type.
233
- rettype = Any
234
- end
235
- if isa (sv, InferenceState)
236
- # TODO (#48913) implement a proper recursion handling for irinterp:
237
- # This works just because currently the `:terminate` condition guarantees that
238
- # irinterp doesn't fail into unresolved cycles, but it's not a good solution.
239
- # We should revisit this once we have a better story for handling cycles in irinterp.
240
- if ! isempty (sv. pclimitations) # remove self, if present
241
- delete! (sv. pclimitations, sv)
242
- for caller in callers_in_cycle (sv)
243
- delete! (sv. pclimitations, caller)
239
+ # it is available to the compiler in case it ends up needing it for the invoke.
240
+ if isa (sv, InferenceState) && infer_compilation_signature (interp) && (! is_removable_if_unused (all_effects) || ! call_result_unused (si))
241
+ i = 1
242
+ function infercalls2 (interp, sv)
243
+ local napplicable = length (applicable)
244
+ local multiple_matches = napplicable > 1
245
+ while i <= napplicable
246
+ (; match, edges, edge_idx) = applicable[i]
247
+ i += 1
248
+ method = match. method
249
+ sig = match. spec_types
250
+ mi = specialize_method (match; preexisting= true )
251
+ if mi === nothing || ! const_prop_methodinstance_heuristic (interp, mi, arginfo, sv)
252
+ csig = get_compileable_sig (method, sig, match. sparams)
253
+ if csig != = nothing && (! seenall || csig != = sig) # corresponds to whether the first look already looked at this, so repeating abstract_call_method is not useful
254
+ sp_ = ccall (:jl_type_intersection_with_env , Any, (Any, Any), csig, method. sig):: SimpleVector
255
+ if match. sparams === sp_[2 ]
256
+ mresult = abstract_call_method (interp, method, csig, match. sparams, multiple_matches, StmtInfo (false ), sv):: Future
257
+ isready (mresult) || return false # wait for mresult Future to resolve off the callstack before continuing
258
+ end
259
+ end
260
+ end
244
261
end
262
+ return true
245
263
end
264
+ # start making progress on the first call
265
+ infercalls2 (interp, sv) || push! (sv. tasks, infercalls2)
246
266
end
247
267
248
268
gfresult[] = CallMeta (rettype, exctype, all_effects, info, slotrefinements)
@@ -1787,6 +1807,14 @@ function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, si::
1787
1807
i = 1
1788
1808
while i <= length (ctypes)
1789
1809
ct = ctypes[i]
1810
+ if bail_out_apply (interp, InferenceLoopState (res, all_effects), sv)
1811
+ add_remark! (interp, sv, " _apply_iterate inference reached maximally imprecise information: bailing on analysis of more methods." )
1812
+ # there is unanalyzed candidate, widen type and effects to the top
1813
+ let retinfo = NoCallInfo () # NOTE this is necessary to prevent the inlining processing
1814
+ applyresult[] = CallMeta (Any, Any, Effects (), retinfo)
1815
+ return true
1816
+ end
1817
+ end
1790
1818
lct = length (ct)
1791
1819
# truncate argument list at the first Vararg
1792
1820
for k = 1 : lct- 1
@@ -1808,14 +1836,6 @@ function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, si::
1808
1836
res = tmerge (typeinf_lattice (interp), res, rt)
1809
1837
exctype = tmerge (typeinf_lattice (interp), exctype, exct)
1810
1838
all_effects = merge_effects (all_effects, effects)
1811
- if i < length (ctypes) && bail_out_apply (interp, InferenceLoopState (ctypes[i], res, all_effects), sv)
1812
- add_remark! (interp, sv, " _apply_iterate inference reached maximally imprecise information. Bailing on." )
1813
- # there is unanalyzed candidate, widen type and effects to the top
1814
- let retinfo = NoCallInfo () # NOTE this is necessary to prevent the inlining processing
1815
- applyresult[] = CallMeta (Any, Any, Effects (), retinfo)
1816
- return true
1817
- end
1818
- end
1819
1839
end
1820
1840
i += 1
1821
1841
end
0 commit comments