mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
When reporting the declarations that lead to a cycle, we end up printing an
extra "through reference here" on the function declaration:
class C2: C1, P {
| |- note: through reference here
| `- note: through reference here
15 | // expected-note@-1 2{{through reference here}}
16 | override func run(a: A) {}
| | | `- note: while resolving type 'A'
| | `- note: through reference here
| |- error: circular reference
| |- note: through reference here
| `- note: through reference here
(cherry picked from commit b71cc440c715498114932216d7084fa30657b6bf)
206 lines
5.9 KiB
C++
206 lines
5.9 KiB
C++
//===--- Evaluator.cpp - Request Evaluator Implementation -----------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the Evaluator class that evaluates and caches
|
|
// requests.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "swift/AST/Evaluator.h"
|
|
#include "swift/AST/DeclContext.h"
|
|
#include "swift/AST/DiagnosticEngine.h"
|
|
#include "swift/AST/TypeCheckRequests.h" // for ResolveMacroRequest
|
|
#include "swift/Basic/Assertions.h"
|
|
#include "swift/Basic/LangOptions.h"
|
|
#include "swift/Basic/Range.h"
|
|
#include "swift/Basic/SourceManager.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/SaveAndRestore.h"
|
|
#include <vector>
|
|
|
|
using namespace swift;
|
|
|
|
AbstractRequestFunction *
|
|
Evaluator::getAbstractRequestFunction(uint8_t zoneID, uint8_t requestID) const {
|
|
for (const auto &zone : requestFunctionsByZone) {
|
|
if (zone.first == zoneID) {
|
|
if (requestID < zone.second.size())
|
|
return zone.second[requestID];
|
|
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void Evaluator::registerRequestFunctions(
|
|
Zone zone,
|
|
ArrayRef<AbstractRequestFunction *> functions) {
|
|
uint8_t zoneID = static_cast<uint8_t>(zone);
|
|
#ifndef NDEBUG
|
|
for (const auto &zone : requestFunctionsByZone) {
|
|
assert(zone.first != zoneID);
|
|
}
|
|
#endif
|
|
|
|
requestFunctionsByZone.push_back({zoneID, functions});
|
|
}
|
|
|
|
Evaluator::Evaluator(DiagnosticEngine &diags, const LangOptions &opts)
|
|
: diags(diags),
|
|
debugDumpCycles(opts.DebugDumpCycles),
|
|
recorder(opts.RecordRequestReferences) {}
|
|
|
|
SourceLoc Evaluator::getInnermostSourceLoc(
|
|
llvm::function_ref<bool(SourceLoc)> fn) {
|
|
for (auto request : llvm::reverse(activeRequests)) {
|
|
SourceLoc loc = request.getNearestLoc();
|
|
if (fn(loc))
|
|
return loc;
|
|
}
|
|
|
|
return SourceLoc();
|
|
}
|
|
|
|
bool Evaluator::checkDependency(const ActiveRequest &request) {
|
|
// Record this as an active request.
|
|
if (activeRequests.insert(request)) {
|
|
return false;
|
|
}
|
|
|
|
// Diagnose cycle.
|
|
diagnoseCycle(request);
|
|
return true;
|
|
}
|
|
|
|
void Evaluator::finishedRequest(const ActiveRequest &request) {
|
|
assert(activeRequests.back() == request);
|
|
activeRequests.pop_back();
|
|
}
|
|
|
|
void Evaluator::diagnoseCycle(const ActiveRequest &request) {
|
|
if (debugDumpCycles) {
|
|
const auto printIndent = [](llvm::raw_ostream &OS, unsigned indent) {
|
|
OS.indent(indent);
|
|
OS << "`--";
|
|
};
|
|
|
|
unsigned indent = 1;
|
|
auto &OS = llvm::errs();
|
|
|
|
OS << "===CYCLE DETECTED===\n";
|
|
for (const auto &step : activeRequests) {
|
|
printIndent(OS, indent);
|
|
if (step == request) {
|
|
OS.changeColor(llvm::raw_ostream::GREEN);
|
|
simple_display(OS, step);
|
|
OS.resetColor();
|
|
} else {
|
|
simple_display(OS, step);
|
|
}
|
|
OS << "\n";
|
|
indent += 4;
|
|
}
|
|
|
|
printIndent(OS, indent);
|
|
OS.changeColor(llvm::raw_ostream::GREEN);
|
|
simple_display(OS, request);
|
|
|
|
OS.changeColor(llvm::raw_ostream::RED);
|
|
OS << " (cyclic dependency)";
|
|
OS.resetColor();
|
|
|
|
OS << "\n";
|
|
}
|
|
|
|
request.diagnoseCycle(diags);
|
|
for (const auto &step : llvm::reverse(activeRequests)) {
|
|
if (step == request) return;
|
|
|
|
// Reporting the lifetime dependence location generates a redundant
|
|
// diagnostic.
|
|
if (step.getAs<LifetimeDependenceInfoRequest>()) {
|
|
continue;
|
|
}
|
|
|
|
step.noteCycleStep(diags);
|
|
}
|
|
|
|
llvm_unreachable("Diagnosed a cycle but it wasn't represented in the stack");
|
|
}
|
|
|
|
void evaluator::DependencyRecorder::recordDependency(
|
|
const DependencyCollector::Reference &ref) {
|
|
if (activeRequestReferences.empty())
|
|
return;
|
|
|
|
activeRequestReferences.back().insert(ref);
|
|
}
|
|
|
|
evaluator::DependencyCollector::DependencyCollector(
|
|
evaluator::DependencyRecorder &parent) : parent(parent) {
|
|
#ifndef NDEBUG
|
|
assert(!parent.isRecording &&
|
|
"Probably not a good idea to allow nested recording");
|
|
parent.isRecording = true;
|
|
#endif
|
|
}
|
|
|
|
evaluator::DependencyCollector::~DependencyCollector() {
|
|
#ifndef NDEBUG
|
|
assert(parent.isRecording &&
|
|
"Should have been recording this whole time");
|
|
parent.isRecording = false;
|
|
#endif
|
|
}
|
|
|
|
void evaluator::DependencyCollector::addUsedMember(DeclContext *subject,
|
|
DeclBaseName name) {
|
|
assert(subject->isTypeContext());
|
|
return parent.recordDependency(Reference::usedMember(subject, name));
|
|
}
|
|
|
|
void evaluator::DependencyCollector::addPotentialMember(DeclContext *subject) {
|
|
assert(subject->isTypeContext());
|
|
return parent.recordDependency(Reference::potentialMember(subject));
|
|
}
|
|
|
|
void evaluator::DependencyCollector::addTopLevelName(DeclBaseName name) {
|
|
return parent.recordDependency(Reference::topLevel(name));
|
|
}
|
|
|
|
void evaluator::DependencyCollector::addDynamicLookupName(DeclBaseName name) {
|
|
return parent.recordDependency(Reference::dynamic(name));
|
|
}
|
|
|
|
void evaluator::DependencyRecorder::enumerateReferencesInFile(
|
|
const SourceFile *SF, ReferenceEnumerator f) const {
|
|
auto entry = fileReferences.find(SF);
|
|
if (entry == fileReferences.end()) {
|
|
return;
|
|
}
|
|
|
|
for (const auto &ref : entry->getSecond()) {
|
|
switch (ref.kind) {
|
|
case DependencyCollector::Reference::Kind::Empty:
|
|
case DependencyCollector::Reference::Kind::Tombstone:
|
|
llvm_unreachable("Cannot enumerate dead reference!");
|
|
case DependencyCollector::Reference::Kind::UsedMember:
|
|
case DependencyCollector::Reference::Kind::PotentialMember:
|
|
case DependencyCollector::Reference::Kind::TopLevel:
|
|
case DependencyCollector::Reference::Kind::Dynamic:
|
|
f(ref);
|
|
}
|
|
}
|
|
}
|