ManagedValue SILGenFunction::emitPreconditionOptionalHasValue(SILLocation loc, ManagedValue optional) { // Generate code to the optional is present, and if not, abort with a message // (provided by the stdlib). SILBasicBlock *contBB = createBasicBlock(); SILBasicBlock *failBB = createBasicBlock(); bool hadCleanup = optional.hasCleanup(); bool hadLValue = optional.isLValue(); auto noneDecl = getASTContext().getOptionalNoneDecl(); auto someDecl = getASTContext().getOptionalSomeDecl(); if (optional.getType().isAddress()) { // We forward in the creation routine for // unchecked_take_enum_data_addr. switch_enum_addr is a +0 operation. B.createSwitchEnumAddr(loc, optional.getValue(), /*defaultDest*/ nullptr, {{someDecl, contBB}, {noneDecl, failBB}}); } else { B.createSwitchEnum(loc, optional.forward(*this), /*defaultDest*/ nullptr, {{someDecl, contBB}, {noneDecl, failBB}}); } B.emitBlock(failBB); // Call the standard library implementation of _diagnoseUnexpectedNilOptional. if (auto diagnoseFailure = getASTContext().getDiagnoseUnexpectedNilOptional(nullptr)) { ManagedValue args[4]; emitSourceLocationArgs(*this, loc, args); emitApplyOfLibraryIntrinsic(loc, diagnoseFailure, SubstitutionMap(), args, SGFContext()); } B.createUnreachable(loc); B.clearInsertionPoint(); B.emitBlock(contBB); ManagedValue result; SILType payloadType = optional.getType().getAnyOptionalObjectType(); if (payloadType.isObject()) { result = B.createOwnedPHIArgument(payloadType); } else { result = B.createUncheckedTakeEnumDataAddr(loc, optional, someDecl, payloadType); } if (hadCleanup) { return result; } if (hadLValue) { return ManagedValue::forLValue(result.forward(*this)); } return ManagedValue::forUnmanaged(result.forward(*this)); }
ManagedValue Scope::popPreservingValue(ManagedValue mv) { // If we have a value, make sure that it is an object. The reason why is // that we want to make sure that we are not forwarding a cleanup for a // stack location that will be destroyed by this scope. assert(mv && mv.getType().isObject() && (mv.getType().isTrivial(cleanups.SGF.getModule()) || mv.getOwnershipKind() == ValueOwnershipKind::Trivial || mv.hasCleanup())); CleanupCloner cloner(cleanups.SGF, mv); SILValue value = mv.forward(cleanups.SGF); pop(); return cloner.clone(value); }
ManagedValue SILGenBuilder::createUncheckedEnumData(SILLocation loc, ManagedValue operand, EnumElementDecl *element) { if (operand.hasCleanup()) { SILValue newValue = SILBuilder::createUncheckedEnumData(loc, operand.forward(gen), element); return gen.emitManagedRValueWithCleanup(newValue); } ManagedValue borrowedBase = operand.borrow(gen, loc); SILValue newValue = SILBuilder::createUncheckedEnumData( loc, borrowedBase.getValue(), element); return ManagedValue::forUnmanaged(newValue); }
ManagedValue SILGenBuilder::createUncheckedTakeEnumDataAddr( SILLocation loc, ManagedValue operand, EnumElementDecl *element, SILType ty) { // First see if we have a cleanup. If we do, we are going to forward and emit // a managed buffer with cleanup. if (operand.hasCleanup()) { return gen.emitManagedBufferWithCleanup( SILBuilder::createUncheckedTakeEnumDataAddr(loc, operand.forward(gen), element, ty)); } SILValue result = SILBuilder::createUncheckedTakeEnumDataAddr( loc, operand.getUnmanagedValue(), element, ty); if (operand.isLValue()) return ManagedValue::forLValue(result); return ManagedValue::forUnmanaged(result); }
ManagedValue SILGenFunction::emitUncheckedGetOptionalValueFrom(SILLocation loc, ManagedValue addrOrValue, const TypeLowering &optTL, SGFContext C) { OptionalTypeKind OTK; SILType origPayloadTy = addrOrValue.getType().getAnyOptionalObjectType(SGM.M, OTK); auto formalOptionalTy = addrOrValue.getType().getSwiftRValueType(); auto formalPayloadTy = formalOptionalTy ->getAnyOptionalObjectType() ->getCanonicalType(); auto someDecl = getASTContext().getOptionalSomeDecl(OTK); ManagedValue payload; // Take the payload from the optional. Cheat a bit in the +0 // case—UncheckedTakeEnumData will never actually invalidate an Optional enum // value. SILValue payloadVal; if (!addrOrValue.getType().isAddress()) { payloadVal = B.createUncheckedEnumData(loc, addrOrValue.forward(*this), someDecl); } else { payloadVal = B.createUncheckedTakeEnumDataAddr(loc, addrOrValue.forward(*this), someDecl, origPayloadTy); if (optTL.isLoadable()) payloadVal = B.createLoad(loc, payloadVal); } // Produce a correctly managed value. if (addrOrValue.hasCleanup()) payload = emitManagedRValueWithCleanup(payloadVal); else payload = ManagedValue::forUnmanaged(payloadVal); // Reabstract it to the substituted form, if necessary. return emitOrigToSubstValue(loc, payload, AbstractionPattern::getOpaque(), formalPayloadTy, C); }
ManagedValue SILGenFunction::emitUncheckedGetOptionalValueFrom(SILLocation loc, ManagedValue addrOrValue, const TypeLowering &optTL, SGFContext C) { SILType origPayloadTy = addrOrValue.getType().getAnyOptionalObjectType(); auto someDecl = getASTContext().getOptionalSomeDecl(); ManagedValue payload; // Take the payload from the optional. Cheat a bit in the +0 // case--UncheckedTakeEnumData will never actually invalidate an Optional enum // value. SILValue payloadVal; if (!addrOrValue.getType().isAddress()) { payloadVal = B.createUncheckedEnumData(loc, addrOrValue.forward(*this), someDecl); } else { payloadVal = B.createUncheckedTakeEnumDataAddr(loc, addrOrValue.forward(*this), someDecl, origPayloadTy); if (optTL.isLoadable()) payloadVal = optTL.emitLoad(B, loc, payloadVal, LoadOwnershipQualifier::Take); } // Produce a correctly managed value. if (addrOrValue.hasCleanup()) payload = emitManagedRValueWithCleanup(payloadVal); else payload = ManagedValue::forUnmanaged(payloadVal); return payload; }
SILGenFunction::OpaqueValueState SILGenFunction::emitOpenExistential( SILLocation loc, ManagedValue existentialValue, CanArchetypeType openedArchetype, SILType loweredOpenedType) { // Open the existential value into the opened archetype value. bool isUnique = true; bool canConsume; ManagedValue archetypeMV; SILType existentialType = existentialValue.getType(); switch (existentialType.getPreferredExistentialRepresentation(SGM.M)) { case ExistentialRepresentation::Opaque: { if (existentialType.isAddress()) { SILValue archetypeValue = B.createOpenExistentialAddr( loc, existentialValue.forward(*this), loweredOpenedType); if (existentialValue.hasCleanup()) { canConsume = true; // Leave a cleanup to deinit the existential container. enterDeinitExistentialCleanup(existentialValue.getValue(), CanType(), ExistentialRepresentation::Opaque); archetypeMV = emitManagedBufferWithCleanup(archetypeValue); } else { canConsume = false; archetypeMV = ManagedValue::forUnmanaged(archetypeValue); } } else { SILValue archetypeValue = B.createOpenExistentialOpaque( loc, existentialValue.forward(*this), loweredOpenedType); assert(!existentialValue.hasCleanup()); canConsume = false; archetypeMV = ManagedValue::forUnmanaged(archetypeValue); } break; } case ExistentialRepresentation::Metatype: assert(existentialType.isObject()); archetypeMV = ManagedValue::forUnmanaged( B.createOpenExistentialMetatype( loc, existentialValue.forward(*this), loweredOpenedType)); // Metatypes are always trivial. Consuming would be a no-op. canConsume = false; break; case ExistentialRepresentation::Class: { assert(existentialType.isObject()); SILValue archetypeValue = B.createOpenExistentialRef( loc, existentialValue.forward(*this), loweredOpenedType); canConsume = existentialValue.hasCleanup(); archetypeMV = (canConsume ? emitManagedRValueWithCleanup(archetypeValue) : ManagedValue::forUnmanaged(archetypeValue)); break; } case ExistentialRepresentation::Boxed: if (existentialType.isAddress()) { existentialValue = emitLoad(loc, existentialValue.getValue(), getTypeLowering(existentialType), SGFContext::AllowGuaranteedPlusZero, IsNotTake); } existentialType = existentialValue.getType(); assert(existentialType.isObject()); // NB: Don't forward the cleanup, because consuming a boxed value won't // consume the box reference. archetypeMV = ManagedValue::forUnmanaged( B.createOpenExistentialBox(loc, existentialValue.getValue(), loweredOpenedType)); // The boxed value can't be assumed to be uniquely referenced. We can never // consume it. // TODO: We could use isUniquelyReferenced to shorten the duration of // the box to the point that the opaque value is copied out. isUnique = false; canConsume = false; break; case ExistentialRepresentation::None: llvm_unreachable("not existential"); } setArchetypeOpeningSite(openedArchetype, archetypeMV.getValue()); assert(!canConsume || isUnique); (void) isUnique; return SILGenFunction::OpaqueValueState{ archetypeMV, /*isConsumable*/ canConsume, /*hasBeenConsumed*/ false }; }
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->getAsClassOrClassExtensionContext(); 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) 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->getParameterList(1), 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); 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)); } }
SILGenFunction::OpaqueValueState SILGenFunction::emitOpenExistential( SILLocation loc, ManagedValue existentialValue, ArchetypeType *openedArchetype, SILType loweredOpenedType, AccessKind accessKind) { // Open the existential value into the opened archetype value. bool isUnique = true; bool canConsume; ManagedValue archetypeMV; SILType existentialType = existentialValue.getType(); switch (existentialType.getPreferredExistentialRepresentation(SGM.M)) { case ExistentialRepresentation::Opaque: { SILValue archetypeValue; if (existentialType.isAddress()) { OpenedExistentialAccess allowedAccess = getOpenedExistentialAccessFor(accessKind); if (!loweredOpenedType.isAddress()) { assert(!silConv.useLoweredAddresses() && "Non-address loweredOpenedType is only allowed under opaque " "value mode"); loweredOpenedType = loweredOpenedType.getAddressType(); } archetypeValue = B.createOpenExistentialAddr(loc, existentialValue.forward(*this), loweredOpenedType, allowedAccess); } else { archetypeValue = B.createOpenExistentialOpaque( loc, existentialValue.forward(*this), loweredOpenedType); } if (existentialValue.hasCleanup()) { // With CoW existentials we can't consume the boxed value inside of // the existential. (We could only do so after a uniqueness check on // the box holding the value). canConsume = false; enterDestroyCleanup(existentialValue.getValue()); archetypeMV = ManagedValue::forUnmanaged(archetypeValue); } else { canConsume = false; archetypeMV = ManagedValue::forUnmanaged(archetypeValue); } break; } case ExistentialRepresentation::Metatype: assert(existentialType.isObject()); archetypeMV = ManagedValue::forUnmanaged( B.createOpenExistentialMetatype( loc, existentialValue.forward(*this), loweredOpenedType)); // Metatypes are always trivial. Consuming would be a no-op. canConsume = false; break; case ExistentialRepresentation::Class: { assert(existentialType.isObject()); SILValue archetypeValue = B.createOpenExistentialRef( loc, existentialValue.forward(*this), loweredOpenedType); canConsume = existentialValue.hasCleanup(); archetypeMV = (canConsume ? emitManagedRValueWithCleanup(archetypeValue) : ManagedValue::forUnmanaged(archetypeValue)); break; } case ExistentialRepresentation::Boxed: if (existentialType.isAddress()) { existentialValue = emitLoad(loc, existentialValue.getValue(), getTypeLowering(existentialType), SGFContext::AllowGuaranteedPlusZero, IsNotTake); } existentialType = existentialValue.getType(); assert(existentialType.isObject()); // NB: Don't forward the cleanup, because consuming a boxed value won't // consume the box reference. archetypeMV = ManagedValue::forUnmanaged(B.createOpenExistentialBox( loc, existentialValue.getValue(), loweredOpenedType.getAddressType())); // The boxed value can't be assumed to be uniquely referenced. We can never // consume it. // TODO: We could use isUniquelyReferenced to shorten the duration of // the box to the point that the opaque value is copied out. isUnique = false; canConsume = false; break; case ExistentialRepresentation::None: llvm_unreachable("not existential"); } assert(!canConsume || isUnique); (void) isUnique; return SILGenFunction::OpaqueValueState{ archetypeMV, /*isConsumable*/ canConsume, /*hasBeenConsumed*/ false }; }