#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" #include "revng/ADT/STLExtras.h" #include "revng/Support/Assert.h" template struct KeyedObjectTraits; template struct concrete_types_traits; template using concrete_types_traits_t = typename concrete_types_traits::type; // clang-format off template concept ConcreteTypeTraitCompatible = requires { typename concrete_types_traits_t; } && StrictSpecializationOf, std::tuple>; template concept HasLLVMRTTI = requires(T *A) { { A->classof(A) } -> std::same_as; }; template concept Upcastable = HasLLVMRTTI and ConcreteTypeTraitCompatible; template using pointee = typename std::pointer_traits>::element_type; template concept Dereferenceable = requires(T A) { { *A }; }; // clang-format on static_assert(Dereferenceable); static_assert(not Dereferenceable); template concept UpcastablePointerLike = Dereferenceable and Upcastable>; // clang-format off template concept NotUpcastablePointerLike = not UpcastablePointerLike; template requires(not std::is_void_v) ReturnT upcast(P &&Upcastable, const L &Callable, ReturnT &&IfNull) { // clang-format on using pointee = std::remove_reference_t; using concrete_types = concrete_types_traits_t; auto *Pointer = &*Upcastable; if (Pointer == nullptr) return std::forward(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, std::forward(IfNull)); } } else { revng_abort(); } } template llvm::Error upcast(P &&Upcastable, const L &Callable, llvm::Error IfNull) { // clang-format on 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)) { llvm::consumeError(std::move(IfNull)); return Callable(*Upcasted); } else { return upcast(Upcastable, Callable, std::move(IfNull)); } } else { revng_abort(); } } template void upcast(P &&Upcastable, L &&Callable) { auto Wrapper = [&](auto &Upcasted) { Callable(Upcasted); return true; }; upcast(Upcastable, Wrapper, false); } template void invokeByKey(const KeyT &Key, L &&Callable) { auto Upcastable = KeyedObjectTraits

::fromKey(Key); upcast(Upcastable, [&Callable](const UpcastedT &C) { Callable(static_cast(nullptr)); }); } template ReturnT invokeByKey(const KeyT &Key, L &&Callable, const ReturnT &IfNull) { auto Upcastable = KeyedObjectTraits

::fromKey(Key); auto ToCall = [&Callable](const UpcastedT &C) { return Callable(static_cast(nullptr)); }; return upcast(Upcastable, ToCall, IfNull); } /// 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(L &&Callable) { ::upcast(Pointer, std::forward(Callable)); } template void upcast(L &&Callable) const { ::upcast(Pointer, std::forward(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())); } return *this; } UpcastablePointer(const UpcastablePointer &Other) : UpcastablePointer(nullptr) { *this = Other; } UpcastablePointer &operator=(UpcastablePointer &&Other) { if (&Other != this) { Pointer.reset(Other.Pointer.release()); } 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); } private: inner_pointer Pointer; };