//===--- 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 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; 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 { 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 class Holder final : public HolderBase { public: const Request request; Holder(const Request &request) : HolderBase(TypeID::value, hash_value(request)), request(request) { } Holder(Request &&request) : HolderBase(TypeID::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 &>(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 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 ::type>::type, typename = typename std::enable_if< !std::is_same::value>::type> explicit AnyRequest(T &&value) : storageKind(StorageKind::Normal) { stored = llvm::IntrusiveRefCntPtr( new Holder(std::forward(value))); } /// Cast to a specific (known) type. template const Request &castTo() const { assert(stored->typeID == TypeID::value && "wrong type in cast"); return static_cast *>(stored.get())->request; } /// Try casting to a specific (known) type, returning \c nullptr on /// failure. template const Request *getAs() const { if (stored->typeID != TypeID::value) return nullptr; return &static_cast *>(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 { 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 static unsigned getHashValue(const Request &request) { return swift::AnyRequest::hashForHolder(swift::TypeID::value, hash_value(request)); } static bool isEqual(const swift::AnyRequest &lhs, const swift::AnyRequest &rhs) { return lhs == rhs; } template static bool isEqual(const Request &lhs, const swift::AnyRequest &rhs) { if (rhs == getEmptyKey() || rhs == getTombstoneKey()) return false; const Request *rhsRequest = rhs.getAs(); if (!rhsRequest) return false; return lhs == *rhsRequest; } }; } // end namespace llvm #endif // SWIFT_AST_ANYREQUEST_H