Skip to content

Commit 2755f74

Browse files
authored
Merge pull request #40502 from slavapestov/rqm-diagnose-confluence-violation
RequirementMachine: Diagnose non-confluent rewrite systems instead of asserting
2 parents 0e370db + 7f7bf2f commit 2755f74

File tree

9 files changed

+181
-91
lines changed

9 files changed

+181
-91
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/KnuthBendix.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -528,11 +528,6 @@ RewriteSystem::computeConfluentCompletion(unsigned maxIterations,
528528
auto to = lhs.getLHS().end();
529529
while (from < to) {
530530
Trie.findAll(from, to, [&](unsigned j) {
531-
// We don't have to consider the same pair of rules more than once,
532-
// since those critical pairs were already resolved.
533-
if (!CheckedOverlaps.insert(std::make_pair(i, j)).second)
534-
return;
535-
536531
const auto &rhs = getRule(j);
537532
if (rhs.isSimplified())
538533
return;
@@ -554,6 +549,11 @@ RewriteSystem::computeConfluentCompletion(unsigned maxIterations,
554549
return;
555550
}
556551

552+
// We don't have to consider the same pair of rules more than once,
553+
// since those critical pairs were already resolved.
554+
if (!CheckedOverlaps.insert(std::make_pair(i, j)).second)
555+
return;
556+
557557
// Try to repair the confluence violation by adding a new rule.
558558
if (computeCriticalPair(from, lhs, rhs,
559559
resolvedCriticalPairs,

lib/AST/RequirementMachine/RequirementLowering.cpp

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -342,20 +342,18 @@ void swift::rewriting::realizeRequirement(
342342
ModuleDecl *moduleForInference,
343343
SmallVectorImpl<StructuralRequirement> &result) {
344344
auto firstType = req.getFirstType();
345-
if (moduleForInference) {
346-
auto firstLoc = (reqRepr ? reqRepr->getFirstTypeRepr()->getStartLoc()
347-
: SourceLoc());
348-
inferRequirements(firstType, firstLoc, moduleForInference, result);
349-
}
350-
351345
auto loc = (reqRepr ? reqRepr->getSeparatorLoc() : SourceLoc());
352346

353347
switch (req.getKind()) {
354348
case RequirementKind::Superclass:
355349
case RequirementKind::Conformance: {
356350
auto secondType = req.getSecondType();
357351
if (moduleForInference) {
358-
auto secondLoc = (reqRepr ? reqRepr->getSecondTypeRepr()->getStartLoc()
352+
auto firstLoc = (reqRepr ? reqRepr->getSubjectRepr()->getStartLoc()
353+
: SourceLoc());
354+
inferRequirements(firstType, firstLoc, moduleForInference, result);
355+
356+
auto secondLoc = (reqRepr ? reqRepr->getConstraintRepr()->getStartLoc()
359357
: SourceLoc());
360358
inferRequirements(secondType, secondLoc, moduleForInference, result);
361359
}
@@ -365,6 +363,12 @@ void swift::rewriting::realizeRequirement(
365363
}
366364

367365
case RequirementKind::Layout: {
366+
if (moduleForInference) {
367+
auto firstLoc = (reqRepr ? reqRepr->getSubjectRepr()->getStartLoc()
368+
: SourceLoc());
369+
inferRequirements(firstType, firstLoc, moduleForInference, result);
370+
}
371+
368372
SmallVector<Requirement, 2> reqs;
369373
desugarLayoutRequirement(firstType, req.getLayoutConstraint(), reqs);
370374

@@ -377,6 +381,10 @@ void swift::rewriting::realizeRequirement(
377381
case RequirementKind::SameType: {
378382
auto secondType = req.getSecondType();
379383
if (moduleForInference) {
384+
auto firstLoc = (reqRepr ? reqRepr->getFirstTypeRepr()->getStartLoc()
385+
: SourceLoc());
386+
inferRequirements(firstType, firstLoc, moduleForInference, result);
387+
380388
auto secondLoc = (reqRepr ? reqRepr->getSecondTypeRepr()->getStartLoc()
381389
: SourceLoc());
382390
inferRequirements(secondType, secondLoc, moduleForInference, result);

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: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class InferredGenericSignatureRequestRQM;
3636
class LayoutConstraint;
3737
class ProtocolDecl;
3838
class Requirement;
39+
class RequirementSignatureRequestRQM;
3940
class Type;
4041
class UnifiedStatsReporter;
4142

@@ -47,6 +48,7 @@ class RewriteContext;
4748
class RequirementMachine final {
4849
friend class swift::ASTContext;
4950
friend class swift::rewriting::RewriteContext;
51+
friend class swift::RequirementSignatureRequestRQM;
5052
friend class swift::AbstractGenericSignatureRequestRQM;
5153
friend class swift::InferredGenericSignatureRequestRQM;
5254

@@ -84,17 +86,17 @@ class RequirementMachine final {
8486
RequirementMachine &operator=(RequirementMachine &&) = delete;
8587

8688
void initWithGenericSignature(CanGenericSignature sig);
87-
void initWithProtocols(ArrayRef<const ProtocolDecl *> protos);
89+
CompletionResult initWithProtocols(ArrayRef<const ProtocolDecl *> protos);
8890
void initWithAbstractRequirements(
8991
ArrayRef<GenericTypeParamType *> genericParams,
9092
ArrayRef<Requirement> requirements);
91-
void initWithWrittenRequirements(
93+
CompletionResult initWithWrittenRequirements(
9294
ArrayRef<GenericTypeParamType *> genericParams,
9395
ArrayRef<StructuralRequirement> requirements);
9496

9597
bool isComplete() const;
9698

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

99101
MutableTerm getLongestValidPrefix(const MutableTerm &term) const;
100102

lib/AST/RequirementMachine/RequirementMachineRequests.cpp

Lines changed: 45 additions & 4 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"
@@ -241,16 +242,41 @@ RequirementSignatureRequestRQM::evaluate(Evaluator &evaluator,
241242

242243
// We build requirement signatures for all protocols in a strongly connected
243244
// component at the same time.
244-
auto *machine = ctx.getRewriteContext().getRequirementMachine(proto);
245-
auto requirements = machine->computeMinimalProtocolRequirements();
245+
auto component = ctx.getRewriteContext().getProtocolComponent(proto);
246+
247+
// Heap-allocate the requirement machine to save stack space.
248+
std::unique_ptr<RequirementMachine> machine(new RequirementMachine(
249+
ctx.getRewriteContext()));
250+
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+
}
270+
271+
auto minimalRequirements = machine->computeMinimalProtocolRequirements();
246272

247273
bool debug = machine->getDebugOptions().contains(DebugFlags::Minimization);
248274

249275
// The requirement signature for the actual protocol that the result
250276
// was kicked off with.
251277
ArrayRef<Requirement> result;
252278

253-
for (const auto &pair : requirements) {
279+
for (const auto &pair : minimalRequirements) {
254280
auto *otherProto = pair.first;
255281
const auto &reqs = pair.second;
256282

@@ -393,7 +419,10 @@ InferredGenericSignatureRequestRQM::evaluate(
393419
return false;
394420
};
395421

422+
SourceLoc loc;
396423
if (genericParamList) {
424+
loc = genericParamList->getLAngleLoc();
425+
397426
// Extensions never have a parent signature.
398427
assert(genericParamList->getOuterParameters() == nullptr || !parentSig);
399428

@@ -433,6 +462,9 @@ InferredGenericSignatureRequestRQM::evaluate(
433462
}
434463

435464
if (whereClause) {
465+
if (loc.isInvalid())
466+
loc = whereClause.getLoc();
467+
436468
std::move(whereClause).visitRequirements(
437469
TypeResolutionStage::Structural,
438470
visitRequirement);
@@ -457,7 +489,16 @@ InferredGenericSignatureRequestRQM::evaluate(
457489
std::unique_ptr<RequirementMachine> machine(new RequirementMachine(
458490
ctx.getRewriteContext()));
459491

460-
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+
}
461502

462503
auto minimalRequirements =
463504
machine->computeMinimalGenericSignatureRequirements();

0 commit comments

Comments
 (0)