[llvm] [orc-rt] CallableTraitsHelper - record operator()'s noexcept-specifier (PR #206891)

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 30 22:20:37 PDT 2026


https://github.com/lhames created https://github.com/llvm/llvm-project/pull/206891

Adds a `bool IsNoexcept` template parameter to CallableTraitsHelper's impl-class template argument (after the existing IsConst from 4bab60f2c63). It records the noexcept-specification on the callable's function type.

Specializations are added for noexcept-qualified forms. Existing specializations propagate `IsNoexcept = false`. CallableArgInfoImpl exposes the captured bool as `static constexpr bool is_noexcept`.

Existing pass-through adapters (ErrorHandlerTraitsImplAdapter, ErrorWrapImplAdapter, WFHandlerTraitsImplAdapter) are updated to accept and discard the additional argument.

>From ba96deda3d313be8f3c09a107dcf600de79030ab Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Wed, 1 Jul 2026 15:11:37 +1000
Subject: [PATCH] [orc-rt] CallableTraitsHelper - record operator()'s
 noexcept-specifier

Adds a `bool IsNoexcept` template parameter to CallableTraitsHelper's
impl-class template argument (after the existing IsConst from 4bab60f2c63). It
records the noexcept-specification on the callable's function type.

Specializations are added for noexcept-qualified forms. Existing specializations
propagate `IsNoexcept = false`. CallableArgInfoImpl exposes the captured bool as
`static constexpr bool is_noexcept`.

Existing pass-through adapters (ErrorHandlerTraitsImplAdapter,
ErrorWrapImplAdapter, WFHandlerTraitsImplAdapter) are updated to accept and
discard the additional argument.
---
 orc-rt/include/orc-rt/CallableTraitsHelper.h  | 82 +++++++++++++++----
 orc-rt/include/orc-rt/Error.h                 |  5 +-
 orc-rt/include/orc-rt/WrapperFunction.h       |  2 +-
 orc-rt/unittests/CallableTraitsHelperTest.cpp | 73 +++++++++++++++++
 4 files changed, 145 insertions(+), 17 deletions(-)

