public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r13-7423] libstdc++: Do not use std::expected::value() in monadic ops (LWG 3938)
@ 2023-06-06 22:58 Jonathan Wakely
  0 siblings, 0 replies; only message in thread
From: Jonathan Wakely @ 2023-06-06 22:58 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:ff58310f274e496c6ce6ef86211be734d94e5dee

commit r13-7423-gff58310f274e496c6ce6ef86211be734d94e5dee
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Jun 1 11:16:49 2023 +0100

    libstdc++: Do not use std::expected::value() in monadic ops (LWG 3938)
    
    The monadic operations in std::expected always check has_value() so we
    can avoid the execptional path in value() and the assertions in error()
    by accessing _M_val and _M_unex directly. This means that the monadic
    operations no longer require _M_unex to be copyable so that it can be
    thrown from value(), as modified by LWG 3938.
    
    This also fixes two incorrect uses of std::move in transform(F&&)& and
    transform(F&&) const& which I found while making these changes.
    
    Now that move-only error types are supported, it's possible to properly
    test the constraints that LWG 3877 added to and_then and transform. The
    lwg3877.cc test now does that.
    
    libstdc++-v3/ChangeLog:
    
            * include/std/expected (expected::and_then, expected::or_else)
            (expected::transform_error): Use _M_val and _M_unex instead of
            calling value() and error(), as per LWG 3938.
            (expected::transform): Likewise. Remove incorrect std::move
            calls from lvalue overloads.
            (expected<void, E>::and_then, expected<void, E>::or_else)
            (expected<void, E>::transform): Use _M_unex instead of calling
            error().
            * testsuite/20_util/expected/lwg3877.cc: Add checks for and_then
            and transform, and for std::expected<void, E>.
            * testsuite/20_util/expected/lwg3938.cc: New test.
    
    (cherry picked from commit fe94f8b7e022b7e154f6c47cc292d4463bddac5e)

Diff:
---
 libstdc++-v3/include/std/expected                  |  78 ++++++-----
 libstdc++-v3/testsuite/20_util/expected/lwg3877.cc | 145 +++++++++++++++++----
 libstdc++-v3/testsuite/20_util/expected/lwg3938.cc | 142 ++++++++++++++++++++
 3 files changed, 298 insertions(+), 67 deletions(-)

diff --git a/libstdc++-v3/include/std/expected b/libstdc++-v3/include/std/expected
index 5ea0d6a7cb9..a63557448f7 100644
--- a/libstdc++-v3/include/std/expected
+++ b/libstdc++-v3/include/std/expected
@@ -745,8 +745,7 @@ namespace __expected
       {
 	if (_M_has_value) [[likely]]
 	  return std::move(_M_val);
-	_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(
-				  std::move(_M_unex)));
+	_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(std::move(_M_unex)));
       }
 
       constexpr _Tp&&
@@ -754,8 +753,7 @@ namespace __expected
       {
 	if (_M_has_value) [[likely]]
 	  return std::move(_M_val);
-	_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(
-				  std::move(_M_unex)));
+	_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(std::move(_M_unex)));
       }
 
       constexpr const _Er&
@@ -849,9 +847,9 @@ namespace __expected
 	  static_assert(is_same_v<typename _Up::error_type, _Er>);
 
 	  if (has_value())
-	    return std::__invoke(std::forward<_Fn>(__f), value());
+	    return std::__invoke(std::forward<_Fn>(__f), _M_val);
 	  else
-	    return _Up(unexpect, error());
+	    return _Up(unexpect, _M_unex);
 	}
 
       template<typename _Fn> requires is_constructible_v<_Er, const _Er&>
@@ -863,9 +861,9 @@ namespace __expected
 	  static_assert(is_same_v<typename _Up::error_type, _Er>);
 
 	  if (has_value())
-	    return std::__invoke(std::forward<_Fn>(__f), value());
+	    return std::__invoke(std::forward<_Fn>(__f), _M_val);
 	  else
-	    return _Up(unexpect, error());
+	    return _Up(unexpect, _M_unex);
 	}
 
       template<typename _Fn> requires is_constructible_v<_Er, _Er>
@@ -877,9 +875,9 @@ namespace __expected
 	  static_assert(is_same_v<typename _Up::error_type, _Er>);
 
 	  if (has_value())
-	    return std::__invoke(std::forward<_Fn>(__f), std::move(value()));
+	    return std::__invoke(std::forward<_Fn>(__f), std::move(_M_val));
 	  else
