//===--- 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 emitLocalDeinit) { RegularLocation loc(dd); loc.markAutoGenerated(); auto cd = cast(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(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 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(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 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(selfValue)) { if (auto *mu = dyn_cast(ddi->getOperand())) { if (auto *asi = dyn_cast(mu->getOperand())) { B.createDeallocStack(loc, asi); } } } // Return. B.createReturn(loc, emitEmptyTuple(loc)); } 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); } // Get deinitOnExecutor FuncDecl *swiftDeinitOnExecutorDecl = 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(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 { /// let element: T /// let next: Node? /// } /// /// 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 &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 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(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 (auto *structDecl = dyn_cast(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(nom); SmallVector, 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(dtor.getDecl()); auto cd = cast(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 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)); }