Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

subtype: fast path for Type == TypeVar #56640

Merged
merged 1 commit into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions src/subtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -1660,6 +1660,42 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t
return sub;
}

static int equal_var(jl_tvar_t *v, jl_value_t *x, jl_stenv_t *e)
{
assert(e->Loffset == 0);
// Theoretically bounds change would be merged for union inputs.
// But intersection is not happy as splitting helps to avoid circular env.
assert(!e->intersection || !jl_is_uniontype(x));
jl_varbinding_t *vb = lookup(e, v);
if (e->intersection && vb != NULL && vb->lb == vb->ub && jl_is_typevar(vb->lb))
return equal_var((jl_tvar_t *)vb->lb, x, e);
record_var_occurrence(vb, e, 2);
if (vb == NULL)
return e->ignore_free || (
local_forall_exists_subtype(x, v->lb, e, 2, !jl_has_free_typevars(x)) &&
local_forall_exists_subtype(v->ub, x, e, 0, 0));
if (!vb->right)
return local_forall_exists_subtype(x, vb->lb, e, 2, !jl_has_free_typevars(x)) &&
local_forall_exists_subtype(vb->ub, x, e, 0, 0);
if (vb->lb == x)
return var_lt(v, x, e, 0);
if (!subtype_ccheck(x, vb->ub, e))
return 0;
jl_value_t *lb = simple_join(vb->lb, x);
JL_GC_PUSH1(&lb);
if (!e->intersection || !jl_is_typevar(lb) || !reachable_var(lb, v, e))
vb->lb = lb;
JL_GC_POP();
if (vb->ub == x)
return 1;
if (!subtype_ccheck(vb->lb, x, e))
return 0;
// skip `simple_meet` here as we have proven `x <: vb->ub`
if (!e->intersection || !reachable_var(x, v, e))
vb->ub = x;
return 1;
}

static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e)
{
if (obviously_egal(x, y)) return 1;
Expand Down Expand Up @@ -1690,6 +1726,12 @@ static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e)
}
}

if (e->Loffset == 0 && jl_is_typevar(y) && jl_is_type(x) && (!e->intersection || !jl_is_uniontype(x))) {
// Fastpath for Type == TypeVar.
// Avoid duplicated `<:` check between adjacent `var_gt` and `var_lt`
return equal_var((jl_tvar_t *)y, x, e);
}

jl_saved_unionstate_t oldLunions; push_unionstate(&oldLunions, &e->Lunions);

int sub = local_forall_exists_subtype(x, y, e, 2, -1);
Expand Down
16 changes: 16 additions & 0 deletions test/subtype.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2730,3 +2730,19 @@ let S = Dict{V,V} where {V},
@test A <: typeintersect(S, T)
@test A <: typeintersect(T, S)
end

#issue 56606
let
A = Tuple{Val{1}}
B = Tuple{Val}
for _ in 1:30
A = Tuple{Val{A}}
B = Tuple{Val{<:B}}
end
@test A <: B
end
@testintersect(
Val{Tuple{Int,S,T}} where {S<:Any,T<:Vector{Vector{Int}}},
Val{Tuple{T,R,S}} where {T,R<:Vector{T},S<:Vector{R}},
Val{Tuple{Int, Vector{Int}, T}} where T<:Vector{Vector{Int}},
)
Loading