//===--- SILGenMaterializeForSet.cpp - Open-coded materializeForSet -------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // Emission of materializeForSet. // //===----------------------------------------------------------------------===// #include "SILGen.h" #include "ArgumentSource.h" #include "LValue.h" #include "RValue.h" #include "Scope.h" #include "Initialization.h" #include "swift/AST/AST.h" #include "swift/AST/DiagnosticsSIL.h" #include "swift/AST/Decl.h" #include "swift/AST/Types.h" #include "swift/AST/DiagnosticsCommon.h" #include "swift/AST/Mangle.h" #include "swift/SIL/PrettyStackTrace.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILUndef.h" #include "swift/SIL/TypeLowering.h" #include "llvm/Support/raw_ostream.h" #include "ASTVisitor.h" using namespace swift; using namespace Lowering; namespace { static std::string getMaterializeForSetCallbackName(ProtocolConformance *conformance, FuncDecl *requirement) { DeclContext *dc = requirement; ClosureExpr closure(/*patterns*/ nullptr, /*throws*/ SourceLoc(), /*arrow*/ SourceLoc(), /*in*/ SourceLoc(), /*result*/ TypeLoc(), /*discriminator*/ 0, /*context*/ requirement); closure.setType(TupleType::getEmpty(dc->getASTContext())); closure.getCaptureInfo().setGenericParamCaptures(true); Mangle::Mangler mangler; if (conformance) { // Concrete witness thunk for a conformance: // // Mangle this as if it were a conformance thunk for a closure // within the requirement. mangler.append("_TTW"); mangler.mangleProtocolConformance(conformance); } else { // Default witness thunk or concrete implementation: // // Mangle this as if it were a closure within the requirement. mangler.append("_T"); } mangler.mangleClosureEntity(&closure, /*uncurryLevel=*/1); return mangler.finalize(); } /// A helper class for emitting materializeForSet. /// /// The formal type of materializeForSet is: /// /// (self: Self) -> (temporary: Builtin.RawPointer, /// inout storage: Builtin.ValueBuffer, /// indices...) /// -> (address: Builtin.RawPointer, /// callback: (@thin (address: Builtin.RawPointer, /// inout storage: Builtin.ValueBuffer, /// inout self: Self, /// @thick selfType: Self.Type) -> ())?) /// struct MaterializeForSetEmitter { SILGenModule &SGM; AbstractStorageDecl *RequirementStorage; AbstractionPattern RequirementStoragePattern; SILType RequirementStorageType; FuncDecl *Witness; AbstractStorageDecl *WitnessStorage; AbstractionPattern WitnessStoragePattern; ArrayRef WitnessSubs; CanGenericSignature GenericSig; GenericParamList *GenericParams; SILLinkage Linkage; // Assume that we don't need to reabstract 'self'. Right now, // that's always true; if we ever reabstract Optional (or other // nominal types) and allow "partial specialization" extensions, // this will break, and we'll have to do inout-translation in // the callback buffer. CanType SelfInterfaceType; CanType SubstSelfType; CanType SubstStorageType; AccessSemantics TheAccessSemantics; bool IsSuper; std::string CallbackName; SILType WitnessStorageType; private: MaterializeForSetEmitter(SILGenModule &SGM, FuncDecl *witness, ArrayRef witnessSubs, Type selfInterfaceType, Type selfType) : SGM(SGM), RequirementStorage(nullptr), RequirementStoragePattern(AbstractionPattern::getInvalid()), Witness(witness), WitnessStorage(witness->getAccessorStorageDecl()), WitnessStoragePattern(AbstractionPattern::getInvalid()), WitnessSubs(witnessSubs), GenericParams(nullptr), SelfInterfaceType(selfInterfaceType->getCanonicalType()), SubstSelfType(selfType->getCanonicalType()), TheAccessSemantics(AccessSemantics::Ordinary), IsSuper(false) { // Determine the formal type of the storage. CanType witnessIfaceType = WitnessStorage->getInterfaceType()->getCanonicalType(); if (isa(WitnessStorage)) witnessIfaceType = cast(witnessIfaceType).getResult(); SubstStorageType = getSubstWitnessInterfaceType(witnessIfaceType); WitnessStoragePattern = SGM.Types.getAbstractionPattern(WitnessStorage); WitnessStorageType = SGM.Types.getLoweredType(WitnessStoragePattern, SubstStorageType) .getObjectType(); } public: static MaterializeForSetEmitter forWitnessThunk(SILGenModule &SGM, ProtocolConformance *conformance, FuncDecl *requirement, FuncDecl *witness, ArrayRef witnessSubs) { Type selfInterfaceType, selfType; if (conformance) { selfInterfaceType = conformance->getInterfaceType(); selfType = conformance->getType(); } else { auto *proto = cast(requirement->getDeclContext()); selfInterfaceType = proto->getProtocolSelf()->getDeclaredType(); selfType = proto->getProtocolSelf()->getArchetype(); } MaterializeForSetEmitter emitter(SGM, witness, witnessSubs, selfInterfaceType, selfType); if (conformance) { if (auto signature = conformance->getGenericSignature()) emitter.GenericSig = signature->getCanonicalSignature(); emitter.GenericParams = conformance->getGenericParams(); emitter.Linkage = SGM.Types.getLinkageForProtocolConformance( conformance->getRootNormalConformance(), ForDefinition); } else { auto signature = requirement->getGenericSignatureOfContext(); emitter.GenericSig = signature->getCanonicalSignature(); emitter.GenericParams = requirement->getGenericParamsOfContext(); // FIXME: really should be the protocol's linkage emitter.Linkage = SILLinkage::Public; } emitter.RequirementStorage = requirement->getAccessorStorageDecl(); // Determine the desired abstraction pattern of the storage type // in the requirement and the witness. emitter.RequirementStoragePattern = SGM.Types.getAbstractionPattern(emitter.RequirementStorage); emitter.RequirementStorageType = SGM.Types.getLoweredType(emitter.RequirementStoragePattern, emitter.SubstStorageType) .getObjectType(); emitter.CallbackName = getMaterializeForSetCallbackName( conformance, requirement); return emitter; } static MaterializeForSetEmitter forConcreteImplementation(SILGenModule &SGM, FuncDecl *witness, ArrayRef witnessSubs) { Type selfInterfaceType = witness->computeInterfaceSelfType(false)->getLValueOrInOutObjectType(); Type selfType = witness->computeSelfType()->getLValueOrInOutObjectType(); MaterializeForSetEmitter emitter(SGM, witness, witnessSubs, selfInterfaceType, selfType); SILDeclRef constant(witness); auto constantInfo = SGM.Types.getConstantInfo(constant); if (auto signature = witness->getGenericSignatureOfContext()) emitter.GenericSig = signature->getCanonicalSignature(); emitter.GenericParams = constantInfo.ContextGenericParams; emitter.Linkage = constant.getLinkage(ForDefinition); emitter.RequirementStorage = emitter.WitnessStorage; emitter.RequirementStoragePattern = emitter.WitnessStoragePattern; emitter.RequirementStorageType = emitter.WitnessStorageType; // When we're emitting a standard implementation, use direct semantics. // If we used TheAccessSemantics::Ordinary here, the only downside would // be unnecessary vtable dispatching for class materializeForSets. if (!emitter.WitnessStorage->hasObservers() && (emitter.WitnessStorage->hasStorage() || emitter.WitnessStorage->hasAddressors())) emitter.TheAccessSemantics = AccessSemantics::DirectToStorage; else if (emitter.WitnessStorage->hasClangNode() || emitter.WitnessStorage->getAttrs().hasAttribute()) emitter.TheAccessSemantics = AccessSemantics::Ordinary; else emitter.TheAccessSemantics = AccessSemantics::DirectToAccessor; emitter.CallbackName = getMaterializeForSetCallbackName( /*conformance=*/nullptr, witness); return emitter; } bool shouldOpenCode() const { // We need to open-code if there's an abstraction difference in the // result address. if (RequirementStorageType != WitnessStorageType) return true; // We also need to open-code if the witness is defined in a // protocol context because IRGen won't know how to reconstruct // the type parameters. (In principle, this can be done in the // callback storage if we need to.) if (Witness->getDeclContext()->getAsProtocolOrProtocolExtensionContext()) return true; return false; } void emit(SILGenFunction &gen); SILValue emitUsingStorage(SILGenFunction &gen, SILLocation loc, ManagedValue self, RValue &&indices); SILValue emitUsingAddressor(SILGenFunction &gen, SILLocation loc, ManagedValue self, RValue &&indices, SILValue callbackBuffer, SILFunction *&callback); SILFunction *createAddressorCallback(SILFunction &F, SILType ownerType, AddressorKind addressorKind); SILValue emitUsingGetterSetter(SILGenFunction &gen, SILLocation loc, ManagedValue self, RValue &&indices, SILValue resultBuffer, SILValue callbackBuffer, SILFunction *&callback); SILFunction *createSetterCallback(SILFunction &F, const TypeLowering *indicesTL, CanType indicesFormalType); using GeneratorFn = llvm::function_ref; SILFunction *createCallback(SILFunction &F, GeneratorFn generator); RValue collectIndicesFromParameters(SILGenFunction &gen, SILLocation loc, ArrayRef sourceIndices); LValue buildSelfLValue(SILGenFunction &gen, SILLocation loc, ManagedValue self) { // All of the complexity here is tied up with class types. If the // substituted type isn't a reference type, then we can't have a // class-bounded protocol or inheritance, and the simple case just // works. AbstractionPattern selfPattern(SubstSelfType); // Metatypes and bases of non-mutating setters on value types // are always rvalues. if (!SubstSelfType->mayHaveSuperclass()) { if (self.getType().isObject()) return LValue::forValue(self, SubstSelfType); else { if (!self.isLValue()) self = ManagedValue::forLValue(self.getValue()); return LValue::forAddress(self, selfPattern, SubstSelfType); } } CanType witnessSelfType = Witness->computeInterfaceSelfType(false)->getCanonicalType(); witnessSelfType = getSubstWitnessInterfaceType(witnessSelfType); if (auto selfTuple = dyn_cast(witnessSelfType)) { assert(selfTuple->getNumElements() == 1); witnessSelfType = selfTuple.getElementType(0); } witnessSelfType = witnessSelfType.getLValueOrInOutObjectType(); // Eagerly loading here could cause an unnecessary // load+materialize in some cases, but it's not really important. SILValue selfValue = self.getValue(); if (selfValue->getType().isAddress()) { selfValue = gen.B.createLoad(loc, selfValue); } // Do a derived-to-base conversion if necessary. if (witnessSelfType != SubstSelfType) { auto selfSILType = SILType::getPrimitiveObjectType(witnessSelfType); selfValue = gen.B.createUpcast(loc, selfValue, selfSILType); } // Recreate as a borrowed value. self = ManagedValue::forUnmanaged(selfValue); return LValue::forClassReference(self); } LValue buildLValue(SILGenFunction &gen, SILLocation loc, ManagedValue self, RValue &&indices, AccessKind accessKind) { // Begin with the 'self' value. LValue lv = buildSelfLValue(gen, loc, self); auto strategy = WitnessStorage->getAccessStrategy(TheAccessSemantics, accessKind); // Drill down to the member storage. lv.addMemberComponent(gen, loc, WitnessStorage, WitnessSubs, IsSuper, accessKind, TheAccessSemantics, strategy, SubstStorageType, std::move(indices)); SILType expectedTy = SGM.Types.getLoweredType( lv.getOrigFormalType(), lv.getSubstFormalType()).getObjectType(); SILType actualTy = lv.getTypeOfRValue().getObjectType(); assert(expectedTy == actualTy); // Reabstract back to the requirement pattern. if (actualTy != RequirementStorageType) { SILType substTy = SGM.getLoweredType(SubstStorageType); // FIXME: we can do transforms between two abstraction patterns now // Translate to the fully-substituted formal type... if (actualTy != substTy) lv.addOrigToSubstComponent(substTy); // ...then back to the requirement type using the abstraction pattern // of the requirement.. if (substTy != RequirementStorageType) lv.addSubstToOrigComponent(RequirementStoragePattern, RequirementStorageType); } return lv; } /// Given part of the witness's interface type, produce its /// substitution according to the witness substitutions. CanType getSubstWitnessInterfaceType(CanType type) { return SubstSelfType->getTypeOfMember(SGM.SwiftModule, type, WitnessStorage->getDeclContext()) ->getReferenceStorageReferent() ->getCanonicalType(); } }; } // end anonymous namespace void MaterializeForSetEmitter::emit(SILGenFunction &gen) { SILLocation loc = Witness; loc.markAutoGenerated(); gen.F.setBare(IsBare); SmallVector params; gen.collectThunkParams(loc, params, /*allowPlusZero*/ true); ManagedValue self = params.back(); SILValue resultBuffer = params[0].getUnmanagedValue(); SILValue callbackBuffer = params[1].getUnmanagedValue(); auto indices = ArrayRef(params).slice(2).drop_back(); // If there's an abstraction difference, we always need to use the // get/set pattern. AccessStrategy strategy; if (WitnessStorage->getType()->is() || (RequirementStorageType != WitnessStorageType)) { strategy = AccessStrategy::DispatchToAccessor; } else { strategy = WitnessStorage->getAccessStrategy(TheAccessSemantics, AccessKind::ReadWrite); } // Handle the indices. RValue indicesRV; if (isa(WitnessStorage)) { indicesRV = collectIndicesFromParameters(gen, loc, indices); } else { assert(indices.empty() && "indices for a non-subscript?"); } // As above, assume that we don't need to reabstract 'self'. // Choose the right implementation. SILValue address; SILFunction *callbackFn = nullptr; switch (strategy) { case AccessStrategy::Storage: address = emitUsingStorage(gen, loc, self, std::move(indicesRV)); break; case AccessStrategy::Addressor: address = emitUsingAddressor(gen, loc, self, std::move(indicesRV), callbackBuffer, callbackFn); break; case AccessStrategy::DirectToAccessor: case AccessStrategy::DispatchToAccessor: address = emitUsingGetterSetter(gen, loc, self, std::move(indicesRV), resultBuffer, callbackBuffer, callbackFn); break; } // Return the address as a Builtin.RawPointer. SILType rawPointerTy = SILType::getRawPointerType(gen.getASTContext()); address = gen.B.createAddressToPointer(loc, address, rawPointerTy); SILType resultTupleTy = gen.F.mapTypeIntoContext( gen.F.getLoweredFunctionType()->getSILResult()); SILType optCallbackTy = resultTupleTy.getTupleElementType(1); // Form the callback. SILValue callback; if (callbackFn) { // Make a reference to the function. callback = gen.B.createFunctionRef(loc, callbackFn); // If it's polymorphic, cast to RawPointer and then back to the // right monomorphic type. The safety of this cast relies on some // assumptions about what exactly IRGen can reconstruct from the // callback's thick type argument. if (callbackFn->getLoweredFunctionType()->isPolymorphic()) { callback = gen.B.createThinFunctionToPointer(loc, callback, rawPointerTy); OptionalTypeKind optKind; auto callbackTy = optCallbackTy.getAnyOptionalObjectType(SGM.M, optKind); callback = gen.B.createPointerToThinFunction(loc, callback, callbackTy); } callback = gen.B.createOptionalSome(loc, callback, optCallbackTy); } else { callback = gen.B.createOptionalNone(loc, optCallbackTy); } // Form the result and return. auto result = gen.B.createTuple(loc, resultTupleTy, { address, callback }); gen.Cleanups.emitCleanupsForReturn(CleanupLocation::get(loc)); gen.B.createReturn(loc, result); } /// Recursively walk into the given formal index type, expanding tuples, /// in order to form the arguments to a subscript accessor. static void translateIndices(SILGenFunction &gen, SILLocation loc, AbstractionPattern pattern, CanType formalType, ArrayRef &sourceIndices, RValue &result) { // Expand if the pattern was a tuple. if (pattern.isTuple()) { auto formalTupleType = cast(formalType); for (auto i : indices(formalTupleType.getElementTypes())) { translateIndices(gen, loc, pattern.getTupleElementType(i), formalTupleType.getElementType(i), sourceIndices, result); } return; } assert(!sourceIndices.empty() && "ran out of elements in index!"); ManagedValue value = sourceIndices.front(); sourceIndices = sourceIndices.slice(1); // We're going to build an RValue here, so make sure we translate // indirect arguments to be scalar if we have a loadable type. if (value.getType().isAddress()) { auto &valueTL = gen.getTypeLowering(value.getType()); if (!valueTL.isAddressOnly()) { value = gen.emitLoad(loc, value.forward(gen), valueTL, SGFContext(), IsTake); } } // Reabstract the subscripts from the requirement pattern to the // formal type. value = gen.emitOrigToSubstValue(loc, value, pattern, formalType); // Invoking the accessor will expect a value of the formal type, so // don't reabstract to that here. // Add that to the result, further expanding if necessary. result.addElement(gen, value, formalType, loc); } RValue MaterializeForSetEmitter:: collectIndicesFromParameters(SILGenFunction &gen, SILLocation loc, ArrayRef sourceIndices) { auto witnessSubscript = cast(WitnessStorage); CanType witnessIndicesType = witnessSubscript->getIndicesInterfaceType()->getCanonicalType(); CanType substIndicesType = getSubstWitnessInterfaceType(witnessIndicesType); auto reqSubscript = cast(RequirementStorage); auto pattern = SGM.Types.getIndicesAbstractionPattern(reqSubscript); RValue result(pattern, substIndicesType); // Translate and reabstract the index values by recursively walking // the abstracted index type. translateIndices(gen, loc, pattern, substIndicesType, sourceIndices, result); assert(sourceIndices.empty() && "index value not claimed!"); return result; } SILFunction *MaterializeForSetEmitter::createCallback(SILFunction &F, GeneratorFn generator) { auto callbackType = SGM.Types.getMaterializeForSetCallbackType(WitnessStorage, GenericSig, SelfInterfaceType); auto callback = SGM.M.getOrCreateFunction(Witness, CallbackName, Linkage, callbackType, IsBare, F.isTransparent(), F.isFragile()); callback->setContextGenericParams(GenericParams); callback->setDebugScope(new (SGM.M) SILDebugScope(Witness, callback)); PrettyStackTraceSILFunction X("silgen materializeForSet callback", callback); { SILGenFunction gen(SGM, *callback); auto makeParam = [&](unsigned index) -> SILArgument* { SILType type = gen.F.mapTypeIntoContext( callbackType->getParameters()[index].getSILType()); return new (SGM.M) SILArgument(gen.F.begin(), type); }; // Add arguments for all the parameters. auto valueBuffer = makeParam(0); auto storageBuffer = makeParam(1); auto self = makeParam(2); (void) makeParam(3); SILLocation loc = Witness; loc.markAutoGenerated(); // Call the generator function we were provided. { LexicalScope scope(gen.Cleanups, gen, CleanupLocation::get(loc)); generator(gen, loc, valueBuffer, storageBuffer, self); } // Return void. auto result = gen.emitEmptyTuple(loc); gen.B.createReturn(loc, result); } callback->verify(); return callback; } /// Emit a materializeForSet operation that projects storage, assuming /// that no cleanups or callbacks are required. SILValue MaterializeForSetEmitter::emitUsingStorage(SILGenFunction &gen, SILLocation loc, ManagedValue self, RValue &&indices) { LValue lvalue = buildLValue(gen, loc, self, std::move(indices), AccessKind::ReadWrite); ManagedValue address = gen.emitAddressOfLValue(loc, std::move(lvalue), AccessKind::ReadWrite); return address.getUnmanagedValue(); } /// Emit a materializeForSet operation that calls a mutable addressor. /// /// If it's not an unsafe addressor, this uses a callback function to /// write the l-value back. SILValue MaterializeForSetEmitter::emitUsingAddressor(SILGenFunction &gen, SILLocation loc, ManagedValue self, RValue &&indices, SILValue callbackBuffer, SILFunction *&callback) { bool isDirect = (TheAccessSemantics != AccessSemantics::Ordinary); // Call the mutable addressor. auto addressor = gen.getAddressorDeclRef(WitnessStorage, AccessKind::ReadWrite, isDirect); ArgumentSource baseRV = gen.prepareAccessorBaseArg(loc, self, SubstSelfType, addressor); SILType addressType = WitnessStorageType.getAddressType(); auto result = gen.emitAddressorAccessor(loc, addressor, WitnessSubs, std::move(baseRV), IsSuper, isDirect, std::move(indices), addressType); SILValue address = result.first.getUnmanagedValue(); AddressorKind addressorKind = WitnessStorage->getMutableAddressor()->getAddressorKind(); ManagedValue owner = result.second; if (!owner) { assert(addressorKind == AddressorKind::Unsafe); } else { SILValue allocatedCallbackBuffer = gen.B.createAllocValueBuffer(loc, owner.getType(), callbackBuffer); gen.B.createStore(loc, owner.forward(gen), allocatedCallbackBuffer); callback = createAddressorCallback(gen.F, owner.getType(), addressorKind); } return address; } /// Emit a materializeForSet callback to clean up after an addressor /// with an owner result. SILFunction * MaterializeForSetEmitter::createAddressorCallback(SILFunction &F, SILType ownerType, AddressorKind addressorKind) { return createCallback(F, [&](SILGenFunction &gen, SILLocation loc, SILValue resultBuffer, SILValue callbackStorage, SILValue self) { auto ownerAddress = gen.B.createProjectValueBuffer(loc, ownerType, callbackStorage); auto owner = gen.B.createLoad(loc, ownerAddress); switch (addressorKind) { case AddressorKind::NotAddressor: case AddressorKind::Unsafe: llvm_unreachable("owner with unexpected addressor kind"); case AddressorKind::Owning: case AddressorKind::NativeOwning: gen.B.createStrongRelease(loc, owner); break; case AddressorKind::NativePinning: gen.B.createStrongUnpin(loc, owner); break; } gen.B.createDeallocValueBuffer(loc, ownerType, callbackStorage); }); } /// Emit a materializeForSet operation that simply loads the l-value /// into the result buffer. This operation creates a callback to write /// the l-value back. SILValue MaterializeForSetEmitter::emitUsingGetterSetter(SILGenFunction &gen, SILLocation loc, ManagedValue self, RValue &&indices, SILValue resultBuffer, SILValue callbackBuffer, SILFunction *&callback) { // Copy the indices into the callback storage. const TypeLowering *indicesTL = nullptr; CleanupHandle indicesCleanup = CleanupHandle::invalid(); CanType indicesFormalType; if (isa(WitnessStorage)) { indicesFormalType = indices.getType(); indicesTL = &gen.getTypeLowering(indicesFormalType); SILValue allocatedCallbackBuffer = gen.B.createAllocValueBuffer(loc, indicesTL->getLoweredType(), callbackBuffer); // Emit into the buffer. auto init = gen.useBufferAsTemporary(allocatedCallbackBuffer, *indicesTL); indicesCleanup = init->getInitializedCleanup(); indices.copyInto(gen, loc, init.get()); } // Set up the result buffer. resultBuffer = gen.B.createPointerToAddress(loc, resultBuffer, RequirementStorageType.getAddressType()); TemporaryInitialization init(resultBuffer, CleanupHandle::invalid()); // Evaluate the getter into the result buffer. LValue lv = buildLValue(gen, loc, self, std::move(indices), AccessKind::Read); RValue result = gen.emitLoadOfLValue(loc, std::move(lv), SGFContext(&init)); if (!result.isInContext()) { std::move(result).forwardInto(gen, loc, &init); } // Forward the cleanup on the saved indices. if (indicesCleanup.isValid()) { gen.Cleanups.setCleanupState(indicesCleanup, CleanupState::Dead); } callback = createSetterCallback(gen.F, indicesTL, indicesFormalType); return resultBuffer; } namespace { class DeallocateValueBuffer : public Cleanup { SILValue Buffer; SILType ValueType; public: DeallocateValueBuffer(SILType valueType, SILValue buffer) : Buffer(buffer), ValueType(valueType) {} void emit(SILGenFunction &gen, CleanupLocation loc) override { gen.B.createDeallocValueBuffer(loc, ValueType, Buffer); } }; } /// Emit a materializeForSet callback that stores the value from the /// result buffer back into the l-value. SILFunction * MaterializeForSetEmitter::createSetterCallback(SILFunction &F, const TypeLowering *indicesTL, CanType indicesFormalType) { return createCallback(F, [&](SILGenFunction &gen, SILLocation loc, SILValue value, SILValue callbackBuffer, SILValue self) { // If this is a subscript, we need to handle the indices in the // callback storage. RValue indices; if (indicesTL) { assert(isa(WitnessStorage)); SILType indicesTy = indicesTL->getLoweredType(); // Enter a cleanup to deallocate the callback storage. gen.Cleanups.pushCleanup(indicesTy, callbackBuffer); // Project the value out, loading if necessary, and take // ownership of it. SILValue indicesV = gen.B.createProjectValueBuffer(loc, indicesTy, callbackBuffer); if (indicesTL->isLoadable()) indicesV = gen.B.createLoad(loc, indicesV); ManagedValue mIndices = gen.emitManagedRValueWithCleanup(indicesV, *indicesTL); // Explode as an r-value. indices = RValue(gen, loc, indicesFormalType, mIndices); } // The callback gets the address of 'self' at +0. ManagedValue mSelf = ManagedValue::forLValue(self); // That's enough to build the l-value. LValue lvalue = buildLValue(gen, loc, mSelf, std::move(indices), AccessKind::Write); // The callback gets the value at +1. auto &valueTL = gen.getTypeLowering(lvalue.getTypeOfRValue()); value = gen.B.createPointerToAddress(loc, value, valueTL.getLoweredType().getAddressType()); if (valueTL.isLoadable()) value = gen.B.createLoad(loc, value); ManagedValue mValue = gen.emitManagedRValueWithCleanup(value, valueTL); RValue rvalue(gen, loc, lvalue.getSubstFormalType(), mValue); // Finally, call the setter. gen.emitAssignToLValue(loc, std::move(rvalue), std::move(lvalue)); }); } /// Emit an open-coded protocol-witness thunk for materializeForSet if /// delegating to the standard implementation isn't good enough. /// /// materializeForSet sometimes needs to be open-coded because of the /// thin callback function, which is dependent but cannot be reabstracted. /// /// - In a protocol extension, the callback doesn't know how to capture /// or reconstruct the generic conformance information. /// /// - The abstraction pattern of the variable from the witness may /// differ from the abstraction pattern of the protocol, likely forcing /// a completely different access pattern (e.g. to write back a /// reabstracted value instead of modifying it in-place). /// /// \return true if special code was emitted bool SILGenFunction:: maybeEmitMaterializeForSetThunk(ProtocolConformance *conformance, FuncDecl *requirement, FuncDecl *witness, ArrayRef witnessSubs) { MaterializeForSetEmitter emitter = MaterializeForSetEmitter::forWitnessThunk( SGM, conformance, requirement, witness, witnessSubs); if (!emitter.shouldOpenCode()) return false; emitter.emit(*this); return true; } /// Emit a concrete implementation of materializeForSet. void SILGenFunction::emitMaterializeForSet(FuncDecl *decl) { assert(decl->getAccessorKind() == AccessorKind::IsMaterializeForSet); MagicFunctionName = SILGenModule::getMagicFunctionName(decl); MaterializeForSetEmitter emitter = MaterializeForSetEmitter::forConcreteImplementation( SGM, decl, getForwardingSubstitutions()); emitter.emit(*this); }