Skip to content

Commit 24e4aad

Browse files
authored
Merge pull request #36460 from slavapestov/non-well-founded-conditional-requirement-inference
GSB: Narrow scope of conditional requirement inference
2 parents 97ed91b + 8e4598e commit 24e4aad

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)