Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[libc++] Implement std::move_only_function (P0288R9) #94670

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: users/philnik777/spr/add_pointer_int_pair
Choose a base branch
Loading
from

Conversation

philnik777
Copy link
Contributor

@philnik777 philnik777 commented Jun 6, 2024

Closes #105157

@philnik777 philnik777 requested a review from a team as a code owner June 6, 2024 19:36
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Jun 6, 2024
@llvmbot
Copy link
Member

llvmbot commented Jun 6, 2024

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

Changes
  • [libc++] Add __pointer_int_pair
  • [libc++] Implement P0288R9 (move_only_function)

Patch is 217.38 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/94670.diff

51 Files Affected:

  • (modified) libcxx/docs/FeatureTestMacroTable.rst (+1-1)
  • (modified) libcxx/docs/ReleaseNotes/19.rst (+1)
  • (modified) libcxx/docs/Status/Cxx23Papers.csv (+1-1)
  • (modified) libcxx/include/CMakeLists.txt (+4)
  • (modified) libcxx/include/__configuration/abi.h (+3-1)
  • (added) libcxx/include/__functional/move_only_function.h (+93)
  • (added) libcxx/include/__functional/move_only_function_common.h (+46)
  • (added) libcxx/include/__functional/move_only_function_impl.h (+233)
  • (modified) libcxx/include/__std_clang_module (+1)
  • (added) libcxx/include/__utility/pointer_int_pair.h (+157)
  • (modified) libcxx/include/functional (+1)
  • (modified) libcxx/include/module.modulemap (+4)
  • (modified) libcxx/include/version (+1-1)
  • (modified) libcxx/modules/std.cppm.in (+1)
  • (added) libcxx/test/libcxx/private_headers.verify.cpp (+717)
  • (added) libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.move/assert.engaged.cpp (+20)
  • (added) libcxx/test/libcxx/utilities/pointer_int_pair.pass.cpp (+37)
  • (added) libcxx/test/libcxx/utilities/pointer_int_pair/assert.constructor.pass.cpp (+44)
  • (added) libcxx/test/libcxx/utilities/pointer_int_pair/constinit.verify.cpp (+21)
  • (added) libcxx/test/libcxx/utilities/pointer_int_pair/pointer_int_pair.pass.cpp (+104)
  • (added) libcxx/test/libcxx/utilities/pointer_int_pair/static_asserts.verify.cpp (+23)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp (+10-22)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp (+10-22)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/assignment/functor.pass.cpp (+84)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/assignment/move.pass.cpp (+91)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/assignment/move_other.pass.cpp (+62)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/assignment/nullptr.pass.cpp (+73)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/lvalue.pass.cpp (+102)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/lvalue_const.pass.cpp (+118)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/lvalue_const_noexcept.pass.cpp (+113)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/lvalue_noexcept.pass.cpp (+102)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/normal.pass.cpp (+106)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/normal_const.pass.cpp (+112)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/normal_const_noexcept.pass.cpp (+112)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/normal_noexcept.pass.cpp (+106)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue.pass.cpp (+104)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue_const.pass.cpp (+107)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue_const_noexcept.pass.cpp (+107)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue_noexcept.pass.cpp (+104)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/common.h (+80)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/ctors/default.pass.cpp (+28)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/ctors/functor.pass.cpp (+115)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/ctors/in_place.pass.cpp (+46)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/ctors/in_place_init_list.pass.cpp (+46)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/ctors/move.pass.cpp (+106)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/ctors/move_other.pass.cpp (+44)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/ctors/nullptr.pass.cpp (+28)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/swap.adl.pass.cpp (+73)
  • (added) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/swap.member.pass.cpp (+73)
  • (modified) libcxx/test/support/type_algorithms.h (+36)
  • (modified) libcxx/utils/generate_feature_test_macro_components.py (-1)
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 0297068785e8b..69296fdc98ae2 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -340,7 +340,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_mdspan``                                       ``202207L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_move_only_function``                           *unimplemented*
+    ``__cpp_lib_move_only_function``                           ``202110L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_optional``                                     ``202110L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/19.rst b/libcxx/docs/ReleaseNotes/19.rst
index 0bc343acd281c..9e142d2bdda92 100644
--- a/libcxx/docs/ReleaseNotes/19.rst
+++ b/libcxx/docs/ReleaseNotes/19.rst
@@ -54,6 +54,7 @@ Implemented Papers
 - P2713R1 - Escaping improvements in ``std::format``
 - P2231R1 - Missing ``constexpr`` in ``std::optional`` and ``std::variant``
 - P0019R8 - ``std::atomic_ref``
+- P0288R9 - ``move_only_function``
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 01387a404f5d6..b5da5cad5f060 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -23,7 +23,7 @@
 "`P2136R3 <https://wg21.link/P2136R3>`__","LWG","invoke_r","June 2021","|Complete|","17.0"
 "`P2166R1 <https://wg21.link/P2166R1>`__","LWG","A Proposal to Prohibit std::basic_string and std::basic_string_view construction from nullptr","June 2021","|Complete|","13.0"
 "","","","","","",""
