Skip to content

Commit 789f90d

Browse files
committed
TypeWitnessSystem: Handle ambiguities
Since we collect same-type constraints by scanning requirement signatures, the type witness system must be prerared to face conflicting solutions for a particular type witness
1 parent 20e3464 commit 789f90d

File tree

3 files changed

+277
-40
lines changed

3 files changed

+277
-40
lines changed

lib/Sema/TypeCheckProtocol.h

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,8 +1039,23 @@ class TypeWitnessSystem final {
10391039
void mergeEquivalenceClasses(EquivalenceClass *equivClass1,
10401040
const EquivalenceClass *equivClass2);
10411041

1042-
/// Determine whether \p ty1 is a better resolved type than \p ty2.
1043-
static bool isBetterResolvedType(Type ty1, Type ty2);
1042+
/// The result of comparing two resolved types targeting a single equivalence
1043+
/// class, in terms of their relative impact on solving the system.
1044+
enum class ResolvedTypeComparisonResult {
1045+
/// The first resolved type is a better choice than the second one.
1046+
Better,
1047+
1048+
/// The first resolved type is an equivalent or worse choice than the
1049+
/// second one.
1050+
EquivalentOrWorse,
1051+
1052+
/// Both resolved types are concrete and mutually exclusive.
1053+
Ambiguity
1054+
};
1055+
1056+
/// Compare the given resolved types as targeting a single equivalence class,
1057+
/// in terms of the their relative impact on solving the system.
1058+
static ResolvedTypeComparisonResult compareResolvedTypes(Type ty1, Type ty2);
10441059
};
10451060

10461061
/// Captures the state needed to infer associated types.

