[PATCH] libstdc++: check value in std::{con,dis}junction
Jonathan Wakely
jwakely.gcc@gmail.com
Wed Apr 29 22:18:52 GMT 2026
On Wed, 29 Apr 2026 at 23:12, Jonathan Wakely <jwakely.gcc@gmail.com> wrote:
>
> 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 ...
P.S. I didn't use any string literal in the new static assertions, I
don't think it's really needed here. The errors without it look like
this:
/home/jwakely/gcc/17/include/c++/17.0.0/type_traits:3820:23: error:
static assertion failed
3820 | static_assert(is_convertible_v<decltype(type::value), bool>);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* error: could not convert 'const S' to 'bool'
That seems pretty clear already, without adding words to say
"type::value must be convertible to bool", especially since those
words aren't even correct, e.g. if the problem is that type::value
doesn't exist at all, or is private.
If we let the compiler tell us what's wrong, it will be more accurate, e.g.
/home/jwakely/gcc/17/include/c++/17.0.0/type_traits:3831:49: error:
'value' is not a member of 'T2'
3831 | static_assert(is_convertible_v<decltype(type::value), bool>);
| ^~~~
or:
/home/jwakely/gcc/17/include/c++/17.0.0/type_traits:3831:49: error:
'constexpr const S T2::value' is private within this context
3831 | static_assert(is_convertible_v<decltype(type::value), bool>);
| ^~~~
conj.cc:11:22: note: declared private here
11 | static constexpr S value{};
| ^~~~~
More information about the Libstdc++
mailing list