mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[NFC] TypeLowering: add CustomDeinit.
Teach SIL type lowering to recursively track custom vs. default deinit status. Determine whether each type recursively only has default deinitialization. This includes any recursive deinitializers that may be invoked by releasing a reference held by this type. If a type only has default deinitialization, then the deinitializer cannot have any semantically-visible side effects. It cannot write to any memory
This commit is contained in:
@@ -118,6 +118,14 @@ public struct Type : TypeProperties, CustomStringConvertible, NoReflectionChildr
|
||||
return CanonicalType(bridged: bridged.getApproximateFormalPackType());
|
||||
}
|
||||
|
||||
/// True if destroying a value of this type might invoke a custom deinitialer
|
||||
/// with side effects. This includes any recursive deinitializers that may be
|
||||
/// invoked by releasing a reference. False if this only has default
|
||||
/// deinitialization.
|
||||
public func mayHaveCustomDeinit(in function: Function) -> Bool {
|
||||
return bridged.mayHaveCustomDeinit(function.bridged)
|
||||
}
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Properties of lowered `SILFunctionType`s
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
@@ -292,6 +292,7 @@ struct BridgedType {
|
||||
BRIDGED_INLINE bool isAddressableForDeps(BridgedFunction f) const;
|
||||
BRIDGED_INLINE SWIFT_IMPORT_UNSAFE BridgedASTType getRawLayoutSubstitutedLikeType() const;
|
||||
BRIDGED_INLINE SWIFT_IMPORT_UNSAFE BridgedASTType getRawLayoutSubstitutedCountType() const;
|
||||
BRIDGED_INLINE bool mayHaveCustomDeinit(BridgedFunction f) const;
|
||||
BRIDGED_INLINE SwiftInt getCaseIdxOfEnumType(BridgedStringRef name) const;
|
||||
static BRIDGED_INLINE SwiftInt getNumBoxFields(BridgedCanType boxTy);
|
||||
static SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedType getBoxFieldType(BridgedCanType boxTy,
|
||||
|
||||
@@ -406,6 +406,10 @@ BridgedASTType BridgedType::getRawLayoutSubstitutedCountType() const {
|
||||
return {unbridged().getRawLayoutSubstitutedCountType().getPointer()};
|
||||
}
|
||||
|
||||
bool BridgedType::mayHaveCustomDeinit(BridgedFunction f) const {
|
||||
return unbridged().mayHaveCustomDeinit(*f.getFunction());
|
||||
}
|
||||
|
||||
SwiftInt BridgedType::getCaseIdxOfEnumType(BridgedStringRef name) const {
|
||||
return unbridged().getCaseIdxOfEnumType(name.unbridged());
|
||||
}
|
||||
|
||||
@@ -962,6 +962,12 @@ public:
|
||||
/// lifetime-dependent value.
|
||||
bool isAddressableForDeps(const SILFunction &function) const;
|
||||
|
||||
/// True if destroying a value of this type might invoke a custom deinitialer
|
||||
/// with side effects. This includes any recursive deinitializers that may be
|
||||
/// invoked by releasing a reference. False if this only has default
|
||||
/// deinitialization.
|
||||
bool mayHaveCustomDeinit(const SILFunction &function) const;
|
||||
|
||||
/// Returns true if this type is an actor type. Returns false if this is any
|
||||
/// other type. This includes distributed actors. To check for distributed
|
||||
/// actors and actors, use isAnyActor().
|
||||
|
||||
@@ -116,6 +116,18 @@ enum HasRawLayout_t : bool {
|
||||
HasRawLayout = true
|
||||
};
|
||||
|
||||
/// Does this type only have default deinitialization or might it invoke a
|
||||
/// custom deinitialer with side effects? This includes any recursive
|
||||
/// deinitializers that may be invoked by releasing a reference.
|
||||
///
|
||||
/// If a type only has default deinitialization, then the deinitializer cannot
|
||||
/// have any semantically-visible side effects. It cannot write to any memory
|
||||
/// reachable from another object that won't be freed during deinitialization.
|
||||
enum MayHaveCustomDeinit_t : bool {
|
||||
HasOnlyDefaultDeinit = false,
|
||||
MayHaveCustomDeinit = true,
|
||||
};
|
||||
|
||||
class SILTypeProperties {
|
||||
// These are chosen so that bitwise-or merges the flags properly.
|
||||
//
|
||||
@@ -132,6 +144,7 @@ class SILTypeProperties {
|
||||
HasPackFlag = 1 << 8,
|
||||
AddressableForDependenciesFlag = 1 << 9,
|
||||
HasRawLayoutFlag = 1 << 10,
|
||||
CustomDeinitFlag = 1 << 11,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -150,7 +163,8 @@ public:
|
||||
HasRawPointer_t hasRawPointer = DoesNotHaveRawPointer,
|
||||
IsLexical_t isLexical = IsNotLexical, HasPack_t hasPack = HasNoPack,
|
||||
IsAddressableForDependencies_t isAFD = IsNotAddressableForDependencies,
|
||||
HasRawLayout_t hasRawLayout = DoesNotHaveRawLayout)
|
||||
HasRawLayout_t hasRawLayout = DoesNotHaveRawLayout,
|
||||
MayHaveCustomDeinit_t customDeinit = HasOnlyDefaultDeinit)
|
||||
: Flags((isTrivial ? 0U : NonTrivialFlag) |
|
||||
(isFixedABI ? 0U : NonFixedABIFlag) |
|
||||
(isAddressOnly ? AddressOnlyFlag : 0U) |
|
||||
@@ -160,7 +174,8 @@ public:
|
||||
(isLexical ? LexicalFlag : 0U) |
|
||||
(hasPack ? HasPackFlag : 0U) |
|
||||
(isAFD ? AddressableForDependenciesFlag : 0U) |
|
||||
(hasRawLayout ? HasRawLayoutFlag : 0U)) {}
|
||||
(hasRawLayout ? HasRawLayoutFlag : 0U) |
|
||||
(customDeinit ? CustomDeinitFlag : 0U)) {}
|
||||
|
||||
constexpr bool operator==(SILTypeProperties p) const {
|
||||
return Flags == p.Flags;
|
||||
@@ -183,13 +198,16 @@ public:
|
||||
|
||||
static constexpr SILTypeProperties forReference() {
|
||||
return {IsNotTrivial, IsFixedABI, IsNotAddressOnly, IsNotResilient,
|
||||
IsNotTypeExpansionSensitive, DoesNotHaveRawPointer, IsLexical};
|
||||
IsNotTypeExpansionSensitive, DoesNotHaveRawPointer, IsLexical,
|
||||
HasNoPack, IsNotAddressableForDependencies, DoesNotHaveRawLayout,
|
||||
MayHaveCustomDeinit};
|
||||
}
|
||||
|
||||
static constexpr SILTypeProperties forOpaque() {
|
||||
return {IsNotTrivial, IsNotFixedABI, IsAddressOnly, IsNotResilient,
|
||||
IsNotTypeExpansionSensitive, HasRawPointer, IsLexical,
|
||||
HasNoPack, IsAddressableForDependencies, HasRawLayout};
|
||||
HasNoPack, IsAddressableForDependencies, HasRawLayout,
|
||||
MayHaveCustomDeinit};
|
||||
}
|
||||
|
||||
static constexpr SILTypeProperties forResilient() {
|
||||
@@ -240,6 +258,9 @@ public:
|
||||
HasRawLayout_t isOrContainsRawLayout() const {
|
||||
return HasRawLayout_t((Flags & HasRawLayoutFlag) != 0);
|
||||
}
|
||||
MayHaveCustomDeinit_t mayHaveCustomDeinit() const {
|
||||
return MayHaveCustomDeinit_t((Flags & CustomDeinitFlag) != 0);
|
||||
}
|
||||
|
||||
void setNonTrivial() { Flags |= NonTrivialFlag; }
|
||||
void setIsOrContainsRawPointer() { Flags |= HasRawPointerFlag; }
|
||||
@@ -262,6 +283,10 @@ public:
|
||||
void setHasRawLayout() {
|
||||
Flags |= HasRawLayoutFlag;
|
||||
}
|
||||
void setCustomDeinit(MayHaveCustomDeinit_t hasCustomDeinit) {
|
||||
Flags = (Flags & ~CustomDeinitFlag)
|
||||
| (hasCustomDeinit ? CustomDeinitFlag : 0);
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace swift
|
||||
|
||||
@@ -198,6 +198,10 @@ public:
|
||||
return Properties.isTypeExpansionSensitive();
|
||||
}
|
||||
|
||||
IsInfiniteType_t isInfinite() const {
|
||||
return Properties.isInfinite();
|
||||
}
|
||||
|
||||
/// Should a value of this type have its lifetime tied to its lexical scope?
|
||||
bool isLexical() const {
|
||||
return Properties.isLexical();
|
||||
@@ -596,6 +600,17 @@ class TypeConverter {
|
||||
return isa<FunctionType>(type) && type->hasTypeParameter();
|
||||
});
|
||||
}
|
||||
|
||||
friend bool operator==(const TypeKey &lhs,
|
||||
const TypeKey &rhs) {
|
||||
return lhs.OrigType == rhs.OrigType && lhs.SubstType == rhs.SubstType
|
||||
&& lhs.expansionContext == rhs.expansionContext
|
||||
&& lhs.IsCacheable == rhs.IsCacheable;
|
||||
}
|
||||
friend bool operator!=(const TypeKey &lhs,
|
||||
const TypeKey &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
friend struct llvm::DenseMapInfo<CachingTypeKey>;
|
||||
@@ -661,6 +676,13 @@ class TypeConverter {
|
||||
llvm::DenseMap<SILDeclRef, TypeExpansionContext>
|
||||
CaptureTypeExpansionContexts;
|
||||
|
||||
/// True while lowering a non-layout type.
|
||||
bool isLoweringNonLayoutType = false;
|
||||
|
||||
/// True if this lowering should be cached.
|
||||
///
|
||||
bool isCacheableLowering(const TypeLowering *tl);
|
||||
|
||||
CanAnyFunctionType makeConstantInterfaceType(SILDeclRef constant);
|
||||
|
||||
// Types converted during foreign bridging.
|
||||
@@ -733,6 +755,28 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// Wraps lowering of any type that does not contribute to the layout of its
|
||||
// parent type. Avoids caching incomplete 'isInfinite' properties for types
|
||||
// with complete layouts.
|
||||
class NonLayoutTypeRAII {
|
||||
TypeConverter &TC;
|
||||
|
||||
public:
|
||||
const bool wasLoweringNonFieldType;
|
||||
|
||||
NonLayoutTypeRAII(TypeConverter &TC)
|
||||
: TC(TC), wasLoweringNonFieldType(TC.isLoweringNonLayoutType)
|
||||
{
|
||||
TC.isLoweringNonLayoutType = true;
|
||||
}
|
||||
NonLayoutTypeRAII(const NonLayoutTypeRAII &) = delete;
|
||||
NonLayoutTypeRAII &operator=(const NonLayoutTypeRAII &) = delete;
|
||||
|
||||
~NonLayoutTypeRAII() {
|
||||
TC.isLoweringNonLayoutType = wasLoweringNonFieldType;
|
||||
}
|
||||
};
|
||||
|
||||
/// Return the CaptureKind to use when capturing a decl.
|
||||
CaptureKind getDeclCaptureKind(CapturedValue capture,
|
||||
TypeExpansionContext expansion);
|
||||
|
||||
@@ -1373,6 +1373,13 @@ SILType::getConcurrencyDiagnosticBehavior(SILFunction *fn) const {
|
||||
return getASTType()->getConcurrencyDiagnosticBehaviorLimit(fromDC);
|
||||
}
|
||||
|
||||
bool SILType::mayHaveCustomDeinit(const SILFunction &function) const {
|
||||
auto contextType =
|
||||
hasTypeParameter() ? function.mapTypeIntoContext(*this) : *this;
|
||||
auto properties = function.getTypeProperties(contextType);
|
||||
return properties.mayHaveCustomDeinit();
|
||||
}
|
||||
|
||||
namespace swift::test {
|
||||
// Arguments:
|
||||
// - SILValue: value
|
||||
|
||||
@@ -258,6 +258,8 @@ namespace {
|
||||
}
|
||||
|
||||
RetTy handleReference(CanType type) {
|
||||
// TODO: Consider final classes with no user deinit to be
|
||||
// HasOnlyDefaultDeinit.
|
||||
return handleReference(type, SILTypeProperties::forReference());
|
||||
}
|
||||
|
||||
@@ -754,6 +756,7 @@ namespace {
|
||||
RetTy
|
||||
visitArchetypeType(CanArchetypeType type, AbstractionPattern origType,
|
||||
IsTypeExpansionSensitive_t isSensitive) {
|
||||
// TODO: Add a HasOnlyDefaultDeinit "layout protocol".
|
||||
auto LayoutInfo = type->getLayoutConstraint();
|
||||
if (LayoutInfo) {
|
||||
if (LayoutInfo->isFixedSizeTrivial()) {
|
||||
@@ -2625,6 +2628,9 @@ namespace {
|
||||
if (origType.isNoncopyable(structType)) {
|
||||
properties.setNonTrivial();
|
||||
properties.setLexical(IsLexical);
|
||||
if (D->getValueTypeDestructor()) {
|
||||
properties.setCustomDeinit(MayHaveCustomDeinit);
|
||||
}
|
||||
if (properties.isAddressOnly())
|
||||
return handleMoveOnlyAddressOnly(structType, properties);
|
||||
return new (TC) MoveOnlyLoadableStructTypeLowering(
|
||||
@@ -2635,6 +2641,13 @@ namespace {
|
||||
if (!origType.isEscapable(structType)) {
|
||||
properties.setNonTrivial();
|
||||
}
|
||||
// Merge the CustomDeinit properties of the type parameters.
|
||||
if (hasConditionalDefaultDeinit(structType, D)) {
|
||||
auto genericProps = classifyTypeParameters(structType, TC, Expansion);
|
||||
if (genericProps.mayHaveCustomDeinit() == HasOnlyDefaultDeinit) {
|
||||
properties.setCustomDeinit(HasOnlyDefaultDeinit);
|
||||
}
|
||||
}
|
||||
return handleAggregateByProperties<LoadableStructTypeLowering>(structType,
|
||||
properties);
|
||||
}
|
||||
@@ -2728,6 +2741,9 @@ namespace {
|
||||
if (origType.isNoncopyable(enumType)) {
|
||||
properties.setNonTrivial();
|
||||
properties.setLexical(IsLexical);
|
||||
if (D->getValueTypeDestructor()) {
|
||||
properties.setCustomDeinit(MayHaveCustomDeinit);
|
||||
}
|
||||
if (properties.isAddressOnly())
|
||||
return handleMoveOnlyAddressOnly(enumType, properties);
|
||||
return new (TC)
|
||||
@@ -2769,6 +2785,43 @@ namespace {
|
||||
}
|
||||
return new (TC) LoadableLoweringClass(type, props, Expansion);
|
||||
}
|
||||
|
||||
private:
|
||||
bool hasConditionalDefaultDeinit(CanType type, StructDecl *structDecl) {
|
||||
if (type->isArray() || type->is_ArrayBuffer()
|
||||
|| type->is_ContiguousArrayBuffer() || type->isDictionary()) {
|
||||
return true;
|
||||
}
|
||||
if (ProtocolDecl *DestructorSafeContainer =
|
||||
TC.Context.getProtocol(KnownProtocolKind::DestructorSafeContainer)) {
|
||||
return bool(lookupConformance(type, DestructorSafeContainer));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Merge the type properties of all the generic parameters. Useful for
|
||||
// summarizing the properties of indirectly stored types, such as Array
|
||||
// elements. Only non-layout flags, such as CustomDeinitFlag are relevant in
|
||||
// the result.
|
||||
//
|
||||
// This may return an incomplete 'isInfinite' lowering on self-recursion. In
|
||||
// this case, the non-layout flags have their default values.
|
||||
SILTypeProperties classifyTypeParameters(CanType type, TypeConverter &tc,
|
||||
TypeExpansionContext expansion) {
|
||||
TypeConverter::NonLayoutTypeRAII nonLayoutLowering(tc);
|
||||
SILTypeProperties props;
|
||||
if (auto bgt = dyn_cast<BoundGenericType>(type)) {
|
||||
for (auto paramType : bgt->getGenericArgs()) {
|
||||
// Use an opaque abstraction pattern for the element type because
|
||||
// abstraction does not apply to the generic parameter itself.
|
||||
AbstractionPattern origElementType = AbstractionPattern::getOpaque();
|
||||
auto &lowering = tc.getTypeLowering(origElementType, paramType,
|
||||
expansion);
|
||||
props.addSubobject(lowering.getRecursiveProperties());
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
@@ -2823,7 +2876,22 @@ void TypeConverter::removeNullEntry(const TypeKey &k) {
|
||||
void TypeConverter::insert(const TypeKey &k, const TypeLowering *tl) {
|
||||
if (!k.isCacheable()) return;
|
||||
|
||||
LoweredTypes[k.getCachingKey()] = tl;
|
||||
if (isCacheableLowering(tl)) {
|
||||
LoweredTypes[k.getCachingKey()] = tl;
|
||||
if (k == k.getKeyForMinimalExpansion())
|
||||
return;
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
removeNullEntry(k.getKeyForMinimalExpansion());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Infinite lowerings are incomplete. They are only cached when they
|
||||
// correspond to a self-recursive layout, which can never have a complete
|
||||
// lowering. Avoid caching an infinite non-layout lowering so that the
|
||||
// complete layout can be lowered later.
|
||||
bool TypeConverter::isCacheableLowering(const TypeLowering *tl) {
|
||||
return !tl || !isLoweringNonLayoutType || !tl->isInfinite();
|
||||
}
|
||||
|
||||
/// Lower each of the elements of the substituted type according to
|
||||
@@ -2981,9 +3049,6 @@ TypeConverter::getTypeLowering(AbstractionPattern origType,
|
||||
insert(key.getKeyForMinimalExpansion(), lowering);
|
||||
} else {
|
||||
insert(key, lowering);
|
||||
#ifndef NDEBUG
|
||||
removeNullEntry(key.getKeyForMinimalExpansion());
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -3168,7 +3233,7 @@ void TypeConverter::verifyLowering(const TypeLowering &lowering,
|
||||
AbstractionPattern origType,
|
||||
CanType substType,
|
||||
TypeExpansionContext forExpansion) {
|
||||
if (TypeLoweringDisableVerification) {
|
||||
if (TypeLoweringDisableVerification || !isCacheableLowering(&lowering)) {
|
||||
return;
|
||||
}
|
||||
verifyLexicalLowering(lowering, origType, substType, forExpansion);
|
||||
@@ -3815,9 +3880,6 @@ const TypeLowering &TypeConverter::getTypeLoweringForLoweredType(
|
||||
insert(key.getKeyForMinimalExpansion(), lowering);
|
||||
else {
|
||||
insert(key, lowering);
|
||||
#ifndef NDEBUG
|
||||
removeNullEntry(key.getKeyForMinimalExpansion());
|
||||
#endif
|
||||
}
|
||||
|
||||
return *lowering;
|
||||
@@ -5499,6 +5561,10 @@ void TypeLowering::print(llvm::raw_ostream &os) const {
|
||||
<< ".\n"
|
||||
<< "isLexical: " << BOOL(Properties.isLexical()) << ".\n"
|
||||
<< "isOrContainsPack: " << BOOL(Properties.isOrContainsPack()) << ".\n"
|
||||
<< "isAddressableForDependencies: "
|
||||
<< BOOL(Properties.isAddressableForDependencies()) << ".\n"
|
||||
<< "hasOnlyDefaultDeinit: "
|
||||
<< BOOL(Properties.mayHaveCustomDeinit() == HasOnlyDefaultDeinit) << ".\n"
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// TODO: This pass is about to be removed because TypeLowering's CustomDeinit
|
||||
// property is superior.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "swift/SILOptimizer/Analysis/DestructorAnalysis.h"
|
||||
#include "swift/SIL/SILInstruction.h"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// RUN: %target-sil-opt -test-runner \
|
||||
// RUN: -enable-experimental-feature LifetimeDependence \
|
||||
// RUN: -enable-experimental-feature Lifetimes \
|
||||
// RUN: %s -o /dev/null 2>&1 | %FileCheck %s
|
||||
|
||||
// REQUIRES: swift_feature_LifetimeDependence
|
||||
// REQUIRES: swift_feature_Lifetimes
|
||||
|
||||
sil_stage raw
|
||||
|
||||
@@ -11,10 +11,33 @@ import Swift
|
||||
|
||||
struct S : ~Copyable {}
|
||||
|
||||
class C {}
|
||||
|
||||
struct SRef : ~Copyable {
|
||||
var c: C
|
||||
}
|
||||
|
||||
struct SDeinit : ~Copyable {
|
||||
static var c: C
|
||||
|
||||
deinit {
|
||||
Self.c = C()
|
||||
}
|
||||
}
|
||||
|
||||
struct SCopyable {}
|
||||
|
||||
struct ArrayWrapperOfSCopyable {
|
||||
let array: [SCopyable]
|
||||
}
|
||||
|
||||
struct ArrayWrapperOfC {
|
||||
let array: [C]
|
||||
}
|
||||
|
||||
// CHECK-LABEL: begin {{.*}} print-type-lowering with: @argument[0]
|
||||
// CHECK: isLexical: true
|
||||
// CHECK: hasOnlyDefaultDeinit: true
|
||||
// CHECK-LABEL: end {{.*}} print-type-lowering with: @argument[0]
|
||||
sil [ossa] @move_only_argument : $@convention(thin) (@owned S) -> () {
|
||||
bb0(%0 : @owned $S):
|
||||
@@ -26,6 +49,7 @@ bb0(%0 : @owned $S):
|
||||
|
||||
// CHECK-LABEL: begin {{.*}} print-type-lowering with: @argument[0]
|
||||
// CHECK: isOrContainsPack: true
|
||||
// CHECK: hasOnlyDefaultDeinit: false
|
||||
// CHECK-LABEL: end {{.*}} print-type-lowering with: @argument[0]
|
||||
sil @pack_argument : $@convention(thin) <each T> (@pack_guaranteed Pack{repeat each T}) -> () {
|
||||
bb0(%0 : $*Pack{repeat each T}):
|
||||
@@ -104,6 +128,7 @@ entry(%instance : $*GE<T>):
|
||||
|
||||
// CHECK-LABEL: begin {{.*}} token: print_ast_type_lowering with: %instance
|
||||
// CHECK: isTrivial: true
|
||||
// CHECK: hasOnlyDefaultDeinit: true
|
||||
// CHECK-LABEL: end {{.*}} token: print_ast_type_lowering with: %instance
|
||||
sil @token : $@convention(thin) (@in Builtin.RawPointer) -> () {
|
||||
entry(%addr : $*Builtin.RawPointer):
|
||||
@@ -147,6 +172,10 @@ extension GENE: Escapable where T: Escapable {}
|
||||
// CHECK-LABEL: begin running test 1 of 1 on gsnc_argument: print-type-lowering with: @argument[0]
|
||||
// CHECK: Type Lowering for lowered type: $*GSNC<T>.
|
||||
// CHECK: isTrivial: false.
|
||||
|
||||
// FIXME: Lower BitwiseCopyable archetypes as trivial!
|
||||
// HECK: hasOnlyDefaultDeinit: true
|
||||
|
||||
// CHECK-LABEL: end running test 1 of 1 on gsnc_argument: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @gsnc_argument : $@convention(thin) <T: BitwiseCopyable> (@in_guaranteed GSNC<T>) -> () {
|
||||
bb0(%0 : $*GSNC<T>):
|
||||
@@ -157,6 +186,10 @@ bb0(%0 : $*GSNC<T>):
|
||||
|
||||
// CHECK-LABEL: begin running test 1 of 1 on gsne_argument: print-type-lowering with: @argument[0]
|
||||
// CHECK: isTrivial: false.
|
||||
|
||||
// FIXME: Lower BitwiseCopyable archetypes as trivial!
|
||||
// HECK: hasOnlyDefaultDeinit: true
|
||||
|
||||
// CHECK-LABEL: end running test 1 of 1 on gsne_argument: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @gsne_argument : $@convention(thin) <T: BitwiseCopyable> (@in_guaranteed GSNE<T>) -> () {
|
||||
bb0(%0 : $*GSNE<T>):
|
||||
@@ -167,6 +200,10 @@ bb0(%0 : $*GSNE<T>):
|
||||
|
||||
// CHECK-LABEL: begin running test 1 of 1 on genc_argument: print-type-lowering with: @argument[0]
|
||||
// CHECK: isTrivial: false.
|
||||
|
||||
// FIXME: Lower BitwiseCopyable archetypes as trivial!
|
||||
// HECK: hasOnlyDefaultDeinit: true
|
||||
|
||||
// CHECK-LABEL: end running test 1 of 1 on genc_argument: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @genc_argument : $@convention(thin) <T: BitwiseCopyable> (@in_guaranteed GENC<T>) -> () {
|
||||
bb0(%0 : $*GENC<T>):
|
||||
@@ -177,6 +214,10 @@ bb0(%0 : $*GENC<T>):
|
||||
|
||||
// CHECK-LABEL: begin running test 1 of 1 on gene_argument: print-type-lowering with: @argument[0]
|
||||
// CHECK: isTrivial: false.
|
||||
|
||||
// FIXME: Lower BitwiseCopyable archetypes as trivial!
|
||||
// HECK: hasOnlyDefaultDeinit: true
|
||||
|
||||
// CHECK-LABEL: end running test 1 of 1 on gene_argument: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @gene_argument : $@convention(thin) <T: BitwiseCopyable> (@in_guaranteed GENE<T>) -> () {
|
||||
bb0(%0 : $*GENE<T>):
|
||||
@@ -189,11 +230,18 @@ struct Bitwise<T: BitwiseCopyable>: BitwiseCopyable {
|
||||
var x: T
|
||||
}
|
||||
|
||||
// TODO: This should return 'isTrivial: true'. This is particularly inexcusable because the 'Bitwise' cannot be
|
||||
// nontrivial over any 'T', *and* the declaration itself is declared BitwiseCopyable.
|
||||
// TODO: This should return 'isTrivial: true' and
|
||||
// 'hasOnlyDefaultDeinit: true' . This is particularly inexcusable
|
||||
// because the 'Bitwise' cannot be nontrivial over any 'T', *and* the
|
||||
// declaration itself is declared BitwiseCopyable.
|
||||
//
|
||||
// CHECK-LABEL: begin running test 1 of 1 on bitwise_argument: print-type-lowering with: @argument[0]
|
||||
// CHECK: isTrivial: false.
|
||||
|
||||
|
||||
// FIXME: Lower BitwiseCopyable archetypes as trivial!
|
||||
// HECK: hasOnlyDefaultDeinit: true
|
||||
|
||||
// CHECK-LABEL: end running test 1 of 1 on bitwise_argument: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @bitwise_argument : $@convention(thin) <T: BitwiseCopyable> (@in_guaranteed Bitwise<T>) -> () {
|
||||
bb0(%0 : $*Bitwise<T>):
|
||||
@@ -204,6 +252,7 @@ bb0(%0 : $*Bitwise<T>):
|
||||
|
||||
// CHECK-LABEL: begin running test 1 of 1 on gsnc_specialized_argument: print-type-lowering with: @argument[0]
|
||||
// CHECK: isTrivial: true.
|
||||
// CHECK: hasOnlyDefaultDeinit: true
|
||||
// CHECK-LABEL: end running test 1 of 1 on gsnc_specialized_argument: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @gsnc_specialized_argument : $@convention(thin) (@in_guaranteed GSNC<SCopyable>) -> () {
|
||||
bb0(%0 : $*GSNC<SCopyable>):
|
||||
@@ -214,6 +263,7 @@ bb0(%0 : $*GSNC<SCopyable>):
|
||||
|
||||
// CHECK-LABEL: begin running test 1 of 1 on gsne_specialized_argument: print-type-lowering with: @argument[0]
|
||||
// CHECK: isTrivial: true.
|
||||
// CHECK: hasOnlyDefaultDeinit: true
|
||||
// CHECK-LABEL: end running test 1 of 1 on gsne_specialized_argument: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @gsne_specialized_argument : $@convention(thin) (@in_guaranteed GSNE<SCopyable>) -> () {
|
||||
bb0(%0 : $*GSNE<SCopyable>):
|
||||
@@ -224,6 +274,7 @@ bb0(%0 : $*GSNE<SCopyable>):
|
||||
|
||||
// CHECK-LABEL: begin running test 1 of 1 on genc_specialized_argument: print-type-lowering with: @argument[0]
|
||||
// CHECK: isTrivial: true.
|
||||
// CHECK: hasOnlyDefaultDeinit: true
|
||||
// CHECK-LABEL: end running test 1 of 1 on genc_specialized_argument: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @genc_specialized_argument : $@convention(thin) (@in_guaranteed GENC<SCopyable>) -> () {
|
||||
bb0(%0 : $*GENC<SCopyable>):
|
||||
@@ -234,6 +285,7 @@ bb0(%0 : $*GENC<SCopyable>):
|
||||
|
||||
// CHECK-LABEL: begin running test 1 of 1 on gene_specialized_argument: print-type-lowering with: @argument[0]
|
||||
// CHECK: isTrivial: true.
|
||||
// CHECK: hasOnlyDefaultDeinit: true
|
||||
// CHECK-LABEL: end running test 1 of 1 on gene_specialized_argument: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @gene_specialized_argument : $@convention(thin) (@in_guaranteed GENE<SCopyable>) -> () {
|
||||
bb0(%0 : $*GENE<SCopyable>):
|
||||
@@ -250,8 +302,7 @@ extension GSNCInt: Copyable where T: Copyable {}
|
||||
|
||||
struct GSNEInt<T: ~Escapable>: ~Escapable {
|
||||
var x: Int
|
||||
// TODO: dependsOn(immortal)
|
||||
@_unsafeNonescapableResult
|
||||
@_lifetime(immortal)
|
||||
init() { x = 0 }
|
||||
}
|
||||
|
||||
@@ -273,6 +324,7 @@ extension GENEInt: Escapable where T: Escapable {}
|
||||
|
||||
// CHECK-LABEL: begin running test 1 of 1 on gsncint_argument: print-type-lowering with: @argument[0]
|
||||
// CHECK: isTrivial: true.
|
||||
// CHECK: hasOnlyDefaultDeinit: true
|
||||
// CHECK-LABEL: end running test 1 of 1 on gsncint_argument: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @gsncint_argument : $@convention(thin) <T: BitwiseCopyable> (@in_guaranteed GSNCInt<T>) -> () {
|
||||
bb0(%0 : $*GSNCInt<T>):
|
||||
@@ -283,6 +335,7 @@ bb0(%0 : $*GSNCInt<T>):
|
||||
|
||||
// CHECK-LABEL: begin running test 1 of 1 on gsneint_argument: print-type-lowering with: @argument[0]
|
||||
// CHECK: isTrivial: true.
|
||||
// CHECK: hasOnlyDefaultDeinit: true
|
||||
// CHECK-LABEL: end running test 1 of 1 on gsneint_argument: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @gsneint_argument : $@convention(thin) <T: BitwiseCopyable> (@in_guaranteed GSNEInt<T>) -> () {
|
||||
bb0(%0 : $*GSNEInt<T>):
|
||||
@@ -293,6 +346,7 @@ bb0(%0 : $*GSNEInt<T>):
|
||||
|
||||
// CHECK-LABEL: begin running test 1 of 1 on gencint_argument: print-type-lowering with: @argument[0]
|
||||
// CHECK: isTrivial: true.
|
||||
// CHECK: hasOnlyDefaultDeinit: true
|
||||
// CHECK-LABEL: end running test 1 of 1 on gencint_argument: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @gencint_argument : $@convention(thin) <T: BitwiseCopyable> (@in_guaranteed GENCInt<T>) -> () {
|
||||
bb0(%0 : $*GENCInt<T>):
|
||||
@@ -303,6 +357,7 @@ bb0(%0 : $*GENCInt<T>):
|
||||
|
||||
// CHECK-LABEL: begin running test 1 of 1 on geneint_argument: print-type-lowering with: @argument[0]
|
||||
// CHECK: isTrivial: true.
|
||||
// CHECK: hasOnlyDefaultDeinit: true
|
||||
// CHECK-LABEL: end running test 1 of 1 on geneint_argument: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @geneint_argument : $@convention(thin) <T: BitwiseCopyable> (@in_guaranteed GENEInt<T>) -> () {
|
||||
bb0(%0 : $*GENEInt<T>):
|
||||
@@ -310,3 +365,88 @@ bb0(%0 : $*GENEInt<T>):
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// CustomDeinit
|
||||
// Generic struct and generic enum with BitwiseCopyable members are checked above.
|
||||
// =============================================================================
|
||||
|
||||
// CHECK-LABEL: begin running test 1 of 1 on noncopyable_argument: print-type-lowering with: @argument[0]
|
||||
// CHECK: hasOnlyDefaultDeinit: true
|
||||
// CHECK-LABEL: end running test 1 of 1 on noncopyable_argument: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @noncopyable_argument : $@convention(thin) (@guaranteed S) -> () {
|
||||
bb0(%0 : @guaranteed $S):
|
||||
specify_test "print-type-lowering @argument[0]"
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: begin running test 1 of 1 on ref_argument: print-type-lowering with: @argument[0]
|
||||
// CHECK: hasOnlyDefaultDeinit: false
|
||||
// CHECK-LABEL: end running test 1 of 1 on ref_argument: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @ref_argument : $@convention(thin) (@guaranteed C) -> () {
|
||||
bb0(%0 : @guaranteed $C):
|
||||
specify_test "print-type-lowering @argument[0]"
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: begin running test 1 of 1 on sref_argument: print-type-lowering with: @argument[0]
|
||||
// CHECK: hasOnlyDefaultDeinit: false
|
||||
// CHECK-LABEL: end running test 1 of 1 on sref_argument: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @sref_argument : $@convention(thin) (@guaranteed SRef) -> () {
|
||||
bb0(%0 : @guaranteed $SRef):
|
||||
specify_test "print-type-lowering @argument[0]"
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: begin running test 1 of 1 on noncopyable_deinit: print-type-lowering with: @argument[0]
|
||||
// CHECK: hasOnlyDefaultDeinit: false
|
||||
// CHECK-LABEL: end running test 1 of 1 on noncopyable_deinit: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @noncopyable_deinit : $@convention(thin) (@guaranteed SDeinit) -> () {
|
||||
bb0(%0 : @guaranteed $SDeinit):
|
||||
specify_test "print-type-lowering @argument[0]"
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: begin running test 1 of 1 on array_wrapper_scopyable: print-type-lowering with: @argument[0]
|
||||
// CHECK: hasOnlyDefaultDeinit: true
|
||||
// CHECK-LABEL: end running test 1 of 1 on array_wrapper_scopyable: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @array_wrapper_scopyable : $@convention(thin) (@guaranteed ArrayWrapperOfSCopyable) -> () {
|
||||
bb0(%0 : @guaranteed $ArrayWrapperOfSCopyable):
|
||||
specify_test "print-type-lowering @argument[0]"
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: begin running test 1 of 1 on array_wrapper_c: print-type-lowering with: @argument[0]
|
||||
// CHECK: hasOnlyDefaultDeinit: false
|
||||
// CHECK-LABEL: end running test 1 of 1 on array_wrapper_c: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @array_wrapper_c : $@convention(thin) (@guaranteed ArrayWrapperOfC) -> () {
|
||||
bb0(%0 : @guaranteed $ArrayWrapperOfC):
|
||||
specify_test "print-type-lowering @argument[0]"
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: begin running test 1 of 1 on rawspan_argument: print-type-lowering with: @argument[0]
|
||||
// CHECK: hasOnlyDefaultDeinit: true
|
||||
// CHECK-LABEL: end running test 1 of 1 on rawspan_argument: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @rawspan_argument : $@convention(thin) (@guaranteed RawSpan) -> () {
|
||||
bb0(%0 : @guaranteed $RawSpan):
|
||||
specify_test "print-type-lowering @argument[0]"
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
// CHECK-LABEL: begin running test 1 of 1 on span_argument: print-type-lowering with: @argument[0]
|
||||
// CHECK: hasOnlyDefaultDeinit: true
|
||||
// CHECK-LABEL: end running test 1 of 1 on span_argument: print-type-lowering with: @argument[0]
|
||||
sil [ossa] @span_argument : $@convention(thin) <T>(@guaranteed Span<T>) -> () {
|
||||
bb0(%0 : @guaranteed $Span<T>):
|
||||
specify_test "print-type-lowering @argument[0]"
|
||||
%retval = tuple ()
|
||||
return %retval : $()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// RUN: %target-swift-frontend -emit-sil -O %S/Inputs/type_lowering_resilience_other.swift -primary-file %s -enable-library-evolution
|
||||
// RUN: %target-swift-frontend -emit-sil -sil-verify-all -O %S/Inputs/type_lowering_resilience_other.swift -primary-file %s -enable-library-evolution
|
||||
|
||||
@inline(__always) public func generic<T>(_ t: T) {
|
||||
_ = Holder<T>(t)
|
||||
@@ -7,3 +7,59 @@
|
||||
public func concrete() {
|
||||
generic(Inner())
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Test self-recursion in TypeConverter::getTypeLowering.
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Lowering: Stuff -> Array -> Stuff
|
||||
//
|
||||
// The toArray() implementation forces SILGen to lower the type.
|
||||
|
||||
enum Stuff {
|
||||
case array([Stuff])
|
||||
case any(Any)
|
||||
|
||||
func toArray() throws -> [Stuff] {
|
||||
guard case .array(let array) = self else { return [] }
|
||||
return array
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Lowering: StructOfArray -> Array -> Member -> StructOfArray
|
||||
//
|
||||
// Lowering the generic parameter of a bound Array is needed to determine whether the type has a custom deinit. But the
|
||||
// self-recursion does not indicate that StructOfArray requires indirect storage of the members field. And StructOfArray
|
||||
// should still be considered a trivial type.
|
||||
//
|
||||
// The additional Int field and enums types here are all necessary to force SIL verification to break if TypeLowering
|
||||
// incorrectly caches a nontrivial lowering of StructOfArray.
|
||||
|
||||
public struct StructOfArray {
|
||||
public var start: Int
|
||||
public var members: [Member]
|
||||
|
||||
public init(
|
||||
_ start: Int,
|
||||
_ members: [Member],
|
||||
) {
|
||||
self.start = start
|
||||
self.members = members
|
||||
}
|
||||
|
||||
public enum Member {
|
||||
case custom(StructOfArray)
|
||||
}
|
||||
}
|
||||
|
||||
public indirect enum EnumOfStructOfArray {
|
||||
case structOfArray(StructOfArray)
|
||||
|
||||
var _associatedValue: StructOfArray {
|
||||
switch self {
|
||||
case let .structOfArray(v): return v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user