public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r12-4506] libstdc++: Implement monadic operations for std::optional (P0798R8)
@ 2021-10-19 14:01 Jonathan Wakely
  0 siblings, 0 replies; only message in thread
From: Jonathan Wakely @ 2021-10-19 14:01 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:82b2e4f8cf5a01c6724fe3f465a77ee03cfcaae2

commit r12-4506-g82b2e4f8cf5a01c6724fe3f465a77ee03cfcaae2
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Oct 19 11:06:56 2021 +0100

    libstdc++: Implement monadic operations for std::optional (P0798R8)
    
    Another new addition to the C++23 working draft.
    
    The new member functions of std::optional are only defined for C++23,
    but the new members of _Optional_payload_base are defined for C++20 so
    that they can be used in non-propagating-cache in <ranges>. The
    _Optional_payload_base::_M_construct member can also be used in
    non-propagating-cache now, because it's constexpr since r12-4389.
    
    There will be an LWG issue about the feature test macro, suggesting that
    we should just bump the value of __cpp_lib_optional instead. I haven't
    done that here, but it can be changed once consensus is reached on the
    change.
    
    libstdc++-v3/ChangeLog:
    
            * include/std/optional (_Optional_payload_base::_Storage): Add
            constructor taking a callable function to invoke.
            (_Optional_payload_base::_M_apply): New function.
            (__cpp_lib_monadic_optional): Define for C++23.
            (optional::and_then, optional::transform, optional::or_else):
            Define for C++23.
            * include/std/ranges (__detail::__cached): Remove.
            (__detail::__non_propagating_cache): Remove use of __cached for
            contained value. Use _Optional_payload_base::_M_construct and
            _Optional_payload_base::_M_apply to set the contained value.
            * include/std/version (__cpp_lib_monadic_optional): Define.
            * testsuite/20_util/optional/monadic/and_then.cc: New test.
            * testsuite/20_util/optional/monadic/or_else.cc: New test.
            * testsuite/20_util/optional/monadic/or_else_neg.cc: New test.
            * testsuite/20_util/optional/monadic/transform.cc: New test.
            * testsuite/20_util/optional/monadic/version.cc: New test.

Diff:
---
 libstdc++-v3/include/std/optional                  | 182 ++++++++++++++++++++-
 libstdc++-v3/include/std/ranges                    |  42 +----
 libstdc++-v3/include/std/version                   |   3 +
 .../testsuite/20_util/optional/monadic/and_then.cc | 120 ++++++++++++++
 .../testsuite/20_util/optional/monadic/or_else.cc  | 103 ++++++++++++
 .../20_util/optional/monadic/or_else_neg.cc        |  30 ++++
 .../20_util/optional/monadic/transform.cc          | 123 ++++++++++++++
 .../testsuite/20_util/optional/monadic/version.cc  |  10 ++
 8 files changed, 573 insertions(+), 40 deletions(-)

diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional
index b69268b3642..eac91d3c160 100644
--- a/libstdc++-v3/include/std/optional
+++ b/libstdc++-v3/include/std/optional
@@ -1,6 +1,7 @@
 // <optional> -*- C++ -*-
 
 // Copyright (C) 2013-2021 Free Software Foundation, Inc.
+// Copyright The GNU Toolchain Authors.
 //
 // This file is part of the GNU ISO C++ Library.  This library is free
 // software; you can redistribute it and/or modify it under the
@@ -44,6 +45,10 @@
 #include <bits/utility.h> // in_place_t
 #if __cplusplus > 201703L
 # include <compare>
+# include <bits/invoke.h> // std::__invoke
+#endif
+#if __cplusplus > 202002L
+# include <concepts>
 #endif
 
 namespace std _GLIBCXX_VISIBILITY(default)
@@ -81,6 +86,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// Tag to disengage optional objects.
   inline constexpr nullopt_t nullopt { nullopt_t::_Construct::_Token };
 
