Skip to content

Commit ce600d8

Browse files
authored
Merge pull request #41971 from hborla/conflicting-requirement-diagnostics
[RequirementMachine] Diagnose conflicting requirements.
2 parents 26ba097 + 85be43f commit ce600d8

11 files changed

+283
-41
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2491,6 +2491,9 @@ ERROR(recursive_superclass_constraint,none,
24912491
ERROR(requires_generic_param_same_type_does_not_conform,none,
24922492
"same-type constraint type %0 does not conform to required protocol %1",
24932493
(Type, Identifier))
2494+
ERROR(same_type_does_not_inherit,none,
2495+
"same-type constraint type %0 does not inherit from required superclass %1",
2496+
(Type, Type))
24942497
ERROR(requires_same_concrete_type,none,
24952498
"generic signature requires types %0 and %1 to be the same", (Type, Type))
24962499
WARNING(redundant_conformance_constraint,none,
@@ -2506,6 +2509,9 @@ WARNING(missing_protocol_refinement, none,
25062509
ERROR(same_type_conflict,none,
25072510
"%select{generic parameter |protocol |}0%1 cannot be equal to both "
25082511
"%2 and %3", (unsigned, Type, Type, Type))
2512+
ERROR(requirement_conflict,none,
2513+
"no type for %0 can satisfy both %1",
2514+
(Type, StringRef))
25092515
WARNING(redundant_same_type_to_concrete,none,
25102516
"redundant same-type constraint %0 == %1", (Type, Type))
25112517
NOTE(same_type_redundancy_here,none,

lib/AST/RequirementMachine/Diagnostics.h

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ struct RequirementError {
3030
enum class Kind {
3131
/// A constraint to a non-protocol, non-class type, e.g. T: Int.
3232
InvalidTypeRequirement,
33-
/// A type mismatch, e.g. Int == String.
34-
ConcreteTypeMismatch,
35-
/// A requirement proven to be false, e.g. Bool: Collection
33+
/// A type requirement on a trivially invalid subject type,
34+
/// e.g. Bool: Collection.
35+
InvalidRequirementSubject,
36+
/// A pair of conflicting requirements, T == Int, T == String
3637
ConflictingRequirement,
3738
/// A redundant requirement, e.g. T == T.
3839
RedundantRequirement,
@@ -41,11 +42,20 @@ struct RequirementError {
4142
/// The invalid requirement.
4243
Requirement requirement;
4344

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

4651
private:
4752
RequirementError(Kind kind, Requirement requirement, SourceLoc loc)
48-
: kind(kind), requirement(requirement), loc(loc) {}
53+
: kind(kind), requirement(requirement), conflictingRequirement(None), loc(loc) {}
54+
55+
RequirementError(Kind kind, Requirement requirement,
56+
Requirement conflict,
57+
SourceLoc loc)
58+
: kind(kind), requirement(requirement), conflictingRequirement(conflict), loc(loc) {}
4959

5060
public:
5161
static RequirementError forInvalidTypeRequirement(Type subjectType,
@@ -55,18 +65,22 @@ struct RequirementError {
5565
return {Kind::InvalidTypeRequirement, requirement, loc};
5666
}
5767

58-
static RequirementError forConcreteTypeMismatch(Type type1,
59-
Type type2,
60-
SourceLoc loc) {
61-
Requirement requirement(RequirementKind::SameType, type1, type2);
62-
return {Kind::ConcreteTypeMismatch, requirement, loc};
68+
static RequirementError forInvalidRequirementSubject(Requirement req,
69+
SourceLoc loc) {
70+
return {Kind::InvalidRequirementSubject, req, loc};
6371
}
6472

6573
static RequirementError forConflictingRequirement(Requirement req,
6674
SourceLoc loc) {
6775
return {Kind::ConflictingRequirement, req, loc};
6876
}
6977

78+
static RequirementError forConflictingRequirement(Requirement first,
79+
Requirement second,
80+
SourceLoc loc) {
81+
return {Kind::ConflictingRequirement, first, second, loc};
82+
}
83+
7084
static RequirementError forRedundantRequirement(Requirement req,
7185
SourceLoc loc) {
7286
return {Kind::RedundantRequirement, req, loc};

lib/AST/RequirementMachine/RequirementLowering.cpp

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,8 @@ static void desugarSameTypeRequirement(Type lhs, Type rhs, SourceLoc loc,
213213
return true;
214214
}
215215

216-
errors.push_back(
217-
RequirementError::forConcreteTypeMismatch(sugaredFirstType,
218-
secondType,
219-
loc));
216+
errors.push_back(RequirementError::forConflictingRequirement(
217+
{RequirementKind::SameType, sugaredFirstType, secondType}, loc));
220218
recordedErrors = true;
221219
return true;
222220
}
@@ -249,7 +247,7 @@ static void desugarSuperclassRequirement(Type subjectType,
249247
RequirementError::forRedundantRequirement(requirement, loc));
250248
} else {
251249
errors.push_back(
252-
RequirementError::forConflictingRequirement(requirement, loc));
250+
RequirementError::forInvalidRequirementSubject(requirement, loc));
253251
}
254252

255253
return;
@@ -271,7 +269,7 @@ static void desugarLayoutRequirement(Type subjectType,
271269
RequirementError::forRedundantRequirement(requirement, loc));
272270
} else {
273271
errors.push_back(
274-
RequirementError::forConflictingRequirement(requirement, loc));
272+
RequirementError::forInvalidRequirementSubject(requirement, loc));
275273
}
276274

277275
return;
@@ -295,7 +293,7 @@ static void desugarConformanceRequirement(Type subjectType, Type constraintType,
295293
auto *module = protoDecl->getParentModule();
296294
auto conformance = module->lookupConformance(subjectType, protoDecl);
297295
if (conformance.isInvalid()) {
298-
errors.push_back(RequirementError::forConflictingRequirement(
296+
errors.push_back(RequirementError::forInvalidRequirementSubject(
299297
{RequirementKind::Conformance, subjectType, constraintType}, loc));
300298
return;
301299
}
@@ -712,20 +710,7 @@ bool swift::rewriting::diagnoseRequirementErrors(
712710
break;
713711
}
714712

715-
case RequirementError::Kind::ConcreteTypeMismatch: {
716-
auto type1 = error.requirement.getFirstType();
717-
auto type2 = error.requirement.getSecondType();
718-
719-
if (!type1->hasError() && !type2->hasError()) {
720-
ctx.Diags.diagnose(loc, diag::requires_same_concrete_type,
721-
type1, type2);
722-
diagnosedError = true;
723-
}
724-
725-
break;
726-
}
727-
728-
case RequirementError::Kind::ConflictingRequirement: {
713+
case RequirementError::Kind::InvalidRequirementSubject: {
729714
auto subjectType = error.requirement.getFirstType();
730715
if (subjectType->hasError())
731716
break;
@@ -736,6 +721,37 @@ bool swift::rewriting::diagnoseRequirementErrors(
736721
break;
737722
}
738723

724+
case RequirementError::Kind::ConflictingRequirement: {
725+
auto requirement = error.requirement;
726+
auto conflict = error.conflictingRequirement;
727+
728+
if (!conflict) {
729+
if (requirement.getFirstType()->hasError() ||
730+
requirement.getSecondType()->hasError()) {
731+
// Don't emit a cascading error.
732+
break;
733+
}
734+
ctx.Diags.diagnose(loc, diag::requires_same_concrete_type,
735+
requirement.getFirstType(),
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);
749+
}
750+
751+
diagnosedError = true;
752+
break;
753+
}
754+
739755
case RequirementError::Kind::RedundantRequirement: {
740756
auto requirement = error.requirement;
741757
switch (requirement.getKind()) {

lib/AST/RequirementMachine/RequirementMachine.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ RequirementMachine::initWithProtocolWrittenRequirements(
366366

367367
// For RequirementMachine::verify() when called by generic signature queries;
368368
// We have a single valid generic parameter at depth 0, index 0.
369-
Params.push_back(component[0]->getSelfInterfaceType()->getCanonicalType());
369+
Params.push_back(component[0]->getSelfInterfaceType());
370370

371371
if (Dump) {
372372
llvm::dbgs() << "Adding protocols";
@@ -530,6 +530,13 @@ void RequirementMachine::freeze() {
530530
System.freeze();
531531
}
532532

533+
void RequirementMachine::computeRequirementDiagnostics(
534+
SmallVectorImpl<RequirementError> &errors, SourceLoc signatureLoc) {
535+
System.computeRedundantRequirementDiagnostics(errors);
536+
System.computeConflictDiagnostics(errors, signatureLoc, Map,
537+
getGenericParams());
538+
}
539+
533540
std::string RequirementMachine::getRuleAsStringForDiagnostics(
534541
unsigned ruleID) const {
535542
const auto &rule = System.getRule(ruleID);

lib/AST/RequirementMachine/RequirementMachine.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "llvm/ADT/DenseMap.h"
1919
#include <vector>
2020

21+
#include "Diagnostics.h"
2122
#include "PropertyMap.h"
2223
#include "RewriteContext.h"
2324
#include "RewriteSystem.h"
@@ -115,6 +116,9 @@ class RequirementMachine final {
115116

116117
void freeze();
117118

119+
void computeRequirementDiagnostics(SmallVectorImpl<RequirementError> &errors,
120+
SourceLoc signatureLoc);
121+
118122
MutableTerm getLongestValidPrefix(const MutableTerm &term) const;
119123

120124
void buildRequirementsFromRules(

lib/AST/RequirementMachine/RequirementMachineRequests.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ RequirementSignatureRequestRQM::evaluate(Evaluator &evaluator,
410410
if (ctx.LangOpts.RequirementMachineProtocolSignatures ==
411411
RequirementMachineMode::Enabled) {
412412
SmallVector<RequirementError, 4> errors;
413-
machine->System.computeRedundantRequirementDiagnostics(errors);
413+
machine->computeRequirementDiagnostics(errors, proto->getLoc());
414414
diagnoseRequirementErrors(ctx, errors,
415415
/*allowConcreteGenericParams=*/false);
416416
}
@@ -843,7 +843,7 @@ InferredGenericSignatureRequestRQM::evaluate(
843843
if (attempt == 0 &&
844844
ctx.LangOpts.RequirementMachineInferredSignatures ==
845845
RequirementMachineMode::Enabled) {
846-
machine->System.computeRedundantRequirementDiagnostics(errors);
846+
machine->computeRequirementDiagnostics(errors, loc);
847847
diagnoseRequirementErrors(ctx, errors, allowConcreteGenericParams);
848848
}
849849

lib/AST/RequirementMachine/RewriteSystem.cpp

Lines changed: 71 additions & 0 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"
@@ -752,6 +753,76 @@ void RewriteSystem::freeze() {
752753
ConflictingRules.clear();
753754
}
754755

756+
static Optional<Requirement>
757+
getRequirementForDiagnostics(Type subject, Symbol property,
758+
const PropertyMap &map,
759+
TypeArrayView<GenericTypeParamType> genericParams,
760+
const MutableTerm &prefix) {
761+
switch (property.getKind()) {
762+
case Symbol::Kind::ConcreteType: {
763+
auto concreteType = map.getTypeFromSubstitutionSchema(
764+
property.getConcreteType(), property.getSubstitutions(),
765+
genericParams, prefix);
766+
return Requirement(RequirementKind::SameType, subject, concreteType);
767+
}
768+
769+
case Symbol::Kind::Superclass: {
770+
auto concreteType = map.getTypeFromSubstitutionSchema(
771+
property.getConcreteType(), property.getSubstitutions(),
772+
genericParams, prefix);
773+
return Requirement(RequirementKind::Superclass, subject, concreteType);
774+
}
775+
776+
case Symbol::Kind::Protocol:
777+
return Requirement(RequirementKind::Conformance, subject,
778+
property.getProtocol()->getDeclaredInterfaceType());
779+
780+
case Symbol::Kind::Layout:
781+
return Requirement(RequirementKind::Layout, subject,
782+
property.getLayoutConstraint());
783+
784+
default:
785+
return None;
786+
}
787+
}
788+
789+
void RewriteSystem::computeConflictDiagnostics(
790+
SmallVectorImpl<RequirementError> &errors, SourceLoc signatureLoc,
791+
const PropertyMap &propertyMap,
792+
TypeArrayView<GenericTypeParamType> genericParams) {
793+
for (auto pair : ConflictingRules) {
794+
const auto &firstRule = getRule(pair.first);
795+
const auto &secondRule = getRule(pair.second);
796+
797+
assert(firstRule.isPropertyRule() && secondRule.isPropertyRule());
798+
799+
if (firstRule.isSubstitutionSimplified() ||
800+
secondRule.isSubstitutionSimplified())
801+
continue;
802+
803+
bool chooseFirstRule = firstRule.getRHS().size() > secondRule.getRHS().size();
804+
auto subjectRule = chooseFirstRule ? firstRule : secondRule;
805+
auto subjectTerm = subjectRule.getRHS();
806+
807+
auto suffixRule = chooseFirstRule ? secondRule : firstRule;
808+
auto suffixTerm = suffixRule.getRHS();
809+
810+
// If the root protocol of the subject term isn't in this minimization
811+
// domain, the conflict was already diagnosed.
812+
if (!isInMinimizationDomain(subjectTerm[0].getRootProtocol()))
813+
continue;
814+
815+
Type subject = propertyMap.getTypeForTerm(subjectTerm, genericParams);
816+
MutableTerm prefix(subjectTerm.begin(), subjectTerm.end() - suffixTerm.size());
817+
errors.push_back(RequirementError::forConflictingRequirement(
818+
*getRequirementForDiagnostics(subject, *subjectRule.isPropertyRule(),
819+
propertyMap, genericParams, MutableTerm()),
820+
*getRequirementForDiagnostics(subject, *suffixRule.isPropertyRule(),
821+
propertyMap, genericParams, prefix),
822+
signatureLoc));
823+
}
824+
}
825+
755826
void RewriteSystem::dump(llvm::raw_ostream &out) const {
756827
out << "Rewrite system: {\n";
757828
for (const auto &rule : Rules) {

lib/AST/RequirementMachine/RewriteSystem.h

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

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

232+
void computeConflictDiagnostics(SmallVectorImpl<RequirementError> &errors,
233+
SourceLoc signatureLoc,
234+
const PropertyMap &map,
235+
TypeArrayView<GenericTypeParamType> genericParams);
236+
232237
private:
233238
struct CriticalPair {
234239
MutableTerm LHS;

test/Generics/concrete_conflict.swift

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift
2-
// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=on -requirement-machine-inferred-signatures=on 2>&1 | %FileCheck %s
1+
// RUN: %target-swift-frontend -typecheck -verify %s -debug-generic-signatures -requirement-machine-protocol-signatures=on -requirement-machine-inferred-signatures=on 2>&1 | %FileCheck %s
32

43
protocol P1 {
54
associatedtype T where T == Int
@@ -25,11 +24,11 @@ protocol P3 {
2524
// CHECK-LABEL: .P4@
2625
// CHECK-LABEL: Requirement signature: <Self where Self.[P4]T : P1, Self.[P4]T : P2, Self.[P4]T : P3>
2726

27+
// expected-error@+3{{no type for 'Self.T.T' can satisfy both 'Self.T.T == String' and 'Self.T.T == Int'}}
28+
// expected-error@+2{{no type for 'Self.T.T' can satisfy both 'Self.T.T == Float' and 'Self.T.T == Int'}}
29+
// expected-error@+1{{no type for 'Self.T.T' can satisfy both 'Self.T.T == Float' and 'Self.T.T == String'}}
2830
protocol P4 {
2931
associatedtype T : P1 & P2 & P3
30-
// expected-note@-1 2{{same-type constraint 'Self.T.T' == 'Int' implied here}}
31-
// expected-error@-2 {{'Self.T.T' cannot be equal to both 'String' and 'Int'}}
32-
// expected-error@-3 {{'Self.T.T' cannot be equal to both 'Float' and 'Int'}}
3332
}
3433

3534
class Class<T> {}
@@ -38,5 +37,5 @@ extension Class where T == Bool {
3837
// CHECK-LABEL: .badRequirement()@
3938
// CHECK-NEXT: <T>
4039
func badRequirement() where T == Int { }
41-
// expected-error@-1 {{generic parameter 'T' cannot be equal to both 'Int' and 'Bool'}}
42-
}
40+
// expected-error@-1 {{no type for 'T' can satisfy both 'T == Int' and 'T == Bool'}}
41+
}

test/Generics/protocol_self_concrete_error.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77

88
struct S {}
99

10+
// expected-error@+1 {{no type for 'Self' can satisfy both 'Self == S' and 'Self : P'}}
1011
protocol P where Self == S {}

0 commit comments

Comments
 (0)