| //===--- SILGenConvert.cpp - Type Conversion Routines ---------------------===// |
| // |
| // 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 "SILGen.h" |
| #include "Scope.h" |
| #include "swift/AST/ASTContext.h" |
| #include "swift/AST/Decl.h" |
| #include "swift/AST/ProtocolConformance.h" |
| #include "swift/AST/Types.h" |
| #include "swift/Basic/type_traits.h" |
| #include "swift/SIL/SILArgument.h" |
| #include "swift/SIL/TypeLowering.h" |
| #include "Initialization.h" |
| #include "LValue.h" |
| #include "RValue.h" |
| #include "ArgumentSource.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include "llvm/Support/SaveAndRestore.h" |
| |
| using namespace swift; |
| using namespace Lowering; |
| |
| // FIXME: With some changes to their callers, all of the below functions |
| // could be re-worked to use emitInjectEnum(). |
| ManagedValue |
| SILGenFunction::emitInjectOptional(SILLocation loc, |
| const TypeLowering &optTL, |
| SGFContext ctxt, |
| llvm::function_ref<ManagedValue(SGFContext)> generator) { |
| SILType optTy = optTL.getLoweredType(); |
| SILType objectTy = optTy.getAnyOptionalObjectType(); |
| assert(objectTy && "expected type was not optional"); |
| |
| auto someDecl = getASTContext().getOptionalSomeDecl(); |
| |
| // If the value is loadable, just emit and wrap. |
| // TODO: honor +0 contexts? |
| if (optTL.isLoadable()) { |
| ManagedValue objectResult = generator(SGFContext()); |
| auto some = B.createEnum(loc, objectResult.forward(*this), someDecl, optTy); |
| return emitManagedRValueWithCleanup(some, optTL); |
| } |
| |
| // Otherwise it's address-only; try to avoid spurious copies by |
| // evaluating into the context. |
| |
| // Prepare a buffer for the object value. |
| return B.bufferForExpr( |
| loc, optTy.getObjectType(), optTL, ctxt, |
| [&](SILValue optBuf) { |
| auto objectBuf = B.createInitEnumDataAddr(loc, optBuf, someDecl, objectTy); |
| |
| // Evaluate the value in-place into that buffer. |
| TemporaryInitialization init(objectBuf, CleanupHandle::invalid()); |
| ManagedValue objectResult = generator(SGFContext(&init)); |
| if (!objectResult.isInContext()) { |
| objectResult.forwardInto(*this, loc, objectBuf); |
| } |
| |
| // Finalize the outer optional buffer. |
| B.createInjectEnumAddr(loc, optBuf, someDecl); |
| }); |
| } |
| |
| void SILGenFunction::emitInjectOptionalValueInto(SILLocation loc, |
| ArgumentSource &&value, |
| SILValue dest, |
| const TypeLowering &optTL) { |
| SILType optType = optTL.getLoweredType(); |
| assert(dest->getType() == optType.getAddressType()); |
| auto loweredPayloadTy = optType.getAnyOptionalObjectType(); |
| assert(loweredPayloadTy); |
| |
| // Project out the payload area. |
| auto someDecl = getASTContext().getOptionalSomeDecl(); |
| auto destPayload = |
| B.createInitEnumDataAddr(loc, dest, someDecl, |
| loweredPayloadTy.getAddressType()); |
| |
| // Emit the value into the payload area. |
| TemporaryInitialization emitInto(destPayload, CleanupHandle::invalid()); |
| std::move(value).forwardInto(*this, &emitInto); |
| |
| // Inject the tag. |
| B.createInjectEnumAddr(loc, dest, someDecl); |
| } |
| |
| void SILGenFunction::emitInjectOptionalNothingInto(SILLocation loc, |
| SILValue dest, |
| const TypeLowering &optTL) { |
| assert(optTL.getLoweredType().getAnyOptionalObjectType()); |
| |
| B.createInjectEnumAddr(loc, dest, getASTContext().getOptionalNoneDecl()); |
| } |
| |
| /// Return a value for an optional ".None" of the specified type. This only |
| /// works for loadable enum types. |
| SILValue SILGenFunction::getOptionalNoneValue(SILLocation loc, |
| const TypeLowering &optTL) { |
| assert(optTL.isLoadable() && "Address-only optionals cannot use this"); |
| assert(optTL.getLoweredType().getAnyOptionalObjectType()); |
| |
| return B.createEnum(loc, SILValue(), getASTContext().getOptionalNoneDecl(), |
| optTL.getLoweredType()); |
| } |
| |
| /// Return a value for an optional ".Some(x)" of the specified type. This only |
| /// works for loadable enum types. |
| ManagedValue SILGenFunction:: |
| getOptionalSomeValue(SILLocation loc, ManagedValue value, |
| const TypeLowering &optTL) { |
| assert(optTL.isLoadable() && "Address-only optionals cannot use this"); |
| SILType optType = optTL.getLoweredType(); |
| CanType formalOptType = optType.getSwiftRValueType(); |
| |
| assert(formalOptType.getAnyOptionalObjectType()); |
| auto someDecl = getASTContext().getOptionalSomeDecl(); |
| |
| SILValue result = |
| B.createEnum(loc, value.forward(*this), someDecl, optTL.getLoweredType()); |
| return emitManagedRValueWithCleanup(result, optTL); |
| } |
| |
| static void emitSourceLocationArgs(SILGenFunction &gen, |
| SILLocation loc, |
| ManagedValue (&args)[4]) { |
| auto &ctx = gen.getASTContext(); |
| auto sourceLoc = loc.getSourceLoc(); |
| |
| StringRef filename = ""; |
| unsigned line = 0; |
| if (sourceLoc.isValid()) { |
| unsigned bufferID = ctx.SourceMgr.findBufferContainingLoc(sourceLoc); |
| filename = ctx.SourceMgr.getIdentifierForBuffer(bufferID); |
| line = ctx.SourceMgr.getLineAndColumn(sourceLoc).first; |
| } |
| |
| bool isASCII = true; |
| for (unsigned char c : filename) { |
| if (c > 127) { |
| isASCII = false; |
| break; |
| } |
| } |
| |
| auto wordTy = SILType::getBuiltinWordType(ctx); |
| auto i1Ty = SILType::getBuiltinIntegerType(1, ctx); |
| |
| // File |
| SILValue literal = gen.B.createStringLiteral(loc, filename, |
| StringLiteralInst::Encoding::UTF8); |
| args[0] = ManagedValue::forUnmanaged(literal); |
| // File length |
| literal = gen.B.createIntegerLiteral(loc, wordTy, filename.size()); |
| args[1] = ManagedValue::forUnmanaged(literal); |
| // File is ascii |
| literal = gen.B.createIntegerLiteral(loc, i1Ty, isASCII); |
| args[2] = ManagedValue::forUnmanaged(literal); |
| // Line |
| literal = gen.B.createIntegerLiteral(loc, wordTy, line); |
| args[3] = ManagedValue::forUnmanaged(literal); |
| } |
| |
| 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, {}, 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)); |
| } |
| |
| SILValue SILGenFunction::emitDoesOptionalHaveValue(SILLocation loc, |
| SILValue addrOrValue) { |
| auto boolTy = SILType::getBuiltinIntegerType(1, getASTContext()); |
| SILValue yes = B.createIntegerLiteral(loc, boolTy, 1); |
| SILValue no = B.createIntegerLiteral(loc, boolTy, 0); |
| auto someDecl = getASTContext().getOptionalSomeDecl(); |
| |
| if (addrOrValue->getType().isAddress()) |
| return B.createSelectEnumAddr(loc, addrOrValue, boolTy, no, |
| std::make_pair(someDecl, yes)); |
| return B.createSelectEnum(loc, addrOrValue, boolTy, no, |
| std::make_pair(someDecl, yes)); |
| } |
| |
| ManagedValue SILGenFunction::emitCheckedGetOptionalValueFrom(SILLocation loc, |
| ManagedValue src, |
| const TypeLowering &optTL, |
| SGFContext C) { |
| // TODO: Make this take optTL. |
| return emitPreconditionOptionalHasValue(loc, src); |
| } |
| |
| 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; |
| } |
| |
| /// Emit an optional-to-optional transformation. |
| ManagedValue |
| SILGenFunction::emitOptionalToOptional(SILLocation loc, |
| ManagedValue input, |
| SILType resultTy, |
| ValueTransformRef transformValue) { |
| auto contBB = createBasicBlock(); |
| auto isNotPresentBB = createBasicBlock(); |
| auto isPresentBB = createBasicBlock(); |
| |
| // Create a temporary for the output optional. |
| auto &resultTL = getTypeLowering(resultTy); |
| |
| // If the result is address-only, we need to return something in memory, |
| // otherwise the result is the BBArgument in the merge point. |
| SILValue result; |
| if (resultTL.isAddressOnly()) |
| result = emitTemporaryAllocation(loc, resultTy); |
| else |
| result = contBB->createPHIArgument(resultTL.getLoweredType(), |
| ValueOwnershipKind::Owned); |
| |
| // Branch on whether the input is optional, this doesn't consume the value. |
| auto isPresent = emitDoesOptionalHaveValue(loc, input.getValue()); |
| B.createCondBranch(loc, isPresent, isPresentBB, isNotPresentBB); |
| |
| // If it's present, apply the recursive transformation to the value. |
| B.emitBlock(isPresentBB); |
| SILValue branchArg; |
| { |
| // Don't allow cleanups to escape the conditional block. |
| FullExpr presentScope(Cleanups, CleanupLocation::get(loc)); |
| |
| CanType resultValueTy = |
| resultTy.getSwiftRValueType().getAnyOptionalObjectType(); |
| assert(resultValueTy); |
| SILType loweredResultValueTy = getLoweredType(resultValueTy); |
| |
| // Pull the value out. This will load if the value is not address-only. |
| auto &inputTL = getTypeLowering(input.getType()); |
| auto inputValue = emitUncheckedGetOptionalValueFrom(loc, input, |
| inputTL, SGFContext()); |
| |
| // Transform it. |
| auto resultValue = transformValue(*this, loc, inputValue, |
| loweredResultValueTy); |
| |
| // Inject that into the result type if the result is address-only. |
| if (resultTL.isAddressOnly()) { |
| ArgumentSource resultValueRV(loc, RValue(*this, loc, |
| resultValueTy, resultValue)); |
| emitInjectOptionalValueInto(loc, std::move(resultValueRV), |
| result, resultTL); |
| } else { |
| resultValue = getOptionalSomeValue(loc, resultValue, resultTL); |
| branchArg = resultValue.forward(*this); |
| } |
| } |
| if (branchArg) |
| B.createBranch(loc, contBB, branchArg); |
| else |
| B.createBranch(loc, contBB); |
| |
| // If it's not present, inject 'nothing' into the result. |
| B.emitBlock(isNotPresentBB); |
| if (resultTL.isAddressOnly()) { |
| emitInjectOptionalNothingInto(loc, result, resultTL); |
| B.createBranch(loc, contBB); |
| } else { |
| branchArg = getOptionalNoneValue(loc, resultTL); |
| B.createBranch(loc, contBB, branchArg); |
| } |
| |
| // Continue. |
| B.emitBlock(contBB); |
| if (resultTL.isAddressOnly()) |
| return emitManagedBufferWithCleanup(result, resultTL); |
| |
| return emitManagedRValueWithCleanup(result, resultTL); |
| } |
| |
| SILGenFunction::OpaqueValueRAII::~OpaqueValueRAII() { |
| auto entry = Self.OpaqueValues.find(OpaqueValue); |
| assert(entry != Self.OpaqueValues.end()); |
| Self.OpaqueValues.erase(entry); |
| } |
| |
| RValue |
| SILGenFunction::emitPointerToPointer(SILLocation loc, |
| ManagedValue input, |
| CanType inputType, |
| CanType outputType, |
| SGFContext C) { |
| auto converter = getASTContext().getConvertPointerToPointerArgument(nullptr); |
| |
| // The generic function currently always requires indirection, but pointers |
| // are always loadable. |
| auto origBuf = emitTemporaryAllocation(loc, input.getType()); |
| B.emitStoreValueOperation(loc, input.forward(*this), origBuf, |
| StoreOwnershipQualifier::Init); |
| auto origValue = emitManagedBufferWithCleanup(origBuf); |
| |
| // Invoke the conversion intrinsic to convert to the destination type. |
| Substitution subs[2] = { |
| getPointerSubstitution(inputType), |
| getPointerSubstitution(outputType), |
| }; |
| |
| return emitApplyOfLibraryIntrinsic(loc, converter, subs, origValue, C); |
| } |
| |
| |
| namespace { |
| |
| /// This is an initialization for an address-only existential in memory. |
| class ExistentialInitialization : public KnownAddressInitialization { |
| CleanupHandle Cleanup; |
| public: |
| /// \param existential The existential container |
| /// \param address Address of value in existential container |
| /// \param concreteFormalType Unlowered AST type of value |
| /// \param repr Representation of container |
| ExistentialInitialization(SILValue existential, SILValue address, |
| CanType concreteFormalType, |
| ExistentialRepresentation repr, |
| SILGenFunction &gen) |
| : KnownAddressInitialization(address) { |
| // Any early exit before we store a value into the existential must |
| // clean up the existential container. |
| Cleanup = gen.enterDeinitExistentialCleanup(existential, |
| concreteFormalType, |
| repr); |
| } |
| |
| void finishInitialization(SILGenFunction &gen) override { |
| SingleBufferInitialization::finishInitialization(gen); |
| gen.Cleanups.setCleanupState(Cleanup, CleanupState::Dead); |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| ManagedValue SILGenFunction::emitExistentialErasure( |
| SILLocation loc, |
| CanType concreteFormalType, |
| const TypeLowering &concreteTL, |
| const TypeLowering &existentialTL, |
| ArrayRef<ProtocolConformanceRef> conformances, |
| SGFContext C, |
| llvm::function_ref<ManagedValue (SGFContext)> F, |
| bool allowEmbeddedNSError) { |
| // Mark the needed conformances as used. |
| for (auto conformance : conformances) |
| SGM.useConformance(conformance); |
| |
| // If we're erasing to the 'Error' type, we might be able to get an NSError |
| // representation more efficiently. |
| auto &ctx = getASTContext(); |
| if (ctx.LangOpts.EnableObjCInterop && conformances.size() == 1 && |
| conformances[0].getRequirement() == ctx.getErrorDecl() && |
| ctx.getNSErrorDecl()) { |
| auto nsErrorDecl = ctx.getNSErrorDecl(); |
| |
| // If the concrete type is NSError or a subclass thereof, just erase it |
| // directly. |
| auto nsErrorType = nsErrorDecl->getDeclaredType()->getCanonicalType(); |
| if (nsErrorType->isExactSuperclassOf(concreteFormalType, nullptr)) { |
| ManagedValue nsError = F(SGFContext()); |
| if (nsErrorType != concreteFormalType) { |
| nsError = ManagedValue(B.createUpcast(loc, nsError.getValue(), |
| getLoweredType(nsErrorType)), |
| nsError.getCleanup()); |
| } |
| return emitBridgedToNativeError(loc, nsError); |
| } |
| |
| // If the concrete type is known to conform to _BridgedStoredNSError, |
| // call the _nsError witness getter to extract the NSError directly, |
| // then just erase the NSError. |
| if (auto storedNSErrorConformance = |
| SGM.getConformanceToBridgedStoredNSError(loc, concreteFormalType)) { |
| auto nsErrorVar = SGM.getNSErrorRequirement(loc); |
| if (!nsErrorVar) return emitUndef(loc, existentialTL.getLoweredType()); |
| |
| SubstitutionList nsErrorVarSubstitutions; |
| |
| // Devirtualize. Maybe this should be done implicitly by |
| // emitPropertyLValue? |
| if (storedNSErrorConformance->isConcrete()) { |
| if (auto witnessVar = storedNSErrorConformance->getConcrete() |
| ->getWitness(nsErrorVar, nullptr)) { |
| nsErrorVar = cast<VarDecl>(witnessVar.getDecl()); |
| nsErrorVarSubstitutions = witnessVar.getSubstitutions(); |
| } |
| } |
| |
| auto nativeError = F(SGFContext()); |
| |
| FormalEvaluationScope writebackScope(*this); |
| auto nsError = |
| emitRValueForPropertyLoad(loc, nativeError, concreteFormalType, |
| /*super*/ false, nsErrorVar, |
| nsErrorVarSubstitutions, |
| AccessSemantics::Ordinary, nsErrorType, |
| SGFContext()) |
| .getAsSingleValue(*this, loc); |
| |
| return emitBridgedToNativeError(loc, nsError); |
| } |
| |
| // Otherwise, if it's an archetype, try calling the _getEmbeddedNSError() |
| // witness to try to dig out the embedded NSError. But don't do this |
| // when we're being called recursively. |
| if (isa<ArchetypeType>(concreteFormalType) && allowEmbeddedNSError) { |
| auto contBB = createBasicBlock(); |
| auto isNotPresentBB = createBasicBlock(); |
| auto isPresentBB = createBasicBlock(); |
| |
| // Call swift_stdlib_getErrorEmbeddedNSError to attempt to extract an |
| // NSError from the value. |
| auto getEmbeddedNSErrorFn = SGM.getGetErrorEmbeddedNSError(loc); |
| if (!getEmbeddedNSErrorFn) |
| return emitUndef(loc, existentialTL.getLoweredType()); |
| |
| Substitution getEmbeddedNSErrorSubstitutions[1] = { |
| Substitution(concreteFormalType, conformances) |
| }; |
| |
| ManagedValue concreteValue = F(SGFContext()); |
| ManagedValue potentialNSError = |
| emitApplyOfLibraryIntrinsic(loc, |
| getEmbeddedNSErrorFn, |
| getEmbeddedNSErrorSubstitutions, |
| { concreteValue.copy(*this, loc) }, |
| SGFContext()) |
| .getAsSingleValue(*this, loc); |
| |
| // We're going to consume 'concreteValue' in exactly one branch, |
| // so kill its cleanup now and recreate it on both branches. |
| (void) concreteValue.forward(*this); |
| |
| // Check whether we got an NSError back. |
| std::pair<EnumElementDecl*, SILBasicBlock*> cases[] = { |
| { ctx.getOptionalSomeDecl(), isPresentBB }, |
| { ctx.getOptionalNoneDecl(), isNotPresentBB } |
| }; |
| B.createSwitchEnum(loc, potentialNSError.forward(*this), |
| /*default*/ nullptr, cases); |
| |
| // If we did get an NSError, emit the existential erasure from that |
| // NSError. |
| B.emitBlock(isPresentBB); |
| SILValue branchArg; |
| { |
| // Don't allow cleanups to escape the conditional block. |
| FullExpr presentScope(Cleanups, CleanupLocation::get(loc)); |
| enterDestroyCleanup(concreteValue.getValue()); |
| |
| // Receive the error value. It's typed as an 'AnyObject' for |
| // layering reasons, so perform an unchecked cast down to NSError. |
| SILType anyObjectTy = |
| potentialNSError.getType().getAnyOptionalObjectType(); |
| SILValue nsError = isPresentBB->createPHIArgument( |
| anyObjectTy, ValueOwnershipKind::Owned); |
| nsError = B.createUncheckedRefCast(loc, nsError, |
| getLoweredType(nsErrorType)); |
| |
| branchArg = emitBridgedToNativeError(loc, |
| emitManagedRValueWithCleanup(nsError)) |
| .forward(*this); |
| } |
| B.createBranch(loc, contBB, branchArg); |
| |
| // If we did not get an NSError, just directly emit the existential. |
| // Since this is a recursive call, make sure we don't end up in this |
| // path again. |
| B.emitBlock(isNotPresentBB); |
| { |
| FullExpr presentScope(Cleanups, CleanupLocation::get(loc)); |
| concreteValue = emitManagedRValueWithCleanup(concreteValue.getValue()); |
| branchArg = emitExistentialErasure(loc, concreteFormalType, concreteTL, |
| existentialTL, conformances, |
| SGFContext(), |
| [&](SGFContext C) { |
| return concreteValue; |
| }, |
| /*allowEmbeddedNSError=*/false) |
| .forward(*this); |
| } |
| B.createBranch(loc, contBB, branchArg); |
| |
| // Continue. |
| B.emitBlock(contBB); |
| |
| SILValue existentialResult = contBB->createPHIArgument( |
| existentialTL.getLoweredType(), ValueOwnershipKind::Owned); |
| return emitManagedRValueWithCleanup(existentialResult, existentialTL); |
| } |
| } |
| |
| switch (existentialTL.getLoweredType().getObjectType() |
| .getPreferredExistentialRepresentation(SGM.M, concreteFormalType)) { |
| case ExistentialRepresentation::None: |
| llvm_unreachable("not an existential type"); |
| case ExistentialRepresentation::Metatype: { |
| assert(existentialTL.isLoadable()); |
| |
| SILValue metatype = F(SGFContext()).getUnmanagedValue(); |
| assert(metatype->getType().castTo<AnyMetatypeType>()->getRepresentation() |
| == MetatypeRepresentation::Thick); |
| |
| auto upcast = |
| B.createInitExistentialMetatype(loc, metatype, |
| existentialTL.getLoweredType(), |
| conformances); |
| return ManagedValue::forUnmanaged(upcast); |
| } |
| case ExistentialRepresentation::Class: { |
| assert(existentialTL.isLoadable()); |
| |
| ManagedValue sub = F(SGFContext()); |
| SILValue v = B.createInitExistentialRef(loc, |
| existentialTL.getLoweredType(), |
| concreteFormalType, |
| sub.getValue(), |
| conformances); |
| return ManagedValue(v, sub.getCleanup()); |
| } |
| case ExistentialRepresentation::Boxed: { |
| // Allocate the existential. |
| auto *existential = B.createAllocExistentialBox(loc, |
| existentialTL.getLoweredType(), |
| concreteFormalType, |
| conformances); |
| auto *valueAddr = B.createProjectExistentialBox(loc, |
| concreteTL.getLoweredType(), |
| existential); |
| // Initialize the concrete value in-place. |
| ExistentialInitialization init(existential, valueAddr, concreteFormalType, |
| ExistentialRepresentation::Boxed, *this); |
| ManagedValue mv = F(SGFContext(&init)); |
| if (!mv.isInContext()) { |
| mv.forwardInto(*this, loc, init.getAddress()); |
| init.finishInitialization(*this); |
| } |
| |
| return emitManagedRValueWithCleanup(existential); |
| } |
| case ExistentialRepresentation::Opaque: { |
| |
| // If the concrete value is a pseudogeneric archetype, first erase it to |
| // its upper bound. |
| auto anyObjectProto = getASTContext() |
| .getProtocol(KnownProtocolKind::AnyObject); |
| auto anyObjectTy = anyObjectProto |
| ? anyObjectProto->getDeclaredType()->getCanonicalType() |
| : CanType(); |
| auto eraseToAnyObject = |
| [&, concreteFormalType, F](SGFContext C) -> ManagedValue { |
| auto concreteValue = F(SGFContext()); |
| auto anyObjectConformance = SGM.SwiftModule |
| ->lookupConformance(concreteFormalType, anyObjectProto, nullptr); |
| ProtocolConformanceRef buf[] = { |
| *anyObjectConformance, |
| }; |
| |
| auto asAnyObject = B.createInitExistentialRef(loc, |
| SILType::getPrimitiveObjectType(anyObjectTy), |
| concreteFormalType, |
| concreteValue.getValue(), |
| getASTContext().AllocateCopy(buf)); |
| return ManagedValue(asAnyObject, concreteValue.getCleanup()); |
| }; |
| |
| auto concreteTLPtr = &concreteTL; |
| if (this->F.getLoweredFunctionType()->isPseudogeneric()) { |
| if (anyObjectTy && concreteFormalType->is<ArchetypeType>()) { |
| concreteFormalType = anyObjectTy; |
| concreteTLPtr = &getTypeLowering(anyObjectTy); |
| F = eraseToAnyObject; |
| } |
| } |
| |
| if (!C.getEmitInto() && !silConv.useLoweredAddresses()) { |
| // We should never create new buffers just for init_existential under |
| // opaque values mode: This is a case of an opaque value that we can |
| // "treat" as a by-value one |
| ManagedValue sub = F(SGFContext()); |
| SILValue v = B.createInitExistentialOpaque( |
| loc, existentialTL.getLoweredType(), concreteFormalType, |
| sub.getValue(), conformances); |
| return ManagedValue(v, sub.getCleanup()); |
| } |
| |
| // Allocate the existential. |
| return B.bufferForExpr( |
| loc, existentialTL.getLoweredType(), existentialTL, C, |
| [&](SILValue existential) { |
| // Allocate the concrete value inside the container. |
| SILValue valueAddr = B.createInitExistentialAddr( |
| loc, existential, |
| concreteFormalType, |
| concreteTLPtr->getLoweredType(), |
| conformances); |
| // Initialize the concrete value in-place. |
| InitializationPtr init( |
| new ExistentialInitialization(existential, valueAddr, concreteFormalType, |
| ExistentialRepresentation::Opaque, |
| *this)); |
| ManagedValue mv = F(SGFContext(init.get())); |
| if (!mv.isInContext()) { |
| mv.forwardInto(*this, loc, init->getAddress()); |
| init->finishInitialization(*this); |
| } |
| }); |
| } |
| } |
| |
| llvm_unreachable("Unhandled ExistentialRepresentation in switch."); |
| } |
| |
| ManagedValue SILGenFunction::emitClassMetatypeToObject(SILLocation loc, |
| ManagedValue v, |
| SILType resultTy) { |
| SILValue value = v.getUnmanagedValue(); |
| |
| // Convert the metatype to objc representation. |
| auto metatypeTy = value->getType().castTo<MetatypeType>(); |
| auto objcMetatypeTy = CanMetatypeType::get(metatypeTy.getInstanceType(), |
| MetatypeRepresentation::ObjC); |
| value = B.createThickToObjCMetatype(loc, value, |
| SILType::getPrimitiveObjectType(objcMetatypeTy)); |
| |
| // Convert to an object reference. |
| value = B.createObjCMetatypeToObject(loc, value, resultTy); |
| return emitManagedRValueWithCleanup(value); |
| } |
| |
| ManagedValue SILGenFunction::emitExistentialMetatypeToObject(SILLocation loc, |
| ManagedValue v, |
| SILType resultTy) { |
| SILValue value = v.getUnmanagedValue(); |
| |
| // Convert the metatype to objc representation. |
| auto metatypeTy = value->getType().castTo<ExistentialMetatypeType>(); |
| auto objcMetatypeTy = CanExistentialMetatypeType::get( |
| metatypeTy.getInstanceType(), |
| MetatypeRepresentation::ObjC); |
| value = B.createThickToObjCMetatype(loc, value, |
| SILType::getPrimitiveObjectType(objcMetatypeTy)); |
| |
| // Convert to an object reference. |
| value = B.createObjCExistentialMetatypeToObject(loc, value, resultTy); |
| |
| return emitManagedRValueWithCleanup(value); |
| } |
| |
| ManagedValue SILGenFunction::emitProtocolMetatypeToObject(SILLocation loc, |
| CanType inputTy, |
| SILType resultTy) { |
| ProtocolDecl *protocol = inputTy->castTo<MetatypeType>() |
| ->getInstanceType()->castTo<ProtocolType>()->getDecl(); |
| |
| SILValue value = B.createObjCProtocol(loc, protocol, resultTy); |
| |
| // Protocol objects, despite being global objects, inherit default reference |
| // counting semantics from NSObject, so we need to retain the protocol |
| // reference when we use it to prevent it being released and attempting to |
| // deallocate itself. It doesn't matter if we ever actually clean up that |
| // retain though. |
| value = B.createCopyValue(loc, value); |
| return emitManagedRValueWithCleanup(value); |
| } |
| |
| SILGenFunction::OpaqueValueState |
| SILGenFunction::emitOpenExistential( |
| SILLocation loc, |
| ManagedValue existentialValue, |
| CanArchetypeType 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); |
| archetypeValue = |
| B.createOpenExistentialAddr(loc, existentialValue.forward(*this), |
| loweredOpenedType, allowedAccess); |
| } else { |
| archetypeValue = B.createOpenExistentialOpaque( |
| 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); |
| } |
| 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 |
| }; |
| } |
| |
| ManagedValue SILGenFunction::manageOpaqueValue(OpaqueValueState &entry, |
| SILLocation loc, |
| SGFContext C) { |
| // If the opaque value is consumable, we can just return the |
| // value with a cleanup. There is no need to retain it separately. |
| if (entry.IsConsumable) { |
| assert(!entry.HasBeenConsumed |
| && "Uniquely-referenced opaque value already consumed"); |
| entry.HasBeenConsumed = true; |
| return entry.Value; |
| } |
| |
| assert(!entry.Value.hasCleanup()); |
| |
| // If the context wants a +0 value, guaranteed or immediate, we can |
| // give it to them, because OpenExistential emission guarantees the |
| // value. |
| if (C.isGuaranteedPlusZeroOk()) { |
| return entry.Value; |
| } |
| |
| // If the context wants us to initialize a buffer, copy there instead |
| // of making a temporary allocation. |
| if (auto I = C.getEmitInto()) { |
| if (SILValue address = I->getAddressForInPlaceInitialization()) { |
| entry.Value.copyInto(*this, address, loc); |
| I->finishInitialization(*this); |
| return ManagedValue::forInContext(); |
| } |
| } |
| |
| // Otherwise, copy the value into a temporary. |
| return entry.Value.copyUnmanaged(*this, loc); |
| } |