[libcxx-commits] [libcxx] [libc++] Make __is_std_ templates variable templates (PR #150590)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jul 25 01:34:27 PDT 2025


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/150590

None

>From 8a02a0bfa5660a3916c5d8d22bd84b915cffadd7 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 25 Jul 2025 10:33:58 +0200
Subject: [PATCH] [libc++] Make __is_std_ templates variable templates

---
 libcxx/include/__expected/expected.h   | 50 +++++++++++++-------------
 libcxx/include/__expected/unexpected.h | 17 +++------
 libcxx/include/optional                | 19 +++++-----
 libcxx/include/span                    |  6 ++--
 4 files changed, 41 insertions(+), 51 deletions(-)

diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 0f446b870723b..54f9c732e4b33 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -32,6 +32,7 @@
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_replaceable.h>
 #include <__type_traits/is_same.h>
+#include <__type_traits/is_specialization.h>
 #include <__type_traits/is_swappable.h>
 #include <__type_traits/is_trivially_constructible.h>
 #include <__type_traits/is_trivially_destructible.h>
@@ -65,10 +66,7 @@ template <class _Tp, class _Err>
 class expected;
 
 template <class _Tp>
-struct __is_std_expected : false_type {};
-
-template <class _Tp, class _Err>
-struct __is_std_expected<expected<_Tp, _Err>> : true_type {};
+concept __is_std_expected_v = __is_specialization_v<_Tp, expected>;
 
 struct __expected_construct_in_place_from_invoke_tag {};
 struct __expected_construct_unexpected_from_invoke_tag {};
@@ -450,8 +448,8 @@ class __expected_base {
 template <class _Tp, class _Err>
 class expected : private __expected_base<_Tp, _Err> {
   static_assert(!is_reference_v<_Tp> && !is_function_v<_Tp> && !is_same_v<remove_cv_t<_Tp>, in_place_t> &&
-                    !is_same_v<remove_cv_t<_Tp>, unexpect_t> && !__is_std_unexpected<remove_cv_t<_Tp>>::value &&
-                    __valid_std_unexpected<_Err>::value,
+                    !is_same_v<remove_cv_t<_Tp>, unexpect_t> && !__is_std_unexpected_v<remove_cv_t<_Tp>> &&
+                    __is_std_unexpected_v<_Err>,
                 "[expected.object.general] A program that instantiates the definition of template expected<T, E> for a "
                 "reference type, a function type, or for possibly cv-qualified types in_place_t, unexpect_t, or a "
                 "specialization of unexpected for the T parameter is ill-formed. A program that instantiates the "
@@ -557,8 +555,8 @@ class expected : private __expected_base<_Tp, _Err> {
 
   template <class _Up = _Tp>
     requires(!is_same_v<remove_cvref_t<_Up>, in_place_t> && !is_same_v<expected, remove_cvref_t<_Up>> &&
-             is_constructible_v<_Tp, _Up> && !__is_std_unexpected<remove_cvref_t<_Up>>::value &&
-             (!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_expected<remove_cvref_t<_Up>>::value))
+             is_constructible_v<_Tp, _Up> && !__is_std_unexpected_v<remove_cvref_t<_Up>> &&
+             (!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_expected_v<remove_cvref_t<_Up>>))
   _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp>)
       expected(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
       : __base(in_place, std::forward<_Up>(__u)) {}
@@ -670,7 +668,7 @@ class expected : private __expected_base<_Tp, _Err> {
 
   template <class _Up = _Tp>
   _LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(_Up&& __v)
-    requires(!is_same_v<expected, remove_cvref_t<_Up>> && !__is_std_unexpected<remove_cvref_t<_Up>>::value &&
+    requires(!is_same_v<expected, remove_cvref_t<_Up>> && !__is_std_unexpected_v<remove_cvref_t<_Up>> &&
              is_constructible_v<_Tp, _Up> && is_assignable_v<_Tp&, _Up> &&
              (is_nothrow_constructible_v<_Tp, _Up> || is_nothrow_move_constructible_v<_Tp> ||
               is_nothrow_move_constructible_v<_Err>))
@@ -923,7 +921,7 @@ class expected : private __expected_base<_Tp, _Err> {
     requires is_constructible_v<_Err, _Err&>
   _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
     using _Up = remove_cvref_t<invoke_result_t<_Func, _Tp&>>;
-    static_assert(__is_std_expected<_Up>::value, "The result of f(value()) must be a specialization of std::expected");
+    static_assert(__is_std_expected_v<_Up>, "The result of f(value()) must be a specialization of std::expected");
     static_assert(is_same_v<typename _Up::error_type, _Err>,
                   "The result of f(value()) must have the same error_type as this expected");
     if (has_value()) {
@@ -936,7 +934,7 @@ class expected : private __expected_base<_Tp, _Err> {
     requires is_constructible_v<_Err, const _Err&>
   _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& {
     using _Up = remove_cvref_t<invoke_result_t<_Func, const _Tp&>>;
-    static_assert(__is_std_expected<_Up>::value, "The result of f(value()) must be a specialization of std::expected");
+    static_assert(__is_std_expected_v<_Up>, "The result of f(value()) must be a specialization of std::expected");
     static_assert(is_same_v<typename _Up::error_type, _Err>,
                   "The result of f(value()) must have the same error_type as this expected");
     if (has_value()) {
@@ -950,7 +948,7 @@ class expected : private __expected_base<_Tp, _Err> {
   _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && {
     using _Up = remove_cvref_t<invoke_result_t<_Func, _Tp&&>>;
     static_assert(
-        __is_std_expected<_Up>::value, "The result of f(std::move(value())) must be a specialization of std::expected");
+        __is_std_expected_v<_Up>, "The result of f(std::move(value())) must be a specialization of std::expected");
     static_assert(is_same_v<typename _Up::error_type, _Err>,
                   "The result of f(std::move(value())) must have the same error_type as this expected");
     if (has_value()) {
@@ -964,7 +962,7 @@ class expected : private __expected_base<_Tp, _Err> {
   _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
     using _Up = remove_cvref_t<invoke_result_t<_Func, const _Tp&&>>;
     static_assert(
-        __is_std_expected<_Up>::value, "The result of f(std::move(value())) must be a specialization of std::expected");
+        __is_std_expected_v<_Up>, "The result of f(std::move(value())) must be a specialization of std::expected");
     static_assert(is_same_v<typename _Up::error_type, _Err>,
                   "The result of f(std::move(value())) must have the same error_type as this expected");
     if (has_value()) {
@@ -977,7 +975,7 @@ class expected : private __expected_base<_Tp, _Err> {
     requires is_constructible_v<_Tp, _Tp&>
   _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) & {
     using _Gp = remove_cvref_t<invoke_result_t<_Func, _Err&>>;
-    static_assert(__is_std_expected<_Gp>::value, "The result of f(error()) must be a specialization of std::expected");
+    static_assert(__is_std_expected_v<_Gp>, "The result of f(error()) must be a specialization of std::expected");
     static_assert(is_same_v<typename _Gp::value_type, _Tp>,
                   "The result of f(error()) must have the same value_type as this expected");
     if (has_value()) {
@@ -990,7 +988,7 @@ class expected : private __expected_base<_Tp, _Err> {
     requires is_constructible_v<_Tp, const _Tp&>
   _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) const& {
     using _Gp = remove_cvref_t<invoke_result_t<_Func, const _Err&>>;
-    static_assert(__is_std_expected<_Gp>::value, "The result of f(error()) must be a specialization of std::expected");
+    static_assert(__is_std_expected_v<_Gp>, "The result of f(error()) must be a specialization of std::expected");
     static_assert(is_same_v<typename _Gp::value_type, _Tp>,
                   "The result of f(error()) must have the same value_type as this expected");
     if (has_value()) {
@@ -1004,7 +1002,7 @@ class expected : private __expected_base<_Tp, _Err> {
   _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) && {
     using _Gp = remove_cvref_t<invoke_result_t<_Func, _Err&&>>;
     static_assert(
-        __is_std_expected<_Gp>::value, "The result of f(std::move(error())) must be a specialization of std::expected");
+        __is_std_expected_v<_Gp>, "The result of f(std::move(error())) must be a specialization of std::expected");
     static_assert(is_same_v<typename _Gp::value_type, _Tp>,
                   "The result of f(std::move(error())) must have the same value_type as this expected");
     if (has_value()) {
@@ -1018,7 +1016,7 @@ class expected : private __expected_base<_Tp, _Err> {
   _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) const&& {
     using _Gp = remove_cvref_t<invoke_result_t<_Func, const _Err&&>>;
     static_assert(
-        __is_std_expected<_Gp>::value, "The result of f(std::move(error())) must be a specialization of std::expected");
+        __is_std_expected_v<_Gp>, "The result of f(std::move(error())) must be a specialization of std::expected");
     static_assert(is_same_v<typename _Gp::value_type, _Tp>,
                   "The result of f(std::move(error())) must have the same value_type as this expected");
     if (has_value()) {
@@ -1166,7 +1164,7 @@ class expected : private __expected_base<_Tp, _Err> {
   template <class _T2>
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const _T2& __v)
 #  if _LIBCPP_STD_VER >= 26
-    requires(!__is_std_expected<_T2>::value) && requires {
+    requires(!__is_std_expected_v<_T2>) && requires {
       { *__x == __v } -> __core_convertible_to<bool>;
     }
 #  endif
@@ -1667,7 +1665,7 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> {
     requires is_constructible_v<_Err, _Err&>
   _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
     using _Up = remove_cvref_t<invoke_result_t<_Func>>;
-    static_assert(__is_std_expected<_Up>::value, "The result of f() must be a specialization of std::expected");
+    static_assert(__is_std_expected_v<_Up>, "The result of f() must be a specialization of std::expected");
     static_assert(
         is_same_v<typename _Up::error_type, _Err>, "The result of f() must have the same error_type as this expected");
     if (has_value()) {
@@ -1680,7 +1678,7 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> {
     requires is_constructible_v<_Err, const _Err&>
   _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& {
     using _Up = remove_cvref_t<invoke_result_t<_Func>>;
-    static_assert(__is_std_expected<_Up>::value, "The result of f() must be a specialization of std::expected");
+    static_assert(__is_std_expected_v<_Up>, "The result of f() must be a specialization of std::expected");
     static_assert(
         is_same_v<typename _Up::error_type, _Err>, "The result of f() must have the same error_type as this expected");
     if (has_value()) {
@@ -1693,7 +1691,7 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> {
     requires is_constructible_v<_Err, _Err&&>
   _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && {
     using _Up = remove_cvref_t<invoke_result_t<_Func>>;
-    static_assert(__is_std_expected<_Up>::value, "The result of f() must be a specialization of std::expected");
+    static_assert(__is_std_expected_v<_Up>, "The result of f() must be a specialization of std::expected");
     static_assert(
         is_same_v<typename _Up::error_type, _Err>, "The result of f() must have the same error_type as this expected");
     if (has_value()) {
@@ -1706,7 +1704,7 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> {
     requires is_constructible_v<_Err, const _Err&&>
   _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
     using _Up = remove_cvref_t<invoke_result_t<_Func>>;
-    static_assert(__is_std_expected<_Up>::value, "The result of f() must be a specialization of std::expected");
+    static_assert(__is_std_expected_v<_Up>, "The result of f() must be a specialization of std::expected");
     static_assert(
         is_same_v<typename _Up::error_type, _Err>, "The result of f() must have the same error_type as this expected");
     if (has_value()) {
@@ -1718,7 +1716,7 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> {
   template <class _Func>
   _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) & {
     using _Gp = remove_cvref_t<invoke_result_t<_Func, _Err&>>;
-    static_assert(__is_std_expected<_Gp>::value, "The result of f(error()) must be a specialization of std::expected");
+    static_assert(__is_std_expected_v<_Gp>, "The result of f(error()) must be a specialization of std::expected");
     static_assert(is_same_v<typename _Gp::value_type, _Tp>,
                   "The result of f(error()) must have the same value_type as this expected");
     if (has_value()) {
@@ -1730,7 +1728,7 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> {
   template <class _Func>
   _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) const& {
     using _Gp = remove_cvref_t<invoke_result_t<_Func, const _Err&>>;
-    static_assert(__is_std_expected<_Gp>::value, "The result of f(error()) must be a specialization of std::expected");
+    static_assert(__is_std_expected_v<_Gp>, "The result of f(error()) must be a specialization of std::expected");
     static_assert(is_same_v<typename _Gp::value_type, _Tp>,
                   "The result of f(error()) must have the same value_type as this expected");
     if (has_value()) {
@@ -1743,7 +1741,7 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> {
   _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) && {
     using _Gp = remove_cvref_t<invoke_result_t<_Func, _Err&&>>;
     static_assert(
-        __is_std_expected<_Gp>::value, "The result of f(std::move(error())) must be a specialization of std::expected");
+        __is_std_expected_v<_Gp>, "The result of f(std::move(error())) must be a specialization of std::expected");
     static_assert(is_same_v<typename _Gp::value_type, _Tp>,
                   "The result of f(std::move(error())) must have the same value_type as this expected");
     if (has_value()) {
@@ -1756,7 +1754,7 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> {
   _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) const&& {
     using _Gp = remove_cvref_t<invoke_result_t<_Func, const _Err&&>>;
     static_assert(
-        __is_std_expected<_Gp>::value, "The result of f(std::move(error())) must be a specialization of std::expected");
+        __is_std_expected_v<_Gp>, "The result of f(std::move(error())) must be a specialization of std::expected");
     static_assert(is_same_v<typename _Gp::value_type, _Tp>,
                   "The result of f(std::move(error())) must have the same value_type as this expected");
     if (has_value()) {
diff --git a/libcxx/include/__expected/unexpected.h b/libcxx/include/__expected/unexpected.h
index 6904889b8c6b1..d642f062fe6a0 100644
--- a/libcxx/include/__expected/unexpected.h
+++ b/libcxx/include/__expected/unexpected.h
@@ -17,6 +17,7 @@
 #include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_same.h>
+#include <__type_traits/is_specialization.h>
 #include <__type_traits/is_swappable.h>
 #include <__type_traits/is_volatile.h>
 #include <__type_traits/negation.h>
@@ -42,23 +43,15 @@ template <class _Err>
 class unexpected;
 
 template <class _Tp>
-struct __is_std_unexpected : false_type {};
-
-template <class _Err>
-struct __is_std_unexpected<unexpected<_Err>> : true_type {};
+concept __is_std_unexpected_v = __is_specialization_v<_Tp, unexpected>;
 
 template <class _Tp>
-using __valid_std_unexpected _LIBCPP_NODEBUG = _BoolConstant< //
-    is_object_v<_Tp> &&                                       //
-    !is_array_v<_Tp> &&                                       //
-    !__is_std_unexpected<_Tp>::value &&                       //
-    !is_const_v<_Tp> &&                                       //
-    !is_volatile_v<_Tp>                                       //
-    >;
+concept __valid_std_unexpected =
+    is_object_v<_Tp> && !is_array_v<_Tp> && !__is_std_unexpected_v<_Tp> && !is_const_v<_Tp> && !is_volatile_v<_Tp>;
 
 template <class _Err>
 class unexpected {
-  static_assert(__valid_std_unexpected<_Err>::value,
+  static_assert(__valid_std_unexpected<_Err>,
                 "[expected.un.general] states a program that instantiates std::unexpected for a non-object type, an "
                 "array type, a specialization of unexpected, or a cv-qualified type is ill-formed.");
 
diff --git a/libcxx/include/optional b/libcxx/include/optional
index e81bff50daad6..fad1a49506a03 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -214,6 +214,7 @@ namespace std {
 #  include <__type_traits/is_replaceable.h>
 #  include <__type_traits/is_same.h>
 #  include <__type_traits/is_scalar.h>
+#  include <__type_traits/is_specialization.h>
 #  include <__type_traits/is_swappable.h>
 #  include <__type_traits/is_trivially_assignable.h>
 #  include <__type_traits/is_trivially_constructible.h>
@@ -575,9 +576,7 @@ concept __is_derived_from_optional = requires(const _Tp& __t) { []<class _Up>(co
 #    endif // _LIBCPP_STD_VER >= 20
 
 template <class _Tp>
-struct __is_std_optional : false_type {};
-template <class _Tp>
-struct __is_std_optional<optional<_Tp>> : true_type {};
+inline constexpr bool __is_std_optional_v = __is_specialization_v<_Tp, optional>;
 
 template <class _Tp>
 class _LIBCPP_DECLSPEC_EMPTY_BASES optional
@@ -618,7 +617,7 @@ private:
   template <class _Up>
   using _CheckOptionalArgsCtor _LIBCPP_NODEBUG =
       _If< _IsNotSame<__remove_cvref_t<_Up>, in_place_t>::value && _IsNotSame<__remove_cvref_t<_Up>, optional>::value &&
-               (!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_optional<__remove_cvref_t<_Up>>::value),
+               (!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_optional_v<__remove_cvref_t<_Up>>),
            _CheckOptionalArgsConstructor,
            __check_tuple_constructor_fail >;
   template <class _QualUp>
@@ -869,8 +868,8 @@ public:
   template <class _Func>
   _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
     using _Up = invoke_result_t<_Func, value_type&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(value()) must be a specialization of std::optional");
+    static_assert(
+        __is_std_optional_v<remove_cvref_t<_Up>>, "Result of f(value()) must be a specialization of std::optional");
     if (*this)
       return std::invoke(std::forward<_Func>(__f), value());
     return remove_cvref_t<_Up>();
@@ -879,8 +878,8 @@ public:
   template <class _Func>
   _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& {
     using _Up = invoke_result_t<_Func, const value_type&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
-                  "Result of f(value()) must be a specialization of std::optional");
+    static_assert(
+        __is_std_optional_v<remove_cvref_t<_Up>>, "Result of f(value()) must be a specialization of std::optional");
     if (*this)
       return std::invoke(std::forward<_Func>(__f), value());
     return remove_cvref_t<_Up>();
@@ -889,7 +888,7 @@ public:
   template <class _Func>
   _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && {
     using _Up = invoke_result_t<_Func, value_type&&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
+    static_assert(__is_std_optional_v<remove_cvref_t<_Up>>,
                   "Result of f(std::move(value())) must be a specialization of std::optional");
     if (*this)
       return std::invoke(std::forward<_Func>(__f), std::move(value()));
@@ -899,7 +898,7 @@ public:
   template <class _Func>
   _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
     using _Up = invoke_result_t<_Func, const value_type&&>;
-    static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
+    static_assert(__is_std_optional_v<remove_cvref_t<_Up>>,
                   "Result of f(std::move(value())) must be a specialization of std::optional");
     if (*this)
       return std::invoke(std::forward<_Func>(__f), std::move(value()));
diff --git a/libcxx/include/span b/libcxx/include/span
index 3d4f9e4ba7831..718914012c1de 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -203,14 +203,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 #  if _LIBCPP_STD_VER >= 20
 
 template <class _Tp>
-struct __is_std_span : false_type {};
+inline constexpr bool __is_std_span_v = false;
 
 template <class _Tp, size_t _Sz>
-struct __is_std_span<span<_Tp, _Sz>> : true_type {};
+inline constexpr bool __is_std_span_v<span<_Tp, _Sz>> = true;
 
 template <class _Range, class _ElementType>
 concept __span_compatible_range =
-    !__is_std_span<remove_cvref_t<_Range>>::value &&                //
+    !__is_std_span_v<remove_cvref_t<_Range>> &&                     //
     ranges::contiguous_range<_Range> &&                             //
     ranges::sized_range<_Range> &&                                  //
     (ranges::borrowed_range<_Range> || is_const_v<_ElementType>) && //



More information about the libcxx-commits mailing list