lib/Sema/TypeCheckProtocolInference.cpp

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2222,14 +2222,28 @@ void TypeWitnessSystem::addTypeWitness(Identifier name, Type type) {
22222222

22232223
auto &tyWitness = this->TypeWitnesses[name];
22242224

2225-
// Assume that the type resolves the type witness.
2226-
//
2227-
// If we already have a resolved type, keep going only if the new one is
2228-
// better.
2229-
if (tyWitness.EquivClass && tyWitness.EquivClass->getResolvedType()) {
2230-
if (!isBetterResolvedType(type, tyWitness.EquivClass->getResolvedType())) {
2225+
// Assume that the type resolves the equivalence class.
2226+
if (tyWitness.EquivClass) {
2227+
// Nothing else to do if the equivalence class had been marked as ambiguous.
2228+
if (tyWitness.EquivClass->isAmbiguous()) {
22312229
return;
22322230
}
2231+
2232+
// If we already have a resolved type, keep going only if the new one is
2233+
// a better choice.
2234+
const Type currResolvedTy = tyWitness.EquivClass->getResolvedType();
2235+
if (currResolvedTy) {
2236+
switch (compareResolvedTypes(type, currResolvedTy)) {
2237+
case ResolvedTypeComparisonResult::Better:
2238+
break;
2239+
case ResolvedTypeComparisonResult::EquivalentOrWorse:
2240+
return;
2241+
case ResolvedTypeComparisonResult::Ambiguity:
2242+
// Mark the equivalence class as ambiguous and give up.
2243+
tyWitness.EquivClass->setAmbiguous();
2244+
return;
2245+
}
2246+
}
22332247
}
22342248

22352249
// If we can find an existing equivalence class for this type, use it.
@@ -2374,14 +2388,27 @@ void TypeWitnessSystem::mergeEquivalenceClasses(
23742388
return;
23752389
}
23762390

2377-
// Merge the second resolved type into the first.
2391+
// Merge the second equivalence class into the first.
23782392
if (equivClass1->getResolvedType() && equivClass2->getResolvedType()) {
2379-
if (isBetterResolvedType(equivClass2->getResolvedType(),
2380-
equivClass1->getResolvedType())) {
2393+
switch (compareResolvedTypes(equivClass2->getResolvedType(),
2394+
equivClass1->getResolvedType())) {
2395+
case ResolvedTypeComparisonResult::Better:
23812396
equivClass1->setResolvedType(equivClass2->getResolvedType());
2397+
break;
2398+
case ResolvedTypeComparisonResult::EquivalentOrWorse:
2399+
break;
2400+
case ResolvedTypeComparisonResult::Ambiguity:
2401+
equivClass1->setAmbiguous();
2402+
break;
23822403
}
2404+
} else if (equivClass1->isAmbiguous()) {
2405+
// Ambiguity is retained.
23832406
} else if (equivClass2->getResolvedType()) {
2407+
// Carry over the resolved type.
23842408
equivClass1->setResolvedType(equivClass2->getResolvedType());
2409+
} else if (equivClass2->isAmbiguous()) {
2410+
// Carry over ambiguity.
2411+
equivClass1->setAmbiguous();
23852412
}
23862413

23872414
// Migrate members of the second equivalence class to the first.
@@ -2396,13 +2423,22 @@ void TypeWitnessSystem::mergeEquivalenceClasses(
23962423
delete equivClass2;
23972424
}
23982425

2399-
bool TypeWitnessSystem::isBetterResolvedType(Type ty1, Type ty2) {
2426+
TypeWitnessSystem::ResolvedTypeComparisonResult
2427+
TypeWitnessSystem::compareResolvedTypes(Type ty1, Type ty2) {
24002428
assert(ty1 && ty2);
2401-
assert(
2402-
(ty1->isTypeParameter() || ty2->isTypeParameter() || ty1->isEqual(ty2)) &&
2403-
"Ambigious concrete resolved type");
2429+
if (!ty1->isTypeParameter()) {
2430+
if (ty2->isTypeParameter()) {
2431+
// A concrete type is better than a type parameter.
2432+
return ResolvedTypeComparisonResult::Better;
2433+
} else if (!ty1->isEqual(ty2)) {
2434+
return ResolvedTypeComparisonResult::Ambiguity;
2435+
}
2436+
}
24042437

2405-
return !ty1->isTypeParameter() && ty2->isTypeParameter();
2438+
// Anything else is either equivalent (i.e. actually equal concrete types or
2439+
// type parameter vs. type parameter), or worse (i.e. type parameter vs.
2440+
// concrete type).
2441+
return ResolvedTypeComparisonResult::EquivalentOrWorse;
24062442
}
24072443

24082444
void ConformanceChecker::resolveTypeWitnesses() {

test/decl/protocol/req/associated_type_inference_fixed_type.swift

Lines changed: 210 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,20 @@ protocol P7b: P7a where A == Bool {}
8181
// CHECK-NEXT: }
8282
struct S7: P7b {}
8383

84-
// FIXME: Handle ambiguities.
85-
//protocol P8a where A == Never {
86-
// associatedtype A
87-
//}
88-
//protocol P8b where A == Bool {
89-
// associatedtype A
90-
//}
91-
//struct S8: P8a, P8b {}
84+
protocol P8a where A == Never {
85+
associatedtype A // expected-note {{protocol requires nested type 'A'; do you want to add it?}}
86+
}
87+
protocol P8b where A == Bool {
88+
associatedtype A
89+
}
90+
do {
91+
// CHECK-LABEL: Abstract type witness system for conformance of Conformer to P8a: {
92+
// CHECK-NEXT: A => (ambiguous),
93+
// CHECK-NEXT: }
94+
struct Conformer: P8a, P8b {}
95+
// expected-error@-1 {{type 'Conformer' does not conform to protocol 'P8a'}}
96+
// expected-error@-2 {{type 'Conformer' does not conform to protocol 'P8b'}}
97+
}
9298

9399
protocol P9a where A == Never {
94100
associatedtype A
@@ -427,19 +433,199 @@ do {
427433
struct Conformer2<B: Sequence>: P27c_2 where B.Element == Int {}
428434
}
429435

430-
// FIXME: Handle ambiguities.
431-
//protocol P28a where A == Int {
432-
// associatedtype A
433-
//}
434-
//protocol P28b where A == Bool {
435-
// associatedtype A
436-
//}
437-
//protocol P28c where A == Never {
438-
// associatedtype A
439-
//}
440-
//protocol Q28a: P28a, P28b {}
441-
//protocol Q28b: P28a, P28b, P28c {}
442-
//do {
443-
// struct Conformer1: Q28a {}
444-
// struct Conformer2: Q28b {}
445-
//}
436+
protocol P28a where A == Int {
437+
associatedtype A // expected-note 2 {{protocol requires nested type 'A'; do you want to add it?}}
438+
}
439+
protocol P28b where A == Bool {
440+
associatedtype A
441+
}
442+
protocol P28c where A == Never {
443+
associatedtype A
444+
}
445+
protocol Q28a: P28a, P28b {}
446+
// expected-error@-1 {{'Self.A' cannot be equal to both 'Bool' and 'Int'}}
447+
// expected-note@-2 {{same-type constraint 'Self.A' == 'Int' implied here}}
448+
protocol Q28b: P28a, P28b, P28c {}
449+
// expected-error@-1 {{'Self.A' cannot be equal to both 'Bool' and 'Int'}}
450+
// expected-error@-2 {{'Self.A' cannot be equal to both 'Never' and 'Int'}}
451+
// expected-note@-3 {{same-type constraint 'Self.A' == 'Int' implied here}}
452+
// expected-note@-4 {{same-type constraint 'Self.A' == 'Int' implied here}}
453+
do {
454+
// CHECK-LABEL: Abstract type witness system for conformance of Conformer1 to P28a: {
455+
// CHECK-NEXT: A => (ambiguous),
456+
// CHECK-NEXT: }
457+
struct Conformer1: Q28a {}
458+
// expected-error@-1 {{type 'Conformer1' does not conform to protocol 'P28a'}}
459+
// expected-error@-2 {{type 'Conformer1' does not conform to protocol 'P28b'}}
460+
461+
// CHECK-LABEL: Abstract type witness system for conformance of Conformer2 to P28a: {
462+
// CHECK-NEXT: A => (ambiguous),
463+
// CHECK-NEXT: }
464+
struct Conformer2: Q28b {}
465+
// expected-error@-1 {{type 'Conformer2' does not conform to protocol 'P28a'}}
466+
// expected-error@-2 {{type 'Conformer2' does not conform to protocol 'P28b'}}
467+
// expected-error@-3 {{type 'Conformer2' does not conform to protocol 'P28c'}}
468+
}
469+
470+
protocol P29a where A == Int {
471+
associatedtype A // expected-note {{protocol requires nested type 'A'; do you want to add it?}}
472+
associatedtype B // expected-note {{protocol requires nested type 'B'; do you want to add it?}}
473+
}
474+
protocol P29b where B == Never {
475+
associatedtype B
476+
}
477+
protocol P29c where A == B {
478+
associatedtype A // expected-note {{protocol requires nested type 'A'; do you want to add it?}}
479+
associatedtype B // expected-note {{protocol requires nested type 'B'; do you want to add it?}}
480+
}
481+
protocol Q29a: P29a, P29b, P29c {}
482+
// expected-error@-1 {{'Self.B' cannot be equal to both 'Never' and 'Int'}}
483+
// expected-note@-2 {{same-type constraint 'Self.A' == 'Int' implied here}}
484+
protocol Q29b: P29c, P29a, P29b {}
485+
// expected-error@-1 {{'Self.B' cannot be equal to both 'Never' and 'Int'}}
486+
// expected-note@-2 {{same-type constraint 'Self.A' == 'Int' implied here}}
487+
do {
488+
// CHECK-LABEL: Abstract type witness system for conformance of Conformer1 to P29a: {
489+
// CHECK-NEXT: A => (ambiguous), [[EQUIV_CLASS:0x[0-9a-f]+]]
490+
// CHECK-NEXT: B => (ambiguous), [[EQUIV_CLASS]]
491+
// CHECK-NEXT: }
492+
struct Conformer1: Q29a {}
493+
// expected-error@-1 {{type 'Conformer1' does not conform to protocol 'P29a'}}
494+
// expected-error@-2 {{type 'Conformer1' does not conform to protocol 'P29b'}}
495+
// expected-error@-3 {{type 'Conformer1' does not conform to protocol 'P29c'}}
496+
497+
// CHECK-LABEL: Abstract type witness system for conformance of Conformer2 to P29c: {
498+
// CHECK-NEXT: A => (ambiguous), [[EQUIV_CLASS:0x[0-9a-f]+]]
499+
// CHECK-NEXT: B => (ambiguous), [[EQUIV_CLASS]]
500+
// CHECK-NEXT: }
501+
struct Conformer2: Q29b {}
502+
// expected-error@-1 {{type 'Conformer2' does not conform to protocol 'P29a'}}
503+
// expected-error@-2 {{type 'Conformer2' does not conform to protocol 'P29b'}}
504+
// expected-error@-3 {{type 'Conformer2' does not conform to protocol 'P29c'}}
505+
}
506+
507+
protocol P30a where A == Int {
508+
associatedtype A
509+
}
510+
protocol P30b where A == Never {
511+
associatedtype A
512+
}
513+
protocol P30c where A == B {
514+
associatedtype A // expected-note {{protocol requires nested type 'A'; do you want to add it?}}
515+
associatedtype B // expected-note {{protocol requires nested type 'B'; do you want to add it?}}
516+
}
517+
protocol Q30: P30c, P30a, P30b {}
518+
// expected-error@-1 {{'Self.A' cannot be equal to both 'Never' and 'Int'}}
519+
// expected-note@-2 {{same-type constraint 'Self.A' == 'Int' implied here}}
520+
do {
521+
// CHECK-LABEL: Abstract type witness system for conformance of Conformer to P30c: {
522+
// CHECK-NEXT: A => (ambiguous), [[EQUIV_CLASS:0x[0-9a-f]+]]
523+
// CHECK-NEXT: B => (ambiguous), [[EQUIV_CLASS]]
524+
// CHECK-NEXT: }
525+
struct Conformer: Q30 {}
526+
// expected-error@-1 {{type 'Conformer' does not conform to protocol 'P30a'}}
527+
// expected-error@-2 {{type 'Conformer' does not conform to protocol 'P30b'}}
528+
// expected-error@-3 {{type 'Conformer' does not conform to protocol 'P30c'}}
529+
}
530+
531+
protocol P31a where B == Int {
532+
associatedtype B
533+
}
534+
protocol P31b where B == Never {
535+
associatedtype B
536+
}
537+
protocol P31c where B == A {
538+
associatedtype A // expected-note {{protocol requires nested type 'A'; do you want to add it?}}
539+
associatedtype B // expected-note {{protocol requires nested type 'B'; do you want to add it?}}
540+
}
541+
protocol Q31: P31c, P31a, P31b {}
542+
// expected-error@-1 {{'Self.B' cannot be equal to both 'Never' and 'Int'}}
543+
// expected-note@-2 {{same-type constraint 'Self.B' == 'Int' implied here}}
544+
do {
545+
// CHECK-LABEL: Abstract type witness system for conformance of Conformer to P31c: {
546+
// CHECK-NEXT: A => (ambiguous), [[EQUIV_CLASS:0x[0-9a-f]+]]
547+
// CHECK-NEXT: B => (ambiguous), [[EQUIV_CLASS]]
548+
// CHECK-NEXT: }
549+
struct Conformer: Q31 {}
550+
// expected-error@-1 {{type 'Conformer' does not conform to protocol 'P31a'}}
551+
// expected-error@-2 {{type 'Conformer' does not conform to protocol 'P31b'}}
552+
// expected-error@-3 {{type 'Conformer' does not conform to protocol 'P31c'}}
553+
}
554+
555+
protocol P32a where A == Int {
556+
associatedtype A
557+
}
558+
protocol P32b where A == Bool {
559+
associatedtype A
560+
}
561+
protocol P32c where B == Void {
562+
associatedtype B
563+
}
564+
protocol P32d where B == Never {
565+
associatedtype B
566+
}
567+
protocol P32e where A == B {
568+
associatedtype A // expected-note {{protocol requires nested type 'A'; do you want to add it?}}
569+
associatedtype B // expected-note {{protocol requires nested type 'B'; do you want to add it?}}
570+
}
571+
protocol Q32: P32e, P32a, P32b, P32c, P32d {}
572+
// expected-error@-1 {{'Self.B' cannot be equal to both 'Never' and 'Int'}}
573+
// expected-error@-2 {{'Self.B' cannot be equal to both 'Void' and 'Int'}}
574+
// expected-error@-3 {{'Self.A' cannot be equal to both 'Bool' and 'Int'}}
575+
// expected-note@-4 3 {{same-type constraint 'Self.A' == 'Int' implied here}}
576+
do {
577+
// CHECK-LABEL: Abstract type witness system for conformance of Conformer to P32e: {
578+
// CHECK-NEXT: A => (ambiguous), [[EQUIV_CLASS:0x[0-9a-f]+]]
579+
// CHECK-NEXT: B => (ambiguous), [[EQUIV_CLASS]]
580+
// CHECK-NEXT: }
581+
struct Conformer: Q32 {}
582+
// expected-error@-1 {{type 'Conformer' does not conform to protocol 'P32a'}}
583+
// expected-error@-2 {{type 'Conformer' does not conform to protocol 'P32b'}}
584+
// expected-error@-3 {{type 'Conformer' does not conform to protocol 'P32c'}}
585+
// expected-error@-4 {{type 'Conformer' does not conform to protocol 'P32d'}}
586+
// expected-error@-5 {{type 'Conformer' does not conform to protocol 'P32e'}}
587+
}
588+
589+
protocol P33a where A == Int {
590+
associatedtype A
591+
}
592+
protocol P33b where A == Int {
593+
associatedtype A
594+
}
595+
protocol Q33: P33a, P33b {}
596+
do {
597+
// CHECK-LABEL: Abstract type witness system for conformance of Conformer to P33a: {
598+
// CHECK-NEXT: A => Int,
599+
// CHECK-NEXT: }
600+
struct Conformer: Q33 {}
601+
}
602+
603+
protocol P34a {
604+
associatedtype A = Void
605+
}
606+
protocol P34b {
607+
associatedtype A = Never
608+
}
609+
protocol Q34a: P34a, P34b {}
610+
protocol Q34b: P34b, P34a {}
611+
protocol Q34c: P34a, P34b {
612+
associatedtype A // expected-note {{protocol requires nested type 'A'; do you want to add it?}}
613+
}
614+
do {
615+
// FIXME: should really be ambiguous (source-breaking)?
616+
// CHECK-LABEL: Abstract type witness system for conformance of Conformer1 to P34a: {
617+
// CHECK-NEXT: A => Void,
618+
// CHECK-NEXT: }
619+
struct Conformer1: Q34a {}
620+
621+
// FIXME: should really be ambiguous (source-breaking)?
622+
// CHECK-LABEL: Abstract type witness system for conformance of Conformer2 to P34b: {
623+
// CHECK-NEXT: A => Never,
624+
// CHECK-NEXT: }
625+
struct Conformer2: Q34b {}
626+
627+
// CHECK-LABEL: Abstract type witness system for conformance of Conformer3 to Q34c: {
628+
// CHECK-NEXT: A => (unresolved){{$}}
629+
// CHECK-NEXT: }
630+
struct Conformer3: Q34c {} // expected-error {{type 'Conformer3' does not conform to protocol 'Q34c'}}
631+
}

0 commit comments

Comments
 (0)