Skip to content

SIL: Detect and bail out on infinite aggregates. #41111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions include/swift/SIL/TypeLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,13 @@ enum IsTypeExpansionSensitive_t : bool {
IsTypeExpansionSensitive = true
};

/// Is the type infinitely defined in terms of itself? (Such types can never
/// be concretely instantiated, but may still arise from generic specialization.)
enum IsInfiniteType_t : bool {
IsNotInfiniteType = false,
IsInfiniteType = true,
};

/// Extended type information used by SIL.
class TypeLowering {
public:
Expand All @@ -164,6 +171,7 @@ class TypeLowering {
AddressOnlyFlag = 1 << 2,
ResilientFlag = 1 << 3,
TypeExpansionSensitiveFlag = 1 << 4,
InfiniteFlag = 1 << 5,
};

uint8_t Flags;
Expand Down Expand Up @@ -224,6 +232,9 @@ class TypeLowering {
return IsTypeExpansionSensitive_t(
(Flags & TypeExpansionSensitiveFlag) != 0);
}
IsInfiniteType_t isInfinite() const {
return IsInfiniteType_t((Flags & InfiniteFlag) != 0);
}

void setNonTrivial() { Flags |= NonTrivialFlag; }
void setNonFixedABI() { Flags |= NonFixedABIFlag; }
Expand All @@ -233,6 +244,7 @@ class TypeLowering {
Flags = (Flags & ~TypeExpansionSensitiveFlag) |
(isTypeExpansionSensitive ? TypeExpansionSensitiveFlag : 0);
}
void setInfinite() { Flags |= InfiniteFlag; }
};

private:
Expand Down Expand Up @@ -720,6 +732,9 @@ class TypeConverter {

CanGenericSignature CurGenericSignature;

/// Stack of types currently being lowered as part of an aggregate.
llvm::SetVector<CanType> AggregateFieldsBeingLowered;

/// Mapping for types independent on contextual generic parameters.
llvm::DenseMap<CachingTypeKey, const TypeLowering *> LoweredTypes;

Expand Down Expand Up @@ -767,6 +782,31 @@ class TypeConverter {
CanGenericSignature getCurGenericSignature() const {
return CurGenericSignature;
}

// RAII type used when lowering nominal aggregate types (structs and enums)
// to catch self-recursion. Although Sema tries to catch direct circularity
// in value type definitions, it can't catch circularity that arises from
// generic substitutions. SIL may however be exposed to these circularities
// at any point by substituting bound generic types either during SILGen or
// after passes that perform generic specialization.
class LowerAggregateTypeRAII {
TypeConverter &TC;

public:
// True if the aggregate about to be lowered is already being lowered,
// indicating a circularity.
const bool IsInfinite;

LowerAggregateTypeRAII(TypeConverter &TC, CanType aggregate)
: TC(TC), IsInfinite(!TC.AggregateFieldsBeingLowered.insert(aggregate))
{}

~LowerAggregateTypeRAII() {
if (!IsInfinite) {
TC.AggregateFieldsBeingLowered.pop_back();
}
}
};

class GenericContextRAII {
TypeConverter &TC;
Expand Down
8 changes: 7 additions & 1 deletion lib/IRGen/GenStruct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1290,7 +1290,13 @@ const TypeInfo *TypeConverter::convertStructType(TypeBase *key, CanType type,
// All resilient structs have the same opaque lowering, since they are
// indistinguishable as values --- except that we have to track
// ABI-accessibility.
if (IGM.isResilient(D, ResilienceExpansion::Maximal)) {
//
// Treat infinitely-sized types as resilient as well, since they can never
// be concretized.
if (IGM.isResilient(D, ResilienceExpansion::Maximal)
|| IGM.getSILTypes().getTypeLowering(SILType::getPrimitiveAddressType(type),
TypeExpansionContext::minimal())
.getRecursiveProperties().isInfinite()) {
auto structAccessible =
IsABIAccessible_t(IGM.getSILModule().isTypeMetadataAccessible(type));
return &getResilientStructTypeInfo(structAccessible);
Expand Down
5 changes: 5 additions & 0 deletions lib/SIL/IR/SILType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ bool SILType::isTrivial(const SILFunction &F) const {
}

bool SILType::isEmpty(const SILFunction &F) const {
// Infinite types are never empty.
if (F.getTypeLowering(*this).getRecursiveProperties().isInfinite()) {
return false;
}

if (auto tupleTy = getAs<TupleType>()) {
// A tuple is empty if it either has no elements or if all elements are
// empty.
Expand Down
21 changes: 21 additions & 0 deletions lib/SIL/IR/TypeLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1686,6 +1686,15 @@ namespace {
auto silType = SILType::getPrimitiveObjectType(type);
return new (TC) OpaqueValueTypeLowering(silType, properties, Expansion);
}

TypeLowering *handleInfinite(CanType type,
RecursiveProperties properties) {
// Infinite types cannot actually be instantiated, so treat them as
// opaque for code generation purposes.
properties.setAddressOnly();
properties.setInfinite();
return handleAddressOnly(type, properties);
}

#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
TypeLowering * \
Expand Down Expand Up @@ -1779,6 +1788,12 @@ namespace {

properties = mergeIsTypeExpansionSensitive(isSensitive, properties);

// Bail out if the struct layout relies on itself.
TypeConverter::LowerAggregateTypeRAII loweringStruct(TC, structType);
if (loweringStruct.IsInfinite) {
return handleInfinite(structType, properties);
}

if (handleResilience(structType, D, properties))
return handleAddressOnly(structType, properties);

Expand Down Expand Up @@ -1821,6 +1836,12 @@ namespace {

properties = mergeIsTypeExpansionSensitive(isSensitive, properties);

// Bail out if the enum layout relies on itself.
TypeConverter::LowerAggregateTypeRAII loweringEnum(TC, enumType);
if (loweringEnum.IsInfinite) {
return handleInfinite(enumType, properties);
}

if (handleResilience(enumType, D, properties))
return handleAddressOnly(enumType, properties);

Expand Down
37 changes: 37 additions & 0 deletions test/SILGen/infinite_type_from_generic_substitution.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// RUN: %target-swift-emit-sil -verify %s

protocol P {
associatedtype A
}

struct S<T: P> {
var s: T.A
}

struct SR: P {
typealias A = S<SR>
}

struct SU<T: P> {
var x: S<T>
}

func foo(x: SU<SR>) -> SU<SR> { return x }
func bar(x: S<SR>) -> S<SR> { return x }
func bas(x: S<SR>) -> S<SR> { return x.s }

enum E<T: P> {
case recursive(T.A)
case blah
}

struct ER: P {
typealias A = E<ER>
}

enum EU<T: P> {
case x(E<T>)
}

func zim(x: EU<ER>) -> EU<ER> { return x }
func zang(x: E<ER>) -> E<ER> { return x }