aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Wakely <jwakely@redhat.com>2024-07-23 12:45:37 +0100
committerThomas Koenig <tkoenig@gcc.gnu.org>2024-07-28 19:05:56 +0200
commit0e3c6a936568c859e4653854f0cd4206f390d56e (patch)
tree561677ccb612bcf5110b1f85bd2ad9e2efc9479a
parentab1a0e683d8a10bcd59aab38e44bbce57c35bb49 (diff)
downloadgcc-0e3c6a936568c859e4653854f0cd4206f390d56e.zip
gcc-0e3c6a936568c859e4653854f0cd4206f390d56e.tar.gz
gcc-0e3c6a936568c859e4653854f0cd4206f390d56e.tar.bz2
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.
-rw-r--r--libstdc++-v3/include/std/optional58
-rw-r--r--libstdc++-v3/testsuite/20_util/optional/cons/lwg3836.cc58
2 files changed, 100 insertions, 16 deletions
diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional
index 344be5e..700e704 100644
--- a/libstdc++-v3/include/std/optional
+++ b/libstdc++-v3/include/std/optional
@@ -749,16 +749,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Tp>
inline constexpr bool __is_optional_v<optional<_Tp>> = true;
+ template<typename _Tp, typename _Wp>
+ 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<const _Wp&, _Tp>,
+ is_constructible<_Tp, const _Wp>, is_convertible<const _Wp, _Tp>
+ >;
+
template<typename _Tp, typename _Up>
- using __converts_from_optional =
- __or_<is_constructible<_Tp, const optional<_Up>&>,
- is_constructible<_Tp, optional<_Up>&>,
- is_constructible<_Tp, const optional<_Up>&&>,
- is_constructible<_Tp, optional<_Up>&&>,
- is_convertible<const optional<_Up>&, _Tp>,
- is_convertible<optional<_Up>&, _Tp>,
- is_convertible<const optional<_Up>&&, _Tp>,
- is_convertible<optional<_Up>&&, _Tp>>;
+ using __converts_from_optional
+ = __converts_from_any_cvref<_Tp, optional<_Up>>;
template<typename _Tp, typename _Up>
using __assigns_from_optional =
@@ -800,6 +801,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename... _Cond>
using _Requires = enable_if_t<__and_v<_Cond...>, bool>;
+ // _GLIBCXX_RESOLVE_LIB_DEFECTS
+ // 3836. std::expected<bool, E1> conversion constructor
+ // expected(const expected<U, G>&) should take precedence over
+ // expected(U&&) with operator bool
+ template<typename _From, typename = remove_cv_t<_Tp>>
+ struct __not_constructing_bool_from_optional
+ : true_type
+ { };
+
+ template<typename _From>
+ struct __not_constructing_bool_from_optional<_From, bool>
+ : bool_constant<!__is_optional_v<__remove_cvref_t<_From>>>
+ { };
+
+ template<typename _From, typename = remove_cv_t<_Tp>>
+ struct __construct_from_contained_value
+ : __not_<__converts_from_optional<_Tp, _From>>
+ { };
+
+ template<typename _From>
+ struct __construct_from_contained_value<_From, bool>
+ : true_type
+ { };
+
public:
using value_type = _Tp;
@@ -811,7 +836,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Up = _Tp,
_Requires<__not_self<_Up>, __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<typename _Up = _Tp,
_Requires<__not_self<_Up>, __not_tag<_Up>,
is_constructible<_Tp, _Up>,
- __not_<is_convertible<_Up, _Tp>>> = false>
+ __not_<is_convertible<_Up, _Tp>>,
+ __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_same<_Tp, _Up>>,
is_constructible<_Tp, const _Up&>,
is_convertible<const _Up&, _Tp>,
- __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_same<_Tp, _Up>>,
is_constructible<_Tp, const _Up&>,
__not_<is_convertible<const _Up&, _Tp>>,
- __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_same<_Tp, _Up>>,
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_same<_Tp, _Up>>,
is_constructible<_Tp, _Up>,
__not_<is_convertible<_Up, _Tp>>,
- __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 0000000..816eb1b
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/optional/cons/lwg3836.cc
@@ -0,0 +1,58 @@
+// { dg-do run { target c++17 } }
+
+#include <optional>
+#include <testsuite_hooks.h>
+
+constexpr void
+test_convert_contained_value_to_bool()
+{
+ struct False { constexpr operator bool() const { return false; } };
+
+ False f;
+ std::optional<False> o = f;
+
+ // Should use optional(const optional<U>&) ctor, not optional(U&&):
+ std::optional<bool> o2 = o;
+
+ // Contained value should be static_cast<bool>(f) not static_cast<bool>(o):
+ VERIFY( o2.value() == false );
+
+ std::optional<False> o3;
+ std::optional<const bool> o4 = o3;
+ // Should have no contained value, not static_cast<bool>(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<False> o = f;
+
+ // Should use optional(const optional<U>&) ctor, not optional(U&&):
+ std::optional<bool> o2(o);
+
+ // Contained value should be static_cast<bool>(f) not static_cast<bool>(o):
+ VERIFY( o2.value() == false );
+
+ std::optional<False> o3;
+ std::optional<const bool> o4(o3);
+ // Should have no contained value, not static_cast<bool>(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
+}