diff --git a/orc-rt/include/orc-rt/CallableTraitsHelper.h b/orc-rt/include/orc-rt/CallableTraitsHelper.h
index f37ccc9e9303b..32024a007aa89 100644
--- a/orc-rt/include/orc-rt/CallableTraitsHelper.h
+++ b/orc-rt/include/orc-rt/CallableTraitsHelper.h
@@ -24,7 +24,8 @@ namespace orc_rt {
 ///
 /// This can be used to simplify the implementation of classes that need to
 /// operate on callable types.
-template <template <bool /* is_const */, typename...> typename ImplT,
+template <template <bool /* is_const */, bool /* is_noexcept */,
+                    typename...> typename ImplT,
           typename C>
 struct CallableTraitsHelper
     : CallableTraitsHelper<
@@ -32,40 +33,93 @@ struct CallableTraitsHelper
           decltype(&std::remove_cv_t<std::remove_reference_t<C>>::operator())> {
 };
 
-template <template <bool /* is_const */, typename...> typename ImplT,
+template <template <bool /* is_const */, bool /* is_noexcept */,
+                    typename...> typename ImplT,
           typename RetT, typename... ArgTs>
 struct CallableTraitsHelper<ImplT, RetT(ArgTs...)>
-    : ImplT</* is_const = */ false, RetT, ArgTs...> {};
+    : ImplT</* is_const = */ false, /* is_noexcept = */ false, RetT, ArgTs...> {
+};
+
+template <template <bool /* is_const */, bool /* is_noexcept */,
+                    typename...> typename ImplT,
+          typename RetT, typename... ArgTs>
+struct CallableTraitsHelper<ImplT, RetT(ArgTs...) noexcept>
+    : ImplT</* is_const = */ false, /* is_noexcept = */ true, RetT, ArgTs...> {
+};
 
-template <template <bool /* is_const */, typename...> typename ImplT,
+template <template <bool /* is_const */, bool /* is_noexcept */,
+                    typename...> typename ImplT,
           typename RetT, typename... ArgTs>
 struct CallableTraitsHelper<ImplT, RetT(ArgTs...) const>
-    : ImplT</* is_const = */ true, RetT, ArgTs...> {};
+    : ImplT</* is_const = */ true, /* is_noexcept = */ false, RetT, ArgTs...> {
+};
 
-template <template <bool /* is_const */, typename...> typename ImplT,
+template <template <bool /* is_const */, bool /* is_noexcept */,
+                    typename...> typename ImplT,
+          typename RetT, typename... ArgTs>
+struct CallableTraitsHelper<ImplT, RetT(ArgTs...) const noexcept>
+    : ImplT</* is_const = */ true, /* is_noexcept = */ true, RetT, ArgTs...> {};
+
+template <template <bool /* is_const */, bool /* is_noexcept */,
+                    typename...> typename ImplT,
           typename RetT, typename... ArgTs>
 struct CallableTraitsHelper<ImplT, RetT (*)(ArgTs...)>
-    : ImplT</* is_const = */ false, RetT, ArgTs...> {};
+    : ImplT</* is_const = */ false, /* is_noexcept = */ false, RetT, ArgTs...> {
+};
+
+template <template <bool /* is_const */, bool /* is_noexcept */,
+                    typename...> typename ImplT,
+          typename RetT, typename... ArgTs>
+struct CallableTraitsHelper<ImplT, RetT (*)(ArgTs...) noexcept>
+    : ImplT</* is_const = */ false, /* is_noexcept = */ true, RetT, ArgTs...> {
+};
 
-template <template <bool /* is_const */, typename...> typename ImplT,
+template <template <bool /* is_const */, bool /* is_noexcept */,
+                    typename...> typename ImplT,
           typename RetT, typename... ArgTs>
 struct CallableTraitsHelper<ImplT, RetT (&)(ArgTs...)>
-    : ImplT</* is_const = */ false, RetT, ArgTs...> {};
+    : ImplT</* is_const = */ false, /* is_noexcept = */ false, RetT, ArgTs...> {
+};
+
+template <template <bool /* is_const */, bool /* is_noexcept */,
+                    typename...> typename ImplT,
+          typename RetT, typename... ArgTs>
+struct CallableTraitsHelper<ImplT, RetT (&)(ArgTs...) noexcept>
+    : ImplT</* is_const = */ false, /* is_noexcept = */ true, RetT, ArgTs...> {
+};
 
-template <template <bool /* is_const */, typename...> typename ImplT,
+template <template <bool /* is_const */, bool /* is_noexcept */,
+                    typename...> typename ImplT,
           typename ClassT, typename RetT, typename... ArgTs>
 struct CallableTraitsHelper<ImplT, RetT (ClassT::*)(ArgTs...)>
-    : ImplT</* is_const = */ false, RetT, ArgTs...> {};
+    : ImplT</* is_const = */ false, /* is_noexcept = */ false, RetT, ArgTs...> {
+};
 
-template <template <bool /* is_const */, typename...> typename ImplT,
+template <template <bool /* is_const */, bool /* is_noexcept */,
+                    typename...> typename ImplT,
+          typename ClassT, typename RetT, typename... ArgTs>
+struct CallableTraitsHelper<ImplT, RetT (ClassT::*)(ArgTs...) noexcept>
+    : ImplT</* is_const = */ false, /* is_noexcept = */ true, RetT, ArgTs...> {
+};
+
+template <template <bool /* is_const */, bool /* is_noexcept */,
+                    typename...> typename ImplT,
           typename ClassT, typename RetT, typename... ArgTs>
 struct CallableTraitsHelper<ImplT, RetT (ClassT::*)(ArgTs...) const>
-    : ImplT</* is_const = */ true, RetT, ArgTs...> {};
+    : ImplT</* is_const = */ true, /* is_noexcept = */ false, RetT, ArgTs...> {
+};
+
+template <template <bool /* is_const */, bool /* is_noexcept */,
+                    typename...> typename ImplT,
+          typename ClassT, typename RetT, typename... ArgTs>
+struct CallableTraitsHelper<ImplT, RetT (ClassT::*)(ArgTs...) const noexcept>
+    : ImplT</* is_const = */ true, /* is_noexcept = */ true, RetT, ArgTs...> {};
 
 namespace detail {
-template <bool IsConst, typename RetT, typename... ArgTs>
+template <bool IsConst, bool IsNoexcept, typename RetT, typename... ArgTs>
 struct CallableArgInfoImpl {
   static constexpr bool is_const = IsConst;
+  static constexpr bool is_noexcept = IsNoexcept;
   typedef RetT return_type;
   typedef std::tuple<ArgTs...> args_tuple_type;
 };
diff --git a/orc-rt/include/orc-rt/Error.h b/orc-rt/include/orc-rt/Error.h
index 2da7801ab8fcf..bf460d0db7d8f 100644
--- a/orc-rt/include/orc-rt/Error.h
+++ b/orc-rt/include/orc-rt/Error.h
@@ -283,7 +283,8 @@ struct ErrorHandlerTraitsImpl<void, std::unique_ptr<ErrT>> {
   }
 };
 
-template <bool /* const */, typename RetT, typename ArgT>
+template <bool /* is_const */, bool /* is_noexcept */, typename RetT,
+          typename ArgT>
 struct ErrorHandlerTraitsImplAdapter : ErrorHandlerTraitsImpl<RetT, ArgT> {};
 
 } // namespace detail.
@@ -652,7 +653,7 @@ template <> struct ErrorWrapImpl<void> {
   }
 };
 
-template <bool /* const */, typename RetT>
+template <bool /* is_const */, bool /* is_noexcept */, typename RetT>
 struct ErrorWrapImplAdapter : ErrorWrapImpl<RetT> {};
 
 template <typename Callable>
