aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Wakely <jwakely@redhat.com>2024-07-24 18:08:03 +0100
committerThomas Koenig <tkoenig@gcc.gnu.org>2024-07-28 19:05:56 +0200
commitab1a0e683d8a10bcd59aab38e44bbce57c35bb49 (patch)
tree864d7a1db367f16f45a14de0448cf100ff9eb2e2
parenta9273a2991a74421f4627145f520a1eaf37736c7 (diff)
downloadgcc-ab1a0e683d8a10bcd59aab38e44bbce57c35bb49.zip
gcc-ab1a0e683d8a10bcd59aab38e44bbce57c35bb49.tar.gz
gcc-ab1a0e683d8a10bcd59aab38e44bbce57c35bb49.tar.bz2
libstdc++: Implement LWG 3836 for std::expected bool conversions
libstdc++-v3/ChangeLog: * include/std/expected (expected): Constrain constructors to prevent problematic bool conversions, as per LWG 3836. * testsuite/20_util/expected/lwg3836.cc: New test.
-rw-r--r--libstdc++-v3/include/std/expected59
-rw-r--r--libstdc++-v3/testsuite/20_util/expected/lwg3836.cc34
2 files changed, 77 insertions, 16 deletions
diff --git a/libstdc++-v3/include/std/expected b/libstdc++-v3/include/std/expected
index 86026c3..2594cfe 100644
--- a/libstdc++-v3/include/std/expected
+++ b/libstdc++-v3/include/std/expected
@@ -314,6 +314,17 @@ namespace __expected
__guard.release();
}
}
+
+ // _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
+
+ // If T is cv bool, remove_cvref_t<U> is not a specialization of expected.
+ template<typename _Tp, typename _Up>
+ concept __not_constructing_bool_from_expected
+ = ! is_same_v<remove_cv_t<_Tp>, bool>
+ || ! __is_expected<remove_cvref_t<_Up>>;
}
/// @endcond
@@ -327,26 +338,41 @@ namespace __expected
static_assert( ! __expected::__is_unexpected<remove_cv_t<_Tp>> );
static_assert( __expected::__can_be_unexpected<_Er> );
- template<typename _Up, typename _Err, typename _Unex = unexpected<_Er>>
+ // If T is not cv bool, converts-from-any-cvref<T, expected<U, G>> and
+ // is_constructible<unexpected<E>, cv expected<U, G> ref-qual> are false.
+ template<typename _Up, typename _Gr, typename _Unex = unexpected<_Er>,
+ typename = remove_cv_t<_Tp>>
static constexpr bool __cons_from_expected
- = __or_v<is_constructible<_Tp, expected<_Up, _Err>&>,
- is_constructible<_Tp, expected<_Up, _Err>>,
- is_constructible<_Tp, const expected<_Up, _Err>&>,
- is_constructible<_Tp, const expected<_Up, _Err>>,
- is_convertible<expected<_Up, _Err>&, _Tp>,
- is_convertible<expected<_Up, _Err>, _Tp>,
- is_convertible<const expected<_Up, _Err>&, _Tp>,
- is_convertible<const expected<_Up, _Err>, _Tp>,
- is_constructible<_Unex, expected<_Up, _Err>&>,
- is_constructible<_Unex, expected<_Up, _Err>>,
- is_constructible<_Unex, const expected<_Up, _Err>&>,
- is_constructible<_Unex, const expected<_Up, _Err>>
+ = __or_v<is_constructible<_Tp, expected<_Up, _Gr>&>,
+ is_constructible<_Tp, expected<_Up, _Gr>>,
+ is_constructible<_Tp, const expected<_Up, _Gr>&>,
+ is_constructible<_Tp, const expected<_Up, _Gr>>,
+ is_convertible<expected<_Up, _Gr>&, _Tp>,
+ is_convertible<expected<_Up, _Gr>, _Tp>,
+ is_convertible<const expected<_Up, _Gr>&, _Tp>,
+ is_convertible<const expected<_Up, _Gr>, _Tp>,
+ is_constructible<_Unex, expected<_Up, _Gr>&>,
+ is_constructible<_Unex, expected<_Up, _Gr>>,
+ is_constructible<_Unex, const expected<_Up, _Gr>&>,
+ is_constructible<_Unex, const expected<_Up, _Gr>>
>;
- template<typename _Up, typename _Err>
+ // _GLIBCXX_RESOLVE_LIB_DEFECTS
+ // If t is cv bool, we know it can be constructed from expected<U, G>,
+ // but we don't want to cause the expected(U&&) constructor to be used,
+ // so we only check the is_constructible<unexpected<E>, ...> cases.
+ template<typename _Up, typename _Gr, typename _Unex>
+ static constexpr bool __cons_from_expected<_Up, _Gr, _Unex, bool>
+ = __or_v<is_constructible<_Unex, expected<_Up, _Gr>&>,
+ is_constructible<_Unex, expected<_Up, _Gr>>,
+ is_constructible<_Unex, const expected<_Up, _Gr>&>,
+ is_constructible<_Unex, const expected<_Up, _Gr>>
+ >;
+
+ template<typename _Up, typename _Gr>
constexpr static bool __explicit_conv
= __or_v<__not_<is_convertible<_Up, _Tp>>,
- __not_<is_convertible<_Err, _Er>>
+ __not_<is_convertible<_Gr, _Er>>
>;
template<typename _Up>
@@ -445,8 +471,9 @@ namespace __expected
template<typename _Up = _Tp>
requires (!is_same_v<remove_cvref_t<_Up>, expected>)
&& (!is_same_v<remove_cvref_t<_Up>, in_place_t>)
- && (!__expected::__is_unexpected<remove_cvref_t<_Up>>)
&& is_constructible_v<_Tp, _Up>
+ && (!__expected::__is_unexpected<remove_cvref_t<_Up>>)
+ && __expected::__not_constructing_bool_from_expected<_Tp, _Up>
constexpr explicit(!is_convertible_v<_Up, _Tp>)
expected(_Up&& __v)
noexcept(is_nothrow_constructible_v<_Tp, _Up>)
diff --git a/libstdc++-v3/testsuite/20_util/expected/lwg3836.cc b/libstdc++-v3/testsuite/20_util/expected/lwg3836.cc
new file mode 100644
index 0000000..cd029c4
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/expected/lwg3836.cc
@@ -0,0 +1,34 @@
+// { dg-do run { target c++23 } }
+
+#include <expected>
+#include <testsuite_hooks.h>
+
+constexpr void
+test_convert_contained_value_to_bool()
+{
+ struct BaseError { };
+ struct DerivedError : BaseError { };
+
+ std::expected<bool, DerivedError> e = false;
+
+ // Should use expected(const expected<U, G>&) ctor, not expected(U&&):
+ std::expected<bool, BaseError> e2 = e;
+
+ // Contained value should be e.value() not static_cast<bool>(e):
+ VERIFY( e2.value() == false );
+
+ std::expected<bool, DerivedError> e3(std::unexpect);
+ std::expected<const bool, BaseError> e4 = e3;
+ // Should have error, not static_cast<bool>(e3):
+ VERIFY( ! e4.has_value() );
+}
+
+int main()
+{
+ test_convert_contained_value_to_bool();
+
+ static_assert([] {
+ test_convert_contained_value_to_bool();
+ return true;
+ }());
+}