Skip to content

Commit c654fd2

Browse files
committed
subtyping: fast path for lhs union and rhs typevar
Fixes #55230
1 parent 2193895 commit c654fd2

File tree

2 files changed

+60
-17
lines changed

2 files changed

+60
-17
lines changed

src/subtype.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1312,7 +1312,20 @@ static int try_subtype_by_bounds(jl_value_t *a, jl_value_t *b, jl_stenv_t *e);
13121312
static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param)
13131313
{
13141314
if (jl_is_uniontype(x)) {
1315-
if (x == y) return 1;
1315+
if (obviously_egal(x, y))
1316+
return 1;
1317+
// TODO: the e->intersection check prevents creating more accurate environments that
1318+
// it later cannot handle (e.g. omit_bad_union), causing the test for #54356 to fail
1319+
if (!e->intersection && e->Runions.depth == 0 && jl_is_typevar(y)) {
1320+
// Similar to fast path for repeated elements: if there have been no outer
1321+
// unions on the right, and the right side is a typevar, then we can handle the
1322+
// typevar first before picking a union element, under the theory that it may
1323+
// be easy to match or reject this whole union in comparing and setting the lb
1324+
// and ub of the variable binding, without needing to examine each element
1325+
jl_varbinding_t *yy = lookup(e, (jl_tvar_t*)y);
1326+
if ((yy ? yy->lb : ((jl_tvar_t*)y)->lb) == jl_bottom_type)
1327+
return subtype_var((jl_tvar_t*)y, x, e, 1, param);
1328+
}
13161329
x = pick_union_element(x, e, 0);
13171330
}
13181331
if (jl_is_uniontype(y)) {

test/subtype.jl

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -707,16 +707,17 @@ macro testintersect(a, b, result)
707707
a = esc(a)
708708
b = esc(b)
709709
result = esc(result)
710-
Base.remove_linenums!(quote
710+
# use a manual macrocall expression since Test will examine this __source__ value
711+
return quote
711712
# test real intersect
712-
@test $cmp(_type_intersect($a, $b), $result)
713-
@test $cmp(_type_intersect($b, $a), $result)
713+
$(Expr(:macrocall, :var"@test", __source__, :($cmp(_type_intersect($a, $b), $result))))
714+
$(Expr(:macrocall, :var"@test", __source__, :($cmp(_type_intersect($b, $a), $result))))
714715
# test simplified intersect
715716
if !($result === Union{})
716-
@test typeintersect($a, $b) != Union{}
717-
@test typeintersect($b, $a) != Union{}
717+
$(Expr(:macrocall, :var"@test", __source__, :(typeintersect($a, $b) != Union{})))
718+
$(Expr(:macrocall, :var"@test", __source__, :(typeintersect($b, $a) != Union{})))
718719
end
719-
end)
720+
end
720721
end
721722

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

2271+
function equal_envs(env1, env2)
2272+
length(env1) == length(env2) || return false
2273+
for i = 1:length(env1)
2274+
a = env1[i]
2275+
b = env2[i]
2276+
if a isa TypeVar
2277+
if !(b isa TypeVar && a.name == b.name && a.lb == b.lb && a.ub == b.ub)
2278+
return false
2279+
end
2280+
elseif !(a == b)
2281+
return false
2282+
end
2283+
end
2284+
return true
2285+
end
2286+
22702287
# issue #43064
22712288
let
2272-
env_tuple(@nospecialize(x), @nospecialize(y)) = (intersection_env(x, y)[2]...,)
2273-
all_var(x::UnionAll) = (x.var, all_var(x.body)...)
2274-
all_var(x::DataType) = ()
2289+
env_tuple(@nospecialize(x), @nospecialize(y)) = intersection_env(x, y)[2]
22752290
TT0 = Tuple{Type{T},Union{Real,Missing,Nothing}} where {T}
22762291
TT1 = Union{Type{Int8},Type{Int16}}
22772292
@test env_tuple(Tuple{TT1,Missing}, TT0) ===
22782293
env_tuple(Tuple{TT1,Nothing}, TT0) ===
2279-
env_tuple(Tuple{TT1,Int}, TT0) === all_var(TT0)
2294+
env_tuple(Tuple{TT1,Int}, TT0) ===
2295+
Core.svec(TT0.var)
22802296

22812297
TT0 = Tuple{T1,T2,Union{Real,Missing,Nothing}} where {T1,T2}
22822298
TT1 = Tuple{T1,T2,Union{Real,Missing,Nothing}} where {T2,T1}
22832299
TT2 = Tuple{Union{Int,Int8},Union{Int,Int8},Int}
22842300
TT3 = Tuple{Int,Union{Int,Int8},Int}
2285-
@test env_tuple(TT2, TT0) === all_var(TT0)
2286-
@test env_tuple(TT2, TT1) === all_var(TT1)
2287-
@test env_tuple(TT3, TT0) === Base.setindex(all_var(TT0), Int, 1)
2288-
@test env_tuple(TT3, TT1) === Base.setindex(all_var(TT1), Int, 2)
2301+
@test equal_envs(env_tuple(TT2, TT0), Core.svec(TypeVar(:T1, Union{Int, Int8}), TypeVar(:T2, Union{Int, Int8})))
2302+
@test equal_envs(env_tuple(TT2, TT1), Core.svec(TypeVar(:T2, Union{Int, Int8}), TypeVar(:T1, Union{Int, Int8})))
2303+
@test equal_envs(env_tuple(TT3, TT0), Core.svec(Int, TypeVar(:T2, Union{Int, Int8})))
2304+
@test equal_envs(env_tuple(TT3, TT1), Core.svec(TypeVar(:T2, Union{Int, Int8}), Int))
22892305

22902306
TT0 = Tuple{T1,T2,T1,Union{Real,Missing,Nothing}} where {T1,T2}
22912307
TT1 = Tuple{T1,T2,T1,Union{Real,Missing,Nothing}} where {T2,T1}
22922308
TT2 = Tuple{Int,Union{Int,Int8},Int,Int}
2293-
@test env_tuple(TT2, TT0) === Base.setindex(all_var(TT0), Int, 1)
2294-
@test env_tuple(TT2, TT1) === Base.setindex(all_var(TT1), Int, 2)
2309+
@test equal_envs(env_tuple(TT2, TT0), Core.svec(Int, TypeVar(:T2, Union{Int, Int8})))
2310+
@test equal_envs(env_tuple(TT2, TT1), Core.svec(TypeVar(:T2, Union{Int, Int8}), Int))
22952311
end
22962312

22972313
#issue #46735
@@ -2686,3 +2702,17 @@ let S = Tuple{Val{<:T}, Union{Int,T}} where {T},
26862702
@testintersect(S, T, !Union{})
26872703
@test !Base.has_free_typevars(typeintersect(S, T))
26882704
end
2705+
2706+
#issue 55230
2707+
let T1 = NTuple{12, Union{Val{1}, Val{2}, Val{3}, Val{4}, Val{5}, Val{6}}}
2708+
T2 = Tuple{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any}
2709+
@test T1 <: T2
2710+
T2 = Tuple{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Val}
2711+
@test T1 <: T2
2712+
T2 = Tuple{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Real}
2713+
@test !(T1 <: T2)
2714+
T2 = Tuple{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Union{Val,Real}}
2715+
@test T1 <: T2
2716+
T2 = Tuple{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,<:Union{String,Real}}
2717+
@test !(T1 <: T2)
2718+
end

0 commit comments

Comments
 (0)