@@ -137,12 +137,12 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
137137 if splitunions
138138 splitsigs = switchtupleunion (sig)
139139 for sig_n in splitsigs
140- rt, edgecycle, edge = abstract_call_method (interp, method, sig_n, svec (), multiple_matches, sv)
140+ rt, edgecycle, edgelimited, edge = abstract_call_method (interp, method, sig_n, svec (), multiple_matches, sv)
141141 if edge != = nothing
142142 push! (edges, edge)
143143 end
144144 this_argtypes = applicable_argtypes === nothing ? argtypes : applicable_argtypes[i]
145- const_rt, const_result = abstract_call_method_with_const_args (interp, rt, f, this_argtypes, match, sv, edgecycle, false )
145+ const_rt, const_result = abstract_call_method_with_const_args (interp, rt, f, this_argtypes, match, sv, edgecycle, edgelimited, false )
146146 if const_rt != = rt && const_rt ⊑ rt
147147 rt = const_rt
148148 end
@@ -156,14 +156,14 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
156156 end
157157 end
158158 else
159- this_rt, edgecycle, edge = abstract_call_method (interp, method, sig, match. sparams, multiple_matches, sv)
159+ this_rt, edgecycle, edgelimited, edge = abstract_call_method (interp, method, sig, match. sparams, multiple_matches, sv)
160160 if edge != = nothing
161161 push! (edges, edge)
162162 end
163163 # try constant propagation with argtypes for this match
164164 # this is in preparation for inlining, or improving the return result
165165 this_argtypes = applicable_argtypes === nothing ? argtypes : applicable_argtypes[i]
166- const_this_rt, const_result = abstract_call_method_with_const_args (interp, this_rt, f, this_argtypes, match, sv, edgecycle, false )
166+ const_this_rt, const_result = abstract_call_method_with_const_args (interp, this_rt, f, this_argtypes, match, sv, edgecycle, edgelimited, false )
167167 if const_this_rt != = this_rt && const_this_rt ⊑ this_rt
168168 this_rt = const_this_rt
169169 end
@@ -315,14 +315,15 @@ const RECURSION_MSG = "Bounded recursion detected. Call was widened to force con
315315function abstract_call_method (interp:: AbstractInterpreter , method:: Method , @nospecialize (sig), sparams:: SimpleVector , hardlimit:: Bool , sv:: InferenceState )
316316 if method. name === :depwarn && isdefined (Main, :Base ) && method. module === Main. Base
317317 add_remark! (interp, sv, " Refusing to infer into `depwarn`" )
318- return Any, false , nothing
318+ return Any, false , false , nothing
319319 end
320320 topmost = nothing
321321 # Limit argument type tuple growth of functions:
322322 # look through the parents list to see if there's a call to the same method
323323 # and from the same method.
324324 # Returns the topmost occurrence of that repeated edge.
325325 edgecycle = false
326+ edgelimited = false
326327 # The `method_for_inference_heuristics` will expand the given method's generator if
327328 # necessary in order to retrieve this field from the generated `CodeInfo`, if it exists.
328329 # The other `CodeInfo`s we inspect will already have this field inflated, so we just
@@ -383,7 +384,7 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp
383384 # we have a self-cycle in the call-graph, but not in the inference graph (typically):
384385 # break this edge now (before we record it) by returning early
385386 # (non-typically, this means that we lose the ability to detect a guaranteed StackOverflow in some cases)
386- return Any, true , nothing
387+ return Any, true , true , nothing
387388 end
388389 topmost = nothing
389390 edgecycle = true
@@ -432,14 +433,15 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp
432433 # since it's very unlikely that we'll try to inline this,
433434 # or want make an invoke edge to its calling convention return type.
434435 # (non-typically, this means that we lose the ability to detect a guaranteed StackOverflow in some cases)
435- return Any, true , nothing
436+ return Any, true , true , nothing
436437 end
437438 add_remark! (interp, sv, RECURSION_MSG)
438439 topmost = topmost:: InferenceState
439440 parentframe = topmost. parent
440441 poison_callstack (sv, parentframe === nothing ? topmost : parentframe)
441442 sig = newsig
442443 sparams = svec ()
444+ edgelimited = true
443445 end
444446 end
445447
@@ -471,14 +473,14 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp
471473
472474 rt, edge = typeinf_edge (interp, method, sig, sparams, sv)
473475 if edge === nothing
474- edgecycle = true
476+ edgecycle = edgelimited = true
475477 end
476- return rt, edgecycle, edge
478+ return rt, edgecycle, edgelimited, edge
477479end
478480
479481function abstract_call_method_with_const_args (interp:: AbstractInterpreter , @nospecialize (rettype),
480482 @nospecialize (f), argtypes:: Vector{Any} , match:: MethodMatch ,
481- sv:: InferenceState , edgecycle:: Bool ,
483+ sv:: InferenceState , edgecycle:: Bool , edgelimited :: Bool ,
482484 va_override:: Bool )
483485 mi = maybe_get_const_prop_profitable (interp, rettype, f, argtypes, match, sv, edgecycle)
484486 mi === nothing && return Any, nothing
@@ -489,7 +491,11 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter, @nosp
489491 # if there might be a cycle, check to make sure we don't end up
490492 # calling ourselves here.
491493 if edgecycle && _any (InfStackUnwind (sv)) do infstate
492- return match. method === infstate. linfo. def && any (infstate. result. overridden_by_const)
494+ # if the type complexity limiting didn't decide to limit the call signature (`edgelimited = false`)
495+ # we can relax the cycle detection by comparing `MethodInstance`s and allow inference to
496+ # propagate different constant elements if the recursion is finite over the lattice
497+ return (edgelimited ? match. method === infstate. linfo. def : mi === infstate. linfo) &&
498+ any (infstate. result. overridden_by_const)
493499 end
494500 add_remark! (interp, sv, " [constprop] Edge cycle encountered" )
495501 return Any, nothing
@@ -1231,15 +1237,15 @@ end
12311237function abstract_call_opaque_closure (interp:: AbstractInterpreter , closure:: PartialOpaque , argtypes:: Vector{Any} , sv:: InferenceState )
12321238 pushfirst! (argtypes, closure. env)
12331239 sig = argtypes_to_type (argtypes)
1234- rt, edgecycle, edge = abstract_call_method (interp, closure. source:: Method , sig, Core. svec (), false , sv)
1240+ rt, edgecycle, edgelimited, edge = abstract_call_method (interp, closure. source:: Method , sig, Core. svec (), false , sv)
12351241 edge != = nothing && add_backedge! (edge, sv)
12361242 tt = closure. typ
12371243 sigT = unwrap_unionall (tt). parameters[1 ]
12381244 match = MethodMatch (sig, Core. svec (), closure. source:: Method , sig <: rewrap_unionall (sigT, tt))
12391245 info = OpaqueClosureCallInfo (match)
12401246 if ! edgecycle
12411247 const_rettype, result = abstract_call_method_with_const_args (interp, rt, closure, argtypes,
1242- match, sv, edgecycle, closure. isva)
1248+ match, sv, edgecycle, edgelimited, closure. isva)
12431249 if const_rettype ⊑ rt
12441250 rt = const_rettype
12451251 end
0 commit comments