@@ -51,14 +51,24 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
5151 end
5252
5353 (; 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
5566
5667 # final result
5768 gfresult = Future {CallMeta} ()
5869 # intermediate work for computing gfresult
5970 rettype = exctype = Bottom
6071 conditionals = nothing # keeps refinement information of call argument types when the return type is boolean
61- seenall = true
6272 const_results = nothing # or const_results::Vector{Union{Nothing,ConstResult}} if any const results are available
6373 fargs = arginfo. fargs
6474 all_effects = EFFECTS_TOTAL
@@ -69,16 +79,14 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
6979 f = Core. Box (f)
7080 atype = Core. Box (atype)
7181 function infercalls (interp, sv)
72- napplicable = length (applicable)
73- multiple_matches = napplicable > 1
82+ local napplicable = length (applicable)
83+ local multiple_matches = napplicable > 1
7484 while i <= napplicable
7585 (; match, edges, edge_idx) = applicable[i]
7686 method = match. method
7787 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." )
8290 break
8391 end
8492 # 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),
162170 Any[Bottom for _ in 1 : length (argtypes)]
163171 end
164172 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)
166174 conditionals[1 ][i] = conditionals[1 ][i] ⊔ ᵢ cnd. thentype
167175 conditionals[2 ][i] = conditionals[2 ][i] ⊔ ᵢ cnd. elsetype
168176 end
169177 end
170178 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+
176180 i += 1
177181 return true
178182 end # function handle1
@@ -184,12 +188,12 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
184188 end
185189 end # while
186190
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
193197 if ! fully_covering (matches) || any_ambig (matches)
194198 # Account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature.
195199 all_effects = Effects (all_effects; nothrow= false )
@@ -198,51 +202,67 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
198202 if sv isa InferenceState && fargs != = nothing
199203 slotrefinements = collect_slot_refinements (𝕃ᵢ, applicable, argtypes, fargs, sv)
200204 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
201231 else
202232 # there is unanalyzed candidate, widen type and effects to the top
203233 rettype = exctype = Any
204234 all_effects = Effects ()
235+ const_results = nothing
205236 end
206237
207- rettype = from_interprocedural! (interp, rettype, sv, arginfo, conditionals)
208-
209238 # 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
244261 end
262+ return true
245263 end
264+ # start making progress on the first call
265+ infercalls2 (interp, sv) || push! (sv. tasks, infercalls2)
246266 end
247267
248268 gfresult[] = CallMeta (rettype, exctype, all_effects, info, slotrefinements)
@@ -1787,6 +1807,14 @@ function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, si::
17871807 i = 1
17881808 while i <= length (ctypes)
17891809 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
17901818 lct = length (ct)
17911819 # truncate argument list at the first Vararg
17921820 for k = 1 : lct- 1
@@ -1808,14 +1836,6 @@ function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, si::
18081836 res = tmerge (typeinf_lattice (interp), res, rt)
18091837 exctype = tmerge (typeinf_lattice (interp), exctype, exct)
18101838 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
18191839 end
18201840 i += 1
18211841 end
0 commit comments