-"`P0288R9 <https://wg21.link/P0288R9>`__","LWG","``any_invocable``","October 2021","",""
+"`P0288R9 <https://wg21.link/P0288R9>`__","LWG","``move_only_function``","October 2021","|Complete|","19.0"
 "`P0798R8 <https://wg21.link/P0798R8>`__","LWG","Monadic operations for ``std::optional``","October 2021","|Complete|","14.0"
 "`P0849R8 <https://wg21.link/P0849R8>`__","LWG","``auto(x)``: ``DECAY_COPY`` in the language","October 2021","|Complete|","14.0"
 "`P1072R10 <https://wg21.link/P1072R10>`__","LWG","``basic_string::resize_and_overwrite``","October 2021","|Complete|","14.0"
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index cfe1f44777bca..17573d3e67de8 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -399,6 +399,9 @@ set(files
   __functional/is_transparent.h
   __functional/mem_fn.h
   __functional/mem_fun_ref.h
+  __functional/move_only_function.h
+  __functional/move_only_function_common.h
+  __functional/move_only_function_impl.h
   __functional/not_fn.h
   __functional/operations.h
   __functional/perfect_forward.h
@@ -857,6 +860,7 @@ set(files
   __utility/no_destroy.h
   __utility/pair.h
   __utility/piecewise_construct.h
+  __utility/pointer_int_pair.h
   __utility/priority_tag.h
   __utility/private_constructor_tag.h
   __utility/rel_ops.h
diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h
index 17aceb042f524..31c23ff7fa889 100644
--- a/libcxx/include/__configuration/abi.h
+++ b/libcxx/include/__configuration/abi.h
@@ -90,7 +90,9 @@
 #  define _LIBCPP_ABI_USE_WRAP_ITER_IN_STD_ARRAY
 #  define _LIBCPP_ABI_USE_WRAP_ITER_IN_STD_STRING_VIEW
 // Dont' add an inline namespace for `std::filesystem`
-#    define _LIBCPP_ABI_NO_FILESYSTEM_INLINE_NAMESPACE
+#  define _LIBCPP_ABI_NO_FILESYSTEM_INLINE_NAMESPACE
+// Enable clang::trivial_abi for std::move_only_function
+#  define _LIBCPP_ABI_SMALL_BUFFER_TRIVIAL_ABI
 #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/move_only_function.h b/libcxx/include/__functional/move_only_function.h
new file mode 100644
index 0000000000000..4cda276e37d7f
--- /dev/null
+++ b/libcxx/include/__functional/move_only_function.h
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___FUNCTIONAL_MOVE_ONLY_FUNCTION_H
+#define _LIBCPP___FUNCTIONAL_MOVE_ONLY_FUNCTION_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 23
+
+// move_only_function design:
+//
+// move_only_function has a small buffer with a size of `3 * sizeof(void*)` bytes. This buffer can only be used when the
+// object that should be stored is trivially relocatable (currently only when it is trivially move constructible and
+// trivially destructible). There is also a bool in the lower bits of the vptr stored which is set when the contained
+// object is not trivially destructible.
+//
+// trivially relocatable: It would also be possible to store nothrow_move_constructible types, but that would mean
+// that move_only_function itself would not be trivially relocatable anymore. The decision to keep move_only_function
+// trivially relocatable was made because we expect move_only_function to be mostly used to store a functor. To only
+// forward functors there is std::function_ref (not voted in yet, expected in C++26).
+//
+// buffer size: We did a survey of six implementations from various vendors. Three of them had a buffer size of 24 bytes
+// on 64 bit systems. This also allows storing a std::string or std::vector inside the small buffer (once the compiler
+// has full support of trivially_relocatable annotations).
+//
+// trivially-destructible bit: This allows us to keep the overall binary size smaller because we don't have to store
+// a pointer to a noop function inside the vtable. It also avoids loading the vtable during destruction, potentially
+// resulting in fewer cache misses. The downside is that calling the function now also requires setting the lower bits
+// of the pointer to zero, but this is a very fast operation on modern CPUs.
+
+// NOLINTBEGIN(readability-duplicate-include)
+#  define _LIBCPP_IN_MOVE_ONLY_FUNCTION_H
+
+#  include <__functional/move_only_function_impl.h>
+
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_REF &
+#  include <__functional/move_only_function_impl.h>
+
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_REF &&
+#  include <__functional/move_only_function_impl.h>
+
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_CV const
+#  include <__functional/move_only_function_impl.h>
+
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_CV const
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_REF &
+#  include <__functional/move_only_function_impl.h>
+
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_CV const
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_REF &&
+#  include <__functional/move_only_function_impl.h>
+
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_NOEXCEPT true
+#  include <__functional/move_only_function_impl.h>
+
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_NOEXCEPT true
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_REF &
+#  include <__functional/move_only_function_impl.h>
+
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_NOEXCEPT true
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_REF &&
+#  include <__functional/move_only_function_impl.h>
+
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_NOEXCEPT true
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_CV const
+#  include <__functional/move_only_function_impl.h>
+
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_NOEXCEPT true
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_CV const
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_REF &
+#  include <__functional/move_only_function_impl.h>
+
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_NOEXCEPT true
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_CV const
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_REF &&
+#  include <__functional/move_only_function_impl.h>
+
+#  undef _LIBCPP_IN_MOVE_ONLY_FUNCTION_H
+// NOLINTEND(readability-duplicate-include)
+
+#endif // _LIBCPP_STD_VER > 20
+
+#endif // _LIBCPP___FUNCTIONAL_MOVE_ONLY_FUNCTION_H
diff --git a/libcxx/include/__functional/move_only_function_common.h b/libcxx/include/__functional/move_only_function_common.h
new file mode 100644
index 0000000000000..45978bfc91aad
--- /dev/null
+++ b/libcxx/include/__functional/move_only_function_common.h
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___FUNCTIONAL_MOVE_ONLY_FUNCTION_COMMON_H
+#define _LIBCPP___FUNCTIONAL_MOVE_ONLY_FUNCTION_COMMON_H
+
+#include <__config>
+#include <__type_traits/integral_constant.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class...>
+class move_only_function;
+
+template <class>
+struct __is_move_only_function : false_type {};
+
+template <class... _Args>
+struct __is_move_only_function<move_only_function<_Args...>> : true_type {};
+
+template <class _BufferT, class _ReturnT, class... _ArgTypes>
+struct _MoveOnlyFunctionTrivialVTable {
+  using _CallFunc = _ReturnT(_BufferT&, _ArgTypes...);
+
+  _CallFunc* __call_;
+};
+
+template <class _BufferT, class _ReturnT, class... _ArgTypes>
+struct _MoveOnlyFunctionNonTrivialVTable : _MoveOnlyFunctionTrivialVTable<_BufferT, _ReturnT, _ArgTypes...> {
+  using _DestroyFunc = void(_BufferT&) noexcept;
+
+  _DestroyFunc* __destroy_;
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FUNCTIONAL_MOVE_ONLY_FUNCTION_COMMON_H
diff --git a/libcxx/include/__functional/move_only_function_impl.h b/libcxx/include/__functional/move_only_function_impl.h
new file mode 100644
index 0000000000000..9d006ea346162
--- /dev/null
+++ b/libcxx/include/__functional/move_only_function_impl.h
@@ -0,0 +1,233 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// This header is unguarded on purpose. This header is an implementation detail of move_only_function.h
+// and generates multiple versions of std::move_only_function
+
+#include <__config>
+#include <__functional/invoke.h>
+#include <__functional/move_only_function_common.h>
+#include <__type_traits/is_trivially_destructible.h>
+#include <__utility/exchange.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+#include <__utility/move.h>
+#include <__utility/pointer_int_pair.h>
+#include <__utility/small_buffer.h>
+#include <__utility/swap.h>
+#include <cstddef>
+#include <cstring>
+#include <initializer_list>
+#include <new>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#ifndef _LIBCPP_IN_MOVE_ONLY_FUNCTION_H
+#  error This header should only be included from move_only_function.h
+#endif
+
+#ifndef _LIBCPP_MOVE_ONLY_FUNCTION_CV
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_CV
+#endif
+
+#ifndef _LIBCPP_MOVE_ONLY_FUNCTION_REF
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_REF
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_INVOKE_QUALS _LIBCPP_MOVE_ONLY_FUNCTION_CV&
+#else
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_INVOKE_QUALS _LIBCPP_MOVE_ONLY_FUNCTION_CV _LIBCPP_MOVE_ONLY_FUNCTION_REF
+#endif
+
+#ifndef _LIBCPP_MOVE_ONLY_FUNCTION_NOEXCEPT
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_NOEXCEPT false
+#endif
+
+#define _LIBCPP_MOVE_ONLY_FUNCTION_CVREF _LIBCPP_MOVE_ONLY_FUNCTION_CV _LIBCPP_MOVE_ONLY_FUNCTION_REF
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#ifdef _LIBCPP_ABI_MOVE_ONLY_FUNCTION_TRIVIAL_ABI
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_TRIVIAL_ABI [[_Clang::__trivial_abi__]]
+#else
+#  define _LIBCPP_MOVE_ONLY_FUNCTION_TRIVIAL_ABI
+#endif
+
+template <class...>
+class move_only_function;
+
+template <class _ReturnT, class... _ArgTypes>
+class _LIBCPP_MOVE_ONLY_FUNCTION_TRIVIAL_ABI move_only_function<_ReturnT(
+    _ArgTypes...) _LIBCPP_MOVE_ONLY_FUNCTION_CVREF noexcept(_LIBCPP_MOVE_ONLY_FUNCTION_NOEXCEPT)> {
+private:
+  static constexpr size_t __buffer_size_      = 3 * sizeof(void*);
+  static constexpr size_t __buffer_alignment_ = alignof(void*);
+  using _BufferT                              = __small_buffer<__buffer_size_, __buffer_alignment_>;
+
+  using _TrivialVTable    = _MoveOnlyFunctionTrivialVTable<_BufferT, _ReturnT, _ArgTypes...>;
+  using _NonTrivialVTable = _MoveOnlyFunctionNonTrivialVTable<_BufferT, _ReturnT, _ArgTypes...>;
+
+  template <class _Functor>
+  static constexpr _TrivialVTable __trivial_vtable_ = {
+      .__call_ = [](_BufferT& __buffer, _ArgTypes... __args) noexcept(_LIBCPP_MOVE_ONLY_FUNCTION_NOEXCEPT) -> _ReturnT {
+        return std::invoke_r<_ReturnT>(
+            static_cast<_Functor _LIBCPP_MOVE_ONLY_FUNCTION_INVOKE_QUALS>(*__buffer.__get<_Functor>()),
+            std::forward<_ArgTypes>(__args)...);
+      }};
+
+  template <class _Functor>
+  static constexpr _NonTrivialVTable __non_trivial_vtable_{
+      __trivial_vtable_<_Functor>,
+      [](_BufferT& __buffer) noexcept -> void {
+        std::destroy_at(__buffer.__get<_Functor>());
+        __buffer.__dealloc<_Functor>();
+      },
+  };
+
+  template <class _Functor>
+  _LIBCPP_HIDE_FROM_ABI __pointer_bool_pair<const _TrivialVTable*> __get_vptr() {
+    if constexpr (_BufferT::__fits_in_buffer<_Functor> && is_trivially_destructible_v<_Functor>) {
+      return {&__trivial_vtable_<_Functor>, false};
+    } else {
+      return {&__non_trivial_vtable_<_Functor>, true};
+    }
+  }
+
+  template <class _VT>
+  static constexpr bool __is_callable_from = [] {
+    using _DVT = decay_t<_VT>;
+    if (_LIBCPP_MOVE_ONLY_FUNCTION_NOEXCEPT) {
+      return is_nothrow_invocable_r_v<_ReturnT, _DVT _LIBCPP_MOVE_ONLY_FUNCTION_CVREF, _ArgTypes...> &&
+             is_nothrow_invocable_r_v<_ReturnT, _DVT _LIBCPP_MOVE_ONLY_FUNCTION_INVOKE_QUALS, _ArgTypes...>;
+    } else {
+      return is_invocable_r_v<_ReturnT, _DVT _LIBCPP_MOVE_ONLY_FUNCTION_CVREF, _ArgTypes...> &&
+             is_invocable_r_v<_ReturnT, _DVT _LIBCPP_MOVE_ONLY_FUNCTION_INVOKE_QUALS, _ArgTypes...>;
+    }
+  }();
+
+  template <class _Func, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void __construct(_Args&&... __args) {
+    static_assert(is_constructible_v<decay_t<_Func>, _Func>);
+
+    using _StoredFunc = decay_t<_Func>;
+    __vtable_         = __get_vptr<_StoredFunc>();
+    __buffer_.__construct<_StoredFunc>(std::forward<_Args>(__args)...);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI void __reset() {
+    if (__vtable_.__get_value())
+      static_cast<const _NonTrivialVTable*>(__vtable_.__get_ptr())->__destroy_(__buffer_);
+    __vtable_ = {};
+  }
+
+public:
+  using result_type = _ReturnT;
+
+  // [func.wrap.move.ctor]
+  move_only_function() noexcept = default;
+  _LIBCPP_HIDE_FROM_ABI move_only_function(nullptr_t) noexcept {}
+  _LIBCPP_HIDE_FROM_ABI move_only_function(move_only_function&& __other) noexcept
+      : __vtable_(__other.__vtable_), __buffer_(std::move(__other.__buffer_)) {
+    __other.__vtable_ = {};
+  }
+
+  template <class _Func>
+    requires(!is_same_v<remove_cvref_t<_Func>, move_only_function> && !__is_inplace_type<_Func>::value &&
+             __is_callable_from<_Func>)
+  _LIBCPP_HIDE_FROM_ABI move_only_function(_Func&& __func) noexcept {
+    using _StoredFunc = decay_t<_Func>;
+
+    if constexpr ((is_pointer_v<_StoredFunc> && is_function_v<remove_pointer_t<_StoredFunc>>) ||
+                  is_member_function_pointer_v<_StoredFunc>) {
+      if (__func != nullptr) {
+        __vtable_ = __get_vptr<_StoredFunc>();
+        static_assert(_BufferT::__fits_in_buffer<_StoredFunc>);
+        __buffer_.__construct<_StoredFunc>(std::forward<_Func>(__func));
+      }
+    } else if constexpr (__is_move_only_function<_StoredFunc>::value) {
+      if (__func) {
+        __vtable_ = std::exchange(__func.__vtable_, {});
+        __buffer_ = std::move(__func.__buffer_);
+      }
+    } else {
+      __construct<_Func>(std::forward<_Func>(__func));
+    }
+  }
+
+  template <class _Func, class... _Args>
+    requires is_constructible_v<decay_t<_Func>, _Args...> && __is_callable_from<_Func>
+  _LIBCPP_HIDE_FROM_ABI explicit move_only_function(in_place_type_t<_Func>, _Args&&... __args) {
+    static_assert(is_same_v<decay_t<_Func>, _Func>);
+    __construct<_Func>(std::forward<_Args>(__args)...);
+  }
+
+  template <class _Func, class _InitListType, class... _Args>
+    requires is_constructible_v<decay_t<_Func>, initializer_list<_InitListType>&, _Args...> && __is_callable_from<_Func>
+  _LIBCPP_HIDE_FROM_ABI explicit move_only_function(
+      in_place_type_t<_Func>, initializer_list<_InitListType> __il, _Args&&... __args) {
+    static_assert(is_same_v<decay_t<_Func>, _Func>);
+    __construct<_Func>(__il, std::forward<_Args>(__args)...);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI move_only_function& operator=(move_only_function&& __other) noexcept {
+    move_only_function(std::move(__other)).swap(*this);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI move_only_function& operator=(nullptr_t) noexcept {
+    __reset();
+    return *this;
+  }
+
+  template <class _Func>
+    requires(!is_same_v<remove_cvref_t<_Func>, move_only_function> && !__is_inplace_type<_Func>::value &&
+             __is_callable_from<_Func>)
+  _LIBCPP_HIDE_FROM_ABI move_only_function& operator=(_Func&& __func) {
+    move_only_function(std::forward<_Func>(__func)).swap(*this);
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI ~move_only_function() { __reset(); }
+
+  // [func.wrap.move.inv]
+  _LIBCPP_HIDE_FROM_ABI explicit operator bool() const noexcept { return __vtable_.__get_ptr() != nullptr; }
+
+  _LIBCPP_HIDE_FROM_ABI _ReturnT operator()(_ArgTypes... __args) _LIBCPP_MOVE_ONLY_FUNCTION_CVREF
+      noexcept(_LIBCPP_MOVE_ONLY_FUNCTION_NOEXCEPT) {
+    _LIBCPP_ASSERT(static_cast<bool>(*this), "Tried to call a disengaged move_only_function");
+    const auto __call = static_cast<_ReturnT (*)(_BufferT&, _ArgTypes...)>(__vtable_.__get_ptr()->__call_);
+    return __call(__buffer_, std::forward<_ArgTypes>(__args)...);
+  }
+
+  // [func.wrap.move.util]
+  _LIBCPP_HIDE_FROM_ABI void swap(move_only_function& __other) noexcept {
+    std::swap(__vtable_, __other.__vtable_);
+    std::swap(__buffer_, __other.__buffer_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend void swap(move_only_function& __lhs, move_only_function& __rhs) noexcept {
+    __lhs.swap(__rhs);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend bool operator==(const move_only_function& __func, nullptr_t) noexcept { return !__func; }
+
+private:
+  __pointer_bool_pair<const _TrivialVTable*> __vtable_ = {};
+  mutable _BufferT __buffer_;
+
+  template <class...>
+  friend class move_only_function;
+};
+
+#undef _LIBCPP_MOVE_ONLY_FUNCTION_CV
+#undef _LIBCPP_MOVE_ONLY_FUNCTION_REF
+#undef _LIBCPP_MOVE_ONLY_FUNCTION_NOEXCEPT
+#undef _LIBCPP_MOVE_ONLY_FUNCTION_INVOKE_QUALS
+#undef _LIBCPP_MOVE_ONLY_FUNCTION_CVREF
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__std_clang_module b/libcxx/include/__std_clang_module
index 18d6ce6b46c1f..e48a3e876ef84 100644
--- a/libcxx/include/__std_clang_module
+++ b/libcxx/include/__std_clang_module
@@ -136,6 +136,7 @@
 #include <mdspan>
 #include <memory>
 #include <memory_resource>
+#include <module.modulemap.in>
 #include <mutex>
 #include <new>
 #include <numbers>
diff --git a/libcxx/include/__utility/pointer_int_pair.h b/libcxx/include/__utility/pointer_int_pair.h
new file mode 100644
index 0000000000000..5a08b74686166
--- /dev/null
+++ b/libcxx/include/__utility/pointer_int_pair.h
@@ -0,0 +1,157 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Except...
[truncated]

@philnik777 philnik777 changed the base branch from main to users/philnik777/spr/add_pointer_int_pair June 6, 2024 19:36
Copy link

github-actions bot commented Jun 6, 2024

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff 57808202adc20ee4901364906c20f5b4e115b7ae cb723b8b3623590ba5da6648b98fe8eec020c325 --extensions ,cpp,h -- libcxx/include/__functional/move_only_function.h libcxx/include/__functional/move_only_function_common.h libcxx/include/__functional/move_only_function_impl.h libcxx/test/libcxx/private_headers.verify.cpp libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.move/assert.engaged.cpp libcxx/test/libcxx/utilities/pointer_int_pair.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/assignment/functor.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/assignment/move.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/assignment/move_other.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/assignment/nullptr.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/lvalue.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/lvalue_const.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/lvalue_const_noexcept.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/lvalue_noexcept.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/normal.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/normal_const.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/normal_const_noexcept.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/normal_noexcept.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue_const.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue_const_noexcept.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue_noexcept.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/common.h libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/ctors/default.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/ctors/functor.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/ctors/in_place.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/ctors/in_place_init_list.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/ctors/move.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/ctors/move_other.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/ctors/nullptr.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/swap.adl.pass.cpp libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/swap.member.pass.cpp libcxx/include/__configuration/abi.h libcxx/include/__utility/pointer_int_pair.h libcxx/include/functional libcxx/include/version libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp libcxx/test/support/type_algorithms.h
View the diff from clang-format here.
diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index fc76655325..8debceecd2 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -226,11 +226,11 @@ END-SCRIPT
 #include <__algorithm/unwrap_iter.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/unwrap_iter.h'}}
 #include <__algorithm/unwrap_range.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/unwrap_range.h'}}
 #include <__algorithm/upper_bound.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/upper_bound.h'}}
-#include <__availability> // expected-error@*:* {{use of private header from outside its module: '__availability'}}
+#include <__availability>   // expected-error@*:* {{use of private header from outside its module: '__availability'}}
 #include <__bit/bit_cast.h> // expected-error@*:* {{use of private header from outside its module: '__bit/bit_cast.h'}}
 #include <__bit/byteswap.h> // expected-error@*:* {{use of private header from outside its module: '__bit/byteswap.h'}}
-#include <__bit_reference> // expected-error@*:* {{use of private header from outside its module: '__bit_reference'}}
-#include <__bits> // expected-error@*:* {{use of private header from outside its module: '__bits'}}
+#include <__bit_reference>  // expected-error@*:* {{use of private header from outside its module: '__bit_reference'}}
+#include <__bits>           // expected-error@*:* {{use of private header from outside its module: '__bits'}}
 #include <__charconv/chars_format.h> // expected-error@*:* {{use of private header from outside its module: '__charconv/chars_format.h'}}
 #include <__charconv/from_chars_result.h> // expected-error@*:* {{use of private header from outside its module: '__charconv/from_chars_result.h'}}
 #include <__charconv/tables.h> // expected-error@*:* {{use of private header from outside its module: '__charconv/tables.h'}}
@@ -239,7 +239,7 @@ END-SCRIPT
 #include <__chrono/calendar.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/calendar.h'}}
 #include <__chrono/convert_to_timespec.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/convert_to_timespec.h'}}
 #include <__chrono/convert_to_tm.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/convert_to_tm.h'}}
-#include <__chrono/day.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/day.h'}}
+#include <__chrono/day.h>      // expected-error@*:* {{use of private header from outside its module: '__chrono/day.h'}}
 #include <__chrono/duration.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/duration.h'}}
 #include <__chrono/file_clock.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/file_clock.h'}}
 #include <__chrono/formatter.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/formatter.h'}}
@@ -256,7 +256,7 @@ END-SCRIPT
 #include <__chrono/system_clock.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/system_clock.h'}}
 #include <__chrono/time_point.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/time_point.h'}}
 #include <__chrono/weekday.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/weekday.h'}}
-#include <__chrono/year.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/year.h'}}
+#include <__chrono/year.h>    // expected-error@*:* {{use of private header from outside its module: '__chrono/year.h'}}
 #include <__chrono/year_month.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/year_month.h'}}
 #include <__chrono/year_month_day.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/year_month_day.h'}}
 #include <__chrono/year_month_weekday.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/year_month_weekday.h'}}
@@ -380,15 +380,15 @@ END-SCRIPT
 #include <__functional/unwrap_ref.h> // expected-error@*:* {{use of private header from outside its module: '__functional/unwrap_ref.h'}}
 #include <__functional/weak_result_type.h> // expected-error@*:* {{use of private header from outside its module: '__functional/weak_result_type.h'}}
 #include <__fwd/array.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/array.h'}}
-#include <__fwd/get.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/get.h'}}
-#include <__fwd/hash.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/hash.h'}}
+#include <__fwd/get.h>   // expected-error@*:* {{use of private header from outside its module: '__fwd/get.h'}}
+#include <__fwd/hash.h>  // expected-error@*:* {{use of private header from outside its module: '__fwd/hash.h'}}
 #include <__fwd/memory_resource.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/memory_resource.h'}}
-#include <__fwd/pair.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/pair.h'}}
-#include <__fwd/span.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/span.h'}}
-#include <__fwd/string.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/string.h'}}
+#include <__fwd/pair.h>        // expected-error@*:* {{use of private header from outside its module: '__fwd/pair.h'}}
+#include <__fwd/span.h>        // expected-error@*:* {{use of private header from outside its module: '__fwd/span.h'}}
+#include <__fwd/string.h>      // expected-error@*:* {{use of private header from outside its module: '__fwd/string.h'}}
 #include <__fwd/string_view.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/string_view.h'}}
-#include <__fwd/tuple.h> // expected-error@*:* {{use of private header from outside its module: '__fwd/tuple.h'}}
-#include <__ios/fpos.h> // expected-error@*:* {{use of private header from outside its module: '__ios/fpos.h'}}
+#include <__fwd/tuple.h>       // expected-error@*:* {{use of private header from outside its module: '__fwd/tuple.h'}}
+#include <__ios/fpos.h>        // expected-error@*:* {{use of private header from outside its module: '__ios/fpos.h'}}
 #include <__iterator/access.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/access.h'}}
 #include <__iterator/advance.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/advance.h'}}
 #include <__iterator/back_insert_iterator.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/back_insert_iterator.h'}}
@@ -427,8 +427,8 @@ END-SCRIPT
 #include <__iterator/sortable.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/sortable.h'}}
 #include <__iterator/unreachable_sentinel.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/unreachable_sentinel.h'}}
 #include <__iterator/wrap_iter.h> // expected-error@*:* {{use of private header from outside its module: '__iterator/wrap_iter.h'}}
-#include <__locale> // expected-error@*:* {{use of private header from outside its module: '__locale'}}
-#include <__mbstate_t.h> // expected-error@*:* {{use of private header from outside its module: '__mbstate_t.h'}}
+#include <__locale>               // expected-error@*:* {{use of private header from outside its module: '__locale'}}
+#include <__mbstate_t.h>        // expected-error@*:* {{use of private header from outside its module: '__mbstate_t.h'}}
 #include <__memory/addressof.h> // expected-error@*:* {{use of private header from outside its module: '__memory/addressof.h'}}
 #include <__memory/align.h> // expected-error@*:* {{use of private header from outside its module: '__memory/align.h'}}
 #include <__memory/allocate_at_least.h> // expected-error@*:* {{use of private header from outside its module: '__memory/allocate_at_least.h'}}
@@ -463,7 +463,7 @@ END-SCRIPT
 #include <__memory_resource/pool_options.h> // expected-error@*:* {{use of private header from outside its module: '__memory_resource/pool_options.h'}}
 #include <__memory_resource/synchronized_pool_resource.h> // expected-error@*:* {{use of private header from outside its module: '__memory_resource/synchronized_pool_resource.h'}}
 #include <__memory_resource/unsynchronized_pool_resource.h> // expected-error@*:* {{use of private header from outside its module: '__memory_resource/unsynchronized_pool_resource.h'}}
-#include <__mutex_base> // expected-error@*:* {{use of private header from outside its module: '__mutex_base'}}
+#include <__mutex_base>  // expected-error@*:* {{use of private header from outside its module: '__mutex_base'}}
 #include <__node_handle> // expected-error@*:* {{use of private header from outside its module: '__node_handle'}}
 #include <__numeric/accumulate.h> // expected-error@*:* {{use of private header from outside its module: '__numeric/accumulate.h'}}
 #include <__numeric/adjacent_difference.h> // expected-error@*:* {{use of private header from outside its module: '__numeric/adjacent_difference.h'}}
@@ -516,7 +516,7 @@ END-SCRIPT
 #include <__random/uniform_real_distribution.h> // expected-error@*:* {{use of private header from outside its module: '__random/uniform_real_distribution.h'}}
 #include <__random/weibull_distribution.h> // expected-error@*:* {{use of private header from outside its module: '__random/weibull_distribution.h'}}
 #include <__ranges/access.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/access.h'}}
-#include <__ranges/all.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/all.h'}}
+#include <__ranges/all.h>    // expected-error@*:* {{use of private header from outside its module: '__ranges/all.h'}}
 #include <__ranges/common_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/common_view.h'}}
 #include <__ranges/concepts.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/concepts.h'}}
 #include <__ranges/copyable_box.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/copyable_box.h'}}
@@ -550,8 +550,8 @@ END-SCRIPT
 #include <__ranges/view_interface.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/view_interface.h'}}
 #include <__ranges/views.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/views.h'}}
 #include <__ranges/zip_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/zip_view.h'}}
-#include <__split_buffer> // expected-error@*:* {{use of private header from outside its module: '__split_buffer'}}
-#include <__std_stream> // expected-error@*:* {{use of private header from outside its module: '__std_stream'}}
+#include <__split_buffer>      // expected-error@*:* {{use of private header from outside its module: '__split_buffer'}}
+#include <__std_stream>        // expected-error@*:* {{use of private header from outside its module: '__std_stream'}}
 #include <__string/char_traits.h> // expected-error@*:* {{use of private header from outside its module: '__string/char_traits.h'}}
 #include <__string/extern_template_lists.h> // expected-error@*:* {{use of private header from outside its module: '__string/extern_template_lists.h'}}
 #include <__thread/poll_with_backoff.h> // expected-error@*:* {{use of private header from outside its module: '__thread/poll_with_backoff.h'}}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/assignment/move_other.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/assignment/move_other.pass.cpp
index b15e9337a1..c9da751102 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/assignment/move_other.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/assignment/move_other.pass.cpp
@@ -45,7 +45,7 @@ void test2() {
     assert(f2() == 109);
   }
   {
-    std::move_only_function<int() const& noexcept> f1 = [] noexcept { return 109; };
+    std::move_only_function<int() const & noexcept> f1 = [] noexcept { return 109; };
     std::move_only_function<T> f2;
     std::same_as<std::move_only_function<T>&> decltype(auto) ret = (f2 = std::move(f1));
     assert(&ret == &f2);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/lvalue_const_noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/lvalue_const_noexcept.pass.cpp
index d8129f541f..2f41506839 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/lvalue_const_noexcept.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/lvalue_const_noexcept.pass.cpp
@@ -29,26 +29,26 @@ static_assert(std::is_invocable_v<std::move_only_function<void() const & noexcep
 
 void test() {
   {
-    called                                            = false;
-    std::move_only_function<void() const& noexcept> f = &call_func;
+    called                                             = false;
+    std::move_only_function<void() const & noexcept> f = &call_func;
     f();
     assert(called);
   }
   {
-    called                                            = false;
-    std::move_only_function<void() const& noexcept> f = TriviallyDestructible{};
+    called                                             = false;
+    std::move_only_function<void() const & noexcept> f = TriviallyDestructible{};
     f();
     assert(called);
   }
   {
-    called                                            = false;
-    std::move_only_function<void() const& noexcept> f = TriviallyDestructibleTooLarge{};
+    called                                             = false;
+    std::move_only_function<void() const & noexcept> f = TriviallyDestructibleTooLarge{};
     f();
     assert(called);
   }
   {
-    called                                            = false;
-    std::move_only_function<void() const& noexcept> f = NonTrivial{};
+    called                                             = false;
+    std::move_only_function<void() const & noexcept> f = NonTrivial{};
     f();
     assert(called);
   }
@@ -63,7 +63,7 @@ void test() {
   }
   {
     CallType type;
-    std::move_only_function<void() const& noexcept> f = CallTypeCheckerNoexcept{&type};
+    std::move_only_function<void() const & noexcept> f = CallTypeCheckerNoexcept{&type};
     f();
     assert(type == CallType::ConstLValue);
     type = CallType::None;
@@ -80,26 +80,26 @@ void test() {
 
 void test_return() {
   {
-    called                                              = false;
-    std::move_only_function<int(int) const& noexcept> f = &get_val;
+    called                                               = false;
+    std::move_only_function<int(int) const & noexcept> f = &get_val;
     assert(f(3) == 3);
     assert(!called);
   }
   {
-    called                                              = false;
-    std::move_only_function<int(int) const& noexcept> f = TriviallyDestructible{};
+    called                                               = false;
+    std::move_only_function<int(int) const & noexcept> f = TriviallyDestructible{};
     assert(f(3) == 3);
     assert(!called);
   }
   {
-    called                                              = false;
-    std::move_only_function<int(int) const& noexcept> f = TriviallyDestructibleTooLarge{};
+    called                                               = false;
+    std::move_only_function<int(int) const & noexcept> f = TriviallyDestructibleTooLarge{};
     assert(f(3) == 3);
     assert(!called);
   }
   {
-    called                                              = false;
-    std::move_only_function<int(int) const& noexcept> f = NonTrivial{};
+    called                                               = false;
+    std::move_only_function<int(int) const & noexcept> f = NonTrivial{};
     assert(f(3) == 3);
     assert(!called);
   }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/lvalue_noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/lvalue_noexcept.pass.cpp
index d14f527f81..c3d9788b85 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/lvalue_noexcept.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/lvalue_noexcept.pass.cpp
@@ -27,26 +27,26 @@ static_assert(!std::is_invocable_v<std::move_only_function<void() & noexcept> co
 
 void test() {
   {
-    called                                      = false;
-    std::move_only_function<void()& noexcept> f = &call_func;
+    called                                       = false;
+    std::move_only_function<void() & noexcept> f = &call_func;
     f();
     assert(called);
   }
   {
-    called                                      = false;
-    std::move_only_function<void()& noexcept> f = TriviallyDestructible{};
+    called                                       = false;
+    std::move_only_function<void() & noexcept> f = TriviallyDestructible{};
     f();
     assert(called);
   }
   {
-    called                                      = false;
-    std::move_only_function<void()& noexcept> f = TriviallyDestructibleTooLarge{};
+    called                                       = false;
+    std::move_only_function<void() & noexcept> f = TriviallyDestructibleTooLarge{};
     f();
     assert(called);
   }
   {
-    called                                      = false;
-    std::move_only_function<void()& noexcept> f = NonTrivial{};
+    called                                       = false;
+    std::move_only_function<void() & noexcept> f = NonTrivial{};
     f();
     assert(called);
   }
@@ -61,7 +61,7 @@ void test() {
   }
   {
     CallType type;
-    std::move_only_function<void()& noexcept> f = CallTypeCheckerNoexcept{&type};
+    std::move_only_function<void() & noexcept> f = CallTypeCheckerNoexcept{&type};
     f();
     assert(type == CallType::LValue);
   }
@@ -69,26 +69,26 @@ void test() {
 
 void test_return() {
   {
-    called                                        = false;
-    std::move_only_function<int(int)& noexcept> f = &get_val;
+    called                                         = false;
+    std::move_only_function<int(int) & noexcept> f = &get_val;
     assert(f(3) == 3);
     assert(!called);
   }
   {
-    called                                        = false;
-    std::move_only_function<int(int)& noexcept> f = TriviallyDestructible{};
+    called                                         = false;
+    std::move_only_function<int(int) & noexcept> f = TriviallyDestructible{};
     assert(f(3) == 3);
     assert(!called);
   }
   {
-    called                                        = false;
-    std::move_only_function<int(int)& noexcept> f = TriviallyDestructibleTooLarge{};
+    called                                         = false;
+    std::move_only_function<int(int) & noexcept> f = TriviallyDestructibleTooLarge{};
     assert(f(3) == 3);
     assert(!called);
   }
   {
-    called                                        = false;
-    std::move_only_function<int(int)& noexcept> f = NonTrivial{};
+    called                                         = false;
+    std::move_only_function<int(int) & noexcept> f = NonTrivial{};
     assert(f(3) == 3);
     assert(!called);
   }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue.pass.cpp
index f51b48564d..016914f420 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue.pass.cpp
@@ -28,42 +28,42 @@ static_assert(!std::is_invocable_v<std::move_only_function<void() &&> const&&>);
 
 void test() {
   {
-    called                              = false;
-    std::move_only_function<void()&&> f = &call_func;
+    called                               = false;
+    std::move_only_function<void() &&> f = &call_func;
     std::move(f)();
     assert(called);
   }
   {
-    called                              = false;
-    std::move_only_function<void()&&> f = TriviallyDestructible{};
+    called                               = false;
+    std::move_only_function<void() &&> f = TriviallyDestructible{};
     std::move(f)();
     assert(called);
   }
   {
-    called                              = false;
-    std::move_only_function<void()&&> f = TriviallyDestructibleTooLarge{};
+    called                               = false;
+    std::move_only_function<void() &&> f = TriviallyDestructibleTooLarge{};
     std::move(f)();
     assert(called);
   }
   {
-    called                              = false;
-    std::move_only_function<void()&&> f = NonTrivial{};
+    called                               = false;
+    std::move_only_function<void() &&> f = NonTrivial{};
     std::move(f)();
     assert(called);
   }
   {
-    std::move_only_function<void(S)&&> f = &S::func;
+    std::move_only_function<void(S) &&> f = &S::func;
     assert(f);
   }
   {
-    decltype(&S::func) ptr               = nullptr;
-    std::move_only_function<void(S)&&> f = ptr;
+    decltype(&S::func) ptr                = nullptr;
+    std::move_only_function<void(S) &&> f = ptr;
     assert(!f);
   }
   {
     CallType type;
-    std::move_only_function<void()&&> f = CallTypeChecker{&type};
-    type                                = CallType::None;
+    std::move_only_function<void() &&> f = CallTypeChecker{&type};
+    type                                 = CallType::None;
     std::move(f)();
     assert(type == CallType::RValue);
   }
@@ -71,26 +71,26 @@ void test() {
 
 void test_return() {
   {
-    called                                = false;
-    std::move_only_function<int(int)&&> f = &get_val;
+    called                                 = false;
+    std::move_only_function<int(int) &&> f = &get_val;
     assert(std::move(f)(3) == 3);
     assert(!called);
   }
   {
-    called                                = false;
-    std::move_only_function<int(int)&&> f = TriviallyDestructible{};
+    called                                 = false;
+    std::move_only_function<int(int) &&> f = TriviallyDestructible{};
     assert(std::move(f)(3) == 3);
     assert(!called);
   }
   {
-    called                                = false;
-    std::move_only_function<int(int)&&> f = TriviallyDestructibleTooLarge{};
+    called                                 = false;
+    std::move_only_function<int(int) &&> f = TriviallyDestructibleTooLarge{};
     assert(std::move(f)(3) == 3);
     assert(!called);
   }
   {
-    called                                = false;
-    std::move_only_function<int(int)&&> f = NonTrivial{};
+    called                                 = false;
+    std::move_only_function<int(int) &&> f = NonTrivial{};
     assert(std::move(f)(3) == 3);
     assert(!called);
   }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue_const_noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue_const_noexcept.pass.cpp
index 245b60eb45..e566096e03 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue_const_noexcept.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue_const_noexcept.pass.cpp
@@ -28,42 +28,42 @@ static_assert(std::is_invocable_v<std::move_only_function<void() const && noexce
 
 void test() {
   {
-    called                                             = false;
-    std::move_only_function<void() const&& noexcept> f = &call_func;
+    called                                              = false;
+    std::move_only_function<void() const && noexcept> f = &call_func;
     std::move(f)();
     assert(called);
   }
   {
-    called                                             = false;
-    std::move_only_function<void() const&& noexcept> f = TriviallyDestructible{};
+    called                                              = false;
+    std::move_only_function<void() const && noexcept> f = TriviallyDestructible{};
     std::move(f)();
     assert(called);
   }
   {
-    called                                             = false;
-    std::move_only_function<void() const&& noexcept> f = TriviallyDestructibleTooLarge{};
+    called                                              = false;
+    std::move_only_function<void() const && noexcept> f = TriviallyDestructibleTooLarge{};
     std::move(f)();
     assert(called);
   }
   {
-    called                                             = false;
-    std::move_only_function<void() const&& noexcept> f = NonTrivial{};
+    called                                              = false;
+    std::move_only_function<void() const && noexcept> f = NonTrivial{};
     std::move(f)();
     assert(called);
   }
   {
-    std::move_only_function<void(S) const&& noexcept> f = &S::func;
+    std::move_only_function<void(S) const && noexcept> f = &S::func;
     assert(f);
   }
   {
-    decltype(&S::func) ptr                              = nullptr;
-    std::move_only_function<void(S) const&& noexcept> f = ptr;
+    decltype(&S::func) ptr                               = nullptr;
+    std::move_only_function<void(S) const && noexcept> f = ptr;
     assert(!f);
   }
   {
     CallType type;
-    std::move_only_function<void() const&& noexcept> f = CallTypeCheckerNoexcept{&type};
-    type                                               = CallType::None;
+    std::move_only_function<void() const && noexcept> f = CallTypeCheckerNoexcept{&type};
+    type                                                = CallType::None;
     std::move(f)();
     assert(type == CallType::ConstRValue);
     type = CallType::None;
@@ -74,26 +74,26 @@ void test() {
 
 void test_return() {
   {
-    called                                               = false;
-    std::move_only_function<int(int) const&& noexcept> f = &get_val;
+    called                                                = false;
+    std::move_only_function<int(int) const && noexcept> f = &get_val;
     assert(std::move(f)(3) == 3);
     assert(!called);
   }
   {
-    called                                               = false;
-    std::move_only_function<int(int) const&& noexcept> f = TriviallyDestructible{};
+    called                                                = false;
+    std::move_only_function<int(int) const && noexcept> f = TriviallyDestructible{};
     assert(std::move(f)(3) == 3);
     assert(!called);
   }
   {
-    called                                               = false;
-    std::move_only_function<int(int) const&& noexcept> f = TriviallyDestructibleTooLarge{};
+    called                                                = false;
+    std::move_only_function<int(int) const && noexcept> f = TriviallyDestructibleTooLarge{};
     assert(std::move(f)(3) == 3);
     assert(!called);
   }
   {
-    called                                               = false;
-    std::move_only_function<int(int) const&& noexcept> f = NonTrivial{};
+    called                                                = false;
+    std::move_only_function<int(int) const && noexcept> f = NonTrivial{};
     assert(std::move(f)(3) == 3);
     assert(!called);
   }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue_noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue_noexcept.pass.cpp
index 87201aeec8..1e9e5ee623 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue_noexcept.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue_noexcept.pass.cpp
@@ -28,42 +28,42 @@ static_assert(!std::is_invocable_v<std::move_only_function<void() && noexcept> c
 
 void test() {
   {
-    called                                       = false;
-    std::move_only_function<void()&& noexcept> f = &call_func;
+    called                                        = false;
+    std::move_only_function<void() && noexcept> f = &call_func;
     std::move(f)();
     assert(called);
   }
   {
-    called                                       = false;
-    std::move_only_function<void()&& noexcept> f = TriviallyDestructible{};
+    called                                        = false;
+    std::move_only_function<void() && noexcept> f = TriviallyDestructible{};
     std::move(f)();
     assert(called);
   }
   {
-    called                                       = false;
-    std::move_only_function<void()&& noexcept> f = TriviallyDestructibleTooLarge{};
+    called                                        = false;
+    std::move_only_function<void() && noexcept> f = TriviallyDestructibleTooLarge{};
     std::move(f)();
     assert(called);
   }
   {
-    called                                       = false;
-    std::move_only_function<void()&& noexcept> f = NonTrivial{};
+    called                                        = false;
+    std::move_only_function<void() && noexcept> f = NonTrivial{};
     std::move(f)();
     assert(called);
   }
   {
-    std::move_only_function<void(S)&& noexcept> f = &S::func;
+    std::move_only_function<void(S) && noexcept> f = &S::func;
     assert(f);
   }
   {
-    decltype(&S::func) ptr                        = nullptr;
-    std::move_only_function<void(S)&& noexcept> f = ptr;
+    decltype(&S::func) ptr                         = nullptr;
+    std::move_only_function<void(S) && noexcept> f = ptr;
     assert(!f);
   }
   {
     CallType type;
-    std::move_only_function<void()&& noexcept> f = CallTypeCheckerNoexcept{&type};
-    type                                         = CallType::None;
+    std::move_only_function<void() && noexcept> f = CallTypeCheckerNoexcept{&type};
+    type                                          = CallType::None;
     std::move(f)();
     assert(type == CallType::RValue);
   }
@@ -71,26 +71,26 @@ void test() {
 
 void test_return() {
   {
-    called                                         = false;
-    std::move_only_function<int(int)&& noexcept> f = &get_val;
+    called                                          = false;
+    std::move_only_function<int(int) && noexcept> f = &get_val;
     assert(std::move(f)(3) == 3);
     assert(!called);
   }
   {
-    called                                         = false;
-    std::move_only_function<int(int)&& noexcept> f = TriviallyDestructible{};
+    called                                          = false;
+    std::move_only_function<int(int) && noexcept> f = TriviallyDestructible{};
     assert(std::move(f)(3) == 3);
     assert(!called);
   }
   {
-    called                                         = false;
-    std::move_only_function<int(int)&& noexcept> f = TriviallyDestructibleTooLarge{};
+    called                                          = false;
+    std::move_only_function<int(int) && noexcept> f = TriviallyDestructibleTooLarge{};
     assert(std::move(f)(3) == 3);
     assert(!called);
   }
   {
-    called                                         = false;
-    std::move_only_function<int(int)&& noexcept> f = NonTrivial{};
+    called                                          = false;
+    std::move_only_function<int(int) && noexcept> f = NonTrivial{};
     assert(std::move(f)(3) == 3);
     assert(!called);
   }

@philnik777 philnik777 force-pushed the users/philnik777/spr/add_pointer_int_pair branch 2 times, most recently from 177070b to b8a792a Compare June 6, 2024 20:31
@EricWF
Copy link
Member

EricWF commented Jun 6, 2024

I appreciate you putting this up. Is this ready for review?

# define _LIBCPP_MOVE_ONLY_FUNCTION_CV
#endif

#ifndef _LIBCPP_MOVE_ONLY_FUNCTION_REF
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really dislike this. It produces terrible diagnostics for users.

I feel like we could find another way to represent the function type so we can limit the duplication.

If we strip the qualifiers and turn them into an enumeration like _Const|_RValue|_Noexcept, we could use a traits class to produce the correct function types as needed.

We still have to spell out the top level move_only_function class a few times, but we can implement everything in a base that takes this enumeration and the desugared type.

Copy link
Member

@EricWF EricWF Jun 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's what I'm thinking.

#ifndef  _LIBCPP___FUNCTIONAL_MOVE_ONLY_FUNCTION_H
#define _LIBCPP___FUNCTIONAL_MOVE_ONLY_FUNCTION_H

#include <__assert>
#include <__config>
#include <__functional/invoke.h>
#include <__type_traits/decay.h>
#include <__type_traits/invoke.h>
#include <__type_traits/is_function.h>
#include <__type_traits/is_member_function_pointer.h>
#include <__type_traits/is_pointer.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_trivially_constructible.h>
#include <__type_traits/is_trivially_destructible.h>
#include <__type_traits/remove_cvref.h>
#include <__type_traits/remove_pointer.h>
#include <__utility/forward.h>
#include <__utility/in_place.h>
#include <__utility/swap.h>
#include <cstddef>
#include <initializer_list>
#include <new>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#  pragma GCC system_header
#endif

_LIBCPP_BEGIN_NAMESPACE_STD

enum class _Quals : unsigned {
  _None     = 0,
  _Const    = 1,
  _RVRef    = 2,
  _LVRef    = 4,
  _NoExcept = 8,
};

constexpr _Quals operator|(_Quals __x, _Quals __y) {
  return static_cast<_Quals>(static_cast<unsigned>(__x) | static_cast<unsigned>(__y));
}
constexpr _Quals operator&(_Quals __x, _Quals __y) {
  return static_cast<_Quals>(static_cast<unsigned>(__x) & static_cast<unsigned>(__y));
}
constexpr _Quals operator^(_Quals __x, _Quals __y) {
  return static_cast<_Quals>(static_cast<unsigned>(__x) ^ static_cast<unsigned>(__y));
}
constexpr _Quals operator~(_Quals __x) { return static_cast<_Quals>(~static_cast<unsigned>(__x)); }

constexpr bool operator==(const _Quals __x, const _Quals __y) {
  return static_cast<unsigned>(__x) == static_cast<unsigned>(__y);
}
constexpr bool operator!=(const _Quals __x, const _Quals __y) {
  return static_cast<unsigned>(__x) != static_cast<unsigned>(__y);
}

constexpr _Quals __ref_quals(_Quals __q) { return __q & (_Quals::_RVRef | _Quals::_LVRef); }
constexpr _Quals __cv_quals(_Quals __q) { return __q & _Quals::_Const; }
constexpr _Quals __cvref_quals(_Quals __q) { return __q & (_Quals::_Const | _Quals::_RVRef | _Quals::_LVRef); }

constexpr bool __is_noexcept(_Quals __q) { return (__q & _Quals::_NoExcept) == _Quals::_NoExcept; }

template <class _Ptr>
struct __unqualify_type;

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...)> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_None;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) const> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_Const;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...)&> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_LVRef;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) const&> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_Const | _Quals::_LVRef;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) &&> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_RVRef;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) const&&> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_Const | _Quals::_RVRef;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) noexcept> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_NoExcept;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) const noexcept> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_Const | _Quals::_NoExcept;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) & noexcept> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_LVRef | _Quals::_NoExcept;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) const & noexcept> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_Const | _Quals::_LVRef | _Quals::_NoExcept;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) && noexcept> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_RVRef | _Quals::_NoExcept;
};

template <class _Ret, class... _Args>
struct __unqualify_type<_Ret(_Args...) const && noexcept> {
  using type                     = _Ret(_Args...);
  static constexpr _Quals __qual = _Quals::_Const | _Quals::_RVRef | _Quals::_NoExcept;
};

template <class _Tp>
struct __identity_type {
  using type = _Tp;
};

template <_Quals __qual, class _Ret>
constexpr auto __qualify_type_impl() {
  static_assert(!__is_noexcept(__qual), "Noexcept qualifier is not allowed on type");
  if constexpr (__qual == _Quals::_None) {
    return __identity_type<_Ret>{};
  } else if constexpr (__qual == _Quals::_Const) {
    return __identity_type<_Ret const>{};
  } else if constexpr (__qual == _Quals::_LVRef) {
    return __identity_type<_Ret&>{};
  } else if constexpr (__qual == (_Quals::_Const | _Quals::_LVRef)) {
    return __identity_type<_Ret const&>{};
  } else if constexpr (__qual == _Quals::_RVRef) {
    return __identity_type<_Ret&&>{};
  } else if constexpr (__qual == (_Quals::_Const | _Quals::_RVRef)) {
    return __identity_type<_Ret const&&>{};
  } else {
    static_assert(sizeof(__qual) == 0, "Invalid qualifier");
  }
}
template <_Quals __qual, class _Ret>
using __qualify_type_t = typename decltype(__qualify_type_impl<__qual, _Ret>())::type;

template <_Quals __quals, class _Tp>
inline __qualify_type_t<__quals, _Tp> __cast_as(void* __ptr) {
  return std::forward<__qualify_type_t<__quals, _Tp>>(*static_cast<_Tp*>(__ptr));
}
template <class _Tp, _Quals __q>
struct __move_only_function_base;

struct __small_function_buffer {
  union {
    void* __ptr_;
    std::byte __buf_[3 * sizeof(void*)];
  };
};

struct _AllocGuard {
  _AllocGuard()                   = delete;
  _AllocGuard(_AllocGuard const&) = delete;

  _AllocGuard(size_t __size, size_t __align)
      : __ptr_(__libcpp_allocate(__size, __align)), __size_(__size), __align_(__align) {}

  void* __release() {
    void* __ret = __ptr_;
    __ptr_      = nullptr;
    return __ret;
  }

  ~_AllocGuard() {
    if (__ptr_) {
      __libcpp_deallocate(__ptr_, __size_, __align_);
    }
  }

  void* __ptr_;
  size_t __size_;
  size_t __align_;
};

template <class _Functor>
constexpr bool __use_inline_storage =
    sizeof(_Functor) <= sizeof(__small_function_buffer) &&
    alignof(_Functor) <= alignof(__small_function_buffer) && std::is_trivially_destructible_v<_Functor> &&
    std::is_trivially_move_constructible_v<_Functor>;

template <class...>
struct move_only_function;


template <class _Ret, class... _Args>
struct __move_only_vtable {
  using _BufferT = __small_function_buffer;

  using __destroy_t = void (*)(__small_function_buffer&);
  using __call_t    = _Ret (*)(__small_function_buffer&, _Args...);

  __call_t __call_       = nullptr;
  __destroy_t __destroy_ = nullptr;
};

template <class _Ret, class... _Args, _Quals __q>
struct __move_only_function_base<_Ret(_Args...), __q> {
private:
  using _BufferT = __small_function_buffer;
  using _VTable = __move_only_vtable<_Ret, _Args...>;

  static constexpr _Quals __inv_quals =
      __ref_quals(__q) == _Quals::_None ? __cv_quals(__q) | _Quals::_LVRef : __cvref_quals(__q);


  template <class _RawFunctor>
  static constexpr _VTable __create() {
    using _Functor = std::decay_t<_RawFunctor>;

    if constexpr (__use_inline_storage<_Functor>) {
      return {.__call_ = [](_BufferT& __buffer, _Args... __args) noexcept(__is_noexcept(__q)) -> _Ret {
                return std::invoke_r<_Ret>(
                    __cast_as<__inv_quals, _Functor>(__buffer.__buf_), std::forward<_Args>(__args)...);
              },
              .__destroy_ = nullptr};
    } else {
      return {.__call_ = [](_BufferT& __buffer, _Args... __args) noexcept(__is_noexcept(__q)) -> _Ret {
                return std::invoke_r<_Ret>(
                    __cast_as<__inv_quals, _Functor>(__buffer.__ptr_), std::forward<_Args>(__args)...);
              },
              .__destroy_ = [](_BufferT& __buffer) noexcept -> void {
                static_cast<_Functor*>(__buffer.__ptr_)->~_Functor();
                __libcpp_deallocate(__buffer.__ptr_, sizeof(_Functor), alignof(_Functor));
              }};
    };
  }

  template <class _Functor>
  static constexpr _VTable __functor_vtable = __create<_Functor>();

  mutable _BufferT __buffer_;
  const _VTable* __vtable_ = {};

  template <class _VT>
  static constexpr bool __is_callable_from = []() {
    if constexpr (__is_noexcept(__q)) {
      return is_nothrow_invocable_r_v<_Ret, __qualify_type_t<__cvref_quals(__q), _VT>, _Args...> &&
             is_nothrow_invocable_r_v<_Ret, __qualify_type_t<__inv_quals, _VT>, _Args...>;
    } else {
      return is_invocable_r_v<_Ret, __qualify_type_t<__cvref_quals(__q), _VT>, _Args...> &&
             is_invocable_r_v<_Ret, __qualify_type_t<__inv_quals, _VT>, _Args...>;
    }
  }();

  template <class _Func, class... _FArgs>
  void __construct(_FArgs&&... __args) {
    using _DFunc = decay_t<_Func>;
    if (__use_inline_storage<_DFunc>) {
      ::new (&__buffer_.__buf_) _DFunc(std::forward<_FArgs>(__args)...);
    } else {
      _AllocGuard __alloc(sizeof(_DFunc), alignof(_DFunc));
      ::new (__alloc.__ptr_) _DFunc(std::forward<_FArgs>(__args)...);
      __buffer_.__ptr_ = __alloc.__release();
    }
    __vtable_ = &__functor_vtable<_Func>;
  }

  _LIBCPP_HIDE_FROM_ABI void __reset() {
    if (__vtable_ && __vtable_->__destroy_)
      __vtable_->__destroy_(__buffer_);
    __vtable_ = nullptr;
  }

public:
  using result_type = _Ret;

  // [func.wrap.move.ctor]
  __move_only_function_base() noexcept = default;

  _LIBCPP_HIDE_FROM_ABI __move_only_function_base(__move_only_function_base&& __other) noexcept
      : __buffer_(__other.__buffer_), __vtable_(__other.__vtable_)  {
    __other.__vtable_ = {};
  }

  template <_Quals __call_qual>
  static constexpr bool __has_cvref_quals = __cvref_quals(__q) == __call_qual;

  _LIBCPP_HIDE_FROM_ABI ~__move_only_function_base() { __reset(); }

  explicit operator bool() const noexcept { return __vtable_ != nullptr; }

  _LIBCPP_HIDE_FROM_ABI _Ret operator()(_Args... __args) noexcept(__is_noexcept(__q))
    requires __has_cvref_quals<_Quals::_None>
  {
    _LIBCPP_ASSERT(static_cast<bool>(*this), "Tried to call a disengaged move_only_function");
    return __vtable_->__call_(__buffer_, std::forward<_Args>(__args)...);
  }

  _LIBCPP_HIDE_FROM_ABI _Ret operator()(_Args... __args) const noexcept(__is_noexcept(__q))
    requires __has_cvref_quals<_Quals::_Const>
  {
    _LIBCPP_ASSERT(static_cast<bool>(*this), "Tried to call a disengaged move_only_function");
    return __vtable_->__call_(__buffer_, std::forward<_Args>(__args)...);
  }

  _LIBCPP_HIDE_FROM_ABI _Ret operator()(_Args... __args) & noexcept(__is_noexcept(__q))
    requires __has_cvref_quals< _Quals::_LVRef>
  {
    _LIBCPP_ASSERT(static_cast<bool>(*this), "Tried to call a disengaged move_only_function");
    return __vtable_->__call_(__buffer_, std::forward<_Args>(__args)...);
  }

  _LIBCPP_HIDE_FROM_ABI _Ret operator()(_Args... __args) && noexcept(__is_noexcept(__q))
    requires __has_cvref_quals<_Quals::_RVRef>
  {
    _LIBCPP_ASSERT(static_cast<bool>(*this), "Tried to call a disengaged move_only_function");
    return __vtable_->__call_(__buffer_, std::forward<_Args>(__args)...);
  }

  _LIBCPP_HIDE_FROM_ABI _Ret operator()(_Args... __args) const& noexcept(__is_noexcept(__q))
    requires __has_cvref_quals<_Quals::_Const | _Quals::_LVRef>
  {
    _LIBCPP_ASSERT(static_cast<bool>(*this), "Tried to call a disengaged move_only_function");
    return __vtable_->__call_(__buffer_, std::forward<_Args>(__args)...);
  }

  _LIBCPP_HIDE_FROM_ABI _Ret operator()(_Args... __args) const && noexcept(__is_noexcept(__q))
    requires __has_cvref_quals<_Quals::_Const | _Quals::_RVRef>
  {
    _LIBCPP_ASSERT(static_cast<bool>(*this), "Tried to call a disengaged move_only_function");
    return __vtable_->__call_(__buffer_, std::forward<_Args>(__args)...);
  }

private:
  template <class, _Quals>
  friend struct __move_only_function_base;
  template <class...>
  friend struct move_only_function;
};

template <class _Ret>
struct __is_move_only_function : false_type {};
template <class... _Obj>
struct __is_move_only_function<move_only_function<_Obj...>> : true_type{};

template <class _Tp>
struct move_only_function<_Tp>
    : __move_only_function_base<typename __unqualify_type<_Tp>::type, __unqualify_type<_Tp>::__qual> {
  using __base = __move_only_function_base<typename __unqualify_type<_Tp>::type, __unqualify_type<_Tp>::__qual>;

  move_only_function() noexcept = default;
  _LIBCPP_HIDE_FROM_ABI move_only_function(nullptr_t) noexcept {}
  move_only_function(move_only_function const&) = delete;
  move_only_function(move_only_function&&)      = default;

  ~move_only_function() = default;

  template <class _Func>
    requires(!is_same_v<remove_cvref_t<_Func>, move_only_function> && !__is_inplace_type<_Func>::value &&
             __base::template __is_callable_from<_Func>)
  _LIBCPP_HIDE_FROM_ABI move_only_function(_Func&& __func) {
    using _StoredFunc = decay_t<_Func>;

    if constexpr ((is_pointer_v<_StoredFunc> && is_function_v<remove_pointer_t<_StoredFunc>>) ||
                  is_member_function_pointer_v<_StoredFunc>) {
      if (__func != nullptr) {
        this->template __construct<_StoredFunc>(std::forward<_Func>(__func));
      }
    } else if constexpr (__is_move_only_function<_StoredFunc>::value) {
      if (__func) {
        std::swap(this->__vtable_, __func.__vtable_);
        this->__buffer_ = __func.__buffer_;
      }
    } else {
      this->template __construct<_Func>(std::forward<_Func>(__func));
    }
  }

  template <class _Func, class... _FArgs>
    requires is_constructible_v<decay_t<_Func>, _FArgs...> && __base::template
  __is_callable_from<_Func> _LIBCPP_HIDE_FROM_ABI explicit move_only_function(
      in_place_type_t<_Func>, _FArgs&&... __args) {
    static_assert(is_same_v<decay_t<_Func>, _Func>);
    this->template __construct<_Func>(std::forward<_FArgs>(__args)...);
  }

  template <class _Func, class _InitListType, class... _Args>
    requires is_constructible_v<decay_t<_Func>, initializer_list<_InitListType>&, _Args...> && __base::template
  __is_callable_from<_Func> _LIBCPP_HIDE_FROM_ABI explicit move_only_function(
      in_place_type_t<_Func>, initializer_list<_InitListType> __il, _Args&&... __args) {
    static_assert(is_same_v<decay_t<_Func>, _Func>);
    this->template __construct<_Func>(__il, std::forward<_Args>(__args)...);
  }

  _LIBCPP_HIDE_FROM_ABI move_only_function& operator=(move_only_function&& __other) noexcept {
    move_only_function(std::move(__other)).swap(*this);
    return *this;
  }

  _LIBCPP_HIDE_FROM_ABI move_only_function& operator=(nullptr_t) noexcept {
    this->__reset();
    return *this;
  }

  template <class _Func>
    requires(!is_same_v<remove_cvref_t<_Func>, move_only_function> && !__is_inplace_type<_Func>::value &&
             __base::template __is_callable_from<_Func>)
  _LIBCPP_HIDE_FROM_ABI move_only_function& operator=(_Func&& __func) {
    move_only_function(std::forward<_Func>(__func)).swap(*this);
    return *this;
  }

  explicit operator bool() const noexcept { return this->__vtable_ != nullptr; }
  using __base::operator();

  // [func.wrap.move.util]
  _LIBCPP_HIDE_FROM_ABI void swap(move_only_function& __other) noexcept {
    std::swap(this->__vtable_, __other.__vtable_);
    std::swap(this->__buffer_, __other.__buffer_);
  }

  _LIBCPP_HIDE_FROM_ABI friend void swap(move_only_function& __lhs, move_only_function& __rhs) noexcept {
    __lhs.swap(__rhs);
  }

  _LIBCPP_HIDE_FROM_ABI friend bool operator==(const move_only_function& __func, nullptr_t) noexcept { return !__func; }
};

_LIBCPP_END_NAMESPACE_STD

#endif // _LIBCPP___FUNCTIONAL_MOVE_ONLY_FUNCTION_H

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give an example? A few people already claimed that it gives terrible diagnostics, but I've yet to see one.

Copy link
Member

@EricWF EricWF Jun 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, so I stand corrected here.

Normally, the way the terrible diagnostics come about is through the diagnostic caret pointing at a macro expansion. For example, were an invalid expression formed via a macro expansion, it would produce a diagnostic like:
https://godbolt.org/z/dKr1nzPd9

<source>:12:1: error: no matching function for call to 'invoke'
   12 | EXPAND

However, since we never actually expand any of the expressions fully from a macro, and none of the macros lead an expression (where the caret might point, _VSTD was notorious for this), the diagnostics produced by your implementation don't get all muddy.

Neither do they really appear to hide too much information. I was worried it would be hard to tell the actual CV, ref, and noexcept qualifiers were present from a particular diagnostic. And while you can't see what's used from the source, the clang diagnostics always seem to provide enough information to figure out which specialization your in. But that also seems to work:

/home/eric/llvm-project/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.move/call/rvalue_const_noexcept.pass.cpp:51:5: error: no matching function for call to object of type 'std::move_only_function<void () const && noexcept>'
  51 |     f();
       |     ^
 /home/eric/llvm-project/libcxx/include/__functional/move_only_function_impl.h:200:34: note: candidate function not viable: expects an rvalue for object argument
   200 |   _LIBCPP_HIDE_FROM_ABI _ReturnT operator()(_ArgTypes... __args) _LIBCPP_MOVE_ONLY_FUNCTION_CVREF

Now I still hate using the preprocessor, and dislike creating non-modular re-includable headers. And I have a strong aversion to its use. But diagnostics don't appear to be an issue here.

EDIT: See my more recent comments on the "repeated-include" strategy. I've warmed up to it substantially.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ldionne The implementation is attached to this comment (which was marked as resolved, unresolving for visibility.)


struct CallTypeChecker {
CallType* type;
using enum CallType;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice types.

};

template <class _BufferT, class _ReturnT, class... _ArgTypes>
struct _MoveOnlyFunctionNonTrivialVTable : _MoveOnlyFunctionTrivialVTable<_BufferT, _ReturnT, _ArgTypes...> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to instantiate this type with _ReturnT or _ArgTypes....

That's alot of template bloat.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate? I don't see how we can have the vtable function pointer without the template arguments.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You either don't need the inheritance, or you don't need to give the "destruct" base class all the arguments, you only need that for the call.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call function pointer is in the base class though. I always want the call pointer and only sometimes the destructor pointer.

@EricWF
Copy link
Member

EricWF commented Jun 7, 2024

After implementing a version myself, I think there's a bunch of unneeded complexity in this type.
I compared the two implementations on a personal compiler-explorer which is set up to compare libc++ patches (I'm working to provide to other developers).

The use of __libcpp_allocate and __libcpp_deallocate allow the compiler to optimize away the allocations in mine, but not in this implementation. The result is the difference between each test optimizing to basically zero code vs a bunch of code.

This is the codegen for one of the call tests

test(): # @test()
  push rax
  lea rax, [rip + std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<void (*)() noexcept>]
  cmp rax, 7
  jbe .LBB0_8
  lea rax, [rip + std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<TriviallyDestructible>]
  cmp rax, 7
  jbe .LBB0_8
  mov byte ptr [rip + called], 0
  mov edi, 40
  mov esi, 1
  call operator new[](unsigned long, std::align_val_t)@PLT
  lea rcx, [rip + std::__2::move_only_function<void () noexcept &&>::__non_trivial_vtable_<TriviallyDestructibleTooLarge>]
  cmp rcx, 7
  jbe .LBB0_8
  mov byte ptr [rip + called], 1
  mov esi, 40
  mov edx, 1
  mov rdi, rax
  call operator delete[](void*, unsigned long, std::align_val_t)@PLT
  mov byte ptr [rip + called], 0
  mov edi, 1
  mov esi, 1
  call operator new[](unsigned long, std::align_val_t)@PLT
  lea rcx, [rip + std::__2::move_only_function<void () noexcept &&>::__non_trivial_vtable_<NonTrivial>]
  cmp rcx, 7
  jbe .LBB0_8
  mov byte ptr [rip + called], 1
  mov esi, 1
  mov edx, 1
  mov rdi, rax
  call operator delete[](void*, unsigned long, std::align_val_t)@PLT
  lea rax, [rip + std::__2::move_only_function<void (S) noexcept &&>::__trivial_vtable_<void (S::*)() noexcept &&>]
  cmp rax, 7
  jbe .LBB0_11
  lea rax, [rip + std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<CallTypeCheckerNoexcept>]
  cmp rax, 7
  jbe .LBB0_8
  pop rax
  ret
.LBB0_8:
  ud2
.LBB0_11:
  lea rdi, [rip + .L.str.2]
  lea rsi, [rip + .L.str.1]
  lea rcx, [rip + .L__PRETTY_FUNCTION__.test()]
  mov edx, 56
  call __assert_fail@PLT
  mov rdi, rax
  call __clang_call_terminate
  mov rdi, rax
  call __clang_call_terminate
test_return(): # @test_return()
  push rax
  lea rax, [rip + std::__2::move_only_function<int (int) noexcept &&>::__trivial_vtable_<int (*)(int) noexcept>]
  cmp rax, 7
  jbe .LBB1_9
  lea rax, [rip + std::__2::move_only_function<int (int) noexcept &&>::__trivial_vtable_<TriviallyDestructible>]
  cmp rax, 7
  jbe .LBB1_9
  mov byte ptr [rip + called], 0
  mov edi, 40
  mov esi, 1
  call operator new[](unsigned long, std::align_val_t)@PLT
  lea rcx, [rip + std::__2::move_only_function<int (int) noexcept &&>::__non_trivial_vtable_<TriviallyDestructibleTooLarge>]
  cmp rcx, 7
  jbe .LBB1_9
  cmp byte ptr [rip + called], 1
  je .LBB1_11
  mov esi, 40
  mov edx, 1
  mov rdi, rax
  call operator delete[](void*, unsigned long, std::align_val_t)@PLT
  mov byte ptr [rip + called], 0
  mov edi, 1
  mov esi, 1
  call operator new[](unsigned long, std::align_val_t)@PLT
  lea rcx, [rip + std::__2::move_only_function<int (int) noexcept &&>::__non_trivial_vtable_<NonTrivial>]
  cmp rcx, 7
  jbe .LBB1_9
  cmp byte ptr [rip + called], 1
  je .LBB1_13
  mov esi, 1
  mov edx, 1
  mov rdi, rax
  pop rax
  jmp operator delete[](void*, unsigned long, std::align_val_t)@PLT # TAILCALL
.LBB1_9:
  ud2
.LBB1_11:
  lea rdi, [rip + .L.str.6]
  lea rsi, [rip + .L.str.1]
  lea rcx, [rip + .L__PRETTY_FUNCTION__.test_return()]
  mov edx, 89
  call __assert_fail@PLT
.LBB1_13:
  lea rdi, [rip + .L.str.6]
  lea rsi, [rip + .L.str.1]
  lea rcx, [rip + .L__PRETTY_FUNCTION__.test_return()]
  mov edx, 95
  call __assert_fail@PLT
  mov rdi, rax
  call __clang_call_terminate
  mov rdi, rax
  call __clang_call_terminate
main: # @main
  push rax
  call test_return()
  call test()
  xor eax, eax
  pop rcx
  ret
__clang_call_terminate: # @__clang_call_terminate
  push rax
  call __cxa_begin_catch@PLT
  call std::terminate()@PLT
std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<void (*)() noexcept>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&): # @std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<void (*)() noexcept>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&)
  jmp qword ptr [rdi] # TAILCALL
std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<TriviallyDestructible>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&): # @std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<TriviallyDestructible>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&)
  mov byte ptr [rip + called], 1
  ret
std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<TriviallyDestructibleTooLarge>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&): # @std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<TriviallyDestructibleTooLarge>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&)
  mov byte ptr [rip + called], 1
  ret
std::__2::move_only_function<void () noexcept &&>::__non_trivial_vtable_<TriviallyDestructibleTooLarge>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&): # @std::__2::move_only_function<void () noexcept &&>::__non_trivial_vtable_<TriviallyDestructibleTooLarge>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&)
  mov rdi, qword ptr [rdi]
  mov esi, 40
  mov edx, 1
  jmp operator delete[](void*, unsigned long, std::align_val_t)@PLT # TAILCALL
std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<NonTrivial>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&): # @std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<NonTrivial>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&)
  mov byte ptr [rip + called], 1
  ret
std::__2::move_only_function<void () noexcept &&>::__non_trivial_vtable_<NonTrivial>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&): # @std::__2::move_only_function<void () noexcept &&>::__non_trivial_vtable_<NonTrivial>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&)
  mov rdi, qword ptr [rdi]
  mov esi, 1
  mov edx, 1
  jmp operator delete[](void*, unsigned long, std::align_val_t)@PLT # TAILCALL
std::__2::move_only_function<void (S) noexcept &&>::__trivial_vtable_<void (S::*)() noexcept &&>::{lambda(std::__2::__small_buffer<24ul, 8ul>&, S)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&, S): # @std::__2::move_only_function<void (S) noexcept &&>::__trivial_vtable_<void (S::*)() noexcept &&>::{lambda(std::__2::__small_buffer<24ul, 8ul>&, S)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&, S)
  push rax
  mov rax, rdi
  lea rdi, [rsp + 7]
  add rdi, qword ptr [rax + 8]
  call qword ptr [rax]
  pop rax
  ret
std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<CallTypeCheckerNoexcept>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&): # @std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<CallTypeCheckerNoexcept>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&)
  mov rax, qword ptr [rdi]
  mov dword ptr [rax], 2
  ret
std::__2::move_only_function<int (int) noexcept &&>::__trivial_vtable_<int (*)(int) noexcept>::{lambda(std::__2::__small_buffer<24ul, 8ul>&, int)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&, int): # @std::__2::move_only_function<int (int) noexcept &&>::__trivial_vtable_<int (*)(int) noexcept>::{lambda(std::__2::__small_buffer<24ul, 8ul>&, int)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&, int)
  mov rax, rdi
  mov edi, esi
  jmp qword ptr [rax] # TAILCALL
std::__2::move_only_function<int (int) noexcept &&>::__trivial_vtable_<TriviallyDestructible>::{lambda(std::__2::__small_buffer<24ul, 8ul>&, int)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&, int): # @std::__2::move_only_function<int (int) noexcept &&>::__trivial_vtable_<TriviallyDestructible>::{lambda(std::__2::__small_buffer<24ul, 8ul>&, int)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&, int)
  mov eax, esi
  ret
std::__2::move_only_function<int (int) noexcept &&>::__trivial_vtable_<TriviallyDestructibleTooLarge>::{lambda(std::__2::__small_buffer<24ul, 8ul>&, int)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&, int): # @std::__2::move_only_function<int (int) noexcept &&>::__trivial_vtable_<TriviallyDestructibleTooLarge>::{lambda(std::__2::__small_buffer<24ul, 8ul>&, int)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&, int)
  mov eax, esi
  ret
std::__2::move_only_function<int (int) noexcept &&>::__non_trivial_vtable_<TriviallyDestructibleTooLarge>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&): # @std::__2::move_only_function<int (int) noexcept &&>::__non_trivial_vtable_<TriviallyDestructibleTooLarge>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&)
  mov rdi, qword ptr [rdi]
  mov esi, 40
  mov edx, 1
  jmp operator delete[](void*, unsigned long, std::align_val_t)@PLT # TAILCALL
std::__2::move_only_function<int (int) noexcept &&>::__trivial_vtable_<NonTrivial>::{lambda(std::__2::__small_buffer<24ul, 8ul>&, int)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&, int): # @std::__2::move_only_function<int (int) noexcept &&>::__trivial_vtable_<NonTrivial>::{lambda(std::__2::__small_buffer<24ul, 8ul>&, int)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&, int)
  mov eax, esi
  ret
std::__2::move_only_function<int (int) noexcept &&>::__non_trivial_vtable_<NonTrivial>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&): # @std::__2::move_only_function<int (int) noexcept &&>::__non_trivial_vtable_<NonTrivial>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&)
  mov rdi, qword ptr [rdi]
  mov esi, 1
  mov edx, 1
  jmp operator delete[](void*, unsigned long, std::align_val_t)@PLT # TAILCALL
called:
  .byte 0 # 0x0

.L.str.1:
  .asciz "/tmp/compiler-explorer-compiler202457-3044864-1gsqf1r.1a4w/example.cpp"

.L__PRETTY_FUNCTION__.test():
  .asciz "void test()"

.L.str.2:
  .asciz "f"

.L__PRETTY_FUNCTION__.test_return():
  .asciz "void test_return()"

.L.str.6:
  .asciz "!called"

std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<void (*)() noexcept>:
  .quad std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<void (*)() noexcept>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&)

std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<TriviallyDestructible>:
  .quad std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<TriviallyDestructible>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&)

std::__2::move_only_function<void () noexcept &&>::__non_trivial_vtable_<TriviallyDestructibleTooLarge>:
  .quad std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<TriviallyDestructibleTooLarge>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&)
  .quad std::__2::move_only_function<void () noexcept &&>::__non_trivial_vtable_<TriviallyDestructibleTooLarge>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&)

std::__2::move_only_function<void () noexcept &&>::__non_trivial_vtable_<NonTrivial>:
  .quad std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<NonTrivial>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&)
  .quad std::__2::move_only_function<void () noexcept &&>::__non_trivial_vtable_<NonTrivial>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&)

std::__2::move_only_function<void (S) noexcept &&>::__trivial_vtable_<void (S::*)() noexcept &&>:
  .quad std::__2::move_only_function<void (S) noexcept &&>::__trivial_vtable_<void (S::*)() noexcept &&>::{lambda(std::__2::__small_buffer<24ul, 8ul>&, S)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&, S)

std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<CallTypeCheckerNoexcept>:
  .quad std::__2::move_only_function<void () noexcept &&>::__trivial_vtable_<CallTypeCheckerNoexcept>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&)

std::__2::move_only_function<int (int) noexcept &&>::__trivial_vtable_<int (*)(int) noexcept>:
  .quad std::__2::move_only_function<int (int) noexcept &&>::__trivial_vtable_<int (*)(int) noexcept>::{lambda(std::__2::__small_buffer<24ul, 8ul>&, int)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&, int)

std::__2::move_only_function<int (int) noexcept &&>::__trivial_vtable_<TriviallyDestructible>:
  .quad std::__2::move_only_function<int (int) noexcept &&>::__trivial_vtable_<TriviallyDestructible>::{lambda(std::__2::__small_buffer<24ul, 8ul>&, int)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&, int)

std::__2::move_only_function<int (int) noexcept &&>::__non_trivial_vtable_<TriviallyDestructibleTooLarge>:
  .quad std::__2::move_only_function<int (int) noexcept &&>::__trivial_vtable_<TriviallyDestructibleTooLarge>::{lambda(std::__2::__small_buffer<24ul, 8ul>&, int)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&, int)
  .quad std::__2::move_only_function<int (int) noexcept &&>::__non_trivial_vtable_<TriviallyDestructibleTooLarge>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&)

std::__2::move_only_function<int (int) noexcept &&>::__non_trivial_vtable_<NonTrivial>:
  .quad std::__2::move_only_function<int (int) noexcept &&>::__trivial_vtable_<NonTrivial>::{lambda(std::__2::__small_buffer<24ul, 8ul>&, int)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&, int)
  .quad std::__2::move_only_function<int (int) noexcept &&>::__non_trivial_vtable_<NonTrivial>::{lambda(std::__2::__small_buffer<24ul, 8ul>&)#1}::__invoke(std::__2::__small_buffer<24ul, 8ul>&)

DW.ref.__gxx_personality_v0:
  .quad __gxx_personality_v0

vs

test(): # @test()
  mov byte ptr [rip + called], 1
  ret
test_return(): # @test_return()
  mov byte ptr [rip + called], 0
  ret
main: # @main
  mov byte ptr [rip + called], 1
  xor eax, eax
  ret
called:
  .byte 0 # 0x0

I also think the __pointer_int_pair idiom is unneeded, and should be removed. It causes the optimizer to lose track of the __call_ function pointer, which prevents the ellision of the call. That's a big issue, and one that I think should block.

Further, the use of __small_buffer prevents the ellision of the allocations, further hurting the optimizer.
I think this too should be simplified before proceeding. You can replace it with

union { 
   void* __ptr_; 
   std::byte __buff_[sizeof(void*) * 3];
 }

I'm very happy to proceed using your mechanism for stamping out the specializations using repeated includes, In fact I prefer it over my overcomplicated metaprogramming.

however I think there are a lot of simplifications that can be taken from my implementation that make things more readable and performant.

To Summarize, the requested changes are:

  • remove __pointer_int_pair.
  • remove __small_buffer.
  • Simplify the vtable. There's no need for inheritance. Use __delete_ == nullptr to signal trivially destructible types.

Copy link
Member

@EricWF EricWF left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see the previous comment for the requested changes.

@EricWF
Copy link
Member

EricWF commented Jun 7, 2024

Oh, I just noticed there are no tests for exception safety. Could you please add some throwing types? (Or perhaps I just missed those tests?).

Currently this code terminates:

#include <functional>
#include <cassert>

struct ThrowsOnConstruct {
  explicit ThrowsOnConstruct(int x) : value(x) { if (x == 42) throw 42; }
  ThrowsOnConstruct(ThrowsOnConstruct const&) = delete;
  ThrowsOnConstruct(ThrowsOnConstruct && other) : value(other.value) { if (other.value == 101) throw 101; other.value = -1; }

  ~ThrowsOnConstruct() {
    assert(value != -1);
    value = -1;
  }

  int operator()() const noexcept { return value; }

  int value;
};

int main() {
  using MV = std::move_only_function<int() const>;
  try {
    MV f(ThrowsOnConstruct(101));
  } catch (...) {
  }
}

In particular, I don't think this constructor can be noexcept.

I also think there are some missing tests for reference_wrapper.

@philnik777
Copy link
Contributor Author

@EricWF When switching to __libcpp_operator_{new,delete} everything gets compiled exactly the same on my system. Could you disclose which test you used for this code? You also claim that the __pointer_int_pair causes the optimizer to lose track of the function pointer causing the call to not be elided, but I can't see any calls other than new and delete in the code you posted either.

}

_LIBCPP_HIDE_FROM_ABI void __reset() {
if (__vtable_.__get_value())
Copy link
Contributor

@huixie90 huixie90 Jun 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, I think this is the only place where the bool is used. IIUC, the only purpose of the bool is an optimisation that skips the virtual destructor call for trivially destructible types. I think we can avoid using this bool at all. You can just have a vtable that contains "destroy" function pointer all the time. for trivially destrutible type, you just make the destroy function pointer nullptr. and here you simply check __vtable_.destroy_ != nullptr

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bool is in the low bit of the pointer to avoid the need to even look at the vtable in the destructor. The cost of zeroing the lower bits of the pointer is pretty much non-existent on modern platforms (the delay is at most a single cycle - most likely not even that) and we gain that we avoid a potential cache miss during destruction.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's other cost here.

There's complexity, there's ABI risk. To speak on the ABI risk.:

From my investigations Clang has trouble optimizing around this, where it doesn't have trouble optimizing around what @huixie90 is suggesting, which I've implemented and tested out.

And there's two reviewers asking you to remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then provide the fucking evidence.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far the conclusion was that this bool is used to optimise the destructor for trivially destructible types, to save one vtable ptr indirection, at the cost of code complexity and some pointer pokery in other operations like operator(). And the cost is debatable. (At least, you two seem to disagree on this cost)

Personally, I'd suggest to create some benchmarks, focusing on some realistic use cases and see if optimisation focusing on the destructor is worth it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main thing I'm objecting to currently is Eric's behaviour. He's showing zero evidence for his claims while being extremely patronizing to the point of being insulting [1] and claiming he's got knowledge of other's mental states which he obviously has no access to [2].

[1]

@philnik777 I would encourage you to read about the importance of learning to "kill your darlings". The saying comes from creating writing, but is equally important here.

[2] (#94324)

I think we discovered that this type is not needed (and somewhat harmful to the optimizer)

Anyways, yes, benchmarks would be nice, but I'm not convinced we can produce any micro-benchmarks that are close to realistic. Anything we produce will be highly biased based on the usage we expect. Cache misses on the scale of a program are also extremely hard to emulate in any micro benchmark. If you have any suggestions they're very welcome. I'd love to have some more detailed data on this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philnik777 It seems we're having trouble communicating. I'm happy to meet with you over video chat, hopefully that can clear up any miscommunications.

I promise there is no malice or ill-intent.

The comment about "killing your darlings" is honest advice. Perhaps the phrase comes off poorly if you've never heard it before. Here's a blog post explaining the saying.

What Does It Mean to Kill Your Darlings?
Writing is a painful process and most experienced writers will tell you that good writing involves substantial rewriting. An essential part of the rewriting process is combing through your work and cutting out material that isn’t essential. Sometimes this means we have to lose things that we are proud of and attached to. When you edit out material like this, you are killing your darlings.

I've provided a complete & test-passing implementation to compare against. It's the baseline I used for this review. I'm confident my results are repeatable. Please let me know If can help you reproduce the output I've stated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've provided a complete & test-passing implementation to compare against. It's the baseline I used for this review. I'm confident my results are repeatable.

Sorry, I'm trying to follow the discussion after the fact but I can't find where you posted that implementation. Can you please share the link again?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ldionne You can find it in the comment here.

@EricWF
Copy link
Member

EricWF commented Jun 10, 2024

@EricWF When switching to __libcpp_operator_{new,delete} everything gets compiled exactly the same on my system. Could you disclose which test you used for this code? You also claim that the __pointer_int_pair causes the optimizer to lose track of the function pointer causing the call to not be elided, but I can't see any calls other than new and delete in the code you posted either.

I meant __libcpp_allocate and __libcpp_deallocate. That's my mistake.

I may have misread the assembly earlier, it's possible there's some other issue causing the compiler to fail to optimize your code away in the same way the compiler optimizes the implementation I provided.

@philnik777 I would encourage you to read about the importance of learning to "kill your darlings". The saying comes from creating writing, but is equally important here.

Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not certain how to best make a decision about the bit-stealing mechanism yet, but I do have a few comments.

libcxx/include/__functional/move_only_function.h Outdated Show resolved Hide resolved
@Curve
Copy link
Member

Curve commented Oct 7, 2024

Is there something one could help with in this PR? It'd be great to see move_only_function coming to libc++ in the foreseeable future :)

@philnik777 philnik777 force-pushed the users/philnik777/spr/add_pointer_int_pair branch from b8a792a to e945335 Compare November 4, 2024 13:50
@philnik777 philnik777 force-pushed the users/philnik777/spr/add_pointer_int_pair branch from e945335 to 5780820 Compare November 4, 2024 15:13
Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the most effective way of settling on the bit-stealing issue would be to benchmark something like this:

  1. Set up ~100 functions that do random stuff (e.g. they all return numbers from 1 to 100)
  2. Set up a std::vector<std::move_only_function<int()>> with some large number of functions in it.
  3. Assign from the 100 functions randomly into the vector, so that the vector contains roughly uniformly distributed function pointers.
  4. Measure how long it takes to call all the functions in the vector using this bit-stealing implementation, and an implementation where we store a null entry in the destructor instead.

Similarly, for this benchmark we could measure the time it takes for destroying the vector.

@philnik777 philnik777 force-pushed the users/philnik777/spr/add_pointer_int_pair branch from 5780820 to d28c822 Compare November 7, 2024 11:37
@frederick-vs-ja frederick-vs-ja linked an issue Mar 17, 2025 that may be closed by this pull request
@frederick-vs-ja
Copy link
Contributor

frederick-vs-ja commented May 14, 2025

FYI P2548R2 added relaxing wording to [func.wrap.general] to allow unwrapping in construction. I think we should implement the allowance for move_only_function in C++23 as a DR. libstdc++ starts doing so recently.

We can't unwrap in function construction though, IIUC, because its target object is observable. But we can unwrap function when constructing a move_only_function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

P0288R9: move_only_function
8 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.