Skip to content

Commit 56f1c8a

Browse files
authored
typeintersect: fix UnionAll unaliasing bug caused by innervars. (JuliaLang#53553)
typeintersect: fix `UnionAll` unaliasing bug caused by innervars.
2 parents 6335386 + 02f27c2 commit 56f1c8a

File tree

2 files changed

+81
-16
lines changed

2 files changed

+81
-16
lines changed

src/subtype.c

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -881,10 +881,20 @@ static jl_unionall_t *unalias_unionall(jl_unionall_t *u, jl_stenv_t *e)
881881
// in the environment, rename to get a fresh var.
882882
JL_GC_PUSH1(&u);
883883
while (btemp != NULL) {
884-
if (btemp->var == u->var ||
885-
// outer var can only refer to inner var if bounds changed
884+
int aliased = btemp->var == u->var ||
885+
// outer var can only refer to inner var if bounds changed (mainly for subtyping path)
886886
(btemp->lb != btemp->var->lb && jl_has_typevar(btemp->lb, u->var)) ||
887-
(btemp->ub != btemp->var->ub && jl_has_typevar(btemp->ub, u->var))) {
887+
(btemp->ub != btemp->var->ub && jl_has_typevar(btemp->ub, u->var));
888+
if (!aliased && btemp->innervars != NULL) {
889+
for (size_t i = 0; i < jl_array_len(btemp->innervars); i++) {
890+
jl_tvar_t *ivar = (jl_tvar_t*)jl_array_ptr_ref(btemp->innervars, i);
891+
if (ivar == u->var) {
892+
aliased = 1;
893+
break;
894+
}
895+
}
896+
}
897+
if (aliased) {
888898
u = jl_rename_unionall(u);
889899
break;
890900
}
@@ -2839,7 +2849,7 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind
28392849

28402850
// I. Handle indirect innervars (make them behave like direct innervars).
28412851
// 1) record if btemp->lb/ub has indirect innervars.
2842-
// 2) substitute `vb->var` with `varval`/`varval`
2852+
// 2) substitute `vb->var` with `varval`/`newvar`
28432853
// note: We only store the innervar in the outmost `varbinding`,
28442854
// thus we must check all inner env to ensure the recording/substitution
28452855
// is complete
@@ -2903,6 +2913,7 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind
29032913
}
29042914
envind++;
29052915
}
2916+
// FIXME: innervar that depend on `ivar` should also be updated.
29062917
}
29072918
}
29082919
}
@@ -3018,7 +3029,8 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind
30183029
}
30193030

