| //===--- 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 "SILGenFunction.h" |
| #include "RValue.h" |
| #include "ArgumentScope.h" |
| #include "swift/AST/GenericSignature.h" |
| #include "swift/AST/SubstitutionMap.h" |
| #include "swift/SIL/TypeLowering.h" |
| |
| using namespace swift; |
| using namespace Lowering; |
| |
| void SILGenFunction::emitDestroyingDestructor(DestructorDecl *dd) { |
| MagicFunctionName = DeclName(SGM.M.getASTContext().getIdentifier("deinit")); |
| |
| RegularLocation Loc(dd); |
| if (dd->isImplicit()) |
| Loc.markAutoGenerated(); |
| |
| auto cd = cast<ClassDecl>(dd->getDeclContext()); |
| SILValue selfValue = emitSelfDecl(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(false, false, CleanupLocation::get(Loc)); |
| |
| emitProfilerIncrement(dd->getTypecheckedBody()); |
| // Emit the destructor body. |
| emitStmt(dd->getTypecheckedBody()); |
| |
| Optional<SILValue> maybeReturnValue; |
| SILLocation returnLoc(Loc); |
| std::tie(maybeReturnValue, returnLoc) = emitEpilogBB(Loc); |
| |
| if (!maybeReturnValue) |
| return; |
| |
| auto cleanupLoc = CleanupLocation::get(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(SGM.M.getSwiftModule(), |
| superclass); |
| 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 = |
| ManagedValue::forUnmanaged(resultSelfValue).borrow(*this, cleanupLoc); |
| |
| if (classTy != borrowedValue.getType()) { |
| borrowedValue = |
| B.createUncheckedRefCast(cleanupLoc, borrowedValue, classTy); |
| } |
| |
| // 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) { |
| MagicFunctionName = DeclName(SGM.M.getASTContext().getIdentifier("deinit")); |
| |
| // The deallocating destructor is always auto-generated. |
| RegularLocation loc(dd); |
| loc.markAutoGenerated(); |
| |
| // Emit the prolog. |
| SILValue initialSelfValue = emitSelfDecl(dd->getImplicitSelfDecl()); |
| |
| // Form a reference to the destroying destructor. |
| SILDeclRef dtorConstant(dd, SILDeclRef::Kind::Destroyer); |
| auto classTy = initialSelfValue->getType(); |
| auto classDecl = classTy.getASTType()->getAnyNominal(); |
| ManagedValue dtorValue; |
| SILType dtorTy; |
| auto subMap = classTy.getASTType() |
| ->getContextSubstitutionMap(SGM.M.getSwiftModule(), |
| classDecl); |
| std::tie(dtorValue, dtorTy) |
| = emitSiblingMethodRef(loc, initialSelfValue, dtorConstant, subMap); |
| |
| // Call the destroying destructor. |
| SILValue selfForDealloc; |
| { |
| FullExpr CleanupScope(Cleanups, CleanupLocation::get(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, false); |
| |
| emitProfilerIncrement(dd->getTypecheckedBody()); |
| |
| // Return. |
| B.createReturn(loc, emitEmptyTuple(loc)); |
| } |
| |
| void SILGenFunction::emitIVarDestroyer(SILDeclRef ivarDestroyer) { |
| auto cd = cast<ClassDecl>(ivarDestroyer.getDecl()); |
| RegularLocation loc(cd); |
| loc.markAutoGenerated(); |
| |
| ManagedValue selfValue = ManagedValue::forUnmanaged( |
| emitSelfDecl(cd->getDestructor()->getImplicitSelfDecl())); |
| |
| auto cleanupLoc = CleanupLocation::get(loc); |
| prepareEpilog(false, false, cleanupLoc); |
| { |
| Scope S(*this, cleanupLoc); |
| emitClassMemberDestruction(selfValue, cd, cleanupLoc); |
| } |
| |
| B.createReturn(loc, emitEmptyTuple(loc)); |
| emitEpilog(loc); |
| } |
| |
| void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue, |
| ClassDecl *cd, |
| CleanupLocation cleanupLoc) { |
| selfValue = selfValue.borrow(*this, cleanupLoc); |
| for (VarDecl *vd : cd->getStoredProperties()) { |
| const TypeLowering &ti = getTypeLowering(vd->getType()); |
| if (!ti.isTrivial()) { |
| SILValue addr = |
| B.createRefElementAddr(cleanupLoc, selfValue.getValue(), 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*/); |
| } |
| } |
| |
| if (cd->isRootDefaultActor()) { |
| auto builtinName = getASTContext().getIdentifier( |
| getBuiltinName(BuiltinValueKind::DestroyDefaultActor)); |
| auto resultTy = SGM.Types.getEmptyTupleType(); |
| |
| B.createBuiltin(cleanupLoc, builtinName, resultTy, /*subs*/{}, |
| { selfValue.getValue() }); |
| } |
| } |
| |
| |
| void SILGenFunction::emitObjCDestructor(SILDeclRef dtor) { |
| auto dd = cast<DestructorDecl>(dtor.getDecl()); |
| auto cd = cast<ClassDecl>(dd->getDeclContext()); |
| MagicFunctionName = DeclName(SGM.M.getASTContext().getIdentifier("deinit")); |
| |
| RegularLocation loc(dd); |
| if (dd->isImplicit()) |
| loc.markAutoGenerated(); |
| |
| SILValue selfValue = emitSelfDecl(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(false, false, CleanupLocation::get(loc)); |
| |
| emitProfilerIncrement(dd->getTypecheckedBody()); |
| // Emit the destructor body. |
| emitStmt(dd->getTypecheckedBody()); |
| |
| Optional<SILValue> maybeReturnValue; |
| SILLocation returnLoc(loc); |
| std::tie(maybeReturnValue, returnLoc) = emitEpilogBB(loc); |
| |
| if (!maybeReturnValue) |
| return; |
| |
| auto cleanupLoc = CleanupLocation::get(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(SGM.M.getSwiftModule(), |
| superclass); |
| |
| B.createApply(cleanupLoc, superclassDtorValue, subMap, superSelf); |
| |
| // We know that the givne 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)); |
| } |