[gcc(refs/users/aoliva/heads/testme)] libstdc++: check value in std::{con, dis}junction
Alexandre Oliva
aoliva@gcc.gnu.org
Thu Apr 30 04:44:50 GMT 2026
https://gcc.gnu.org/g:e2e3dca171e30d7b40fea3bb76ee8000ff8a9f77
commit e2e3dca171e30d7b40fea3bb76ee8000ff8a9f77
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..58923aacd90e 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 __detail::__conjunction_impl<void, _Bn...>::type::value;
+ static_assert(is_convertible_v<decltype(value), bool>);
+ };
+
+ template<>
+ struct conjunction<>
+ : true_type
+ { };
+
+ template<typename... _Bn>
+ struct disjunction
+ : __detail::__disjunction_impl<void, _Bn...>::type
+ {
+ using __detail::__disjunction_impl<void, _Bn...>::type::value;
+ static_assert(is_convertible_v<decltype(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..ff2558d5ce6c
--- /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|evaluates to false" }
More information about the Libstdc++-cvs
mailing list