Skip to content

Commit

Permalink
fix up Conditional-forwarding for vararg methods
Browse files Browse the repository at this point in the history
If `Conditional` is imposed on argument that is squashed to vararg-tuple
then we don't need to `widenconditional` on `isva_given_argtypes`
(because `Conditional`-information is already widened during the
conversion to that vararg-tuple type -- since `PartialStruct` now never
wraps slot wrappers with the explicit check within its constructor).

Fixes #47435.
  • Loading branch information
aviatesk committed Nov 3, 2022
1 parent 0ff2373 commit 564276a
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 51 deletions.
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

0 comments on commit 564276a

Please sign in to comment.