[gcc(refs/users/aoliva/heads/testme)] libstdc++: check value in std::{con, dis}junction

Alexandre Oliva aoliva@gcc.gnu.org
Sat May 2 01:53:30 GMT 2026


https://gcc.gnu.org/g:89cdfae1f36f51b92ae1f14f6b61ce19d9efda3f

commit 89cdfae1f36f51b92ae1f14f6b61ce19d9efda3f
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Tue Apr 28 14:45:37 2026 -0300

    libstdc++: check value in std::{con,dis}junction
    
    When std::{con,dis}junction is passed a type that doesn't have a
    "value" static data member, or whose value doesn't convert to bool, is
    ambiguous or inaccessible, we stop iterating over the types and use
    that type for the {con,dis}junction, without any diagnostic.
    
    We'd get a diagnostic when attempting to use its value data member as
    a bool, but if the member isn't used, compilation is successful,
    despite the failure to meet the requirements for "value" in
    [meta.logical]/4.
    
    Check that the selected type parameter's value member is convertible
    to bool at the user-visible class; an assert in the _impl class would
    fuse errors whose malformed type starts a shared tail.
    
    
    for  libstdc++-v3/ChangeLog
    
            * include/std/type_traits (disjunction, conjunction): Check
            that value converts to bool.  Move implementation down.

Diff:
---
 libstdc++-v3/include/std/type_traits               | 142 +++++++++++----------
 .../logical_traits/requirements/junction_neg.cc    |  31 +++++
 2 files changed, 105 insertions(+), 68 deletions(-)

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 1c03c22e8eed..ee0c7388dedf 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -213,74 +213,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { };
   /// @endcond
 
-#ifdef __cpp_lib_logical_traits // C++ >= 17
-
-  /// @cond undocumented
-  template<typename... _Bn>
-    inline constexpr bool __or_v = __or_<_Bn...>::value;
-  template<typename... _Bn>
-    inline constexpr bool __and_v = __and_<_Bn...>::value;
-
-  namespace __detail
-  {
-    template<typename /* = void */, typename _B1, typename... _Bn>
-      struct __disjunction_impl
-      { using type = _B1; };
-
-    template<typename _B1, typename _B2, typename... _Bn>
-      struct __disjunction_impl<__enable_if_t<!bool(_B1::value)>, _B1, _B2, _Bn...>
-      { using type = typename __disjunction_impl<void, _B2, _Bn...>::type; };
-
-    template<typename /* = void */, typename _B1, typename... _Bn>
-      struct __conjunction_impl
-      { using type = _B1; };
-
-    template<typename _B1, typename _B2, typename... _Bn>
-      struct __conjunction_impl<__enable_if_t<bool(_B1::value)>, _B1, _B2, _Bn...>
-      { using type = typename __conjunction_impl<void, _B2, _Bn...>::type; };
-  } // namespace __detail
-  /// @endcond
-
-  template<typename... _Bn>
-    struct conjunction
-    : __detail::__conjunction_impl<void, _Bn...>::type
-    { };
-
-  template<>
-    struct conjunction<>
-    : true_type
-    { };
-
-  template<typename... _Bn>
-    struct disjunction
-    : __detail::__disjunction_impl<void, _Bn...>::type
-    { };
-
-  template<>
-    struct disjunction<>
-    : false_type
-    { };
-
-  template<typename _Pp>
-    struct negation
-    : __not_<_Pp>::type
-    { };
-
-  /** @ingroup variable_templates
-   * @{
-   */
-  template<typename... _Bn>
-    inline constexpr bool conjunction_v = conjunction<_Bn...>::value;
-
-  template<typename... _Bn>
-    inline constexpr bool disjunction_v = disjunction<_Bn...>::value;
-
-  template<typename _Pp>
-    inline constexpr bool negation_v = negation<_Pp>::value;
-  /// @}
-
-#endif // __cpp_lib_logical_traits
-
   // Forward declarations
   template<typename>
     struct is_object;
@@ -3871,6 +3803,80 @@ template<typename _Ret, typename _Fn, typename... _Args>
 /// @}
 #endif // __cpp_lib_type_trait_variable_templates
 
