Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/swift/AST/ArchetypeBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) { }
Expand Down
9 changes: 9 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
29 changes: 23 additions & 6 deletions lib/AST/ArchetypeBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is just a refactoring of what was there before, but it seems odd to use RequirementSource::Redundant here rather than RequirementSource::Protocol. Maybe we should (separately!) see if we can make that change to align it with what we do below.

RequirementSource InnerSource(InnerKind, Source.getLoc());

bool inserted = Visited.insert(Proto).second;
assert(inserted);
(void) inserted;
Expand All @@ -1024,10 +1035,15 @@ bool ArchetypeBuilder::addConformanceRequirement(PotentialArchetype *PAT,
if (auto AssocType = dyn_cast<AssociatedTypeDecl>(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;
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down
25 changes: 25 additions & 0 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -2999,6 +3000,30 @@ void ProtocolDecl::createGenericParamsIfMissing() {
setGenericParams(createGenericParams(this));
}

GenericSignature *ProtocolDecl::getRequirementSignature() {
auto module = getParentModule();

auto genericSig = getGenericSignature();
// The signature should look like <Self where Self : ThisProtocol>, 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 {
Expand Down
14 changes: 14 additions & 0 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4371,6 +4371,20 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
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) {
Expand Down
70 changes: 70 additions & 0 deletions test/Generics/protocol_requirement_signatures.swift
Original file line number Diff line number Diff line change
@@ -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: <Self>
// CHECK-NEXT: Canonical requirement signature: <τ_0_0>
protocol P1 {}

// CHECK-LABEL: .P2@
// CHECK-NEXT: Requirement signature: <Self>
// CHECK-NEXT: Canonical requirement signature: <τ_0_0>
protocol P2 {}

// CHECK-LABEL: .P3@
// CHECK-NEXT: Requirement signature: <Self>
// CHECK-NEXT: Canonical requirement signature: <τ_0_0>
protocol P3 {}

// basic protocol
// CHECK-LABEL: .Q1@
// CHECK-NEXT: Requirement signature: <Self where Self.X : P1>
// 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: <Self where Self : Q1>
// 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: <Self where Self : Q1>
// 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: <Self where Self : Q1, Self.X : P2>
// 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: <Self where Self : Q2, Self : Q3, Self : Q4>
// 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: <Self where Self : Q2, Self : Q3, Self : Q4>
// 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: <Self where Self : Q2, Self : Q3, Self : Q4, Self.X : P3>
// 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
}