Skip to content

Commit b28cd59

Browse files
committed
[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
1 parent d5b3079 commit b28cd59

File tree

11 files changed

+380
-19
lines changed

11 files changed

+380
-19
lines changed

SwiftCompilerSources/Sources/SIL/Type.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,14 @@ public struct Type : TypeProperties, CustomStringConvertible, NoReflectionChildr
118118
return CanonicalType(bridged: bridged.getApproximateFormalPackType());
119119
}
120120

121+
/// True if destroying a value of this type might invoke a custom deinitialer
122+
/// with side effects. This includes any recursive deinitializers that may be
123+
/// invoked by releasing a reference. False if this only has default
124+
/// deinitialization.
125+
public func mayHaveCustomDeinit(in function: Function) -> Bool {
126+
return bridged.mayHaveCustomDeinit(function.bridged)
127+
}
128+
121129
//===--------------------------------------------------------------------===//
122130
// Properties of lowered `SILFunctionType`s
123131
//===--------------------------------------------------------------------===//

include/swift/SIL/SILBridging.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ struct BridgedType {
292292
BRIDGED_INLINE bool isAddressableForDeps(BridgedFunction f) const;
293293
BRIDGED_INLINE SWIFT_IMPORT_UNSAFE BridgedASTType getRawLayoutSubstitutedLikeType() const;
294294
BRIDGED_INLINE SWIFT_IMPORT_UNSAFE BridgedASTType getRawLayoutSubstitutedCountType() const;
295+
BRIDGED_INLINE bool mayHaveCustomDeinit(BridgedFunction f) const;
295296
BRIDGED_INLINE SwiftInt getCaseIdxOfEnumType(BridgedStringRef name) const;
296297
static BRIDGED_INLINE SwiftInt getNumBoxFields(BridgedCanType boxTy);
297298
static SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedType getBoxFieldType(BridgedCanType boxTy,

include/swift/SIL/SILBridgingImpl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,10 @@ BridgedASTType BridgedType::getRawLayoutSubstitutedCountType() const {
406406
return {unbridged().getRawLayoutSubstitutedCountType().getPointer()};
407407
}
408408

409+
bool BridgedType::mayHaveCustomDeinit(BridgedFunction f) const {
410+
return unbridged().mayHaveCustomDeinit(*f.getFunction());
411+
}
412+
409413
SwiftInt BridgedType::getCaseIdxOfEnumType(BridgedStringRef name) const {
410414
return unbridged().getCaseIdxOfEnumType(name.unbridged());
411415
}

include/swift/SIL/SILType.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,12 @@ class SILType {
962962
/// lifetime-dependent value.
963963
bool isAddressableForDeps(const SILFunction &function) const;
964964

965+
/// True if destroying a value of this type might invoke a custom deinitialer
966+
/// with side effects. This includes any recursive deinitializers that may be
967+
/// invoked by releasing a reference. False if this only has default
968+
/// deinitialization.
969+
bool mayHaveCustomDeinit(const SILFunction &function) const;
970+
965971
/// Returns true if this type is an actor type. Returns false if this is any
966972
/// other type. This includes distributed actors. To check for distributed
967973
/// actors and actors, use isAnyActor().

include/swift/SIL/SILTypeProperties.h

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,18 @@ enum HasRawLayout_t : bool {
116116
HasRawLayout = true
117117
};
118118

119+
/// Does this type only have default deinitialization or might it invoke a
120+
/// custom deinitialer with side effects? This includes any recursive
121+
/// deinitializers that may be invoked by releasing a reference.
122+
///
123+
/// If a type only has default deinitialization, then the deinitializer cannot
124+
/// have any semantically-visible side effects. It cannot write to any memory
125+
/// reachable from another object that won't be freed during deinitialization.
126+
enum MayHaveCustomDeinit_t : bool {
127+
HasOnlyDefaultDeinit = false,
128+
MayHaveCustomDeinit = true,
129+
};
130+
119131
class SILTypeProperties {
120132
// These are chosen so that bitwise-or merges the flags properly.
121133
//
@@ -132,6 +144,7 @@ class SILTypeProperties {
132144
HasPackFlag = 1 << 8,
133145
AddressableForDependenciesFlag = 1 << 9,
134146
HasRawLayoutFlag = 1 << 10,
147+
CustomDeinitFlag = 1 << 11,
135148
};
136149
// clang-format on
137150

@@ -150,7 +163,8 @@ class SILTypeProperties {
150163
HasRawPointer_t hasRawPointer = DoesNotHaveRawPointer,
151164
IsLexical_t isLexical = IsNotLexical, HasPack_t hasPack = HasNoPack,
152165
IsAddressableForDependencies_t isAFD = IsNotAddressableForDependencies,
153-
HasRawLayout_t hasRawLayout = DoesNotHaveRawLayout)
166+
HasRawLayout_t hasRawLayout = DoesNotHaveRawLayout,
167+
MayHaveCustomDeinit_t customDeinit = HasOnlyDefaultDeinit)
154168
: Flags((isTrivial ? 0U : NonTrivialFlag) |
155169
(isFixedABI ? 0U : NonFixedABIFlag) |
156170
(isAddressOnly ? AddressOnlyFlag : 0U) |
@@ -160,7 +174,8 @@ class SILTypeProperties {
160174
(isLexical ? LexicalFlag : 0U) |
161175
(hasPack ? HasPackFlag : 0U) |
162176
(isAFD ? AddressableForDependenciesFlag : 0U) |
163-
(hasRawLayout ? HasRawLayoutFlag : 0U)) {}
177+
(hasRawLayout ? HasRawLayoutFlag : 0U) |
178+
(customDeinit ? CustomDeinitFlag : 0U)) {}
164179

165180
constexpr bool operator==(SILTypeProperties p) const {
166181
return Flags == p.Flags;
@@ -183,13 +198,16 @@ class SILTypeProperties {
183198

184199
static constexpr SILTypeProperties forReference() {
185200
return {IsNotTrivial, IsFixedABI, IsNotAddressOnly, IsNotResilient,
186-
IsNotTypeExpansionSensitive, DoesNotHaveRawPointer, IsLexical};
201+
IsNotTypeExpansionSensitive, DoesNotHaveRawPointer, IsLexical,
202+
HasNoPack, IsNotAddressableForDependencies, DoesNotHaveRawLayout,
203+
MayHaveCustomDeinit};
187204
}
188205

189206
static constexpr SILTypeProperties forOpaque() {
190207
return {IsNotTrivial, IsNotFixedABI, IsAddressOnly, IsNotResilient,
191208
IsNotTypeExpansionSensitive, HasRawPointer, IsLexical,
192-
HasNoPack, IsAddressableForDependencies, HasRawLayout};
209+
HasNoPack, IsAddressableForDependencies, HasRawLayout,
210+
MayHaveCustomDeinit};
193211
}
194212

195213
static constexpr SILTypeProperties forResilient() {
@@ -240,6 +258,9 @@ class SILTypeProperties {
240258
HasRawLayout_t isOrContainsRawLayout() const {
241259
return HasRawLayout_t((Flags & HasRawLayoutFlag) != 0);
242260
}
261+
MayHaveCustomDeinit_t mayHaveCustomDeinit() const {
262+
return MayHaveCustomDeinit_t((Flags & CustomDeinitFlag) != 0);
263+
}
243264

244265
void setNonTrivial() { Flags |= NonTrivialFlag; }
245266
void setIsOrContainsRawPointer() { Flags |= HasRawPointerFlag; }
@@ -262,6 +283,10 @@ class SILTypeProperties {
262283
void setHasRawLayout() {
263284
Flags |= HasRawLayoutFlag;
264285
}
286+
void setCustomDeinit(MayHaveCustomDeinit_t hasCustomDeinit) {
287+
Flags = (Flags & ~CustomDeinitFlag)
288+
| (hasCustomDeinit ? CustomDeinitFlag : 0);
289+
}
265290
};
266291

267292
} // end namespace swift

include/swift/SIL/TypeLowering.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,10 @@ class TypeLowering {
198198
return Properties.isTypeExpansionSensitive();
199199
}
200200

201+
IsInfiniteType_t isInfinite() const {
202+
return Properties.isInfinite();
203+
}
204+
201205
/// Should a value of this type have its lifetime tied to its lexical scope?
202206
bool isLexical() const {
203207
return Properties.isLexical();
@@ -596,6 +600,17 @@ class TypeConverter {
596600
return isa<FunctionType>(type) && type->hasTypeParameter();
597601
});
598602
}
603+
604+
friend bool operator==(const TypeKey &lhs,
605+
const TypeKey &rhs) {
606+
return lhs.OrigType == rhs.OrigType && lhs.SubstType == rhs.SubstType
607+
&& lhs.expansionContext == rhs.expansionContext
608+
&& lhs.IsCacheable == rhs.IsCacheable;
609+
}
610+
friend bool operator!=(const TypeKey &lhs,
611+
const TypeKey &rhs) {
612+
return !(lhs == rhs);
613+
}
599614
};
600615

601616
friend struct llvm::DenseMapInfo<CachingTypeKey>;
@@ -661,6 +676,13 @@ class TypeConverter {
661676
llvm::DenseMap<SILDeclRef, TypeExpansionContext>
662677
CaptureTypeExpansionContexts;
663678

679+
/// True while lowering a non-layout type.
680+
bool isLoweringNonLayoutType = false;
681+
682+
/// True if this lowering should be cached.
683+
///
684+
bool isCacheableLowering(const TypeLowering *tl);
685+
664686
CanAnyFunctionType makeConstantInterfaceType(SILDeclRef constant);
665687

666688
// Types converted during foreign bridging.
@@ -733,6 +755,28 @@ class TypeConverter {
733755
}
734756
};
735757

758+
// Wraps lowering of any type that does not contribute to the layout of its
759+
// parent type. Avoids caching incomplete 'isInfinite' properties for types
760+
// with complete layouts.
761+
class NonLayoutTypeRAII {
762+
TypeConverter &TC;
763+
764+
public:
765+
const bool wasLoweringNonFieldType;
766+
767+
NonLayoutTypeRAII(TypeConverter &TC)
768+
: TC(TC), wasLoweringNonFieldType(TC.isLoweringNonLayoutType)
769+
{
770+
TC.isLoweringNonLayoutType = true;
771+
}
772+
NonLayoutTypeRAII(const NonLayoutTypeRAII &) = delete;
773+
NonLayoutTypeRAII &operator=(const NonLayoutTypeRAII &) = delete;
774+
775+
~NonLayoutTypeRAII() {
776+
TC.isLoweringNonLayoutType = wasLoweringNonFieldType;
777+
}
778+
};
779+
736780
/// Return the CaptureKind to use when capturing a decl.
737781
CaptureKind getDeclCaptureKind(CapturedValue capture,
738782
TypeExpansionContext expansion);

lib/SIL/IR/SILType.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,6 +1373,13 @@ SILType::getConcurrencyDiagnosticBehavior(SILFunction *fn) const {
13731373
return getASTType()->getConcurrencyDiagnosticBehaviorLimit(fromDC);
13741374
}
13751375

1376+
bool SILType::mayHaveCustomDeinit(const SILFunction &function) const {
1377+
auto contextType =
1378+
hasTypeParameter() ? function.mapTypeIntoContext(*this) : *this;
1379+
auto properties = function.getTypeProperties(contextType);
1380+
return properties.mayHaveCustomDeinit();
1381+
}
1382+
13761383
namespace swift::test {
13771384
// Arguments:
13781385
// - SILValue: value

lib/SIL/IR/TypeLowering.cpp

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,8 @@ namespace {
258258
}
259259

260260
RetTy handleReference(CanType type) {
261+
// TODO: Consider final classes with no user deinit to be
262+
// HasOnlyDefaultDeinit.
261263
return handleReference(type, SILTypeProperties::forReference());
262264
}
263265

@@ -754,6 +756,7 @@ namespace {
754756
RetTy
755757
visitArchetypeType(CanArchetypeType type, AbstractionPattern origType,
756758
IsTypeExpansionSensitive_t isSensitive) {
759+
// TODO: Add a HasOnlyDefaultDeinit "layout protocol".
757760
auto LayoutInfo = type->getLayoutConstraint();
758761
if (LayoutInfo) {
759762
if (LayoutInfo->isFixedSizeTrivial()) {
@@ -2625,6 +2628,9 @@ namespace {
26252628
if (origType.isNoncopyable(structType)) {
26262629
properties.setNonTrivial();
26272630
properties.setLexical(IsLexical);
2631+
if (D->getValueTypeDestructor()) {
2632+
properties.setCustomDeinit(MayHaveCustomDeinit);
2633+
}
26282634
if (properties.isAddressOnly())
26292635
return handleMoveOnlyAddressOnly(structType, properties);
26302636
return new (TC) MoveOnlyLoadableStructTypeLowering(
@@ -2635,6 +2641,13 @@ namespace {
26352641
if (!origType.isEscapable(structType)) {
26362642
properties.setNonTrivial();
26372643
}
2644+
// Merge the CustomDeinit properties of the type parameters.
2645+
if (hasConditionalDefaultDeinit(structType, D)) {
2646+
auto genericProps = classifyTypeParameters(structType, TC, Expansion);
2647+
if (genericProps.mayHaveCustomDeinit() == HasOnlyDefaultDeinit) {
2648+
properties.setCustomDeinit(HasOnlyDefaultDeinit);
2649+
}
2650+
}
26382651
return handleAggregateByProperties<LoadableStructTypeLowering>(structType,
26392652
properties);
26402653
}
@@ -2728,6 +2741,9 @@ namespace {
27282741
if (origType.isNoncopyable(enumType)) {
27292742
properties.setNonTrivial();
27302743
properties.setLexical(IsLexical);
2744+
if (D->getValueTypeDestructor()) {
2745+
properties.setCustomDeinit(MayHaveCustomDeinit);
2746+
}
27312747
if (properties.isAddressOnly())
27322748
return handleMoveOnlyAddressOnly(enumType, properties);
27332749
return new (TC)
@@ -2769,6 +2785,43 @@ namespace {
27692785
}
27702786
return new (TC) LoadableLoweringClass(type, props, Expansion);
27712787
}
2788+
2789+
private:
2790+
bool hasConditionalDefaultDeinit(CanType type, StructDecl *structDecl) {
2791+
if (type->isArray() || type->is_ArrayBuffer()
2792+
|| type->is_ContiguousArrayBuffer() || type->isDictionary()) {
2793+
return true;
2794+
}
2795+
if (ProtocolDecl *DestructorSafeContainer =
2796+
TC.Context.getProtocol(KnownProtocolKind::DestructorSafeContainer)) {
2797+
return bool(lookupConformance(type, DestructorSafeContainer));
2798+
}
2799+
return false;
2800+
}
2801+
2802+
// Merge the type properties of all the generic parameters. Useful for
2803+
// summarizing the properties of indirectly stored types, such as Array
2804+
// elements. Only non-layout flags, such as CustomDeinitFlag are relevant in
2805+
// the result.
2806+
//
2807+
// This may return an incomplete 'isInfinite' lowering on self-recursion. In
2808+
// this case, the non-layout flags have their default values.
2809+
SILTypeProperties classifyTypeParameters(CanType type, TypeConverter &tc,
2810+
TypeExpansionContext expansion) {
2811+
TypeConverter::NonLayoutTypeRAII nonLayoutLowering(tc);
2812+
SILTypeProperties props;
2813+
if (auto bgt = dyn_cast<BoundGenericType>(type)) {
2814+
for (auto paramType : bgt->getGenericArgs()) {
2815+
// Use an opaque abstraction pattern for the element type because
2816+
// abstraction does not apply to the generic parameter itself.
2817+
AbstractionPattern origElementType = AbstractionPattern::getOpaque();
2818+
auto &lowering = tc.getTypeLowering(origElementType, paramType,
2819+
expansion);
2820+
props.addSubobject(lowering.getRecursiveProperties());
2821+
}
2822+
}
2823+
return props;
2824+
}
27722825
};
27732826
} // end anonymous namespace
27742827

@@ -2823,7 +2876,22 @@ void TypeConverter::removeNullEntry(const TypeKey &k) {
28232876
void TypeConverter::insert(const TypeKey &k, const TypeLowering *tl) {
28242877
if (!k.isCacheable()) return;
28252878

2826-
LoweredTypes[k.getCachingKey()] = tl;
2879+
if (isCacheableLowering(tl)) {
2880+
LoweredTypes[k.getCachingKey()] = tl;
2881+
if (k == k.getKeyForMinimalExpansion())
2882+
return;
2883+
}
2884+
#ifndef NDEBUG
2885+
removeNullEntry(k.getKeyForMinimalExpansion());
2886+
#endif
2887+
}
2888+
2889+
// Infinite lowerings are incomplete. They are only cached when they
2890+
// correspond to a self-recursive layout, which can never have a complete
2891+
// lowering. Avoid caching an infinite non-layout lowering so that the
2892+
// complete layout can be lowered later.
2893+
bool TypeConverter::isCacheableLowering(const TypeLowering *tl) {
2894+
return !tl || !isLoweringNonLayoutType || !tl->isInfinite();
28272895
}
28282896

28292897
/// Lower each of the elements of the substituted type according to
@@ -2981,9 +3049,6 @@ TypeConverter::getTypeLowering(AbstractionPattern origType,
29813049
insert(key.getKeyForMinimalExpansion(), lowering);
29823050
} else {
29833051
insert(key, lowering);
2984-
#ifndef NDEBUG
2985-
removeNullEntry(key.getKeyForMinimalExpansion());
2986-
#endif
29873052
}
29883053

29893054
#ifndef NDEBUG
@@ -3168,7 +3233,7 @@ void TypeConverter::verifyLowering(const TypeLowering &lowering,
31683233
AbstractionPattern origType,
31693234
CanType substType,
31703235
TypeExpansionContext forExpansion) {
3171-
if (TypeLoweringDisableVerification) {
3236+
if (TypeLoweringDisableVerification || !isCacheableLowering(&lowering)) {
31723237
return;
31733238
}
31743239
verifyLexicalLowering(lowering, origType, substType, forExpansion);
@@ -3815,9 +3880,6 @@ const TypeLowering &TypeConverter::getTypeLoweringForLoweredType(
38153880
insert(key.getKeyForMinimalExpansion(), lowering);
38163881
else {
38173882
insert(key, lowering);
3818-
#ifndef NDEBUG
3819-
removeNullEntry(key.getKeyForMinimalExpansion());
3820-
#endif
38213883
}
38223884

38233885
return *lowering;
@@ -5499,6 +5561,10 @@ void TypeLowering::print(llvm::raw_ostream &os) const {
54995561
<< ".\n"
55005562
<< "isLexical: " << BOOL(Properties.isLexical()) << ".\n"
55015563
<< "isOrContainsPack: " << BOOL(Properties.isOrContainsPack()) << ".\n"
5564+
<< "isAddressableForDependencies: "
5565+
<< BOOL(Properties.isAddressableForDependencies()) << ".\n"
5566+
<< "hasOnlyDefaultDeinit: "
5567+
<< BOOL(Properties.mayHaveCustomDeinit() == HasOnlyDefaultDeinit) << ".\n"
55025568
<< "\n";
55035569
}
55045570

lib/SILOptimizer/Analysis/DestructorAnalysis.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
12+
//
13+
// TODO: This pass is about to be removed because TypeLowering's CustomDeinit
14+
// property is superior.
15+
//===----------------------------------------------------------------------===//
1216

1317
#include "swift/SILOptimizer/Analysis/DestructorAnalysis.h"
1418
#include "swift/SIL/SILInstruction.h"

0 commit comments

Comments
 (0)