Skip to content

Commit 3fe4aaa

Browse files
committed
RequirementMachine: Fix subtle bug in isRecursivelyConstructingRequirementMachine()
I don't have a reduced test case. It was possible for computing the requirement signatures of a connected component to have finished, and yet for the ProtocolDecl::hasComputedRequirementSignature() method to return false, if we had evaluated a RequirementSignatureRequestRQM but not the top-level RequirementSignatureRequest. Instead, track whether we've computed the signatures for a component directly. I don't have a reduced test case. It would arise with associated type inference, which uses this predicate to break nasty cycles.
1 parent 1e821f2 commit 3fe4aaa

File tree

3 files changed

+40
-9
lines changed

3 files changed

+40
-9
lines changed

lib/AST/RequirementMachine/RequirementMachineRequests.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "swift/AST/RequirementSignature.h"
2929
#include "swift/AST/TypeCheckRequests.h"
3030
#include "swift/AST/TypeRepr.h"
31+
#include "swift/Basic/Defer.h"
3132
#include "swift/Basic/Statistic.h"
3233
#include <memory>
3334
#include <vector>
@@ -214,9 +215,15 @@ RequirementSignatureRequestRQM::evaluate(Evaluator &evaluator,
214215
ctx.AllocateCopy(typeAliases));
215216
}
216217

218+
auto &rewriteCtx = ctx.getRewriteContext();
219+
217220
// We build requirement signatures for all protocols in a strongly connected
218221
// component at the same time.
219-
auto component = ctx.getRewriteContext().getProtocolComponent(proto);
222+
auto component = rewriteCtx.startComputingRequirementSignatures(proto);
223+
224+
SWIFT_DEFER {
225+
rewriteCtx.finishComputingRequirementSignatures(proto);
226+
};
220227

221228
// Collect user-written requirements from the protocols in this connected
222229
// component.
@@ -234,7 +241,7 @@ RequirementSignatureRequestRQM::evaluate(Evaluator &evaluator,
234241
for (;;) {
235242
// Heap-allocate the requirement machine to save stack space.
236243
std::unique_ptr<RequirementMachine> machine(new RequirementMachine(
237-
ctx.getRewriteContext()));
244+
rewriteCtx));
238245

239246
auto status = machine->initWithProtocolWrittenRequirements(component, protos);
240247
if (status.first != CompletionResult::Success) {

lib/AST/RequirementMachine/RewriteContext.cpp

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,8 @@ RewriteContext::getProtocolComponentImpl(const ProtocolDecl *proto) {
288288
///
289289
/// This can only be called once, to prevent multiple requirement machines
290290
/// for being built with the same component.
291-
ArrayRef<const ProtocolDecl *> RewriteContext::getProtocolComponent(
291+
ArrayRef<const ProtocolDecl *>
292+
RewriteContext::startComputingRequirementSignatures(
292293
const ProtocolDecl *proto) {
293294
auto &component = getProtocolComponentImpl(proto);
294295

@@ -305,6 +306,17 @@ ArrayRef<const ProtocolDecl *> RewriteContext::getProtocolComponent(
305306
return component.Protos;
306307
}
307308

309+
/// Mark the component as having completed, which will ensure that
310+
/// isRecursivelyComputingRequirementMachine() returns false.
311+
void RewriteContext::finishComputingRequirementSignatures(
312+
const ProtocolDecl *proto) {
313+
auto &component = getProtocolComponentImpl(proto);
314+
315+
assert(component.ComputingRequirementSignatures &&
316+
"Didn't call startComputingRequirementSignatures()");
317+
component.ComputedRequirementSignatures = true;
318+
}
319+
308320
/// Get the list of protocols in the strongly connected component (SCC)
309321
/// of the protocol dependency graph containing the given protocol.
310322
///
@@ -340,11 +352,12 @@ RequirementMachine *RewriteContext::getRequirementMachine(
340352
return newMachine;
341353
}
342354

355+
/// Note: This doesn't use Evaluator::hasActiveRequest(), because in reality
356+
/// the active request could be for any protocol in the connected component.
357+
///
358+
/// Instead, just check a flag set in the component itself.
343359
bool RewriteContext::isRecursivelyConstructingRequirementMachine(
344360
const ProtocolDecl *proto) {
345-
if (proto->isRequirementSignatureComputed())
346-
return false;
347-
348361
auto found = Protos.find(proto);
349362
if (found == Protos.end())
350363
return false;
@@ -353,7 +366,10 @@ bool RewriteContext::isRecursivelyConstructingRequirementMachine(
353366
if (component == Components.end())
354367
return false;
355368

356-
return component->second.ComputingRequirementSignatures;
369+
// If we've started but not finished, we're in the middle of computing
370+
// requirement signatures.
371+
return (component->second.ComputingRequirementSignatures &&
372+
!component->second.ComputedRequirementSignatures);
357373
}
358374

359375
/// We print stats in the destructor, which should get executed at the end of

lib/AST/RequirementMachine/RewriteContext.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,14 @@ class RewriteContext final {
8686
/// The members of this connected component.
8787
ArrayRef<const ProtocolDecl *> Protos;
8888

89-
/// Whether we are currently computing the requirement signatures of
89+
/// Whether we have started computing the requirement signatures of
9090
/// the protocols in this component.
9191
bool ComputingRequirementSignatures = false;
9292

93+
/// Whether we have finished computing the requirement signatures of
94+
/// the protocols in this component.
95+
bool ComputedRequirementSignatures = false;
96+
9397
/// Each connected component has a lazily-created requirement machine
9498
/// built from the requirement signatures of the protocols in this
9599
/// component.
@@ -194,7 +198,11 @@ class RewriteContext final {
194198
RequirementMachine *getRequirementMachine(CanGenericSignature sig);
195199
bool isRecursivelyConstructingRequirementMachine(CanGenericSignature sig);
196200

197-
ArrayRef<const ProtocolDecl *> getProtocolComponent(const ProtocolDecl *proto);
201+
ArrayRef<const ProtocolDecl *>
202+
startComputingRequirementSignatures(const ProtocolDecl *proto);
203+
204+
void finishComputingRequirementSignatures(const ProtocolDecl *proto);
205+
198206
RequirementMachine *getRequirementMachine(const ProtocolDecl *proto);
199207
bool isRecursivelyConstructingRequirementMachine(const ProtocolDecl *proto);
200208

0 commit comments

Comments
 (0)