Files
swift-mirror/lib/SILGen/SILGenDestructor.cpp
Doug Gregor 7789a0ce64 [SE-0371] Back-deploy support for main-actor-isolated deinit
When targeting a platform that predates the introduction of isolated
deinit, make a narrow exception that allows main-actor-isolated deinit
to work through a special, inlineable entrypoint that is
back-deployed. This implementation

  1. Calls into the real implementation when available, otherwise
  2. Checks if we're on the main thread, destroying immediately when
we are, otherwise
  3. Creates a new task on the main actor to handle destruction.

This implementation is less efficient than the implementation in the
runtime, but allows us to back-deploy this functionality as far back
as concurrency goes.

Fixes rdar://151029118.
2025-06-30 17:38:08 -07:00

803 lines
30 KiB
C++

//===--- SILGenDestructor.cpp - SILGen for destructors --------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "ArgumentScope.h"
#include "RValue.h"
#include "SILGenFunction.h"
#include "SILGenFunctionBuilder.h"
#include "SwitchEnumBuilder.h"
#include "swift/AST/ConformanceLookup.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticsSIL.h"
#include "swift/AST/GenericSignature.h"
#include "swift/AST/SubstitutionMap.h"
#include "swift/Basic/Assertions.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILLinkage.h"
#include "swift/SIL/SILMoveOnlyDeinit.h"
#include "swift/SIL/SILValue.h"
#include "swift/SIL/TypeLowering.h"
#include "llvm/ADT/SmallSet.h"
using namespace swift;
using namespace Lowering;
void SILGenFunction::emitDistributedRemoteActorDeinit(
SILValue selfValue, DestructorDecl *dd, bool isIsolated,
llvm::function_ref<void()> emitLocalDeinit) {
RegularLocation loc(dd);
loc.markAutoGenerated();
auto cd = cast<ClassDecl>(dd->getDeclContext()->getSelfNominalTypeDecl());
if (isIsolated || !cd->isDistributedActor()) {
emitLocalDeinit();
B.createReturn(loc, emitEmptyTuple(loc));
return;
}
auto remoteBB = createBasicBlock("remoteActorDeinitBB");
auto finishBB = createBasicBlock("finishDeinitBB");
auto localBB = createBasicBlock("localActorDeinitBB");
auto selfTy = F.mapTypeIntoContext(cd->getDeclaredInterfaceType());
emitDistributedIfRemoteBranch(SILLocation(loc), selfValue, selfTy,
/*if remote=*/remoteBB, /*if local=*/localBB);
// Emit remote BB
{
B.emitBlock(remoteBB);
auto cleanupLoc = CleanupLocation(loc);
auto &C = cd->getASTContext();
{
FullExpr CleanupScope(Cleanups, cleanupLoc);
ManagedValue borrowedSelf = emitManagedBeginBorrow(loc, selfValue);
// Note that we do NOT execute user-declared the deinit body.
// They would be free to access state which does not exist in a remote DA
// we are a remote instance,
// the only properties we can destroy are the id and system properties.
for (VarDecl *vd : cd->getStoredProperties()) {
if (getActorIsolation(vd) == ActorIsolation::ActorInstance)
continue;
// Just to double-check, we only want to destroy `id` and `actorSystem`
if (vd->getBaseIdentifier() == C.Id_id ||
vd->getBaseIdentifier() == C.Id_actorSystem) {
destroyClassMember(cleanupLoc, borrowedSelf, vd);
}
}
if (cd->isRootDefaultActor()) {
emitDestroyDefaultActor(cleanupLoc, borrowedSelf.getValue());
}
}
B.createDeallocRef(loc, selfValue);
B.createBranch(loc, finishBB);
}
// Emit local BB
{
B.emitBlock(localBB);
emitLocalDeinit();
B.createBranch(loc, finishBB);
}
// Emit finish BB
B.emitBlock(finishBB);
// Return.
B.createReturn(loc, emitEmptyTuple(loc));
}
void SILGenFunction::emitDestroyingDestructor(DestructorDecl *dd) {
MagicFunctionName = DeclName(SGM.M.getASTContext().getIdentifier("deinit"));
RegularLocation Loc(dd);
if (dd->isImplicit())
Loc.markAutoGenerated();
if (dd->requiresUnavailableDeclABICompatibilityStubs())
emitApplyOfUnavailableCodeReached();
auto cd = cast<ClassDecl>(dd->getDeclContext());
SILValue selfValue = emitSelfDeclForDestructor(dd->getImplicitSelfDecl());
ManagedValue managedSelf;
if (selfValue->getOwnershipKind() == OwnershipKind::Unowned) {
managedSelf = ManagedValue::forUnownedObjectValue(selfValue);
} else {
managedSelf = ManagedValue::forBorrowedRValue(selfValue);
}
auto ai = swift::getActorIsolation(dd);
auto actor = emitExecutor(Loc, ai, managedSelf);
if (actor) {
ExpectedExecutor.set(*actor);
} else {
ExpectedExecutor.setUnnecessary();
}
// Jump to the expected executor.
if (actor) {
// For a synchronous function, check that we're on the same executor.
// Note: if we "know" that the code is completely Sendable-safe, this
// is unnecessary. The type checker will need to make this determination.
emitPreconditionCheckExpectedExecutor(Loc, *actor);
}
// Create a basic block to jump to for the implicit destruction behavior
// of releasing the elements and calling the superclass destructor.
// We won't actually emit the block until we finish with the destructor body.
prepareEpilog(dd, std::nullopt, std::nullopt, CleanupLocation(Loc));
// Emit the destructor body.
emitProfilerIncrement(dd->getTypecheckedBody());
emitStmt(dd->getTypecheckedBody());
std::optional<SILValue> maybeReturnValue;
SILLocation returnLoc(Loc);
std::tie(maybeReturnValue, returnLoc) = emitEpilogBB(Loc);
if (!maybeReturnValue)
return;
auto cleanupLoc = CleanupLocation(Loc);
// If we have a superclass, invoke its destructor.
SILValue resultSelfValue;
SILType objectPtrTy = SILType::getNativeObjectType(F.getASTContext());
SILType classTy = selfValue->getType();
if (cd->hasSuperclass() && !cd->isNativeNSObjectSubclass()) {
Type superclassTy =
dd->mapTypeIntoContext(cd->getSuperclass());
ClassDecl *superclass = superclassTy->getClassOrBoundGenericClass();
auto superclassDtorDecl = superclass->getDestructor();
SILDeclRef dtorConstant =
SILDeclRef(superclassDtorDecl, SILDeclRef::Kind::Destroyer);
SILType baseSILTy = getLoweredLoadableType(superclassTy);
SILValue baseSelf = B.createUpcast(cleanupLoc, selfValue, baseSILTy);
ManagedValue dtorValue;
SILType dtorTy;
auto subMap
= superclassTy->getContextSubstitutionMap(superclass);
// We completely drop the generic signature if all generic parameters were
// concrete.
if (subMap && subMap.getGenericSignature()->areAllParamsConcrete())
subMap = SubstitutionMap();
std::tie(dtorValue, dtorTy)
= emitSiblingMethodRef(cleanupLoc, baseSelf, dtorConstant, subMap);
resultSelfValue = B.createApply(cleanupLoc, dtorValue.forward(*this),
subMap, baseSelf);
} else {
resultSelfValue = selfValue;
}
ArgumentScope S(*this, Loc);
ManagedValue borrowedValue = B.borrowObjectRValue(
*this, cleanupLoc, resultSelfValue, ManagedValue::ScopeKind::Lexical);
if (classTy != borrowedValue.getType()) {
borrowedValue =
B.createUncheckedRefCast(cleanupLoc, borrowedValue, classTy);
}
// A distributed actor must invoke `actorSystem.resignID` as it deinits.
if (cd->isDistributedActor()) {
// This must only be called by a *local* distributed actor (not a remote proxy).
// Since this call is emitted after the user-declared body of the deinit,
// just before returning; this is guaranteed to only be executed in the local
// actor case - because the body is never executed for a remote proxy either.
emitDistributedActorSystemResignIDCall(
cleanupLoc, cd, ManagedValue::forBorrowedRValue(selfValue));
}
// Release our members.
emitClassMemberDestruction(borrowedValue, cd, cleanupLoc);
S.pop();
if (resultSelfValue->getType() != objectPtrTy) {
resultSelfValue =
B.createUncheckedRefCast(cleanupLoc, resultSelfValue, objectPtrTy);
}
if (resultSelfValue->getOwnershipKind() != OwnershipKind::Owned) {
assert(resultSelfValue->getOwnershipKind() == OwnershipKind::Guaranteed);
resultSelfValue = B.createUncheckedOwnershipConversion(
cleanupLoc, resultSelfValue, OwnershipKind::Owned);
}
B.createReturn(returnLoc, resultSelfValue);
}
void SILGenFunction::emitDeallocatingDestructor(DestructorDecl *dd,
bool isIsolated) {
auto *nom = dd->getDeclContext()->getSelfNominalTypeDecl();
if (isa<ClassDecl>(nom))
return emitDeallocatingClassDestructor(dd, isIsolated);
assert(!nom->canBeCopyable());
return emitDeallocatingMoveOnlyDestructor(dd);
}
void SILGenFunction::emitDeallocatingClassDestructor(DestructorDecl *dd,
bool isIsolated) {
MagicFunctionName = DeclName(SGM.M.getASTContext().getIdentifier("deinit"));
// The deallocating destructor is always auto-generated.
RegularLocation loc(dd);
loc.markAutoGenerated();
if (dd->requiresUnavailableDeclABICompatibilityStubs())
emitApplyOfUnavailableCodeReached();
// Emit the prolog.
SILValue initialSelfValue =
emitSelfDeclForDestructor(dd->getImplicitSelfDecl());
emitDistributedRemoteActorDeinit(initialSelfValue, dd, isIsolated, [=] {
// Form a reference to the destroying destructor.
SILDeclRef dtorConstant(dd, SILDeclRef::Kind::Destroyer);
auto classTy = initialSelfValue->getType();
auto subMap = F.getForwardingSubstitutionMap();
ManagedValue dtorValue;
SILType dtorTy;
std::tie(dtorValue, dtorTy) =
emitSiblingMethodRef(loc, initialSelfValue, dtorConstant, subMap);
// Call the destroying destructor.
SILValue selfForDealloc;
{
FullExpr CleanupScope(Cleanups, CleanupLocation(loc));
ManagedValue borrowedSelf = emitManagedBeginBorrow(loc, initialSelfValue);
selfForDealloc = B.createApply(loc, dtorValue.forward(*this), subMap,
borrowedSelf.getUnmanagedValue());
}
// Balance out the +1 from the self argument using end_lifetime.
//
// The issue here is that:
//
// 1. Self is passed into deallocating deinits at +1.
// 2. Destroying deinits take in self as a +0 value that is then returned at
// +1.
//
// This means that the lifetime of self can not be modeled statically in a
// deallocating deinit without analyzing the body of the destroying deinit
// (something that violates semantic sil). Thus we add an artificial destroy
// of self before the actual destroy of self so that the verifier can
// understand that self is being properly balanced.
B.createEndLifetime(loc, initialSelfValue);
// Deallocate the object.
selfForDealloc = B.createUncheckedRefCast(loc, selfForDealloc, classTy);
B.createDeallocRef(loc, selfForDealloc);
});
}
void SILGenFunction::emitDeallocatingMoveOnlyDestructor(DestructorDecl *dd) {
MagicFunctionName = DeclName(SGM.M.getASTContext().getIdentifier("deinit"));
RegularLocation loc(dd);
if (dd->isImplicit())
loc.markAutoGenerated();
if (dd->requiresUnavailableDeclABICompatibilityStubs())
emitApplyOfUnavailableCodeReached();
// Emit the prolog.
auto selfValue = emitSelfDeclForDestructor(dd->getImplicitSelfDecl());
// Create a basic block to jump to for the implicit destruction behavior
// of releasing the elements and calling the superclass destructor.
// We won't actually emit the block until we finish with the destructor body.
prepareEpilog(dd, std::nullopt, std::nullopt, CleanupLocation(loc));
auto cleanupLoc = CleanupLocation(loc);
emitProfilerIncrement(dd->getTypecheckedBody());
emitStmt(dd->getTypecheckedBody());
std::optional<SILValue> maybeReturnValue;
SILLocation returnLoc(loc);
std::tie(maybeReturnValue, returnLoc) = emitEpilogBB(loc);
// Clean up our members, consuming our +1 self value as we do it.
emitMoveOnlyMemberDestruction(selfValue,
dd->getDeclContext()->getSelfNominalTypeDecl(),
cleanupLoc);
if (auto *ddi = dyn_cast<DropDeinitInst>(selfValue)) {
if (auto *mu =
dyn_cast<MarkUnresolvedNonCopyableValueInst>(ddi->getOperand())) {
if (auto *asi = dyn_cast<AllocStackInst>(mu->getOperand())) {
B.createDeallocStack(loc, asi);
}
}
}
// Return.
B.createReturn(loc, emitEmptyTuple(loc));
}
/// Determine whether the availability for the body the given destructor predates the introduction of
/// support for isolated deinit.
static bool availabilityPredatesIsolatedDeinit(DestructorDecl *dd) {
ASTContext &ctx = dd->getASTContext();
if (ctx.LangOpts.DisableAvailabilityChecking)
return false;
auto deploymentAvailability = AvailabilityRange::forDeploymentTarget(ctx);
return !deploymentAvailability.isContainedIn(ctx.getIsolatedDeinitAvailability());
}
void SILGenFunction::emitIsolatingDestructor(DestructorDecl *dd) {
MagicFunctionName = DeclName(SGM.M.getASTContext().getIdentifier("deinit"));
// The deallocating destructor is always auto-generated.
RegularLocation loc(dd);
loc.markAutoGenerated();
// Emit the prolog.
SILValue selfValue = emitSelfDeclForDestructor(dd->getImplicitSelfDecl());
// Remote actor proxies don't need isolation
// Emit check for remote actor before performing isolation
emitDistributedRemoteActorDeinit(selfValue, dd, false, [=] {
// Form a reference to the destroying destructor.
SILDeclRef dtorConstant(dd, SILDeclRef::Kind::IsolatedDeallocator);
auto classTy = selfValue->getType();
auto classDecl = classTy.getASTType()->getAnyNominal();
ManagedValue dtorValue;
SILType dtorTy;
auto subMap = classTy.getASTType()->getContextSubstitutionMap(classDecl);
std::tie(dtorValue, dtorTy) =
emitSiblingMethodRef(loc, selfValue, dtorConstant, subMap);
// Get an executor
auto ai = swift::getActorIsolation(dd);
SILValue executor;
{
FullExpr CleanupScope(Cleanups, CleanupLocation(loc));
auto actor = *emitExecutor(
loc, ai, ManagedValue::forUnmanagedOwnedValue(selfValue));
executor = B.createExtractExecutor(loc, actor);
}
// Determine whether we need the main-actor back-deployment version of
// this function.
bool useMainActorBackDeploy =
ai.isMainActor() && availabilityPredatesIsolatedDeinit(dd);
// Get deinitOnExecutor
FuncDecl *swiftDeinitOnExecutorDecl =
useMainActorBackDeploy ? SGM.getDeinitOnExecutorMainActorBackDeploy()
: SGM.getDeinitOnExecutor();
if (!swiftDeinitOnExecutorDecl) {
dd->diagnose(diag::missing_deinit_on_executor_function);
return;
}
SILFunction *swiftDeinitOnExecutorSILFunc = SGM.getFunction(
SILDeclRef(swiftDeinitOnExecutorDecl, SILDeclRef::Kind::Func),
NotForDefinition);
SILValue swiftDeinitOnExecutorFunc =
B.createFunctionRefFor(loc, swiftDeinitOnExecutorSILFunc);
// Cast self to AnyObject preserving owned ownership
CanType selfType = selfValue->getType().getASTType();
CanType anyObjectType = getASTContext().getAnyObjectType();
SILType anyObjectLoweredType =
getTypeLowering(anyObjectType).getLoweredType();
auto conformances = collectExistentialConformances(
selfType->getCanonicalType(), anyObjectType);
auto castedSelf = B.createInitExistentialRef(
loc, anyObjectLoweredType, selfType, selfValue, conformances);
// Cast isolated deallocator to (__owned AnyObject) -> Void
auto workFuncType1 = SILFunctionType::get(
/*genericSig*/ nullptr, SILFunctionType::ExtInfo::getThin(),
SILCoroutineKind::None, ParameterConvention::Direct_Unowned,
{SILParameterInfo(anyObjectLoweredType.getASTType(),
ParameterConvention::Direct_Owned)},
/*interfaceYields*/ {},
/* results */ {},
/*interfaceErrorResults*/ std::nullopt,
/* patternSubs */ {},
/* invocationSubs */ {}, getASTContext());
SILType workFuncType = SILType::getPrimitiveObjectType(workFuncType1);
SILValue dtx = dtorValue.getValue();
auto castedDeallocator =
B.createConvertFunction(loc, dtx, workFuncType, false);
auto wordTy = SILType::getBuiltinWordType(getASTContext());
auto *flagsInst =
B.createIntegerLiteral(loc, wordTy, 0);
// Schedule isolated execution
B.createApply(loc, swiftDeinitOnExecutorFunc, {},
{castedSelf, castedDeallocator, executor, flagsInst});
});
}
void SILGenFunction::emitIVarDestroyer(SILDeclRef ivarDestroyer) {
auto cd = cast<ClassDecl>(ivarDestroyer.getDecl());
RegularLocation loc(cd);
loc.markAutoGenerated();
ManagedValue selfValue;
{
SILValue rawSelfValue =
emitSelfDeclForDestructor(cd->getDestructor()->getImplicitSelfDecl());
if (rawSelfValue->getOwnershipKind() == OwnershipKind::Unowned) {
selfValue = ManagedValue::forUnownedObjectValue(rawSelfValue);
} else {
selfValue = ManagedValue::forBorrowedRValue(rawSelfValue);
}
}
assert(selfValue);
auto cleanupLoc = CleanupLocation(loc);
prepareEpilog(cd, std::nullopt, std::nullopt, cleanupLoc);
{
Scope S(*this, cleanupLoc);
// Self is effectively guaranteed for the duration of any destructor. For
// ObjC classes, self may be unowned. A conversion to guaranteed is required
// to access its members.
if (selfValue.getOwnershipKind() != OwnershipKind::Guaranteed) {
// %guaranteedSelf = unchecked_ownership_conversion %self to @guaranteed
// ...
// end_borrow %guaranteedSelf
auto guaranteedSelf = B.createUncheckedOwnershipConversion(
cleanupLoc, selfValue.forward(*this), OwnershipKind::Guaranteed);
selfValue = emitManagedBorrowedRValueWithCleanup(guaranteedSelf);
}
emitClassMemberDestruction(selfValue, cd, cleanupLoc);
}
B.createReturn(loc, emitEmptyTuple(loc));
emitEpilog(loc);
}
void SILGenFunction::destroyClassMember(SILLocation cleanupLoc,
ManagedValue selfValue, VarDecl *D) {
const TypeLowering &ti = getTypeLowering(D->getTypeInContext());
if (!ti.isTrivial()) {
SILValue addr =
B.createRefElementAddr(cleanupLoc, selfValue.getValue(), D,
ti.getLoweredType().getAddressType());
addr = B.createBeginAccess(
cleanupLoc, addr, SILAccessKind::Deinit, SILAccessEnforcement::Static,
false /*noNestedConflict*/, false /*fromBuiltin*/);
B.createDestroyAddr(cleanupLoc, addr);
B.createEndAccess(cleanupLoc, addr, false /*is aborting*/);
}
}
/// Finds stored properties that have the same type as `cd` and thus form
/// a recursive structure.
///
/// Example:
///
/// class Node<T> {
/// let element: T
/// let next: Node<T>?
/// }
///
/// In the above example `next` is a recursive link and would be recognized
/// by this function and added to the result set.
static void findRecursiveLinks(ClassDecl *cd,
llvm::SmallSetVector<VarDecl *, 4> &result) {
auto selfTy = cd->getDeclaredInterfaceType();
// Collect all stored properties that would form a recursive structure,
// so we can remove the recursion and prevent the call stack from
// overflowing.
for (VarDecl *vd : cd->getStoredProperties()) {
auto Ty = vd->getInterfaceType()->getOptionalObjectType();
if (Ty && Ty->getCanonicalType() == selfTy->getCanonicalType()) {
result.insert(vd);
}
}
// NOTE: Right now we only optimize linear recursion, so if there is more
// than one stored property of the same type, clear out the set and don't
// perform any recursion optimization.
if (result.size() > 1) {
result.clear();
}
}
void SILGenFunction::emitRecursiveChainDestruction(ManagedValue selfValue,
ClassDecl *cd,
VarDecl *recursiveLink,
CleanupLocation cleanupLoc) {
auto selfTy = F.mapTypeIntoContext(cd->getDeclaredInterfaceType());
auto selfTyLowered = getTypeLowering(selfTy).getLoweredType();
SILBasicBlock *cleanBB = createBasicBlock();
SILBasicBlock *noneBB = createBasicBlock();
SILBasicBlock *notUniqueBB = createBasicBlock();
SILBasicBlock *uniqueBB = createBasicBlock();
SILBasicBlock *someBB = createBasicBlock();
SILBasicBlock *loopBB = createBasicBlock();
// var iter = self.link
// self.link = nil
auto Ty = getTypeLowering(F.mapTypeIntoContext(recursiveLink->getInterfaceType())).getLoweredType();
auto optionalNone = B.createOptionalNone(cleanupLoc, Ty);
SILValue varAddr =
B.createRefElementAddr(cleanupLoc, selfValue.getValue(), recursiveLink,
Ty.getAddressType());
auto *iterAddr = B.createAllocStack(cleanupLoc, Ty);
SILValue addr = B.createBeginAccess(
cleanupLoc, varAddr, SILAccessKind::Modify, SILAccessEnforcement::Static,
true /*noNestedConflict*/, false /*fromBuiltin*/);
SILValue iter = B.createLoad(cleanupLoc, addr, LoadOwnershipQualifier::Take);
B.createStore(cleanupLoc, optionalNone, addr, StoreOwnershipQualifier::Init);
B.createEndAccess(cleanupLoc, addr, false /*is aborting*/);
B.createStore(cleanupLoc, iter, iterAddr, StoreOwnershipQualifier::Init);
B.createBranch(cleanupLoc, loopBB);
// while iter != nil {
{
B.emitBlock(loopBB);
auto iterBorrow = ManagedValue::forBorrowedAddressRValue(iterAddr);
SwitchEnumBuilder switchBuilder(B, cleanupLoc, iterBorrow);
switchBuilder.addOptionalSomeCase(someBB);
switchBuilder.addOptionalNoneCase(noneBB);
std::move(switchBuilder).emit();
}
// if isKnownUniquelyReferenced(&iter) {
{
B.emitBlock(someBB);
auto isUnique = B.createIsUnique(cleanupLoc, iterAddr);
B.createCondBranch(cleanupLoc, isUnique, uniqueBB, notUniqueBB);
}
// we have a uniquely referenced link, so we need to deinit
{
B.emitBlock(uniqueBB);
// let tail = iter.unsafelyUnwrapped.next
// iter = tail
SILValue iterBorrow = B.createLoadBorrow(cleanupLoc, iterAddr);
auto *link = B.createUncheckedEnumData(
cleanupLoc, iterBorrow, getASTContext().getOptionalSomeDecl(),
selfTyLowered);
varAddr = B.createRefElementAddr(cleanupLoc, link, recursiveLink,
Ty.getAddressType());
addr = B.createBeginAccess(
cleanupLoc, varAddr, SILAccessKind::Read, SILAccessEnforcement::Static,
true /* noNestedConflict */, false /*fromBuiltin*/);
// The deinit of `iter` will decrement the ref count of the field
// containing the next element and potentially leading to its
// deinitialization, causing the recursion. The prevent that,
// we `load [copy]` here to ensure the object stays alive until
// we explicitly release it in the next step of the iteration.
iter = B.createLoad(cleanupLoc, addr, LoadOwnershipQualifier::Copy);
B.createEndAccess(cleanupLoc, addr, false /*is aborting*/);
B.createEndBorrow(cleanupLoc, iterBorrow);
B.createStore(cleanupLoc, iter, iterAddr, StoreOwnershipQualifier::Assign);
B.createBranch(cleanupLoc, loopBB);
}
// the next link in the chain is not unique, so we are done here
{
B.emitBlock(notUniqueBB);
B.createBranch(cleanupLoc, cleanBB);
}
// we reached the end of the chain
{
B.emitBlock(noneBB);
B.createBranch(cleanupLoc, cleanBB);
}
{
B.emitBlock(cleanBB);
B.createDestroyAddr(cleanupLoc, iterAddr);
B.createDeallocStack(cleanupLoc, iterAddr);
}
}
void SILGenFunction::emitDestroyDefaultActor(CleanupLocation cleanupLoc,
SILValue selfValue) {
// TODO(distributed): we may need to call the distributed destroy here
// instead?
auto builtinName = getASTContext().getIdentifier(
getBuiltinName(BuiltinValueKind::DestroyDefaultActor));
auto resultTy = SGM.Types.getEmptyTupleType();
B.createBuiltin(cleanupLoc, builtinName, resultTy, /*subs*/ {}, {selfValue});
}
void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue,
ClassDecl *cd,
CleanupLocation cleanupLoc) {
assert(selfValue.getOwnershipKind() == OwnershipKind::Guaranteed);
// Before we destroy all fields, we check if any of them are
// recursively the same type as `self`, so we can iteratively
// deinitialize them, to prevent deep recursion and potential
// stack overflows.
llvm::SmallSetVector<VarDecl *, 4> recursiveLinks;
findRecursiveLinks(cd, recursiveLinks);
/// Destroy all members.
{
for (VarDecl *vd : cd->getStoredProperties()) {
if (recursiveLinks.contains(vd))
continue;
destroyClassMember(cleanupLoc, selfValue, vd);
}
if (!recursiveLinks.empty()) {
assert(recursiveLinks.size() == 1 && "Only linear recursion supported.");
emitRecursiveChainDestruction(selfValue, cd, recursiveLinks[0], cleanupLoc);
}
}
{
if (cd->isRootDefaultActor()) {
emitDestroyDefaultActor(cleanupLoc, selfValue.getValue());
}
}
}
void SILGenFunction::emitMoveOnlyMemberDestruction(SILValue selfValue,
NominalTypeDecl *nom,
CleanupLocation cleanupLoc) {
if (!isa<DropDeinitInst>(selfValue)) {
// drop_deinit invalidates any user-defined struct/enum deinit
// before the individual members are destroyed.
selfValue = B.createDropDeinit(cleanupLoc, selfValue);
}
if (selfValue->getType().isObject()) {
// A destroy value that uses the result of a drop_deinit implicitly performs
// memberwise destruction.
B.emitDestroyValueOperation(cleanupLoc, selfValue);
return;
}
// self has been stored into a temporary
assert(!selfValue->getType().isObject());
if (isa<StructDecl>(nom)) {
for (VarDecl *vd : nom->getStoredProperties()) {
const TypeLowering &ti = getTypeLowering(vd->getTypeInContext());
if (ti.isTrivial())
continue;
SILValue addr = B.createStructElementAddr(
cleanupLoc, selfValue, vd, ti.getLoweredType().getAddressType());
addr = B.createBeginAccess(
cleanupLoc, addr, SILAccessKind::Deinit, SILAccessEnforcement::Static,
false /*noNestedConflict*/, false /*fromBuiltin*/);
B.createDestroyAddr(cleanupLoc, addr);
B.createEndAccess(cleanupLoc, addr, false /*is aborting*/);
}
} else {
auto *origBlock = B.getInsertionBB();
auto *enumDecl = cast<EnumDecl>(nom);
SmallVector<std::pair<EnumElementDecl *, SILBasicBlock *>, 8> caseCleanups;
auto *contBlock = createBasicBlock();
for (auto *enumElt : enumDecl->getAllElements()) {
auto *enumBlock = createBasicBlock();
SILBuilder builder(enumBlock, enumBlock->begin());
if (enumElt->hasAssociatedValues()) {
auto *take = builder.createUncheckedTakeEnumDataAddr(
cleanupLoc, selfValue, enumElt);
builder.createDestroyAddr(cleanupLoc, take);
}
// Branch to the continue trampoline block.
builder.createBranch(cleanupLoc, contBlock);
caseCleanups.emplace_back(enumElt, enumBlock);
// Set the insertion point to after this enum block so we insert the
// next new block after this block.
B.setInsertionPoint(enumBlock);
}
B.setInsertionPoint(origBlock);
B.createSwitchEnumAddr(cleanupLoc, selfValue, nullptr, caseCleanups);
B.setInsertionPoint(contBlock);
}
}
void SILGenFunction::emitObjCDestructor(SILDeclRef dtor) {
auto dd = cast<DestructorDecl>(dtor.getDecl());
auto cd = cast<ClassDecl>(dd->getDeclContext()->getImplementedObjCContext());
MagicFunctionName = DeclName(SGM.M.getASTContext().getIdentifier("deinit"));
RegularLocation loc(dd);
if (dd->isImplicit())
loc.markAutoGenerated();
if (dd->requiresUnavailableDeclABICompatibilityStubs())
emitApplyOfUnavailableCodeReached();
SILValue selfValue = emitSelfDeclForDestructor(dd->getImplicitSelfDecl());
// Create a basic block to jump to for the implicit destruction behavior
// of releasing the elements and calling the superclass destructor.
// We won't actually emit the block until we finish with the destructor body.
prepareEpilog(dd, std::nullopt, std::nullopt, CleanupLocation(loc));
emitProfilerIncrement(dd->getTypecheckedBody());
// Emit the destructor body.
emitStmt(dd->getTypecheckedBody());
std::optional<SILValue> maybeReturnValue;
SILLocation returnLoc(loc);
std::tie(maybeReturnValue, returnLoc) = emitEpilogBB(loc);
if (!maybeReturnValue)
return;
auto cleanupLoc = CleanupLocation(loc);
// Note: the ivar destroyer is responsible for destroying the
// instance variables before the object is actually deallocated.
// Form a reference to the superclass -dealloc.
Type superclassTy = dd->mapTypeIntoContext(cd->getSuperclass());
assert(superclassTy && "Emitting Objective-C -dealloc without superclass?");
ClassDecl *superclass = superclassTy->getClassOrBoundGenericClass();
auto superclassDtorDecl = superclass->getDestructor();
auto superclassDtor = SILDeclRef(superclassDtorDecl,
SILDeclRef::Kind::Deallocator)
.asForeign();
auto superclassDtorType =
SGM.Types.getConstantType(getTypeExpansionContext(), superclassDtor);
SILValue superclassDtorValue = B.createObjCSuperMethod(
cleanupLoc, selfValue, superclassDtor,
superclassDtorType);
// Call the superclass's -dealloc.
SILType superclassSILTy = getLoweredLoadableType(superclassTy);
SILValue superSelf = B.createUpcast(cleanupLoc, selfValue, superclassSILTy);
assert(superSelf->getOwnershipKind() == OwnershipKind::Owned);
auto subMap
= superclassTy->getContextSubstitutionMap(superclass);
B.createApply(cleanupLoc, superclassDtorValue, subMap, superSelf);
// We know that the given value came in at +1, but we pass the relevant value
// as unowned to the destructor. Create a fake balance for the verifier to be
// happy.
B.createEndLifetime(cleanupLoc, superSelf);
// Return.
B.createReturn(returnLoc, emitEmptyTuple(cleanupLoc));
}