Skip to content

Commit a9e7726

Browse files
Merge pull request #41146 from AnthonyLatsis/val-witness-res-untied
ConformanceChecker: Resolve value witnesses unless they depend on invalid type witnesses
2 parents c059506 + 6e8dcbe commit a9e7726

File tree

10 files changed

+207
-60
lines changed

10 files changed

+207
-60
lines changed

lib/Sema/CSDiagnostics.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3149,8 +3149,9 @@ bool ContextualFailure::tryProtocolConformanceFixIt(
31493149
ProtocolConformanceState::Incomplete, /*isUnchecked=*/false);
31503150
ConformanceChecker checker(getASTContext(), &conformance,
31513151
missingWitnesses);
3152-
checker.resolveValueWitnesses();
3152+
// Type witnesses must be resolved first.
31533153
checker.resolveTypeWitnesses();
3154+
checker.resolveValueWitnesses();
31543155
}
31553156

31563157
for (auto decl : missingWitnesses) {

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 72 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4847,12 +4847,6 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup(
48474847
void ConformanceChecker::ensureRequirementsAreSatisfied() {
48484848
Conformance->finishSignatureConformances();
48494849
auto proto = Conformance->getProtocol();
4850-
4851-
if (CheckedRequirementSignature)
4852-
return;
4853-
4854-
CheckedRequirementSignature = true;
4855-
48564850
auto &diags = proto->getASTContext().Diags;
48574851

48584852
auto DC = Conformance->getDeclContext();
@@ -4935,6 +4929,71 @@ void ConformanceChecker::ensureRequirementsAreSatisfied() {
49354929

49364930
#pragma mark Protocol conformance checking
49374931

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+
49384997
void ConformanceChecker::resolveValueWitnesses() {
49394998
for (auto member : Proto->getMembers()) {
49404999
auto requirement = dyn_cast<ValueDecl>(member);
@@ -5086,10 +5145,6 @@ void ConformanceChecker::resolveValueWitnesses() {
50865145
continue;
50875146
}
50885147

5089-
// If this is an accessor for a storage decl, ignore it.
5090-
if (isa<AccessorDecl>(requirement))
5091-
continue;
5092-
50935148
// If this requirement is part of a pair of imported async requirements,
50945149
// where one has already been witnessed, we can skip it.
50955150
//
@@ -5106,6 +5161,13 @@ void ConformanceChecker::resolveValueWitnesses() {
51065161
continue;
51075162
}
51085163

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+
51095171
// Try to resolve the witness.
51105172
switch (resolveWitnessTryingAllStrategies(requirement)) {
51115173
case ResolveWitnessResult::Success:
@@ -5193,14 +5255,6 @@ void ConformanceChecker::checkConformance(MissingWitnessDiagnosisKind Kind) {
51935255
// Diagnose missing type witnesses for now.
51945256
diagnoseMissingWitnesses(Kind);
51955257

5196-
// If we complain about any associated types, there is no point in continuing.
5197-
// FIXME: Not really true. We could check witnesses that don't involve the
5198-
// failed associated types.
5199-
if (AlreadyComplained) {
5200-
Conformance->setInvalid();
5201-
return;
5202-
}
5203-
52045258
// Diagnose missing value witnesses later.
52055259
SWIFT_DEFER { diagnoseMissingWitnesses(Kind); };
52065260

lib/Sema/TypeCheckProtocol.h

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -716,9 +716,6 @@ class ConformanceChecker : public WitnessChecker {
716716
/// Whether we've already complained about problems with this conformance.
717717
bool AlreadyComplained = false;
718718

719-
/// Whether we checked the requirement signature already.
720-
bool CheckedRequirementSignature = false;
721-
722719
/// Mapping from Objective-C methods to the set of requirements within this
723720
/// protocol that have the same selector and instance/class designation.
724721
llvm::SmallDenseMap<ObjCMethodKey, TinyPtrVector<AbstractFunctionDecl *>, 4>
@@ -791,6 +788,10 @@ class ConformanceChecker : public WitnessChecker {
791788
ResolveWitnessResult resolveTypeWitnessViaLookup(
792789
AssociatedTypeDecl *assocType);
793790

791+
/// Check whether all of the protocol's generic requirements are satisfied by
792+
/// the chosen type witnesses.
793+
void ensureRequirementsAreSatisfied();
794+
794795
/// Diagnose or defer a diagnostic, as appropriate.
795796
///
796797
/// \param requirement The requirement with which this diagnostic is
@@ -849,10 +850,6 @@ class ConformanceChecker : public WitnessChecker {
849850
/// directly as possible.
850851
void resolveSingleTypeWitness(AssociatedTypeDecl *assocType);
851852

852-
/// Check all of the protocols requirements are actually satisfied by a
853-
/// the chosen type witnesses.
854-
void ensureRequirementsAreSatisfied();
855-
856853
/// Check the entire protocol conformance, ensuring that all
857854
/// witnesses are resolved and emitting any diagnostics.
858855
void checkConformance(MissingWitnessDiagnosisKind Kind);

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'}}

0 commit comments

Comments
 (0)