From: Jonathan Wakely Date: Tue, 23 Jul 2024 11:45:37 +0000 (+0100) Subject: libstdc++: Implement LWG 3836 for std::optional bool conversions X-Git-Tag: basepoints/gcc-16~7245 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=75618d2fd72ccc1c0d76646ef3bb03cb137afdd2;p=thirdparty%2Fgcc.git libstdc++: Implement LWG 3836 for std::optional bool conversions libstdc++-v3/ChangeLog: * include/std/optional (optional): Constrain constructors to prevent problematic bool conversions, as per LWG 3836. * testsuite/20_util/optional/cons/lwg3836.cc: New test. --- diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional index 344be5e44d3..700e7047aba 100644 --- a/libstdc++-v3/include/std/optional +++ b/libstdc++-v3/include/std/optional @@ -749,16 +749,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template inline constexpr bool __is_optional_v> = true; + template + using __converts_from_any_cvref = __or_< + is_constructible<_Tp, _Wp&>, is_convertible<_Wp&, _Tp>, + is_constructible<_Tp, _Wp>, is_convertible<_Wp, _Tp>, + is_constructible<_Tp, const _Wp&>, is_convertible, + is_constructible<_Tp, const _Wp>, is_convertible + >; + template - using __converts_from_optional = - __or_&>, - is_constructible<_Tp, optional<_Up>&>, - is_constructible<_Tp, const optional<_Up>&&>, - is_constructible<_Tp, optional<_Up>&&>, - is_convertible&, _Tp>, - is_convertible&, _Tp>, - is_convertible&&, _Tp>, - is_convertible&&, _Tp>>; + using __converts_from_optional + = __converts_from_any_cvref<_Tp, optional<_Up>>; template using __assigns_from_optional = @@ -800,6 +801,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template using _Requires = enable_if_t<__and_v<_Cond...>, bool>; + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3836. std::expected conversion constructor + // expected(const expected&) should take precedence over + // expected(U&&) with operator bool + template> + struct __not_constructing_bool_from_optional + : true_type + { }; + + template + struct __not_constructing_bool_from_optional<_From, bool> + : bool_constant>> + { }; + + template> + struct __construct_from_contained_value + : __not_<__converts_from_optional<_Tp, _From>> + { }; + + template + struct __construct_from_contained_value<_From, bool> + : true_type + { }; + public: using value_type = _Tp; @@ -811,7 +836,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template, __not_tag<_Up>, is_constructible<_Tp, _Up>, - is_convertible<_Up, _Tp>> = true> + is_convertible<_Up, _Tp>, + __not_constructing_bool_from_optional<_Up>> = true> constexpr optional(_Up&& __t) noexcept(is_nothrow_constructible_v<_Tp, _Up>) @@ -820,7 +846,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template, __not_tag<_Up>, is_constructible<_Tp, _Up>, - __not_>> = false> + __not_>, + __not_constructing_bool_from_optional<_Up>> = false> explicit constexpr optional(_Up&& __t) noexcept(is_nothrow_constructible_v<_Tp, _Up>) @@ -830,7 +857,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Requires<__not_>, is_constructible<_Tp, const _Up&>, is_convertible, - __not_<__converts_from_optional<_Tp, _Up>>> = true> + __construct_from_contained_value<_Up>> = true> constexpr optional(const optional<_Up>& __t) noexcept(is_nothrow_constructible_v<_Tp, const _Up&>) @@ -843,7 +870,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Requires<__not_>, is_constructible<_Tp, const _Up&>, __not_>, - __not_<__converts_from_optional<_Tp, _Up>>> = false> + __construct_from_contained_value<_Up>> = false> explicit constexpr optional(const optional<_Up>& __t) noexcept(is_nothrow_constructible_v<_Tp, const _Up&>) @@ -856,7 +883,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Requires<__not_>, is_constructible<_Tp, _Up>, is_convertible<_Up, _Tp>, - __not_<__converts_from_optional<_Tp, _Up>>> = true> + __construct_from_contained_value<_Up>> = true> constexpr optional(optional<_Up>&& __t) noexcept(is_nothrow_constructible_v<_Tp, _Up>) @@ -869,7 +896,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Requires<__not_>, is_constructible<_Tp, _Up>, __not_>, - __not_<__converts_from_optional<_Tp, _Up>>> = false> + __construct_from_contained_value<_Up>> = false> explicit constexpr optional(optional<_Up>&& __t) noexcept(is_nothrow_constructible_v<_Tp, _Up>) @@ -895,7 +922,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Args...>) : _Base(std::in_place, __il, std::forward<_Args>(__args)...) { } - // Assignment operators. _GLIBCXX20_CONSTEXPR optional& operator=(nullopt_t) noexcept diff --git a/libstdc++-v3/testsuite/20_util/optional/cons/lwg3836.cc b/libstdc++-v3/testsuite/20_util/optional/cons/lwg3836.cc new file mode 100644 index 00000000000..816eb1b7def --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/cons/lwg3836.cc @@ -0,0 +1,58 @@ +// { dg-do run { target c++17 } } + +#include +#include + +constexpr void +test_convert_contained_value_to_bool() +{ + struct False { constexpr operator bool() const { return false; } }; + + False f; + std::optional o = f; + + // Should use optional(const optional&) ctor, not optional(U&&): + std::optional o2 = o; + + // Contained value should be static_cast(f) not static_cast(o): + VERIFY( o2.value() == false ); + + std::optional o3; + std::optional o4 = o3; + // Should have no contained value, not static_cast(o3): + VERIFY( ! o4.has_value() ); +} + +constexpr void +test_convert_contained_value_to_bool_explicit() +{ + struct False { constexpr explicit operator bool() const { return false; } }; + + False f; + std::optional o = f; + + // Should use optional(const optional&) ctor, not optional(U&&): + std::optional o2(o); + + // Contained value should be static_cast(f) not static_cast(o): + VERIFY( o2.value() == false ); + + std::optional o3; + std::optional o4(o3); + // Should have no contained value, not static_cast(o3): + VERIFY( ! o4.has_value() ); +} + +int main() +{ + test_convert_contained_value_to_bool(); + test_convert_contained_value_to_bool_explicit(); + +#if __cpp_lib_optional >= 202106 + static_assert([] { + test_convert_contained_value_to_bool(); + test_convert_contained_value_to_bool_explicit(); + return true; + }()); +#endif +}