Skip to content

Commit 0f191f2

Browse files
martinholtersantoine-levitt
authored andcommitted
Normalize (simplify) UnionAlls when used as type parameter (JuliaLang#36211)
1 parent 5e52e93 commit 0f191f2

File tree

3 files changed

+101
-1
lines changed

3 files changed

+101
-1
lines changed

src/jltypes.c

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,6 +1153,92 @@ static jl_value_t *extract_wrapper(jl_value_t *t JL_PROPAGATES_ROOT) JL_GLOBALLY
11531153
return NULL;
11541154
}
11551155

1156+
int _may_substitute_ub(jl_value_t *v, jl_tvar_t *var, int inside_inv, int *cov_count) JL_NOTSAFEPOINT
1157+
{
1158+
if (v == (jl_value_t*)var) {
1159+
if (inside_inv) {
1160+
return 0;
1161+
}
1162+
else {
1163+
(*cov_count)++;
1164+
return *cov_count <= 1 || jl_is_concrete_type(var->ub);
1165+
}
1166+
}
1167+
else if (jl_is_uniontype(v)) {
1168+
return _may_substitute_ub(((jl_uniontype_t*)v)->a, var, inside_inv, cov_count) &&
1169+
_may_substitute_ub(((jl_uniontype_t*)v)->b, var, inside_inv, cov_count);
1170+
}
1171+
else if (jl_is_unionall(v)) {
1172+
jl_unionall_t *ua = (jl_unionall_t*)v;
1173+
if (ua->var == var)
1174+
return 1;
1175+
return _may_substitute_ub(ua->var->lb, var, inside_inv, cov_count) &&
1176+
_may_substitute_ub(ua->var->ub, var, inside_inv, cov_count) &&
1177+
_may_substitute_ub(ua->body, var, inside_inv, cov_count);
1178+
}
1179+
else if (jl_is_datatype(v)) {
1180+
int invar = inside_inv || !jl_is_tuple_type(v);
1181+
for (size_t i = 0; i < jl_nparams(v); i++) {
1182+
if (!_may_substitute_ub(jl_tparam(v,i), var, invar, cov_count))
1183+
return 0;
1184+
}
1185+
}
1186+
else if (jl_is_vararg(v)) {
1187+
jl_vararg_t *va = (jl_vararg_t*)v;
1188+
int old_count = *cov_count;
1189+
if (va->T && !_may_substitute_ub(va->T, var, inside_inv, cov_count))
1190+
return 0;
1191+
if (*cov_count > old_count && !jl_is_concrete_type(var->ub))
1192+
return 0;
1193+
if (va->N && !_may_substitute_ub(va->N, var, 1, cov_count))
1194+
return 0;
1195+
}
1196+
return 1;
1197+
}
1198+
1199+
// Check whether `var` may be replaced with its upper bound `ub` in `v where var<:ub`
1200+
// Conditions:
1201+
// * `var` does not appear in invariant position
1202+
// * `var` appears at most once (in covariant position) and not in a `Vararg`
1203+
// unless the upper bound is concrete (diagonal rule)
1204+
int may_substitute_ub(jl_value_t *v, jl_tvar_t *var) JL_NOTSAFEPOINT
1205+
{
1206+
int cov_count = 0;
1207+
return _may_substitute_ub(v, var, 0, &cov_count);
1208+
}
1209+
1210+
jl_value_t *normalize_unionalls(jl_value_t *t)
1211+
{
1212+
JL_GC_PUSH1(&t);
1213+
if (jl_is_uniontype(t)) {
1214+
jl_uniontype_t *u = (jl_uniontype_t*)t;
1215+
jl_value_t *a = NULL;
1216+
jl_value_t *b = NULL;
1217+
JL_GC_PUSH2(&a, &b);
1218+
a = normalize_unionalls(u->a);
1219+
b = normalize_unionalls(u->b);
1220+
if (a != u->a || b != u->b) {
1221+
t = jl_new_struct(jl_uniontype_type, a, b);
1222+
}
1223+
JL_GC_POP();
1224+
}
1225+
else if (jl_is_unionall(t)) {
1226+
jl_unionall_t *u = (jl_unionall_t*)t;
1227+
jl_value_t *body = normalize_unionalls(u->body);
1228+
if (body != u->body) {
1229+
JL_GC_PUSH1(&body);
1230+
t = jl_new_struct(jl_unionall_type, u->var, body);
1231+
JL_GC_POP();
1232+
u = (jl_unionall_t*)t;
1233+
}
1234+
1235+
if (u->var->lb == u->var->ub || may_substitute_ub(body, u->var))
1236+
t = jl_instantiate_unionall(u, u->var->ub);
1237+
}
1238+
JL_GC_POP();
1239+
return t;
1240+
}
1241+
11561242
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);
11571243

11581244
static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **iparams, size_t ntp,
@@ -1162,6 +1248,11 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value
11621248
jl_typename_t *tn = dt->name;
11631249
int istuple = (tn == jl_tuple_typename);
11641250
int isnamedtuple = (tn == jl_namedtuple_typename);
1251+
if (dt->name != jl_type_typename) {
1252+
for (size_t i = 0; i < ntp; i++)
1253+
iparams[i] = normalize_unionalls(iparams[i]);
1254+
}
1255+
11651256
// check type cache
11661257
if (cacheable) {
11671258
size_t i;

test/core.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7560,3 +7560,12 @@ function f18621()
75607560
end
75617561
@test f18621() == 1:5
75627562
@test [_ for _ in 1:5] == 1:5
7563+
7564+
# issue #35130
7565+
const T35130 = Tuple{Vector{Int}, <:Any}
7566+
@eval struct A35130
7567+
x::Vector{Tuple{Vector{Int}, Any}}
7568+
A35130(x) = $(Expr(:new, :A35130, :x))
7569+
end
7570+
h35130(x) = A35130(Any[x][1]::Vector{T35130})
7571+
@test h35130(T35130[([1],1)]) isa A35130

test/subtype.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ function test_diagonal()
140140
@test !issub(Type{Tuple{T,Any} where T}, Type{Tuple{T,T}} where T)
141141
@test !issub(Type{Tuple{T,Any,T} where T}, Type{Tuple{T,T,T}} where T)
142142
@test_broken issub(Type{Tuple{T} where T}, Type{Tuple{T}} where T)
143-
@test_broken issub(Ref{Tuple{T} where T}, Ref{Tuple{T}} where T)
143+
@test issub(Ref{Tuple{T} where T}, Ref{Tuple{T}} where T)
144144
@test !issub(Type{Tuple{T,T} where T}, Type{Tuple{T,T}} where T)
145145
@test !issub(Type{Tuple{T,T,T} where T}, Type{Tuple{T,T,T}} where T)
146146
@test isequal_type(Ref{Tuple{T, T} where Int<:T<:Int},

0 commit comments

Comments
 (0)