| //===--- AnyRequest.h - Requests 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 AnyRequest class, which describes a stored request. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef SWIFT_AST_ANYREQUEST_H |
| #define SWIFT_AST_ANYREQUEST_H |
| |
| #include "swift/Basic/TypeID.h" |
| #include "llvm/ADT/DenseMapInfo.h" |
| #include "llvm/ADT/Hashing.h" |
| #include "llvm/ADT/IntrusiveRefCntPtr.h" |
| #include <string> |
| |
| namespace llvm { |
| class raw_ostream; |
| } |
| |
| namespace swift { |
| |
| using llvm::hash_code; |
| using llvm::hash_value; |
| |
| class DiagnosticEngine; |
| |
| /// Stores a request (for the \c Evaluator class) of any kind. |
| /// |
| /// Requests must be value types and provide the following API to be stored in |
| /// an \c AnyRequest instance: |
| /// |
| /// - 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 AnyRequest { |
| friend llvm::DenseMapInfo<swift::AnyRequest>; |
| |
| static hash_code hashForHolder(uint64_t typeID, hash_code requestHash) { |
| return hash_combine(hash_value(typeID), requestHash); |
| } |
| |
| /// 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; |
| |
| /// Hash value for the request itself. |
| const hash_code hash; |
| |
| protected: |
| /// Initialize base with type ID and hash code. |
| HolderBase(uint64_t typeID, hash_code hash) |
| : typeID(typeID), hash(AnyRequest::hashForHolder(typeID, hash)) { } |
| |
| public: |
| virtual ~HolderBase(); |
| |
| /// 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; |
| }; |
| |
| /// 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); |
| } |
| }; |
| |
| /// 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); |
| } |
| |
| 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 &operator=(AnyRequest &&other) { |
| storageKind = other.storageKind; |
| stored = std::move(other.stored); |
| other.storageKind = StorageKind::Empty; |
| other.stored = nullptr; |
| return *this; |
| } |
| |
| // Create a local template typename `ValueType` 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, |
| 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))); |
| } |
| |
| /// 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; |
| } |
| |
| /// 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); |
| } |
| |
| /// Compare two instances for equality. |
| friend bool operator==(const AnyRequest &lhs, const AnyRequest &rhs) { |
| if (lhs.storageKind != rhs.storageKind) { |
| return false; |
| } |
| |
| 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::AnyRequest> { |
| static inline swift::AnyRequest getEmptyKey() { |
| return swift::AnyRequest::getEmptyKey(); |
| } |
| static inline swift::AnyRequest getTombstoneKey() { |
| return swift::AnyRequest::getTombstoneKey(); |
| } |
| static unsigned getHashValue(const swift::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)); |
| } |
| static bool isEqual(const swift::AnyRequest &lhs, |
| const swift::AnyRequest &rhs) { |
| return lhs == rhs; |
| } |
| template <typename Request> |
| static bool isEqual(const Request &lhs, |
| const swift::AnyRequest &rhs) { |
| if (rhs == getEmptyKey() || rhs == getTombstoneKey()) |
| return false; |
| const Request *rhsRequest = rhs.getAs<Request>(); |
| if (!rhsRequest) |
| return false; |
| return lhs == *rhsRequest; |
| } |
| }; |
| |
| } // end namespace llvm |
| |
| #endif // SWIFT_AST_ANYREQUEST_H |