Skip to content

Commit fe19399

Browse files
committed
SIL: Detect and bail out on infinite aggregates.
Sema diagnoses obvious cases of value types defined in terms of themselves, but it can't catch cases that arise as a result of generic substitution, such as if a generic struct has a field of associated type that becomes the same as the struct type itself. SIL may end up handling these types, even though they can't be instantiated (the runtime will complain and crash if the metadata is requested), either during SILGen because they were written in source code, or as a result of generic specialization during optimization. SIL thus can't rely on diagnostics preventing such types from appearing, so it needs to be able to continue gracefully when they show up. Add checks in type lowering for when a struct or enum lowering ends up depending on itself, and generate an infinite type lowering that's opaque and address-only. Fixes rdar://80310017
1 parent e3fece0 commit fe19399

File tree

5 files changed

+110
-1
lines changed

5 files changed

+110
-1
lines changed

include/swift/SIL/TypeLowering.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,13 @@ enum IsTypeExpansionSensitive_t : bool {
153153
IsTypeExpansionSensitive = true
154154
};
155155

156+
/// Is the type infinitely defined in terms of itself? (Such types can never
157+
/// be concretely instantiated, but may still arise from generic specialization.)
158+
enum IsInfiniteType_t : bool {
159+
IsNotInfiniteType = false,
160+
IsInfiniteType = true,
161+
};
162+
156163
/// Extended type information used by SIL.
157164
class TypeLowering {
158165
public:
@@ -164,6 +171,7 @@ class TypeLowering {
164171
AddressOnlyFlag = 1 << 2,
165172
ResilientFlag = 1 << 3,
166173
TypeExpansionSensitiveFlag = 1 << 4,
174+
InfiniteFlag = 1 << 5,
167175
};
168176

169177
uint8_t Flags;
@@ -224,6 +232,9 @@ class TypeLowering {
224232
return IsTypeExpansionSensitive_t(
225233
(Flags & TypeExpansionSensitiveFlag) != 0);
226234
}
235+
IsInfiniteType_t isInfinite() const {
236+
return IsInfiniteType_t((Flags & InfiniteFlag) != 0);
237+
}
227238

228239
void setNonTrivial() { Flags |= NonTrivialFlag; }
229240
void setNonFixedABI() { Flags |= NonFixedABIFlag; }
@@ -233,6 +244,7 @@ class TypeLowering {
233244
Flags = (Flags & ~TypeExpansionSensitiveFlag) |
234245
(isTypeExpansionSensitive ? TypeExpansionSensitiveFlag : 0);
235246
}
247+
void setInfinite() { Flags |= InfiniteFlag; }
236248
};
237249

