mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Remove the DeclContext parameter from ResolveMacroRequest, we can now retrieve the DeclContext either from the CustomAttr or macro expansion expr/decl directly.
234 lines
7.0 KiB
C++
234 lines
7.0 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/DiagnosticsSema.h"
|
|
#include "swift/Basic/Assertions.h"
|
|
#include "swift/Basic/Defer.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";
|
|
}
|
|
|
|
// Perform some filtering to avoid emitting duplicate 'through reference here'
|
|
// notes.
|
|
DiagnosticQueue cycleDiags(diags, /*emitOnDestruction*/ true);
|
|
SWIFT_DEFER {
|
|
auto isDefaultStepNote = [](const Diagnostic &diag) -> bool {
|
|
return diag.getID() == diag::circular_reference_through.ID;
|
|
};
|
|
// First populate seen locs for everything except default step notes. If
|
|
// we have a diagnostic or custom note at a given location we want to prefer
|
|
// that over the default step note.
|
|
llvm::DenseSet<SourceLoc> seenLocs;
|
|
cycleDiags.forEach([&](const Diagnostic &diag) {
|
|
if (isDefaultStepNote(diag))
|
|
return;
|
|
|
|
if (auto loc = diag.getLocOrDeclLoc())
|
|
seenLocs.insert(loc);
|
|
});
|
|
// Then we can filter out unnecessary default step notes.
|
|
cycleDiags.filter([&](const Diagnostic &diag) -> bool {
|
|
if (!isDefaultStepNote(diag))
|
|
return true;
|
|
|
|
auto loc = diag.getLocOrDeclLoc();
|
|
return loc && seenLocs.insert(loc).second;
|
|
});
|
|
};
|
|
|
|
request.diagnoseCycle(cycleDiags.getDiags());
|
|
|
|
for (const auto &step : llvm::reverse(activeRequests)) {
|
|
if (step == request) {
|
|
// Note that we diagnosed a cycle for the outermost step to ensure it
|
|
// also returns a cyclic result.
|
|
diagnosedActiveCycles.insert(step);
|
|
return;
|
|
}
|
|
step.noteCycleStep(cycleDiags.getDiags());
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|