Skip to content

Commit 3dbfa97

Browse files
committed
[Associated type inference] Check solutions against full requirement signature
When we have a potential assignment of associated types to type witnesses during associated type inference, check that set of type witnesses against the requirements in the requirement signature, so that we can reject any solutions that fail some of the protocol's requirements. This is most of rdar://problem/31830524 --- but gets thwarted by the inability of associated type inference to work across multiple protocols.
1 parent dbb973a commit 3dbfa97

File tree

5 files changed

+97
-15
lines changed

5 files changed

+97
-15
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4012,6 +4012,7 @@ void ConformanceChecker::resolveTypeWitnesses() {
40124012
typedef decltype(typeWitnesses)::ScopeTy TypeWitnessesScope;
40134013
unsigned numTypeWitnesses = 0;
40144014
SmallVector<InferredTypeWitnessesSolution, 4> solutions;
4015+
SmallVector<InferredTypeWitnessesSolution, 4> nonViableSolutions;
40154016

40164017
// Information to use for diagnosing failures when we don't have
40174018
// something more specific.
@@ -4194,13 +4195,44 @@ void ConformanceChecker::resolveTypeWitnesses() {
41944195
if (replaced.isNull())
41954196
return true;
41964197

4197-
if (checkTypeWitness(TC, DC, assocType, replaced))
4198-
return true;
4199-
42004198
known->first = replaced;
42014199
}
42024200
}
42034201

4202+
// Check any same-type requirements in the protocol's requirement signature.
4203+
if (Proto->isRequirementSignatureComputed()) {
4204+
SubstOptions options(None);
4205+
options.getTentativeTypeWitness =
4206+
[&](const NormalProtocolConformance *conformance,
4207+
AssociatedTypeDecl *assocType) -> Type {
4208+
if (conformance != Conformance) return Type(0);
4209+
4210+
return typeWitnesses.begin(assocType)->first;
4211+
};
4212+
4213+
auto substitutions =
4214+
SubstitutionMap::getProtocolSubstitutions(
4215+
Proto, Conformance->getType(),
4216+
ProtocolConformanceRef(Conformance));
4217+
4218+
auto requirementSig = Proto->getRequirementSignature();
4219+
auto result =
4220+
TC.checkGenericArguments(DC, SourceLoc(), SourceLoc(),
4221+
Conformance->getType(), requirementSig,
4222+
QuerySubstitutionMap{substitutions},
4223+
TypeChecker::LookUpConformance(
4224+
TC, Conformance->getDeclContext()),
4225+
nullptr, None, nullptr, options);
4226+
switch (result) {
4227+
case RequirementCheckResult::Failure:
4228+
case RequirementCheckResult::UnsatisfiedDependency:
4229+
return true;
4230+
4231+
case RequirementCheckResult::Success:
4232+
case RequirementCheckResult::SubstitutionFailure:
4233+
return false;
4234+
}
4235+
}
42044236
return false;
42054237
};
42064238

@@ -4262,9 +4294,7 @@ void ConformanceChecker::resolveTypeWitnesses() {
42624294
}
42634295

42644296
/// Check the current set of type witnesses.
4265-
if (checkCurrentTypeWitnesses()) {
4266-
return;
4267-
}
4297+
bool invalid = checkCurrentTypeWitnesses();
42684298

42694299
// Determine whether there is already a solution with the same
42704300
// bindings.
@@ -4284,8 +4314,9 @@ void ConformanceChecker::resolveTypeWitnesses() {
42844314
return;
42854315
}
42864316

4287-
solutions.push_back(InferredTypeWitnessesSolution());
4288-
auto &solution = solutions.back();
4317+
auto &solutionList = invalid ? nonViableSolutions : solutions;
4318+
solutionList.push_back(InferredTypeWitnessesSolution());
4319+
auto &solution = solutionList.back();
42894320

42904321
// Copy the type witnesses.
42914322
for (auto assocType : unresolvedAssocTypes) {
@@ -4479,6 +4510,12 @@ void ConformanceChecker::resolveTypeWitnesses() {
44794510
}
44804511
}
44814512

