[PATCH] libstdc++: check value in std::{con,dis}junction
Jonathan Wakely
jwakely.gcc@gmail.com
Wed Apr 29 22:12:32 GMT 2026
On Wed, 29 Apr 2026 at 22:52, Jonathan Wakely <jwakely.gcc@gmail.com> wrote:
>
> On Wed, 29 Apr 2026 at 22:38, Jonathan Wakely <jwakely.gcc@gmail.com> wrote:
> >
> > On Wed, 29 Apr 2026 at 22:33, Jonathan Wakely <jwakely.gcc@gmail.com> wrote:
> > >
> > > On Wed, 29 Apr 2026, 20:53 Alexandre Oliva, <oliva@adacore.com> wrote:
> > > >
> > > > Oops, I forgot to post the following to the libstdc++ mailing list.
> > > > https://gcc.gnu.org/pipermail/gcc-patches/2026-April/712661.html
> > > >
> > > > -------------------- Start of forwarded message --------------------
> > > > From: Alexandre Oliva <oliva@adacore.com>
> > > > To: gcc-patches@gcc.gnu.org
> > > > Subject: [PATCH] libstdc++: check value in std::{con,dis}junction
> > > > Date: Fri, 10 Apr 2026 09:02:49 -0300
> > > >
> > > >
> > > > 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.
> > > >
> > > > Regstrapped on x86_64-linux-gnu. Ok to install? Not a regression, so
> > > > for stage1 maybe?
> > >
> > >
> > > Hmm, this might be a regression caused by r13-2230-g390f94eee1ae69
> > >
> > > Before that change I think we required all template arguments to have
> > > a ::value that is implicitly convertible to bool. Now we only require
> > > explicit conversion, and we stop if we reach a type that fails to meet
> > > that condition.
> > >
> > > >
> > > >
> > > >
> > > > for libstdc++-v3/ChangeLog
> > > >
> > > > * include/std/type_traits (disjunction, conjunction): Check
> > > > that value converts to bool.
> > > > ---
> > > > libstdc++-v3/include/std/type_traits | 15 ++++++++++-
> > > > .../logical_traits/requirements/junction_neg.cc | 27 ++++++++++++++++++++
> > > > 2 files changed, 40 insertions(+), 2 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 59be925e10a6c..238450518c083 100644
> > > > --- a/libstdc++-v3/include/std/type_traits
> > > > +++ b/libstdc++-v3/include/std/type_traits
> > > > @@ -241,10 +241,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > > > } // namespace __detail
> > > > /// @endcond
> > > >
> > > > + template<typename _From, typename _To>
> > > > + struct is_convertible;
> > > > +
> > > > template<typename... _Bn>
> > > > struct conjunction
> > > > : __detail::__conjunction_impl<void, _Bn...>::type
> > > > - { };
> > > > + {
> > > > + static_assert(is_convertible<decltype(__detail::__conjunction_impl<void, _Bn...>
> > > > + ::type::value), bool>::value,
> > > > + "result type's value is not convertible to bool");
> > >
> > >
> > > The string literal should be phrased as "must be..." instead of "is not..."
> > >
> > > That avoids and possible confusion when the assertion fails and we the
> > > "assertion failed: X is not Y" string. Was the required condition "X
> > > is not Y" and that failed? Or is the condition "X is Y" and that is
> > > what failed?
> > >
> > > Saying "X must be Y" is clear what the required condition is.
> > >
> > > But I'm concerned that we enforce the static assert in all cases now,
> > > when we really only need to enforce it sometimes. An alternative
> > > implementation would be something like:
> > >
> > > --- a/libstdc++-v3/include/std/type_traits
> > > +++ b/libstdc++-v3/include/std/type_traits
> > > @@ -233,7 +233,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > >
> > > template<typename /* = void */, typename _B1, typename... _Bn>
> > > struct __conjunction_impl
> > > - { using type = _B1; };
> > > + {
> > > + using type = _B1;
> > > +
> > > + static_assert(is_convertible<decltype(type::value), bool>::value,
> > > + "type::value must be convertible to bool");
> > > + };
> > >
> > > template<typename _B1, typename _B2, typename... _Bn>
> > > struct __conjunction_impl<__enable_if_t<bool(_B1::value)>, _B1,
> > > _B2, _Bn...>
> > >
> > > This only checks the condition in the case where bool(_B1::value) is
> > > either false or fails substitution (because it doesn't exist, or isn't
> > > accessible, or isn't explicitly convertible to bool).
>
> My suggestion to check in __conjunction_impl doesn't work for the
> ctTtf case, it fails to give an error. I don't immediately see why
> that is.
Ah, it _does_ work, but the static_assert for ctTtf comes from the
same instantiation as for cT and so is suppressed. I think G++ only
prints each unique static_assert failure once in a given scope.
We can solve that by using a different type, T2, in the ctTtf and dtTtf cases.
So here's a complete patch that passes the new test ...
-------------- next part --------------
commit 5d35fecac3e031ba9dc27942744bc8d82d1ef885
Author: Alexandre Oliva <oliva@adacore.com>
AuthorDate: Wed Apr 29 23:02:53 2026
Commit: Jonathan Wakely <redi@gcc.gnu.org>
CommitDate: Wed Apr 29 23:11:23 2026
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.
libstdc++-v3/ChangeLog:
* include/std/type_traits (disjunction, conjunction): Check
that value converts to bool.
* testsuite/20_util/logical_traits/requirements/junction_neg.cc:
New test.
Co-authored-by: Jonathan Wakely <jwakely@redhat.com>
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 1c03c22e8eed..e6422ab5065a 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,81 @@ 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;
+ static_assert(is_convertible_v<decltype(type::value), bool>);
+ };
+
+ 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;
+ static_assert(is_convertible_v<decltype(type::value), bool>);
+ };
+
+ 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
+
+
#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..bb23bc482aed
--- /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{};
+};
+
+struct T2 {
+ 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.
+
+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, T2> ctTtf; // { dg-error "here" }
+std::conjunction<std::false_type, T> cfTtf;
+
+std::disjunction<T> dT; // { dg-error "here" }
+std::disjunction<T, std::true_type> dTt; // { dg-error "here" }
+std::disjunction<T, std::false_type> dTf; // { dg-error "here" }
+std::disjunction<std::false_type, T2> dfTtf; // { dg-error "here" }
+std::disjunction<std::true_type, T> dtTtf;
+
+// { dg-prune-output "(static assertion failed|could not convert)" }
More information about the Libstdc++
mailing list