238250
private:
@@ -720,6 +732,9 @@ class TypeConverter {
720732

721733
CanGenericSignature CurGenericSignature;
722734

735+
/// Stack of types currently being lowered as part of an aggregate.
736+
llvm::SetVector<CanType> AggregateFieldsBeingLowered;
737+
723738
/// Mapping for types independent on contextual generic parameters.
724739
llvm::DenseMap<CachingTypeKey, const TypeLowering *> LoweredTypes;
725740

@@ -767,6 +782,31 @@ class TypeConverter {
767782
CanGenericSignature getCurGenericSignature() const {
768783
return CurGenericSignature;
769784
}
785+
786+
// RAII type used when lowering nominal aggregate types (structs and enums)
787+
// to catch self-recursion. Although Sema tries to catch direct circularity
788+
// in value type definitions, it can't catch circularity that arises from
789+
// generic substitutions. SIL may however be exposed to these circularities
790+
// at any point by substituting bound generic types either during SILGen or
791+
// after passes that perform generic specialization.
792+
class LowerAggregateTypeRAII {
793+
TypeConverter &TC;
794+
795+
public:
796+
// True if the aggregate about to be lowered is already being lowered,
797+
// indicating a circularity.
798+
const bool IsInfinite;
799+
800+
LowerAggregateTypeRAII(TypeConverter &TC, CanType aggregate)
801+
: TC(TC), IsInfinite(!TC.AggregateFieldsBeingLowered.insert(aggregate))
802+
{}
803+
804+
~LowerAggregateTypeRAII() {
805+
if (!IsInfinite) {
806+
TC.AggregateFieldsBeingLowered.pop_back();
807+
}
808+
}
809+
};
770810

771811
class GenericContextRAII {
772812
TypeConverter &TC;

lib/IRGen/GenStruct.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1290,7 +1290,13 @@ const TypeInfo *TypeConverter::convertStructType(TypeBase *key, CanType type,
12901290
// All resilient structs have the same opaque lowering, since they are
12911291
// indistinguishable as values --- except that we have to track
12921292
// ABI-accessibility.
1293-
if (IGM.isResilient(D, ResilienceExpansion::Maximal)) {
1293+
//
1294+
// Treat infinitely-sized types as resilient as well, since they can never
1295+
// be concretized.
1296+
if (IGM.isResilient(D, ResilienceExpansion::Maximal)
1297+
|| IGM.getSILTypes().getTypeLowering(SILType::getPrimitiveAddressType(type),
1298+
TypeExpansionContext::minimal())
1299+
.getRecursiveProperties().isInfinite()) {
12941300
auto structAccessible =
12951301
IsABIAccessible_t(IGM.getSILModule().isTypeMetadataAccessible(type));
12961302
return &getResilientStructTypeInfo(structAccessible);

lib/SIL/IR/SILType.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ bool SILType::isTrivial(const SILFunction &F) const {
109109
}
110110

111111
bool SILType::isEmpty(const SILFunction &F) const {
112+
// Infinite types are never empty.
113+
if (F.getTypeLowering(*this).getRecursiveProperties().isInfinite()) {
114+
return false;
115+
}
116+
112117
if (auto tupleTy = getAs<TupleType>()) {
113118
// A tuple is empty if it either has no elements or if all elements are
114119
// empty.

lib/SIL/IR/TypeLowering.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1686,6 +1686,15 @@ namespace {
16861686
auto silType = SILType::getPrimitiveObjectType(type);
16871687
return new (TC) OpaqueValueTypeLowering(silType, properties, Expansion);
16881688
}
1689+
1690+
TypeLowering *handleInfinite(CanType type,
1691+
RecursiveProperties properties) {
1692+
// Infinite types cannot actually be instantiated, so treat them as
1693+
// opaque for code generation purposes.
1694+
properties.setAddressOnly();
1695+
properties.setInfinite();
1696+
return handleAddressOnly(type, properties);
1697+
}
16891698

16901699
#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
16911700
TypeLowering * \
@@ -1779,6 +1788,12 @@ namespace {
17791788

17801789
properties = mergeIsTypeExpansionSensitive(isSensitive, properties);
17811790

1791+
// Bail out if the struct layout relies on itself.
1792+
TypeConverter::LowerAggregateTypeRAII loweringStruct(TC, structType);
1793+
if (loweringStruct.IsInfinite) {
1794+
return handleInfinite(structType, properties);
1795+
}
1796+
17821797
if (handleResilience(structType, D, properties))
17831798
return handleAddressOnly(structType, properties);
17841799

@@ -1821,6 +1836,12 @@ namespace {
18211836

18221837
properties = mergeIsTypeExpansionSensitive(isSensitive, properties);
18231838

1839+
// Bail out if the enum layout relies on itself.
1840+
TypeConverter::LowerAggregateTypeRAII loweringEnum(TC, enumType);
1841+
if (loweringEnum.IsInfinite) {
1842+
return handleInfinite(enumType, properties);
1843+
}
1844+
18241845
if (handleResilience(enumType, D, properties))
18251846
return handleAddressOnly(enumType, properties);
18261847

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %target-swift-emit-sil -verify %s
2+
3+
protocol P {
4+
associatedtype A
5+
}
6+
7+
struct S<T: P> {
8+
var s: T.A
9+
}
10+
11+
struct SR: P {
12+
typealias A = S<SR>
13+
}
14+
15+
struct SU<T: P> {
16+
var x: S<T>
17+
}
18+
19+
func foo(x: SU<SR>) -> SU<SR> { return x }
20+
func bar(x: S<SR>) -> S<SR> { return x }
21+
func bas(x: S<SR>) -> S<SR> { return x.s }
22+
23+
enum E<T: P> {
24+
case recursive(T.A)
25+
case blah
26+
}
27+
28+
struct ER: P {
29+
typealias A = E<ER>
30+
}
31+
32+
enum EU<T: P> {
33+
case x(E<T>)
34+
}
35+
36+
func zim(x: EU<ER>) -> EU<ER> { return x }
37+
func zang(x: E<ER>) -> E<ER> { return x }

0 commit comments

Comments
 (0)