public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r13-4754] libstdc++: Add monadic operations to std::expected for C++23 (P2505R5)
@ 2022-12-16 21:00 Jonathan Wakely
  0 siblings, 0 replies; only message in thread
From: Jonathan Wakely @ 2022-12-16 21:00 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:8d9e2776a6d2bfe6662f79182e85ab79f3cc9522

commit r13-4754-g8d9e2776a6d2bfe6662f79182e85ab79f3cc9522
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Dec 15 15:47:38 2022 +0000

    libstdc++: Add monadic operations to std::expected for C++23 (P2505R5)
    
    This was approved for C++23 last month in Kona.
    
    libstdc++-v3/ChangeLog:
    
            * include/std/expected (expected): Add monadic operations.
            (expected<void, E>): Likewise.
            * include/std/version (__cpp_lib_expected): Bump value.
            * testsuite/20_util/expected/synopsis.cc: Adjust expected macro
            value.
            * testsuite/20_util/expected/version.cc: Likewise.
            * testsuite/20_util/expected/illformed_neg.cc: Prune additional
            errors from ill-formed monadic operations.
            * testsuite/20_util/expected/observers.cc: Check error_or.
            * testsuite/20_util/expected/monadic.cc: New test.

Diff:
---
 libstdc++-v3/include/std/expected                  | 579 ++++++++++++++++++++-
 libstdc++-v3/include/std/version                   |   2 +-
 .../testsuite/20_util/expected/illformed_neg.cc    |   1 +
 libstdc++-v3/testsuite/20_util/expected/monadic.cc | 280 ++++++++++
 .../testsuite/20_util/expected/observers.cc        |  20 +
 .../testsuite/20_util/expected/synopsis.cc         |   2 +-
 libstdc++-v3/testsuite/20_util/expected/version.cc |   2 +-
 7 files changed, 882 insertions(+), 4 deletions(-)

diff --git a/libstdc++-v3/include/std/expected b/libstdc++-v3/include/std/expected
index 2fe25a90d2d..555779581f2 100644
--- a/libstdc++-v3/include/std/expected
+++ b/libstdc++-v3/include/std/expected
@@ -35,6 +35,7 @@
 
 #include <initializer_list>
 #include <bits/exception.h>	// exception
+#include <bits/invoke.h>	// __invoke
 #include <bits/stl_construct.h>	// construct_at
 #include <bits/utility.h>	// in_place_t
 
