On Thu, 14 Aug 2025, Yihan Wang wrote: > libstdc++-v3/ChangeLog: > > * include/std/expected: Add missing constraint as per LWG 4222. > * testsuite/20_util/expected/lwg4222.cc: New test. > > Signed-off-by: Yihan Wang <yronglin...@gmail.com> > --- > libstdc++-v3/include/std/expected | 1 + > .../testsuite/20_util/expected/lwg4222.cc | 32 +++++++++++++++++++ > 2 files changed, 33 insertions(+) > create mode 100644 libstdc++-v3/testsuite/20_util/expected/lwg4222.cc > > diff --git a/libstdc++-v3/include/std/expected > b/libstdc++-v3/include/std/expected > index 60f1565f15b..4eaaab693e1 100644 > --- a/libstdc++-v3/include/std/expected > +++ b/libstdc++-v3/include/std/expected > @@ -474,6 +474,7 @@ namespace __expected > template<typename _Up = remove_cv_t<_Tp>> > requires (!is_same_v<remove_cvref_t<_Up>, expected>) > && (!is_same_v<remove_cvref_t<_Up>, in_place_t>) > + && (!is_same_v<remove_cvref_t<_Up>, unexpect_t>) > && is_constructible_v<_Tp, _Up> > && (!__expected::__is_unexpected<remove_cvref_t<_Up>>) > && __expected::__not_constructing_bool_from_expected<_Tp, _Up> > diff --git a/libstdc++-v3/testsuite/20_util/expected/lwg4222.cc > b/libstdc++-v3/testsuite/20_util/expected/lwg4222.cc > new file mode 100644 > index 00000000000..2483afba853 > --- /dev/null > +++ b/libstdc++-v3/testsuite/20_util/expected/lwg4222.cc > @@ -0,0 +1,32 @@ > +// { dg-do compile { target c++23 } } > + > +// LWG 4222. 'expected' constructor from a single value missing a constraint > + > +#include <expected> > +#include <type_traits> > +#include <testsuite_hooks.h> > + > +struct T { > + explicit T(auto) {} > +}; > +struct E { > + E(int) {} > +}; > + > +struct V { > + explicit V(std::unexpect_t) {} > +}; > + > +static_assert(!std::is_constructible_v<std::expected<T, E>, > std::unexpect_t>); > +static_assert(!std::is_constructible_v<std::expected<T, E>, std::unexpect_t > &>); > +static_assert(!std::is_constructible_v<std::expected<T, E>, std::unexpect_t > &&>); > +static_assert(!std::is_constructible_v<std::expected<T, E>, const > std::unexpect_t>); > +static_assert(!std::is_constructible_v<std::expected<T, E>, const > std::unexpect_t &>); > +static_assert(!std::is_constructible_v<std::expected<T, E>, const > std::unexpect_t &&>); > + > +void test() { > + std::expected<V, int> e1(std::in_place, std::unexpect); > + VERIFY( e1.has_value() ); > + std::expected<int, V> e2(std::unexpect, std::unexpect); > + VERIFY( !e2.has_value() );
These VERIFY asserts aren't actually checked because this test file is specified as a compile-only test via 'dg-do compile'. We can either turn this into a runnable test by specifying 'dg-do run' (and defining main()), or we can keep it a compile-only test and make test() constexpr and add something like static_assert(test()). (In theory constexpr tests are stronger than runtime tests since they diagnose UB, so I generally prefer the latter. Ideally we should have both a runtime and constexpr test but that seems overkill here.) > +} > -- > 2.39.5 > >