Skip to content

Commit a4230a6

Browse files
committed
[RequirementMachine] Simplify computing and emitting conflict diagnostics.
Instead of computing different combinations of conflicting requirement kinds, emit the same error message for all of them.
1 parent 31028e5 commit a4230a6

File tree

7 files changed

+79
-133
lines changed

7 files changed

+79
-133
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2509,6 +2509,9 @@ WARNING(missing_protocol_refinement, none,
25092509
ERROR(same_type_conflict,none,
25102510
"%select{generic parameter |protocol |}0%1 cannot be equal to both "
25112511
"%2 and %3", (unsigned, Type, Type, Type))
2512+
ERROR(requirement_conflict,none,
2513+
"no type for %0 can satisfy both %1",
2514+
(Type, StringRef))
25122515
WARNING(redundant_same_type_to_concrete,none,
25132516
"redundant same-type constraint %0 == %1", (Type, Type))
25142517
NOTE(same_type_redundancy_here,none,

lib/AST/RequirementMachine/Diagnostics.h

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,23 @@ struct RequirementError {
3939
RedundantRequirement,
4040
} kind;
4141

42-
/// The type parameter on which there is an invalid or conflicting
43-
/// requirement.
44-
///
45-
/// FIXME: We probably want to just store two separate requirements
46-
/// in the case of a confict. Right now, the conflicting constraint
47-
/// types are both stored in the requirement below, and this serves
48-
/// as the subject type.
49-
Type typeParameter;
50-
5142
/// The invalid requirement.
5243
Requirement requirement;
5344

45+
/// A requirement that conflicts with \c requirement. Both
46+
/// requirements will have the same subject type.
47+
Optional<Requirement> conflictingRequirement;
48+
5449
SourceLoc loc;
5550

5651
private:
5752
RequirementError(Kind kind, Requirement requirement, SourceLoc loc)
58-
: kind(kind), typeParameter(Type()), requirement(requirement), loc(loc) {}
53+
: kind(kind), requirement(requirement), conflictingRequirement(None), loc(loc) {}
5954

60-
RequirementError(Kind kind, Type subject, Requirement requirement, SourceLoc loc)
61-
: kind(kind), typeParameter(subject), requirement(requirement), loc(loc) {}
55+
RequirementError(Kind kind, Requirement requirement,
56+
Requirement conflict,
57+
SourceLoc loc)
58+
: kind(kind), requirement(requirement), conflictingRequirement(conflict), loc(loc) {}
6259

6360
public:
6461
static RequirementError forInvalidTypeRequirement(Type subjectType,
@@ -78,10 +75,10 @@ struct RequirementError {
7875
return {Kind::ConflictingRequirement, req, loc};
7976
}
8077

81-
static RequirementError forConflictingRequirement(Type typeParameter,
82-
Requirement req,
78+
static RequirementError forConflictingRequirement(Requirement first,
79+
Requirement second,
8380
SourceLoc loc) {
84-
return {Kind::ConflictingRequirement, typeParameter, req, loc};
81+
return {Kind::ConflictingRequirement, first, second, loc};
8582
}
8683

8784
static RequirementError forRedundantRequirement(Requirement req,

lib/AST/RequirementMachine/RequirementLowering.cpp

Lines changed: 18 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -723,44 +723,29 @@ bool swift::rewriting::diagnoseRequirementErrors(
723723

724724
case RequirementError::Kind::ConflictingRequirement: {
725725
auto requirement = error.requirement;
726-
auto subjectType = error.typeParameter;
727-
switch (requirement.getKind()) {
728-
case RequirementKind::SameType:
726+
auto conflict = error.conflictingRequirement;
727+
728+
if (!conflict) {
729729
if (requirement.getFirstType()->hasError() ||
730730
requirement.getSecondType()->hasError()) {
731731
// Don't emit a cascading error.
732-
} else if (subjectType) {
733-
ctx.Diags.diagnose(loc, diag::same_type_conflict,
734-
false, subjectType,
735-
requirement.getFirstType(),
736-
requirement.getSecondType());
737-
} else {
738-
ctx.Diags.diagnose(loc, diag::requires_same_concrete_type,
739-
requirement.getFirstType(),
740-
requirement.getSecondType());
732+
break;
741733
}
742-
break;
743-
case RequirementKind::Conformance:
744-
ctx.Diags.diagnose(loc, diag::requires_generic_param_same_type_does_not_conform,
734+
ctx.Diags.diagnose(loc, diag::requires_same_concrete_type,
745735
requirement.getFirstType(),
746-
requirement.getProtocolDecl()->getName());
747-
break;
748-
case RequirementKind::Superclass:
749-
if (subjectType) {
750-
ctx.Diags.diagnose(loc, diag::conflicting_superclass_constraints,
751-
subjectType, requirement.getFirstType(),
752-
requirement.getSecondType());
753-
} else {
754-
ctx.Diags.diagnose(loc, diag::same_type_does_not_inherit,
755-
requirement.getFirstType(),
756-
requirement.getSecondType());
757-
}
758-
break;
759-
case RequirementKind::Layout:
760-
ctx.Diags.diagnose(loc, diag::requires_generic_param_same_type_does_not_conform,
761-
requirement.getFirstType(),
762-
ctx.getIdentifier(requirement.getLayoutConstraint()->getName()));
763-
break;
736+
requirement.getSecondType());
737+
} else {
738+
auto options = PrintOptions::forDiagnosticArguments();
739+
std::string requirements;
740+
llvm::raw_string_ostream OS(requirements);
741+
OS << "'";
742+
requirement.print(OS, options);
743+
OS << "' and '";
744+
conflict->print(OS, options);
745+
OS << "'";
746+
747+
ctx.Diags.diagnose(loc, diag::requirement_conflict,
748+
requirement.getFirstType(), requirements);
764749
}
765750

766751
diagnosedError = true;

lib/AST/RequirementMachine/RewriteSystem.cpp

Lines changed: 4 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -776,80 +776,10 @@ void RewriteSystem::computeConflictDiagnostics(
776776
continue;
777777

778778
Type subject = propertyMap.getTypeForTerm(subjectTerm, genericParams);
779-
780-
// Record conflicting requirements on a type parameter, e.g.
781-
// conflicting superclass requirements:
782-
//
783-
// class C1 {}
784-
// class C2 {}
785-
// protocol P { associatedtype A: C1 }
786-
// func conflict<T: P>(_: T) where T.A: C2 {}
787-
if (firstProperty->getKind() == secondProperty->getKind() &&
788-
firstTerm.back().getKind() != Symbol::Kind::Name) {
789-
switch (firstProperty->getKind()) {
790-
case Symbol::Kind::ConcreteType:
791-
errors.push_back(RequirementError::forConflictingRequirement(subject,
792-
{RequirementKind::SameType, firstProperty->getConcreteType(),
793-
secondProperty->getConcreteType()},
794-
signatureLoc));
795-
continue;
796-
797-
case Symbol::Kind::Superclass:
798-
// FIXME: shoving the conflicting superclass types into a superclass
799-
// requiement is a little gross.
800-
errors.push_back(RequirementError::forConflictingRequirement(subject,
801-
{RequirementKind::Superclass, firstProperty->getConcreteType(),
802-
secondProperty->getConcreteType()},
803-
signatureLoc));
804-
continue;
805-
806-
// FIXME: Conflicting layout requirements?
807-
default:
808-
continue;
809-
}
810-
}
811-
812-
auto recordError = [&](Symbol subject, Symbol constraint) {
813-
auto subjectType = subject.getConcreteType();
814-
switch (constraint.getKind()) {
815-
case Symbol::Kind::ConcreteType:
816-
errors.push_back(RequirementError::forConflictingRequirement(
817-
{RequirementKind::SameType, subjectType, constraint.getConcreteType()},
818-
signatureLoc));
819-
return;
820-
821-
case Symbol::Kind::Superclass:
822-
errors.push_back(RequirementError::forConflictingRequirement(
823-
{RequirementKind::Superclass, subjectType, constraint.getConcreteType()},
824-
signatureLoc));
825-
return;
826-
827-
case Symbol::Kind::Protocol:
828-
errors.push_back(RequirementError::forConflictingRequirement(
829-
{RequirementKind::Conformance, subjectType,
830-
constraint.getProtocol()->getDeclaredInterfaceType()},
831-
signatureLoc));
832-
return;
833-
834-
case Symbol::Kind::Layout:
835-
errors.push_back(RequirementError::forConflictingRequirement(
836-
{RequirementKind::Layout, subjectType, constraint.getLayoutConstraint()},
837-
signatureLoc));
838-
return;
839-
840-
case Symbol::Kind::ConcreteConformance:
841-
case Symbol::Kind::AssociatedType:
842-
case Symbol::Kind::GenericParam:
843-
case Symbol::Kind::Name:
844-
return;
845-
}
846-
};
847-
848-
if (firstProperty->getKind() == Symbol::Kind::ConcreteType) {
849-
recordError(*firstProperty, *secondProperty);
850-
} else if (secondProperty->getKind() == Symbol::Kind::ConcreteType) {
851-
recordError(*secondProperty, *firstProperty);
852-
}
779+
errors.push_back(RequirementError::forConflictingRequirement(
780+
*firstRule->getPropertyRequirement(subject),
781+
*secondRule->getPropertyRequirement(subject),
782+
signatureLoc));
853783
}
854784
}
855785

lib/AST/RequirementMachine/Rule.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,33 @@ Optional<Symbol> Rule::isPropertyRule() const {
4343
return property;
4444
}
4545

46+
Optional<Requirement> Rule::getPropertyRequirement(Type subject) const {
47+
auto property = isPropertyRule();
48+
if (!property)
49+
return None;
50+
51+
switch (property->getKind()) {
52+
case Symbol::Kind::ConcreteType:
53+
return Requirement(RequirementKind::SameType, subject,
54+
property->getConcreteType());
55+
56+
case Symbol::Kind::Superclass:
57+
return Requirement(RequirementKind::Superclass, subject,
58+
property->getConcreteType());
59+
60+
case Symbol::Kind::Protocol:
61+
return Requirement(RequirementKind::Conformance, subject,
62+
property->getProtocol()->getDeclaredInterfaceType());
63+
64+
case Symbol::Kind::Layout:
65+
return Requirement(RequirementKind::Layout, subject,
66+
property->getLayoutConstraint());
67+
68+
default:
69+
return None;
70+
}
71+
}
72+
4673
/// If this is a rule of the form T.[P] => T where [P] is a protocol symbol,
4774
/// return the protocol P, otherwise return nullptr.
4875
const ProtocolDecl *Rule::isProtocolConformanceRule() const {

lib/AST/RequirementMachine/Rule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#ifndef SWIFT_RULE_H
1414
#define SWIFT_RULE_H
1515

16+
#include "swift/AST/Requirement.h"
17+
1618
#include "Symbol.h"
1719
#include "Term.h"
1820

@@ -107,6 +109,8 @@ class Rule final {
107109

108110
Optional<Symbol> isPropertyRule() const;
109111

112+
Optional<Requirement> getPropertyRequirement(Type subject) const;
113+
110114
const ProtocolDecl *isProtocolConformanceRule() const;
111115

112116
const ProtocolDecl *isAnyConformanceRule() const;

test/Generics/requirement_machine_diagnostics.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ protocol ProtoAlias2 {
210210
}
211211

212212
func basicConflict<T: ProtoAlias1 & ProtoAlias2>(_:T) where T.A1 == T.A2 {}
213-
// expected-error@-1{{generic signature requires types 'Int' and 'String' to be the same}}
213+
// expected-error@-1{{no type for 'T.A1' can satisfy both 'T.A1 == Int' and 'T.A1 == String'}}
214214

215215
protocol RequiresAnyObject {
216216
associatedtype A: AnyObject
@@ -228,17 +228,17 @@ protocol RequiresSuperclass {
228228
func testMissingRequirements() {
229229
struct S {}
230230
func conflict1<T: RequiresAnyObject>(_: T) where T.A == S {}
231-
// expected-error@-1{{same-type constraint type 'S' does not conform to required protocol 'AnyObject'}}
231+
// expected-error@-1{{no type for 'T.A' can satisfy both 'T.A == S' and 'T.A : AnyObject'}}
232232

233233
func conflict2<T: RequiresConformance>(_: T) where T.A == C {}
234-
// expected-error@-1{{same-type constraint type 'C' does not conform to required protocol 'P'}}
234+
// expected-error@-1{{no type for 'T.A' can satisfy both 'T.A : P' and 'T.A == C'}}
235235

236236
class C {}
237237
func conflict3<T: RequiresSuperclass>(_: T) where T.A == C {}
238-
// expected-error@-1{{type 'T.A' cannot be a subclass of both 'Super' and 'C'}}
238+
// expected-error@-1{{no type for 'T.A' can satisfy both 'T.A : Super' and 'T.A : C'}}
239239

240240
func conflict4<T: RequiresSuperclass>(_: T) where T.A: C {}
241-
// expected-error@-1{{type 'T.A' cannot be a subclass of both 'Super' and 'C'}}
241+
// expected-error@-1{{no type for 'T.A' can satisfy both 'T.A : Super' and 'T.A : C'}}
242242
}
243243

244244
protocol Fooable {
@@ -265,44 +265,44 @@ func sameTypeConflicts() {
265265
var bar: Y { return Y() }
266266
}
267267

268-
// expected-error@+1{{generic parameter 'T.Foo' cannot be equal to both 'Y' and 'X'}}
268+
// expected-error@+1{{no type for 'T.Foo' can satisfy both 'T.Foo == Y' and 'T.Foo == X'}}
269269
func fail1<
270270
T: Fooable, U: Fooable
271271
>(_ t: T, u: U) -> (X, Y)
272272
where T.Foo == X, U.Foo == Y, T.Foo == U.Foo {
273273
fatalError()
274274
}
275275

276-
// expected-error@+1{{generic parameter 'T.Foo' cannot be equal to both 'X' and 'Y'}}
276+
// expected-error@+1{{no type for 'T.Foo' can satisfy both 'T.Foo == X' and 'T.Foo == Y'}}
277277
func fail2<
278278
T: Fooable, U: Fooable
279279
>(_ t: T, u: U) -> (X, Y)
280280
where T.Foo == U.Foo, T.Foo == X, U.Foo == Y {
281281
fatalError()
282282
}
283283

284-
// expected-error@+1{{same-type constraint type 'X' does not conform to required protocol 'Fooable'}}
284+
// expected-error@+1{{no type for 'T.Bar' can satisfy both 'T.Bar : Fooable' and 'T.Bar == X'}}
285285
func fail3<T: Barrable>(_ t: T) -> X
286286
where T.Bar == X {
287287
fatalError()
288288
}
289289

290-
// expected-error@+1{{generic parameter 'T.Bar.Foo' cannot be equal to both 'Z' and 'X'}}
290+
// expected-error@+1{{no type for 'T.Bar.Foo' can satisfy both 'T.Bar.Foo == Z' and 'T.Bar.Foo == X'}}
291291
func fail4<T: Barrable>(_ t: T) -> (Y, Z)
292292
where
293293
T.Bar == Y,
294294
T.Bar.Foo == Z {
295295
fatalError()
296296
}
297297

298-
// expected-error@+1{{generic parameter 'T.Bar.Foo' cannot be equal to both 'Z' and 'X'}}
298+
// expected-error@+1{{no type for 'T.Bar.Foo' can satisfy both 'T.Bar.Foo == Z' and 'T.Bar.Foo == X'}}
299299
func fail5<T: Barrable>(_ t: T) -> (Y, Z)
300300
where
301301
T.Bar.Foo == Z,
302302
T.Bar == Y {
303303
fatalError()
304304
}
305305

306-
// expected-error@+1{{generic parameter 'T.X' cannot be equal to both 'Int' and 'String'}}
306+
// expected-error@+1{{no type for 'T.X' can satisfy both 'T.X == Int' and 'T.X == String'}}
307307
func fail6<U, T: Concrete>(_: U, _: T) where T.X == String {}
308308
}

0 commit comments

Comments
 (0)