Skip to content

Commit 918b849

Browse files
committed
recursive types: fix implementation of references_name
To do the old version correctly, we would need to carefully track whether we end up using that parameter as a parameter of any apply_type call in the fieldtype computation of any of the fields, recursively. Since any of those might cause recursion in the constructor graph, resulting in trying to layout some concrete type before we have finished computing the rest of the fieldtypes. That seems unnecessarily difficult to do well, so instead we go back to just doing the trivial cases.
1 parent 30a73de commit 918b849

File tree

2 files changed

+59
-24
lines changed

2 files changed

+59
-24
lines changed

src/builtins.c

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1695,35 +1695,36 @@ static int equiv_field_types(jl_value_t *old, jl_value_t *ft)
16951695
// syntax-enforced restrictions) is via being passed as a type parameter. Thus
16961696
// we can conservatively check this by examining only the parameters of the
16971697
// dependent types.
1698-
// affects_layout is a hack introduced by #35275 to workaround a problem
1699-
// introduced by #34223: it checks whether we will potentially need to
1700-
// compute the layout of the object before we have fully computed the types of
1701-
// the fields during recursion over the allocation of the parameters for the
1702-
// field types (of the concrete subtypes)
1703-
static int references_name(jl_value_t *p, jl_typename_t *name, int affects_layout) JL_NOTSAFEPOINT
1704-
{
1705-
if (jl_is_uniontype(p))
1706-
return references_name(((jl_uniontype_t*)p)->a, name, affects_layout) ||
1707-
references_name(((jl_uniontype_t*)p)->b, name, affects_layout);
1708-
if (jl_is_unionall(p))
1709-
return references_name((jl_value_t*)((jl_unionall_t*)p)->var->lb, name, 0) ||
1710-
references_name((jl_value_t*)((jl_unionall_t*)p)->var->ub, name, 0) ||
1711-
references_name(((jl_unionall_t*)p)->body, name, affects_layout);
1698+
// affects_layout is a (conservative) analysis of layout_uses_free_typevars
1699+
// freevars is a (conservative) analysis of what calling jl_has_bound_typevars from name->wrapper gives (TODO: just call this instead?)
1700+
static int references_name(jl_value_t *p, jl_typename_t *name, int affects_layout, int freevars) JL_NOTSAFEPOINT
1701+
{
1702+
if (freevars && !jl_has_free_typevars(p))
1703+
freevars = 0;
1704+
if (!affects_layout && !freevars)
1705+
return 0;
1706+
while (jl_is_unionall(p)) {
1707+
if (references_name((jl_value_t*)((jl_unionall_t*)p)->var->lb, name, 0, freevars) ||
1708+
references_name((jl_value_t*)((jl_unionall_t*)p)->var->ub, name, 0, freevars))
1709+
return 1;
1710+
p = ((jl_unionall_t*)p)->body;
1711+
}
1712+
if (jl_is_uniontype(p)) {
1713+
return references_name(((jl_uniontype_t*)p)->a, name, affects_layout, freevars) ||
1714+
references_name(((jl_uniontype_t*)p)->b, name, affects_layout, freevars);
1715+
}
17121716
if (jl_is_typevar(p))
17131717
return 0; // already checked by unionall, if applicable
17141718
if (jl_is_datatype(p)) {
17151719
jl_datatype_t *dp = (jl_datatype_t*)p;
1716-
if (affects_layout && dp->name == name)
1720+
if (dp->name == name)
17171721
return 1;
1718-
// affects_layout checks whether we will need to attempt to layout this
1719-
// type (based on whether all copies of it have the same layout) in
1720-
// that case, we still need to check the recursive parameters for
1721-
// layout recursion happening also, but we know it won't itself cause
1722-
// problems for the layout computation
1723-
affects_layout = ((jl_datatype_t*)jl_unwrap_unionall(dp->name->wrapper))->layout == NULL;
1722+
if (!freevars)
1723+
return 0;
1724+
affects_layout = jl_is_namedtuple_type(dp) || jl_field_names(dp) != jl_emptysvec || ((jl_datatype_t*)jl_unwrap_unionall(dp->name->wrapper))->layout == NULL;
17241725
size_t i, l = jl_nparams(p);
17251726
for (i = 0; i < l; i++) {
1726-
if (references_name(jl_tparam(p, i), name, affects_layout))
1727+
if (references_name(jl_tparam(p, i), name, 0, freevars))
17271728
return 1;
17281729
}
17291730
}
@@ -1759,12 +1760,12 @@ JL_CALLABLE(jl_f__typebody)
17591760
// able to compute the layout of the object before needing to
17601761
// publish it, so we must assume it cannot be inlined, if that
17611762
// check passes, then we also still need to check the fields too.
1762-
if (!dt->name->mutabl && (nf == 0 || !references_name((jl_value_t*)dt->super, dt->name, 1))) {
1763+
if (!dt->name->mutabl && (nf == 0 || !references_name((jl_value_t*)dt->super, dt->name, 0, 1))) {
17631764
int mayinlinealloc = 1;
17641765
size_t i;
17651766
for (i = 0; i < nf; i++) {
17661767
jl_value_t *fld = jl_svecref(ft, i);
1767-
if (references_name(fld, dt->name, 1)) {
1768+
if (references_name(fld, dt->name, 1, 1)) {
17681769
mayinlinealloc = 0;
17691770
break;
17701771
}

test/core.jl

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,18 @@ mutable struct FooFoo{A,B} y::FooFoo{A} end
414414

415415
@test FooFoo{Int} <: FooFoo{Int,AbstractString}.types[1]
416416

417+
# make sure this self-referential struct doesn't crash type layout
418+
struct SelfTyA{V}
419+
a::Base.RefValue{V}
420+
end
421+
struct SelfTyB{T}
422+
a::T
423+
b::SelfTyA{SelfTyB{T}}
424+
end
425+
let T = Base.RefValue{SelfTyB{Int}}
426+
@test sizeof(T) === sizeof(Int)
427+
@test sizeof(T.types[1]) === 2 * sizeof(Int)
428+
end
417429

418430
let x = (2,3)
419431
@test +(x...) == 5
@@ -4110,7 +4122,29 @@ end
41104122
let z1 = Z14477()
41114123
@test isa(z1, Z14477)
41124124
@test isa(z1.fld, Z14477)
4125+
@test isdefined(z1, :fld)
4126+
@test !isdefined(z1.fld, :fld)
4127+
end
4128+
struct Z14477B
4129+
fld::Union{Nothing,Z14477B}
4130+
Z14477B() = new(new(nothing))
41134131
end
4132+
let z1 = Z14477B()
4133+
@test isa(z1, Z14477B)
4134+
@test isa(z1.fld, Z14477B)
4135+
@test isa(z1.fld.fld, Nothing)
4136+
end
4137+
struct Z14477C{T}
4138+
fld::Z14477C{Int8}
4139+
Z14477C() = new{Int16}(new{Int8}())
4140+
end
4141+
let z1 = Z14477C()
4142+
@test isa(z1, Z14477C)
4143+
@test isa(z1.fld, Z14477C)
4144+
@test isdefined(z1, :fld)
4145+
@test !isdefined(z1.fld, :fld)
4146+
end
4147+
41144148

41154149
# issue #8846, generic macros
41164150
macro m8846(a, b=0)

0 commit comments

Comments
 (0)