diff --git a/libstdc++-v3/include/std/expected b/libstdc++-v3/include/std/expected index 591fc72a4388..a03a7d3f722f 100644 --- a/libstdc++-v3/include/std/expected +++ b/libstdc++-v3/include/std/expected @@ -1163,15 +1163,17 @@ namespace __expected { __t == __u } -> convertible_to; { __e == __e2 } -> convertible_to; } + [[nodiscard]] friend constexpr bool operator==(const expected& __x, const expected<_Up, _Er2>& __y) noexcept(noexcept(bool(*__x == *__y)) && noexcept(bool(__x.error() == __y.error()))) { + if (__x.has_value() != __y.has_value()) + return false; if (__x.has_value()) - return __y.has_value() && bool(*__x == *__y); - else - return !__y.has_value() && bool(__x.error() == __y.error()); + return *__x == *__y; + return __x.error() == __y.error(); } template _Vp> @@ -1179,19 +1181,29 @@ namespace __expected && requires (const _Tp& __t, const _Up& __u) { { __t == __u } -> convertible_to; } + [[nodiscard]] friend constexpr bool operator==(const expected<_Vp, _Er>& __x, const _Up& __v) noexcept(noexcept(bool(*__x == __v))) - { return __x.has_value() && bool(*__x == __v); } + { + if (__x.has_value()) + return *__x == __v; + return false; + } template requires requires (const _Er& __e, const _Er2& __e2) { { __e == __e2 } -> convertible_to; } + [[nodiscard]] friend constexpr bool operator==(const expected& __x, const unexpected<_Er2>& __e) noexcept(noexcept(bool(__x.error() == __e.error()))) - { return !__x.has_value() && bool(__x.error() == __e.error()); } + { + if (!__x.has_value()) + return __x.error() == __e.error(); + return false; + } friend constexpr void swap(expected& __x, expected& __y) @@ -1841,24 +1853,31 @@ namespace __expected && requires (const _Er& __e, const _Er2& __e2) { { __e == __e2 } -> convertible_to; } + [[nodiscard]] friend constexpr bool operator==(const expected& __x, const expected<_Up, _Er2>& __y) noexcept(noexcept(bool(__x.error() == __y.error()))) { + if (__x.has_value() != __y.has_value()) + return false; if (__x.has_value()) - return __y.has_value(); - else - return !__y.has_value() && bool(__x.error() == __y.error()); + return true; + return __x.error() == __y.error(); } template requires requires (const _Er& __e, const _Er2& __e2) { { __e == __e2 } -> convertible_to; } + [[nodiscard]] friend constexpr bool operator==(const expected& __x, const unexpected<_Er2>& __e) noexcept(noexcept(bool(__x.error() == __e.error()))) - { return !__x.has_value() && bool(__x.error() == __e.error()); } + { + if (!__x.has_value()) + return __x.error() == __e.error(); + return false; + } friend constexpr void swap(expected& __x, expected& __y) diff --git a/libstdc++-v3/testsuite/20_util/expected/equality.cc b/libstdc++-v3/testsuite/20_util/expected/equality.cc index db19b1510a73..cc122f469082 100644 --- a/libstdc++-v3/testsuite/20_util/expected/equality.cc +++ b/libstdc++-v3/testsuite/20_util/expected/equality.cc @@ -28,19 +28,45 @@ test_eq() std::expected e2; VERIFY(e2 == e2); VERIFY(e1 == e2); + VERIFY(e2 == e1); VERIFY(e1 != std::unexpected(1)); + e1 = std::unexpected(1); VERIFY(e1 == std::unexpected(1)); VERIFY(e1 != std::unexpected(2)); VERIFY(e1 != e2); + VERIFY(e2 != e1); + VERIFY(e1 != 1); + + e2 = std::unexpected(1); + VERIFY(e1 == e2); + VERIFY(e2 == e1); + + e2 = std::unexpected(2); + VERIFY(e1 != e2); + VERIFY(e2 != e1); std::expected e3; VERIFY(e3 == e3); VERIFY(e3 != std::unexpected(1)); + std::expected e4; + VERIFY(e3 == e4); + VERIFY(e4 == e3); + e3 = std::unexpected(1); VERIFY(e3 == e3); VERIFY(e3 == std::unexpected(1)); VERIFY(e3 != std::unexpected(2)); + VERIFY(e3 != e4); + VERIFY(e4 != e3); + + e4 = e3; + VERIFY(e3 == e4); + VERIFY(e4 == e3); + + e4 = std::unexpected(4); + VERIFY(e3 != e4); + VERIFY(e4 != e3); return true; } diff --git a/libstdc++-v3/testsuite/20_util/expected/lwg4366.cc b/libstdc++-v3/testsuite/20_util/expected/lwg4366.cc new file mode 100644 index 000000000000..35d53714f036 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/expected/lwg4366.cc @@ -0,0 +1,33 @@ +// { dg-do run { target c++23 } } + +// LWG 4366. Heterogeneous comparison of expected may be ill-formed + +#include +#include + +struct Bool +{ + operator bool() const { return true; } + explicit operator bool() { throw; } +}; + +struct E1 { + friend Bool operator==(E1, E1) { return {}; } +} e1; + +struct E2 { + friend Bool operator==(E1, E2) { return {}; } +} e2; + +int main() +{ + std::expected u1(std::unexpect, e1); + VERIFY(u1 == u1); + + std::unexpected u2(e2); + VERIFY(u1 == u2); + + std::expected u3(std::unexpect, e1); + VERIFY(u3 == u3); + VERIFY(u3 == u2); +}