mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
@@ -10,7 +10,8 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines the AnyRequest class, which describes a stored request.
|
||||
// This file defines type-erasing wrappers for requests used by the Evaluator
|
||||
// class.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@@ -21,7 +22,6 @@
|
||||
#include "swift/Basic/TypeID.h"
|
||||
#include "llvm/ADT/DenseMapInfo.h"
|
||||
#include "llvm/ADT/Hashing.h"
|
||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||
#include <string>
|
||||
|
||||
namespace llvm {
|
||||
@@ -35,7 +35,236 @@ using llvm::hash_value;
|
||||
|
||||
class DiagnosticEngine;
|
||||
|
||||
/// Stores a request (for the \c Evaluator class) of any kind.
|
||||
/// A collection of functions that describe how to perform operations for a
|
||||
/// specific concrete request, obtained using
|
||||
/// \c AnyRequestVTable::get<ConcreteRequest>().
|
||||
struct AnyRequestVTable {
|
||||
template<typename Request>
|
||||
struct Impl {
|
||||
static void copy(const void *input, void *output) {
|
||||
new (output) Request(*static_cast<const Request *>(input));
|
||||
}
|
||||
static hash_code getHash(const void *ptr) {
|
||||
return hash_value(*static_cast<const Request *>(ptr));
|
||||
}
|
||||
static void deleter(void *ptr) {
|
||||
static_cast<Request *>(ptr)->~Request();
|
||||
}
|
||||
static bool isEqual(const void *lhs, const void *rhs) {
|
||||
return *static_cast<const Request *>(lhs) ==
|
||||
*static_cast<const Request *>(rhs);
|
||||
}
|
||||
static void simpleDisplay(const void *ptr, llvm::raw_ostream &out) {
|
||||
simple_display(out, *static_cast<const Request *>(ptr));
|
||||
}
|
||||
static void diagnoseCycle(const void *ptr, DiagnosticEngine &diags) {
|
||||
static_cast<const Request *>(ptr)->diagnoseCycle(diags);
|
||||
}
|
||||
static void noteCycleStep(const void *ptr, DiagnosticEngine &diags) {
|
||||
static_cast<const Request *>(ptr)->noteCycleStep(diags);
|
||||
}
|
||||
static SourceLoc getNearestLoc(const void *ptr) {
|
||||
return static_cast<const Request *>(ptr)->getNearestLoc();
|
||||
}
|
||||
};
|
||||
|
||||
const uint64_t typeID;
|
||||
const size_t requestSize;
|
||||
const std::function<void(const void *, void *)> copy;
|
||||
const std::function<hash_code(const void *)> getHash;
|
||||
const std::function<void(void *)> deleter;
|
||||
const std::function<bool(const void *, const void *)> isEqual;
|
||||
const std::function<void(const void *, llvm::raw_ostream &)> simpleDisplay;
|
||||
const std::function<void(const void *, DiagnosticEngine &)> diagnoseCycle;
|
||||
const std::function<void(const void *, DiagnosticEngine &)> noteCycleStep;
|
||||
const std::function<SourceLoc(const void *)> getNearestLoc;
|
||||
|
||||
template <typename Request>
|
||||
static const AnyRequestVTable *get() {
|
||||
static const AnyRequestVTable vtable = {
|
||||
TypeID<Request>::value,
|
||||
sizeof(Request),
|
||||
&Impl<Request>::copy,
|
||||
&Impl<Request>::getHash,
|
||||
&Impl<Request>::deleter,
|
||||
&Impl<Request>::isEqual,
|
||||
&Impl<Request>::simpleDisplay,
|
||||
&Impl<Request>::diagnoseCycle,
|
||||
&Impl<Request>::noteCycleStep,
|
||||
&Impl<Request>::getNearestLoc,
|
||||
};
|
||||
return &vtable;
|
||||
}
|
||||
};
|
||||
|
||||
/// Base class for request type-erasing wrappers.
|
||||
template <typename Derived>
|
||||
class AnyRequestBase {
|
||||
friend llvm::DenseMapInfo<Derived>;
|
||||
|
||||
protected:
|
||||
static hash_code hashForHolder(uint64_t typeID, hash_code requestHash) {
|
||||
return hash_combine(typeID, requestHash);
|
||||
}
|
||||
|
||||
enum class StorageKind : uint8_t {
|
||||
Normal,
|
||||
Empty,
|
||||
Tombstone,
|
||||
};
|
||||
|
||||
/// The vtable and storage kind.
|
||||
llvm::PointerIntPair<const AnyRequestVTable *, 2, StorageKind> vtableAndKind;
|
||||
|
||||
StorageKind getStorageKind() const { return vtableAndKind.getInt(); }
|
||||
|
||||
/// Whether this object is storing a value, and is not empty or a tombstone.
|
||||
bool hasStorage() const {
|
||||
switch (getStorageKind()) {
|
||||
case StorageKind::Empty:
|
||||
case StorageKind::Tombstone:
|
||||
return false;
|
||||
case StorageKind::Normal:
|
||||
return true;
|
||||
}
|
||||
llvm_unreachable("Unhandled case in switch");
|
||||
}
|
||||
|
||||
/// Retrieve the vtable to perform operations on the type-erased request.
|
||||
const AnyRequestVTable *getVTable() const {
|
||||
assert(hasStorage() && "Shouldn't be querying empty or tombstone");
|
||||
return vtableAndKind.getPointer();
|
||||
}
|
||||
|
||||
AnyRequestBase(const AnyRequestVTable *vtable, StorageKind storageKind) {
|
||||
vtableAndKind.setPointer(vtable);
|
||||
vtableAndKind.setInt(storageKind);
|
||||
assert((bool)vtable == hasStorage() && "Must have a vtable with storage");
|
||||
}
|
||||
|
||||
AnyRequestBase(const AnyRequestBase &other) {
|
||||
vtableAndKind = other.vtableAndKind;
|
||||
}
|
||||
|
||||
private:
|
||||
Derived &asDerived() {
|
||||
return *static_cast<Derived *>(this);
|
||||
}
|
||||
const Derived &asDerived() const {
|
||||
return *static_cast<const Derived *>(this);
|
||||
}
|
||||
|
||||
public:
|
||||
/// Cast to a specific (known) type.
|
||||
template<typename Request>
|
||||
const Request &castTo() const {
|
||||
assert(getVTable()->typeID == TypeID<Request>::value &&
|
||||
"Wrong type in cast");
|
||||
return *static_cast<const Request *>(asDerived().getRawStorage());
|
||||
}
|
||||
|
||||
/// Try casting to a specific (known) type, returning \c nullptr on
|
||||
/// failure.
|
||||
template<typename Request>
|
||||
const Request *getAs() const {
|
||||
if (getVTable()->typeID != TypeID<Request>::value)
|
||||
return nullptr;
|
||||
|
||||
return static_cast<const Request *>(asDerived().getRawStorage());
|
||||
}
|
||||
|
||||
/// Diagnose a cycle detected for this request.
|
||||
void diagnoseCycle(DiagnosticEngine &diags) const {
|
||||
getVTable()->diagnoseCycle(asDerived().getRawStorage(), diags);
|
||||
}
|
||||
|
||||
/// Note that this request is part of a cycle.
|
||||
void noteCycleStep(DiagnosticEngine &diags) const {
|
||||
getVTable()->noteCycleStep(asDerived().getRawStorage(), diags);
|
||||
}
|
||||
|
||||
/// Retrieve the nearest source location to which this request applies.
|
||||
SourceLoc getNearestLoc() const {
|
||||
return getVTable()->getNearestLoc(asDerived().getRawStorage());
|
||||
}
|
||||
|
||||
/// Compare two instances for equality.
|
||||
friend bool operator==(const Derived &lhs, const Derived &rhs) {
|
||||
// If the storage kinds don't match, we're done.
|
||||
if (lhs.getStorageKind() != rhs.getStorageKind())
|
||||
return false;
|
||||
|
||||
// If the storage kinds do match, but there's no storage, they're trivially
|
||||
// equal.
|
||||
if (!lhs.hasStorage())
|
||||
return true;
|
||||
|
||||
// Must be storing the same kind of request.
|
||||
if (lhs.getVTable()->typeID != rhs.getVTable()->typeID)
|
||||
return false;
|
||||
|
||||
return lhs.getVTable()->isEqual(lhs.getRawStorage(), rhs.getRawStorage());
|
||||
}
|
||||
|
||||
friend bool operator!=(const Derived &lhs, const Derived &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
friend hash_code hash_value(const Derived &req) {
|
||||
// If there's no storage, return a trivial hash value.
|
||||
if (!req.hasStorage())
|
||||
return 1;
|
||||
|
||||
auto reqHash = req.getVTable()->getHash(req.getRawStorage());
|
||||
return hashForHolder(req.getVTable()->typeID, reqHash);
|
||||
}
|
||||
|
||||
friend void simple_display(llvm::raw_ostream &out, const Derived &req) {
|
||||
req.getVTable()->simpleDisplay(req.getRawStorage(), out);
|
||||
}
|
||||
};
|
||||
|
||||
/// Provides a view onto a request that is stored on the stack. Objects of this
|
||||
/// class must not outlive the request they reference.
|
||||
///
|
||||
/// Requests must be value types and provide the following API:
|
||||
///
|
||||
/// - Copy constructor
|
||||
/// - Equality operator (==)
|
||||
/// - Hashing support (hash_value)
|
||||
/// - TypeID support (see swift/Basic/TypeID.h)
|
||||
/// - Display support (free function):
|
||||
/// void simple_display(llvm::raw_ostream &, const T &);
|
||||
/// - Cycle diagnostics operations:
|
||||
/// void diagnoseCycle(DiagnosticEngine &diags) const;
|
||||
/// void noteCycleStep(DiagnosticEngine &diags) const;
|
||||
///
|
||||
class ActiveRequest final : public AnyRequestBase<ActiveRequest> {
|
||||
friend class AnyRequestBase;
|
||||
friend class AnyRequest;
|
||||
friend llvm::DenseMapInfo<ActiveRequest>;
|
||||
|
||||
/// Pointer to the request stored on the stack.
|
||||
const void *storage;
|
||||
|
||||
/// Creates an \c ActiveRequest without storage.
|
||||
explicit ActiveRequest(StorageKind storageKind)
|
||||
: AnyRequestBase(/*vtable*/ nullptr, storageKind) {}
|
||||
|
||||
const void *getRawStorage() const { return storage; }
|
||||
|
||||
public:
|
||||
/// Creates a new \c ActiveRequest referencing a concrete request on the
|
||||
/// stack.
|
||||
template <typename Request>
|
||||
explicit ActiveRequest(const Request &request)
|
||||
: AnyRequestBase(AnyRequestVTable::get<Request>(), StorageKind::Normal) {
|
||||
storage = &request;
|
||||
}
|
||||
};
|
||||
|
||||
/// Stores a request (for the \c Evaluator class) of any kind. Unlike
|
||||
/// \c ActiveRequest, this wrapper has ownership of the underlying request.
|
||||
///
|
||||
/// Requests must be value types and provide the following API to be stored in
|
||||
/// an \c AnyRequest instance:
|
||||
@@ -50,246 +279,154 @@ class DiagnosticEngine;
|
||||
/// void diagnoseCycle(DiagnosticEngine &diags) const;
|
||||
/// void noteCycleStep(DiagnosticEngine &diags) const;
|
||||
///
|
||||
class AnyRequest {
|
||||
friend llvm::DenseMapInfo<swift::AnyRequest>;
|
||||
class AnyRequest final : public AnyRequestBase<AnyRequest> {
|
||||
friend class AnyRequestBase;
|
||||
friend llvm::DenseMapInfo<AnyRequest>;
|
||||
|
||||
static hash_code hashForHolder(uint64_t typeID, hash_code requestHash) {
|
||||
return hash_combine(typeID, requestHash);
|
||||
}
|
||||
/// Pointer to the request on the heap.
|
||||
void *storage;
|
||||
|
||||
/// Abstract base class used to hold the specific request kind.
|
||||
class HolderBase : public llvm::RefCountedBase<HolderBase> {
|
||||
public:
|
||||
/// The type ID of the request being stored.
|
||||
const uint64_t typeID;
|
||||
/// Creates an \c AnyRequest without storage.
|
||||
explicit AnyRequest(StorageKind storageKind)
|
||||
: AnyRequestBase(/*vtable*/ nullptr, storageKind) {}
|
||||
|
||||
/// Hash value for the request itself.
|
||||
const hash_code hash;
|
||||
const void *getRawStorage() const { return storage; }
|
||||
|
||||
protected:
|
||||
/// Initialize base with type ID and hash code.
|
||||
HolderBase(uint64_t typeID, hash_code hash)
|
||||
: typeID(typeID), hash(AnyRequest::hashForHolder(typeID, hash)) { }
|
||||
/// Whether this wrapper is storing the same underlying request as an
|
||||
/// \c ActiveRequest.
|
||||
bool isStorageEqual(const ActiveRequest &other) const {
|
||||
// If either wrapper isn't storing anything, just return false.
|
||||
if (!hasStorage() || !other.hasStorage())
|
||||
return false;
|
||||
|
||||
public:
|
||||
virtual ~HolderBase();
|
||||
if (getVTable()->typeID != other.getVTable()->typeID)
|
||||
return false;
|
||||
|
||||
/// Determine whether this request is equivalent to the \c other
|
||||
/// request.
|
||||
virtual bool equals(const HolderBase &other) const = 0;
|
||||
|
||||
/// Display.
|
||||
virtual void display(llvm::raw_ostream &out) const = 0;
|
||||
|
||||
/// Diagnose a cycle detected for this request.
|
||||
virtual void diagnoseCycle(DiagnosticEngine &diags) const = 0;
|
||||
|
||||
/// Note that this request is part of a cycle.
|
||||
virtual void noteCycleStep(DiagnosticEngine &diags) const = 0;
|
||||
|
||||
/// Retrieve the nearest source location to which this request applies.
|
||||
virtual SourceLoc getNearestLoc() const = 0;
|
||||
};
|
||||
|
||||
/// Holds a value that can be used as a request input/output.
|
||||
template<typename Request>
|
||||
class Holder final : public HolderBase {
|
||||
public:
|
||||
const Request request;
|
||||
|
||||
Holder(const Request &request)
|
||||
: HolderBase(TypeID<Request>::value, hash_value(request)),
|
||||
request(request) { }
|
||||
|
||||
Holder(Request &&request)
|
||||
: HolderBase(TypeID<Request>::value, hash_value(request)),
|
||||
request(std::move(request)) { }
|
||||
|
||||
virtual ~Holder() { }
|
||||
|
||||
/// Determine whether this request is equivalent to another.
|
||||
///
|
||||
/// The caller guarantees that the typeIDs are the same.
|
||||
virtual bool equals(const HolderBase &other) const override {
|
||||
assert(typeID == other.typeID && "Caller should match typeIDs");
|
||||
return request == static_cast<const Holder<Request> &>(other).request;
|
||||
}
|
||||
|
||||
/// Display.
|
||||
virtual void display(llvm::raw_ostream &out) const override {
|
||||
simple_display(out, request);
|
||||
}
|
||||
|
||||
/// Diagnose a cycle detected for this request.
|
||||
virtual void diagnoseCycle(DiagnosticEngine &diags) const override {
|
||||
request.diagnoseCycle(diags);
|
||||
}
|
||||
|
||||
/// Note that this request is part of a cycle.
|
||||
virtual void noteCycleStep(DiagnosticEngine &diags) const override {
|
||||
request.noteCycleStep(diags);
|
||||
}
|
||||
|
||||
/// Retrieve the nearest source location to which this request applies.
|
||||
virtual SourceLoc getNearestLoc() const override {
|
||||
return request.getNearestLoc();
|
||||
}
|
||||
};
|
||||
|
||||
/// FIXME: Inefficient. Use the low bits.
|
||||
enum class StorageKind {
|
||||
Normal,
|
||||
Empty,
|
||||
Tombstone,
|
||||
} storageKind = StorageKind::Normal;
|
||||
|
||||
/// The data stored in this value.
|
||||
llvm::IntrusiveRefCntPtr<HolderBase> stored;
|
||||
|
||||
AnyRequest(StorageKind storageKind) : storageKind(storageKind) {
|
||||
assert(storageKind != StorageKind::Normal);
|
||||
return getVTable()->isEqual(getRawStorage(), other.getRawStorage());
|
||||
}
|
||||
|
||||
public:
|
||||
AnyRequest(const AnyRequest &other) = default;
|
||||
AnyRequest &operator=(const AnyRequest &other) = default;
|
||||
|
||||
AnyRequest(AnyRequest &&other)
|
||||
: storageKind(other.storageKind), stored(std::move(other.stored)) {
|
||||
other.storageKind = StorageKind::Empty;
|
||||
AnyRequest(const AnyRequest &other) : AnyRequestBase(other) {
|
||||
if (hasStorage()) {
|
||||
// For now, just allocate a new buffer and copy across the request. This
|
||||
// should only happen when performing operations on the (disabled by
|
||||
// default) dependency graph.
|
||||
storage = llvm::safe_malloc(getVTable()->requestSize);
|
||||
getVTable()->copy(other.storage, storage);
|
||||
}
|
||||
}
|
||||
|
||||
AnyRequest &operator=(AnyRequest &&other) {
|
||||
storageKind = other.storageKind;
|
||||
stored = std::move(other.stored);
|
||||
other.storageKind = StorageKind::Empty;
|
||||
other.stored = nullptr;
|
||||
AnyRequest &operator=(const AnyRequest &other) {
|
||||
if (&other != this) {
|
||||
this->~AnyRequest();
|
||||
new (this) AnyRequest(other);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Create a local template typename `ValueType` in the template specialization
|
||||
AnyRequest(AnyRequest &&other) : AnyRequestBase(other),
|
||||
storage(other.storage) {
|
||||
new (&other) AnyRequest(StorageKind::Empty);
|
||||
}
|
||||
|
||||
AnyRequest &operator=(AnyRequest &&other) {
|
||||
if (&other != this) {
|
||||
this->~AnyRequest();
|
||||
new (this) AnyRequest(std::move(other));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Create a local template typename `Request` in the template specialization
|
||||
// so that we can refer to it in the SFINAE condition as well as the body of
|
||||
// the template itself. The SFINAE condition allows us to remove this
|
||||
// constructor from candidacy when evaluating explicit construction with an
|
||||
// instance of `AnyRequest`. If we do not do so, we will find ourselves with
|
||||
// ambiguity with this constructor and the defined move constructor above.
|
||||
/// Construct a new instance with the given value.
|
||||
template <typename T,
|
||||
typename ValueType = typename std::remove_cv<
|
||||
typename std::remove_reference<T>::type>::type,
|
||||
template <typename T, typename Request = std::decay_t<T>,
|
||||
typename = typename std::enable_if<
|
||||
!std::is_same<ValueType, AnyRequest>::value>::type>
|
||||
explicit AnyRequest(T &&value) : storageKind(StorageKind::Normal) {
|
||||
stored = llvm::IntrusiveRefCntPtr<HolderBase>(
|
||||
new Holder<ValueType>(std::forward<T>(value)));
|
||||
!std::is_same<Request, AnyRequest>::value>::type>
|
||||
explicit AnyRequest(T &&request)
|
||||
: AnyRequestBase(AnyRequestVTable::get<Request>(), StorageKind::Normal) {
|
||||
storage = llvm::safe_malloc(sizeof(Request));
|
||||
new (storage) Request(std::forward<T>(request));
|
||||
}
|
||||
|
||||
/// Cast to a specific (known) type.
|
||||
template<typename Request>
|
||||
const Request &castTo() const {
|
||||
assert(stored->typeID == TypeID<Request>::value && "wrong type in cast");
|
||||
return static_cast<const Holder<Request> *>(stored.get())->request;
|
||||
/// Construct an \c AnyRequest from an \c ActiveRequest, allowing the
|
||||
/// underlying request to persist.
|
||||
explicit AnyRequest(const ActiveRequest &req)
|
||||
: AnyRequestBase(req.getVTable(), StorageKind::Normal) {
|
||||
assert(req.hasStorage());
|
||||
storage = llvm::safe_malloc(getVTable()->requestSize);
|
||||
getVTable()->copy(req.storage, storage);
|
||||
}
|
||||
|
||||
/// Try casting to a specific (known) type, returning \c nullptr on
|
||||
/// failure.
|
||||
template<typename Request>
|
||||
const Request *getAs() const {
|
||||
if (stored->typeID != TypeID<Request>::value)
|
||||
return nullptr;
|
||||
|
||||
return &static_cast<const Holder<Request> *>(stored.get())->request;
|
||||
}
|
||||
|
||||
/// Diagnose a cycle detected for this request.
|
||||
void diagnoseCycle(DiagnosticEngine &diags) const {
|
||||
stored->diagnoseCycle(diags);
|
||||
}
|
||||
|
||||
/// Note that this request is part of a cycle.
|
||||
void noteCycleStep(DiagnosticEngine &diags) const {
|
||||
stored->noteCycleStep(diags);
|
||||
}
|
||||
|
||||
/// Retrieve the nearest source location to which this request applies.
|
||||
SourceLoc getNearestLoc() const {
|
||||
return stored->getNearestLoc();
|
||||
}
|
||||
|
||||
/// Compare two instances for equality.
|
||||
friend bool operator==(const AnyRequest &lhs, const AnyRequest &rhs) {
|
||||
if (lhs.storageKind != rhs.storageKind) {
|
||||
return false;
|
||||
~AnyRequest() {
|
||||
if (hasStorage()) {
|
||||
getVTable()->deleter(storage);
|
||||
std::free(storage);
|
||||
}
|
||||
|
||||
if (lhs.storageKind != StorageKind::Normal)
|
||||
return true;
|
||||
|
||||
if (lhs.stored->typeID != rhs.stored->typeID)
|
||||
return false;
|
||||
|
||||
return lhs.stored->equals(*rhs.stored);
|
||||
}
|
||||
|
||||
friend bool operator!=(const AnyRequest &lhs, const AnyRequest &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
friend hash_code hash_value(const AnyRequest &any) {
|
||||
if (any.storageKind != StorageKind::Normal)
|
||||
return 1;
|
||||
|
||||
return any.stored->hash;
|
||||
}
|
||||
|
||||
friend void simple_display(llvm::raw_ostream &out, const AnyRequest &any) {
|
||||
any.stored->display(out);
|
||||
}
|
||||
|
||||
/// Return the result of calling simple_display as a string.
|
||||
std::string getAsString() const;
|
||||
|
||||
static AnyRequest getEmptyKey() {
|
||||
return AnyRequest(StorageKind::Empty);
|
||||
}
|
||||
|
||||
static AnyRequest getTombstoneKey() {
|
||||
return AnyRequest(StorageKind::Tombstone);
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace swift
|
||||
|
||||
namespace llvm {
|
||||
template<>
|
||||
struct DenseMapInfo<swift::ActiveRequest> {
|
||||
using ActiveRequest = swift::ActiveRequest;
|
||||
static inline ActiveRequest getEmptyKey() {
|
||||
return ActiveRequest(ActiveRequest::StorageKind::Empty);
|
||||
}
|
||||
static inline ActiveRequest getTombstoneKey() {
|
||||
return ActiveRequest(ActiveRequest::StorageKind::Tombstone);
|
||||
}
|
||||
static unsigned getHashValue(const ActiveRequest &request) {
|
||||
return hash_value(request);
|
||||
}
|
||||
static bool isEqual(const ActiveRequest &lhs, const ActiveRequest &rhs) {
|
||||
return lhs == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct DenseMapInfo<swift::AnyRequest> {
|
||||
static inline swift::AnyRequest getEmptyKey() {
|
||||
return swift::AnyRequest::getEmptyKey();
|
||||
using AnyRequest = swift::AnyRequest;
|
||||
using ActiveRequest = swift::ActiveRequest;
|
||||
|
||||
static inline AnyRequest getEmptyKey() {
|
||||
return AnyRequest(AnyRequest::StorageKind::Empty);
|
||||
}
|
||||
static inline swift::AnyRequest getTombstoneKey() {
|
||||
return swift::AnyRequest::getTombstoneKey();
|
||||
return AnyRequest(AnyRequest::StorageKind::Tombstone);
|
||||
}
|
||||
static unsigned getHashValue(const swift::AnyRequest &request) {
|
||||
static unsigned getHashValue(const AnyRequest &request) {
|
||||
return hash_value(request);
|
||||
}
|
||||
template <typename Request>
|
||||
static unsigned getHashValue(const Request &request) {
|
||||
return swift::AnyRequest::hashForHolder(swift::TypeID<Request>::value,
|
||||
hash_value(request));
|
||||
return AnyRequest::hashForHolder(swift::TypeID<Request>::value,
|
||||
hash_value(request));
|
||||
}
|
||||
static bool isEqual(const swift::AnyRequest &lhs,
|
||||
const swift::AnyRequest &rhs) {
|
||||
static unsigned getHashValue(const ActiveRequest &request) {
|
||||
return hash_value(request);
|
||||
}
|
||||
static bool isEqual(const AnyRequest &lhs, const AnyRequest &rhs) {
|
||||
return lhs == rhs;
|
||||
}
|
||||
template <typename Request>
|
||||
static bool isEqual(const Request &lhs,
|
||||
const swift::AnyRequest &rhs) {
|
||||
if (rhs == getEmptyKey() || rhs == getTombstoneKey())
|
||||
static bool isEqual(const Request &lhs, const AnyRequest &rhs) {
|
||||
if (!rhs.hasStorage())
|
||||
return false;
|
||||
const Request *rhsRequest = rhs.getAs<Request>();
|
||||
if (!rhsRequest)
|
||||
return false;
|
||||
return lhs == *rhsRequest;
|
||||
|
||||
auto *rhsRequest = rhs.getAs<Request>();
|
||||
return rhsRequest && lhs == *rhsRequest;
|
||||
}
|
||||
static bool isEqual(const ActiveRequest &lhs, const AnyRequest &rhs) {
|
||||
return rhs.isStorageEqual(lhs);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@ class Evaluator {
|
||||
|
||||
/// A vector containing all of the active evaluation requests, which
|
||||
/// is treated as a stack and is used to detect cycles.
|
||||
llvm::SetVector<AnyRequest> activeRequests;
|
||||
llvm::SetVector<ActiveRequest> activeRequests;
|
||||
|
||||
/// A cache that stores the results of requests.
|
||||
llvm::DenseMap<AnyRequest, AnyValue> cache;
|
||||
@@ -308,7 +308,7 @@ public:
|
||||
typename std::enable_if<!Request::hasExternalCache>::type* = nullptr>
|
||||
void cacheOutput(const Request &request,
|
||||
typename Request::OutputType &&output) {
|
||||
cache.insert({getCanonicalRequest(request), std::move(output)});
|
||||
cache.insert({AnyRequest(request), std::move(output)});
|
||||
}
|
||||
|
||||
/// Clear the cache stored within this evaluator.
|
||||
@@ -320,24 +320,13 @@ public:
|
||||
/// Is the given request, or an equivalent, currently being evaluated?
|
||||
template <typename Request>
|
||||
bool hasActiveRequest(const Request &request) const {
|
||||
return activeRequests.count(AnyRequest(request));
|
||||
return activeRequests.count(ActiveRequest(request));
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Request>
|
||||
const AnyRequest &getCanonicalRequest(const Request &request) {
|
||||
// FIXME: DenseMap ought to let us do this with one hash lookup.
|
||||
auto iter = dependencies.find_as(request);
|
||||
if (iter != dependencies.end())
|
||||
return iter->first;
|
||||
auto insertResult = dependencies.insert({AnyRequest(request), {}});
|
||||
assert(insertResult.second && "just checked if the key was already there");
|
||||
return insertResult.first->first;
|
||||
}
|
||||
|
||||
/// Diagnose a cycle detected in the evaluation of the given
|
||||
/// request.
|
||||
void diagnoseCycle(const AnyRequest &request);
|
||||
void diagnoseCycle(const ActiveRequest &request);
|
||||
|
||||
/// Check the dependency from the current top of the stack to
|
||||
/// the given request, including cycle detection and diagnostics.
|
||||
@@ -345,14 +334,16 @@ private:
|
||||
/// \returns true if a cycle was detected, in which case this function has
|
||||
/// already diagnosed the cycle. Otherwise, returns \c false and adds this
|
||||
/// request to the \c activeRequests stack.
|
||||
bool checkDependency(const AnyRequest &request);
|
||||
bool checkDependency(const ActiveRequest &request);
|
||||
|
||||
/// Produce the result of the request without caching.
|
||||
template<typename Request>
|
||||
llvm::Expected<typename Request::OutputType>
|
||||
getResultUncached(const Request &request) {
|
||||
auto activeReq = ActiveRequest(request);
|
||||
|
||||
// Check for a cycle.
|
||||
if (checkDependency(getCanonicalRequest(request))) {
|
||||
if (checkDependency(activeReq)) {
|
||||
return llvm::Error(
|
||||
llvm::make_unique<CyclicalRequestError<Request>>(request, *this));
|
||||
}
|
||||
@@ -360,7 +351,7 @@ private:
|
||||
// Make sure we remove this from the set of active requests once we're
|
||||
// done.
|
||||
SWIFT_DEFER {
|
||||
assert(activeRequests.back().castTo<Request>() == request);
|
||||
assert(activeRequests.back() == activeReq);
|
||||
activeRequests.pop_back();
|
||||
};
|
||||
|
||||
@@ -421,7 +412,7 @@ private:
|
||||
return result;
|
||||
|
||||
// Cache the result.
|
||||
cache.insert({getCanonicalRequest(request), *result});
|
||||
cache.insert({AnyRequest(request), *result});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -170,8 +170,9 @@ namespace llvm {
|
||||
void simple_display(raw_ostream &out, const Optional<T> &opt) {
|
||||
if (opt) {
|
||||
simple_display(out, *opt);
|
||||
} else {
|
||||
out << "None";
|
||||
}
|
||||
out << "None";
|
||||
}
|
||||
} // end namespace llvm
|
||||
|
||||
|
||||
Reference in New Issue
Block a user