Skip to content

Sema: Diagnose missing entries in protocol inheritance clause when requirement machine is enabled #42187

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
38 changes: 38 additions & 0 deletions lib/Sema/TypeCheckDeclPrimary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1695,6 +1695,39 @@ static void diagnoseWrittenPlaceholderTypes(ASTContext &Ctx,
}
}

/// Make sure that every protocol conformance requirement on 'Self' is
/// directly stated in the protocol's inheritance clause.
///
/// This disallows protocol declarations where a conformance requirement on
/// 'Self' is implied by some other requirement, such as this:
///
/// protocol Other { ... }
/// protocol Foo { associatedtype A : Other }
/// protocol Bar {
/// associatedtype A : Foo where Self == A.A
/// }
///
/// Since name lookup is upstream of generic signature computation, we
/// want 'Other' to appear in the inheritance clause of 'Bar', so that
/// name lookup on Bar can find members of Other.
static void checkProtocolRefinementRequirements(ProtocolDecl *proto) {
auto requiredProtos = proto->getGenericSignature()->getRequiredProtocols(
proto->getSelfInterfaceType());

for (auto *otherProto : requiredProtos) {
// Every protocol 'P' has an implied requirement 'Self : P'; skip it.
if (otherProto == proto)
continue;

// GenericSignature::getRequiredProtocols() canonicalizes the protocol
// list by dropping protocols that are inherited by other protocols in
// the list. Any protocols that remain in the list other than 'proto'
// itself are implied by a conformance requirement on 'Self', but are
// not (transitively) inherited by 'proto'.
proto->diagnose(diag::missing_protocol_refinement, proto, otherProto);
}
}

namespace {
class DeclChecker : public DeclVisitor<DeclChecker> {
public:
Expand Down Expand Up @@ -2670,6 +2703,11 @@ class DeclChecker : public DeclVisitor<DeclChecker> {

checkInheritanceClause(PD);

if (PD->getASTContext().LangOpts.RequirementMachineProtocolSignatures
== RequirementMachineMode::Enabled) {
checkProtocolRefinementRequirements(PD);
}

TypeChecker::checkDeclCircularity(PD);
if (PD->isResilient())
if (!SF || SF->Kind != SourceFileKind::Interface)
Expand Down
4 changes: 4 additions & 0 deletions test/Generics/rdar51908331.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ protocol PH {
}

protocol PI {
// expected-warning@-1 {{protocol 'PI' should be declared to refine 'Decodable' due to a same-type constraint on 'Self'}}
// expected-warning@-2 {{protocol 'PI' should be declared to refine 'Encodable' due to a same-type constraint on 'Self'}}
// expected-warning@-3 {{protocol 'PI' should be declared to refine 'Hashable' due to a same-type constraint on 'Self'}}
// expected-warning@-4 {{protocol 'PI' should be declared to refine 'SIMDScalar' due to a same-type constraint on 'Self'}}
associatedtype A8 where A8.A7 == Self // expected-warning {{redundant same-type constraint 'Self.A8.A7' == 'Self'}}
associatedtype A9 where A9.A7 == Self, A9.A8 == A8
associatedtype A10 where A10.A7 == Self, A10.A8 == A8, A10.A9 == A9 // expected-warning {{redundant same-type constraint 'Self.A10.A7' == 'Self'}}
Expand Down
11 changes: 4 additions & 7 deletions test/Generics/redundant_protocol_refinement.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %target-typecheck-verify-swift -requirement-machine-protocol-signatures=verify -requirement-machine-inferred-signatures=verify
// RUN: %target-swift-frontend -typecheck -debug-generic-signatures -requirement-machine-protocol-signatures=verify %s 2>&1 | %FileCheck %s
// RUN: %target-typecheck-verify-swift -requirement-machine-protocol-signatures=on
// RUN: %target-swift-frontend -typecheck -debug-generic-signatures -requirement-machine-protocol-signatures=on %s 2>&1 | %FileCheck %s

// CHECK-LABEL: redundant_protocol_refinement.(file).Base@
// CHECK-LABEL: Requirement signature: <Self>
Expand All @@ -12,8 +12,7 @@ protocol Middle : Base {}
// CHECK-LABEL: redundant_protocol_refinement.(file).Derived@
// CHECK-LABEL: Requirement signature: <Self where Self : Middle>
protocol Derived : Middle, Base {}
// expected-note@-1 {{conformance constraint 'Self' : 'Base' implied here}}
// expected-warning@-2 {{redundant conformance constraint 'Self' : 'Base'}}
// expected-warning@-1 {{redundant conformance constraint 'Self' : 'Base'}}

// CHECK-LABEL: redundant_protocol_refinement.(file).Derived2@
// CHECK-LABEL: Requirement signature: <Self where Self : Middle>
Expand All @@ -22,8 +21,7 @@ protocol Derived2 : Middle {}
// CHECK-LABEL: redundant_protocol_refinement.(file).MoreDerived@
// CHECK-LABEL: Requirement signature: <Self where Self : Derived2>
protocol MoreDerived : Derived2, Base {}
// expected-note@-1 {{conformance constraint 'Self' : 'Base' implied here}}
// expected-warning@-2 {{redundant conformance constraint 'Self' : 'Base'}}
// expected-warning@-1 {{redundant conformance constraint 'Self' : 'Base'}}

protocol P1 {}

Expand All @@ -40,7 +38,6 @@ protocol Good: P2, P1 where Assoc == Self {}
// missing refinement of 'P1'
protocol Bad: P2 where Assoc == Self {}
// expected-warning@-1 {{protocol 'Bad' should be declared to refine 'P1' due to a same-type constraint on 'Self'}}
// expected-note@-2 {{conformance constraint 'Self' : 'P1' implied here}}

// CHECK-LABEL: redundant_protocol_refinement.(file).Bad@
// CHECK-LABEL: Requirement signature: <Self where Self : P2, Self == Self.[P2]Assoc>