Skip to content

Commit b1b3c43

Browse files
committed
[Request-evaluator] Add -debug-cycles flag to print cycles.
As a debugging aid, introduce a new frontend flag `-debug-cycles` that will emit a debug dump whenever the request-evaluator encounters a cyclic dependency, while otherwise allowing compilation to continue.
1 parent 2deea3c commit b1b3c43

File tree

9 files changed

+79
-7
lines changed

9 files changed

+79
-7
lines changed

include/swift/AST/Evaluator.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "swift/AST/AnyRequest.h"
2222
#include "swift/Basic/AnyValue.h"
23+
#include "swift/Basic/CycleDiagnosticKind.h"
2324
#include "swift/Basic/Defer.h"
2425
#include "llvm/ADT/DenseMap.h"
2526
#include "llvm/ADT/Optional.h"
@@ -124,7 +125,7 @@ class Evaluator {
124125
DiagnosticEngine &diags;
125126

126127
/// Whether to diagnose cycles or ignore them completely.
127-
bool shouldDiagnoseCycles;
128+
CycleDiagnosticKind shouldDiagnoseCycles;
128129

129130
/// A vector containing all of the active evaluation requests, which
130131
/// is treated as a stack and is used to detect cycles.
@@ -147,7 +148,7 @@ class Evaluator {
147148
public:
148149
/// Construct a new evaluator that can emit cyclic-dependency
149150
/// diagnostics through the given diagnostics engine.
150-
Evaluator(DiagnosticEngine &diags, bool shouldDiagnoseCycles);
151+
Evaluator(DiagnosticEngine &diags, CycleDiagnosticKind shouldDiagnoseCycles);
151152

152153
/// Evaluate the given request and produce its result,
153154
/// consulting/populating the cache as required.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===--- CycleDiagnosticKind.h - Cycle Diagnostic Kind ----------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file defines the CycleDiagnosticKind enum.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef CycleDiagnosticKind_h
18+
#define CycleDiagnosticKind_h
19+
20+
namespace swift {
21+
22+
/// How to diagnose cycles when they are encountered during evaluation.
23+
enum class CycleDiagnosticKind {
24+
/// Don't diagnose cycles at all.
25+
NoDiagnose,
26+
/// Diagnose cycles as full-fledged errors.
27+
FullDiagnose,
28+
/// Diagnose cycles via debugging dumps.
29+
DebugDiagnose,
30+
};
31+
32+
} // end namespace swift
33+
34+
#endif /* CycleDiagnosticKind_h */

include/swift/Basic/LangOptions.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#define SWIFT_BASIC_LANGOPTIONS_H
2020

2121
#include "swift/Config.h"
22+
#include "swift/Basic/CycleDiagnosticKind.h"
2223
#include "swift/Basic/LLVM.h"
2324
#include "swift/Basic/Version.h"
2425
#include "clang/Basic/VersionTuple.h"
@@ -168,6 +169,10 @@ namespace swift {
168169
/// This is for testing purposes.
169170
std::string DebugForbidTypecheckPrefix;
170171

172+
/// \brief How to diagnose cycles encountered
173+
CycleDiagnosticKind EvaluatorCycleDiagnostics =
174+
CycleDiagnosticKind::NoDiagnose;
175+
171176
/// \brief The upper bound, in bytes, of temporary data that can be
172177
/// allocated by the constraint solver.
173178
unsigned SolverMemoryThreshold = 512 * 1024 * 1024;

include/swift/Option/FrontendOptions.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ def debug_forbid_typecheck_prefix : Separate<["-"], "debug-forbid-typecheck-pref
167167
HelpText<"Triggers llvm fatal_error if typechecker tries to typecheck a decl "
168168
"with the provided prefix name">;
169169

170+
def debug_cycles : Flag<["-"], "debug-cycles">,
171+
HelpText<"Print out debug dumps when cycles are detected in evaluation">;
172+
170173
def debug_time_compilation : Flag<["-"], "debug-time-compilation">,
171174
HelpText<"Prints the time taken by each compilation phase">;
172175
def debug_time_function_bodies : Flag<["-"], "debug-time-function-bodies">,

lib/AST/ASTContext.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ ASTContext::ASTContext(LangOptions &langOpts, SearchPathOptions &SearchPathOpts,
463463
SearchPathOpts(SearchPathOpts),
464464
SourceMgr(SourceMgr),
465465
Diags(Diags),
466-
evaluator(Diags, /*shouldDiagnoseCycles=*/false),
466+
evaluator(Diags, langOpts.EvaluatorCycleDiagnostics),
467467
TheBuiltinModule(createBuiltinModule(*this)),
468468
StdlibModuleName(getIdentifier(STDLIB_NAME)),
469469
SwiftShimsModuleName(getIdentifier(SWIFT_SHIMS_NAME)),

lib/AST/Evaluator.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ std::string AnyRequest::getAsString() const {
3232
return result;
3333
}
3434

35-
Evaluator::Evaluator(DiagnosticEngine &diags, bool shouldDiagnoseCycles)
35+
Evaluator::Evaluator(DiagnosticEngine &diags,
36+
CycleDiagnosticKind shouldDiagnoseCycles)
3637
: diags(diags), shouldDiagnoseCycles(shouldDiagnoseCycles) { }
3738

3839
bool Evaluator::checkDependency(const AnyRequest &request) {
@@ -45,8 +46,23 @@ bool Evaluator::checkDependency(const AnyRequest &request) {
4546
return false;
4647
}
4748

48-
if (shouldDiagnoseCycles)
49+
switch (shouldDiagnoseCycles) {
50+
case CycleDiagnosticKind::NoDiagnose:
51+
return true;
52+
53+
case CycleDiagnosticKind::DebugDiagnose: {
54+
llvm::dbgs() << "===CYCLE DETECTED===\n";
55+
llvm::DenseSet<AnyRequest> visited;
56+
std::string prefixStr;
57+
printDependencies(activeRequests.front(), llvm::dbgs(), visited,
58+
prefixStr, /*lastChild=*/true);
59+
return true;
60+
}
61+
62+
case CycleDiagnosticKind::FullDiagnose:
4963
diagnoseCycle(request);
64+
return true;
65+
}
5066

5167
return true;
5268
}

lib/Frontend/CompilerInvocation.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,10 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
253253
Opts.DebugForbidTypecheckPrefix = A->getValue();
254254
}
255255

256+
if (Args.getLastArg(OPT_debug_cycles)) {
257+
Opts.EvaluatorCycleDiagnostics = CycleDiagnosticKind::DebugDiagnose;
258+
}
259+
256260
if (const Arg *A = Args.getLastArg(OPT_solver_memory_threshold)) {
257261
unsigned threshold;
258262
if (StringRef(A->getValue()).getAsInteger(10, threshold)) {

test/decl/class/circular_inheritance.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
// RUN: %target-typecheck-verify-swift
2+
// RUN: not %target-swift-frontend -typecheck -debug-cycles %s 2> %t.cycles
3+
// RUN: %FileCheck %s < %t.cycles
24

35
class C : B { } // expected-error{{circular class inheritance 'C' -> 'B' -> 'A' -> 'C'}}
46
class B : A { } // expected-note{{class 'B' declared here}}
@@ -37,3 +39,10 @@ class Outer3
3739
: Outer3.Inner<Int> {
3840
class Inner<T> {}
3941
}
42+
43+
// CHECK: ===CYCLE DETECTED===
44+
// CHECK-NEXT: `--SuperclassTypeRequest
45+
// CHECK-NEXT: `--InheritedTypeRequest(circular_inheritance.(file).Left@
46+
// CHECK-NEXT: `--SuperclassTypeRequest
47+
// CHECK-NEXT: `--InheritedTypeRequest(circular_inheritance.(file).Right@
48+
// CHECK-NEXT: `--SuperclassTypeRequest{{.*}}(cyclic dependency)

unittests/AST/ArithmeticEvaluator.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ TEST(ArithmeticEvaluator, Simple) {
200200

201201
SourceManager sourceMgr;
202202
DiagnosticEngine diags(sourceMgr);
203-
Evaluator evaluator(diags, /*shouldDiagnoseCycles=*/true);
203+
Evaluator evaluator(diags, CycleDiagnosticKind::FullDiagnose);
204204

205205
const double expectedResult = (3.14159 + 2.71828) * 42.0;
206206
EXPECT_EQ(evaluator(InternallyCachedEvaluationRule(product)),
@@ -320,7 +320,7 @@ TEST(ArithmeticEvaluator, Cycle) {
320320

321321
SourceManager sourceMgr;
322322
DiagnosticEngine diags(sourceMgr);
323-
Evaluator evaluator(diags, /*shouldDiagnoseCycles=*/true);
323+
Evaluator evaluator(diags, CycleDiagnosticKind::FullDiagnose);
324324

325325
// Evaluate when there is a cycle.
326326
UncachedEvaluationRule::brokeCycle = false;

0 commit comments

Comments
 (0)