+  template<typename _Fn> struct _Optional_func { _Fn& _M_f; };
+
   /**
    *  @brief Exception class thrown when a disengaged optional object is
    *  dereferenced.
@@ -211,6 +218,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    : _M_value(__il, std::forward<_Args>(__args)...)
 	    { }
 
+#if __cplusplus >= 202002L
+	  template<typename _Fn, typename _Arg>
+	    constexpr
+	    _Storage(_Optional_func<_Fn> __f, _Arg&& __arg)
+	    : _M_value(std::__invoke(std::forward<_Fn>(__f._M_f),
+				     std::forward<_Arg>(__arg)))
+	    { }
+#endif
+
 	  _Empty_byte _M_empty;
 	  _Up _M_value;
 	};
@@ -232,6 +248,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    : _M_value(__il, std::forward<_Args>(__args)...)
 	    { }
 
+#if __cplusplus >= 202002L
+	  template<typename _Fn, typename _Arg>
+	    constexpr
+	    _Storage(_Optional_func<_Fn> __f, _Arg&& __arg)
+	    : _M_value(std::__invoke(std::forward<_Fn>(__f._M_f),
+				     std::forward<_Arg>(__arg)))
+	    { }
+#endif
+
 	  // User-provided destructor is needed when _Up has non-trivial dtor.
 	  _GLIBCXX20_CONSTEXPR ~_Storage() { }
 
@@ -260,6 +285,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_payload._M_value.~_Stored_type();
       }
 
+#if __cplusplus >= 202002L
+      template<typename _Fn, typename _Up>
+	constexpr void
+	_M_apply(_Optional_func<_Fn> __f, _Up&& __x)
+	{
+	  std::construct_at(std::__addressof(this->_M_payload),
+			    __f, std::forward<_Up>(__x));
+	  _M_engaged = true;
+	}
+#endif
+
       // The _M_get() operations have _M_engaged as a precondition.
       // They exist to access the contained value with the appropriate
       // const-qualification, because _M_payload has had the const removed.
@@ -637,6 +673,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
   class optional;
 
+  template<typename _Tp>
+    inline constexpr bool __is_optional_v = false;
+  template<typename _Tp>
+    inline constexpr bool __is_optional_v<optional<_Tp>> = true;
+
   template<typename _Tp, typename _Up>
     using __converts_from_optional =
       __or_<is_constructible<_Tp, const optional<_Up>&>,
@@ -1002,7 +1043,143 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    return static_cast<_Tp>(std::forward<_Up>(__u));
 	}
 
+#if __cplusplus > 202002L && __cpp_lib_concepts
+#define __cpp_lib_monadic_optional 202110L
+
+      // [optional.monadic]
+
+      template<typename _Fn> requires invocable<_Fn, _Tp&>
+	constexpr auto
+	and_then(_Fn&& __f) &
+	{
+	  using _Up = remove_cvref_t<invoke_result_t<_Fn, _Tp&>>;
+	  static_assert(__is_optional_v<remove_cvref_t<_Up>>);
+	  if (has_value())
+	    return std::__invoke(std::forward<_Fn>(__f), **this);
+	  else
+	    return _Up();
+	}
+
+      template<typename _Fn> requires invocable<_Fn, const _Tp&>
+	constexpr auto
+	and_then(_Fn&& __f) const &
+	{
+	  using _Up = remove_cvref_t<invoke_result_t<_Fn, const _Tp&>>;
+	  static_assert(__is_optional_v<_Up>);
+	  if (has_value())
+	    return std::__invoke(std::forward<_Fn>(__f), **this);
+	  else
+	    return _Up();
+	}
+
+      template<typename _Fn> requires invocable<_Fn, _Tp>
+	constexpr auto
+	and_then(_Fn&& __f) &&
+	{
+	  using _Up = remove_cvref_t<invoke_result_t<_Fn, _Tp>>;
+	  static_assert(__is_optional_v<remove_cvref_t<_Up>>);
+	  if (has_value())
+	    return std::__invoke(std::forward<_Fn>(__f), std::move(**this));
+	  else
+	    return _Up();
+	}
+
+      template<typename _Fn> requires invocable<_Fn, const _Tp>
+	constexpr auto
+	and_then(_Fn&& __f) const &&
+	{
+	  using _Up = remove_cvref_t<invoke_result_t<_Fn, const _Tp>>;
+	  static_assert(__is_optional_v<remove_cvref_t<_Up>>);
+	  if (has_value())
+	    return std::__invoke(std::forward<_Fn>(__f), std::move(**this));
+	  else
+	    return _Up();
+	}
+
+      template<typename _Fn> requires invocable<_Fn, _Tp&>
+	constexpr auto
+	transform(_Fn&& __f) &
+	{
+	  using _Up = invoke_result_t<_Fn, _Tp&>;
+	  if (has_value())
+	    return optional<_Up>(_Optional_func<_Fn>{__f}, **this);
+	  else
+	    return optional<_Up>();
+	}
+
+      template<typename _Fn> requires invocable<_Fn, const _Tp&>
+	constexpr auto
+	transform(_Fn&& __f) const &
+	{
+	  using _Up = invoke_result_t<_Fn, const _Tp&>;
+	  if (has_value())
+	    return optional<_Up>(_Optional_func<_Fn>{__f}, **this);
+	  else
+	    return optional<_Up>();
+	}
+
+      template<typename _Fn> requires invocable<_Fn, _Tp>
+	constexpr auto
+	transform(_Fn&& __f) &&
+	{
+	  using _Up = invoke_result_t<_Fn, _Tp>;
+	  if (has_value())
+	    return optional<_Up>(_Optional_func<_Fn>{__f}, std::move(**this));
+	  else
+	    return optional<_Up>();
+	}
+
+      template<typename _Fn> requires invocable<_Fn, const _Tp>
+	constexpr auto
+	transform(_Fn&& __f) const &&
+	{
+	  using _Up = invoke_result_t<_Fn, const _Tp>;
+	  if (has_value())
+	    return optional<_Up>(_Optional_func<_Fn>{__f}, std::move(**this));
+	  else
+	    return optional<_Up>();
+	}
+
+      template<typename _Fn> requires invocable<_Fn> && copy_constructible<_Tp>
+	constexpr optional
+	or_else(_Fn&& __f) const&
+	{
+	  using _Up = invoke_result_t<_Fn>;
+	  static_assert( is_same_v<remove_cvref_t<_Up>, optional> );
+
+	  if (has_value())
+	    return *this;
+	  else
+	    return std::forward<_Fn>(__f)();
+	}
+
+      template<typename _Fn> requires invocable<_Fn> && move_constructible<_Tp>
+	constexpr optional
+	or_else(_Fn&& __f) &&
+	{
+	  using _Up = invoke_result_t<_Fn>;
+	  static_assert( is_same_v<remove_cvref_t<_Up>, optional> );
+
+	  if (has_value())
+	    return std::move(*this);
+	  else
+	    return std::forward<_Fn>(__f)();
+	}
+#endif
+
       _GLIBCXX20_CONSTEXPR void reset() noexcept { this->_M_reset(); }
+
+    private:
+#if __cplusplus >= 202002L
+      template<typename _Up> friend class optional;
+
+      template<typename _Fn, typename _Value>
+	explicit constexpr
+	optional(_Optional_func<_Fn> __f, _Value&& __v)
+	{
+	  this->_M_payload._M_apply(__f, std::forward<_Value>(__v));
+	}
+#endif
     };
 
   template<typename _Tp>
@@ -1241,11 +1418,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return !__rhs || __lhs >= *__rhs; }
 
 #ifdef __cpp_lib_three_way_comparison
-  template<typename _Tp>
-    inline constexpr bool __is_optional_v = false;
-  template<typename _Tp>
-    inline constexpr bool __is_optional_v<optional<_Tp>> = true;
-
   template<typename _Tp, typename _Up>
     requires (!__is_optional_v<_Up>)
       && three_way_comparable_with<_Tp, _Up>
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index b8de400dfbb..18bd087985c 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -1159,34 +1159,10 @@ namespace views::__adaptor
 	// (such as join_view::_M_inner).
       };
 
-    template<typename _Tp>
-      struct __cached
-      {
-	struct _Deref_t { };
-	static constexpr _Deref_t __deref{};
-
-	// Initialize _M_t directly from the result of dereferencing __i.
-	// This avoids any unwanted temporary materialization that would
-	// occur if *__i was bound to a reference before initializing _M_t.
-	template<typename _Iter>
-	  constexpr explicit
-	  __cached(_Deref_t, _Iter&& __i)
-	  : _M_t(*__i)
-	  { }
-
-	template<typename... _Args>
-	  constexpr explicit
-	  __cached(_Args&&... __args)
-	  : _M_t(std::forward<_Args>(__args)...)
-	  { }
-
-	_Tp _M_t;
-      };
-
     template<typename _Tp>
       requires is_object_v<_Tp>
       struct __non_propagating_cache<_Tp>
-      : protected _Optional_base<__cached<_Tp>>
+      : protected _Optional_base<_Tp>
       {
 	__non_propagating_cache() = default;
 
@@ -1218,9 +1194,7 @@ namespace views::__adaptor
 	operator=(_Tp __val)
 	{
 	  this->_M_reset();
-	  std::construct_at(std::__addressof(this->_M_payload._M_payload),
-			    std::in_place, std::move(__val));
-	  this->_M_payload._M_engaged = true;
+	  this->_M_payload._M_construct(std::move(__val));
 	  return *this;
 	}
 
@@ -1230,22 +1204,20 @@ namespace views::__adaptor
 
 	constexpr _Tp&
 	operator*() noexcept
-	{ return this->_M_get()._M_t; }
+	{ return this->_M_get(); }
 
 	constexpr const _Tp&
 	operator*() const noexcept
-	{ return this->_M_get()._M_t; }
+	{ return this->_M_get(); }
 
 	template<typename _Iter>
 	  constexpr _Tp&
 	  _M_emplace_deref(const _Iter& __i)
 	  {
 	    this->_M_reset();
-	    // Use the special constructor of __cached<_Tp> that does *__i.
-	    std::construct_at(std::__addressof(this->_M_payload._M_payload),
-			      std::in_place, __cached<_Tp>::__deref, __i);
-	    this->_M_payload._M_engaged = true;
-	    return **this;
+	    auto __f = [] (auto& __x) { return *__x; };
+	    this->_M_payload._M_apply(_Optional_func{__f}, __i);
+	    return this->_M_get();
 	  }
       };
 
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index 2b118301da7..0a7b28abee6 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -292,6 +292,9 @@
 #define __cpp_lib_adaptor_iterator_pair_constructor 202106L
 #define __cpp_lib_invoke_r 202106L
 #define __cpp_lib_is_scoped_enum 202011L
+#if __cpp_lib_concepts
+# define __cpp_lib_monadic_optional 202110L
+#endif
 #define __cpp_lib_move_only_function 202110L
 #define __cpp_lib_string_contains 202011L
 #if _GLIBCXX_USE_CXX11_ABI // Only supported with cxx11-abi
diff --git a/libstdc++-v3/testsuite/20_util/optional/monadic/and_then.cc b/libstdc++-v3/testsuite/20_util/optional/monadic/and_then.cc
new file mode 100644
index 00000000000..02dcafe6c58
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/optional/monadic/and_then.cc
@@ -0,0 +1,120 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <optional>
+
+#ifndef __cpp_lib_monadic_optional
+# error "Feature test macro for monadic optional is missing in <optional>"
+#elif __cpp_lib_monadic_optional < 202110L
+# error "Feature test macro for monadic optional has wrong value in <optional>"
+#endif
+
+#include <testsuite_hooks.h>
+
+constexpr bool
+test_and_then()
+{
+  std::optional<int> o;
+  auto r = o.and_then([](int) -> std::optional<short> { throw 1; });
+  VERIFY( !r.has_value() );
+  static_assert( std::is_same_v<decltype(r), std::optional<short>> );
+
+  o = 111;
+  r = o.and_then([](int i) -> std::optional<short> { return {i/10}; });
+  VERIFY( *r == 11 );
+
+  return true;
+}
+
+static_assert( test_and_then() );
+
+enum { CalledLvalue = 1, CalledConst = 2, PassedLvalue = 4, PassedConst = 8 };
+
+struct F
+{
+  template<typename This, typename Value>
+    static constexpr std::optional<int>
+    called_as()
+    {
+      int res = 0;
+      if constexpr (std::is_lvalue_reference_v<This>)
+	res |= CalledLvalue;
+      if constexpr (std::is_const_v<std::remove_reference_t<This>>)
+	res |= CalledConst;
+
+      if constexpr (std::is_lvalue_reference_v<Value>)
+	res |= PassedLvalue;
+      if constexpr (std::is_const_v<std::remove_reference_t<Value>>)
+	res |= PassedConst;
+
+      return {res};
+    }
+
+  template<typename T>
+    constexpr std::optional<int>
+    operator()(T&&) &
+    { return called_as<F&, T>(); }
+
+  template<typename T>
+    constexpr std::optional<int>
+    operator()(T&&) const &
+    { return called_as<const F&, T>(); }
+
+  template<typename T>
+    constexpr std::optional<int>
+    operator()(T&&) &&
+    { return called_as<F, T>(); }
+
+  template<typename T>
+    constexpr std::optional<int>
+    operator()(T&&) const &&
+    { return called_as<const F, T>(); }
+};
+
+constexpr bool
+test_forwarding()
+{
+  std::optional<long> o = 1;
+  F f;
+
+  VERIFY( *o.and_then(f) == (PassedLvalue|CalledLvalue) );
+  VERIFY( *o.and_then(std::move(f)) == PassedLvalue );
+  VERIFY( *std::move(o).and_then(f) == CalledLvalue );
+  VERIFY( *std::move(o).and_then(std::move(f)) == 0 );
+
+  const auto& co = o;
+
+  VERIFY( *co.and_then(f) == (PassedLvalue|PassedConst|CalledLvalue) );
+  VERIFY( *co.and_then(std::move(f)) == (PassedLvalue|PassedConst) );
+  VERIFY( *std::move(co).and_then(f) == (PassedConst|CalledLvalue) );
+  VERIFY( *std::move(co).and_then(std::move(f)) == PassedConst );
+
+  const auto& cf = f;
+
+  VERIFY( *o.and_then(cf) == (PassedLvalue|CalledLvalue|CalledConst) );
+  VERIFY( *o.and_then(std::move(cf)) == (PassedLvalue|CalledConst) );
+  VERIFY( *std::move(o).and_then(cf) == (CalledLvalue|CalledConst) );
+  VERIFY( *std::move(o).and_then(std::move(cf)) == CalledConst );
+
+  VERIFY( *co.and_then(cf) == (PassedLvalue|PassedConst|CalledLvalue|CalledConst) );
+  VERIFY( *co.and_then(std::move(cf)) == (PassedLvalue|PassedConst|CalledConst) );
+  VERIFY( *std::move(co).and_then(cf) == (PassedConst|CalledLvalue|CalledConst) );
+  VERIFY( *std::move(co).and_then(std::move(cf)) == (PassedConst|CalledConst) );
+
+  o = std::nullopt;
+
+  VERIFY( ! o.and_then(f).has_value() );
+  VERIFY( ! co.and_then(f).has_value() );
+  VERIFY( ! std::move(o).and_then(f).has_value() );
+  VERIFY( ! std::move(co).and_then(f).has_value() );
+
+  return true;
+}
+
+static_assert( test_forwarding() );
+
+int main()
+{
+  test_and_then();
+  test_forwarding();
+}
diff --git a/libstdc++-v3/testsuite/20_util/optional/monadic/or_else.cc b/libstdc++-v3/testsuite/20_util/optional/monadic/or_else.cc
new file mode 100644
index 00000000000..05081046c80
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/optional/monadic/or_else.cc
@@ -0,0 +1,103 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <optional>
+#include <testsuite_hooks.h>
+
+constexpr bool
+test_or_else()
+{
+  std::optional<int> o;
+  auto&& r = o.or_else([]() -> std::optional<int> { return {303}; });
+  VERIFY( !o );
+  VERIFY( *r == 303 );
+  static_assert( std::is_same_v<decltype(r), std::optional<int>&&> );
+
+  o = 808;
+  const std::optional<int> tr = 909;
+  auto&& r2 = o.or_else([&]() -> const auto& { return tr; });
+  static_assert( std::is_same_v<decltype(r2), std::optional<int>&&> );
+  VERIFY( r2 == o );
+
+  return true;
+}
+
+static_assert( test_or_else() );
+
+constexpr bool
+test_move()
+{
+  struct X
+  {
+    constexpr X() { }
+    constexpr X(const X&) { copied = true; }
+    constexpr X(X&& x) { moved = true; x.gone = true; }
+
+    bool copied = false;
+    bool moved = false;
+    bool gone = false;
+  };
+
+  std::optional<X> o(std::in_place);
+
+  auto f = []{ return std::optional<X>{}; };
+  VERIFY( o.or_else(f)->copied );
+  VERIFY( ! o->gone );
+
+  VERIFY( std::move(o).or_else(f)->moved );
+  VERIFY( o->gone );
+
+  struct move_only
+  {
+    constexpr move_only() { }
+    constexpr move_only(move_only&&) { }
+  };
+
+  std::optional<move_only> mo;
+  // doesn't require copy
+  std::move(mo).or_else([] { return std::optional<move_only>{}; });
+
+  return true;
+}
+
+static_assert( test_move() );
+
+constexpr bool
+test_call()
+{
+  struct F
+  {
+    constexpr std::optional<int> operator()() & { return {1}; }
+    constexpr std::optional<int> operator()() && { return {2}; }
+    constexpr std::optional<int> operator()() const & { return {3}; };
+    constexpr std::optional<int> operator()() const && { return {4}; }
+  };
+
+  std::optional<int> o;
+  F f;
+
+  VERIFY( *o.or_else(f) == 1 );
+  VERIFY( *std::move(o).or_else(f) == 1 );
+
+  VERIFY( *o.or_else(std::move(f)) == 2 );
+  VERIFY( *std::move(o).or_else(std::move(f)) == 2 );
+
+  const F& cf = f;
+
+  VERIFY( *o.or_else(cf) == 3 );
+  VERIFY( *std::move(o).or_else(cf) == 3 );
+
+  VERIFY( *o.or_else(std::move(cf)) == 4 );
+  VERIFY( *std::move(o).or_else(std::move(cf)) == 4 );
+
+  return true;
+}
+
+static_assert( test_call() );
+
+int main()
+{
+  test_or_else();
+  test_move();
+  test_call();
+}
diff --git a/libstdc++-v3/testsuite/20_util/optional/monadic/or_else_neg.cc b/libstdc++-v3/testsuite/20_util/optional/monadic/or_else_neg.cc
new file mode 100644
index 00000000000..16e94864f3b
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/optional/monadic/or_else_neg.cc
@@ -0,0 +1,30 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do compile { target c++23 } }
+
+#include <optional>
+
+void
+test01()
+{
+  std::optional<int> o;
+  o.or_else([&] { return o; }); // OK
+  o.or_else([] { return std::optional<short>(); }); // { dg-error "here" }
+  o.or_else([] { return 1; }); // { dg-error "here" }
+  std::move(o).or_else([] { return std::optional<short>(); }); // { dg-error "here" }
+  std::move(o).or_else([] { return 1; }); // { dg-error "here" }
+}
+
+// { dg-prune-output "static assertion failed" }
+
+void
+test02()
+{
+  struct move_only
+  {
+    move_only() { }
+    move_only(move_only&&) { }
+  };
+
+  std::optional<move_only> mo;
+  mo.or_else([]{ return std::optional<move_only>{}; }); // { dg-error "no matching function" }
+}
diff --git a/libstdc++-v3/testsuite/20_util/optional/monadic/transform.cc b/libstdc++-v3/testsuite/20_util/optional/monadic/transform.cc
new file mode 100644
index 00000000000..d01ccb2e0f2
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/optional/monadic/transform.cc
@@ -0,0 +1,123 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <optional>
+#include <testsuite_hooks.h>
+
+constexpr bool
+test_transform()
+{
+  std::optional<int> o;
+  auto&& r = o.transform([](int) -> unsigned { throw 1; });
+  static_assert( std::is_same_v<decltype(r), std::optional<unsigned>&&> );
+  VERIFY( ! r.has_value() );
+
+  o = 10;
+  auto&& r2 = o.transform([](int i) -> unsigned { return i + 2u; });
+  static_assert( std::is_same_v<decltype(r2), std::optional<unsigned>&&> );
+  VERIFY( *r2 == 12u );
+
+  return true;
+}
+
+static_assert( test_transform() );
+
+enum { CalledLvalue = 1, CalledConst = 2, PassedLvalue = 4, PassedConst = 8 };
+
+struct F
+{
+  template<typename This, typename Value>
+    static constexpr int
+    called_as()
+    {
+      int res = 0;
+      if constexpr (std::is_lvalue_reference_v<This>)
+	res |= CalledLvalue;
+      if constexpr (std::is_const_v<std::remove_reference_t<This>>)
+	res |= CalledConst;
+
+      if constexpr (std::is_lvalue_reference_v<Value>)
+	res |= PassedLvalue;
+      if constexpr (std::is_const_v<std::remove_reference_t<Value>>)
+	res |= PassedConst;
+
+      return res;
+    }
+
+  template<typename T>
+    constexpr int
+    operator()(T&&) &
+    { return called_as<F&, T>(); }
+
+  template<typename T>
+    constexpr int
+    operator()(T&&) const &
+    { return called_as<const F&, T>(); }
+
+  template<typename T>
+    constexpr int
+    operator()(T&&) &&
+    { return called_as<F, T>(); }
+
+  template<typename T>
+    constexpr int
+    operator()(T&&) const &&
+    { return called_as<const F, T>(); }
+};
+
+constexpr bool
+test_forwarding()
+{
+  std::optional<long> o = 1;
+  F f;
+
+  VERIFY( *o.transform(f) == (PassedLvalue|CalledLvalue) );
+  VERIFY( *o.transform(std::move(f)) == PassedLvalue );
+  VERIFY( *std::move(o).transform(f) == CalledLvalue );
+  VERIFY( *std::move(o).transform(std::move(f)) == 0 );
+
+  const auto& co = o;
+
+  VERIFY( *co.transform(f) == (PassedLvalue|PassedConst|CalledLvalue) );
+  VERIFY( *co.transform(std::move(f)) == (PassedLvalue|PassedConst) );
+  VERIFY( *std::move(co).transform(f) == (PassedConst|CalledLvalue) );
+  VERIFY( *std::move(co).transform(std::move(f)) == PassedConst );
+
+  const auto& cf = f;
+
+  VERIFY( *o.transform(cf) == (PassedLvalue|CalledLvalue|CalledConst) );
+  VERIFY( *o.transform(std::move(cf)) == (PassedLvalue|CalledConst) );
+  VERIFY( *std::move(o).transform(cf) == (CalledLvalue|CalledConst) );
+  VERIFY( *std::move(o).transform(std::move(cf)) == CalledConst );
+
+  VERIFY( *co.transform(cf) == (PassedLvalue|PassedConst|CalledLvalue|CalledConst) );
+  VERIFY( *co.transform(std::move(cf)) == (PassedLvalue|PassedConst|CalledConst) );
+  VERIFY( *std::move(co).transform(cf) == (PassedConst|CalledLvalue|CalledConst) );
+  VERIFY( *std::move(co).transform(std::move(cf)) == (PassedConst|CalledConst) );
+
+  o = std::nullopt;
+
+  VERIFY( ! o.transform(f).has_value() );
+  VERIFY( ! co.transform(f).has_value() );
+  VERIFY( ! std::move(o).transform(f).has_value() );
+  VERIFY( ! std::move(co).transform(f).has_value() );
+
+  return true;
+}
+
+static_assert( test_forwarding() );
+
+constexpr bool
+test_copy_elision()
+{
+  return true;
+}
+
+static_assert( test_copy_elision() );
+
+int main()
+{
+  test_transform();
+  test_forwarding();
+  test_copy_elision();
+}
diff --git a/libstdc++-v3/testsuite/20_util/optional/monadic/version.cc b/libstdc++-v3/testsuite/20_util/optional/monadic/version.cc
new file mode 100644
index 00000000000..90b2a90a5df
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/optional/monadic/version.cc
@@ -0,0 +1,10 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do preprocess { target c++23 } }
+
+#include <version>
+
+#ifndef __cpp_lib_monadic_optional
+# error "Feature test macro for monadic optional is missing in <version>"
+#elif __cpp_lib_monadic_optional < 202110L
+# error "Feature test macro for monadic optional has wrong value in <version>"
+#endif


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

only message in thread, other threads:[~2021-10-19 14:01 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-19 14:01 [gcc r12-4506] libstdc++: Implement monadic operations for std::optional (P0798R8) 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).