[libcxx-commits] [libcxx] [libc++] Cooperation between `std::optional` and other standard types (PR #93672)

Mital Ashok via libcxx-commits libcxx-commits at lists.llvm.org
Wed May 29 05:40:16 PDT 2024


https://github.com/MitalAshok updated https://github.com/llvm/llvm-project/pull/93672

>From b063ea3a65d1674dcad639194c052668bc3b2fd3 Mon Sep 17 00:00:00 2001
From: Mital Ashok <mital at mitalashok.co.uk>
Date: Sat, 25 May 2024 07:57:09 +0100
Subject: [PATCH] [libc++] Cooperation between std::optional and other standard
 types

As a proof of concept, support std::optional<std::reference_wrapper<T>> and make it have a similar ABI to T*.
---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__configuration/abi.h          |   3 +
 .../include/__functional/reference_wrapper.h  |  30 ++++
 libcxx/include/__optional/cooperate.h         |  60 +++++++
 libcxx/include/module.modulemap               |   2 +
 libcxx/include/optional                       | 155 +++++++++++++++++-
 .../optional.object.observe/bool.pass.cpp     |  48 ++++++
 .../dereference.pass.cpp                      |  44 +++++
 .../optional.object.swap/swap.pass.cpp        | 109 ++++++++++++
 .../optional.object/triviality.pass.cpp       |  72 ++++++++
 .../cooperate/optional.object/types.pass.cpp  |  32 ++++
 .../optional/cooperate/optional_size.pass.cpp |  43 +++++
 12 files changed, 593 insertions(+), 6 deletions(-)
 create mode 100644 libcxx/include/__optional/cooperate.h
 create mode 100644 libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.observe/bool.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.observe/dereference.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.swap/swap.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/optional/cooperate/optional.object/triviality.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/optional/cooperate/optional.object/types.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/optional/cooperate/optional_size.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index cfe1f44777bca..f0310e30f2c10 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -572,6 +572,7 @@ set(files
   __numeric/transform_exclusive_scan.h
   __numeric/transform_inclusive_scan.h
   __numeric/transform_reduce.h
+  __optional/cooperate.h
   __ostream/basic_ostream.h
   __ostream/print.h
   __pstl/backends/libdispatch.h
diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h
index c570b9aaba48d..dbd66c4ef0f68 100644
--- a/libcxx/include/__configuration/abi.h
+++ b/libcxx/include/__configuration/abi.h
@@ -89,6 +89,9 @@
 // requires code not to make these assumptions.
 #  define _LIBCPP_ABI_USE_WRAP_ITER_IN_STD_ARRAY
 #  define _LIBCPP_ABI_USE_WRAP_ITER_IN_STD_STRING_VIEW
+// Cooperative definition of std::optional<T> for some standard types T
+// such that std::optional<T> holds a T and nothing else
+#  define _LIBCPP_ABI_COOPERATIVE_OPTIONAL 1
 #elif _LIBCPP_ABI_VERSION == 1
 #  if !(defined(_LIBCPP_OBJECT_FORMAT_COFF) || defined(_LIBCPP_OBJECT_FORMAT_XCOFF))
 // Enable compiling copies of now inline methods into the dylib to support
diff --git a/libcxx/include/__functional/reference_wrapper.h b/libcxx/include/__functional/reference_wrapper.h
index ab5d7c7cee115..3bfdb5951c520 100644
--- a/libcxx/include/__functional/reference_wrapper.h
+++ b/libcxx/include/__functional/reference_wrapper.h
@@ -16,6 +16,7 @@
 #include <__functional/invoke.h>
 #include <__functional/weak_result_type.h>
 #include <__memory/addressof.h>
+#include <__optional/cooperate.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/remove_cvref.h>
@@ -115,6 +116,14 @@ class _LIBCPP_TEMPLATE_VIS reference_wrapper : public __weak_result_type<_Tp> {
   }
 
 #endif // _LIBCPP_STD_VER >= 26
+
+#if _LIBCPP_STD_VER >= 17
+  template <class>
+  friend struct __optional::__cooperate;
+
+  constexpr explicit reference_wrapper(__optional::__disengaged_construct, __optional::__disengaged_construct) noexcept
+      : __f_(nullptr) {}
+#endif
 };
 
 #if _LIBCPP_STD_VER >= 17
@@ -149,6 +158,27 @@ void ref(const _Tp&&) = delete;
 template <class _Tp>
 void cref(const _Tp&&) = delete;
 
+#if _LIBCPP_STD_VER >= 17
+
+template <class _Tp>
+struct __optional::__cooperate<reference_wrapper<_Tp>> {
+  static constexpr bool __do_cooperate() { return true; }
+
+  static constexpr bool __is_engaged(const reference_wrapper<_Tp>& __v) { return __v.__f_ != nullptr; }
+
+  template <class... _Args>
+  static constexpr void __construct_over(reference_wrapper<_Tp>& __v, _Args&&... __args) {
+    __v = reference_wrapper<_Tp>(std::forward<_Args>(__args)...);
+  }
+
+  template <class _Up>
+  static constexpr void __disengage(reference_wrapper<_Tp>& __v) {
+    __v.__f_ = nullptr;
+  }
+};
+
+#endif // _LIBCPP_STD_VER >= 17
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___FUNCTIONAL_REFERENCE_WRAPPER_H
diff --git a/libcxx/include/__optional/cooperate.h b/libcxx/include/__optional/cooperate.h
new file mode 100644
index 0000000000000..fd1b09b3cfb96
--- /dev/null
+++ b/libcxx/include/__optional/cooperate.h
@@ -0,0 +1,60 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___OPTIONAL_COOPERATE_H
+#define _LIBCPP___OPTIONAL_COOPERATE_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 17
+
+namespace __optional {
+
+struct __disengaged_construct {
+  constexpr explicit __disengaged_construct() = default;
+#  if _LIBCPP_STD_VER < 20
+
+private:
+  explicit __disengaged_construct(int, int) {} // This is not an aggregate
+#  endif
+};
+
+/// To cooperate with std::optional<T>, specialize std::__optional::__cooperate and implement the member functions as
+/// documented. Must be constructible from _Tp(__disengaged_construct, __disengaged_construct) to be in a "disengaged"
+/// state. An object in a disengaged state never has its destructor called.
+template <class _Tp>
+struct __cooperate {
+  // Return "true" if the cooperated layout should be used
+  static constexpr bool __do_cooperate() { return false; }
+
+  // Return if __v is not in a disengaged state
+  static constexpr bool __is_engaged(const _Tp& /*__v*/) { return false; }
+
+  // Given a __v where !__is_engaged(__v), act as if destroy_at(addressof(__v)); construct_at(addressof(__v), __args...)
+  template <class... _Args>
+  static constexpr void __construct_over(_Tp& /*__v*/, _Args&&... /*__args*/) {}
+
+  // Given a __v, make it so !__is_engaged(__v). __v may already be disengaged
+  template <class _Up>
+  static constexpr void __disengage(_Tp& /*__v*/) {}
+};
+
+} // namespace __optional
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___OPTIONAL_COOPERATE_H
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 48391b2a12095..fdaf368f483fa 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1580,6 +1580,8 @@ module std_private_numeric_transform_exclusive_scan [system] { header "__numeric
 module std_private_numeric_transform_inclusive_scan [system] { header "__numeric/transform_inclusive_scan.h" }
 module std_private_numeric_transform_reduce         [system] { header "__numeric/transform_reduce.h" }
 
+module std_private_optional_cooperate [system] { header "__optional/cooperate.h" }
+
 module std_private_pstl_backends_libdispatch       [system] { header "__pstl/backends/libdispatch.h" }
 module std_private_pstl_backends_serial            [system] { header "__pstl/backends/serial.h" }
 module std_private_pstl_backends_std_thread        [system] { header "__pstl/backends/std_thread.h" }
diff --git a/libcxx/include/optional b/libcxx/include/optional
index 622e150f7a9f7..81e53ea0dbbc0 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -189,6 +189,7 @@ namespace std {
 #include <__fwd/functional.h>
 #include <__memory/addressof.h>
 #include <__memory/construct_at.h>
+#include <__optional/cooperate.h>
 #include <__tuple/sfinae_helpers.h>
 #include <__type_traits/add_pointer.h>
 #include <__type_traits/conditional.h>
@@ -274,11 +275,20 @@ inline constexpr nullopt_t nullopt{nullopt_t::__secret_tag{}, nullopt_t::__secre
 
 struct __optional_construct_from_invoke_tag {};
 
+#ifdef _LIBCPP_ABI_COOPERATIVE_OPTIONAL
+template <class _Tp, bool = is_trivially_destructible<_Tp>::value, bool = __optional::__cooperate<_Tp>::__do_cooperate()>
+#else
 template <class _Tp, bool = is_trivially_destructible<_Tp>::value>
 struct __optional_destruct_base;
+#endif
 
+#ifdef _LIBCPP_ABI_COOPERATIVE_OPTIONAL
+template <class _Tp, bool _Cooperate>
+struct __optional_destruct_base<_Tp, false, false> {
+#else
 template <class _Tp>
 struct __optional_destruct_base<_Tp, false> {
+#endif
   typedef _Tp value_type;
   static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
   union {
@@ -311,10 +321,17 @@ struct __optional_destruct_base<_Tp, false> {
       __engaged_ = false;
     }
   }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return this->__engaged_; }
 };
 
+#ifdef _LIBCPP_ABI_COOPERATIVE_OPTIONAL
+template <class _Tp>
+struct __optional_destruct_base<_Tp, true, false> {
+#else
 template <class _Tp>
 struct __optional_destruct_base<_Tp, true> {
+#endif
   typedef _Tp value_type;
   static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
   union {
@@ -341,15 +358,126 @@ struct __optional_destruct_base<_Tp, true> {
       __engaged_ = false;
     }
   }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return this->__engaged_; }
 };
 
+#ifdef _LIBCPP_ABI_COOPERATIVE_OPTIONAL
+template <class _Tp>
+struct __optional_destruct_base<_Tp, false, true> {
+  using value_type = _Tp;
+  static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
+  union {
+    value_type __val_;
+    value_type __disengaged_state_;
+  };
+
+#if __has_builtin(__builtin_is_within_lifetime)
+  _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get_disengaged() & { return __disengaged_state_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr const value_type& __get_disengaged() const& { return __disengaged_state_; }
+#else
+  _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get_disengaged() & { return __val_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr const value_type& __get_disengaged() const& { return __val_; }
+#endif
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__optional_destruct_base() {
+    if (has_value())
+      __val_.~value_type();
+  }
+
+#if __has_builtin(__builtin_is_within_lifetime)
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __disengaged_state_(__optional::__disengaged_construct(), __optional::__disengaged_construct()) {}
+#else
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __val_(__optional::__disengaged_construct(), __optional::__disengaged_construct()) {}
+#endif
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
+      : __val_(std::forward<_Args>(__args)...) {}
+
+#  if _LIBCPP_STD_VER >= 23
+  template <class _Fp, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base(
+      __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
+      : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)) {}
+#  endif
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
+    if (has_value()) {
+      __val_.~value_type();
+      std::construct_at(std::addressof(__get_disengaged()), __optional::__disengaged_construct(), __optional::__disengaged_construct());
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept {
+#if __has_builtin(__builtin_is_within_lifetime)
+    if consteval {
+      return __builtin_is_within_lifetime(__val_);
+    }
+#endif
+    return __optional::__cooperate<_Tp>::__is_engaged(*std::launder(std::addressof(__val_)));
+  }
+};
+
+template <class _Tp>
+struct __optional_destruct_base<_Tp, true, true> {
+  using value_type = _Tp;
+  static_assert(is_object_v<value_type>, "instantiation of optional with a non-object type is undefined behavior");
+  union {
+    value_type __disengaged_val_;
+    value_type __val_;
+  };
+
+#if __has_builtin(__builtin_is_within_lifetime)
+  _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get_disengaged() & { return __disengaged_state_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr const value_type& __get_disengaged() const& { return __disengaged_state_; }
+#else
+  _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get_disengaged() & { return __val_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr const value_type& __get_disengaged() const& { return __val_; }
+#endif
+
+#if __has_builtin(__builtin_is_within_lifetime)
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __disengaged_state_(__optional::__disengaged_construct(), __optional::__disengaged_construct()) {}
+#else
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base() noexcept : __val_(__optional::__disengaged_construct(), __optional::__disengaged_construct()) {}
+#endif
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_destruct_base(in_place_t, _Args&&... __args)
+      : __val_(std::forward<_Args>(__args)...), __engaged_(true) {}
+
+#  if _LIBCPP_STD_VER >= 23
+  template <class _Fp, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_destruct_base(
+      __optional_construct_from_invoke_tag, _Fp&& __f, _Args&&... __args)
+      : __val_(std::invoke(std::forward<_Fp>(__f), std::forward<_Args>(__args)...)), __engaged_(true) {}
+#  endif
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
+    if (has_value()) {
+      __val_.~value_type();
+      std::construct_at(std::addressof(__get_disengaged()), __optional::__disengaged_construct(), __optional::__disengaged_construct());
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept {
+#if __has_builtin(__builtin_is_within_lifetime)
+    if consteval {
+      return __builtin_is_within_lifetime(__val_);
+    }
+#endif
+    return __optional::__cooperate<_Tp>::__is_engaged(*std::launder(std::addressof(__val_)));
+  }
+};
+#endif
+
 template <class _Tp, bool = is_reference<_Tp>::value>
 struct __optional_storage_base : __optional_destruct_base<_Tp> {
   using __base     = __optional_destruct_base<_Tp>;
   using value_type = _Tp;
   using __base::__base;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return this->__engaged_; }
+  _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __base::has_value(); }
 
   _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() & noexcept { return this->__val_; }
   _LIBCPP_HIDE_FROM_ABI constexpr const value_type& __get() const& noexcept { return this->__val_; }
@@ -359,8 +487,23 @@ struct __optional_storage_base : __optional_destruct_base<_Tp> {
   template <class... _Args>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_Args&&... __args) {
     _LIBCPP_ASSERT_INTERNAL(!has_value(), "__construct called for engaged __optional_storage");
-    std::__construct_at(std::addressof(this->__val_), std::forward<_Args>(__args)...);
-    this->__engaged_ = true;
+#ifdef _LIBCPP_ABI_COOPERATIVE_OPTIONAL
+    if constexpr (__optional::__cooperate<_Tp>::__do_cooperate()) {
+#if __has_builtin(__builtin_is_within_lifetime)
+      if consteval {
+        // No operations are ever done on a disengaged value. Ensure it is not destroyed.
+        std::__construct_at(std::addressof(this->__val_), std::forward<_Args>(__args)...);
+        return;
+      }
+#endif
+      __optional::__cooperate<_Tp>::__construct_over(*std::launder(std::addressof(this->__val_)), std::forward<_Args>(__args)...);
+    } else {
+#endif
+      std::__construct_at(std::addressof(this->__val_), std::forward<_Args>(__args)...);
+      this->__engaged_ = true;
+#ifdef _LIBCPP_ABI_COOPERATIVE_OPTIONAL
+    }
+#endif
   }
 
   template <class _That>
@@ -371,11 +514,11 @@ struct __optional_storage_base : __optional_destruct_base<_Tp> {
 
   template <class _That>
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from(_That&& __opt) {
-    if (this->__engaged_ == __opt.has_value()) {
-      if (this->__engaged_)
+    if (this->has_value() == __opt.has_value()) {
+      if (this->has_value())
         this->__val_ = std::forward<_That>(__opt).__get();
     } else {
-      if (this->__engaged_)
+      if (this->has_value())
         this->reset();
       else
         __construct(std::forward<_That>(__opt).__get());
diff --git a/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.observe/bool.pass.cpp b/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.observe/bool.pass.cpp
new file mode 100644
index 0000000000000..f03deda22bcc0
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.observe/bool.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+// <optional>
+
+// constexpr explicit optional<T>::operator bool() const noexcept;
+
+#include <optional>
+#include <functional>
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+
+using std::optional;
+
+template <typename T, typename F>
+constexpr bool test(F init) {
+  {
+    const optional<T> opt;
+    (void)opt;
+    ASSERT_NOEXCEPT(bool(opt));
+    static_assert(!std::is_convertible_v<optional<T>, bool>);
+  }
+  {
+    constexpr optional<T> opt;
+    static_assert(!opt);
+  }
+  {
+    constexpr optional<T> opt(init());
+    static_assert(opt);
+  }
+  return true;
+}
+
+int f() { return 0; }
+
+int main(int, char**) {
+  static int i;
+  test<std::reference_wrapper<int>>([]() -> auto& { return i; });
+  test<std::reference_wrapper<int()>>([]() -> auto& { return f; });
+}
diff --git a/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.observe/dereference.pass.cpp b/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.observe/dereference.pass.cpp
new file mode 100644
index 0000000000000..d0ffc216ab4e5
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.observe/dereference.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+// <optional>
+
+// constexpr T& optional<T>::operator*() &;
+
+#include <optional>
+#include <type_traits>
+#include <functional>
+#include <cassert>
+
+#include "test_macros.h"
+
+using std::optional;
+
+template <typename T, typename G>
+constexpr auto test(T init, G test) {
+  {
+    optional<T> opt;
+    (void)opt;
+    ASSERT_SAME_TYPE(decltype(*opt), T&);
+    LIBCPP_STATIC_ASSERT(noexcept(*opt));
+  }
+
+  optional<T> opt(init);
+  return test(*opt);
+}
+
+int main(int, char**) {
+  {
+    static int i;
+    assert(test<std::reference_wrapper<int>>(i, [](std::reference_wrapper<int>& r) { return &r.get(); }) == &i);
+#if TEST_STD_VER > 17
+    static_assert(test<std::reference_wrapper<int>>(i, [](std::reference_wrapper<int>& r) { return &r.get(); }) == &i);
+#endif
+  }
+}
diff --git a/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.swap/swap.pass.cpp b/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.swap/swap.pass.cpp
new file mode 100644
index 0000000000000..c056ebbe9ba4f
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/optional.object.swap/swap.pass.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+// <optional>
+
+// void swap(optional&)
+//     noexcept(is_nothrow_move_constructible<T>::value &&
+//              is_nothrow_swappable<T>::value)
+
+#include <optional>
+#include <type_traits>
+#include <functional>
+#include <cassert>
+
+#include "test_macros.h"
+
+using std::optional;
+
+template <typename T>
+struct equal_to {
+  constexpr bool operator()(T& l, T& r) const noexcept { return l == r; }
+};
+
+template <typename T>
+struct equal_to<std::reference_wrapper<T>> {
+  constexpr bool operator()(std::reference_wrapper<T>& l, std::reference_wrapper<T>& r) const noexcept {
+    return std::addressof(l.get()) == std::addressof(r.get());
+  }
+};
+
+template <class T>
+TEST_CONSTEXPR_CXX20 bool check_swap(T left, T right) {
+  constexpr equal_to<T> is_equal;
+  {
+    optional<T> opt1;
+    optional<T> opt2;
+    static_assert(
+        noexcept(opt1.swap(opt2)) == (std::is_nothrow_move_constructible_v<T> && std::is_nothrow_swappable_v<T>));
+    assert(static_cast<bool>(opt1) == false);
+    assert(static_cast<bool>(opt2) == false);
+    opt1.swap(opt2);
+    assert(static_cast<bool>(opt1) == false);
+    assert(static_cast<bool>(opt2) == false);
+  }
+  {
+    optional<T> opt1(left);
+    optional<T> opt2;
+    static_assert(
+        noexcept(opt1.swap(opt2)) == (std::is_nothrow_move_constructible_v<T> && std::is_nothrow_swappable_v<T>));
+    assert(static_cast<bool>(opt1) == true);
+    assert(is_equal(*opt1, left));
+    assert(static_cast<bool>(opt2) == false);
+    opt1.swap(opt2);
+    assert(static_cast<bool>(opt1) == false);
+    assert(static_cast<bool>(opt2) == true);
+    assert(is_equal(*opt2, left));
+  }
+  {
+    optional<T> opt1;
+    optional<T> opt2(right);
+    static_assert(
+        noexcept(opt1.swap(opt2)) == (std::is_nothrow_move_constructible_v<T> && std::is_nothrow_swappable_v<T>));
+    assert(static_cast<bool>(opt1) == false);
+    assert(static_cast<bool>(opt2) == true);
+    assert(is_equal(*opt2, right));
+    opt1.swap(opt2);
+    assert(static_cast<bool>(opt1) == true);
+    assert(is_equal(*opt1, right));
+    assert(static_cast<bool>(opt2) == false);
+  }
+  {
+    optional<T> opt1(left);
+    optional<T> opt2(right);
+    static_assert(
+        noexcept(opt1.swap(opt2)) == (std::is_nothrow_move_constructible_v<T> && std::is_nothrow_swappable_v<T>));
+    assert(static_cast<bool>(opt1) == true);
+    assert(is_equal(*opt1, left));
+    assert(static_cast<bool>(opt2) == true);
+    assert(is_equal(*opt2, right));
+    opt1.swap(opt2);
+    assert(static_cast<bool>(opt1) == true);
+    assert(is_equal(*opt1, right));
+    assert(static_cast<bool>(opt2) == true);
+    assert(is_equal(*opt2, left));
+  }
+  return true;
+}
+
+int f() noexcept { return 0; }
+int g() noexcept { return 0; }
+
+int main(int, char**) {
+  int i, j;
+  check_swap<std::reference_wrapper<int>>(i, j);
+  check_swap<std::reference_wrapper<const int>>(i, j);
+  check_swap<std::reference_wrapper<int()>>(f, g);
+  check_swap<std::reference_wrapper<int() noexcept>>(f, g);
+#if TEST_STD_VER > 17
+  static int ii, jj;
+  static_assert(check_swap<std::reference_wrapper<const int>>(ii, jj));
+  static_assert(check_swap<std::reference_wrapper<int()>>(f, g));
+#endif
+}
diff --git a/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/triviality.pass.cpp b/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/triviality.pass.cpp
new file mode 100644
index 0000000000000..5b18e0fbc026a
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/triviality.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <optional>
+
+// The following special member functions should propagate the triviality of
+// the element held in the optional (see P0602R4):
+//
+// constexpr optional(const optional& rhs);
+// constexpr optional(optional&& rhs) noexcept(see below);
+// constexpr optional<T>& operator=(const optional& rhs);
+// constexpr optional<T>& operator=(optional&& rhs) noexcept(see below);
+
+#include <optional>
+#include <functional>
+#include <type_traits>
+
+#include "archetypes.h"
+
+#include "test_macros.h"
+
+constexpr bool implies(bool p, bool q) { return !p || q; }
+
+template <class T>
+struct SpecialMemberTest {
+  using O = std::optional<T>;
+
+  static_assert(implies(std::is_trivially_copy_constructible_v<T>, std::is_trivially_copy_constructible_v<O>),
+                "optional<T> is trivially copy constructible if T is trivially copy constructible.");
+
+  static_assert(implies(std::is_trivially_move_constructible_v<T>, std::is_trivially_move_constructible_v<O>),
+                "optional<T> is trivially move constructible if T is trivially move constructible");
+
+  static_assert(implies(std::is_trivially_copy_constructible_v<T> && std::is_trivially_copy_assignable_v<T> &&
+                            std::is_trivially_destructible_v<T>,
+
+                        std::is_trivially_copy_assignable_v<O>),
+                "optional<T> is trivially copy assignable if T is "
+                "trivially copy constructible, "
+                "trivially copy assignable, and "
+                "trivially destructible");
+
+  static_assert(implies(std::is_trivially_move_constructible_v<T> && std::is_trivially_move_assignable_v<T> &&
+                            std::is_trivially_destructible_v<T>,
+
+                        std::is_trivially_move_assignable_v<O>),
+                "optional<T> is trivially move assignable if T is "
+                "trivially move constructible, "
+                "trivially move assignable, and"
+                "trivially destructible.");
+};
+
+template <class... Args>
+static void sink(Args&&...) {}
+
+template <class... TestTypes>
+struct DoTestsMetafunction {
+  DoTestsMetafunction() { sink(SpecialMemberTest<TestTypes>{}...); }
+};
+
+int main(int, char**) {
+  DoTestsMetafunction< std::reference_wrapper<int>,
+                       std::reference_wrapper<int()>,
+                       std::reference_wrapper<int() noexcept> >{};
+}
diff --git a/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/types.pass.cpp b/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/types.pass.cpp
new file mode 100644
index 0000000000000..d012e9157f3fb
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/optional/cooperate/optional.object/types.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+// <optional>
+
+// template <class T>
+// class optional
+// {
+// public:
+//     typedef T value_type;
+//     ...
+
+#include <optional>
+#include <functional>
+#include <type_traits>
+
+#include "test_macros.h"
+
+using std::optional;
+
+template <class Opt, class T>
+void test() {
+  static_assert(std::is_same<typename Opt::value_type, T>::value, "");
+}
+
+int main(int, char**) { test<optional<std::reference_wrapper<int>>, std::reference_wrapper<int>>(); }
diff --git a/libcxx/test/libcxx/utilities/optional/cooperate/optional_size.pass.cpp b/libcxx/test/libcxx/utilities/optional/cooperate/optional_size.pass.cpp
new file mode 100644
index 0000000000000..786187ac53a9b
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/optional/cooperate/optional_size.pass.cpp
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// <optional>
+
+// template <class T> class optional;
+
+#include <optional>
+#include <functional>
+
+template <class T>
+struct type_with_bool {
+  T value;
+  bool has_value;
+};
+
+template <class T>
+using cooperative_type =
+#ifdef _LIBCPP_ABI_COOPERATIVE_OPTIONAL
+    T;
+#else
+    type_with_bool<T>;
+#endif
+
+int main(int, char**) {
+  static_assert(
+      sizeof(std::optional<std::reference_wrapper<int>>) == sizeof(cooperative_type<std::reference_wrapper<int>>));
+  static_assert(sizeof(std::optional<const std::reference_wrapper<int>>) ==
+                sizeof(type_with_bool<const std::reference_wrapper<int>>));
+  static_assert(
+      sizeof(std::optional<std::reference_wrapper<int()>>) == sizeof(cooperative_type<std::reference_wrapper<int()>>));
+  static_assert(sizeof(std::optional<const std::reference_wrapper<int()>>) ==
+                sizeof(type_with_bool<const std::reference_wrapper<int()>>));
+
+  return 0;
+}



More information about the libcxx-commits mailing list