4513+
// If we have no solution, but we did find something that is nonviable,
4514+
// use the first nonviable one to improve error reporting.
4515+
if (solutions.empty() && !nonViableSolutions.empty()) {
4516+
solutions.push_back(std::move(nonViableSolutions.front()));
4517+
}
4518+
44824519
// If we found a single solution, take it.
44834520
if (solutions.size() == 1) {
44844521
// Record each of the deduced witnesses.

test/Constraints/associated_types.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ protocol XReqt {}
6262
protocol YReqt {}
6363

6464
protocol SameTypedDefaultWithReqts {
65-
associatedtype X: XReqt // expected-note{{}}
66-
associatedtype Y: YReqt // expected-note{{}}
65+
associatedtype X: XReqt
66+
associatedtype Y: YReqt
6767
static var x: X { get }
6868
static var y: Y { get }
6969
}
@@ -86,7 +86,7 @@ struct UsesSameTypedDefaultWithoutSatisfyingReqts: SameTypedDefaultWithReqts {
8686
}
8787

8888
protocol SameTypedDefaultBaseWithReqts {
89-
associatedtype X: XReqt // expected-note{{}}
89+
associatedtype X: XReqt
9090
static var x: X { get }
9191
}
9292
protocol SameTypedDefaultDerivedWithReqts: SameTypedDefaultBaseWithReqts {

test/Generics/associated_type_where_clause.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,48 @@ protocol P4 {
157157
protocol P5: P3, P4 {
158158
associatedtype B where B == A?
159159
}
160+
161+
// Associated type inference should account for where clauses.
162+
protocol P6 {
163+
associatedtype A
164+
}
165+
166+
struct X1 { }
167+
168+
struct X2 { }
169+
170+
struct Y1 : P6 {
171+
typealias A = X1
172+
}
173+
174+
struct Y2 : P6 {
175+
typealias A = X2
176+
}
177+
178+
protocol P7 {
179+
associatedtype B: P6 // expected-note{{ambiguous inference of associated type 'B': 'Y1' vs. 'Y2'}}
180+
associatedtype C: P6 where B.A == C.A
181+
182+
func getB() -> B
183+
func getC() -> C
184+
}
185+
186+
struct Z1 : P7 {
187+
func getB() -> Y1 { return Y1() }
188+
func getB() -> Y2 { return Y2() }
189+
190+
func getC() -> Y1 { return Y1() }
191+
}
192+
193+
func testZ1(z1: Z1) {
194+
let _: Z1.C = Y1()
195+
}
196+
197+
198+
struct Z2 : P7 { // expected-error{{type 'Z2' does not conform to protocol 'P7'}}
199+
func getB() -> Y1 { return Y1() } // expected-note{{matching requirement 'getB()' to this declaration inferred associated type to 'Y1'}}
200+
func getB() -> Y2 { return Y2() } // expected-note{{matching requirement 'getB()' to this declaration inferred associated type to 'Y2'}}
201+
202+
func getC() -> Y1 { return Y1() }
203+
func getC() -> Y2 { return Y2() }
204+
}

test/decl/protocol/conforms/failure.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,17 @@ struct P5Conformer : P5 { // expected-error {{does not conform}}
7575

7676

7777
protocol P6Base {
78-
associatedtype Foo // expected-note {{protocol requires nested type 'Foo'; do you want to add it?}}
78+
associatedtype Foo
7979
func foo()
80-
func bar() -> Foo
80+
func bar() -> Foo // expected-note{{protocol requires function 'bar()' with type '() -> P6Conformer.Bar?'; do you want to add a stub?}}
8181
}
8282
extension P6Base {
8383
}
8484
protocol P6 : P6Base {
8585
associatedtype Bar // expected-note {{protocol requires nested type 'Bar'}}
8686
}
8787
extension P6 {
88-
func bar() -> Bar? { return nil }
88+
func bar() -> Bar? { return nil } // expected-note{{candidate has non-matching type '<Self> () -> Self.Bar?' [with Foo = P6Conformer.Bar?]}}
8989
}
9090

9191
struct P6Conformer : P6 { // expected-error 2 {{does not conform}}

validation-test/compiler_crashers/28737-genericenv-nullptr-too-much-circularity.swift renamed to validation-test/compiler_crashers_fixed/28737-genericenv-nullptr-too-much-circularity.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
77

88
// REQUIRES: asserts
9-
// RUN: not --crash %target-swift-frontend %s -emit-ir
9+
// RUN: not %target-swift-frontend %s -emit-ir
1010
protocol A:a{typealias e}class a:A{typealias e:S

0 commit comments

Comments
 (0)