1313#include " phasar/DataFlow/IfdsIde/EdgeFunctionSingletonCache.h"
1414#include " phasar/Utils/ByRef.h"
1515#include " phasar/Utils/EmptyBaseOptimizationUtils.h"
16+ #include " phasar/Utils/ErrorFwd.h"
1617#include " phasar/Utils/Macros.h"
1718#include " phasar/Utils/TypeTraits.h"
1819
@@ -37,14 +38,23 @@ template <typename L> class EdgeFunction;
3738template <typename EF> class EdgeFunctionRef ;
3839
3940template <typename T>
40- concept IsEdgeFunction =
41- requires (const T &EF, const EdgeFunction<typename T::l_t > &TEEF,
42- EdgeFunctionRef<T> CEF, typename T::l_t Src) {
43- typename T::l_t ;
44- { EF.computeTarget (Src) } -> std::convertible_to<typename T::l_t >;
45- { T::compose (CEF, TEEF) } -> std::same_as<EdgeFunction<typename T::l_t >>;
46- { T::join (CEF, TEEF) } -> std::same_as<EdgeFunction<typename T::l_t >>;
47- };
41+ concept IsEdgeFunction = requires (const T &EF, typename T::l_t Src) {
42+ typename T::l_t ;
43+ { EF.computeTarget (Src) } -> std::convertible_to<typename T::l_t >;
44+ };
45+
46+ template <typename T>
47+ concept HasEFCompose = requires (EdgeFunctionRef<T> EFRef,
48+ const EdgeFunction<typename T::l_t > &EF) {
49+ {
50+ T::compose (EFRef, EF)
51+ } -> std::convertible_to<EdgeFunction<typename T::l_t >>;
52+ };
53+ template <typename T>
54+ concept HasEFJoin = requires (EdgeFunctionRef<T> EFRef,
55+ const EdgeFunction<typename T::l_t > &EF) {
56+ { T::join (EFRef, EF) } -> std::convertible_to<EdgeFunction<typename T::l_t >>;
57+ };
4858
4959enum class EdgeFunctionAllocationPolicy {
5060 SmallObjectOptimized,
@@ -100,29 +110,31 @@ class EdgeFunctionBase {
100110// / \brief Non-null reference to an edge function that is guarenteed to be
101111// / managed by an EdgeFunction object.
102112template <typename EF>
103- class [[clang::trivial_abi ]] EdgeFunctionRef final : EdgeFunctionBase {
113+ class [[gsl::Pointer(EF) ]] EdgeFunctionRef final : EdgeFunctionBase {
104114 template <typename L> friend class EdgeFunction ;
105115
106116public:
107117 using l_t = typename EF::l_t ;
108118
109- EdgeFunctionRef (const EdgeFunctionRef &) noexcept = default ;
110- EdgeFunctionRef &operator =(const EdgeFunctionRef &) noexcept = default ;
111- ~EdgeFunctionRef () = default ;
112-
113- const EF *operator ->() const noexcept { return getPtr<EF>(Instance); }
114- const EF *get () const noexcept { return getPtr<EF>(Instance); }
115- const EF &operator *() const noexcept { return *getPtr<EF>(Instance); }
119+ [[nodiscard]] constexpr const EF *operator ->() const noexcept {
120+ return getPtr<EF>(Instance);
121+ }
122+ [[nodiscard]] constexpr const EF *get () const noexcept {
123+ return getPtr<EF>(Instance);
124+ }
125+ [[nodiscard]] constexpr const EF &operator *() const noexcept {
126+ return *getPtr<EF>(Instance);
127+ }
116128
117- [[nodiscard]] bool isCached () const noexcept {
129+ [[nodiscard]] constexpr bool isCached () const noexcept {
118130 if constexpr (IsSOOCandidate<EF>) {
119131 return false ;
120132 } else {
121133 return IsCached;
122134 }
123135 }
124136
125- [[nodiscard]] EdgeFunctionSingletonCache<EF> *
137+ [[nodiscard]] constexpr EdgeFunctionSingletonCache<EF> *
126138 getCacheOrNull () const noexcept {
127139 if (isCached ()) {
128140 return static_cast <const CachedRefCounted<EF> *>(Instance)->Cache ;
@@ -148,46 +160,46 @@ class [[clang::trivial_abi]] EdgeFunctionRef final : EdgeFunctionBase {
148160template <typename L>
149161// -- combined copy and move assignment
150162// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
151- class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase {
163+ class [[clang::trivial_abi, gsl::Owner ]] EdgeFunction final : EdgeFunctionBase {
152164public:
153165 using l_t = L;
154166
155167 // --- Constructors
156168
157169 // / Default-initializes the edge-function with nullptr
158- EdgeFunction () noexcept = default ;
170+ constexpr EdgeFunction () noexcept = default ;
159171 // / Default-initializes the edge-function with nullptr
160- EdgeFunction (std::nullptr_t ) noexcept : EdgeFunction () {}
172+ constexpr EdgeFunction (std::nullptr_t ) noexcept : EdgeFunction () {}
161173 // / Copy constructor. Increments the ref-count, if not small-object-optimized.
162174 EdgeFunction (const EdgeFunction &Other) noexcept
163175 : EdgeFunction (Other.EF , Other.VTAndHeapAlloc ) {}
164176 // / Move constructor. Does not increment the ref-count, but instead leaves the
165177 // / moved-from edge function in the nullptr state.
166- EdgeFunction (EdgeFunction &&Other) noexcept
178+ constexpr EdgeFunction (EdgeFunction &&Other) noexcept
167179 : EF (std::exchange (Other.EF , nullptr )),
168180 VTAndHeapAlloc (
169181 std::exchange (Other.VTAndHeapAlloc , decltype (VTAndHeapAlloc){})) {}
170182
171183 // / Standard swap; does not affect ref-counts
172- void swap (EdgeFunction &Other) noexcept {
184+ constexpr void swap (EdgeFunction &Other) noexcept {
173185 std::swap (EF, Other.EF );
174186 std::swap (VTAndHeapAlloc, Other.VTAndHeapAlloc );
175187 }
176188 // / Standard swap; does not affect ref-counts
177- friend void swap (EdgeFunction &LHS, EdgeFunction &RHS) noexcept {
189+ constexpr friend void swap (EdgeFunction &LHS, EdgeFunction &RHS) noexcept {
178190 LHS.swap (RHS);
179191 }
180192
181193 // / Combined copy- and move assignment. If the assigned-to edge function is
182194 // / not null, invokes the destructor on it, before overwriting its content.
183- EdgeFunction &operator =(EdgeFunction Other) noexcept {
195+ constexpr EdgeFunction &operator =(EdgeFunction Other) noexcept {
184196 std::destroy_at (this );
185197 return *new (this ) EdgeFunction (std::move (Other)); // NOLINT
186198 }
187199 // / Null-assignment operator. Decrements the ref-count if not
188200 // / small-object-optimized or already null. Leaves the assigned-to edge
189201 // / function in the nullptr state.
190- EdgeFunction &operator =(std::nullptr_t ) noexcept {
202+ constexpr EdgeFunction &operator =(std::nullptr_t ) noexcept {
191203 std::destroy_at (this );
192204 return *new (this ) EdgeFunction (); // NOLINT
193205 }
@@ -581,7 +593,9 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase {
581593
582594 // / Gets an opaque identifier for this edge function. Only meant for
583595 // / comparisons of object-identity. Do not dereference!
584- [[nodiscard]] const void *getOpaqueValue () const noexcept { return EF; }
596+ [[nodiscard]] constexpr const void *getOpaqueValue () const noexcept {
597+ return EF;
598+ }
585599
586600 // / Gets the cache where this edge function is being cached in. If this edge
587601 // / function is not cached (i.e., isCached() returns false), returns nullptr.
@@ -646,83 +660,117 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase {
646660 // NOLINTEND(readability-identifier-naming)
647661 };
648662
663+ template <typename ConcreteEF>
664+ static l_t computeTargetThunk (const void *EF, ByConstRef<l_t > Source) {
665+ return getPtr<ConcreteEF>(EF)->computeTarget (Source);
666+ }
667+
668+ template <typename ConcreteEF>
669+ static EdgeFunction composeThunk (const void *EF, const EdgeFunction &SecondEF,
670+ AllocationPolicy Policy) {
671+ if constexpr (HasEFCompose<ConcreteEF>) {
672+ return ConcreteEF::compose (
673+ EdgeFunctionRef<ConcreteEF>(
674+ EF, Policy == AllocationPolicy::CustomHeapAllocated),
675+ SecondEF);
676+ } else {
677+ composeEFPureVirtualError (llvm::getTypeName<ConcreteEF>(),
678+ llvm::getTypeName<l_t >());
679+ }
680+ }
681+
682+ template <typename ConcreteEF>
683+ static EdgeFunction joinThunk (const void *EF, const EdgeFunction &OtherEF,
684+ AllocationPolicy Policy) {
685+ if constexpr (HasEFJoin<ConcreteEF>) {
686+ return ConcreteEF::join (
687+ EdgeFunctionRef<ConcreteEF>(
688+ EF, Policy == AllocationPolicy::CustomHeapAllocated),
689+ OtherEF);
690+ } else {
691+ joinEFPureVirtualError (llvm::getTypeName<ConcreteEF>(),
692+ llvm::getTypeName<l_t >());
693+ }
694+ }
695+
696+ template <typename ConcreteEF>
697+ static bool equalsThunk (const void *EF1, const void *EF2) noexcept {
698+ static_assert (IsEqualityComparable<ConcreteEF> ||
699+ std::is_empty_v<ConcreteEF>,
700+ " An EdgeFunction must be equality comparable with "
701+ " operator==. Only if the type is empty, i.e. has no "
702+ " members, the comparison can be inferred." );
703+ if constexpr (IsEqualityComparable<ConcreteEF>) {
704+ return *getPtr<ConcreteEF>(EF1) == *getPtr<ConcreteEF>(EF2);
705+ } else {
706+ return true ;
707+ }
708+ }
709+
710+ template <typename ConcreteEF>
711+ static void printThunk (const void *EF, llvm::raw_ostream &OS) {
712+ if constexpr (is_llvm_printable_v<ConcreteEF>) {
713+ OS << *getPtr<ConcreteEF>(EF);
714+ } else {
715+ OS << llvm::getTypeName<ConcreteEF>();
716+ }
717+ }
718+
719+ template <typename ConcreteEF>
720+ static bool isConstantThunk (const void *EF) noexcept {
721+ if constexpr (HasIsConstant<ConcreteEF>) {
722+ static_assert (
723+ std::is_nothrow_invocable_v<decltype (&ConcreteEF::isConstant),
724+ const ConcreteEF &>,
725+ " The function isConstant() must be noexcept!" );
726+ return getPtr<ConcreteEF>(EF)->isConstant ();
727+ } else {
728+ return false ;
729+ }
730+ }
731+
732+ template <typename ConcreteEF>
733+ static void destroyThunk (const void *EF, AllocationPolicy Policy) noexcept {
734+ if constexpr (!IsSOOCandidate<ConcreteEF>) {
735+ if (Policy != AllocationPolicy::CustomHeapAllocated) {
736+ assert (Policy == AllocationPolicy::DefaultHeapAllocated);
737+ delete static_cast <const RefCounted<ConcreteEF> *>(EF);
738+ } else {
739+ auto CEF = static_cast <const CachedRefCounted<ConcreteEF> *>(EF);
740+ CEF->Cache ->erase (CEF->Value );
741+ delete CEF;
742+ }
743+ }
744+ }
745+
746+ template <typename ConcreteEF>
747+ static size_t getHashCodeThunk (const void *EF, const void *VT) noexcept {
748+ if constexpr (is_std_hashable_v<ConcreteEF>) {
749+ return std::hash<ConcreteEF>{}(*getPtr<ConcreteEF>(EF));
750+ } else if constexpr (is_llvm_hashable_v<ConcreteEF>) {
751+ using llvm::hash_value;
752+ return hash_value (*getPtr<ConcreteEF>(EF));
753+ } else {
754+ return llvm::hash_combine (EF, VT);
755+ }
756+ }
757+
758+ template <typename ConcreteEF>
759+ static size_t depthThunk (const void *EF) noexcept {
760+ if constexpr (HasDepth<ConcreteEF>) {
761+ return getPtr<ConcreteEF>(EF)->depth ();
762+ } else {
763+ return 1 ;
764+ }
765+ }
766+
649767 template <typename ConcreteEF>
650768 static constexpr VTable VTableFor = {
651- [](const void *EF, ByConstRef<l_t > Source) {
652- return getPtr<ConcreteEF>(EF)->computeTarget (Source);
653- },
654- [](const void *EF, const EdgeFunction &SecondEF,
655- AllocationPolicy Policy) {
656- return ConcreteEF::compose (
657- EdgeFunctionRef<ConcreteEF>(
658- EF, Policy == AllocationPolicy::CustomHeapAllocated),
659- SecondEF);
660- },
661- [](const void *EF, const EdgeFunction &OtherEF, AllocationPolicy Policy) {
662- return ConcreteEF::join (
663- EdgeFunctionRef<ConcreteEF>(
664- EF, Policy == AllocationPolicy::CustomHeapAllocated),
665- OtherEF);
666- },
667- [](const void *EF1, const void *EF2) noexcept {
668- static_assert (IsEqualityComparable<ConcreteEF> ||
669- std::is_empty_v<ConcreteEF>,
670- " An EdgeFunction must be equality comparable with "
671- " operator==. Only if the type is empty, i.e. has no "
672- " members, the comparison can be inferred." );
673- if constexpr (IsEqualityComparable<ConcreteEF>) {
674- return *getPtr<ConcreteEF>(EF1) == *getPtr<ConcreteEF>(EF2);
675- } else {
676- return true ;
677- }
678- },
679- [](const void *EF, llvm::raw_ostream &OS) {
680- if constexpr (is_llvm_printable_v<ConcreteEF>) {
681- OS << *getPtr<ConcreteEF>(EF);
682- } else {
683- OS << llvm::getTypeName<ConcreteEF>();
684- }
685- },
686- [](const void *EF) noexcept {
687- if constexpr (HasIsConstant<ConcreteEF>) {
688- static_assert (
689- std::is_nothrow_invocable_v<decltype (&ConcreteEF::isConstant),
690- const ConcreteEF &>,
691- " The function isConstant() must be noexcept!" );
692- return getPtr<ConcreteEF>(EF)->isConstant ();
693- } else {
694- return false ;
695- }
696- },
697- [](const void *EF, AllocationPolicy Policy) noexcept {
698- if constexpr (!IsSOOCandidate<ConcreteEF>) {
699- if (Policy != AllocationPolicy::CustomHeapAllocated) {
700- assert (Policy == AllocationPolicy::DefaultHeapAllocated);
701- delete static_cast <const RefCounted<ConcreteEF> *>(EF);
702- } else {
703- auto CEF = static_cast <const CachedRefCounted<ConcreteEF> *>(EF);
704- CEF->Cache ->erase (CEF->Value );
705- delete CEF;
706- }
707- }
708- },
709- [](const void *EF, const void *VT) noexcept -> size_t {
710- if constexpr (is_std_hashable_v<ConcreteEF>) {
711- return std::hash<ConcreteEF>{}(*getPtr<ConcreteEF>(EF));
712- } else if constexpr (is_llvm_hashable_v<ConcreteEF>) {
713- using llvm::hash_value;
714- return hash_value (*getPtr<ConcreteEF>(EF));
715- } else {
716- return llvm::hash_combine (EF, VT);
717- }
718- },
719- [](const void *EF) noexcept -> size_t {
720- if constexpr (HasDepth<ConcreteEF>) {
721- return getPtr<ConcreteEF>(EF)->depth ();
722- } else {
723- return 1 ;
724- }
725- },
769+ &computeTargetThunk<ConcreteEF>, &composeThunk<ConcreteEF>,
770+ &joinThunk<ConcreteEF>, &equalsThunk<ConcreteEF>,
771+ &printThunk<ConcreteEF>, &isConstantThunk<ConcreteEF>,
772+ &destroyThunk<ConcreteEF>, &getHashCodeThunk<ConcreteEF>,
773+ &depthThunk<ConcreteEF>,
726774 };
727775
728776 // Utility ctor for (copy) construction. Increments the ref-count if
0 commit comments