+#ifdef __cpp_lib_logical_traits // C++ >= 17
+
+  /// @cond undocumented
+  template<typename... _Bn>
+    inline constexpr bool __or_v = __or_<_Bn...>::value;
+  template<typename... _Bn>
+    inline constexpr bool __and_v = __and_<_Bn...>::value;
+
+  namespace __detail
+  {
+    template<typename /* = void */, typename _B1, typename... _Bn>
+      struct __disjunction_impl
+      { using type = _B1; };
+
+    template<typename _B1, typename _B2, typename... _Bn>
+      struct __disjunction_impl<__enable_if_t<!bool(_B1::value)>, _B1, _B2, _Bn...>
+      { using type = typename __disjunction_impl<void, _B2, _Bn...>::type; };
+
+    template<typename /* = void */, typename _B1, typename... _Bn>
+      struct __conjunction_impl
+      { using type = _B1; };
+
+    template<typename _B1, typename _B2, typename... _Bn>
+      struct __conjunction_impl<__enable_if_t<bool(_B1::value)>, _B1, _B2, _Bn...>
+      { using type = typename __conjunction_impl<void, _B2, _Bn...>::type; };
+  } // namespace __detail
+  /// @endcond
+
+  template<typename... _Bn>
+    struct conjunction
+    : __detail::__conjunction_impl<void, _Bn...>::type
+    {
+      using __type = __detail::__conjunction_impl<void, _Bn...>::type;
+      static_assert(is_convertible_v<decltype(__type::value), bool>);
+    };
+
+  template<>
+    struct conjunction<>
+    : true_type
+    { };
+
+  template<typename... _Bn>
+    struct disjunction
+    : __detail::__disjunction_impl<void, _Bn...>::type
+    {
+      using __type = __detail::__disjunction_impl<void, _Bn...>::type;
+      static_assert(is_convertible_v<decltype(__type::value), bool>);
+    };
+
+  template<>
+    struct disjunction<>
+    : false_type
+    { };
+
+  template<typename _Pp>
+    struct negation
+    : __not_<_Pp>::type
+    { };
+
+  /** @ingroup variable_templates
+   * @{
+   */
+  template<typename... _Bn>
+    inline constexpr bool conjunction_v = conjunction<_Bn...>::value;
+
+  template<typename... _Bn>
+    inline constexpr bool disjunction_v = disjunction<_Bn...>::value;
+
+  template<typename _Pp>
+    inline constexpr bool negation_v = negation<_Pp>::value;
+  /// @}
+
+#endif // __cpp_lib_logical_traits
+
 #ifdef __cpp_lib_has_unique_object_representations // C++ >= 17 && HAS_UNIQ_OBJ_REP
   /// has_unique_object_representations
   /// @since C++17
diff --git a/libstdc++-v3/testsuite/20_util/logical_traits/requirements/junction_neg.cc b/libstdc++-v3/testsuite/20_util/logical_traits/requirements/junction_neg.cc
new file mode 100644
index 000000000000..a9090659459e
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/logical_traits/requirements/junction_neg.cc
@@ -0,0 +1,31 @@
+// { dg-do compile { target c++17 } }
+
+#include <type_traits>
+
+class S {}; // not convertible to bool
+
+struct T {
+  static constexpr S value{};
+};
+
+// 4 Every template type argument for which Bi::value is instantiated shall be
+// usable as a base class and shall have a member value which is convertible to
+// bool, is not hidden, and is unambiguously available in the type.
+
+// ctT and dfT test that we don't fuse error messages when the type that
+// doesn't satisfy the requirements start a shared tail of types in the _impl
+// instantiations.
+
+std::conjunction<T> cT; // { dg-error "here" }
+std::conjunction<T, std::true_type> cTt; // { dg-error "here" }
+std::conjunction<T, std::false_type> cTf; // { dg-error "here" }
+std::conjunction<std::true_type, T> ctT; // { dg-error "here" }
+std::conjunction<std::false_type, T> cfT;
+
+std::disjunction<T> dT; // { dg-error "here" }
+std::disjunction<T, std::false_type> dTf; // { dg-error "here" }
+std::disjunction<T, std::true_type> dTt; // { dg-error "here" }
+std::disjunction<std::false_type, T> dfT; // { dg-error "here" }
+std::disjunction<std::true_type, T> dtT;
+
+// { dg-prune-output "static assertion failed|could not convert|evaluates to false" }


More information about the Libstdc++-cvs mailing list