-	    return _Up(unexpect, std::move(error()));
+	    return _Up(unexpect, std::move(_M_unex));
 	}
 
 
@@ -892,9 +890,9 @@ namespace __expected
 	  static_assert(is_same_v<typename _Up::error_type, _Er>);
 
 	  if (has_value())
-	    return std::__invoke(std::forward<_Fn>(__f), std::move(value()));
+	    return std::__invoke(std::forward<_Fn>(__f), std::move(_M_val));
 	  else
-	    return _Up(unexpect, std::move(error()));
+	    return _Up(unexpect, std::move(_M_unex));
 	}
 
       template<typename _Fn> requires is_constructible_v<_Tp, _Tp&>
@@ -906,9 +904,9 @@ namespace __expected
 	  static_assert(is_same_v<typename _Gr::value_type, _Tp>);
 
 	  if (has_value())
-	    return _Gr(in_place, value());
+	    return _Gr(in_place, _M_val);
 	  else
-	    return std::__invoke(std::forward<_Fn>(__f), error());
+	    return std::__invoke(std::forward<_Fn>(__f), _M_unex);
 	}
 
       template<typename _Fn> requires is_constructible_v<_Tp, const _Tp&>
@@ -920,9 +918,9 @@ namespace __expected
 	  static_assert(is_same_v<typename _Gr::value_type, _Tp>);
 
 	  if (has_value())
-	    return _Gr(in_place, value());
+	    return _Gr(in_place, _M_val);
 	  else
-	    return std::__invoke(std::forward<_Fn>(__f), error());
+	    return std::__invoke(std::forward<_Fn>(__f), _M_unex);
 	}
 
 
@@ -935,9 +933,9 @@ namespace __expected
 	  static_assert(is_same_v<typename _Gr::value_type, _Tp>);
 
 	  if (has_value())
-	    return _Gr(in_place, std::move(value()));
+	    return _Gr(in_place, std::move(_M_val));
 	  else
-	    return std::__invoke(std::forward<_Fn>(__f), std::move(error()));
+	    return std::__invoke(std::forward<_Fn>(__f), std::move(_M_unex));
 	}
 
       template<typename _Fn> requires is_constructible_v<_Tp, const _Tp>
@@ -949,9 +947,9 @@ namespace __expected
 	  static_assert(is_same_v<typename _Gr::value_type, _Tp>);
 
 	  if (has_value())
-	    return _Gr(in_place, std::move(value()));
+	    return _Gr(in_place, std::move(_M_val));
 	  else
-	    return std::__invoke(std::forward<_Fn>(__f), std::move(error()));
+	    return std::__invoke(std::forward<_Fn>(__f), std::move(_M_unex));
 	}
 
       template<typename _Fn> requires is_constructible_v<_Er, _Er&>
@@ -967,7 +965,7 @@ namespace __expected
 					       _M_val);
 			});
 	  else
-	    return _Res(unexpect, std::move(error()));
+	    return _Res(unexpect, _M_unex);
 	}
 
       template<typename _Fn> requires is_constructible_v<_Er, const _Er&>
@@ -983,7 +981,7 @@ namespace __expected
 					       _M_val);
 			});
 	  else
-	    return _Res(unexpect, std::move(error()));
+	    return _Res(unexpect, _M_unex);
 	}
 
       template<typename _Fn> requires is_constructible_v<_Er, _Er>
@@ -999,7 +997,7 @@ namespace __expected
 					       std::move(_M_val));
 			});
 	  else
-	    return _Res(unexpect, std::move(error()));
+	    return _Res(unexpect, std::move(_M_unex));
 	}
 
       template<typename _Fn> requires is_constructible_v<_Er, const _Er>
@@ -1015,7 +1013,7 @@ namespace __expected
 					       std::move(_M_val));
 			});
 	  else
-	    return _Res(unexpect, std::move(error()));
+	    return _Res(unexpect, std::move(_M_unex));
 	}
 
       template<typename _Fn> requires is_constructible_v<_Tp, _Tp&>
@@ -1026,7 +1024,7 @@ namespace __expected
 	  using _Res = expected<_Tp, _Gr>;
 
 	  if (has_value())
