Skip to content

Commit 09c828c

Browse files
authored
Merge pull request #81784 from atrick/typelower-customdeinit
TypeLowering: add Type.hasCustomDeinit
2 parents 276706e + b28cd59 commit 09c828c

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)