#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include #include #include #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" #include "llvm/Support/YAMLTraits.h" #include "revng/ADT/CompilationTime.h" #include "revng/ADT/STLExtras.h" #include "revng/Pipeline/Rank.h" #include "revng/Support/MetaAddress.h" #include "revng/Support/YAMLTraits.h" namespace pipeline { /// A location represents a position of an entity in the ranked artifact system. /// /// Each location has a rank associated with it, it's set using /// \tparam Rank. /// /// In serialized form, a location is the name of its rank plus a list of /// the values of its keys (one key per rank depth) separated by '/'. The types /// are determined by the parents of the rank (see `Rank::Parent`). /// /// In deserialized form, the name gets lifted to a compilation time and can be /// accessed at `Rank::RankName`. The tuple of the keys is publicly inherited /// from, so it can either be accessed as normal tuple (for example, using /// `std::get` after casting. There's a cast helper members /// (`tuple()`)) or using special accessors like `at()`. template class Location : public Rank::Tuple { private: using Tuple = typename Rank::Tuple; static_assert(std::tuple_size_v == Rank::Depth); static constexpr size_t Size = Rank::Depth; private: constexpr static llvm::StringRef Separator = "/"; public: using Tuple::Tuple; Location(const Location &) = default; Location(Location &&) = default; Location &operator=(const Location &) = default; Location &operator=(Location &&) = default; Tuple &tuple() { return *this; } const Tuple &tuple() const { return *this; } template AnotherRank> auto &at(const AnotherRank &) { return std::get(tuple()); } template AnotherRank> const auto &at(const AnotherRank &) const { return std::get(tuple()); } template AnotherRank> auto &back() { return std::get - 1>(tuple()); } const auto &back() { return std::get - 1>(tuple()); } auto parent() const requires(not std::is_same_v) { return Location::convert(*this); } /// A static helper function for constructing locations from different /// location with related ranks. /// /// If output location's rank is higher, all the remaining keys are left /// uninitialized (or default constructed, depending on the type). /// /// If output location's rank is lower, all the extra keys get discarded. template requires(RankConvertibleTo || RankConvertibleTo) static constexpr Location convert(const Location &Another) { Location Result; constexpr auto Common = std::min(Rank::Depth, AnotherRank::Depth); compile_time::repeat([&Result, &Another] { std::get(Result.tuple()) = std::get(Another.tuple()); }); return Result; } public: /// Serializes a location into a string. /// /// TODO: look into the constexpr implementation once constexpr strings are /// implemented (clang-15+) std::string toString() const { std::string Result; Result += Separator; Result += Rank::RankName; compile_time::repeat([&Result, this] { Result += Separator; Result += ::toString(std::get(tuple())); }); return Result; } /// Deserializes the location from a string. /// /// If the string is not a valid location OR if its rank is different /// from this location's rank, `std::nullopt` is returned instead. static constexpr std::optional> fromString(llvm::StringRef String) { Location Result; auto MaybeSteps = compile_time::split(Separator, String); if (!MaybeSteps.has_value()) return std::nullopt; revng_assert(MaybeSteps.value().size() == Size + 2); constexpr llvm::StringRef ExpectedName = Rank::RankName; if (MaybeSteps->at(0) != "" || MaybeSteps->at(1) != ExpectedName) return std::nullopt; auto Success = compile_time::repeatAnd([&] { using T = typename std::tuple_element::type; using revng::detail::fromStringImpl; auto MaybeValue = fromStringImpl(MaybeSteps->at(Idx + 2)); if (auto Error = MaybeValue.takeError()) { llvm::consumeError(std::move(Error)); return false; } std::get(Result) = std::move(*MaybeValue); return true; }); if (Success == false) return std::nullopt; return Result; } }; /// Constructs a new location from arbitrary arguments. /// /// The first argument is used to indicate the rank of location to be /// constructed, the rest are the arguments. template requires std::is_convertible_v, typename Rank::Tuple> inline constexpr Location location(const Rank &, Args &&...As) { return Location(As...); } /// Constructs a new location from arbitrary arguments and instantly serializes /// it into its string representation. template requires std::is_convertible_v, typename Rank::Tuple> inline std::string locationString(const Rank &R, Args &&...As) { return location(R, std::forward(As)...).toString(); } /// A helper interface for location deserialization without the need to /// explicitly mention the expected rank's type. /// /// It takes a reference to the corresponding rank object as its first argument. template inline constexpr std::optional> locationFromString(const Rank &, llvm::StringRef String) { return Location::fromString(String); } /// A helper interface for location conversion. /// /// It discloses the static `convert` member in an easier-to-access fashion. template requires(RankConvertibleTo) inline constexpr Location convertLocation(const ResultRank &Result, const Location &Input) { return Location::convert(Input); } namespace detail { /// Shorthand to "decay type then make a const pointer to it". template using ConstP = std::add_pointer_t>>; } // namespace detail /// A helper function used for deserializing any number of differently /// ranked locations at once. /// /// \param Serialized is the string containing the serialized location. /// \param Expected indicates the expected rank to be returned. /// \param Supported lists all the other ranks that are supported, they must all /// be convertible to the \ref Expected rank. /// /// \returns a valid location of \ref Expected rank if the \ref Serialized /// string contains a valid serialized form of any location type within the /// \ref Supported list (including \ref Expected), `std::nullopt` otherwise. template requires(RankConvertibleTo && ...) inline constexpr std::optional> genericLocationFromString(llvm::StringRef Serialized, const ExpectedRank &Expected, const SupportedRanks &...Supported) { Location Result; bool ParsedOnce = false; using TupleType = std::tuple, detail::ConstP...>; TupleType Tuple{ &Expected, &Supported... }; constexpr size_t TupleSize = std::tuple_size_v; compile_time::repeat([&, Serialized] { auto MaybeLoc = locationFromString(*std::get(Tuple), Serialized); if (MaybeLoc.has_value()) { Result = Location::convert(*MaybeLoc); revng_assert(ParsedOnce == false, "Duplicate supported ranks are not allowed"); ParsedOnce = true; } }); if (ParsedOnce) return Result; else return std::nullopt; } /// The simplified interface for generic location deserialization allowing for /// fetching a single key, indicated by \tparam Idx. template requires(RankConvertibleTo && ...) constexpr std::optional> genericLocationFromString(llvm::StringRef Serialized, const ExpectedRank &Expected, const SupportedRanks &...Supported) { static_assert(Idx < std::decay_t::Depth); auto Result = genericLocationFromString(Serialized, Expected, Supported...); if (Result.has_value()) return std::get(Result->tuple()); else return std::nullopt; } } // namespace pipeline template struct llvm::yaml::ScalarTraits> { static void output(const pipeline::Location &Value, void *, llvm::raw_ostream &Output) { Output << Value.toString(); } static StringRef input(llvm::StringRef Scalar, void *, pipeline::Location &Value) { auto MaybeValue = pipeline::Location::fromString(Scalar); revng_assert(MaybeValue.has_value()); Value = std::move(*MaybeValue); return StringRef(); } static QuotingType mustQuote(StringRef) { return QuotingType::Double; } };