-	    return _Res(in_place, value());
+	    return _Res(in_place, _M_val);
 	  else
 	    return _Res(__unexpect_inv{}, [&]() {
 			  return std::__invoke(std::forward<_Fn>(__f),
@@ -1042,7 +1040,7 @@ namespace __expected
 	  using _Res = expected<_Tp, _Gr>;
 
 	  if (has_value())
-	    return _Res(in_place, value());
+	    return _Res(in_place, _M_val);
 	  else
 	    return _Res(__unexpect_inv{}, [&]() {
 			  return std::__invoke(std::forward<_Fn>(__f),
@@ -1058,7 +1056,7 @@ namespace __expected
 	  using _Res = expected<_Tp, _Gr>;
 
 	  if (has_value())
-	    return _Res(in_place, std::move(value()));
+	    return _Res(in_place, std::move(_M_val));
 	  else
 	    return _Res(__unexpect_inv{}, [&]() {
 			  return std::__invoke(std::forward<_Fn>(__f),
@@ -1074,7 +1072,7 @@ namespace __expected
 	  using _Res = expected<_Tp, _Gr>;
 
 	  if (has_value())
-	    return _Res(in_place, std::move(value()));
+	    return _Res(in_place, std::move(_M_val));
 	  else
 	    return _Res(__unexpect_inv{}, [&]() {
 			  return std::__invoke(std::forward<_Fn>(__f),
@@ -1530,7 +1528,7 @@ namespace __expected
 	  if (has_value())
 	    return std::__invoke(std::forward<_Fn>(__f));
 	  else
-	    return _Up(unexpect, error());
+	    return _Up(unexpect, _M_unex);
 	}
 
      template<typename _Fn> requires is_constructible_v<_Er, const _Er&>
@@ -1544,7 +1542,7 @@ namespace __expected
 	  if (has_value())
 	    return std::__invoke(std::forward<_Fn>(__f));
 	  else
-	    return _Up(unexpect, error());
+	    return _Up(unexpect, _M_unex);
 	}
 
       template<typename _Fn> requires is_constructible_v<_Er, _Er>
@@ -1558,7 +1556,7 @@ namespace __expected
 	  if (has_value())
 	    return std::__invoke(std::forward<_Fn>(__f));
 	  else
-	    return _Up(unexpect, std::move(error()));
+	    return _Up(unexpect, std::move(_M_unex));
 	}
 
        template<typename _Fn> requires is_constructible_v<_Er, const _Er>
@@ -1572,7 +1570,7 @@ namespace __expected
 	  if (has_value())
 	    return std::__invoke(std::forward<_Fn>(__f));
 	  else
-	    return _Up(unexpect, std::move(error()));
+	    return _Up(unexpect, std::move(_M_unex));
 	}
 
       template<typename _Fn>
@@ -1586,7 +1584,7 @@ namespace __expected
 	  if (has_value())
 	    return _Gr();
 	  else
-	    return std::__invoke(std::forward<_Fn>(__f), error());
+	    return std::__invoke(std::forward<_Fn>(__f), _M_unex);
 	}
 
       template<typename _Fn>
@@ -1600,7 +1598,7 @@ namespace __expected
 	  if (has_value())
 	    return _Gr();
 	  else
-	    return std::__invoke(std::forward<_Fn>(__f), error());
+	    return std::__invoke(std::forward<_Fn>(__f), _M_unex);
 	}
 
       template<typename _Fn>
@@ -1614,7 +1612,7 @@ namespace __expected
 	  if (has_value())
 	    return _Gr();
 	  else
-	    return std::__invoke(std::forward<_Fn>(__f), std::move(error()));
+	    return std::__invoke(std::forward<_Fn>(__f), std::move(_M_unex));
 	}
 
       template<typename _Fn>
@@ -1628,7 +1626,7 @@ namespace __expected
 	  if (has_value())
 	    return _Gr();
 	  else
-	    return std::__invoke(std::forward<_Fn>(__f), std::move(error()));
+	    return std::__invoke(std::forward<_Fn>(__f), std::move(_M_unex));
 	}
 
       template<typename _Fn> requires is_constructible_v<_Er, _Er&>
@@ -1641,7 +1639,7 @@ namespace __expected
 	  if (has_value())
 	    return _Res(__in_place_inv{}, std::forward<_Fn>(__f));
 	  else
-	    return _Res(unexpect, error());
+	    return _Res(unexpect, _M_unex);
 	}
 
       template<typename _Fn> requires is_constructible_v<_Er, const _Er&>
@@ -1654,7 +1652,7 @@ namespace __expected
 	  if (has_value())
 	    return _Res(__in_place_inv{}, std::forward<_Fn>(__f));
 	  else
-	    return _Res(unexpect, error());
+	    return _Res(unexpect, _M_unex);
 	}
 
       template<typename _Fn> requires is_constructible_v<_Er, _Er>
@@ -1667,7 +1665,7 @@ namespace __expected
 	  if (has_value())
 	    return _Res(__in_place_inv{}, std::forward<_Fn>(__f));
 	  else
-	    return _Res(unexpect, std::move(error()));
+	    return _Res(unexpect, std::move(_M_unex));
 	}
 
       template<typename _Fn> requires is_constructible_v<_Er, const _Er>
@@ -1680,7 +1678,7 @@ namespace __expected
 	  if (has_value())
 	    return _Res(__in_place_inv{}, std::forward<_Fn>(__f));
 	  else
-	    return _Res(unexpect, std::move(error()));
+	    return _Res(unexpect, std::move(_M_unex));
 	}
 
       template<typename _Fn>
