Skip to content

Commit 7395439

Browse files
N5N3Kristoffer Carlsson
authored andcommitted
subtype: save some union stack space for ∃ free cases. (#58159)
and avoid eager UnionAll unwrapping to hit more fast path. close #58129 (test passed locally) close #56350 (MWE returns `Tuple{Any, Any, Vararg}` now.) (cherry picked from commit 334c316)
1 parent d3c44fe commit 7395439

File tree

2 files changed

+39
-9
lines changed

2 files changed

+39
-9
lines changed

src/subtype.c

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ static jl_varbinding_t *lookup(jl_stenv_t *e, jl_tvar_t *v) JL_GLOBALLY_ROOTED J
128128

129129
static int statestack_get(jl_unionstate_t *st, int i) JL_NOTSAFEPOINT
130130
{
131-
assert(i >= 0 && i <= 32767); // limited by the depth bit.
131+
assert(i >= 0 && i < 32767); // limited by the depth bit.
132132
// get the `i`th bit in an array of 32-bit words
133133
jl_bits_stack_t *stack = &st->stack;
134134
while (i >= sizeof(stack->data) * 8) {
@@ -142,7 +142,7 @@ static int statestack_get(jl_unionstate_t *st, int i) JL_NOTSAFEPOINT
142142

143143
static void statestack_set(jl_unionstate_t *st, int i, int val) JL_NOTSAFEPOINT
144144
{
145-
assert(i >= 0 && i <= 32767); // limited by the depth bit.
145+
assert(i >= 0 && i < 32767); // limited by the depth bit.
146146
jl_bits_stack_t *stack = &st->stack;
147147
while (i >= sizeof(stack->data) * 8) {
148148
if (__unlikely(stack->next == NULL)) {
@@ -1409,11 +1409,14 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param)
14091409
}
14101410
if (jl_is_unionall(y)) {
14111411
jl_varbinding_t *xb = lookup(e, (jl_tvar_t*)x);
1412-
if (xb == NULL ? !e->ignore_free : !xb->right) {
1412+
jl_value_t *xub = xb == NULL ? ((jl_tvar_t *)x)->ub : xb->ub;
1413+
if ((xb == NULL ? !e->ignore_free : !xb->right) && xub != y) {
14131414
// We'd better unwrap `y::UnionAll` eagerly if `x` isa ∀-var.
14141415
// This makes sure the following cases work correct:
14151416
// 1) `∀T <: Union{∃S, SomeType{P}} where {P}`: `S == Any` ==> `S >: T`
14161417
// 2) `∀T <: Union{∀T, SomeType{P}} where {P}`:
1418+
// note: if xub == y we'd better try `subtype_var` as `subtype_left_var`
1419+
// hit `==` based fast path.
14171420
return subtype_unionall(x, (jl_unionall_t*)y, e, 1, param);
14181421
}
14191422
}
@@ -1551,6 +1554,8 @@ static int has_exists_typevar(jl_value_t *x, jl_stenv_t *e) JL_NOTSAFEPOINT
15511554
return env != NULL && jl_has_bound_typevars(x, env);
15521555
}
15531556

1557+
static int forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param);
1558+
15541559
static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param, int limit_slow)
15551560
{
15561561
int16_t oldRmore = e->Runions.more;
@@ -1564,7 +1569,18 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t
15641569
return jl_subtype(x, y);
15651570
int has_exists = (!kindx && has_exists_typevar(x, e)) ||
15661571
(!kindy && has_exists_typevar(y, e));
1567-
if (has_exists && (is_exists_typevar(x, e) != is_exists_typevar(y, e))) {
1572+
if (!has_exists) {
1573+
// We can use ∀_∃_subtype safely for ∃ free inputs.
1574+
// This helps to save some bits in union stack.
1575+
jl_saved_unionstate_t oldRunions; push_unionstate(&oldRunions, &e->Runions);
1576+
e->Lunions.used = e->Runions.used = 0;
1577+
e->Lunions.depth = e->Runions.depth = 0;
1578+
e->Lunions.more = e->Runions.more = 0;
1579+
sub = forall_exists_subtype(x, y, e, param);
1580+
pop_unionstate(&e->Runions, &oldRunions);
1581+
return sub;
1582+
}
1583+
if (is_exists_typevar(x, e) != is_exists_typevar(y, e)) {
15681584
e->Lunions.used = 0;
15691585
while (1) {
15701586
e->Lunions.more = 0;
@@ -1578,7 +1594,7 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t
15781594
if (limit_slow == -1)
15791595
limit_slow = kindx || kindy;
15801596
jl_savedenv_t se;
1581-
save_env(e, &se, has_exists);
1597+
save_env(e, &se, 1);
15821598
int count, limited = 0, ini_count = 0;
15831599
jl_saved_unionstate_t latestLunions = {0, 0, 0, NULL};
15841600
while (1) {
@@ -1596,26 +1612,26 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t
15961612
limited = 1;
15971613
if (!sub || !next_union_state(e, 0))
15981614
break;
1599-
if (limited || !has_exists || e->Runions.more == oldRmore) {
1615+
if (limited || e->Runions.more == oldRmore) {
16001616
// re-save env and freeze the ∃decision for previous ∀Union
16011617
// Note: We could ignore the rest `∃Union` decisions if `x` and `y`
16021618
// contain no ∃ typevar, as they have no effect on env.
16031619
ini_count = count;
16041620
push_unionstate(&latestLunions, &e->Lunions);
1605-
re_save_env(e, &se, has_exists);
1621+
re_save_env(e, &se, 1);
16061622
e->Runions.more = oldRmore;
16071623
}
16081624
}
16091625
if (sub || e->Runions.more == oldRmore)
16101626
break;
16111627
assert(e->Runions.more > oldRmore);
16121628
next_union_state(e, 1);
1613-
restore_env(e, &se, has_exists); // also restore Rdepth here
1629+
restore_env(e, &se, 1); // also restore Rdepth here
16141630
e->Runions.more = oldRmore;
16151631
}
16161632
if (!sub)
16171633
assert(e->Runions.more == oldRmore);
1618-
else if (limited || !has_exists)
1634+
else if (limited)
16191635
e->Runions.more = oldRmore;
16201636
free_env(&se);
16211637
return sub;

test/subtype.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2717,3 +2717,17 @@ end
27172717
Tuple{Type{Complex{T}} where T, Type{Complex{T}} where T, Type{String}},
27182718
Tuple{Type{Complex{T}}, Type{Complex{T}}, Type{String}} where T
27192719
)
2720+
2721+
#issue 58129
2722+
for k in 1:500
2723+
@eval struct $(Symbol(:T58129, k)){T} end
2724+
end
2725+
let Tvar = TypeVar(:Tvar)
2726+
V = UnionAll(Tvar, Union{(@eval($(Symbol(:T58129, k)){$Tvar}) for k in 1:500)...})
2727+
@test Set{<:V} <: AbstractSet{<:V}
2728+
end
2729+
let Tvar1 = TypeVar(:Tvar1), Tvar2 = TypeVar(:Tvar2)
2730+
V1 = UnionAll(Tvar1, Union{(@eval($(Symbol(:T58129, k)){$Tvar1}) for k in 1:100)...})
2731+
V2 = UnionAll(Tvar2, Union{(@eval($(Symbol(:T58129, k)){$Tvar2}) for k in 1:100)...})
2732+
@test Set{<:V2} <: AbstractSet{<:V1}
2733+
end

0 commit comments

Comments
 (0)