Skip to content

Commit 99f5e36

Browse files
committed
[RequirementMachine] Compute and diagnose conflicting rules in the minimal
rewrite system of a generic/requirement signature.
1 parent e512e7a commit 99f5e36

File tree

4 files changed

+176
-1
lines changed

4 files changed

+176
-1
lines changed

lib/AST/RequirementMachine/RequirementMachineRequests.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ RequirementSignatureRequestRQM::evaluate(Evaluator &evaluator,
411411
RequirementMachineMode::Enabled) {
412412
SmallVector<RequirementError, 4> errors;
413413
machine->System.computeRedundantRequirementDiagnostics(errors);
414+
machine->System.computeConflictDiagnostics(errors, proto->getLoc());
414415
diagnoseRequirementErrors(ctx, errors,
415416
/*allowConcreteGenericParams=*/false);
416417
}
@@ -844,6 +845,7 @@ InferredGenericSignatureRequestRQM::evaluate(
844845
ctx.LangOpts.RequirementMachineInferredSignatures ==
845846
RequirementMachineMode::Enabled) {
846847
machine->System.computeRedundantRequirementDiagnostics(errors);
848+
machine->System.computeConflictDiagnostics(errors, loc);
847849
diagnoseRequirementErrors(ctx, errors, allowConcreteGenericParams);
848850
}
849851

lib/AST/RequirementMachine/RewriteSystem.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,73 @@ void RewriteSystem::freeze() {
752752
ConflictingRules.clear();
753753
}
754754

