Skip to content

Commit 4bb4efc

Browse files
committed
Sema: Resolve type witnesses for conditional conformances early
This allows for correctly diagnosing references to default type witnesses with unsatisfied requirements once we start realizing types in members. This change also lays the groundwork for a special case of default type witness resolution behavior when associated type collisions occur in the presence of multiple regular and conditional conformances. Previously, depending on evaluation order, a default type witness for a conditional conformance could satisfy a type requirement for a regular conformance, and the following snippet would have compiled: protocol A { associatedtype X } protocol B { associatedtype X = Bool } struct Foo<T>: A {} // error: type 'Foo<T>' does not conform to protocol 'A' extension Foo: B where T == Int {} If the conditional requirements of a conformance are satisfied by those of another conformance, default type witnesses for the former, more general conformance, can now satisfy type requirements for the latter conformance – but not vice versa – in case of associated type collisions. For example, both conformances below will associate 'X' with a single type witness synthesized for the less constrained conformance to B. struct Foo<T>: B { internal typealias X = Bool // synthesized! } extension Foo: A where T == Int {}
1 parent c19fdcf commit 4bb4efc

File tree

6 files changed

+184
-10
lines changed

6 files changed

+184
-10
lines changed

lib/AST/DiagnosticEngine.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,10 @@ static bool isInterestingTypealias(Type type) {
425425
/// Decide whether to show the desugared type or not. We filter out some
426426
/// cases to avoid too much noise.
427427
static bool shouldShowAKA(Type type, StringRef typeName) {
428+
// (aka '<<error type>>') does not contribute to a message whatsoever.
429+
if (type->is<ErrorType>())
430+
return false;
431+
428432
// Canonical types are already desugared.
429433
if (type->isCanonical())
430434
return false;

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1736,6 +1736,11 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
17361736
// Check for circular inheritance of the raw type.
17371737
(void)ED->hasCircularRawValue();
17381738

1739+
// Resolve all the type witnesses we need now to correctly diagnose
1740+
// references to default type witnesses with unsatisfied requirements
1741+
// once we start realizing types in members.
1742+
TypeChecker::resolveTypeWitnessesForConditionalConformances(ED);
1743+
17391744
for (Decl *member : ED->getMembers())
17401745
visit(member);
17411746

@@ -1775,6 +1780,11 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
17751780

17761781
checkGenericParams(SD);
17771782

1783+
// Resolve all the type witnesses we need now to correctly diagnose
1784+
// references to default type witnesses with unsatisfied requirements
1785+
// once we start realizing types in members.
1786+
TypeChecker::resolveTypeWitnessesForConditionalConformances(SD);
1787+
17781788
// Force lowering of stored properties.
17791789
(void) SD->getStoredProperties();
17801790

@@ -1905,6 +1915,11 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
19051915
// Check for circular inheritance.
19061916
(void)CD->hasCircularInheritance();
19071917

1918+
// Resolve all the type witnesses we need now to correctly diagnose
1919+
// references to default type witnesses with unsatisfied requirements
1920+
// once we start realizing types in members.
1921+
TypeChecker::resolveTypeWitnessesForConditionalConformances(CD);
1922+
19081923
// Force lowering of stored properties.
19091924
(void) CD->getStoredProperties();
19101925

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5055,6 +5055,69 @@ LookupAllConformancesInContextRequest::evaluate(
50555055
return DC->getLocalConformances(ConformanceLookupKind::All);
50565056
}
50575057

5058+
void TypeChecker::resolveTypeWitnessesForConditionalConformances(
5059+
const NominalTypeDecl *nominal) {
5060+
// Non-generic types won't have conditional conformances.
5061+
const auto genericSig = nominal->getGenericSignature();
5062+
if (!genericSig)
5063+
return;
5064+
5065+
// Collect all the normal conformances for this nominal.
5066+
SmallVector<NormalProtocolConformance *, 2> conformances;
5067+
for (const auto conf: nominal->getAllConformances())
5068+
if (const auto normal = dyn_cast<NormalProtocolConformance>(conf))
5069+
conformances.push_back(normal);
5070+
5071+
// If the conditional requirements of a conformance are satisfied by those
5072+
// of another conformance, we want default type witnesses for the former,
5073+
// more general conformance, to be able to satisfy type requirements for
5074+
// the latter conformance in case of associated type collisions – but not
5075+
// vice versa (granted that the protocols do not match, of course).
5076+
// E.g., both conformances below should associate 'A' with a single
5077+
// type witness synthesized for the less constrained conformance to P1.
5078+
//
5079+
// protocol P1 { associatedtype A = Int }
5080+
// protocol P2 { associatedtype A }
5081+
//
5082+
// struct Foo<T>: P1 { }
5083+
// extension Foo: P2 where T == String { }
5084+
//
5085+
// To achieve this behavior, conformance X, whose conditional requirements
5086+
// are satisfied by those of conformance Y, must be checked before Y.
5087+
//
5088+
// First off, move all regular conformances to the front.
5089+
const auto it = llvm::partition(conformances,
5090+
[](const NormalProtocolConformance *conf) {
5091+
const auto reqs = conf->getConditionalRequirementsIfAvailable();
5092+
return reqs && reqs->empty();
5093+
});
5094+
5095+
// If there are no conditional conformances, there's nothing to resolve.
5096+
if (it == conformances.end())
5097+
return;
5098+
5099+
// Then, sort the remaining conditional conformances from less
5100+
// to more constrained.
5101+
std::sort(it, conformances.end(),
5102+
[](const NormalProtocolConformance *conf1,
5103+
const NormalProtocolConformance *conf2) {
5104+
const auto ext1 = cast<ExtensionDecl>(conf1->getDeclContext());
5105+
const auto ext2 = cast<ExtensionDecl>(conf2->getDeclContext());
5106+
5107+
return ext1->getGenericSignature()
5108+
->requirementsNotSatisfiedBy(ext2->getGenericSignature()).empty();
5109+
});
5110+
5111+
for (const auto conf: conformances) {
5112+
llvm::SetVector<ValueDecl *> missingWitnesses;
5113+
ConformanceChecker checker(nominal->getASTContext(), conf,
5114+
missingWitnesses,
5115+
/*suppressDiagnostics*/true);
5116+
checker.resolveTypeWitnesses();
5117+
checker.diagnoseMissingWitnesses(MissingWitnessDiagnosisKind::ErrorFixIt);
5118+
}
5119+
}
5120+
50585121
void TypeChecker::checkConformancesInContext(DeclContext *dc,
50595122
IterableDeclContext *idc) {
50605123
// For anything imported from Clang, lazily check conformances.

lib/Sema/TypeCheckType.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -977,7 +977,9 @@ static void maybeDiagnoseBadConformanceRef(DeclContext *dc,
977977
Type parentTy,
978978
SourceLoc loc,
979979
TypeDecl *typeDecl) {
980-
auto protocol = dyn_cast<ProtocolDecl>(typeDecl->getDeclContext());
980+
assert(isa<AssociatedTypeDecl>(typeDecl) || isa<TypeAliasDecl>(typeDecl));
981+
982+
const auto protocol = dyn_cast<ProtocolDecl>(typeDecl->getDeclContext());
981983

982984
// If we weren't given a conformance, go look it up.
983985
ProtocolConformance *conformance = nullptr;
@@ -991,16 +993,18 @@ static void maybeDiagnoseBadConformanceRef(DeclContext *dc,
991993

992994
// If any errors have occurred, don't bother diagnosing this cross-file
993995
// issue.
994-
ASTContext &ctx = dc->getASTContext();
996+
const auto &ctx = dc->getASTContext();
995997
if (ctx.Diags.hadAnyError())
996998
return;
997999

998-
auto diagCode =
999-
(!protocol || (conformance && !conformance->getConditionalRequirementsIfAvailable()))
1000-
? diag::unsupported_recursion_in_associated_type_reference
1001-
: diag::broken_associated_type_witness;
1000+
const auto diagCode =
1001+
((!protocol && !cast<TypeAliasDecl>(typeDecl)->isImplicit()) ||
1002+
(conformance && !conformance->getConditionalRequirementsIfAvailable()))
1003+
? diag::unsupported_recursion_in_associated_type_reference
1004+
: diag::broken_associated_type_witness;
10021005

1003-
ctx.Diags.diagnose(loc, diagCode, isa<TypeAliasDecl>(typeDecl), typeDecl->getFullName(), parentTy);
1006+
ctx.Diags.diagnose(loc, diagCode, isa<TypeAliasDecl>(typeDecl),
1007+
typeDecl->getFullName(), parentTy);
10041008
}
10051009

10061010
/// Returns a valid type or ErrorType in case of an error.

lib/Sema/TypeChecker.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,9 @@ class LookUpConformance {
976976
/// Check all of the conformances in the given context.
977977
void checkConformancesInContext(DeclContext *dc, IterableDeclContext *idc);
978978

979+
void resolveTypeWitnessesForConditionalConformances(
980+
const NominalTypeDecl *nominal);
981+
979982
/// Check that the type of the given property conforms to NSCopying.
980983
ProtocolConformanceRef checkConformanceToNSCopying(VarDecl *var);
981984

test/Generics/conditional_conformances.swift

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
// RUN: %target-typecheck-verify-swift
1+
// RUN: %target-typecheck-verify-swift -print-ast | %FileCheck %s -check-prefix=DEFAULT_TYPE_WITNESS
22
// RUN: %target-typecheck-verify-swift -debug-generic-signatures > %t.dump 2>&1
33
// RUN: %FileCheck %s < %t.dump
4+
// RUN: %target-typecheck-verify-swift
45

56
protocol P1 {}
67
protocol P2 {}
@@ -208,11 +209,11 @@ func subclass_bad() {
208209
struct InheritEqual<T> {}
209210
// CHECK-LABEL: ExtensionDecl line={{.*}} base=InheritEqual
210211
// CHECK-LABEL: ExtensionDecl line={{.*}} base=InheritEqual
212+
// CHECK-LABEL: ExtensionDecl line={{.*}} base=InheritEqual
211213
// CHECK-NEXT: (normal_conformance type=InheritEqual<T> protocol=P2
212214
// CHECK-NEXT: conforms_to: T P1)
213215
extension InheritEqual: P2 where T: P1 {} // expected-note {{requirement from conditional conformance of 'InheritEqual<U>' to 'P2'}}
214216
// CHECK-LABEL: ExtensionDecl line={{.*}} base=InheritEqual
215-
// CHECK-LABEL: ExtensionDecl line={{.*}} base=InheritEqual
216217
// CHECK-NEXT: (normal_conformance type=InheritEqual<T> protocol=P5
217218
// CHECK-NEXT: (normal_conformance type=InheritEqual<T> protocol=P2
218219
// CHECK-NEXT: conforms_to: T P1)
@@ -238,11 +239,11 @@ extension InheritLess: P5 {} // expected-error{{type 'T' does not conform to pro
238239
struct InheritMore<T> {}
239240
// CHECK-LABEL: ExtensionDecl line={{.*}} base=InheritMore
240241
// CHECK-LABEL: ExtensionDecl line={{.*}} base=InheritMore
242+
// CHECK-LABEL: ExtensionDecl line={{.*}} base=InheritMore
241243
// CHECK-NEXT: (normal_conformance type=InheritMore<T> protocol=P2
242244
// CHECK-NEXT: conforms_to: T P1)
243245
extension InheritMore: P2 where T: P1 {} // expected-note {{requirement from conditional conformance of 'InheritMore<U>' to 'P2'}}
244246
// CHECK-LABEL: ExtensionDecl line={{.*}} base=InheritMore
245-
// CHECK-LABEL: ExtensionDecl line={{.*}} base=InheritMore
246247
// CHECK-NEXT: (normal_conformance type=InheritMore<T> protocol=P5
247248
// CHECK-NEXT: (normal_conformance type=InheritMore<T> protocol=P2
248249
// CHECK-NEXT: conforms_to: T P1)
@@ -429,3 +430,87 @@ func sr_10992_foo(_ fn: (SR_10992_S<String>) -> Void) {}
429430
func sr_10992_bar(_ fn: (SR_10992_P) -> Void) {
430431
sr_10992_foo(fn) // expected-error {{global function 'sr_10992_foo' requires that 'String' conform to 'SR_10992_P'}}
431432
}
433+
434+
// SR-7516
435+
436+
protocol SR_7516_P1 { associatedtype X = Void } // expected-note {{protocol requires nested type 'X'; do you want to add it?}}
437+
protocol SR_7516_P2 { associatedtype X = Bool }
438+
protocol SR_7516_P3 { associatedtype X = Void }
439+
protocol SR_7516_P4 { associatedtype X } // expected-note {{protocol requires nested type 'X'; do you want to add it?}}
440+
441+
struct SR_7516_S1<T> {
442+
class InnerBase<U: Sequence> where U.Element == X {}
443+
class InnerDerived: InnerBase<[X]> {}
444+
// expected-error@-2 {{'SR_7516_S1<T>.X' (aka '()') requires the types 'T' and 'Never' be equivalent}}
445+
// expected-error@-2 {{'SR_7516_S1<T>.X' (aka '()') requires the types 'T' and 'Never' be equivalent}}
446+
}
447+
extension SR_7516_S1: SR_7516_P1 where T == Never { // expected-note 2{{requirement specified as 'T' == 'Never' [with T = T]}}
448+
func bar(arg: X) {}
449+
// DEFAULT_TYPE_WITNESS: extension SR_7516_S1 : SR_7516_P1
450+
// DEFAULT_TYPE_WITNESS-NEXT: internal func bar(arg: X)
451+
// DEFAULT_TYPE_WITNESS-NEXT: internal typealias X = Void
452+
}
453+
454+
struct SR_7516_S2<T>: SR_7516_P1 {
455+
// DEFAULT_TYPE_WITNESS: struct SR_7516_S2<T> : SR_7516_P1
456+
// DEFAULT_TYPE_WITNESS-NEXT: internal typealias X = Void
457+
}
458+
extension SR_7516_S2: SR_7516_P2 where T == Never {}
459+
extension SR_7516_S2 {
460+
func foo(arg: X) {}
461+
}
462+
463+
struct SR_7516_S3<T>: SR_7516_P1 {
464+
let bar: X
465+
// DEFAULT_TYPE_WITNESS: struct SR_7516_S3<T> : SR_7516_P1
466+
// DEFAULT_TYPE_WITNESS-NEXT: internal let bar: X
467+
// DEFAULT_TYPE_WITNESS-NEXT: internal typealias X = Void
468+
}
469+
extension SR_7516_S3: SR_7516_P4 where T == Never {}
470+
471+
struct SR_7516_S4<T>: SR_7516_P4 { // expected-error {{type 'SR_7516_S4<T>' does not conform to protocol 'SR_7516_P4'}}
472+
let bar: X
473+
}
474+
extension SR_7516_S4: SR_7516_P1 where T == Never {} // expected-error {{type 'SR_7516_S4<T>' does not conform to protocol 'SR_7516_P1'}}
475+
476+
class SR_7516_C1<T> {
477+
var bar: X { fatalError() } // expected-error {{'SR_7516_C1<T>.X' (aka '()') requires the types 'T' and 'Never' be equivalent}}
478+
}
479+
// FIXME: Should this be an ambiguous situation where the user is
480+
// required to provide an explicit witness for X?
481+
extension SR_7516_C1: SR_7516_P1 where T == Never {
482+
func foo(arg: X) {}
483+
}
484+
extension SR_7516_C1: SR_7516_P3 where T == Never { // expected-note 2{{requirement specified as 'T' == 'Never' [with T = T]}}
485+
486+
// DEFAULT_TYPE_WITNESS: extension SR_7516_C1 : SR_7516_P3
487+
// DEFAULT_TYPE_WITNESS-NEXT: internal typealias X = Void
488+
}
489+
extension SR_7516_C1 {
490+
typealias Y = X // expected-error {{'SR_7516_C1<T>.X' (aka '()') requires the types 'T' and 'Never' be equivalent}}
491+
}
492+
493+
enum SR_7516_E1<T, U> {
494+
// FIXME: Diagnostics QoI
495+
case never(X) // expected-error {{type 'T' does not conform to protocol 'Equatable'}}
496+
}
497+
extension SR_7516_E1: SR_7516_P1 where T: Equatable {
498+
// DEFAULT_TYPE_WITNESS: extension SR_7516_E1 : SR_7516_P1
499+
// DEFAULT_TYPE_WITNESS-NEXT: internal typealias X = Void
500+
}
501+
extension SR_7516_E1: SR_7516_P4 where T: Equatable, U: Equatable {
502+
func foo(arg: X) {}
503+
}
504+
505+
struct SR_7516_S5<T> {
506+
var bar: X { fatalError() }
507+
}
508+
extension SR_7516_S5: SR_7516_P2 where T == Never {}
509+
extension SR_7516_S5: SR_7516_P3 where T == String {}
510+
extension SR_7516_S5: SR_7516_P1 {
511+
func foo(arg: X) {}
512+
// DEFAULT_TYPE_WITNESS: extension SR_7516_S5 : SR_7516_P1
513+
// DEFAULT_TYPE_WITNESS-NEXT: internal func foo(arg: X)
514+
// DEFAULT_TYPE_WITNESS-NEXT: internal typealias X = Void
515+
}
516+
extension SR_7516_S5: SR_7516_P4 where T: Equatable {}

0 commit comments

Comments
 (0)