mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Remove SimpleRequest::breakCycle
This patch removes the need for Request objects to provide a default cycle-breaking value, instead opting to return llvm::Expected so clients must handle a cycle failure explicitly. Currently, all clients do the 'default' behavior, but this opens the possibility for future requests to handle failures explicitly.
This commit is contained in:
committed by
Harlan Haskins
parent
be0e1643d6
commit
5a6985f39e
@@ -42,11 +42,11 @@ private:
|
||||
friend class SimpleRequest;
|
||||
|
||||
// Evaluation.
|
||||
AccessLevel evaluate(Evaluator &evaluator, ValueDecl *decl) const;
|
||||
llvm::Expected<AccessLevel> evaluate(Evaluator &evaluator,
|
||||
ValueDecl *decl) const;
|
||||
|
||||
public:
|
||||
// Cycle handling
|
||||
AccessLevel breakCycle() const { return AccessLevel::Private; }
|
||||
void diagnoseCycle(DiagnosticEngine &diags) const;
|
||||
void noteCycleStep(DiagnosticEngine &diags) const;
|
||||
|
||||
@@ -71,11 +71,11 @@ private:
|
||||
friend class SimpleRequest;
|
||||
|
||||
// Evaluation.
|
||||
AccessLevel evaluate(Evaluator &evaluator, AbstractStorageDecl *decl) const;
|
||||
llvm::Expected<AccessLevel>
|
||||
evaluate(Evaluator &evaluator, AbstractStorageDecl *decl) const;
|
||||
|
||||
public:
|
||||
// Cycle handling
|
||||
AccessLevel breakCycle() const { return AccessLevel::Private; }
|
||||
void diagnoseCycle(DiagnosticEngine &diags) const;
|
||||
void noteCycleStep(DiagnosticEngine &diags) const;
|
||||
|
||||
@@ -98,14 +98,11 @@ private:
|
||||
friend class SimpleRequest;
|
||||
|
||||
// Evaluation.
|
||||
DefaultAndMax
|
||||
llvm::Expected<DefaultAndMax>
|
||||
evaluate(Evaluator &evaluator, ExtensionDecl *decl) const;
|
||||
|
||||
public:
|
||||
// Cycle handling
|
||||
DefaultAndMax
|
||||
breakCycle() const { return std::make_pair(AccessLevel::Private,
|
||||
AccessLevel::Private); }
|
||||
void diagnoseCycle(DiagnosticEngine &diags) const;
|
||||
void noteCycleStep(DiagnosticEngine &diags) const;
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/SetVector.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/PrettyStackTrace.h"
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
@@ -52,7 +53,7 @@ using AbstractRequestFunction = void(void);
|
||||
/// Form the specific request function for the given request type.
|
||||
template<typename Request>
|
||||
using RequestFunction =
|
||||
typename Request::OutputType(const Request &, Evaluator &);
|
||||
llvm::Expected<typename Request::OutputType>(const Request &, Evaluator &);
|
||||
|
||||
/// Pretty stack trace handler for an arbitrary request.
|
||||
template<typename Request>
|
||||
@@ -69,6 +70,49 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// An llvm::ErrorInfo container for a request in which a cycle was detected
|
||||
/// and diagnosed.
|
||||
template <typename Request>
|
||||
struct CyclicalRequestError :
|
||||
public llvm::ErrorInfo<CyclicalRequestError<Request>> {
|
||||
public:
|
||||
static char ID;
|
||||
const Request &request;
|
||||
|
||||
CyclicalRequestError(const Request &request): request(request) {}
|
||||
|
||||
virtual void log(llvm::raw_ostream &out) const {
|
||||
out << "Cycle detected:\n";
|
||||
simple_display(out, request);
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
virtual std::error_code convertToErrorCode() const {
|
||||
// This is essentially unused, but is a temporary requirement for
|
||||
// llvm::ErrorInfo subclasses.
|
||||
llvm_unreachable("shouldn't get std::error_code from CyclicalRequestError");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Request>
|
||||
char CyclicalRequestError<Request>::ID = '\0';
|
||||
|
||||
/// Evaluates a given request or returns a default value if a cycle is detected.
|
||||
template <typename Request>
|
||||
typename Request::OutputType
|
||||
evaluateOrDefault(
|
||||
Evaluator &eval, Request req, typename Request::OutputType def) {
|
||||
auto result = eval(req);
|
||||
if (auto err = result.takeError()) {
|
||||
llvm::handleAllErrors(std::move(err),
|
||||
[](const CyclicalRequestError<Request> &E) {
|
||||
// cycle detected
|
||||
});
|
||||
return def;
|
||||
}
|
||||
return *result;
|
||||
}
|
||||
|
||||
/// Report that a request of the given kind is being evaluated, so it
|
||||
/// can be recorded by the stats reporter.
|
||||
template<typename Request>
|
||||
@@ -101,7 +145,6 @@ void reportEvaluatedRequest(UnifiedStatsReporter &stats,
|
||||
///
|
||||
/// void diagnoseCycle(DiagnosticEngine &diags) const;
|
||||
/// void noteCycleStep(DiagnosticEngine &diags) const;
|
||||
/// OutputType breakCycle() const;
|
||||
/// - Caching policy:
|
||||
///
|
||||
/// static const bool isEverCached;
|
||||
@@ -211,10 +254,13 @@ public:
|
||||
/// Evaluate the given request and produce its result,
|
||||
/// consulting/populating the cache as required.
|
||||
template<typename Request>
|
||||
typename Request::OutputType operator()(const Request &request) {
|
||||
llvm::Expected<typename Request::OutputType>
|
||||
operator()(const Request &request) {
|
||||
// Check for a cycle.
|
||||
if (checkDependency(AnyRequest(request)))
|
||||
return request.breakCycle();
|
||||
if (checkDependency(AnyRequest(request))) {
|
||||
return llvm::Error(
|
||||
llvm::make_unique<CyclicalRequestError<Request>>(request));
|
||||
}
|
||||
|
||||
// Make sure we remove this from the set of active requests once we're
|
||||
// done.
|
||||
@@ -232,9 +278,10 @@ public:
|
||||
/// Use this to describe cases where there are multiple (known)
|
||||
/// requests that all need to be satisfied.
|
||||
template<typename ...Requests>
|
||||
std::tuple<typename Requests::OutputType...>
|
||||
std::tuple<llvm::Expected<typename Requests::OutputType>...>
|
||||
operator()(const Requests &...requests) {
|
||||
return std::tuple<typename Requests::OutputType...>((*this)(requests)...);
|
||||
return std::tuple<llvm::Expected<typename Requests::OutputType>...>(
|
||||
(*this)(requests)...);
|
||||
}
|
||||
|
||||
/// Clear the cache stored within this evaluator.
|
||||
@@ -260,7 +307,8 @@ private:
|
||||
/// be cached.
|
||||
template<typename Request,
|
||||
typename std::enable_if<Request::isEverCached>::type * = nullptr>
|
||||
typename Request::OutputType getResult(const Request &request) {
|
||||
llvm::Expected<typename Request::OutputType>
|
||||
getResult(const Request &request) {
|
||||
// The request can be cached, but check a predicate to determine
|
||||
// whether this particular instance is cached. This allows more
|
||||
// fine-grained control over which instances get cache.
|
||||
@@ -274,13 +322,15 @@ private:
|
||||
/// will never be cached.
|
||||
template<typename Request,
|
||||
typename std::enable_if<!Request::isEverCached>::type * = nullptr>
|
||||
typename Request::OutputType getResult(const Request &request) {
|
||||
llvm::Expected<typename Request::OutputType>
|
||||
getResult(const Request &request) {
|
||||
return getResultUncached(request);
|
||||
}
|
||||
|
||||
/// Produce the result of the request without caching.
|
||||
template<typename Request>
|
||||
typename Request::OutputType getResultUncached(const Request &request) {
|
||||
llvm::Expected<typename Request::OutputType>
|
||||
getResultUncached(const Request &request) {
|
||||
// Clear out the dependencies on this request; we're going to recompute
|
||||
// them now anyway.
|
||||
dependencies[AnyRequest(request)].clear();
|
||||
@@ -298,7 +348,8 @@ private:
|
||||
/// and detect recursion.
|
||||
template<typename Request,
|
||||
typename std::enable_if<Request::hasExternalCache>::type * = nullptr>
|
||||
typename Request::OutputType getResultCached(const Request &request) {
|
||||
llvm::Expected<typename Request::OutputType>
|
||||
getResultCached(const Request &request) {
|
||||
// If there is a cached result, return it.
|
||||
if (auto cached = request.getCachedResult())
|
||||
return *cached;
|
||||
@@ -306,8 +357,11 @@ private:
|
||||
// Compute the result.
|
||||
auto result = getResultUncached(request);
|
||||
|
||||
// Cache the result.
|
||||
request.cacheResult(result);
|
||||
// Cache the result if applicable.
|
||||
if (!result)
|
||||
return result;
|
||||
|
||||
request.cacheResult(*result);
|
||||
|
||||
// Return it.
|
||||
return result;
|
||||
@@ -319,6 +373,9 @@ private:
|
||||
typename Request,
|
||||
typename std::enable_if<!Request::hasExternalCache>::type * = nullptr>
|
||||
typename Request::OutputType getResultCached(const Request &request) {
|
||||
llvm::Expected<typename Request::OutputType>
|
||||
getResultCached(const Request &request) {
|
||||
AnyRequest anyRequest{request};
|
||||
// If we already have an entry for this request in the cache, return it.
|
||||
auto known = cache.find_as(request);
|
||||
if (known != cache.end()) {
|
||||
@@ -327,9 +384,11 @@ private:
|
||||
|
||||
// Compute the result.
|
||||
auto result = getResultUncached(request);
|
||||
if (!result)
|
||||
return result;
|
||||
|
||||
// Cache the result.
|
||||
cache.insert({AnyRequest(request), result});
|
||||
cache.insert({AnyRequest(request), *result});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,6 @@ public:
|
||||
bool isCached() const { return true; }
|
||||
|
||||
// Cycle handling
|
||||
DirectlyReferencedTypeDecls breakCycle() const { return { }; }
|
||||
void diagnoseCycle(DiagnosticEngine &diags) const;
|
||||
void noteCycleStep(DiagnosticEngine &diags) const;
|
||||
};
|
||||
@@ -127,7 +126,6 @@ public:
|
||||
bool isCached() const { return true; }
|
||||
|
||||
// Cycle handling
|
||||
DirectlyReferencedTypeDecls breakCycle() const { return { }; }
|
||||
void diagnoseCycle(DiagnosticEngine &diags) const;
|
||||
void noteCycleStep(DiagnosticEngine &diags) const;
|
||||
};
|
||||
@@ -145,14 +143,14 @@ private:
|
||||
friend class SimpleRequest;
|
||||
|
||||
// Evaluation.
|
||||
ClassDecl *evaluate(Evaluator &evaluator, NominalTypeDecl *subject) const;
|
||||
llvm::Expected<ClassDecl *>
|
||||
evaluate(Evaluator &evaluator, NominalTypeDecl *subject) const;
|
||||
|
||||
public:
|
||||
// Caching
|
||||
bool isCached() const { return true; }
|
||||
|
||||
// Cycle handling
|
||||
ClassDecl *breakCycle() const { return nullptr; }
|
||||
void diagnoseCycle(DiagnosticEngine &diags) const;
|
||||
void noteCycleStep(DiagnosticEngine &diags) const;
|
||||
};
|
||||
@@ -170,7 +168,8 @@ private:
|
||||
friend class SimpleRequest;
|
||||
|
||||
// Evaluation.
|
||||
NominalTypeDecl *evaluate(Evaluator &evaluator, ExtensionDecl *ext) const;
|
||||
llvm::Expected<NominalTypeDecl *>
|
||||
evaluate(Evaluator &evaluator, ExtensionDecl *ext) const;
|
||||
|
||||
public:
|
||||
// Separate caching.
|
||||
@@ -179,7 +178,6 @@ public:
|
||||
void cacheResult(NominalTypeDecl *value) const;
|
||||
|
||||
// Cycle handling
|
||||
NominalTypeDecl *breakCycle() const { return nullptr; }
|
||||
void diagnoseCycle(DiagnosticEngine &diags) const;
|
||||
void noteCycleStep(DiagnosticEngine &diags) const;
|
||||
};
|
||||
|
||||
@@ -54,15 +54,9 @@ enum class CacheKind {
|
||||
///
|
||||
/// The \c Derived class needs to implement several operations. The most
|
||||
/// important one takes an evaluator and the input values, then computes the
|
||||
/// final result:
|
||||
/// final result, optionally bubbling up errors from recursive evaulations:
|
||||
/// \code
|
||||
/// Output evaluate(Evaluator &evaluator, Inputs...) const;
|
||||
/// \endcode
|
||||
///
|
||||
/// The \c Derived class will also need to implement an operation to break a
|
||||
/// cycle if one is found, i.e.,
|
||||
/// \code
|
||||
/// OutputType breakCycle() const;
|
||||
/// llvm::Expected<Output> evaluate(Evaluator &evaluator, Inputs...) const;
|
||||
/// \endcode
|
||||
///
|
||||
/// Cycle diagnostics can be handled in one of two ways. Either the \c Derived
|
||||
@@ -103,8 +97,8 @@ class SimpleRequest {
|
||||
}
|
||||
|
||||
template<size_t ...Indices>
|
||||
Output callDerived(Evaluator &evaluator,
|
||||
llvm::index_sequence<Indices...>) const {
|
||||
llvm::Expected<Output>
|
||||
callDerived(Evaluator &evaluator, llvm::index_sequence<Indices...>) const {
|
||||
static_assert(sizeof...(Indices) > 0, "Subclass must define evaluate()");
|
||||
return asDerived().evaluate(evaluator, std::get<Indices>(storage)...);
|
||||
}
|
||||
@@ -131,8 +125,8 @@ public:
|
||||
: storage(inputs...) { }
|
||||
|
||||
/// Request evaluation function that will be registered with the evaluator.
|
||||
static OutputType evaluateRequest(const Derived &request,
|
||||
Evaluator &evaluator) {
|
||||
static llvm::Expected<OutputType>
|
||||
evaluateRequest(const Derived &request, Evaluator &evaluator) {
|
||||
return request.callDerived(evaluator,
|
||||
llvm::index_sequence_for<Inputs...>());
|
||||
}
|
||||
|
||||
@@ -52,13 +52,13 @@ private:
|
||||
friend class SimpleRequest;
|
||||
|
||||
// Evaluation.
|
||||
Type evaluate(Evaluator &evaluator,
|
||||
llvm::PointerUnion<TypeDecl *, ExtensionDecl *> decl,
|
||||
unsigned index) const;
|
||||
llvm::Expected<Type>
|
||||
evaluate(Evaluator &evaluator,
|
||||
llvm::PointerUnion<TypeDecl *, ExtensionDecl *> decl,
|
||||
unsigned index) const;
|
||||
|
||||
public:
|
||||
// Cycle handling
|
||||
Type breakCycle() const { return Type(); }
|
||||
void diagnoseCycle(DiagnosticEngine &diags) const;
|
||||
void noteCycleStep(DiagnosticEngine &diags) const;
|
||||
|
||||
@@ -81,11 +81,11 @@ private:
|
||||
friend class SimpleRequest;
|
||||
|
||||
// Evaluation.
|
||||
Type evaluate(Evaluator &evaluator, NominalTypeDecl *classDecl) const;
|
||||
llvm::Expected<Type>
|
||||
evaluate(Evaluator &evaluator, NominalTypeDecl *classDecl) const;
|
||||
|
||||
public:
|
||||
// Cycle handling
|
||||
Type breakCycle() const { return Type(); }
|
||||
void diagnoseCycle(DiagnosticEngine &diags) const;
|
||||
void noteCycleStep(DiagnosticEngine &diags) const;
|
||||
|
||||
@@ -108,11 +108,11 @@ private:
|
||||
friend class SimpleRequest;
|
||||
|
||||
// Evaluation.
|
||||
Type evaluate(Evaluator &evaluator, EnumDecl *enumDecl) const;
|
||||
llvm::Expected<Type>
|
||||
evaluate(Evaluator &evaluator, EnumDecl *enumDecl) const;
|
||||
|
||||
public:
|
||||
// Cycle handling
|
||||
Type breakCycle() const { return Type(); }
|
||||
void diagnoseCycle(DiagnosticEngine &diags) const;
|
||||
void noteCycleStep(DiagnosticEngine &diags) const;
|
||||
|
||||
@@ -136,12 +136,11 @@ private:
|
||||
friend class SimpleRequest;
|
||||
|
||||
// Evaluation.
|
||||
llvm::TinyPtrVector<ValueDecl *> evaluate(Evaluator &evaluator,
|
||||
ValueDecl *decl) const;
|
||||
llvm::Expected<llvm::TinyPtrVector<ValueDecl *>>
|
||||
evaluate(Evaluator &evaluator, ValueDecl *decl) const;
|
||||
|
||||
public:
|
||||
// Cycle handling
|
||||
llvm::TinyPtrVector<ValueDecl *> breakCycle() const { return { }; }
|
||||
void diagnoseCycle(DiagnosticEngine &diags) const;
|
||||
void noteCycleStep(DiagnosticEngine &diags) const;
|
||||
|
||||
@@ -164,11 +163,10 @@ private:
|
||||
friend class SimpleRequest;
|
||||
|
||||
// Evaluation.
|
||||
bool evaluate(Evaluator &evaluator, ValueDecl *decl) const;
|
||||
llvm::Expected<bool> evaluate(Evaluator &evaluator, ValueDecl *decl) const;
|
||||
|
||||
public:
|
||||
// Cycle handling
|
||||
bool breakCycle() const;
|
||||
void diagnoseCycle(DiagnosticEngine &diags) const;
|
||||
void noteCycleStep(DiagnosticEngine &diags) const;
|
||||
|
||||
@@ -191,11 +189,10 @@ private:
|
||||
friend class SimpleRequest;
|
||||
|
||||
// Evaluation.
|
||||
bool evaluate(Evaluator &evaluator, ValueDecl *decl) const;
|
||||
llvm::Expected<bool> evaluate(Evaluator &evaluator, ValueDecl *decl) const;
|
||||
|
||||
public:
|
||||
// Cycle handling
|
||||
bool breakCycle() const;
|
||||
void diagnoseCycle(DiagnosticEngine &diags) const;
|
||||
void noteCycleStep(DiagnosticEngine &diags) const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user