Skip to content

Commit 596ce65

Browse files
authored
fix #47658, state stack overflow on unions containing typevars (#48221)
1 parent b8adb2e commit 596ce65

File tree

2 files changed

+57
-1
lines changed

2 files changed

+57
-1
lines changed

src/subtype.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,6 +1243,19 @@ static int subtype_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_stenv_t *e, in
12431243
return ans;
12441244
}
12451245

1246+
static int equal_unions(jl_uniontype_t *x, jl_uniontype_t *y, jl_stenv_t *e)
1247+
{
1248+
jl_value_t *saved=NULL; jl_savedenv_t se;
1249+
JL_GC_PUSH1(&saved);
1250+
save_env(e, &saved, &se);
1251+
int eq = forall_exists_equal(x->a, y->a, e) && forall_exists_equal(x->b, y->b, e);
1252+
if (!eq)
1253+
restore_env(e, saved, &se);
1254+
free_env(&se);
1255+
JL_GC_POP();
1256+
return eq;
1257+
}
1258+
12461259
// `param` means we are currently looking at a parameter of a type constructor
12471260
// (as opposed to being outside any type constructor, or comparing variable bounds).
12481261
// this is used to record the positions where type variables occur for the
@@ -1432,6 +1445,27 @@ static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e)
14321445
(is_definite_length_tuple_type(x) && is_indefinite_length_tuple_type(y)))
14331446
return 0;
14341447

1448+
if (jl_is_uniontype(x) && jl_is_uniontype(y)) {
1449+
// For 2 unions, try a more efficient greedy algorithm that compares the unions
1450+
// componentwise. If it returns `false`, we forget it and proceed with the usual
1451+
// algorithm. If it returns `true` we try returning `true`, but need to come back
1452+
// here to try the usual algorithm if subtyping later fails.
1453+
jl_unionstate_t *state = &e->Runions;
1454+
jl_saved_unionstate_t oldRunions; push_unionstate(&oldRunions, state);
1455+
if (state->depth >= state->used) {
1456+
statestack_set(state, state->used, 0);
1457+
state->used++;
1458+
}
1459+
int ui = statestack_get(state, state->depth);
1460+
state->depth++;
1461+
if (ui == 0) {
1462+
state->more = state->depth; // memorize that this was the deepest available choice
1463+
if (equal_unions((jl_uniontype_t*)x, (jl_uniontype_t*)y, e))
1464+
return 1;
1465+
pop_unionstate(state, &oldRunions);
1466+
}
1467+
}
1468+
14351469
jl_saved_unionstate_t oldLunions; push_unionstate(&oldLunions, &e->Lunions);
14361470
e->Lunions.used = 0;
14371471
int sub;

test/subtype.jl

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2362,6 +2362,16 @@ end
23622362
Tuple{Type{Tuple{Val{T},T}}, Val{T}} where T,
23632363
Union{})
23642364

2365+
@test only(intersection_env(Val{Union{Val{Val{T}} where {T},Int}}, Val{Union{T,Int}} where T)[2]) === Val{Val{T}} where {T}
2366+
2367+
# issue 47654
2368+
Vec47654{T} = Union{AbstractVector{T}, AbstractVector{Union{T,Nothing}}}
2369+
struct Wrapper47654{T, V<:Vec47654{T}}
2370+
v::V
2371+
end
2372+
abstract type P47654{A} end
2373+
@test Wrapper47654{P47654, Vector{Union{P47654,Nothing}}} <: Wrapper47654
2374+
23652375
@testset "known subtype/intersect issue" begin
23662376
#issue 45874
23672377
# Causes a hang due to jl_critical_error calling back into malloc...
@@ -2390,7 +2400,7 @@ end
23902400
@test_broken (Tuple{Q,Int} where Q<:Int) <: Tuple{T,T} where T
23912401

23922402
# issue 24333
2393-
@test_broken (Type{Union{Ref,Cvoid}} <: Type{Union{T,Cvoid}} where T)
2403+
@test (Type{Union{Ref,Cvoid}} <: Type{Union{T,Cvoid}} where T)
23942404

23952405
# issue 22123
23962406
t1 = Ref{Ref{Ref{Union{Int64, T}}} where T}
@@ -2401,6 +2411,18 @@ end
24012411
@test_broken (Tuple{T1,T1} where T1<:(Val{T2} where T2)) <: (Tuple{Val{S},Val{S}} where S)
24022412
end
24032413

2414+
# issue #47658
2415+
let T = Ref{NTuple{8, Ref{Union{Int, P}}}} where P,
2416+
S = Ref{NTuple{8, Ref{Union{Int, P}}}} where P
2417+
# note T and S are identical but we need 2 copies to avoid being fooled by pointer equality
2418+
@test T <: Union{Int, S}
2419+
end
2420+
2421+
# try to fool a greedy algorithm that picks X=Int, Y=String here
2422+
@test Tuple{Ref{Union{Int,String}}, Ref{Union{Int,String}}} <: Tuple{Ref{Union{X,Y}}, Ref{X}} where {X,Y}
2423+
# this slightly more complex case has been broken since 1.0 (worked in 0.6)
2424+
@test_broken Tuple{Ref{Union{Int,String,Missing}}, Ref{Union{Int,String}}} <: Tuple{Ref{Union{X,Y}}, Ref{X}} where {X,Y}
2425+
24042426
@test !(Tuple{Any, Any, Any} <: Tuple{Any, Vararg{T}} where T)
24052427

24062428
abstract type MyAbstract47877{C}; end

0 commit comments

Comments
 (0)