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

subtyping: fast path for lhs union and rhs typevar #55413

Merged
merged 1 commit into from
Aug 14, 2024
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
16 changes: 15 additions & 1 deletion src/subtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -1312,7 +1312,21 @@ static int try_subtype_by_bounds(jl_value_t *a, jl_value_t *b, jl_stenv_t *e);
static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param)
{
if (jl_is_uniontype(x)) {
if (x == y) return 1;
if (obviously_egal(x, y))
return 1;
if (e->Runions.depth == 0 && jl_is_typevar(y) && !jl_has_free_typevars(x) && !jl_has_free_typevars(((jl_tvar_t*)y)->ub)) {
// Similar to fast path for repeated elements: if there have been no outer
// unions on the right, and the right side is a typevar, then we can handle the
// typevar first before picking a union element, under the theory that it may
// be easy to match or reject this whole union in comparing and setting the lb
// and ub of the variable binding, without needing to examine each element.
// However, if x contains any free typevars, then each element with a free
// typevar must be handled separately from the union of all elements without
// free typevars, since the typevars presence might lead to those elements
// getting eliminated (omit_bad_union) or degenerate (Union{Ptr{T}, Ptr}) or
// combined (Union{T, S} where {T, S <: T}).
return subtype_var((jl_tvar_t*)y, x, e, 1, param);
}
x = pick_union_element(x, e, 0);
}
if (jl_is_uniontype(y)) {
Expand Down
67 changes: 51 additions & 16 deletions test/subtype.jl
Original file line number Diff line number Diff line change
Expand Up @@ -707,16 +707,17 @@ macro testintersect(a, b, result)
a = esc(a)
b = esc(b)
result = esc(result)
Base.remove_linenums!(quote
# use a manual macrocall expression since Test will examine this __source__ value
return quote
# test real intersect
@test $cmp(_type_intersect($a, $b), $result)
@test $cmp(_type_intersect($b, $a), $result)
$(Expr(:macrocall, :var"@test", __source__, :($cmp(_type_intersect($a, $b), $result))))
$(Expr(:macrocall, :var"@test", __source__, :($cmp(_type_intersect($b, $a), $result))))
# test simplified intersect
if !($result === Union{})
@test typeintersect($a, $b) != Union{}
@test typeintersect($b, $a) != Union{}
$(Expr(:macrocall, :var"@test", __source__, :(typeintersect($a, $b) != Union{})))
$(Expr(:macrocall, :var"@test", __source__, :(typeintersect($b, $a) != Union{})))
end
end)
end
end

abstract type IT4805_2{N, T} end
Expand Down Expand Up @@ -2267,31 +2268,46 @@ let S = Tuple{Integer, U} where {II<:Array, U<:Tuple{Vararg{II, 1}}}
@testintersect(S, Tuple{Int, U} where {N, U<:Tuple{Any,Any,Vararg{Any,N}}}, Union{})
end

function equal_envs(env1, env2)
length(env1) == length(env2) || return false
for i = 1:length(env1)
a = env1[i]
b = env2[i]
if a isa TypeVar
if !(b isa TypeVar && a.name == b.name && a.lb == b.lb && a.ub == b.ub)
return false
end
elseif !(a == b)
return false
end
end
return true
end

# issue #43064
let
env_tuple(@nospecialize(x), @nospecialize(y)) = (intersection_env(x, y)[2]...,)
all_var(x::UnionAll) = (x.var, all_var(x.body)...)
all_var(x::DataType) = ()
env_tuple(@nospecialize(x), @nospecialize(y)) = intersection_env(x, y)[2]
TT0 = Tuple{Type{T},Union{Real,Missing,Nothing}} where {T}
TT1 = Union{Type{Int8},Type{Int16}}
@test env_tuple(Tuple{TT1,Missing}, TT0) ===
env_tuple(Tuple{TT1,Nothing}, TT0) ===
env_tuple(Tuple{TT1,Int}, TT0) === all_var(TT0)
env_tuple(Tuple{TT1,Int}, TT0) ===
Core.svec(TT0.var)

TT0 = Tuple{T1,T2,Union{Real,Missing,Nothing}} where {T1,T2}
TT1 = Tuple{T1,T2,Union{Real,Missing,Nothing}} where {T2,T1}
TT2 = Tuple{Union{Int,Int8},Union{Int,Int8},Int}
TT3 = Tuple{Int,Union{Int,Int8},Int}
@test env_tuple(TT2, TT0) === all_var(TT0)
@test env_tuple(TT2, TT1) === all_var(TT1)
@test env_tuple(TT3, TT0) === Base.setindex(all_var(TT0), Int, 1)
@test env_tuple(TT3, TT1) === Base.setindex(all_var(TT1), Int, 2)
@test equal_envs(env_tuple(TT2, TT0), Core.svec(TypeVar(:T1, Union{Int, Int8}), TypeVar(:T2, Union{Int, Int8})))
@test equal_envs(env_tuple(TT2, TT1), Core.svec(TypeVar(:T2, Union{Int, Int8}), TypeVar(:T1, Union{Int, Int8})))
@test equal_envs(env_tuple(TT3, TT0), Core.svec(Int, TypeVar(:T2, Union{Int, Int8})))
@test equal_envs(env_tuple(TT3, TT1), Core.svec(TypeVar(:T2, Union{Int, Int8}), Int))

TT0 = Tuple{T1,T2,T1,Union{Real,Missing,Nothing}} where {T1,T2}
TT1 = Tuple{T1,T2,T1,Union{Real,Missing,Nothing}} where {T2,T1}
TT2 = Tuple{Int,Union{Int,Int8},Int,Int}
@test env_tuple(TT2, TT0) === Base.setindex(all_var(TT0), Int, 1)
@test env_tuple(TT2, TT1) === Base.setindex(all_var(TT1), Int, 2)
@test equal_envs(env_tuple(TT2, TT0), Core.svec(Int, TypeVar(:T2, Union{Int, Int8})))
@test equal_envs(env_tuple(TT2, TT1), Core.svec(TypeVar(:T2, Union{Int, Int8}), Int))
end

#issue #46735
Expand Down Expand Up @@ -2686,3 +2702,22 @@ let S = Tuple{Val{<:T}, Union{Int,T}} where {T},
@testintersect(S, T, !Union{})
@test !Base.has_free_typevars(typeintersect(S, T))
end

#issue 55230
let T1 = NTuple{12, Union{Val{1}, Val{2}, Val{3}, Val{4}, Val{5}, Val{6}}}
T2 = Tuple{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any}
@test T1 <: T2
T2 = Tuple{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Val}
@test T1 <: T2
T2 = Tuple{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Real}
@test !(T1 <: T2)
T2 = Tuple{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Union{Val,Real}}
@test T1 <: T2
T2 = Tuple{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Union{String,Real}}
@test !(T1 <: T2)
T2 = Tuple{<:Union{Val,Real},<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any}
@test T1 <: T2
T2 = Tuple{<:Union{String,Real},<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any}
@test !(T1 <: T2)
@test Tuple{Union{Val{1},Val{2}}} <: Tuple{S} where {T, S<:Val{T}}
end