Skip to content

Commit c65f21b

Browse files
committed
effects: relax recursion detection for effects analysis
In a similar spirit to #40561, we can relax the recursion detection to guarantee `:terminates` effect and allow the effects analysis to not taint `:terminates` effect when there are no cycles in `MethodInstance`s in a call graph. fix #45781
1 parent c882602 commit c65f21b

File tree

1 file changed

+41
-15
lines changed

1 file changed

+41
-15
lines changed

base/compiler/abstractinterpretation.jl

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -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)
661665
end
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
664693
struct 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

Comments
 (0)