diff --git a/libstdc++-v3/testsuite/20_util/expected/lwg3877.cc b/libstdc++-v3/testsuite/20_util/expected/lwg3877.cc
index 556d8d5dc9e..876275bfdb0 100644
--- a/libstdc++-v3/testsuite/20_util/expected/lwg3877.cc
+++ b/libstdc++-v3/testsuite/20_util/expected/lwg3877.cc
@@ -1,6 +1,8 @@
 // { dg-options "-std=gnu++23" }
 // { dg-do compile { target c++23 } }
 
+// LWG 3877. Incorrect constraints on const-qualified monadic overloads
+
 #include <expected>
 
 struct T1
@@ -20,45 +22,134 @@ struct T3
   T3(const T3&&) { }
 };
 
+template<typename Exp, typename F>
+concept Has_and_then = requires(Exp&& exp, F f) {
+  std::forward<Exp>(exp).and_then(f);
+};
+
+using ExpiT1 = std::expected<int, T1>;
+static_assert( Has_and_then<ExpiT1&, ExpiT1(int)> );
+static_assert( Has_and_then<const ExpiT1&, ExpiT1(int)> );
+static_assert( Has_and_then<ExpiT1&&, ExpiT1(int)> );
+static_assert( Has_and_then<const ExpiT1&&, ExpiT1(int)> );
+
+using ExpiT2 = std::expected<int, T2>;
+static_assert( !Has_and_then<ExpiT2&, ExpiT2(int)> );
+static_assert( !Has_and_then<const ExpiT2&, ExpiT2(int)> );
+static_assert( !Has_and_then<ExpiT2&&, ExpiT2(int)> );
+static_assert( !Has_and_then<const ExpiT2&&, ExpiT2(int)> );
+
+using ExpiT3 = std::expected<int, T3>;
+static_assert( Has_and_then<ExpiT3&, ExpiT3(int)> );
+static_assert( !Has_and_then<const ExpiT3&, ExpiT3(int)> );
+static_assert( Has_and_then<ExpiT3&&, ExpiT3(int)> ); // uses and_then(F) const &&
+static_assert( Has_and_then<const ExpiT3&&, ExpiT3(int)> );
+
 template<typename Exp, typename F>
 concept Has_or_else = requires(Exp&& exp, F f) {
   std::forward<Exp>(exp).or_else(f);
 };
 
