[llvm] [ADT] Introduce a `static_cast_to` (PR #165803)

Mircea Trofin via llvm-commits llvm-commits at lists.llvm.org
Mon Nov 3 10:45:17 PST 2025


https://github.com/mtrofin updated https://github.com/llvm/llvm-project/pull/165803

>From b0877cd73debe2f07530f391a9fab1037883a899 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  | 47 +++++++++++++++++++++++++++
 llvm/unittests/ADT/STLExtrasTest.cpp |  1 +
 llvm/unittests/Support/Casting.cpp   | 48 ++++++++++++++++++++++++++++
 3 files changed, 96 insertions(+)

diff --git a/llvm/include/llvm/Support/Casting.h b/llvm/include/llvm/Support/Casting.h
index 6f6df2e9703ea..79dcea0ec9d34 100644
--- a/llvm/include/llvm/Support/Casting.h
+++ b/llvm/include/llvm/Support/Casting.h
@@ -816,6 +816,37 @@ template <typename... Types> struct IsaAndPresentCheckPredicate {
     return isa_and_present<Types...>(Val);
   }
 };
+
+template <typename U> struct StaticCastPredicate {
+  template <typename T> decltype(auto) operator()(const T &Val) const {
+    return static_cast<U>(Val);
+  } // namespace detail
+};
+
+template <typename U> struct DynCastPredicate {
+  template <typename T> decltype(auto) operator()(const T &Val) const {
+    return dyn_cast<U>(Val);
+  } // namespace detail
+};
+
+template <typename U> struct CastPredicate {
+  template <typename T> decltype(auto) operator()(const T &Val) const {
+    return cast<U>(Val);
+  } // namespace detail
+};
+
+template <typename U> struct CastIfPresentPredicate {
+  template <typename T> decltype(auto) operator()(const T &Val) const {
+    return cast_if_present<U>(Val);
+  }
+};
+
+template <typename U> struct DynCastIfPresentPredicate {
+  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 +872,22 @@ template <typename... Types>
 inline constexpr detail::IsaAndPresentCheckPredicate<Types...>
     IsaAndPresentPred{};
 
+template <typename... Types>
+inline constexpr detail::StaticCastPredicate<Types...> StaticCastTo{};
+
+template <typename... Types>
+inline constexpr detail::CastPredicate<Types...> CastTo{};
+
+template <typename... Types>
+inline constexpr detail::CastIfPresentPredicate<Types...> CastIfPresentTo{};
+
+template <typename... Types>
+inline constexpr detail::DynCastIfPresentPredicate<Types...>
+    DynCastIfPresentTo{};
+
+template <typename... Types>
+inline constexpr detail::DynCastPredicate<Types...> DynCastTo{};
+
 } // end namespace llvm
 
 #endif // LLVM_SUPPORT_CASTING_H
diff --git a/llvm/unittests/ADT/STLExtrasTest.cpp b/llvm/unittests/ADT/STLExtrasTest.cpp
index 966b1f01e8a31..84b04fd03b359 100644
--- a/llvm/unittests/ADT/STLExtrasTest.cpp
+++ b/llvm/unittests/ADT/STLExtrasTest.cpp
@@ -9,6 +9,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.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



More information about the llvm-commits mailing list