@@ -653,14 +653,43 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp
653653 # this edge is known to terminate
654654 edge_effects = Effects (edge_effects; terminates= ALWAYS_TRUE)
655655 elseif edgecycle
656- # Some sort of recursion was detected. Even if we did not limit types,
657- # we cannot guarantee that the call will terminate
658- edge_effects = Effects (edge_effects; terminates= TRISTATE_UNKNOWN)
656+ # Some sort of recursion was detected.
657+ if edge != = nothing && ! edgelimited && ! is_edge_recursed (edge, sv)
658+ # no `MethodInstance` cycles -- don't taint :terminate
659+ else
660+ # we cannot guarantee that the call will terminate
661+ edge_effects = Effects (edge_effects; terminates= ALWAYS_FALSE)
662+ end
659663 end
660664 return MethodCallResult (rt, edgecycle, edgelimited, edge, edge_effects)
661665end
662666
663- # keeps result and context information of abstract method call, will be used by succeeding constant-propagation
667+ function is_edge_recursed (edge:: MethodInstance , sv:: InferenceState )
668+ return any (InfStackUnwind (sv)) do infstate
669+ return edge === infstate. linfo
670+ end
671+ end
672+
673+ function is_method_recursed (method:: Method , sv:: InferenceState )
674+ return any (InfStackUnwind (sv)) do infstate
675+ return method === infstate. linfo. def
676+ end
677+ end
678+
679+ function is_constprop_edge_recursed (edge:: MethodInstance , sv:: InferenceState )
680+ return any (InfStackUnwind (sv)) do infstate
681+ return edge === infstate. linfo && any (infstate. result. overridden_by_const)
682+ end
683+ end
684+
685+ function is_constprop_method_recursed (method:: Method , sv:: InferenceState )
686+ return any (InfStackUnwind (sv)) do infstate
687+ return method === infstate. linfo. def && any (infstate. result. overridden_by_const)
688+ end
689+ end
690+
691+ # keeps result and context information of abstract_method_call, which will later be used for
692+ # backedge computation, and concrete evaluation or constant-propagation
664693struct MethodCallResult
665694 rt
666695 edgecycle:: Bool
@@ -802,17 +831,14 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter, resul
802831 if inf_result === nothing
803832 # if there might be a cycle, check to make sure we don't end up
804833 # calling ourselves here.
805- let result = result # prevent capturing
806- if result. edgecycle && _any (InfStackUnwind (sv)) do infstate
807- # if the type complexity limiting didn't decide to limit the call signature (`result.edgelimited = false`)
808- # we can relax the cycle detection by comparing `MethodInstance`s and allow inference to
809- # propagate different constant elements if the recursion is finite over the lattice
810- return (result. edgelimited ? match. method === infstate. linfo. def : mi === infstate. linfo) &&
811- any (infstate. result. overridden_by_const)
812- end
813- add_remark! (interp, sv, " [constprop] Edge cycle encountered" )
814- return nothing
815- end
834+ if result. edgecycle && (result. edgelimited ?
835+ is_constprop_method_recursed (match. method, sv) :
836+ # if the type complexity limiting didn't decide to limit the call signature (`result.edgelimited = false`)
837+ # we can relax the cycle detection by comparing `MethodInstance`s and allow inference to
838+ # propagate different constant elements if the recursion is finite over the lattice
839+ is_constprop_edge_recursed (mi, sv))
840+ add_remark! (interp, sv, " [constprop] Edge cycle encountered" )
841+ return nothing
816842 end
817843 inf_result = InferenceResult (mi, (arginfo, sv))
818844 if ! any (inf_result. overridden_by_const)
0 commit comments