Skip to content

Commit f1517b4

Browse files
N5N3KristofferC
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 66a3cc9 commit f1517b4

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
@@ -139,7 +139,7 @@ static jl_varbinding_t *lookup(jl_stenv_t *e, jl_tvar_t *v) JL_GLOBALLY_ROOTED J
139139

140140
static int statestack_get(jl_unionstate_t *st, int i) JL_NOTSAFEPOINT
141141
{
142-
assert(i >= 0 && i <= 32767); // limited by the depth bit.
142+
assert(i >= 0 && i < 32767); // limited by the depth bit.
143143
// get the `i`th bit in an array of 32-bit words
144144
jl_bits_stack_t *stack = &st->stack;
145145
while (i >= sizeof(stack->data) * 8) {
@@ -153,7 +153,7 @@ static int statestack_get(jl_unionstate_t *st, int i) JL_NOTSAFEPOINT
153153

154154
static void statestack_set(jl_unionstate_t *st, int i, int val) JL_NOTSAFEPOINT
155155
{
156-
assert(i >= 0 && i <= 32767); // limited by the depth bit.
156+
assert(i >= 0 && i < 32767); // limited by the depth bit.
157157
jl_bits_stack_t *stack = &st->stack;
158158
while (i >= sizeof(stack->data) * 8) {
159159
if (__unlikely(stack->next == NULL)) {
@@ -1448,11 +1448,14 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param)
14481448
}
14491449
if (jl_is_unionall(y)) {
14501450
jl_varbinding_t *xb = lookup(e, (jl_tvar_t*)x);
1451-
if (xb == NULL ? !e->ignore_free : !xb->right) {
1451+
jl_value_t *xub = xb == NULL ? ((jl_tvar_t *)x)->ub : xb->ub;
1452+
if ((xb == NULL ? !e->ignore_free : !xb->right) && xub != y) {
14521453
// We'd better unwrap `y::UnionAll` eagerly if `x` isa ∀-var.
14531454
// This makes sure the following cases work correct:
14541455
// 1) `∀T <: Union{∃S, SomeType{P}} where {P}`: `S == Any` ==> `S >: T`
14551456
// 2) `∀T <: Union{∀T, SomeType{P}} where {P}`:
1457+
// note: if xub == y we'd better try `subtype_var` as `subtype_left_var`
1458+
// hit `==` based fast path.
14561459
return subtype_unionall(x, (jl_unionall_t*)y, e, 1, param);
14571460
}
14581461
}
@@ -1590,6 +1593,8 @@ static int has_exists_typevar(jl_value_t *x, jl_stenv_t *e) JL_NOTSAFEPOINT
15901593
return env != NULL && jl_has_bound_typevars(x, env);
15911594
}
15921595

1596+
static int forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param);
1597+
15931598
static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param, int limit_slow)
15941599
{
15951600
int16_t oldRmore = e->Runions.more;
@@ -1603,7 +1608,18 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t
16031608
return jl_subtype(x, y);
16041609
int has_exists = (!kindx && has_exists_typevar(x, e)) ||
16051610
(!kindy && has_exists_typevar(y, e));
1606-
if (has_exists && (is_exists_typevar(x, e) != is_exists_typevar(y, e))) {
1611+
if (!has_exists) {
1612+
// We can use ∀_∃_subtype safely for ∃ free inputs.
1613+
// This helps to save some bits in union stack.
1614+
jl_saved_unionstate_t oldRunions; push_unionstate(&oldRunions, &e->Runions);
1615+
e->Lunions.used = e->Runions.used = 0;
1616+
e->Lunions.depth = e->Runions.depth = 0;
1617+
e->Lunions.more = e->Runions.more = 0;
1618+
sub = forall_exists_subtype(x, y, e, param);
1619+
pop_unionstate(&e->Runions, &oldRunions);
1620+
return sub;
1621+
}
1622+
if (is_exists_typevar(x, e) != is_exists_typevar(y, e)) {
16071623
e->Lunions.used = 0;
16081624
while (1) {
16091625
e->Lunions.more = 0;
@@ -1617,7 +1633,7 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t
16171633
if (limit_slow == -1)
16181634
limit_slow = kindx || kindy;
16191635
jl_savedenv_t se;
1620-
save_env(e, &se, has_exists);
1636+
save_env(e, &se, 1);
16211637
int count, limited = 0, ini_count = 0;
16221638
jl_saved_unionstate_t latestLunions = {0, 0, 0, NULL};
16231639
while (1) {
@@ -1635,26 +1651,26 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t
16351651
limited = 1;
16361652
if (!sub || !next_union_state(e, 0))
16371653
break;
1638-
if (limited || !has_exists || e->Runions.more == oldRmore) {
1654+
if (limited || e->Runions.more == oldRmore) {
16391655
// re-save env and freeze the ∃decision for previous ∀Union
16401656
// Note: We could ignore the rest `∃Union` decisions if `x` and `y`
16411657
// contain no ∃ typevar, as they have no effect on env.
16421658
ini_count = count;
16431659
push_unionstate(&latestLunions, &e->Lunions);
1644-
re_save_env(e, &se, has_exists);
1660+
re_save_env(e, &se, 1);
16451661
e->Runions.more = oldRmore;
16461662
}
16471663
}
16481664
if (sub || e->Runions.more == oldRmore)
16491665
break;
16501666
assert(e->Runions.more > oldRmore);
16511667
next_union_state(e, 1);
1652-
restore_env(e, &se, has_exists); // also restore Rdepth here
1668+
restore_env(e, &se, 1); // also restore Rdepth here
16531669
e->Runions.more = oldRmore;
16541670
}
16551671
if (!sub)
16561672
assert(e->Runions.more == oldRmore);
1657-
else if (limited || !has_exists)
1673+
else if (limited)
16581674
e->Runions.more = oldRmore;
16591675
free_env(&se);
16601676
return sub;

test/subtype.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2768,3 +2768,17 @@ end
27682768
Tuple{Type{Complex{T}} where T, Type{Complex{T}} where T, Type{String}},
27692769
Tuple{Type{Complex{T}}, Type{Complex{T}}, Type{String}} where T
27702770
)
2771+
2772+
#issue 58129
2773+
for k in 1:500
2774+
@eval struct $(Symbol(:T58129, k)){T} end
2775+
end
2776+
let Tvar = TypeVar(:Tvar)
2777+
V = UnionAll(Tvar, Union{(@eval($(Symbol(:T58129, k)){$Tvar}) for k in 1:500)...})
2778+
@test Set{<:V} <: AbstractSet{<:V}
2779+
end
2780+
let Tvar1 = TypeVar(:Tvar1), Tvar2 = TypeVar(:Tvar2)
2781+
V1 = UnionAll(Tvar1, Union{(@eval($(Symbol(:T58129, k)){$Tvar1}) for k in 1:100)...})
2782+
V2 = UnionAll(Tvar2, Union{(@eval($(Symbol(:T58129, k)){$Tvar2}) for k in 1:100)...})
2783+
@test Set{<:V2} <: AbstractSet{<:V1}
2784+
end

0 commit comments

Comments
 (0)