| //===--- SILGenPoly.cpp - Function Type Thunks ----------------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Swift function types can be equivalent or have a subtyping relationship even |
| // if the SIL-level lowering of the calling convention is different. The |
| // routines in this file implement thunking between lowered function types. |
| // |
| // |
| // Re-abstraction thunks |
| // ===================== |
| // After SIL type lowering, generic substitutions become explicit, for example |
| // the AST type (Int) -> Int passes the Ints directly, whereas (T) -> T with Int |
| // substituted for T will pass the Ints like a T, as an address-only value with |
| // opaque type metadata. Such a thunk is called a "re-abstraction thunk" -- the |
| // AST-level type of the function value does not change, only the manner in |
| // which parameters and results are passed. See the comment in |
| // AbstractionPattern.h for details. |
| // |
| // Function conversion thunks |
| // ========================== |
| // In Swift's AST-level type system, certain types have a subtype relation |
| // involving a representation change. For example, a concrete type is always |
| // a subtype of any protocol it conforms to. The upcast from the concrete |
| // type to an existential type for the protocol requires packaging the |
| // payload together with type metadata and witness tables. |
| // |
| // Between function types, the type (A) -> B is defined to be a subtype of |
| // (A') -> B' iff A' is a subtype of A, and B is a subtype of B' -- parameters |
| // are contravariant, and results are covariant. |
| // |
| // A subtype conversion of a function value (A) -> B is performed by wrapping |
| // the function value in a thunk of type (A') -> B'. The thunk takes an A' and |
| // converts it into an A, calls the inner function value, and converts the |
| // result from B to B'. |
| // |
| // VTable thunks |
| // ============= |
| // |
| // If a base class is generic and a derived class substitutes some generic |
| // parameter of the base with a concrete type, the derived class can override |
| // methods in the base that involved generic types. In the derived class, a |
| // method override that involves substituted types will have a different |
| // SIL lowering than the base method. In this case, the overridden vtable entry |
| // will point to a thunk which transforms parameters and results and invokes |
| // the derived method. |
| // |
| // Some limited forms of subtyping are also supported for method overrides; |
| // namely, a derived method's parameter can be a superclass of, or more |
| // optional than, a parameter of the base, and result can be a subclass of, |
| // or less optional than, the result of the base. |
| // |
| // Witness thunks |
| // ============== |
| // |
| // Protocol witness methods are called with an additional generic parameter |
| // bound to the Self type, and thus always require a thunk. Thunks are also |
| // required for conditional conformances, since the extra requirements aren't |
| // part of the protocol and so any witness tables need to be loaded from the |
| // original protocol's witness table and passed into the real witness method. |
| // |
| // Thunks for class method witnesses dispatch through the vtable allowing |
| // inherited witnesses to be overridden in subclasses. Hence a witness thunk |
| // might require two levels of abstraction difference -- the method might |
| // override a base class method with more generic types, and the protocol |
| // requirement may involve associated types which are always concrete in the |
| // conforming class. |
| // |
| // Other thunks |
| // ============ |
| // |
| // Foreign-to-native, native-to-foreign thunks for declarations and function |
| // values are implemented in SILGenBridging.cpp. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "Initialization.h" |
| #include "LValue.h" |
| #include "RValue.h" |
| #include "SILGen.h" |
| #include "SILGenFunction.h" |
| #include "SILGenFunctionBuilder.h" |
| #include "Scope.h" |
| #include "swift/AST/ASTMangler.h" |
| #include "swift/AST/Decl.h" |
| #include "swift/AST/DiagnosticsSIL.h" |
| #include "swift/AST/ExistentialLayout.h" |
| #include "swift/AST/GenericEnvironment.h" |
| #include "swift/AST/ProtocolConformance.h" |
| #include "swift/AST/TypeCheckRequests.h" |
| #include "swift/AST/Types.h" |
| #include "swift/SIL/PrettyStackTrace.h" |
| #include "swift/SIL/SILArgument.h" |
| #include "swift/SIL/TypeLowering.h" |
| #include "llvm/Support/Compiler.h" |
| |
| using namespace swift; |
| using namespace Lowering; |
| |
| |
| /// A helper function that pulls an element off the front of an array. |
| template <class T> |
| static const T &claimNext(ArrayRef<T> &array) { |
| assert(!array.empty() && "claiming next from empty array!"); |
| const T &result = array.front(); |
| array = array.slice(1); |
| return result; |
| } |
| |
| namespace { |
| /// An abstract class for transforming first-class SIL values. |
| class Transform { |
| private: |
| SILGenFunction &SGF; |
| SILLocation Loc; |
| |
| public: |
| Transform(SILGenFunction &SGF, SILLocation loc) : SGF(SGF), Loc(loc) {} |
| virtual ~Transform() = default; |
| |
| /// Transform an arbitrary value. |
| RValue transform(RValue &&input, |
| AbstractionPattern inputOrigType, |
| CanType inputSubstType, |
| AbstractionPattern outputOrigType, |
| CanType outputSubstType, |
| SILType outputLoweredTy, |
| SGFContext ctxt); |
| |
| /// Transform an arbitrary value. |
| ManagedValue transform(ManagedValue input, |
| AbstractionPattern inputOrigType, |
| CanType inputSubstType, |
| AbstractionPattern outputOrigType, |
| CanType outputSubstType, |
| SILType outputLoweredTy, |
| SGFContext ctxt); |
| |
| /// Transform a metatype value. |
| ManagedValue transformMetatype(ManagedValue fn, |
| AbstractionPattern inputOrigType, |
| CanMetatypeType inputSubstType, |
| AbstractionPattern outputOrigType, |
| CanMetatypeType outputSubstType, |
| SILType outputLoweredTy); |
| |
| /// Transform a tuple value. |
| ManagedValue transformTuple(ManagedValue input, |
| AbstractionPattern inputOrigType, |
| CanTupleType inputSubstType, |
| AbstractionPattern outputOrigType, |
| CanTupleType outputSubstType, |
| SILType outputLoweredTy, |
| SGFContext ctxt); |
| |
| /// Transform a function value. |
| ManagedValue transformFunction(ManagedValue fn, |
| AbstractionPattern inputOrigType, |
| CanAnyFunctionType inputSubstType, |
| AbstractionPattern outputOrigType, |
| CanAnyFunctionType outputSubstType, |
| const TypeLowering &expectedTL); |
| }; |
| } // end anonymous namespace |
| ; |
| |
| static ArrayRef<ProtocolConformanceRef> |
| collectExistentialConformances(ModuleDecl *M, CanType fromType, CanType toType) { |
| assert(!fromType.isAnyExistentialType()); |
| |
| auto layout = toType.getExistentialLayout(); |
| auto protocols = layout.getProtocols(); |
| |
| SmallVector<ProtocolConformanceRef, 4> conformances; |
| for (auto proto : protocols) { |
| auto conformance = |
| M->lookupConformance(fromType, proto->getDecl()); |
| assert(conformance); |
| conformances.push_back(conformance); |
| } |
| |
| return M->getASTContext().AllocateCopy(conformances); |
| } |
| |
| static ManagedValue emitTransformExistential(SILGenFunction &SGF, |
| SILLocation loc, |
| ManagedValue input, |
| CanType inputType, |
| CanType outputType, |
| SGFContext ctxt) { |
| assert(inputType != outputType); |
| |
| FormalEvaluationScope scope(SGF); |
| |
| if (inputType->isAnyExistentialType()) { |
| CanType openedType = OpenedArchetypeType::getAny(inputType); |
| SILType loweredOpenedType = SGF.getLoweredType(openedType); |
| |
| input = SGF.emitOpenExistential(loc, input, |
| loweredOpenedType, AccessKind::Read); |
| inputType = openedType; |
| } |
| |
| // Build conformance table |
| CanType fromInstanceType = inputType; |
| CanType toInstanceType = outputType; |
| |
| // Look through metatypes |
| while (isa<MetatypeType>(fromInstanceType) && |
| isa<ExistentialMetatypeType>(toInstanceType)) { |
| fromInstanceType = cast<MetatypeType>(fromInstanceType) |
| .getInstanceType(); |
| toInstanceType = cast<ExistentialMetatypeType>(toInstanceType) |
| .getInstanceType(); |
| } |
| |
| ArrayRef<ProtocolConformanceRef> conformances = |
| collectExistentialConformances(SGF.SGM.M.getSwiftModule(), |
| fromInstanceType, |
| toInstanceType); |
| |
| // Build result existential |
| AbstractionPattern opaque = AbstractionPattern::getOpaque(); |
| const TypeLowering &concreteTL = SGF.getTypeLowering(opaque, inputType); |
| const TypeLowering &expectedTL = SGF.getTypeLowering(outputType); |
| return SGF.emitExistentialErasure( |
| loc, inputType, concreteTL, expectedTL, |
| conformances, ctxt, |
| [&](SGFContext C) -> ManagedValue { |
| return SGF.manageOpaqueValue(input, loc, C); |
| }); |
| } |
| |
| /// Apply this transformation to an arbitrary value. |
| RValue Transform::transform(RValue &&input, |
| AbstractionPattern inputOrigType, |
| CanType inputSubstType, |
| AbstractionPattern outputOrigType, |
| CanType outputSubstType, |
| SILType outputLoweredTy, |
| SGFContext ctxt) { |
| // Fast path: we don't have a tuple. |
| auto inputTupleType = dyn_cast<TupleType>(inputSubstType); |
| if (!inputTupleType) { |
| assert(!isa<TupleType>(outputSubstType) && |
| "transformation introduced a tuple?"); |
| auto result = transform(std::move(input).getScalarValue(), |
| inputOrigType, inputSubstType, |
| outputOrigType, outputSubstType, |
| outputLoweredTy, ctxt); |
| return RValue(SGF, Loc, outputSubstType, result); |
| } |
| |
| // Okay, we have a tuple. The output type will also be a tuple unless |
| // there's a subtyping conversion that erases tuples, but that's currently |
| // not allowed by the typechecker, which considers existential erasure to |
| // be a conversion relation, not a subtyping one. Anyway, it would be |
| // possible to support that here, but since it's not currently required... |
| assert(isa<TupleType>(outputSubstType) && |
| "subtype constraint erasing tuple is not currently implemented"); |
| auto outputTupleType = cast<TupleType>(outputSubstType); |
| assert(inputTupleType->getNumElements() == outputTupleType->getNumElements()); |
| assert(outputLoweredTy.is<TupleType>() && |
| "expected lowered output type wasn't a tuple when formal type was"); |
| assert(outputLoweredTy.castTo<TupleType>()->getNumElements() == |
| outputTupleType->getNumElements()); |
| |
| // Pull the r-value apart. |
| SmallVector<RValue, 8> inputElts; |
| std::move(input).extractElements(inputElts); |
| |
| // Emit into the context initialization if it's present and possible |
| // to split. |
| SmallVector<InitializationPtr, 4> eltInitsBuffer; |
| MutableArrayRef<InitializationPtr> eltInits; |
| auto tupleInit = ctxt.getEmitInto(); |
| if (!ctxt.getEmitInto() |
| || !ctxt.getEmitInto()->canSplitIntoTupleElements()) { |
| tupleInit = nullptr; |
| } else { |
| eltInits = tupleInit->splitIntoTupleElements(SGF, Loc, outputTupleType, |
| eltInitsBuffer); |
| } |
| |
| // At this point, if tupleInit is non-null, we must emit all of the |
| // elements into their corresponding contexts. |
| assert(tupleInit == nullptr || |
| eltInits.size() == inputTupleType->getNumElements()); |
| |
| SmallVector<ManagedValue, 8> outputExpansion; |
| for (auto eltIndex : indices(inputTupleType->getElementTypes())) { |
| // Determine the appropriate context for the element. |
| SGFContext eltCtxt; |
| if (tupleInit) eltCtxt = SGFContext(eltInits[eltIndex].get()); |
| |
| // Recurse. |
| RValue outputElt = transform(std::move(inputElts[eltIndex]), |
| inputOrigType.getTupleElementType(eltIndex), |
| inputTupleType.getElementType(eltIndex), |
| outputOrigType.getTupleElementType(eltIndex), |
| outputTupleType.getElementType(eltIndex), |
| outputLoweredTy.getTupleElementType(eltIndex), |
| eltCtxt); |
| |
| // Force the r-value into its context if necessary. |
| assert(!outputElt.isInContext() || tupleInit != nullptr); |
| if (tupleInit && !outputElt.isInContext()) { |
| std::move(outputElt).forwardInto(SGF, Loc, eltInits[eltIndex].get()); |
| } else { |
| std::move(outputElt).getAll(outputExpansion); |
| } |
| } |
| |
| // If we emitted into context, be sure to finish the overall initialization. |
| if (tupleInit) { |
| tupleInit->finishInitialization(SGF); |
| return RValue::forInContext(); |
| } |
| |
| return RValue(SGF, outputExpansion, outputTupleType); |
| } |
| |
| // Single @objc protocol value metatypes can be converted to the ObjC |
| // Protocol class type. |
| static bool isProtocolClass(Type t) { |
| auto classDecl = t->getClassOrBoundGenericClass(); |
| if (!classDecl) |
| return false; |
| |
| ASTContext &ctx = classDecl->getASTContext(); |
| return (classDecl->getName() == ctx.Id_Protocol && |
| classDecl->getModuleContext()->getName() == ctx.Id_ObjectiveC); |
| }; |
| |
| static ManagedValue emitManagedLoad(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue addr, |
| const TypeLowering &addrTL) { |
| // SEMANTIC ARC TODO: When the verifier is finished, revisit this. |
| if (!addr.hasCleanup()) |
| return SGF.B.createLoadBorrow(loc, addr); |
| |
| auto loadedValue = addrTL.emitLoad(SGF.B, loc, addr.forward(SGF), |
| LoadOwnershipQualifier::Take); |
| return SGF.emitManagedRValueWithCleanup(loadedValue, addrTL); |
| } |
| |
| /// Apply this transformation to an arbitrary value. |
| ManagedValue Transform::transform(ManagedValue v, |
| AbstractionPattern inputOrigType, |
| CanType inputSubstType, |
| AbstractionPattern outputOrigType, |
| CanType outputSubstType, |
| SILType loweredResultTy, |
| SGFContext ctxt) { |
| // Load if the result isn't address-only. All the translation routines |
| // expect this. |
| if (v.getType().isAddress()) { |
| auto &inputTL = SGF.getTypeLowering(v.getType()); |
| if (!inputTL.isAddressOnly()) { |
| v = emitManagedLoad(SGF, Loc, v, inputTL); |
| } |
| } |
| |
| // Downstream code expects the lowered result type to be an object if |
| // it's loadable, so make sure that's satisfied. |
| auto &expectedTL = SGF.getTypeLowering(loweredResultTy); |
| loweredResultTy = expectedTL.getLoweredType(); |
| |
| // Nothing to convert |
| if (v.getType() == loweredResultTy) |
| return v; |
| |
| CanType inputObjectType = inputSubstType.getOptionalObjectType(); |
| bool inputIsOptional = (bool) inputObjectType; |
| |
| CanType outputObjectType = outputSubstType.getOptionalObjectType(); |
| bool outputIsOptional = (bool) outputObjectType; |
| |
| // If the value is less optional than the desired formal type, wrap in |
| // an optional. |
| if (outputIsOptional && !inputIsOptional) { |
| return SGF.emitInjectOptional( |
| Loc, expectedTL, ctxt, [&](SGFContext objectCtxt) { |
| return transform(v, inputOrigType, inputSubstType, |
| outputOrigType.getOptionalObjectType(), |
| outputObjectType, |
| loweredResultTy.getOptionalObjectType(), |
| objectCtxt); |
| }); |
| } |
| |
| // If the value is an optional, but the desired formal type isn't an |
| // optional or Any, force it. |
| if (inputIsOptional && !outputIsOptional && |
| !outputSubstType->isExistentialType()) { |
| // isImplicitUnwrap is hardcoded true because the looseness in types of |
| // @objc witnesses/overrides that we're handling here only allows IUOs, |
| // not explicit Optionals. |
| v = SGF.emitCheckedGetOptionalValueFrom(Loc, v, |
| /*isImplicitUnwrap*/ true, |
| SGF.getTypeLowering(v.getType()), |
| SGFContext()); |
| |
| // Check if we have any more conversions remaining. |
| if (v.getType() == loweredResultTy) |
| return v; |
| |
| inputIsOptional = false; |
| } |
| |
| // Optional-to-optional conversion. |
| if (inputIsOptional && outputIsOptional) { |
| // If the conversion is trivial, just cast. |
| if (SGF.SGM.Types.checkForABIDifferences(SGF.SGM.M, |
| v.getType(), loweredResultTy) |
| == TypeConverter::ABIDifference::CompatibleRepresentation) { |
| if (v.getType().isAddress()) |
| return SGF.B.createUncheckedAddrCast(Loc, v, loweredResultTy); |
| return SGF.B.createUncheckedBitCast(Loc, v, loweredResultTy); |
| } |
| |
| auto transformOptionalPayload = |
| [&](SILGenFunction &SGF, SILLocation loc, ManagedValue input, |
| SILType loweredResultTy, SGFContext context) -> ManagedValue { |
| return transform(input, inputOrigType.getOptionalObjectType(), |
| inputObjectType, outputOrigType.getOptionalObjectType(), |
| outputObjectType, loweredResultTy, context); |
| }; |
| |
| return SGF.emitOptionalToOptional(Loc, v, loweredResultTy, |
| transformOptionalPayload); |
| } |
| |
| // Abstraction changes: |
| |
| // - functions |
| if (auto outputFnType = dyn_cast<AnyFunctionType>(outputSubstType)) { |
| auto inputFnType = cast<AnyFunctionType>(inputSubstType); |
| return transformFunction(v, |
| inputOrigType, inputFnType, |
| outputOrigType, outputFnType, |
| expectedTL); |
| } |
| |
| // - tuples of transformable values |
| if (auto outputTupleType = dyn_cast<TupleType>(outputSubstType)) { |
| auto inputTupleType = cast<TupleType>(inputSubstType); |
| return transformTuple(v, |
| inputOrigType, inputTupleType, |
| outputOrigType, outputTupleType, |
| loweredResultTy, ctxt); |
| } |
| |
| // - metatypes |
| if (auto outputMetaType = dyn_cast<MetatypeType>(outputSubstType)) { |
| if (auto inputMetaType = dyn_cast<MetatypeType>(inputSubstType)) { |
| return transformMetatype(v, |
| inputOrigType, inputMetaType, |
| outputOrigType, outputMetaType, |
| loweredResultTy); |
| } |
| } |
| |
| // Subtype conversions: |
| |
| // A base class method returning Self can be used in place of a derived |
| // class method returning Self. |
| if (auto inputSelfType = dyn_cast<DynamicSelfType>(inputSubstType)) { |
| inputSubstType = inputSelfType.getSelfType(); |
| } |
| |
| // - casts for classes |
| if (outputSubstType->getClassOrBoundGenericClass() && |
| inputSubstType->getClassOrBoundGenericClass()) { |
| auto class1 = inputSubstType->getClassOrBoundGenericClass(); |
| auto class2 = outputSubstType->getClassOrBoundGenericClass(); |
| |
| // CF <-> Objective-C via toll-free bridging. |
| if ((class1->getForeignClassKind() == ClassDecl::ForeignKind::CFType) ^ |
| (class2->getForeignClassKind() == ClassDecl::ForeignKind::CFType)) { |
| return SGF.B.createUncheckedRefCast(Loc, v, loweredResultTy); |
| } |
| |
| if (outputSubstType->isExactSuperclassOf(inputSubstType)) { |
| // Upcast to a superclass. |
| return SGF.B.createUpcast(Loc, v, loweredResultTy); |
| } else { |
| // FIXME: Should only happen with the DynamicSelfType case above, |
| // except that convenience inits return the static self and not |
| // DynamicSelfType. |
| assert(inputSubstType->isExactSuperclassOf(outputSubstType) |
| && "should be inheritance relationship between input and output"); |
| return SGF.B.createUncheckedRefCast(Loc, v, loweredResultTy); |
| } |
| } |
| |
| // - upcasts for collections |
| if (outputSubstType->getStructOrBoundGenericStruct() && |
| inputSubstType->getStructOrBoundGenericStruct()) { |
| auto *inputStruct = inputSubstType->getStructOrBoundGenericStruct(); |
| auto *outputStruct = outputSubstType->getStructOrBoundGenericStruct(); |
| |
| // Attempt collection upcast only if input and output declarations match. |
| if (inputStruct == outputStruct) { |
| FuncDecl *fn = nullptr; |
| auto &ctx = SGF.getASTContext(); |
| if (inputStruct == ctx.getArrayDecl()) { |
| fn = SGF.SGM.getArrayForceCast(Loc); |
| } else if (inputStruct == ctx.getDictionaryDecl()) { |
| fn = SGF.SGM.getDictionaryUpCast(Loc); |
| } else if (inputStruct == ctx.getSetDecl()) { |
| fn = SGF.SGM.getSetUpCast(Loc); |
| } else { |
| llvm_unreachable("unsupported collection upcast kind"); |
| } |
| |
| return SGF.emitCollectionConversion(Loc, fn, inputSubstType, |
| outputSubstType, v, ctxt) |
| .getScalarValue(); |
| } |
| } |
| |
| // - upcasts from an archetype |
| if (outputSubstType->getClassOrBoundGenericClass()) { |
| if (auto archetypeType = dyn_cast<ArchetypeType>(inputSubstType)) { |
| if (archetypeType->getSuperclass()) { |
| // Replace the cleanup with a new one on the superclass value so we |
| // always use concrete retain/release operations. |
| return SGF.B.createUpcast(Loc, v, loweredResultTy); |
| } |
| } |
| } |
| |
| // - metatype to Protocol conversion |
| if (isProtocolClass(outputSubstType)) { |
| if (auto metatypeTy = dyn_cast<MetatypeType>(inputSubstType)) { |
| return SGF.emitProtocolMetatypeToObject(Loc, metatypeTy, |
| SGF.getLoweredLoadableType(outputSubstType)); |
| } |
| } |
| |
| // - metatype to AnyObject conversion |
| if (outputSubstType->isAnyObject() && |
| isa<MetatypeType>(inputSubstType)) { |
| return SGF.emitClassMetatypeToObject(Loc, v, |
| SGF.getLoweredLoadableType(outputSubstType)); |
| } |
| |
| // - existential metatype to AnyObject conversion |
| if (outputSubstType->isAnyObject() && |
| isa<ExistentialMetatypeType>(inputSubstType)) { |
| return SGF.emitExistentialMetatypeToObject(Loc, v, |
| SGF.getLoweredLoadableType(outputSubstType)); |
| } |
| |
| // - block to AnyObject conversion (under ObjC interop) |
| if (outputSubstType->isAnyObject() && |
| SGF.getASTContext().LangOpts.EnableObjCInterop) { |
| if (auto inputFnType = dyn_cast<AnyFunctionType>(inputSubstType)) { |
| if (inputFnType->getRepresentation() == FunctionTypeRepresentation::Block) |
| return SGF.B.createBlockToAnyObject(Loc, v, loweredResultTy); |
| } |
| } |
| |
| // - existentials |
| if (outputSubstType->isAnyExistentialType()) { |
| // We have to re-abstract payload if its a metatype or a function |
| v = SGF.emitSubstToOrigValue(Loc, v, AbstractionPattern::getOpaque(), |
| inputSubstType); |
| return emitTransformExistential(SGF, Loc, v, |
| inputSubstType, outputSubstType, |
| ctxt); |
| } |
| |
| // - upcasting class-constrained existentials or metatypes thereof |
| if (inputSubstType->isAnyExistentialType()) { |
| auto instanceType = inputSubstType; |
| while (auto metatypeType = dyn_cast<ExistentialMetatypeType>(instanceType)) |
| instanceType = metatypeType.getInstanceType(); |
| |
| auto layout = instanceType.getExistentialLayout(); |
| if (layout.getSuperclass()) { |
| CanType openedType = OpenedArchetypeType::getAny(inputSubstType); |
| SILType loweredOpenedType = SGF.getLoweredType(openedType); |
| |
| FormalEvaluationScope scope(SGF); |
| |
| auto payload = SGF.emitOpenExistential(Loc, v, |
| loweredOpenedType, |
| AccessKind::Read); |
| payload = payload.ensurePlusOne(SGF, Loc); |
| return transform(payload, |
| AbstractionPattern::getOpaque(), |
| openedType, |
| outputOrigType, |
| outputSubstType, |
| loweredResultTy, |
| ctxt); |
| } |
| } |
| |
| // - T : Hashable to AnyHashable |
| if (isa<StructType>(outputSubstType) && |
| outputSubstType->getAnyNominal() == |
| SGF.getASTContext().getAnyHashableDecl()) { |
| auto *protocol = SGF.getASTContext().getProtocol( |
| KnownProtocolKind::Hashable); |
| auto conformance = SGF.SGM.M.getSwiftModule()->lookupConformance( |
| inputSubstType, protocol); |
| auto addr = v.getType().isAddress() ? v : v.materialize(SGF, Loc); |
| auto result = SGF.emitAnyHashableErasure(Loc, addr, inputSubstType, |
| conformance, ctxt); |
| if (result.isInContext()) |
| return ManagedValue::forInContext(); |
| return std::move(result).getAsSingleValue(SGF, Loc); |
| } |
| |
| // Should have handled the conversion in one of the cases above. |
| llvm_unreachable("Unhandled transform?"); |
| } |
| |
| ManagedValue Transform::transformMetatype(ManagedValue meta, |
| AbstractionPattern inputOrigType, |
| CanMetatypeType inputSubstType, |
| AbstractionPattern outputOrigType, |
| CanMetatypeType outputSubstType, |
| SILType expectedType) { |
| assert(!meta.hasCleanup() && "metatype with cleanup?!"); |
| |
| auto wasRepr = meta.getType().castTo<MetatypeType>()->getRepresentation(); |
| auto willBeRepr = expectedType.castTo<MetatypeType>()->getRepresentation(); |
| |
| SILValue result; |
| |
| if ((wasRepr == MetatypeRepresentation::Thick && |
| willBeRepr == MetatypeRepresentation::Thin) || |
| (wasRepr == MetatypeRepresentation::Thin && |
| willBeRepr == MetatypeRepresentation::Thick)) { |
| // If we have a thin-to-thick abstraction change, cook up new a metatype |
| // value out of nothing -- thin metatypes carry no runtime state. |
| result = SGF.B.createMetatype(Loc, expectedType); |
| } else { |
| // Otherwise, we have a metatype subtype conversion of thick metatypes. |
| assert(wasRepr == willBeRepr && "Unhandled metatype conversion"); |
| result = SGF.B.createUpcast(Loc, meta.getUnmanagedValue(), expectedType); |
| } |
| |
| return ManagedValue::forUnmanaged(result); |
| } |
| |
| /// Explode a managed tuple into a bunch of managed elements. |
| /// |
| /// If the tuple is in memory, the result elements will also be in |
| /// memory. |
| static void explodeTuple(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue managedTuple, |
| SmallVectorImpl<ManagedValue> &out) { |
| // If the tuple is empty, there's nothing to do. |
| if (managedTuple.getType().castTo<TupleType>()->getNumElements() == 0) |
| return; |
| |
| SmallVector<SILValue, 16> elements; |
| bool isPlusOne = managedTuple.hasCleanup(); |
| |
| if (managedTuple.getType().isAddress()) { |
| SGF.B.emitDestructureAddressOperation(loc, managedTuple.forward(SGF), |
| elements); |
| } else { |
| SGF.B.emitDestructureValueOperation(loc, managedTuple.forward(SGF), |
| elements); |
| } |
| |
| for (auto element : elements) { |
| if (!isPlusOne) |
| out.push_back(ManagedValue::forUnmanaged(element)); |
| else if (element->getType().isAddress()) |
| out.push_back(SGF.emitManagedBufferWithCleanup(element)); |
| else |
| out.push_back(SGF.emitManagedRValueWithCleanup(element)); |
| } |
| } |
| |
| /// Apply this transformation to all the elements of a tuple value, |
| /// which just entails mapping over each of its component elements. |
| ManagedValue Transform::transformTuple(ManagedValue inputTuple, |
| AbstractionPattern inputOrigType, |
| CanTupleType inputSubstType, |
| AbstractionPattern outputOrigType, |
| CanTupleType outputSubstType, |
| SILType outputLoweredTy, |
| SGFContext ctxt) { |
| const TypeLowering &outputTL = |
| SGF.getTypeLowering(outputLoweredTy); |
| assert((outputTL.isAddressOnly() == inputTuple.getType().isAddress() || |
| !SGF.silConv.useLoweredAddresses()) && |
| "expected loadable inputs to have been loaded"); |
| |
| // If there's no representation difference, we're done. |
| if (outputLoweredTy == inputTuple.getType().copyCategory(outputLoweredTy)) |
| return inputTuple; |
| |
| assert(inputOrigType.matchesTuple(outputSubstType)); |
| assert(outputOrigType.matchesTuple(outputSubstType)); |
| |
| auto inputType = inputTuple.getType().castTo<TupleType>(); |
| assert(outputSubstType->getNumElements() == inputType->getNumElements()); |
| |
| // If the tuple is address only, we need to do the operation in memory. |
| SILValue outputAddr; |
| if (outputTL.isAddressOnly() && SGF.silConv.useLoweredAddresses()) |
| outputAddr = SGF.getBufferForExprResult(Loc, outputLoweredTy, ctxt); |
| |
| // Explode the tuple into individual managed values. |
| SmallVector<ManagedValue, 4> inputElts; |
| explodeTuple(SGF, Loc, inputTuple, inputElts); |
| |
| // Track all the managed elements whether or not we're actually |
| // emitting to an address, just so that we can disable them after. |
| SmallVector<ManagedValue, 4> outputElts; |
| |
| for (auto index : indices(inputType->getElementTypes())) { |
| auto &inputEltTL = SGF.getTypeLowering(inputElts[index].getType()); |
| ManagedValue inputElt = inputElts[index]; |
| if (inputElt.getType().isAddress() && !inputEltTL.isAddressOnly()) { |
| inputElt = emitManagedLoad(SGF, Loc, inputElt, inputEltTL); |
| } |
| |
| auto inputEltOrigType = inputOrigType.getTupleElementType(index); |
| auto inputEltSubstType = inputSubstType.getElementType(index); |
| auto outputEltOrigType = outputOrigType.getTupleElementType(index); |
| auto outputEltSubstType = outputSubstType.getElementType(index); |
| auto outputEltLoweredTy = outputLoweredTy.getTupleElementType(index); |
| |
| // If we're emitting to memory, project out this element in the |
| // destination buffer, then wrap that in an Initialization to |
| // track the cleanup. |
| Optional<TemporaryInitialization> outputEltTemp; |
| if (outputAddr) { |
| SILValue outputEltAddr = |
| SGF.B.createTupleElementAddr(Loc, outputAddr, index); |
| auto &outputEltTL = SGF.getTypeLowering(outputEltLoweredTy); |
| assert(outputEltTL.isAddressOnly() == inputEltTL.isAddressOnly()); |
| auto cleanup = |
| SGF.enterDormantTemporaryCleanup(outputEltAddr, outputEltTL); |
| outputEltTemp.emplace(outputEltAddr, cleanup); |
| } |
| |
| SGFContext eltCtxt = |
| (outputEltTemp ? SGFContext(&outputEltTemp.getValue()) : SGFContext()); |
| auto outputElt = transform(inputElt, |
| inputEltOrigType, inputEltSubstType, |
| outputEltOrigType, outputEltSubstType, |
| outputEltLoweredTy, eltCtxt); |
| |
| // If we're not emitting to memory, remember this element for |
| // later assembly into a tuple. |
| if (!outputEltTemp) { |
| assert(outputElt); |
| assert(!inputEltTL.isAddressOnly() || !SGF.silConv.useLoweredAddresses()); |
| outputElts.push_back(outputElt); |
| continue; |
| } |
| |
| // Otherwise, make sure we emit into the slot. |
| auto &temp = outputEltTemp.getValue(); |
| auto outputEltAddr = temp.getManagedAddress(); |
| |
| // That might involve storing directly. |
| if (!outputElt.isInContext()) { |
| outputElt.forwardInto(SGF, Loc, outputEltAddr.getValue()); |
| temp.finishInitialization(SGF); |
| } |
| |
| outputElts.push_back(outputEltAddr); |
| } |
| |
| // Okay, disable all the individual element cleanups and collect |
| // the values for a potential tuple aggregate. |
| SmallVector<SILValue, 4> outputEltValues; |
| for (auto outputElt : outputElts) { |
| SILValue value = outputElt.forward(SGF); |
| if (!outputAddr) outputEltValues.push_back(value); |
| } |
| |
| // If we're emitting to an address, just manage that. |
| if (outputAddr) |
| return SGF.manageBufferForExprResult(outputAddr, outputTL, ctxt); |
| |
| // Otherwise, assemble the tuple value and manage that. |
| auto outputTuple = |
| SGF.B.createTuple(Loc, outputLoweredTy, outputEltValues); |
| return SGF.emitManagedRValueWithCleanup(outputTuple, outputTL); |
| } |
| |
| void SILGenFunction::collectThunkParams( |
| SILLocation loc, SmallVectorImpl<ManagedValue> ¶ms, |
| SmallVectorImpl<SILArgument *> *indirectResults) { |
| // Add the indirect results. |
| for (auto resultTy : F.getConventions().getIndirectSILResultTypes( |
| getTypeExpansionContext())) { |
| auto paramTy = F.mapTypeIntoContext(resultTy); |
| // Lower result parameters in the context of the function: opaque result |
| // types will be lowered to their underlying type if allowed by resilience. |
| auto inContextParamTy = F.getLoweredType(paramTy.getASTType()) |
| .getCategoryType(paramTy.getCategory()); |
| SILArgument *arg = F.begin()->createFunctionArgument(inContextParamTy); |
| if (indirectResults) |
| indirectResults->push_back(arg); |
| } |
| |
| // Add the parameters. |
| auto paramTypes = F.getLoweredFunctionType()->getParameters(); |
| for (auto param : paramTypes) { |
| auto paramTy = F.mapTypeIntoContext( |
| F.getConventions().getSILType(param, getTypeExpansionContext())); |
| // Lower parameters in the context of the function: opaque result types will |
| // be lowered to their underlying type if allowed by resilience. |
| auto inContextParamTy = F.getLoweredType(paramTy.getASTType()) |
| .getCategoryType(paramTy.getCategory()); |
| params.push_back(B.createInputFunctionArgument(inContextParamTy, loc)); |
| } |
| } |
| |
| /// Force a ManagedValue to be stored into a temporary initialization |
| /// if it wasn't emitted that way directly. |
| static void emitForceInto(SILGenFunction &SGF, SILLocation loc, |
| ManagedValue result, TemporaryInitialization &temp) { |
| if (result.isInContext()) return; |
| result.ensurePlusOne(SGF, loc).forwardInto(SGF, loc, temp.getAddress()); |
| temp.finishInitialization(SGF); |
| } |
| |
| namespace { |
| class TranslateIndirect : public Cleanup { |
| AbstractionPattern InputOrigType, OutputOrigType; |
| CanType InputSubstType, OutputSubstType; |
| SILValue Input, Output; |
| |
| public: |
| TranslateIndirect(AbstractionPattern inputOrigType, CanType inputSubstType, |
| AbstractionPattern outputOrigType, CanType outputSubstType, |
| SILValue input, SILValue output) |
| : InputOrigType(inputOrigType), OutputOrigType(outputOrigType), |
| InputSubstType(inputSubstType), OutputSubstType(outputSubstType), |
| Input(input), Output(output) { |
| assert(input->getType().isAddress()); |
| assert(output->getType().isAddress()); |
| } |
| |
| void emit(SILGenFunction &SGF, CleanupLocation loc, |
| ForUnwind_t forUnwind) override { |
| FullExpr scope(SGF.Cleanups, loc); |
| |
| // Re-assert ownership of the input value. |
| auto inputMV = SGF.emitManagedBufferWithCleanup(Input); |
| |
| // Set up an initialization of the output buffer. |
| auto &outputTL = SGF.getTypeLowering(Output->getType()); |
| auto outputInit = SGF.useBufferAsTemporary(Output, outputTL); |
| |
| // Transform into the output buffer. |
| auto mv = SGF.emitTransformedValue(loc, inputMV, |
| InputOrigType, InputSubstType, |
| OutputOrigType, OutputSubstType, |
| Output->getType().getObjectType(), |
| SGFContext(outputInit.get())); |
| emitForceInto(SGF, loc, mv, *outputInit); |
| |
| // Disable the cleanup; we've kept our promise to leave the inout |
| // initialized. |
| outputInit->getManagedAddress().forward(SGF); |
| } |
| |
| void dump(SILGenFunction &SGF) const override { |
| llvm::errs() << "TranslateIndirect(" |
| << InputOrigType << ", " << InputSubstType << ", " |
| << OutputOrigType << ", " << OutputSubstType << ", " |
| << Output << ", " << Input << ")\n"; |
| } |
| }; |
| |
| class TranslateArguments { |
| SILGenFunction &SGF; |
| SILLocation Loc; |
| ArrayRef<ManagedValue> Inputs; |
| SmallVectorImpl<ManagedValue> &Outputs; |
| CanSILFunctionType OutputTypesFuncTy; |
| ArrayRef<SILParameterInfo> OutputTypes; |
| public: |
| TranslateArguments(SILGenFunction &SGF, SILLocation loc, |
| ArrayRef<ManagedValue> inputs, |
| SmallVectorImpl<ManagedValue> &outputs, |
| CanSILFunctionType outputTypesFuncTy, |
| ArrayRef<SILParameterInfo> outputTypes) |
| : SGF(SGF), Loc(loc), Inputs(inputs), Outputs(outputs), |
| OutputTypesFuncTy(outputTypesFuncTy), OutputTypes(outputTypes) {} |
| |
| void translate(AbstractionPattern inputOrigFunctionType, |
| AnyFunctionType::CanParamArrayRef inputSubstTypes, |
| AbstractionPattern outputOrigFunctionType, |
| AnyFunctionType::CanParamArrayRef outputSubstTypes) { |
| if (inputSubstTypes.size() == 1 && |
| outputSubstTypes.size() != 1) { |
| // SE-0110 tuple splat. Turn the output into a single value of tuple |
| // type, and translate. |
| auto inputOrigType = inputOrigFunctionType.getFunctionParamType(0); |
| auto inputSubstType = inputSubstTypes[0].getPlainType(); |
| |
| // Build an abstraction pattern for the output. |
| SmallVector<AbstractionPattern, 8> outputOrigTypes; |
| for (unsigned i = 0, e = outputOrigFunctionType.getNumFunctionParams(); |
| i < e; ++i) { |
| outputOrigTypes.push_back( |
| outputOrigFunctionType.getFunctionParamType(i)); |
| } |
| auto outputOrigType = AbstractionPattern::getTuple(outputOrigTypes); |
| |
| // Build the substituted output tuple type. Note that we deliberately |
| // don't use composeInput() because we want to drop ownership |
| // qualifiers. |
| SmallVector<TupleTypeElt, 8> elts; |
| for (auto param : outputSubstTypes) { |
| assert(!param.isVariadic()); |
| assert(!param.isInOut()); |
| elts.emplace_back(param.getParameterType()); |
| } |
| auto outputSubstType = cast<TupleType>( |
| TupleType::get(elts, SGF.getASTContext()) |
| ->getCanonicalType()); |
| |
| // Translate the input tuple value into the output tuple value. Note |
| // that the output abstraction pattern is a tuple, and we explode tuples |
| // into multiple parameters, so if the input abstraction pattern is |
| // opaque, this will explode the input value. Otherwise, the input |
| // parameters will be mapped one-to-one to the output parameters. |
| translate(inputOrigType, inputSubstType, |
| outputOrigType, outputSubstType); |
| return; |
| } |
| |
| // Otherwise, parameters are always reabstracted one-to-one. |
| assert(inputSubstTypes.size() == outputSubstTypes.size()); |
| |
| SmallVector<AbstractionPattern, 8> inputOrigTypes; |
| SmallVector<AbstractionPattern, 8> outputOrigTypes; |
| for (auto i : indices(inputSubstTypes)) { |
| inputOrigTypes.push_back(inputOrigFunctionType.getFunctionParamType(i)); |
| outputOrigTypes.push_back(outputOrigFunctionType.getFunctionParamType(i)); |
| } |
| |
| translate(inputOrigTypes, inputSubstTypes, |
| outputOrigTypes, outputSubstTypes); |
| } |
| |
| void translate(ArrayRef<AbstractionPattern> inputOrigTypes, |
| AnyFunctionType::CanParamArrayRef inputSubstTypes, |
| ArrayRef<AbstractionPattern> outputOrigTypes, |
| AnyFunctionType::CanParamArrayRef outputSubstTypes) { |
| assert(inputOrigTypes.size() == inputSubstTypes.size()); |
| assert(outputOrigTypes.size() == outputSubstTypes.size()); |
| assert(inputOrigTypes.size() == outputOrigTypes.size()); |
| |
| for (auto i : indices(inputOrigTypes)) { |
| translate(inputOrigTypes[i], inputSubstTypes[i], |
| outputOrigTypes[i], outputSubstTypes[i]); |
| } |
| } |
| |
| void translate(AbstractionPattern inputOrigType, |
| AnyFunctionType::CanParam inputSubstType, |
| AbstractionPattern outputOrigType, |
| AnyFunctionType::CanParam outputSubstType) { |
| // Note that it's OK for the input to be inout but not the output; |
| // this means we're just going to load the inout and pass it on as a |
| // scalar. |
| if (outputSubstType.isInOut()) { |
| assert(inputSubstType.isInOut()); |
| auto inputValue = claimNextInput(); |
| auto outputLoweredTy = claimNextOutputType(); |
| |
| translateInOut(inputOrigType, inputSubstType.getParameterType(), |
| outputOrigType, outputSubstType.getParameterType(), |
| inputValue, outputLoweredTy); |
| } else { |
| translate(inputOrigType, inputSubstType.getParameterType(), |
| outputOrigType, outputSubstType.getParameterType()); |
| } |
| } |
| |
| |
| void translate(AbstractionPattern inputOrigType, |
| CanType inputSubstType, |
| AbstractionPattern outputOrigType, |
| CanType outputSubstType) { |
| // Most of this function is about tuples: tuples can be represented |
| // as one or many values, with varying levels of indirection. |
| auto inputTupleType = dyn_cast<TupleType>(inputSubstType); |
| auto outputTupleType = dyn_cast<TupleType>(outputSubstType); |
| |
| // Case where the input type is an exploded tuple. |
| if (inputOrigType.isTuple()) { |
| if (outputOrigType.isTuple()) { |
| // Both input and output are exploded tuples, easy case. |
| return translateParallelExploded(inputOrigType, |
| inputTupleType, |
| outputOrigType, |
| outputTupleType); |
| } |
| |
| // Tuple types are subtypes of their optionals |
| if (auto outputObjectType = outputSubstType.getOptionalObjectType()) { |
| auto outputOrigObjectType = outputOrigType.getOptionalObjectType(); |
| |
| if (auto outputTupleType = dyn_cast<TupleType>(outputObjectType)) { |
| // The input is exploded and the output is an optional tuple. |
| // Translate values and collect them into a single optional |
| // payload. |
| |
| auto result = |
| translateAndImplodeIntoOptional(inputOrigType, |
| inputTupleType, |
| outputOrigObjectType, |
| outputTupleType); |
| Outputs.push_back(result); |
| return; |
| } |
| |
| // Tuple types are subtypes of optionals of Any, too. |
| assert(outputObjectType->isAny()); |
| |
| // First, construct the existential. |
| auto result = |
| translateAndImplodeIntoAny(inputOrigType, |
| inputTupleType, |
| outputOrigObjectType, |
| outputObjectType); |
| |
| // Now, convert it to an optional. |
| translateSingle(outputOrigObjectType, outputObjectType, |
| outputOrigType, outputSubstType, |
| result, claimNextOutputType()); |
| return; |
| } |
| |
| if (outputSubstType->isAny()) { |
| claimNextOutputType(); |
| |
| auto result = |
| translateAndImplodeIntoAny(inputOrigType, |
| inputTupleType, |
| outputOrigType, |
| outputSubstType); |
| Outputs.push_back(result); |
| return; |
| } |
| |
| if (outputTupleType) { |
| // The input is exploded and the output is not. Translate values |
| // and store them to a result tuple in memory. |
| assert(outputOrigType.isTypeParameter() && |
| "Output is not a tuple and is not opaque?"); |
| |
| auto outputTy = SGF.getSILType(claimNextOutputType(), |
| OutputTypesFuncTy); |
| auto &outputTL = SGF.getTypeLowering(outputTy); |
| if (SGF.silConv.useLoweredAddresses()) { |
| auto temp = SGF.emitTemporary(Loc, outputTL); |
| translateAndImplodeInto(inputOrigType, inputTupleType, |
| outputOrigType, outputTupleType, *temp); |
| |
| Outputs.push_back(temp->getManagedAddress()); |
| } else { |
| auto result = translateAndImplodeIntoValue( |
| inputOrigType, inputTupleType, outputOrigType, outputTupleType, |
| outputTL.getLoweredType()); |
| Outputs.push_back(result); |
| } |
| return; |
| } |
| |
| llvm_unreachable("Unhandled conversion from exploded tuple"); |
| } |
| |
| // Handle output being an exploded tuple when the input is opaque. |
| if (outputOrigType.isTuple()) { |
| if (inputTupleType) { |
| // The input is exploded and the output is not. Translate values |
| // and store them to a result tuple in memory. |
| assert(inputOrigType.isTypeParameter() && |
| "Input is not a tuple and is not opaque?"); |
| |
| return translateAndExplodeOutOf(inputOrigType, |
| inputTupleType, |
| outputOrigType, |
| outputTupleType, |
| claimNextInput()); |
| } |
| |
| // FIXME: IUO<Tuple> to Tuple |
| llvm_unreachable("Unhandled conversion to exploded tuple"); |
| } |
| |
| // Okay, we are now working with a single value turning into a |
| // single value. |
| auto inputElt = claimNextInput(); |
| auto outputEltType = claimNextOutputType(); |
| translateSingle(inputOrigType, inputSubstType, |
| outputOrigType, outputSubstType, |
| inputElt, outputEltType); |
| } |
| |
| private: |
| /// Take a tuple that has been exploded in the input and turn it into |
| /// a tuple value in the output. |
| ManagedValue translateAndImplodeIntoValue(AbstractionPattern inputOrigType, |
| CanTupleType inputType, |
| AbstractionPattern outputOrigType, |
| CanTupleType outputType, |
| SILType loweredOutputTy) { |
| assert(loweredOutputTy.is<TupleType>()); |
| |
| SmallVector<ManagedValue, 4> elements; |
| assert(outputType->getNumElements() == inputType->getNumElements()); |
| assert(loweredOutputTy.castTo<TupleType>()->getNumElements() == |
| outputType->getNumElements()); |
| for (unsigned i : indices(outputType->getElementTypes())) { |
| auto inputOrigEltType = inputOrigType.getTupleElementType(i); |
| auto inputEltType = inputType.getElementType(i); |
| auto outputOrigEltType = outputOrigType.getTupleElementType(i); |
| auto outputEltType = outputType.getElementType(i); |
| SILType loweredOutputEltTy = loweredOutputTy.getTupleElementType(i); |
| |
| ManagedValue elt; |
| if (auto inputEltTupleType = dyn_cast<TupleType>(inputEltType)) { |
| elt = translateAndImplodeIntoValue(inputOrigEltType, |
| inputEltTupleType, |
| outputOrigEltType, |
| cast<TupleType>(outputEltType), |
| loweredOutputEltTy); |
| } else { |
| elt = claimNextInput(); |
| |
| // Load if necessary. |
| if (elt.getType().isAddress()) { |
| // This code assumes that we have each element at +1. So, if we do |
| // not have a cleanup, we emit a load [copy]. This can occur if we |
| // are translating in_guaranteed parameters. |
| IsTake_t isTakeVal = elt.isPlusZero() ? IsNotTake : IsTake; |
| elt = SGF.emitLoad(Loc, elt.forward(SGF), |
| SGF.getTypeLowering(elt.getType()), SGFContext(), |
| isTakeVal); |
| } |
| } |
| |
| if (elt.getType() != loweredOutputEltTy) |
| elt = translatePrimitive(inputOrigEltType, inputEltType, |
| outputOrigEltType, outputEltType, |
| elt, loweredOutputEltTy); |
| |
| elements.push_back(elt); |
| } |
| |
| SmallVector<SILValue, 4> forwarded; |
| for (auto &elt : elements) |
| forwarded.push_back(elt.forward(SGF)); |
| |
| auto tuple = SGF.B.createTuple(Loc, loweredOutputTy, forwarded); |
| return SGF.emitManagedRValueWithCleanup(tuple); |
| } |
| |
| /// Handle a tuple that has been exploded in the input but wrapped in |
| /// an optional in the output. |
| ManagedValue |
| translateAndImplodeIntoOptional(AbstractionPattern inputOrigType, |
| CanTupleType inputTupleType, |
| AbstractionPattern outputOrigType, |
| CanTupleType outputTupleType) { |
| assert(!inputTupleType->hasElementWithOwnership() && |
| !outputTupleType->hasElementWithOwnership()); |
| assert(inputTupleType->getNumElements() == |
| outputTupleType->getNumElements()); |
| |
| // Collect the tuple elements. |
| auto &loweredTL = SGF.getTypeLowering(outputOrigType, outputTupleType); |
| auto loweredTy = loweredTL.getLoweredType(); |
| auto optionalTy = SGF.getSILType(claimNextOutputType(), |
| OutputTypesFuncTy); |
| auto someDecl = SGF.getASTContext().getOptionalSomeDecl(); |
| if (loweredTL.isLoadable() || !SGF.silConv.useLoweredAddresses()) { |
| auto payload = |
| translateAndImplodeIntoValue(inputOrigType, inputTupleType, |
| outputOrigType, outputTupleType, |
| loweredTy); |
| |
| return SGF.B.createEnum(Loc, payload, someDecl, optionalTy); |
| } else { |
| auto optionalBuf = SGF.emitTemporaryAllocation(Loc, optionalTy); |
| auto tupleBuf = SGF.B.createInitEnumDataAddr(Loc, optionalBuf, someDecl, |
| loweredTy); |
| |
| auto tupleTemp = SGF.useBufferAsTemporary(tupleBuf, loweredTL); |
| |
| translateAndImplodeInto(inputOrigType, inputTupleType, |
| outputOrigType, outputTupleType, |
| *tupleTemp); |
| |
| SGF.B.createInjectEnumAddr(Loc, optionalBuf, someDecl); |
| |
| auto payload = tupleTemp->getManagedAddress(); |
| if (payload.hasCleanup()) { |
| payload.forward(SGF); |
| return SGF.emitManagedBufferWithCleanup(optionalBuf); |
| } |
| return ManagedValue::forUnmanaged(optionalBuf); |
| } |
| } |
| |
| /// Handle a tuple that has been exploded in the input but wrapped |
| /// in an existential in the output. |
| ManagedValue |
| translateAndImplodeIntoAny(AbstractionPattern inputOrigType, |
| CanTupleType inputTupleType, |
| AbstractionPattern outputOrigType, |
| CanType outputSubstType) { |
| auto existentialTy = SGF.getLoweredType(outputOrigType, outputSubstType); |
| auto existentialBuf = SGF.emitTemporaryAllocation(Loc, existentialTy); |
| |
| auto opaque = AbstractionPattern::getOpaque(); |
| auto &concreteTL = SGF.getTypeLowering(opaque, inputTupleType); |
| |
| auto tupleBuf = |
| SGF.B.createInitExistentialAddr(Loc, existentialBuf, |
| inputTupleType, |
| concreteTL.getLoweredType(), |
| /*conformances=*/{}); |
| |
| auto tupleTemp = SGF.useBufferAsTemporary(tupleBuf, concreteTL); |
| translateAndImplodeInto(inputOrigType, inputTupleType, |
| opaque, inputTupleType, |
| *tupleTemp); |
| |
| auto payload = tupleTemp->getManagedAddress(); |
| if (SGF.silConv.useLoweredAddresses()) { |
| // We always need to return the existential buf with a cleanup even if |
| // we stored trivial values, since SILGen maintains the invariant that |
| // forwarding a non-trivial value (i.e. an Any) into memory must be done |
| // at +1. |
| payload.forward(SGF); |
| return SGF.emitManagedBufferWithCleanup(existentialBuf); |
| } |
| |
| // We are under opaque value(s) mode - load the any and init an opaque |
| auto loadedPayload = SGF.B.createLoadCopy(Loc, payload); |
| auto &anyTL = SGF.getTypeLowering(opaque, outputSubstType); |
| return SGF.B.createInitExistentialValue( |
| Loc, anyTL.getLoweredType(), inputTupleType, loadedPayload, |
| /*Conformances=*/{}); |
| } |
| |
| /// Handle a tuple that has been exploded in both the input and |
| /// the output. |
| void translateParallelExploded(AbstractionPattern inputOrigType, |
| CanTupleType inputSubstType, |
| AbstractionPattern outputOrigType, |
| CanTupleType outputSubstType) { |
| assert(inputOrigType.matchesTuple(inputSubstType)); |
| assert(outputOrigType.matchesTuple(outputSubstType)); |
| assert(!inputSubstType->hasElementWithOwnership() && |
| !outputSubstType->hasElementWithOwnership()); |
| assert(inputSubstType->getNumElements() == |
| outputSubstType->getNumElements()); |
| |
| for (auto index : indices(outputSubstType.getElementTypes())) { |
| translate(inputOrigType.getTupleElementType(index), |
| inputSubstType.getElementType(index), |
| outputOrigType.getTupleElementType(index), |
| outputSubstType.getElementType(index)); |
| } |
| } |
| |
| /// Given that a tuple value is being passed indirectly in the |
| /// input, explode it and translate the elements. |
| void translateAndExplodeOutOf(AbstractionPattern inputOrigType, |
| CanTupleType inputSubstType, |
| AbstractionPattern outputOrigType, |
| CanTupleType outputSubstType, |
| ManagedValue inputTupleAddr) { |
| assert(inputOrigType.isTypeParameter()); |
| assert(outputOrigType.matchesTuple(outputSubstType)); |
| assert(!inputSubstType->hasElementWithOwnership() && |
| !outputSubstType->hasElementWithOwnership()); |
| assert(inputSubstType->getNumElements() == |
| outputSubstType->getNumElements()); |
| |
| SmallVector<ManagedValue, 4> inputEltAddrs; |
| explodeTuple(SGF, Loc, inputTupleAddr, inputEltAddrs); |
| assert(inputEltAddrs.size() == outputSubstType->getNumElements()); |
| |
| for (auto index : indices(outputSubstType.getElementTypes())) { |
| auto inputEltOrigType = inputOrigType.getTupleElementType(index); |
| auto inputEltSubstType = inputSubstType.getElementType(index); |
| auto outputEltOrigType = outputOrigType.getTupleElementType(index); |
| auto outputEltSubstType = outputSubstType.getElementType(index); |
| auto inputEltAddr = inputEltAddrs[index]; |
| assert(inputEltAddr.getType().isAddress() || |
| !SGF.silConv.useLoweredAddresses()); |
| |
| if (auto outputEltTupleType = dyn_cast<TupleType>(outputEltSubstType)) { |
| assert(outputEltOrigType.isTuple()); |
| auto inputEltTupleType = cast<TupleType>(inputEltSubstType); |
| translateAndExplodeOutOf(inputEltOrigType, |
| inputEltTupleType, |
| outputEltOrigType, |
| outputEltTupleType, |
| inputEltAddr); |
| } else { |
| auto outputType = claimNextOutputType(); |
| translateSingle(inputEltOrigType, |
| inputEltSubstType, |
| outputEltOrigType, |
| outputEltSubstType, |
| inputEltAddr, |
| outputType); |
| } |
| } |
| } |
| |
| /// Given that a tuple value is being passed indirectly in the |
| /// output, translate the elements and implode it. |
| void translateAndImplodeInto(AbstractionPattern inputOrigType, |
| CanTupleType inputSubstType, |
| AbstractionPattern outputOrigType, |
| CanTupleType outputSubstType, |
| TemporaryInitialization &tupleInit) { |
| assert(inputOrigType.matchesTuple(inputSubstType)); |
| assert(outputOrigType.matchesTuple(outputSubstType)); |
| assert(!inputSubstType->hasElementWithOwnership() && |
| !outputSubstType->hasElementWithOwnership()); |
| assert(inputSubstType->getNumElements() == |
| outputSubstType->getNumElements()); |
| |
| auto outputLoweredTy = tupleInit.getAddress()->getType(); |
| assert(outputLoweredTy.castTo<TupleType>()->getNumElements() == |
| outputSubstType->getNumElements()); |
| |
| SmallVector<CleanupHandle, 4> cleanups; |
| |
| for (auto index : indices(outputSubstType.getElementTypes())) { |
| auto inputEltOrigType = inputOrigType.getTupleElementType(index); |
| auto inputEltSubstType = inputSubstType.getElementType(index); |
| auto outputEltOrigType = outputOrigType.getTupleElementType(index); |
| auto outputEltSubstType = outputSubstType.getElementType(index); |
| auto eltAddr = |
| SGF.B.createTupleElementAddr(Loc, tupleInit.getAddress(), index); |
| |
| auto &outputEltTL = SGF.getTypeLowering(eltAddr->getType()); |
| CleanupHandle eltCleanup = |
| SGF.enterDormantTemporaryCleanup(eltAddr, outputEltTL); |
| if (eltCleanup.isValid()) cleanups.push_back(eltCleanup); |
| |
| TemporaryInitialization eltInit(eltAddr, eltCleanup); |
| if (auto outputEltTupleType = dyn_cast<TupleType>(outputEltSubstType)) { |
| auto inputEltTupleType = cast<TupleType>(inputEltSubstType); |
| translateAndImplodeInto(inputEltOrigType, inputEltTupleType, |
| outputEltOrigType, outputEltTupleType, |
| eltInit); |
| } else { |
| // Otherwise, we come from a single value. |
| auto input = claimNextInput(); |
| translateSingleInto(inputEltOrigType, inputEltSubstType, |
| outputEltOrigType, outputEltSubstType, |
| input, eltInit); |
| } |
| } |
| |
| // Deactivate all the element cleanups and activate the tuple cleanup. |
| for (auto cleanup : cleanups) |
| SGF.Cleanups.forwardCleanup(cleanup); |
| tupleInit.finishInitialization(SGF); |
| } |
| |
| // Translate into a temporary. |
| void translateIndirect(AbstractionPattern inputOrigType, |
| CanType inputSubstType, |
| AbstractionPattern outputOrigType, |
| CanType outputSubstType, ManagedValue input, |
| SILType resultTy) { |
| auto &outputTL = SGF.getTypeLowering(resultTy); |
| auto temp = SGF.emitTemporary(Loc, outputTL); |
| translateSingleInto(inputOrigType, inputSubstType, outputOrigType, |
| outputSubstType, input, *temp); |
| Outputs.push_back(temp->getManagedAddress()); |
| } |
| |
| // Translate into an owned argument. |
| void translateIntoOwned(AbstractionPattern inputOrigType, |
| CanType inputSubstType, |
| AbstractionPattern outputOrigType, |
| CanType outputSubstType, |
| ManagedValue input, |
| SILType outputLoweredTy) { |
| auto output = translatePrimitive(inputOrigType, inputSubstType, |
| outputOrigType, outputSubstType, |
| input, outputLoweredTy); |
| |
| // If our output is guaranteed or unowned, we need to create a copy here. |
| if (output.getOwnershipKind() != OwnershipKind::Owned) |
| output = output.copyUnmanaged(SGF, Loc); |
| |
| Outputs.push_back(output); |
| } |
| |
| // Translate into a guaranteed argument. |
| void translateIntoGuaranteed(AbstractionPattern inputOrigType, |
| CanType inputSubstType, |
| AbstractionPattern outputOrigType, |
| CanType outputSubstType, |
| ManagedValue input, |
| SILType outputLoweredTy) { |
| auto output = translatePrimitive(inputOrigType, inputSubstType, |
| outputOrigType, outputSubstType, |
| input, outputLoweredTy); |
| |
| // If our output value is not guaranteed, we need to: |
| // |
| // 1. Unowned - Copy + Borrow. |
| // 2. Owned - Borrow. |
| // 3. Trivial - do nothing. |
| // |
| // This means we can first transition unowned => owned and then handle |
| // the new owned value using the same code path as values that are |
| // initially owned. |
| if (output.getOwnershipKind() == OwnershipKind::Unowned) { |
| assert(!output.hasCleanup()); |
| output = SGF.emitManagedRetain(Loc, output.getValue()); |
| } |
| |
| // If the output is unowned or owned, create a borrow. |
| if (output.getOwnershipKind() != OwnershipKind::Guaranteed) { |
| output = SGF.emitManagedBeginBorrow(Loc, output.getValue()); |
| } |
| |
| Outputs.push_back(output); |
| } |
| |
| /// Translate a single value and add it as an output. |
| void translateSingle(AbstractionPattern inputOrigType, |
| CanType inputSubstType, |
| AbstractionPattern outputOrigType, |
| CanType outputSubstType, |
| ManagedValue input, |
| SILParameterInfo result) { |
| auto resultTy = SGF.getSILType(result, OutputTypesFuncTy); |
| // Easy case: we want to pass exactly this value. |
| if (input.getType() == resultTy) { |
| switch (result.getConvention()) { |
| case ParameterConvention::Direct_Owned: |
| case ParameterConvention::Indirect_In: |
| if (!input.hasCleanup() && |
| input.getOwnershipKind() != OwnershipKind::None) |
| input = input.copyUnmanaged(SGF, Loc); |
| break; |
| |
| default: |
| break; |
| } |
| |
| Outputs.push_back(input); |
| return; |
| } |
| |
| switch (result.getConvention()) { |
| // Direct translation is relatively easy. |
| case ParameterConvention::Direct_Owned: |
| case ParameterConvention::Direct_Unowned: |
| translateIntoOwned(inputOrigType, inputSubstType, outputOrigType, |
| outputSubstType, input, resultTy); |
| return; |
| case ParameterConvention::Direct_Guaranteed: |
| translateIntoGuaranteed(inputOrigType, inputSubstType, outputOrigType, |
| outputSubstType, input, resultTy); |
| return; |
| case ParameterConvention::Indirect_In: { |
| if (SGF.silConv.useLoweredAddresses()) { |
| translateIndirect(inputOrigType, inputSubstType, outputOrigType, |
| outputSubstType, input, resultTy); |
| return; |
| } |
| translateIntoOwned(inputOrigType, inputSubstType, outputOrigType, |
| outputSubstType, input, resultTy); |
| return; |
| } |
| case ParameterConvention::Indirect_In_Guaranteed: { |
| if (SGF.silConv.useLoweredAddresses()) { |
| translateIndirect(inputOrigType, inputSubstType, outputOrigType, |
| outputSubstType, input, resultTy); |
| return; |
| } |
| translateIntoGuaranteed(inputOrigType, inputSubstType, outputOrigType, |
| outputSubstType, input, resultTy); |
| return; |
| } |
| case ParameterConvention::Indirect_Inout: |
| llvm_unreachable("inout reabstraction handled elsewhere"); |
| case ParameterConvention::Indirect_InoutAliasable: |
| llvm_unreachable("abstraction difference in aliasable argument not " |
| "allowed"); |
| case ParameterConvention::Indirect_In_Constant: |
| llvm_unreachable("in_constant convention not allowed in SILGen"); |
| } |
| |
| llvm_unreachable("Covered switch isn't covered?!"); |
| } |
| |
| void translateInOut(AbstractionPattern inputOrigType, |
| CanType inputSubstType, |
| AbstractionPattern outputOrigType, |
| CanType outputSubstType, |
| ManagedValue input, |
| SILParameterInfo result) { |
| auto resultTy = SGF.getSILType(result, OutputTypesFuncTy); |
| assert(input.isLValue()); |
| if (input.getType() == resultTy) { |
| Outputs.push_back(input); |
| return; |
| } |
| |
| // Create a temporary of the right type. |
| auto &temporaryTL = SGF.getTypeLowering(resultTy); |
| auto temporary = SGF.emitTemporary(Loc, temporaryTL); |
| |
| // Take ownership of the input value. This leaves the input l-value |
| // effectively uninitialized, but we'll push a cleanup that will put |
| // a value back into it. |
| FullExpr scope(SGF.Cleanups, CleanupLocation::get(Loc)); |
| auto ownedInput = |
| SGF.emitManagedBufferWithCleanup(input.getLValueAddress()); |
| |
| // Translate the input value into the temporary. |
| translateSingleInto(inputOrigType, inputSubstType, |
| outputOrigType, outputSubstType, |
| ownedInput, *temporary); |
| |
| // Forward the cleanup on the temporary. We're about to push a new |
| // cleanup that will re-assert ownership of this value. |
| auto temporaryAddr = temporary->getManagedAddress().forward(SGF); |
| |
| // Leave the scope in which we did the forward translation. This |
| // ensures that the value in the input buffer is destroyed |
| // immediately rather than (potentially) arbitrarily later |
| // at a point where we want to put new values in the input buffer. |
| scope.pop(); |
| |
| // Push the cleanup to perform the reverse translation. This cleanup |
| // asserts ownership of the value of the temporary. |
| SGF.Cleanups.pushCleanup<TranslateIndirect>(outputOrigType, |
| outputSubstType, |
| inputOrigType, |
| inputSubstType, |
| temporaryAddr, |
| input.getLValueAddress()); |
| |
| // Add the temporary as an l-value argument. |
| Outputs.push_back(ManagedValue::forLValue(temporaryAddr)); |
| } |
| |
| /// Translate a single value and initialize the given temporary with it. |
| void translateSingleInto(AbstractionPattern inputOrigType, |
| CanType inputSubstType, |
| AbstractionPattern outputOrigType, |
| CanType outputSubstType, |
| ManagedValue input, |
| TemporaryInitialization &temp) { |
| auto output = translatePrimitive(inputOrigType, inputSubstType, |
| outputOrigType, outputSubstType, |
| input, temp.getAddress()->getType(), |
| SGFContext(&temp)); |
| forceInto(output, temp); |
| } |
| |
| /// Apply primitive translation to the given value. |
| ManagedValue translatePrimitive(AbstractionPattern inputOrigType, |
| CanType inputSubstType, |
| AbstractionPattern outputOrigType, |
| CanType outputSubstType, |
| ManagedValue input, |
| SILType loweredOutputTy, |
| SGFContext context = SGFContext()) { |
| return SGF.emitTransformedValue(Loc, input, |
| inputOrigType, inputSubstType, |
| outputOrigType, outputSubstType, |
| loweredOutputTy, context); |
| } |
| |
| /// Force the given result into the given initialization. |
| void forceInto(ManagedValue result, TemporaryInitialization &temp) { |
| emitForceInto(SGF, Loc, result, temp); |
| } |
| |
| ManagedValue claimNextInput() { |
| return claimNext(Inputs); |
| } |
| |
| SILParameterInfo claimNextOutputType() { |
| return claimNext(OutputTypes); |
| } |
| }; |
| } // end anonymous namespace |
| |
| /// Apply trivial conversions to a value to handle differences between the inner |
| /// and outer types of a function conversion thunk. |
| static ManagedValue applyTrivialConversions(SILGenFunction &SGF, |
| SILLocation loc, |
| ManagedValue innerValue, |
| SILType outerType) { |
| auto innerASTTy = innerValue.getType().getASTType(); |
| auto outerASTTy = outerType.getASTType(); |
| if (innerASTTy->hasArchetype()) |
| innerASTTy = innerASTTy->mapTypeOutOfContext()->getCanonicalType(); |
| if (outerASTTy->hasArchetype()) |
| outerASTTy = outerASTTy->mapTypeOutOfContext()->getCanonicalType(); |
| |
| if (innerASTTy == outerASTTy) { |
| return innerValue; |
| } |
| if (innerASTTy->getClassOrBoundGenericClass() |
| && outerASTTy->getClassOrBoundGenericClass()) { |
| if (outerASTTy->isExactSuperclassOf(innerASTTy)) { |
| return SGF.B.createUpcast(loc, innerValue, outerType); |
| } else if (innerASTTy->isExactSuperclassOf(outerASTTy)) { |
| return SGF.B.createUncheckedRefCast(loc, innerValue, outerType); |
| } |
| } else if (auto innerFnTy = dyn_cast<SILFunctionType>(innerASTTy)) { |
| if (auto outerFnTy = dyn_cast<SILFunctionType>(outerASTTy)) { |
| auto abiDiffA = |
| SGF.SGM.Types.checkFunctionForABIDifferences(SGF.SGM.M, |
| innerFnTy, |
| outerFnTy); |
| auto abiDiffB = |
| SGF.SGM.Types.checkFunctionForABIDifferences(SGF.SGM.M, |
| outerFnTy, |
| innerFnTy); |
| |
| if (abiDiffA == TypeConverter::ABIDifference::CompatibleRepresentation |
| || abiDiffA == TypeConverter::ABIDifference::CompatibleCallingConvention |
| || abiDiffB == TypeConverter::ABIDifference::CompatibleRepresentation |
| || abiDiffB == TypeConverter::ABIDifference::CompatibleCallingConvention) { |
| return SGF.B.createConvertFunction(loc, innerValue, outerType); |
| } |
| } |
| } |
| |
| llvm_unreachable("unhandled reabstraction type mismatch"); |
| } |
| |
| /// Forward arguments according to a function type's ownership conventions. |
| static void forwardFunctionArguments(SILGenFunction &SGF, |
| SILLocation loc, |
| CanSILFunctionType fTy, |
| ArrayRef<ManagedValue> managedArgs, |
| SmallVectorImpl<SILValue> &forwardedArgs) { |
| auto argTypes = fTy->getParameters(); |
| for (auto index : indices(managedArgs)) { |
| auto arg = managedArgs[index]; |
| auto argTy = argTypes[index]; |
| auto argSubstTy = |
| argTy.getArgumentType(SGF.SGM.M, fTy, SGF.getTypeExpansionContext()); |
| |
| arg = applyTrivialConversions(SGF, loc, arg, |
| SILType::getPrimitiveObjectType(argSubstTy)); |
| |
| if (argTy.isConsumed()) { |
| forwardedArgs.push_back(arg.ensurePlusOne(SGF, loc).forward(SGF)); |
| continue; |
| } |
| |
| if (isGuaranteedParameter(argTy.getConvention())) { |
| forwardedArgs.push_back( |
| SGF.emitManagedBeginBorrow(loc, arg.getValue()).getValue()); |
| continue; |
| } |
| |
| forwardedArgs.push_back(arg.getValue()); |
| } |
| } |
| |
| namespace { |
| class YieldInfo { |
| SmallVector<AbstractionPattern, 1> OrigTypes; |
| SmallVector<AnyFunctionType::Param, 1> Yields; |
| ArrayRef<SILYieldInfo> LoweredInfos; |
| public: |
| YieldInfo(SILGenModule &SGM, SILDeclRef function, |
| CanSILFunctionType loweredType, SubstitutionMap subs) { |
| LoweredInfos = loweredType->getUnsubstitutedType(SGM.M)->getYields(); |
| |
| auto accessor = cast<AccessorDecl>(function.getDecl()); |
| auto storage = accessor->getStorage(); |
| |
| OrigTypes.push_back( |
| SGM.Types.getAbstractionPattern(storage, /*nonobjc*/ true)); |
| |
| SmallVector<AnyFunctionType::Yield, 1> yieldsBuffer; |
| auto yields = AnyFunctionRef(accessor).getYieldResults(yieldsBuffer); |
| assert(yields.size() == 1); |
| Yields.push_back(yields[0].getCanonical().subst(subs).asParam()); |
| } |
| |
| ArrayRef<AbstractionPattern> getOrigTypes() const { return OrigTypes; } |
| AnyFunctionType::CanParamArrayRef getSubstTypes() const { |
| return AnyFunctionType::CanParamArrayRef(Yields); |
| } |
| ArrayRef<SILYieldInfo> getLoweredTypes() const { return LoweredInfos; } |
| }; |
| } |
| |
| static ManagedValue manageYield(SILGenFunction &SGF, SILValue value, |
| SILYieldInfo info) { |
| switch (info.getConvention()) { |
| case ParameterConvention::Indirect_Inout: |
| case ParameterConvention::Indirect_InoutAliasable: |
| return ManagedValue::forLValue(value); |
| case ParameterConvention::Direct_Owned: |
| case ParameterConvention::Indirect_In: |
| case ParameterConvention::Indirect_In_Constant: |
| return SGF.emitManagedRValueWithCleanup(value); |
| case ParameterConvention::Direct_Guaranteed: |
| case ParameterConvention::Direct_Unowned: |
| if (value.getOwnershipKind() == OwnershipKind::None) |
| return ManagedValue::forUnmanaged(value); |
| return ManagedValue::forBorrowedObjectRValue(value); |
| case ParameterConvention::Indirect_In_Guaranteed: |
| return ManagedValue::forBorrowedAddressRValue(value); |
| } |
| llvm_unreachable("bad kind"); |
| } |
| |
| static void manageYields(SILGenFunction &SGF, ArrayRef<SILValue> yields, |
| ArrayRef<SILYieldInfo> yieldInfos, |
| SmallVectorImpl<ManagedValue> &yieldMVs) { |
| assert(yields.size() == yieldInfos.size()); |
| for (auto i : indices(yields)) { |
| yieldMVs.push_back(manageYield(SGF, yields[i], yieldInfos[i])); |
| } |
| } |
| |
| /// Translate the values yielded to us back out to the caller. |
| static void translateYields(SILGenFunction &SGF, SILLocation loc, |
| ArrayRef<SILValue> innerYields, |
| const YieldInfo &innerInfos, |
| const YieldInfo &outerInfos) { |
| assert(innerInfos.getOrigTypes().size() == innerInfos.getSubstTypes().size()); |
| assert(outerInfos.getOrigTypes().size() == outerInfos.getSubstTypes().size()); |
| assert(innerInfos.getOrigTypes().size() == outerInfos.getOrigTypes().size()); |
| |
| SmallVector<ManagedValue, 4> innerMVs; |
| manageYields(SGF, innerYields, innerInfos.getLoweredTypes(), innerMVs); |
| |
| FullExpr scope(SGF.Cleanups, CleanupLocation::get(loc)); |
| |
| // Map the SILYieldInfos into the local context and incidentally turn |
| // them into SILParameterInfos. |
| SmallVector<SILParameterInfo, 4> outerLoweredTypesAsParameters; |
| for (auto unmappedInfo : outerInfos.getLoweredTypes()) { |
| auto mappedTy = SGF.F.mapTypeIntoContext( |
| unmappedInfo.getSILStorageInterfaceType()); |
| outerLoweredTypesAsParameters.push_back({mappedTy.getASTType(), |
| unmappedInfo.getConvention()}); |
| } |
| |
| // Translate the yields as if they were arguments. |
| SmallVector<ManagedValue, 4> outerMVs; |
| TranslateArguments translator(SGF, loc, innerMVs, outerMVs, |
| CanSILFunctionType(), |
| outerLoweredTypesAsParameters); |
| |
| translator.translate(innerInfos.getOrigTypes(), innerInfos.getSubstTypes(), |
| outerInfos.getOrigTypes(), outerInfos.getSubstTypes()); |
| |
| // Prepare a destination for the unwind; use the current cleanup stack |
| // as the depth so that we branch right to it. |
| SILBasicBlock *unwindBB = SGF.createBasicBlock(FunctionSection::Postmatter); |
| JumpDest unwindDest(unwindBB, SGF.Cleanups.getCleanupsDepth(), |
| CleanupLocation::get(loc)); |
| |
| // Emit the yield. |
| SGF.emitRawYield(loc, outerMVs, unwindDest, /*unique*/ true); |
| |
| // Emit the unwind block. |
| { |
| SILGenSavedInsertionPoint savedIP(SGF, unwindBB, |
| FunctionSection::Postmatter); |
| |
| // Emit all active cleanups. |
| SGF.Cleanups.emitCleanupsForReturn(CleanupLocation::get(loc), IsForUnwind); |
| SGF.B.createUnwind(loc); |
| } |
| } |
| |
| namespace { |
| |
| /// A helper class to translate the inner results to the outer results. |
| /// |
| /// Creating a result-translation plan involves three basic things: |
| /// - building SILArguments for each of the outer indirect results |
| /// - building a list of SILValues for each of the inner indirect results |
| /// - building a list of Operations to perform which will reabstract |
| /// the inner results to match the outer. |
| class ResultPlanner { |
| SILGenFunction &SGF; |
| SILLocation Loc; |
| |
| /// A single result-translation operation. |
| struct Operation { |
| enum Kind { |
| /// Take the last N direct outer results, tuple them, and make that a |
| /// new direct outer result. |
| /// |
| /// Valid: NumElements, OuterResult |
| TupleDirect, |
| |
| /// Take the last direct outer result, inject it into an optional |
| /// type, and make that a new direct outer result. |
| /// |
| /// Valid: SomeDecl, OuterResult |
| InjectOptionalDirect, |
| |
| /// Finish building an optional Some in the given address. |
| /// |
| /// Valid: SomeDecl, OuterResultAddr |
| InjectOptionalIndirect, |
| |
| /// Take the next direct inner result and just make it a direct |
| /// outer result. |
| /// |
| /// Valid: InnerResult, OuterResult. |
| DirectToDirect, |
| |
| /// Take the next direct inner result and store it into an |
| /// outer result address. |
| /// |
| /// Valid: InnerDirect, OuterResultAddr. |
| DirectToIndirect, |
| |
| /// Take from an indirect inner result and make it the next outer |
| /// direct result. |
| /// |
| /// Valid: InnerResultAddr, OuterResult. |
| IndirectToDirect, |
| |
| /// Take from an indirect inner result into an outer indirect result. |
| /// |
| /// Valid: InnerResultAddr, OuterResultAddr. |
| IndirectToIndirect, |
| |
| /// Take a value out of the source inner result address, reabstract |
| /// it, and initialize the destination outer result address. |
| /// |
| /// Valid: reabstraction info, InnerAddress, OuterAddress. |
| ReabstractIndirectToIndirect, |
| |
| /// Take a value out of the source inner result address, reabstract |
| /// it, and add it as the next direct outer result. |
| /// |
| /// Valid: reabstraction info, InnerAddress, OuterResult. |
| ReabstractIndirectToDirect, |
| |
| /// Take the next direct inner result, reabstract it, and initialize |
| /// the destination outer result address. |
| /// |
| /// Valid: reabstraction info, InnerResult, OuterAddress. |
| ReabstractDirectToIndirect, |
| |
| /// Take the next direct inner result, reabstract it, and add it as |
| /// the next direct outer result. |
| /// |
| /// Valid: reabstraction info, InnerResult, OuterResult. |
| ReabstractDirectToDirect, |
| }; |
| |
| Operation(Kind kind) : TheKind(kind) {} |
| |
| Kind TheKind; |
| |
| // Reabstraction information. Only valid for reabstraction kinds. |
| AbstractionPattern InnerOrigType = AbstractionPattern::getInvalid(); |
| AbstractionPattern OuterOrigType = AbstractionPattern::getInvalid(); |
| CanType InnerSubstType, OuterSubstType; |
| |
| union { |
| SILValue InnerResultAddr; |
| SILResultInfo InnerResult; |
| unsigned NumElements; |
| EnumElementDecl *SomeDecl; |
| }; |
| |
| union { |
| SILValue OuterResultAddr; |
| SILResultInfo OuterResult; |
| }; |
| }; |
| |
| struct PlanData { |
| ArrayRef<SILResultInfo> OuterResults; |
| ArrayRef<SILResultInfo> InnerResults; |
| SmallVectorImpl<SILValue> &InnerIndirectResultAddrs; |
| size_t NextOuterIndirectResultIndex; |
| }; |
| |
| SmallVector<Operation, 8> Operations; |
| public: |
| ResultPlanner(SILGenFunction &SGF, SILLocation loc) : SGF(SGF), Loc(loc) {} |
| |
| void plan(AbstractionPattern innerOrigType, CanType innerSubstType, |
| AbstractionPattern outerOrigType, CanType outerSubstType, |
| CanSILFunctionType innerFnType, CanSILFunctionType outerFnType, |
| SmallVectorImpl<SILValue> &innerIndirectResultAddrs) { |
| // Assert that the indirect results are set up like we expect. |
| assert(innerIndirectResultAddrs.empty()); |
| assert(SGF.F.begin()->args_size() |
| >= SILFunctionConventions(outerFnType, SGF.SGM.M) |
| .getNumIndirectSILResults()); |
| |
| innerIndirectResultAddrs.reserve( |
| SILFunctionConventions(innerFnType, SGF.SGM.M) |
| .getNumIndirectSILResults()); |
| |
| PlanData data = {outerFnType->getUnsubstitutedType(SGF.SGM.M)->getResults(), |
| innerFnType->getUnsubstitutedType(SGF.SGM.M)->getResults(), |
| innerIndirectResultAddrs, 0}; |
| |
| // Recursively walk the result types. |
| plan(innerOrigType, innerSubstType, outerOrigType, outerSubstType, data); |
| |
| // Assert that we consumed and produced all the indirect result |
| // information we needed. |
| assert(data.OuterResults.empty()); |
| assert(data.InnerResults.empty()); |
| assert(data.InnerIndirectResultAddrs.size() == |
| SILFunctionConventions(innerFnType, SGF.SGM.M) |
| .getNumIndirectSILResults()); |
| assert(data.NextOuterIndirectResultIndex |
| == SILFunctionConventions(outerFnType, SGF.SGM.M) |
| .getNumIndirectSILResults()); |
| } |
| |
| SILValue execute(SILValue innerResult); |
| |
| private: |
| void execute(ArrayRef<SILValue> innerDirectResults, |
| SmallVectorImpl<SILValue> &outerDirectResults); |
| void executeInnerTuple(SILValue innerElement, |
| SmallVector<SILValue, 4> &innerDirectResults); |
| |
| void plan(AbstractionPattern innerOrigType, CanType innerSubstType, |
| AbstractionPattern outerOrigType, CanType outerSubstType, |
| PlanData &planData); |
| |
| void planIntoIndirectResult(AbstractionPattern innerOrigType, |
| CanType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| PlanData &planData, |
| SILValue outerResultAddr); |
| void planTupleIntoIndirectResult(AbstractionPattern innerOrigType, |
| CanTupleType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| PlanData &planData, |
| SILValue outerResultAddr); |
| void planScalarIntoIndirectResult(AbstractionPattern innerOrigType, |
| CanType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| PlanData &planData, |
| SILResultInfo innerResult, |
| SILValue outerResultAddr); |
| |
| void planIntoDirectResult(AbstractionPattern innerOrigType, |
| CanType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| PlanData &planData, |
| SILResultInfo outerResult); |
| void planScalarIntoDirectResult(AbstractionPattern innerOrigType, |
| CanType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| PlanData &planData, |
| SILResultInfo innerResult, |
| SILResultInfo outerResult); |
| void planTupleIntoDirectResult(AbstractionPattern innerOrigType, |
| CanTupleType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| PlanData &planData, |
| SILResultInfo outerResult); |
| |
| void planFromIndirectResult(AbstractionPattern innerOrigType, |
| CanType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| PlanData &planData, |
| SILValue innerResultAddr); |
| void planTupleFromIndirectResult(AbstractionPattern innerOrigType, |
| CanTupleType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanTupleType outerSubstType, |
| PlanData &planData, |
| SILValue innerResultAddr); |
| void planTupleFromDirectResult(AbstractionPattern innerOrigType, |
| CanTupleType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanTupleType outerSubstType, |
| PlanData &planData, SILResultInfo innerResult); |
| void planScalarFromIndirectResult(AbstractionPattern innerOrigType, |
| CanType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| SILValue innerResultAddr, |
| SILResultInfo outerResult, |
| SILValue optOuterResultAddr); |
| |
| /// Claim the next inner result from the plan data. |
| SILResultInfo claimNextInnerResult(PlanData &data) { |
| return claimNext(data.InnerResults); |
| } |
| |
| /// Claim the next outer result from the plan data. If it's indirect, |
| /// grab its SILArgument. |
| std::pair<SILResultInfo, SILValue> claimNextOuterResult(PlanData &data) { |
| SILResultInfo result = claimNext(data.OuterResults); |
| |
| SILValue resultAddr; |
| if (SGF.silConv.isSILIndirect(result)) { |
| resultAddr = |
| SGF.F.begin()->getArgument(data.NextOuterIndirectResultIndex++); |
| } |
| |
| return { result, resultAddr }; |
| } |
| |
| /// Create a temporary address suitable for passing to the given inner |
| /// indirect result and add it as an inner indirect result. |
| SILValue addInnerIndirectResultTemporary(PlanData &data, |
| SILResultInfo innerResult) { |
| assert(SGF.silConv.isSILIndirect(innerResult) || |
| !SGF.silConv.useLoweredAddresses()); |
| auto temporary = |
| SGF.emitTemporaryAllocation(Loc, |
| SGF.getSILType(innerResult, CanSILFunctionType())); |
| data.InnerIndirectResultAddrs.push_back(temporary); |
| return temporary; |
| } |
| |
| /// Cause the next inner indirect result to be emitted directly into |
| /// the given outer result address. |
| void addInPlace(PlanData &data, SILValue outerResultAddr) { |
| data.InnerIndirectResultAddrs.push_back(outerResultAddr); |
| // Does not require an Operation. |
| } |
| |
| Operation &addOperation(Operation::Kind kind) { |
| Operations.emplace_back(kind); |
| return Operations.back(); |
| } |
| |
| void addDirectToDirect(SILResultInfo innerResult, SILResultInfo outerResult) { |
| auto &op = addOperation(Operation::DirectToDirect); |
| op.InnerResult = innerResult; |
| op.OuterResult = outerResult; |
| } |
| |
| void addDirectToIndirect(SILResultInfo innerResult, |
| SILValue outerResultAddr) { |
| auto &op = addOperation(Operation::DirectToIndirect); |
| op.InnerResult = innerResult; |
| op.OuterResultAddr = outerResultAddr; |
| } |
| |
| void addIndirectToDirect(SILValue innerResultAddr, |
| SILResultInfo outerResult) { |
| auto &op = addOperation(Operation::IndirectToDirect); |
| op.InnerResultAddr = innerResultAddr; |
| op.OuterResult = outerResult; |
| } |
| |
| void addIndirectToIndirect(SILValue innerResultAddr, |
| SILValue outerResultAddr) { |
| auto &op = addOperation(Operation::IndirectToIndirect); |
| op.InnerResultAddr = innerResultAddr; |
| op.OuterResultAddr = outerResultAddr; |
| } |
| |
| void addTupleDirect(unsigned numElements, SILResultInfo outerResult) { |
| auto &op = addOperation(Operation::TupleDirect); |
| op.NumElements = numElements; |
| op.OuterResult = outerResult; |
| } |
| |
| void addInjectOptionalDirect(EnumElementDecl *someDecl, |
| SILResultInfo outerResult) { |
| auto &op = addOperation(Operation::InjectOptionalDirect); |
| op.SomeDecl = someDecl; |
| op.OuterResult = outerResult; |
| } |
| |
| void addInjectOptionalIndirect(EnumElementDecl *someDecl, |
| SILValue outerResultAddr) { |
| auto &op = addOperation(Operation::InjectOptionalIndirect); |
| op.SomeDecl = someDecl; |
| op.OuterResultAddr = outerResultAddr; |
| } |
| |
| void addReabstractDirectToDirect(AbstractionPattern innerOrigType, |
| CanType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| SILResultInfo innerResult, |
| SILResultInfo outerResult) { |
| auto &op = addOperation(Operation::ReabstractDirectToDirect); |
| op.InnerResult = innerResult; |
| op.OuterResult = outerResult; |
| op.InnerOrigType = innerOrigType; |
| op.InnerSubstType = innerSubstType; |
| op.OuterOrigType = outerOrigType; |
| op.OuterSubstType = outerSubstType; |
| } |
| |
| void addReabstractDirectToIndirect(AbstractionPattern innerOrigType, |
| CanType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| SILResultInfo innerResult, |
| SILValue outerResultAddr) { |
| auto &op = addOperation(Operation::ReabstractDirectToIndirect); |
| op.InnerResult = innerResult; |
| op.OuterResultAddr = outerResultAddr; |
| op.InnerOrigType = innerOrigType; |
| op.InnerSubstType = innerSubstType; |
| op.OuterOrigType = outerOrigType; |
| op.OuterSubstType = outerSubstType; |
| } |
| |
| void addReabstractIndirectToDirect(AbstractionPattern innerOrigType, |
| CanType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| SILValue innerResultAddr, |
| SILResultInfo outerResult) { |
| auto &op = addOperation(Operation::ReabstractIndirectToDirect); |
| op.InnerResultAddr = innerResultAddr; |
| op.OuterResult = outerResult; |
| op.InnerOrigType = innerOrigType; |
| op.InnerSubstType = innerSubstType; |
| op.OuterOrigType = outerOrigType; |
| op.OuterSubstType = outerSubstType; |
| } |
| |
| void addReabstractIndirectToIndirect(AbstractionPattern innerOrigType, |
| CanType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| SILValue innerResultAddr, |
| SILValue outerResultAddr) { |
| auto &op = addOperation(Operation::ReabstractIndirectToIndirect); |
| op.InnerResultAddr = innerResultAddr; |
| op.OuterResultAddr = outerResultAddr; |
| op.InnerOrigType = innerOrigType; |
| op.InnerSubstType = innerSubstType; |
| op.OuterOrigType = outerOrigType; |
| op.OuterSubstType = outerSubstType; |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| /// Plan the reabstraction of a call result. |
| void ResultPlanner::plan(AbstractionPattern innerOrigType, |
| CanType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| PlanData &planData) { |
| // The substituted types must match up in tuple-ness and arity. |
| assert( |
| isa<TupleType>(innerSubstType) == isa<TupleType>(outerSubstType) || |
| (isa<TupleType>(innerSubstType) && |
| (outerSubstType->isAny() || outerSubstType->getOptionalObjectType()))); |
| assert(!isa<TupleType>(outerSubstType) || |
| cast<TupleType>(innerSubstType)->getNumElements() == |
| cast<TupleType>(outerSubstType)->getNumElements()); |
| |
| // If the inner abstraction pattern is a tuple, that result will be expanded. |
| if (innerOrigType.isTuple()) { |
| auto innerSubstTupleType = cast<TupleType>(innerSubstType); |
| |
| // If the outer abstraction pattern is also a tuple, that result will also |
| // be expanded, in parallel with the inner pattern. |
| if (outerOrigType.isTuple()) { |
| auto outerSubstTupleType = cast<TupleType>(outerSubstType); |
| assert(innerSubstTupleType->getNumElements() |
| == outerSubstTupleType->getNumElements()); |
| |
| // Otherwise, recursively descend into the tuples. |
| for (auto eltIndex : indices(innerSubstTupleType.getElementTypes())) { |
| plan(innerOrigType.getTupleElementType(eltIndex), |
| innerSubstTupleType.getElementType(eltIndex), |
| outerOrigType.getTupleElementType(eltIndex), |
| outerSubstTupleType.getElementType(eltIndex), |
| planData); |
| } |
| return; |
| } |
| |
| // Otherwise, the next outer result must be either opaque or optional. |
| // In either case, it corresponds to a single result. |
| auto outerResult = claimNextOuterResult(planData); |
| |
| // Base the plan on whether the single result is direct or indirect. |
| if (SGF.silConv.isSILIndirect(outerResult.first)) { |
| assert(outerResult.second); |
| planTupleIntoIndirectResult(innerOrigType, innerSubstTupleType, |
| outerOrigType, outerSubstType, |
| planData, outerResult.second); |
| } else { |
| planTupleIntoDirectResult(innerOrigType, innerSubstTupleType, |
| outerOrigType, outerSubstType, |
| planData, outerResult.first); |
| } |
| return; |
| } |
| |
| // Otherwise, the inner pattern is a scalar; claim the next inner result. |
| SILResultInfo innerResult = claimNextInnerResult(planData); |
| |
| assert((!outerOrigType.isTuple() || innerResult.isFormalIndirect()) && |
| "outer pattern is a tuple, inner pattern is not, but inner result is " |
| "not indirect?"); |
| |
| // If the inner result is a tuple, we need to expand from a temporary. |
| if (innerResult.isFormalIndirect() && outerOrigType.isTuple()) { |
| if (SGF.silConv.isSILIndirect(innerResult)) { |
| SILValue innerResultAddr = |
| addInnerIndirectResultTemporary(planData, innerResult); |
| planTupleFromIndirectResult( |
| innerOrigType, cast<TupleType>(innerSubstType), outerOrigType, |
| cast<TupleType>(outerSubstType), planData, innerResultAddr); |
| } else { |
| assert(!SGF.silConv.useLoweredAddresses() && |
| "Formal Indirect Results that are not SIL Indirect are only " |
| "allowed in opaque values mode"); |
| planTupleFromDirectResult(innerOrigType, cast<TupleType>(innerSubstType), |
| outerOrigType, cast<TupleType>(outerSubstType), |
| planData, innerResult); |
| } |
| return; |
| } |
| |
| // Otherwise, the outer pattern is a scalar; claim the next outer result. |
| auto outerResult = claimNextOuterResult(planData); |
| |
| // If the outer result is indirect, plan to emit into that. |
| if (SGF.silConv.isSILIndirect(outerResult.first)) { |
| assert(outerResult.second); |
| planScalarIntoIndirectResult(innerOrigType, innerSubstType, |
| outerOrigType, outerSubstType, |
| planData, innerResult, outerResult.second); |
| |
| } else { |
| planScalarIntoDirectResult(innerOrigType, innerSubstType, |
| outerOrigType, outerSubstType, |
| planData, innerResult, outerResult.first); |
| } |
| } |
| |
| /// Plan the emission of a call result into an outer result address. |
| void ResultPlanner::planIntoIndirectResult(AbstractionPattern innerOrigType, |
| CanType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| PlanData &planData, |
| SILValue outerResultAddr) { |
| // outerOrigType can be a tuple if we're also injecting into an optional. |
| |
| // If the inner pattern is a tuple, expand it. |
| if (innerOrigType.isTuple()) { |
| planTupleIntoIndirectResult(innerOrigType, cast<TupleType>(innerSubstType), |
| outerOrigType, outerSubstType, |
| planData, outerResultAddr); |
| |
| // Otherwise, it's scalar. |
| } else { |
| // Claim the next inner result. |
| SILResultInfo innerResult = claimNextInnerResult(planData); |
| |
| planScalarIntoIndirectResult(innerOrigType, innerSubstType, |
| outerOrigType, outerSubstType, |
| planData, innerResult, outerResultAddr); |
| } |
| } |
| |
| /// Plan the emission of a call result into an outer result address, |
| /// given that the inner abstraction pattern is a tuple. |
| void |
| ResultPlanner::planTupleIntoIndirectResult(AbstractionPattern innerOrigType, |
| CanTupleType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| PlanData &planData, |
| SILValue outerResultAddr) { |
| assert(innerOrigType.isTuple()); |
| // outerOrigType can be a tuple if we're doing something like |
| // injecting into an optional tuple. |
| |
| auto outerSubstTupleType = dyn_cast<TupleType>(outerSubstType); |
| |
| // If the outer type is not a tuple, it must be optional. |
| if (!outerSubstTupleType) { |
| // Figure out what kind of optional it is. |
| CanType outerSubstObjectType = outerSubstType.getOptionalObjectType(); |
| if (outerSubstObjectType) { |
| auto someDecl = SGF.getASTContext().getOptionalSomeDecl(); |
| |
| // Prepare the value slot in the optional value. |
| SILType outerObjectType = |
| outerResultAddr->getType().getOptionalObjectType(); |
| SILValue outerObjectResultAddr |
| = SGF.B.createInitEnumDataAddr(Loc, outerResultAddr, someDecl, |
| outerObjectType); |
| |
| // Emit into that address. |
| planTupleIntoIndirectResult( |
| innerOrigType, innerSubstType, outerOrigType.getOptionalObjectType(), |
| outerSubstObjectType, planData, outerObjectResultAddr); |
| |
| // Add an operation to finish the enum initialization. |
| addInjectOptionalIndirect(someDecl, outerResultAddr); |
| return; |
| } |
| |
| assert(outerSubstType->isAny()); |
| |
| // Prepare the value slot in the existential. |
| auto opaque = AbstractionPattern::getOpaque(); |
| SILValue outerConcreteResultAddr |
| = SGF.B.createInitExistentialAddr(Loc, outerResultAddr, innerSubstType, |
| SGF.getLoweredType(opaque, innerSubstType), |
| /*conformances=*/{}); |
| |
| // Emit into that address. |
| planTupleIntoIndirectResult(innerOrigType, innerSubstType, |
| innerOrigType, innerSubstType, |
| planData, outerConcreteResultAddr); |
| return; |
| } |
| |
| assert(innerSubstType->getNumElements() |
| == outerSubstTupleType->getNumElements()); |
| |
| for (auto eltIndex : indices(innerSubstType.getElementTypes())) { |
| // Project the address of the element. |
| SILValue outerEltResultAddr = |
| SGF.B.createTupleElementAddr(Loc, outerResultAddr, eltIndex); |
| |
| // Plan to emit into that location. |
| planIntoIndirectResult(innerOrigType.getTupleElementType(eltIndex), |
| innerSubstType.getElementType(eltIndex), |
| outerOrigType.getTupleElementType(eltIndex), |
| outerSubstTupleType.getElementType(eltIndex), |
| planData, outerEltResultAddr); |
| } |
| } |
| |
| /// Plan the emission of a call result as a single outer direct result. |
| void |
| ResultPlanner::planIntoDirectResult(AbstractionPattern innerOrigType, |
| CanType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| PlanData &planData, |
| SILResultInfo outerResult) { |
| assert(!outerOrigType.isTuple() || !SGF.silConv.useLoweredAddresses()); |
| |
| // If the inner pattern is a tuple, expand it. |
| if (innerOrigType.isTuple()) { |
| planTupleIntoDirectResult(innerOrigType, cast<TupleType>(innerSubstType), |
| outerOrigType, outerSubstType, |
| planData, outerResult); |
| |
| // Otherwise, it's scalar. |
| } else { |
| // Claim the next inner result. |
| SILResultInfo innerResult = claimNextInnerResult(planData); |
| |
| planScalarIntoDirectResult(innerOrigType, innerSubstType, |
| outerOrigType, outerSubstType, |
| planData, innerResult, outerResult); |
| } |
| } |
| |
| /// Plan the emission of a call result as a single outer direct result, |
| /// given that the inner abstraction pattern is a tuple. |
| void |
| ResultPlanner::planTupleIntoDirectResult(AbstractionPattern innerOrigType, |
| CanTupleType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| PlanData &planData, |
| SILResultInfo outerResult) { |
| assert(innerOrigType.isTuple()); |
| |
| auto outerSubstTupleType = dyn_cast<TupleType>(outerSubstType); |
| |
| // If the outer type is not a tuple, it must be optional or we are under |
| // opaque value mode |
| if (!outerSubstTupleType) { |
| CanType outerSubstObjectType = outerSubstType.getOptionalObjectType(); |
| |
| if (outerSubstObjectType) { |
| auto someDecl = SGF.getASTContext().getOptionalSomeDecl(); |
| SILType outerObjectType = |
| SGF.getSILType(outerResult, CanSILFunctionType()) |
| .getOptionalObjectType(); |
| SILResultInfo outerObjectResult(outerObjectType.getASTType(), |
| outerResult.getConvention()); |
| |
| // Plan to leave the tuple elements as a single direct outer result. |
| planTupleIntoDirectResult( |
| innerOrigType, innerSubstType, outerOrigType.getOptionalObjectType(), |
| outerSubstObjectType, planData, outerObjectResult); |
| |
| // Take that result and inject it into an optional. |
| addInjectOptionalDirect(someDecl, outerResult); |
| return; |
| } else { |
| assert(!SGF.silConv.useLoweredAddresses() && |
| "inner type was a tuple but outer type was neither a tuple nor " |
| "optional nor are we under opaque value mode"); |
| assert(outerSubstType->isAny()); |
| |
| auto opaque = AbstractionPattern::getOpaque(); |
| auto anyType = SGF.getLoweredType(opaque, outerSubstType); |
| auto outerResultAddr = SGF.emitTemporaryAllocation(Loc, anyType); |
| |
| SILValue outerConcreteResultAddr = SGF.B.createInitExistentialAddr( |
| Loc, outerResultAddr, innerSubstType, |
| SGF.getLoweredType(opaque, innerSubstType), /*conformances=*/{}); |
| |
| planTupleIntoIndirectResult(innerOrigType, innerSubstType, innerOrigType, |
| innerSubstType, planData, |
| outerConcreteResultAddr); |
| |
| addReabstractIndirectToDirect(innerOrigType, innerSubstType, |
| outerOrigType, outerSubstType, |
| outerConcreteResultAddr, outerResult); |
| return; |
| } |
| } |
| |
| // Otherwise, the outer type is a tuple. |
| assert(innerSubstType->getNumElements() |
| == outerSubstTupleType->getNumElements()); |
| |
| // Create direct outer results for each of the elements. |
| for (auto eltIndex : indices(innerSubstType.getElementTypes())) { |
| auto outerEltType = |
| SGF.getSILType(outerResult, CanSILFunctionType()) |
| .getTupleElementType(eltIndex); |
| SILResultInfo outerEltResult(outerEltType.getASTType(), |
| outerResult.getConvention()); |
| |
| planIntoDirectResult(innerOrigType.getTupleElementType(eltIndex), |
| innerSubstType.getElementType(eltIndex), |
| outerOrigType.getTupleElementType(eltIndex), |
| outerSubstTupleType.getElementType(eltIndex), |
| planData, outerEltResult); |
| } |
| |
| // Bind them together into a single tuple. |
| addTupleDirect(innerSubstType->getNumElements(), outerResult); |
| } |
| |
| /// Plan the emission of a call result as a single outer direct result, |
| /// given that the inner abstraction pattern is not a tuple. |
| void ResultPlanner::planScalarIntoDirectResult(AbstractionPattern innerOrigType, |
| CanType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| PlanData &planData, |
| SILResultInfo innerResult, |
| SILResultInfo outerResult) { |
| assert(!innerOrigType.isTuple()); |
| assert(!outerOrigType.isTuple()); |
| |
| // If the inner result is indirect, plan to emit from that. |
| if (SGF.silConv.isSILIndirect(innerResult)) { |
| SILValue innerResultAddr = |
| addInnerIndirectResultTemporary(planData, innerResult); |
| planScalarFromIndirectResult(innerOrigType, innerSubstType, |
| outerOrigType, outerSubstType, |
| innerResultAddr, outerResult, SILValue()); |
| return; |
| } |
| |
| // Otherwise, we have two direct results. |
| |
| // If there's no abstraction difference, it's just returned directly. |
| if (SGF.getSILType(innerResult, CanSILFunctionType()) |
| == SGF.getSILType(outerResult, CanSILFunctionType())) { |
| addDirectToDirect(innerResult, outerResult); |
| |
| // Otherwise, we need to reabstract. |
| } else { |
| addReabstractDirectToDirect(innerOrigType, innerSubstType, |
| outerOrigType, outerSubstType, |
| innerResult, outerResult); |
| } |
| } |
| |
| /// Plan the emission of a call result into an outer result address, |
| /// given that the inner abstraction pattern is not a tuple. |
| void |
| ResultPlanner::planScalarIntoIndirectResult(AbstractionPattern innerOrigType, |
| CanType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| PlanData &planData, |
| SILResultInfo innerResult, |
| SILValue outerResultAddr) { |
| assert(!innerOrigType.isTuple()); |
| assert(!outerOrigType.isTuple()); |
| |
| bool hasAbstractionDifference = |
| (innerResult.getInterfaceType() != outerResultAddr->getType().getASTType()); |
| |
| // If the inner result is indirect, we need some memory to emit it into. |
| if (SGF.silConv.isSILIndirect(innerResult)) { |
| // If there's no abstraction difference, that can just be |
| // in-place into the outer result address. |
| if (!hasAbstractionDifference) { |
| addInPlace(planData, outerResultAddr); |
| |
| // Otherwise, we'll need a temporary. |
| } else { |
| SILValue innerResultAddr = |
| addInnerIndirectResultTemporary(planData, innerResult); |
| addReabstractIndirectToIndirect(innerOrigType, innerSubstType, |
| outerOrigType, outerSubstType, |
| innerResultAddr, outerResultAddr); |
| } |
| |
| // Otherwise, the inner result is direct. |
| } else { |
| // If there's no abstraction difference, we just need to store. |
| if (!hasAbstractionDifference) { |
| addDirectToIndirect(innerResult, outerResultAddr); |
| |
| // Otherwise, we need to reabstract and store. |
| } else { |
| addReabstractDirectToIndirect(innerOrigType, innerSubstType, |
| outerOrigType, outerSubstType, |
| innerResult, outerResultAddr); |
| } |
| } |
| } |
| |
| /// Plan the emission of a call result from an inner result address. |
| void ResultPlanner::planFromIndirectResult(AbstractionPattern innerOrigType, |
| CanType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| PlanData &planData, |
| SILValue innerResultAddr) { |
| assert(!innerOrigType.isTuple()); |
| |
| if (outerOrigType.isTuple()) { |
| planTupleFromIndirectResult(innerOrigType, cast<TupleType>(innerSubstType), |
| outerOrigType, cast<TupleType>(outerSubstType), |
| planData, innerResultAddr); |
| } else { |
| auto outerResult = claimNextOuterResult(planData); |
| planScalarFromIndirectResult(innerOrigType, innerSubstType, |
| outerOrigType, outerSubstType, |
| innerResultAddr, |
| outerResult.first, outerResult.second); |
| } |
| } |
| |
| /// Plan the emission of a call result from an inner result address, given |
| /// that the outer abstraction pattern is a tuple. |
| void |
| ResultPlanner::planTupleFromIndirectResult(AbstractionPattern innerOrigType, |
| CanTupleType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanTupleType outerSubstType, |
| PlanData &planData, |
| SILValue innerResultAddr) { |
| assert(!innerOrigType.isTuple()); |
| assert(innerSubstType->getNumElements() == outerSubstType->getNumElements()); |
| assert(outerOrigType.isTuple()); |
| |
| for (auto eltIndex : indices(innerSubstType.getElementTypes())) { |
| // Project the address of the element. |
| SILValue innerEltResultAddr = |
| SGF.B.createTupleElementAddr(Loc, innerResultAddr, eltIndex); |
| |
| // Plan to expand from that location. |
| planFromIndirectResult(innerOrigType.getTupleElementType(eltIndex), |
| innerSubstType.getElementType(eltIndex), |
| outerOrigType.getTupleElementType(eltIndex), |
| outerSubstType.getElementType(eltIndex), |
| planData, innerEltResultAddr); |
| } |
| } |
| |
| void ResultPlanner::planTupleFromDirectResult(AbstractionPattern innerOrigType, |
| CanTupleType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanTupleType outerSubstType, |
| PlanData &planData, |
| SILResultInfo innerResult) { |
| |
| assert(!innerOrigType.isTuple()); |
| auto outerSubstTupleType = dyn_cast<TupleType>(outerSubstType); |
| |
| assert(outerSubstTupleType && "Outer type must be a tuple"); |
| assert(innerSubstType->getNumElements() == |
| outerSubstTupleType->getNumElements()); |
| |
| // Create direct outer results for each of the elements. |
| for (auto eltIndex : indices(innerSubstType.getElementTypes())) { |
| AbstractionPattern newOuterOrigType = |
| outerOrigType.getTupleElementType(eltIndex); |
| AbstractionPattern newInnerOrigType = |
| innerOrigType.getTupleElementType(eltIndex); |
| if (newOuterOrigType.isTuple()) { |
| planTupleFromDirectResult( |
| newInnerOrigType, |
| cast<TupleType>(innerSubstType.getElementType(eltIndex)), |
| newOuterOrigType, |
| cast<TupleType>(outerSubstTupleType.getElementType(eltIndex)), |
| planData, innerResult); |
| continue; |
| } |
| |
| auto outerResult = claimNextOuterResult(planData); |
| auto elemType = outerSubstTupleType.getElementType(eltIndex); |
| SILResultInfo eltResult(elemType, outerResult.first.getConvention()); |
| planScalarIntoDirectResult( |
| newInnerOrigType, innerSubstType.getElementType(eltIndex), |
| newOuterOrigType, outerSubstTupleType.getElementType(eltIndex), |
| planData, eltResult, outerResult.first); |
| } |
| } |
| |
| /// Plan the emission of a call result from an inner result address, |
| /// given that the outer abstraction pattern is not a tuple. |
| void |
| ResultPlanner::planScalarFromIndirectResult(AbstractionPattern innerOrigType, |
| CanType innerSubstType, |
| AbstractionPattern outerOrigType, |
| CanType outerSubstType, |
| SILValue innerResultAddr, |
| SILResultInfo outerResult, |
| SILValue optOuterResultAddr) { |
| assert(!innerOrigType.isTuple()); |
| assert(!outerOrigType.isTuple()); |
| assert(SGF.silConv.isSILIndirect(outerResult) == bool(optOuterResultAddr)); |
| |
| bool hasAbstractionDifference = |
| (innerResultAddr->getType().getASTType() != outerResult.getInterfaceType()); |
| |
| // The outer result can be indirect, and it doesn't necessarily have an |
| // abstraction difference. Note that we should only end up in this path |
| // in cases where simply forwarding the outer result address wasn't possible. |
| |
| if (SGF.silConv.isSILIndirect(outerResult)) { |
| assert(optOuterResultAddr); |
| if (!hasAbstractionDifference) { |
| addIndirectToIndirect(innerResultAddr, optOuterResultAddr); |
| } else { |
| addReabstractIndirectToIndirect(innerOrigType, innerSubstType, |
| outerOrigType, outerSubstType, |
| innerResultAddr, optOuterResultAddr); |
| } |
| } else { |
| if (!hasAbstractionDifference) { |
| addIndirectToDirect(innerResultAddr, outerResult); |
| } else { |
| addReabstractIndirectToDirect(innerOrigType, innerSubstType, |
| outerOrigType, outerSubstType, |
| innerResultAddr, outerResult); |
| } |
| } |
| } |
| |
| void ResultPlanner::executeInnerTuple( |
| SILValue innerElement, SmallVector<SILValue, 4> &innerDirectResults) { |
| // NOTE: We know that our value is at +1 here. |
| assert(innerElement->getType().getAs<TupleType>() && |
| "Only supports tuple inner types"); |
| |
| SGF.B.emitDestructureValueOperation( |
| Loc, innerElement, [&](unsigned index, SILValue elt) { |
| if (elt->getType().is<TupleType>()) |
| return executeInnerTuple(elt, innerDirectResults); |
| innerDirectResults.push_back(elt); |
| }); |
| } |
| |
| SILValue ResultPlanner::execute(SILValue innerResult) { |
| // The code emission here assumes that we don't need to have |
| // active cleanups for all the result values we're not actively |
| // transforming. In other words, it's not "exception-safe". |
| |
| // Explode the inner direct results. |
| SmallVector<SILValue, 4> innerDirectResults; |
| auto innerResultTupleType = innerResult->getType().getAs<TupleType>(); |
| if (!innerResultTupleType) { |
| innerDirectResults.push_back(innerResult); |
| } else { |
| { |
| Scope S(SGF.Cleanups, CleanupLocation::get(Loc)); |
| |
| // First create an rvalue cleanup for our direct result. |
| assert(innerResult.getOwnershipKind().isCompatibleWith( |
| OwnershipKind::Owned)); |
| executeInnerTuple(innerResult, innerDirectResults); |
| // Then allow the cleanups to be emitted in the proper reverse order. |
| } |
| } |
| |
| // Translate the result values. |
| SmallVector<SILValue, 4> outerDirectResults; |
| execute(innerDirectResults, outerDirectResults); |
| |
| // Implode the outer direct results. |
| SILValue outerResult; |
| if (outerDirectResults.size() == 1) { |
| outerResult = outerDirectResults[0]; |
| } else { |
| outerResult = SGF.B.createTuple(Loc, outerDirectResults); |
| } |
| |
| return outerResult; |
| } |
| |
| void ResultPlanner::execute(ArrayRef<SILValue> innerDirectResults, |
| SmallVectorImpl<SILValue> &outerDirectResults) { |
| // A helper function to claim an inner direct result. |
| auto claimNextInnerDirectResult = [&](SILResultInfo result) -> ManagedValue { |
| auto resultValue = claimNext(innerDirectResults); |
| assert(resultValue->getType() == SGF.getSILType(result, CanSILFunctionType())); |
| auto &resultTL = SGF.getTypeLowering(result.getInterfaceType()); |
| switch (result.getConvention()) { |
| case ResultConvention::Indirect: |
| assert(!SGF.silConv.isSILIndirect(result) |
| && "claiming indirect result as direct!"); |
| LLVM_FALLTHROUGH; |
| case ResultConvention::Owned: |
| case ResultConvention::Autoreleased: |
| return SGF.emitManagedRValueWithCleanup(resultValue, resultTL); |
| case ResultConvention::UnownedInnerPointer: |
| // FIXME: We can't reasonably lifetime-extend an inner-pointer result |
| // through a thunk. We don't know which parameter to the thunk was |
| // originally 'self'. |
| SGF.SGM.diagnose(Loc.getSourceLoc(), diag::not_implemented, |
| "reabstraction of returns_inner_pointer function"); |
| LLVM_FALLTHROUGH; |
| case ResultConvention::Unowned: |
| return SGF.emitManagedRetain(Loc, resultValue, resultTL); |
| } |
| llvm_unreachable("bad result convention!"); |
| }; |
| |
| // A helper function to add an outer direct result. |
| auto addOuterDirectResult = [&](ManagedValue resultValue, |
| SILResultInfo result) { |
| resultValue = applyTrivialConversions(SGF, Loc, resultValue, |
| SGF.getSILTypeInContext(result, CanSILFunctionType())); |
| outerDirectResults.push_back(resultValue.forward(SGF)); |
| }; |
| |
| auto emitReabstract = |
| [&](Operation &op, bool innerIsIndirect, bool outerIsIndirect) { |
| // Set up the inner result. |
| ManagedValue innerResult; |
| if (innerIsIndirect) { |
| innerResult = SGF.emitManagedBufferWithCleanup(op.InnerResultAddr); |
| } else { |
| innerResult = claimNextInnerDirectResult(op.InnerResult); |
| } |
| |
| // Set up the context into which to emit the outer result. |
| SGFContext outerResultCtxt; |
| Optional<TemporaryInitialization> outerResultInit; |
| SILType outerResultTy; |
| if (outerIsIndirect) { |
| outerResultTy = op.OuterResultAddr->getType(); |
| outerResultInit.emplace(op.OuterResultAddr, CleanupHandle::invalid()); |
| outerResultCtxt = SGFContext(&*outerResultInit); |
| } else { |
| outerResultTy = |
| SGF.F.mapTypeIntoContext( |
| SGF.getSILType(op.OuterResult, CanSILFunctionType())); |
| } |
| |
| // Perform the translation. |
| auto translated = |
| SGF.emitTransformedValue(Loc, innerResult, |
| op.InnerOrigType, op.InnerSubstType, |
| op.OuterOrigType, op.OuterSubstType, |
| outerResultTy, outerResultCtxt); |
| |
| // If the outer is indirect, force it into the context. |
| if (outerIsIndirect) { |
| if (!translated.isInContext()) { |
| translated.forwardInto(SGF, Loc, op.OuterResultAddr); |
| } |
| |
| // Otherwise, it's a direct result. |
| } else { |
| addOuterDirectResult(translated, op.OuterResult); |
| } |
| }; |
| |
| // Execute each operation. |
| for (auto &op : Operations) { |
| switch (op.TheKind) { |
| case Operation::DirectToDirect: { |
| auto result = claimNextInnerDirectResult(op.InnerResult); |
| addOuterDirectResult(result, op.OuterResult); |
| continue; |
| } |
| |
| case Operation::DirectToIndirect: { |
| auto result = claimNextInnerDirectResult(op.InnerResult); |
| SGF.B.emitStoreValueOperation(Loc, result.forward(SGF), |
| op.OuterResultAddr, |
| StoreOwnershipQualifier::Init); |
| continue; |
| } |
| |
| case Operation::IndirectToDirect: { |
| auto resultAddr = op.InnerResultAddr; |
| auto &resultTL = SGF.getTypeLowering(resultAddr->getType()); |
| auto result = SGF.emitManagedRValueWithCleanup( |
| resultTL.emitLoad(SGF.B, Loc, resultAddr, |
| LoadOwnershipQualifier::Take), |
| resultTL); |
| addOuterDirectResult(result, op.OuterResult); |
| continue; |
| } |
| |
| case Operation::IndirectToIndirect: { |
| // The type could be address-only; just take. |
| SGF.B.createCopyAddr(Loc, op.InnerResultAddr, op.OuterResultAddr, |
| IsTake, IsInitialization); |
| continue; |
| } |
| |
| case Operation::ReabstractIndirectToIndirect: |
| emitReabstract(op, /*indirect source*/ true, /*indirect dest*/ true); |
| continue; |
| case Operation::ReabstractIndirectToDirect: |
| emitReabstract(op, /*indirect source*/ true, /*indirect dest*/ false); |
| continue; |
| case Operation::ReabstractDirectToIndirect: |
| emitReabstract(op, /*indirect source*/ false, /*indirect dest*/ true); |
| continue; |
| case Operation::ReabstractDirectToDirect: |
| emitReabstract(op, /*indirect source*/ false, /*indirect dest*/ false); |
| continue; |
| |
| case Operation::TupleDirect: { |
| auto firstEltIndex = outerDirectResults.size() - op.NumElements; |
| auto elts = makeArrayRef(outerDirectResults).slice(firstEltIndex); |
| auto tupleType = SGF.F.mapTypeIntoContext( |
| SGF.getSILType(op.OuterResult, CanSILFunctionType())); |
| auto tuple = SGF.B.createTuple(Loc, tupleType, elts); |
| outerDirectResults.resize(firstEltIndex); |
| outerDirectResults.push_back(tuple); |
| continue; |
| } |
| |
| case Operation::InjectOptionalDirect: { |
| SILValue value = outerDirectResults.pop_back_val(); |
| auto tupleType = SGF.F.mapTypeIntoContext( |
| SGF.getSILType(op.OuterResult, CanSILFunctionType())); |
| SILValue optValue = SGF.B.createEnum(Loc, |