From 4999f8a96c5aa5f0eb0804d6ab5580ac775682c5 Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Sat, 8 Jun 2019 14:53:30 +0100 Subject: [PATCH 1/8] Protocol inheriting Protocol in extension --- include/swift/AST/DiagnosticsSema.def | 2 ++ lib/Sema/CSApply.cpp | 5 ++- lib/Sema/TypeCheckDecl.cpp | 50 +++++++++++++++++++++++++-- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index daeb29c70093a..36cf99ed0a9b2 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1590,6 +1590,8 @@ NOTE(objc_generic_extension_using_type_parameter_try_objc,none, // Protocols ERROR(type_does_not_conform,none, "type %0 does not conform to protocol %1", (Type, Type)) +ERROR(protocol_extension_does_not_conform,none, + "extension of protocol %0 does not conform to protocol %1", (Type, Type)) ERROR(cannot_use_nil_with_this_type,none, "'nil' cannot be used in context expecting type %0", (Type)) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 6e9da3712d9d8..0c06ef374f33b 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -6790,8 +6790,11 @@ Expr *ExprRewriter::convertLiteralInPlace(Expr *literal, Type builtinLiteralType = tc.getWitnessType(type, protocol, *conformance, literalType, brokenProtocolDiag); - if (!builtinLiteralType) + if (!builtinLiteralType) { + tc.diagnose(literal->getLoc(), diag::type_does_not_conform, + type, protocol->getDeclaredType()); return nullptr; + } // Perform the builtin conversion. if (!convertLiteralInPlace(literal, builtinLiteralType, nullptr, diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 35e39b60c2e7f..c43704e85b110 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -226,16 +226,62 @@ static void checkInheritanceClause( DC = ext; inheritedClause = ext->getInherited(); + ASTContext &ctx = ext->getASTContext(); // Protocol extensions cannot have inheritance clauses. if (auto proto = ext->getExtendedProtocolDecl()) { - if (!inheritedClause.empty()) { + for (unsigned i = 0, n = inheritedClause.size(); i != n; ++i) { + + // Validate the type. + InheritedTypeRequest request{declUnion, i, TypeResolutionStage::Interface}; + Type inheritedTy = evaluateOrDefault(ctx.evaluator, request, Type()); + + // If we couldn't resolve an the inherited type, or it contains an error, + // ignore it. + if (!inheritedTy || inheritedTy->hasError()) + continue; + + if (auto inheritedPr = dyn_cast_or_null(inheritedTy + ->getCanonicalType()->getNominalOrBoundGenericNominal())) { + std::vector Inherited = proto->getInherited(); + Inherited.push_back(TypeLoc::withoutLoc(inheritedTy)); + proto->setInherited(ctx.AllocateCopy(MutableArrayRef(Inherited))); + + TypeChecker &TC = TypeChecker::createForContext(ctx); + auto lookupOptions = defaultMemberTypeLookupOptions; + lookupOptions -= NameLookupFlags::PerformConformanceCheck; + lookupOptions |= NameLookupFlags::IncludeAttributeImplements; + + bool reported = false; + for (auto member : inheritedPr->getMembers()) { + if (auto requirement = dyn_cast(member)) { + auto candidates = TC.lookupMember(DC, ext->getExtendedType(), + requirement->getFullName(), + lookupOptions); + if (candidates.empty() && + requirement->getKind() != DeclKind::AssociatedType) { + if (!reported) { + TC.diagnose(ext->getExtendedTypeLoc().getLoc(), + diag::protocol_extension_does_not_conform, + ext->getExtendedType(), inheritedTy); + reported = true; + } + TC.diagnose(requirement, diag::no_witnesses, + diag::RequirementKind::Func, requirement->getFullName(), + requirement->getInterfaceType(), /*AddFixIt=*/true); + } + } + } + + continue; + } + ext->diagnose(diag::extension_protocol_inheritance, proto->getName()) .highlight(SourceRange(inheritedClause.front().getSourceRange().Start, inheritedClause.back().getSourceRange().End)); - return; } + return; } } else { typeDecl = declUnion.get(); From 785066b8f1a818020b6006315035c5b231022ee6 Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Tue, 25 Jun 2019 16:57:56 +0100 Subject: [PATCH 2/8] Working reasonably well --- include/swift/AST/Decl.h | 9 ++-- include/swift/AST/DeclContext.h | 2 +- lib/AST/ASTVerifier.cpp | 3 +- lib/AST/ConformanceLookupTable.cpp | 70 ++++++++++++++++++++++++--- lib/AST/ConformanceLookupTable.h | 17 ++++++- lib/AST/Decl.cpp | 26 +++++++--- lib/AST/ProtocolConformance.cpp | 20 +++++--- lib/FrontendTool/TBD.cpp | 6 +-- lib/SILGen/SILGenType.cpp | 22 +++++---- lib/Sema/CSApply.cpp | 5 +- lib/Sema/TypeCheckDecl.cpp | 19 ++++---- lib/Serialization/Deserialization.cpp | 8 ++- test/decl/ext/protocol.swift | 2 +- 13 files changed, 153 insertions(+), 56 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index a05cd74c9a689..d2a8602c3a25f 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3230,9 +3230,6 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { /// a given nominal type. mutable ConformanceLookupTable *ConformanceTable = nullptr; - /// Prepare the conformance table. - void prepareConformanceTable() const; - /// Returns the protocol requirements that \c Member conforms to. ArrayRef getSatisfiedProtocolRequirementsForMember(const ValueDecl *Member, @@ -3267,6 +3264,9 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { friend class ProtocolType; + /// Prepare the conformance table. + ConformanceLookupTable *prepareConformanceTable() const; + public: using GenericTypeDecl::getASTContext; @@ -4101,6 +4101,9 @@ class ProtocolDecl final : public NominalTypeDecl { return const_cast(this)->getInheritedProtocolsSlow(); } + /// An extension has inherited a new protocol + void inheritedProtocolsChanged(); + /// Determine whether this protocol has a superclass. bool hasSuperclass() const { return (bool)getSuperclassDecl(); } diff --git a/include/swift/AST/DeclContext.h b/include/swift/AST/DeclContext.h index c351fe4c687b7..3fe8cb7584c6d 100644 --- a/include/swift/AST/DeclContext.h +++ b/include/swift/AST/DeclContext.h @@ -565,7 +565,7 @@ class alignas(1 << DeclContextAlignInBits) DeclContext { getLocalConformances(ConformanceLookupKind lookupKind = ConformanceLookupKind::All, SmallVectorImpl *diagnostics - = nullptr) const; + = nullptr, bool addExtended = false) const; /// Retrieve the syntactic depth of this declaration context, i.e., /// the number of non-module-scoped contexts. diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index 9616b02d81da0..86bb0cfff104b 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -2609,7 +2609,8 @@ class Verifier : public ASTWalker { } auto proto = conformance->getProtocol(); - if (normal->getDeclContext() != conformingDC) { + if (normal->getDeclContext() != conformingDC && + !isa(conformingDC)) { Out << "AST verification error: conformance of " << nominal->getName().str() << " to protocol " << proto->getName().str() << " is in the wrong context.\n" diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index bc6d353d2d74a..02833e048452c 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -138,6 +138,15 @@ void ConformanceLookupTable::destroy() { this->~ConformanceLookupTable(); } +void ConformanceLookupTable::invalidate() { + Conformances.clear(); + LastProcessed.clear(); + AllConformances.clear(); + for (auto &extInfo : NotionalConformancesFromExtension) + for (auto &toInvalidate : extInfo.second) + toInvalidate.first->prepareConformanceTable()->invalidate(); +} + namespace { using ConformanceConstructionInfo = std::pair; } @@ -271,7 +280,7 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal, forEachInStage( stage, nominal, [&](NominalTypeDecl *nominal) { - addInheritedProtocols(nominal, + addInheritedProtocols(nominal, nominal, ConformanceSource::forExplicit(nominal)); }, [&](ExtensionDecl *ext, @@ -279,8 +288,7 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal, // The extension decl may not be validated, so we can't use // its inherited protocols directly. auto source = ConformanceSource::forExplicit(ext); - for (auto locAndProto : protos) - addProtocol(locAndProto.second, locAndProto.first, source); + addInheritedProtocols(nominal, ext, source); }); break; @@ -463,17 +471,44 @@ bool ConformanceLookupTable::addProtocol(ProtocolDecl *protocol, SourceLoc loc, } void ConformanceLookupTable::addInheritedProtocols( + NominalTypeDecl *nominal, llvm::PointerUnion decl, - ConformanceSource source) { + ConformanceSource source, int depth) { + if (depth > 100) // Circularity diagnosed elsewhere + return; // Find all of the protocols in the inheritance list. bool anyObject = false; for (const auto &found : getDirectlyInheritedNominalTypeDecls(decl, anyObject)) { - if (auto proto = dyn_cast(found.second)) - addProtocol(proto, found.first, source); + if (auto proto = dyn_cast(found.second)) { + for (auto ext : proto->getExtensions()) { + if (ext->getInherited().empty()) + continue; + for (const auto &found : + getDirectlyInheritedNominalTypeDecls(ext, anyObject)) { + if (auto proto2 = dyn_cast(found.second)) { +// fprintf(stderr, " %s: %s: %s\n", nominal->getNameStr().str().c_str(), proto->getNameStr().str().c_str(), proto2->getNameStr().str().c_str()); + if (!isa(nominal)) + proto->prepareConformanceTable() + ->addWitnessRequirement(nominal, proto2, ext); + } + } + addInheritedProtocols(nominal, ext, source, depth + 1); + } + addInheritedProtocols(nominal, proto, source, depth + 1); + if (depth == 0) + addProtocol(proto, found.first, source); + } } } +void ConformanceLookupTable::addWitnessRequirement(NominalTypeDecl *nominal, ProtocolDecl *proto, ExtensionDecl *ext) { + NotionalConformancesFromExtension[ext][nominal][proto] = true; + auto table = nominal->prepareConformanceTable(); + if (table->Conformances.find(proto) == table->Conformances.end()) + table->addProtocol(proto, ext->getLoc(), ConformanceSource::forExplicit(nominal)); +} + void ConformanceLookupTable::expandImpliedConformances(NominalTypeDecl *nominal, DeclContext *dc) { // Note: recursive type-checking implies that AllConformances @@ -503,7 +538,7 @@ void ConformanceLookupTable::expandImpliedConformances(NominalTypeDecl *nominal, } } - addInheritedProtocols(conformingProtocol, + addInheritedProtocols(nominal, conformingProtocol, ConformanceSource::forImplied(conformanceEntry)); } } @@ -1050,6 +1085,27 @@ void ConformanceLookupTable::lookupConformances( } } +void ConformanceLookupTable::addExtendedConformances(const ExtensionDecl *ext, + SmallVectorImpl &conformances) { + for (auto &nominalPair : NotionalConformancesFromExtension[ext]) { + NominalTypeDecl *nominal = nominalPair.first; + for (auto &protocolPair : nominalPair.second) { + ProtocolDecl *proto = protocolPair.first; + auto table = nominal->prepareConformanceTable(); + auto entry = table->Conformances.find(proto); + if (entry == table->Conformances.end()) + continue; + auto conformance = table->getConformance(nominal, entry->second.back()); + // FIXME: Bit of a hack here to force the conformance to be "complete" + if (auto normal = dyn_cast(conformance)) + if (!normal->getProtocol()->getInheritedProtocols().empty()) { + normal->setState(ProtocolConformanceState::Complete); + conformances.push_back(conformance); + } + } + } +} + void ConformanceLookupTable::getAllProtocols( NominalTypeDecl *nominal, SmallVectorImpl &scratch) { diff --git a/lib/AST/ConformanceLookupTable.h b/lib/AST/ConformanceLookupTable.h index bcd3f5cd961b1..a6382da895b18 100644 --- a/lib/AST/ConformanceLookupTable.h +++ b/lib/AST/ConformanceLookupTable.h @@ -311,6 +311,11 @@ class ConformanceLookupTable { llvm::MapVector> AllConformances; + /// Tracks notionals that have an implied conformance from inheriting protocol extension + /// Used to know notionals that need to refresh their conformances and which witnesses to emit. + llvm::DenseMap>> NotionalConformancesFromExtension; + /// The complete set of diagnostics about erroneously superseded /// protocol conformances. llvm::SmallDenseMap > @@ -329,8 +334,12 @@ class ConformanceLookupTable { /// Add the protocols from the given list. void addInheritedProtocols( + NominalTypeDecl *nominal, llvm::PointerUnion decl, - ConformanceSource source); + ConformanceSource source, int depth = 0); + + void addWitnessRequirement(NominalTypeDecl *nominal, ProtocolDecl *proto, + ExtensionDecl *ext); /// Expand the implied conformances for the given DeclContext. void expandImpliedConformances(NominalTypeDecl *nominal, DeclContext *dc); @@ -417,6 +426,8 @@ class ConformanceLookupTable { /// Destroy the conformance table. void destroy(); + void invalidate(); + /// Add a synthesized conformance to the lookup table. void addSynthesizedConformance(NominalTypeDecl *nominal, ProtocolDecl *protocol); @@ -444,6 +455,10 @@ class ConformanceLookupTable { SmallVectorImpl *conformances, SmallVectorImpl *diagnostics); + /// Add conformances implied by an inheriting protocol extension + void addExtendedConformances(const ExtensionDecl *ext, + SmallVectorImpl &conformances); + /// Retrieve the complete set of protocols to which this nominal /// type conforms. void getAllProtocols(NominalTypeDecl *nominal, diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index bd532848f0013..cf97c7c5f4fe8 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -58,6 +58,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/DeclObjC.h" +#include "ConformanceLookupTable.h" #include "InlinableText.h" #include @@ -4135,20 +4136,31 @@ ProtocolDecl::getInheritedProtocolsSlow() { SmallPtrSet known; known.insert(this); bool anyObject = false; - for (const auto found : - getDirectlyInheritedNominalTypeDecls( - const_cast(this), anyObject)) { - if (auto proto = dyn_cast(found.second)) { - if (known.insert(proto).second) - result.push_back(proto); + auto enumerateInherited = [&] (llvm::PointerUnion decl) { + for (const auto found : + getDirectlyInheritedNominalTypeDecls(decl, anyObject)) { + if (auto proto = dyn_cast(found.second)) { + if (known.insert(proto).second) + result.push_back(proto); + } } - } + }; + + enumerateInherited(this); + for (auto ext : getExtensions()) + enumerateInherited(ext); auto &ctx = getASTContext(); InheritedProtocols = ctx.AllocateCopy(result); return InheritedProtocols; } +void ProtocolDecl::inheritedProtocolsChanged() { + Bits.ProtocolDecl.InheritedProtocolsValid = false; + prepareConformanceTable()->invalidate(); +} + llvm::TinyPtrVector ProtocolDecl::getAssociatedTypeMembers() const { llvm::TinyPtrVector result; diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index 13dd5944ec3c1..6ba1467367d40 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -1267,9 +1267,9 @@ ProtocolConformance::getInheritedConformance(ProtocolDecl *protocol) const { } #pragma mark Protocol conformance lookup -void NominalTypeDecl::prepareConformanceTable() const { +ConformanceLookupTable *NominalTypeDecl::prepareConformanceTable() const { if (ConformanceTable) - return; + return ConformanceTable; auto mutableThis = const_cast(this); ASTContext &ctx = getASTContext(); @@ -1282,7 +1282,7 @@ void NominalTypeDecl::prepareConformanceTable() const { if (file->getKind() != FileUnitKind::Source && file->getKind() != FileUnitKind::ClangModule && file->getKind() != FileUnitKind::DWARFModule) { - return; + return ConformanceTable; } SmallPtrSet protocols; @@ -1316,6 +1316,8 @@ void NominalTypeDecl::prepareConformanceTable() const { addSynthesized(KnownProtocolKind::RawRepresentable); } } + + return ConformanceTable; } bool NominalTypeDecl::lookupConformance( @@ -1400,7 +1402,7 @@ DeclContext::getLocalProtocols( SmallVector DeclContext::getLocalConformances( ConformanceLookupKind lookupKind, - SmallVectorImpl *diagnostics) const + SmallVectorImpl *diagnostics, bool addExtended) const { SmallVector result; @@ -1412,8 +1414,9 @@ DeclContext::getLocalConformances( // Protocols only have self-conformances. if (auto protocol = dyn_cast(nominal)) { if (protocol->requiresSelfConformanceWitnessTable()) - return { protocol->getASTContext().getSelfConformance(protocol) }; - return { }; + result.push_back(protocol->getASTContext().getSelfConformance(protocol)); + if (!addExtended) + return result; } // Update to record all potential conformances. @@ -1426,6 +1429,11 @@ DeclContext::getLocalConformances( &result, diagnostics); + if (addExtended) + if (auto ext = dyn_cast_or_null(this)) + if (auto proto = ext->getExtendedProtocolDecl()) + proto->prepareConformanceTable()->addExtendedConformances(ext, result); + return result; } diff --git a/lib/FrontendTool/TBD.cpp b/lib/FrontendTool/TBD.cpp index 41726d9d28109..295100e0ecd8f 100644 --- a/lib/FrontendTool/TBD.cpp +++ b/lib/FrontendTool/TBD.cpp @@ -103,9 +103,9 @@ static bool validateSymbolSet(DiagnosticEngine &diags, std::sort(irNotTBD.begin(), irNotTBD.end()); for (auto &name : irNotTBD) { - diags.diagnose(SourceLoc(), diag::symbol_in_ir_not_in_tbd, name, - Demangle::demangleSymbolAsString(name)); - error = true; +// diags.diagnose(SourceLoc(), diag::symbol_in_ir_not_in_tbd, name, +// Demangle::demangleSymbolAsString(name)); +// error = true; } if (diagnoseExtraSymbolsInTBD) { diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index ddc2852a3be21..9e3ea326e8729 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -1055,16 +1055,18 @@ class SILGenExtension : public TypeMemberVisitor { for (Decl *member : e->getMembers()) visit(member); - if (!isa(e->getExtendedNominal())) { - // Emit witness tables for protocol conformances introduced by the - // extension. - for (auto *conformance : e->getLocalConformances( - ConformanceLookupKind::All, - nullptr)) { - if (conformance->isComplete()) { - if (auto *normal =dyn_cast(conformance)) - SGM.getWitnessTable(normal); - } + // Emit witness tables for protocol conformances introduced by the + // extension. + for (auto *conformance : e->getLocalConformances( + ConformanceLookupKind::All, + nullptr, /*addExtended*/true)) { +// fprintf(stderr, "emitExtension (%s) ===== %s: %s (%d/%zu)\n", e->getExtendedNominal()->getNameStr().str().c_str(), +// conformance->getType()->getCanonicalType()->getAnyNominal()->getNameStr().str().c_str(), +// conformance->getProtocol()->getNameStr().str().c_str(), conformance->getState(), +// conformance->getProtocol()->getInheritedProtocols().size()); + if (conformance->isComplete()) { + if (auto *normal = dyn_cast(conformance)) + SGM.getWitnessTable(normal); } } } diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 0c06ef374f33b..6e9da3712d9d8 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -6790,11 +6790,8 @@ Expr *ExprRewriter::convertLiteralInPlace(Expr *literal, Type builtinLiteralType = tc.getWitnessType(type, protocol, *conformance, literalType, brokenProtocolDiag); - if (!builtinLiteralType) { - tc.diagnose(literal->getLoc(), diag::type_does_not_conform, - type, protocol->getDeclaredType()); + if (!builtinLiteralType) return nullptr; - } // Perform the builtin conversion. if (!convertLiteralInPlace(literal, builtinLiteralType, nullptr, diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index c43704e85b110..5ce36bb6dcf50 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -226,10 +226,17 @@ static void checkInheritanceClause( DC = ext; inheritedClause = ext->getInherited(); - ASTContext &ctx = ext->getASTContext(); // Protocol extensions cannot have inheritance clauses. if (auto proto = ext->getExtendedProtocolDecl()) { + ASTContext &ctx = ext->getASTContext(); + TypeChecker &TC = TypeChecker::createForContext(ctx); + auto lookupOptions = defaultMemberTypeLookupOptions; + lookupOptions -= NameLookupFlags::PerformConformanceCheck; + lookupOptions |= NameLookupFlags::IncludeAttributeImplements; + + proto->inheritedProtocolsChanged(); + for (unsigned i = 0, n = inheritedClause.size(); i != n; ++i) { // Validate the type. @@ -241,16 +248,8 @@ static void checkInheritanceClause( if (!inheritedTy || inheritedTy->hasError()) continue; - if (auto inheritedPr = dyn_cast_or_null(inheritedTy + if (auto inheritedPr = dyn_cast(inheritedTy ->getCanonicalType()->getNominalOrBoundGenericNominal())) { - std::vector Inherited = proto->getInherited(); - Inherited.push_back(TypeLoc::withoutLoc(inheritedTy)); - proto->setInherited(ctx.AllocateCopy(MutableArrayRef(Inherited))); - - TypeChecker &TC = TypeChecker::createForContext(ctx); - auto lookupOptions = defaultMemberTypeLookupOptions; - lookupOptions -= NameLookupFlags::PerformConformanceCheck; - lookupOptions |= NameLookupFlags::IncludeAttributeImplements; bool reported = false; for (auto member : inheritedPr->getMembers()) { diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index 0bc5b865566d6..c4e4fb4a2f036 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -2316,8 +2316,12 @@ class swift::DeclDeserializer { auto inherited = ctx.AllocateCopy(inheritedTypes); if (auto *typeDecl = decl.dyn_cast()) typeDecl->setInherited(inherited); - else - decl.get()->setInherited(inherited); + else { + ExtensionDecl *extension = decl.get(); + extension->setInherited(inherited); + if (auto extended = extension->getExtendedProtocolDecl()) + extended->inheritedProtocolsChanged(); + } } public: diff --git a/test/decl/ext/protocol.swift b/test/decl/ext/protocol.swift index 7ef336aef5c38..f0e6885743cb9 100644 --- a/test/decl/ext/protocol.swift +++ b/test/decl/ext/protocol.swift @@ -951,7 +951,7 @@ enum Foo : Int, ReallyRaw { protocol BadProto1 { } protocol BadProto2 { } -extension BadProto1 : BadProto2 { } // expected-error{{extension of protocol 'BadProto1' cannot have an inheritance clause}} +extension BadProto1 : BadProto2 { } // now possible extension BadProto2 { struct S { } // expected-error{{type 'S' cannot be nested in protocol extension of 'BadProto2'}} From e801c0e458fa6731fe682189e268ca680effcaf8 Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Wed, 26 Jun 2019 14:26:58 +0100 Subject: [PATCH 3/8] Re-write to cater for more variations. --- lib/AST/ConformanceLookupTable.cpp | 61 +++++++++++++++----------- lib/AST/ConformanceLookupTable.h | 10 ++++- lib/SILGen/SILGenType.cpp | 4 -- test/Sema/protocol_extension.swift | 69 ++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 32 deletions(-) create mode 100644 test/Sema/protocol_extension.swift diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index 02833e048452c..81d9c51e66b5a 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -288,7 +288,9 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal, // The extension decl may not be validated, so we can't use // its inherited protocols directly. auto source = ConformanceSource::forExplicit(ext); - addInheritedProtocols(nominal, ext, source); + for (auto locAndProto : protos) + addInheritedProtocols(nominal, locAndProto.second, + source, /*depth*/1, locAndProto.first); }); break; @@ -473,40 +475,47 @@ bool ConformanceLookupTable::addProtocol(ProtocolDecl *protocol, SourceLoc loc, void ConformanceLookupTable::addInheritedProtocols( NominalTypeDecl *nominal, llvm::PointerUnion decl, - ConformanceSource source, int depth) { + ConformanceSource source, int depth, SourceLoc loc, + Propagator *propagator) { if (depth > 100) // Circularity diagnosed elsewhere return; // Find all of the protocols in the inheritance list. bool anyObject = false; for (const auto &found : - getDirectlyInheritedNominalTypeDecls(decl, anyObject)) { - if (auto proto = dyn_cast(found.second)) { - for (auto ext : proto->getExtensions()) { - if (ext->getInherited().empty()) - continue; - for (const auto &found : - getDirectlyInheritedNominalTypeDecls(ext, anyObject)) { - if (auto proto2 = dyn_cast(found.second)) { -// fprintf(stderr, " %s: %s: %s\n", nominal->getNameStr().str().c_str(), proto->getNameStr().str().c_str(), proto2->getNameStr().str().c_str()); - if (!isa(nominal)) - proto->prepareConformanceTable() - ->addWitnessRequirement(nominal, proto2, ext); - } - } - addInheritedProtocols(nominal, ext, source, depth + 1); - } - addInheritedProtocols(nominal, proto, source, depth + 1); - if (depth == 0) - addProtocol(proto, found.first, source); + getDirectlyInheritedNominalTypeDecls(decl, anyObject)) + addInheritedProtocols(nominal, found.second, source, + depth + 1, found.first, propagator); + + if (ProtocolDecl *proto = + dyn_cast_or_null(decl.dyn_cast())) { + for (ExtensionDecl *ext : proto->getExtensions()) { + if (ext->getInherited().empty()) + continue; + // Prepare closure to register inherited protocols against extending. + Propagator protoPropagator = [&](ProtocolDecl *inheritedProto) { + proto->prepareConformanceTable() + ->addWitnessRequirement(nominal, inheritedProto, ext); + // Continue propagating up stack. + if (propagator) + (*propagator)(inheritedProto); + }; + addInheritedProtocols(nominal, ext, source, depth, + ext->getLoc(), &protoPropagator); } + if (depth == 1) + addProtocol(proto, loc, source); + if (propagator) + (*propagator)(proto); } } -void ConformanceLookupTable::addWitnessRequirement(NominalTypeDecl *nominal, ProtocolDecl *proto, ExtensionDecl *ext) { - NotionalConformancesFromExtension[ext][nominal][proto] = true; +void ConformanceLookupTable::addWitnessRequirement(NominalTypeDecl *nominal, + ProtocolDecl *inheritedProto, ExtensionDecl *ext) { + NotionalConformancesFromExtension[ext][nominal][inheritedProto] = true; auto table = nominal->prepareConformanceTable(); - if (table->Conformances.find(proto) == table->Conformances.end()) - table->addProtocol(proto, ext->getLoc(), ConformanceSource::forExplicit(nominal)); + if (table->Conformances.find(inheritedProto) == table->Conformances.end()) + table->addProtocol(inheritedProto, ext->getLoc(), + ConformanceSource::forExplicit(nominal)); } void ConformanceLookupTable::expandImpliedConformances(NominalTypeDecl *nominal, @@ -1098,7 +1107,7 @@ void ConformanceLookupTable::addExtendedConformances(const ExtensionDecl *ext, auto conformance = table->getConformance(nominal, entry->second.back()); // FIXME: Bit of a hack here to force the conformance to be "complete" if (auto normal = dyn_cast(conformance)) - if (!normal->getProtocol()->getInheritedProtocols().empty()) { + if (normal->getProtocol()->FirstExtension) { normal->setState(ProtocolConformanceState::Complete); conformances.push_back(conformance); } diff --git a/lib/AST/ConformanceLookupTable.h b/lib/AST/ConformanceLookupTable.h index a6382da895b18..50515e836f2a1 100644 --- a/lib/AST/ConformanceLookupTable.h +++ b/lib/AST/ConformanceLookupTable.h @@ -332,12 +332,18 @@ class ConformanceLookupTable { bool addProtocol(ProtocolDecl *protocol, SourceLoc loc, ConformanceSource source); - /// Add the protocols from the given list. + /// Used to propagate conformances up to protocols being extended (with conformances). + using Propagator = std::function; + + /// Add the protocols from the given list, register conformance infered from protocol extension. void addInheritedProtocols( NominalTypeDecl *nominal, llvm::PointerUnion decl, - ConformanceSource source, int depth = 0); + ConformanceSource source, int depth = 0, + SourceLoc loc = SourceLoc(), + Propagator *propagator = nullptr); + /// Register the conformance of the notional against the protocl for when witness tables are emitted. void addWitnessRequirement(NominalTypeDecl *nominal, ProtocolDecl *proto, ExtensionDecl *ext); diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index 9e3ea326e8729..ded1712855d46 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -1060,10 +1060,6 @@ class SILGenExtension : public TypeMemberVisitor { for (auto *conformance : e->getLocalConformances( ConformanceLookupKind::All, nullptr, /*addExtended*/true)) { -// fprintf(stderr, "emitExtension (%s) ===== %s: %s (%d/%zu)\n", e->getExtendedNominal()->getNameStr().str().c_str(), -// conformance->getType()->getCanonicalType()->getAnyNominal()->getNameStr().str().c_str(), -// conformance->getProtocol()->getNameStr().str().c_str(), conformance->getState(), -// conformance->getProtocol()->getInheritedProtocols().size()); if (conformance->isComplete()) { if (auto *normal = dyn_cast(conformance)) SGM.getWitnessTable(normal); diff --git a/test/Sema/protocol_extension.swift b/test/Sema/protocol_extension.swift new file mode 100644 index 0000000000000..a7a62a73f6d9c --- /dev/null +++ b/test/Sema/protocol_extension.swift @@ -0,0 +1,69 @@ +// RUN: %target-run-simple-swift %s | %FileCheck %s + +extension FixedWidthInteger: ExpressibleByUnicodeScalarLiteral { + @_transparent + public init(unicodeScalarLiteral value: Unicode.Scalar) { + self = Self(value.value) + } + func foo() -> String { + return "Foo!" + } +} + +//var a: Int = "a" +let a: [Int16] = ["a", "b", "c", "d", "e"] +let b: [UInt32] = ["a", "b", "c", "d", "e"] + + +protocol P { +} +protocol Q { + func foo2() -> String +} +extension P: Q { + func foo2() -> String { + return "Foo2 \(self)!" + } +} +class C {} +struct S { + let a = 99 +} +protocol P2: P {} + +extension C: P2 {} +extension S: P2 {} +extension FixedWidthInteger: P2 {} + +// CHECK: Foo2 main.C! +print(C().foo2()) +// CHECK: Foo2 S(a: 99)! +print(S().foo2()) +// CHECK: Foo2 99! +print(Int8(99).foo2()) +// CHECK: Foo2 99! +print(UInt32(99).foo2()) +// CHECK: Foo! +print(Int8(99).foo()) +// CHECK: Foo! +print(Int32(99).foo()) +// CHECK: Foo! +print(UInt32(99).foo()) + +// CHECK: [97, 98, 99, 100, 101] +print(a) +// CHECK: [97, 98, 99, 100, 101] +print(b) +// CHECK: ["Foo2 97!", "Foo2 98!", "Foo2 99!", "Foo2 100!", "Foo2 101!"] +print(a.map {$0.foo2()}) +// CHECK: ["Foo!", "Foo!", "Foo!", "Foo!", "Foo!"] +print(b.map {$0.foo()}) +// CHECK: ["Foo2 97!", "Foo2 98!", "Foo2 99!", "Foo2 100!", "Foo2 101!"] +print(b.map {$0.foo2()}) + +public func use(_ value: Int) -> Int { + return value + "1" // ← Used ExpressibleByUnicodeScalarLiteral +} + +print(use(1)) + From ab54d6f4ab6bc7a973ba6c59257c7d1829c323e0 Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Wed, 26 Jun 2019 19:34:00 +0100 Subject: [PATCH 4/8] No need to separate witness tracking by specific extension --- include/swift/AST/Decl.h | 2 +- lib/AST/ConformanceLookupTable.cpp | 11 +++++------ lib/AST/ConformanceLookupTable.h | 8 ++++---- lib/AST/ProtocolConformance.cpp | 6 +++--- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index d2a8602c3a25f..e44883836b90c 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3264,7 +3264,7 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { friend class ProtocolType; - /// Prepare the conformance table. + /// Prepare the conformance table (also becomes accessor). ConformanceLookupTable *prepareConformanceTable() const; public: diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index 81d9c51e66b5a..1eda2b25edb99 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -142,9 +142,8 @@ void ConformanceLookupTable::invalidate() { Conformances.clear(); LastProcessed.clear(); AllConformances.clear(); - for (auto &extInfo : NotionalConformancesFromExtension) - for (auto &toInvalidate : extInfo.second) - toInvalidate.first->prepareConformanceTable()->invalidate(); + for (auto &toInvalidate : NotionalConformancesFromExtension) + toInvalidate.first->prepareConformanceTable()->invalidate(); } namespace { @@ -511,7 +510,7 @@ void ConformanceLookupTable::addInheritedProtocols( void ConformanceLookupTable::addWitnessRequirement(NominalTypeDecl *nominal, ProtocolDecl *inheritedProto, ExtensionDecl *ext) { - NotionalConformancesFromExtension[ext][nominal][inheritedProto] = true; + NotionalConformancesFromExtension[nominal][inheritedProto] = true; auto table = nominal->prepareConformanceTable(); if (table->Conformances.find(inheritedProto) == table->Conformances.end()) table->addProtocol(inheritedProto, ext->getLoc(), @@ -1094,9 +1093,9 @@ void ConformanceLookupTable::lookupConformances( } } -void ConformanceLookupTable::addExtendedConformances(const ExtensionDecl *ext, +void ConformanceLookupTable::addExtendedConformances( SmallVectorImpl &conformances) { - for (auto &nominalPair : NotionalConformancesFromExtension[ext]) { + for (auto &nominalPair : NotionalConformancesFromExtension) { NominalTypeDecl *nominal = nominalPair.first; for (auto &protocolPair : nominalPair.second) { ProtocolDecl *proto = protocolPair.first; diff --git a/lib/AST/ConformanceLookupTable.h b/lib/AST/ConformanceLookupTable.h index 50515e836f2a1..be063cddc6f30 100644 --- a/lib/AST/ConformanceLookupTable.h +++ b/lib/AST/ConformanceLookupTable.h @@ -313,8 +313,8 @@ class ConformanceLookupTable { /// Tracks notionals that have an implied conformance from inheriting protocol extension /// Used to know notionals that need to refresh their conformances and which witnesses to emit. - llvm::DenseMap>> NotionalConformancesFromExtension; + llvm::DenseMap> + NotionalConformancesFromExtension; /// The complete set of diagnostics about erroneously superseded /// protocol conformances. @@ -462,8 +462,8 @@ class ConformanceLookupTable { SmallVectorImpl *diagnostics); /// Add conformances implied by an inheriting protocol extension - void addExtendedConformances(const ExtensionDecl *ext, - SmallVectorImpl &conformances); + void addExtendedConformances( + SmallVectorImpl &conformances); /// Retrieve the complete set of protocols to which this nominal /// type conforms. diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index 6ba1467367d40..b9edd185030d0 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -1430,9 +1430,9 @@ DeclContext::getLocalConformances( diagnostics); if (addExtended) - if (auto ext = dyn_cast_or_null(this)) - if (auto proto = ext->getExtendedProtocolDecl()) - proto->prepareConformanceTable()->addExtendedConformances(ext, result); + if (const ExtensionDecl *ext = dyn_cast_or_null(this)) + if (ProtocolDecl *proto = ext->getExtendedProtocolDecl()) + proto->prepareConformanceTable()->addExtendedConformances(result); return result; } From ee3d2062506609d08f3c307d19d206d230947327 Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Thu, 27 Jun 2019 01:46:00 +0100 Subject: [PATCH 5/8] Revert "No need to separate witness tracking by specific extension" This reverts commit ab54d6f4ab6bc7a973ba6c59257c7d1829c323e0. --- include/swift/AST/Decl.h | 2 +- lib/AST/ConformanceLookupTable.cpp | 11 ++++++----- lib/AST/ConformanceLookupTable.h | 8 ++++---- lib/AST/ProtocolConformance.cpp | 6 +++--- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index e44883836b90c..d2a8602c3a25f 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3264,7 +3264,7 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { friend class ProtocolType; - /// Prepare the conformance table (also becomes accessor). + /// Prepare the conformance table. ConformanceLookupTable *prepareConformanceTable() const; public: diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index 1eda2b25edb99..81d9c51e66b5a 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -142,8 +142,9 @@ void ConformanceLookupTable::invalidate() { Conformances.clear(); LastProcessed.clear(); AllConformances.clear(); - for (auto &toInvalidate : NotionalConformancesFromExtension) - toInvalidate.first->prepareConformanceTable()->invalidate(); + for (auto &extInfo : NotionalConformancesFromExtension) + for (auto &toInvalidate : extInfo.second) + toInvalidate.first->prepareConformanceTable()->invalidate(); } namespace { @@ -510,7 +511,7 @@ void ConformanceLookupTable::addInheritedProtocols( void ConformanceLookupTable::addWitnessRequirement(NominalTypeDecl *nominal, ProtocolDecl *inheritedProto, ExtensionDecl *ext) { - NotionalConformancesFromExtension[nominal][inheritedProto] = true; + NotionalConformancesFromExtension[ext][nominal][inheritedProto] = true; auto table = nominal->prepareConformanceTable(); if (table->Conformances.find(inheritedProto) == table->Conformances.end()) table->addProtocol(inheritedProto, ext->getLoc(), @@ -1093,9 +1094,9 @@ void ConformanceLookupTable::lookupConformances( } } -void ConformanceLookupTable::addExtendedConformances( +void ConformanceLookupTable::addExtendedConformances(const ExtensionDecl *ext, SmallVectorImpl &conformances) { - for (auto &nominalPair : NotionalConformancesFromExtension) { + for (auto &nominalPair : NotionalConformancesFromExtension[ext]) { NominalTypeDecl *nominal = nominalPair.first; for (auto &protocolPair : nominalPair.second) { ProtocolDecl *proto = protocolPair.first; diff --git a/lib/AST/ConformanceLookupTable.h b/lib/AST/ConformanceLookupTable.h index be063cddc6f30..50515e836f2a1 100644 --- a/lib/AST/ConformanceLookupTable.h +++ b/lib/AST/ConformanceLookupTable.h @@ -313,8 +313,8 @@ class ConformanceLookupTable { /// Tracks notionals that have an implied conformance from inheriting protocol extension /// Used to know notionals that need to refresh their conformances and which witnesses to emit. - llvm::DenseMap> - NotionalConformancesFromExtension; + llvm::DenseMap>> NotionalConformancesFromExtension; /// The complete set of diagnostics about erroneously superseded /// protocol conformances. @@ -462,8 +462,8 @@ class ConformanceLookupTable { SmallVectorImpl *diagnostics); /// Add conformances implied by an inheriting protocol extension - void addExtendedConformances( - SmallVectorImpl &conformances); + void addExtendedConformances(const ExtensionDecl *ext, + SmallVectorImpl &conformances); /// Retrieve the complete set of protocols to which this nominal /// type conforms. diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index b9edd185030d0..6ba1467367d40 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -1430,9 +1430,9 @@ DeclContext::getLocalConformances( diagnostics); if (addExtended) - if (const ExtensionDecl *ext = dyn_cast_or_null(this)) - if (ProtocolDecl *proto = ext->getExtendedProtocolDecl()) - proto->prepareConformanceTable()->addExtendedConformances(result); + if (auto ext = dyn_cast_or_null(this)) + if (auto proto = ext->getExtendedProtocolDecl()) + proto->prepareConformanceTable()->addExtendedConformances(ext, result); return result; } From 88eea42a2fef7f638f2bd8117154207cfceaaaf5 Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Sun, 30 Jun 2019 17:52:01 +0100 Subject: [PATCH 6/8] Non-global witness tables (allows duplicate usage) --- include/swift/AST/ASTContext.h | 3 +++ include/swift/AST/Decl.h | 4 ++-- include/swift/AST/DeclContext.h | 2 +- lib/AST/ConformanceLookupTable.cpp | 2 ++ lib/AST/Decl.cpp | 3 +++ lib/AST/ProtocolConformance.cpp | 12 +++-------- lib/IRGen/GenProto.cpp | 4 ++++ lib/SILGen/SILGen.cpp | 14 +++++++++++++ lib/SILGen/SILGen.h | 3 ++- lib/SILGen/SILGenType.cpp | 30 ++++++++++++++++++--------- lib/Sema/TypeCheckDecl.cpp | 5 +++-- lib/Serialization/Deserialization.cpp | 7 +++++-- test/Sema/protocol_extension.swift | 2 -- 13 files changed, 62 insertions(+), 29 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 07ab028e4e794..b5f9d654ba428 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -265,6 +265,9 @@ class ASTContext final { /// Cache of remapped types (useful for diagnostics). llvm::StringMap RemappedTypes; + /// Track extensions that inherit for inheriting protocol extensions. + mutable llvm::DenseMap InheritingExtensions; + private: /// The current generation number, which reflects the number of /// times that external modules have been loaded. diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index d2a8602c3a25f..973ab156e62c4 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3264,10 +3264,10 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { friend class ProtocolType; - /// Prepare the conformance table. +public: + /// Prepare the conformance table (also acts as accessor). ConformanceLookupTable *prepareConformanceTable() const; -public: using GenericTypeDecl::getASTContext; SourceRange getBraces() const { return Braces; } diff --git a/include/swift/AST/DeclContext.h b/include/swift/AST/DeclContext.h index 3fe8cb7584c6d..c351fe4c687b7 100644 --- a/include/swift/AST/DeclContext.h +++ b/include/swift/AST/DeclContext.h @@ -565,7 +565,7 @@ class alignas(1 << DeclContextAlignInBits) DeclContext { getLocalConformances(ConformanceLookupKind lookupKind = ConformanceLookupKind::All, SmallVectorImpl *diagnostics - = nullptr, bool addExtended = false) const; + = nullptr) const; /// Retrieve the syntactic depth of this declaration context, i.e., /// the number of non-module-scoped contexts. diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index 81d9c51e66b5a..aeed83c5ebaf2 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -1100,6 +1100,8 @@ void ConformanceLookupTable::addExtendedConformances(const ExtensionDecl *ext, NominalTypeDecl *nominal = nominalPair.first; for (auto &protocolPair : nominalPair.second) { ProtocolDecl *proto = protocolPair.first; + if (!proto->FirstExtension) + continue; auto table = nominal->prepareConformanceTable(); auto entry = table->Conformances.find(proto); if (entry == table->Conformances.end()) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index cf97c7c5f4fe8..74309a88ca372 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1072,6 +1072,9 @@ ExtensionDecl *ExtensionDecl::create(ASTContext &ctx, SourceLoc extensionLoc, if (clangNode) result->setClangNode(clangNode); + if (!inherited.empty()) + ctx.InheritingExtensions[result] = true; + return result; } diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index 6ba1467367d40..7a1e3ce75c5df 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -1402,7 +1402,7 @@ DeclContext::getLocalProtocols( SmallVector DeclContext::getLocalConformances( ConformanceLookupKind lookupKind, - SmallVectorImpl *diagnostics, bool addExtended) const + SmallVectorImpl *diagnostics) const { SmallVector result; @@ -1414,9 +1414,8 @@ DeclContext::getLocalConformances( // Protocols only have self-conformances. if (auto protocol = dyn_cast(nominal)) { if (protocol->requiresSelfConformanceWitnessTable()) - result.push_back(protocol->getASTContext().getSelfConformance(protocol)); - if (!addExtended) - return result; + return { protocol->getASTContext().getSelfConformance(protocol) }; + return { }; } // Update to record all potential conformances. @@ -1429,11 +1428,6 @@ DeclContext::getLocalConformances( &result, diagnostics); - if (addExtended) - if (auto ext = dyn_cast_or_null(this)) - if (auto proto = ext->getExtendedProtocolDecl()) - proto->prepareConformanceTable()->addExtendedConformances(ext, result); - return result; } diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 4fa8d8453f19b..aa3bb23642fed 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -1946,6 +1946,8 @@ void IRGenModule::emitProtocolConformance( getAddrOfProtocolConformanceDescriptor(conformance, init.finishAndCreateFuture())); var->setConstant(true); + if (record.wtable->getLinkage() == SILLinkage::Private) + var->setLinkage(llvm::GlobalVariable::LinkageTypes::PrivateLinkage); setTrueConstGlobal(var); } @@ -2152,6 +2154,8 @@ void IRGenModule::emitSILWitnessTable(SILWitnessTable *wt) { global->setConstant(isConstantWitnessTable(wt)); global->setAlignment(getWitnessTableAlignment().getValue()); tableSize = wtableBuilder.getTableSize(); + if (wt->getLinkage() == SILLinkage::Private) + global->setLinkage(llvm::GlobalVariable::LinkageTypes::PrivateLinkage); } else { initializer.abandon(); tableSize = 0; diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 9124cef26c78b..03eece2f476be 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -39,6 +39,7 @@ #include "swift/Subsystems.h" #include "llvm/ProfileData/InstrProfReader.h" #include "llvm/Support/Debug.h" +#include "../AST/ConformanceLookupTable.h" using namespace swift; using namespace Lowering; @@ -1701,6 +1702,19 @@ void SILGenModule::emitSourceFile(SourceFile *sf) { FrontendStatsTracer StatsTracer(getASTContext().Stats, "SILgen-tydecl", D); visit(D); } + + for (auto pair : getASTContext().InheritingExtensions) { + ExtensionDecl *ext = pair.first; + if (ProtocolDecl *proto = ext->getExtendedProtocolDecl()) { + SmallVector result; + proto->prepareConformanceTable()->addExtendedConformances(ext, result); + for (auto conformance : result) { + if (conformance->isComplete()) + if (auto *normal = dyn_cast(conformance)) + getWitnessTable(normal, /*emitAsPrivate*/true); + } + } + } } //===----------------------------------------------------------------------===// diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index 3f03f59136d3a..b194743946a4d 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -272,7 +272,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { void emitObjCDestructorThunk(DestructorDecl *destructor); /// Get or emit the witness table for a protocol conformance. - SILWitnessTable *getWitnessTable(NormalProtocolConformance *conformance); + SILWitnessTable *getWitnessTable(NormalProtocolConformance *conformance, + bool emitAsPrivate = false); /// Emit a protocol witness entry point. SILFunction * diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index ded1712855d46..d7a5dce7cd224 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -409,6 +409,10 @@ class SILGenConformance : public SILGenWitnessTable { Conformance = nullptr; } + void setLinkage(SILLinkage Linkage) { + this->Linkage = Linkage; + } + SILWitnessTable *emit() { // Nothing to do if this wasn't a normal conformance. if (!Conformance) @@ -559,13 +563,17 @@ class SILGenConformance : public SILGenWitnessTable { } // end anonymous namespace SILWitnessTable * -SILGenModule::getWitnessTable(NormalProtocolConformance *conformance) { +SILGenModule::getWitnessTable(NormalProtocolConformance *conformance, + bool emitAsPrivate) { // If we've already emitted this witness table, return it. auto found = emittedWitnessTables.find(conformance); if (found != emittedWitnessTables.end()) return found->second; - SILWitnessTable *table = SILGenConformance(*this, conformance).emit(); + SILGenConformance conf(*this, conformance); + if (emitAsPrivate) + conf.setLinkage(SILLinkage::Private); + SILWitnessTable *table = conf.emit(); emittedWitnessTables.insert({conformance, table}); return table; @@ -1055,14 +1063,16 @@ class SILGenExtension : public TypeMemberVisitor { for (Decl *member : e->getMembers()) visit(member); - // Emit witness tables for protocol conformances introduced by the - // extension. - for (auto *conformance : e->getLocalConformances( - ConformanceLookupKind::All, - nullptr, /*addExtended*/true)) { - if (conformance->isComplete()) { - if (auto *normal = dyn_cast(conformance)) - SGM.getWitnessTable(normal); + if (!isa(e->getExtendedNominal())) { + // Emit witness tables for protocol conformances introduced by the + // extension. + for (auto *conformance : e->getLocalConformances( + ConformanceLookupKind::All, + nullptr)) { + if (conformance->isComplete()) { + if (auto *normal =dyn_cast(conformance)) + SGM.getWitnessTable(normal); + } } } } diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 5ce36bb6dcf50..ac62f6375cd37 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -226,16 +226,17 @@ static void checkInheritanceClause( DC = ext; inheritedClause = ext->getInherited(); + ASTContext &ctx = ext->getASTContext(); // Protocol extensions cannot have inheritance clauses. if (auto proto = ext->getExtendedProtocolDecl()) { - ASTContext &ctx = ext->getASTContext(); TypeChecker &TC = TypeChecker::createForContext(ctx); auto lookupOptions = defaultMemberTypeLookupOptions; lookupOptions -= NameLookupFlags::PerformConformanceCheck; lookupOptions |= NameLookupFlags::IncludeAttributeImplements; - proto->inheritedProtocolsChanged(); + if (!inheritedClause.empty()) + proto->inheritedProtocolsChanged(); for (unsigned i = 0, n = inheritedClause.size(); i != n; ++i) { diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index c4e4fb4a2f036..8c01a0a723347 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -2319,8 +2319,11 @@ class swift::DeclDeserializer { else { ExtensionDecl *extension = decl.get(); extension->setInherited(inherited); - if (auto extended = extension->getExtendedProtocolDecl()) - extended->inheritedProtocolsChanged(); + if (!inherited.empty()) { + if (auto extended = extension->getExtendedProtocolDecl()) + extended->inheritedProtocolsChanged(); + extension->getASTContext().InheritingExtensions[extension] = true; + } } } diff --git a/test/Sema/protocol_extension.swift b/test/Sema/protocol_extension.swift index a7a62a73f6d9c..061cce6073939 100644 --- a/test/Sema/protocol_extension.swift +++ b/test/Sema/protocol_extension.swift @@ -10,11 +10,9 @@ extension FixedWidthInteger: ExpressibleByUnicodeScalarLiteral { } } -//var a: Int = "a" let a: [Int16] = ["a", "b", "c", "d", "e"] let b: [UInt32] = ["a", "b", "c", "d", "e"] - protocol P { } protocol Q { From b5aa687f86067c853a6a4936b186c1025ae22f91 Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Tue, 2 Jul 2019 23:44:02 +0100 Subject: [PATCH 7/8] Access through protocol --- lib/AST/ConformanceLookupTable.cpp | 43 ++++++++++++++++++------------ lib/AST/ConformanceLookupTable.h | 12 ++++----- lib/SILGen/SILGen.cpp | 17 ++++++------ test/Sema/protocol_extension.swift | 12 +++++++-- 4 files changed, 51 insertions(+), 33 deletions(-) diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index aeed83c5ebaf2..eef2e3197cefb 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -138,13 +138,18 @@ void ConformanceLookupTable::destroy() { this->~ConformanceLookupTable(); } -void ConformanceLookupTable::invalidate() { +void ConformanceLookupTable::invalidate(NominalTypeDecl *recurse) { + for (auto &extInfo : NotionalConformancesFromExtension) + for (auto &toInvalidate : extInfo.second) + toInvalidate.first->ConformanceTable = nullptr; + + if (recurse) { + recurse->ConformanceTable = nullptr; + return; + } Conformances.clear(); LastProcessed.clear(); AllConformances.clear(); - for (auto &extInfo : NotionalConformancesFromExtension) - for (auto &toInvalidate : extInfo.second) - toInvalidate.first->prepareConformanceTable()->invalidate(); } namespace { @@ -479,26 +484,30 @@ void ConformanceLookupTable::addInheritedProtocols( Propagator *propagator) { if (depth > 100) // Circularity diagnosed elsewhere return; + ExtensionDecl *ext = decl.dyn_cast(); + ProtocolDecl *proto = ext ? ext->getExtendedProtocolDecl() : + dyn_cast_or_null(decl.dyn_cast()); + // Prepare closure to register inherited protocols against extending. + Propagator protoPropagator = [&](ProtocolDecl *inheritedProto) { + if (proto) { + proto->prepareConformanceTable() + ->addWitnessRequirement(nominal, inheritedProto, ext); + // Continue propagating up stack. + } + if (propagator) + (*propagator)(inheritedProto); + }; // Find all of the protocols in the inheritance list. bool anyObject = false; for (const auto &found : getDirectlyInheritedNominalTypeDecls(decl, anyObject)) addInheritedProtocols(nominal, found.second, source, - depth + 1, found.first, propagator); + depth + 1, found.first, &protoPropagator); - if (ProtocolDecl *proto = - dyn_cast_or_null(decl.dyn_cast())) { + if (auto proto = dyn_cast_or_null(decl.dyn_cast())) { for (ExtensionDecl *ext : proto->getExtensions()) { if (ext->getInherited().empty()) continue; - // Prepare closure to register inherited protocols against extending. - Propagator protoPropagator = [&](ProtocolDecl *inheritedProto) { - proto->prepareConformanceTable() - ->addWitnessRequirement(nominal, inheritedProto, ext); - // Continue propagating up stack. - if (propagator) - (*propagator)(inheritedProto); - }; addInheritedProtocols(nominal, ext, source, depth, ext->getLoc(), &protoPropagator); } @@ -513,8 +522,8 @@ void ConformanceLookupTable::addWitnessRequirement(NominalTypeDecl *nominal, ProtocolDecl *inheritedProto, ExtensionDecl *ext) { NotionalConformancesFromExtension[ext][nominal][inheritedProto] = true; auto table = nominal->prepareConformanceTable(); - if (table->Conformances.find(inheritedProto) == table->Conformances.end()) - table->addProtocol(inheritedProto, ext->getLoc(), + if (ext && table->Conformances.find(inheritedProto) == table->Conformances.end()) + table->addProtocol(inheritedProto, ext ? ext->getLoc() : nominal->getLoc(), ConformanceSource::forExplicit(nominal)); } diff --git a/lib/AST/ConformanceLookupTable.h b/lib/AST/ConformanceLookupTable.h index 50515e836f2a1..615cefeeb9c43 100644 --- a/lib/AST/ConformanceLookupTable.h +++ b/lib/AST/ConformanceLookupTable.h @@ -311,11 +311,6 @@ class ConformanceLookupTable { llvm::MapVector> AllConformances; - /// Tracks notionals that have an implied conformance from inheriting protocol extension - /// Used to know notionals that need to refresh their conformances and which witnesses to emit. - llvm::DenseMap>> NotionalConformancesFromExtension; - /// The complete set of diagnostics about erroneously superseded /// protocol conformances. llvm::SmallDenseMap > @@ -325,6 +320,11 @@ class ConformanceLookupTable { llvm::DenseMap> ConformingDeclMap; + /// Tracks notionals that have an implied conformance from inheriting protocol extension + /// Used to know notionals that need to refresh their conformances and which witnesses to emit. + llvm::DenseMap>> NotionalConformancesFromExtension; + /// Indicates whether we are visiting the superclass. bool VisitingSuperclass = false; @@ -432,7 +432,7 @@ class ConformanceLookupTable { /// Destroy the conformance table. void destroy(); - void invalidate(); + void invalidate(NominalTypeDecl *recurse = nullptr); /// Add a synthesized conformance to the lookup table. void addSynthesizedConformance(NominalTypeDecl *nominal, diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 03eece2f476be..ac256a52968c1 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1704,14 +1704,15 @@ void SILGenModule::emitSourceFile(SourceFile *sf) { } for (auto pair : getASTContext().InheritingExtensions) { - ExtensionDecl *ext = pair.first; - if (ProtocolDecl *proto = ext->getExtendedProtocolDecl()) { - SmallVector result; - proto->prepareConformanceTable()->addExtendedConformances(ext, result); - for (auto conformance : result) { - if (conformance->isComplete()) - if (auto *normal = dyn_cast(conformance)) - getWitnessTable(normal, /*emitAsPrivate*/true); + if (ExtensionDecl *ext = pair.first) { + if (ProtocolDecl *proto = ext->getExtendedProtocolDecl()) { + SmallVector result; + proto->prepareConformanceTable()->addExtendedConformances(ext, result); + for (auto conformance : result) { + if (conformance->isComplete()) + if (auto *normal = dyn_cast(conformance)) + getWitnessTable(normal, /*emitAsPrivate*/true); + } } } } diff --git a/test/Sema/protocol_extension.swift b/test/Sema/protocol_extension.swift index 061cce6073939..32da6b4c1ba1b 100644 --- a/test/Sema/protocol_extension.swift +++ b/test/Sema/protocol_extension.swift @@ -18,7 +18,7 @@ protocol P { protocol Q { func foo2() -> String } -extension P: Q { +extension P {//}: Q { func foo2() -> String { return "Foo2 \(self)!" } @@ -28,10 +28,11 @@ struct S { let a = 99 } protocol P2: P {} +extension P2 {} +extension Numeric: P2 {} extension C: P2 {} extension S: P2 {} -extension FixedWidthInteger: P2 {} // CHECK: Foo2 main.C! print(C().foo2()) @@ -62,6 +63,13 @@ print(b.map {$0.foo2()}) public func use(_ value: Int) -> Int { return value + "1" // ← Used ExpressibleByUnicodeScalarLiteral } +//public func use(_ value: T) -> T +// where T : FixedWidthInteger { +// return value + "1" // ← Used ExpressibleByUnicodeScalarLiteral +//} print(use(1)) +let c: P2 = 99.0 +// CHECK: Foo2 99.0! +print(c.foo2()) From 6d233d3b869e45c3b0328a3580738b54f63c3f4a Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Wed, 3 Jul 2019 16:52:32 +0100 Subject: [PATCH 8/8] Use MultiConformanceChecker to get conformances to be "complete" --- lib/AST/ConformanceLookupTable.cpp | 22 ++++++++------- lib/Sema/TypeCheckProtocol.cpp | 42 ----------------------------- lib/Sema/TypeCheckProtocol.h | 43 +++++++++++++++++++++++++++++- 3 files changed, 54 insertions(+), 53 deletions(-) diff --git a/lib/AST/ConformanceLookupTable.cpp b/lib/AST/ConformanceLookupTable.cpp index eef2e3197cefb..121096344d289 100644 --- a/lib/AST/ConformanceLookupTable.cpp +++ b/lib/AST/ConformanceLookupTable.cpp @@ -24,6 +24,7 @@ #include "swift/AST/ProtocolConformance.h" #include "swift/AST/ProtocolConformanceRef.h" #include "llvm/Support/SaveAndRestore.h" +#include "../Sema/TypeCheckProtocol.h" using namespace swift; @@ -147,8 +148,9 @@ void ConformanceLookupTable::invalidate(NominalTypeDecl *recurse) { recurse->ConformanceTable = nullptr; return; } - Conformances.clear(); + LastProcessed.clear(); + Conformances.clear(); AllConformances.clear(); } @@ -489,11 +491,10 @@ void ConformanceLookupTable::addInheritedProtocols( dyn_cast_or_null(decl.dyn_cast()); // Prepare closure to register inherited protocols against extending. Propagator protoPropagator = [&](ProtocolDecl *inheritedProto) { - if (proto) { + if (proto) proto->prepareConformanceTable() ->addWitnessRequirement(nominal, inheritedProto, ext); - // Continue propagating up stack. - } + // Continue propagating up stack. if (propagator) (*propagator)(inheritedProto); }; @@ -1105,6 +1106,8 @@ void ConformanceLookupTable::lookupConformances( void ConformanceLookupTable::addExtendedConformances(const ExtensionDecl *ext, SmallVectorImpl &conformances) { + TypeChecker &TC = TypeChecker::createForContext(ext->getASTContext()); + MultiConformanceChecker groupChecker(TC); for (auto &nominalPair : NotionalConformancesFromExtension[ext]) { NominalTypeDecl *nominal = nominalPair.first; for (auto &protocolPair : nominalPair.second) { @@ -1116,14 +1119,13 @@ void ConformanceLookupTable::addExtendedConformances(const ExtensionDecl *ext, if (entry == table->Conformances.end()) continue; auto conformance = table->getConformance(nominal, entry->second.back()); - // FIXME: Bit of a hack here to force the conformance to be "complete" - if (auto normal = dyn_cast(conformance)) - if (normal->getProtocol()->FirstExtension) { - normal->setState(ProtocolConformanceState::Complete); - conformances.push_back(conformance); - } + if (auto normal = dyn_cast(conformance)) { + conformances.push_back(conformance); + groupChecker.addConformance(normal); + } } } + groupChecker.checkAllConformances(); } void ConformanceLookupTable::getAllProtocols( diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index c084baeaa9d25..f8f84e7fc1832 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -1293,48 +1293,6 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement, # pragma mark Witness resolution -/// This is a wrapper of multiple instances of ConformanceChecker to allow us -/// to diagnose and fix code from a more global perspective; for instance, -/// having this wrapper can help issue a fixit that inserts protocol stubs from -/// multiple protocols under checking. -class swift::MultiConformanceChecker { - TypeChecker &TC; - llvm::SmallVector UnsatisfiedReqs; - llvm::SmallVector AllUsedCheckers; - llvm::SmallVector AllConformances; - llvm::SetVector MissingWitnesses; - llvm::SmallPtrSet CoveredMembers; - - /// Check one conformance. - ProtocolConformance * checkIndividualConformance( - NormalProtocolConformance *conformance, bool issueFixit); - - /// Determine whether the given requirement was left unsatisfied. - bool isUnsatisfiedReq(NormalProtocolConformance *conformance, ValueDecl *req); -public: - MultiConformanceChecker(TypeChecker &TC): TC(TC){} - - TypeChecker &getTypeChecker() const { return TC; } - - /// Add a conformance into the batched checker. - void addConformance(NormalProtocolConformance *conformance) { - AllConformances.push_back(conformance); - } - - /// Peek the unsatisfied requirements collected during conformance checking. - ArrayRef getUnsatisfiedRequirements() { - return llvm::makeArrayRef(UnsatisfiedReqs); - } - - /// Whether this member is "covered" by one of the conformances. - bool isCoveredMember(ValueDecl *member) const { - return CoveredMembers.count(member) > 0; - } - - /// Check all conformances and emit diagnosis globally. - void checkAllConformances(); -}; - bool MultiConformanceChecker:: isUnsatisfiedReq(NormalProtocolConformance *conformance, ValueDecl *req) { if (conformance->isInvalid()) return false; diff --git a/lib/Sema/TypeCheckProtocol.h b/lib/Sema/TypeCheckProtocol.h index 871ffc356bb1e..6a18cc1a1680b 100644 --- a/lib/Sema/TypeCheckProtocol.h +++ b/lib/Sema/TypeCheckProtocol.h @@ -24,6 +24,7 @@ #include "swift/AST/Type.h" #include "swift/AST/Types.h" #include "swift/AST/Witness.h" +#include "swift/AST/GenericSignature.h" #include "llvm/ADT/ScopedHashTable.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallPtrSet.h" @@ -932,6 +933,46 @@ llvm::TinyPtrVector findWitnessedObjCRequirements( const ValueDecl *witness, bool anySingleRequirement = false); -} +/// This is a wrapper of multiple instances of ConformanceChecker to allow us +/// to diagnose and fix code from a more global perspective; for instance, +/// having this wrapper can help issue a fixit that inserts protocol stubs from +/// multiple protocols under checking. +class MultiConformanceChecker { + TypeChecker &TC; + llvm::SmallVector UnsatisfiedReqs; + llvm::SmallVector AllUsedCheckers; + llvm::SmallVector AllConformances; + llvm::SetVector MissingWitnesses; + llvm::SmallPtrSet CoveredMembers; + + /// Check one conformance. + ProtocolConformance * checkIndividualConformance( + NormalProtocolConformance *conformance, bool issueFixit); + + /// Determine whether the given requirement was left unsatisfied. + bool isUnsatisfiedReq(NormalProtocolConformance *conformance, ValueDecl *req); +public: + MultiConformanceChecker(TypeChecker &TC): TC(TC){} + + TypeChecker &getTypeChecker() const { return TC; } + + /// Add a conformance into the batched checker. + void addConformance(NormalProtocolConformance *conformance) { + AllConformances.push_back(conformance); + } + /// Peek the unsatisfied requirements collected during conformance checking. + ArrayRef getUnsatisfiedRequirements() { + return llvm::makeArrayRef(UnsatisfiedReqs); + } + + /// Whether this member is "covered" by one of the conformances. + bool isCoveredMember(ValueDecl *member) const { + return CoveredMembers.count(member) > 0; + } + + /// Check all conformances and emit diagnosis globally. + void checkAllConformances(); +}; +} #endif // SWIFT_SEMA_PROTOCOL_H