/// Carry out the operations required for an indirect conditional cast /// using a scalar cast operation. void swift::emitIndirectConditionalCastWithScalar( SILBuilder &B, ModuleDecl *M, SILLocation loc, CastConsumptionKind consumption, SILValue src, CanType sourceType, SILValue dest, CanType targetType, SILBasicBlock *indirectSuccBB, SILBasicBlock *indirectFailBB, ProfileCounter TrueCount, ProfileCounter FalseCount) { assert(canUseScalarCheckedCastInstructions(B.getModule(), sourceType, targetType)); // We only need a different failure block if the cast consumption // requires us to destroy the source value. SILBasicBlock *scalarFailBB; if (!shouldDestroyOnFailure(consumption)) { scalarFailBB = indirectFailBB; } else { scalarFailBB = B.splitBlockForFallthrough(); } // We always need a different success block. SILBasicBlock *scalarSuccBB = B.splitBlockForFallthrough(); auto &srcTL = B.getModule().Types.getTypeLowering(src->getType()); // Always take; this works under an assumption that retaining the // result is equivalent to retaining the source. That means that // these casts would not be appropriate for bridging-like conversions. SILValue srcValue = srcTL.emitLoadOfCopy(B, loc, src, IsTake); SILType targetValueType = dest->getType().getObjectType(); B.createCheckedCastBranch(loc, /*exact*/ false, srcValue, targetValueType, scalarSuccBB, scalarFailBB, TrueCount, FalseCount); // Emit the success block. B.setInsertionPoint(scalarSuccBB); { auto &targetTL = B.getModule().Types.getTypeLowering(targetValueType); SILValue succValue = scalarSuccBB->createPhiArgument( targetValueType, ValueOwnershipKind::Owned); if (!shouldTakeOnSuccess(consumption)) targetTL.emitCopyValue(B, loc, succValue); targetTL.emitStoreOfCopy(B, loc, succValue, dest, IsInitialization); B.createBranch(loc, indirectSuccBB); } // Emit the failure block. if (shouldDestroyOnFailure(consumption)) { B.setInsertionPoint(scalarFailBB); srcTL.emitDestroyValue(B, loc, srcValue); B.createBranch(loc, indirectFailBB); } }
void SILGenFunction::emitClassConstructorInitializer(ConstructorDecl *ctor) { MagicFunctionName = SILGenModule::getMagicFunctionName(ctor); assert(ctor->getBody() && "Class constructor without a body?"); // True if this constructor delegates to a peer constructor with self.init(). bool isDelegating = false; if (!ctor->hasStubImplementation()) { isDelegating = ctor->getDelegatingOrChainedInitKind(nullptr) == ConstructorDecl::BodyInitKind::Delegating; } // Set up the 'self' argument. If this class has a superclass, we set up // self as a box. This allows "self reassignment" to happen in super init // method chains, which is important for interoperating with Objective-C // classes. We also use a box for delegating constructors, since the // delegated-to initializer may also replace self. // // TODO: If we could require Objective-C classes to have an attribute to get // this behavior, we could avoid runtime overhead here. VarDecl *selfDecl = ctor->getImplicitSelfDecl(); auto *dc = ctor->getDeclContext(); auto selfClassDecl = dc->getSelfClassDecl(); bool NeedsBoxForSelf = isDelegating || (selfClassDecl->hasSuperclass() && !ctor->hasStubImplementation()); bool usesObjCAllocator = Lowering::usesObjCAllocator(selfClassDecl); // If needed, mark 'self' as uninitialized so that DI knows to // enforce its DI properties on stored properties. MarkUninitializedInst::Kind MUKind; if (isDelegating) { if (ctor->isObjC()) MUKind = MarkUninitializedInst::DelegatingSelfAllocated; else MUKind = MarkUninitializedInst::DelegatingSelf; } else if (selfClassDecl->requiresStoredPropertyInits() && usesObjCAllocator) { // Stored properties will be initialized in a separate // .cxx_construct method called by the Objective-C runtime. assert(selfClassDecl->hasSuperclass() && "Cannot use ObjC allocation without a superclass"); MUKind = MarkUninitializedInst::DerivedSelfOnly; } else if (selfClassDecl->hasSuperclass()) MUKind = MarkUninitializedInst::DerivedSelf; else MUKind = MarkUninitializedInst::RootSelf; if (NeedsBoxForSelf) { // Allocate the local variable for 'self'. emitLocalVariableWithCleanup(selfDecl, MUKind)->finishInitialization(*this); } // Emit the prolog for the non-self arguments. // FIXME: Handle self along with the other body patterns. uint16_t ArgNo = emitProlog(ctor->getParameters(), /*selfParam=*/nullptr, TupleType::getEmpty(F.getASTContext()), ctor, ctor->hasThrows()); SILType selfTy = getLoweredLoadableType(selfDecl->getType()); ManagedValue selfArg = B.createInputFunctionArgument(selfTy, selfDecl); if (!NeedsBoxForSelf) { SILLocation PrologueLoc(selfDecl); PrologueLoc.markAsPrologue(); SILDebugVariable DbgVar(selfDecl->isLet(), ++ArgNo); B.createDebugValue(PrologueLoc, selfArg.getValue(), DbgVar); } if (!ctor->hasStubImplementation()) { assert(selfTy.hasReferenceSemantics() && "can't emit a value type ctor here"); if (NeedsBoxForSelf) { SILLocation prologueLoc = RegularLocation(ctor); prologueLoc.markAsPrologue(); // SEMANTIC ARC TODO: When the verifier is complete, review this. B.emitStoreValueOperation(prologueLoc, selfArg.forward(*this), VarLocs[selfDecl].value, StoreOwnershipQualifier::Init); } else { selfArg = B.createMarkUninitialized(selfDecl, selfArg, MUKind); VarLocs[selfDecl] = VarLoc::get(selfArg.getValue()); } } // Prepare the end of initializer location. SILLocation endOfInitLoc = RegularLocation(ctor); endOfInitLoc.pointToEnd(); // Create a basic block to jump to for the implicit 'self' return. // We won't emit the block until after we've emitted the body. prepareEpilog(Type(), ctor->hasThrows(), CleanupLocation::get(endOfInitLoc)); auto resultType = ctor->mapTypeIntoContext(ctor->getResultInterfaceType()); // If the constructor can fail, set up an alternative epilog for constructor // failure. SILBasicBlock *failureExitBB = nullptr; SILArgument *failureExitArg = nullptr; auto &resultLowering = getTypeLowering(resultType); if (ctor->getFailability() != OTK_None) { SILBasicBlock *failureBB = createBasicBlock(FunctionSection::Postmatter); RegularLocation loc(ctor); loc.markAutoGenerated(); // On failure, we'll clean up everything and return nil instead. SILGenSavedInsertionPoint savedIP(*this, failureBB, FunctionSection::Postmatter); failureExitBB = createBasicBlock(); failureExitArg = failureExitBB->createPhiArgument( resultLowering.getLoweredType(), ValueOwnershipKind::Owned); Cleanups.emitCleanupsForReturn(ctor, IsForUnwind); SILValue nilResult = B.createEnum(loc, SILValue(), getASTContext().getOptionalNoneDecl(), resultLowering.getLoweredType()); B.createBranch(loc, failureExitBB, nilResult); B.setInsertionPoint(failureExitBB); B.createReturn(loc, failureExitArg); FailDest = JumpDest(failureBB, Cleanups.getCleanupsDepth(), ctor); } // Handle member initializers. if (isDelegating) { // A delegating initializer does not initialize instance // variables. } else if (ctor->hasStubImplementation()) { // Nor does a stub implementation. } else if (selfClassDecl->requiresStoredPropertyInits() && usesObjCAllocator) { // When the class requires all stored properties to have initial // values and we're using Objective-C's allocation, stored // properties are initialized via the .cxx_construct method, which // will be called by the runtime. // Note that 'self' has been fully initialized at this point. } else { // Emit the member initializers. emitMemberInitializers(ctor, selfDecl, selfClassDecl); } emitProfilerIncrement(ctor->getBody()); // Emit the constructor body. emitStmt(ctor->getBody()); // Emit the call to super.init() right before exiting from the initializer. if (NeedsBoxForSelf) { if (auto *SI = ctor->getSuperInitCall()) { B.setInsertionPoint(ReturnDest.getBlock()); emitRValue(SI); B.emitBlock(B.splitBlockForFallthrough(), ctor); ReturnDest = JumpDest(B.getInsertionBB(), ReturnDest.getDepth(), ReturnDest.getCleanupLocation()); B.clearInsertionPoint(); } } CleanupStateRestorationScope SelfCleanupSave(Cleanups); // Build a custom epilog block, since the AST representation of the // constructor decl (which has no self in the return type) doesn't match the // SIL representation. { // Ensure that before we add additional cleanups, that we have emitted all // cleanups at this point. assert(!Cleanups.hasAnyActiveCleanups(getCleanupsDepth(), ReturnDest.getDepth()) && "emitting epilog in wrong scope"); SILGenSavedInsertionPoint savedIP(*this, ReturnDest.getBlock()); auto cleanupLoc = CleanupLocation(ctor); // If we're using a box for self, reload the value at the end of the init // method. if (NeedsBoxForSelf) { ManagedValue storedSelf = ManagedValue::forUnmanaged(VarLocs[selfDecl].value); selfArg = B.createLoadCopy(cleanupLoc, storedSelf); } else { // We have to do a retain because we are returning the pointer +1. // // SEMANTIC ARC TODO: When the verifier is complete, we will need to // change this to selfArg = B.emitCopyValueOperation(...). Currently due // to the way that SILGen performs folding of copy_value, destroy_value, // the returned selfArg may be deleted causing us to have a // dead-pointer. Instead just use the old self value since we have a // class. selfArg = B.createCopyValue(cleanupLoc, selfArg); } // Inject the self value into an optional if the constructor is failable. if (ctor->getFailability() != OTK_None) { RegularLocation loc(ctor); loc.markAutoGenerated(); selfArg = B.createEnum(loc, selfArg, getASTContext().getOptionalSomeDecl(), getLoweredLoadableType(resultType)); } // Save our cleanup state. We want all other potential cleanups to fire, but // not this one. if (selfArg.hasCleanup()) SelfCleanupSave.pushCleanupState(selfArg.getCleanup(), CleanupState::Dormant); // Translate our cleanup to the new top cleanup. // // This is needed to preserve the invariant in getEpilogBB that when // cleanups are emitted, everything above ReturnDest.getDepth() has been // emitted. This is not true if we use ManagedValue and friends in the // epilogBB, thus the translation. We perform the same check above that // getEpilogBB performs to ensure that we still do not have the same // problem. ReturnDest = std::move(ReturnDest).translate(getTopCleanup()); } // Emit the epilog and post-matter. auto returnLoc = emitEpilog(ctor, /*UsesCustomEpilog*/true); // Unpop our selfArg cleanup, so we can forward. std::move(SelfCleanupSave).pop(); // Finish off the epilog by returning. If this is a failable ctor, then we // actually jump to the failure epilog to keep the invariant that there is // only one SIL return instruction per SIL function. if (B.hasValidInsertionPoint()) { if (failureExitBB) B.createBranch(returnLoc, failureExitBB, selfArg.forward(*this)); else B.createReturn(returnLoc, selfArg.forward(*this)); } }
void SILGenFunction::prepareRethrowEpilog(CleanupLocation cleanupLoc) { auto exnType = SILType::getExceptionType(getASTContext()); SILBasicBlock *rethrowBB = createBasicBlock(FunctionSection::Postmatter); rethrowBB->createPhiArgument(exnType, ValueOwnershipKind::Owned); ThrowDest = JumpDest(rethrowBB, getCleanupsDepth(), cleanupLoc); }
void SILGenFunction::emitValueConstructor(ConstructorDecl *ctor) { MagicFunctionName = SILGenModule::getMagicFunctionName(ctor); if (ctor->isMemberwiseInitializer()) return emitImplicitValueConstructor(*this, ctor); // True if this constructor delegates to a peer constructor with self.init(). bool isDelegating = ctor->getDelegatingOrChainedInitKind(nullptr) == ConstructorDecl::BodyInitKind::Delegating; // Get the 'self' decl and type. VarDecl *selfDecl = ctor->getImplicitSelfDecl(); auto &lowering = getTypeLowering(selfDecl->getType()); // Decide if we need to do extra work to warn on unsafe behavior in pre-Swift-5 // modes. MarkUninitializedInst::Kind MUIKind; if (isDelegating) { MUIKind = MarkUninitializedInst::DelegatingSelf; } else if (getASTContext().isSwiftVersionAtLeast(5)) { MUIKind = MarkUninitializedInst::RootSelf; } else { auto *dc = ctor->getParent(); if (isa<ExtensionDecl>(dc) && dc->getSelfStructDecl()->getParentModule() != dc->getParentModule()) { MUIKind = MarkUninitializedInst::CrossModuleRootSelf; } else { MUIKind = MarkUninitializedInst::RootSelf; } } // Allocate the local variable for 'self'. emitLocalVariableWithCleanup(selfDecl, MUIKind)->finishInitialization(*this); SILValue selfLV = VarLocs[selfDecl].value; // Emit the prolog. emitProlog(ctor->getParameters(), /*selfParam=*/nullptr, ctor->getResultInterfaceType(), ctor, ctor->hasThrows()); emitConstructorMetatypeArg(*this, ctor); // Create a basic block to jump to for the implicit 'self' return. // We won't emit this until after we've emitted the body. // The epilog takes a void return because the return of 'self' is implicit. prepareEpilog(Type(), ctor->hasThrows(), CleanupLocation(ctor)); // If the constructor can fail, set up an alternative epilog for constructor // failure. SILBasicBlock *failureExitBB = nullptr; SILArgument *failureExitArg = nullptr; auto resultType = ctor->mapTypeIntoContext(ctor->getResultInterfaceType()); auto &resultLowering = getTypeLowering(resultType); if (ctor->getFailability() != OTK_None) { SILBasicBlock *failureBB = createBasicBlock(FunctionSection::Postmatter); // On failure, we'll clean up everything (except self, which should have // been cleaned up before jumping here) and return nil instead. SILGenSavedInsertionPoint savedIP(*this, failureBB, FunctionSection::Postmatter); failureExitBB = createBasicBlock(); Cleanups.emitCleanupsForReturn(ctor, IsForUnwind); // Return nil. if (F.getConventions().hasIndirectSILResults()) { // Inject 'nil' into the indirect return. assert(F.getIndirectResults().size() == 1); B.createInjectEnumAddr(ctor, F.getIndirectResults()[0], getASTContext().getOptionalNoneDecl()); B.createBranch(ctor, failureExitBB); B.setInsertionPoint(failureExitBB); B.createReturn(ctor, emitEmptyTuple(ctor)); } else { // Pass 'nil' as the return value to the exit BB. failureExitArg = failureExitBB->createPhiArgument( resultLowering.getLoweredType(), ValueOwnershipKind::Owned); SILValue nilResult = B.createEnum(ctor, SILValue(), getASTContext().getOptionalNoneDecl(), resultLowering.getLoweredType()); B.createBranch(ctor, failureExitBB, nilResult); B.setInsertionPoint(failureExitBB); B.createReturn(ctor, failureExitArg); } FailDest = JumpDest(failureBB, Cleanups.getCleanupsDepth(), ctor); } // If this is not a delegating constructor, emit member initializers. if (!isDelegating) { auto *typeDC = ctor->getDeclContext(); auto *nominal = typeDC->getSelfNominalTypeDecl(); emitMemberInitializers(ctor, selfDecl, nominal); } emitProfilerIncrement(ctor->getBody()); // Emit the constructor body. emitStmt(ctor->getBody()); // Build a custom epilog block, since the AST representation of the // constructor decl (which has no self in the return type) doesn't match the // SIL representation. SILValue selfValue; { SILGenSavedInsertionPoint savedIP(*this, ReturnDest.getBlock()); assert(B.getInsertionBB()->empty() && "Epilog already set up?"); auto cleanupLoc = CleanupLocation::get(ctor); if (!F.getConventions().hasIndirectSILResults()) { // Otherwise, load and return the final 'self' value. selfValue = lowering.emitLoad(B, cleanupLoc, selfLV, LoadOwnershipQualifier::Copy); // Inject the self value into an optional if the constructor is failable. if (ctor->getFailability() != OTK_None) { selfValue = B.createEnum(cleanupLoc, selfValue, getASTContext().getOptionalSomeDecl(), getLoweredLoadableType(resultType)); } } else { // If 'self' is address-only, copy 'self' into the indirect return slot. assert(F.getConventions().getNumIndirectSILResults() == 1 && "no indirect return for address-only ctor?!"); // Get the address to which to store the result. SILValue completeReturnAddress = F.getIndirectResults()[0]; SILValue returnAddress; switch (ctor->getFailability()) { // For non-failable initializers, store to the return address directly. case OTK_None: returnAddress = completeReturnAddress; break; // If this is a failable initializer, project out the payload. case OTK_Optional: case OTK_ImplicitlyUnwrappedOptional: returnAddress = B.createInitEnumDataAddr(cleanupLoc, completeReturnAddress, getASTContext().getOptionalSomeDecl(), selfLV->getType()); break; } // We have to do a non-take copy because someone else may be using the // box (e.g. someone could have closed over it). B.createCopyAddr(cleanupLoc, selfLV, returnAddress, IsNotTake, IsInitialization); // Inject the enum tag if the result is optional because of failability. if (ctor->getFailability() != OTK_None) { // Inject the 'Some' tag. B.createInjectEnumAddr(cleanupLoc, completeReturnAddress, getASTContext().getOptionalSomeDecl()); } } } // Finally, emit the epilog and post-matter. auto returnLoc = emitEpilog(ctor, /*UsesCustomEpilog*/true); // Finish off the epilog by returning. If this is a failable ctor, then we // actually jump to the failure epilog to keep the invariant that there is // only one SIL return instruction per SIL function. if (B.hasValidInsertionPoint()) { if (!failureExitBB) { // If we're not returning self, then return () since we're returning Void. if (!selfValue) { SILLocation loc(ctor); loc.markAutoGenerated(); selfValue = emitEmptyTuple(loc); } B.createReturn(returnLoc, selfValue); } else { if (selfValue) B.createBranch(returnLoc, failureExitBB, selfValue); else B.createBranch(returnLoc, failureExitBB); } } }