[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