Files
swift-mirror/include/swift/AST/SimpleRequest.h
Doug Gregor a6f46584ec [Evaluator] Indirect evaluation of uncached requests through a separate table.
The bundling of the form of a request (e.g., the storage that makes up a request)
with the function that evaluates the request value requires us to perform
ad hoc indirection to address the AST —> Sema layering violation. For
example, ClassDecl::getSuperclass() calls through the LazyResolver (when
available) to form the appropriate request. This means that we cannot
use the the request-evaluator’s cache when LazyResolver is null, forcing
all cached state into the AST.

Provide the evaluator with a zone-based registration system, where each
request “zone” (e.g., the type checker’s requests) registers
callbacks to evaluate each kind of request within that zone. The
evaluator indirects through this table of function pointers, allowing
the request classes themselves to be available at a lower level (AST)
than the functions that perform the computation when the value isn’t
in the cache (e.g., Sema).

We are not taking advantage of the indirection yet; that’ll come in a
follow-up commit.
2018-06-29 15:41:55 -07:00

173 lines
5.9 KiB
C++

//===--- SimpleRequest.h - Simple Request Instances -------------*- C++ -*-===//
//
// 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 defines the SimpleRequest class template, which makes it easier
// to define new request kinds.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_AST_SIMPLEREQUEST_H
#define SWIFT_AST_SIMPLEREQUEST_H
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticsCommon.h"
#include "swift/Basic/SimpleDisplay.h"
#include "swift/Basic/TypeID.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/STLExtras.h"
#include <tuple>
namespace swift {
class Evaluator;
/// Describes how the result for a particular request will be cached.
enum class CacheKind {
/// The result for a particular request should never be cached.
Uncached,
/// The result for a particular request should be cached within the
/// evaluator itself.
Cached,
/// The result of a particular request will be cached via some separate
/// mechanism, such as a mutable data structure.
SeparatelyCached,
};
/// CRTP base class that describes a request operation that takes values
/// with the given input types (\c Inputs...) and produces an output of
/// the given type.
///
/// \tparam Derived The final, derived class type for the request.
/// \tparam Caching Describes how the output value is cached, if at all.
/// \tparam Output The type of the result produced by evaluating this request.
/// \tparam Inputs The types of the inputs to this request, i.e., the values
/// that comprise the request itself. These will determine the uniqueness of
/// the request.
///
/// 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:
/// \code
/// Output operator()(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;
/// \endcode
///
/// Cycle diagnostics can be handled in one of two ways. Either the \c Derived
/// class can implement the two cycle-diagnosing operations directly:
/// \code
/// void diagnoseCycle(DiagnosticEngine &diags) const;
/// void noteCycleStep(DiagnosticEngine &diags) const;
/// \endcode
///
/// Or the \c Derived class can provide a "diagnostic location" operation and
/// diagnostic values for the main cycle diagnostic and a "note" describing a
/// step within the chain of diagnostics:
/// \code
/// T getCycleDiagnosticLoc(Inputs...) const;
/// static constexpr Diag<Inputs...> cycleDiagnostic = ...;
/// static constexpr Diag<Inputs...> cycleStepDiagnostic = ...;
/// \endcode
///
/// Value caching is determined by the \c Caching parameter. When
/// \c Caching == CacheKind::SeparatelyCached, the \c Derived class is
/// responsible for implementing the two operations responsible to managing
/// the cache:
/// \code
/// Optional<Output> getCachedResult() const;
/// void cacheResult(Output value) const;
/// \endcode
template<typename Derived, CacheKind Caching, typename Output,
typename ...Inputs>
class SimpleRequest {
std::tuple<Inputs...> storage;
Derived &asDerived() {
return *static_cast<Derived *>(this);
}
const Derived &asDerived() const {
return *static_cast<const Derived *>(this);
}
template<size_t ...Indices>
Output callDerived(Evaluator &evaluator,
llvm::index_sequence<Indices...>) const {
static_assert(sizeof...(Indices) > 0, "Subclass must define operator()");
return asDerived()(evaluator, std::get<Indices>(storage)...);
}
template<size_t ...Indices>
void diagnoseImpl(DiagnosticEngine &diags, Diag<Inputs...> diag,
llvm::index_sequence<Indices...>) const {
diags.diagnose(
asDerived().getCycleDiagnosticLoc(std::get<Indices>(storage)...),
diag, std::get<Indices>(storage)...);
}
protected:
/// Retrieve the storage value directly.
const std::tuple<Inputs...> &getStorage() const { return storage; }
public:
static const bool isEverCached = (Caching != CacheKind::Uncached);
static const bool hasExternalCache = (Caching == CacheKind::SeparatelyCached);
using OutputType = Output;
explicit SimpleRequest(const Inputs& ...inputs)
: storage(inputs...) { }
/// Request evaluation function that will be registered with the evaluator.
static OutputType evaluate(const Derived &request, Evaluator &evaluator) {
return request.callDerived(evaluator,
llvm::index_sequence_for<Inputs...>());
}
void diagnoseCycle(DiagnosticEngine &diags) const {
diagnoseImpl(diags, Derived::cycleDiagnostic,
llvm::index_sequence_for<Inputs...>());
}
void noteCycleStep(DiagnosticEngine &diags) const {
diagnoseImpl(diags, Derived::cycleStepDiagnostic,
llvm::index_sequence_for<Inputs...>());
}
friend bool operator==(const SimpleRequest &lhs, const SimpleRequest &rhs) {
return lhs.storage == rhs.storage;
}
friend bool operator!=(const SimpleRequest &lhs, const SimpleRequest &rhs) {
return !(lhs == rhs);
}
friend hash_code hash_value(const SimpleRequest &request) {
using llvm::hash_combine;
return hash_combine(TypeID<Derived>::value, request.storage);
}
friend void simple_display(llvm::raw_ostream &out,
const Derived &request) {
out << TypeID<Derived>::getName();
simple_display(out, request.storage);
}
};
}
#endif // SWIFT_BASIC_SIMPLEREQUEST_H