Skip to content

Commit 8e4598e

Browse files
committed
GSB: Narrow scope of conditional requirement inference
If we have a conformance requirement T : P, and a concrete type requirement T == G<...>, and G _conditionally_ conforms to P, we would infer the conditional requirements of G needed to satisfy the conformance. However, if the conformance requirement T : P was not explicit, this would mean in practice that we would need to infer an infinite number of conditional requirements, because there might be an infinite number of types T for which T : P. Previously we would infer these up to some limit, based on how many levels of nested types the GSB had expanded. Since this is untenable, let's instead change the rules so that conditional requirement inference is only performed when the concretizing requirement was explicit.
1 parent 58dbf60 commit 8e4598e

File tree

4 files changed

+122
-15
lines changed

4 files changed

+122
-15
lines changed

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1422,6 +1422,9 @@ class GenericSignatureBuilder::FloatingRequirementSource {
14221422
/// Whether this is an explicitly-stated requirement.
14231423
bool isExplicit() const;
14241424

1425+
/// Whether this is a derived requirement.
1426+
bool isDerived() const;
1427+
14251428
/// Whether this is a top-level requirement written in source.
14261429
/// FIXME: This is a hack because expandConformanceRequirement()
14271430
/// is too eager; we should remove this once we fix it properly.

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1758,6 +1758,37 @@ SourceLoc FloatingRequirementSource::getLoc() const {
17581758
return SourceLoc();
17591759
}
17601760

1761+
bool FloatingRequirementSource::isDerived() const {
1762+
switch (kind) {
1763+
case Explicit:
1764+
case Inferred:
1765+
case NestedTypeNameMatch:
1766+
return false;
1767+
1768+
case AbstractProtocol:
1769+
switch (storage.get<const RequirementSource *>()->kind) {
1770+
case RequirementSource::RequirementSignatureSelf:
1771+
return false;
1772+
1773+
case RequirementSource::Concrete:
1774+
case RequirementSource::Explicit:
1775+
case RequirementSource::Inferred:
1776+
case RequirementSource::NestedTypeNameMatch:
1777+
case RequirementSource::Parent:
1778+
case RequirementSource::ProtocolRequirement:
1779+
case RequirementSource::InferredProtocolRequirement:
1780+
case RequirementSource::Superclass:
1781+
case RequirementSource::Layout:
1782+
case RequirementSource::EquivalentType:
1783+
return true;
1784+
}
1785+
1786+
case Resolved:
1787+
return storage.get<const RequirementSource *>()->isDerivedRequirement();
1788+
}
1789+
llvm_unreachable("unhandled kind");
1790+
}
1791+
17611792
bool FloatingRequirementSource::isExplicit() const {
17621793
switch (kind) {
17631794
case Explicit:
@@ -2556,9 +2587,20 @@ GenericSignatureBuilder::resolveConcreteConformance(ResolvedType type,
25562587
} else {
25572588
concreteSource = concreteSource->viaConcrete(*this, conformance);
25582589
equivClass->recordConformanceConstraint(*this, type, proto, concreteSource);
2559-
if (addConditionalRequirements(conformance, /*inferForModule=*/nullptr,
2560-
concreteSource->getLoc()))
2561-
return nullptr;
2590+
2591+
// Only infer conditional requirements from explicit sources.
2592+
bool hasExplicitSource = llvm::any_of(
2593+
equivClass->concreteTypeConstraints,
2594+
[](const ConcreteConstraint &constraint) {
2595+
return (!constraint.source->isDerivedRequirement() &&
2596+
constraint.source->getLoc().isValid());
2597+
});
2598+
2599+
if (hasExplicitSource) {
2600+
if (addConditionalRequirements(conformance, /*inferForModule=*/nullptr,
2601+
concreteSource->getLoc()))
2602+
return nullptr;
2603+
}
25622604
}
25632605

25642606
return concreteSource;
@@ -2590,9 +2632,20 @@ const RequirementSource *GenericSignatureBuilder::resolveSuperConformance(
25902632

25912633
superclassSource = superclassSource->viaSuperclass(*this, conformance);
25922634
equivClass->recordConformanceConstraint(*this, type, proto, superclassSource);
2593-
if (addConditionalRequirements(conformance, /*inferForModule=*/nullptr,
2594-
superclassSource->getLoc()))
2595-
return nullptr;
2635+
2636+
// Only infer conditional requirements from explicit sources.
2637+
bool hasExplicitSource = llvm::any_of(
2638+
equivClass->superclassConstraints,
2639+
[](const ConcreteConstraint &constraint) {
2640+
return (!constraint.source->isDerivedRequirement() &&
2641+
constraint.source->getLoc().isValid());
2642+
});
2643+
2644+
if (hasExplicitSource) {
2645+
if (addConditionalRequirements(conformance, /*inferForModule=*/nullptr,
2646+
superclassSource->getLoc()))
2647+
return nullptr;
2648+
}
25962649

25972650
return superclassSource;
25982651
}
@@ -4599,9 +4652,12 @@ ConstraintResult GenericSignatureBuilder::addTypeRequirement(
45994652

46004653
// FIXME: diagnose if there's no conformance.
46014654
if (conformance) {
4602-
if (addConditionalRequirements(conformance, inferForModule,
4603-
source.getLoc()))
4604-
return ConstraintResult::Conflicting;
4655+
// Only infer conditional requirements from explicit sources.
4656+
if (!source.isDerived()) {
4657+
if (addConditionalRequirements(conformance, inferForModule,
4658+
source.getLoc()))
4659+
return ConstraintResult::Conflicting;
4660+
}
46054661
}
46064662
}
46074663
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// RUN: %target-typecheck-verify-swift
2+
// RUN: not %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s
3+
4+
5+
// Valid example
6+
struct EquatableBox<T : Equatable> {
7+
// CHECK: Generic signature: <T, U where T == Array<U>, U : Equatable>
8+
func withArray<U>(_: U) where T == Array<U> {}
9+
}
10+
11+
12+
// A very elaborate invalid example (see comment in mergeP1AndP2())
13+
struct G<T> {}
14+
15+
protocol P {}
16+
extension G : P where T : P {}
17+
18+
protocol P1 {
19+
associatedtype T
20+
associatedtype U where U == G<T>
21+
associatedtype R : P1
22+
}
23+
24+
protocol P2 {
25+
associatedtype U : P
26+
associatedtype R : P2
27+
}
28+
29+
func takesP<T : P>(_: T.Type) {}
30+
// expected-note@-1 {{where 'T' = 'T.T'}}
31+
// expected-note@-2 {{where 'T' = 'T.R.T'}}
32+
// expected-note@-3 {{where 'T' = 'T.R.R.T'}}
33+
// expected-note@-4 {{where 'T' = 'T.R.R.R.T'}}
34+
35+
// CHECK: Generic signature: <T where T : P1, T : P2>
36+
func mergeP1AndP2<T : P1 & P2>(_: T) {
37+
// P1 implies that T.(R)*.U == G<T.(R)*.T>, and P2 implies that T.(R)*.U : P.
38+
//
39+
// These together would seem to imply that G<T.(R)*.T> : P, therefore
40+
// the conditional conformance G : P should imply that T.(R)*.T : P.
41+
//
42+
// However, this would require us to infer an infinite number of
43+
// conformance requirements in the signature of mergeP1AndP2() of the
44+
// form T.(R)*.T : P.
45+
//
46+
// Since we're unable to represent that, make sure that a) we don't crash,
47+
// b) we reject the conformance T.(R)*.T : P.
48+
49+
takesP(T.T.self) // expected-error {{global function 'takesP' requires that 'T.T' conform to 'P'}}
50+
takesP(T.R.T.self) // expected-error {{global function 'takesP' requires that 'T.R.T' conform to 'P'}}
51+
takesP(T.R.R.T.self) // expected-error {{global function 'takesP' requires that 'T.R.R.T' conform to 'P'}}
52+
takesP(T.R.R.R.T.self) // expected-error {{global function 'takesP' requires that 'T.R.R.R.T' conform to 'P'}}
53+
}
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
11
// Verifies that all of the generic signatures in the standard library are
22
// minimal and canonical.
33

4-
// RUN: not %target-typecheck-verify-swift -verify-generic-signatures Swift 2>&1 | %FileCheck %s
5-
6-
// CHECK-NOT: error:
7-
// CHECK-DAG: error: unexpected error produced: generic requirement 'τ_0_0.Index : Strideable' is redundant in <τ_0_0 where τ_0_0 : RandomAccessCollection, τ_0_0.Index : Strideable, τ_0_0.Indices == Range<τ_0_0.Index>, τ_0_0.Index.Stride == Int>
8-
// CHECK-DAG: error: diagnostic produced elsewhere: generic requirement 'τ_0_0.Index : Strideable' is redundant in <τ_0_0 where τ_0_0 : RandomAccessCollection, τ_0_0.Index : Strideable, τ_0_0.Indices == Range<τ_0_0.Index>, τ_0_0.Index.Stride == Int>
9-
// CHECK-NOT: error:
4+
// RUN: %target-typecheck-verify-swift -verify-generic-signatures Swift

0 commit comments

Comments
 (0)