Skip to content

Commit 408d248

Browse files
committed
Allow exact redefinition for types with recursive supertype reference
1 parent f2f188d commit 408d248

File tree

4 files changed

+131
-0
lines changed

4 files changed

+131
-0
lines changed

src/builtins.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2200,6 +2200,9 @@ static int equiv_type(jl_value_t *ta, jl_value_t *tb)
22002200
JL_GC_PUSH2(&a, &b);
22012201
a = jl_rewrap_unionall((jl_value_t*)dta->super, dta->name->wrapper);
22022202
b = jl_rewrap_unionall((jl_value_t*)dtb->super, dtb->name->wrapper);
2203+
// if tb recursively refers to itself in its supertype, assume that it refers to ta
2204+
// before checking whether the supertypes are equal
2205+
b = jl_substitute_datatype(b, dtb, dta);
22032206
if (!jl_types_equal(a, b))
22042207
goto no;
22052208
JL_TRY {

src/jltypes.c

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1607,6 +1607,113 @@ jl_value_t *jl_rewrap_unionall_(jl_value_t *t, jl_value_t *u)
16071607
return t;
16081608
}
16091609

1610+
// Create a copy of type expression t where any occurrence of data type x is replaced by y.
1611+
// If x does not occur in t, return t without any copy.
1612+
// For example, jl_substitute_datatype(Foo{Bar}, Foo{T}, Qux{S}) is Qux{Bar}, with T and S
1613+
// free type variables.
1614+
// To substitute type variables, use jl_substitute_var instead.
1615+
jl_value_t *jl_substitute_datatype(jl_value_t *t, jl_datatype_t * x, jl_datatype_t * y)
1616+
{
1617+
if jl_is_datatype(t) {
1618+
jl_datatype_t *typ = (jl_datatype_t*)t;
1619+
// For datatypes call itself recursively on the parameters to form new parameters.
1620+
// Then, if typename(t) == typename(x), rewrap the wrapper of y around the new
1621+
// parameters. Otherwise, do the same around the wrapper of t.
1622+
// This ensures that the types and supertype are properly set.
1623+
// Start by check whether there is a parameter that needs replacing.
1624+
long i_firstnewparam = -1;
1625+
size_t nparams = jl_svec_len(typ->parameters);
1626+
jl_value_t *firstnewparam = NULL;
1627+
JL_GC_PUSH1(&firstnewparam);
1628+
for (size_t i = 0; i < nparams; i++) {
1629+
jl_value_t *param = NULL;
1630+
JL_GC_PUSH1(&param);
1631+
param = jl_svecref(typ->parameters, i);
1632+
firstnewparam = jl_substitute_datatype(param, x, y);
1633+
if (param != firstnewparam) {
1634+
i_firstnewparam = i;
1635+
JL_GC_POP();
1636+
break;
1637+
}
1638+
JL_GC_POP();
1639+
}
1640+
// If one of the parameters needs to be updated, or if the type name is that to
1641+
// substitute, create a new datataype
1642+
if (i_firstnewparam != -1 || typ->name == x->name) {
1643+
jl_datatype_t *uw = typ->name == x->name ? y : typ; // substitution occurs here
1644+
jl_value_t *wrapper = uw->name->wrapper;
1645+
jl_datatype_t *w = (jl_datatype_t*)jl_unwrap_unionall(wrapper);
1646+
jl_svec_t *sv = jl_alloc_svec_uninit(jl_svec_len(uw->parameters));
1647+
jl_value_t **vals = jl_svec_data(sv);
1648+
// no JL_GC_PUSHARGS(vals, ...) since GC is already aware of sv
1649+
for (long i = 0; i < i_firstnewparam; i++) { // copy the identical parameters
1650+
vals[i] = jl_svecref(typ->parameters, i); // value
1651+
}
1652+
if (i_firstnewparam != -1) { // insert the first non-identical parameter
1653+
vals[i_firstnewparam] = firstnewparam;
1654+
}
1655+
for (size_t i = i_firstnewparam+1; i < nparams; i++) { // insert the remaining parameters
1656+
vals[i] = jl_substitute_datatype(jl_svecref(typ->parameters, i), x, y);
1657+
}
1658+
if (jl_is_tuple_type(wrapper)) {
1659+
// special case for tuples, since the wrapper (Tuple) does not have as
1660+
// many parameters as t (it only has a Vararg instead).
1661+
t = jl_apply_tuple_type(sv, 0);
1662+
} else {
1663+
t = jl_instantiate_type_in_env((jl_value_t*)w, (jl_unionall_t*)wrapper, vals);
1664+
}
1665+
}
1666+
JL_GC_POP();
1667+
}
1668+
else if jl_is_unionall(t) { // recursively call itself on body and var bounds
1669+
jl_unionall_t* ut = (jl_unionall_t*)t;
1670+
jl_value_t *lb = NULL;
1671+
jl_value_t *ub = NULL;
1672+
jl_value_t *body = NULL;
1673+
JL_GC_PUSH3(&lb, &ub, &body);
1674+
lb = jl_substitute_datatype(ut->var->lb, x, y);
1675+
ub = jl_substitute_datatype(ut->var->ub, x, y);
1676+
body = jl_substitute_datatype(ut->body, x, y);
1677+
if (lb != ut->var->lb || ub != ut->var->ub) {
1678+
jl_tvar_t *newtvar = jl_new_typevar(ut->var->name, lb, ub);
1679+
JL_GC_PUSH1(&newtvar);
1680+
t = jl_new_struct(jl_unionall_type, newtvar, body);
1681+
JL_GC_POP();
1682+
}
1683+
else if (body != ut->body) {
1684+
t = jl_new_struct(jl_unionall_type, ut->var, body);
1685+
}
1686+
JL_GC_POP();
1687+
}
1688+
else if jl_is_uniontype(t) { // recursively call itself on a and b
1689+
jl_uniontype_t *u = (jl_uniontype_t*)t;
1690+
jl_value_t *a = NULL;
1691+
jl_value_t *b = NULL;
1692+
JL_GC_PUSH2(&a, &b);
1693+
a = jl_substitute_datatype(u->a, x, y);
1694+
b = jl_substitute_datatype(u->b, x, y);
1695+
if (a != u->a || b != u->b) {
1696+
t = jl_new_struct(jl_uniontype_type, a, b);
1697+
}
1698+
JL_GC_POP();
1699+
}
1700+
else if jl_is_vararg(t) { // recursively call itself on T
1701+
jl_vararg_t *vt = (jl_vararg_t*)t;
1702+
jl_value_t *rT = NULL;
1703+
JL_GC_PUSH1(&rT);
1704+
rT = jl_substitute_datatype(vt->T, x, y);
1705+
if (rT != vt->T) {
1706+
jl_task_t *ct = jl_current_task;
1707+
t = jl_gc_alloc(ct->ptls, sizeof(jl_vararg_t), jl_vararg_type);
1708+
jl_set_typetagof((jl_vararg_t *)t, jl_vararg_tag, 0);
1709+
((jl_vararg_t *)t)->T = rT;
1710+
((jl_vararg_t *)t)->N = vt->N;
1711+
}
1712+
JL_GC_POP();
1713+
}
1714+
return t;
1715+
}
1716+
16101717
static jl_value_t *lookup_type_stack(jl_typestack_t *stack, jl_datatype_t *tt, size_t ntp,
16111718
jl_value_t **iparams)
16121719
{

src/julia_internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,7 @@ jl_unionall_t *jl_rename_unionall(jl_unionall_t *u);
746746
JL_DLLEXPORT jl_value_t *jl_unwrap_unionall(jl_value_t *v JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT;
747747
JL_DLLEXPORT jl_value_t *jl_rewrap_unionall(jl_value_t *t, jl_value_t *u);
748748
JL_DLLEXPORT jl_value_t *jl_rewrap_unionall_(jl_value_t *t, jl_value_t *u);
749+
jl_value_t* jl_substitute_datatype(jl_value_t *t, jl_datatype_t * x, jl_datatype_t * y);
749750
int jl_count_union_components(jl_value_t *v);
750751
JL_DLLEXPORT jl_value_t *jl_nth_union_component(jl_value_t *v JL_PROPAGATES_ROOT, int i) JL_NOTSAFEPOINT;
751752
int jl_find_union_component(jl_value_t *haystack, jl_value_t *needle, unsigned *nth) JL_NOTSAFEPOINT;

test/core.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5610,6 +5610,26 @@ end
56105610
x::Array{T} where T<:Integer
56115611
end
56125612

5613+
# issue #54757, type redefinitions with recursive reference in supertype
5614+
struct T54757{A>:Int,N} <: AbstractArray{Tuple{X,Union{T54757{Union{X,Integer}},T54757{A,N}},Vararg{T54757,N}} where X, N}
5615+
x::A
5616+
y::Union{A,T54757{A,N}}
5617+
z::T54757{A}
5618+
end
5619+
5620+
struct T54757{A>:Int,N} <: AbstractArray{Tuple{X,Union{T54757{Union{X,Integer}},T54757{A,N}},Vararg{T54757,N}} where X, N}
5621+
x::A
5622+
y::Union{A,T54757{A,N}}
5623+
z::T54757{A}
5624+
end
5625+
5626+
@test_throws ErrorException struct T54757{A>:Int,N} <: AbstractArray{Tuple{X,Union{T54757{Union{X,Integer}},T54757{A}},Vararg{T54757,N}} where X, N}
5627+
x::A
5628+
y::Union{A,T54757{A,N}}
5629+
z::T54757{A}
5630+
end
5631+
5632+
56135633
let a = Vector{Core.TypeofBottom}(undef, 2)
56145634
@test a[1] == Union{}
56155635
@test a == [Union{}, Union{}]

0 commit comments

Comments
 (0)