Skip to content

Commit 3138020

Browse files
committed
RequirementMachine: Diagnose non-confluent rewrite systems instead of asserting
We assert when building a rewrite system for an existing generic signature, or in AbstractGenericSignatureRequest, where there is no source location. In RequirementSignatureRequest and InferredGenericSignatureRequest we now produce a diagnostic.
1 parent 360fbc6 commit 3138020

File tree

5 files changed

+123
-34
lines changed

5 files changed

+123
-34
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2533,6 +2533,11 @@ WARNING(associated_type_override_typealias,none,
25332533
"associated type %0 is redundant with type %0 declared in inherited "
25342534
"%1 %2", (Identifier, DescriptiveDeclKind, Type))
25352535

2536+
ERROR(requirement_machine_completion_failed,none,
2537+
"cannot build rewrite system for %select{generic signature|protocol}0; "
2538+
"%select{step|depth}1 limit exceeded",
2539+
(unsigned, unsigned))
2540+
25362541
ERROR(associated_type_objc,none,
25372542
"associated type %0 cannot be declared inside '@objc' protocol %1",
25382543
(Identifier, Identifier))

lib/AST/RequirementMachine/RequirementMachine.cpp

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,32 @@ RequirementMachine::RequirementMachine(RewriteContext &ctx)
3535

3636
RequirementMachine::~RequirementMachine() {}
3737

