[libcxx-commits] [libcxx] constexpr function ref (PR #186692)
via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Mar 24 00:31:45 PDT 2026
https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/186692
>From bbb94dafea0cf8d620dff55733833fd8c2fb74a7 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 8 Jun 2024 17:32:42 -0700
Subject: [PATCH 01/22] [libc++] P0792R14: 'function_ref'
---
libcxx/include/CMakeLists.txt | 4 +
libcxx/include/__functional/function_ref.h | 46 +++++
.../__functional/function_ref_common.h | 72 +++++++
.../include/__functional/function_ref_impl.h | 181 ++++++++++++++++++
libcxx/include/__utility/nontype.h | 34 ++++
libcxx/include/functional | 4 +
libcxx/include/module.modulemap.in | 2 +
libcxx/include/utility | 10 +
.../func.wrap.ref/call/const.pass.cpp | 124 ++++++++++++
.../call/const_noexcept.pass.cpp | 124 ++++++++++++
.../func.wrap.ref/call/default.pass.cpp | 119 ++++++++++++
.../func.wrap.ref/call/noexcept.pass.cpp | 119 ++++++++++++
12 files changed, 839 insertions(+)
create mode 100644 libcxx/include/__functional/function_ref.h
create mode 100644 libcxx/include/__functional/function_ref_common.h
create mode 100644 libcxx/include/__functional/function_ref_impl.h
create mode 100644 libcxx/include/__utility/nontype.h
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const.pass.cpp
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const_noexcept.pass.cpp
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/default.pass.cpp
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/noexcept.pass.cpp
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 5cdf29b94e3eb..3b30fc47e4b2d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -431,6 +431,9 @@ set(files
__functional/compose.h
__functional/default_searcher.h
__functional/function.h
+ __functional/function_ref_common.h
+ __functional/function_ref_impl.h
+ __functional/function_ref.h
__functional/hash.h
__functional/identity.h
__functional/invoke.h
@@ -940,6 +943,7 @@ set(files
__utility/lazy_synth_three_way_comparator.h
__utility/move.h
__utility/no_destroy.h
+ __utility/nontype.h
__utility/pair.h
__utility/piecewise_construct.h
__utility/priority_tag.h
diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
new file mode 100644
index 0000000000000..01cc22a64018e
--- /dev/null
+++ b/libcxx/include/__functional/function_ref.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_FUNCTION_REF_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+// NOLINTBEGIN(readability-duplicate-include)
+
+#define _LIBCPP_FUNCTION_REF_CV
+#define _LIBCPP_FUNCTION_REF_NOEXCEPT false
+#include <__functional/function_ref_impl.h>
+#undef _LIBCPP_FUNCTION_REF_CV
+#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
+
+#define _LIBCPP_FUNCTION_REF_CV
+#define _LIBCPP_FUNCTION_REF_NOEXCEPT true
+#include <__functional/function_ref_impl.h>
+#undef _LIBCPP_FUNCTION_REF_CV
+#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
+
+#define _LIBCPP_FUNCTION_REF_CV const
+#define _LIBCPP_FUNCTION_REF_NOEXCEPT false
+#include <__functional/function_ref_impl.h>
+#undef _LIBCPP_FUNCTION_REF_CV
+#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
+
+#define _LIBCPP_FUNCTION_REF_CV const
+#define _LIBCPP_FUNCTION_REF_NOEXCEPT true
+#include <__functional/function_ref_impl.h>
+#undef _LIBCPP_FUNCTION_REF_CV
+#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
+
+// NOLINTEND(readability-duplicate-include)
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
new file mode 100644
index 0000000000000..f7d5575ebaffd
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
+
+#include <__config>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/remove_pointer.h>
+#include <__utility/nontype.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <class>
+inline constexpr bool __is_function_ref = false;
+
+template <class _Rp, class... _ArgTypes>
+inline constexpr bool __is_function_ref<function_ref<_Rp, _ArgTypes...>> = true;
+
+template <class _Fp, class _Tp>
+struct __function_ref_bind;
+
+template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...), _Tp> {
+ using type = _Rp(_ArgTypes...);
+};
+
+template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept, _Tp> {
+ using type = _Rp(_ArgTypes...) noexcept;
+};
+
+template <class _Tp, class _Mp, class _Gp>
+ requires is_object_v<_Mp>
+struct __function_ref_bind<_Mp _Gp::*, _Tp> {
+ using type = invoke_result_t<_Mp _Gp::*, _Tp&>;
+};
+
+template <class _Fp, class _Tp>
+using __function_ref_bind_t = __function_ref_bind<_Fp, _Tp>::type;
+
+template <class _Fp>
+ requires is_function_v<_Fp>
+function_ref(_Fp*) -> function_ref<_Fp>;
+
+template <auto _Fn>
+ requires is_function_v<remove_pointer_t<decltype(_Fn)>>
+function_ref(nontype_t<_Fn>) -> function_ref<remove_pointer_t<decltype(_Fn)>>;
+
+template <auto _Fn, class _Tp>
+function_ref(nontype_t<_Fn>, _Tp&&) -> function_ref<__function_ref_bind_t<decltype(_Fn), _Tp&>>;
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
new file mode 100644
index 0000000000000..4e1cd13a02ae2
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -0,0 +1,181 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__assert>
+#include <__config>
+#include <__functional/function_ref_common.h>
+#include <__functional/invoke.h>
+#include <__memory/addressof.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_member_pointer.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_pointer.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/forward.h>
+#include <__utility/nontype.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <class _Rp, class... _ArgTypes>
+class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT)> {
+private:
+# if _LIBCPP_FUNCTION_REF_NOEXCEPT == true
+ template <class... _Tp>
+ static constexpr bool __is_invocable_using = is_nothrow_invocable_r_v<_Rp, _Tp..., _ArgTypes...>;
+# else
+ template <class... _Tp>
+ static constexpr bool __is_invocable_using = is_invocable_r_v<_Rp, _Tp..., _ArgTypes...>;
+# endif
+
+ union __storage_t {
+ void* __obj_ptr;
+ void const* __obj_const_ptr;
+ void (*__fn_ptr)();
+
+ constexpr explicit __storage_t() noexcept : __obj_ptr(nullptr) {};
+
+ template <class _Tp>
+ constexpr explicit __storage_t(_Tp* __ptr) noexcept {
+ if constexpr (is_object_v<_Tp>) {
+ if constexpr (is_const_v<_Tp>) {
+ __obj_const_ptr = __ptr;
+ } else {
+ __obj_ptr = __ptr;
+ }
+ } else {
+ static_assert(is_function_v<_Tp>);
+ __fn_ptr = reinterpret_cast<void (*)()>(__ptr);
+ }
+ }
+ } __storage_;
+
+ template <class _Tp>
+ _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_t __storage) {
+ if constexpr (is_object_v<_Tp>) {
+ if constexpr (is_const_v<_Tp>) {
+ return static_cast<_Tp*>(__storage.__obj_const_ptr);
+ } else {
+ return static_cast<_Tp*>(__storage.__obj_ptr);
+ }
+ } else {
+ static_assert(is_function_v<_Tp>);
+ return reinterpret_cast<_Tp*>(__storage.__fn_ptr);
+ }
+ }
+
+ using __vtable_call_t = _Rp (*)(__storage_t, _ArgTypes&&...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+ __vtable_call_t __vtable_call_;
+
+public:
+ template <class _Fp>
+ requires is_function_v<_Fp> && __is_invocable_using<_Fp>
+ _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
+ : __storage_(__fn_ptr),
+ __vtable_call_(
+ [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ return __get<_Fp>(__storage)(std::forward<_ArgTypes>(__args)...);
+ }) {
+ _LIBCPP_ASSERT_UNCATEGORIZED(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
+ }
+
+ template <class _Fp, class _Tp = remove_reference_t<_Fp>>
+ requires(!__is_function_ref<remove_cvref_t<_Fp>> && !is_member_pointer_v<_Tp> &&
+ __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&>)
+ _LIBCPP_HIDE_FROM_ABI function_ref(_Fp&& __obj) noexcept
+ : __storage_(std::addressof(__obj)),
+ __vtable_call_(
+ [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
+ return __obj(std::forward<_ArgTypes>(__args)...);
+ }) {}
+
+ template <auto _Fn>
+ requires __is_invocable_using<decltype(_Fn)>
+ constexpr function_ref(nontype_t<_Fn>) noexcept
+ : __vtable_call_([](__storage_t, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ return std::invoke_r<_Rp>(_Fn, std::forward<_ArgTypes>(__args)...);
+ }) {
+ if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
+ static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
+ }
+ }
+
+ template <auto _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
+ requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp&>
+ constexpr function_ref(nontype_t<_Fn>, _Up&& __obj) noexcept
+ : __storage_(std::addressof(__obj)),
+ __vtable_call_(
+ [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
+ return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
+ }) {
+ if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
+ static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
+ }
+ }
+
+ template <auto _Fn, class _Tp>
+ requires __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp*>
+ constexpr function_ref(nontype_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
+ : __storage_(__obj_ptr),
+ __vtable_call_(
+ [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
+ return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
+ }) {
+ if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
+ static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
+ }
+
+ if constexpr (is_member_pointer_v<decltype(_Fn)>) {
+ _LIBCPP_ASSERT_UNCATEGORIZED(__obj_ptr != nullptr, "the object pointer should not be a nullptr");
+ }
+ }
+
+ constexpr function_ref(const function_ref&) noexcept = default;
+
+ constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+ template <class _Tp>
+ requires(!__is_function_ref<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_nontype_t<_Tp>)
+ function_ref& operator=(_Tp) = delete;
+
+ constexpr _Rp operator()(_ArgTypes... __args) const noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) {
+ return __vtable_call_(__storage_, std::forward<_ArgTypes>(__args)...);
+ }
+};
+
+template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
+ _Tp> {
+ using type = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+};
+
+template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
+ _Tp> {
+ using type = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__utility/nontype.h b/libcxx/include/__utility/nontype.h
new file mode 100644
index 0000000000000..86893f17e2d96
--- /dev/null
+++ b/libcxx/include/__utility/nontype.h
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___UTILITY_NONTYPE_H
+#define _LIBCPP___UTILITY_NONTYPE_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <auto _Vp>
+struct nontype_t {
+ explicit nontype_t() = default;
+};
+
+template <auto _Vp>
+inline constexpr nontype_t<_Vp> nontype{};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___UTILITY_NONTYPE_H
diff --git a/libcxx/include/functional b/libcxx/include/functional
index 9ebcd818ec840..dbc023ac637d7 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -569,6 +569,10 @@ POLICY: For non-variadic implementations, the number of arguments is limited
# include <__type_traits/unwrap_ref.h>
# endif
+# if _LIBCPP_STD_VER >= 26
+# include <__functional/function_ref.h>
+# endif
+
# include <version>
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 9012ed18cbd79..9ef908c66ca7c 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1425,6 +1425,7 @@ module std [system] {
}
module default_searcher { header "__functional/default_searcher.h" }
module function { header "__functional/function.h" }
+ module function_ref { header "__functional/function_ref.h" }
module hash { header "__functional/hash.h" }
module identity { header "__functional/identity.h" }
module invoke { header "__functional/invoke.h" }
@@ -2186,6 +2187,7 @@ module std [system] {
module lazy_synth_three_way_comparator { header "__utility/lazy_synth_three_way_comparator.h" }
module move { header "__utility/move.h" }
module no_destroy { header "__utility/no_destroy.h" }
+ module nontype { header "__utility/nontype.h" }
module pair {
header "__utility/pair.h"
export std.utility.piecewise_construct
diff --git a/libcxx/include/utility b/libcxx/include/utility
index 1b19243afca1b..f6cffd3898c21 100644
--- a/libcxx/include/utility
+++ b/libcxx/include/utility
@@ -250,6 +250,15 @@ template <size_t I>
template <size_t I>
inline constexpr in_place_index_t<I> in_place_index{};
+// nontype argument tag
+template<auto V>
+ struct nontype_t {
+ explicit nontype_t() = default;
+ };
+template<auto V> constexpr nontype_t<V> nontype{};
+
+template<auto V> constexpr nontype_t<V> nontype{};
+
// [utility.underlying], to_underlying
template <class T>
constexpr underlying_type_t<T> to_underlying( T value ) noexcept; // C++23
@@ -292,6 +301,7 @@ template <class T>
# endif
# if _LIBCPP_STD_VER >= 26
+# include <__utility/nontype.h>
# include <__variant/monostate.h>
# endif
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const.pass.cpp
new file mode 100644
index 0000000000000..53d7a5922353d
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const.pass.cpp
@@ -0,0 +1,124 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_invocable_v<std::function_ref<void() const>&>);
+static_assert(std::is_invocable_v<std::function_ref<void() const>>);
+static_assert(std::is_invocable_v<std::function_ref<void() const>&&>);
+static_assert(std::is_invocable_v<std::function_ref<void() const> const&>);
+static_assert(std::is_invocable_v<std::function_ref<void() const> const>);
+static_assert(std::is_invocable_v<std::function_ref<void() const> const&&>);
+
+int fn() { return 42; }
+
+struct {
+ int operator()() const { return 42; }
+} fn_obj;
+
+void test() {
+ // template<class F> function_ref(F* f) noexcept;
+ {
+ // initialized from a function
+ std::function_ref<int() const> fn_ref = fn;
+ assert(fn_ref() == 42);
+ }
+ {
+ // initialized from a function pointer
+ std::function_ref<int() const> fn_ref = &fn;
+ assert(fn_ref() == 42);
+ }
+
+ // template<class F> constexpr function_ref(F&& f) noexcept;
+ {
+ // initialized from a function object
+ std::function_ref<int() const> fn_ref = fn_obj;
+ assert(fn_ref() == 42);
+ }
+}
+
+struct S {
+ int data_mem = 42;
+
+ int fn_mem() const { return 42; }
+};
+
+void test_nontype_t() {
+ // template<auto f> constexpr function_ref(nontype_t<f>) noexcept;
+ {
+ // initialized from a function through `nontype_t`
+ std::function_ref<int() const> fn_ref = std::nontype_t<fn>();
+ assert(fn_ref() == 42);
+ }
+ {
+ // initialized from a function pointer through `nontype_t`
+ std::function_ref<int() const> fn_ref = std::nontype_t<&fn>();
+ assert(fn_ref() == 42);
+ }
+ {
+ // initialized from a function object through `nontype_t`
+ std::function_ref<int() const> fn_ref = std::nontype_t<fn_obj>();
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t`
+ std::function_ref<int(S) const> fn_ref = std::nontype_t<&S::data_mem>();
+ assert(fn_ref(s) == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t`
+ std::function_ref<int(S) const> fn_ref = std::nontype_t<&S::fn_mem>();
+ assert(fn_ref(s) == 42);
+ }
+
+ // template<auto f, class U>
+ // constexpr function_ref(nontype_t<f>, U&& obj) noexcept;
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t` and bound to an object through a reference
+ std::function_ref<int() const> fn_ref = {std::nontype_t<&S::data_mem>(), s};
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t` and bound to an object through a reference
+ std::function_ref<int() const> fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+ assert(fn_ref() == 42);
+ }
+
+ // template<auto f, class T>
+ // constexpr function_ref(nontype_t<f>, cv T* obj) noexcept;
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t` and bound to an object through a pointer
+ std::function_ref<int() const> fn_ref = {std::nontype_t<&S::data_mem>(), &s};
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t` and bound to an object through a pointer
+ static_assert(std::is_same_v<decltype(&s), S*>);
+ std::function_ref<int() const> fn_ref = {std::nontype_t<&S::fn_mem>(), &s};
+ assert(fn_ref() == 42);
+ }
+}
+
+int main(int, char**) {
+ test();
+ test_nontype_t();
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const_noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const_noexcept.pass.cpp
new file mode 100644
index 0000000000000..18d44e4b7ef0b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const_noexcept.pass.cpp
@@ -0,0 +1,124 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_invocable_v<std::function_ref<void() const noexcept>&>);
+static_assert(std::is_invocable_v<std::function_ref<void() const noexcept>>);
+static_assert(std::is_invocable_v<std::function_ref<void() const noexcept>&&>);
+static_assert(std::is_invocable_v<std::function_ref<void() const noexcept> const&>);
+static_assert(std::is_invocable_v<std::function_ref<void() const noexcept> const >);
+static_assert(std::is_invocable_v<std::function_ref<void() const noexcept> const&&>);
+
+int fn() noexcept { return 42; }
+
+struct {
+ int operator()() const noexcept { return 42; }
+} fn_obj;
+
+void test() {
+ // template<class F> function_ref(F* f) noexcept;
+ {
+ // initialized from a function
+ std::function_ref<int() const noexcept> fn_ref = fn;
+ assert(fn_ref() == 42);
+ }
+ {
+ // initialized from a function pointer
+ std::function_ref<int() const noexcept> fn_ref = &fn;
+ assert(fn_ref() == 42);
+ }
+
+ // template<class F> const noexceptexpr function_ref(F&& f) noexcept;
+ {
+ // initialized from a function object
+ std::function_ref<int() const noexcept> fn_ref = fn_obj;
+ assert(fn_ref() == 42);
+ }
+}
+
+struct S {
+ int data_mem = 42;
+
+ int fn_mem() const noexcept { return 42; }
+};
+
+void test_nontype_t() {
+ // template<auto f> const noexceptexpr function_ref(nontype_t<f>) noexcept;
+ {
+ // initialized from a function through `nontype_t`
+ std::function_ref<int() const noexcept> fn_ref = std::nontype_t<fn>();
+ assert(fn_ref() == 42);
+ }
+ {
+ // initialized from a function pointer through `nontype_t`
+ std::function_ref<int() const noexcept> fn_ref = std::nontype_t<&fn>();
+ assert(fn_ref() == 42);
+ }
+ {
+ // initialized from a function object through `nontype_t`
+ std::function_ref<int() const noexcept> fn_ref = std::nontype_t<fn_obj>();
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t`
+ std::function_ref<int(S) const noexcept> fn_ref = std::nontype_t<&S::data_mem>();
+ assert(fn_ref(s) == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t`
+ std::function_ref<int(S) const noexcept> fn_ref = std::nontype_t<&S::fn_mem>();
+ assert(fn_ref(s) == 42);
+ }
+
+ // template<auto f, class U>
+ // const noexceptexpr function_ref(nontype_t<f>, U&& obj) noexcept;
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t` and bound to an object through a reference
+ std::function_ref<int() const noexcept> fn_ref = {std::nontype_t<&S::data_mem>(), s};
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t` and bound to an object through a reference
+ std::function_ref<int() const noexcept> fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+ assert(fn_ref() == 42);
+ }
+
+ // template<auto f, class T>
+ // const noexceptexpr function_ref(nontype_t<f>, cv T* obj) noexcept;
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t` and bound to an object through a pointer
+ std::function_ref<int() const noexcept> fn_ref = {std::nontype_t<&S::data_mem>(), &s};
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t` and bound to an object through a pointer
+ static_assert(std::is_same_v<decltype(&s), S*>);
+ std::function_ref<int() const noexcept> fn_ref = {std::nontype_t<&S::fn_mem>(), &s};
+ assert(fn_ref() == 42);
+ }
+}
+
+int main(int, char**) {
+ test();
+ test_nontype_t();
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/default.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/default.pass.cpp
new file mode 100644
index 0000000000000..ebc07a6c3520c
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/default.pass.cpp
@@ -0,0 +1,119 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_invocable_v<std::function_ref<void()>&>);
+static_assert(std::is_invocable_v<std::function_ref<void()>>);
+static_assert(std::is_invocable_v<std::function_ref<void()>&&>);
+static_assert(std::is_invocable_v<std::function_ref<void()> const&>);
+static_assert(std::is_invocable_v<std::function_ref<void()> const>);
+static_assert(std::is_invocable_v<std::function_ref<void()> const&&>);
+
+int fn() { return 42; }
+
+struct {
+ int operator()() { return 42; }
+} fn_obj;
+
+void test() {
+ // template<class F> function_ref(F* f) noexcept;
+ {
+ // initialized from a function
+ std::function_ref<int()> fn_ref = fn;
+ assert(fn_ref() == 42);
+ }
+ {
+ // initialized from a function pointer
+ std::function_ref<int()> fn_ref = &fn;
+ assert(fn_ref() == 42);
+ }
+
+ // template<class F> constexpr function_ref(F&& f) noexcept;
+ {
+ // initialized from a function object
+ std::function_ref<int()> fn_ref = fn_obj;
+ assert(fn_ref() == 42);
+ }
+}
+
+struct S {
+ int data_mem = 42;
+
+ int fn_mem() { return 42; }
+};
+
+void test_nontype_t() {
+ // template<auto f> constexpr function_ref(nontype_t<f>) noexcept;
+ {
+ // initialized from a function through `nontype_t`
+ std::function_ref<int()> fn_ref = std::nontype_t<fn>();
+ assert(fn_ref() == 42);
+ }
+ {
+ // initialized from a function pointer through `nontype_t`
+ std::function_ref<int()> fn_ref = std::nontype_t<&fn>();
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t`
+ std::function_ref<int(S)> fn_ref = std::nontype_t<&S::data_mem>();
+ assert(fn_ref(s) == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t`
+ std::function_ref<int(S)> fn_ref = std::nontype_t<&S::fn_mem>();
+ assert(fn_ref(s) == 42);
+ }
+
+ // template<auto f, class U>
+ // constexpr function_ref(nontype_t<f>, U&& obj) noexcept;
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t` and bound to an object through a reference
+ std::function_ref<int()> fn_ref = {std::nontype_t<&S::data_mem>(), s};
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t` and bound to an object through a reference
+ std::function_ref<int()> fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+ assert(fn_ref() == 42);
+ }
+
+ // template<auto f, class T>
+ // constexpr function_ref(nontype_t<f>, cv T* obj) noexcept;
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t` and bound to an object through a pointer
+ std::function_ref<int()> fn_ref = {std::nontype_t<&S::data_mem>(), &s};
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t` and bound to an object through a pointer
+ static_assert(std::is_same_v<decltype(&s), S*>);
+ std::function_ref<int()> fn_ref = {std::nontype_t<&S::fn_mem>(), &s};
+ assert(fn_ref() == 42);
+ }
+}
+
+int main(int, char**) {
+ test();
+ test_nontype_t();
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/noexcept.pass.cpp
new file mode 100644
index 0000000000000..9b58e677ed6ba
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/noexcept.pass.cpp
@@ -0,0 +1,119 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_invocable_v<std::function_ref<void() noexcept>&>);
+static_assert(std::is_invocable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_invocable_v<std::function_ref<void() noexcept>&&>);
+static_assert(std::is_invocable_v<std::function_ref<void() noexcept> const&>);
+static_assert(std::is_invocable_v<std::function_ref<void() noexcept> const>);
+static_assert(std::is_invocable_v<std::function_ref<void() noexcept> const&&>);
+
+int fn() noexcept { return 42; }
+
+struct {
+ int operator()() noexcept { return 42; }
+} fn_obj;
+
+void test() {
+ // template<class F> function_ref(F* f) noexcept;
+ {
+ // initialized from a function
+ std::function_ref<int() noexcept> fn_ref = fn;
+ assert(fn_ref() == 42);
+ }
+ {
+ // initialized from a function pointer
+ std::function_ref<int() noexcept> fn_ref = &fn;
+ assert(fn_ref() == 42);
+ }
+
+ // template<class F> constexpr function_ref(F&& f) noexcept;
+ {
+ // initialized from a function object
+ std::function_ref<int() noexcept> fn_ref = fn_obj;
+ assert(fn_ref() == 42);
+ }
+}
+
+struct S {
+ int data_mem = 42;
+
+ int fn_mem() noexcept { return 42; }
+};
+
+void test_nontype_t() {
+ // template<auto f> constexpr function_ref(nontype_t<f>) noexcept;
+ {
+ // initialized from a function through `nontype_t`
+ std::function_ref<int() noexcept> fn_ref = std::nontype_t<fn>();
+ assert(fn_ref() == 42);
+ }
+ {
+ // initialized from a function pointer through `nontype_t`
+ std::function_ref<int() noexcept> fn_ref = std::nontype_t<&fn>();
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t`
+ std::function_ref<int(S) noexcept> fn_ref = std::nontype_t<&S::data_mem>();
+ assert(fn_ref(s) == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t`
+ std::function_ref<int(S) noexcept> fn_ref = std::nontype_t<&S::fn_mem>();
+ assert(fn_ref(s) == 42);
+ }
+
+ // template<auto f, class U>
+ // constexpr function_ref(nontype_t<f>, U&& obj) noexcept;
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t` and bound to an object through a reference
+ std::function_ref<int() noexcept> fn_ref = {std::nontype_t<&S::data_mem>(), s};
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t` and bound to an object through a reference
+ std::function_ref<int() noexcept> fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+ assert(fn_ref() == 42);
+ }
+
+ // template<auto f, class T>
+ // constexpr function_ref(nontype_t<f>, cv T* obj) noexcept;
+ {
+ S s;
+ // initialized from a pointer to data member through `nontype_t` and bound to an object through a pointer
+ std::function_ref<int() noexcept> fn_ref = {std::nontype_t<&S::data_mem>(), &s};
+ assert(fn_ref() == 42);
+ }
+ {
+ S s;
+ // initialized from a pointer to function member through `nontype_t` and bound to an object through a pointer
+ static_assert(std::is_same_v<decltype(&s), S*>);
+ std::function_ref<int() noexcept> fn_ref = {std::nontype_t<&S::fn_mem>(), &s};
+ assert(fn_ref() == 42);
+ }
+}
+
+int main(int, char**) {
+ test();
+ test_nontype_t();
+ return 0;
+}
>From 043f6110f85ea1247272510530360688d87fb07e Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 8 Jun 2024 23:22:06 -0700
Subject: [PATCH 02/22] [libc++] P0792R14: 'function_ref'
---
.../__functional/function_ref_common.h | 8 +-
.../include/__functional/function_ref_impl.h | 48 +++++------
.../func.wrap.ref/ctor/ctad.pass.cpp | 84 +++++++++++++++++++
.../{call => invoke}/const.pass.cpp | 0
.../{call => invoke}/const_noexcept.pass.cpp | 0
.../{call => invoke}/default.pass.cpp | 0
.../{call => invoke}/noexcept.pass.cpp | 0
7 files changed, 111 insertions(+), 29 deletions(-)
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp
rename libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/{call => invoke}/const.pass.cpp (100%)
rename libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/{call => invoke}/const_noexcept.pass.cpp (100%)
rename libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/{call => invoke}/default.pass.cpp (100%)
rename libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/{call => invoke}/noexcept.pass.cpp (100%)
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index f7d5575ebaffd..d34a47286cf58 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -33,14 +33,14 @@ template <class _Rp, class... _ArgTypes>
inline constexpr bool __is_function_ref<function_ref<_Rp, _ArgTypes...>> = true;
template <class _Fp, class _Tp>
-struct __function_ref_bind;
+struct __function_ref_bind {};
-template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...), _Tp> {
using type = _Rp(_ArgTypes...);
};
-template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept, _Tp> {
using type = _Rp(_ArgTypes...) noexcept;
};
@@ -48,7 +48,7 @@ struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept, _Tp> {
template <class _Tp, class _Mp, class _Gp>
requires is_object_v<_Mp>
struct __function_ref_bind<_Mp _Gp::*, _Tp> {
- using type = invoke_result_t<_Mp _Gp::*, _Tp&>;
+ using type = invoke_result_t<_Mp _Gp::*, _Tp&>();
};
template <class _Fp, class _Tp>
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 4e1cd13a02ae2..2e6ed45f32b19 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -46,6 +46,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
static constexpr bool __is_invocable_using = is_invocable_r_v<_Rp, _Tp..., _ArgTypes...>;
# endif
+ // use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
+ // pointers
union __storage_t {
void* __obj_ptr;
void const* __obj_const_ptr;
@@ -82,18 +84,17 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
}
}
- using __vtable_call_t = _Rp (*)(__storage_t, _ArgTypes&&...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
- __vtable_call_t __vtable_call_;
+ using __call_t = _Rp (*)(__storage_t, _ArgTypes&&...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+ __call_t __call_;
public:
template <class _Fp>
requires is_function_v<_Fp> && __is_invocable_using<_Fp>
_LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
: __storage_(__fn_ptr),
- __vtable_call_(
- [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
- return __get<_Fp>(__storage)(std::forward<_ArgTypes>(__args)...);
- }) {
+ __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ return __get<_Fp>(__storage)(std::forward<_ArgTypes>(__args)...);
+ }) {
_LIBCPP_ASSERT_UNCATEGORIZED(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
}
@@ -102,16 +103,15 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
__is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&>)
_LIBCPP_HIDE_FROM_ABI function_ref(_Fp&& __obj) noexcept
: __storage_(std::addressof(__obj)),
- __vtable_call_(
- [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
- _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
- return __obj(std::forward<_ArgTypes>(__args)...);
- }) {}
+ __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
+ return __obj(std::forward<_ArgTypes>(__args)...);
+ }) {}
template <auto _Fn>
requires __is_invocable_using<decltype(_Fn)>
constexpr function_ref(nontype_t<_Fn>) noexcept
- : __vtable_call_([](__storage_t, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ : __call_([](__storage_t, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
return std::invoke_r<_Rp>(_Fn, std::forward<_ArgTypes>(__args)...);
}) {
if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
@@ -123,11 +123,10 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp&>
constexpr function_ref(nontype_t<_Fn>, _Up&& __obj) noexcept
: __storage_(std::addressof(__obj)),
- __vtable_call_(
- [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
- _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
- return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
- }) {
+ __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
+ return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
+ }) {
if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
}
@@ -137,11 +136,10 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
requires __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp*>
constexpr function_ref(nontype_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
: __storage_(__obj_ptr),
- __vtable_call_(
- [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
- auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
- return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
- }) {
+ __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
+ return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
+ }) {
if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
}
@@ -160,17 +158,17 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
function_ref& operator=(_Tp) = delete;
constexpr _Rp operator()(_ArgTypes... __args) const noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) {
- return __vtable_call_(__storage_, std::forward<_ArgTypes>(__args)...);
+ return __call_(__storage_, std::forward<_ArgTypes>(__args)...);
}
};
-template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
_Tp> {
using type = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
};
-template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
_Tp> {
using type = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp
new file mode 100644
index 0000000000000..c7490fc0982e3
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+int fn(int, float) { return 42; }
+
+int fn_noexcept(int, float) noexcept { return 42; }
+
+struct S {
+ int data_mem = 42;
+
+ int fn_mem(int, float) { return 42; }
+ int fn_mem_noexcept(int, float) noexcept { return 42; }
+};
+
+void test() {
+ // template<class F>
+ // function_ref(F*) -> function_ref<F>;
+ {
+ std::function_ref fn_ref = fn;
+ static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+ }
+ {
+ std::function_ref fn_ref = fn_noexcept;
+ static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+ }
+
+ // template<auto f>
+ // function_ref(nontype_t<f>) -> function_ref<...>;
+ {
+ std::function_ref fn_ref = std::nontype_t<fn>();
+ static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+ }
+ {
+ std::function_ref fn_ref = std::nontype_t<fn_noexcept>();
+ static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+ }
+
+ // template<auto f, class T>
+ // function_ref(nontype_t<f>, T&&) -> function_ref<...>;
+ {
+ int arg = 0;
+ std::function_ref fn_ref = {std::nontype_t<fn>(), arg};
+ static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float)>>);
+ }
+ {
+ S s;
+ std::function_ref fn_ref = {std::nontype_t<&S::data_mem>(), s};
+ static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int&()>>);
+ }
+ {
+ const S s;
+ std::function_ref fn_ref = {std::nontype_t<&S::data_mem>(), s};
+ static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int const&()>>);
+ }
+ {
+ S s;
+ std::function_ref fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+ static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+ }
+ {
+ S s;
+ std::function_ref fn_ref = {std::nontype_t<&S::fn_mem_noexcept>(), s};
+ static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+ }
+}
+
+int main(int, char**) {
+ test();
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp
similarity index 100%
rename from libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const.pass.cpp
rename to libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const_noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp
similarity index 100%
rename from libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const_noexcept.pass.cpp
rename to libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/default.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
similarity index 100%
rename from libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/default.pass.cpp
rename to libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp
similarity index 100%
rename from libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/noexcept.pass.cpp
rename to libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp
>From 45f04ea5861ca1acece9a01fdec1e1ed9596b75d Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 01:50:20 -0700
Subject: [PATCH 03/22] [libc++] P0792R14: 'function_ref'
---
libcxx/modules/std/functional.inc | 4 ++++
libcxx/modules/std/utility.inc | 5 +++++
2 files changed, 9 insertions(+)
diff --git a/libcxx/modules/std/functional.inc b/libcxx/modules/std/functional.inc
index 9ef8f584611fc..be804b19f75f8 100644
--- a/libcxx/modules/std/functional.inc
+++ b/libcxx/modules/std/functional.inc
@@ -95,6 +95,10 @@ export namespace std {
using std::function;
+#if _LIBCPP_STD_VER >= 26
+ using std::function_ref;
+#endif // _LIBCPP_STD_VER >= 26
+
using std::swap;
using std::operator==;
diff --git a/libcxx/modules/std/utility.inc b/libcxx/modules/std/utility.inc
index 77c21b87640dd..28f8e36e30af9 100644
--- a/libcxx/modules/std/utility.inc
+++ b/libcxx/modules/std/utility.inc
@@ -89,6 +89,11 @@ export namespace std {
using std::in_place_index;
using std::in_place_index_t;
+#if _LIBCPP_STD_VER >= 26
+ using std::nontype;
+ using std::nontype_t;
+#endif // _LIBCPP_STD_VER >= 23
+
// [depr.relops]
namespace rel_ops {
using rel_ops::operator!=;
>From bd5c50b6900d81882bda5db1e56b56bfb17559ff Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 03:41:35 -0700
Subject: [PATCH 04/22] [libc++] P0792R14: 'function_ref'
---
libcxx/include/CMakeLists.txt | 2 +-
libcxx/include/__utility/nontype.h | 5 +++++
libcxx/include/module.modulemap.in | 2 ++
3 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 3b30fc47e4b2d..90dc684944342 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -431,9 +431,9 @@ set(files
__functional/compose.h
__functional/default_searcher.h
__functional/function.h
+ __functional/function_ref.h
__functional/function_ref_common.h
__functional/function_ref_impl.h
- __functional/function_ref.h
__functional/hash.h
__functional/identity.h
__functional/invoke.h
diff --git a/libcxx/include/__utility/nontype.h b/libcxx/include/__utility/nontype.h
index 86893f17e2d96..6a91d2cace162 100644
--- a/libcxx/include/__utility/nontype.h
+++ b/libcxx/include/__utility/nontype.h
@@ -27,6 +27,11 @@ struct nontype_t {
template <auto _Vp>
inline constexpr nontype_t<_Vp> nontype{};
+template <class>
+inline constexpr bool __is_nontype_t = false;
+template <auto _Vp>
+inline constexpr bool __is_nontype_t<nontype_t<_Vp>> = true;
+
#endif // _LIBCPP_STD_VER >= 26
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 9ef908c66ca7c..b9f0ffeb2b41d 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1426,6 +1426,8 @@ module std [system] {
module default_searcher { header "__functional/default_searcher.h" }
module function { header "__functional/function.h" }
module function_ref { header "__functional/function_ref.h" }
+ module function_ref_common { header "__functional/function_ref_common.h" }
+ module function_ref_impl { header "__functional/function_ref_impl.h" }
module hash { header "__functional/hash.h" }
module identity { header "__functional/identity.h" }
module invoke { header "__functional/invoke.h" }
>From a697bc80b3e3c10a0f464035b0f03759e61af042 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 03:50:12 -0700
Subject: [PATCH 05/22] [libc++] P0792R14: 'function_ref'
---
.../include/__functional/function_ref_impl.h | 18 +++++++++---------
libcxx/include/__utility/nontype.h | 2 +-
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 2e6ed45f32b19..261beb2d04dad 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -53,10 +53,10 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
void const* __obj_const_ptr;
void (*__fn_ptr)();
- constexpr explicit __storage_t() noexcept : __obj_ptr(nullptr) {};
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t() noexcept : __obj_ptr(nullptr){};
template <class _Tp>
- constexpr explicit __storage_t(_Tp* __ptr) noexcept {
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t(_Tp* __ptr) noexcept {
if constexpr (is_object_v<_Tp>) {
if constexpr (is_const_v<_Tp>) {
__obj_const_ptr = __ptr;
@@ -110,7 +110,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
template <auto _Fn>
requires __is_invocable_using<decltype(_Fn)>
- constexpr function_ref(nontype_t<_Fn>) noexcept
+ _LIBCPP_HIDE_FROM_ABI constexpr function_ref(nontype_t<_Fn>) noexcept
: __call_([](__storage_t, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
return std::invoke_r<_Rp>(_Fn, std::forward<_ArgTypes>(__args)...);
}) {
@@ -121,7 +121,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
template <auto _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp&>
- constexpr function_ref(nontype_t<_Fn>, _Up&& __obj) noexcept
+ _LIBCPP_HIDE_FROM_ABI constexpr function_ref(nontype_t<_Fn>, _Up&& __obj) noexcept
: __storage_(std::addressof(__obj)),
__call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
_LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
@@ -134,7 +134,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
template <auto _Fn, class _Tp>
requires __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp*>
- constexpr function_ref(nontype_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
+ _LIBCPP_HIDE_FROM_ABI constexpr function_ref(nontype_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
: __storage_(__obj_ptr),
__call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
@@ -149,15 +149,15 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
}
}
- constexpr function_ref(const function_ref&) noexcept = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr function_ref(const function_ref&) noexcept = default;
- constexpr function_ref& operator=(const function_ref&) noexcept = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr function_ref& operator=(const function_ref&) noexcept = default;
template <class _Tp>
requires(!__is_function_ref<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_nontype_t<_Tp>)
- function_ref& operator=(_Tp) = delete;
+ _LIBCPP_HIDE_FROM_ABI function_ref& operator=(_Tp) = delete;
- constexpr _Rp operator()(_ArgTypes... __args) const noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) {
+ _LIBCPP_HIDE_FROM_ABI constexpr _Rp operator()(_ArgTypes... __args) const noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) {
return __call_(__storage_, std::forward<_ArgTypes>(__args)...);
}
};
diff --git a/libcxx/include/__utility/nontype.h b/libcxx/include/__utility/nontype.h
index 6a91d2cace162..3a10840154009 100644
--- a/libcxx/include/__utility/nontype.h
+++ b/libcxx/include/__utility/nontype.h
@@ -21,7 +21,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <auto _Vp>
struct nontype_t {
- explicit nontype_t() = default;
+ _LIBCPP_HIDE_FROM_ABI explicit nontype_t() = default;
};
template <auto _Vp>
>From 079e19f878f336959bc4ab6e6d138ab2c5717c10 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 03:55:54 -0700
Subject: [PATCH 06/22] [libc++] P0792R14: 'function_ref'
---
libcxx/include/__functional/function_ref_common.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index d34a47286cf58..b2d1864e582c5 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -11,6 +11,7 @@
#include <__config>
#include <__type_traits/invoke.h>
+#include <__type_traits/is_function.h>
#include <__type_traits/is_object.h>
#include <__type_traits/remove_pointer.h>
#include <__utility/nontype.h>
>From 5df75f1651a5111bcf45e5f227a5e9f90e5d3c3f Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 04:01:41 -0700
Subject: [PATCH 07/22] [libc++] P0792R14: 'function_ref'
---
libcxx/include/__functional/function_ref_impl.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 261beb2d04dad..50951228c10a8 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -17,6 +17,7 @@
#include <__type_traits/is_member_pointer.h>
#include <__type_traits/is_object.h>
#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_reference.h>
#include <__type_traits/is_void.h>
#include <__type_traits/remove_cvref.h>
#include <__type_traits/remove_pointer.h>
>From d502218b944badf282f3e0312c75b5c247171a25 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 04:41:50 -0700
Subject: [PATCH 08/22] [libc++] P0792R14: 'function_ref'
---
libcxx/include/__functional/function_ref.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
index 01cc22a64018e..8fcebb8aa4825 100644
--- a/libcxx/include/__functional/function_ref.h
+++ b/libcxx/include/__functional/function_ref.h
@@ -10,6 +10,7 @@
#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
#include <__config>
+#include <__functional/function_ref_common.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
>From 7c1c7e4a9c5b4f723479475235e48aeeb2442c29 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 05:10:54 -0700
Subject: [PATCH 09/22] [libc++] P0792R14: 'function_ref'
---
libcxx/include/module.modulemap.in | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index b9f0ffeb2b41d..37bde6aafe7f2 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1427,7 +1427,7 @@ module std [system] {
module function { header "__functional/function.h" }
module function_ref { header "__functional/function_ref.h" }
module function_ref_common { header "__functional/function_ref_common.h" }
- module function_ref_impl { header "__functional/function_ref_impl.h" }
+ module function_ref_impl { textual header "__functional/function_ref_impl.h" }
module hash { header "__functional/hash.h" }
module identity { header "__functional/identity.h" }
module invoke { header "__functional/invoke.h" }
>From 3591a3a12dcf27e78fcd36c8e9cb84738f44d7eb Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 11:18:14 -0700
Subject: [PATCH 10/22] [libc++] P0792R14: 'function_ref'
---
libcxx/include/__functional/function_ref_impl.h | 1 -
libcxx/include/module.modulemap.in | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 50951228c10a8..d992d6661cf1b 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -8,7 +8,6 @@
#include <__assert>
#include <__config>
-#include <__functional/function_ref_common.h>
#include <__functional/invoke.h>
#include <__memory/addressof.h>
#include <__type_traits/invoke.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 37bde6aafe7f2..183383e893f24 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1426,7 +1426,7 @@ module std [system] {
module default_searcher { header "__functional/default_searcher.h" }
module function { header "__functional/function.h" }
module function_ref { header "__functional/function_ref.h" }
- module function_ref_common { header "__functional/function_ref_common.h" }
+ module function_ref_common { textual header "__functional/function_ref_common.h" }
module function_ref_impl { textual header "__functional/function_ref_impl.h" }
module hash { header "__functional/hash.h" }
module identity { header "__functional/identity.h" }
>From cfdcc5f769f83608c78bb2d84238952913f3f560 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 11:23:21 -0700
Subject: [PATCH 11/22] [libc++] P0792R14: 'function_ref'
---
libcxx/include/functional | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/libcxx/include/functional b/libcxx/include/functional
index dbc023ac637d7..14e29f58cca2e 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -485,6 +485,11 @@ template <class R, class ... ArgTypes>
template <class R, class ... ArgTypes>
void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept;
+// [func.wrap.ref], non-owning wrapper
+template<class... S> class function_ref; // freestanding, not defined, since C++26
+template<class R, class... ArgTypes>
+ class function_ref<R(ArgTypes...) cv noexcept(noex)>; // freestanding, since C++26
+
template <class T> struct hash;
template <> struct hash<bool>;
>From 2701971b68e4b34bdf505e5445b62c0d9ca11216 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 11:58:22 -0700
Subject: [PATCH 12/22] [libc++] P0792R14: 'function_ref'
---
libcxx/include/__functional/function_ref_impl.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index d992d6661cf1b..daae5c87d9470 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -53,7 +53,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
void const* __obj_const_ptr;
void (*__fn_ptr)();
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t() noexcept : __obj_ptr(nullptr){};
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t() noexcept : __obj_ptr(nullptr) {}
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t(_Tp* __ptr) noexcept {
>From 0390be2ca09d79a4ead4c55652e1df6ffc2214c9 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 8 Mar 2026 10:52:40 +0000
Subject: [PATCH 13/22] rebase + put in experimental + rename nontype to
constant_arg
---
libcxx/docs/FeatureTestMacroTable.rst | 2 +-
libcxx/docs/ReleaseNotes/23.rst | 2 +
libcxx/docs/Status/Cxx2cPapers.csv | 2 +-
libcxx/include/CMakeLists.txt | 2 +-
libcxx/include/__configuration/experimental.h | 1 +
libcxx/include/__functional/function_ref.h | 2 +
.../__functional/function_ref_common.h | 18 ++++----
.../include/__functional/function_ref_impl.h | 20 ++++----
.../__utility/{nontype.h => constant_arg.h} | 16 +++----
libcxx/include/module.modulemap.in | 2 +-
libcxx/include/utility | 12 ++---
libcxx/include/version | 4 +-
libcxx/modules/std/utility.inc | 4 +-
.../functional.version.compile.pass.cpp | 4 +-
.../version.version.compile.pass.cpp | 4 +-
.../func.wrap.ref/ctor/ctad.pass.cpp | 18 ++++----
.../func.wrap.ref/invoke/const.pass.cpp | 46 +++++++++----------
.../invoke/const_noexcept.pass.cpp | 46 +++++++++----------
.../func.wrap.ref/invoke/default.pass.cpp | 42 ++++++++---------
.../func.wrap.ref/invoke/noexcept.pass.cpp | 42 ++++++++---------
.../generate_feature_test_macro_components.py | 3 +-
21 files changed, 149 insertions(+), 143 deletions(-)
rename libcxx/include/__utility/{nontype.h => constant_arg.h} (63%)
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 0f65770a4fa14..e3f9ccb3e7515 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -468,7 +468,7 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_fstream_native_handle`` ``202306L``
---------------------------------------------------------- -----------------
- ``__cpp_lib_function_ref`` *unimplemented*
+ ``__cpp_lib_function_ref`` ``202306L``
---------------------------------------------------------- -----------------
``__cpp_lib_generate_random`` *unimplemented*
---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index bcf527869ac94..348764b084ad5 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -39,6 +39,8 @@ Implemented Papers
------------------
- P2440R1: ``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right`` (`Github <https://llvm.org/PR105184>`__)
+- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__) This feature is currently experimental and
+ therefore requires ``-fexperimental-library``.
Improvements and New Features
-----------------------------
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 29642fc53cac6..6b135060c9d03 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -14,7 +14,7 @@
"`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
"`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
"`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","","","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
-"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","","","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
+"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","23","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
"`P2874R2 <https://wg21.link/P2874R2>`__","P2874R2: Mandating Annex D Require No More","2023-06 (Varna)","|Complete|","12","`#105377 <https://github.com/llvm/llvm-project/issues/105377>`__",""
"`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","2023-06 (Varna)","","","`#105378 <https://github.com/llvm/llvm-project/issues/105378>`__",""
"`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","2023-06 (Varna)","|Complete|","19","`#105380 <https://github.com/llvm/llvm-project/issues/105380>`__","Change of ``__cpp_lib_variant`` is completed in LLVM 20. Change of ``__cpp_lib_format`` is blocked by `P2419R2 <https://wg21.link/P2419R2>`__."
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 90dc684944342..a2c69f1c77c2a 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -928,6 +928,7 @@ set(files
__utility/as_lvalue.h
__utility/auto_cast.h
__utility/cmp.h
+ __utility/constant_arg.h
__utility/convert_to_integral.h
__utility/declval.h
__utility/default_three_way_comparator.h
@@ -943,7 +944,6 @@ set(files
__utility/lazy_synth_three_way_comparator.h
__utility/move.h
__utility/no_destroy.h
- __utility/nontype.h
__utility/pair.h
__utility/piecewise_construct.h
__utility/priority_tag.h
diff --git a/libcxx/include/__configuration/experimental.h b/libcxx/include/__configuration/experimental.h
index bb38d8297c63d..1e12c50802895 100644
--- a/libcxx/include/__configuration/experimental.h
+++ b/libcxx/include/__configuration/experimental.h
@@ -34,5 +34,6 @@
#define _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
#define _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
#define _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
+#define _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
#endif // _LIBCPP___CONFIGURATION_EXPERIMENTAL_H
diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
index 8fcebb8aa4825..fbfb0d8a8b3a8 100644
--- a/libcxx/include/__functional/function_ref.h
+++ b/libcxx/include/__functional/function_ref.h
@@ -16,6 +16,7 @@
# pragma GCC system_header
#endif
+#if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
// NOLINTBEGIN(readability-duplicate-include)
#define _LIBCPP_FUNCTION_REF_CV
@@ -43,5 +44,6 @@
#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
// NOLINTEND(readability-duplicate-include)
+#endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index b2d1864e582c5..f1d1ee2279d9c 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -14,7 +14,7 @@
#include <__type_traits/is_function.h>
#include <__type_traits/is_object.h>
#include <__type_traits/remove_pointer.h>
-#include <__utility/nontype.h>
+#include <__utility/constant_arg.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -22,7 +22,7 @@
_LIBCPP_BEGIN_NAMESPACE_STD
-#if _LIBCPP_STD_VER >= 26
+#if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
template <class...>
class function_ref;
@@ -38,22 +38,22 @@ struct __function_ref_bind {};
template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...), _Tp> {
- using type = _Rp(_ArgTypes...);
+ using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...);
};
template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept, _Tp> {
- using type = _Rp(_ArgTypes...) noexcept;
+ using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept;
};
template <class _Tp, class _Mp, class _Gp>
requires is_object_v<_Mp>
struct __function_ref_bind<_Mp _Gp::*, _Tp> {
- using type = invoke_result_t<_Mp _Gp::*, _Tp&>();
+ using type _LIBCPP_NODEBUG = invoke_result_t<_Mp _Gp::*, _Tp&>();
};
template <class _Fp, class _Tp>
-using __function_ref_bind_t = __function_ref_bind<_Fp, _Tp>::type;
+using __function_ref_bind_t _LIBCPP_NODEBUG = __function_ref_bind<_Fp, _Tp>::type;
template <class _Fp>
requires is_function_v<_Fp>
@@ -61,12 +61,12 @@ function_ref(_Fp*) -> function_ref<_Fp>;
template <auto _Fn>
requires is_function_v<remove_pointer_t<decltype(_Fn)>>
-function_ref(nontype_t<_Fn>) -> function_ref<remove_pointer_t<decltype(_Fn)>>;
+function_ref(constant_arg_t<_Fn>) -> function_ref<remove_pointer_t<decltype(_Fn)>>;
template <auto _Fn, class _Tp>
-function_ref(nontype_t<_Fn>, _Tp&&) -> function_ref<__function_ref_bind_t<decltype(_Fn), _Tp&>>;
+function_ref(constant_arg_t<_Fn>, _Tp&&) -> function_ref<__function_ref_bind_t<decltype(_Fn), _Tp&>>;
-#endif // _LIBCPP_STD_VER >= 26
+#endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index daae5c87d9470..4627fa6325305 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -21,8 +21,8 @@
#include <__type_traits/remove_cvref.h>
#include <__type_traits/remove_pointer.h>
#include <__type_traits/remove_reference.h>
+#include <__utility/constant_arg.h>
#include <__utility/forward.h>
-#include <__utility/nontype.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -30,7 +30,7 @@
_LIBCPP_BEGIN_NAMESPACE_STD
-#if _LIBCPP_STD_VER >= 26
+#if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
template <class...>
class function_ref;
@@ -84,7 +84,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
}
}
- using __call_t = _Rp (*)(__storage_t, _ArgTypes&&...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+ using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, _ArgTypes&&...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
__call_t __call_;
public:
@@ -110,7 +110,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
template <auto _Fn>
requires __is_invocable_using<decltype(_Fn)>
- _LIBCPP_HIDE_FROM_ABI constexpr function_ref(nontype_t<_Fn>) noexcept
+ _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>) noexcept
: __call_([](__storage_t, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
return std::invoke_r<_Rp>(_Fn, std::forward<_ArgTypes>(__args)...);
}) {
@@ -121,7 +121,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
template <auto _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp&>
- _LIBCPP_HIDE_FROM_ABI constexpr function_ref(nontype_t<_Fn>, _Up&& __obj) noexcept
+ _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _Up&& __obj) noexcept
: __storage_(std::addressof(__obj)),
__call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
_LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
@@ -134,7 +134,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
template <auto _Fn, class _Tp>
requires __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp*>
- _LIBCPP_HIDE_FROM_ABI constexpr function_ref(nontype_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
+ _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
: __storage_(__obj_ptr),
__call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
@@ -154,7 +154,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
_LIBCPP_HIDE_FROM_ABI constexpr function_ref& operator=(const function_ref&) noexcept = default;
template <class _Tp>
- requires(!__is_function_ref<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_nontype_t<_Tp>)
+ requires(!__is_function_ref<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_constant_arg_t<_Tp>)
_LIBCPP_HIDE_FROM_ABI function_ref& operator=(_Tp) = delete;
_LIBCPP_HIDE_FROM_ABI constexpr _Rp operator()(_ArgTypes... __args) const noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) {
@@ -165,15 +165,15 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
_Tp> {
- using type = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+ using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
};
template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
_Tp> {
- using type = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+ using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
};
-#endif // _LIBCPP_STD_VER >= 26
+#endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__utility/nontype.h b/libcxx/include/__utility/constant_arg.h
similarity index 63%
rename from libcxx/include/__utility/nontype.h
rename to libcxx/include/__utility/constant_arg.h
index 3a10840154009..f299ec509ed5c 100644
--- a/libcxx/include/__utility/nontype.h
+++ b/libcxx/include/__utility/constant_arg.h
@@ -6,8 +6,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef _LIBCPP___UTILITY_NONTYPE_H
-#define _LIBCPP___UTILITY_NONTYPE_H
+#ifndef _LIBCPP___UTILITY_CONSTANT_ARG_H
+#define _LIBCPP___UTILITY_CONSTANT_ARG_H
#include <__config>
@@ -20,20 +20,20 @@ _LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 26
template <auto _Vp>
-struct nontype_t {
- _LIBCPP_HIDE_FROM_ABI explicit nontype_t() = default;
+struct constant_arg_t {
+ _LIBCPP_HIDE_FROM_ABI explicit constant_arg_t() = default;
};
template <auto _Vp>
-inline constexpr nontype_t<_Vp> nontype{};
+inline constexpr constant_arg_t<_Vp> constant_arg{};
template <class>
-inline constexpr bool __is_nontype_t = false;
+inline constexpr bool __is_constant_arg_t = false;
template <auto _Vp>
-inline constexpr bool __is_nontype_t<nontype_t<_Vp>> = true;
+inline constexpr bool __is_constant_arg_t<constant_arg_t<_Vp>> = true;
#endif // _LIBCPP_STD_VER >= 26
_LIBCPP_END_NAMESPACE_STD
-#endif // _LIBCPP___UTILITY_NONTYPE_H
+#endif // _LIBCPP___UTILITY_CONSTANT_ARG_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 183383e893f24..17ae13e91da81 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2174,6 +2174,7 @@ module std [system] {
export std_core.type_traits.decay // the macro expansion uses that trait
}
module cmp { header "__utility/cmp.h" }
+ module constant_arg { header "__utility/constant_arg.h" }
module convert_to_integral { header "__utility/convert_to_integral.h" }
module default_three_way_comparator { header "__utility/default_three_way_comparator.h" }
module element_count { header "__utility/element_count.h" }
@@ -2189,7 +2190,6 @@ module std [system] {
module lazy_synth_three_way_comparator { header "__utility/lazy_synth_three_way_comparator.h" }
module move { header "__utility/move.h" }
module no_destroy { header "__utility/no_destroy.h" }
- module nontype { header "__utility/nontype.h" }
module pair {
header "__utility/pair.h"
export std.utility.piecewise_construct
diff --git a/libcxx/include/utility b/libcxx/include/utility
index f6cffd3898c21..fbed5ec799972 100644
--- a/libcxx/include/utility
+++ b/libcxx/include/utility
@@ -250,14 +250,12 @@ template <size_t I>
template <size_t I>
inline constexpr in_place_index_t<I> in_place_index{};
-// nontype argument tag
+// constant_arg argument tag
template<auto V>
- struct nontype_t {
- explicit nontype_t() = default;
+ struct constant_arg_t {
+ explicit constant_arg_t() = default;
};
-template<auto V> constexpr nontype_t<V> nontype{};
-
-template<auto V> constexpr nontype_t<V> nontype{};
+template<auto V> constexpr constant_arg_t<V> constant_arg_t{};
// [utility.underlying], to_underlying
template <class T>
@@ -301,7 +299,7 @@ template <class T>
# endif
# if _LIBCPP_STD_VER >= 26
-# include <__utility/nontype.h>
+# include <__utility/constant_arg.h>
# include <__variant/monostate.h>
# endif
diff --git a/libcxx/include/version b/libcxx/include/version
index 562538c5393cc..e0c358edd8940 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -581,7 +581,9 @@ __cpp_lib_void_t 201411L <type_traits>
# if _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
# define __cpp_lib_fstream_native_handle 202306L
# endif
-// # define __cpp_lib_function_ref 202306L
+# if _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
+# define __cpp_lib_function_ref 202306L
+# endif
// # define __cpp_lib_generate_random 202403L
// # define __cpp_lib_hazard_pointer 202306L
// # define __cpp_lib_inplace_vector 202406L
diff --git a/libcxx/modules/std/utility.inc b/libcxx/modules/std/utility.inc
index 28f8e36e30af9..aacab46e6df02 100644
--- a/libcxx/modules/std/utility.inc
+++ b/libcxx/modules/std/utility.inc
@@ -90,8 +90,8 @@ export namespace std {
using std::in_place_index_t;
#if _LIBCPP_STD_VER >= 26
- using std::nontype;
- using std::nontype_t;
+ using std::constant_arg;
+ using std::constant_arg_t;
#endif // _LIBCPP_STD_VER >= 23
// [depr.relops]
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
index b7b7d0334830a..eaab9bdd80ad8 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
@@ -494,7 +494,7 @@
# endif
# endif
-# if !defined(_LIBCPP_VERSION)
+# if !defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
# ifndef __cpp_lib_function_ref
# error "__cpp_lib_function_ref should be defined in c++26"
# endif
@@ -503,7 +503,7 @@
# endif
# else
# ifdef __cpp_lib_function_ref
-# error "__cpp_lib_function_ref should not be defined because it is unimplemented in libc++!"
+# error "__cpp_lib_function_ref should not be defined when the requirement '!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF' is not met!"
# endif
# endif
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index e74c2b58717a9..e3866130f9981 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -7008,7 +7008,7 @@
# endif
# endif
-# if !defined(_LIBCPP_VERSION)
+# if !defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
# ifndef __cpp_lib_function_ref
# error "__cpp_lib_function_ref should be defined in c++26"
# endif
@@ -7017,7 +7017,7 @@
# endif
# else
# ifdef __cpp_lib_function_ref
-# error "__cpp_lib_function_ref should not be defined because it is unimplemented in libc++!"
+# error "__cpp_lib_function_ref should not be defined when the requirement '!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF' is not met!"
# endif
# endif
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp
index c7490fc0982e3..9ef7bd66a8953 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp
@@ -39,41 +39,41 @@ void test() {
}
// template<auto f>
- // function_ref(nontype_t<f>) -> function_ref<...>;
+ // function_ref(constant_arg_t<f>) -> function_ref<...>;
{
- std::function_ref fn_ref = std::nontype_t<fn>();
+ std::function_ref fn_ref = std::constant_arg_t<fn>();
static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
}
{
- std::function_ref fn_ref = std::nontype_t<fn_noexcept>();
+ std::function_ref fn_ref = std::constant_arg_t<fn_noexcept>();
static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
}
// template<auto f, class T>
- // function_ref(nontype_t<f>, T&&) -> function_ref<...>;
+ // function_ref(constant_arg_t<f>, T&&) -> function_ref<...>;
{
int arg = 0;
- std::function_ref fn_ref = {std::nontype_t<fn>(), arg};
+ std::function_ref fn_ref = {std::constant_arg_t<fn>(), arg};
static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float)>>);
}
{
S s;
- std::function_ref fn_ref = {std::nontype_t<&S::data_mem>(), s};
+ std::function_ref fn_ref = {std::constant_arg_t<&S::data_mem>(), s};
static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int&()>>);
}
{
const S s;
- std::function_ref fn_ref = {std::nontype_t<&S::data_mem>(), s};
+ std::function_ref fn_ref = {std::constant_arg_t<&S::data_mem>(), s};
static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int const&()>>);
}
{
S s;
- std::function_ref fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+ std::function_ref fn_ref = {std::constant_arg_t<&S::fn_mem>(), s};
static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
}
{
S s;
- std::function_ref fn_ref = {std::nontype_t<&S::fn_mem_noexcept>(), s};
+ std::function_ref fn_ref = {std::constant_arg_t<&S::fn_mem_noexcept>(), s};
static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
}
}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp
index 53d7a5922353d..26a32e91adfd7 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp
@@ -55,70 +55,70 @@ struct S {
int fn_mem() const { return 42; }
};
-void test_nontype_t() {
- // template<auto f> constexpr function_ref(nontype_t<f>) noexcept;
+void test_constant_arg_t() {
+ // template<auto f> constexpr function_ref(constant_arg_t<f>) noexcept;
{
- // initialized from a function through `nontype_t`
- std::function_ref<int() const> fn_ref = std::nontype_t<fn>();
+ // initialized from a function through `constant_arg_t`
+ std::function_ref<int() const> fn_ref = std::constant_arg_t<fn>();
assert(fn_ref() == 42);
}
{
- // initialized from a function pointer through `nontype_t`
- std::function_ref<int() const> fn_ref = std::nontype_t<&fn>();
+ // initialized from a function pointer through `constant_arg_t`
+ std::function_ref<int() const> fn_ref = std::constant_arg_t<&fn>();
assert(fn_ref() == 42);
}
{
- // initialized from a function object through `nontype_t`
- std::function_ref<int() const> fn_ref = std::nontype_t<fn_obj>();
+ // initialized from a function object through `constant_arg_t`
+ std::function_ref<int() const> fn_ref = std::constant_arg_t<fn_obj>();
assert(fn_ref() == 42);
}
{
S s;
- // initialized from a pointer to data member through `nontype_t`
- std::function_ref<int(S) const> fn_ref = std::nontype_t<&S::data_mem>();
+ // initialized from a pointer to data member through `constant_arg_t`
+ std::function_ref<int(S) const> fn_ref = std::constant_arg_t<&S::data_mem>();
assert(fn_ref(s) == 42);
}
{
S s;
- // initialized from a pointer to function member through `nontype_t`
- std::function_ref<int(S) const> fn_ref = std::nontype_t<&S::fn_mem>();
+ // initialized from a pointer to function member through `constant_arg_t`
+ std::function_ref<int(S) const> fn_ref = std::constant_arg_t<&S::fn_mem>();
assert(fn_ref(s) == 42);
}
// template<auto f, class U>
- // constexpr function_ref(nontype_t<f>, U&& obj) noexcept;
+ // constexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
{
S s;
- // initialized from a pointer to data member through `nontype_t` and bound to an object through a reference
- std::function_ref<int() const> fn_ref = {std::nontype_t<&S::data_mem>(), s};
+ // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a reference
+ std::function_ref<int() const> fn_ref = {std::constant_arg_t<&S::data_mem>(), s};
assert(fn_ref() == 42);
}
{
S s;
- // initialized from a pointer to function member through `nontype_t` and bound to an object through a reference
- std::function_ref<int() const> fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+ // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a reference
+ std::function_ref<int() const> fn_ref = {std::constant_arg_t<&S::fn_mem>(), s};
assert(fn_ref() == 42);
}
// template<auto f, class T>
- // constexpr function_ref(nontype_t<f>, cv T* obj) noexcept;
+ // constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
{
S s;
- // initialized from a pointer to data member through `nontype_t` and bound to an object through a pointer
- std::function_ref<int() const> fn_ref = {std::nontype_t<&S::data_mem>(), &s};
+ // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a pointer
+ std::function_ref<int() const> fn_ref = {std::constant_arg_t<&S::data_mem>(), &s};
assert(fn_ref() == 42);
}
{
S s;
- // initialized from a pointer to function member through `nontype_t` and bound to an object through a pointer
+ // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a pointer
static_assert(std::is_same_v<decltype(&s), S*>);
- std::function_ref<int() const> fn_ref = {std::nontype_t<&S::fn_mem>(), &s};
+ std::function_ref<int() const> fn_ref = {std::constant_arg_t<&S::fn_mem>(), &s};
assert(fn_ref() == 42);
}
}
int main(int, char**) {
test();
- test_nontype_t();
+ test_constant_arg_t();
return 0;
}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp
index 18d44e4b7ef0b..26ace9c496b88 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp
@@ -55,70 +55,70 @@ struct S {
int fn_mem() const noexcept { return 42; }
};
-void test_nontype_t() {
- // template<auto f> const noexceptexpr function_ref(nontype_t<f>) noexcept;
+void test_constant_arg_t() {
+ // template<auto f> const noexceptexpr function_ref(constant_arg_t<f>) noexcept;
{
- // initialized from a function through `nontype_t`
- std::function_ref<int() const noexcept> fn_ref = std::nontype_t<fn>();
+ // initialized from a function through `constant_arg_t`
+ std::function_ref<int() const noexcept> fn_ref = std::constant_arg_t<fn>();
assert(fn_ref() == 42);
}
{
- // initialized from a function pointer through `nontype_t`
- std::function_ref<int() const noexcept> fn_ref = std::nontype_t<&fn>();
+ // initialized from a function pointer through `constant_arg_t`
+ std::function_ref<int() const noexcept> fn_ref = std::constant_arg_t<&fn>();
assert(fn_ref() == 42);
}
{
- // initialized from a function object through `nontype_t`
- std::function_ref<int() const noexcept> fn_ref = std::nontype_t<fn_obj>();
+ // initialized from a function object through `constant_arg_t`
+ std::function_ref<int() const noexcept> fn_ref = std::constant_arg_t<fn_obj>();
assert(fn_ref() == 42);
}
{
S s;
- // initialized from a pointer to data member through `nontype_t`
- std::function_ref<int(S) const noexcept> fn_ref = std::nontype_t<&S::data_mem>();
+ // initialized from a pointer to data member through `constant_arg_t`
+ std::function_ref<int(S) const noexcept> fn_ref = std::constant_arg_t<&S::data_mem>();
assert(fn_ref(s) == 42);
}
{
S s;
- // initialized from a pointer to function member through `nontype_t`
- std::function_ref<int(S) const noexcept> fn_ref = std::nontype_t<&S::fn_mem>();
+ // initialized from a pointer to function member through `constant_arg_t`
+ std::function_ref<int(S) const noexcept> fn_ref = std::constant_arg_t<&S::fn_mem>();
assert(fn_ref(s) == 42);
}
// template<auto f, class U>
- // const noexceptexpr function_ref(nontype_t<f>, U&& obj) noexcept;
+ // const noexceptexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
{
S s;
- // initialized from a pointer to data member through `nontype_t` and bound to an object through a reference
- std::function_ref<int() const noexcept> fn_ref = {std::nontype_t<&S::data_mem>(), s};
+ // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a reference
+ std::function_ref<int() const noexcept> fn_ref = {std::constant_arg_t<&S::data_mem>(), s};
assert(fn_ref() == 42);
}
{
S s;
- // initialized from a pointer to function member through `nontype_t` and bound to an object through a reference
- std::function_ref<int() const noexcept> fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+ // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a reference
+ std::function_ref<int() const noexcept> fn_ref = {std::constant_arg_t<&S::fn_mem>(), s};
assert(fn_ref() == 42);
}
// template<auto f, class T>
- // const noexceptexpr function_ref(nontype_t<f>, cv T* obj) noexcept;
+ // const noexceptexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
{
S s;
- // initialized from a pointer to data member through `nontype_t` and bound to an object through a pointer
- std::function_ref<int() const noexcept> fn_ref = {std::nontype_t<&S::data_mem>(), &s};
+ // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a pointer
+ std::function_ref<int() const noexcept> fn_ref = {std::constant_arg_t<&S::data_mem>(), &s};
assert(fn_ref() == 42);
}
{
S s;
- // initialized from a pointer to function member through `nontype_t` and bound to an object through a pointer
+ // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a pointer
static_assert(std::is_same_v<decltype(&s), S*>);
- std::function_ref<int() const noexcept> fn_ref = {std::nontype_t<&S::fn_mem>(), &s};
+ std::function_ref<int() const noexcept> fn_ref = {std::constant_arg_t<&S::fn_mem>(), &s};
assert(fn_ref() == 42);
}
}
int main(int, char**) {
test();
- test_nontype_t();
+ test_constant_arg_t();
return 0;
}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
index ebc07a6c3520c..ce9d7f8d73716 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
@@ -55,65 +55,65 @@ struct S {
int fn_mem() { return 42; }
};
-void test_nontype_t() {
- // template<auto f> constexpr function_ref(nontype_t<f>) noexcept;
+void test_constant_arg_t() {
+ // template<auto f> constexpr function_ref(constant_arg_t<f>) noexcept;
{
- // initialized from a function through `nontype_t`
- std::function_ref<int()> fn_ref = std::nontype_t<fn>();
+ // initialized from a function through `constant_arg_t`
+ std::function_ref<int()> fn_ref = std::constant_arg_t<fn>();
assert(fn_ref() == 42);
}
{
- // initialized from a function pointer through `nontype_t`
- std::function_ref<int()> fn_ref = std::nontype_t<&fn>();
+ // initialized from a function pointer through `constant_arg_t`
+ std::function_ref<int()> fn_ref = std::constant_arg_t<&fn>();
assert(fn_ref() == 42);
}
{
S s;
- // initialized from a pointer to data member through `nontype_t`
- std::function_ref<int(S)> fn_ref = std::nontype_t<&S::data_mem>();
+ // initialized from a pointer to data member through `constant_arg_t`
+ std::function_ref<int(S)> fn_ref = std::constant_arg_t<&S::data_mem>();
assert(fn_ref(s) == 42);
}
{
S s;
- // initialized from a pointer to function member through `nontype_t`
- std::function_ref<int(S)> fn_ref = std::nontype_t<&S::fn_mem>();
+ // initialized from a pointer to function member through `constant_arg_t`
+ std::function_ref<int(S)> fn_ref = std::constant_arg_t<&S::fn_mem>();
assert(fn_ref(s) == 42);
}
// template<auto f, class U>
- // constexpr function_ref(nontype_t<f>, U&& obj) noexcept;
+ // constexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
{
S s;
- // initialized from a pointer to data member through `nontype_t` and bound to an object through a reference
- std::function_ref<int()> fn_ref = {std::nontype_t<&S::data_mem>(), s};
+ // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a reference
+ std::function_ref<int()> fn_ref = {std::constant_arg_t<&S::data_mem>(), s};
assert(fn_ref() == 42);
}
{
S s;
- // initialized from a pointer to function member through `nontype_t` and bound to an object through a reference
- std::function_ref<int()> fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+ // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a reference
+ std::function_ref<int()> fn_ref = {std::constant_arg_t<&S::fn_mem>(), s};
assert(fn_ref() == 42);
}
// template<auto f, class T>
- // constexpr function_ref(nontype_t<f>, cv T* obj) noexcept;
+ // constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
{
S s;
- // initialized from a pointer to data member through `nontype_t` and bound to an object through a pointer
- std::function_ref<int()> fn_ref = {std::nontype_t<&S::data_mem>(), &s};
+ // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a pointer
+ std::function_ref<int()> fn_ref = {std::constant_arg_t<&S::data_mem>(), &s};
assert(fn_ref() == 42);
}
{
S s;
- // initialized from a pointer to function member through `nontype_t` and bound to an object through a pointer
+ // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a pointer
static_assert(std::is_same_v<decltype(&s), S*>);
- std::function_ref<int()> fn_ref = {std::nontype_t<&S::fn_mem>(), &s};
+ std::function_ref<int()> fn_ref = {std::constant_arg_t<&S::fn_mem>(), &s};
assert(fn_ref() == 42);
}
}
int main(int, char**) {
test();
- test_nontype_t();
+ test_constant_arg_t();
return 0;
}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp
index 9b58e677ed6ba..7c947143ec0bd 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp
@@ -55,65 +55,65 @@ struct S {
int fn_mem() noexcept { return 42; }
};
-void test_nontype_t() {
- // template<auto f> constexpr function_ref(nontype_t<f>) noexcept;
+void test_constant_arg_t() {
+ // template<auto f> constexpr function_ref(constant_arg_t<f>) noexcept;
{
- // initialized from a function through `nontype_t`
- std::function_ref<int() noexcept> fn_ref = std::nontype_t<fn>();
+ // initialized from a function through `constant_arg_t`
+ std::function_ref<int() noexcept> fn_ref = std::constant_arg_t<fn>();
assert(fn_ref() == 42);
}
{
- // initialized from a function pointer through `nontype_t`
- std::function_ref<int() noexcept> fn_ref = std::nontype_t<&fn>();
+ // initialized from a function pointer through `constant_arg_t`
+ std::function_ref<int() noexcept> fn_ref = std::constant_arg_t<&fn>();
assert(fn_ref() == 42);
}
{
S s;
- // initialized from a pointer to data member through `nontype_t`
- std::function_ref<int(S) noexcept> fn_ref = std::nontype_t<&S::data_mem>();
+ // initialized from a pointer to data member through `constant_arg_t`
+ std::function_ref<int(S) noexcept> fn_ref = std::constant_arg_t<&S::data_mem>();
assert(fn_ref(s) == 42);
}
{
S s;
- // initialized from a pointer to function member through `nontype_t`
- std::function_ref<int(S) noexcept> fn_ref = std::nontype_t<&S::fn_mem>();
+ // initialized from a pointer to function member through `constant_arg_t`
+ std::function_ref<int(S) noexcept> fn_ref = std::constant_arg_t<&S::fn_mem>();
assert(fn_ref(s) == 42);
}
// template<auto f, class U>
- // constexpr function_ref(nontype_t<f>, U&& obj) noexcept;
+ // constexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
{
S s;
- // initialized from a pointer to data member through `nontype_t` and bound to an object through a reference
- std::function_ref<int() noexcept> fn_ref = {std::nontype_t<&S::data_mem>(), s};
+ // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a reference
+ std::function_ref<int() noexcept> fn_ref = {std::constant_arg_t<&S::data_mem>(), s};
assert(fn_ref() == 42);
}
{
S s;
- // initialized from a pointer to function member through `nontype_t` and bound to an object through a reference
- std::function_ref<int() noexcept> fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+ // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a reference
+ std::function_ref<int() noexcept> fn_ref = {std::constant_arg_t<&S::fn_mem>(), s};
assert(fn_ref() == 42);
}
// template<auto f, class T>
- // constexpr function_ref(nontype_t<f>, cv T* obj) noexcept;
+ // constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
{
S s;
- // initialized from a pointer to data member through `nontype_t` and bound to an object through a pointer
- std::function_ref<int() noexcept> fn_ref = {std::nontype_t<&S::data_mem>(), &s};
+ // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a pointer
+ std::function_ref<int() noexcept> fn_ref = {std::constant_arg_t<&S::data_mem>(), &s};
assert(fn_ref() == 42);
}
{
S s;
- // initialized from a pointer to function member through `nontype_t` and bound to an object through a pointer
+ // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a pointer
static_assert(std::is_same_v<decltype(&s), S*>);
- std::function_ref<int() noexcept> fn_ref = {std::nontype_t<&S::fn_mem>(), &s};
+ std::function_ref<int() noexcept> fn_ref = {std::constant_arg_t<&S::fn_mem>(), &s};
assert(fn_ref() == 42);
}
}
int main(int, char**) {
test();
- test_nontype_t();
+ test_constant_arg_t();
return 0;
}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 9d2e095ab9ff7..f1e7a92d0d44c 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -693,7 +693,8 @@ def add_version_header(tc):
"c++26": 202306 # P0792R14 function_ref: a type-erased callable reference
},
"headers": ["functional"],
- "unimplemented": True,
+ "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF",
+ "libcxx_guard": "_LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF"
},
{
"name": "__cpp_lib_gcd_lcm",
>From 65a491512d64e5baa5bdcf4eab695f574701e873 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 13 Mar 2026 17:03:58 +0000
Subject: [PATCH 14/22] tests
---
libcxx/include/__functional/function_ref.h | 15 --
.../__functional/function_ref_common.h | 6 -
.../include/__functional/function_ref_impl.h | 62 +++---
libcxx/include/module.modulemap.in | 2 +-
.../assert.constant_arg_ptr.pass.cpp | 31 +++
.../assert.function_ptr.pass.cpp | 26 +++
.../constant_arg.mandates.verify.cpp | 48 +++++
.../func.wrap.ref/{ctor => }/ctad.pass.cpp | 0
.../func.wrap.ref.ctor/constant_arg.pass.cpp | 106 +++++++++
.../func.wrap.ref.ctor/function_ptr.pass.cpp | 119 ++++++++++
.../func.wrap.ref.ctor/ref.pass.cpp | 203 ++++++++++++++++++
.../func.wrap.ref/invoke/default.pass.cpp | 2 +-
.../trivially_copyable.compile.pass.cpp | 33 +++
13 files changed, 601 insertions(+), 52 deletions(-)
create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp
create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
rename libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/{ctor => }/ctad.pass.cpp (100%)
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp
diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
index fbfb0d8a8b3a8..e46706a1a8aaf 100644
--- a/libcxx/include/__functional/function_ref.h
+++ b/libcxx/include/__functional/function_ref.h
@@ -20,28 +20,13 @@
// NOLINTBEGIN(readability-duplicate-include)
#define _LIBCPP_FUNCTION_REF_CV
-#define _LIBCPP_FUNCTION_REF_NOEXCEPT false
#include <__functional/function_ref_impl.h>
#undef _LIBCPP_FUNCTION_REF_CV
-#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
-#define _LIBCPP_FUNCTION_REF_CV
-#define _LIBCPP_FUNCTION_REF_NOEXCEPT true
-#include <__functional/function_ref_impl.h>
-#undef _LIBCPP_FUNCTION_REF_CV
-#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
-
-#define _LIBCPP_FUNCTION_REF_CV const
-#define _LIBCPP_FUNCTION_REF_NOEXCEPT false
-#include <__functional/function_ref_impl.h>
-#undef _LIBCPP_FUNCTION_REF_CV
-#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
#define _LIBCPP_FUNCTION_REF_CV const
-#define _LIBCPP_FUNCTION_REF_NOEXCEPT true
#include <__functional/function_ref_impl.h>
#undef _LIBCPP_FUNCTION_REF_CV
-#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
// NOLINTEND(readability-duplicate-include)
#endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index f1d1ee2279d9c..f921787f951f5 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -27,12 +27,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <class...>
class function_ref;
-template <class>
-inline constexpr bool __is_function_ref = false;
-
-template <class _Rp, class... _ArgTypes>
-inline constexpr bool __is_function_ref<function_ref<_Rp, _ArgTypes...>> = true;
-
template <class _Fp, class _Tp>
struct __function_ref_bind {};
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 4627fa6325305..65c9088b9c892 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -6,10 +6,14 @@
//
//===----------------------------------------------------------------------===//
+// This header is unguarded on purpose. This header is an implementation detail of function_ref.h
+// and generates multiple versions of std::function_ref
+
#include <__assert>
#include <__config>
#include <__functional/invoke.h>
#include <__memory/addressof.h>
+#include <__type_traits/conditional.h>
#include <__type_traits/invoke.h>
#include <__type_traits/is_const.h>
#include <__type_traits/is_function.h>
@@ -17,6 +21,7 @@
#include <__type_traits/is_object.h>
#include <__type_traits/is_pointer.h>
#include <__type_traits/is_reference.h>
+#include <__type_traits/is_same.h>
#include <__type_traits/is_void.h>
#include <__type_traits/remove_cvref.h>
#include <__type_traits/remove_pointer.h>
@@ -28,6 +33,10 @@
# pragma GCC system_header
#endif
+#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+# error This header should only be included from function_ref.h
+#endif
+
_LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
@@ -35,16 +44,13 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <class...>
class function_ref;
-template <class _Rp, class... _ArgTypes>
-class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT)> {
+template <class _Rp, class... _ArgTypes, bool __is_noexcept>
+class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
private:
-# if _LIBCPP_FUNCTION_REF_NOEXCEPT == true
- template <class... _Tp>
- static constexpr bool __is_invocable_using = is_nothrow_invocable_r_v<_Rp, _Tp..., _ArgTypes...>;
-# else
template <class... _Tp>
- static constexpr bool __is_invocable_using = is_invocable_r_v<_Rp, _Tp..., _ArgTypes...>;
-# endif
+ static constexpr bool __is_invocable_using =
+ _If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
+ value;
// use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
// pointers
@@ -84,7 +90,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
}
}
- using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, _ArgTypes&&...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+ using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, _ArgTypes&&...) noexcept(__is_noexcept);
__call_t __call_;
public:
@@ -92,26 +98,26 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
requires is_function_v<_Fp> && __is_invocable_using<_Fp>
_LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
: __storage_(__fn_ptr),
- __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
return __get<_Fp>(__storage)(std::forward<_ArgTypes>(__args)...);
}) {
- _LIBCPP_ASSERT_UNCATEGORIZED(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
+ _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
}
- template <class _Fp, class _Tp = remove_reference_t<_Fp>>
- requires(!__is_function_ref<remove_cvref_t<_Fp>> && !is_member_pointer_v<_Tp> &&
+ template <class _Fn, class _Tp = remove_reference_t<_Fn>>
+ requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
__is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&>)
- _LIBCPP_HIDE_FROM_ABI function_ref(_Fp&& __obj) noexcept
+ _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
: __storage_(std::addressof(__obj)),
- __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
_LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
return __obj(std::forward<_ArgTypes>(__args)...);
}) {}
template <auto _Fn>
- requires __is_invocable_using<decltype(_Fn)>
+ requires __is_invocable_using<const decltype(_Fn)&>
_LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>) noexcept
- : __call_([](__storage_t, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ : __call_([](__storage_t, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
return std::invoke_r<_Rp>(_Fn, std::forward<_ArgTypes>(__args)...);
}) {
if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
@@ -123,7 +129,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp&>
_LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _Up&& __obj) noexcept
: __storage_(std::addressof(__obj)),
- __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
_LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
}) {
@@ -136,7 +142,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
requires __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp*>
_LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
: __storage_(__obj_ptr),
- __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+ __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
}) {
@@ -154,24 +160,22 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
_LIBCPP_HIDE_FROM_ABI constexpr function_ref& operator=(const function_ref&) noexcept = default;
template <class _Tp>
- requires(!__is_function_ref<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_constant_arg_t<_Tp>)
+ requires(!is_same_v<_Tp, function_ref>) && (!is_pointer_v<_Tp>) && (!__is_constant_arg_t<_Tp>)
_LIBCPP_HIDE_FROM_ABI function_ref& operator=(_Tp) = delete;
- _LIBCPP_HIDE_FROM_ABI constexpr _Rp operator()(_ArgTypes... __args) const noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) {
+ _LIBCPP_HIDE_FROM_ABI constexpr _Rp operator()(_ArgTypes... __args) const noexcept(__is_noexcept) {
return __call_(__storage_, std::forward<_ArgTypes>(__args)...);
}
};
-template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
-struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
- _Tp> {
- using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes, bool __is_noexcept>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept), _Tp> {
+ using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
};
-template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
-struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
- _Tp> {
- using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes, bool __is_noexcept>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(__is_noexcept), _Tp> {
+ using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
};
#endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 17ae13e91da81..71e3798fd5829 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1426,7 +1426,7 @@ module std [system] {
module default_searcher { header "__functional/default_searcher.h" }
module function { header "__functional/function.h" }
module function_ref { header "__functional/function_ref.h" }
- module function_ref_common { textual header "__functional/function_ref_common.h" }
+ module function_ref_common { header "__functional/function_ref_common.h" }
module function_ref_impl { textual header "__functional/function_ref_impl.h" }
module hash { header "__functional/hash.h" }
module identity { header "__functional/identity.h" }
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp
new file mode 100644
index 0000000000000..085b09a4905ba
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// template<auto f, class T>
+// constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
+// Preconditions: If is_member_pointer_v<F> is true, obj is not a null pointer.
+
+#include <functional>
+
+#include "check_assertion.h"
+
+struct A {
+ void f() {}
+};
+
+int main(int, char**) {
+ TEST_LIBCPP_ASSERT_FAILURE(std::function_ref<void()>(std::constant_arg<&A::f>, static_cast<A*>(nullptr)),
+ "the object pointer should not be a nullptr");
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
new file mode 100644
index 0000000000000..371bf714f38c9
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// template<class F> function_ref(F* f) noexcept;
+// Preconditions: f is not a null pointer.
+
+#include <functional>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ TEST_LIBCPP_ASSERT_FAILURE(
+ std::function_ref<void()>(static_cast<void (*)()>(nullptr)), "the function pointer should not be a nullptr");
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
new file mode 100644
index 0000000000000..4e26f940ff430
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<auto f> constexpr function_ref(constant_arg_t<f>) noexcept;
+// template<auto f, class U>
+// constexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
+// template<auto f, class T>
+// constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
+
+// Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
+
+#include <functional>
+#include <utility>
+
+struct A {
+ void f();
+};
+
+// clang-format off
+void test() {
+ std::function_ref<void()> f1(std::constant_arg<static_cast<void (*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+ // expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': the function pointer should not be a nullptr}}
+
+ std::function_ref<void(A)> f2(std::constant_arg<static_cast<void (A::*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+ // expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': the function pointer should not be a nullptr}}
+
+ int i;
+ std::function_ref<void()> f3(std::constant_arg<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+ // expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': the function pointer should not be a nullptr}}
+
+ A a;
+ std::function_ref<void()> f4(std::constant_arg<static_cast<void (A::*)()>(nullptr)>, a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+ // expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': the function pointer should not be a nullptr}}
+
+ std::function_ref<void()> f5(std::constant_arg<static_cast<void (*)(int*)>(nullptr)>, &i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+ // expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': the function pointer should not be a nullptr}}
+
+ std::function_ref<void()> f6(std::constant_arg<static_cast<void (A::*)()>(nullptr)>, &a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+ // expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': the function pointer should not be a nullptr}}
+}
+// clang-format on
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
similarity index 100%
rename from libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp
rename to libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp
new file mode 100644
index 0000000000000..5e54af54cfe1f
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp
@@ -0,0 +1,106 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<auto f> constexpr function_ref(constant_arg_t<f>) noexcept;
+
+#include "__utility/constant_arg.h"
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints: is-invocable-using<const F&> is true.
+
+auto l1 = [] {};
+auto l1_noexcept = [] noexcept {};
+auto l2 = [](int) {};
+auto l2_noexcept = [](int) noexcept {};
+
+struct NonConst {
+ void operator()() noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<NonConst{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int)>, std::constant_arg_t<l2>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int)>, std::constant_arg_t<l2>>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1_noexcept>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<NonConst{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l2_noexcept>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) noexcept>, std::constant_arg_t<l2_noexcept>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1_noexcept>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int) noexcept>, std::constant_arg_t<l2_noexcept>>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<NonConst{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) const>, std::constant_arg_t<l2>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int) const>, std::constant_arg_t<l2>>);
+
+// const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1_noexcept>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<NonConst{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l2_noexcept>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_arg_t<l2_noexcept>>);
+
+static_assert(
+ std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1_noexcept>>);
+static_assert(
+ std::is_nothrow_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_arg_t<l2_noexcept>>);
+
+constexpr double f1(int x, double y) noexcept { return x + y; }
+
+constexpr bool test() {
+ {
+ std::function_ref<void()> f(std::constant_arg<[] {}>);
+ f();
+ }
+ {
+ // explicit
+ std::function_ref<void()> f = std::constant_arg<[] {}>;
+ f();
+ }
+ {
+ // const
+ std::function_ref<int() const> f(std::constant_arg<[] { return 42; }>);
+ assert(f() == 42);
+ }
+ {
+ // noexcept
+ std::function_ref<double(int, double) noexcept> f(std::constant_arg<&f1>);
+ assert(f(1, 2.0) == 3.0);
+ }
+ {
+ // const noexcept
+ std::function_ref<double(int, double) const noexcept> f(std::constant_arg<&f1>);
+ assert(f(1, 2.0) == 3.0);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
new file mode 100644
index 0000000000000..8dccedc1f4199
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -0,0 +1,119 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<class F> function_ref(F* f) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - is_function_v<F> is true, and
+// - is-invocable-using<F> is true.
+
+struct A {
+ int i;
+ void f() {}
+ void operator()(auto...) const {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, void (*)()>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, void (*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (*)(int)>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*)>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, void (*)()>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, void (*)() noexcept>);
+
+// non-const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, A (*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, A (*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) noexcept>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, A (*)() noexcept>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double)>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, A)>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double)>);
+static_assert(
+ std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double) noexcept>);
+
+// const noexcept(true)
+static_assert(
+ std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, double) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, double)>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, A) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
+ void (*)(int, double) noexcept>);
+
+int fn() { return 42; }
+
+int fn_noexcept(int i, A a) noexcept { return i + a.i; }
+
+void test() {
+ {
+ // simple case
+ std::function_ref<int()> f(&fn);
+ assert(f() == 42);
+ }
+ {
+ // explicit(false)
+ std::function_ref<int()> f = &fn;
+ assert(f() == 42);
+ }
+ {
+ std::function_ref<int(int, A)> f(&fn_noexcept);
+ assert(f(4, A{5}) == 9);
+ }
+ {
+ // noexcept
+ std::function_ref<int(int, A) noexcept> f(&fn_noexcept);
+ assert(f(4, A{5}) == 9);
+ }
+ {
+ // const
+ auto x = [](int x, int y, int z) { return x + y - z; };
+ std::function_ref<int(int, int, int) const> f(+x);
+ assert(f(2, 3, 4) == 1);
+ }
+ {
+ // const noexcept
+ std::function_ref<int(int, A) const noexcept> f(&fn_noexcept);
+ assert(f(4, A{5}) == 9);
+ }
+}
+
+int main(int, char**) {
+ test();
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
new file mode 100644
index 0000000000000..4657e7b129d2e
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -0,0 +1,203 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<class F> constexpr function_ref(F&&) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - remove_cvref_t<F> is not the same type as function_ref,
+// - is_member_pointer_v<T> is false, and
+// - is-invocable-using<cv T&> is true.
+
+struct A {
+ int i;
+ void f() {}
+ void operator()(auto...) const {}
+};
+
+constexpr auto l1 = [] {};
+using L1 = std::remove_cvref_t<decltype(l1)>;
+
+constexpr auto l2 = [] { return A{5}; };
+using L2 = std::remove_cvref_t<decltype(l2)>;
+
+constexpr auto l2_noexcept = [] noexcept { return A{5}; };
+using L2Noexcept = std::remove_cvref_t<decltype(l2_noexcept)>;
+
+constexpr auto l3 = [](int x, double d) { return x + d; };
+using L3 = std::remove_cvref_t<decltype(l3)>;
+
+constexpr auto l3_noexcept = [](int x, double d) noexcept { return x + d; };
+using L3Noexcept = std::remove_cvref_t<decltype(l3_noexcept)>;
+
+struct NonConstInvocable {
+ int i;
+
+ constexpr int operator()(int x, double y) {
+ ++i;
+ return x + y + i;
+ }
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1 const&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1&&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1 const&&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L2&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::function_ref<int()>&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*)>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1 const&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1 const&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::function_ref<int()>&>);
+
+// non-const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, std::function_ref<A&() noexcept>>);
+
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, L2&>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, L2 const&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) noexcept>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, std::function_ref<A&() noexcept>>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, L3&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, const L3&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, L3&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, const L3&&>);
+static_assert(
+ std::is_constructible_v<std::function_ref<void(int, double) const>, std::function_ref<int(int, double) const>>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(int, double)>, NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double)>, const NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, L3&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, const L3&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, L3&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, const L3&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>,
+ std::function_ref<int(int, double) const>>);
+
+// const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>,
+ std::function_ref<int(int, double) const noexcept>>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3&&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3&&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
+ std::function_ref<int(int, double) const noexcept>>);
+
+struct F {
+ int i;
+ constexpr int operator()(auto&&...) { return 5 + i; }
+
+ constexpr int operator()(auto&&...) const { return 6 + i; }
+};
+
+constexpr bool test() {
+ {
+ std::function_ref<void()> f(l1);
+ f();
+ }
+ {
+ // explicit(false)
+ std::function_ref<void()> f = l1;
+ f();
+ }
+ {
+ // noexcept
+ std::function_ref<A() noexcept> f(l2_noexcept);
+ auto a = f();
+ assert(a.i == 5);
+ }
+ {
+ // const
+ std::function_ref<double(int, double) const> f(l3);
+ assert(f(1, 2.0) == 3.0);
+ }
+ {
+ // const noexcept
+ std::function_ref<double(int, double) const noexcept> f(l3_noexcept);
+ assert(f(1, 2.0) == 3.0);
+ }
+ {
+ // no copies of original callable
+ auto local = [i = 5] mutable { return i++; };
+ std::function_ref<int()> f(local);
+ assert(f() == 5);
+ assert(local() == 6);
+ assert(f() == 7);
+ }
+ {
+ // const correctness
+ F f{5};
+
+ std::function_ref<int()> f1(f);
+ assert(f1() == 10);
+ assert(std::as_const(f1)() == 10);
+
+ std::function_ref<int() const> f2(f);
+ assert(f2() == 11);
+ assert(std::as_const(f2)() == 11);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
index ce9d7f8d73716..e73e778313dec 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+// REQUIRES: std-at-least-c++26
#include <cassert>
#include <functional>
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp
new file mode 100644
index 0000000000000..72e26f13868ea
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
+#include <concepts>
+#include <functional>
+#include <type_traits>
+
+struct A {
+ A() {}
+ A(const A&) {}
+ ~A() {}
+};
+
+static_assert(std::copyable<std::function_ref<A(const A&)>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&)>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) noexcept>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) noexcept>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) const>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) const>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) const noexcept>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) const noexcept>>);
>From b6dcd687aa4a22347a23bc7854008f5d574a6fc0 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 14 Mar 2026 15:28:01 +0000
Subject: [PATCH 15/22] remove experimental
---
libcxx/docs/ReleaseNotes/23.rst | 3 +--
libcxx/include/__configuration/experimental.h | 1 -
libcxx/include/__functional/function_ref.h | 4 ++--
.../include/__functional/function_ref_common.h | 4 ++--
libcxx/include/__functional/function_ref_impl.h | 4 ++--
libcxx/include/version | 4 +---
.../functional.version.compile.pass.cpp | 16 +++++-----------
.../version.version.compile.pass.cpp | 16 +++++-----------
.../generate_feature_test_macro_components.py | 2 --
9 files changed, 18 insertions(+), 36 deletions(-)
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 348764b084ad5..cbc23bfcd9f82 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -39,8 +39,7 @@ Implemented Papers
------------------
- P2440R1: ``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right`` (`Github <https://llvm.org/PR105184>`__)
-- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__) This feature is currently experimental and
- therefore requires ``-fexperimental-library``.
+- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
Improvements and New Features
-----------------------------
diff --git a/libcxx/include/__configuration/experimental.h b/libcxx/include/__configuration/experimental.h
index 1e12c50802895..bb38d8297c63d 100644
--- a/libcxx/include/__configuration/experimental.h
+++ b/libcxx/include/__configuration/experimental.h
@@ -34,6 +34,5 @@
#define _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
#define _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
#define _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
-#define _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
#endif // _LIBCPP___CONFIGURATION_EXPERIMENTAL_H
diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
index e46706a1a8aaf..864e921e37824 100644
--- a/libcxx/include/__functional/function_ref.h
+++ b/libcxx/include/__functional/function_ref.h
@@ -16,7 +16,7 @@
# pragma GCC system_header
#endif
-#if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
+#if _LIBCPP_STD_VER >= 26
// NOLINTBEGIN(readability-duplicate-include)
#define _LIBCPP_FUNCTION_REF_CV
@@ -29,6 +29,6 @@
#undef _LIBCPP_FUNCTION_REF_CV
// NOLINTEND(readability-duplicate-include)
-#endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
+#endif // _LIBCPP_STD_VER >= 26
#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index f921787f951f5..a757d0d308bc5 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -22,7 +22,7 @@
_LIBCPP_BEGIN_NAMESPACE_STD
-#if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
+#if _LIBCPP_STD_VER >= 26
template <class...>
class function_ref;
@@ -60,7 +60,7 @@ function_ref(constant_arg_t<_Fn>) -> function_ref<remove_pointer_t<decltype(_Fn)
template <auto _Fn, class _Tp>
function_ref(constant_arg_t<_Fn>, _Tp&&) -> function_ref<__function_ref_bind_t<decltype(_Fn), _Tp&>>;
-#endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
+#endif // _LIBCPP_STD_VER >= 26
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 65c9088b9c892..bcdd3d92bf7b9 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -39,7 +39,7 @@
_LIBCPP_BEGIN_NAMESPACE_STD
-#if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
+#if _LIBCPP_STD_VER >= 26
template <class...>
class function_ref;
@@ -178,6 +178,6 @@ struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV &
using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
};
-#endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
+#endif // _LIBCPP_STD_VER >= 26
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/version b/libcxx/include/version
index e0c358edd8940..14398c997d895 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -581,9 +581,7 @@ __cpp_lib_void_t 201411L <type_traits>
# if _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
# define __cpp_lib_fstream_native_handle 202306L
# endif
-# if _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
-# define __cpp_lib_function_ref 202306L
-# endif
+# define __cpp_lib_function_ref 202306L
// # define __cpp_lib_generate_random 202403L
// # define __cpp_lib_hazard_pointer 202306L
// # define __cpp_lib_inplace_vector 202406L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
index eaab9bdd80ad8..eedb1eb8eb2a7 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
@@ -494,17 +494,11 @@
# endif
# endif
-# if !defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
-# ifndef __cpp_lib_function_ref
-# error "__cpp_lib_function_ref should be defined in c++26"
-# endif
-# if __cpp_lib_function_ref != 202306L
-# error "__cpp_lib_function_ref should have the value 202306L in c++26"
-# endif
-# else
-# ifdef __cpp_lib_function_ref
-# error "__cpp_lib_function_ref should not be defined when the requirement '!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF' is not met!"
-# endif
+# ifndef __cpp_lib_function_ref
+# error "__cpp_lib_function_ref should be defined in c++26"
+# endif
+# if __cpp_lib_function_ref != 202306L
+# error "__cpp_lib_function_ref should have the value 202306L in c++26"
# endif
# ifndef __cpp_lib_invoke
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index e3866130f9981..0df8428c7fb81 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -7008,17 +7008,11 @@
# endif
# endif
-# if !defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
-# ifndef __cpp_lib_function_ref
-# error "__cpp_lib_function_ref should be defined in c++26"
-# endif
-# if __cpp_lib_function_ref != 202306L
-# error "__cpp_lib_function_ref should have the value 202306L in c++26"
-# endif
-# else
-# ifdef __cpp_lib_function_ref
-# error "__cpp_lib_function_ref should not be defined when the requirement '!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF' is not met!"
-# endif
+# ifndef __cpp_lib_function_ref
+# error "__cpp_lib_function_ref should be defined in c++26"
+# endif
+# if __cpp_lib_function_ref != 202306L
+# error "__cpp_lib_function_ref should have the value 202306L in c++26"
# endif
# ifndef __cpp_lib_gcd_lcm
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f1e7a92d0d44c..5438cc34bb857 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -693,8 +693,6 @@ def add_version_header(tc):
"c++26": 202306 # P0792R14 function_ref: a type-erased callable reference
},
"headers": ["functional"],
- "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF",
- "libcxx_guard": "_LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF"
},
{
"name": "__cpp_lib_gcd_lcm",
>From 97866ed1d1c76b60bedf51b6f3fa9ec19248a968 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 14 Mar 2026 20:14:24 +0000
Subject: [PATCH 16/22] test for constant_arg_ref
---
.../include/__functional/function_ref_impl.h | 6 +-
.../func.wrap.ref.ctor/constant_arg.pass.cpp | 52 ++-
.../constant_arg_ref.pass.cpp | 319 ++++++++++++++++++
.../func.wrap.ref.ctor/function_ptr.pass.cpp | 20 ++
.../func.wrap.ref.ctor/ref.pass.cpp | 25 ++
5 files changed, 414 insertions(+), 8 deletions(-)
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index bcdd3d92bf7b9..7df579186d032 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -74,7 +74,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
__fn_ptr = reinterpret_cast<void (*)()>(__ptr);
}
}
- } __storage_;
+ };
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_t __storage) {
@@ -90,6 +90,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
}
}
+ __storage_t __storage_;
+
using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, _ArgTypes&&...) noexcept(__is_noexcept);
__call_t __call_;
@@ -126,7 +128,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
}
template <auto _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
- requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp&>
+ requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<const decltype(_Fn)&, _LIBCPP_FUNCTION_REF_CV _Tp&>
_LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _Up&& __obj) noexcept
: __storage_(std::addressof(__obj)),
__call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp
index 5e54af54cfe1f..7adc6108e9b50 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp
@@ -10,7 +10,6 @@
// template<auto f> constexpr function_ref(constant_arg_t<f>) noexcept;
-#include "__utility/constant_arg.h"
#include <cassert>
#include <functional>
#include <utility>
@@ -25,13 +24,13 @@ auto l1_noexcept = [] noexcept {};
auto l2 = [](int) {};
auto l2_noexcept = [](int) noexcept {};
-struct NonConst {
+struct NonConstInvocable {
void operator()() noexcept {}
};
// non-const noexcept(false)
static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>>);
-static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<NonConst{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<NonConstInvocable{}>>);
static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l2>>);
static_assert(std::is_constructible_v<std::function_ref<void(int)>, std::constant_arg_t<l2>>);
@@ -41,7 +40,7 @@ static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int)>, std:
// non-const noexcept
static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1_noexcept>>);
static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1>>);
-static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<NonConst{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<NonConstInvocable{}>>);
static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l2_noexcept>>);
static_assert(std::is_constructible_v<std::function_ref<void(int) noexcept>, std::constant_arg_t<l2_noexcept>>);
@@ -50,7 +49,7 @@ static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int) noexce
// const noexcept(false)
static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1>>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<NonConst{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<NonConstInvocable{}>>);
static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l2>>);
static_assert(std::is_constructible_v<std::function_ref<void(int) const>, std::constant_arg_t<l2>>);
@@ -60,7 +59,8 @@ static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int) const>
// const noexcept
static_assert(std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1_noexcept>>);
static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1>>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<NonConst{}>>);
+static_assert(
+ !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<NonConstInvocable{}>>);
static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l2_noexcept>>);
static_assert(std::is_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_arg_t<l2_noexcept>>);
@@ -71,6 +71,17 @@ static_assert(
constexpr double f1(int x, double y) noexcept { return x + y; }
+struct Int {
+ int i;
+ constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+ constexpr int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
constexpr bool test() {
{
std::function_ref<void()> f(std::constant_arg<[] {}>);
@@ -96,11 +107,40 @@ constexpr bool test() {
std::function_ref<double(int, double) const noexcept> f(std::constant_arg<&f1>);
assert(f(1, 2.0) == 3.0);
}
+ {
+ // with conversions
+ std::function_ref<Int(int, int, int)> f(std::constant_arg<NeedsConversion{}>);
+ assert(f(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const> f2(std::constant_arg<NeedsConversion{}>);
+ assert(f2(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<NeedsConversion{}>);
+ assert(f3(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<NeedsConversion{}>);
+ assert(f4(1, 2, 3).i == 6);
+ }
+ {
+ // with conversions function pointer
+ std::function_ref<Int(int, int, int)> f(std::constant_arg<&needs_conversion>);
+ assert(f(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const> f2(std::constant_arg<&needs_conversion>);
+ assert(f2(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<&needs_conversion>);
+ assert(f3(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<&needs_conversion>);
+ assert(f4(1, 2, 3).i == 6);
+ }
return true;
}
int main(int, char**) {
test();
+ static_assert(test());
return 0;
}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp
new file mode 100644
index 0000000000000..3d77f59263496
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp
@@ -0,0 +1,319 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<auto f, class U>
+// constexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - is_rvalue_reference_v<U&&> is false, and
+// - is-invocable-using<const F&, cv T&> is true.
+
+auto l1 = [](int) {};
+auto l1_noexcept = [](int) noexcept {};
+auto l2 = [](int, double) {};
+auto l2_noexcept = [](int, double) noexcept {};
+auto l3 = [](int&) {};
+auto l3_noexcept = [](int&) noexcept {};
+
+struct NonConstInvocable {
+ void operator()(long) noexcept {}
+};
+
+struct A {
+ int i;
+ void f() {}
+ void f_const() const {}
+ void f_noexcept() noexcept {}
+ void f_const_noexcept() const noexcept {}
+ void g(int&) {}
+ void g_const(int&) const {}
+ void g_noexcept(int&) noexcept {}
+ void g_const_noexcept(int&) const noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>, int&&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<NonConstInvocable{}>, long&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_arg_t<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l2>, int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l3>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l3>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<&A::g>, A&>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_arg_t<l2>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_arg_t<l3>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_arg_t<&A::f>, A&>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1_noexcept>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1_noexcept>, int&&>);
+
+static_assert(
+ !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<NonConstInvocable{}>, long&>);
+
+static_assert(
+ std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_arg_t<l2_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_arg_t<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l2_noexcept>, int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l3_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l3>, const int&>);
+static_assert(
+ !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l3_noexcept>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<&A::f_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<&A::g_noexcept>, A&>);
+
+// the constructor is noexcept
+static_assert(
+ std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1_noexcept>, int&>);
+static_assert(
+ std::is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_arg_t<l2_noexcept>, int&>);
+static_assert(
+ std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l3_noexcept>, int&>);
+static_assert(
+ std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<&A::f_noexcept>, A&>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1>, int&&>);
+
+static_assert(
+ !std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<NonConstInvocable{}>, long&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_arg_t<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l2>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l3>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l3>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::f_const>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::g_const>, A&>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_arg_t<l2>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::f_const>, A&>);
+
+// const noexcept
+static_assert(
+ std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1_noexcept>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1>, int&>);
+
+static_assert(
+ !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1_noexcept>, int&&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+ std::constant_arg_t<NonConstInvocable{}>,
+ long&>);
+
+static_assert(
+ std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_arg_t<l2_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_arg_t<l2>, int&>);
+static_assert(
+ !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l2_noexcept>, int&>);
+
+static_assert(
+ !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l3_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l3>, const int&>);
+static_assert(
+ !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l3_noexcept>, const int&>);
+
+static_assert(
+ std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f_const_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f_const>, A&>);
+static_assert(
+ !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f_noexcept>, A&>);
+static_assert(
+ !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::g_const_noexcept>, A&>);
+
+// the constructor is noexcept
+static_assert(
+ std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1_noexcept>, int&>);
+static_assert(
+ std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_arg_t<l2_noexcept>, int&>);
+static_assert(
+ std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f_const_noexcept>, A&>);
+
+constexpr double f1(int x, double y) noexcept { return x + y; }
+
+struct M {
+ int i;
+ constexpr int f() { return i; }
+ constexpr int f_const() const { return i + 5; }
+ constexpr int f_noexcept() noexcept { return i + 7; }
+ constexpr int f_const_noexcept() const noexcept { return i + 9; }
+ constexpr int g(int& j) {
+ j = 42;
+ return i + j;
+ }
+ constexpr int g_const(int& j) const {
+ j = 42;
+ return i + j + 1;
+ }
+ constexpr int g_noexcept(int& j) noexcept {
+ j = 42;
+ return i + j + 2;
+ }
+ constexpr int g_const_noexcept(int& j) const noexcept {
+ j = 42;
+ return i + j + 3;
+ }
+};
+
+struct Int {
+ int i;
+ constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+ constexpr int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+ {
+ int i = 0;
+ std::function_ref<void()> f(std::constant_arg<[](int) {}>, i);
+ f();
+ }
+ {
+ // explicit
+ int i = 0;
+ std::function_ref<void()> f = {std::constant_arg<[](int) {}>, i};
+ f();
+ }
+ {
+ // mutate
+ int i;
+ std::function_ref<double(double)> f(
+ std::constant_arg<[](int& j, double d) {
+ j = 5;
+ return j + d;
+ }>,
+ i);
+ assert(f(3.3) == 8.3);
+ assert(i == 5);
+ }
+ {
+ // const
+ int i = 5;
+ std::function_ref<int() const> f(std::constant_arg<[](int i) { return i + 42; }>, i);
+ assert(f() == 47);
+ }
+ {
+ // noexcept
+ int i = 5;
+ std::function_ref<double(double) noexcept> f(std::constant_arg<&f1>, i);
+ assert(f(2.0) == 7.0);
+ }
+ {
+ // const noexcept
+ int i = 5;
+ std::function_ref<double( double) const noexcept> f(std::constant_arg<&f1>, i);
+ assert(f(2.0) == 7.0);
+ }
+ {
+ // member ptr
+ M m{3};
+ std::function_ref<int()> f(std::constant_arg<&M::f>, m);
+ assert(f() == 3);
+
+ int j = 0;
+ std::function_ref<int(int&)> g(std::constant_arg<&M::g>, m);
+ assert(g(j) == 45);
+ assert(j == 42);
+
+ std::function_ref<int() const> f_const(std::constant_arg<&M::f_const>, m);
+ assert(f_const() == 8);
+
+ j = 0;
+ std::function_ref<int(int&)> g_const(std::constant_arg<&M::g_const>, m);
+ assert(g_const(j) == 46);
+ assert(j == 42);
+
+ std::function_ref<int() noexcept> f_noexcept(std::constant_arg<&M::f_noexcept>, m);
+ assert(f_noexcept() == 10);
+
+ j = 0;
+ std::function_ref<int(int&) noexcept> g_noexcept(std::constant_arg<&M::g_noexcept>, m);
+ assert(g_noexcept(j) == 47);
+ assert(j == 42);
+
+ std::function_ref<int() const noexcept> f_const_noexcept(std::constant_arg<&M::f_const_noexcept>, m);
+ assert(f_const_noexcept() == 12);
+
+ j = 0;
+ std::function_ref<int(int&) const noexcept> g_const_noexcept(std::constant_arg<&M::g_const_noexcept>, m);
+ assert(g_const_noexcept(j) == 48);
+ assert(j == 42);
+ }
+ {
+ // with conversions
+ std::function_ref<Int(int, int, int)> f(std::constant_arg<NeedsConversion{}>);
+ assert(f(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const> f2(std::constant_arg<NeedsConversion{}>);
+ assert(f2(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<NeedsConversion{}>);
+ assert(f3(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<NeedsConversion{}>);
+ assert(f4(1, 2, 3).i == 6);
+ }
+ {
+ // with conversions function pointer
+ std::function_ref<Int(int, int, int)> f(std::constant_arg<&needs_conversion>);
+ assert(f(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const> f2(std::constant_arg<&needs_conversion>);
+ assert(f2(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<&needs_conversion>);
+ assert(f3(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<&needs_conversion>);
+ assert(f4(1, 2, 3).i == 6);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
index 8dccedc1f4199..22835c48ebcf9 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -80,6 +80,13 @@ int fn() { return 42; }
int fn_noexcept(int i, A a) noexcept { return i + a.i; }
+struct Int {
+ int i;
+ Int(int ii) noexcept : i(ii) {}
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
void test() {
{
// simple case
@@ -111,6 +118,19 @@ void test() {
std::function_ref<int(int, A) const noexcept> f(&fn_noexcept);
assert(f(4, A{5}) == 9);
}
+ {
+ std::function_ref<Int(int, int, int)> f(&needs_conversion);
+ assert(f(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const> f2(&needs_conversion);
+ assert(f2(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) noexcept> f3(&needs_conversion);
+ assert(f3(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const noexcept> f4(&needs_conversion);
+ assert(f4(1, 2, 3).i == 6);
+ }
}
int main(int, char**) {
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 4657e7b129d2e..f0f6411521f6d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -146,6 +146,15 @@ struct F {
constexpr int operator()(auto&&...) const { return 6 + i; }
};
+struct Int {
+ int i;
+ constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+ constexpr int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
constexpr bool test() {
{
std::function_ref<void()> f(l1);
@@ -192,6 +201,22 @@ constexpr bool test() {
assert(f2() == 11);
assert(std::as_const(f2)() == 11);
}
+ {
+ // with conversions
+ NeedsConversion c{};
+
+ std::function_ref<Int(int, int, int)> f(c);
+ assert(f(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const> f2(c);
+ assert(f2(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) noexcept> f3(c);
+ assert(f3(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const noexcept> f4(c);
+ assert(f4(1, 2, 3).i == 6);
+ }
return true;
}
>From c66679665ddc454d4b8fbb36915fb737d817ef86 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 14 Mar 2026 20:43:59 +0000
Subject: [PATCH 17/22] test for constant_arg_ptr
---
.../include/__functional/function_ref_impl.h | 2 +-
.../constant_arg_ptr.pass.cpp | 302 ++++++++++++++++++
.../constant_arg_ref.pass.cpp | 37 ++-
3 files changed, 323 insertions(+), 18 deletions(-)
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ptr.pass.cpp
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 7df579186d032..bc3078ed47660 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -141,7 +141,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
}
template <auto _Fn, class _Tp>
- requires __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp*>
+ requires __is_invocable_using<const decltype(_Fn)&, _LIBCPP_FUNCTION_REF_CV _Tp*>
_LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
: __storage_(__obj_ptr),
__call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ptr.pass.cpp
new file mode 100644
index 0000000000000..20b0ca855f08d
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ptr.pass.cpp
@@ -0,0 +1,302 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<auto f, class T>
+// constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints: is-invocable-using<const F&, cv T*> is true.
+
+auto l1 = [](int*) {};
+auto l1_const = [](const int*) {};
+auto l1_noexcept = [](int*) noexcept {};
+auto l1_const_noexcept = [](const int*) noexcept {};
+auto l2 = [](int*, double) {};
+auto l2_const = [](const int*, double) {};
+auto l2_noexcept = [](int*, double) noexcept {};
+auto l2_const_noexcept = [](const int*, double) noexcept {};
+
+struct NonConstInvocable {
+ void operator()(int*) noexcept {}
+};
+
+struct A {
+ int i;
+ void f() {}
+ void f_const() const {}
+ void f_noexcept() noexcept {}
+ void f_const_noexcept() const noexcept {}
+ void g(int&) {}
+ void g_const(int&) const {}
+ void g_noexcept(int&) noexcept {}
+ void g_const_noexcept(int&) const noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>, const int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_arg_t<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l2>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<&A::g>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_arg_t<l2>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_arg_t<&A::f>, A*>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1_noexcept>, int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1>, int*>);
+
+static_assert(
+ !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<NonConstInvocable{}>, int*>);
+
+static_assert(
+ std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_arg_t<l2_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_arg_t<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l2_noexcept>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<&A::f_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<&A::g_noexcept>, A*>);
+
+// the constructor is noexcept
+static_assert(
+ std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1_noexcept>, int*>);
+static_assert(
+ std::is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_arg_t<l2_noexcept>, int*>);
+static_assert(
+ std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<&A::f_noexcept>, A*>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1_const>, int*>);
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1_const>, const int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1>, int*>);
+
+static_assert(
+ !std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_arg_t<l2_const>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l2_const>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::f_const>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::g_const>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1_const>, int*>);
+static_assert(
+ std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1_const>, const int*>);
+static_assert(
+ std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_arg_t<l2_const>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::f_const>, A*>);
+
+// const noexcept
+static_assert(
+ std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1_const_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1_const>, int*>);
+static_assert(
+ !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1_noexcept>, int*>);
+
+static_assert(
+ !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const noexcept>,
+ std::constant_arg_t<l2_const_noexcept>,
+ int*>);
+static_assert(
+ !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l2_const_noexcept>, int*>);
+
+static_assert(
+ std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f_const_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f_const>, A*>);
+static_assert(
+ !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f_noexcept>, A*>);
+static_assert(
+ !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::g_const_noexcept>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+ std::constant_arg_t<l1_const_noexcept>,
+ int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const noexcept>,
+ std::constant_arg_t<l2_const_noexcept>,
+ int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+ std::constant_arg_t<&A::f_const_noexcept>,
+ A*>);
+
+constexpr double f1(const int* x, double y) noexcept { return *x + y; }
+
+struct M {
+ int i;
+ constexpr int f() { return i; }
+ constexpr int f_const() const { return i + 5; }
+ constexpr int f_noexcept() noexcept { return i + 7; }
+ constexpr int f_const_noexcept() const noexcept { return i + 9; }
+ constexpr int g(int& j) {
+ j = 42;
+ return i + j;
+ }
+ constexpr int g_const(int& j) const {
+ j = 42;
+ return i + j + 1;
+ }
+ constexpr int g_noexcept(int& j) noexcept {
+ j = 42;
+ return i + j + 2;
+ }
+ constexpr int g_const_noexcept(int& j) const noexcept {
+ j = 42;
+ return i + j + 3;
+ }
+};
+
+struct Int {
+ int i;
+ constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+ constexpr int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+ {
+ int i = 0;
+ std::function_ref<void()> f(std::constant_arg<[](int*) {}>, &i);
+ f();
+ }
+ {
+ // explicit
+ int i = 0;
+ std::function_ref<void()> f = {std::constant_arg<[](int*) {}>, &i};
+ f();
+ }
+ {
+ // mutate
+ int i;
+ std::function_ref<double(double)> f(
+ std::constant_arg<[](int* j, double d) {
+ *j = 5;
+ return *j + d;
+ }>,
+ &i);
+ assert(f(3.3) == 8.3);
+ assert(i == 5);
+ }
+ {
+ // const
+ int i = 5;
+ std::function_ref<int() const> f(std::constant_arg<[](const int* i) { return *i + 42; }>, &i);
+ assert(f() == 47);
+ }
+ {
+ // noexcept
+ int i = 5;
+ std::function_ref<double(double) noexcept> f(std::constant_arg<&f1>, &i);
+ assert(f(2.0) == 7.0);
+ }
+ {
+ // const noexcept
+ int i = 5;
+ std::function_ref<double(double) const noexcept> f(std::constant_arg<&f1>, &i);
+ assert(f(2.0) == 7.0);
+ }
+ {
+ // member ptr
+ M m{3};
+ std::function_ref<int()> f(std::constant_arg<&M::f>, &m);
+ assert(f() == 3);
+
+ int j = 0;
+ std::function_ref<int(int&)> g(std::constant_arg<&M::g>, &m);
+ assert(g(j) == 45);
+ assert(j == 42);
+
+ std::function_ref<int() const> f_const(std::constant_arg<&M::f_const>, &m);
+ assert(f_const() == 8);
+
+ j = 0;
+ std::function_ref<int(int&)> g_const(std::constant_arg<&M::g_const>, &m);
+ assert(g_const(j) == 46);
+ assert(j == 42);
+
+ std::function_ref<int() noexcept> f_noexcept(std::constant_arg<&M::f_noexcept>, &m);
+ assert(f_noexcept() == 10);
+
+ j = 0;
+ std::function_ref<int(int&) noexcept> g_noexcept(std::constant_arg<&M::g_noexcept>, &m);
+ assert(g_noexcept(j) == 47);
+ assert(j == 42);
+
+ std::function_ref<int() const noexcept> f_const_noexcept(std::constant_arg<&M::f_const_noexcept>, &m);
+ assert(f_const_noexcept() == 12);
+
+ j = 0;
+ std::function_ref<int(int&) const noexcept> g_const_noexcept(std::constant_arg<&M::g_const_noexcept>, &m);
+ assert(g_const_noexcept(j) == 48);
+ assert(j == 42);
+ }
+ {
+ // with conversions
+ int i = 1;
+ std::function_ref<Int(int, int)> f(std::constant_arg<NeedsConversion{}>, i);
+ assert(f(2, 3).i == 6);
+
+ std::function_ref<Int(int, int) const> f2(std::constant_arg<NeedsConversion{}>, i);
+ assert(f2(2, 3).i == 6);
+
+ std::function_ref<Int(int, int) noexcept> f3(std::constant_arg<NeedsConversion{}>, i);
+ assert(f3(2, 3).i == 6);
+
+ std::function_ref<Int(int, int) const noexcept> f4(std::constant_arg<NeedsConversion{}>, i);
+ assert(f4(2, 3).i == 6);
+ }
+ {
+ // with conversions function pointer
+ int i = 1;
+ std::function_ref<Int(int, int)> f(std::constant_arg<&needs_conversion>, i);
+ assert(f(2, 3).i == 6);
+
+ std::function_ref<Int(int, int) const> f2(std::constant_arg<&needs_conversion>, i);
+ assert(f2(2, 3).i == 6);
+
+ std::function_ref<Int(int, int) noexcept> f3(std::constant_arg<&needs_conversion>, i);
+ assert(f3(2, 3).i == 6);
+
+ std::function_ref<Int(int, int) const noexcept> f4(std::constant_arg<&needs_conversion>, i);
+ assert(f4(2, 3).i == 6);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp
index 3d77f59263496..43eb5133c76fd 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp
@@ -242,7 +242,7 @@ constexpr bool test() {
{
// const noexcept
int i = 5;
- std::function_ref<double( double) const noexcept> f(std::constant_arg<&f1>, i);
+ std::function_ref<double(double) const noexcept> f(std::constant_arg<&f1>, i);
assert(f(2.0) == 7.0);
}
{
@@ -282,31 +282,34 @@ constexpr bool test() {
}
{
// with conversions
- std::function_ref<Int(int, int, int)> f(std::constant_arg<NeedsConversion{}>);
- assert(f(1, 2, 3).i == 6);
+ int i = 1;
- std::function_ref<Int(int, int, int) const> f2(std::constant_arg<NeedsConversion{}>);
- assert(f2(1, 2, 3).i == 6);
+ std::function_ref<Int(int, int)> f(std::constant_arg<NeedsConversion{}>, i);
+ assert(f(2, 3).i == 6);
- std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<NeedsConversion{}>);
- assert(f3(1, 2, 3).i == 6);
+ std::function_ref<Int(int, int) const> f2(std::constant_arg<NeedsConversion{}>, i);
+ assert(f2(2, 3).i == 6);
- std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<NeedsConversion{}>);
- assert(f4(1, 2, 3).i == 6);
+ std::function_ref<Int(int, int) noexcept> f3(std::constant_arg<NeedsConversion{}>, i);
+ assert(f3(2, 3).i == 6);
+
+ std::function_ref<Int(int, int) const noexcept> f4(std::constant_arg<NeedsConversion{}>, i);
+ assert(f4(2, 3).i == 6);
}
{
// with conversions function pointer
- std::function_ref<Int(int, int, int)> f(std::constant_arg<&needs_conversion>);
- assert(f(1, 2, 3).i == 6);
+ int i = 1;
+ std::function_ref<Int(int, int)> f(std::constant_arg<&needs_conversion>, i);
+ assert(f(2, 3).i == 6);
- std::function_ref<Int(int, int, int) const> f2(std::constant_arg<&needs_conversion>);
- assert(f2(1, 2, 3).i == 6);
+ std::function_ref<Int(int, int) const> f2(std::constant_arg<&needs_conversion>, i);
+ assert(f2(2, 3).i == 6);
- std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<&needs_conversion>);
- assert(f3(1, 2, 3).i == 6);
+ std::function_ref<Int(int, int) noexcept> f3(std::constant_arg<&needs_conversion>, i);
+ assert(f3(2, 3).i == 6);
- std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<&needs_conversion>);
- assert(f4(1, 2, 3).i == 6);
+ std::function_ref<Int(int, int) const noexcept> f4(std::constant_arg<&needs_conversion>, i);
+ assert(f4(2, 3).i == 6);
}
return true;
>From d265b3671dc36b8179284f6308a5f56f50d737d4 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 14 Mar 2026 20:49:05 +0000
Subject: [PATCH 18/22] remove fun_ptr member
---
.../include/__functional/function_ref_impl.h | 26 +++++++++----------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index bc3078ed47660..f0c7c40765013 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -54,24 +54,24 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
// use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
// pointers
+ // todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be supported
union __storage_t {
- void* __obj_ptr;
- void const* __obj_const_ptr;
- void (*__fn_ptr)();
+ void* __obj_ptr_;
+ void const* __obj_const_ptr_;
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t() noexcept : __obj_ptr(nullptr) {}
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t() noexcept : __obj_ptr_(nullptr) {}
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t(_Tp* __ptr) noexcept {
if constexpr (is_object_v<_Tp>) {
if constexpr (is_const_v<_Tp>) {
- __obj_const_ptr = __ptr;
+ __obj_const_ptr_ = __ptr;
} else {
- __obj_ptr = __ptr;
+ __obj_ptr_ = __ptr;
}
} else {
static_assert(is_function_v<_Tp>);
- __fn_ptr = reinterpret_cast<void (*)()>(__ptr);
+ __obj_ptr_ = reinterpret_cast<void*>(__ptr);
}
}
};
@@ -80,13 +80,13 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
_LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_t __storage) {
if constexpr (is_object_v<_Tp>) {
if constexpr (is_const_v<_Tp>) {
- return static_cast<_Tp*>(__storage.__obj_const_ptr);
+ return static_cast<_Tp*>(__storage.__obj_const_ptr_);
} else {
- return static_cast<_Tp*>(__storage.__obj_ptr);
+ return static_cast<_Tp*>(__storage.__obj_ptr_);
}
} else {
static_assert(is_function_v<_Tp>);
- return reinterpret_cast<_Tp*>(__storage.__fn_ptr);
+ return reinterpret_cast<_Tp*>(__storage.__obj_ptr_);
}
}
@@ -142,8 +142,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
template <auto _Fn, class _Tp>
requires __is_invocable_using<const decltype(_Fn)&, _LIBCPP_FUNCTION_REF_CV _Tp*>
- _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
- : __storage_(__obj_ptr),
+ _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr_) noexcept
+ : __storage_(__obj_ptr_),
__call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
@@ -153,7 +153,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
}
if constexpr (is_member_pointer_v<decltype(_Fn)>) {
- _LIBCPP_ASSERT_UNCATEGORIZED(__obj_ptr != nullptr, "the object pointer should not be a nullptr");
+ _LIBCPP_ASSERT_UNCATEGORIZED(__obj_ptr_ != nullptr, "the object pointer should not be a nullptr");
}
}
>From 2762807effd25a5971bf986963f6f17f224147f9 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 15 Mar 2026 14:43:35 +0000
Subject: [PATCH 19/22] invoke
---
.../func.wrap/func.wrap.ref/ctad.pass.cpp | 2 +-
.../func.wrap.ref.ctor/assign.delete.pass.cpp | 117 +++++++
.../func.wrap.ref.ctor/copy.pass.cpp | 107 ++++++
.../func.wrap.ref.ctor/copy_assign.pass.cpp | 135 ++++++++
.../func.wrap.ref.ctor/move.pass.cpp | 105 ++++++
.../func.wrap.ref.ctor/move_assign.pass.cpp | 133 ++++++++
.../func.wrap.ref.inv/invoke.pass.cpp | 315 ++++++++++++++++++
.../func.wrap.ref/invoke/const.pass.cpp | 124 -------
.../invoke/const_noexcept.pass.cpp | 124 -------
.../func.wrap.ref/invoke/default.pass.cpp | 119 -------
.../func.wrap.ref/invoke/noexcept.pass.cpp | 119 -------
11 files changed, 913 insertions(+), 487 deletions(-)
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
delete mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp
delete mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp
delete mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
delete mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
index 9ef7bd66a8953..591c397fee2d8 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+// REQUIRES: std-at-least-c++26
#include <cassert>
#include <functional>
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
new file mode 100644
index 0000000000000..956b34695f71a
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -0,0 +1,117 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<class T> function_ref& operator=(T) = delete;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - T is not the same type as function_ref,
+// - is_pointer_v<T> is false, and
+// - T is not a specialization of constant_arg_t.
+
+// non const noexcept(false)
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const>>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() noexcept>>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void()>, void (*)()>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, void (*)(int)>);
+
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_arg_t<[] {}>>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_arg_t<[](int) {}>>);
+
+// const noexcept(false)
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const>, void (*)()>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, void (*)(int)>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::constant_arg_t<[] { return 42; }>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::constant_arg_t<[](int) { return 42; }>>);
+
+// non-const noexcept(true)
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() noexcept>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, void (*)() noexcept>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_arg_t<[] noexcept {} >>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_arg_t<[](int) noexcept {}>>);
+
+// const noexcept(true)
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)() noexcept>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)(int) noexcept>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_arg_t<[] noexcept {}>>);
+static_assert(
+ !std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_arg_t<[](int) noexcept {}>>);
+
+constexpr int forty_two() { return 42; }
+
+constexpr bool test() {
+ {
+ std::function_ref<int()> f(std::constant_arg<[] { return 41; }>);
+ f = std::function_ref<int()>(std::constant_arg<[] { return 42; }>);
+ assert(f() == 42);
+ }
+ if !consteval {
+ std::function_ref<int() > f(std::constant_arg<[] { return 41; }>);
+ f = &forty_two;
+ assert(f() == 42);
+ }
+ {
+ std::function_ref<int() > f(std::constant_arg<[] { return 41; }>);
+ f = std::constant_arg<[] { return 42; }>;
+ assert(f() == 42);
+ }
+ {
+ // const
+ std::function_ref<int() const> f(std::constant_arg<[] { return 41; }>);
+ f = std::constant_arg<[] { return 42; }>;
+ assert(f() == 42);
+ }
+ {
+ // noexcept
+ std::function_ref<int() noexcept> f(std::constant_arg<[] noexcept { return 41; }>);
+ f = std::constant_arg<[] noexcept { return 42; }>;
+ assert(f() == 42);
+ }
+ {
+ // const noexcept
+ std::function_ref<int() const noexcept> f(std::constant_arg<[] noexcept { return 41; }>);
+ f = std::constant_arg<[] noexcept { return 42; }>;
+ assert(f() == 42);
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
new file mode 100644
index 0000000000000..5339d75660d4b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr function_ref(const function_ref&) noexcept = default;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_copy_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() const noexcept>>);
+
+constexpr double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+ int i;
+ constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+ constexpr int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+ {
+ std::function_ref<void()> f(std::constant_arg<[] {}>);
+ auto f2 = f;
+ f2();
+ }
+ {
+ // const
+ std::function_ref<int() const> f(std::constant_arg<[] { return 42; }>);
+ auto f2 = f;
+
+ assert(f2() == 42);
+ }
+ {
+ // noexcept
+ std::function_ref<double(int, double) noexcept> f(std::constant_arg<&f1>);
+ auto f2 = f;
+ assert(f2(1, 2.0) == 3.0);
+ }
+ {
+ // const noexcept
+ std::function_ref<double(int, double) const noexcept> f(std::constant_arg<&f1>);
+ auto f2 = f;
+ assert(f2(1, 2.0) == 3.0);
+ }
+ {
+ // with conversions
+ std::function_ref<Int(int, int, int)> f(std::constant_arg<NeedsConversion{}>);
+ auto f_copy = f;
+ assert(f_copy(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const> f2(std::constant_arg<NeedsConversion{}>);
+ auto f2_copy = f2;
+ assert(f2_copy(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<NeedsConversion{}>);
+ auto f3_copy = f3;
+ assert(f3_copy(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<NeedsConversion{}>);
+ auto f4_copy = f4;
+ assert(f4_copy(1, 2, 3).i == 6);
+ }
+ {
+ // with conversions function pointer
+ std::function_ref<Int(int, int, int)> f(std::constant_arg<&needs_conversion>);
+ auto f_copy = f;
+ assert(f_copy(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const> f2(std::constant_arg<&needs_conversion>);
+ auto f2_copy = f2;
+ assert(f2_copy(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<&needs_conversion>);
+ auto f3_copy = f3;
+ assert(f3_copy(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<&needs_conversion>);
+ auto f4_copy = f4;
+ assert(f4_copy(1, 2, 3).i == 6);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
new file mode 100644
index 0000000000000..c91da893db377
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
@@ -0,0 +1,135 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_copy_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() const noexcept>>);
+
+constexpr double plus(int x, double y) noexcept { return x + y; }
+constexpr double minus(int x, double y) noexcept { return x - y; }
+
+struct Int {
+ int i;
+ constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+ constexpr int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+constexpr int zero(Int, Int, Int) noexcept { return 0; }
+
+constexpr bool test() {
+ {
+ std::function_ref<void()> f(std::constant_arg<[] {}>);
+ std::function_ref<void()> f2(std::constant_arg<[] {}>);
+ f2 = f;
+ f();
+ f2();
+ }
+ {
+ // const
+ std::function_ref<int() const> f(std::constant_arg<[] { return 42; }>);
+ std::function_ref<int() const> f2(std::constant_arg<[] { return 41; }>);
+ f2 = f;
+
+ assert(f() == 42);
+ assert(f2() == 42);
+ }
+ {
+ // noexcept
+ std::function_ref<double(int, double) noexcept> f(std::constant_arg<&plus>);
+ std::function_ref<double(int, double) noexcept> f2(std::constant_arg<&minus>);
+ f2 = f;
+ assert(f(1, 2.0) == 3.0);
+ assert(f2(1, 2.0) == 3.0);
+ }
+ {
+ // const noexcept
+ std::function_ref<double(int, double) const noexcept> f(std::constant_arg<&plus>);
+ std::function_ref<double(int, double) const noexcept> f2(std::constant_arg<&minus>);
+ f2 = f;
+ assert(f(1, 2.0) == 3.0);
+ assert(f2(1, 2.0) == 3.0);
+ }
+ {
+ // with conversions
+ std::function_ref<Int(int, int, int)> f(std::constant_arg<[](int, int, int) { return Int{1}; }>);
+ std::function_ref<Int(int, int, int)> f2(std::constant_arg<NeedsConversion{}>);
+ f = f2;
+ assert(f(1, 2, 3).i == 6);
+ assert(f2(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const> f_const(std::constant_arg<[](int, int, int) { return Int{1}; }>);
+ std::function_ref<Int(int, int, int) const> f2_const(std::constant_arg<NeedsConversion{}>);
+ f_const = f2_const;
+ assert(f_const(1, 2, 3).i == 6);
+ assert(f2_const(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) noexcept> f_noexcept(
+ std::constant_arg<[](int, int, int) noexcept { return Int{1}; }>);
+ std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::constant_arg<NeedsConversion{}>);
+ f_noexcept = f2_noexcept;
+ assert(f_noexcept(1, 2, 3).i == 6);
+ assert(f2_noexcept(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
+ std::constant_arg<[](int, int, int) noexcept { return Int{1}; }>);
+ std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::constant_arg<NeedsConversion{}>);
+ f_const_noexcept = f2_const_noexcept;
+ assert(f_const_noexcept(1, 2, 3).i == 6);
+ assert(f2_const_noexcept(1, 2, 3).i == 6);
+ }
+ {
+ // with conversions function pointer
+ std::function_ref<Int(int, int, int)> f(std::constant_arg<&zero>);
+ std::function_ref<Int(int, int, int)> f2(std::constant_arg<&needs_conversion>);
+ f = f2;
+ assert(f(1, 2, 3).i == 6);
+ assert(f2(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const> f_const(std::constant_arg<&zero>);
+ std::function_ref<Int(int, int, int) const> f2_const(std::constant_arg<&needs_conversion>);
+ f_const = f2_const;
+ assert(f_const(1, 2, 3).i == 6);
+ assert(f2_const(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::constant_arg<&zero>);
+ std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::constant_arg<&needs_conversion>);
+ f_noexcept = f2_noexcept;
+ assert(f_noexcept(1, 2, 3).i == 6);
+ assert(f2_noexcept(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::constant_arg<&zero>);
+ std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::constant_arg<&needs_conversion>);
+ f_const_noexcept = f2_const_noexcept;
+ assert(f_const_noexcept(1, 2, 3).i == 6);
+ assert(f2_const_noexcept(1, 2, 3).i == 6);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
new file mode 100644
index 0000000000000..56d81cb997329
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
@@ -0,0 +1,105 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_move_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() const noexcept>>);
+
+constexpr double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+ int i;
+ constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+ constexpr int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+ {
+ std::function_ref<void()> f(std::constant_arg<[] {}>);
+ auto f2 = std::move(f);
+ f2();
+ }
+ {
+ // const
+ std::function_ref<int() const> f(std::constant_arg<[] { return 42; }>);
+ auto f2 = std::move(f);
+
+ assert(f2() == 42);
+ }
+ {
+ // noexcept
+ std::function_ref<double(int, double) noexcept> f(std::constant_arg<&f1>);
+ auto f2 = std::move(f);
+ assert(f2(1, 2.0) == 3.0);
+ }
+ {
+ // const noexcept
+ std::function_ref<double(int, double) const noexcept> f(std::constant_arg<&f1>);
+ auto f2 = std::move(f);
+ assert(f2(1, 2.0) == 3.0);
+ }
+ {
+ // with conversions
+ std::function_ref<Int(int, int, int)> f(std::constant_arg<NeedsConversion{}>);
+ auto f_copy = std::move(f);
+ assert(f_copy(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const> f2(std::constant_arg<NeedsConversion{}>);
+ auto f2_copy = std::move(f2);
+ assert(f2_copy(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<NeedsConversion{}>);
+ auto f3_copy = std::move(f3);
+ assert(f3_copy(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<NeedsConversion{}>);
+ auto f4_copy = std::move(f4);
+ assert(f4_copy(1, 2, 3).i == 6);
+ }
+ {
+ // with conversions function pointer
+ std::function_ref<Int(int, int, int)> f(std::constant_arg<&needs_conversion>);
+ auto f_copy = std::move(f);
+ assert(f_copy(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const> f2(std::constant_arg<&needs_conversion>);
+ auto f2_copy = std::move(f2);
+ assert(f2_copy(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<&needs_conversion>);
+ auto f3_copy = std::move(f3);
+ assert(f3_copy(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<&needs_conversion>);
+ auto f4_copy = std::move(f4);
+ assert(f4_copy(1, 2, 3).i == 6);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
new file mode 100644
index 0000000000000..45e6323dd2e52
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
@@ -0,0 +1,133 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_move_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() const noexcept>>);
+
+constexpr double plus(int x, double y) noexcept { return x + y; }
+constexpr double minus(int x, double y) noexcept { return x - y; }
+
+struct Int {
+ int i;
+ constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+ constexpr int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+constexpr int zero(Int, Int, Int) noexcept { return 0; }
+
+constexpr bool test() {
+ {
+ std::function_ref<void()> f(std::constant_arg<[] {}>);
+ std::function_ref<void()> f2(std::constant_arg<[] {}>);
+ f2 = std::move(f);
+ f();
+ f2();
+ }
+ {
+ // const
+ std::function_ref<int() const> f(std::constant_arg<[] { return 42; }>);
+ std::function_ref<int() const> f2(std::constant_arg<[] { return 41; }>);
+ f2 = std::move(f);
+
+ assert(f() == 42);
+ assert(f2() == 42);
+ }
+ {
+ // noexcept
+ std::function_ref<double(int, double) noexcept> f(std::constant_arg<&plus>);
+ std::function_ref<double(int, double) noexcept> f2(std::constant_arg<&minus>);
+ f2 = std::move(f);
+ assert(f(1, 2.0) == 3.0);
+ assert(f2(1, 2.0) == 3.0);
+ }
+ {
+ // const noexcept
+ std::function_ref<double(int, double) const noexcept> f(std::constant_arg<&plus>);
+ std::function_ref<double(int, double) const noexcept> f2(std::constant_arg<&minus>);
+ f2 = std::move(f);
+ assert(f(1, 2.0) == 3.0);
+ assert(f2(1, 2.0) == 3.0);
+ }
+ {
+ // with conversions
+ std::function_ref<Int(int, int, int)> f(std::constant_arg<[](int, int, int) { return Int{1}; }>);
+ std::function_ref<Int(int, int, int)> f2(std::constant_arg<NeedsConversion{}>);
+ f = std::move(f2);
+ assert(f(1, 2, 3).i == 6);
+ assert(f2(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const> f_const(std::constant_arg<[](int, int, int) { return Int{1}; }>);
+ std::function_ref<Int(int, int, int) const> f2_const(std::constant_arg<NeedsConversion{}>);
+ f_const = std::move(f2_const);
+ assert(f_const(1, 2, 3).i == 6);
+ assert(f2_const(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) noexcept> f_noexcept(
+ std::constant_arg<[](int, int, int) noexcept { return Int{1}; }>);
+ std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::constant_arg<NeedsConversion{}>);
+ f_noexcept = std::move(f2_noexcept);
+ assert(f_noexcept(1, 2, 3).i == 6);
+ assert(f2_noexcept(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
+ std::constant_arg<[](int, int, int) noexcept { return Int{1}; }>);
+ std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::constant_arg<NeedsConversion{}>);
+ f_const_noexcept = std::move(f2_const_noexcept);
+ assert(f_const_noexcept(1, 2, 3).i == 6);
+ assert(f2_const_noexcept(1, 2, 3).i == 6);
+ }
+ {
+ // with conversions function pointer
+ std::function_ref<Int(int, int, int)> f(std::constant_arg<&zero>);
+ std::function_ref<Int(int, int, int)> f2(std::constant_arg<&needs_conversion>);
+ f = std::move(f2);
+ assert(f(1, 2, 3).i == 6);
+ assert(f2(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const> f_const(std::constant_arg<&zero>);
+ std::function_ref<Int(int, int, int) const> f2_const(std::constant_arg<&needs_conversion>);
+ f_const = std::move(f2_const);
+ assert(f_const(1, 2, 3).i == 6);
+ assert(f2_const(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::constant_arg<&zero>);
+ std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::constant_arg<&needs_conversion>);
+ f_noexcept = std::move(f2_noexcept);
+ assert(f_noexcept(1, 2, 3).i == 6);
+ assert(f2_noexcept(1, 2, 3).i == 6);
+
+ std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::constant_arg<&zero>);
+ std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::constant_arg<&needs_conversion>);
+ f_const_noexcept = std::move(f2_const_noexcept);
+ assert(f_const_noexcept(1, 2, 3).i == 6);
+ assert(f2_const_noexcept(1, 2, 3).i == 6);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
new file mode 100644
index 0000000000000..6b656c2d74c5d
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -0,0 +1,315 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// R operator()(ArgTypes... args) const noexcept(noex);
+
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "MoveOnly.h"
+
+template <class T, class... Args>
+concept ConstInvocable = requires(const T t, Args... args) {
+ { t(std::forward<Args>(args)...) };
+};
+
+template <class T, class... Args>
+concept ConstNoexceptInvocable = requires(const T t, Args... args) {
+ { t(std::forward<Args>(args)...) } noexcept;
+};
+
+static_assert(ConstInvocable<std::function_ref<void()>>);
+static_assert(!ConstNoexceptInvocable<std::function_ref<void()>>);
+
+static_assert(ConstInvocable<std::function_ref<void() noexcept>>);
+static_assert(ConstNoexceptInvocable<std::function_ref<void() noexcept>>);
+
+static_assert(ConstInvocable<std::function_ref<void() const>>);
+static_assert(!ConstNoexceptInvocable<std::function_ref<void() const>>);
+
+static_assert(ConstInvocable<std::function_ref<void() const noexcept>>);
+static_assert(ConstNoexceptInvocable<std::function_ref<void() const noexcept>>);
+
+struct S {
+ int data = 42;
+ int operator()(int& x) const noexcept { return data + x; }
+ int operator()(const int& x) const noexcept { return data + x + 1; }
+ int operator()(int&& x) const noexcept { return data + x + 2; }
+ int operator()(const int&& x) const noexcept { return data + x + 3; }
+};
+
+struct S2 {
+ double operator()(int x, int y, int z) noexcept { return x + y + z; }
+
+ double operator()(int x, int y, int z) const noexcept { return x + y + z + 1; }
+};
+
+void test_default() {
+ {
+ std::function_ref<void()> f = [] {};
+ f();
+ static_assert(std::is_void_v<decltype(f())>);
+ }
+ {
+ // reference
+ int x = 42;
+ std::function_ref<int&(int&)> f(std::constant_arg<[](int& x) -> int& { return x; }>);
+ std::same_as<int&> decltype(auto) r = f(x);
+ assert(&r == &x);
+ }
+ {
+ // Move only
+ std::function_ref<MoveOnly(MoveOnly)> f(std::constant_arg<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+ std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+ assert(r.get() == 6);
+ }
+ {
+ // different return type
+ std::function_ref<int(int)> f(std::constant_arg<[](int x) -> double { return x + 0.1; }>);
+ std::same_as<int> decltype(auto) r = f(5);
+ assert(r == 5);
+ }
+ {
+ // Args overload resolution
+ S s;
+ int x = 1;
+ std::function_ref<int(int&)> f(s);
+ assert(f(x) == 43);
+
+ std::function_ref<int(const int&)> g(s);
+ assert(g(x) == 44);
+
+ std::function_ref<int(int&&)> h(s);
+ assert(h(std::move(x)) == 45);
+
+ std::function_ref<int(const int&&)> i(s);
+ assert(i(std::move(x)) == 46);
+ }
+ {
+ // const overload
+ S2 s;
+ std::function_ref<double(int, int, int)> f(s);
+ std::same_as<double> decltype(auto) r = f(1, 2, 3);
+ assert(r == 6.0);
+
+ std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+ assert(r2 == 6.0);
+
+ const S2 s2;
+ std::function_ref<double(int, int, int)> f2(s2);
+ std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+ assert(r3 == 7.0);
+
+ std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+ assert(r4 == 7.0);
+ }
+}
+
+void test_const() {
+ {
+ std::function_ref<void() const> f = [] {};
+ f();
+ static_assert(std::is_void_v<decltype(f())>);
+ }
+ {
+ // reference
+ int x = 42;
+ std::function_ref<int&(int&) const> f(std::constant_arg<[](int& x) -> int& { return x; }>);
+ std::same_as<int&> decltype(auto) r = f(x);
+ assert(&r == &x);
+ }
+ {
+ // Move only
+ std::function_ref<MoveOnly(MoveOnly) const> f(
+ std::constant_arg<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+ std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+ assert(r.get() == 6);
+ }
+ {
+ // different return type
+ std::function_ref<int(int) const> f(std::constant_arg<[](int x) -> double { return x + 0.1; }>);
+ std::same_as<int> decltype(auto) r = f(5);
+ assert(r == 5);
+ }
+ {
+ // Args overload resolution
+ S s;
+ int x = 1;
+ std::function_ref<int(int&) const> f(s);
+ assert(f(x) == 43);
+
+ std::function_ref<int(const int&) const> g(s);
+ assert(g(x) == 44);
+
+ std::function_ref<int(int&&) const> h(s);
+ assert(h(std::move(x)) == 45);
+
+ std::function_ref<int(const int&&) const> i(s);
+ assert(i(std::move(x)) == 46);
+ }
+ {
+ // const overload
+ S2 s;
+ std::function_ref<double(int, int, int) const> f(s);
+ std::same_as<double> decltype(auto) r = f(1, 2, 3);
+ assert(r == 7.0);
+
+ std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+ assert(r2 == 7.0);
+
+ const S2 s2;
+ std::function_ref<double(int, int, int) const> f2(s2);
+ std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+ assert(r3 == 7.0);
+
+ std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+ assert(r4 == 7.0);
+ }
+}
+
+void test_noexcept() {
+ {
+ std::function_ref<void() noexcept> f = [] noexcept {};
+ f();
+ static_assert(std::is_void_v<decltype(f())>);
+ }
+ {
+ // reference
+ int x = 42;
+ std::function_ref<int&(int&) noexcept> f(std::constant_arg<[](int& x) noexcept -> int& { return x; }>);
+ std::same_as<int&> decltype(auto) r = f(x);
+ assert(&r == &x);
+ }
+ {
+ // Move only
+ std::function_ref<MoveOnly(MoveOnly) noexcept> f(
+ std::constant_arg<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
+ std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+ assert(r.get() == 6);
+ }
+ {
+ // different return type
+ std::function_ref<int(int) noexcept> f(std::constant_arg<[](int x) noexcept -> double { return x + 0.1; }>);
+ std::same_as<int> decltype(auto) r = f(5);
+ assert(r == 5);
+ }
+ {
+ // Args overload resolution
+ S s;
+ int x = 1;
+ std::function_ref<int(int&) noexcept> f(s);
+ assert(f(x) == 43);
+
+ std::function_ref<int(const int&) noexcept> g(s);
+ assert(g(x) == 44);
+
+ std::function_ref<int(int&&) noexcept> h(s);
+ assert(h(std::move(x)) == 45);
+
+ std::function_ref<int(const int&&) noexcept> i(s);
+ assert(i(std::move(x)) == 46);
+ }
+ {
+ // const overload
+ S2 s;
+ std::function_ref<double(int, int, int) noexcept> f(s);
+ std::same_as<double> decltype(auto) r = f(1, 2, 3);
+ assert(r == 6.0);
+
+ std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+ assert(r2 == 6.0);
+
+ const S2 s2;
+ std::function_ref<double(int, int, int) noexcept> f2(s2);
+ std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+ assert(r3 == 7.0);
+
+ std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+ assert(r4 == 7.0);
+ }
+}
+
+void test_const_noexcept() {
+ {
+ std::function_ref<void() const noexcept> f = [] noexcept {};
+ f();
+ static_assert(std::is_void_v<decltype(f())>);
+ }
+ {
+ // reference
+ int x = 42;
+ std::function_ref<int&(int&) const noexcept> f(std::constant_arg<[](int& x) noexcept -> int& { return x; }>);
+ std::same_as<int&> decltype(auto) r = f(x);
+ assert(&r == &x);
+ }
+ {
+ // Move only
+ std::function_ref<MoveOnly(MoveOnly) const noexcept> f(
+ std::constant_arg<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
+ std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+ assert(r.get() == 6);
+ }
+ {
+ // different return type
+ std::function_ref<int(int) const noexcept> f(std::constant_arg<[](int x) noexcept -> double { return x + 0.1; }>);
+ std::same_as<int> decltype(auto) r = f(5);
+ assert(r == 5);
+ }
+ {
+ // Args overload resolution
+ S s;
+ int x = 1;
+ std::function_ref<int(int&) const noexcept> f(s);
+ assert(f(x) == 43);
+
+ std::function_ref<int(const int&) const noexcept> g(s);
+ assert(g(x) == 44);
+
+ std::function_ref<int(int&&) const noexcept> h(s);
+ assert(h(std::move(x)) == 45);
+
+ std::function_ref<int(const int&&) const noexcept> i(s);
+ assert(i(std::move(x)) == 46);
+ }
+ {
+ // const overload
+ S2 s;
+ std::function_ref<double(int, int, int) const noexcept> f(s);
+ std::same_as<double> decltype(auto) r = f(1, 2, 3);
+ assert(r == 7.0);
+
+ std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+ assert(r2 == 7.0);
+
+ const S2 s2;
+ std::function_ref<double(int, int, int) const noexcept> f2(s2);
+ std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+ assert(r3 == 7.0);
+
+ std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+ assert(r4 == 7.0);
+ }
+}
+
+void test() {
+ test_default();
+ test_const();
+ test_noexcept();
+ test_const_noexcept();
+}
+
+int main(int, char**) {
+ test();
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp
deleted file mode 100644
index 26a32e91adfd7..0000000000000
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-#include "test_macros.h"
-
-static_assert(std::is_invocable_v<std::function_ref<void() const>&>);
-static_assert(std::is_invocable_v<std::function_ref<void() const>>);
-static_assert(std::is_invocable_v<std::function_ref<void() const>&&>);
-static_assert(std::is_invocable_v<std::function_ref<void() const> const&>);
-static_assert(std::is_invocable_v<std::function_ref<void() const> const>);
-static_assert(std::is_invocable_v<std::function_ref<void() const> const&&>);
-
-int fn() { return 42; }
-
-struct {
- int operator()() const { return 42; }
-} fn_obj;
-
-void test() {
- // template<class F> function_ref(F* f) noexcept;
- {
- // initialized from a function
- std::function_ref<int() const> fn_ref = fn;
- assert(fn_ref() == 42);
- }
- {
- // initialized from a function pointer
- std::function_ref<int() const> fn_ref = &fn;
- assert(fn_ref() == 42);
- }
-
- // template<class F> constexpr function_ref(F&& f) noexcept;
- {
- // initialized from a function object
- std::function_ref<int() const> fn_ref = fn_obj;
- assert(fn_ref() == 42);
- }
-}
-
-struct S {
- int data_mem = 42;
-
- int fn_mem() const { return 42; }
-};
-
-void test_constant_arg_t() {
- // template<auto f> constexpr function_ref(constant_arg_t<f>) noexcept;
- {
- // initialized from a function through `constant_arg_t`
- std::function_ref<int() const> fn_ref = std::constant_arg_t<fn>();
- assert(fn_ref() == 42);
- }
- {
- // initialized from a function pointer through `constant_arg_t`
- std::function_ref<int() const> fn_ref = std::constant_arg_t<&fn>();
- assert(fn_ref() == 42);
- }
- {
- // initialized from a function object through `constant_arg_t`
- std::function_ref<int() const> fn_ref = std::constant_arg_t<fn_obj>();
- assert(fn_ref() == 42);
- }
- {
- S s;
- // initialized from a pointer to data member through `constant_arg_t`
- std::function_ref<int(S) const> fn_ref = std::constant_arg_t<&S::data_mem>();
- assert(fn_ref(s) == 42);
- }
- {
- S s;
- // initialized from a pointer to function member through `constant_arg_t`
- std::function_ref<int(S) const> fn_ref = std::constant_arg_t<&S::fn_mem>();
- assert(fn_ref(s) == 42);
- }
-
- // template<auto f, class U>
- // constexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
- {
- S s;
- // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a reference
- std::function_ref<int() const> fn_ref = {std::constant_arg_t<&S::data_mem>(), s};
- assert(fn_ref() == 42);
- }
- {
- S s;
- // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a reference
- std::function_ref<int() const> fn_ref = {std::constant_arg_t<&S::fn_mem>(), s};
- assert(fn_ref() == 42);
- }
-
- // template<auto f, class T>
- // constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
- {
- S s;
- // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a pointer
- std::function_ref<int() const> fn_ref = {std::constant_arg_t<&S::data_mem>(), &s};
- assert(fn_ref() == 42);
- }
- {
- S s;
- // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a pointer
- static_assert(std::is_same_v<decltype(&s), S*>);
- std::function_ref<int() const> fn_ref = {std::constant_arg_t<&S::fn_mem>(), &s};
- assert(fn_ref() == 42);
- }
-}
-
-int main(int, char**) {
- test();
- test_constant_arg_t();
- return 0;
-}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp
deleted file mode 100644
index 26ace9c496b88..0000000000000
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-#include "test_macros.h"
-
-static_assert(std::is_invocable_v<std::function_ref<void() const noexcept>&>);
-static_assert(std::is_invocable_v<std::function_ref<void() const noexcept>>);
-static_assert(std::is_invocable_v<std::function_ref<void() const noexcept>&&>);
-static_assert(std::is_invocable_v<std::function_ref<void() const noexcept> const&>);
-static_assert(std::is_invocable_v<std::function_ref<void() const noexcept> const >);
-static_assert(std::is_invocable_v<std::function_ref<void() const noexcept> const&&>);
-
-int fn() noexcept { return 42; }
-
-struct {
- int operator()() const noexcept { return 42; }
-} fn_obj;
-
-void test() {
- // template<class F> function_ref(F* f) noexcept;
- {
- // initialized from a function
- std::function_ref<int() const noexcept> fn_ref = fn;
- assert(fn_ref() == 42);
- }
- {
- // initialized from a function pointer
- std::function_ref<int() const noexcept> fn_ref = &fn;
- assert(fn_ref() == 42);
- }
-
- // template<class F> const noexceptexpr function_ref(F&& f) noexcept;
- {
- // initialized from a function object
- std::function_ref<int() const noexcept> fn_ref = fn_obj;
- assert(fn_ref() == 42);
- }
-}
-
-struct S {
- int data_mem = 42;
-
- int fn_mem() const noexcept { return 42; }
-};
-
-void test_constant_arg_t() {
- // template<auto f> const noexceptexpr function_ref(constant_arg_t<f>) noexcept;
- {
- // initialized from a function through `constant_arg_t`
- std::function_ref<int() const noexcept> fn_ref = std::constant_arg_t<fn>();
- assert(fn_ref() == 42);
- }
- {
- // initialized from a function pointer through `constant_arg_t`
- std::function_ref<int() const noexcept> fn_ref = std::constant_arg_t<&fn>();
- assert(fn_ref() == 42);
- }
- {
- // initialized from a function object through `constant_arg_t`
- std::function_ref<int() const noexcept> fn_ref = std::constant_arg_t<fn_obj>();
- assert(fn_ref() == 42);
- }
- {
- S s;
- // initialized from a pointer to data member through `constant_arg_t`
- std::function_ref<int(S) const noexcept> fn_ref = std::constant_arg_t<&S::data_mem>();
- assert(fn_ref(s) == 42);
- }
- {
- S s;
- // initialized from a pointer to function member through `constant_arg_t`
- std::function_ref<int(S) const noexcept> fn_ref = std::constant_arg_t<&S::fn_mem>();
- assert(fn_ref(s) == 42);
- }
-
- // template<auto f, class U>
- // const noexceptexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
- {
- S s;
- // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a reference
- std::function_ref<int() const noexcept> fn_ref = {std::constant_arg_t<&S::data_mem>(), s};
- assert(fn_ref() == 42);
- }
- {
- S s;
- // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a reference
- std::function_ref<int() const noexcept> fn_ref = {std::constant_arg_t<&S::fn_mem>(), s};
- assert(fn_ref() == 42);
- }
-
- // template<auto f, class T>
- // const noexceptexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
- {
- S s;
- // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a pointer
- std::function_ref<int() const noexcept> fn_ref = {std::constant_arg_t<&S::data_mem>(), &s};
- assert(fn_ref() == 42);
- }
- {
- S s;
- // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a pointer
- static_assert(std::is_same_v<decltype(&s), S*>);
- std::function_ref<int() const noexcept> fn_ref = {std::constant_arg_t<&S::fn_mem>(), &s};
- assert(fn_ref() == 42);
- }
-}
-
-int main(int, char**) {
- test();
- test_constant_arg_t();
- return 0;
-}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
deleted file mode 100644
index e73e778313dec..0000000000000
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++26
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-#include "test_macros.h"
-
-static_assert(std::is_invocable_v<std::function_ref<void()>&>);
-static_assert(std::is_invocable_v<std::function_ref<void()>>);
-static_assert(std::is_invocable_v<std::function_ref<void()>&&>);
-static_assert(std::is_invocable_v<std::function_ref<void()> const&>);
-static_assert(std::is_invocable_v<std::function_ref<void()> const>);
-static_assert(std::is_invocable_v<std::function_ref<void()> const&&>);
-
-int fn() { return 42; }
-
-struct {
- int operator()() { return 42; }
-} fn_obj;
-
-void test() {
- // template<class F> function_ref(F* f) noexcept;
- {
- // initialized from a function
- std::function_ref<int()> fn_ref = fn;
- assert(fn_ref() == 42);
- }
- {
- // initialized from a function pointer
- std::function_ref<int()> fn_ref = &fn;
- assert(fn_ref() == 42);
- }
-
- // template<class F> constexpr function_ref(F&& f) noexcept;
- {
- // initialized from a function object
- std::function_ref<int()> fn_ref = fn_obj;
- assert(fn_ref() == 42);
- }
-}
-
-struct S {
- int data_mem = 42;
-
- int fn_mem() { return 42; }
-};
-
-void test_constant_arg_t() {
- // template<auto f> constexpr function_ref(constant_arg_t<f>) noexcept;
- {
- // initialized from a function through `constant_arg_t`
- std::function_ref<int()> fn_ref = std::constant_arg_t<fn>();
- assert(fn_ref() == 42);
- }
- {
- // initialized from a function pointer through `constant_arg_t`
- std::function_ref<int()> fn_ref = std::constant_arg_t<&fn>();
- assert(fn_ref() == 42);
- }
- {
- S s;
- // initialized from a pointer to data member through `constant_arg_t`
- std::function_ref<int(S)> fn_ref = std::constant_arg_t<&S::data_mem>();
- assert(fn_ref(s) == 42);
- }
- {
- S s;
- // initialized from a pointer to function member through `constant_arg_t`
- std::function_ref<int(S)> fn_ref = std::constant_arg_t<&S::fn_mem>();
- assert(fn_ref(s) == 42);
- }
-
- // template<auto f, class U>
- // constexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
- {
- S s;
- // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a reference
- std::function_ref<int()> fn_ref = {std::constant_arg_t<&S::data_mem>(), s};
- assert(fn_ref() == 42);
- }
- {
- S s;
- // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a reference
- std::function_ref<int()> fn_ref = {std::constant_arg_t<&S::fn_mem>(), s};
- assert(fn_ref() == 42);
- }
-
- // template<auto f, class T>
- // constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
- {
- S s;
- // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a pointer
- std::function_ref<int()> fn_ref = {std::constant_arg_t<&S::data_mem>(), &s};
- assert(fn_ref() == 42);
- }
- {
- S s;
- // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a pointer
- static_assert(std::is_same_v<decltype(&s), S*>);
- std::function_ref<int()> fn_ref = {std::constant_arg_t<&S::fn_mem>(), &s};
- assert(fn_ref() == 42);
- }
-}
-
-int main(int, char**) {
- test();
- test_constant_arg_t();
- return 0;
-}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp
deleted file mode 100644
index 7c947143ec0bd..0000000000000
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-#include "test_macros.h"
-
-static_assert(std::is_invocable_v<std::function_ref<void() noexcept>&>);
-static_assert(std::is_invocable_v<std::function_ref<void() noexcept>>);
-static_assert(std::is_invocable_v<std::function_ref<void() noexcept>&&>);
-static_assert(std::is_invocable_v<std::function_ref<void() noexcept> const&>);
-static_assert(std::is_invocable_v<std::function_ref<void() noexcept> const>);
-static_assert(std::is_invocable_v<std::function_ref<void() noexcept> const&&>);
-
-int fn() noexcept { return 42; }
-
-struct {
- int operator()() noexcept { return 42; }
-} fn_obj;
-
-void test() {
- // template<class F> function_ref(F* f) noexcept;
- {
- // initialized from a function
- std::function_ref<int() noexcept> fn_ref = fn;
- assert(fn_ref() == 42);
- }
- {
- // initialized from a function pointer
- std::function_ref<int() noexcept> fn_ref = &fn;
- assert(fn_ref() == 42);
- }
-
- // template<class F> constexpr function_ref(F&& f) noexcept;
- {
- // initialized from a function object
- std::function_ref<int() noexcept> fn_ref = fn_obj;
- assert(fn_ref() == 42);
- }
-}
-
-struct S {
- int data_mem = 42;
-
- int fn_mem() noexcept { return 42; }
-};
-
-void test_constant_arg_t() {
- // template<auto f> constexpr function_ref(constant_arg_t<f>) noexcept;
- {
- // initialized from a function through `constant_arg_t`
- std::function_ref<int() noexcept> fn_ref = std::constant_arg_t<fn>();
- assert(fn_ref() == 42);
- }
- {
- // initialized from a function pointer through `constant_arg_t`
- std::function_ref<int() noexcept> fn_ref = std::constant_arg_t<&fn>();
- assert(fn_ref() == 42);
- }
- {
- S s;
- // initialized from a pointer to data member through `constant_arg_t`
- std::function_ref<int(S) noexcept> fn_ref = std::constant_arg_t<&S::data_mem>();
- assert(fn_ref(s) == 42);
- }
- {
- S s;
- // initialized from a pointer to function member through `constant_arg_t`
- std::function_ref<int(S) noexcept> fn_ref = std::constant_arg_t<&S::fn_mem>();
- assert(fn_ref(s) == 42);
- }
-
- // template<auto f, class U>
- // constexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
- {
- S s;
- // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a reference
- std::function_ref<int() noexcept> fn_ref = {std::constant_arg_t<&S::data_mem>(), s};
- assert(fn_ref() == 42);
- }
- {
- S s;
- // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a reference
- std::function_ref<int() noexcept> fn_ref = {std::constant_arg_t<&S::fn_mem>(), s};
- assert(fn_ref() == 42);
- }
-
- // template<auto f, class T>
- // constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
- {
- S s;
- // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a pointer
- std::function_ref<int() noexcept> fn_ref = {std::constant_arg_t<&S::data_mem>(), &s};
- assert(fn_ref() == 42);
- }
- {
- S s;
- // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a pointer
- static_assert(std::is_same_v<decltype(&s), S*>);
- std::function_ref<int() noexcept> fn_ref = {std::constant_arg_t<&S::fn_mem>(), &s};
- assert(fn_ref() == 42);
- }
-}
-
-int main(int, char**) {
- test();
- test_constant_arg_t();
- return 0;
-}
>From e77290bdbf84bfeaa71d48f67e0d2a8d837760c0 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 15 Mar 2026 14:45:09 +0000
Subject: [PATCH 20/22] format
---
libcxx/include/__functional/function_ref.h | 13 ++++++-------
libcxx/include/__functional/function_ref_impl.h | 3 ++-
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
index 864e921e37824..b7fd3a4a2c79f 100644
--- a/libcxx/include/__functional/function_ref.h
+++ b/libcxx/include/__functional/function_ref.h
@@ -19,14 +19,13 @@
#if _LIBCPP_STD_VER >= 26
// NOLINTBEGIN(readability-duplicate-include)
-#define _LIBCPP_FUNCTION_REF_CV
-#include <__functional/function_ref_impl.h>
-#undef _LIBCPP_FUNCTION_REF_CV
+# define _LIBCPP_FUNCTION_REF_CV
+# include <__functional/function_ref_impl.h>
+# undef _LIBCPP_FUNCTION_REF_CV
-
-#define _LIBCPP_FUNCTION_REF_CV const
-#include <__functional/function_ref_impl.h>
-#undef _LIBCPP_FUNCTION_REF_CV
+# define _LIBCPP_FUNCTION_REF_CV const
+# include <__functional/function_ref_impl.h>
+# undef _LIBCPP_FUNCTION_REF_CV
// NOLINTEND(readability-duplicate-include)
#endif // _LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index f0c7c40765013..97bc18baae3ee 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -54,7 +54,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
// use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
// pointers
- // todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be supported
+ // todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
+ // supported
union __storage_t {
void* __obj_ptr_;
void const* __obj_const_ptr_;
>From 557e4dce25c16970bc4f7f6d3464af64dc048524 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 15 Mar 2026 18:42:30 +0000
Subject: [PATCH 21/22] constexpr function ptr
---
.../include/__functional/function_ref_impl.h | 32 +++++++++++++++++--
.../func.wrap.ref.ctor/function_ptr.pass.cpp | 17 +++++++---
2 files changed, 41 insertions(+), 8 deletions(-)
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 97bc18baae3ee..d6a963644926f 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -59,6 +59,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
union __storage_t {
void* __obj_ptr_;
void const* __obj_const_ptr_;
+ _Rp (*__fn_ptr_)(_ArgTypes...);
+ _Rp (*__fn_ptr_noexcept_)(_ArgTypes...) noexcept;
_LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t() noexcept : __obj_ptr_(nullptr) {}
@@ -72,7 +74,19 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
}
} else {
static_assert(is_function_v<_Tp>);
- __obj_ptr_ = reinterpret_cast<void*>(__ptr);
+ if constexpr (is_nothrow_invocable_v<_Tp, _ArgTypes...>) {
+ if constexpr (requires { __fn_ptr_noexcept_ = __ptr; }) {
+ __fn_ptr_noexcept_ = __ptr;
+ } else {
+ __obj_ptr_ = reinterpret_cast<void*>(__ptr);
+ }
+ } else {
+ if constexpr (requires { __fn_ptr_ = __ptr; }) {
+ __fn_ptr_ = __ptr;
+ } else {
+ __obj_ptr_ = reinterpret_cast<void*>(__ptr);
+ }
+ }
}
}
};
@@ -87,7 +101,19 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
}
} else {
static_assert(is_function_v<_Tp>);
- return reinterpret_cast<_Tp*>(__storage.__obj_ptr_);
+ if constexpr (is_nothrow_invocable_v<_Tp, _ArgTypes...>) {
+ if constexpr (requires(_Tp* __ptr) { __storage.__fn_ptr_noexcept_ = __ptr; }) {
+ return __storage.__fn_ptr_noexcept_;
+ } else {
+ return reinterpret_cast<_Tp*>(__storage.__obj_ptr_);
+ }
+ } else {
+ if constexpr (requires(_Tp* __ptr) { __storage.__fn_ptr_ = __ptr; }) {
+ return __storage.__fn_ptr_;
+ } else {
+ return reinterpret_cast<_Tp*>(__storage.__obj_ptr_);
+ }
+ }
}
}
@@ -99,7 +125,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
public:
template <class _Fp>
requires is_function_v<_Fp> && __is_invocable_using<_Fp>
- _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
+ _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fp* __fn_ptr) noexcept
: __storage_(__fn_ptr),
__call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
return __get<_Fp>(__storage)(std::forward<_ArgTypes>(__args)...);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
index 22835c48ebcf9..3c7053709458f 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -76,18 +76,18 @@ static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (
static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
void (*)(int, double) noexcept>);
-int fn() { return 42; }
+constexpr int fn() { return 42; }
-int fn_noexcept(int i, A a) noexcept { return i + a.i; }
+constexpr int fn_noexcept(int i, A a) noexcept { return i + a.i; }
struct Int {
int i;
- Int(int ii) noexcept : i(ii) {}
+ constexpr Int(int ii) noexcept : i(ii) {}
};
-int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
-void test() {
+constexpr bool test() {
{
// simple case
std::function_ref<int()> f(&fn);
@@ -118,6 +118,11 @@ void test() {
std::function_ref<int(int, A) const noexcept> f(&fn_noexcept);
assert(f(4, A{5}) == 9);
}
+
+ return true;
+}
+
+void non_constexpr_test() {
{
std::function_ref<Int(int, int, int)> f(&needs_conversion);
assert(f(1, 2, 3).i == 6);
@@ -135,5 +140,7 @@ void test() {
int main(int, char**) {
test();
+ non_constexpr_test();
+ static_assert(test());
return 0;
}
>From d82c8f79388576e701b56fbd4a9bcb1170032cd7 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Tue, 24 Mar 2026 07:31:20 +0000
Subject: [PATCH 22/22] double unwrapping
---
.../__functional/function_ref_common.h | 73 ++++++++++++++
.../include/__functional/function_ref_impl.h | 94 +++++--------------
.../func.wrap.ref.ctor/assign.delete.pass.cpp | 16 ++--
.../func.wrap.ref.ctor/function_ptr.pass.cpp | 9 ++
.../func.wrap.ref.ctor/ref.pass.cpp | 37 ++++++++
5 files changed, 152 insertions(+), 77 deletions(-)
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index a757d0d308bc5..0b1aa207ca589 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -11,6 +11,7 @@
#include <__config>
#include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
#include <__type_traits/is_function.h>
#include <__type_traits/is_object.h>
#include <__type_traits/remove_pointer.h>
@@ -27,6 +28,78 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <class...>
class function_ref;
+template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization : false_type {};
+
+ // use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
+ // pointers
+ // todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
+ // supported
+ template <class _Rp, class... _ArgTypes>
+ union __storage_func_ref_t {
+ void* __obj_ptr_;
+ void const* __obj_const_ptr_;
+ _Rp (*__fn_ptr_)(_ArgTypes...);
+ _Rp (*__fn_ptr_noexcept_)(_ArgTypes...) noexcept;
+ void(* __fn_ptr_type_erased_)();
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t() noexcept : __obj_ptr_(nullptr) {}
+
+ template <class _Tp>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t(_Tp* __ptr) noexcept {
+ if constexpr (is_object_v<_Tp>) {
+ if constexpr (is_const_v<_Tp>) {
+ __obj_const_ptr_ = __ptr;
+ } else {
+ __obj_ptr_ = __ptr;
+ }
+ } else {
+ static_assert(is_function_v<_Tp>);
+ if constexpr (is_nothrow_invocable_v<_Tp, _ArgTypes...>) {
+ if constexpr (requires { __fn_ptr_noexcept_ = __ptr; }) {
+ __fn_ptr_noexcept_ = __ptr;
+ } else {
+ __fn_ptr_type_erased_ = reinterpret_cast<void(*)()>(__ptr);
+ }
+ } else {
+ if constexpr (requires { __fn_ptr_ = __ptr; }) {
+ __fn_ptr_ = __ptr;
+ } else {
+ __fn_ptr_type_erased_ = reinterpret_cast<void(*)()>(__ptr);
+ }
+ }
+ }
+ }
+
+ template <class _Tp>
+ _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_func_ref_t __storage) {
+ if constexpr (is_object_v<_Tp>) {
+ if constexpr (is_const_v<_Tp>) {
+ return static_cast<_Tp*>(__storage.__obj_const_ptr_);
+ } else {
+ return static_cast<_Tp*>(__storage.__obj_ptr_);
+ }
+ } else {
+ static_assert(is_function_v<_Tp>);
+ if constexpr (is_nothrow_invocable_v<_Tp, _ArgTypes...>) {
+ if constexpr (requires(_Tp* __ptr) { __storage.__fn_ptr_noexcept_ = __ptr; }) {
+ return __storage.__fn_ptr_noexcept_;
+ } else {
+ return reinterpret_cast<_Tp*>(__storage.__fn_ptr_type_erased_);
+ }
+ } else {
+ if constexpr (requires(_Tp* __ptr) { __storage.__fn_ptr_ = __ptr; }) {
+ return __storage.__fn_ptr_;
+ } else {
+ return reinterpret_cast<_Tp*>(__storage.__fn_ptr_type_erased_);
+ }
+ }
+ }
+ }
+ };
+
+
+
template <class _Fp, class _Tp>
struct __function_ref_bind {};
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index d6a963644926f..78c5b6a357357 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -9,6 +9,7 @@
// This header is unguarded on purpose. This header is an implementation detail of function_ref.h
// and generates multiple versions of std::function_ref
+#include "__functional/function_ref_common.h"
#include <__assert>
#include <__config>
#include <__functional/invoke.h>
@@ -28,6 +29,7 @@
#include <__type_traits/remove_reference.h>
#include <__utility/constant_arg.h>
#include <__utility/forward.h>
+#include <type_traits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -44,6 +46,10 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <class...>
class function_ref;
+template <bool _NoExcept2, bool _NoExcept1, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization<function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept2)>, _NoExcept1, _Rp, _ArgTypes...>
+ : is_convertible<_Rp(&)(_ArgTypes...) noexcept(_NoExcept2), _Rp(&)(_ArgTypes...) noexcept(_NoExcept1)> {};
+
template <class _Rp, class... _ArgTypes, bool __is_noexcept>
class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
private:
@@ -52,70 +58,13 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
_If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
value;
- // use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
- // pointers
- // todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
- // supported
- union __storage_t {
- void* __obj_ptr_;
- void const* __obj_const_ptr_;
- _Rp (*__fn_ptr_)(_ArgTypes...);
- _Rp (*__fn_ptr_noexcept_)(_ArgTypes...) noexcept;
-
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t() noexcept : __obj_ptr_(nullptr) {}
-
- template <class _Tp>
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t(_Tp* __ptr) noexcept {
- if constexpr (is_object_v<_Tp>) {
- if constexpr (is_const_v<_Tp>) {
- __obj_const_ptr_ = __ptr;
- } else {
- __obj_ptr_ = __ptr;
- }
- } else {
- static_assert(is_function_v<_Tp>);
- if constexpr (is_nothrow_invocable_v<_Tp, _ArgTypes...>) {
- if constexpr (requires { __fn_ptr_noexcept_ = __ptr; }) {
- __fn_ptr_noexcept_ = __ptr;
- } else {
- __obj_ptr_ = reinterpret_cast<void*>(__ptr);
- }
- } else {
- if constexpr (requires { __fn_ptr_ = __ptr; }) {
- __fn_ptr_ = __ptr;
- } else {
- __obj_ptr_ = reinterpret_cast<void*>(__ptr);
- }
- }
- }
- }
- };
+ template <class _Fn>
+ static constexpr bool __is_convertible_from_specialization_v = __is_convertible_from_specialization<_Fn, __is_noexcept, _Rp, _ArgTypes...>::value;
- template <class _Tp>
- _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_t __storage) {
- if constexpr (is_object_v<_Tp>) {
- if constexpr (is_const_v<_Tp>) {
- return static_cast<_Tp*>(__storage.__obj_const_ptr_);
- } else {
- return static_cast<_Tp*>(__storage.__obj_ptr_);
- }
- } else {
- static_assert(is_function_v<_Tp>);
- if constexpr (is_nothrow_invocable_v<_Tp, _ArgTypes...>) {
- if constexpr (requires(_Tp* __ptr) { __storage.__fn_ptr_noexcept_ = __ptr; }) {
- return __storage.__fn_ptr_noexcept_;
- } else {
- return reinterpret_cast<_Tp*>(__storage.__obj_ptr_);
- }
- } else {
- if constexpr (requires(_Tp* __ptr) { __storage.__fn_ptr_ = __ptr; }) {
- return __storage.__fn_ptr_;
- } else {
- return reinterpret_cast<_Tp*>(__storage.__obj_ptr_);
- }
- }
- }
- }
+ template<class... _Tp>
+ friend class function_ref;
+
+ using __storage_t _LIBCPP_NODEBUG = __storage_func_ref_t<_Rp, _ArgTypes...>;
__storage_t __storage_;
@@ -128,21 +77,28 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
_LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fp* __fn_ptr) noexcept
: __storage_(__fn_ptr),
__call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
- return __get<_Fp>(__storage)(std::forward<_ArgTypes>(__args)...);
+ return __storage_t::template __get<_Fp>(__storage)(std::forward<_ArgTypes>(__args)...);
}) {
_LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
}
template <class _Fn, class _Tp = remove_reference_t<_Fn>>
requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
- __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&>)
+ __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
_LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
: __storage_(std::addressof(__obj)),
__call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
- _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
+ _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__storage_t::template __get<_Tp>(__storage);
return __obj(std::forward<_ArgTypes>(__args)...);
}) {}
+ template <class _Fn, class _Tp = remove_reference_t<_Fn>>
+ requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
+ __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && __is_convertible_from_specialization_v<_Tp>)
+ _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
+ : __storage_(__obj.__storage_),
+ __call_(__obj.__call_) {}
+
template <auto _Fn>
requires __is_invocable_using<const decltype(_Fn)&>
_LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>) noexcept
@@ -159,7 +115,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
_LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _Up&& __obj) noexcept
: __storage_(std::addressof(__obj)),
__call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
- _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
+ _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__storage_t::template __get<_Tp>(__storage);
return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
}) {
if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
@@ -172,7 +128,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
_LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr_) noexcept
: __storage_(__obj_ptr_),
__call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
- auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
+ auto __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
}) {
if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
@@ -189,7 +145,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
_LIBCPP_HIDE_FROM_ABI constexpr function_ref& operator=(const function_ref&) noexcept = default;
template <class _Tp>
- requires(!is_same_v<_Tp, function_ref>) && (!is_pointer_v<_Tp>) && (!__is_constant_arg_t<_Tp>)
+ requires(!__is_convertible_from_specialization_v<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_constant_arg_t<_Tp>)
_LIBCPP_HIDE_FROM_ABI function_ref& operator=(_Tp) = delete;
_LIBCPP_HIDE_FROM_ABI constexpr _Rp operator()(_ArgTypes... __args) const noexcept(__is_noexcept) {
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
index 956b34695f71a..81d1e79a5499f 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -24,9 +24,9 @@
// non const noexcept(false)
static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void()>>);
-static_assert(!std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const>>);
-static_assert(!std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() noexcept>>);
-static_assert(!std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const noexcept>>);
static_assert(std::is_assignable_v<std::function_ref<void()>, void (*)()>);
static_assert(!std::is_assignable_v<std::function_ref<void()>, void (*)(int)>);
@@ -35,10 +35,10 @@ static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_arg_
static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_arg_t<[](int) {}>>);
// const noexcept(false)
-static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const>>);
-static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
-static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
static_assert(std::is_assignable_v<std::function_ref<void() const>, void (*)()>);
static_assert(!std::is_assignable_v<std::function_ref<void() const>, void (*)(int)>);
@@ -50,7 +50,7 @@ static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::consta
static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void()>>);
static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const>>);
static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() noexcept>>);
-static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const noexcept>>);
static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, void (*)() noexcept>);
static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
@@ -61,7 +61,7 @@ static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::con
// const noexcept(true)
static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void()>>);
static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const>>);
-static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const noexcept>>);
static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)() noexcept>);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
index 3c7053709458f..f28f26f707d3b 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -78,8 +78,11 @@ static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double
constexpr int fn() { return 42; }
+constexpr int fn_maythrow(int i, A a) { return i - a.i; }
constexpr int fn_noexcept(int i, A a) noexcept { return i + a.i; }
+constexpr void foo(int) {}
+constexpr void bar(int) noexcept {}
struct Int {
int i;
constexpr Int(int ii) noexcept : i(ii) {}
@@ -135,6 +138,12 @@ void non_constexpr_test() {
std::function_ref<Int(int, int, int) const noexcept> f4(&needs_conversion);
assert(f4(1, 2, 3).i == 6);
+
+ {
+ std::function_ref r1 = foo;
+ std::function_ref r2 = bar;
+ r1 = r2; // ok
+ }
}
}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index f0f6411521f6d..abf118cb66a30 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -155,6 +155,12 @@ struct NeedsConversion {
constexpr int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
};
+constexpr int fn() { return 5; }
+constexpr int fn_noexcept() noexcept { return 6; }
+
+constexpr int one() noexcept { return 1; }
+constexpr int two() noexcept { return 2; }
+
constexpr bool test() {
{
std::function_ref<void()> f(l1);
@@ -218,6 +224,37 @@ constexpr bool test() {
assert(f4(1, 2, 3).i == 6);
}
+ {
+ // noexcept conversion
+ std::function_ref<int() noexcept> f1(&fn_noexcept);
+ std::function_ref<int()> f2(f1);
+ assert(f2() == 6);
+ }
+
+ {
+ // const conversion
+ std::function_ref<int() const> f1(&fn_noexcept);
+ std::function_ref<int()> f2(f1);
+ assert(f2() == 6);
+ }
+
+ {
+ // const noexcept conversion
+ std::function_ref<int() const noexcept> f1(&fn_noexcept);
+ std::function_ref<int()> f2(f1);
+ assert(f2() == 6);
+ }
+
+ {
+ // double unwrapping
+ std::function_ref<int() const noexcept> f1(&one);
+ std::function_ref<int()> f2(f1);
+
+ f1 = &two;
+ assert(f2() == 1 || f2() == 2);
+ LIBCPP_ASSERT(f2() == 1);
+ }
+
return true;
}
More information about the libcxx-commits
mailing list