diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index c4043ab15a161..b1fab2f0f4e69 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -104,7 +104,7 @@ function abstract_call_gf_by_type(@nospecialize(f), argtypes::Vector{Any}, @nosp # 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(f, argtypes, applicable[nonbot]::SimpleVector, sv) + const_rettype = abstract_call_method_with_const_args(rettype, f, argtypes, applicable[nonbot]::SimpleVector, sv) if const_rettype ⊑ rettype # use the better result, if it's a refinement of rettype rettype = const_rettype @@ -142,7 +142,7 @@ function abstract_call_gf_by_type(@nospecialize(f), argtypes::Vector{Any}, @nosp return rettype end -function abstract_call_method_with_const_args(@nospecialize(f), argtypes::Vector{Any}, match::SimpleVector, sv::InferenceState) +function abstract_call_method_with_const_args(@nospecialize(rettype), @nospecialize(f), argtypes::Vector{Any}, match::SimpleVector, sv::InferenceState) method = match[3]::Method nargs::Int = method.nargs method.isva && (nargs -= 1) @@ -159,7 +159,7 @@ function abstract_call_method_with_const_args(@nospecialize(f), argtypes::Vector end end end - haveconst || return Any + haveconst || improvable_via_constant_propagation(rettype) || return Any sig = match[1] sparams = match[2]::SimpleVector code = code_for_method(method, sig, sparams, sv.params.world) @@ -1060,7 +1060,7 @@ function typeinf_local(frame::InferenceState) elseif hd === :return pc´ = n + 1 rt = widenconditional(abstract_eval(stmt.args[1], s[pc], frame)) - if !isa(rt, Const) && !isa(rt, Type) && (!isa(rt, PartialTuple) || frame.cached) + if !isa(rt, Const) && !isa(rt, Type) && !isa(rt, PartialTuple) # only propagate information we know we can store # and is valid inter-procedurally rt = widenconst(rt) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index bbebe610ea49b..418afe10fcb46 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -671,7 +671,7 @@ function analyze_method!(idx::Int, @nospecialize(f), @nospecialize(ft), @nospeci return ConstantCase(quoted(linfo.inferred_const), method, Any[methsp...], metharg) end - isconst, inferred = find_inferred(linfo, atypes, sv) + isconst, inferred = find_inferred(linfo, atypes, sv, stmttyp) if isconst return ConstantCase(inferred, method, Any[methsp...], metharg) end @@ -1152,7 +1152,7 @@ function ssa_substitute_op!(@nospecialize(val), arg_replacements::Vector{Any}, return urs[] end -function find_inferred(linfo::MethodInstance, @nospecialize(atypes), sv::OptimizationState) +function find_inferred(linfo::MethodInstance, @nospecialize(atypes), sv::OptimizationState, @nospecialize(rettype)) # see if the method has a InferenceResult in the current cache # or an existing inferred code info store in `.inferred` haveconst = false @@ -1163,7 +1163,7 @@ function find_inferred(linfo::MethodInstance, @nospecialize(atypes), sv::Optimiz break end end - if haveconst + if haveconst || improvable_via_constant_propagation(rettype) inf_result = cache_lookup(linfo, atypes, sv.params.cache) # Union{Nothing, InferenceResult} else inf_result = nothing diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index d9c7b7c415ce0..ff9273de3cfd5 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -495,15 +495,19 @@ function typeinf_edge(method::Method, @nospecialize(atypes), sparams::SimpleVect frame.parent = caller end typeinf(frame) - return frame.bestguess, frame.inferred ? frame.linfo : nothing + return widenconst_bestguess(frame.bestguess), frame.inferred ? frame.linfo : nothing elseif frame === true # unresolvable cycle return Any, nothing end frame = frame::InferenceState - return frame.bestguess, nothing + return widenconst_bestguess(frame.bestguess), nothing end +function widenconst_bestguess(bestguess) + !isa(bestguess, Const) && !isa(bestguess, Type) && return widenconst(bestguess) + return bestguess +end #### entry points for inferring a MethodInstance given a type signature #### diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl index 9b60dd2971fe3..c77f329ef2341 100644 --- a/base/compiler/typeutils.jl +++ b/base/compiler/typeutils.jl @@ -160,3 +160,12 @@ function unioncomplexity(t::DataType) end unioncomplexity(u::UnionAll) = max(unioncomplexity(u.body), unioncomplexity(u.var.ub)) unioncomplexity(@nospecialize(x)) = 0 + +function improvable_via_constant_propagation(@nospecialize(t)) + if isconcretetype(t) && t <: Tuple + for p in t.parameters + p === DataType && return true + end + end + return false +end diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index c6e59dc51412e..b89c4feb3a437 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -2172,3 +2172,15 @@ f30394(foo::T1, ::Type{T2}) where {T2, T1 <: T2} = foo f30394(foo, T2) = f30394(foo.foo_inner, T2) @test Base.return_types(f30394, (Foo30394_2, Type{Base30394})) == Any[Base30394] + +# PR #30385 + +g30385(args...) = h30385(args...) +h30385(f, args...) = f(args...) +f30385(T, y) = g30385(getfield, g30385(tuple, T, y), 1) +k30385(::Type{AbstractFloat}) = 1 +k30385(x) = "dummy" +j30385(T, y) = k30385(f30385(T, y)) + +@test @inferred(j30385(AbstractFloat, 1)) == 1 +@test @inferred(j30385(:dummy, 1)) == "dummy"