Skip to content

Commit 9b3a2bb

Browse files
N5N3KristofferC
authored andcommitted
Fix #41096 and #43082, make sure env is restored when typeintersect tries a new Union decision (#46350)
* `intersect_all` should always `restore_env`. let `merge_env` track valid `env` change. * Add test. Co-authored-by: Jeff Bezanson <[email protected]> (cherry picked from commit 9aabb4c)
1 parent 0be7f57 commit 9b3a2bb

File tree

2 files changed

+78
-29
lines changed

2 files changed

+78
-29
lines changed

src/subtype.c

+49-24
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,9 @@ static void restore_env(jl_stenv_t *e, jl_value_t *root, jl_savedenv_t *se) JL_N
200200
jl_varbinding_t *v = e->vars;
201201
int i = 0, j = 0;
202202
while (v != NULL) {
203-
if (root) v->lb = jl_svecref(root, i);
204-
i++;
205-
if (root) v->ub = jl_svecref(root, i);
206-
i++;
207-
if (root) v->innervars = (jl_array_t*)jl_svecref(root, i);
208-
i++;
203+
if (root) v->lb = jl_svecref(root, i++);
204+
if (root) v->ub = jl_svecref(root, i++);
205+
if (root) v->innervars = (jl_array_t*)jl_svecref(root, i++);
209206
v->occurs_inv = se->buf[j++];
210207
v->occurs_cov = se->buf[j++];
211208
v = v->prev;
@@ -2323,6 +2320,11 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int
23232320
JL_GC_POP();
23242321
return jl_bottom_type;
23252322
}
2323+
if (jl_is_uniontype(ub) && !jl_is_uniontype(a)) {
2324+
bb->ub = ub;
2325+
bb->lb = jl_bottom_type;
2326+
ub = (jl_value_t*)b;
2327+
}
23262328
}
23272329
if (ub != (jl_value_t*)b) {
23282330
if (jl_has_free_typevars(ub)) {
@@ -3166,26 +3168,50 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa
31663168
return jl_bottom_type;
31673169
}
31683170

3171+
static int merge_env(jl_stenv_t *e, jl_value_t **root, jl_savedenv_t *se, int count)
3172+
{
3173+
if (!count) {
3174+
save_env(e, root, se);
3175+
return 1;
3176+
}
3177+
int n = 0;
3178+
jl_varbinding_t *v = e->vars;
3179+
jl_value_t *ub = NULL, *vub = NULL;
3180+
JL_GC_PUSH2(&ub, &vub);
3181+
while (v != NULL) {
3182+
if (v->ub != v->var->ub || v->lb != v->var->lb) {
3183+
jl_value_t *lb = jl_svecref(*root, n);
3184+
if (v->lb != lb)
3185+
jl_svecset(*root, n, lb ? jl_bottom_type : v->lb);
3186+
ub = jl_svecref(*root, n+1);
3187+
vub = v->ub;
3188+
if (vub != ub)
3189+
jl_svecset(*root, n+1, ub ? simple_join(ub, vub) : vub);
3190+
}
3191+
n = n + 3;
3192+
v = v->prev;
3193+
}
3194+
JL_GC_POP();
3195+
return count + 1;
3196+
}
3197+
31693198
static jl_value_t *intersect_all(jl_value_t *x, jl_value_t *y, jl_stenv_t *e)
31703199
{
31713200
e->Runions.depth = 0;
31723201
e->Runions.more = 0;
31733202
e->Runions.used = 0;
31743203
jl_value_t **is;
3175-
JL_GC_PUSHARGS(is, 3);
3204+
JL_GC_PUSHARGS(is, 4);
31763205
jl_value_t **saved = &is[2];
3177-
jl_savedenv_t se;
3206+
jl_value_t **merged = &is[3];
3207+
jl_savedenv_t se, me;
31783208
save_env(e, saved, &se);
31793209
int lastset = 0, niter = 0, total_iter = 0;
31803210
jl_value_t *ii = intersect(x, y, e, 0);
31813211
is[0] = ii; // root
3182-
if (ii == jl_bottom_type) {
3183-
restore_env(e, *saved, &se);
3184-
}
3185-
else {
3186-
free_env(&se);
3187-
save_env(e, saved, &se);
3188-
}
3212+
if (is[0] != jl_bottom_type)
3213+
niter = merge_env(e, merged, &me, niter);
3214+
restore_env(e, *saved, &se);
31893215
while (e->Runions.more) {
31903216
if (e->emptiness_only && ii != jl_bottom_type)
31913217
break;
@@ -3199,28 +3225,27 @@ static jl_value_t *intersect_all(jl_value_t *x, jl_value_t *y, jl_stenv_t *e)
31993225

32003226
is[0] = ii;
32013227
is[1] = intersect(x, y, e, 0);
3202-
if (is[1] == jl_bottom_type) {
3203-
restore_env(e, *saved, &se);
3204-
}
3205-
else {
3206-
free_env(&se);
3207-
save_env(e, saved, &se);
3208-
}
3228+
if (is[1] != jl_bottom_type)
3229+
niter = merge_env(e, merged, &me, niter);
3230+
restore_env(e, *saved, &se);
32093231
if (is[0] == jl_bottom_type)
32103232
ii = is[1];
32113233
else if (is[1] == jl_bottom_type)
32123234
ii = is[0];
32133235
else {
32143236
// TODO: the repeated subtype checks in here can get expensive
32153237
ii = jl_type_union(is, 2);
3216-
niter++;
32173238
}
32183239
total_iter++;
3219-
if (niter > 3 || total_iter > 400000) {
3240+
if (niter > 4 || total_iter > 400000) {
32203241
ii = y;
32213242
break;
32223243
}
32233244
}
3245+
if (niter){
3246+
restore_env(e, *merged, &me);
3247+
free_env(&me);
3248+
}
32243249
free_env(&se);
32253250
JL_GC_POP();
32263251
return ii;

test/subtype.jl

+29-5
Original file line numberDiff line numberDiff line change
@@ -1928,12 +1928,24 @@ let A = Tuple{Ref{T}, Vararg{T}} where T,
19281928
B = Tuple{Ref{U}, Union{Ref{S}, Ref{U}, Int}, Union{Ref{S}, S}} where S where U,
19291929
C = Tuple{Ref{U}, Union{Ref{S}, Ref{U}, Ref{W}}, Union{Ref{S}, W, V}} where V<:AbstractArray where W where S where U
19301930
I = typeintersect(A, B)
1931+
Ts = (Tuple{Ref{Int}, Int, Int}, Tuple{Ref{Ref{Int}}, Ref{Int}, Ref{Int}})
19311932
@test I != Union{}
19321933
@test I <: A
1933-
@test I <: B
1934-
# avoid stack overflow
1934+
@test_broken I <: B
1935+
for T in Ts
1936+
if T <: A && T <: B
1937+
@test T <: I
1938+
end
1939+
end
19351940
J = typeintersect(A, C)
1936-
@test_broken J != Union{}
1941+
@test J != Union{}
1942+
@test J <: A
1943+
@test_broken J <: C
1944+
for T in Ts
1945+
if T <: A && T <: C
1946+
@test T <: J
1947+
end
1948+
end
19371949
end
19381950

19391951
let A = Tuple{Dict{I,T}, I, T} where T where I,
@@ -1964,8 +1976,9 @@ let A = Tuple{Any, Type{Ref{_A}} where _A},
19641976
B = Tuple{Type{T}, Type{<:Union{Ref{T}, T}}} where T,
19651977
I = typeintersect(A, B)
19661978
@test I != Union{}
1967-
# TODO: this intersection result is still too narrow
1968-
@test_broken Tuple{Type{Ref{Integer}}, Type{Ref{Integer}}} <: I
1979+
@test Tuple{Type{Ref{Integer}}, Type{Ref{Integer}}} <: I
1980+
# TODO: this intersection result seems too wide (I == B) ?
1981+
@test_broken !<:(Tuple{Type{Int}, Type{Int}}, I)
19691982
end
19701983

19711984
@testintersect(Tuple{Type{T}, T} where T<:(Tuple{Vararg{_A, _B}} where _B where _A),
@@ -1996,3 +2009,14 @@ let T = TypeVar(:T, Real),
19962009
@test !(UnionAll(T, UnionAll(V, UnionAll(T, Type{Pair{T, V}}))) <: UnionAll(T, UnionAll(V, Type{Pair{T, V}})))
19972010
@test !(UnionAll(T, UnionAll(V, UnionAll(T, S))) <: UnionAll(T, UnionAll(V, S)))
19982011
end
2012+
2013+
# issue #41096
2014+
let C = Val{Val{B}} where {B}
2015+
@testintersect(Val{<:Union{Missing, Val{false}, Val{true}}}, C, Val{<:Union{Val{true}, Val{false}}})
2016+
@testintersect(Val{<:Union{Nothing, Val{true}, Val{false}}}, C, Val{<:Union{Val{true}, Val{false}}})
2017+
@testintersect(Val{<:Union{Nothing, Val{false}}}, C, Val{Val{false}})
2018+
end
2019+
2020+
#issue #43082
2021+
struct X43082{A, I, B<:Union{Ref{I},I}}; end
2022+
@testintersect(Tuple{X43082{T}, Int} where T, Tuple{X43082{Int}, Any}, Tuple{X43082{Int}, Int})

0 commit comments

Comments
 (0)