Skip to content

Commit b9f3d49

Browse files
martinholtersstaticfloat
authored andcommitted
Normalize (simplify) UnionAlls when used as type parameter (#36211)
(cherry picked from commit fedefe9)
1 parent 0581ce0 commit b9f3d49

File tree

3 files changed

+102
-1
lines changed

3 files changed

+102
-1
lines changed

src/jltypes.c

+92
Original file line numberDiff line numberDiff line change
@@ -1120,6 +1120,93 @@ static jl_value_t *normalize_vararg(jl_value_t *va)
11201120
return va;
11211121
}
11221122

1123+
int _may_substitute_ub(jl_value_t *v, jl_tvar_t *var, int inside_inv, int *cov_count) JL_NOTSAFEPOINT
1124+
{
1125+
if (v == (jl_value_t*)var) {
1126+
if (inside_inv) {
1127+
return 0;
1128+
}
1129+
else {
1130+
(*cov_count)++;
1131+
return *cov_count <= 1 || jl_is_concrete_type(var->ub);
1132+
}
1133+
}
1134+
else if (jl_is_uniontype(v)) {
1135+
return _may_substitute_ub(((jl_uniontype_t*)v)->a, var, inside_inv, cov_count) &&
1136+
_may_substitute_ub(((jl_uniontype_t*)v)->b, var, inside_inv, cov_count);
1137+
}
1138+
else if (jl_is_unionall(v)) {
1139+
jl_unionall_t *ua = (jl_unionall_t*)v;
1140+
if (ua->var == var)
1141+
return 1;
1142+
return _may_substitute_ub(ua->var->lb, var, inside_inv, cov_count) &&
1143+
_may_substitute_ub(ua->var->ub, var, inside_inv, cov_count) &&
1144+
_may_substitute_ub(ua->body, var, inside_inv, cov_count);
1145+
}
1146+
else if (jl_is_vararg_type(v)) {
1147+
int old_count = *cov_count;
1148+
jl_value_t *vaT = jl_tparam0(v);
1149+
jl_value_t *vaN = jl_tparam1(v);
1150+
if (!_may_substitute_ub(vaT, var, inside_inv, cov_count))
1151+
return 0;
1152+
if (*cov_count > old_count && !jl_is_concrete_type(var->ub))
1153+
return 0;
1154+
if (!_may_substitute_ub(vaN, var, 1, cov_count))
1155+
return 0;
1156+
}
1157+
else if (jl_is_datatype(v)) {
1158+
int invar = inside_inv || !jl_is_tuple_type(v);
1159+
for (size_t i = 0; i < jl_nparams(v); i++) {
1160+
if (!_may_substitute_ub(jl_tparam(v,i), var, invar, cov_count))
1161+
return 0;
1162+
}
1163+
}
1164+
return 1;
1165+
}
1166+
1167+
// Check whether `var` may be replaced with its upper bound `ub` in `v where var<:ub`
1168+
// Conditions:
1169+
// * `var` does not appear in invariant position
1170+
// * `var` appears at most once (in covariant position) and not in a `Vararg`
1171+
// unless the upper bound is concrete (diagonal rule)
1172+
int may_substitute_ub(jl_value_t *v, jl_tvar_t *var) JL_NOTSAFEPOINT
1173+
{
1174+
int cov_count = 0;
1175+
return _may_substitute_ub(v, var, 0, &cov_count);
1176+
}
1177+
1178+
jl_value_t *normalize_unionalls(jl_value_t *t)
1179+
{
1180+
JL_GC_PUSH1(&t);
1181+
if (jl_is_uniontype(t)) {
1182+
jl_uniontype_t *u = (jl_uniontype_t*)t;
1183+
jl_value_t *a = NULL;
1184+
jl_value_t *b = NULL;
1185+
JL_GC_PUSH2(&a, &b);
1186+
a = normalize_unionalls(u->a);
1187+
b = normalize_unionalls(u->b);
1188+
if (a != u->a || b != u->b) {
1189+
t = jl_new_struct(jl_uniontype_type, a, b);
1190+
}
1191+
JL_GC_POP();
1192+
}
1193+
else if (jl_is_unionall(t)) {
1194+
jl_unionall_t *u = (jl_unionall_t*)t;
1195+
jl_value_t *body = normalize_unionalls(u->body);
1196+
if (body != u->body) {
1197+
JL_GC_PUSH1(&body);
1198+
t = jl_new_struct(jl_unionall_type, u->var, body);
1199+
JL_GC_POP();
1200+
u = (jl_unionall_t*)t;
1201+
}
1202+
1203+
if (u->var->lb == u->var->ub || may_substitute_ub(body, u->var))
1204+
t = jl_instantiate_unionall(u, u->var->ub);
1205+
}
1206+
JL_GC_POP();
1207+
return t;
1208+
}
1209+
11231210
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);
11241211

11251212
static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **iparams, size_t ntp,
@@ -1129,6 +1216,11 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value
11291216
jl_typename_t *tn = dt->name;
11301217
int istuple = (tn == jl_tuple_typename);
11311218
int isnamedtuple = (tn == jl_namedtuple_typename);
1219+
if (dt->name != jl_type_typename) {
1220+
for (size_t i = 0; i < ntp; i++)
1221+
iparams[i] = normalize_unionalls(iparams[i]);
1222+
}
1223+
11321224
// check type cache
11331225
if (cacheable) {
11341226
size_t i;

test/core.jl

+9
Original file line numberDiff line numberDiff line change
@@ -7540,3 +7540,12 @@ for _ in 1:5
75407540
@test all(x->ismissing(x.i), a)
75417541
end
75427542
end
7543+
7544+
# issue #35130
7545+
const T35130 = Tuple{Vector{Int}, <:Any}
7546+
@eval struct A35130
7547+
x::Vector{Tuple{Vector{Int}, Any}}
7548+
A35130(x) = $(Expr(:new, :A35130, :x))
7549+
end
7550+
h35130(x) = A35130(Any[x][1]::Vector{T35130})
7551+
@test h35130(T35130[([1],1)]) isa A35130

test/subtype.jl

+1-1
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)