#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include "llvm/Support/Casting.h" #include "revng/ADT/Concepts.h" #include "revng/ADT/STLExtras.h" #include "revng/Support/Assert.h" template concept is_pointer = std::is_pointer_v; template struct concrete_types_traits; template using concrete_types_traits_t = typename concrete_types_traits::type; template concept HasConcretTypeTraits = requires { typename concrete_types_traits_t; }; // clang-format off template concept HasLLVMRTTI = requires(T *A) { { A->classof(A) } -> std::same_as; }; // clang-format on template concept Upcastable = HasLLVMRTTI and HasConcretTypeTraits; template using pointee = typename std::pointer_traits>::element_type; template concept PointerLike = requires(T A) { { *A }; }; static_assert(PointerLike); static_assert(not PointerLike); template concept UpcastablePointerLike = PointerLike and Upcastable>; template concept NotUpcastablePointerLike = not UpcastablePointerLike; template ReturnT upcast(P &Upcastable, const L &Callable, const ReturnT &IfNull) { using pointee = std::remove_reference_t; using concrete_types = concrete_types_traits_t; auto *Pointer = &*Upcastable; if (Pointer == nullptr) return IfNull; if constexpr (I < std::tuple_size_v) { using type = std::tuple_element_t; if (auto *Upcasted = llvm::dyn_cast(Pointer)) { return Callable(*Upcasted); } else { return upcast(Upcastable, Callable, IfNull); } } else { revng_abort(); } } template void upcast(P &Upcastable, const L &Callable) { auto Wrapper = [&](auto &Upcasted) { Callable(Upcasted); return true; }; upcast(Upcastable, Wrapper, false); } /// A unique_ptr copiable thanks to LLVM RTTI template class UpcastablePointer { private: template static P *clone(P *Pointer) { auto Dispatcher = [](auto &Upcasted) -> P * { using type = std::remove_reference_t; return new type(Upcasted); }; return ::upcast(Pointer, Dispatcher, static_cast

(nullptr)); } template static void destroy(P *Pointer) { ::upcast(Pointer, [](auto &Upcasted) { delete &Upcasted; }); } public: template void upcast(const L &Callable) { ::upcast(Pointer, Callable); } template void upcast(const L &Callable) const { ::upcast(Pointer, Callable); } private: using concrete_types = concrete_types_traits_t; static constexpr void (*Deleter)(T *) = &destroy; using inner_pointer = std::unique_ptr; public: using pointer = typename inner_pointer::pointer; using element_type = typename inner_pointer::element_type; public: constexpr UpcastablePointer() noexcept : Pointer(nullptr, Deleter) {} constexpr UpcastablePointer(std::nullptr_t P) noexcept : Pointer(P, Deleter) {} explicit UpcastablePointer(pointer P) noexcept : Pointer(P, Deleter) {} public: template Q, typename... Args> static UpcastablePointer make(Args &&...TheArgs) { return UpcastablePointer(new Q(std::forward(TheArgs)...)); } public: UpcastablePointer &operator=(const UpcastablePointer &Other) { if (&Other != this) { Pointer.reset(clone(Other.Pointer.get())); revng_assert(Pointer.get_deleter() == Deleter); } return *this; } UpcastablePointer(const UpcastablePointer &Other) : UpcastablePointer(nullptr) { *this = Other; } UpcastablePointer &operator=(UpcastablePointer &&Other) { if (&Other != this) { Pointer.reset(Other.Pointer.release()); revng_assert(Pointer.get_deleter() == Deleter); } return *this; } UpcastablePointer(UpcastablePointer &&Other) noexcept : UpcastablePointer(nullptr) { *this = std::move(Other); } bool operator==(const UpcastablePointer &Other) const { bool Result = false; upcast([&](auto &Upcasted) { Other.upcast([&](auto &OtherUpcasted) { using ThisType = std::remove_cvref_t; using OtherType = std::remove_cvref_t; if constexpr (std::is_same_v) { Result = Upcasted == OtherUpcasted; } }); }); return Result; } auto get() const noexcept { return Pointer.get(); } auto &operator*() const { return *Pointer; } auto *operator->() const noexcept { return Pointer.operator->(); } void reset(pointer Other = pointer()) noexcept { Pointer.reset(Other); revng_assert(Pointer.get_deleter() == Deleter); } private: inner_pointer Pointer; }; template concept IsUpcastablePointer = is_specialization_v; template concept IsNotUpcastablePointer = not IsUpcastablePointer;