diff --git a/include/swift/AST/ArchetypeBuilder.h b/include/swift/AST/ArchetypeBuilder.h index 9914458ff3bff..75cddd5b1d91d 100644 --- a/include/swift/AST/ArchetypeBuilder.h +++ b/include/swift/AST/ArchetypeBuilder.h @@ -83,6 +83,10 @@ class RequirementSource { /// /// These are dropped when building the GenericSignature. Inherited, + + /// The requirement is the Self: Protocol requirement, when computing a + /// protocol's requirement signature. + ProtocolRequirementSignatureSelf, }; RequirementSource(Kind kind, SourceLoc loc) : StoredKind(kind), Loc(loc) { } diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 3ce71bb0e58c4..0ce66bb0c6fa1 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3575,6 +3575,15 @@ class ProtocolDecl : public NominalTypeDecl { /// created yet. void createGenericParamsIfMissing(); + /// Retrieve the generic signature representing the requirements introduced by + /// this protocol. + /// + /// These are the requirements like any inherited protocols and conformances + /// for associated types that are mentioned literally in this + /// decl. Requirements implied via inheritance are not mentioned, nor is the + /// conformance of Self to this protocol. + GenericSignature *getRequirementSignature(); + // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return D->getKind() == DeclKind::Protocol; diff --git a/lib/AST/ArchetypeBuilder.cpp b/lib/AST/ArchetypeBuilder.cpp index 4d6e0d0f0ed4d..690b7e939f3e2 100644 --- a/lib/AST/ArchetypeBuilder.cpp +++ b/lib/AST/ArchetypeBuilder.cpp @@ -63,6 +63,10 @@ void RequirementSource::dump(llvm::raw_ostream &out, case Inherited: out << "inherited"; break; + + case ProtocolRequirementSignatureSelf: + out << "protocol_requirement_signature_self"; + break; } if (srcMgr && getLoc().isValid()) { @@ -753,6 +757,7 @@ Type ArchetypeBuilder::PotentialArchetype::getTypeInContext( case RequirementSource::Explicit: case RequirementSource::Inferred: case RequirementSource::Protocol: + case RequirementSource::ProtocolRequirementSignatureSelf: case RequirementSource::Redundant: Protos.push_back(conforms.first); break; @@ -1002,8 +1007,14 @@ bool ArchetypeBuilder::addConformanceRequirement(PotentialArchetype *PAT, if (!T->addConformance(Proto, /*updateExistingSource=*/true, Source, *this)) return false; - RequirementSource InnerSource(RequirementSource::Redundant, Source.getLoc()); - + // Conformances to inherit protocols are explicit in a protocol requirement + // signature, but inferred from this conformance otherwise. + auto InnerKind = + Source.getKind() == RequirementSource::ProtocolRequirementSignatureSelf + ? RequirementSource::Explicit + : RequirementSource::Redundant; + RequirementSource InnerSource(InnerKind, Source.getLoc()); + bool inserted = Visited.insert(Proto).second; assert(inserted); (void) inserted; @@ -1024,10 +1035,15 @@ bool ArchetypeBuilder::addConformanceRequirement(PotentialArchetype *PAT, if (auto AssocType = dyn_cast(Member)) { // Add requirements placed directly on this associated type. auto AssocPA = T->getNestedType(AssocType, *this); + // Requirements introduced by the main protocol are explicit in a protocol + // requirement signature, but inferred from this conformance otherwise. + auto Kind = Source.getKind() == + RequirementSource::ProtocolRequirementSignatureSelf + ? RequirementSource::Explicit + : RequirementSource::Protocol; + if (AssocPA != T) { - if (addAbstractTypeParamRequirements(AssocType, AssocPA, - RequirementSource::Protocol, - Visited)) + if (addAbstractTypeParamRequirements(AssocType, AssocPA, Kind, Visited)) return true; } @@ -2060,7 +2076,7 @@ void ArchetypeBuilder::dump(llvm::raw_ostream &out) { void ArchetypeBuilder::addGenericSignature(GenericSignature *sig) { if (!sig) return; - + RequirementSource::Kind sourceKind = RequirementSource::Explicit; for (auto param : sig->getGenericParams()) addGenericParameter(param); @@ -2090,6 +2106,7 @@ static void collectRequirements(ArchetypeBuilder &builder, case RequirementSource::Protocol: case RequirementSource::Redundant: case RequirementSource::Inherited: + case RequirementSource::ProtocolRequirementSignatureSelf: // The requirement was redundant, drop it. return; } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index dd7c2e8b3f3ec..62a16e8c8c9f5 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -16,6 +16,7 @@ #include "swift/AST/Decl.h" #include "swift/AST/AccessScope.h" +#include "swift/AST/ArchetypeBuilder.h" #include "swift/AST/AST.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTWalker.h" @@ -2999,6 +3000,30 @@ void ProtocolDecl::createGenericParamsIfMissing() { setGenericParams(createGenericParams(this)); } +GenericSignature *ProtocolDecl::getRequirementSignature() { + auto module = getParentModule(); + + auto genericSig = getGenericSignature(); + // The signature should look like , and we + // reuse the two parts of it because the parameter and the requirement are + // exactly what we need. + assert(genericSig->getGenericParams().size() == 1 && + genericSig->getRequirements().size() == 1 && + "getRequirementSignature with unexpected generic signature"); + + auto selfType = genericSig->getGenericParams()[0]; + auto requirement = genericSig->getRequirements()[0]; + + RequirementSource source(RequirementSource::ProtocolRequirementSignatureSelf, + getLoc()); + + ArchetypeBuilder builder(getASTContext(), LookUpConformanceInModule(module)); + builder.addGenericParameter(selfType); + builder.addRequirement(requirement, source); + + return builder.getGenericSignature(); +} + /// Returns the default witness for a requirement, or nullptr if there is /// no default. Witness ProtocolDecl::getDefaultWitness(ValueDecl *requirement) const { diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 01ceffd0164e4..4c106e2c52b7b 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -4371,6 +4371,20 @@ class DeclChecker : public DeclVisitor { visit(Member); TC.checkDeclAttributes(PD); + + if (TC.Context.LangOpts.DebugGenericSignatures) { + auto requirementsSig = PD->getRequirementSignature(); + + llvm::errs() << "Protocol requirement signature:\n"; + PD->dumpRef(llvm::errs()); + llvm::errs() << "\n"; + llvm::errs() << "Requirement signature: "; + requirementsSig->print(llvm::errs()); + llvm::errs() << "\n"; + llvm::errs() << "Canonical requirement signature: "; + requirementsSig->getCanonicalSignature()->print(llvm::errs()); + llvm::errs() << "\n"; + } } void visitVarDecl(VarDecl *VD) { diff --git a/test/Generics/protocol_requirement_signatures.swift b/test/Generics/protocol_requirement_signatures.swift new file mode 100644 index 0000000000000..602b8ae7e3d1b --- /dev/null +++ b/test/Generics/protocol_requirement_signatures.swift @@ -0,0 +1,70 @@ +// RUN: %target-typecheck-verify-swift -typecheck %s -verify +// RUN: %target-typecheck-verify-swift -typecheck -debug-generic-signatures %s > %t.dump 2>&1 +// RUN: %FileCheck %s < %t.dump + +// CHECK-LABEL: .P1@ +// CHECK-NEXT: Requirement signature: +// CHECK-NEXT: Canonical requirement signature: <τ_0_0> +protocol P1 {} + +// CHECK-LABEL: .P2@ +// CHECK-NEXT: Requirement signature: +// CHECK-NEXT: Canonical requirement signature: <τ_0_0> +protocol P2 {} + +// CHECK-LABEL: .P3@ +// CHECK-NEXT: Requirement signature: +// CHECK-NEXT: Canonical requirement signature: <τ_0_0> +protocol P3 {} + +// basic protocol +// CHECK-LABEL: .Q1@ +// CHECK-NEXT: Requirement signature: +// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0.X : P1> +protocol Q1 { + associatedtype X: P1 +} + +// inheritance +// CHECK-LABEL: .Q2@ +// CHECK-NEXT: Requirement signature: +// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0 : Q1> +protocol Q2: Q1 {} + +// inheritance without any new requirements +// CHECK-LABEL: .Q3@ +// CHECK-NEXT: Requirement signature: +// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0 : Q1> +protocol Q3: Q1 { + associatedtype X +} + +// inheritance adding a new conformance +// CHECK-LABEL: .Q4@ +// CHECK-NEXT: Requirement signature: +// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0 : Q1, τ_0_0.X : P2> +protocol Q4: Q1 { + associatedtype X: P2 +} + +// multiple inheritance +// CHECK-LABEL: .Q5@ +// CHECK-NEXT: Requirement signature: +// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0 : Q2, τ_0_0 : Q3, τ_0_0 : Q4> +protocol Q5: Q2, Q3, Q4 {} + +// multiple inheritance without any new requirements +// CHECK-LABEL: .Q6@ +// CHECK-NEXT: Requirement signature: +// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0 : Q2, τ_0_0 : Q3, τ_0_0 : Q4> +protocol Q6: Q2, Q3, Q4 { + associatedtype X: P1 +} + +// multiple inheritance with a new conformance +// CHECK-LABEL: .Q7@ +// CHECK-NEXT: Requirement signature: +// CHECK-NEXT: Canonical requirement signature: <τ_0_0 where τ_0_0 : Q2, τ_0_0 : Q3, τ_0_0 : Q4, τ_0_0.X : P3> +protocol Q7: Q2, Q3, Q4 { + associatedtype X: P3 +}