[libcxx-commits] [libcxx] [libc++] Implement the `<type_traits>` parts of P1317R2 (PR #151480)
via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Jul 31 02:30:03 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: A. Jiang (frederick-vs-ja)
<details>
<summary>Changes</summary>
Changes of `std::apply` are still blocked on related changes in P2165R4.
---
Patch is 136.46 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/151480.diff
12 Files Affected:
- (modified) libcxx/docs/ReleaseNotes/22.rst (+2)
- (modified) libcxx/docs/Status/Cxx2cPapers.csv (+1-1)
- (modified) libcxx/include/CMakeLists.txt (+1)
- (added) libcxx/include/__type_traits/is_applicable.h (+105)
- (modified) libcxx/include/module.modulemap.in (+4)
- (modified) libcxx/include/type_traits (+16)
- (modified) libcxx/modules/std/type_traits.inc (+18)
- (modified) libcxx/test/libcxx/type_traits/no_specializations.verify.cpp (+8-1)
- (modified) libcxx/test/std/utilities/meta/derived_from_integral_constant.compile.pass.cpp (+2)
- (added) libcxx/test/std/utilities/meta/meta.rel/is_applicable.compile.pass.cpp (+598)
- (added) libcxx/test/std/utilities/meta/meta.rel/is_nothrow_applicable.compile.pass.cpp (+654)
- (added) libcxx/test/std/utilities/meta/meta.trans/meta.trans.other/apply_result.compile.pass.cpp (+629)
``````````diff
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 15bf46d44b07f..05c375359a688 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -38,6 +38,8 @@ What's New in Libc++ 22.0.0?
Implemented Papers
------------------
+- P1317R2: Remove return type deduction in ``std::apply`` (`Github <https://github.com/llvm/llvm-project/issues/148183>`__)
+ (Only components in ``<type_traits>`` are implemented.)
- P2321R2: ``zip`` (`Github <https://github.com/llvm/llvm-project/issues/105169>`__) (The paper is partially implemented. ``zip_transform_view`` is implemented in this release)
Improvements and New Features
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index febb0c176f9c4..99be92021434e 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -155,5 +155,5 @@
"`P2781R9 <https://wg21.link/P2781R9>`__","``std::constant_wrapper``","2025-06 (Sofia)","","",""
"`P3697R1 <https://wg21.link/P3697R1>`__","Minor additions to C++26 standard library hardening","2025-06 (Sofia)","","",""
"`P3552R3 <https://wg21.link/P3552R3>`__","Add a Coroutine Task Type","2025-06 (Sofia)","","",""
-"`P1317R2 <https://wg21.link/P1317R2>`__","Remove return type deduction in ``std::apply``","2025-06 (Sofia)","","",""
+"`P1317R2 <https://wg21.link/P1317R2>`__","Remove return type deduction in ``std::apply``","2025-06 (Sofia)","|In Progress|","",""
"","","","","",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 51444ec668e2b..a823a2f3a7a50 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -819,6 +819,7 @@ set(files
__type_traits/is_aggregate.h
__type_traits/is_allocator.h
__type_traits/is_always_bitcastable.h
+ __type_traits/is_applicable.h
__type_traits/is_arithmetic.h
__type_traits/is_array.h
__type_traits/is_assignable.h
diff --git a/libcxx/include/__type_traits/is_applicable.h b/libcxx/include/__type_traits/is_applicable.h
new file mode 100644
index 0000000000000..fc97a0c4a9ad1
--- /dev/null
+++ b/libcxx/include/__type_traits/is_applicable.h
@@ -0,0 +1,105 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___TYPE_TRAITS_IS_APPLICABLE_H
+#define _LIBCPP___TYPE_TRAITS_IS_APPLICABLE_H
+
+#include <__config>
+#include <__cstddef/size_t.h>
+#include <__functional/invoke.h>
+#include <__fwd/get.h>
+#include <__tuple/tuple_like.h>
+#include <__tuple/tuple_size.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/declval.h>
+#include <__utility/integer_sequence.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class _Fn, class _Tuple>
+struct __apply_result_disabled_base {};
+
+template <class _Fn, class _Tuple, class _Tp>
+struct __apply_result_enabled_base {
+ using type _LIBCPP_NODEBUG = _Tp;
+};
+
+template <bool _Applicable, bool _Nothrow, class _Tp>
+struct __applicability_traits {
+ static constexpr bool __applicable = true;
+ static constexpr bool __nothrow_applicable = _Nothrow;
+
+ template <class _Fn, class _Tuple>
+ using __base_type _LIBCPP_NODEBUG = __apply_result_enabled_base<_Fn, _Tuple, _Tp>;
+};
+
+template <bool _Nothrow, class _Tp>
+struct __applicability_traits<false, _Nothrow, _Tp> {
+ static_assert(!_Nothrow, "misspecified [_Applicable = false, _Nothrow = true]");
+ static constexpr bool __applicable = false;
+ static constexpr bool __nothrow_applicable = false;
+
+ template <class _Fn, class _Tuple>
+ using __base_type _LIBCPP_NODEBUG = __apply_result_disabled_base<_Fn, _Tuple>;
+};
+
+template <class _Fn, class _Tuple>
+consteval auto __applicability_traits_of() {
+ if constexpr (__tuple_like<_Tuple>)
+ return []<size_t... _Is>(index_sequence<_Is...>) {
+ constexpr bool __is_tuple_applicable = requires(_Fn&& __fn, _Tuple&& __tuple) {
+ std::invoke(static_cast<_Fn&&>(__fn), std::get<_Is>(static_cast<_Tuple&&>(__tuple))...);
+ };
+ if constexpr (__is_tuple_applicable)
+ return __applicability_traits<
+ true,
+ noexcept(std::invoke(std::declval<_Fn>(), std::get<_Is>(std::declval<_Tuple>())...)),
+ decltype(std::invoke(std::declval<_Fn>(), std::get<_Is>(std::declval<_Tuple>())...))>{};
+ else
+ return __applicability_traits<false, false, void>{};
+ }(make_index_sequence<tuple_size_v<remove_reference_t<_Tuple>>>{});
+ else
+ return __applicability_traits<false, false, void>{};
+}
+
+template <class _Fn, class _Tuple>
+struct _LIBCPP_NO_SPECIALIZATIONS is_applicable
+ : bool_constant<decltype(std::__applicability_traits_of<_Fn, _Tuple>())::__applicable> {};
+
+template <class _Fn, class _Tuple>
+struct _LIBCPP_NO_SPECIALIZATIONS is_nothrow_applicable
+ : bool_constant<decltype(std::__applicability_traits_of<_Fn, _Tuple>())::__nothrow_applicable> {};
+
+template <class _Fn, class _Tuple>
+_LIBCPP_NO_SPECIALIZATIONS inline constexpr bool is_applicable_v =
+ decltype(std::__applicability_traits_of<_Fn, _Tuple>())::__applicable;
+
+template <class _Fn, class _Tuple>
+_LIBCPP_NO_SPECIALIZATIONS inline constexpr bool is_nothrow_applicable_v =
+ decltype(std::__applicability_traits_of<_Fn, _Tuple>())::__nothrow_applicable;
+
+template <class _Fn, class _Tuple>
+struct _LIBCPP_NO_SPECIALIZATIONS apply_result
+ : decltype(std::__applicability_traits_of<_Fn, _Tuple>())::template __base_type<_Fn, _Tuple> {};
+
+template <class _Fn, class _Tuple>
+using apply_result_t = apply_result<_Fn, _Tuple>::type;
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___TYPE_TRAITS_IS_APPLICABLE_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 5857a83b5fe14..6766960144a83 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -112,6 +112,10 @@ module std_core [system] {
header "__type_traits/is_always_bitcastable.h"
export std_core.type_traits.integral_constant
}
+ module is_applicable {
+ header "__type_traits/is_applicable.h"
+ export std_core.type_traits.integral_constant
+ }
module is_arithmetic {
header "__type_traits/is_arithmetic.h"
export std_core.type_traits.integral_constant
diff --git a/libcxx/include/type_traits b/libcxx/include/type_traits
index a6e0c1867566b..f8c3f744e07f4 100644
--- a/libcxx/include/type_traits
+++ b/libcxx/include/type_traits
@@ -168,6 +168,10 @@ namespace std
template <class R, class Fn, class... ArgTypes>
struct is_nothrow_invocable_r; // since C++17
+ template<class Fn, class Tuple> struct is_applicable; // since C++26
+ template<class Fn, class Tuple>
+ struct is_nothrow_applicable; // since C++26
+
// Alignment properties and transformations:
template <class T> struct alignment_of;
template <size_t Len, size_t Align = most_stringent_alignment_requirement>
@@ -183,6 +187,7 @@ namespace std
struct result_of<Fn(ArgTypes...)>; // deprecated in C++17; removed in C++20
template <class Fn, class... ArgTypes>
struct invoke_result; // since C++17
+ template<class Fn, class Tuple> struct apply_result; // since C++26
// const-volatile modifications:
template <class T>
@@ -265,6 +270,9 @@ namespace std
template <class Fn, class... ArgTypes>
using invoke_result_t
= typename invoke_result<Fn, ArgTypes...>::type; // since C++17
+ template <class Fn, class... ArgTypes>
+ using apply_result_t
+ = typename invoke_result<Fn, ArgTypes...>::type; // since C++26
template <class...>
using void_t = void; // since C++17
@@ -442,6 +450,10 @@ namespace std
= is_nothrow_invocable<Fn, ArgTypes...>::value; // since C++17
template <class R, class Fn, class... ArgTypes> inline constexpr bool is_nothrow_invocable_r_v
= is_nothrow_invocable_r<R, Fn, ArgTypes...>::value; // since C++17
+ template<class Fn, class Tuple> constexpr bool is_applicable_v
+ = is_applicable<Fn, Tuple>::value; // since C++26
+ template<class Fn, class Tuple> constexpr bool is_nothrow_applicable_v
+ = is_nothrow_applicable<Fn, Tuple>::value; // since C++26
// [meta.logical], logical operator traits:
template<class... B> struct conjunction; // since C++17
@@ -559,6 +571,10 @@ namespace std
# include <__type_traits/reference_converts_from_temporary.h>
# endif
+# if _LIBCPP_STD_VER >= 26
+# include <__type_traits/is_applicable.h>
+# endif
+
# include <version>
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/modules/std/type_traits.inc b/libcxx/modules/std/type_traits.inc
index 6823c86ed153b..0ad2bb4bea27f 100644
--- a/libcxx/modules/std/type_traits.inc
+++ b/libcxx/modules/std/type_traits.inc
@@ -121,6 +121,9 @@ export namespace std {
using std::rank;
// [meta.rel], type relations
+#if _LIBCPP_STD_VER >= 26
+ using std::is_applicable;
+#endif
using std::is_base_of;
#if _LIBCPP_STD_VER >= 26 && __has_builtin(__builtin_is_virtual_base_of)
using std::is_virtual_base_of;
@@ -134,6 +137,9 @@ export namespace std {
using std::is_invocable;
using std::is_invocable_r;
+#if _LIBCPP_STD_VER >= 26
+ using std::is_nothrow_applicable;
+#endif
using std::is_nothrow_invocable;
using std::is_nothrow_invocable_r;
@@ -183,6 +189,9 @@ export namespace std {
using std::remove_pointer_t;
// [meta.trans.other], other transformations
+#if _LIBCPP_STD_VER >= 26
+ using std::apply_result;
+#endif
using std::basic_common_reference;
using std::common_reference;
using std::common_type;
@@ -196,6 +205,9 @@ export namespace std {
using std::unwrap_ref_decay;
using std::unwrap_reference;
+#if _LIBCPP_STD_VER >= 26
+ using std::apply_result_t;
+#endif
using std::common_reference_t;
using std::common_type_t;
using std::conditional_t;
@@ -305,6 +317,9 @@ export namespace std {
using std::rank_v;
// [meta.rel], type relations
+#if _LIBCPP_STD_VER >= 26
+ using std::is_applicable_v;
+#endif
using std::is_base_of_v;
#if _LIBCPP_STD_VER >= 26 && __has_builtin(__builtin_is_virtual_base_of)
using std::is_virtual_base_of_v;
@@ -313,6 +328,9 @@ export namespace std {
using std::is_invocable_r_v;
using std::is_invocable_v;
// using std::is_layout_compatible_v;
+#if _LIBCPP_STD_VER >= 26
+ using std::is_nothrow_applicable_v;
+#endif
using std::is_nothrow_convertible_v;
using std::is_nothrow_invocable_r_v;
using std::is_nothrow_invocable_v;
diff --git a/libcxx/test/libcxx/type_traits/no_specializations.verify.cpp b/libcxx/test/libcxx/type_traits/no_specializations.verify.cpp
index 897ae89365014..b76a75a3b1584 100644
--- a/libcxx/test/libcxx/type_traits/no_specializations.verify.cpp
+++ b/libcxx/test/libcxx/type_traits/no_specializations.verify.cpp
@@ -53,6 +53,11 @@ SPECIALIZE_TRAIT(unwrap_reference); // expected-error {{cannot be specialized}}
SPECIALIZE_TRAIT(unwrap_ref_decay); // expected-error {{cannot be specialized}}
# endif
+# if TEST_STD_VER >= 26
+template <>
+struct std::apply_result<S, S>; // expected-error {{cannot be specialized}}
+# endif
+
# undef SPECIALIZE_TRAIT
# define SPECIALIZE_UTT(Trait) \
template <> \
@@ -165,7 +170,9 @@ SPECIALIZE_BTT(reference_converts_from_temporary); // expected-error 2 {{cannot
# endif
# if TEST_STD_VER >= 26
-SPECIALIZE_BTT(is_virtual_base_of); // expected-error 2 {{cannot be specialized}}
+SPECIALIZE_BTT(is_applicable); // expected-error 2 {{cannot be specialized}}
+SPECIALIZE_BTT(is_nothrow_applicable); // expected-error 2 {{cannot be specialized}}
+SPECIALIZE_BTT(is_virtual_base_of); // expected-error 2 {{cannot be specialized}}
# endif
# undef SPECIALIZE_UTT
diff --git a/libcxx/test/std/utilities/meta/derived_from_integral_constant.compile.pass.cpp b/libcxx/test/std/utilities/meta/derived_from_integral_constant.compile.pass.cpp
index 3db7a214b27bd..eb021f5840c6c 100644
--- a/libcxx/test/std/utilities/meta/derived_from_integral_constant.compile.pass.cpp
+++ b/libcxx/test/std/utilities/meta/derived_from_integral_constant.compile.pass.cpp
@@ -113,4 +113,6 @@ static_assert(std::is_base_of<std::false_type, std::is_scoped_enum<int>>::value,
# if defined(__cpp_lib_is_virtual_base_of) && __cpp_lib_is_virtual_base_of >= 202406L
static_assert(std::is_base_of<std::false_type, std::is_virtual_base_of<int, int>>::value, "");
# endif
+static_assert(std::is_base_of<std::false_type, std::is_applicable<int, int>>::value, "");
+static_assert(std::is_base_of<std::false_type, std::is_nothrow_applicable<int, int>>::value, "");
#endif
diff --git a/libcxx/test/std/utilities/meta/meta.rel/is_applicable.compile.pass.cpp b/libcxx/test/std/utilities/meta/meta.rel/is_applicable.compile.pass.cpp
new file mode 100644
index 0000000000000..bd8531c02a2e0
--- /dev/null
+++ b/libcxx/test/std/utilities/meta/meta.rel/is_applicable.compile.pass.cpp
@@ -0,0 +1,598 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <type_traits>
+
+// template<class Fn, class Tuple> struct is_applicable;
+
+// template<class Fn, class Tuple>
+// constexpr bool is_applicable_v = is_applicable<T, U>::value;
+
+#include <cassert>
+#include <cstddef>
+#include <array>
+#include <complex>
+#include <ranges>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "callable_types.h"
+#include "test_iterators.h"
+
+struct empty_aggregate {};
+
+struct derived_from_tuple_int : std::tuple<int> {};
+
+template <>
+struct std::tuple_size<derived_from_tuple_int> : std::integral_constant<std::size_t, 1> {};
+
+template <std::size_t I>
+ requires(I < 1)
+struct std::tuple_element<I, derived_from_tuple_int> {
+ using type = std::tuple_element_t<I, std::tuple<int>>;
+};
+
+template <class Fn, class Tuple, bool Expected>
+void test_is_applicable() {
+ static_assert(std::is_applicable<Fn, Tuple>::value == Expected);
+ static_assert(std::is_applicable_v<Fn, Tuple> == Expected);
+
+ static_assert(std::is_base_of_v<std::bool_constant<Expected>, std::is_applicable<Fn, Tuple>>);
+ static_assert(std::is_convertible_v<std::is_applicable<Fn, Tuple>*, std::bool_constant<Expected>*>);
+}
+
+template <class Func, class Tuple, bool Expected>
+void test_is_applicable_from_function() {
+ static_assert(std::is_function_v<Func>);
+
+ test_is_applicable<Func, Tuple, Expected>();
+ test_is_applicable<Func&, Tuple, Expected>();
+
+ test_is_applicable<Func*, Tuple, Expected>();
+ test_is_applicable<Func*&, Tuple, Expected>();
+ test_is_applicable<Func* const, Tuple, Expected>();
+ test_is_applicable<Func* const&, Tuple, Expected>();
+ test_is_applicable<Func* volatile, Tuple, Expected>();
+ test_is_applicable<Func* volatile&, Tuple, Expected>();
+ test_is_applicable<Func* const volatile, Tuple, Expected>();
+ test_is_applicable<Func* const volatile&, Tuple, Expected>();
+}
+
+void test_valid() {
+ // test array
+ test_is_applicable_from_function<int(), std::array<int, 0>, true>();
+ test_is_applicable_from_function<int(), std::array<long, 0>&, true>();
+ test_is_applicable_from_function<int(), const std::array<char, 0>, true>();
+ test_is_applicable_from_function<int(), const std::array<std::array<int, 1>, 0>&, true>();
+ test_is_applicable_from_function<int() noexcept, std::array<int, 0>, true>();
+ test_is_applicable_from_function<int() noexcept, std::array<long, 0>&, true>();
+ test_is_applicable_from_function<int() noexcept, const std::array<char, 0>, true>();
+ test_is_applicable_from_function<int() noexcept, const std::array<std::array<int, 1>, 0>&, true>();
+
+ test_is_applicable_from_function<int(long), std::array<int, 1>, true>();
+ test_is_applicable_from_function<int&(int), std::array<long, 1>&, true>();
+ test_is_applicable_from_function<const int && (float), const std::array<double, 1>, true>();
+ test_is_applicable_from_function<void(double), const std::array<char, 1>&, true>();
+ test_is_applicable_from_function<int(long) noexcept, std::array<int, 1>, true>();
+ test_is_applicable_from_function<int&(int) noexcept, std::array<long, 1>&, true>();
+ test_is_applicable_from_function<const int && (float) noexcept, const std::array<double, 1>, true>();
+ test_is_applicable_from_function<void(double) noexcept, const std::array<char, 1>&, true>();
+
+ test_is_applicable_from_function<int(long, int), std::array<int, 2>, true>();
+ test_is_applicable_from_function<int&(long, int), std::array<int, 2>&, true>();
+ test_is_applicable_from_function<const int && (long, int), const std::array<int, 2>, true>();
+ test_is_applicable_from_function<void(long, int), const std::array<int, 2>&, true>();
+ test_is_applicable_from_function<int(long, int) noexcept, std::array<int, 2>, true>();
+ test_is_applicable_from_function<int&(long, int) noexcept, std::array<int, 2>&, true>();
+ test_is_applicable_from_function<const int && (long, int) noexcept, const std::array<int, 2>, true>();
+ test_is_applicable_from_function<void(long, int) noexcept, const std::array<int, 2>&, true>();
+
+ test_is_applicable<ConstCallable<bool>, std::array<int, 0>, true>();
+ test_is_applicable<ConstCallable<bool>, std::array<int, 1>&, true>();
+ test_is_applicable<ConstCallable<bool>, const std::array<int, 2>, true>();
+ test_is_applicable<ConstCallable<bool>, const std::array<int, 3>&, true>();
+
+ test_is_applicable<NoExceptCallable<bool>, std::array<int, 0>, true>();
+ test_is_applicable<NoExceptCallable<bool>, std::array<int, 1>&, true>();
+ test_is_applicable<NoExceptCallable<bool>, const std::array<int, 2>, true>();
+ test_is_applicable<NoExceptCallable<bool>, const std::array<int, 3>&, true>();
+
+ // test complex
+ test_is_applicable_from_function<float(float, float), std::complex<float>, true>();
+ test_is_applicable_from_function<float(float&, float&), std::complex<float>&, true>();
+ test_is_applicable_from_function<void(float, float), const std::complex<float>, true>();
+ test_is_applicable_from_function<double(float, float), const std::complex<float>&, true>();
+ test_is_applicable_from_function<float(float, float) noexcept, std::complex<float>, true>();
+ test_is_applicable_from_function<float(float&, float&) noexcept, std::complex<float>&, true>();
+ test_is_applicable_from_function<void(float, float) noexcept, const std::complex<float>, true>();
+ test_is_applicable_from_function<double(float, float) noexcept, const std::complex<float>&, true>();
+
+ test_is_applicable<ConstCallable<bool>, std::complex<float>, true>();
+ test_is_applicable<ConstCallable<bool>, std::complex<float>&, true>();
+ test_is_applicable<ConstCallable<bool>, const std::complex<float>, true>();
+ test_is_applicable<ConstCallable<bool>, const std::complex<float>&, true>();
+
+ test_is_applicable<NoExceptCallable<bool>, std::complex<float>, true>();
+ test_is_applicable<NoExceptCallable<bool>, std::complex<float>&, true>();
+ test_is_applicable<NoExceptCallable<bool>, const std::complex<float>, true>();
+ test_is_applicable<NoExceptCallable<bool>, const std::complex<float>&, true...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/151480
More information about the libcxx-commits
mailing list