mirror of
https://github.com/apple/swift.git
synced 2026-06-20 15:42:51 +02:00
097b0d3400
We cannot use spare bits or other overlapping storage layout tricks with fundamentally address-only enums, and we can take advantage of this to do borrowing switches or other in-place projections without copying the value. However, for resilient enums, the implementation may use spare bit packing, but the type must be handled address-only outside of its defining module, and we didn't have a way to express that with borrowing switch. Optimization passes have also been running into problems with the complexity that we were using `unchecked_take_enum_data_addr` sometimes as a pure operation. This patch splits the instruction into three: - `unchecked_inplace_enum_data_addr` represents a nondestructive in-place enum projection. It is only allowed for enums whose projection operation is nondestructive. - `unchecked_take_enum_data_addr` represents a destructive enum projection, invalidating the enum and leaving the payload to be further consumed. This matches the current instruction's semantics. - `unchecked_borrow_enum_data_addr` represents a borrowing enum projection. The instruction takes a second operand for "scratch" space, which the enum representation may be copied into in order to avoid invalidating the enum value, so the result is dependent on the lifetime of both the original enum and the scratch buffer. This allows for borrowing switches over resilient enums. `unchecked_borrow_enum_data_addr` is implemented by taking advantage of the "address-only enums can't do spare bit optimization" property at runtime. We inspect the operand type's bitwise-borrowability from its metadata. If the type is bitwise-borrowable, then we are allowed to bitwise-copy the enum to the scratch space and apply the projection to the scratch space, preserving the original value. If the type is not bitwise-borrowable, then we cannot use spare bit optimization in its layout, so we apply the projection in-place. Fixes rdar://174952822.
800 lines
30 KiB
C++
800 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.mapTypeIntoEnvironment(cd->getDeclaredInterfaceType());
|
|
emitDistributedIfRemoteBranch(SILLocation(loc), selfValue, selfTy,
|
|
/*if remote=*/remoteBB, /*if local=*/localBB);
|
|
|
|
// Emit remote BB
|
|
{
|
|
B.emitBlock(remoteBB);
|
|
|
|
auto cleanupLoc = CleanupLocation(loc);
|
|
|
|
{
|
|
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->isSpecialDistributedActorProperty())
|
|
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->mapTypeIntoEnvironment(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
|
|
// _deinitOnExecutor is not generic, even when the enclosing destructor is.
|
|
B.createApply(loc, swiftDeinitOnExecutorFunc, SubstitutionMap(),
|
|
{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.mapTypeIntoEnvironment(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.mapTypeIntoEnvironment(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.createUncheckedEnumDataAddrForTake(
|
|
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->mapTypeIntoEnvironment(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));
|
|
}
|