38+
static void checkCompletionResult(const RequirementMachine &machine,
39+
CompletionResult result) {
40+
switch (result) {
41+
case CompletionResult::Success:
42+
break;
43+
44+
case CompletionResult::MaxIterations:
45+
llvm::errs() << "Rewrite system exceeds maximum completion step count\n";
46+
machine.dump(llvm::errs());
47+
abort();
48+
49+
case CompletionResult::MaxDepth:
50+
llvm::errs() << "Rewrite system exceeds maximum completion depth\n";
51+
machine.dump(llvm::errs());
52+
abort();
53+
}
54+
}
55+
3856
/// Build a requirement machine for the requirements of a generic signature.
3957
///
4058
/// This must only be called exactly once, before any other operations are
4159
/// performed on this requirement machine.
4260
///
4361
/// Used by ASTContext::getOrCreateRequirementMachine().
62+
///
63+
/// Asserts if completion fails within the configured number of steps.
4464
void RequirementMachine::initWithGenericSignature(CanGenericSignature sig) {
4565
Sig = sig;
4666
Params.append(sig.getGenericParams().begin(),
@@ -64,7 +84,8 @@ void RequirementMachine::initWithGenericSignature(CanGenericSignature sig) {
6484
std::move(builder.PermanentRules),
6585
std::move(builder.RequirementRules));
6686

67-
computeCompletion(RewriteSystem::DisallowInvalidRequirements);
87+
auto result = computeCompletion(RewriteSystem::DisallowInvalidRequirements);
88+
checkCompletionResult(*this, result);
6889

6990
if (Dump) {
7091
llvm::dbgs() << "}\n";
@@ -79,7 +100,10 @@ void RequirementMachine::initWithGenericSignature(CanGenericSignature sig) {
79100
/// performed on this requirement machine.
80101
///
81102
/// Used by RequirementSignatureRequest.
82-
void RequirementMachine::initWithProtocols(ArrayRef<const ProtocolDecl *> protos) {
103+
///
104+
/// Returns failure if completion fails within the configured number of steps.
105+
CompletionResult
106+
RequirementMachine::initWithProtocols(ArrayRef<const ProtocolDecl *> protos) {
83107
Protos = protos;
84108

85109
FrontendStatsTracer tracer(Stats, "build-rewrite-system");
@@ -100,12 +124,13 @@ void RequirementMachine::initWithProtocols(ArrayRef<const ProtocolDecl *> protos
100124
std::move(builder.PermanentRules),
101125
std::move(builder.RequirementRules));
102126

103-
// FIXME: Only if the protocols were written in source, though.
104-
computeCompletion(RewriteSystem::AllowInvalidRequirements);
127+
auto result = computeCompletion(RewriteSystem::AllowInvalidRequirements);
105128

106129
if (Dump) {
107130
llvm::dbgs() << "}\n";
108131
}
132+
133+
return result;
109134
}
110135

111136
/// Build a requirement machine from a set of generic parameters and
@@ -115,6 +140,8 @@ void RequirementMachine::initWithProtocols(ArrayRef<const ProtocolDecl *> protos
115140
/// performed on this requirement machine.
116141
///
117142
/// Used by AbstractGenericSignatureRequest.
143+
///
144+
/// Asserts if completion fails within the configured number of steps.
118145
void RequirementMachine::initWithAbstractRequirements(
119146
ArrayRef<GenericTypeParamType *> genericParams,
120147
ArrayRef<Requirement> requirements) {
@@ -139,7 +166,8 @@ void RequirementMachine::initWithAbstractRequirements(
139166
std::move(builder.PermanentRules),
140167
std::move(builder.RequirementRules));
141168

142-
computeCompletion(RewriteSystem::AllowInvalidRequirements);
169+
auto result = computeCompletion(RewriteSystem::AllowInvalidRequirements);
170+
checkCompletionResult(*this, result);
143171

144172
if (Dump) {
145173
llvm::dbgs() << "}\n";
@@ -153,7 +181,10 @@ void RequirementMachine::initWithAbstractRequirements(
153181
/// performed on this requirement machine.
154182
///
155183
/// Used by InferredGenericSignatureRequest.
156-
void RequirementMachine::initWithWrittenRequirements(
184+
///
185+
/// Returns failure if completion fails within the configured number of steps.
186+
CompletionResult
187+
RequirementMachine::initWithWrittenRequirements(
157188
ArrayRef<GenericTypeParamType *> genericParams,
158189
ArrayRef<StructuralRequirement> requirements) {
159190
Params.append(genericParams.begin(), genericParams.end());
@@ -177,17 +208,20 @@ void RequirementMachine::initWithWrittenRequirements(
177208
std::move(builder.PermanentRules),
178209
std::move(builder.RequirementRules));
179210

180-
computeCompletion(RewriteSystem::AllowInvalidRequirements);
211+
auto result = computeCompletion(RewriteSystem::AllowInvalidRequirements);
181212

182213
if (Dump) {
183214
llvm::dbgs() << "}\n";
184215
}
216+
217+
return result;
185218
}
186219

187220
/// Attempt to obtain a confluent rewrite system by iterating the Knuth-Bendix
188221
/// completion procedure together with property map construction until fixed
189222
/// point.
190-
void RequirementMachine::computeCompletion(RewriteSystem::ValidityPolicy policy) {
223+
CompletionResult
224+
RequirementMachine::computeCompletion(RewriteSystem::ValidityPolicy policy) {
191225
while (true) {
192226
// First, run the Knuth-Bendix algorithm to resolve overlapping rules.
193227
auto result = System.computeConfluentCompletion(
@@ -200,26 +234,8 @@ void RequirementMachine::computeCompletion(RewriteSystem::ValidityPolicy policy)
200234
}
201235

202236
// Check for failure.
203-
auto checkCompletionResult = [&]() {
204-
switch (result.first) {
205-
case CompletionResult::Success:
206-
break;
207-
208-
case CompletionResult::MaxIterations:
209-
llvm::errs() << "Generic signature " << Sig
210-
<< " exceeds maximum completion step count\n";
211-
System.dump(llvm::errs());
212-
abort();
213-
214-
case CompletionResult::MaxDepth:
215-
llvm::errs() << "Generic signature " << Sig
216-
<< " exceeds maximum completion depth\n";
217-
System.dump(llvm::errs());
218-
abort();
219-
}
220-
};
221-
222-
checkCompletionResult();
237+
if (result.first != CompletionResult::Success)
238+
return result.first;
223239

224240
// Check invariants.
225241
System.verifyRewriteRules(policy);
@@ -236,7 +252,9 @@ void RequirementMachine::computeCompletion(RewriteSystem::ValidityPolicy policy)
236252
.NumRequirementMachineUnifiedConcreteTerms += result.second;
237253
}
238254

239-
checkCompletionResult();
255+
// Check for failure.
256+
if (result.first != CompletionResult::Success)
257+
return result.first;
240258

241259
// If buildPropertyMap() added new rules, we run another round of
242260
// Knuth-Bendix, and build the property map again.
@@ -250,6 +268,8 @@ void RequirementMachine::computeCompletion(RewriteSystem::ValidityPolicy policy)
250268

251269
assert(!Complete);
252270
Complete = true;
271+
272+
return CompletionResult::Success;
253273
}
254274

255275
bool RequirementMachine::isComplete() const {

lib/AST/RequirementMachine/RequirementMachine.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,17 +86,17 @@ class RequirementMachine final {
8686
RequirementMachine &operator=(RequirementMachine &&) = delete;
8787

8888
void initWithGenericSignature(CanGenericSignature sig);
89-
void initWithProtocols(ArrayRef<const ProtocolDecl *> protos);
89+
CompletionResult initWithProtocols(ArrayRef<const ProtocolDecl *> protos);
9090
void initWithAbstractRequirements(
9191
ArrayRef<GenericTypeParamType *> genericParams,
9292
ArrayRef<Requirement> requirements);
93-
void initWithWrittenRequirements(
93+
CompletionResult initWithWrittenRequirements(
9494
ArrayRef<GenericTypeParamType *> genericParams,
9595
ArrayRef<StructuralRequirement> requirements);
9696

9797
bool isComplete() const;
9898

99-
void computeCompletion(RewriteSystem::ValidityPolicy policy);
99+
CompletionResult computeCompletion(RewriteSystem::ValidityPolicy policy);
100100

101101
MutableTerm getLongestValidPrefix(const MutableTerm &term) const;
102102

lib/AST/RequirementMachine/RequirementMachineRequests.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "RequirementMachine.h"
2222
#include "swift/AST/ASTContext.h"
2323
#include "swift/AST/Decl.h"
24+
#include "swift/AST/DiagnosticsSema.h"
2425
#include "swift/AST/GenericSignature.h"
2526
#include "swift/AST/LazyResolver.h"
2627
#include "swift/AST/Requirement.h"
@@ -247,7 +248,25 @@ RequirementSignatureRequestRQM::evaluate(Evaluator &evaluator,
247248
std::unique_ptr<RequirementMachine> machine(new RequirementMachine(
248249
ctx.getRewriteContext()));
249250

250-
machine->initWithProtocols(component);
251+
auto status = machine->initWithProtocols(component);
252+
if (status != CompletionResult::Success) {
253+
// All we can do at this point is diagnose and give each protocol an empty
254+
// requirement signature.
255+
for (const auto *otherProto : component) {
256+
ctx.Diags.diagnose(otherProto->getLoc(),
257+
diag::requirement_machine_completion_failed,
258+
/*protocol=*/1,
259+
status == CompletionResult::MaxIterations ? 0 : 1);
260+
261+
if (otherProto != proto) {
262+
ctx.evaluator.cacheOutput(
263+
RequirementSignatureRequestRQM{const_cast<ProtocolDecl *>(otherProto)},
264+
ArrayRef<Requirement>());
265+
}
266+
}
267+
268+
return ArrayRef<Requirement>();
269+
}
251270

252271
auto minimalRequirements = machine->computeMinimalProtocolRequirements();
253272

@@ -400,7 +419,10 @@ InferredGenericSignatureRequestRQM::evaluate(
400419
return false;
401420
};
402421

422+
SourceLoc loc;
403423
if (genericParamList) {
424+
loc = genericParamList->getLAngleLoc();
425+
404426
// Extensions never have a parent signature.
405427
assert(genericParamList->getOuterParameters() == nullptr || !parentSig);
406428

@@ -440,6 +462,9 @@ InferredGenericSignatureRequestRQM::evaluate(
440462
}
441463

442464
if (whereClause) {
465+
if (loc.isInvalid())
466+
loc = whereClause.getLoc();
467+
443468
std::move(whereClause).visitRequirements(
444469
TypeResolutionStage::Structural,
445470
visitRequirement);
@@ -464,7 +489,16 @@ InferredGenericSignatureRequestRQM::evaluate(
464489
std::unique_ptr<RequirementMachine> machine(new RequirementMachine(
465490
ctx.getRewriteContext()));
466491

467-
machine->initWithWrittenRequirements(genericParams, requirements);
492+
auto status = machine->initWithWrittenRequirements(genericParams, requirements);
493+
if (status != CompletionResult::Success) {
494+
ctx.Diags.diagnose(loc,
495+
diag::requirement_machine_completion_failed,
496+
/*protocol=*/0,
497+
status == CompletionResult::MaxIterations ? 0 : 1);
498+
499+
auto result = GenericSignature::get(genericParams, {});
500+
return GenericSignatureWithError(result, /*hadError=*/true);
501+
}
468502

469503
auto minimalRequirements =
470504
machine->computeMinimalGenericSignatureRequirements();

test/Generics/non_confluent.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %target-typecheck-verify-swift -requirement-machine-protocol-signatures=on -requirement-machine-inferred-signatures=on
2+
3+
protocol Undecidable // expected-error {{cannot build rewrite system for protocol; depth limit exceeded}}
4+
where A.C == C.A, // expected-error *{{is not a member type}}
5+
A.D == D.A, // expected-error *{{is not a member type}}
6+
B.C == C.B, // expected-error *{{is not a member type}}
7+
B.D == D.B, // expected-error *{{is not a member type}}
8+
C.E == E.C.A, // expected-error *{{is not a member type}}
9+
D.E == E.D.B, // expected-error *{{is not a member type}}
10+
C.C.A == C.C.A.E { // expected-error *{{is not a member type}}
11+
associatedtype A : Undecidable
12+
associatedtype B : Undecidable
13+
associatedtype C : Undecidable
14+
associatedtype D : Undecidable
15+
associatedtype E : Undecidable
16+
}
17+
18+
protocol P1 {
19+
associatedtype T : P1
20+
}
21+
22+
protocol P2 {
23+
associatedtype T : P2
24+
}
25+
26+
func foo<T : P1 & P2>(_: T) {}
27+
// expected-error@-1 {{cannot build rewrite system for generic signature; depth limit exceeded}}
28+
29+
extension P1 where Self : P2 {}
30+
// expected-error@-1 {{cannot build rewrite system for generic signature; depth limit exceeded}}

0 commit comments

Comments
 (0)