30203031
if (vb->innervars != NULL) {
3021-
for (size_t i = 0; i < jl_array_nrows(vb->innervars); i++) {
3032+
size_t len = jl_array_nrows(vb->innervars), count = 0;
3033+
for (size_t i = 0; i < len; i++) {
30223034
jl_tvar_t *var = (jl_tvar_t*)jl_array_ptr_ref(vb->innervars, i);
30233035
// the `btemp->prev` walk is only giving a sort of post-order guarantee (since we are
30243036
// iterating 2 trees at once), so once we set `wrap`, there might remain other branches
@@ -3032,11 +3044,45 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind
30323044
if (wrap) {
30333045
if (wrap->innervars == NULL)
30343046
wrap->innervars = jl_alloc_array_1d(jl_array_any_type, 0);
3047+
// FIXME: `var`'s dependence should also be pushed into `wrap->innervars`.
30353048
jl_array_ptr_1d_push(wrap->innervars, (jl_value_t*)var);
3049+
jl_array_ptr_set(vb->innervars, i, (jl_value_t*)NULL);
3050+
}
3051+
}
3052+
for (size_t i = 0; i < len; i++) {
3053+
jl_tvar_t *var = (jl_tvar_t*)jl_array_ptr_ref(vb->innervars, i);
3054+
if (var) {
3055+
if (count < i)
3056+
jl_array_ptr_set(vb->innervars, count, (jl_value_t*)var);
3057+
count++;
3058+
}
3059+
}
3060+
if (count != len)
3061+
jl_array_del_end(vb->innervars, len - count);
3062+
if (res != jl_bottom_type) {
3063+
while (count > 1) {
3064+
int changed = 0;
3065+
// Now need to re-sort the vb->innervars using the partial-ordering predicate `jl_has_typevar`.
3066+
// If this is slow, we could possibly switch to a simpler graph sort than this triple loop, such as Tarjan's SCC.
3067+
// But for now we use a variant on selection sort for partial-orders.
3068+
for (size_t i = 0; i < count - 1; i++) {
3069+
jl_tvar_t *vari = (jl_tvar_t*)jl_array_ptr_ref(vb->innervars, i);
3070+
for (size_t j = i+1; j < count; j++) {
3071+
jl_tvar_t *varj = (jl_tvar_t*)jl_array_ptr_ref(vb->innervars, j);
3072+
if (jl_has_typevar(varj->lb, vari) || jl_has_typevar(varj->ub, vari)) {
3073+
jl_array_ptr_set(vb->innervars, j, (jl_value_t*)vari);
3074+
jl_array_ptr_set(vb->innervars, i, (jl_value_t*)varj);
3075+
changed = 1;
3076+
break;
3077+
}
3078+
}
3079+
if (changed) break;
3080+
}
3081+
if (!changed) break;
30363082
}
3037-
else if (res != jl_bottom_type) {
3038-
if (jl_has_typevar(res, var))
3039-
res = jl_type_unionall((jl_tvar_t*)var, res);
3083+
for (size_t i = 0; i < count; i++) {
3084+
jl_tvar_t *var = (jl_tvar_t*)jl_array_ptr_ref(vb->innervars, i);
3085+
res = jl_type_unionall(var, res);
30403086
}
30413087
}
30423088
}
@@ -3056,23 +3102,16 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind
30563102
static jl_value_t *intersect_unionall_(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8_t R, int param, jl_varbinding_t *vb)
30573103
{
30583104
jl_varbinding_t *btemp = e->vars;
3059-
// if the var for this unionall (based on identity) already appears somewhere
3060-
// in the environment, rename to get a fresh var.
3061-
// TODO: might need to look inside types in btemp->lb and btemp->ub
30623105
int envsize = 0;
30633106
while (btemp != NULL) {
30643107
envsize++;
30653108
if (envsize > 120) {
30663109
vb->limited = 1;
30673110
return t;
30683111
}
3069-
if (btemp->var == u->var || btemp->lb == (jl_value_t*)u->var ||
3070-
btemp->ub == (jl_value_t*)u->var) {
3071-
u = jl_rename_unionall(u);
3072-
break;
3073-
}
30743112
btemp = btemp->prev;
30753113
}
3114+
u = unalias_unionall(u, e);
30763115
JL_GC_PUSH1(&u);
30773116
vb->var = u->var;
30783117
e->vars = vb;

test/subtype.jl

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2572,6 +2572,32 @@ let a = Tuple{Union{Nothing, Type{Pair{T1}} where T1}}
25722572
@test !Base.has_free_typevars(typeintersect(a, b))
25732573
end
25742574

2575+
#issue 53366
2576+
let Y = Tuple{Val{T}, Val{Val{T}}} where T
2577+
A = Val{Val{T}} where T
2578+
T = TypeVar(:T, UnionAll(A.var, Val{A.var}))
2579+
B = UnionAll(T, Val{T})
2580+
X = Tuple{A, B}
2581+
@testintersect(X, Y, !Union{})
2582+
end
2583+
2584+
#issue 53621 (requires assertions enabled)
2585+
abstract type A53621{T, R, C, U} <: AbstractSet{Union{C, U}} end
2586+
struct T53621{T, R<:Real, C, U} <: A53621{T, R, C, U} end
2587+
let
2588+
U = TypeVar(:U)
2589+
C = TypeVar(:C)
2590+
T = TypeVar(:T)
2591+
R = TypeVar(:R)
2592+
CC = TypeVar(:CC, Union{C, U})
2593+
UU = TypeVar(:UU, Union{C, U})
2594+
S1 = UnionAll(T, UnionAll(R, Type{UnionAll(C, UnionAll(U, T53621{T, R, C, U}))}))
2595+
S2 = UnionAll(C, UnionAll(U, UnionAll(CC, UnionAll(UU, UnionAll(T, UnionAll(R, T53621{T, R, CC, UU}))))))
2596+
S = Tuple{S1, S2}
2597+
T = Tuple{Type{T53621{T, R}}, AbstractSet{T}} where {T, R}
2598+
@testintersect(S, T, !Union{})
2599+
end
2600+
25752601
#issue 53371
25762602
struct T53371{A,B,C,D,E} end
25772603
S53371{A} = Union{Int, <:A}

0 commit comments

Comments
 (0)