Skip to content

Commit d993e02

Browse files
committed
GSB: Disable conditional requirement inference in protocols
If you have a pair of requirements T : P and T == G<U>, the conformance G : P might be conditional, imposing arbitrary requirements on U. In particular, these conditional requirements can mention arbitrary protocols on the right hand side. Introducing these conformance requirements during property map construction is totally fine when building a top-level generic signature, but when building a protocol requirement signature, things get a bit tricky. Because of the design of the requirement machine, it is better if the set of protocols appearing on the right hand side of conformance requirements in another protocol (the "protocol dependencies") are known *before* we start building the requirement signature, because we build the requirement signatures of all protocols in a connected component of this graph simultaneously. Introducing conformance requirements to hithero-unseen protocols after the graph of connected components had already been built would require mutating it in a tricky way, and possibly merging connected components. I didn't find any examples of protocols that rely on conditional requirement inference in our test suite, or in the source compatibility suite. So for now, I'm going to try to disable this feature inside protocols. Another argument in favor of not doing conditional requirement inference in protocols is that we don't do the ordinary kind of requirement inference there either. That is, the following is an error: protocol P { associatedtype T == Set<U> associatedtype U } Unlike with an ordinary top-level generic signature, we don't infer 'U : Hashable' here. So arguably the current behavior of protocols inferring these requirements in the case of a conditional conformance only is also rather odd.
1 parent 81ac321 commit d993e02

File tree

3 files changed

+33
-3
lines changed

3 files changed

+33
-3
lines changed

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,9 @@ struct GenericSignatureBuilder::Implementation {
680680
/// requirements.
681681
bool RebuildingWithoutRedundantConformances = false;
682682

683+
/// Whether we are building a protocol requirement signature.
684+
bool BuildingProtocolRequirementSignature = false;
685+
683686
/// A mapping of redundant explicit requirements to the best root requirement
684687
/// that implies them. Built by computeRedundantRequirements().
685688
using RedundantRequirementMap =
@@ -2313,6 +2316,11 @@ void GenericSignatureBuilder::addConditionalRequirements(
23132316
if (Impl->RebuildingWithoutRedundantConformances)
23142317
return;
23152318

2319+
// We do not perform requirement inference, including conditional requirement
2320+
// inference, inside protocols.
2321+
if (Impl->BuildingProtocolRequirementSignature)
2322+
return;
2323+
23162324
// Abstract conformances don't have associated decl-contexts/modules, but also
23172325
// don't have conditional requirements.
23182326
if (conformance.isConcrete()) {
@@ -3399,10 +3407,12 @@ void EquivalenceClass::modified(GenericSignatureBuilder &builder) {
33993407
}
34003408

34013409
GenericSignatureBuilder::GenericSignatureBuilder(
3402-
ASTContext &ctx)
3410+
ASTContext &ctx,
3411+
bool requirementSignature)
34033412
: Context(ctx), Diags(Context.Diags), Impl(new Implementation) {
34043413
if (auto *Stats = Context.Stats)
34053414
++Stats->getFrontendCounters().NumGenericSignatureBuilders;
3415+
Impl->BuildingProtocolRequirementSignature = requirementSignature;
34063416
}
34073417

34083418
GenericSignatureBuilder::GenericSignatureBuilder(
@@ -8833,7 +8843,8 @@ RequirementSignatureRequest::evaluate(Evaluator &evaluator,
88338843
}
88348844

88358845
auto buildViaGSB = [&]() {
8836-
GenericSignatureBuilder builder(proto->getASTContext());
8846+
GenericSignatureBuilder builder(proto->getASTContext(),
8847+
/*requirementSignature=*/true);
88378848

88388849
// Add all of the generic parameters.
88398850
for (auto gp : *proto->getGenericParams())

lib/AST/GenericSignatureBuilder.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,8 @@ class GenericSignatureBuilder {
496496

497497
public:
498498
/// Construct a new generic signature builder.
499-
explicit GenericSignatureBuilder(ASTContext &ctx);
499+
explicit GenericSignatureBuilder(ASTContext &ctx,
500+
bool requirementSignature=false);
500501
GenericSignatureBuilder(GenericSignatureBuilder &&);
501502
~GenericSignatureBuilder();
502503

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: %target-typecheck-verify-swift
2+
// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures 2>&1 | %FileCheck %s
3+
4+
// CHECK-LABEL: conditional_requirement_inference_in_protocol.(file).Good@
5+
// CHECK-LABEL: Requirement signature: <Self where Self.T == Array<Self.U>, Self.U : Equatable>
6+
7+
protocol Good {
8+
associatedtype T : Equatable // expected-warning {{redundant conformance constraint 'Self.T' : 'Equatable'}}
9+
associatedtype U : Equatable where T == Array<U> // expected-note {{conformance constraint 'Self.T' : 'Equatable' implied here}}
10+
}
11+
12+
// CHECK-LABEL: conditional_requirement_inference_in_protocol.(file).Bad@
13+
// CHECK-LABEL: Requirement signature: <Self where Self.T == Array<Self.U>>
14+
15+
protocol Bad {
16+
associatedtype T : Equatable // expected-warning {{redundant conformance constraint 'Self.T' : 'Equatable'}}
17+
associatedtype U where T == Array<U> // expected-note {{conformance constraint 'Self.T' : 'Equatable' implied here}}
18+
}

0 commit comments

Comments
 (0)