//===--- 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/Statistic.h" #include "swift/Basic/TypeID.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Error.h" #include #include namespace swift { class Evaluator; /// Describes how the result for a particular request will be cached. enum class RequestFlags { /// The result for a particular request should never be cached. Uncached = 1 << 0, /// The result for a particular request should be cached within the /// evaluator itself. Cached = 1 << 1, /// The result of a particular request will be cached via some separate /// mechanism, such as a mutable data structure. SeparatelyCached = 1 << 2, /// The result is separately cached, but the request can also make use /// of the request evaluator's cache for out-of-line storage. This /// is used to optimize caching of requests where the usual case is /// that the value is empty. In this case, the separate mechanism /// can represent the empty state more efficiently than adding a new /// entry to the request evaluator's cache. SplitCached = 1 << 3, /// This request introduces the source component of a source-sink /// incremental dependency pair and defines a new dependency scope. /// /// This bit is optional. High-level requests /// (e.g. \c TypeCheckPrimaryFileRequest) will require it. /// /// For further discussion on incremental dependencies /// see DependencyAnalysis.md. DependencySource = 1 << 4, /// This request introduces the sink component of a source-sink /// incremental dependency pair and is a consumer of the current /// dependency scope. /// /// This bit is optional. Name lookup requests /// (e.g. \c DirectLookupRequest) will require it. /// /// For further discussion on incremental dependencies /// see DependencyAnalysis.md. DependencySink = 1 << 5, }; static constexpr inline RequestFlags operator|(RequestFlags lhs, RequestFlags rhs) { return RequestFlags(static_cast::type>(lhs) | static_cast::type>(rhs)); } /// ------------------------------------------------------------------------- /// Extracting the source location "nearest" a request. /// ------------------------------------------------------------------------- namespace detail { /// Dummy extract function used to detect when we can call /// extractNearestSourceLoc() safely. inline void extractNearestSourceLoc(...) { } /// Metaprogram to determine whether any input is true. constexpr bool anyTrue() { return false; } template constexpr bool anyTrue(bool current, Rest... rest) { return current || anyTrue(rest...); } } /// Determine whether we can extract a nearest source location from a value of /// the given type. template constexpr bool canExtractNearestSourceLoc() { using detail::extractNearestSourceLoc; return !std::is_void::value; } /// Extract source locations when possible, or return an invalid source /// location if not possible. template()>::type> SourceLoc maybeExtractNearestSourceLoc(const T& value) { if constexpr (std::is_pointer_v) { if (value == nullptr) { return SourceLoc(); } } return extractNearestSourceLoc(value); } template()>::type> SourceLoc maybeExtractNearestSourceLoc(const T& value) { return SourceLoc(); } /// Extract the nearest source location from a pointer union. template() && canExtractNearestSourceLoc()>::type> SourceLoc extractNearestSourceLoc(const llvm::PointerUnion &value) { if (auto first = value.template dyn_cast()) { return extractNearestSourceLoc(first); } if (auto second = value.template dyn_cast()) { return extractNearestSourceLoc(second); } return SourceLoc(); } namespace detail { /// Basis case for extracting the nearest source location from a tuple. template= sizeof...(Types))>::type> SourceLoc extractNearestSourceLocTuple(const std::tuple &) { return SourceLoc(); } /// Extract the first, nearest source location from a tuple. template::type> SourceLoc extractNearestSourceLocTuple(const std::tuple &value) { SourceLoc loc = maybeExtractNearestSourceLoc(std::get(value)); if (loc.isValid()) return loc; return extractNearestSourceLocTuple(value); } } namespace detail { constexpr bool cacheContains(RequestFlags kind, RequestFlags needle) { using cache_t = std::underlying_type::type; return (static_cast(kind) & static_cast(needle)) == static_cast(needle); } constexpr bool isEverCached(RequestFlags kind) { return !cacheContains(kind, RequestFlags::Uncached); } constexpr bool hasExternalCache(RequestFlags kind) { return cacheContains(kind, RequestFlags::SeparatelyCached); } constexpr bool hasSplitCache(RequestFlags kind) { return cacheContains(kind, RequestFlags::SplitCached); } constexpr bool isDependencySource(RequestFlags kind) { return cacheContains(kind, RequestFlags::DependencySource); } constexpr bool isDependencySink(RequestFlags kind) { return cacheContains(kind, RequestFlags::DependencySink); } } // end namespace detail /// Extract the first, nearest source location from a tuple. template(), canExtractNearestSourceLoc()...)>::type> SourceLoc extractNearestSourceLoc(const std::tuple &value) { return detail::extractNearestSourceLocTuple<0>(value); } /// ------------------------------------------------------------------------- /// Simple Requests /// ------------------------------------------------------------------------- /// 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 Signature The signature of the request, described as a /// function type whose inputs (\c Inputs) are the parameter types and /// whose output (\c Output) is the result of evaluating this request. /// \tparam Caching Describes how the output value is cached, if at all. /// /// 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 evaluate(Evaluator &evaluator, Inputs...) 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 implementation will use the default "circular reference" diagnostics /// based on the "nearest" source location, which can be provided explicitly by /// implementing the following in the subclass: /// \code /// SourceLoc getNearestLoc() const; /// \endcode /// If not provided, a default implementation for \c getNearestLoc() will pick /// the source location from the first input that provides one. /// /// Value caching is determined by the \c Caching parameter. When /// \c Caching == RequestFlags::SeparatelyCached, the \c Derived class is /// responsible for implementing the two operations responsible to managing /// the cache: /// \code /// Optional getCachedResult() const; /// void cacheResult(Output value) const; /// \endcode /// /// Incremental dependency tracking occurs automatically during /// request evaluation. To support that system, high-level requests that define /// dependency sources should override \c readDependencySource() /// and specify \c RequestFlags::DependencySource in addition to one of /// the 3 caching kinds defined above. /// \code /// evaluator::DependencySource /// readDependencySource(const evaluator::DependencyRecorder &) const; /// \endcode /// /// Requests that define dependency sinks should instead override /// \c writeDependencySink() and use the given evaluator and request /// result to write an edge into the dependency tracker. In addition, /// \c RequestFlags::DependencySource should be specified along with /// one of the 3 caching kinds defined above. /// \code /// void writeDependencySink(evaluator::DependencyCollector &, Output) const; /// \endcode template class SimpleRequest; template class SimpleRequest { std::tuple storage; Derived &asDerived() { return *static_cast(this); } const Derived &asDerived() const { return *static_cast(this); } template Output callDerived(Evaluator &evaluator, std::index_sequence) const { static_assert(sizeof...(Indices) > 0, "Subclass must define evaluate()"); return asDerived().evaluate(evaluator, std::get(storage)...); } protected: /// Retrieve the storage value directly. const std::tuple &getStorage() const { return storage; } public: constexpr static bool isEverCached = detail::isEverCached(Caching); constexpr static bool hasExternalCache = detail::hasExternalCache(Caching); constexpr static bool hasSplitCache = detail::hasSplitCache(Caching); public: constexpr static bool isDependencySource = detail::isDependencySource(Caching); constexpr static bool isDependencySink = detail::isDependencySink(Caching); using OutputType = Output; explicit SimpleRequest(const Inputs& ...inputs) : storage(inputs...) { } /// Request evaluation function that will be registered with the evaluator. static OutputType evaluateRequest(const Derived &request, Evaluator &evaluator) { return request.callDerived(evaluator, std::index_sequence_for()); } /// Retrieve the nearest source location to which this request applies. SourceLoc getNearestLoc() const { return extractNearestSourceLoc(storage); } void diagnoseCycle(DiagnosticEngine &diags) const { diags.diagnose(asDerived().getNearestLoc(), diag::circular_reference); } void noteCycleStep(DiagnosticEngine &diags) const { diags.diagnose(asDerived().getNearestLoc(), diag::circular_reference_through); } 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 llvm::hash_code hash_value(const SimpleRequest &request) { using llvm::hash_combine; return hash_combine(TypeID::value, request.storage); } friend void simple_display(llvm::raw_ostream &out, const Derived &request) { out << TypeID::getName(); simple_display(out, request.storage); } friend FrontendStatsTracer make_tracer(UnifiedStatsReporter *Reporter, const Derived &request) { return make_tracer(Reporter, TypeID::getName(), request.storage); } }; } namespace llvm { template llvm::hash_code hash_value(const SmallVector &vec) { return hash_combine_range(vec.begin(), vec.end()); } } #endif // SWIFT_BASIC_SIMPLEREQUEST_H