Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix up Conditional-forwarding for vararg methods #47438

Merged
merged 1 commit into from
Nov 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion base/compiler/inferenceresult.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function va_process_argtypes(given_argtypes::Vector{Any}, mi::MethodInstance,
# invalidate `Conditional` imposed on varargs
if condargs !== nothing
for (slotid, i) in condargs
if slotid ≥ last
if slotid ≥ last && (1 ≤ i ≤ length(isva_given_argtypes)) # `Conditional` is already widened to vararg-tuple otherwise
isva_given_argtypes[i] = widenconditional(isva_given_argtypes[i])
end
end
Expand Down
112 changes: 62 additions & 50 deletions test/compiler/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2293,66 +2293,78 @@ function _g_ifelse_isa_()
end
@test Base.return_types(_g_ifelse_isa_, ()) == [Int]

@testset "Conditional forwarding" begin
# forward `Conditional` if it conveys a constraint on any other argument
ifelselike(cnd, x, y) = cnd ? x : y
# Conditional forwarding
# ======================

@test Base.return_types((Any,Int,)) do x, y
ifelselike(isa(x, Int), x, y)
end |> only == Int

# should work nicely with union-split
@test Base.return_types((Union{Int,Nothing},)) do x
ifelselike(isa(x, Int), x, 0)
end |> only == Int
# forward `Conditional` if it conveys a constraint on any other argument
ifelselike(cnd, x, y) = cnd ? x : y

@test Base.return_types((Any,Int)) do x, y
ifelselike(!isa(x, Int), y, x)
end |> only == Int
@test Base.return_types((Any,Int,)) do x, y
ifelselike(isa(x, Int), x, y)
end |> only == Int

@test Base.return_types((Any,Int)) do x, y
a = ifelselike(x === 0, x, 0) # ::Const(0)
if a == 0
return y
else
return nothing # dead branch
end
end |> only == Int
# should work nicely with union-split
@test Base.return_types((Union{Int,Nothing},)) do x
ifelselike(isa(x, Int), x, 0)
end |> only == Int

# pick up the first if there are multiple constrained arguments
@test Base.return_types((Any,)) do x
ifelselike(isa(x, Int), x, x)
end |> only == Any
@test Base.return_types((Any,Int)) do x, y
ifelselike(!isa(x, Int), y, x)
end |> only == Int

# just propagate multiple constraints
ifelselike2(cnd1, cnd2, x, y, z) = cnd1 ? x : cnd2 ? y : z
@test Base.return_types((Any,Any)) do x, y
ifelselike2(isa(x, Int), isa(y, Int), x, y, 0)
end |> only == Int
@test Base.return_types((Any,Int)) do x, y
a = ifelselike(x === 0, x, 0) # ::Const(0)
if a == 0
return y
else
return nothing # dead branch
end
end |> only == Int

# work with `invoke`
@test Base.return_types((Any,Any)) do x, y
@invoke ifelselike(isa(x, Int), x::Any, y::Int)
end |> only == Int
# pick up the first if there are multiple constrained arguments
@test Base.return_types((Any,)) do x
ifelselike(isa(x, Int), x, x)
end |> only == Any

# don't be confused with vararg method
vacond(cnd, va...) = cnd ? va : 0
@test Base.return_types((Any,)) do x
# at runtime we will see `va::Tuple{Tuple{Int,Int}, Tuple{Int,Int}}`
vacond(isa(x, Tuple{Int,Int}), x, x)
end |> only == Union{Int,Tuple{Any,Any}}
# just propagate multiple constraints
ifelselike2(cnd1, cnd2, x, y, z) = cnd1 ? x : cnd2 ? y : z
@test Base.return_types((Any,Any)) do x, y
ifelselike2(isa(x, Int), isa(y, Int), x, y, 0)
end |> only == Int

# demonstrate extra constraint propagation for Base.ifelse
@test Base.return_types((Any,Int,)) do x, y
ifelse(isa(x, Int), x, y)
end |> only == Int
# work with `invoke`
@test Base.return_types((Any,Any)) do x, y
@invoke ifelselike(isa(x, Int), x::Any, y::Int)
end |> only == Int

# slot as SSA
@test Base.return_types((Any,Vector{Any})) do x, y
z = x
ifelselike(isa(z, Int), z, length(y))
end |> only === Int
# don't be confused with vararg method
vacond(cnd, va...) = cnd ? va : 0
@test Base.return_types((Any,)) do x
# at runtime we will see `va::Tuple{Tuple{Int,Int}, Tuple{Int,Int}}`
vacond(isa(x, Tuple{Int,Int}), x, x)
end |> only == Union{Int,Tuple{Any,Any}}

# https://github.com/JuliaLang/julia/issues/47435
is_closed_ex(e::InvalidStateException) = true
is_closed_ex(e) = false
function issue47435()
try
catch e
println("caught $e: $(is_closed_ex(e))")
end
end
@test only(Base.return_types(issue47435)) === Nothing

# demonstrate extra constraint propagation for Base.ifelse
@test Base.return_types((Any,Int,)) do x, y
ifelse(isa(x, Int), x, y)
end |> only == Int

# forward conditional information imposed on SSA that is alised to a slot
@test Base.return_types((Any,Vector{Any})) do x, y
z = x
ifelselike(isa(z, Int), z, length(y))
end |> only === Int

# Equivalence of Const(T.instance) and T for singleton types
@test Const(nothing) ⊑ Nothing && Nothing ⊑ Const(nothing)
Expand Down