-using E1 = std::expected<T1, int>;
-static_assert( Has_or_else<E1&, E1(int)> );
-static_assert( Has_or_else<const E1&, E1(int)> );
-static_assert( Has_or_else<E1&&, E1(int)> );
-static_assert( Has_or_else<const E1&&, E1(int)> );
+using ExpT1i = std::expected<T1, int>;
+static_assert( Has_or_else<ExpT1i&, ExpT1i(int)> );
+static_assert( Has_or_else<const ExpT1i&, ExpT1i(int)> );
+static_assert( Has_or_else<ExpT1i&&, ExpT1i(int)> );
+static_assert( Has_or_else<const ExpT1i&&, ExpT1i(int)> );
+
+using ExpT2i = std::expected<T2, int>;
+static_assert( !Has_or_else<ExpT2i&, ExpT2i(int)> );
+static_assert( !Has_or_else<const ExpT2i&, ExpT2i(int)> );
+static_assert( !Has_or_else<ExpT2i&&, ExpT2i(int)> );
+static_assert( !Has_or_else<const ExpT2i&&, ExpT2i(int)> );
+
+using ExpT3i = std::expected<T3, int>;
+static_assert( Has_or_else<ExpT3i&, ExpT3i(int)> );
+static_assert( !Has_or_else<const ExpT3i&, ExpT3i(int)> );
+static_assert( Has_or_else<ExpT3i&&, ExpT3i(int)> ); // uses or_else(F) const &&
+static_assert( Has_or_else<const ExpT3i&&, ExpT3i(int)> );
+
+template<typename Exp, typename F>
+concept Has_transform = requires(Exp&& exp, F f) {
+  std::forward<Exp>(exp).transform(f);
+};
+
+static_assert( Has_transform<ExpiT1&, int(int)> );
+static_assert( Has_transform<const ExpiT1&, int(int)> );
+static_assert( Has_transform<ExpiT1&&, int(int)> );
+static_assert( Has_transform<const ExpiT1&&, int(int)> );
 
-using E2 = std::expected<T2, int>;
-static_assert( !Has_or_else<E2&, E2(int)> );
-static_assert( !Has_or_else<const E2&, E2(int)> );
-static_assert( !Has_or_else<E2&&, E2(int)> );
-static_assert( !Has_or_else<const E2&&, E2(int)> );
+static_assert( !Has_transform<ExpiT2&, int(int)> );
+static_assert( !Has_transform<const ExpiT2&, int(int)> );
+static_assert( !Has_transform<ExpiT2&&, int(int)> );
+static_assert( !Has_transform<const ExpiT2&&, int(int)> );
 
-using E3 = std::expected<T3, int>;
-static_assert( Has_or_else<E3&, E3(int)> );
-static_assert( !Has_or_else<const E3&, E3(int)> );
-static_assert( Has_or_else<E3&&, E3(int)> ); // uses or_else(F) const &&
-static_assert( Has_or_else<const E3&&, E3(int)> );
+static_assert( Has_transform<ExpiT3&, int(int)> );
+static_assert( !Has_transform<const ExpiT3&, int(int)> );
+static_assert( Has_transform<ExpiT3&&, int(int)> ); // uses transform(F) const &&
+static_assert( Has_transform<const ExpiT3&&, int(int)> );
 
 template<typename Exp, typename F>
 concept Has_transform_error = requires(Exp&& exp, F f) {
   std::forward<Exp>(exp).transform_error(f);
 };
 
-static_assert( Has_transform_error<E1&, int(int)> );
-static_assert( Has_transform_error<const E1&, int(int)> );
-static_assert( Has_transform_error<E1&&, int(int)> );
-static_assert( Has_transform_error<const E1&&, int(int)> );
+static_assert( Has_transform_error<ExpT1i&, int(int)> );
+static_assert( Has_transform_error<const ExpT1i&, int(int)> );
+static_assert( Has_transform_error<ExpT1i&&, int(int)> );
+static_assert( Has_transform_error<const ExpT1i&&, int(int)> );
+
+static_assert( !Has_transform_error<ExpT2i&, int(int)> );
+static_assert( !Has_transform_error<const ExpT2i&, int(int)> );
+static_assert( !Has_transform_error<ExpT2i&&, int(int)> );
+static_assert( !Has_transform_error<const ExpT2i&&, int(int)> );
+
+static_assert( Has_transform_error<ExpT3i&, int(int)> );
+static_assert( !Has_transform_error<const ExpT3i&, int(int)> );
+static_assert( Has_transform_error<ExpT3i&&, int(int)> ); // uses transform_error(F) const &&
+static_assert( Has_transform_error<const ExpT3i&&, int(int)> );
+
+// std::expected<cv void, E>
+
+using ExpvT1 = std::expected<void, T1>;
+static_assert( Has_and_then<ExpvT1&, ExpvT1()> );
+static_assert( Has_and_then<const ExpvT1&, ExpvT1()> );
+static_assert( Has_and_then<ExpvT1&&, ExpvT1()> );
+static_assert( Has_and_then<const ExpvT1&&, ExpvT1()> );
+
+using ExpvT2 = std::expected<void, T2>;
+static_assert( !Has_and_then<ExpvT2&, ExpvT2()> );
+static_assert( !Has_and_then<const ExpvT2&, ExpvT2()> );
+static_assert( !Has_and_then<ExpvT2&&, ExpvT2()> );
+static_assert( !Has_and_then<const ExpvT2&&, ExpvT2()> );
+
+using ExpvT3 = std::expected<void, T3>;
+static_assert( Has_and_then<ExpvT3&, ExpvT3()> );
+static_assert( !Has_and_then<const ExpvT3&, ExpvT3()> );
+static_assert( Has_and_then<ExpvT3&&, ExpvT3()> ); // uses and_then(F) const &&
+static_assert( Has_and_then<const ExpvT3&&, ExpvT3()> );
+
+using Expvi = std::expected<void, int>;
+static_assert( Has_or_else<Expvi&, Expvi(int)> );
+static_assert( Has_or_else<const Expvi&, Expvi(int)> );
+static_assert( Has_or_else<Expvi&&, Expvi(int)> );
+static_assert( Has_or_else<const Expvi&&, Expvi(int)> );
+
+static_assert( Has_transform<ExpvT1&, int()> );
+static_assert( Has_transform<const ExpvT1&, int()> );
+static_assert( Has_transform<ExpvT1&&, int()> );
+static_assert( Has_transform<const ExpvT1&&, int()> );
+
+static_assert( !Has_transform<ExpvT2&, int()> );
+static_assert( !Has_transform<const ExpvT2&, int()> );
+static_assert( !Has_transform<ExpvT2&&, int()> );
+static_assert( !Has_transform<const ExpvT2&&, int()> );
 
