diff --git a/include/swift/SIL/TypeLowering.h b/include/swift/SIL/TypeLowering.h index a79f217f91536..a2e7fe9853524 100644 --- a/include/swift/SIL/TypeLowering.h +++ b/include/swift/SIL/TypeLowering.h @@ -641,6 +641,7 @@ class TypeConverter { AbstractionPattern getAbstractionPattern(AbstractStorageDecl *storage); AbstractionPattern getAbstractionPattern(VarDecl *var); AbstractionPattern getAbstractionPattern(SubscriptDecl *subscript); + AbstractionPattern getIndicesAbstractionPattern(SubscriptDecl *subscript); AbstractionPattern getAbstractionPattern(EnumElementDecl *element); SILType getLoweredTypeOfGlobal(VarDecl *var); diff --git a/lib/SIL/AbstractionPattern.cpp b/lib/SIL/AbstractionPattern.cpp index 8355a383ac58c..c2914d7dd1361 100644 --- a/lib/SIL/AbstractionPattern.cpp +++ b/lib/SIL/AbstractionPattern.cpp @@ -39,9 +39,16 @@ AbstractionPattern TypeConverter::getAbstractionPattern(AbstractStorageDecl *dec AbstractionPattern TypeConverter::getAbstractionPattern(SubscriptDecl *decl) { // TODO: honor the declared type? + // TODO: use interface types return AbstractionPattern(decl->getElementType()); } +AbstractionPattern +TypeConverter::getIndicesAbstractionPattern(SubscriptDecl *decl) { + // TODO: use interface types + return AbstractionPattern(decl->getIndicesType()); +} + bool AbstractionPattern::isOpaqueType(CanGenericSignature signature, CanGenericTypeParamType type) { // Enormous hack! We need to be asking the signature about this diff --git a/lib/SILGen/LValue.h b/lib/SILGen/LValue.h index a68a8a502647a..e67f46cea6fe0 100644 --- a/lib/SILGen/LValue.h +++ b/lib/SILGen/LValue.h @@ -305,6 +305,37 @@ class LValue { Path.emplace_back(new T(std::forward(args)...)); } + /// Add a member component to the access path of this lvalue. + void addMemberComponent(SILGenFunction &gen, SILLocation loc, + AbstractStorageDecl *storage, + ArrayRef subs, + bool isSuper, + AccessKind accessKind, + AccessSemantics accessSemantics, + AccessStrategy accessStrategy, + CanType formalRValueType, + RValue &&indices); + + void addMemberVarComponent(SILGenFunction &gen, SILLocation loc, + VarDecl *var, + ArrayRef subs, + bool isSuper, + AccessKind accessKind, + AccessSemantics accessSemantics, + AccessStrategy accessStrategy, + CanType formalRValueType); + + void addMemberSubscriptComponent(SILGenFunction &gen, SILLocation loc, + SubscriptDecl *subscript, + ArrayRef subs, + bool isSuper, + AccessKind accessKind, + AccessSemantics accessSemantics, + AccessStrategy accessStrategy, + CanType formalRValueType, + RValue &&indices, + Expr *indexExprForDiagnostics = nullptr); + /// Add a subst-to-orig reabstraction component. That is, given /// that this l-value trafficks in values following the substituted /// abstraction pattern, make an l-value trafficking in values diff --git a/lib/SILGen/RValue.cpp b/lib/SILGen/RValue.cpp index 4a56075689ad7..e2a6833a9f3aa 100644 --- a/lib/SILGen/RValue.cpp +++ b/lib/SILGen/RValue.cpp @@ -49,61 +49,57 @@ namespace { class ExplodeTupleValue : public CanTypeVisitor + /*Args...=*/ ManagedValue> { public: std::vector &values; SILGenFunction &gen; + SILLocation loc; ExplodeTupleValue(std::vector &values, - SILGenFunction &gen) - : values(values), gen(gen) + SILGenFunction &gen, SILLocation loc) + : values(values), gen(gen), loc(loc) { } - void visitType(CanType t, ManagedValue v, SILLocation l) { + void visitType(CanType formalType, ManagedValue v) { values.push_back(v); } - void visitTupleType(CanTupleType t, ManagedValue mv, SILLocation l) { - bool IsPlusZero = mv.isPlusZeroRValueOrTrivial(); - - SILValue v = mv.forward(gen); - if (v.getType().isAddressOnly(gen.F.getModule())) { - // Destructure address-only types by addressing the individual members. - for (unsigned i = 0, size = t->getNumElements(); i < size; ++i) { - CanType fieldCanTy = t.getElementType(i); - auto &fieldTI = gen.getTypeLowering(fieldCanTy); - SILType fieldTy = fieldTI.getLoweredType(); - SILValue member = gen.B.createTupleElementAddr(l, v, i, - fieldTy.getAddressType()); - assert(fieldTI.getSemanticType() == fieldTy); - if (fieldTI.isLoadable() && !isa(fieldCanTy)) - member = gen.B.createLoad(l, member); - - // If we're returning a +1 value, emit a cleanup for the member to cover - // for the cleanup we disabled for the tuple aggregate. - auto SubValue = IsPlusZero ? ManagedValue::forUnmanaged(member) - : gen.emitManagedRValueWithCleanup(member, fieldTI); - - visit(fieldCanTy, SubValue, l); - } - } else { - // Extract the elements from loadable tuples. - for (unsigned i = 0, size = t->getNumElements(); i < size; ++i) { - CanType fieldCanTy = t.getElementType(i); - auto &fieldTI = gen.getTypeLowering(fieldCanTy); - assert(fieldTI.isLoadable()); - SILValue member = gen.B.createTupleExtract(l, v, i, - fieldTI.getLoweredType()); - - // If we're returning a +1 value, emit a cleanup for the member to cover - // for the cleanup we disabled for the tuple aggregate. - auto SubValue = IsPlusZero ? ManagedValue::forUnmanaged(member) - : gen.emitManagedRValueWithCleanup(member, fieldTI); - - visit(fieldCanTy, SubValue, l); + void visitTupleType(CanTupleType tupleFormalType, ManagedValue tupleMV) { + bool isPlusZero = tupleMV.isPlusZeroRValueOrTrivial(); + SILValue tuple = tupleMV.forward(gen); + + for (auto i : indices(tupleFormalType->getElements())) { + CanType eltFormalType = tupleFormalType.getElementType(i); + assert(eltFormalType->isMaterializable()); + + auto eltTy = tuple.getType().getTupleElementType(i); + assert(eltTy.isAddress() == tuple.getType().isAddress()); + auto &eltTI = gen.getTypeLowering(eltTy); + + // Project the element. + SILValue elt; + if (tuple.getType().isObject()) { + assert(eltTI.isLoadable()); + elt = gen.B.createTupleExtract(loc, tuple, i, eltTy); + } else { + elt = gen.B.createTupleElementAddr(loc, tuple, i, eltTy); + + // RValue has an invariant that loadable values have been + // loaded. Except it's not really an invariant, because + // argument emission likes to lie sometimes. + if (eltTI.isLoadable()) { + elt = gen.B.createLoad(loc, elt); + } } + + // If we're returning a +1 value, emit a cleanup for the member + // to cover for the cleanup we disabled for the tuple aggregate. + auto eltMV = isPlusZero ? ManagedValue::forUnmanaged(elt) + : gen.emitManagedRValueWithCleanup(elt, eltTI); + + visit(eltFormalType, eltMV); } } }; @@ -375,7 +371,7 @@ RValue::RValue(SILGenFunction &gen, SILLocation l, CanType formalType, return; } - ExplodeTupleValue(values, gen).visit(type, v, l); + ExplodeTupleValue(values, gen, l).visit(formalType, v); assert(values.size() == getRValueSize(type)); } @@ -389,7 +385,7 @@ RValue::RValue(SILGenFunction &gen, Expr *expr, ManagedValue v) } assert(v && "creating r-value with consumed value"); - ExplodeTupleValue(values, gen).visit(type, v, expr); + ExplodeTupleValue(values, gen, expr).visit(type, v); assert(values.size() == getRValueSize(type)); } @@ -416,7 +412,7 @@ void RValue::addElement(SILGenFunction &gen, ManagedValue element, assert(!isUsed() && "rvalue already used"); --elementsToBeAdded; - ExplodeTupleValue(values, gen).visit(formalType, element, l); + ExplodeTupleValue(values, gen, l).visit(formalType, element); assert(!isComplete() || values.size() == getRValueSize(type)); } diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 8ad0ccdf9f6b8..c330af0bf6b33 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -991,6 +991,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction bool isSuper, bool isDirectAccessorUse, RValue &&optionalSubscripts, SILValue buffer, SILValue callbackStorage); + bool maybeEmitMaterializeForSetThunk(ProtocolConformance *conformance, + FuncDecl *requirement, + FuncDecl *witness, + ArrayRef witnessSubs, + ArrayRef params); SILDeclRef getAddressorDeclRef(AbstractStorageDecl *decl, AccessKind accessKind, diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index 15e257655392b..4cf05c3f146d2 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -25,6 +25,8 @@ #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" @@ -132,24 +134,18 @@ static CanType getSubstFormalRValueType(Expr *expr) { return expr->getType()->getRValueType()->getCanonicalType(); } -static LValueTypeData getStorageTypeData(SILGenFunction &gen, - AbstractStorageDecl *storage, - CanType substFormalType) { - auto origFormalType = gen.SGM.Types.getAbstractionPattern(storage) - .getReferenceStorageReferentType(); +static LValueTypeData getStorageTypeData(SILGenModule &SGM, + AbstractStorageDecl *storage, + CanType substFormalType) { + auto origFormalType = SGM.Types.getAbstractionPattern(storage) + .getReferenceStorageReferentType(); return { origFormalType, substFormalType, - gen.getLoweredType(origFormalType, substFormalType).getObjectType() + SGM.Types.getLoweredType(origFormalType, substFormalType).getObjectType() }; } -static LValueTypeData getStorageTypeData(SILGenFunction &gen, - AbstractStorageDecl *storage, - Expr *lvalueExpr) { - return getStorageTypeData(gen, storage, getSubstFormalRValueType(lvalueExpr)); -} - /// SILGenLValue - An ASTVisitor for building logical lvalues. class LLVM_LIBRARY_VISIBILITY SILGenLValue : public Lowering::ExprVisitor @@ -717,7 +713,6 @@ namespace { subscriptIndexExpr(subscriptIndexExpr), baseFormalType(baseFormalType) { - assert((optSubscripts != nullptr) == (subscriptIndexExpr != nullptr)); if (optSubscripts) subscripts = std::move(*optSubscripts); } @@ -1063,7 +1058,9 @@ namespace { if (!subscripts.isObviouslyEqual(rhs.subscripts)) { // If the index value doesn't lower to literally the same SILValue's, // do some fuzzy matching to catch the common case. - if (!areCertainlyEqualIndices(subscriptIndexExpr, + if (!subscriptIndexExpr || + !rhs.subscriptIndexExpr || + !areCertainlyEqualIndices(subscriptIndexExpr, rhs.subscriptIndexExpr)) return; } @@ -1389,6 +1386,28 @@ LValue LValue::forAddress(ManagedValue address, return lv; } +void LValue::addMemberComponent(SILGenFunction &gen, SILLocation loc, + AbstractStorageDecl *storage, + ArrayRef subs, + bool isSuper, + AccessKind accessKind, + AccessSemantics accessSemantics, + AccessStrategy accessStrategy, + CanType formalRValueType, + RValue &&indices) { + if (auto var = dyn_cast(storage)) { + assert(!indices); + addMemberVarComponent(gen, loc, var, subs, isSuper, + accessKind, accessSemantics, accessStrategy, + formalRValueType); + } else { + auto subscript = cast(storage); + addMemberSubscriptComponent(gen, loc, subscript, subs, isSuper, + accessKind, accessSemantics, accessStrategy, + formalRValueType, std::move(indices)); + } +} + void LValue::addOrigToSubstComponent(SILType loweredSubstType) { loweredSubstType = loweredSubstType.getObjectType(); assert(getTypeOfRValue() != loweredSubstType && @@ -1535,7 +1554,7 @@ SILGenFunction::emitLValueForAddressedNonMemberVarDecl(SILLocation loc, CanType formalRValueType, AccessKind accessKind, AccessSemantics semantics) { - auto typeData = getStorageTypeData(*this, var, formalRValueType); + auto typeData = getStorageTypeData(SGM, var, formalRValueType); LValue lv; addNonMemberVarDeclAddressorComponent(*this, var, typeData, lv); return lv; @@ -1547,7 +1566,7 @@ static LValue emitLValueForNonMemberVarDecl(SILGenFunction &gen, AccessKind accessKind, AccessSemantics semantics) { LValue lv; - auto typeData = getStorageTypeData(gen, var, formalRValueType); + auto typeData = getStorageTypeData(gen.SGM, var, formalRValueType); switch (var->getAccessStrategy(semantics, accessKind)) { @@ -1580,7 +1599,7 @@ static LValue emitLValueForNonMemberVarDecl(SILGenFunction &gen, } } - return std::move(lv); + return lv; } @@ -1594,7 +1613,7 @@ LValue SILGenLValue::visitDiscardAssignmentExpr(DiscardAssignmentExpr *e, gen.enterDestroyCleanup(address); LValue lv; lv.add(ManagedValue::forUnmanaged(address), typeData); - return std::move(lv); + return lv; } @@ -1626,7 +1645,7 @@ LValue SILGenLValue::visitOpaqueValueExpr(OpaqueValueExpr *e, lv.add(existentialAddr, existentialLV.getTypeData()); lv.add( cast(opened->getOpenedArchetype()->getCanonicalType())); - return std::move(lv); + return lv; } assert(gen.OpaqueValues.count(e) && "Didn't bind OpaqueValueExpr"); @@ -1639,7 +1658,7 @@ LValue SILGenLValue::visitOpaqueValueExpr(OpaqueValueExpr *e, LValue lv; lv.add(ManagedValue::forUnmanaged(entry.value), getValueTypeData(gen, e)); - return std::move(lv); + return lv; } LValue SILGenLValue::visitDotSyntaxBaseIgnoredExpr(DotSyntaxBaseIgnoredExpr *e, @@ -1694,18 +1713,32 @@ LValue SILGenLValue::visitMemberRefExpr(MemberRefExpr *e, getBaseAccessKind(var, accessKind, strategy)); assert(lv.isValid()); - LValueTypeData typeData = getStorageTypeData(gen, var, e); + CanType substFormalRValueType = getSubstFormalRValueType(e); + lv.addMemberVarComponent(gen, e, var, e->getMember().getSubstitutions(), + e->isSuper(), accessKind, e->getAccessSemantics(), + strategy, substFormalRValueType); + return lv; +} + +void LValue::addMemberVarComponent(SILGenFunction &gen, SILLocation loc, + VarDecl *var, + ArrayRef subs, + bool isSuper, + AccessKind accessKind, + AccessSemantics accessSemantics, + AccessStrategy strategy, + CanType formalRValueType) { + LValueTypeData typeData = getStorageTypeData(gen.SGM, var, formalRValueType); + CanType baseFormalType = getSubstFormalType(); // Use the property accessors if the variable has accessors and this isn't a // direct access to underlying storage. if (strategy == AccessStrategy::DirectToAccessor || strategy == AccessStrategy::DispatchToAccessor) { - lv.add(var, e->isSuper(), - strategy == AccessStrategy::DirectToAccessor, - e->getMember().getSubstitutions(), - lv.getSubstFormalType(), - typeData); - return std::move(lv); + add(var, isSuper, + strategy == AccessStrategy::DirectToAccessor, + subs, baseFormalType, typeData); + return; } assert(strategy == AccessStrategy::Addressor || @@ -1714,54 +1747,51 @@ LValue SILGenLValue::visitMemberRefExpr(MemberRefExpr *e, // Otherwise, the lvalue access is performed with a fragile element reference. // Find the substituted storage type. SILType varStorageType = - gen.SGM.Types.getSubstitutedStorageType(var, e->getType()); + gen.SGM.Types.getSubstitutedStorageType(var, formalRValueType); // For static variables, emit a reference to the global variable backing // them. // FIXME: This has to be dynamically looked up for classes, and // dynamically instantiated for generics. if (strategy == AccessStrategy::Storage && var->isStatic()) { - auto baseMeta = e->getBase()->getType()->castTo() - ->getInstanceType(); + auto baseMeta = baseFormalType->castTo()->getInstanceType(); (void)baseMeta; assert(!baseMeta->is() && "generic static stored properties not implemented"); - return emitLValueForNonMemberVarDecl(gen, e, var, - getSubstFormalRValueType(e), - accessKind, e->getAccessSemantics()); + // FIXME: this implicitly drops the earlier components, but maybe + // we ought to evaluate them for side-effects even during the + // formal access? + *this = emitLValueForNonMemberVarDecl(gen, loc, var, + formalRValueType, + accessKind, accessSemantics); + return; } // For member variables, this access is done w.r.t. a base computation that // was already emitted. This member is accessed off of it. if (strategy == AccessStrategy::Addressor) { - lv.add(var, e->isSuper(), /*direct*/ true, - e->getMember().getSubstitutions(), - lv.getSubstFormalType(), - typeData, varStorageType); - } else if (e->getBase()->getType()->mayHaveSuperclass()) { - lv.add(var, varStorageType, typeData); + add(var, isSuper, /*direct*/ true, subs, + baseFormalType, typeData, varStorageType); + } else if (baseFormalType->mayHaveSuperclass()) { + add(var, varStorageType, typeData); } else { - assert(e->getBase()->getType()->getLValueOrInOutObjectType() - ->getStructOrBoundGenericStruct()); - lv.add(var, varStorageType, typeData); + assert(baseFormalType->getStructOrBoundGenericStruct()); + add(var, varStorageType, typeData); } // If the member has weak or unowned storage, convert it away. if (varStorageType.is()) { - lv.add(typeData); + add(typeData); } - - return lv; } LValue SILGenLValue::visitSubscriptExpr(SubscriptExpr *e, AccessKind accessKind) { auto decl = cast(e->getDecl().getDecl()); - auto typeData = getStorageTypeData(gen, decl, e); - AccessStrategy strategy = - decl->getAccessStrategy(e->getAccessSemantics(), accessKind); + auto accessSemantics = e->getAccessSemantics(); + auto strategy = decl->getAccessStrategy(accessSemantics, accessKind); LValue lv = visitRec(e->getBase(), getBaseAccessKind(decl, accessKind, strategy)); @@ -1772,24 +1802,41 @@ LValue SILGenLValue::visitSubscriptExpr(SubscriptExpr *e, // argument emission. RValue index = gen.emitRValue(indexExpr); + CanType formalRValueType = getSubstFormalRValueType(e); + lv.addMemberSubscriptComponent(gen, e, decl, e->getDecl().getSubstitutions(), + e->isSuper(), accessKind, accessSemantics, + strategy, formalRValueType, std::move(index), + indexExpr); + return lv; +} + +void LValue::addMemberSubscriptComponent(SILGenFunction &gen, SILLocation loc, + SubscriptDecl *decl, + ArrayRef subs, + bool isSuper, + AccessKind accessKind, + AccessSemantics accessSemantics, + AccessStrategy strategy, + CanType formalRValueType, + RValue &&indices, + Expr *indexExprForDiagnostics) { + auto typeData = getStorageTypeData(gen.SGM, decl, formalRValueType); + CanType baseFormalType = getSubstFormalType(); + if (strategy == AccessStrategy::DirectToAccessor || strategy == AccessStrategy::DispatchToAccessor) { - lv.add(decl, e->isSuper(), - strategy == AccessStrategy::DirectToAccessor, - e->getDecl().getSubstitutions(), - lv.getSubstFormalType(), - typeData, indexExpr, &index); + add(decl, isSuper, + strategy == AccessStrategy::DirectToAccessor, + subs, baseFormalType, typeData, + indexExprForDiagnostics, &indices); } else { assert(strategy == AccessStrategy::Addressor); auto storageType = - gen.SGM.Types.getSubstitutedStorageType(decl, e->getType()); - lv.add(decl, e->isSuper(), /*direct*/ true, - e->getDecl().getSubstitutions(), - lv.getSubstFormalType(), - typeData, storageType, indexExpr, &index); + gen.SGM.Types.getSubstitutedStorageType(decl, formalRValueType); + add(decl, isSuper, /*direct*/ true, + subs, baseFormalType, typeData, storageType, + indexExprForDiagnostics, &indices); } - - return lv; } LValue SILGenLValue::visitTupleElementExpr(TupleElementExpr *e, @@ -1833,7 +1880,7 @@ LValue SILGenLValue::visitOpenExistentialExpr(OpenExistentialExpr *e, // Sanity check that we did see the OpaqueValueExpr. assert(openedExistentials.count(e->getOpaqueValue()) == 0 && "opened existential not removed?"); - return std::move(lv); + return lv; } static LValueTypeData @@ -1914,7 +1961,7 @@ LValue SILGenFunction::emitPropertyLValue(SILLocation loc, ManagedValue base, ->getTypeOfMember(F.getModule().getSwiftModule(), ivar, nullptr) ->getCanonicalType(); - LValueTypeData typeData = getStorageTypeData(*this, ivar, substFormalType); + LValueTypeData typeData = getStorageTypeData(SGM, ivar, substFormalType); AccessStrategy strategy = ivar->getAccessStrategy(semantics, accessKind); @@ -1949,7 +1996,7 @@ LValue SILGenFunction::emitPropertyLValue(SILLocation loc, ManagedValue base, auto formalRValueType = ivar->getType()->getRValueType()->getReferenceStorageReferent(); auto typeData = - getStorageTypeData(*this, ivar, formalRValueType->getCanonicalType()); + getStorageTypeData(SGM, ivar, formalRValueType->getCanonicalType()); lv.add(typeData); } @@ -2610,3 +2657,701 @@ void SILGenFunction::emitAssignLValueToLValue(SILLocation loc, loaded.assignInto(*this, loc, destAddr); } } + +//===----------------------------------------------------------------------===// +// materializeForSet emission +//===----------------------------------------------------------------------===// + +namespace { + +/// A helper class for emitting materializeForSet. +struct MaterializeForSetEmitter { + SILGenModule &SGM; + ProtocolConformance *Conformance; + FuncDecl *Requirement; + FuncDecl *Witness; + AbstractStorageDecl *RequirementStorage; + AbstractStorageDecl *WitnessStorage; + ArrayRef WitnessSubs; + CanType SubstSelfType; + CanType SubstStorageType; + AccessSemantics TheAccessSemantics; + bool IsSuper; + GenericParamList *OuterGenericParams = nullptr; // initialized in emit() + + AbstractionPattern RequirementStoragePattern; + SILType RequirementStorageType; + SILType WitnessStorageType; + + MaterializeForSetEmitter(SILGenModule &SGM, + ProtocolConformance *conformance, + FuncDecl *requirement, + FuncDecl *witness, + ArrayRef witnessSubs, + SILType selfType) + : SGM(SGM), + Conformance(conformance), Requirement(requirement), Witness(witness), + RequirementStorage(requirement->getAccessorStorageDecl()), + WitnessStorage(witness->getAccessorStorageDecl()), + WitnessSubs(witnessSubs), + RequirementStoragePattern(AbstractionPattern::getInvalid()) + { + // 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. + SubstSelfType = selfType.getSwiftRValueType(); + + // Determine the formal type of the storage. + CanType witnessIfaceType = + WitnessStorage->getInterfaceType()->getCanonicalType(); + if (isa(WitnessStorage)) + witnessIfaceType = cast(witnessIfaceType).getResult(); + SubstStorageType = getSubstWitnessInterfaceType(witnessIfaceType); + + // Determine the desired abstraction pattern of the storage type + // in the requirement and the witness. + RequirementStoragePattern = + SGM.Types.getAbstractionPattern(RequirementStorage); + RequirementStorageType = + SGM.Types.getLoweredType(RequirementStoragePattern, SubstStorageType) + .getObjectType(); + + auto witnessStoragePattern = + SGM.Types.getAbstractionPattern(WitnessStorage); + WitnessStorageType = + SGM.Types.getLoweredType(witnessStoragePattern, SubstStorageType) + .getObjectType(); + + // In a protocol witness thunk, we always want to use ordinary + // access semantics. But when we're changing this to use For standard implementations, we'll need to + // modify this to use direct semantics. + TheAccessSemantics = AccessSemantics::Ordinary; + IsSuper = false; + } + + 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()->isProtocolOrProtocolExtensionContext()) + return true; + + return false; + } + + void emit(SILGenFunction &gen, ManagedValue self, SILValue resultBuffer, + SILValue callbackBuffer, ArrayRef indices); + + 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(SILType ownerType, + AddressorKind addressorKind); + + SILValue emitUsingGetterSetter(SILGenFunction &gen, SILLocation loc, + ManagedValue self, RValue &&indices, + SILValue resultBuffer, + SILValue callbackBuffer, + SILFunction *&callback); + SILFunction *createSetterCallback(const TypeLowering *indicesTL, + CanType indicesFormalType); + + using GeneratorFn = llvm::function_ref; + + SILFunction *createCallback(GeneratorFn generator); + + RValue collectIndicesFromParameters(SILGenFunction &gen, SILLocation loc, + ArrayRef sourceIndices); + + LValue buildLValue(SILGenFunction &gen, SILLocation loc, + ManagedValue self, RValue &&indices, + AccessKind accessKind) { + // Begin with the 'self' value. + AbstractionPattern selfPattern(SubstSelfType); + LValue lv = LValue::forAddress(self, selfPattern, SubstSelfType); + + 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)); + assert(lv.getTypeOfRValue().getObjectType() == WitnessStorageType); + + // Reabstract back to the requirement pattern. + if (RequirementStorageType != WitnessStorageType) { + SILType substTy = SGM.getLoweredType(SubstStorageType).getObjectType(); + + // Translate to the formal type... + if (WitnessStorageType != substTy) + lv.addOrigToSubstComponent(substTy); + + // ...then back to the requirement type. + 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()) + ->getCanonicalType(); + } + +}; + +} // end anonymous namespace + +void MaterializeForSetEmitter::emit(SILGenFunction &gen, ManagedValue self, + SILValue resultBuffer, + SILValue callbackBuffer, + ArrayRef indices) { + SILLocation loc = Witness; + loc.markAutoGenerated(); + + OuterGenericParams = gen.F.getContextGenericParams(); + + // If there's an abstraction difference, we always need to use the + // get/set pattern. + AccessStrategy strategy; + if (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()->getResult().getSILType()); + 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 +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(substIndicesType); + + // Translate and reabstract the index values by recursively walking + // the abstracted index type. + SmallVector translatedIndices; + translateIndices(gen, loc, pattern, substIndicesType, + sourceIndices, result); + assert(sourceIndices.empty() && "index value not claimed!"); + + return result; +} + +static AnyFunctionType *getMaterializeForSetCallbackType(ASTContext &ctx, + Type selfType, + GenericParamList *genericParams) { + // inout storage: Builtin.ValueBuffer, + // inout self: Self, + // @thick selfType: Self.Type) -> () + TupleTypeElt params[] = { + ctx.TheRawPointerType, + InOutType::get(ctx.TheUnsafeValueBufferType), + InOutType::get(selfType), + MetatypeType::get(selfType, MetatypeRepresentation::Thick) + }; + Type input = TupleType::get(params, ctx); + Type result = TupleType::getEmpty(ctx); + FunctionType::ExtInfo extInfo = FunctionType::ExtInfo() + .withRepresentation(FunctionType::Representation::Thin); + + if (genericParams) { + return PolymorphicFunctionType::get(input, result, genericParams, extInfo); + } else { + return FunctionType::get(input, result, extInfo); + } +} + +static Type getSelfTypeForCallbackDeclaration(FuncDecl *witness) { + // We're intentionally using non-interface types here: we want + // something specified in terms of the witness's archetypes, because + // we're going to build the closure as if it were contextually + // within the witness. + auto type = witness->getType()->castTo()->getInput(); + if (auto tuple = type->getAs()) { + assert(tuple->getNumElements() == 1); + type = tuple->getElementType(0); + } + return type->getLValueOrInOutObjectType(); +} + +SILFunction *MaterializeForSetEmitter::createCallback(GeneratorFn generator) { + auto &ctx = SGM.getASTContext(); + + // Mangle this as if it were a conformance thunk for a closure + // within the witness. + llvm::SmallString<128> name; + { + ClosureExpr closure(/*patterns*/ nullptr, + /*throws*/ SourceLoc(), + /*arrow*/ SourceLoc(), + /*in*/ SourceLoc(), + /*result*/ TypeLoc(), + /*discriminator*/ 0, + /*context*/ Witness); + closure.setType(getMaterializeForSetCallbackType(ctx, + getSelfTypeForCallbackDeclaration(Witness), + nullptr)); + + llvm::raw_svector_ostream nameStream(name); + nameStream << "_TTW"; + Mangle::Mangler mangler(nameStream); + mangler.mangleProtocolConformance(Conformance); + mangler.mangleClosureEntity(&closure, ResilienceExpansion::Minimal, 1); + } + + // Create the SILFunctionType for the callback. + CanType selfIfaceType = Conformance->getInterfaceType()->getCanonicalType(); + SILParameterInfo params[] = { + { ctx.TheRawPointerType, ParameterConvention::Direct_Unowned }, + { ctx.TheUnsafeValueBufferType, ParameterConvention::Indirect_Inout }, + { selfIfaceType, ParameterConvention::Indirect_Inout }, + { CanMetatypeType::get(selfIfaceType, MetatypeRepresentation::Thick), + ParameterConvention::Direct_Unowned }, + }; + SILResultInfo result = { + TupleType::getEmpty(ctx), ResultConvention::Unowned + }; + auto extInfo = + SILFunctionType::ExtInfo() + .withRepresentation(SILFunctionTypeRepresentation::Thin); + + GenericSignature *signature = Conformance->getGenericSignature(); + if (signature) signature = signature->getCanonicalSignature(); + + auto callbackType = SILFunctionType::get(signature, extInfo, + /*callee*/ ParameterConvention::Direct_Unowned, + params, result, None, ctx); + + auto linkage = SGM.Types.getLinkageForProtocolConformance( + Conformance->getRootNormalConformance(), ForDefinition); + auto callback = + SGM.M.getOrCreateFunction(Witness, name, linkage, callbackType, + IsBare, IsTransparent, IsNotFragile); + + callback->setContextGenericParams(Conformance->getGenericParams()); + 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(params[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); + RValue selfRV(gen, loc, SubstSelfType, self); + auto result = gen.emitAddressorAccessor(loc, addressor, WitnessSubs, + { loc, std::move(selfRV) }, + IsSuper, isDirect, + std::move(indices), + WitnessStorageType); + 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(owner.getType(), addressorKind); + } + + return address; +} + +/// Emit a materializeForSet callback to clean up after an addressor +/// with an owner result. +SILFunction * +MaterializeForSetEmitter::createAddressorCallback(SILType ownerType, + AddressorKind addressorKind) { + return createCallback([&](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); + return; + + case AddressorKind::NativePinning: + gen.B.createStrongUnpin(loc, owner); + return; + } + llvm_unreachable("bad addressor kind"); + }); +} + +/// 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; + 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(loc, allocatedCallbackBuffer, + *indicesTL); + indicesCleanup = init->getInitializedCleanup(); + + indices.copyInto(gen, init.get(), loc); + } + + // 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); + ManagedValue result = gen.emitLoadOfLValue(loc, std::move(lv), + SGFContext(&init)); + if (!result.isInContext()) { + result.forwardInto(gen, loc, resultBuffer); + } + + // Forward the cleanup on the saved indices. + if (indicesCleanup.isValid()) { + gen.Cleanups.setCleanupState(indicesCleanup, CleanupState::Dead); + } + + callback = createSetterCallback(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(const TypeLowering *indicesTL, + CanType indicesFormalType) { + return createCallback([&](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, + ArrayRef origParams) { + // FIXME: This is all WIP. + if (conformance) return false; + + // 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) -> ())?) + + // Break apart the parameters. self comes last, the result buffer + // comes first, the callback storage buffer comes second, and the + // rest are indices. + ManagedValue self = origParams.back(); + SILValue resultBuffer = origParams[0].getUnmanagedValue(); + SILValue callbackBuffer = origParams[1].getUnmanagedValue(); + ArrayRef indices = origParams.slice(2).drop_back(); + + MaterializeForSetEmitter emitter(SGM, conformance, requirement, witness, + witnessSubs, self.getType()); + + if (!emitter.shouldOpenCode()) + return false; + + emitter.emit(*this, self, resultBuffer, callbackBuffer, indices); + return true; +} diff --git a/lib/SILGen/SILGenPoly.cpp b/lib/SILGen/SILGenPoly.cpp index 1347b8087befc..d63da761c0a10 100644 --- a/lib/SILGen/SILGenPoly.cpp +++ b/lib/SILGen/SILGenPoly.cpp @@ -1718,6 +1718,24 @@ SILGenFunction::emitVTableThunk(SILDeclRef derived, // Protocol witnesses //===----------------------------------------------------------------------===// +static bool maybeOpenCodeProtocolWitness(SILGenFunction &gen, + ProtocolConformance *conformance, + SILDeclRef requirement, + SILDeclRef witness, + ArrayRef witnessSubs, + ArrayRef origParams) { + if (auto witnessFn = dyn_cast(witness.getDecl())) { + if (witnessFn->getAccessorKind() == AccessorKind::IsMaterializeForSet) { + auto reqFn = cast(requirement.getDecl()); + assert(reqFn->getAccessorKind() == AccessorKind::IsMaterializeForSet); + return gen.maybeEmitMaterializeForSetThunk(conformance, reqFn, witnessFn, + witnessSubs, origParams); + } + } + + return false; +} + static SILValue getWitnessFunctionRef(SILGenFunction &gen, ProtocolConformance *conformance, SILDeclRef witness, @@ -1823,6 +1841,11 @@ void SILGenFunction::emitProtocolWitness(ProtocolConformance *conformance, reqtOrigInputTy = reqtOrigInputTy.dropLastTupleElement(); } + // Open-code certain protocol witness "thunks". + if (maybeOpenCodeProtocolWitness(*this, conformance, requirement, + witness, witnessSubs, origParams)) + return; + // Translate the argument values from the requirement abstraction level to // the substituted signature of the witness. SmallVector witnessParams;