https://github.com/mtrofin updated https://github.com/llvm/llvm-project/pull/165803
>From 2dd8a7cf0efaac45e8c6cef5dbd27b495757cebd Mon Sep 17 00:00:00 2001
From: Mircea Trofin <mtrofin at google.com>
Date: Thu, 30 Oct 2025 16:39:49 -0700
Subject: [PATCH] [ADT] Introduce a `static_cast_to`
---
llvm/include/llvm/Support/Casting.h | 52 +++++++++++++++++++++++++++++
llvm/unittests/Support/Casting.cpp | 48 ++++++++++++++++++++++++++
2 files changed, 100 insertions(+)
diff --git a/llvm/include/llvm/Support/Casting.h b/llvm/include/llvm/Support/Casting.h
index 6f6df2e9703ea..aa4a4ce29930d 100644
--- a/llvm/include/llvm/Support/Casting.h
+++ b/llvm/include/llvm/Support/Casting.h
@@ -816,6 +816,42 @@ template <typename... Types> struct IsaAndPresentCheckPredicate {
return isa_and_present<Types...>(Val);
}
};
+
+//===----------------------------------------------------------------------===//
+// Casting Function Objects
+//===----------------------------------------------------------------------===//
+
+/// Usable in generic algorithms like map_range
+template <typename U> struct StaticCastFunc {
+ template <typename T> decltype(auto) operator()(const T &Val) const {
+ return static_cast<U>(Val);
+ }
+};
+
+template <typename U> struct DynCastFunc {
+ template <typename T> decltype(auto) operator()(const T &Val) const {
+ return dyn_cast<U>(Val);
+ }
+};
+
+template <typename U> struct CastFunc {
+ template <typename T> decltype(auto) operator()(const T &Val) const {
+ return cast<U>(Val);
+ }
+};
+
+template <typename U> struct CastIfPresentFunc {
+ template <typename T> decltype(auto) operator()(const T &Val) const {
+ return cast_if_present<U>(Val);
+ }
+};
+
+template <typename U> struct DynCastIfPresentFunc {
+ template <typename T> decltype(auto) operator()(const T &Val) const {
+ return dyn_cast_if_present<U>(Val);
+ }
+};
+
} // namespace detail
/// Function object wrapper for the `llvm::isa` type check. The function call
@@ -841,6 +877,22 @@ template <typename... Types>
inline constexpr detail::IsaAndPresentCheckPredicate<Types...>
IsaAndPresentPred{};
+/// Function objects corresponding to the Cast types defined above.
+template <typename... Types>
+inline constexpr detail::StaticCastFunc<Types...> StaticCastTo{};
+
+template <typename... Types>
+inline constexpr detail::CastFunc<Types...> CastTo{};
+
+template <typename... Types>
+inline constexpr detail::CastIfPresentFunc<Types...> CastIfPresentTo{};
+
+template <typename... Types>
+inline constexpr detail::DynCastIfPresentFunc<Types...> DynCastIfPresentTo{};
+
+template <typename... Types>
+inline constexpr detail::DynCastFunc<Types...> DynCastTo{};
+
} // end namespace llvm
#endif // LLVM_SUPPORT_CASTING_H
diff --git a/llvm/unittests/Support/Casting.cpp b/llvm/unittests/Support/Casting.cpp
index 790675083614b..e9ff4f9e2fea7 100644
--- a/llvm/unittests/Support/Casting.cpp
+++ b/llvm/unittests/Support/Casting.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/Support/Casting.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/IR/User.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
@@ -561,6 +562,53 @@ TEST(CastingTest, assertion_check_unique_ptr) {
<< "Invalid cast of const ref did not cause an abort()";
}
+TEST(Casting, StaticCastPredicate) {
+ std::vector<uint32_t> Values{1, 2};
+
+ EXPECT_TRUE(
+ all_of(map_range(Values, StaticCastTo<uint64_t>),
+ [](const auto &V) { return sizeof(V) == sizeof(uint64_t); }));
+}
+
+TEST(Casting, LLVMRTTIPredicates) {
+ struct Base {
+ enum Kind { BK_Base, BK_Derived };
+ const Kind K;
+ Base(Kind K = BK_Base) : K(K) {}
+ Kind getKind() const { return K; }
+ virtual ~Base() = default;
+ };
+
+ struct Derived : public Base {
+ Derived() : Base(BK_Derived) {}
+ static bool classof(const Base *B) { return B->getKind() == BK_Derived; }
+ };
+
+ Base B;
+ Derived D;
+
+ std::vector<Base *> RTTI{&B, &D, nullptr};
+ auto NonNull = drop_end(RTTI);
+ auto OnlyDerived = drop_begin(NonNull);
+ auto Casted = map_range(OnlyDerived, CastTo<Derived>);
+ auto DynCasted = map_range(NonNull, DynCastTo<Derived>);
+ auto DynCastedIfPresent = map_range(RTTI, DynCastIfPresentTo<Derived>);
+ auto CastedIfPresent = map_range(drop_begin(RTTI), CastIfPresentTo<Derived>);
+
+ auto Same = [](const auto &T) {
+ const auto &[A, B] = T;
+ return A == B;
+ };
+ EXPECT_TRUE(all_of(zip_longest(Casted, std::vector<Derived *>{&D}), Same));
+ EXPECT_TRUE(all_of(
+ zip_longest(DynCasted, std::vector<Derived *>{nullptr, &D}), Same));
+ EXPECT_TRUE(all_of(zip_longest(DynCastedIfPresent,
+ std::vector<Derived *>{nullptr, &D, nullptr}),
+ Same));
+ EXPECT_TRUE(all_of(
+ zip_longest(CastedIfPresent, std::vector<Derived *>{&D, nullptr}), Same));
+}
+
} // end namespace assertion_checks
#endif
} // end namespace