-static_assert( !Has_transform_error<E2&, int(int)> );
-static_assert( !Has_transform_error<const E2&, int(int)> );
-static_assert( !Has_transform_error<E2&&, int(int)> );
-static_assert( !Has_transform_error<const E2&&, int(int)> );
+static_assert( Has_transform<ExpvT3&, int()> );
+static_assert( !Has_transform<const ExpvT3&, int()> );
+static_assert( Has_transform<ExpvT3&&, int()> ); // uses transform(F) const &&
+static_assert( Has_transform<const ExpvT3&&, int()> );
 
-static_assert( Has_transform_error<E3&, int(int)> );
-static_assert( !Has_transform_error<const E3&, int(int)> );
-static_assert( Has_transform_error<E3&&, int(int)> ); // uses transform_error(F) const &&
-static_assert( Has_transform_error<const E3&&, int(int)> );
+static_assert( Has_transform_error<Expvi&, int(int)> );
+static_assert( Has_transform_error<const Expvi&, int(int)> );
+static_assert( Has_transform_error<Expvi&&, int(int)> );
+static_assert( Has_transform_error<const Expvi&&, int(int)> );
diff --git a/libstdc++-v3/testsuite/20_util/expected/lwg3938.cc b/libstdc++-v3/testsuite/20_util/expected/lwg3938.cc
new file mode 100644
index 00000000000..c7e3758a902
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/expected/lwg3938.cc
@@ -0,0 +1,142 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do compile { target c++23 } }
+
+// LWG 3938. Cannot use std::expected monadic ops with move-only error_type
+
+#include <expected>
+#include <testsuite_hooks.h>
+
+struct MoveOnly {
+  constexpr MoveOnly(int i) : i(i) { }
+  constexpr MoveOnly(MoveOnly&&) = default;
+  constexpr MoveOnly(const MoveOnly&& mo) : i(mo.i) { }
+  constexpr bool operator==(const MoveOnly&) const = default;
+  int i;
+};
+
+constexpr bool
+test_and_then()
+{
+  auto fun = [](int i) { return std::expected<long, MoveOnly>(i); };
+
+  std::expected<int, MoveOnly> good(9);
+  std::expected<long, MoveOnly> e1 = std::move(good).and_then(fun);
+  VERIFY( e1 == good );
+  const auto& gooder = good;
+  std::expected<long, MoveOnly> e2 = std::move(gooder).and_then(fun);
+  VERIFY( e2 == gooder );
+
+  std::expected<int, MoveOnly> bad(std::unexpect, 99);
+  std::expected<long, MoveOnly> e3 = std::move(bad).and_then(fun);
+  VERIFY( e3 == bad );
+  const auto& badder = bad;
+  std::expected<long, MoveOnly> e4 = std::move(badder).and_then(fun);
+  VERIFY( e4 == badder );
+
+  auto vun = [] { return std::expected<long, MoveOnly>(1); };
+  std::expected<void, MoveOnly> vud;
+  std::expected<long, MoveOnly> e5 = std::move(vud).and_then(vun);
+  VERIFY( *e5 == 1 );
+  const auto& vudder = vud;
+  std::expected<long, MoveOnly> e6 = std::move(vudder).and_then(vun);
+  VERIFY( *e6 == 1 );
+
+  return true;
+}
+
+static_assert( test_and_then() );
+
+constexpr bool
+test_or_else()
+{
+  auto fun = [](const MoveOnly&& mo) { return std::expected<int, long>(mo.i); };
+
+  std::expected<int, MoveOnly> good(9);
+  std::expected<int, long> e1 = std::move(good).or_else(fun);
+  VERIFY( e1 == good );
+  const auto& gooder = good;
+  std::expected<int, long> e2 = std::move(gooder).or_else(fun);
+  VERIFY( e2 == gooder );
+
+  std::expected<int, MoveOnly> bad(std::unexpect, 99);
+  std::expected<int, long> e3 = std::move(bad).or_else(fun);
+  VERIFY( *e3 == 99 );
+  const auto& badder = bad;
+  std::expected<int, long> e4 = std::move(badder).or_else(fun);
+  VERIFY( *e4 == 99 );
+
+  auto vun = [](const MoveOnly&& mo) { return std::expected<void, long>{}; };
+  std::expected<void, MoveOnly> vud;
+  std::expected<void, long> e5 = std::move(vud).or_else(vun);
+  VERIFY( e5.has_value() );
+  const auto& vudder = vud;
+  std::expected<void, long> e6 = std::move(vudder).or_else(vun);
+  VERIFY( e6.has_value() );
+
+  return true;
+}
+
+static_assert( test_or_else() );
+
+constexpr bool
+test_transform()
+{
+  auto fun = [](int i) { return (long)i; };
+
+  std::expected<int, MoveOnly> good(9);
+  std::expected<long, MoveOnly> e1 = std::move(good).transform(fun);
+  VERIFY( e1 == good );
+  const auto& gooder = good;
+  std::expected<long, MoveOnly> e2 = std::move(gooder).transform(fun);
+  VERIFY( e2 == gooder );
+
+  std::expected<int, MoveOnly> bad(std::unexpect, 99);
+  std::expected<long, MoveOnly> e3 = std::move(bad).transform(fun);
+  VERIFY( e3 == bad );
+  const auto& badder = bad;
+  std::expected<long, MoveOnly> e4 = std::move(badder).transform(fun);
+  VERIFY( e4 == badder );
+
+  auto vun = []() { return 1L; };
+  std::expected<void, MoveOnly> vud;
+  std::expected<long, MoveOnly> e5 = std::move(vud).transform(vun);
+  VERIFY( *e5 == 1 );
+  const auto& vudder = vud;
+  std::expected<long, MoveOnly> e6 = std::move(vudder).transform(vun);
+  VERIFY( *e6 == 1 );
+
+  return true;
+}
+
+static_assert( test_transform() );
+
+constexpr bool
+test_transform_error()
+{
+  auto fun = [](const MoveOnly&& mo) { return (long)mo.i; };
+
+  std::expected<int, MoveOnly> good(9);
+  std::expected<int, long> e1 = std::move(good).transform_error(fun);
+  VERIFY( e1 == good );
+  const auto& gooder = good;
+  std::expected<int, long> e2 = std::move(gooder).transform_error(fun);
+  VERIFY( e2 == gooder );
+
+  std::expected<int, MoveOnly> bad(std::unexpect, 99);
+  std::expected<int, long> e3 = std::move(bad).transform_error(fun);
+  VERIFY( e3.error() == 99 );
+  const auto& badder = bad;
+  std::expected<int, long> e4 = std::move(badder).transform_error(fun);
+  VERIFY( e4.error() == 99 );
+
+  std::expected<void, MoveOnly> vud(std::unexpect, 1);
+  std::expected<void, long> e5 = std::move(vud).transform_error(fun);
+  VERIFY( e5.error() == 1 );
+  const auto& vudder = vud;
+  std::expected<void, long> e6 = std::move(vudder).transform_error(fun);
+  VERIFY( e6.error() == 1 );
+
+  return true;
+}
+
+static_assert( test_transform_error() );

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2023-06-06 22:58 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-06-06 22:58 [gcc r13-7423] libstdc++: Do not use std::expected::value() in monadic ops (LWG 3938) Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).