@@ -49,7 +50,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    * @{
    */
 
-#define __cpp_lib_expected 202202L
+#define __cpp_lib_expected 202211L
 
   /// Discriminated union that holds an expected value or an error value.
   /**
@@ -151,11 +152,20 @@ namespace __expected
   template<typename _Tp>
     constexpr bool __is_unexpected<unexpected<_Tp>> = true;
 
+  template<typename _Fn, typename _Tp>
+    using __result = remove_cvref_t<invoke_result_t<_Fn&&, _Tp&&>>;
+  template<typename _Fn>
+    using __result0 = remove_cvref_t<invoke_result_t<_Fn&&>>;
+
   template<typename _Er>
     concept __can_be_unexpected
       = is_object_v<_Er> && (!is_array_v<_Er>)
 	  && (!__expected::__is_unexpected<_Er>)
 	  && (!is_const_v<_Er>) && (!is_volatile_v<_Er>);
+
+  // Tag types for in-place construction from an invocation result.
+  struct __in_place_inv { };
+  struct __unexpect_inv { };
 }
 /// @endcond
 
@@ -334,6 +344,14 @@ namespace __expected
 		   __not_<is_convertible<_Err, _Er>>
 		  >;
 
+      template<typename _Up>
+	static constexpr bool __same_val
+	  = is_same_v<typename _Up::value_type, _Tp>;
+
+      template<typename _Up>
+	static constexpr bool __same_err
+	  = is_same_v<typename _Up::error_type, _Er>;
+
     public:
       using value_type = _Tp;
       using error_type = _Er;
@@ -791,6 +809,274 @@ namespace __expected
 	  return static_cast<_Tp>(std::forward<_Up>(__v));
 	}
 
+      template<typename _Gr = _Er>
+	constexpr _Er
+	error_or(_Gr&& __e) const&
+	{
+	  static_assert( is_copy_constructible_v<_Er> );
+	  static_assert( is_convertible_v<_Gr, _Er> );
+
+	  if (_M_has_value)
+	    return std::forward<_Gr>(__e);
+	  return _M_unex;
+	}
+
+      template<typename _Gr = _Er>
+	constexpr _Er
+	error_or(_Gr&& __e) &&
+	{
+	  static_assert( is_move_constructible_v<_Er> );
+	  static_assert( is_convertible_v<_Gr, _Er> );
+
+	  if (_M_has_value)
+	    return std::forward<_Gr>(__e);
+	  return std::move(_M_unex);
+	}
+
+      // monadic operations
+
+      template<typename _Fn> requires is_copy_constructible_v<_Er>
+	constexpr auto
+	and_then(_Fn&& __f) &
+	{
+	  using _Up = __expected::__result<_Fn, _Tp&>;
+	  static_assert(__expected::__is_expected<_Up>);
+	  static_assert(is_same_v<typename _Up::error_type, _Er>);
+
+	  if (has_value())
+	    return std::__invoke(std::forward<_Fn>(__f), value());
+	  else
+	    return _Up(unexpect, error());
+	}
+
+      template<typename _Fn> requires is_copy_constructible_v<_Er>
+	constexpr auto
+	and_then(_Fn&& __f) const &
+	{
+	  using _Up = __expected::__result<_Fn, const _Tp&>;
+	  static_assert(__expected::__is_expected<_Up>);
+	  static_assert(is_same_v<typename _Up::error_type, _Er>);
+
+	  if (has_value())
+	    return std::__invoke(std::forward<_Fn>(__f), value());
+	  else
+	    return _Up(unexpect, error());
+	}
+
+      template<typename _Fn> requires is_move_constructible_v<_Er>
+	constexpr auto
+	and_then(_Fn&& __f) &&
+	{
+	  using _Up = __expected::__result<_Fn, _Tp&&>;
+	  static_assert(__expected::__is_expected<_Up>);
+	  static_assert(is_same_v<typename _Up::error_type, _Er>);
+
+	  if (has_value())
+	    return std::__invoke(std::forward<_Fn>(__f), std::move(value()));
+	  else
+	    return _Up(unexpect, std::move(error()));
+	}
+
+
+      template<typename _Fn> requires is_move_constructible_v<_Er>
+	constexpr auto
+	and_then(_Fn&& __f) const &&
+	{
+	  using _Up = __expected::__result<_Fn, const _Tp&&>;
+	  static_assert(__expected::__is_expected<_Up>);
+	  static_assert(is_same_v<typename _Up::error_type, _Er>);
+
+	  if (has_value())
+	    return std::__invoke(std::forward<_Fn>(__f), std::move(value()));
+	  else
+	    return _Up(unexpect, std::move(error()));
+	}
+
+      template<typename _Fn> requires is_copy_constructible_v<_Er>
+	constexpr auto
+	or_else(_Fn&& __f) &
+	{
+	  using _Gr = __expected::__result<_Fn, _Er&>;
+	  static_assert(__expected::__is_expected<_Gr>);
+	  static_assert(is_same_v<typename _Gr::value_type, _Tp>);
+
+	  if (has_value())
+	    return _Gr(in_place, value());
+	  else
+	    return std::__invoke(std::forward<_Fn>(__f), error());
+	}
+
+      template<typename _Fn> requires is_copy_constructible_v<_Er>
+	constexpr auto
+	or_else(_Fn&& __f) const &
+	{
+	  using _Gr = __expected::__result<_Fn, const _Er&>;
+	  static_assert(__expected::__is_expected<_Gr>);
+	  static_assert(is_same_v<typename _Gr::value_type, _Tp>);
+
+	  if (has_value())
+	    return _Gr(in_place, value());
+	  else
+	    return std::__invoke(std::forward<_Fn>(__f), error());
+	}
+
+
+      template<typename _Fn> requires is_move_constructible_v<_Er>
+	constexpr auto
+	or_else(_Fn&& __f) &&
+	{
+	  using _Gr = __expected::__result<_Fn, _Er&&>;
+	  static_assert(__expected::__is_expected<_Gr>);
+	  static_assert(is_same_v<typename _Gr::value_type, _Tp>);
+
+	  if (has_value())
+	    return _Gr(in_place, std::move(value()));
+	  else
+	    return std::__invoke(std::forward<_Fn>(__f), std::move(error()));
+	}
+
+      template<typename _Fn> requires is_move_constructible_v<_Er>
+	constexpr auto
+	or_else(_Fn&& __f) const &&
+	{
+	  using _Gr = __expected::__result<_Fn, const _Er&&>;
+	  static_assert(__expected::__is_expected<_Gr>);
+	  static_assert(is_same_v<typename _Gr::value_type, _Tp>);
+
+	  if (has_value())
+	    return _Gr(in_place, std::move(value()));
+	  else
+	    return std::__invoke(std::forward<_Fn>(__f), std::move(error()));
+	}
+
+      template<typename _Fn> requires is_copy_constructible_v<_Er>
+	constexpr auto
+	transform(_Fn&& __f) &
+	{
+	  using _Up = __expected::__result<_Fn, _Tp&>;
+	  using _Res = expected<_Up, _Er>;
+
+	  if (has_value())
+	    return _Res(__in_place_inv{}, [&]() {
+			  return std::__invoke(std::forward<_Fn>(__f),
+					       _M_val);
+			});
+	  else
+	    return _Res(unexpect, std::move(error()));
+	}
+
+      template<typename _Fn> requires is_copy_constructible_v<_Er>
+	constexpr auto
+	transform(_Fn&& __f) const &
+	{
+	  using _Up = __expected::__result<_Fn, const _Tp&>;
+	  using _Res = expected<_Up, _Er>;
+
+	  if (has_value())
+	    return _Res(__in_place_inv{}, [&]() {
+			  return std::__invoke(std::forward<_Fn>(__f),
+					       _M_val);
+			});
+	  else
+	    return _Res(unexpect, std::move(error()));
+	}
+
+      template<typename _Fn> requires is_move_constructible_v<_Er>
+	constexpr auto
+	transform(_Fn&& __f) &&
+	{
+	  using _Up = __expected::__result<_Fn, _Tp>;
+	  using _Res = expected<_Up, _Er>;
+
+	  if (has_value())
+	    return _Res(__in_place_inv{}, [&]() {
+			  return std::__invoke(std::forward<_Fn>(__f),
+					       std::move(_M_val));
+			});
+	  else
+	    return _Res(unexpect, std::move(error()));
+	}
+
+      template<typename _Fn> requires is_move_constructible_v<_Er>
+	constexpr auto
+	transform(_Fn&& __f) const &&
+	{
+	  using _Up = __expected::__result<_Fn, const _Tp>;
+	  using _Res = expected<_Up, _Er>;
+
+	  if (has_value())
+	    return _Res(__in_place_inv{}, [&]() {
+			  return std::__invoke(std::forward<_Fn>(__f),
+					       std::move(_M_val));
+			});
+	  else
+	    return _Res(unexpect, std::move(error()));
+	}
+
+      template<typename _Fn> requires is_copy_constructible_v<_Tp>
+	constexpr auto
+	transform_error(_Fn&& __f) &
+	{
+	  using _Gr = __expected::__result<_Fn, _Er&>;
+	  using _Res = expected<_Tp, _Gr>;
+
+	  if (has_value())
+	    return _Res(in_place, value());
+	  else
+	    return _Res(__unexpect_inv{}, [&]() {
+			  return std::__invoke(std::forward<_Fn>(__f),
+					       _M_unex);
+			});
+	}
+
+      template<typename _Fn> requires is_copy_constructible_v<_Tp>
+	constexpr auto
+	transform_error(_Fn&& __f) const &
+	{
+	  using _Gr = __expected::__result<_Fn, const _Er&>;
+	  using _Res = expected<_Tp, _Gr>;
+
+	  if (has_value())
+	    return _Res(in_place, value());
+	  else
+	    return _Res(__unexpect_inv{}, [&]() {
+			  return std::__invoke(std::forward<_Fn>(__f),
+					       _M_unex);
+			});
+	}
+
+      template<typename _Fn> requires is_move_constructible_v<_Tp>
+	constexpr auto
+	transform_error(_Fn&& __f) &&
+	{
+	  using _Gr = __expected::__result<_Fn, _Er&&>;
+	  using _Res = expected<_Tp, _Gr>;
+
+	  if (has_value())
+	    return _Res(in_place, std::move(value()));
+	  else
+	    return _Res(__unexpect_inv{}, [&]() {
+			  return std::__invoke(std::forward<_Fn>(__f),
+					       std::move(_M_unex));
+			});
+	}
+
+      template<typename _Fn> requires is_move_constructible_v<_Tp>
+	constexpr auto
+	transform_error(_Fn&& __f) const &&
+	{
+	  using _Gr = __expected::__result<_Fn, const _Er&&>;
+	  using _Res = expected<_Tp, _Gr>;
+
+	  if (has_value())
+	    return _Res(in_place, std::move(value()));
+	  else
+	    return _Res(__unexpect_inv{}, [&]() {
+			  return std::__invoke(std::forward<_Fn>(__f),
+					       std::move(_M_unex));
+			});
+	}
+
       // equality operators
 
       template<typename _Up, typename _Er2>
@@ -888,6 +1174,21 @@ namespace __expected
 	  }
       }
 
+      using __in_place_inv = __expected::__in_place_inv;
+      using __unexpect_inv = __expected::__unexpect_inv;
+
+      template<typename _Fn>
+	explicit constexpr
+	expected(__in_place_inv, _Fn&& __fn)
+	: _M_val(std::forward<_Fn>(__fn)()), _M_has_value(true)
+	{ }
+
+      template<typename _Fn>
+	explicit constexpr
+	expected(__unexpect_inv, _Fn&& __fn)
+	: _M_unex(std::forward<_Fn>(__fn)()), _M_has_value(false)
+	{ }
+
       union {
 	_Tp _M_val;
 	_Er _M_unex;
@@ -910,6 +1211,14 @@ namespace __expected
 		   is_constructible<_Unex, const expected<_Up, _Err>>
 		  >;
 
+      template<typename _Up>
+	static constexpr bool __same_val
+	  = is_same_v<typename _Up::value_type, _Tp>;
+
+      template<typename _Up>
+	static constexpr bool __same_err
+	  = is_same_v<typename _Up::error_type, _Er>;
+
     public:
       using value_type = _Tp;
       using error_type = _Er;
@@ -1180,6 +1489,260 @@ namespace __expected
 	return std::move(_M_unex);
       }
 
+      template<typename _Gr = _Er>
+	constexpr _Er
+	error_or(_Gr&& __e) const&
+	{
+	  static_assert( is_copy_constructible_v<_Er> );
+	  static_assert( is_convertible_v<_Gr, _Er> );
+
+	  if (_M_has_value)
+	    return std::forward<_Gr>(__e);
+	  return _M_unex;
+	}
+
+      template<typename _Gr = _Er>
+	constexpr _Er
+	error_or(_Gr&& __e) &&
+	{
+	  static_assert( is_move_constructible_v<_Er> );
+	  static_assert( is_convertible_v<_Gr, _Er> );
+
+	  if (_M_has_value)
+	    return std::forward<_Gr>(__e);
+	  return std::move(_M_unex);
+	}
+
+      // monadic operations
+
+      template<typename _Fn> requires is_copy_constructible_v<_Er>
+	constexpr auto
+	and_then(_Fn&& __f) &
+	{
+	  using _Up = __expected::__result0<_Fn>;
+	  static_assert(__expected::__is_expected<_Up>);
+	  static_assert(is_same_v<typename _Up::error_type, _Er>);
+
+	  if (has_value())
+	    return std::__invoke(std::forward<_Fn>(__f));
+	  else
+	    return _Up(unexpect, error());
+	}
+
+     template<typename _Fn> requires is_copy_constructible_v<_Er>
+	constexpr auto
+	and_then(_Fn&& __f) const &
+	{
+	  using _Up = __expected::__result0<_Fn>;
+	  static_assert(__expected::__is_expected<_Up>);
+	  static_assert(is_same_v<typename _Up::error_type, _Er>);
+
+	  if (has_value())
+	    return std::__invoke(std::forward<_Fn>(__f));
+	  else
+	    return _Up(unexpect, error());
+	}
+
+      template<typename _Fn> requires is_move_constructible_v<_Er>
+	constexpr auto
+	and_then(_Fn&& __f) &&
+	{
+	  using _Up = __expected::__result0<_Fn>;
+	  static_assert(__expected::__is_expected<_Up>);
+	  static_assert(is_same_v<typename _Up::error_type, _Er>);
+
+	  if (has_value())
+	    return std::__invoke(std::forward<_Fn>(__f));
+	  else
+	    return _Up(unexpect, std::move(error()));
+	}
+
+       template<typename _Fn> requires is_move_constructible_v<_Er>
+	constexpr auto
+	and_then(_Fn&& __f) const &&
+	{
+	  using _Up = __expected::__result0<_Fn>;
+	  static_assert(__expected::__is_expected<_Up>);
+	  static_assert(is_same_v<typename _Up::error_type, _Er>);
+
+	  if (has_value())
+	    return std::__invoke(std::forward<_Fn>(__f));
+	  else
+	    return _Up(unexpect, std::move(error()));
+	}
+
+      template<typename _Fn>
+	constexpr auto
+	or_else(_Fn&& __f) &
+	{
+	  using _Gr = __expected::__result<_Fn, _Er&>;
+	  static_assert(__expected::__is_expected<_Gr>);
+	  static_assert(is_same_v<typename _Gr::value_type, _Tp>);
+
+	  if (has_value())
+	    return _Gr();
+	  else
+	    return std::__invoke(std::forward<_Fn>(__f), error());
+	}
+
+      template<typename _Fn>
+	constexpr auto
+	or_else(_Fn&& __f) const &
+	{
+	  using _Gr = __expected::__result<_Fn, const _Er&>;
+	  static_assert(__expected::__is_expected<_Gr>);
+	  static_assert(is_same_v<typename _Gr::value_type, _Tp>);
+
+	  if (has_value())
+	    return _Gr();
+	  else
+	    return std::__invoke(std::forward<_Fn>(__f), error());
+	}
+
+      template<typename _Fn>
+	constexpr auto
+	or_else(_Fn&& __f) &&
+	{
+	  using _Gr = __expected::__result<_Fn, _Er&&>;
+	  static_assert(__expected::__is_expected<_Gr>);
+	  static_assert(is_same_v<typename _Gr::value_type, _Tp>);
+
+	  if (has_value())
+	    return _Gr();
+	  else
+	    return std::__invoke(std::forward<_Fn>(__f), std::move(error()));
+	}
+
+      template<typename _Fn>
+	constexpr auto
+	or_else(_Fn&& __f) const &&
+	{
+	  using _Gr = __expected::__result<_Fn, const _Er&&>;
+	  static_assert(__expected::__is_expected<_Gr>);
+	  static_assert(is_same_v<typename _Gr::value_type, _Tp>);
+
+	  if (has_value())
+	    return _Gr();
+	  else
+	    return std::__invoke(std::forward<_Fn>(__f), std::move(error()));
+	}
+
+      template<typename _Fn> requires is_copy_constructible_v<_Er>
+	constexpr auto
+	transform(_Fn&& __f) &
+	{
+	  using _Up = __expected::__result0<_Fn>;
+	  using _Res = expected<_Up, _Er>;
+
+	  if (has_value())
+	    return _Res(__in_place_inv{}, std::forward<_Fn>(__f));
+	  else
+	    return _Res(unexpect, error());
+	}
+
+      template<typename _Fn> requires is_copy_constructible_v<_Er>
+	constexpr auto
+	transform(_Fn&& __f) const &
+	{
+	  using _Up = __expected::__result0<_Fn>;
+	  using _Res = expected<_Up, _Er>;
+
+	  if (has_value())
+	    return _Res(__in_place_inv{}, std::forward<_Fn>(__f));
+	  else
+	    return _Res(unexpect, error());
+	}
+
+      template<typename _Fn> requires is_move_constructible_v<_Er>
+	constexpr auto
+	transform(_Fn&& __f) &&
+	{
+	  using _Up = __expected::__result0<_Fn>;
+	  using _Res = expected<_Up, _Er>;
+
+	  if (has_value())
+	    return _Res(__in_place_inv{}, std::forward<_Fn>(__f));
+	  else
+	    return _Res(unexpect, std::move(error()));
+	}
+
+      template<typename _Fn> requires is_move_constructible_v<_Er>
+	constexpr auto
+	transform(_Fn&& __f) const &&
+	{
+	  using _Up = __expected::__result0<_Fn>;
+	  using _Res = expected<_Up, _Er>;
+
+	  if (has_value())
+	    return _Res(__in_place_inv{}, std::forward<_Fn>(__f));
+	  else
+	    return _Res(unexpect, std::move(error()));
+	}
+
+      template<typename _Fn>
+	constexpr auto
+	transform_error(_Fn&& __f) &
+	{
+	  using _Gr = __expected::__result<_Fn, _Er&>;
+	  using _Res = expected<_Tp, _Gr>;
+
+	  if (has_value())
+	    return _Res();
+	  else
+	    return _Res(__unexpect_inv{}, [&]() {
+			  return std::__invoke(std::forward<_Fn>(__f),
+					       _M_unex);
+			});
+	}
+
+      template<typename _Fn>
+	constexpr auto
+	transform_error(_Fn&& __f) const &
+	{
+	  using _Gr = __expected::__result<_Fn, const _Er&>;
+	  using _Res = expected<_Tp, _Gr>;
+
+	  if (has_value())
+	    return _Res();
+	  else
+	    return _Res(__unexpect_inv{}, [&]() {
+			  return std::__invoke(std::forward<_Fn>(__f),
+					       _M_unex);
+			});
+	}
+
+      template<typename _Fn>
+	constexpr auto
+	transform_error(_Fn&& __f) &&
+	{
+	  using _Gr = __expected::__result<_Fn, _Er&&>;
+	  using _Res = expected<_Tp, _Gr>;
+
+	  if (has_value())
+	    return _Res();
+	  else
+	    return _Res(__unexpect_inv{}, [&]() {
+			  return std::__invoke(std::forward<_Fn>(__f),
+					       std::move(_M_unex));
+			});
+	}
+
+      template<typename _Fn>
+	constexpr auto
+	transform_error(_Fn&& __f) const &&
+	{
+	  using _Gr = __expected::__result<_Fn, const _Er&&>;
+	  using _Res = expected<_Tp, _Gr>;
+
+	  if (has_value())
+	    return _Res();
+	  else
+	    return _Res(__unexpect_inv{}, [&]() {
+			  return std::__invoke(std::forward<_Fn>(__f),
+					       std::move(_M_unex));
+			});
+	}
+
       // equality operators
 
       template<typename _Up, typename _Er2>
@@ -1223,6 +1786,20 @@ namespace __expected
 	    _M_unex = std::forward<_Vp>(__v);
 	}
 
+      using __in_place_inv = __expected::__in_place_inv;
+      using __unexpect_inv = __expected::__unexpect_inv;
+
+      template<typename _Fn>
+	explicit constexpr
+	expected(__in_place_inv, _Fn&& __fn)
+	: _M_void(), _M_has_value(true)
+	{ std::forward<_Fn>(__fn)(); }
+
+      template<typename _Fn>
+	explicit constexpr
+	expected(__unexpect_inv, _Fn&& __fn)
+	: _M_unex(std::forward<_Fn>(__fn)()), _M_has_value(false)
+	{ }
 
       union {
 	struct { } _M_void;
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index 61718ebad74..576eebc7dc8 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -306,7 +306,7 @@
 #define __cpp_lib_constexpr_charconv 202207L
 #define __cpp_lib_constexpr_typeinfo 202106L
 #if __cpp_concepts >= 202002L
-# define __cpp_lib_expected 202202L
+# define __cpp_lib_expected 202211L
 #endif
 #define __cpp_lib_invoke_r 202106L
 #define __cpp_lib_is_scoped_enum 202011L
diff --git a/libstdc++-v3/testsuite/20_util/expected/illformed_neg.cc b/libstdc++-v3/testsuite/20_util/expected/illformed_neg.cc
index 921306bc667..f1b0771aeb9 100644
--- a/libstdc++-v3/testsuite/20_util/expected/illformed_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/expected/illformed_neg.cc
@@ -65,3 +65,4 @@ test_expected_error()
 }
 
 // { dg-prune-output "static assertion failed" }
+// { dg-prune-output "function returning an array" }
diff --git a/libstdc++-v3/testsuite/20_util/expected/monadic.cc b/libstdc++-v3/testsuite/20_util/expected/monadic.cc
new file mode 100644
index 00000000000..d82774b0e1f
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/expected/monadic.cc
@@ -0,0 +1,280 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <expected>
+#include <string_view>
+#include <testsuite_hooks.h>
+
+constexpr bool
+test_and_then()
+{
+  std::expected<int, int> e1(1);
+  VERIFY( e1.and_then([]<typename T>(T&& v) {
+    static_assert( std::is_same_v<T, int&> );
+    VERIFY( v == 1 );
+    return std::expected<long, int>(100);
+  }).value() == 100 );
+  VERIFY( std::move(e1).and_then([]<typename T>(T&& v) {
+    static_assert( std::is_same_v<T, int> );
+    VERIFY( v == 1 );
+    return std::expected<long, int>(101);
+  }).value() == 101 );
+  const auto& ce1 = e1;
+  VERIFY( ce1.and_then([]<typename T>(T&& v) {
+    static_assert( std::is_same_v<T, const int&> );
+    VERIFY( v == 1 );
+    return std::expected<long, int>(102);
+  }).value() == 102 );
+  VERIFY( std::move(ce1).and_then([]<typename T>(T&& v) {
+    static_assert( std::is_same_v<T, const int> );
+    VERIFY( v == 1 );
+    return std::expected<long, int>(103);
+  }).value() == 103 );
+
+  auto fail = [] (auto&&) -> std::expected<void, int> { throw 1; };
+  std::expected<int, int> e2(std::unexpect, 2);
+  VERIFY( e2.and_then(fail).error() == 2 );
+  VERIFY( std::move(e2).and_then(fail).error() == 2 );
+  const auto& ce2 = e2;
+  VERIFY( ce2.and_then(fail).error() == 2 );
+  VERIFY( std::move(ce2).and_then(fail).error() == 2 );
+
+  int i = 100;
+  auto vpass = [&] -> std::expected<int, int> { return i++; };
+  std::expected<void, int> v1;
+  VERIFY( v1.and_then(vpass).value() == 100 );
+  VERIFY( std::move(v1).and_then(vpass).value() == 101 );
+  const auto& cv1 = v1;
+  VERIFY( cv1.and_then(vpass).value() == 102 );
+  VERIFY( std::move(cv1).and_then(vpass).value() == 103 );
+
+  auto vfail = [] -> std::expected<int, int> { throw 1; };
+  std::expected<void, int> v2(std::unexpect, 2);
+  VERIFY( v2.and_then(vfail).error() == 2 );
+  VERIFY( std::move(v2).and_then(vfail).error() == 2 );
+  const auto& cv2 = v2;
+  VERIFY( cv2.and_then(vfail).error() == 2 );
+  VERIFY( std::move(cv2).and_then(vfail).error() == 2 );
+
+  static_assert(std::is_same_v<decltype(v1.and_then(vpass)),
+			       decltype(vpass())>);
+  static_assert(std::is_same_v<decltype(cv1.and_then(vpass)),
+			       decltype(vpass())>);
+
+  return true;
+}
+
+constexpr bool
+test_or_else()
+{
+  std::expected<int, int> e1(std::unexpect, 1);
+  VERIFY( e1.or_else([]<typename T>(T&& v) {
+    static_assert( std::is_same_v<T, int&> );
+    VERIFY( v == 1 );
+    return std::expected<int, long>(100);
+  }).value() == 100 );
+  VERIFY( std::move(e1).or_else([]<typename T>(T&& v) {
+    static_assert( std::is_same_v<T, int> );
+    VERIFY( v == 1 );
+    return std::expected<int, long>(101);
+  }).value() == 101 );
+  const auto& ce1 = e1;
+  VERIFY( ce1.or_else([]<typename T>(T&& v) {
+    static_assert( std::is_same_v<T, const int&> );
+    VERIFY( v == 1 );
+    return std::expected<int, long>(102);
+  }).value() == 102 );
+  VERIFY( std::move(ce1).or_else([]<typename T>(T&& v) {
+    static_assert( std::is_same_v<T, const int> );
+    VERIFY( v == 1 );
+    return std::expected<int, long>(103);
+  }).value() == 103 );
+
+  auto f = [] (auto) -> std::expected<int, long> { throw 1; };
+  std::expected<int, int> e2(2);
+  VERIFY( e2.or_else(f).value() == 2 );
+  VERIFY( std::move(e2).or_else(f).value() == 2 );
+  const auto& ce2 = e2;
+  VERIFY( ce2.or_else(f).value() == 2 );
+  VERIFY( std::move(ce2).or_else(f).value() == 2 );
+
+  auto vf = [] (auto) -> std::expected<void, long> { return {}; };
+  std::expected<void, int> v1(std::unexpect, 1);
+  VERIFY( v1.or_else(vf).has_value() );
+  VERIFY( std::move(v1).or_else(vf).has_value() );
+  const auto& cv1 = v1;
+  VERIFY( cv1.or_else(vf).has_value() );
+  VERIFY( std::move(cv1).or_else(vf).has_value() );
+
+  auto vfail = [] (auto) -> std::expected<void, long> { throw 1; };
+  std::expected<void, int> v2;
+  VERIFY( v2.or_else(vfail).has_value() );
+  VERIFY( std::move(v2).or_else(vfail).has_value() );
+  const auto& cv2 = v2;
+  VERIFY( cv2.or_else(vfail).has_value() );
+  VERIFY( std::move(cv2).or_else(vfail).has_value() );
+
+  static_assert(std::is_same_v<decltype(v1.or_else(vf)), decltype(vf(1))>);
+  static_assert(std::is_same_v<decltype(cv1.or_else(vf)), decltype(vf(1))>);
+
+  return true;
+}
+
+constexpr bool
+test_transform()
+{
+  std::expected<int, int> e1(1);
+  VERIFY( e1.transform([]<typename T>(T&& v) {
+    static_assert( std::is_same_v<T, int&> );
+    VERIFY( v == 1 );
+    return std::string_view("100");
+  }).value() == "100" );
+  VERIFY( std::move(e1).transform([]<typename T>(T&& v) {
+    static_assert( std::is_same_v<T, int> );
+    VERIFY( v == 1 );
+    return std::string_view("101");
+  }).value() == "101" );
+  const auto& ce1 = e1;
+  VERIFY( ce1.transform([]<typename T>(T&& v) {
+    static_assert( std::is_same_v<T, const int&> );
+    VERIFY( v == 1 );
+    return std::string_view("102");
+  }).value() == "102" );
+  VERIFY( std::move(ce1).transform([]<typename T>(T&& v) {
+    static_assert( std::is_same_v<T, const int> );
+    VERIFY( v == 1 );
+    return std::string_view("103");
+  }).value() == "103" );
+
+  auto fail = [] (auto&&) -> std::string_view { throw 1; };
+  std::expected<int, int> e2(std::unexpect, 2);
+  VERIFY( e2.transform(fail).error() == 2 );
+  VERIFY( std::move(e2).transform(fail).error() == 2 );
+  const auto& ce2 = e2;
+  VERIFY( ce2.transform(fail).error() == 2 );
+  VERIFY( std::move(ce2).transform(fail).error() == 2 );
+
+  auto vpass = [&] -> std::string_view { return "ok"; };
+  std::expected<void, int> v1;
+  VERIFY( v1.transform(vpass).value() == "ok" );
+  VERIFY( std::move(v1).transform(vpass).value() == "ok" );
+  const auto& cv1 = v1;
+  VERIFY( cv1.transform(vpass).value() == "ok" );
+  VERIFY( std::move(cv1).transform(vpass).value() == "ok" );
+
+  auto vfail = [] -> std::string_view { throw 1; };
+  std::expected<void, int> v2(std::unexpect, 2);
+  VERIFY( v2.transform(vfail).error() == 2 );
+  VERIFY( std::move(v2).transform(vfail).error() == 2 );
+  const auto& cv2 = v2;
+  VERIFY( cv2.transform(vfail).error() == 2 );
+  VERIFY( std::move(cv2).transform(vfail).error() == 2 );
+
+  static_assert(std::is_same_v<decltype(v1.transform(vpass)),
+			       std::expected<decltype(vpass()), int>>);
+  static_assert(std::is_same_v<decltype(cv1.transform(vpass)),
+			       std::expected<decltype(vpass()), int>>);
+
+  return true;
+}
+
+constexpr bool
+test_transform_error()
+{
+  std::expected<int, int> e1(std::unexpect, 1);
+  VERIFY( e1.transform_error([]<typename T>(T&& v) {
+    static_assert( std::is_same_v<T, int&> );
+    VERIFY( v == 1 );
+    return std::string_view("100");
+  }).error() == "100" );
+  VERIFY( std::move(e1).transform_error([]<typename T>(T&& v) {
+    static_assert( std::is_same_v<T, int> );
+    VERIFY( v == 1 );
+    return std::string_view("101");
+  }).error() == "101" );
+  const auto& ce1 = e1;
+  VERIFY( ce1.transform_error([]<typename T>(T&& v) {
+    static_assert( std::is_same_v<T, const int&> );
+    VERIFY( v == 1 );
+    return std::string_view("102");
+  }).error() == "102" );
+  VERIFY( std::move(ce1).transform_error([]<typename T>(T&& v) {
+    static_assert( std::is_same_v<T, const int> );
+    VERIFY( v == 1 );
+    return std::string_view("103");
+  }).error() == "103" );
+
+  auto fail = [] (auto&&) -> std::string_view { throw 1; };
+  std::expected<int, int> e2(2);
+  VERIFY( e2.transform_error(fail).value() == 2 );
+  VERIFY( std::move(e2).transform_error(fail).value() == 2 );
+  const auto& ce2 = e2;
+  VERIFY( ce2.transform_error(fail).value() == 2 );
+  VERIFY( std::move(ce2).transform_error(fail).value() == 2 );
+
+  auto vpass = [&] (auto) -> std::string_view { return "ok"; };
+  std::expected<void, int> v1(std::unexpect, 1);
+  VERIFY( v1.transform_error(vpass).error() == "ok" );
+  VERIFY( std::move(v1).transform_error(vpass).error() == "ok" );
+  const auto& cv1 = v1;
+  VERIFY( cv1.transform_error(vpass).error() == "ok" );
+  VERIFY( std::move(cv1).transform_error(vpass).error() == "ok" );
+
+  auto vfail = [] (auto) -> std::string_view { throw 1; };
+  std::expected<void, int> v2;
+  VERIFY( v2.transform_error(vfail).has_value() );
+  VERIFY( std::move(v2).transform_error(vfail).has_value() );
+  const auto& cv2 = v2;
+  VERIFY( cv2.transform_error(vfail).has_value() );
+  VERIFY( std::move(cv2).transform_error(vfail).has_value() );
+
+  static_assert(std::is_same_v<decltype(v1.transform_error(vpass)),
+			       std::expected<void, decltype(vpass(1))>>);
+  static_assert(std::is_same_v<decltype(cv1.transform_error(vpass)),
+			       std::expected<void, decltype(vpass(1))>>);
+
+  return true;
+}
+
+constexpr bool
+test_temporary_materialization()
+{
+  struct NonCopyable {
+    constexpr NonCopyable(int i) : i(i) { }
+    NonCopyable(const NonCopyable&) = delete;
+    int i;
+  };
+
+  auto xform = [](int i) { return NonCopyable(i); };
+
+  std::expected<int, int> e1(1);
+  std::expected<NonCopyable, int> n1 = e1.transform(xform);
+  VERIFY( n1.value().i == 1 );
+  std::expected<int, int> e2(std::unexpected<int>(2));
+  std::expected<int, NonCopyable> n2 = e2.transform_error(xform);
+  VERIFY( n2.error().i == 2 );
+
+  auto vxform = [] { return NonCopyable(999); };
+  std::expected<void, int> v1;
+  std::expected<NonCopyable, int> nv1 = v1.transform(vxform);
+  VERIFY( nv1.value().i == 999 );
+  std::expected<void, int> v2(std::unexpected<int>(22));
+  std::expected<void, NonCopyable> nv2 = v2.transform_error(xform);
+  VERIFY( nv2.error().i == 22 );
+
+  return true;
+}
+
+int main()
+{
+  static_assert( test_and_then() );
+  test_and_then();
+  static_assert( test_or_else() );
+  test_or_else();
+  static_assert( test_transform() );
+  test_transform();
+  static_assert( test_transform_error() );
+  test_transform_error();
+  static_assert( test_temporary_materialization() );
+  test_temporary_materialization();
+}
diff --git a/libstdc++-v3/testsuite/20_util/expected/observers.cc b/libstdc++-v3/testsuite/20_util/expected/observers.cc
index e76ca378026..9bcd9edb809 100644
--- a/libstdc++-v3/testsuite/20_util/expected/observers.cc
+++ b/libstdc++-v3/testsuite/20_util/expected/observers.cc
@@ -191,6 +191,24 @@ test_value_or()
   return true;
 }
 
+constexpr bool
+test_error_or()
+{
+  std::expected<int, int> e1(1), e2(std::unexpect, 3);
+  VERIFY( e1.error_or(2) == 2 );
+  VERIFY( std::move(e1).error_or(2) == 2 );
+  VERIFY( e2.error_or(2) == 3 );
+  VERIFY( std::move(e2).error_or(2) == 3 );
+
+  std::expected<void, int> e3, e4(std::unexpect, 3);
+  VERIFY( e3.error_or(2) == 2 );
+  VERIFY( std::move(e3).error_or(2) == 2 );
+  VERIFY( e4.error_or(2) == 3 );
+  VERIFY( std::move(e4).error_or(2) == 3 );
+
+  return true;
+}
+
 int main()
 {
   static_assert( test_arrow() );
@@ -206,4 +224,6 @@ int main()
   test_error();
   static_assert( test_value_or() );
   test_value_or();
+  static_assert( test_error_or() );
+  test_error_or();
 }
diff --git a/libstdc++-v3/testsuite/20_util/expected/synopsis.cc b/libstdc++-v3/testsuite/20_util/expected/synopsis.cc
index 3a7eef3eee4..b0439614a27 100644
--- a/libstdc++-v3/testsuite/20_util/expected/synopsis.cc
+++ b/libstdc++-v3/testsuite/20_util/expected/synopsis.cc
@@ -6,7 +6,7 @@
 
 #ifndef __cpp_lib_expected
 # error "Feature-test macro for expected missing in <expected>"
-#elif __cpp_lib_expected != 202202L
+#elif __cpp_lib_expected != 202211L
 # error "Feature-test macro for expected has wrong value in <expected>"
 #endif
 
diff --git a/libstdc++-v3/testsuite/20_util/expected/version.cc b/libstdc++-v3/testsuite/20_util/expected/version.cc
index 78dc61ef55d..98fccf73dc1 100644
--- a/libstdc++-v3/testsuite/20_util/expected/version.cc
+++ b/libstdc++-v3/testsuite/20_util/expected/version.cc
@@ -5,6 +5,6 @@
 
 #ifndef __cpp_lib_expected
 # error "Feature-test macro for expected missing in <version>"
-#elif __cpp_lib_expected != 202202L
+#elif __cpp_lib_expected != 202211L
 # error "Feature-test macro for expected has wrong value in <version>"
 #endif

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

only message in thread, other threads:[~2022-12-16 21:00 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-12-16 21:00 [gcc r13-4754] libstdc++: Add monadic operations to std::expected for C++23 (P2505R5) 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).