[PATCH v3] libstdc++: check value in std::{con,dis}junction
Alexandre Oliva
oliva@adacore.com
Sat May 2 08:33:22 GMT 2026
On Apr 30, 2026, Jonathan Wakely <jwakely@redhat.com> wrote:
> There's no requirement to make the new testcase ill-formed, so I'm
> not sure we need to change anything.
I agree we don't need to change; it just seems good practice to check
that the requirements are met instead of silently swallowing what is
likely an error, that I'm told other compilers report. That said, it's
probably just a pedantic overcompliance matter, since any use of
::value, which is to be expected, would report the error anyway.
So this is probably my last attempt at this. Instead of using
type::value, I introduce __type as a typedef to the base type to
simplify the static_assert.
I'd appreciate an explicit decline in case we don't want this, so that I
don't keep on pinging and waiting for a resolution. (ditto for
https://gcc.gnu.org/pipermail/gcc-patches/2026-April/713470.html BTW :-)
TIA,
Regstrapping on x86_64-linux-gnu. Ok to install?
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. A diagnostic is not required, but it may be useful.
Check that the selected type parameter has a value member, and that
its 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, so introduce __type in the user-visible class to
shorten the assert.
for libstdc++-v3/ChangeLog
* include/std/type_traits (disjunction, conjunction): Check
that value converts to bool. Move implementation down.
---
libstdc++-v3/include/std/type_traits | 142 ++++++++++----------
.../logical_traits/requirements/junction_neg.cc | 31 ++++
2 files changed, 105 insertions(+), 68 deletions(-)
create mode 100644 libstdc++-v3/testsuite/20_util/logical_traits/requirements/junction_neg.cc
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 1c03c22e8eed4..d8de3f013a73c 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
+ {
+ typedef typename __detail::__conjunction_impl<void, _Bn...>::type __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
+ {
+ typedef typename __detail::__disjunction_impl<void, _Bn...>::type __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 0000000000000..bfa502fe03116
--- /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)} }
--
Alexandre Oliva, happy hacker https://blog.lx.oliva.nom.br/
Free Software Activist FSFLA co-founder GNU Toolchain Engineer
More tolerance and less prejudice are key for inclusion and diversity.
Excluding neuro-others for not behaving ""normal"" is *not* inclusive!
More information about the Libstdc++
mailing list