Skip to content

Commit 05fca99

Browse files
Merge pull request #59357 from AnthonyLatsis/cond-conf-overridden-assoc
ConformanceChecker: Don't record a type witness for an overridden associated type if...
2 parents 8b66627 + 85bb2e8 commit 05fca99

File tree

3 files changed

+196
-1
lines changed

3 files changed

+196
-1
lines changed

Diff for: lib/Sema/TypeCheckProtocol.cpp

+31-1
Original file line numberDiff line numberDiff line change
@@ -3449,8 +3449,38 @@ void ConformanceChecker::recordTypeWitness(AssociatedTypeDecl *assocType,
34493449
!overriddenConformance.isConcrete())
34503450
continue;
34513451

3452-
auto overriddenRootConformance =
3452+
auto *overriddenRootConformance =
34533453
overriddenConformance.getConcrete()->getRootNormalConformance();
3454+
auto *overriddenRootConformanceDC =
3455+
overriddenRootConformance->getDeclContext();
3456+
3457+
// Don't record a type witness for an overridden associated type if the
3458+
// conformance to the corresponding inherited protocol
3459+
// - originates in a superclass
3460+
// - originates in a different module
3461+
// - and the current conformance have mismatching conditional requirements
3462+
// This can turn out badly in two ways:
3463+
// - Foremost, we must not *alter* conformances originating in superclasses
3464+
// or other modules. In other cases, we may hit an assertion in an attempt
3465+
// to overwrite an already recorded type witness with a different one.
3466+
// For example, the recorded type witness may be invalid, whereas the
3467+
// other one---valid, and vice versa.
3468+
// - If the current conformance is more restrictive, this type witness may
3469+
// not be a viable candidate for the overridden associated type.
3470+
if (overriddenRootConformanceDC->getSelfNominalTypeDecl() !=
3471+
DC->getSelfNominalTypeDecl())
3472+
continue;
3473+
3474+
if (overriddenRootConformanceDC->getParentModule() != DC->getParentModule())
3475+
continue;
3476+
3477+
auto currConformanceSig = DC->getGenericSignatureOfContext();
3478+
auto overriddenConformanceSig =
3479+
overriddenRootConformanceDC->getGenericSignatureOfContext();
3480+
if (currConformanceSig.getCanonicalSignature() !=
3481+
overriddenConformanceSig.getCanonicalSignature())
3482+
continue;
3483+
34543484
ConformanceChecker(getASTContext(), overriddenRootConformance,
34553485
GlobalMissingWitnesses)
34563486
.recordTypeWitness(overridden, type, typeDecl);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
func assertTypeWitnessForP1_A<T: P1, U>(in: T.Type, is: U.Type) where T.A == U {}
4+
func assertTypeWitnessForP2_A<T: P2, U>(in: T.Type, is: U.Type) where T.A == U {}
5+
6+
protocol P1 {
7+
associatedtype A
8+
// expected-note@-1 2 {{protocol requires nested type 'A'; do you want to add it?}}
9+
// expected-note@-2 2 {{multiple matching types named 'A'}}
10+
}
11+
protocol P2: P1 {
12+
associatedtype A
13+
// expected-note@-1 2 {{protocol requires nested type 'A'; do you want to add it?}}
14+
// expected-note@-2 2 {{multiple matching types named 'A'}}
15+
}
16+
17+
// Conformance to P1 checked first and more restrictive.
18+
struct S1<T> {}
19+
extension S1: P1 where T == Never { // expected-error {{type 'S1<T>' does not conform to protocol 'P1'}}
20+
typealias A = Int // expected-note {{possibly intended match}}
21+
}
22+
extension S1: P2 {
23+
// expected-error@-1 {{type 'S1<T>' does not conform to protocol 'P2'}}
24+
// expected-error@-2 {{'P2' requires the types 'T' and 'Never' be equivalent}}
25+
// expected-note@-3 {{requirement specified as 'T' == 'Never'}}
26+
// expected-note@-4 {{requirement from conditional conformance of 'S1<T>' to 'P1'}}
27+
typealias A = Bool // expected-note {{possibly intended match}}
28+
}
29+
30+
struct S2<T> {}
31+
extension S2: P1 where T == Never {
32+
typealias A = Int
33+
}
34+
extension S2: P2 {} // expected-error {{type 'S2<T>' does not conform to protocol 'P2'}}
35+
36+
struct S3<T> {}
37+
extension S3: P1 where T == Never {}
38+
extension S3: P2 {
39+
// expected-error@-1 {{type 'S3<T>' does not conform to protocol 'P2'}}
40+
// expected-error@-2 {{'P2' requires the types 'T' and 'Never' be equivalent}}
41+
// expected-note@-3 {{requirement specified as 'T' == 'Never'}}
42+
// expected-note@-4 {{requirement from conditional conformance of 'S3<T>' to 'P1'}}
43+
typealias A = Int
44+
}
45+
46+
// Conformance to P1 checked first and less restrictive.
47+
struct S4<T> {}
48+
extension S4: P1 {
49+
typealias A = Int // expected-note {{possibly intended match}}
50+
}
51+
extension S4: P2 where T == Never { // expected-error {{type 'S4<T>' does not conform to protocol 'P2'}}
52+
typealias A = Bool // expected-note {{possibly intended match}}
53+
}
54+
55+
struct S5<T> {}
56+
extension S5: P1 {
57+
typealias A = Int
58+
}
59+
extension S5: P2 where T == Never {}
60+
61+
struct S6<T> {}
62+
extension S6: P1 {} // expected-error {{type 'S6<T>' does not conform to protocol 'P1'}}
63+
extension S6: P2 where T == Never {
64+
typealias A = Bool
65+
}
66+
67+
// Conformance to P2 checked first and more restrictive.
68+
struct S7<T> {}
69+
extension S7: P2 where T == Never { // expected-error {{type 'S7<T>' does not conform to protocol 'P2'}}
70+
typealias A = Bool // expected-note {{possibly intended match}}
71+
}
72+
extension S7: P1 {
73+
typealias A = Int // expected-note {{possibly intended match}}
74+
}
75+
76+
struct S8<T> {}
77+
extension S8: P2 where T == Never {}
78+
extension S8: P1 {
79+
typealias A = Int
80+
}
81+
82+
struct S9<T> {}
83+
extension S9: P2 where T == Never {
84+
typealias A = Bool
85+
}
86+
extension S9: P1 {} // expected-error {{type 'S9<T>' does not conform to protocol 'P1'}}
87+
88+
// Conformance to P2 checked first and less restrictive.
89+
struct S10<T> {}
90+
extension S10: P2 {
91+
// expected-error@-1 {{type 'S10<T>' does not conform to protocol 'P2'}}
92+
// expected-error@-2 {{'P2' requires the types 'T' and 'Never' be equivalent}}
93+
// expected-note@-3 {{requirement specified as 'T' == 'Never'}}
94+
// expected-note@-4 {{requirement from conditional conformance of 'S10<T>' to 'P1'}}
95+
typealias A = Bool // expected-note {{possibly intended match}}
96+
}
97+
extension S10: P1 where T == Never { // expected-error {{type 'S10<T>' does not conform to protocol 'P1'}}
98+
typealias A = Int // expected-note {{possibly intended match}}
99+
}
100+
101+
struct S11<T> {}
102+
extension S11: P2 {} // expected-error {{type 'S11<T>' does not conform to protocol 'P2'}}
103+
extension S11: P1 where T == Never {
104+
typealias A = Int
105+
}
106+
107+
struct S12<T> {}
108+
extension S12: P2 {
109+
// expected-error@-1 {{type 'S12<T>' does not conform to protocol 'P2'}}
110+
// expected-error@-2 {{'P2' requires the types 'T' and 'Never' be equivalent}}
111+
// expected-note@-3 {{requirement specified as 'T' == 'Never'}}
112+
// expected-note@-4 {{requirement from conditional conformance of 'S12<T>' to 'P1'}}
113+
typealias A = Int
114+
}
115+
extension S12: P1 where T == Never {}
116+
117+
// Inherited conformances.
118+
class Base1 {}
119+
class Derived1: Base1 {}
120+
extension Base1: P1 {
121+
typealias A = Bool
122+
}
123+
extension Derived1: P2 {
124+
typealias A = Int
125+
}
126+
assertTypeWitnessForP1_A(in: Base1.self, is: Bool.self)
127+
assertTypeWitnessForP2_A(in: Derived1.self, is: Bool.self)
128+
129+
class Base2 {}
130+
class Derived2: Base2 {}
131+
extension Derived2: P2 {
132+
typealias A = Int
133+
}
134+
extension Base2: P1 {
135+
typealias A = Bool
136+
}
137+
assertTypeWitnessForP1_A(in: Base2.self, is: Bool.self)
138+
assertTypeWitnessForP2_A(in: Derived2.self, is: Bool.self)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -D M -emit-module -module-name M -parse-as-library -o %t %s
3+
// RUN: %target-typecheck-verify-swift -I %t
4+
5+
#if M
6+
7+
public protocol P {
8+
associatedtype A
9+
}
10+
11+
public struct S: P {
12+
public typealias A = Int
13+
}
14+
15+
#else
16+
17+
import M
18+
19+
protocol Q: P {
20+
associatedtype A // expected-note {{multiple matching types named 'A'}}
21+
}
22+
23+
extension S: Q { // expected-error {{type 'S' does not conform to protocol 'Q'}}
24+
typealias A = Bool // expected-note {{possibly intended match}}
25+
}
26+
27+
#endif

0 commit comments

Comments
 (0)