755+
void RewriteSystem::computeConflictDiagnostics(
756+
SmallVectorImpl<RequirementError> &errors, SourceLoc signatureLoc) {
757+
for (auto pair : ConflictingRules) {
758+
auto *firstRule = &getRule(pair.first);
759+
auto *secondRule = &getRule(pair.second);
760+
761+
auto firstProperty = firstRule->isPropertyRule();
762+
auto secondProperty = secondRule->isPropertyRule();
763+
if (!firstProperty || !secondProperty)
764+
continue;
765+
766+
auto recordError = [&](Symbol subject, Symbol constraint) {
767+
auto subjectType = subject.getConcreteType();
768+
switch (constraint.getKind()) {
769+
case Symbol::Kind::ConcreteType:
770+
errors.push_back(RequirementError::forConcreteTypeMismatch(
771+
subjectType, constraint.getConcreteType(), signatureLoc));
772+
return;
773+
774+
case Symbol::Kind::Superclass:
775+
errors.push_back(RequirementError::forSameTypeMissingRequirement(
776+
{RequirementKind::Superclass, subjectType, constraint.getConcreteType()},
777+
signatureLoc));
778+
return;
779+
780+
case Symbol::Kind::Protocol:
781+
errors.push_back(RequirementError::forSameTypeMissingRequirement(
782+
{RequirementKind::Conformance, subjectType,
783+
constraint.getProtocol()->getDeclaredInterfaceType()},
784+
signatureLoc));
785+
return;
786+
787+
case Symbol::Kind::Layout:
788+
errors.push_back(RequirementError::forSameTypeMissingRequirement(
789+
{RequirementKind::Layout, subjectType, constraint.getLayoutConstraint()},
790+
signatureLoc));
791+
return;
792+
793+
case Symbol::Kind::ConcreteConformance:
794+
case Symbol::Kind::AssociatedType:
795+
case Symbol::Kind::GenericParam:
796+
case Symbol::Kind::Name:
797+
return;
798+
}
799+
};
800+
801+
if (firstProperty->getKind() == Symbol::Kind::ConcreteType) {
802+
recordError(*firstProperty, *secondProperty);
803+
} else if (secondProperty->getKind() == Symbol::Kind::ConcreteType) {
804+
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;
818+
}
819+
}
820+
}
821+
755822
void RewriteSystem::dump(llvm::raw_ostream &out) const {
756823
out << "Rewrite system: {\n";
757824
for (const auto &rule : Rules) {

lib/AST/RequirementMachine/RewriteSystem.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,9 @@ class RewriteSystem final {
229229

230230
void computeRedundantRequirementDiagnostics(SmallVectorImpl<RequirementError> &errors);
231231

232+
void computeConflictDiagnostics(SmallVectorImpl<RequirementError> &errors,
233+
SourceLoc signatureLoc);
234+
232235
private:
233236
struct CriticalPair {
234237
MutableTerm LHS;

test/Generics/requirement_machine_diagnostics.swift

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,4 +197,107 @@ func inferred6<T : P11>(_: T) where T.Y : Hashable, T.Z == Set<T.X>, T.X == T.Y
197197

198198
func typeMatcherSugar<T>(_: T) where Array<Int> == Array<T>, Array<Int> == Array<T> {}
199199
// expected-warning@-1 2{{redundant same-type constraint 'Array<Int>' == 'Array<T>'}}
200-
// expected-warning@-2{{redundant same-type constraint 'T' == 'Int'}}
200+
// expected-warning@-2{{redundant same-type constraint 'T' == 'Int'}}
201+
202+
// MARK: - Conflict diagnostics
203+
204+
protocol ProtoAlias1 {
205+
typealias A1 = Int
206+
}
207+
208+
protocol ProtoAlias2 {
209+
typealias A2 = String
210+
}
211+
212+
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}}
214+
215+
protocol RequiresAnyObject {
216+
associatedtype A: AnyObject
217+
}
218+
219+
protocol RequiresConformance {
220+
associatedtype A: P
221+
}
222+
223+
class Super {}
224+
protocol RequiresSuperclass {
225+
associatedtype A: Super
226+
}
227+
228+
func testMissingRequirements() {
229+
struct S {}
230+
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'}}
232+
233+
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'}}
235+
236+
// FIXME: Diagnose conflicting superclass requirements on T.A
237+
class C {}
238+
func conflict3<T: RequiresSuperclass>(_: T) where T.A == C {}
239+
func conflict4<T: RequiresSuperclass>(_: T) where T.A: C {}
240+
}
241+
242+
protocol Fooable {
243+
associatedtype Foo
244+
245+
var foo: Foo { get }
246+
}
247+
248+
protocol Barrable {
249+
associatedtype Bar: Fooable
250+
var bar: Bar { get }
251+
}
252+
253+
func sameTypeConflicts() {
254+
255+
struct X {}
256+
struct Y: Fooable {
257+
typealias Foo = X
258+
var foo: X { return X() }
259+
}
260+
struct Z: Barrable {
261+
typealias Bar = Y
262+
var bar: Y { return Y() }
263+
}
264+
265+
// expected-error@+1{{generic signature requires types 'Y' and 'X' to be the same}}
266+
func fail1<
267+
T: Fooable, U: Fooable
268+
>(_ t: T, u: U) -> (X, Y)
269+
where T.Foo == X, U.Foo == Y, T.Foo == U.Foo {
270+
fatalError()
271+
}
272+
273+
// expected-error@+1{{generic signature requires types 'X' and 'Y' to be the same}}
274+
func fail2<
275+
T: Fooable, U: Fooable
276+
>(_ t: T, u: U) -> (X, Y)
277+
where T.Foo == U.Foo, T.Foo == X, U.Foo == Y {
278+
fatalError()
279+
}
280+
281+
// expected-error@+1{{same-type constraint type 'X' does not conform to required protocol 'Fooable'}}
282+
func fail3<T: Barrable>(_ t: T) -> X
283+
where T.Bar == X {
284+
fatalError()
285+
}
286+
287+
// expected-error@+1{{generic signature requires types 'Z' and 'X' to be the same}}
288+
func fail4<T: Barrable>(_ t: T) -> (Y, Z)
289+
where
290+
T.Bar == Y,
291+
T.Bar.Foo == Z {
292+
fatalError()
293+
}
294+
295+
// expected-error@+1{{generic signature requires types 'Z' and 'X' to be the same}}
296+
func fail5<T: Barrable>(_ t: T) -> (Y, Z)
297+
where
298+
T.Bar.Foo == Z,
299+
T.Bar == Y {
300+
fatalError()
301+
}
302+
303+
}

0 commit comments

Comments
 (0)