Skip to content

Commit f5cf8cd

Browse files
committed
AssociatedTypeInference: Allow inference in compatible constrained extensions
1 parent 82d73b5 commit f5cf8cd

File tree

2 files changed

+63
-31
lines changed

2 files changed

+63
-31
lines changed

lib/Sema/TypeCheckProtocolInference.cpp

Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -171,35 +171,39 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitnesses(
171171

172172
InferredAssociatedTypesByWitnesses result;
173173

174-
auto isExtensionUsableForInference = [&](ExtensionDecl *extension) -> bool {
175-
176-
// The extension where the conformance being checked is declared.
177-
auto conformanceExtension = checker.Conformance->
178-
getDeclContext()->getAsDecl();
179-
if (extension == conformanceExtension)
174+
auto isExtensionUsableForInference = [&](const ExtensionDecl *extension) {
175+
// The context the conformance being checked is declared on.
176+
const auto conformanceCtx = checker.Conformance->getDeclContext();
177+
if (extension == conformanceCtx)
180178
return true;
181179

182-
auto *extendedNominal = extension->getExtendedNominal();
183-
184180
// Invalid case.
181+
const auto extendedNominal = extension->getExtendedNominal();
185182
if (extendedNominal == nullptr)
186183
return true;
187-
188-
// Assume unconstrained concrete extensions we found witnesses in are
189-
// always viable.
190-
if (!isa<ProtocolDecl>(extendedNominal))
191-
return !extension->isConstrainedExtension();
192-
184+
193185
// FIXME: The extension may not have a generic signature set up yet as
194186
// resolving signatures may trigger associated type inference. This cycle
195187
// is now detectable and we should look into untangling it
196188
// - see rdar://55263708
197189
if (!extension->hasComputedGenericSignature())
198190
return true;
199191

200-
// Build a generic signature.
201-
auto extensionSig = extension->getGenericSignature();
202-
192+
// Retrieve the generic signature of the extension.
193+
const auto extensionSig = extension->getGenericSignature();
194+
195+
// If the extension is bound to the nominal the conformance is
196+
// declared on, it is viable for inference when its conditional
197+
// requirements are satisfied by those of the conformance context.
198+
if (!isa<ProtocolDecl>(extendedNominal)) {
199+
// Extensions of non-generic nominals are always viable for inference.
200+
if (!extensionSig)
201+
return true;
202+
203+
return extensionSig->requirementsNotSatisfiedBy(
204+
conformanceCtx->getGenericSignatureOfContext()).empty();
205+
}
206+
203207
// The condition here is a bit more fickle than
204208
// `isExtensionApplied`. That check would prematurely reject
205209
// extensions like `P where AssocType == T` if we're relying on a
@@ -413,9 +417,7 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitnesses(
413417
InferredAssociatedTypes result;
414418
for (auto member : proto->getMembers()) {
415419
auto req = dyn_cast<ValueDecl>(member);
416-
if (!req)
417-
continue;
418-
if (!req->isProtocolRequirement())
420+
if (!req || !req->isProtocolRequirement())
419421
continue;
420422

421423
// Infer type witnesses for associated types.
@@ -449,19 +451,14 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitnesses(
449451

450452
// Check whether any of the associated types we care about are
451453
// referenced in this value requirement.
452-
bool anyAssocTypeMatches = false;
453-
for (auto assocType : checker.getReferencedAssociatedTypes(req)) {
454-
if (assocTypes.count(assocType) > 0) {
455-
anyAssocTypeMatches = true;
456-
break;
457-
}
454+
{
455+
const auto referenced = checker.getReferencedAssociatedTypes(req);
456+
if (llvm::find_if(referenced, [&](AssociatedTypeDecl *const assocType) {
457+
return assocTypes.count(assocType);
458+
}) == referenced.end())
459+
continue;
458460
}
459461

460-
// We cannot deduce anything from the witnesses of this
461-
// requirement; skip it.
462-
if (!anyAssocTypeMatches)
463-
continue;
464-
465462
// Infer associated types from the potential value witnesses for
466463
// this requirement.
467464
auto reqInferred =

test/decl/protocol/req/associated_type_inference.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,3 +527,38 @@ extension S30 {
527527
T.bar()
528528
}
529529
}
530+
531+
protocol P32 {
532+
associatedtype A
533+
associatedtype B
534+
associatedtype C
535+
536+
func foo(arg: A) -> C
537+
var bar: B { get }
538+
}
539+
protocol P33 {
540+
associatedtype A
541+
542+
var baz: A { get } // expected-note {{protocol requires property 'baz' with type 'S31<T>.A' (aka 'Never'); do you want to add a stub?}}
543+
}
544+
protocol P34 {
545+
associatedtype A
546+
547+
func boo() -> A // expected-note {{protocol requires function 'boo()' with type '() -> S31<T>.A' (aka '() -> Never'); do you want to add a stub?}}
548+
}
549+
struct S31<T> {}
550+
extension S31: P32 where T == Int {} // OK
551+
extension S31 where T == Int {
552+
func foo(arg: Never) {}
553+
}
554+
extension S31 where T: Equatable {
555+
var bar: Bool { true }
556+
}
557+
extension S31: P33 where T == Never {} // expected-error {{type 'S31<T>' does not conform to protocol 'P33'}}
558+
extension S31 where T == String {
559+
var baz: Bool { true } // expected-note {{candidate has non-matching type 'Bool' [with A = S31<T>.A]}}
560+
}
561+
extension S31: P34 {} // expected-error {{type 'S31<T>' does not conform to protocol 'P34'}}
562+
extension S31 where T: P32 {
563+
func boo() -> Void {} // expected-note {{candidate has non-matching type '<T> () -> Void' [with A = S31<T>.A]}}
564+
}

0 commit comments

Comments
 (0)