Skip to content

Commit 57557de

Browse files
committed
[Evaluator] Enforce consistent results for cyclic requests
Record when we encounter a request cycle, and enforce that the outer step of the cycle also returns the default value. This fixes a couple of crashers where we were ending up with conflicting values depending on whether the request was queried from within the cycle or from outside it.
1 parent e7c7239 commit 57557de

File tree

9 files changed

+41
-20
lines changed

9 files changed

+41
-20
lines changed

include/swift/AST/Evaluator.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ class Evaluator {
164164
/// is treated as a stack and is used to detect cycles.
165165
llvm::SetVector<ActiveRequest> activeRequests;
166166

167+
/// A set of active requests that have been diagnosed for a cycle.
168+
llvm::DenseSet<ActiveRequest> diagnosedActiveCycles;
169+
167170
/// A cache that stores the results of requests.
168171
evaluator::RequestCache cache;
169172

@@ -344,7 +347,17 @@ class Evaluator {
344347

345348
recorder.beginRequest<Request>();
346349

347-
auto result = getRequestFunction<Request>()(request, *this);
350+
auto result = [&]() -> typename Request::OutputType {
351+
auto reqResult = getRequestFunction<Request>()(request, *this);
352+
353+
// If we diagnosed a cycle for this request, we want to only use the
354+
// default value to ensure we return a consistent result.
355+
if (!diagnosedActiveCycles.empty() &&
356+
diagnosedActiveCycles.erase(activeReq)) {
357+
return defaultValueFn();
358+
}
359+
return reqResult;
360+
}();
348361

349362
recorder.endRequest<Request>(request);
350363

lib/AST/Decl.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2690,10 +2690,16 @@ bool PatternBindingDecl::hasStorage() const {
26902690

26912691
const PatternBindingEntry *
26922692
PatternBindingDecl::getCheckedPatternBindingEntry(unsigned i) const {
2693-
return evaluateOrDefault(
2693+
auto result = evaluateOrDefault(
26942694
getASTContext().evaluator,
26952695
PatternBindingEntryRequest{const_cast<PatternBindingDecl *>(this), i},
26962696
nullptr);
2697+
2698+
// If we ran into a cycle, we can still return the entry.
2699+
if (!result)
2700+
return &getPatternList()[i];
2701+
2702+
return result;
26972703
}
26982704

26992705
void PatternBindingDecl::setPattern(unsigned i, Pattern *P,
@@ -7382,8 +7388,7 @@ bool ProtocolDecl::existentialConformsToSelf() const {
73827388
}
73837389

73847390
bool ProtocolDecl::hasSelfOrAssociatedTypeRequirements() const {
7385-
// Because we will have considered all the protocols in a cyclic hierarchy by
7386-
// the time the cycle is hit.
7391+
// Be conservative and avoid diagnosing if we hit a cycle.
73877392
const bool resultForCycle = false;
73887393

73897394
return evaluateOrDefault(getASTContext().evaluator,

lib/AST/Evaluator.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,12 @@ void Evaluator::diagnoseCycle(const ActiveRequest &request) {
155155
request.diagnoseCycle(cycleDiags.getDiags());
156156

157157
for (const auto &step : llvm::reverse(activeRequests)) {
158-
if (step == request)
158+
if (step == request) {
159+
// Note that we diagnosed a cycle for the outermost step to ensure it
160+
// also returns a cyclic result.
161+
diagnosedActiveCycles.insert(step);
159162
return;
163+
}
160164
step.noteCycleStep(cycleDiags.getDiags());
161165
}
162166

test/Index/circular.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,17 @@ class Cycle2_C: Cycle2_B {}
2828
// CHECK: RelBase | class/Swift | Cycle2_C | {{[^ ]*}}
2929

3030
class TestCase1: XCTestCase {}
31-
// CHECK: [[@LINE-1]]:7 | class(test)/Swift | TestCase1 | {{[^ ]*}} | Def | rel: 0
31+
// CHECK: [[@LINE-1]]:7 | class/Swift | TestCase1 | {{[^ ]*}} | Def | rel: 0
3232
// CHECK: [[@LINE-2]]:18 | class/Swift | XCTestCase | {{[^ ]*}} | Ref,RelBase | rel: 1
33-
// CHECK: RelBase | class(test)/Swift | TestCase1 | {{[^ ]*}}
33+
// CHECK: RelBase | class/Swift | TestCase1 | {{[^ ]*}}
3434
class XCTestCase: TestCase1 {}
3535
// CHECK: [[@LINE-1]]:7 | class/Swift | XCTestCase | {{[^ ]*}} | Def | rel: 0
36-
// CHECK: [[@LINE-2]]:19 | class(test)/Swift | TestCase1 | {{[^ ]*}} | Ref,RelBase | rel: 1
36+
// CHECK: [[@LINE-2]]:19 | class/Swift | TestCase1 | {{[^ ]*}} | Ref,RelBase | rel: 1
3737
// CHECK: RelBase | class/Swift | XCTestCase | {{[^ ]*}}
3838
class TestCase2: TestCase1 {}
39-
// CHECK: [[@LINE-1]]:7 | class(test)/Swift | TestCase2 | {{[^ ]*}} | Def | rel: 0
40-
// CHECK: [[@LINE-2]]:18 | class(test)/Swift | TestCase1 | {{[^ ]*}} | Ref,RelBase | rel: 1
41-
// CHECK: RelBase | class(test)/Swift | TestCase2 | {{[^ ]*}}
39+
// CHECK: [[@LINE-1]]:7 | class/Swift | TestCase2 | {{[^ ]*}} | Def | rel: 0
40+
// CHECK: [[@LINE-2]]:18 | class/Swift | TestCase1 | {{[^ ]*}} | Ref,RelBase | rel: 1
41+
// CHECK: RelBase | class/Swift | TestCase2 | {{[^ ]*}}
4242

4343
protocol SelfCycleP: SelfCycleP {}
4444
// CHECK: [[@LINE-1]]:10 | protocol/Swift | SelfCycleP | {{[^ ]*}} | Def | rel: 0

test/decl/circularity.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ protocol P1 {
9898
}
9999

100100
class C4 {
101-
required init(x: Int) {}
101+
required init(x: Int) {} // expected-note {{'required' initializer is declared in superclass here}}
102102
}
103103

104104
class D4 : C4, P1 { // expected-note@:7 {{through reference here}}
@@ -107,7 +107,7 @@ class D4 : C4, P1 { // expected-note@:7 {{through reference here}}
107107
// expected-note@-2:17 {{through reference here}}
108108
super.init(x: x)
109109
}
110-
}
110+
} // expected-error {{'required' initializer 'init(x:)' must be provided by subclass of 'C4'}}
111111

112112
// https://github.com/apple/swift/issues/54662
113113
// N.B. This used to compile in 5.1.

test/type/explicit_existential_cyclic_protocol.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33

44
// REQUIRES: swift_feature_ExistentialAny
55

6-
// 'HasSelfOrAssociatedTypeRequirementsRequest' should evaluate to false in
7-
// the event of a cycle because we will have considered all the protocols in a
8-
// cyclic hierarchy by the time the cycle is hit.
6+
// 'HasSelfOrAssociatedTypeRequirementsRequest' evaluates to false in the event
7+
// of a cycle.
98
do {
109
do {
1110
protocol P1 : P2 {}
@@ -30,6 +29,6 @@ do {
3029
// expected-explicit-any-error@-2 1 {{protocol 'P2' refines itself}}
3130

3231
let _: P2
33-
// expected-warning@-1 {{use of protocol 'P2' as a type must be written 'any P2'}}
32+
// expected-explicit-any-warning@-1 {{use of protocol 'P2' as a type must be written 'any P2'}}
3433
}
3534
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
// {"kind":"typecheck","signature":"swift::ConformanceChecker::resolveWitnessViaDerivation(swift::ValueDecl*)","signatureAssert":"Assertion failed: (Conformance->getWitnessUncached(requirement).getDecl() == match.Witness && \"Deduced different witnesses?\"), function recordWitness"}
2-
// RUN: not --crash %target-swift-frontend -typecheck %s
2+
// RUN: not %target-swift-frontend -typecheck %s
33
class a : Codable {
44
b = a(

validation-test/compiler_crashers_2/dcd4ff250d0bde99.swift renamed to validation-test/compiler_crashers_2_fixed/dcd4ff250d0bde99.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// {"kind":"typecheck","signature":"swift::ValueDecl::isObjC() const","signatureAssert":"Assertion failed: (!LazySemanticInfo.isObjCComputed || LazySemanticInfo.isObjC == value), function setIsObjC"}
2-
// RUN: not --crash %target-swift-frontend -typecheck %s
2+
// RUN: not %target-swift-frontend -typecheck %s
33
// REQUIRES: objc_interop
44
protocol a
55
@objc protocol b : c
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
// {"kind":"typecheck","signature":"swift::ASTContext::getSpecializedConformance(swift::Type, swift::NormalProtocolConformance*, swift::SubstitutionMap)","signatureAssert":"Assertion failed: (substitutions.getGenericSignature().getCanonicalSignature() == generic->getGenericSignature().getCanonicalSignature()), function getSpecializedConformance"}
2-
// RUN: not --crash %target-swift-frontend -typecheck %s
2+
// RUN: not %target-swift-frontend -typecheck %s
33
protocol a{ < } protocol b { associatedtype c : a where d == Self }
44
class e<f> : a where f : b, f.c == e

0 commit comments

Comments
 (0)