https://github.com/steakhal created https://github.com/llvm/llvm-project/pull/190422
This is my idea for a more generic `llvm::Registry` replacement. From db9a2a7a31f5e714a5a085703cb52ba25fd5a70c Mon Sep 17 00:00:00 2001 From: Balazs Benics <[email protected]> Date: Fri, 3 Apr 2026 22:44:29 +0100 Subject: [PATCH] Add Registry V2 prototype --- .../Core/TUSummary/TUSummaryBuilder.cpp | 264 ++++++++++++++++++ 1 file changed, 264 insertions(+) diff --git a/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.cpp b/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.cpp index f3280b02ce5ef..861bb85f81bde 100644 --- a/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.cpp +++ b/clang/lib/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.cpp @@ -2,7 +2,13 @@ #include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h" #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h" #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h" +#include "llvm/ADT/STLFunctionalExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/Error.h" +#include <list> #include <memory> +#include <type_traits> #include <utility> using namespace clang; @@ -19,3 +25,261 @@ TUSummaryBuilder::addSummaryImpl(EntityId Entity, auto [It, Inserted] = EntitySummaries.try_emplace(Entity, std::move(Data)); return {It->second.get(), Inserted}; } + +namespace clang::ssaf::v2 { + +template <typename... Headers> struct Header : Headers... { + template <class... Ts> + Header(Ts &&...Args) : Headers{std::forward<Ts>(Args)}... {} +}; + +struct Name { + const llvm::StringRef Name; +}; +struct Description { + const llvm::StringRef Description; +}; + +template <typename Base, typename... CtorParamTypes> struct InstantiateOnHeap { + static_assert(std::has_virtual_destructor_v<Base>); + using ReturnType = std::unique_ptr<Base>; + + template <class Derived> static ReturnType create(CtorParamTypes &&...Args) { + return std::make_unique<Derived>(std::forward<CtorParamTypes>(Args)...); + } +}; + +template <typename Base, typename... CtorParamTypes> struct InstantiateOnStack { + static_assert(!std::is_polymorphic_v<Base>); + using ReturnType = Base; + + template <class Derived> static ReturnType create(CtorParamTypes &&...Args) { + static_assert(sizeof(Base) == sizeof(Derived)); + return Derived(std::forward<CtorParamTypes>(Args)...); + } +}; + +/// Headers: +/// - Header<Name> +/// - Header<Description> +/// - Header<Name, Description> +/// - Header<YourCustom> +/// CreationStrategy: +/// - InstantiateOnStack (returns T) +/// - InstantiateOnHeap (returns unique_ptr<T>) +/// - YourCustom (returns whatever you want for T) +/// Base: The base type of +template <class Headers, template <typename...> class CreationStrategy, + typename Base, typename... CtorParamTypes> +class Registry { +public: + static auto entries() { return llvm::iterator_range{Nodes}; } + template <typename Derived> struct Add { + template <class... Ts> Add(Ts &&...Args) { + Nodes.emplace_back(&SpecificCreationStrategy::template create<Derived>, + std::forward<Ts>(Args)...); + } + }; + +private: + Registry() = delete; + using SpecificCreationStrategy = CreationStrategy<Base, CtorParamTypes...>; + using ReturnType = typename SpecificCreationStrategy::ReturnType; + + class Factory { + using FactoryFnRef = llvm::function_ref<ReturnType(CtorParamTypes...)>; + + public: + explicit Factory(FactoryFnRef F) : F(F) {} + ReturnType instantiate(CtorParamTypes... Args) const { + return F(std::forward<CtorParamTypes>(Args)...); + } + + private: + const FactoryFnRef F; + }; + + struct RecipeData : Factory, Headers { + template <class First, class... Ts> + RecipeData(First &&Arg, Ts &&...Args) + : Factory{std::forward<First>(Arg)}, + Headers{std::forward<Ts>(Args)...} {} + }; + + static inline std::list<RecipeData> Nodes; +}; + +/// Mimics the llvm::Registry current behavior: +/// Creates nodes holding the Name and Description. +/// They also instantiate on the heap using unique_ptrs. +template <typename Base, typename... CtorParamTypes> +using BasicRegistry = + v2::Registry<v2::Header<v2::Name, v2::Description>, v2::InstantiateOnHeap, + Base, CtorParamTypes...>; + +} // namespace clang::ssaf::v2 + +//------------------ +struct PluginBase { + virtual ~PluginBase() = 0; + + struct Data { + int Version; + }; + virtual Data getData() const = 0; +}; + +struct FancyPlugin final : PluginBase { + Data getData() const override { return Data{404}; } +}; + +struct MyFormatInfo { + using ser = llvm::function_ref<void(int)>; + using deser = llvm::function_ref<int(std::string)>; + using namer = llvm::function_ref<llvm::StringLiteral()>; + MyFormatInfo(namer F, ser s, deser d) : For(F()), s(s), d(d) {} + + llvm::StringLiteral For; + ser s; + deser d; +}; + +extern void serializeFancy(int); +extern int deserializeFancy(std::string); + +// This compile-time binds all fields of the Base class. +template <auto... Args> struct MyFormatInfoFor : MyFormatInfo { + MyFormatInfoFor() : MyFormatInfo{Args...} {} +}; + +// We can't pass a string-literal as a NTTP, but we can pass a function +// returning the desired literal. Classic metaprogramming trick. +static constexpr llvm::StringLiteral FancyName() { return "Fancy"; } +struct FancyFormatInfo + : MyFormatInfoFor<FancyName, serializeFancy, deserializeFancy> {}; + +//------------------ + +using MyBasicRegistry = v2::BasicRegistry<PluginBase>; +static auto RegisterCommonEntry = + MyBasicRegistry::Add<FancyPlugin>("Name", "Desc"); +static void test_basicregistry() { + for (const auto &Recipe : MyBasicRegistry::entries()) { + llvm::errs() << "Name: " << Recipe.Name << "\n"; + llvm::errs() << "Desc: " << Recipe.Description << "\n"; + std::unique_ptr<PluginBase> Uptr = Recipe.instantiate(); + int V = Uptr->getData().Version; + } +} + +//------------------ + +using MyFormatInfoRegistry = + v2::Registry<v2::Header<v2::Name>, v2::InstantiateOnStack, MyFormatInfo>; +static auto RegisterFancy = + MyFormatInfoRegistry::Add<FancyFormatInfo>("FancyFormatInfo"); +static void test_myformatinfo() { + for (const auto &Recipe : MyFormatInfoRegistry::entries()) { + llvm::StringRef Name = Recipe.Name; + MyFormatInfo Instance = Recipe.instantiate(); + llvm::StringLiteral For = Instance.For; + llvm::function_ref<void(int)> s = Instance.s; + llvm::function_ref<int(std::string)> d = Instance.d; + } +} + +//------------------ + +using PluginRegistry = v2::Registry<v2::Header<v2::Name, v2::Description>, + v2::InstantiateOnHeap, PluginBase>; +static auto RegisterFancyPlugin = + PluginRegistry::Add<FancyPlugin>("FancyPlugin", "desc"); + +static void test_fancy_plugin() { + for (const auto &Recipe : PluginRegistry::entries()) { + llvm::StringRef Name = Recipe.Name; + llvm::StringRef Desc = Recipe.Description; + std::unique_ptr<PluginBase> Instance = Recipe.instantiate(); + int V = Instance->getData().Version; + } +} + +//------------------ + +struct FailableBase { + virtual ~FailableBase() = 0; + virtual float brum_brum() = 0; + + using ReturnType = llvm::Expected<std::unique_ptr<FailableBase>>; + + // Custom create function that returns llvm::Expected. + // This essentially behaves as the 'constructor' of 'Derived' that can do + // precondition validation and fail for different reasons. + template <class Derived> static ReturnType create(int Num) { + if (Num > 100) + return llvm::createStringError("Greater than 100"); + + // It doesn't need to actually forward the 'int' if we don't want to. + return std::make_unique<Derived>(); + } +}; + +struct FailableDerived final : FailableBase { + float brum_brum() override { return 3.1f; } +}; + +template <typename Base, typename... CtorParamTypes> +struct InstantiateUsingStaticMemberCreate { + using ReturnType = typename Base::ReturnType; + + template <class Derived> static ReturnType create(CtorParamTypes &&...Args) { + return Base::template create<Derived>( + std::forward<CtorParamTypes>(Args)...); + } +}; + +using FailableRegistry = + v2::Registry<v2::Header<v2::Name, v2::Description>, + InstantiateUsingStaticMemberCreate, FailableBase, int>; +static auto RegisterFailable = + FailableRegistry::Add<FailableDerived>("FancyPlugin", "desc"); +static void test_failable(int N) { + for (const auto &Recipe : FailableRegistry::entries()) { + llvm::StringRef Name = Recipe.Name; + llvm::StringRef Desc = Recipe.Description; + llvm::Expected<std::unique_ptr<FailableBase>> ExpectedInstance = + Recipe.instantiate(N); + if (!ExpectedInstance) { + llvm::logAllUnhandledErrors(ExpectedInstance.takeError(), llvm::errs()); + continue; + } + float f = (*ExpectedInstance)->brum_brum(); + } +} + +//------------------ + +struct ConsumedID { + const unsigned ID; + + ConsumedID(unsigned ID) : ID(ID) { + llvm::errs() << "ConsumedID ctor side-effect ran with consumed ID " << ID + << "\n"; + } +}; + +template <unsigned I> struct ConsumedIdOf : ConsumedID { + ConsumedIdOf() : ConsumedID{I} {} +}; +using ConsumedIDRegistry = + v2::Registry<v2::Header<>, v2::InstantiateOnStack, ConsumedID>; +static auto ReserveID = ConsumedIDRegistry::Add<ConsumedIdOf<10>>(); + +static void test_consumed_ids() { + for (const auto &Recipe : ConsumedIDRegistry::entries()) { + ConsumedID Obj = Recipe.instantiate(); + unsigned UsedID = Obj.ID; + } +} + +//------------------ _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
