Skip to content

Commit

Permalink
Normalize (simplify) UnionAlls when used as type parameter (#36211)
Browse files Browse the repository at this point in the history
(cherry picked from commit fedefe9)
  • Loading branch information
martinholters authored and staticfloat committed Dec 22, 2022
1 parent 0581ce0 commit b9f3d49
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 1 deletion.
92 changes: 92 additions & 0 deletions src/jltypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,93 @@ static jl_value_t *normalize_vararg(jl_value_t *va)
return va;
}

int _may_substitute_ub(jl_value_t *v, jl_tvar_t *var, int inside_inv, int *cov_count) JL_NOTSAFEPOINT
{
if (v == (jl_value_t*)var) {
if (inside_inv) {
return 0;
}
else {
(*cov_count)++;
return *cov_count <= 1 || jl_is_concrete_type(var->ub);
}
}
else if (jl_is_uniontype(v)) {
return _may_substitute_ub(((jl_uniontype_t*)v)->a, var, inside_inv, cov_count) &&
_may_substitute_ub(((jl_uniontype_t*)v)->b, var, inside_inv, cov_count);
}
else if (jl_is_unionall(v)) {
jl_unionall_t *ua = (jl_unionall_t*)v;
if (ua->var == var)
return 1;
return _may_substitute_ub(ua->var->lb, var, inside_inv, cov_count) &&
_may_substitute_ub(ua->var->ub, var, inside_inv, cov_count) &&
_may_substitute_ub(ua->body, var, inside_inv, cov_count);
}
else if (jl_is_vararg_type(v)) {
int old_count = *cov_count;
jl_value_t *vaT = jl_tparam0(v);
jl_value_t *vaN = jl_tparam1(v);
if (!_may_substitute_ub(vaT, var, inside_inv, cov_count))
return 0;
if (*cov_count > old_count && !jl_is_concrete_type(var->ub))
return 0;
if (!_may_substitute_ub(vaN, var, 1, cov_count))
return 0;
}
else if (jl_is_datatype(v)) {
int invar = inside_inv || !jl_is_tuple_type(v);
for (size_t i = 0; i < jl_nparams(v); i++) {
if (!_may_substitute_ub(jl_tparam(v,i), var, invar, cov_count))
return 0;
}
}
return 1;
}

// Check whether `var` may be replaced with its upper bound `ub` in `v where var<:ub`
// Conditions:
// * `var` does not appear in invariant position
// * `var` appears at most once (in covariant position) and not in a `Vararg`
// unless the upper bound is concrete (diagonal rule)
int may_substitute_ub(jl_value_t *v, jl_tvar_t *var) JL_NOTSAFEPOINT
{
int cov_count = 0;
return _may_substitute_ub(v, var, 0, &cov_count);
}

jl_value_t *normalize_unionalls(jl_value_t *t)
{
JL_GC_PUSH1(&t);
if (jl_is_uniontype(t)) {
jl_uniontype_t *u = (jl_uniontype_t*)t;
jl_value_t *a = NULL;
jl_value_t *b = NULL;
JL_GC_PUSH2(&a, &b);
a = normalize_unionalls(u->a);
b = normalize_unionalls(u->b);
if (a != u->a || b != u->b) {
t = jl_new_struct(jl_uniontype_type, a, b);
}
JL_GC_POP();
}
else if (jl_is_unionall(t)) {
jl_unionall_t *u = (jl_unionall_t*)t;
jl_value_t *body = normalize_unionalls(u->body);
if (body != u->body) {
JL_GC_PUSH1(&body);
t = jl_new_struct(jl_unionall_type, u->var, body);
JL_GC_POP();
u = (jl_unionall_t*)t;
}

if (u->var->lb == u->var->ub || may_substitute_ub(body, u->var))
t = jl_instantiate_unionall(u, u->var->ub);
}
JL_GC_POP();
return t;
}

static jl_value_t *_jl_instantiate_type_in_env(jl_value_t *ty, jl_unionall_t *env, jl_value_t **vals, jl_typeenv_t *prev, jl_typestack_t *stack);

static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **iparams, size_t ntp,
Expand All @@ -1129,6 +1216,11 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value
jl_typename_t *tn = dt->name;
int istuple = (tn == jl_tuple_typename);
int isnamedtuple = (tn == jl_namedtuple_typename);
if (dt->name != jl_type_typename) {
for (size_t i = 0; i < ntp; i++)
iparams[i] = normalize_unionalls(iparams[i]);
}

// check type cache
if (cacheable) {
size_t i;
Expand Down
9 changes: 9 additions & 0 deletions test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7540,3 +7540,12 @@ for _ in 1:5
@test all(x->ismissing(x.i), a)
end
end

# issue #35130
const T35130 = Tuple{Vector{Int}, <:Any}
@eval struct A35130
x::Vector{Tuple{Vector{Int}, Any}}
A35130(x) = $(Expr(:new, :A35130, :x))
end
h35130(x) = A35130(Any[x][1]::Vector{T35130})
@test h35130(T35130[([1],1)]) isa A35130
2 changes: 1 addition & 1 deletion test/subtype.jl
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ function test_diagonal()
@test !issub(Type{Tuple{T,Any} where T}, Type{Tuple{T,T}} where T)
@test !issub(Type{Tuple{T,Any,T} where T}, Type{Tuple{T,T,T}} where T)
@test_broken issub(Type{Tuple{T} where T}, Type{Tuple{T}} where T)
@test_broken issub(Ref{Tuple{T} where T}, Ref{Tuple{T}} where T)
@test issub(Ref{Tuple{T} where T}, Ref{Tuple{T}} where T)
@test !issub(Type{Tuple{T,T} where T}, Type{Tuple{T,T}} where T)
@test !issub(Type{Tuple{T,T,T} where T}, Type{Tuple{T,T,T}} where T)
@test isequal_type(Ref{Tuple{T, T} where Int<:T<:Int},
Expand Down

0 comments on commit b9f3d49

Please sign in to comment.