Skip to content

Commit caee699

Browse files
committed
[RequirementMachine] Mention the type parameter that is the subject of
two conflicting requirements in diagnostics where possible.
1 parent deb62f7 commit caee699

File tree

6 files changed

+95
-22
lines changed

6 files changed

+95
-22
lines changed

lib/AST/RequirementMachine/Diagnostics.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,26 @@ struct RequirementError {
4040
RedundantRequirement,
4141
} kind;
4242

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

4655
SourceLoc loc;
4756

4857
private:
4958
RequirementError(Kind kind, Requirement requirement, SourceLoc loc)
50-
: kind(kind), requirement(requirement), loc(loc) {}
59+
: kind(kind), typeParameter(Type()), requirement(requirement), loc(loc) {}
60+
61+
RequirementError(Kind kind, Type subject, Requirement requirement, SourceLoc loc)
62+
: kind(kind), typeParameter(subject), requirement(requirement), loc(loc) {}
5163

5264
public:
5365
static RequirementError forInvalidTypeRequirement(Type subjectType,
@@ -69,6 +81,12 @@ struct RequirementError {
6981
return {Kind::ConflictingRequirement, req, loc};
7082
}
7183

84+
static RequirementError forConflictingRequirement(Type typeParameter,
85+
Requirement req,
86+
SourceLoc loc) {
87+
return {Kind::ConflictingRequirement, typeParameter, req, loc};
88+
}
89+
7290
static RequirementError forSameTypeMissingRequirement(Requirement req,
7391
SourceLoc loc) {
7492
return {Kind::SameTypeMissingRequirement, req, loc};

lib/AST/RequirementMachine/RequirementLowering.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,24 @@ bool swift::rewriting::diagnoseRequirementErrors(
726726
}
727727

728728
case RequirementError::Kind::ConflictingRequirement: {
729+
// FIXME: Unify this case with SameTypeMissingRequirement.
730+
if (auto subjectType = error.typeParameter) {
731+
auto firstType = error.requirement.getFirstType();
732+
auto secondType = error.requirement.getSecondType();
733+
734+
if (error.requirement.getKind() == RequirementKind::SameType) {
735+
ctx.Diags.diagnose(loc, diag::same_type_conflict,
736+
false, subjectType, firstType, secondType);
737+
} else {
738+
assert(error.requirement.getKind() == RequirementKind::Superclass);
739+
ctx.Diags.diagnose(loc, diag::conflicting_superclass_constraints,
740+
subjectType, firstType, secondType);
741+
742+
}
743+
diagnosedError = true;
744+
break;
745+
}
746+
729747
auto subjectType = error.requirement.getFirstType();
730748
if (subjectType->hasError())
731749
break;

lib/AST/RequirementMachine/RequirementMachine.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,8 @@ void RequirementMachine::freeze() {
533533
void RequirementMachine::computeRequirementDiagnostics(
534534
SmallVectorImpl<RequirementError> &errors, SourceLoc signatureLoc) {
535535
System.computeRedundantRequirementDiagnostics(errors);
536-
System.computeConflictDiagnostics(errors, signatureLoc);
536+
System.computeConflictDiagnostics(errors, signatureLoc, Map,
537+
getGenericParams());
537538
}
538539

539540
std::string RequirementMachine::getRuleAsStringForDiagnostics(

lib/AST/RequirementMachine/RewriteSystem.cpp

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "llvm/Support/raw_ostream.h"
1616
#include <algorithm>
1717
#include <vector>
18+
#include "PropertyMap.h"
1819
#include "RewriteContext.h"
1920
#include "RewriteLoop.h"
2021
#include "RewriteSystem.h"
@@ -753,7 +754,9 @@ void RewriteSystem::freeze() {
753754
}
754755

755756
void RewriteSystem::computeConflictDiagnostics(
756-
SmallVectorImpl<RequirementError> &errors, SourceLoc signatureLoc) {
757+
SmallVectorImpl<RequirementError> &errors, SourceLoc signatureLoc,
758+
const PropertyMap &propertyMap,
759+
TypeArrayView<GenericTypeParamType> genericParams) {
757760
for (auto pair : ConflictingRules) {
758761
auto *firstRule = &getRule(pair.first);
759762
auto *secondRule = &getRule(pair.second);
@@ -763,6 +766,48 @@ void RewriteSystem::computeConflictDiagnostics(
763766
if (!firstProperty || !secondProperty)
764767
continue;
765768

769+
MutableTerm firstTerm(
770+
firstRule->getLHS().begin(), firstRule->getLHS().end() - 1);
771+
MutableTerm secondTerm(
772+
secondRule->getLHS().begin(), secondRule->getLHS().end() - 1);
773+
774+
auto firstSubject = propertyMap.getTypeForTerm(firstTerm, genericParams);
775+
auto secondSubject = propertyMap.getTypeForTerm(secondTerm, genericParams);
776+
assert(firstSubject && secondSubject);
777+
778+
// Record conflicting requirements on a type parameter, e.g.
779+
// conflicting superclass requirements:
780+
//
781+
// class C1 {}
782+
// class C2 {}
783+
// protocol P { associatedtype A: C1 }
784+
// func conflict<T: P>(_: T) where T.A: C2 {}
785+
if (firstProperty->getKind() == secondProperty->getKind() &&
786+
firstTerm.back().getKind() != Symbol::Kind::Name &&
787+
firstSubject->isEqual(secondSubject)) {
788+
switch (firstProperty->getKind()) {
789+
case Symbol::Kind::ConcreteType:
790+
errors.push_back(RequirementError::forConflictingRequirement(firstSubject,
791+
{RequirementKind::SameType, firstProperty->getConcreteType(),
792+
secondProperty->getConcreteType()},
793+
signatureLoc));
794+
continue;
795+
796+
case Symbol::Kind::Superclass:
797+
// FIXME: shoving the conflicting superclass types into a superclass
798+
// requiement is a little gross.
799+
errors.push_back(RequirementError::forConflictingRequirement(firstSubject,
800+
{RequirementKind::Superclass, firstProperty->getConcreteType(),
801+
secondProperty->getConcreteType()},
802+
signatureLoc));
803+
continue;
804+
805+
// FIXME: Conflicting layout requirements?
806+
default:
807+
continue;
808+
}
809+
}
810+
766811
auto recordError = [&](Symbol subject, Symbol constraint) {
767812
auto subjectType = subject.getConcreteType();
768813
switch (constraint.getKind()) {
@@ -802,19 +847,6 @@ void RewriteSystem::computeConflictDiagnostics(
802847
recordError(*firstProperty, *secondProperty);
803848
} else if (secondProperty->getKind() == Symbol::Kind::ConcreteType) {
804849
recordError(*secondProperty, *firstProperty);
805-
} else {
806-
// FIXME: This can happen when there are conflicting requirements
807-
// on a type parameter, e.g. conflicting superclass requirements:
808-
//
809-
// class C1 {}
810-
// class C2 {}
811-
// protocol P { associatedtype A: C1 }
812-
// func conflict<T: P>(_: T) where T.A: C2 {}
813-
//
814-
// In this case, we want to compute the type `T.A` from
815-
// its corresponding term. For this, we need the property map
816-
// from the RequirementMachine.
817-
continue;
818850
}
819851
}
820852
}

lib/AST/RequirementMachine/RewriteSystem.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,9 @@ class RewriteSystem final {
230230
void computeRedundantRequirementDiagnostics(SmallVectorImpl<RequirementError> &errors);
231231

232232
void computeConflictDiagnostics(SmallVectorImpl<RequirementError> &errors,
233-
SourceLoc signatureLoc);
233+
SourceLoc signatureLoc,
234+
const PropertyMap &map,
235+
TypeArrayView<GenericTypeParamType> genericParams);
234236

235237
private:
236238
struct CriticalPair {

test/Generics/requirement_machine_diagnostics.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -233,10 +233,12 @@ func testMissingRequirements() {
233233
func conflict2<T: RequiresConformance>(_: T) where T.A == C {}
234234
// expected-error@-1{{same-type constraint type 'C' does not conform to required protocol 'P'}}
235235

236-
// FIXME: Diagnose conflicting superclass requirements on T.A
237236
class C {}
238237
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'}}
239+
239240
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'}}
240242
}
241243

242244
protocol Fooable {
@@ -262,15 +264,15 @@ func sameTypeConflicts() {
262264
var bar: Y { return Y() }
263265
}
264266

265-
// expected-error@+1{{generic signature requires types 'Y' and 'X' to be the same}}
267+
// expected-error@+1{{generic parameter 'T.Foo' cannot be equal to both 'Y' and 'X'}}
266268
func fail1<
267269
T: Fooable, U: Fooable
268270
>(_ t: T, u: U) -> (X, Y)
269271
where T.Foo == X, U.Foo == Y, T.Foo == U.Foo {
270272
fatalError()
271273
}
272274

273-
// expected-error@+1{{generic signature requires types 'X' and 'Y' to be the same}}
275+
// expected-error@+1{{generic parameter 'T.Foo' cannot be equal to both 'X' and 'Y'}}
274276
func fail2<
275277
T: Fooable, U: Fooable
276278
>(_ t: T, u: U) -> (X, Y)
@@ -284,15 +286,15 @@ func sameTypeConflicts() {
284286
fatalError()
285287
}
286288

287-
// expected-error@+1{{generic signature requires types 'Z' and 'X' to be the same}}
289+
// expected-error@+1{{generic parameter 'T.Bar.Foo' cannot be equal to both 'Z' and 'X'}}
288290
func fail4<T: Barrable>(_ t: T) -> (Y, Z)
289291
where
290292
T.Bar == Y,
291293
T.Bar.Foo == Z {
292294
fatalError()
293295
}
294296

295-
// expected-error@+1{{generic signature requires types 'Z' and 'X' to be the same}}
297+
// expected-error@+1{{generic parameter 'T.Bar.Foo' cannot be equal to both 'Z' and 'X'}}
296298
func fail5<T: Barrable>(_ t: T) -> (Y, Z)
297299
where
298300
T.Bar.Foo == Z,

0 commit comments

Comments
 (0)