aboutsummaryrefslogtreecommitdiff
path: root/libstdc++-v3/testsuite
diff options
context:
space:
mode:
authorTomasz Kamiński <tkaminsk@redhat.com>2025-05-14 12:04:24 +0200
committerTomasz Kamiński <tkaminsk@redhat.com>2025-05-26 11:25:28 +0200
commit545433e9bd32e965726956cb238d53b39844b85c (patch)
tree5ea55e17995e52322eda2fbf682464147ad08ba8 /libstdc++-v3/testsuite
parentc61743a186cfecd646334817f356f799502ee71a (diff)
downloadgcc-545433e9bd32e965726956cb238d53b39844b85c.zip
gcc-545433e9bd32e965726956cb238d53b39844b85c.tar.gz
gcc-545433e9bd32e965726956cb238d53b39844b85c.tar.bz2
libstdc++: Implement C++26 function_ref [PR119126]
This patch implements C++26 function_ref as specified in P0792R14, with correction for constraints for constructor accepting nontype_t parameter from LWG 4256. As function_ref may store a pointer to the const object, __Ptrs::_M_obj is changed to const void*, so again we do not cast away const from const objects. To help with necessary casts, a __polyfunc::__cast_to helper is added, that accepts reference to or target type direclty. The _Invoker now defines additional call methods used by function_ref: _S_ptrs() for invoking target passed by reference, and __S_nttp, _S_bind_ptr, _S_bind_ref for handling constructors accepting nontype_t. The existing _S_call_storage is changed to thin wrapper, that initialies _Ptrs, and forwards to _S_call_ptrs. This reduced the most uses of _Storage::_M_ptr and _Storage::_M_ref, so this functions was removed, and _Manager uses were adjusted. Finally we make function_ref available in freestanding mode, as move_only_function and copyable_function are currently only available in hosted, so we define _Manager and _Mo_base only if either __glibcxx_move_only_function or __glibcxx_copyable_function is defined. PR libstdc++/119126 libstdc++-v3/ChangeLog: * doc/doxygen/stdheader.cc: Added funcref_impl.h file. * include/Makefile.am: Added funcref_impl.h file. * include/Makefile.in: Added funcref_impl.h file. * include/bits/funcref_impl.h: New file. * include/bits/funcwrap.h: (_Ptrs::_M_obj): Const-qualify. (_Storage::_M_ptr, _Storage::_M_ref): Remove. (__polyfunc::__cast_to) Define. (_Base_invoker::_S_ptrs, _Base_invoker::_S_nttp) (_Base_invoker::_S_bind_ptrs, _Base_invoker::_S_bind_ref) (_Base_invoker::_S_call_ptrs): Define. (_Base_invoker::_S_call_storage): Foward to _S_call_ptrs. (_Manager::_S_local, _Manager::_S_ptr): Adjust for _M_obj being const qualified. (__polyfunc::_Manager, __polyfunc::_Mo_base): Guard with __glibcxx_move_only_function || __glibcxx_copyable_function. (__polyfunc::__skip_first_arg, __polyfunc::__deduce_funcref) (std::function_ref) [__glibcxx_function_ref]: Define. * include/bits/utility.h (std::nontype_t, std::nontype) (__is_nontype_v) [__glibcxx_function_ref]: Define. * include/bits/version.def: Define function_ref. * include/bits/version.h: Regenerate. * include/std/functional: Define __cpp_lib_function_ref. * src/c++23/std.cc.in (std::nontype_t, std::nontype) (std::function_ref) [__cpp_lib_function_ref]: Export. * testsuite/20_util/function_ref/assign.cc: New test. * testsuite/20_util/function_ref/call.cc: New test. * testsuite/20_util/function_ref/cons.cc: New test. * testsuite/20_util/function_ref/cons_neg.cc: New test. * testsuite/20_util/function_ref/conv.cc: New test. * testsuite/20_util/function_ref/deduction.cc: New test. * testsuite/20_util/function_ref/mutation.cc: New test. Reviewed-by: Jonathan Wakely <jwakely@redhat.com> Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
Diffstat (limited to 'libstdc++-v3/testsuite')
-rw-r--r--libstdc++-v3/testsuite/20_util/function_ref/assign.cc108
-rw-r--r--libstdc++-v3/testsuite/20_util/function_ref/call.cc186
-rw-r--r--libstdc++-v3/testsuite/20_util/function_ref/cons.cc218
-rw-r--r--libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc30
-rw-r--r--libstdc++-v3/testsuite/20_util/function_ref/conv.cc259
-rw-r--r--libstdc++-v3/testsuite/20_util/function_ref/deduction.cc103
-rw-r--r--libstdc++-v3/testsuite/20_util/function_ref/mutation.cc85
7 files changed, 989 insertions, 0 deletions
diff --git a/libstdc++-v3/testsuite/20_util/function_ref/assign.cc b/libstdc++-v3/testsuite/20_util/function_ref/assign.cc
new file mode 100644
index 0000000..9b02dc4
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_ref/assign.cc
@@ -0,0 +1,108 @@
+// { dg-do compile { target c++26 } }
+
+#include <functional>
+
+#ifndef __cpp_lib_function_ref
+# error "Feature-test macro for function_ref missing in <functional>"
+#elif __cpp_lib_function_ref != 202306L
+# error "Feature-test macro for function_ref has wrong value in <functional>"
+#endif
+
+using std::nontype;
+using std::nontype_t;
+using std::function_ref;
+
+using std::is_nothrow_move_assignable_v;
+using std::is_nothrow_copy_assignable_v;
+using std::is_nothrow_assignable_v;
+using std::is_assignable_v;
+using std::is_nothrow_swappable_v;
+using std::is_trivially_copyable_v;
+
+static_assert( is_nothrow_move_assignable_v<function_ref<void()>> );
+static_assert( is_nothrow_copy_assignable_v<function_ref<void()>> );
+static_assert( is_nothrow_swappable_v<function_ref<void()>> );
+
+static_assert( ! is_assignable_v<function_ref<void()>, std::nullptr_t> );
+
+static_assert( is_nothrow_assignable_v<function_ref<void()>, void()> );
+static_assert( is_nothrow_assignable_v<function_ref<void()>, void(&)()> );
+static_assert( is_nothrow_assignable_v<function_ref<void()>, void(*)()> );
+static_assert( is_nothrow_assignable_v<function_ref<void()>, int()> );
+static_assert( is_nothrow_assignable_v<function_ref<void()>, int(&)()> );
+static_assert( is_nothrow_assignable_v<function_ref<void()>, int(*)()> );
+static_assert( ! is_nothrow_assignable_v<function_ref<void()>, void(int)> );
+static_assert( is_nothrow_assignable_v<function_ref<void(int)>, void(int)> );
+
+static_assert( is_nothrow_assignable_v<function_ref<void()>,
+ void() noexcept> );
+static_assert( is_nothrow_assignable_v<function_ref<void() noexcept>,
+ void() noexcept> );
+static_assert( ! is_assignable_v<function_ref<void() noexcept>, void() > );
+
+struct S
+{
+ int x;
+ int f();
+};
+int funS(S);
+
+static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
+ decltype(funS)> );
+static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
+ decltype(&funS)> );
+static_assert( ! is_assignable_v<function_ref<int(S)>, decltype(&S::x)> );
+static_assert( ! is_assignable_v<function_ref<int(S)>, decltype(&S::f)> );
+
+static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
+ nontype_t<funS>> );
+static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
+ nontype_t<&funS>> );
+static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
+ nontype_t<&S::x>> );
+static_assert( is_nothrow_assignable_v<function_ref<int(S)>,
+ nontype_t<&S::f>> );
+struct Q
+{
+ void operator()() const;
+};
+
+static_assert( ! is_assignable_v<function_ref<void()>, Q> );
+static_assert( ! is_assignable_v<function_ref<void()>, Q&> );
+static_assert( ! is_assignable_v<function_ref<void()>, const Q&> );
+static_assert( ! is_assignable_v<function_ref<void() const>, Q> );
+static_assert( ! is_assignable_v<function_ref<void() const>, Q&> );
+static_assert( ! is_assignable_v<function_ref<void() const>, const Q&> );
+
+static_assert( is_nothrow_assignable_v<function_ref<void()>,
+ nontype_t<Q{}>> );
+static_assert( is_nothrow_assignable_v<function_ref<void() const>,
+ nontype_t<Q{}>> );
+
+constexpr bool
+test_constexpr()
+{
+ function_ref<void(S)> fp(nontype<funS>);
+ fp = nontype<funS>;
+ fp = nontype<&funS>;
+ fp = nontype<&S::x>;
+ fp = nontype<&S::f>;
+
+ constexpr Q cq;
+ function_ref<void() const> fq(cq);
+ fq = nontype<cq>;
+ return true;
+}
+static_assert( test_constexpr() );
+
+void func();
+
+void
+test_instantiation()
+{
+ function_ref<void(S)> fp(funS);
+ fp = funS;
+ fp = &funS;
+
+ test_constexpr();
+}
diff --git a/libstdc++-v3/testsuite/20_util/function_ref/call.cc b/libstdc++-v3/testsuite/20_util/function_ref/call.cc
new file mode 100644
index 0000000..a91c6b4
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_ref/call.cc
@@ -0,0 +1,186 @@
+// { dg-do run { target c++26 } }
+
+#include <functional>
+#include <utility>
+#include <testsuite_hooks.h>
+
+using std::nontype;
+using std::function_ref;
+
+using std::is_same_v;
+using std::is_invocable_v;
+using std::is_nothrow_invocable_v;
+using std::invoke_result_t;
+
+// Check return types
+static_assert( is_same_v<void, invoke_result_t<function_ref<void()>>> );
+static_assert( is_same_v<int, invoke_result_t<function_ref<int()>>> );
+static_assert( is_same_v<int&, invoke_result_t<function_ref<int&()>>> );
+
+// Const qualier applies to target object
+static_assert( is_invocable_v< function_ref<void()> const > );
+static_assert( is_invocable_v< function_ref<void()> const &> );
+static_assert( is_invocable_v< function_ref<void() const> > );
+static_assert( is_invocable_v< function_ref<void() const> &> );
+static_assert( is_invocable_v< function_ref<void() const> const > );
+static_assert( is_invocable_v< function_ref<void() const> const &> );
+
+// With noexcept-specifier
+static_assert( ! is_nothrow_invocable_v< function_ref<void()> > );
+static_assert( ! is_nothrow_invocable_v< function_ref<void() noexcept(false)> > );
+static_assert( is_nothrow_invocable_v< function_ref<void() noexcept> > );
+
+void
+test01()
+{
+ struct F
+ {
+ int operator()() { return 0; }
+ int operator()() const { return 1; }
+ };
+
+ function_ref<int()> f0{F{}};
+ VERIFY( f0() == 0 );
+ VERIFY( std::move(f0)() == 0 );
+
+ function_ref<int()> f1{nontype<F{}>};
+ VERIFY( f1() == 1 );
+ VERIFY( std::move(f1)() == 1 );
+
+ function_ref<int() const> f2{F{}};
+ VERIFY( f2() == 1 );
+ VERIFY( std::as_const(f2)() == 1 );
+ VERIFY( std::move(f2)() == 1 );
+ VERIFY( std::move(std::as_const(f2))() == 1 );
+
+ function_ref<int() const> f3{nontype<F{}>};
+ VERIFY( f3() == 1 );
+ VERIFY( std::as_const(f3)() == 1 );
+ VERIFY( std::move(f3)() == 1 );
+ VERIFY( std::move(std::as_const(f3))() == 1 );
+}
+
+void
+test02()
+{
+ struct F
+ {
+ struct Arg {};
+ int operator()(Arg& arg) const { return 0; }
+ int operator()(const Arg& arg) const { return 1; }
+ };
+ F::Arg arg;
+
+ function_ref<int()> f0{std::nontype<F{}>, arg};
+ VERIFY( f0() == 0 );
+ VERIFY( std::move(f0)() == 0 );
+
+ function_ref<int() const> f1{std::nontype<F{}>, arg};
+ VERIFY( f1() == 1 );
+ VERIFY( std::as_const(f1)() == 1 );
+}
+
+void
+test03()
+{
+ struct F
+ {
+ struct Arg {};
+ int operator()(Arg* arg) const { return 0; }
+ int operator()(const Arg* arg) const { return 1; }
+ };
+ F::Arg arg;
+
+ function_ref<int()> f0{std::nontype<F{}>, &arg};
+ VERIFY( f0() == 0 );
+ VERIFY( std::move(f0)() == 0 );
+
+ function_ref<int() const> f1{std::nontype<F{}>, &arg};
+ VERIFY( f1() == 1 );
+ VERIFY( std::as_const(f1)() == 1 );
+}
+
+void
+test04()
+{
+ constexpr int (*fp)() = [] { return 0; };
+ function_ref<int()> f0{fp};
+ VERIFY( f0() == 0 );
+ VERIFY( std::move(f0)() == 0 );
+
+ function_ref<int()> f1{nontype<fp>};
+ VERIFY( f1() == 0 );
+ VERIFY( std::move(f1)() == 0 );
+
+ const function_ref<int() const> f2{fp};
+ VERIFY( f2() == 0 );
+ VERIFY( std::move(f2)() == 0 );
+
+ const function_ref<int() const> f3{nontype<fp>};
+ VERIFY( f2() == 0 );
+ VERIFY( std::move(f2)() == 0 );
+}
+
+using ftype = int(int);
+int twice(int x) { return x * 2; }
+int cube(int x) { return x * x * x; }
+int callback_ptr(ftype* f, int x) { return f(x); }
+int callback_ref(ftype& f, int x) { return f(x); }
+
+void
+test05()
+{
+
+ function_ref<int(int)> r1(nontype<&callback_ptr>, &twice);
+ VERIFY( r1(2) == 4 );
+ function_ref<int(int)> r2(nontype<&callback_ptr>, cube);
+ VERIFY( r2(2) == 8 );
+
+ function_ref<int(int)> r3(nontype<&callback_ref>, twice);
+ VERIFY( r3(3) == 6 );
+ function_ref<int(int)> r4(nontype<&callback_ref>, cube);
+ VERIFY( r4(3) == 27 );
+
+ // Checks if distinction between reference and pointer
+ // is preserved.
+ struct F
+ {
+ static
+ int operator()(ftype* f, int x)
+ { return f(x) + 1000; }
+
+ static
+ int operator()(ftype& f, int x)
+ { return f(x) + 2000; }
+ };
+ function_ref<int(int)> r5(nontype<F{}>, &twice);
+ VERIFY( r5(2) == 1004 );
+ function_ref<int(int)> r6(nontype<F{}>, twice);
+ VERIFY( r6(2) == 2008 );
+ function_ref<int(int)> r7(nontype<F{}>, &cube);
+ VERIFY( r7(3) == 1006 );
+ function_ref<int(int)> r8(nontype<F{}>, cube);
+ VERIFY( r8(3) == 2027 );
+}
+
+struct Incomplete;
+
+void
+test_params()
+{
+ auto f = [](auto&&) {};
+ // There is discussion if this is supported.
+ // std::function_ref<void(Incomplete)> f1(f);
+ std::function_ref<void(Incomplete&)> f2(f);
+ // See PR120259, this should be supported.
+ // std::function_ref<void(Incomplete&&)> f3(f);
+}
+
+int main()
+{
+ test01();
+ test02();
+ test03();
+ test04();
+ test_params();
+}
diff --git a/libstdc++-v3/testsuite/20_util/function_ref/cons.cc b/libstdc++-v3/testsuite/20_util/function_ref/cons.cc
new file mode 100644
index 0000000..a91f5ba
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_ref/cons.cc
@@ -0,0 +1,218 @@
+// { dg-do compile { target c++26 } }
+// { dg-add-options no_pch }
+
+#include <functional>
+
+#ifndef __cpp_lib_function_ref
+# error "Feature-test macro for function_ref missing in <functional>"
+#elif __cpp_lib_function_ref != 202306L
+# error "Feature-test macro for function_ref has wrong value in <functional>"
+#endif
+
+using std::nontype;
+using std::nontype_t;
+using std::function_ref;
+
+using std::is_default_constructible_v;
+using std::is_nothrow_copy_constructible_v;
+using std::is_nothrow_move_constructible_v;
+using std::is_nothrow_constructible_v;
+using std::is_constructible_v;
+using std::is_trivially_copyable_v;
+
+static_assert( ! is_default_constructible_v<function_ref<void()>> );
+static_assert( is_nothrow_move_constructible_v<function_ref<void()>> );
+static_assert( is_nothrow_copy_constructible_v<function_ref<void()>> );
+static_assert( is_trivially_copyable_v<function_ref<void()>> );
+
+static_assert( ! is_constructible_v<function_ref<void()>, std::nullptr_t> );
+
+static_assert( is_nothrow_constructible_v<function_ref<void()>, void()> );
+static_assert( is_nothrow_constructible_v<function_ref<void()>, void(&)()> );
+static_assert( is_nothrow_constructible_v<function_ref<void()>, void(*)()> );
+static_assert( is_nothrow_constructible_v<function_ref<void()>, int()> );
+static_assert( is_nothrow_constructible_v<function_ref<void()>, int(&)()> );
+static_assert( is_nothrow_constructible_v<function_ref<void()>, int(*)()> );
+static_assert( ! is_constructible_v<function_ref<void()>, void(int)> );
+static_assert( is_nothrow_constructible_v<function_ref<void(int)>, void(int)> );
+
+static_assert( is_nothrow_constructible_v<function_ref<void()>,
+ void() noexcept> );
+static_assert( is_nothrow_constructible_v<function_ref<void() noexcept>,
+ void() noexcept> );
+static_assert( ! is_constructible_v<function_ref<void() noexcept>,
+ void() > );
+
+struct S
+{
+ int x;
+ int f();
+};
+int funS(S);
+
+static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
+ decltype(funS)> );
+static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
+ decltype(&funS)> );
+static_assert( ! is_constructible_v<function_ref<int(S)>,
+ decltype(&S::x)> );
+static_assert( ! is_constructible_v<function_ref<int(S)>,
+ decltype(&S::f)> );
+
+static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
+ nontype_t<funS>> );
+static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
+ nontype_t<&funS>> );
+static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
+ nontype_t<&S::x>> );
+static_assert( is_nothrow_constructible_v<function_ref<int(S)>,
+ nontype_t<&S::f>> );
+
+static_assert( is_nothrow_constructible_v<function_ref<int()>,
+ nontype_t<funS>, S&> );
+static_assert( is_nothrow_constructible_v<function_ref<int()>,
+ nontype_t<&funS>, S&> );
+static_assert( is_nothrow_constructible_v<function_ref<int()>,
+ nontype_t<&S::x>, S&> );
+static_assert( is_nothrow_constructible_v<function_ref<int()>,
+ nontype_t<&S::f>, S&> );
+
+static_assert( ! is_constructible_v<function_ref<int()>,
+ nontype_t<funS>, S*> );
+static_assert( ! is_constructible_v<function_ref<int()>,
+ nontype_t<&funS>, S*> );
+static_assert( is_nothrow_constructible_v<function_ref<int()>,
+ nontype_t<&S::x>, S*> );
+static_assert( is_nothrow_constructible_v<function_ref<int()>,
+ nontype_t<&S::f>, S*> );
+
+struct M
+{
+ void operator()();
+};
+
+
+static_assert( is_nothrow_constructible_v<function_ref<void()>, M> );
+static_assert( is_nothrow_constructible_v<function_ref<void()>, M&> );
+static_assert( ! is_constructible_v<function_ref<void()>, const M&> );
+static_assert( ! is_constructible_v<function_ref<void() const>, M> );
+static_assert( ! is_constructible_v<function_ref<void() const>, const M&> );
+static_assert( ! is_constructible_v<function_ref<void()>,
+ nontype_t<M{}>> );
+static_assert( ! is_constructible_v<function_ref<void() const>,
+ nontype_t<M{}>> );
+struct Q
+{
+ void operator()(int) const;
+ void operator()(int*) const;
+};
+
+static_assert( is_nothrow_constructible_v<function_ref<void(int)>, Q> );
+static_assert( is_nothrow_constructible_v<function_ref<void(int)>, Q&> );
+static_assert( is_nothrow_constructible_v<function_ref<void(int)>, const Q&> );
+static_assert( is_nothrow_constructible_v<function_ref<void(int) const>, Q> );
+static_assert( is_nothrow_constructible_v<function_ref<void(int) const>, Q&> );
+static_assert( is_nothrow_constructible_v<function_ref<void(int) const>, const Q&> );
+
+static_assert( is_nothrow_constructible_v<function_ref<void(int)>,
+ nontype_t<Q{}>> );
+static_assert( is_nothrow_constructible_v<function_ref<void(int) const>,
+ nontype_t<Q{}>> );
+static_assert( is_nothrow_constructible_v<function_ref<void()>,
+ nontype_t<Q{}>, int&> );
+static_assert( is_nothrow_constructible_v<function_ref<void() const>,
+ nontype_t<Q{}>, int&> );
+static_assert( ! is_constructible_v<function_ref<void()>,
+ nontype_t<Q{}>, int> );
+static_assert( ! is_constructible_v<function_ref<void() const>,
+ nontype_t<Q{}>, int> );
+
+static_assert( is_nothrow_constructible_v<function_ref<void()>,
+ nontype_t<Q{}>, int*> );
+static_assert( ! is_constructible_v<function_ref<void() const>,
+ nontype_t<Q{}>, int*> );
+
+struct L
+{
+ void operator()() &;
+};
+
+static_assert( is_nothrow_constructible_v<function_ref<void()>, L> );
+static_assert( is_nothrow_constructible_v<function_ref<void()>, L&> );
+static_assert( ! is_constructible_v<function_ref<void()>, const L&> );
+static_assert( ! is_constructible_v<function_ref<void() const>, L> );
+static_assert( ! is_constructible_v<function_ref<void() const>, const L&> );
+static_assert( ! is_constructible_v<function_ref<void()>,
+ nontype_t<L{}>> );
+static_assert( ! is_constructible_v<function_ref<void() const>,
+ nontype_t<L{}>> );
+
+struct R
+{
+ void operator()(float) const&&;
+};
+
+static_assert( ! is_constructible_v<function_ref<void(float)>, R> );
+static_assert( ! is_constructible_v<function_ref<void(float)>, R&> );
+static_assert( ! is_constructible_v<function_ref<void(float) const>, R> );
+static_assert( ! is_constructible_v<function_ref<void(float) const>, R&> );
+static_assert( ! is_constructible_v<function_ref<void(float) const>, const R&> );
+
+static_assert( ! is_constructible_v<function_ref<void(float)>,
+ nontype_t<R{}>> );
+static_assert( ! is_constructible_v<function_ref<void(float) const>,
+ nontype_t<R{}>> );
+
+constexpr bool
+test_constexpr()
+{
+ function_ref<void(S)> fp1(nontype<funS>);
+ function_ref<void(S)> fp3(nontype<&funS>);
+ function_ref<void(S)> fp4(nontype<&S::x>);
+ function_ref<void(S)> fp5(nontype<&S::f>);
+
+ S s;
+ function_ref<void()> fp6(nontype<&funS>, s);
+ function_ref<void()> fp7(nontype<&S::x>, s);
+ function_ref<void()> fp8(nontype<&S::x>, &s);
+ function_ref<void()> fp9(nontype<&S::f>, s);
+ function_ref<void()> fp10(nontype<&S::f>, &s);
+
+ M m;
+ function_ref<void()> fm1(m);
+ function_ref<void()> fm2(std::move(m));
+
+ Q q;
+ constexpr Q cq;
+ function_ref<void(int)> fq1(q);
+ function_ref<void(int) const> fq2(q);
+ function_ref<void(int) const> fq3(std::move(q));
+
+ function_ref<void(int)> fcq1(cq);
+ function_ref<void(int) const> f(cq);
+ function_ref<void(int)> fcq3(nontype<cq>);
+ function_ref<void(int) const> fcq4(nontype<cq>);
+
+ int i = 0;
+ function_ref<void()> fcq5(nontype<cq>, i);
+ function_ref<void() const> fcq6(nontype<cq>, i);
+ function_ref<void()> fcq7(nontype<cq>, &i);
+
+ L l;
+ function_ref<void()> fl1(l);
+ function_ref<void()> fl2(std::move(l));
+
+ return true;
+}
+static_assert( test_constexpr() );
+
+void func();
+
+void
+test_instantiation()
+{
+ function_ref<void(S)> fp1(funS);
+ function_ref<void(S)> fp2(&funS);
+
+ test_constexpr();
+}
diff --git a/libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc b/libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc
new file mode 100644
index 0000000..050090d
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc
@@ -0,0 +1,30 @@
+// { dg-do compile { target c++26 } }
+
+#include <functional>
+
+using std::nontype;
+using std::function_ref;
+
+struct S
+{
+ int x;
+ void foo();
+};
+S s;
+
+constexpr int(*fp)(S) = nullptr;
+constexpr int S::*mdp = nullptr;
+constexpr int (S::*mfp)() = nullptr;
+
+function_ref<int(S)> fd1(nontype<fp>); // { dg-error "from here" }
+function_ref<int(S)> fd2(nontype<mdp>); // { dg-error "from here" }
+function_ref<int(S)> fd3(nontype<mfp>); // { dg-error "from here" }
+
+function_ref<int()> br4(nontype<fp>, s); // { dg-error "from here" }
+function_ref<int()> br5(nontype<mdp>, s); // { dg-error "from here" }
+function_ref<int()> br6(nontype<mfp>, s); // { dg-error "from here" }
+
+function_ref<int()> bp7(nontype<mdp>, &s); // { dg-error "from here" }
+function_ref<int()> bp8(nontype<mfp>, &s); // { dg-error "from here" }
+
+// { dg-prune-output "static assertion failed" }
diff --git a/libstdc++-v3/testsuite/20_util/function_ref/conv.cc b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
new file mode 100644
index 0000000..7dc7b8c
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc
@@ -0,0 +1,259 @@
+// { dg-do run { target c++26 } }
+// { dg-require-effective-target hosted }
+
+#include <functional>
+#include <testsuite_hooks.h>
+
+using std::function_ref;
+
+static_assert( std::is_constructible_v<std::function_ref<void() const>,
+ std::function_ref<void()>> );
+
+// Non-trivial args, guarantess that type is not passed by copy
+struct CountedArg
+{
+ CountedArg() = default;
+ CountedArg(const CountedArg& f) noexcept : counter(f.counter) { ++counter; }
+ CountedArg& operator=(CountedArg&&) = delete;
+
+ int counter = 0;
+};
+CountedArg const c;
+
+// The C++26 [func.wrap.general] p2 does not currently cover funciton_ref,
+// so we make extra copies of arguments.
+
+void
+test01()
+{
+ auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
+ std::function_ref<int(CountedArg) const noexcept> r1(f);
+ std::move_only_function<int(CountedArg) const noexcept> m1(f);
+ std::copyable_function<int(CountedArg) const noexcept> c1(f);
+
+ // Complatible signatures
+ std::function_ref<int(CountedArg) const noexcept> r2m(m1);
+ VERIFY( r2m(c) == 2 );
+ std::function_ref<int(CountedArg) const noexcept> r2c(c1);
+ VERIFY( r2c(c) == 2 );
+
+ std::function_ref<int(CountedArg) const> r3r(r1);
+ VERIFY( r3r(c) == 2 );
+ std::function_ref<int(CountedArg) const> r3m(m1);
+ VERIFY( r3m(c) == 2 );
+ std::function_ref<int(CountedArg) const> r3c(c1);
+ VERIFY( r3c(c) == 2 );
+
+ std::function_ref<int(CountedArg)> r4r(r1);
+ VERIFY( r4r(c) == 2 );
+ std::function_ref<int(CountedArg)> r4m(m1);
+ VERIFY( r4m(c) == 2 );
+ std::function_ref<int(CountedArg)> r4c(c1);
+ VERIFY( r4c(c) == 2 );
+
+ // Incompatible signatures
+ std::function_ref<long(CountedArg) const noexcept> r5r(r1);
+ VERIFY( r5r(c) == 2 );
+ std::function_ref<long(CountedArg) const noexcept> r5m(m1);
+ VERIFY( r5r(c) == 2 );
+ std::function_ref<long(CountedArg) const noexcept> r5c(c1);
+ VERIFY( r5r(c) == 2 );
+}
+
+void
+test02()
+{
+ // Constructing move_only_function and copyable_function from function_ref,
+ // have not chance to restore manager, so we store function_ref inside.
+ auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
+ std::function_ref<int(CountedArg) const noexcept> r1(f);
+
+ std::move_only_function<int(CountedArg) const noexcept> m1(r1);
+ VERIFY( m1(c) == 2 );
+
+ std::copyable_function<int(CountedArg) const noexcept> c1(r1);
+ VERIFY( c1(c) == 2 );
+}
+
+void
+test03()
+{
+ struct F
+ {
+ int operator()(CountedArg const& arg) noexcept
+ { return arg.counter; }
+
+ int operator()(CountedArg const& arg) const noexcept
+ { return arg.counter + 1000; }
+ };
+
+ F f;
+ std::function_ref<int(CountedArg) const> r1(f);
+ VERIFY( r1(c) == 1001 );
+
+ // Call const overload as std::function_ref<int(CountedArg) const>
+ // inside std::function_ref<int(CountedArg)> would do.
+ std::function_ref<int(CountedArg)> r2(r1);
+ VERIFY( r2(c) == 1002 );
+ std::move_only_function<int(CountedArg)> m2(r1);
+ VERIFY( m2(c) == 1002 );
+
+ // Call non-const overload as const-qualifed operator() for
+ // std::function_ref<int(CountedArg)> do.
+ std::function_ref<int(CountedArg)> r3(f);
+ VERIFY( r3(c) == 1 );
+ std::function_ref<int(CountedArg) const> r4(r3);
+ VERIFY( r4(c) == 2 );
+ std::move_only_function<int(CountedArg) const> m4(r3);
+ VERIFY( m4(c) == 2 );
+}
+
+void
+test04()
+{
+ auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
+ std::function_ref<int(CountedArg)> w1(f);
+ // function_ref stores function_ref due incompatibile signatures
+ std::function_ref<int(CountedArg const&)> w2(std::move(w1));
+ // copy is made when passing to int(CountedArg)
+ VERIFY( w2(c) == 1 );
+ // wrapped 3 times
+ std::function_ref<int(CountedArg)> w3(w2);
+ VERIFY( w3(c) == 2 );
+ // wrapped 4 times
+ std::function_ref<int(CountedArg const&)> w4(w3);
+ VERIFY( w4(c) == 2 );
+ // wrapped 5 times
+ std::function_ref<int(CountedArg)> w5(w4);
+ VERIFY( w5(c) == 3 );
+}
+
+void
+test05()
+{
+ // No special interoperability with std::function
+ auto f = [](CountedArg const& arg) noexcept { return arg.counter; };
+ std::function<int(CountedArg)> f1(f);
+ std::function_ref<int(CountedArg) const> c1(std::move(f1));
+ VERIFY( c1(c) == 2 );
+
+ std::function_ref<int(CountedArg) const> c2(f);
+ std::function<int(CountedArg)> f2(c2);
+ VERIFY( f2(c) == 2 );
+}
+
+void
+test06()
+{
+ auto* func = +[]{ static int x; return &x; };
+ std::move_only_function<const void*() const> m1(func);
+ std::function_ref<const void*() const> rm1(m1);
+ VERIFY( m1() == rm1() );
+ std::copyable_function<const void*() const> c1(func);
+ std::function_ref<const void*() const> rc1(c1);
+ VERIFY( c1() == rc1() );
+
+ struct Trivial
+ {
+ void const* operator()() const
+ { return this; }
+ };
+ std::move_only_function<const void*() const> m2(Trivial{});
+ std::function_ref<const void*() const> rm2(m2);
+ VERIFY( m2() == rm2() );
+ std::copyable_function<const void*() const> c2(Trivial{});
+ std::function_ref<const void*() const> rc2(c2);
+ VERIFY( c2() == rc2() );
+
+ struct NonTrivial : Trivial
+ {
+ NonTrivial() {}
+ NonTrivial(NonTrivial&&) noexcept {}
+ NonTrivial(const NonTrivial&) {}
+ };
+ std::move_only_function<const void*() const> m3(NonTrivial{});
+ std::function_ref<const void*() const> rm3(m3);
+ VERIFY( m3() == rm3() );
+ std::copyable_function<const void*() const> c3(NonTrivial{});
+ std::function_ref<const void*() const> rc3(c3);
+ VERIFY( c3() == rc3() );
+
+ struct Large : Trivial
+ {
+ int tab[10];
+ };
+ std::move_only_function<const void*() const> m4(Large{});
+ std::function_ref<const void*() const> rm4(m4);
+ VERIFY( m4() == rm4() );
+ std::copyable_function<const void*() const> c4(Large{});
+ std::function_ref<const void*() const> rc4(c4);
+ VERIFY( c4() == rc4() );
+}
+
+void
+test07()
+{
+ int (*f1)() = [] { return 1; };
+ int (*f2)() = [] { return 2; };
+
+ std::function_ref<int() const> r1(f1);
+ std::move_only_function<int() const> m1(f1);
+ std::copyable_function<int() const> c1(f1);
+
+ std::function_ref<int() const> r2r(r1);
+ VERIFY( r2r() == 1 );
+ r1 = f2;
+ VERIFY( r2r() == 1 ); // same-siganture, copy constructor is used
+
+ std::function_ref<int() const> r2m(m1);
+ VERIFY( r2m() == 1 );
+ m1 = f2;
+ VERIFY( r2m() == 2 );
+
+ std::function_ref<int() const> r2c(c1);
+ VERIFY( r2c() == 1 );
+ c1 = f2;
+ VERIFY( r2c() == 2 );
+
+ std::function_ref<int()> r3r(r1);
+ VERIFY( r3r() == 2 );
+ r1 = f1;
+ VERIFY( r3r() == 1 ); // converting-constructor
+
+ std::function_ref<int()> r3m(m1);
+ VERIFY( r3m() == 2 );
+ m1 = f1;
+ VERIFY( r3m() == 1 );
+
+ std::function_ref<int()> r3c(c1);
+ VERIFY( r3c() == 2 );
+ c1 = f1;
+ VERIFY( r3c() == 1 );
+
+}
+
+constexpr bool
+test08()
+{
+ auto f = [](int x) noexcept { return x; };
+ std::function_ref<int(int) const noexcept> rf(f);
+
+ std::function_ref<int(int) const noexcept> rr1(rf);
+ std::function_ref<int(int)> rr2(rf);
+ std::function_ref<int(long)> rr3(rf);
+ return true;
+};
+
+
+int main()
+{
+ test01();
+ test02();
+ test03();
+ test04();
+ test05();
+ test06();
+ test07();
+
+ static_assert( test08() );
+}
diff --git a/libstdc++-v3/testsuite/20_util/function_ref/deduction.cc b/libstdc++-v3/testsuite/20_util/function_ref/deduction.cc
new file mode 100644
index 0000000..2940b87
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_ref/deduction.cc
@@ -0,0 +1,103 @@
+// { dg-do compile { target c++26 } }
+
+#include <functional>
+#include <type_traits>
+
+using std::is_same_v;
+using std::nontype;
+using std::nontype_t;
+using std::function_ref;
+
+int i = 0;
+
+void f0();
+void f0n() noexcept;
+
+static_assert( is_same_v<decltype(function_ref(f0)),
+ function_ref<void()>> );
+static_assert( is_same_v<decltype(function_ref(f0n)),
+ function_ref<void() noexcept>> );
+static_assert( is_same_v<decltype(function_ref(nontype<f0>)),
+ function_ref<void()>> );
+static_assert( is_same_v<decltype(function_ref(nontype<f0n>)),
+ function_ref<void() noexcept>> );
+
+void f1(int);
+void f1n(int) noexcept;
+
+static_assert( is_same_v<decltype(function_ref(f1)),
+ function_ref<void(int)>> );
+static_assert( is_same_v<decltype(function_ref(f1n)),
+ function_ref<void(int) noexcept>> );
+static_assert( is_same_v<decltype(function_ref(nontype<f1>)),
+ function_ref<void(int)>> );
+static_assert( is_same_v<decltype(function_ref(nontype<f1n>)),
+ function_ref<void(int) noexcept>> );
+static_assert( is_same_v<decltype(function_ref(nontype<f1>, i)),
+ function_ref<void()>> );
+static_assert( is_same_v<decltype(function_ref(nontype<f1n>, i)),
+ function_ref<void() noexcept>> );
+
+void f2(int*, int);
+void f2n(int*, int) noexcept;
+
+static_assert( is_same_v<decltype(function_ref(f2)),
+ function_ref<void(int*, int)>> );
+static_assert( is_same_v<decltype(function_ref(f2n)),
+ function_ref<void(int*, int) noexcept>> );
+static_assert( is_same_v<decltype(function_ref(nontype<f2>)),
+ function_ref<void(int*, int)>> );
+static_assert( is_same_v<decltype(function_ref(nontype<f2n>)),
+ function_ref<void(int*, int) noexcept>> );
+static_assert( is_same_v<decltype(function_ref(nontype<f2>, &i)),
+ function_ref<void(int)>> );
+static_assert( is_same_v<decltype(function_ref(nontype<f2n>, &i)),
+ function_ref<void(int) noexcept>> );
+
+struct S
+{
+ int mem;
+ int f();
+ int fn() noexcept;
+
+ int fc(int) const;
+ int fcn(int) const noexcept;
+
+ int fl(int) &;
+ int fln(int) & noexcept;
+
+ int fcl(float) const&;
+ int fcln(float) const& noexcept;
+};
+S s{};
+const S cs{};
+
+static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, s)),
+ function_ref<int&()>> );
+static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, cs)),
+ function_ref<const int&()>> );
+static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, &s)),
+ function_ref<int&()>> );
+static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, &cs)),
+ function_ref<const int&()>> );
+
+static_assert( is_same_v<decltype(function_ref(nontype<&S::f>, s)),
+ function_ref<int()>> );
+static_assert( is_same_v<decltype(function_ref(nontype<&S::fn>, &s)),
+ function_ref<int() noexcept>> );
+
+static_assert( is_same_v<decltype(function_ref(nontype<&S::fc>, &s)),
+ function_ref<int(int)>> );
+static_assert( is_same_v<decltype(function_ref(nontype<&S::fcn>, s)),
+ function_ref<int(int) noexcept>> );
+
+static_assert( is_same_v<decltype(function_ref(nontype<&S::fl>, &s)),
+ function_ref<int(int)>> );
+static_assert( is_same_v<decltype(function_ref(nontype<&S::fln>, s)),
+ function_ref<int(int) noexcept>> );
+
+static_assert( is_same_v<decltype(function_ref(nontype<&S::fcl>, s)),
+ function_ref<int(float)>> );
+static_assert( is_same_v<decltype(function_ref(nontype<&S::fcln>, &s)),
+ function_ref<int(float) noexcept>> );
+
diff --git a/libstdc++-v3/testsuite/20_util/function_ref/mutation.cc b/libstdc++-v3/testsuite/20_util/function_ref/mutation.cc
new file mode 100644
index 0000000..32c6931
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/function_ref/mutation.cc
@@ -0,0 +1,85 @@
+// { dg-do run { target c++26 } }
+
+#include <functional>
+#include <testsuite_hooks.h>
+
+using std::nontype;
+using std::function_ref;
+
+void
+test01()
+{
+ struct F {
+ int v;
+ int operator()() { return v; }
+ };
+ F f1{2}, f2{5};
+
+ function_ref<int()> r1(f1);
+ function_ref<long()> r2(f1);
+
+ VERIFY( r1() == 2 );
+ VERIFY( r2() == 2 );
+
+ f1.v = 10;
+
+ VERIFY( r1() == 10 );
+ VERIFY( r2() == 10 );
+
+ r1 = function_ref<int()>(f2);
+ r2 = function_ref<long()>(f2);
+
+ VERIFY( r1() == 5 );
+ VERIFY( r2() == 5 );
+
+ f2.v = 13;
+
+ VERIFY( r1() == 13 );
+ VERIFY( r2() == 13 );
+
+ r1 = function_ref<int()>(f1);
+ r2 = function_ref<long()>(f1);
+
+ f1.v = 20;
+ VERIFY( r1() == 20 );
+ VERIFY( r2() == 20 );
+}
+
+void
+test02()
+{
+ struct S
+ {
+ int x;
+ int f() { return x; };
+ };
+ S s{10};
+
+ function_ref<int()> r1(nontype<&S::x>, s);
+ function_ref<long()> r2(nontype<&S::x>, &s);
+
+ VERIFY( r1() == 10 );
+ VERIFY( r2() == 10 );
+
+ s.x = 20;
+
+ VERIFY( r1() == 20 );
+ VERIFY( r2() == 20 );
+
+ r1 = function_ref<int()>(nontype<&S::f>, &s);
+ r2 = function_ref<long()>(nontype<&S::f>, s);
+
+ VERIFY( r1() == 20 );
+ VERIFY( r2() == 20 );
+
+ s.x = 30;
+
+ VERIFY( r1() == 30 );
+ VERIFY( r2() == 30 );
+}
+
+int main()
+{
+ test01();
+ test02();
+}