Skip to content

Commit 6e8dcbe

Browse files
committed
ConformanceChecker: Resolve value witnesses unless they depend on invalid type witnesses
1 parent f2b3e7b commit 6e8dcbe

File tree

8 files changed

+201
-42
lines changed

8 files changed

+201
-42
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4929,6 +4929,71 @@ void ConformanceChecker::ensureRequirementsAreSatisfied() {
49294929

49304930
#pragma mark Protocol conformance checking
49314931

4932+
/// Determine whether mapping the interface type of the given protocol non-type
4933+
/// requirement into the context of the given conformance produces a well-formed
4934+
/// type.
4935+
static bool
4936+
hasInvalidTypeInConformanceContext(const ValueDecl *requirement,
4937+
NormalProtocolConformance *conformance) {
4938+
assert(!isa<TypeDecl>(requirement));
4939+
assert(requirement->getDeclContext()->getSelfProtocolDecl() ==
4940+
conformance->getProtocol());
4941+
4942+
// FIXME: getInterfaceType() on properties returns contextual types that have
4943+
// been mapped out of context, but mapTypeOutOfContext() does not reconstitute
4944+
// type parameters that were substituted with concrete types. Instead,
4945+
// patterns should be refactored to use interface types, at least if they
4946+
// appear in type contexts.
4947+
auto interfaceTy = requirement->getInterfaceType();
4948+
4949+
// Skip the curried 'self' parameter.
4950+
if (requirement->hasCurriedSelf())
4951+
interfaceTy = interfaceTy->castTo<AnyFunctionType>()->getResult();
4952+
4953+
// For subscripts, build a regular function type to skip walking generic
4954+
// requirements.
4955+
if (auto *gft = interfaceTy->getAs<GenericFunctionType>()) {
4956+
interfaceTy = FunctionType::get(gft->getParams(), gft->getResult(),
4957+
gft->getExtInfo());
4958+
}
4959+
4960+
if (!interfaceTy->hasTypeParameter())
4961+
return false;
4962+
4963+
const auto subs = SubstitutionMap::getProtocolSubstitutions(
4964+
conformance->getProtocol(),
4965+
conformance->getDeclContext()->mapTypeIntoContext(conformance->getType()),
4966+
ProtocolConformanceRef(conformance));
4967+
4968+
class Walker final : public TypeWalker {
4969+
const SubstitutionMap &Subs;
4970+
const ProtocolDecl *Proto;
4971+
4972+
public:
4973+
explicit Walker(const SubstitutionMap &Subs, const ProtocolDecl *Proto)
4974+
: Subs(Subs), Proto(Proto) {}
4975+
4976+
Action walkToTypePre(Type ty) override {
4977+
if (!ty->hasTypeParameter())
4978+
return Action::SkipChildren;
4979+
4980+
auto *const dmt = ty->getAs<DependentMemberType>();
4981+
if (!dmt)
4982+
return Action::Continue;
4983+
4984+
// We only care about 'Self'-rooted type parameters.
4985+
if (!dmt->getRootGenericParam()->isEqual(Proto->getSelfInterfaceType()))
4986+
return Action::SkipChildren;
4987+
4988+
if (ty.subst(Subs)->hasError())
4989+
return Action::Stop;
4990+
return Action::SkipChildren;
4991+
}
4992+
};
4993+
4994+
return interfaceTy.walk(Walker(subs, conformance->getProtocol()));
4995+
}
4996+
49324997
void ConformanceChecker::resolveValueWitnesses() {
49334998
for (auto member : Proto->getMembers()) {
49344999
auto requirement = dyn_cast<ValueDecl>(member);
@@ -5096,6 +5161,13 @@ void ConformanceChecker::resolveValueWitnesses() {
50965161
continue;
50975162
}
50985163

5164+
// Try substituting into the requirement's interface type. If we fail,
5165+
// either a generic requirement was not satisfied or we tripped on an
5166+
// invalid type witness, and there's no point in resolving a witness.
5167+
if (hasInvalidTypeInConformanceContext(requirement, Conformance)) {
5168+
continue;
5169+
}
5170+
50995171
// Try to resolve the witness.
51005172
switch (resolveWitnessTryingAllStrategies(requirement)) {
51015173
case ResolveWitnessResult::Success:
@@ -5183,14 +5255,6 @@ void ConformanceChecker::checkConformance(MissingWitnessDiagnosisKind Kind) {
51835255
// Diagnose missing type witnesses for now.
51845256
diagnoseMissingWitnesses(Kind);
51855257

5186-
// If we complain about any associated types, there is no point in continuing.
5187-
// FIXME: Not really true. We could check witnesses that don't involve the
5188-
// failed associated types.
5189-
if (AlreadyComplained) {
5190-
Conformance->setInvalid();
5191-
return;
5192-
}
5193-
51945258
// Diagnose missing value witnesses later.
51955259
SWIFT_DEFER { diagnoseMissingWitnesses(Kind); };
51965260

test/decl/protocol/conforms/failure.swift

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,119 @@ extension UInt32: ExpressibleByStringLiteral {}
131131
// the type actually conforming), do not crash when failing to find
132132
// the associated witness type.
133133
let diagnose: UInt32 = "reta"
134+
135+
// Test that we attempt to resolve a value witness unless mapping its interface
136+
// type into the conformance context produces an invalid type.
137+
protocol P7 {
138+
associatedtype A: Sequence // expected-note {{protocol requires nested type 'A'; do you want to add it?}}
139+
140+
subscript(subscript1 _: A) -> Never { get }
141+
subscript<T>(subscript2 _: T) -> Never where T: Sequence, T.Element == A { get }
142+
// expected-note@-1 {{protocol requires subscript with type '<T> (subscript2: T) -> Never'; do you want to add a stub?}}
143+
144+
func method1(_: A)
145+
func method2() -> A.Element
146+
func method3<T>(_: T) where T: Sequence, T.Element == A
147+
// expected-note@-1 {{protocol requires function 'method3' with type '<T> (T) -> ()'; do you want to add a stub?}}
148+
func method4(_: Never) // expected-note {{protocol requires function 'method4' with type '(Never) -> ()'; do you want to add a stub?}}
149+
}
150+
do {
151+
struct Conformer: P7 {} // expected-error {{type 'Conformer' does not conform to protocol 'P7'}}
152+
}
153+
154+
protocol P8 {
155+
associatedtype A: Sequence where A.Element == Never
156+
157+
func method1(_: A) // expected-note {{protocol requires function 'method1' with type '(Conformer.A) -> ()' (aka '(Array<Bool>) -> ()'); do you want to add a stub?}}
158+
func method2() -> A.Element // expected-note {{protocol requires function 'method2()' with type '() -> Bool'; do you want to add a stub?}}
159+
func method3<T>(_: T) where T: Sequence, T.Element == A
160+
// expected-note@-1 {{protocol requires function 'method3' with type '<T> (T) -> ()'; do you want to add a stub?}}
161+
func method4(_: Never) // expected-note {{protocol requires function 'method4' with type '(Never) -> ()'; do you want to add a stub?}}
162+
}
163+
do {
164+
struct Conformer: P8 {
165+
// expected-error@-1 {{type 'Conformer' does not conform to protocol 'P8'}}
166+
// expected-error@-2 {{'P8' requires the types 'Bool' and 'Never' be equivalent}}
167+
// expected-note@-3 {{requirement specified as 'Self.A.Element' == 'Never' [with Self = Conformer]}}
168+
typealias A = Array<Bool>
169+
}
170+
}
171+
172+
protocol P9a {
173+
associatedtype A
174+
}
175+
protocol P9b: P9a where A: Sequence {
176+
subscript(subscript1 _: A.Element) -> Never { get }
177+
subscript<T>(subscript2 _: T) -> Never where T: Sequence, T.Element == A.Element { get }
178+
// expected-note@-1 {{protocol requires subscript with type '<T> (subscript2: T) -> Never'; do you want to add a stub?}}
179+
180+
func method1(_: A) // expected-note {{protocol requires function 'method1' with type '(Conformer.A) -> ()' (aka '(Bool) -> ()'); do you want to add a stub?}}
181+
func method2() -> A.Element
182+
func method3<T>(_: T) where T: Sequence, T.Element == A
183+
// expected-note@-1 {{protocol requires function 'method3' with type '<T> (T) -> ()'; do you want to add a stub?}}
184+
func method4(_: Never) // expected-note {{protocol requires function 'method4' with type '(Never) -> ()'; do you want to add a stub?}}
185+
}
186+
do {
187+
struct Conformer: P9b {
188+
// expected-error@-1 {{type 'Conformer' does not conform to protocol 'P9b'}}
189+
// expected-error@-2 {{type 'Conformer.A' (aka 'Bool') does not conform to protocol 'Sequence'}}
190+
typealias A = Bool
191+
}
192+
}
193+
194+
protocol P10a {
195+
associatedtype A
196+
}
197+
protocol P10b: P10a where A: Sequence, A.Element == Never {
198+
func method1(_: A) // expected-note {{protocol requires function 'method1' with type '(Conformer.A) -> ()' (aka '(Array<Bool>) -> ()'); do you want to add a stub?}}
199+
func method2() -> A.Element // expected-note {{protocol requires function 'method2()' with type '() -> Bool'; do you want to add a stub?}}
200+
func method3<T>(_: T) where T: Sequence, T.Element == A
201+
// expected-note@-1 {{protocol requires function 'method3' with type '<T> (T) -> ()'; do you want to add a stub?}}
202+
func method4(_: Never) // expected-note {{protocol requires function 'method4' with type '(Never) -> ()'; do you want to add a stub?}}
203+
}
204+
do {
205+
struct Conformer: P10b {
206+
// expected-error@-1 {{type 'Conformer' does not conform to protocol 'P10b'}}
207+
// expected-error@-2 {{'P10b' requires the types 'Bool' and 'Never' be equivalent}}
208+
// expected-note@-3 {{requirement specified as 'Self.A.Element' == 'Never' [with Self = Conformer]}}
209+
typealias A = Array<Bool>
210+
}
211+
}
212+
213+
protocol P11 {
214+
associatedtype A: Equatable
215+
// FIXME: Should not resolve witness for 'method', but Type::subst doesn't care
216+
// about conditional requirements when querying type witnesses.
217+
// expected-note@+1 {{protocol requires function 'method()' with type '() -> Conformer.A' (aka '() -> Array<P11>'); do you want to add a stub?}}
218+
func method() -> A
219+
}
220+
do {
221+
struct Conformer: P11 {
222+
// expected-error@-1 {{type 'Conformer' does not conform to protocol 'P11'}}
223+
// expected-error@-2 {{type 'P11' does not conform to protocol 'Equatable'}} // FIXME: Crappy diagnostics
224+
// expected-error@-3 {{'P11' requires that 'P11' conform to 'Equatable'}}
225+
// expected-note@-4 {{requirement specified as 'P11' : 'Equatable'}}
226+
// expected-note@-5 {{requirement from conditional conformance of 'Conformer.A' (aka 'Array<P11>') to 'Equatable'}}
227+
typealias A = Array<any P11>
228+
}
229+
}
230+
231+
protocol P12 {
232+
associatedtype A: Sequence where A.Element == Never // expected-note {{protocol requires nested type 'A'; do you want to add it?}}
233+
// FIXME: Should not resolve witness for 'prop', but getInterfaceType() returns
234+
// '(Self) -> Never' instead of '(Self) -> Self.A.Element', and the invalid
235+
// type parameter is never found (see 'hasInvalidTypeInConformanceContext').
236+
// This happens because getInterfaceType() on properties returns contextual
237+
// types that have been mapped out of context, but mapTypeOutOfContext() does
238+
// not reconstitute type parameters that were substituted with concrete types.
239+
// Instead, patterns should be refactored to use interface types, at least if
240+
// they appear in type contexts.
241+
//
242+
// expected-note@+1 {{protocol requires property 'prop' with type '(Conformer) -> Never'; do you want to add a stub?}}
243+
var prop: (Self) -> A.Element { get }
244+
}
245+
do {
246+
struct Conformer: P12 { // expected-error {{type 'Conformer' does not conform to protocol 'P12'}}
247+
typealias A = Bool // expected-note {{possibly intended match 'Conformer.A' (aka 'Bool') does not conform to 'Sequence'}}
248+
}
249+
}

test/decl/protocol/conforms/fixit_stub.swift

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -162,17 +162,18 @@ protocol ProtocolChain2 : ProtocolChain1 {
162162
class Adopter15 : ProtocolChain2 {} //expected-error {{type 'Adopter15' does not conform to protocol 'ProtocolChain2'}} expected-error {{type 'Adopter15' does not conform to protocol 'ProtocolChain1'}}
163163

164164
protocol ProtocolParallel1 {
165-
associatedtype Foo1 // expected-note {{protocol requires nested type 'Foo1'; do you want to add it?}}{{57-57=\n typealias Foo1 = <#type#>\n\n typealias Foo2 = <#type#>\n\n typealias Foo3 = <#type#>\n\n typealias Bar1 = <#type#>\n\n typealias Bar2 = <#type#>\n\n typealias Bar3 = <#type#>\n}}
166-
associatedtype Foo2 // expected-note {{protocol requires nested type 'Foo2'; do you want to add it?}}{{57-57=\n typealias Foo1 = <#type#>\n\n typealias Foo2 = <#type#>\n\n typealias Foo3 = <#type#>\n\n typealias Bar1 = <#type#>\n\n typealias Bar2 = <#type#>\n\n typealias Bar3 = <#type#>\n}}
167-
associatedtype Foo3 // expected-note {{protocol requires nested type 'Foo3'; do you want to add it?}}{{57-57=\n typealias Foo1 = <#type#>\n\n typealias Foo2 = <#type#>\n\n typealias Foo3 = <#type#>\n\n typealias Bar1 = <#type#>\n\n typealias Bar2 = <#type#>\n\n typealias Bar3 = <#type#>\n}}
168-
func Foo4()
165+
associatedtype Foo1 // expected-note {{protocol requires nested type 'Foo1'; do you want to add it?}}{{57-57=\n typealias Foo1 = <#type#>\n\n typealias Foo2 = <#type#>\n\n typealias Foo3 = <#type#>\n\n func Foo4() {\n <#code#>\n \}\n\n typealias Bar1 = <#type#>\n\n typealias Bar2 = <#type#>\n\n typealias Bar3 = <#type#>\n}}
166+
associatedtype Foo2 // expected-note {{protocol requires nested type 'Foo2'; do you want to add it?}}{{57-57=\n typealias Foo1 = <#type#>\n\n typealias Foo2 = <#type#>\n\n typealias Foo3 = <#type#>\n\n func Foo4() {\n <#code#>\n \}\n\n typealias Bar1 = <#type#>\n\n typealias Bar2 = <#type#>\n\n typealias Bar3 = <#type#>\n}}
167+
associatedtype Foo3 // expected-note {{protocol requires nested type 'Foo3'; do you want to add it?}}{{57-57=\n typealias Foo1 = <#type#>\n\n typealias Foo2 = <#type#>\n\n typealias Foo3 = <#type#>\n\n func Foo4() {\n <#code#>\n \}\n\n typealias Bar1 = <#type#>\n\n typealias Bar2 = <#type#>\n\n typealias Bar3 = <#type#>\n}}
168+
// FIXME: Why do we add stubs for all missing requirements when the note implies a single one?
169+
func Foo4() // expected-note {{protocol requires function 'Foo4()' with type '() -> ()'; do you want to add a stub?}}{{57-57=\n typealias Foo1 = <#type#>\n\n typealias Foo2 = <#type#>\n\n typealias Foo3 = <#type#>\n\n func Foo4() {\n <#code#>\n \}\n\n typealias Bar1 = <#type#>\n\n typealias Bar2 = <#type#>\n\n typealias Bar3 = <#type#>\n}}
169170
}
170171

171172
protocol ProtocolParallel2 {
172-
associatedtype Bar1 // expected-note {{protocol requires nested type 'Bar1'; do you want to add it?}}{{57-57=\n typealias Foo1 = <#type#>\n\n typealias Foo2 = <#type#>\n\n typealias Foo3 = <#type#>\n\n typealias Bar1 = <#type#>\n\n typealias Bar2 = <#type#>\n\n typealias Bar3 = <#type#>\n}}
173-
associatedtype Bar2 // expected-note {{protocol requires nested type 'Bar2'; do you want to add it?}}{{57-57=\n typealias Foo1 = <#type#>\n\n typealias Foo2 = <#type#>\n\n typealias Foo3 = <#type#>\n\n typealias Bar1 = <#type#>\n\n typealias Bar2 = <#type#>\n\n typealias Bar3 = <#type#>\n}}
174-
associatedtype Bar3 // expected-note {{protocol requires nested type 'Bar3'; do you want to add it?}}{{57-57=\n typealias Foo1 = <#type#>\n\n typealias Foo2 = <#type#>\n\n typealias Foo3 = <#type#>\n\n typealias Bar1 = <#type#>\n\n typealias Bar2 = <#type#>\n\n typealias Bar3 = <#type#>\n}}
175-
func Bar4()
173+
associatedtype Bar1 // expected-note {{protocol requires nested type 'Bar1'; do you want to add it?}}{{57-57=\n typealias Foo1 = <#type#>\n\n typealias Foo2 = <#type#>\n\n typealias Foo3 = <#type#>\n\n func Foo4() {\n <#code#>\n \}\n\n typealias Bar1 = <#type#>\n\n typealias Bar2 = <#type#>\n\n typealias Bar3 = <#type#>\n}}
174+
associatedtype Bar2 // expected-note {{protocol requires nested type 'Bar2'; do you want to add it?}}{{57-57=\n typealias Foo1 = <#type#>\n\n typealias Foo2 = <#type#>\n\n typealias Foo3 = <#type#>\n\n func Foo4() {\n <#code#>\n \}\n\n typealias Bar1 = <#type#>\n\n typealias Bar2 = <#type#>\n\n typealias Bar3 = <#type#>\n}}
175+
associatedtype Bar3 // expected-note {{protocol requires nested type 'Bar3'; do you want to add it?}}{{57-57=\n typealias Foo1 = <#type#>\n\n typealias Foo2 = <#type#>\n\n typealias Foo3 = <#type#>\n\n func Foo4() {\n <#code#>\n \}\n\n typealias Bar1 = <#type#>\n\n typealias Bar2 = <#type#>\n\n typealias Bar3 = <#type#>\n}}
176+
func Bar4() // expected-note {{protocol requires function 'Bar4()' with type '() -> ()'; do you want to add a stub?}}{{57-57=\n func Bar4() {\n <#code#>\n \}\n}}
176177
}
177178

178179
class Adopter16 : ProtocolParallel1, ProtocolParallel2 {} // expected-error {{type 'Adopter16' does not conform to protocol 'ProtocolParallel1'}} expected-error {{type 'Adopter16' does not conform to protocol 'ProtocolParallel2'}}

test/decl/protocol/special/DistributedActor.swift

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,8 @@ distributed actor D2 {
2020
}
2121

2222
distributed actor D3 {
23-
// expected-error@-1{{protocol 'DistributedActor' is broken; cannot derive conformance for type 'D3'}}
24-
// expected-error@-2{{protocol 'DistributedActor' is broken; cannot derive conformance for type 'D3'}} // FIXME(distributed): duplicate errors
25-
// expected-error@-3{{type 'D3' does not conform to protocol 'Identifiable'}}
26-
// expected-error@-4{{type 'D3' does not conform to protocol 'DistributedActor'}}
27-
// expected-error@-5{{type 'D3' does not conform to protocol 'DistributedActor'}}
23+
// expected-error@-1{{type 'D3' does not conform to protocol 'Identifiable'}}
24+
// expected-error@-2{{type 'D3' does not conform to protocol 'DistributedActor'}}
2825

2926
var id: Int { 0 }
3027
// expected-error@-1{{property 'id' cannot be defined explicitly, as it conflicts with distributed actor synthesized stored property}}
@@ -36,11 +33,8 @@ struct OtherActorIdentity: Sendable, Hashable, Codable {}
3633

3734
distributed actor D4 {
3835
// expected-error@-1{{actor 'D4' has no initializers}}
39-
// expected-error@-2{{type 'D4' does not conform to protocol 'DistributedActor'}} // FIXME(distributed): duplicated errors
40-
// expected-error@-3{{type 'D4' does not conform to protocol 'DistributedActor'}}
41-
// expected-error@-4{{protocol 'DistributedActor' is broken; cannot derive conformance for type 'D4'}} // FIXME(distributed): duplicated errors
42-
// expected-error@-5{{protocol 'DistributedActor' is broken; cannot derive conformance for type 'D4'}}
43-
// expected-error@-6{{type 'D4' does not conform to protocol 'Identifiable'}}
36+
// expected-error@-2{{type 'D4' does not conform to protocol 'DistributedActor'}}
37+
// expected-error@-3{{type 'D4' does not conform to protocol 'Identifiable'}}
4438
let actorSystem: String
4539
// expected-error@-1{{invalid redeclaration of synthesized property 'actorSystem'}}
4640
// expected-error@-2{{property 'actorSystem' cannot be defined explicitly, as it conflicts with distributed actor synthesized stored property}}

test/refactoring/FillStubs/Outputs/basic/P19-8.swift.expected

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@ class C4 : P2 {}
1919
class C5 : P2 {
2020
typealias T2 = <#type#>
2121

22-
func foo1() {
23-
<#code#>
24-
}
25-
2622
typealias T1 = Int
2723
func foo1() {}
2824
}

test/refactoring/FillStubs/Outputs/basic/P27-8.swift.expected

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,6 @@ class C6 : P2 {
2727
class C7 : P2 {
2828
typealias T1 = <#type#>
2929

30-
func foo1() {
31-
<#code#>
32-
}
33-
3430
typealias T2 = Int
3531
func foo1() {}
3632
}

test/refactoring/FillStubs/Outputs/basic/P62-12.swift.expected

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,6 @@ extension C12 : P1 {
6262
extension C12 : P2 {
6363
typealias T2 = <#type#>
6464

65-
func foo1() {
66-
<#code#>
67-
}
68-
6965
typealias T1 = Int
7066
func foo1() {}
7167
}

test/refactoring/FillStubs/Outputs/basic/P71-12.swift.expected

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,6 @@ extension C13 : P1 {
7171
extension C13 : P2 {
7272
typealias T2 = <#type#>
7373

74-
func foo1() {
75-
<#code#>
76-
}
77-
7874
typealias T1 = Int
7975
func foo1() {}
8076
}

0 commit comments

Comments
 (0)