diff --git a/orc-rt/include/orc-rt/WrapperFunction.h b/orc-rt/include/orc-rt/WrapperFunction.h
index 6a89dfdade66a..2474db601e278 100644
--- a/orc-rt/include/orc-rt/WrapperFunction.h
+++ b/orc-rt/include/orc-rt/WrapperFunction.h
@@ -135,7 +135,7 @@ struct WFHandlerTraitsImpl {
   }
 };
 
-template <bool /* is_const */, typename... Ts>
+template <bool /* is_const */, bool /* is_noexcept */, typename... Ts>
 struct WFHandlerTraitsImplAdapter : WFHandlerTraitsImpl<Ts...> {};
 
 template <typename C>
diff --git a/orc-rt/unittests/CallableTraitsHelperTest.cpp b/orc-rt/unittests/CallableTraitsHelperTest.cpp
index 658a15fc16fa1..9b33dd9e8f6c3 100644
--- a/orc-rt/unittests/CallableTraitsHelperTest.cpp
+++ b/orc-rt/unittests/CallableTraitsHelperTest.cpp
@@ -130,3 +130,76 @@ TEST(CallableTraitsHelperTest, FunctionTypeIsNonConst) {
 TEST(CallableTraitsHelperTest, AbominableFunctionTypeIsConst) {
   static_assert(CallableArgInfo<int(int, float) const>::is_const);
 }
+
+// noexcept coverage — mirrors the const-qualifier tests above.
+
+static void freeVoidVoidNoexcept() noexcept {}
+
+TEST(CallableTraitsHelperTest, FreeFunctionNoexcept) {
+  static_assert(!CallableArgInfo<decltype(freeVoidVoid)>::is_noexcept);
+  static_assert(CallableArgInfo<decltype(freeVoidVoidNoexcept)>::is_noexcept);
+}
+
+TEST(CallableTraitsHelperTest, FunctionPointerNoexcept) {
+  static_assert(!CallableArgInfo<int (*)(int, float)>::is_noexcept);
+  static_assert(CallableArgInfo<int (*)(int, float) noexcept>::is_noexcept);
+}
+
+TEST(CallableTraitsHelperTest, FunctionReferenceNoexcept) {
+  static_assert(!CallableArgInfo<int (&)(int, float)>::is_noexcept);
+  static_assert(CallableArgInfo<int (&)(int, float) noexcept>::is_noexcept);
+}
+
+TEST(CallableTraitsHelperTest, NonNoexceptLambdaIsNotNoexcept) {
+  auto L = []() {};
+  static_assert(!CallableArgInfo<decltype(L)>::is_noexcept);
+}
+
+TEST(CallableTraitsHelperTest, NoexceptLambdaIsNoexcept) {
+  auto L = []() noexcept {};
+  static_assert(CallableArgInfo<decltype(L)>::is_noexcept);
+}
+
+TEST(CallableTraitsHelperTest, NoexceptFunctor) {
+  struct F {
+    void operator()() const noexcept {}
+  };
+  static_assert(CallableArgInfo<F>::is_noexcept);
+  static_assert(CallableArgInfo<F>::is_const);
+}
+
+TEST(CallableTraitsHelperTest, NonNoexceptFunctor) {
+  struct F {
+    void operator()() const {}
+  };
+  static_assert(!CallableArgInfo<F>::is_noexcept);
+}
+
+TEST(CallableTraitsHelperTest, NoexceptMemberFnPtr) {
+  struct C {
+    void m(int) noexcept {}
+    void mConstNoexcept(int) const noexcept {}
+    void mConst(int) const {}
+    void mPlain(int) {}
+  };
+  static_assert(CallableArgInfo<decltype(&C::m)>::is_noexcept);
+  static_assert(!CallableArgInfo<decltype(&C::m)>::is_const);
+  static_assert(CallableArgInfo<decltype(&C::mConstNoexcept)>::is_noexcept);
+  static_assert(CallableArgInfo<decltype(&C::mConstNoexcept)>::is_const);
+  static_assert(!CallableArgInfo<decltype(&C::mConst)>::is_noexcept);
+  static_assert(CallableArgInfo<decltype(&C::mConst)>::is_const);
+  static_assert(!CallableArgInfo<decltype(&C::mPlain)>::is_noexcept);
+  static_assert(!CallableArgInfo<decltype(&C::mPlain)>::is_const);
+}
+
+TEST(CallableTraitsHelperTest, FunctionTypeNoexcept) {
+  static_assert(!CallableArgInfo<int(int, float)>::is_noexcept);
+  static_assert(CallableArgInfo<int(int, float) noexcept>::is_noexcept);
+}
+
+// Abominable function type with both const and noexcept cv-qualifiers.
+TEST(CallableTraitsHelperTest, AbominableFunctionTypeConstNoexcept) {
+  using F = int(int, float) const noexcept;
+  static_assert(CallableArgInfo<F>::is_const);
+  static_assert(CallableArgInfo<F>::is_noexcept);
+}



More information about the llvm-commits mailing list