From 241bcdf85c0e076bc3d9b2545b1123d9b14a30f8 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 17 Sep 2018 17:40:24 -0700 Subject: [PATCH 1/7] [ABI] Document associated conformance descriptors. --- docs/ABI/Mangling.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index 71a809066d5..9b4200161cd 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -141,7 +141,7 @@ types where the metadata itself has unknown layout.) global ::= protocol 'TL' // protocol requirements base descriptor global ::= assoc-type-name 'Tl' // associated type descriptor global ::= assoc-type-name 'TM' // default associated type witness accessor - + global ::= type assoc-type-path protocol // associated conformance descriptor REABSTRACT-THUNK-TYPE ::= 'R' // reabstraction thunk helper function REABSTRACT-THUNK-TYPE ::= 'r' // reabstraction thunk From d076e41f32c5be552381c849f0a3786909227e1d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 17 Sep 2018 21:58:56 -0700 Subject: [PATCH 2/7] [IRGen] Put associated conformance accessors in resilient witness table For a resilient conformance, emit the associated conformance accessor functions into the resilient witness table (keyed on the associated conformance descriptor) rather than in the fixed part of the witness table. This is another part of resilience for associated conformances, and a step toward defaults for associated conformances. --- include/swift/IRGen/Linking.h | 9 +-- lib/IRGen/GenDecl.cpp | 14 +--- lib/IRGen/GenMeta.cpp | 8 +- lib/IRGen/GenProto.cpp | 73 +++++++++++++++---- lib/IRGen/IRGenModule.h | 6 +- lib/TBDGen/TBDGen.cpp | 16 ++-- lib/TBDGen/TBDGenVisitor.h | 4 +- stdlib/public/runtime/Metadata.cpp | 2 +- .../protocol_resilience_descriptors.swift | 6 ++ 9 files changed, 90 insertions(+), 48 deletions(-) diff --git a/include/swift/IRGen/Linking.h b/include/swift/IRGen/Linking.h index e5d486f8aee..0eb8ef78fcc 100644 --- a/include/swift/IRGen/Linking.h +++ b/include/swift/IRGen/Linking.h @@ -795,14 +795,13 @@ public: } static LinkEntity - forAssociatedConformanceDescriptor(ProtocolDecl *proto, - CanType associatedType, - ProtocolDecl *associatedProtocol) { + forAssociatedConformanceDescriptor(AssociatedConformance conformance) { LinkEntity entity; entity.setForProtocolAndAssociatedConformance( Kind::AssociatedConformanceDescriptor, - proto, associatedType, - associatedProtocol); + conformance.getSourceProtocol(), + conformance.getAssociation(), + conformance.getAssociatedRequirement()); return entity; } diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index d48d0294270..793ab1e1252 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -3389,21 +3389,15 @@ llvm::GlobalValue *IRGenModule::defineAssociatedTypeDescriptor( llvm::Constant *IRGenModule::getAddrOfAssociatedConformanceDescriptor( AssociatedConformance conformance) { - auto entity = LinkEntity::forAssociatedConformanceDescriptor( - conformance.getSourceProtocol(), - conformance.getAssociation(), - conformance.getAssociatedRequirement()); + auto entity = LinkEntity::forAssociatedConformanceDescriptor(conformance); return getAddrOfLLVMVariable(entity, getPointerAlignment(), ConstantInit(), ProtocolRequirementStructTy, DebugTypeInfo()); } llvm::GlobalValue *IRGenModule::defineAssociatedConformanceDescriptor( - ProtocolDecl *proto, - CanType subject, - ProtocolDecl *requirement, - llvm::Constant *definition) { - auto entity = LinkEntity::forAssociatedConformanceDescriptor(proto, subject, - requirement); + AssociatedConformance conformance, + llvm::Constant *definition) { + auto entity = LinkEntity::forAssociatedConformanceDescriptor(conformance); return defineAlias(entity, definition); } diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index f0198cfa31d..a60fa707f9b 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -712,10 +712,12 @@ namespace { if (entry.isAssociatedConformance()) { // Define the associated conformance descriptor to point to the // current position in the protocol descriptor. + AssociatedConformance conformance( + Proto, + entry.getAssociatedConformancePath(), + entry.getAssociatedConformanceRequirement()); IGM.defineAssociatedConformanceDescriptor( - Proto, - entry.getAssociatedConformancePath(), - entry.getAssociatedConformanceRequirement(), + conformance, B.getAddrOfCurrentPosition(IGM.ProtocolRequirementStructTy)); } diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 0cf4f020ec3..a04e8ae4344 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -1385,6 +1385,13 @@ llvm::Value *uniqueForeignWitnessTableRef(IRGenFunction &IGF, void addAssociatedConformance(AssociatedConformance requirement) { // FIXME: Add static witness tables for type conformances. + auto &entry = SILEntries.front(); + (void)entry; + SILEntries = SILEntries.slice(1); + + if (ResilientConformance) + return; + auto associate = ConformanceInContext.getAssociatedType( requirement.getAssociation())->getCanonicalType(); @@ -1394,9 +1401,8 @@ llvm::Value *uniqueForeignWitnessTableRef(IRGenFunction &IGF, requirement.getAssociation(), requirement.getAssociatedRequirement()); + #ifndef NDEBUG - auto &entry = SILEntries.front(); - (void)entry; assert(entry.getKind() == SILWitnessTable::AssociatedTypeProtocol && "sil witness table does not match protocol"); auto associatedWitness = entry.getAssociatedTypeProtocolWitness(); @@ -1411,8 +1417,6 @@ llvm::Value *uniqueForeignWitnessTableRef(IRGenFunction &IGF, "offset doesn't match ProtocolInfo layout"); #endif - SILEntries = SILEntries.slice(1); - llvm::Constant *wtableAccessFunction = getAssociatedTypeWitnessTableAccessFunction(requirement, associate, @@ -1644,7 +1648,8 @@ llvm::Constant *WitnessTableBuilder:: getAssociatedTypeWitnessTableAccessFunction(AssociatedConformance requirement, CanType associatedType, ProtocolConformanceRef associatedConformance) { - if (!associatedType->hasArchetype()) { + bool hasArchetype = associatedType->hasArchetype(); + if (!hasArchetype && !ResilientConformance) { assert(associatedConformance.isConcrete() && "no concrete conformance for non-dependent type"); return getOrCreateWitnessTableAccessFunction(IGM, @@ -1682,13 +1687,6 @@ getAssociatedTypeWitnessTableAccessFunction(AssociatedConformance requirement, Address destTable(parameters.claimNext(), IGM.getPointerAlignment()); setProtocolWitnessTableName(IGM, destTable.getAddress(), ConcreteType, Conformance.getProtocol()); - IGF.bindLocalTypeDataFromSelfWitnessTable( - &Conformance, - destTable.getAddress(), - [&](CanType type) { - return Conformance.getDeclContext()->mapTypeIntoContext(type) - ->getCanonicalType(); - }); ProtocolDecl *associatedProtocol = requirement.getAssociatedRequirement(); @@ -1710,6 +1708,24 @@ getAssociatedTypeWitnessTableAccessFunction(AssociatedConformance requirement, } } + // If there are no archetypes, return a reference to the table. There is + // no need for a cache. + if (!hasArchetype) { + auto wtable = MetadataResponse::forComplete( + conformanceI->getTable(IGF, &associatedTypeMetadata)) + .getMetadata(); + IGF.Builder.CreateRet(wtable); + return accessor; + } + + IGF.bindLocalTypeDataFromSelfWitnessTable( + &Conformance, + destTable.getAddress(), + [&](CanType type) { + return Conformance.getDeclContext()->mapTypeIntoContext(type) + ->getCanonicalType(); + }); + // If the witness table is directly fulfillable from the type, // we don't need a cache entry. // TODO: maybe we should have a cache entry anyway if the fulfillment @@ -1869,7 +1885,8 @@ llvm::Constant *WitnessTableBuilder::emitResilientWitnessTable() { unsigned count = 0; for (auto &entry : SILWT->getEntries()) { if (entry.getKind() != SILWitnessTable::Method && - entry.getKind() != SILWitnessTable::AssociatedType) + entry.getKind() != SILWitnessTable::AssociatedType && + entry.getKind() != SILWitnessTable::AssociatedTypeProtocol) continue; count++; @@ -1907,6 +1924,36 @@ llvm::Constant *WitnessTableBuilder::emitResilientWitnessTable() { continue; } + // Associated conformance access function. + if (entry.getKind() == SILWitnessTable::AssociatedTypeProtocol) { + const auto &witness = entry.getAssociatedTypeProtocolWitness(); + + // Associated type descriptor. + AssociatedConformance requirement(SILWT->getConformance()->getProtocol(), + witness.Requirement, + witness.Protocol); + auto assocConformanceDescriptor = + IGM.getAddrOfLLVMVariableOrGOTEquivalent( + LinkEntity::forAssociatedConformanceDescriptor(requirement), + Alignment(4), IGM.ProtocolRequirementStructTy); + table.addRelativeAddress(assocConformanceDescriptor); + + auto associate = + ConformanceInContext.getAssociatedType( + witness.Requirement)->getCanonicalType(); + + ProtocolConformanceRef associatedConformance = + ConformanceInContext.getAssociatedConformance(witness.Requirement, + witness.Protocol); + + llvm::Constant *wtableAccessFunction = + getAssociatedTypeWitnessTableAccessFunction(requirement, + associate, + associatedConformance); + table.addRelativeAddress(wtableAccessFunction); + continue; + } + if (entry.getKind() != SILWitnessTable::Method) continue; diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index b9efb2e9ccb..4a8bfe79fdc 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -1237,10 +1237,8 @@ public: llvm::Constant *getAddrOfAssociatedConformanceDescriptor( AssociatedConformance conformance); llvm::GlobalValue *defineAssociatedConformanceDescriptor( - ProtocolDecl *proto, - CanType subject, - ProtocolDecl *requirement, - llvm::Constant *definition); + AssociatedConformance conformance, + llvm::Constant *definition); llvm::Constant *getAddrOfProtocolDescriptor(ProtocolDecl *D, ConstantInit definition = ConstantInit()); diff --git a/lib/TBDGen/TBDGen.cpp b/lib/TBDGen/TBDGen.cpp index 68e0d42ffd9..3d693e5106a 100644 --- a/lib/TBDGen/TBDGen.cpp +++ b/lib/TBDGen/TBDGen.cpp @@ -100,11 +100,8 @@ void TBDGenVisitor::addAssociatedTypeDescriptor(AssociatedTypeDecl *assocType) { } void TBDGenVisitor::addAssociatedConformanceDescriptor( - ProtocolDecl *proto, - CanType subject, - ProtocolDecl *requirement) { - auto entity = LinkEntity::forAssociatedConformanceDescriptor(proto, subject, - requirement); + AssociatedConformance conformance) { + auto entity = LinkEntity::forAssociatedConformanceDescriptor(conformance); addSymbol(entity); } @@ -426,10 +423,11 @@ void TBDGenVisitor::visitProtocolDecl(ProtocolDecl *PD) { if (req.getFirstType()->isEqual(PD->getProtocolSelfType())) continue; - addAssociatedConformanceDescriptor( - PD, - req.getFirstType()->getCanonicalType(), - req.getSecondType()->castTo()->getDecl()); + AssociatedConformance conformance( + PD, + req.getFirstType()->getCanonicalType(), + req.getSecondType()->castTo()->getDecl()); + addAssociatedConformanceDescriptor(conformance); } for (auto *member : PD->getMembers()) { diff --git a/lib/TBDGen/TBDGenVisitor.h b/lib/TBDGen/TBDGenVisitor.h index 9db482ea1ad..e4682652e7e 100644 --- a/lib/TBDGen/TBDGenVisitor.h +++ b/lib/TBDGen/TBDGenVisitor.h @@ -66,9 +66,7 @@ private: void addProtocolRequirementsBaseDescriptor(ProtocolDecl *proto); void addAssociatedTypeDescriptor(AssociatedTypeDecl *assocType); - void addAssociatedConformanceDescriptor(ProtocolDecl *proto, - CanType subject, - ProtocolDecl *requirement); + void addAssociatedConformanceDescriptor(AssociatedConformance conformance); public: TBDGenVisitor(tapi::internal::InterfaceFile &symbols, diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 82651142ccb..2687f3eb26c 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -3650,9 +3650,9 @@ static void initializeResilientWitnessTable(GenericWitnessTable *genericTable, case ProtocolRequirementFlags::Kind::ReadCoroutine: case ProtocolRequirementFlags::Kind::ModifyCoroutine: case ProtocolRequirementFlags::Kind::AssociatedTypeAccessFunction: + case ProtocolRequirementFlags::Kind::AssociatedConformanceAccessFunction: break; case ProtocolRequirementFlags::Kind::BaseProtocol: - case ProtocolRequirementFlags::Kind::AssociatedConformanceAccessFunction: continue; } diff --git a/test/IRGen/protocol_resilience_descriptors.swift b/test/IRGen/protocol_resilience_descriptors.swift index 7dd512ee44c..048b3cfa47a 100644 --- a/test/IRGen/protocol_resilience_descriptors.swift +++ b/test/IRGen/protocol_resilience_descriptors.swift @@ -51,6 +51,12 @@ public protocol P { } public struct ConditionallyConforms { } public struct Y { } +// CHECK-USAGE: @"$S31protocol_resilience_descriptors29ConformsWithAssocRequirementsV010resilient_A008ProtocoleF12TypeDefaultsAAWr" = internal +// CHECK-USAGE-SAME: $S18resilient_protocol29ProtocolWithAssocTypeDefaultsP2T2_AA014OtherResilientC0Tn +// CHECK-USAGE-SAME: $S31protocol_resilience_descriptors29ConformsWithAssocRequirementsV010resilient_A008ProtocoleF12TypeDefaultsAA2T2_AD014OtherResilientI0PWT +public struct ConformsWithAssocRequirements : ProtocolWithAssocTypeDefaults { +} + // CHECK-USAGE: @"$Sx1T_MXA" = // CHECK-USAGE-SAME: i32 0 // CHECK-USAGE-SAME: @"{{got.|__imp_}}$S18resilient_protocol24ProtocolWithRequirementsMp" From b71bef1e42a8ca96e5e8a23137e4e9b4a6e99c89 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 17 Sep 2018 22:45:15 -0700 Subject: [PATCH 3/7] [ABI] Mangling for default associated conformance accessors. Default associated conformance accessors will be used in default witness tables to fill in associated conformances for defaulted associated types. Add (de|re|)mangling support for them and make them linking entities in IRGen. --- docs/ABI/Mangling.rst | 3 ++- include/swift/Demangling/DemangleNodes.def | 1 + include/swift/IRGen/Linking.h | 19 ++++++++++++++++++- lib/Demangling/Demangler.cpp | 9 +++++++++ lib/Demangling/NodePrinter.cpp | 9 +++++++++ lib/Demangling/OldRemangler.cpp | 4 ++++ lib/Demangling/Remangler.cpp | 5 +++++ lib/IRGen/IRGenMangler.h | 13 +++++++++++++ lib/IRGen/Linking.cpp | 10 ++++++++++ test/Demangle/Inputs/manglings.txt | 1 + 10 files changed, 72 insertions(+), 2 deletions(-) diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index 9b4200161cd..cf331843d3a 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -141,7 +141,8 @@ types where the metadata itself has unknown layout.) global ::= protocol 'TL' // protocol requirements base descriptor global ::= assoc-type-name 'Tl' // associated type descriptor global ::= assoc-type-name 'TM' // default associated type witness accessor - global ::= type assoc-type-path protocol // associated conformance descriptor + global ::= type assoc-type-path protocol 'Tn' // associated conformance descriptor + global ::= type assoc-type-path protocol 'TN' // default associated conformance witness accessor REABSTRACT-THUNK-TYPE ::= 'R' // reabstraction thunk helper function REABSTRACT-THUNK-TYPE ::= 'r' // reabstraction thunk diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index cb1d6f3b1cc..ed283d66806 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -211,6 +211,7 @@ NODE(DispatchThunk) NODE(MethodDescriptor) NODE(ProtocolRequirementsBaseDescriptor) NODE(AssociatedConformanceDescriptor) +NODE(DefaultAssociatedConformanceAccessor) NODE(AssociatedTypeDescriptor) NODE(ThrowsAnnotation) NODE(EmptyList) diff --git a/include/swift/IRGen/Linking.h b/include/swift/IRGen/Linking.h index 0eb8ef78fcc..2e99868ffd2 100644 --- a/include/swift/IRGen/Linking.h +++ b/include/swift/IRGen/Linking.h @@ -213,6 +213,11 @@ class LinkEntity { /// is stored in the data. AssociatedConformanceDescriptor, + /// A default accessor for an associated conformance of a protocol. + /// The pointer is a ProtocolDecl*; the index of the associated conformance + /// is stored in the data. + DefaultAssociatedConformanceAccessor, + /// A function which returns the default type metadata for the associated /// type of a protocol. The secondary pointer is a ProtocolDecl*. /// The index of the associated type declaration is stored in the data. @@ -834,6 +839,17 @@ public: return entity; } + static LinkEntity + forDefaultAssociatedConformanceAccessor(AssociatedConformance conformance) { + LinkEntity entity; + entity.setForProtocolAndAssociatedConformance( + Kind::DefaultAssociatedConformanceAccessor, + conformance.getSourceProtocol(), + conformance.getAssociation(), + conformance.getAssociatedRequirement()); + return entity; + } + static LinkEntity forReflectionBuiltinDescriptor(CanType type) { LinkEntity entity; entity.setForType(Kind::ReflectionBuiltinDescriptor, type); @@ -924,7 +940,8 @@ public: LINKENTITY_GET_FIELD(Data, AssociatedConformanceIndex)); } - assert(getKind() == Kind::AssociatedConformanceDescriptor); + assert(getKind() == Kind::AssociatedConformanceDescriptor || + getKind() == Kind::DefaultAssociatedConformanceAccessor); return getAssociatedConformanceByIndex( cast(getDecl()), LINKENTITY_GET_FIELD(Data, AssociatedConformanceIndex)); diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index 758ccd62bae..5a54f3a6ba5 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -1835,6 +1835,15 @@ NodePointer Demangler::demangleThunkOrSpecialization() { protoTy, assocTypePath, requirementTy); } + case 'N': { + NodePointer requirementTy = popProtocol(); + auto assocTypePath = popAssocTypePath(); + NodePointer protoTy = popNode(Node::Kind::Type); + return createWithChildren( + Node::Kind::DefaultAssociatedConformanceAccessor, + protoTy, assocTypePath, requirementTy); + } + case 'H': case 'h': { auto nodeKind = c == 'H' ? Node::Kind::KeyPathEqualsThunkHelper diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index 50e451bff0f..363d552faab 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -323,6 +323,7 @@ private: case Node::Kind::DeclContext: case Node::Kind::DefaultArgumentInitializer: case Node::Kind::DefaultAssociatedTypeMetadataAccessor: + case Node::Kind::DefaultAssociatedConformanceAccessor: case Node::Kind::DependentAssociatedTypeRef: case Node::Kind::DependentGenericSignature: case Node::Kind::DependentGenericParamCount: @@ -1564,6 +1565,14 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) { Printer << ": "; print(Node->getChild(2)); return nullptr; + case Node::Kind::DefaultAssociatedConformanceAccessor: + Printer << "default associated conformance accessor for "; + print(Node->getChild(0)); + Printer << "."; + print(Node->getChild(1)); + Printer << ": "; + print(Node->getChild(2)); + return nullptr; case Node::Kind::AssociatedTypeDescriptor: Printer << "associated type descriptor for "; print(Node->getChild(0)); diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index 2549d1a04bf..e4f08eeb659 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -888,6 +888,10 @@ void Remangler::mangleAssociatedConformanceDescriptor(Node *node) { Out << ""; } +void Remangler::mangleDefaultAssociatedConformanceAccessor(Node *node) { + Out << ""; +} + void Remangler::mangleAssociatedTypeMetadataAccessor(Node *node) { Out << "Wt"; mangleChildNodes(node); // protocol conformance, identifier diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index f7ffe88252d..348b288130f 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -579,6 +579,11 @@ void Remangler::mangleAssociatedConformanceDescriptor(Node *node) { Buffer << "Tn"; } +void Remangler::mangleDefaultAssociatedConformanceAccessor(Node *node) { + mangleChildNodes(node); + Buffer << "TN"; +} + void Remangler::mangleAssociatedTypeMetadataAccessor(Node *node) { mangleChildNodes(node); // protocol conformance, identifier Buffer << "Wt"; diff --git a/lib/IRGen/IRGenMangler.h b/lib/IRGen/IRGenMangler.h index edd77135904..37c5b953481 100644 --- a/lib/IRGen/IRGenMangler.h +++ b/lib/IRGen/IRGenMangler.h @@ -197,6 +197,19 @@ public: return finalize(); } + std::string mangleDefaultAssociatedConformanceAccessor( + const ProtocolDecl *proto, + CanType subject, + const ProtocolDecl *requirement) { + beginMangling(); + appendAnyGenericType(proto); + bool isFirstAssociatedTypeIdentifier = true; + appendAssociatedTypePath(subject, isFirstAssociatedTypeIdentifier); + appendProtocolName(requirement); + appendOperator("TN"); + return finalize(); + } + std::string mangleProtocolConformanceDescriptor( const ProtocolConformance *Conformance) { beginMangling(); diff --git a/lib/IRGen/Linking.cpp b/lib/IRGen/Linking.cpp index 6562b5093ac..b24a2470ce0 100644 --- a/lib/IRGen/Linking.cpp +++ b/lib/IRGen/Linking.cpp @@ -187,6 +187,14 @@ std::string LinkEntity::mangleAsString() const { assocConformance.second); } + case Kind::DefaultAssociatedConformanceAccessor: { + auto assocConformance = getAssociatedConformance(); + return mangler.mangleDefaultAssociatedConformanceAccessor( + cast(getDecl()), + assocConformance.first, + assocConformance.second); + } + case Kind::ProtocolConformanceDescriptor: return mangler.mangleProtocolConformanceDescriptor( cast(getProtocolConformance())); @@ -498,6 +506,7 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const { case Kind::AssociatedTypeMetadataAccessFunction: case Kind::DefaultAssociatedTypeMetadataAccessFunction: case Kind::AssociatedTypeWitnessTableAccessFunction: + case Kind::DefaultAssociatedConformanceAccessor: case Kind::GenericProtocolWitnessTableCache: case Kind::GenericProtocolWitnessTableInstantiationFunction: return SILLinkage::Private; @@ -629,6 +638,7 @@ bool LinkEntity::isAvailableExternally(IRGenModule &IGM) const { case Kind::TypeMetadataCompletionFunction: case Kind::TypeMetadataPattern: case Kind::DefaultAssociatedTypeMetadataAccessFunction: + case Kind::DefaultAssociatedConformanceAccessor: return false; case Kind::ValueWitness: diff --git a/test/Demangle/Inputs/manglings.txt b/test/Demangle/Inputs/manglings.txt index 35fe8a3acfb..57244a99ede 100644 --- a/test/Demangle/Inputs/manglings.txt +++ b/test/Demangle/Inputs/manglings.txt @@ -332,3 +332,4 @@ A.h(A) -> ()inlined generic function <(A, A1)> of $S1T19protocol_resilience17ResilientProtocolPTl --> associated type descriptor for T $S18resilient_protocol21ResilientBaseProtocolTL --> protocol requirements base descriptor for resilient_protocol.ResilientBaseProtocol $S1t1PP10AssocType2_AA1QTn --> associated conformance descriptor for t.P.AssocType2: t.Q +$S1t1PP10AssocType2_AA1QTN --> default associated conformance accessor for t.P.AssocType2: t.Q From 89f3da6d281a9d52962dc1143e1641f42038cfa4 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 18 Sep 2018 10:30:29 -0700 Subject: [PATCH 4/7] [Sema] Start recording default associated conformances. When forming the default witness table for a resilient protocol, look for default associated conformances as well. These are identified by associated conformance requirements whose root associated type has a default type. For such requirements, we look for a conformance and record it as a default associated conformance. Emit a warning in cases where we don't find such a conformance, because the associated type and conformance *may* have been added with the intent of being resilient, and we can't know. This warning might be a terrible idea, but it is only enabled under -enable-resilience (which itself is hidden) and fires in one only place in the standard library (which seems legitimate), so we'll try it for now. --- include/swift/AST/Decl.h | 17 +++ include/swift/AST/DiagnosticsSema.def | 5 + lib/AST/ASTContext.cpp | 26 +++++ lib/Sema/TypeCheckProtocol.cpp | 111 +++++++++++++++++--- test/decl/protocol/resilient_defaults.swift | 18 ++++ 5 files changed, 164 insertions(+), 13 deletions(-) create mode 100644 test/decl/protocol/resilient_defaults.swift diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index ff0ee4a8836..efae469d498 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3979,6 +3979,23 @@ public: /// Record the default witness for a requirement. void setDefaultWitness(ValueDecl *requirement, Witness witness); + /// Returns the default associated conformance witness for an associated + /// type, or \c None if there is no default. + /// + /// \param reqIndex The index of the associated conformance within the + /// requirement signature. + Optional getDefaultAssociatedConformanceWitness( + unsigned reqIndex) const; + + /// Set the default associated conformance witness for the given + /// associated conformance. + /// + /// \param reqIndex The index of the associated conformance within the + /// requirement signature. + void setDefaultAssociatedConformanceWitness( + unsigned reqIndex, + ProtocolConformanceRef conformance); + /// Retrieve the name to use for this protocol when interoperating /// with the Objective-C runtime. StringRef getObjCRuntimeName(llvm::SmallVectorImpl &buffer) const; diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 43d65136737..7789b958ea1 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1686,6 +1686,11 @@ ERROR(type_witness_objc_generic_parameter,none, NOTE(witness_fix_access,none, "mark the %0 as '%select{%error|fileprivate|internal|public|%error}1' to " "satisfy the requirement", (DescriptiveDeclKind, AccessLevel)) +WARNING(assoc_type_default_conformance_failed,none, + "default type %0 for associated type %1 does not satisfy constraint " + "%2: %3", (Type, DeclName, Type, Type)) +NOTE(assoc_type_default_here,none, + "associated type %0 has default type %1 written here", (DeclName, Type)) ERROR(protocol_access,none, "%select{protocol must be declared %select{" diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 91ee24e3ad7..06ba3167075 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -285,6 +285,11 @@ FOR_KNOWN_FOUNDATION_TYPES(CACHE_FOUNDATION_DECL) llvm::DenseMap, Type> DefaultTypeWitnesses; + /// Default associated conformance witnesses for protocols. + llvm::DenseMap, + ProtocolConformanceRef> + DefaultAssociatedConformanceWitnesses; + /// \brief Structure that captures data that is segregated into different /// arenas. struct Arena { @@ -1709,6 +1714,27 @@ void ProtocolDecl::setDefaultTypeWitness(AssociatedTypeDecl *assocType, (void)pair; } +Optional +ProtocolDecl::getDefaultAssociatedConformanceWitness(unsigned reqIndex) const { + auto &ctx = getASTContext(); + auto found = + ctx.getImpl().DefaultAssociatedConformanceWitnesses.find({this, reqIndex}); + if (found == ctx.getImpl().DefaultAssociatedConformanceWitnesses.end()) + return None; + + return found->second; +} + +void ProtocolDecl::setDefaultAssociatedConformanceWitness( + unsigned reqIndex, + ProtocolConformanceRef conformance) { + auto &ctx = getASTContext(); + auto pair = ctx.getImpl().DefaultAssociatedConformanceWitnesses.insert( + std::make_pair(std::make_pair(this, reqIndex), conformance)); + assert(pair.second && "Already have a default associated conformance"); + (void)pair; +} + bool ASTContext::canImportModule(std::pair ModulePath) { // If this module has already been successfully imported, it is importable. if (getLoadedModule(ModulePath) != nullptr) diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 9cc6860adbf..9f6a65e8d9d 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -5252,6 +5252,30 @@ void DefaultWitnessChecker::recordWitness( void TypeChecker::inferDefaultWitnesses(ProtocolDecl *proto) { DefaultWitnessChecker checker(*this, proto); + // Find the default for the given associated type. + auto findAssociatedTypeDefault = + [&](AssociatedTypeDecl *assocType, + AssociatedTypeDecl **defaultedAssocTypeOut = nullptr) -> Type { + auto defaultedAssocType = + AssociatedTypeInference::findDefaultedAssociatedType(*this, assocType); + if (!defaultedAssocType) + return nullptr;; + + Type defaultType = defaultedAssocType->getDefaultDefinitionLoc().getType(); + if (!defaultType) + return nullptr; + + // Map out of its protocol context... + defaultType = defaultType->mapTypeOutOfContext(); + if (defaultType->hasError()) + return nullptr; + + if (defaultedAssocTypeOut) + *defaultedAssocTypeOut = defaultedAssocType; + + return defaultType; + }; + for (auto *requirement : proto->getMembers()) { if (requirement->isInvalid()) continue; @@ -5262,19 +5286,8 @@ void TypeChecker::inferDefaultWitnesses(ProtocolDecl *proto) { if (auto assocType = dyn_cast(valueDecl)) { if (assocType->getOverriddenDecls().empty()) { - if (auto defaultedAssocType = - AssociatedTypeInference::findDefaultedAssociatedType( - *this, assocType)) { - Type defaultType = - defaultedAssocType->getDefaultDefinitionLoc().getType(); - - // Map out of its protocol context... - defaultType = defaultType->mapTypeOutOfContext(); - - if (!defaultType->hasError()) { - proto->setDefaultTypeWitness(assocType, defaultType); - } - } + if (Type defaultType = findAssociatedTypeDefault(assocType)) + proto->setDefaultTypeWitness(assocType, defaultType); } continue; @@ -5288,6 +5301,78 @@ void TypeChecker::inferDefaultWitnesses(ProtocolDecl *proto) { checker.resolveWitnessViaLookup(valueDecl); } + + // Find defaults for any associated conformances rooted on defaulted + // associated types. + auto requirementSignature = proto->getRequirementSignature(); + for (unsigned reqIndex : indices(requirementSignature)) { + const auto &req = requirementSignature[reqIndex]; + if (req.getKind() != RequirementKind::Conformance) + continue; + if (req.getFirstType()->isEqual(proto->getProtocolSelfType())) + continue; + + // Find the innermost dependent member type (e.g., Self.AssocType), so + // we can look at the associated type. + auto depMemTy = req.getFirstType()->getAs(); + if (!depMemTy) + continue; + + while (auto innerDepMemTy = + depMemTy->getBase()->getAs()) + depMemTy = innerDepMemTy; + + if (!depMemTy->getBase()->isEqual(proto->getProtocolSelfType())) + continue; + + auto assocType = depMemTy->getAssocType(); + if (!assocType) + continue; + + // Find the associated type nearest our own protocol, which might have + // a default not available in the associated type referenced by the + // (canonicalized) requirement. + if (assocType->getProtocol() != proto) { + SmallVector found; + proto->getModuleContext()->lookupQualified( + proto, assocType->getFullName(), + NL_QualifiedDefault|NL_ProtocolMembers|NL_OnlyTypes, + found); + if (found.size() == 1 && isa(found[0])) + assocType = cast(found[0]); + } + + // Dig out the default associated type definition. + AssociatedTypeDecl *defaultedAssocType = nullptr; + Type defaultAssocType = findAssociatedTypeDefault(assocType, + &defaultedAssocType); + if (!defaultAssocType) + continue; + + Type defaultAssocTypeInContext = + proto->mapTypeIntoContext(defaultAssocType); + auto requirementProto = + req.getSecondType()->castTo()->getDecl(); + auto conformance = conformsToProtocol(defaultAssocTypeInContext, + requirementProto, proto, + ConformanceCheckFlags::Used); + if (!conformance) { + // Diagnose the lack of a conformance. This is potentially an ABI + // incompatibility. + diagnose(proto, diag::assoc_type_default_conformance_failed, + defaultAssocType, assocType->getFullName(), req.getFirstType(), + req.getSecondType()); + diagnose(defaultedAssocType, diag::assoc_type_default_here, + assocType->getFullName(), defaultAssocType) + .highlight( + defaultedAssocType->getDefaultDefinitionLoc().getSourceRange()); + + continue; + } + + // Record the default associated conformance. + proto->setDefaultAssociatedConformanceWitness(reqIndex, *conformance); + } } void TypeChecker::recordKnownWitness(NormalProtocolConformance *conformance, diff --git a/test/decl/protocol/resilient_defaults.swift b/test/decl/protocol/resilient_defaults.swift new file mode 100644 index 00000000000..b959241bae5 --- /dev/null +++ b/test/decl/protocol/resilient_defaults.swift @@ -0,0 +1,18 @@ +// RUN: %target-typecheck-verify-swift -enable-resilience + +public struct Wrapper: P { } +extension Wrapper: Q where T: Q { } + +public protocol PBase { + associatedtype AssocType +} + +public protocol P: PBase { + override associatedtype AssocType: P = Wrapper + // expected-note@-1{{associated type 'AssocType' has default type 'Wrapper' written here}} +} + +public protocol Q: P where Self.AssocType: Q { } + +public protocol R: Q where Self.AssocType: R { } +// expected-warning@-1{{default type 'Wrapper' for associated type 'AssocType' does not satisfy constraint 'Self.AssocType': 'R'}} From fde7eb5f3de671cc8242a5314d24550b94b2261f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 19 Sep 2018 10:53:46 -0700 Subject: [PATCH 5/7] [AST] Record default associated conformance witnesses directly. Referencing associated conformance witnesses via the index of the requirement in the requirement signature is inconvenient. Reference them by CanType/ProtocolDecl* instead. --- include/swift/AST/Decl.h | 12 ++++-------- lib/AST/ASTContext.cpp | 15 ++++++++++----- lib/Sema/TypeCheckProtocol.cpp | 7 +++---- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index efae469d498..370dcf8dcb3 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3981,19 +3981,15 @@ public: /// Returns the default associated conformance witness for an associated /// type, or \c None if there is no default. - /// - /// \param reqIndex The index of the associated conformance within the - /// requirement signature. Optional getDefaultAssociatedConformanceWitness( - unsigned reqIndex) const; + CanType association, + ProtocolDecl *requirement) const; /// Set the default associated conformance witness for the given /// associated conformance. - /// - /// \param reqIndex The index of the associated conformance within the - /// requirement signature. void setDefaultAssociatedConformanceWitness( - unsigned reqIndex, + CanType association, + ProtocolDecl *requirement, ProtocolConformanceRef conformance); /// Retrieve the name to use for this protocol when interoperating diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 06ba3167075..1d5b0d357b6 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -286,7 +286,7 @@ FOR_KNOWN_FOUNDATION_TYPES(CACHE_FOUNDATION_DECL) DefaultTypeWitnesses; /// Default associated conformance witnesses for protocols. - llvm::DenseMap, + llvm::DenseMap, ProtocolConformanceRef> DefaultAssociatedConformanceWitnesses; @@ -1715,10 +1715,13 @@ void ProtocolDecl::setDefaultTypeWitness(AssociatedTypeDecl *assocType, } Optional -ProtocolDecl::getDefaultAssociatedConformanceWitness(unsigned reqIndex) const { +ProtocolDecl::getDefaultAssociatedConformanceWitness( + CanType association, + ProtocolDecl *requirement) const { auto &ctx = getASTContext(); auto found = - ctx.getImpl().DefaultAssociatedConformanceWitnesses.find({this, reqIndex}); + ctx.getImpl().DefaultAssociatedConformanceWitnesses.find( + std::make_tuple(this, association, requirement)); if (found == ctx.getImpl().DefaultAssociatedConformanceWitnesses.end()) return None; @@ -1726,11 +1729,13 @@ ProtocolDecl::getDefaultAssociatedConformanceWitness(unsigned reqIndex) const { } void ProtocolDecl::setDefaultAssociatedConformanceWitness( - unsigned reqIndex, + CanType association, + ProtocolDecl *requirement, ProtocolConformanceRef conformance) { auto &ctx = getASTContext(); auto pair = ctx.getImpl().DefaultAssociatedConformanceWitnesses.insert( - std::make_pair(std::make_pair(this, reqIndex), conformance)); + std::make_pair(std::make_tuple(this, association, requirement), + conformance)); assert(pair.second && "Already have a default associated conformance"); (void)pair; } diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index 9f6a65e8d9d..4b1f7a56346 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -5304,9 +5304,7 @@ void TypeChecker::inferDefaultWitnesses(ProtocolDecl *proto) { // Find defaults for any associated conformances rooted on defaulted // associated types. - auto requirementSignature = proto->getRequirementSignature(); - for (unsigned reqIndex : indices(requirementSignature)) { - const auto &req = requirementSignature[reqIndex]; + for (const auto &req : proto->getRequirementSignature()) { if (req.getKind() != RequirementKind::Conformance) continue; if (req.getFirstType()->isEqual(proto->getProtocolSelfType())) @@ -5371,7 +5369,8 @@ void TypeChecker::inferDefaultWitnesses(ProtocolDecl *proto) { } // Record the default associated conformance. - proto->setDefaultAssociatedConformanceWitness(reqIndex, *conformance); + proto->setDefaultAssociatedConformanceWitness( + req.getFirstType()->getCanonicalType(), requirementProto, *conformance); } } From fb62977c2be9d0c8f94038f4ad62af8b4430447d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 19 Sep 2018 10:56:20 -0700 Subject: [PATCH 6/7] [IRGen] Emit default associated conformance witnesses. For a resilient protocol that has defaulted associated types, emit default associated conformance witnesses that compute associated conformances based on that default witness. This completes the implementation of resilience protocols that add new, defaulted associated types, rdar://problem/44167982. --- lib/IRGen/GenDecl.cpp | 19 ++++ lib/IRGen/GenMeta.cpp | 92 ++++++++++++++++++- lib/IRGen/IRGenModule.h | 2 + lib/SILGen/SILGenType.cpp | 14 ++- .../protocol_resilience_descriptors.swift | 6 ++ test/Inputs/resilient_protocol.swift | 4 + test/SILGen/protocol_resilience.swift | 12 +++ .../Inputs/protocol_add_requirements.swift | 27 +++++- .../test_protocol_add_requirements.swift | 11 +++ 9 files changed, 182 insertions(+), 5 deletions(-) diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 793ab1e1252..0cc89afd7e4 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -3982,6 +3982,25 @@ IRGenModule::getAddrOfDefaultAssociatedTypeMetadataAccessFunction( return entry; } +llvm::Function * +IRGenModule::getAddrOfDefaultAssociatedConformanceAccessor( + AssociatedConformance requirement) { + auto forDefinition = ForDefinition; + + LinkEntity entity = + LinkEntity::forDefaultAssociatedConformanceAccessor(requirement); + llvm::Function *&entry = GlobalFuncs[entity]; + if (entry) { + if (forDefinition) updateLinkageForDefinition(*this, entry, entity); + return entry; + } + + auto signature = getAssociatedTypeWitnessTableAccessFunctionSignature(); + LinkInfo link = LinkInfo::get(*this, entity, forDefinition); + entry = createFunction(*this, link, signature); + return entry; +} + llvm::Function * IRGenModule::getAddrOfContinuationPrototype(CanSILFunctionType fnType) { LinkEntity entity = LinkEntity::forCoroutineContinuationPrototype(fnType); diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index a60fa707f9b..a75abe04fe7 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -647,7 +647,14 @@ namespace { if (entry.isAssociatedConformance()) { auto flags = Flags(Flags::Kind::AssociatedConformanceAccessFunction); - return { flags, nullptr }; + + // Look for a default witness. + llvm::Constant *defaultImpl = + findDefaultAssociatedConformanceWitness( + entry.getAssociatedConformancePath(), + entry.getAssociatedConformanceRequirement()); + + return { flags, defaultImpl }; } assert(entry.isFunction()); @@ -802,6 +809,89 @@ namespace { IGF.Builder.CreateRet(returnValue); return accessor; } + + llvm::Constant *findDefaultAssociatedConformanceWitness( + CanType association, + ProtocolDecl *requirement) { + if (!DefaultWitnesses) return nullptr; + + for (auto &entry : DefaultWitnesses->getEntries()) { + if (!entry.isValid() || + entry.getKind() != SILWitnessTable::AssociatedTypeProtocol || + entry.getAssociatedTypeProtocolWitness().Protocol != requirement || + entry.getAssociatedTypeProtocolWitness().Requirement != association) + continue; + + auto witness = entry.getAssociatedTypeProtocolWitness().Witness; + return getDefaultAssociatedConformanceAccessFunction( + AssociatedConformance(Proto, association, requirement), + witness); + } + + return nullptr; + } + + llvm::Constant *getDefaultAssociatedConformanceAccessFunction( + AssociatedConformance requirement, + ProtocolConformanceRef conformance) { + auto accessor = + IGM.getAddrOfDefaultAssociatedConformanceAccessor(requirement); + + IRGenFunction IGF(IGM, accessor); + if (IGM.DebugInfo) + IGM.DebugInfo->emitArtificialFunction(IGF, accessor); + + Explosion parameters = IGF.collectParameters(); + + llvm::Value *associatedTypeMetadata = parameters.claimNext(); + llvm::Value *self = parameters.claimNext(); + llvm::Value *wtable = parameters.claimNext(); + + bool hasArchetype = + !conformance.isConcrete() || + conformance.getConcrete()->getType()->hasArchetype(); + if (hasArchetype) { + // Bind local Self type data from the metadata argument. + CanType selfInContext = + Proto->mapTypeIntoContext(Proto->getProtocolSelfType()) + ->getCanonicalType(); + IGF.bindLocalTypeDataFromTypeMetadata(selfInContext, IsExact, self, + MetadataState::Abstract); + IGF.setUnscopedLocalTypeData( + selfInContext, + LocalTypeDataKind::forAbstractProtocolWitnessTable(Proto), + wtable); + + // Bind the associated type metadata. + IGF.bindLocalTypeDataFromTypeMetadata(requirement.getAssociation(), + IsExact, + associatedTypeMetadata, + MetadataState::Abstract); + } + + // For a concrete witness table, call it. + ProtocolDecl *associatedProtocol = requirement.getAssociatedRequirement(); + if (conformance.isConcrete()) { + auto conformanceI = &IGM.getConformanceInfo(associatedProtocol, + conformance.getConcrete()); + auto returnValue = conformanceI->getTable(IGF, &associatedTypeMetadata); + IGF.Builder.CreateRet(returnValue); + return accessor; + } + + // For an abstract table, emit a reference to the witness table. + CanType associatedTypeInContext + = Proto->mapTypeIntoContext(requirement.getAssociation()) + ->getCanonicalType(); + auto returnValue = + emitArchetypeWitnessTableRef( + IGF, + cast(associatedTypeInContext), + associatedProtocol); + IGF.Builder.CreateRet(returnValue); + return accessor; + } + void addAssociatedTypeNames() { std::string AssociatedTypeNames; diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 4a8bfe79fdc..0dab10ee7cd 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -1291,6 +1291,8 @@ public: const AssociatedConformance &association); llvm::Function *getAddrOfDefaultAssociatedTypeMetadataAccessFunction( AssociatedType association); + llvm::Function *getAddrOfDefaultAssociatedConformanceAccessor( + AssociatedConformance requirement); Address getAddrOfObjCISAMask(); diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 135002631f9..4182f48e035 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -769,7 +769,19 @@ public: } void addAssociatedConformance(const AssociatedConformance &req) { - addMissingDefault(); + auto witness = + Proto->getDefaultAssociatedConformanceWitness( + req.getAssociation(), + req.getAssociatedRequirement()); + if (!witness) + return addMissingDefault(); + + auto entry = + SILWitnessTable::AssociatedTypeProtocolWitness{ + req.getAssociation(), + req.getAssociatedRequirement(), + *witness}; + DefaultWitnesses.push_back(entry); } }; diff --git a/test/IRGen/protocol_resilience_descriptors.swift b/test/IRGen/protocol_resilience_descriptors.swift index 048b3cfa47a..5f6c8caf979 100644 --- a/test/IRGen/protocol_resilience_descriptors.swift +++ b/test/IRGen/protocol_resilience_descriptors.swift @@ -14,6 +14,7 @@ // Protocol descriptor // CHECK-DEFINITION-LABEL: @"$S18resilient_protocol29ProtocolWithAssocTypeDefaultsMp" ={{( protected)?}} constant +// CHECK-DEFINITION-SAME: @"$S18resilient_protocol29ProtocolWithAssocTypeDefaultsP2T2_AA014OtherResilientC0TN" // CHECK-DEFINITION-SAME: $S2T118resilient_protocol29ProtocolWithAssocTypeDefaultsPTM // CHECK-DEFINITION-SAME: $S2T218resilient_protocol29ProtocolWithAssocTypeDefaultsPTM @@ -25,6 +26,9 @@ // CHECK-DEFINITION: @"$S1T18resilient_protocol24ProtocolWithRequirementsPTl" ={{( dllexport)?}}{{( protected)?}} alias // CHECK-DEFINITION: @"$S18resilient_protocol29ProtocolWithAssocTypeDefaultsP2T2_AA014OtherResilientC0Tn" ={{( dllexport)?}}{{( protected)?}} alias +// Default associated conformance witnesses +// CHECK-DEFINITION-LABEL: define internal swiftcc i8** @"$S18resilient_protocol29ProtocolWithAssocTypeDefaultsP2T2_AA014OtherResilientC0TN" + // Default associated type witnesses // CHECK-DEFINITION-LABEL: define internal swiftcc %swift.metadata_response @"$S2T118resilient_protocol29ProtocolWithAssocTypeDefaultsPTM" @@ -32,6 +36,8 @@ // CHECK-DEFINITION: getelementptr inbounds i8*, i8** [[WTABLE:%.*]], i32 2 // CHECK-DEFINITION: call{{.*}}S18resilient_protocol7WrapperVMa +// CHECK-DEFINITION-LABEL: define internal swiftcc %swift.metadata_response @"$S9AssocType18resilient_protocol20ResilientSelfDefaultPTM + import resilient_protocol // ---------------------------------------------------------------------------- diff --git a/test/Inputs/resilient_protocol.swift b/test/Inputs/resilient_protocol.swift index e6dfcb8dd76..9a93674a092 100644 --- a/test/Inputs/resilient_protocol.swift +++ b/test/Inputs/resilient_protocol.swift @@ -34,3 +34,7 @@ public protocol ProtocolWithAssocTypeDefaults { associatedtype T1 = Self associatedtype T2: OtherResilientProtocol = Wrapper } + +public protocol ResilientSelfDefault : ResilientBaseProtocol { + associatedtype AssocType: ResilientBaseProtocol = Self +} diff --git a/test/SILGen/protocol_resilience.swift b/test/SILGen/protocol_resilience.swift index e930bfbf77b..e75df7e57bd 100644 --- a/test/SILGen/protocol_resilience.swift +++ b/test/SILGen/protocol_resilience.swift @@ -251,6 +251,13 @@ func inoutResilientProtocol(_ x: inout OtherConformingType) { inoutFunc(&OtherConformingType.staticPropertyInExtension) } +// Protocol is public -- needs resilient witness table +public struct ConformsToP: P { } + +public protocol ResilientAssocTypes { + associatedtype AssocType: P = ConformsToP +} + // CHECK-LABEL: sil_default_witness_table P { // CHECK-NEXT: } @@ -312,3 +319,8 @@ func inoutResilientProtocol(_ x: inout OtherConformingType) { // CHECK-NEXT: method #ReabstractSelfRefined.callback!setter.1: {{.*}} : @$S19protocol_resilience21ReabstractSelfRefinedP8callbackyxxcvs // CHECK-NEXT: method #ReabstractSelfRefined.callback!modify.1: {{.*}} : @$S19protocol_resilience21ReabstractSelfRefinedP8callbackyxxcvM // CHECK-NEXT: } + +// CHECK-LABEL: sil_default_witness_table ResilientAssocTypes { +// CHECK-NEXT: associated_type_protocol (AssocType: P): ConformsToP: P module protocol_resilience +// CHECK-NEXT: associated_type AssocType: ConformsToP +// CHECK-NEXT: } diff --git a/validation-test/Evolution/Inputs/protocol_add_requirements.swift b/validation-test/Evolution/Inputs/protocol_add_requirements.swift index 6e2eb344e6f..81cfc38581a 100644 --- a/validation-test/Evolution/Inputs/protocol_add_requirements.swift +++ b/validation-test/Evolution/Inputs/protocol_add_requirements.swift @@ -176,7 +176,15 @@ public func doSomething(_ t: inout T, k1: T.Key, k2: T #endif } -public struct Wrapper { } +public protocol SimpleProtocol { + static func getString() -> String +} + +public struct Wrapper: SimpleProtocol { + public static func getString() -> String { + return "I am a wrapper for \(T.self)" + } +} public protocol AddAssocTypesProtocol { // FIXME: The presence of a single method requirement causes us to @@ -185,7 +193,7 @@ public protocol AddAssocTypesProtocol { #if AFTER associatedtype AssocType = Self - associatedtype AssocType2 = Wrapper + associatedtype AssocType2: SimpleProtocol = Wrapper #endif } @@ -193,7 +201,6 @@ extension AddAssocTypesProtocol { public func dummy() { } } - public func doSomethingWithAssocTypes(_ value: T) -> String { #if AFTER @@ -202,3 +209,17 @@ public func doSomethingWithAssocTypes(_ value: T) return "there are no associated types yet" #endif } + +public func doSomethingWithAssocConformances(_ value: T) + -> String { +#if AFTER + let at2: Any.Type = T.AssocType2.self + if let simpleType = at2 as? SimpleProtocol.Type { + return simpleType.getString() + } + + return "missing associated conformance" +#else + return "there are no associated conformances yet" +#endif +} diff --git a/validation-test/Evolution/test_protocol_add_requirements.swift b/validation-test/Evolution/test_protocol_add_requirements.swift index 046b6ee59e4..2acfb7e81d3 100644 --- a/validation-test/Evolution/test_protocol_add_requirements.swift +++ b/validation-test/Evolution/test_protocol_add_requirements.swift @@ -154,5 +154,16 @@ ProtocolAddRequirementsTest.test("AddAssociatedTypeRequirements") { } } +ProtocolAddRequirementsTest.test("AddAssociatedConformanceRequirements") { + let addString = AddAssociatedType() + let stringResult = doSomethingWithAssocConformances(addString) + + if getVersion() == 0 { + expectEqual("there are no associated conformances yet", stringResult) + } else { + expectEqual("I am a wrapper for AddAssociatedType", stringResult) + } +} + runAllTests() From 12da7c6e0ecad0f29ee7b56303bd0dbcf174219f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 19 Sep 2018 10:57:28 -0700 Subject: [PATCH 7/7] [SIL Parser] Extend the hack for Self in default witness tables. We *still* need a better way to model this, eventually, but this keeps the SIL parser limping along for default witness tables. --- lib/AST/NameLookup.cpp | 1 + lib/ParseSIL/ParseSIL.cpp | 126 ++++++++++++--------- test/SIL/Parser/default_witness_tables.sil | 4 +- 3 files changed, 77 insertions(+), 54 deletions(-) diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 1ef30d58785..c49efc148d7 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -709,6 +709,7 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC, }; if (Loc.isValid() && + DC->getParentSourceFile() && DC->getParentSourceFile()->Kind != SourceFileKind::REPL && Ctx.LangOpts.EnableASTScopeLookup) { // Find the source file in which we are performing the lookup. diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index e655e141137..75d0bc763a3 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -181,7 +181,8 @@ namespace { ProtocolConformance * parseProtocolConformanceHelper(ProtocolDecl *&proto, GenericEnvironment *GenericEnv, - bool localScope); + bool localScope, + ProtocolDecl *defaultForProto); public: SILParser(Parser &P) : P(P), SILMod(static_cast(P.SIL)->M), @@ -391,15 +392,18 @@ namespace { bool isStartOfSILInstruction(); bool parseSubstitutions(SmallVectorImpl &parsed, - GenericEnvironment *GenericEnv=nullptr); + GenericEnvironment *GenericEnv=nullptr, + ProtocolDecl *defaultForProto = nullptr); ProtocolConformance *parseProtocolConformance(ProtocolDecl *&proto, GenericEnvironment *&genericEnv, - bool localScope); - ProtocolConformance *parseProtocolConformance() { + bool localScope, + ProtocolDecl *defaultForProto); + ProtocolConformance *parseProtocolConformance( + ProtocolDecl *defaultForProto) { ProtocolDecl *dummy; GenericEnvironment *env; - return parseProtocolConformance(dummy, env, true); + return parseProtocolConformance(dummy, env, true, defaultForProto); } Optional @@ -1012,6 +1016,8 @@ bool SILParser::performTypeLocChecking(TypeLoc &T, bool IsSILType, if (!DC) DC = &P.SF; + else if (!GenericEnv) + GenericEnv = DC->getGenericEnvironmentOfContext(); return swift::performTypeLocChecking(P.Context, T, /*isSILMode=*/true, IsSILType, @@ -1510,10 +1516,46 @@ bool SILParser::parseSILBBArgsAtBranch(SmallVector &Args, return false; } +/// Bind any unqualified 'Self' references to the given protocol's 'Self' +/// generic parameter. +/// +/// FIXME: This is a hack to work around the lack of a DeclContext for +/// witness tables. +static void bindProtocolSelfInTypeRepr(TypeLoc &TL, ProtocolDecl *proto) { + if (auto typeRepr = TL.getTypeRepr()) { + // AST walker to update 'Self' references. + class BindProtocolSelf : public ASTWalker { + ProtocolDecl *proto; + GenericTypeParamDecl *selfParam; + Identifier selfId; + + public: + BindProtocolSelf(ProtocolDecl *proto) + : proto(proto), + selfParam(proto->getProtocolSelfType()->getDecl()), + selfId(proto->getASTContext().Id_Self) { + } + + virtual bool walkToTypeReprPre(TypeRepr *T) override { + if (auto ident = dyn_cast(T)) { + auto firstComponent = ident->getComponentRange().front(); + if (firstComponent->getIdentifier() == selfId) + firstComponent->setValue(selfParam, proto); + } + + return true; + } + }; + + typeRepr->walk(BindProtocolSelf(proto)); + } +} + /// Parse the substitution list for an apply instruction or /// specialized protocol conformance. bool SILParser::parseSubstitutions(SmallVectorImpl &parsed, - GenericEnvironment *GenericEnv) { + GenericEnvironment *GenericEnv, + ProtocolDecl *defaultForProto) { // Check for an opening '<' bracket. if (!P.Tok.isContextualPunctuator("<")) return false; @@ -1529,7 +1571,10 @@ bool SILParser::parseSubstitutions(SmallVectorImpl &parsed, if (TyR.isNull()) return true; TypeLoc Ty = TyR.get(); - if (performTypeLocChecking(Ty, /*IsSILType=*/ false, GenericEnv)) + if (defaultForProto) + bindProtocolSelfInTypeRepr(Ty, defaultForProto); + if (performTypeLocChecking(Ty, /*IsSILType=*/ false, GenericEnv, + defaultForProto)) return true; parsed.push_back({Loc, Ty.getType()}); } while (P.consumeIf(tok::comma)); @@ -5725,7 +5770,8 @@ static NormalProtocolConformance *parseNormalProtocolConformance(Parser &P, ProtocolConformance *SILParser::parseProtocolConformance( ProtocolDecl *&proto, GenericEnvironment *&genericEnv, - bool localScope) { + bool localScope, + ProtocolDecl *defaultForProto) { // Parse generic params for the protocol conformance. We need to make sure // they have the right scope. Optional GenericsScope; @@ -5741,7 +5787,8 @@ ProtocolConformance *SILParser::parseProtocolConformance( } ProtocolConformance *retVal = - parseProtocolConformanceHelper(proto, genericEnv, localScope); + parseProtocolConformanceHelper(proto, genericEnv, localScope, + defaultForProto); if (localScope) { GenericsScope.reset(); @@ -5752,13 +5799,19 @@ ProtocolConformance *SILParser::parseProtocolConformance( ProtocolConformance *SILParser::parseProtocolConformanceHelper( ProtocolDecl *&proto, GenericEnvironment *witnessEnv, - bool localScope) { + bool localScope, + ProtocolDecl *defaultForProto) { // Parse AST type. ParserResult TyR = P.parseType(); if (TyR.isNull()) return nullptr; TypeLoc Ty = TyR.get(); - if (performTypeLocChecking(Ty, /*IsSILType=*/ false, witnessEnv)) + if (defaultForProto) { + bindProtocolSelfInTypeRepr(Ty, defaultForProto); + } + + if (performTypeLocChecking(Ty, /*IsSILType=*/ false, witnessEnv, + defaultForProto)) return nullptr; auto ConformingTy = Ty.getType(); @@ -5770,7 +5823,7 @@ ProtocolConformance *SILParser::parseProtocolConformanceHelper( // Parse substitutions for specialized conformance. SmallVector parsedSubs; - if (parseSubstitutions(parsedSubs, witnessEnv)) + if (parseSubstitutions(parsedSubs, witnessEnv, defaultForProto)) return nullptr; if (P.parseToken(tok::l_paren, diag::expected_sil_witness_lparen)) @@ -5778,7 +5831,8 @@ ProtocolConformance *SILParser::parseProtocolConformanceHelper( ProtocolDecl *dummy; GenericEnvironment *specializedEnv; auto genericConform = - parseProtocolConformance(dummy, specializedEnv, localScope); + parseProtocolConformance(dummy, specializedEnv, localScope, + defaultForProto); if (!genericConform) return nullptr; if (P.parseToken(tok::r_paren, diag::expected_sil_witness_rparen)) @@ -5799,7 +5853,7 @@ ProtocolConformance *SILParser::parseProtocolConformanceHelper( if (P.parseToken(tok::l_paren, diag::expected_sil_witness_lparen)) return nullptr; - auto baseConform = parseProtocolConformance(); + auto baseConform = parseProtocolConformance(defaultForProto); if (!baseConform) return nullptr; if (P.parseToken(tok::r_paren, diag::expected_sil_witness_rparen)) @@ -5812,41 +5866,6 @@ ProtocolConformance *SILParser::parseProtocolConformanceHelper( return retVal; } -/// Bind any unqualified 'Self' references to the given protocol's 'Self' -/// generic parameter. -/// -/// FIXME: This is a hack to work around the lack of a DeclContext for -/// witness tables. -static void bindProtocolSelfInTypeRepr(TypeLoc &TL, ProtocolDecl *proto) { - if (auto typeRepr = TL.getTypeRepr()) { - // AST walker to update 'Self' references. - class BindProtocolSelf : public ASTWalker { - ProtocolDecl *proto; - GenericTypeParamDecl *selfParam; - Identifier selfId; - - public: - BindProtocolSelf(ProtocolDecl *proto) - : proto(proto), - selfParam(proto->getProtocolSelfType()->getDecl()), - selfId(proto->getASTContext().Id_Self) { - } - - virtual bool walkToTypeReprPre(TypeRepr *T) override { - if (auto ident = dyn_cast(T)) { - auto firstComponent = ident->getComponentRange().front(); - if (firstComponent->getIdentifier() == selfId) - firstComponent->setValue(selfParam, proto); - } - - return true; - } - }; - - typeRepr->walk(BindProtocolSelf(proto)); - } -} - /// Parser a single SIL vtable entry and add it to either \p witnessEntries /// or \c conditionalConformances. static bool parseSILVTableEntry( @@ -5859,6 +5878,7 @@ static bool parseSILVTableEntry( std::vector &witnessEntries, std::vector &conditionalConformances) { + ProtocolDecl *defaultForProto = isDefaultWitnessTable ? proto : nullptr; Identifier EntryKeyword; SourceLoc KeywordLoc; if (P.parseIdentifier(EntryKeyword, KeywordLoc, @@ -5878,7 +5898,8 @@ static bool parseSILVTableEntry( return true; if (P.parseToken(tok::colon, diag::expected_sil_witness_colon)) return true; - ProtocolConformance *conform = witnessState.parseProtocolConformance(); + ProtocolConformance *conform = + witnessState.parseProtocolConformance(defaultForProto); if (!conform) // Ignore this witness entry for now. return false; @@ -5925,7 +5946,7 @@ static bool parseSILVTableEntry( ProtocolConformanceRef conformance(proto); if (P.Tok.getText() != "dependent") { - auto concrete = witnessState.parseProtocolConformance(); + auto concrete = witnessState.parseProtocolConformance(defaultForProto); if (!concrete) // Ignore this witness entry for now. return false; conformance = ProtocolConformanceRef(concrete); @@ -6045,7 +6066,8 @@ bool SILParserTUState::parseSILWitnessTable(Parser &P) { GenericEnvironment *witnessEnv; auto conf = WitnessState.parseProtocolConformance(proto, witnessEnv, - false/*localScope*/); + false/*localScope*/, + nullptr); WitnessState.ContextGenericEnv = witnessEnv; NormalProtocolConformance *theConformance = conf ? diff --git a/test/SIL/Parser/default_witness_tables.sil b/test/SIL/Parser/default_witness_tables.sil index bb1b8e9cccb..8153e21675a 100644 --- a/test/SIL/Parser/default_witness_tables.sil +++ b/test/SIL/Parser/default_witness_tables.sil @@ -48,7 +48,7 @@ bb0(%0 : $*Self): // CHECK-LABEL: sil_default_witness_table ResilientProtocol { -// CHECK: no_default +// CHECK: associated_type_protocol (T: Proto): Wrapper: specialize ( Wrapper: Proto module witness_tables) // CHECK: associated_type T: Wrapper // CHECK: no_default // CHECK: no_default @@ -58,7 +58,7 @@ bb0(%0 : $*Self): // CHECK: } sil_default_witness_table ResilientProtocol { - no_default + associated_type_protocol (T: Proto): Wrapper: specialize ( Wrapper: Proto module witness_tables) associated_type T: Wrapper no_default no_default