Skip to content

Commit

Permalink
subtyping: fast path for lhs union and rhs typevar
Browse files Browse the repository at this point in the history
Fixes #55230
  • Loading branch information
vtjnash committed Aug 14, 2024
1 parent b7aa5e3 commit 1a3ee70
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 17 deletions.
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

0 comments on commit 1a3ee70

Please sign in to comment.