diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index d15c56fb2dedb..5eb27d414d105 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -132,7 +132,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), # if there's a possibility we could constant-propagate a better result # (hopefully without doing too much work), try to do that now # TODO: it feels like this could be better integrated into abstract_call_method / typeinf_edge - const_rettype = abstract_call_method_with_const_args(interp, rettype, f, argtypes, applicable[nonbot]::SimpleVector, sv) + const_rettype = abstract_call_method_with_const_args(interp, rettype, f, argtypes, applicable[nonbot]::SimpleVector, sv, edgecycle) if const_rettype ⊑ rettype # use the better result, if it's a refinement of rettype rettype = const_rettype @@ -185,7 +185,7 @@ function const_prop_profitable(@nospecialize(arg)) return false end -function abstract_call_method_with_const_args(interp::AbstractInterpreter, @nospecialize(rettype), @nospecialize(f), argtypes::Vector{Any}, match::SimpleVector, sv::InferenceState) +function abstract_call_method_with_const_args(interp::AbstractInterpreter, @nospecialize(rettype), @nospecialize(f), argtypes::Vector{Any}, match::SimpleVector, sv::InferenceState, edgecycle::Bool) method = match[3]::Method nargs::Int = method.nargs method.isva && (nargs -= 1) @@ -259,6 +259,17 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter, @nosp inf_cache = get_inference_cache(interp) inf_result = cache_lookup(mi, argtypes, inf_cache) if inf_result === nothing + if edgecycle + # if there might be a cycle, check to make sure we don't end up + # calling ourselves here. + infstate = sv + while !(infstate === nothing) + if method === infstate.linfo.def + return Any + end + infstate = infstate.parent + end + end inf_result = InferenceResult(mi, argtypes) frame = InferenceState(inf_result, #=cache=#false, interp) frame === nothing && return Any # this is probably a bad generated function (unsound), but just ignore it diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 9fadf28bdff51..23767ae32fadc 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -2623,3 +2623,27 @@ end _size_ish(F::NotQRSparse, i::Integer) = size(getprop(F, :B), 1) _call_size_ish(x) = _size_ish(x,1) @test Base.return_types(_call_size_ish, (NotQRSparse,)) == Any[Int] + +module TestConstPropRecursion +mutable struct Node + data + child::Node + sibling::Node +end + +function Base.iterate(n::Node, state::Node = n.child) + n === state && return nothing + return state, state === state.sibling ? n : state.sibling +end + +@inline function depth(node::Node, d) + childd = d + 1 + for c in node + d = max(d, depth(c, childd)) + end + return d +end + +f(n) = depth(n, 1) +end +@test Base.return_types(TestConstPropRecursion.f, (TestConstPropRecursion.Node,)) == Any[Int]