[libcxx-commits] [libcxx] [libc++] Implement `std::function_ref` (PR #186692)

via libcxx-commits libcxx-commits at lists.llvm.org
Sat Jun 6 05:14:18 PDT 2026


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/186692

>From a58ba42f16640166f3a454c18553999c2ef9ba27 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 12 Apr 2026 09:35:44 +0100
Subject: [PATCH 01/11] [libcxx] Implement `std::constant_wrapper`

ci

ci

review comments

review comments

ci

review comments
---
 .../containers/views/views.span/span.cons/deduct.pass.cpp  | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
index e32e787ac3381..6613b2b94808f 100644
--- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
+++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp
@@ -73,6 +73,13 @@ void test_iterator_sentinel() {
     assert(s.size() == 1);
     assert(s.data() == std::data(arr));
   }
+
+  {
+    std::span s{std::begin(arr), std::cw<3>};
+    ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
+    assert(s.size() == std::size(arr));
+    assert(s.data() == std::data(arr));
+  }
 #endif
 }
 

>From 48a96e313c1e53721ddf69ab2c095276aed1c476 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 02/11] [libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

[libc++] P0792R14: 'function_ref'

rebase + put in experimental + rename nontype to constant_arg

tests

remove experimental

test for constant_arg_ref

test for constant_arg_ptr

remove fun_ptr member

invoke

format

constexpr function ptr

double unwrapping

P3948R1

tests for new papers

format

lwg issue

lwg issues

review comment

remove constexpr

optimise small types

ci

ci

ci

ci
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 libcxx/docs/ReleaseNotes/23.rst               |   3 +
 libcxx/docs/Status/Cxx2cIssues.csv            |   4 +-
 libcxx/docs/Status/Cxx2cPapers.csv            |   6 +-
 libcxx/include/CMakeLists.txt                 |   3 +
 libcxx/include/__functional/function_ref.h    |  33 ++
 .../__functional/function_ref_common.h        | 126 ++++++
 .../include/__functional/function_ref_impl.h  | 184 +++++++++
 libcxx/include/functional                     |   9 +
 libcxx/include/module.modulemap.in            |   3 +
 libcxx/include/version                        |   4 +-
 libcxx/modules/std/functional.inc             |   4 +
 libcxx/modules/std/utility.inc                |   5 +
 .../assert.constant_arg_ptr.pass.cpp          |  31 ++
 .../assert.function_ptr.pass.cpp              |  26 ++
 .../constant_arg.mandates.verify.cpp          |  60 +++
 .../internal_call_signature.pass.cpp          | 136 +++++++
 .../functional.version.compile.pass.cpp       |  16 +-
 .../version.version.compile.pass.cpp          |  16 +-
 .../func.wrap/func.wrap.ref/ctad.pass.cpp     | 107 +++++
 .../func.wrap.ref.ctor/assign.delete.pass.cpp | 115 ++++++
 .../constant_wrapper.pass.cpp                 | 175 ++++++++
 .../constant_wrapper_ptr.pass.cpp             | 359 +++++++++++++++++
 .../constant_wrapper_ref.pass.cpp             | 373 ++++++++++++++++++
 .../func.wrap.ref.ctor/copy.pass.cpp          | 131 ++++++
 .../func.wrap.ref.ctor/copy_assign.pass.cpp   | 158 ++++++++
 .../func.wrap.ref.ctor/function_ptr.pass.cpp  | 149 +++++++
 .../func.wrap.ref.ctor/move.pass.cpp          | 129 ++++++
 .../func.wrap.ref.ctor/move_assign.pass.cpp   | 156 ++++++++
 .../func.wrap.ref.ctor/ref.pass.cpp           | 316 +++++++++++++++
 .../func.wrap.ref.inv/invoke.pass.cpp         | 362 +++++++++++++++++
 .../trivially_copyable.compile.pass.cpp       |  33 ++
 .../generate_feature_test_macro_components.py |   3 +-
 33 files changed, 3205 insertions(+), 32 deletions(-)
 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/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
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
 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/constant_wrapper.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.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/function_ptr.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.ctor/ref.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.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/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 1b748e37293df..86fefe85f5fb0 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -478,7 +478,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_fstream_native_handle``                        ``202306L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_function_ref``                                 *unimplemented*
+    ``__cpp_lib_function_ref``                                 ``202603L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_generate_random``                              *unimplemented*
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 03dabc54dcf79..ff8d3113b2fa5 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -51,6 +51,9 @@ Implemented Papers
 - P2542R8: ``views::concat`` (`Github <https://llvm.org/PR105419>`__)
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
+- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
+- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
+- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index 6a836c0491bb6..73975c64da089 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -161,7 +161,7 @@
 "`LWG4251 <https://wg21.link/LWG4251>`__","Move assignment for ``indirect`` unnecessarily requires copy construction","2025-11 (Kona)","","","`#171318 <https://github.com/llvm/llvm-project/issues/171318>`__",""
 "`LWG4253 <https://wg21.link/LWG4253>`__","``basic_const_iterator`` should provide ``iterator_type``","2025-11 (Kona)","","","`#171319 <https://github.com/llvm/llvm-project/issues/171319>`__",""
 "`LWG4255 <https://wg21.link/LWG4255>`__","``move_only_function`` constructor should recognize empty ``copyable_function``\s","2025-11 (Kona)","","","`#171320 <https://github.com/llvm/llvm-project/issues/171320>`__",""
-"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","","","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
+"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","23","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
 "`LWG4257 <https://wg21.link/LWG4257>`__","Stream insertion for ``chrono::local_time`` should be constrained","2025-11 (Kona)","","","`#171322 <https://github.com/llvm/llvm-project/issues/171322>`__",""
 "`LWG4260 <https://wg21.link/LWG4260>`__","Query objects must be default constructible","2025-11 (Kona)","","","`#171323 <https://github.com/llvm/llvm-project/issues/171323>`__",""
 "`LWG4265 <https://wg21.link/LWG4265>`__","``std::midpoint`` should not accept ``const bool``","2025-11 (Kona)","|Complete|","22","`#171324 <https://github.com/llvm/llvm-project/issues/171324>`__",""
@@ -225,7 +225,7 @@
 "`LWG4422 <https://wg21.link/LWG4422>`__","``meta::access_context`` should be a consteval-only type","2025-11 (Kona)","","","`#171384 <https://github.com/llvm/llvm-project/issues/171384>`__",""
 "`LWG4423 <https://wg21.link/LWG4423>`__","``meta::data_member_spec`` allows negative bit-field widths","2025-11 (Kona)","","","`#171385 <https://github.com/llvm/llvm-project/issues/171385>`__",""
 "`LWG4424 <https://wg21.link/LWG4424>`__","``meta::define_aggregate`` should require a class type","2025-11 (Kona)","","","`#171386 <https://github.com/llvm/llvm-project/issues/171386>`__",""
-"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","","","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
+"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","23","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
 "`LWG4426 <https://wg21.link/LWG4426>`__","Clarify what ``meta::reflect_constant_string`` considers a string literal","2025-11 (Kona)","","","`#171388 <https://github.com/llvm/llvm-project/issues/171388>`__",""
 "`LWG4427 <https://wg21.link/LWG4427>`__","``meta::dealias`` needs to work with things that aren't entities","2025-11 (Kona)","","","`#171390 <https://github.com/llvm/llvm-project/issues/171390>`__",""
 "`LWG4428 <https://wg21.link/LWG4428>`__","Metafunctions should not be defined in terms of constant subexpressions","2025-11 (Kona)","","","`#171391 <https://github.com/llvm/llvm-project/issues/171391>`__",""
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 2132e80251657..d8984dca2f694 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>`__."
@@ -192,9 +192,9 @@
 "`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
 "`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
 "`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
-"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","","","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
+"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","23","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
 "`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
-"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","","","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
+"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","23","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
 "`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""
 "`P4022R0 <https://wg21.link/P4022R0>`__","Remove ``try_append_range`` from ``inplace_vector`` for now","2026-03 (Croydon)","","","`#189608 <https://github.com/llvm/llvm-project/issues/189608>`__",""
 "`P4037R1 <https://wg21.link/P4037R1>`__","Supporting ``signed char`` and ``unsigned char`` in random number generation","2026-03 (Croydon)","","","`#189609 <https://github.com/llvm/llvm-project/issues/189609>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 10dfb4b4d58cb..f7328a568e73b 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -437,6 +437,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/hash.h
   __functional/identity.h
   __functional/invoke.h
diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
new file mode 100644
index 0000000000000..b7fd3a4a2c79f
--- /dev/null
+++ b/libcxx/include/__functional/function_ref.h
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+#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
+#endif
+
+#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 const
+#  include <__functional/function_ref_impl.h>
+#  undef _LIBCPP_FUNCTION_REF_CV
+
+// NOLINTEND(readability-duplicate-include)
+#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
new file mode 100644
index 0000000000000..61979231b56e8
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -0,0 +1,126 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_const.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/remove_pointer.h>
+#include <__utility/constant_wrapper.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 _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
+union __storage_func_ref_t {
+  void* __obj_ptr_;
+  void const* __obj_const_ptr_;
+  void (*__fn_ptr_)();
+
+  _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>);
+      __fn_ptr_ = 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>);
+      return reinterpret_cast<_Tp*>(__storage.__fn_ptr_);
+    }
+  }
+};
+
+template <class _Fp, class _Tp>
+struct __function_ref_bind {};
+
+// F is of the form R(*)(G, A...) noexcept(E) for a type G.
+template <bool _Noexcept, class _Tp, class _Rp, class _Gp, class... _ArgTypes>
+struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept(_Noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(_Noexcept);
+};
+
+template <class _Tp, class _Mp, class _Gp>
+  requires is_object_v<_Mp>
+struct __function_ref_bind<_Mp _Gp::*, _Tp> {
+  using type _LIBCPP_NODEBUG = invoke_result_t<_Mp _Gp::*, _Tp&>() noexcept;
+};
+
+template <class _Fp, class _Tp>
+using __function_ref_bind_t _LIBCPP_NODEBUG = __function_ref_bind<_Fp, _Tp>::type;
+
+template <class _Fp>
+  requires is_function_v<_Fp>
+function_ref(_Fp*) -> function_ref<_Fp>;
+
+template <auto _Cw, class _Fn>
+  requires is_function_v<remove_pointer_t<_Fn>>
+function_ref(constant_wrapper<_Cw, _Fn>) -> function_ref<remove_pointer_t<_Fn>>;
+
+template <auto _Cw, class _Fn, class _Tp>
+function_ref(constant_wrapper<_Cw, _Fn>, _Tp&&) -> function_ref<__function_ref_bind_t<_Fn, _Tp&>>;
+
+template <class>
+constexpr bool __is_constant_wrapper = false;
+
+template <auto _Value>
+constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
+
+template <class _Arg>
+struct __function_ref_arg_fwd {
+  using type _LIBCPP_NODEBUG = _Arg&&;
+};
+
+template <class _Arg>
+  requires(!is_reference_v<_Arg> && is_trivially_copyable_v<_Arg> && sizeof(_Arg) <= 16)
+struct __function_ref_arg_fwd<_Arg> {
+  using type _LIBCPP_NODEBUG = _Arg;
+};
+
+#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..a4c98d3031602
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -0,0 +1,184 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// This header is unguarded on purpose. This header is an implementation detail of function_ref.h
+// and generates multiple versions of std::function_ref
+
+#include <__assert>
+#include <__config>
+#include <__functional/function_ref_common.h>
+#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_convertible.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_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>
+#include <__type_traits/remove_reference.h>
+#include <__utility/constant_wrapper.h>
+#include <__utility/forward.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  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
+
+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:
+  template <class... _Tp>
+  static constexpr bool __is_invocable_using =
+      _If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
+          value;
+
+  template <class _Fn>
+  static constexpr bool __is_convertible_from_specialization_v =
+      __is_convertible_from_specialization<_Fn, __is_noexcept, _Rp, _ArgTypes...>::value;
+
+  template <class... _Tp>
+  friend class function_ref;
+
+  template <class _Arg>
+  using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
+
+  using __storage_t _LIBCPP_NODEBUG = __storage_func_ref_t;
+
+  using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, __arg_t<_ArgTypes>...) noexcept(__is_noexcept);
+
+  __storage_t __storage_;
+  __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),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          return __storage_t::template __get<_Fp>(__storage)(static_cast<__arg_t<_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_convertible_from_specialization_v<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          return __obj1(static_cast<__arg_t<_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 _Cw, class _Fn>
+    requires __is_invocable_using<const _Fn&>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f) noexcept
+      : __call_([](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          return std::invoke_r<_Rp>(decltype(__f)::value, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+    if constexpr (sizeof...(_ArgTypes) > 0 && (__constexpr_param<remove_cvref_t<_ArgTypes>> && ...)) {
+      static_assert(
+          !requires {
+            typename constant_wrapper<std::invoke(decltype(__f)::value, remove_cvref_t<_ArgTypes>::value...)>;
+          },
+          "function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments "
+          "and produces a result with a structural type");
+    }
+  }
+
+  template <auto _Cw, class _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
+    requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp&>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f, _Up&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+  }
+
+  template <auto _Cw, class _Fn, class _Tp>
+    requires __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp*>
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f,
+                                               _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr_) noexcept
+      : __storage_(__obj_ptr_),
+        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+          auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, static_cast<__arg_t<_ArgTypes>>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
+    }
+
+    if constexpr (is_member_pointer_v<_Fn>) {
+      _LIBCPP_ASSERT_NON_NULL(__obj_ptr_ != nullptr, "the object pointer should not be a nullptr");
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(const function_ref&) noexcept = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+  template <class _Tp>
+    requires(!__is_convertible_from_specialization_v<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_constant_wrapper<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI function_ref& operator=(_Tp) = delete;
+
+  _LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes... __args) const noexcept(__is_noexcept) {
+    return __call_(__storage_, std::forward<_ArgTypes>(__args)...);
+  }
+};
+
+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, 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_END_NAMESPACE_STD
diff --git a/libcxx/include/functional b/libcxx/include/functional
index 9d588231d548d..df79d616a57e2 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -487,6 +487,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>;
@@ -571,6 +576,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 29f3818c39671..317846bbfcba1 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1436,6 +1436,9 @@ 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            { 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" }
diff --git a/libcxx/include/version b/libcxx/include/version
index d4d29b4d31a72..e60da9ef20653 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -130,7 +130,7 @@ __cpp_lib_freestanding_optional                         202311L <optional>
 __cpp_lib_freestanding_string_view                      202311L <string_view>
 __cpp_lib_freestanding_variant                          202311L <variant>
 __cpp_lib_fstream_native_handle                         202306L <fstream>
-__cpp_lib_function_ref                                  202306L <functional>
+__cpp_lib_function_ref                                  202603L <functional>
 __cpp_lib_gcd_lcm                                       201606L <numeric>
 __cpp_lib_generate_random                               202403L <random>
 __cpp_lib_generic_associative_lookup                    201304L <map> <set>
@@ -590,7 +590,7 @@ __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
+# define __cpp_lib_function_ref                         202603L
 // # define __cpp_lib_generate_random                      202403L
 // # define __cpp_lib_hazard_pointer                       202306L
 // # define __cpp_lib_inplace_vector                       202406L
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..62400c2b15179 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::constant_wrapper;
+  using std::cw;
+#endif // _LIBCPP_STD_VER >= 23
+
   // [depr.relops]
   namespace rel_ops {
     using rel_ops::operator!=;
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..9703de0a59c2c
--- /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 || libcpp-hardening-mode=fast
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// template<auto f, class T>
+//   constexpr function_ref(constant_wrapper<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::cw<&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..e69776d0e3c90
--- /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 || libcpp-hardening-mode=fast
+// 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..519957eec6878
--- /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,60 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 c, class F>
+//   constexpr function_ref(constant_wrapper<c, F>) noexcept;
+// template<auto c, class F, class U>
+//   constexpr function_ref(constant_wrapper<c, F>, U&& obj) noexcept;
+// template<auto c, class F, class T>
+//   constexpr function_ref(constant_wrapper<c, F>, cv T* obj) noexcept;
+
+// Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
+
+// For the first overload,
+// f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>... satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)> is not a valid type.
+
+#include <functional>
+#include <utility>
+
+struct A {
+  void f();
+};
+
+struct B {
+  constexpr int operator()(std::constant_wrapper<42>) const { return 42; }
+  constexpr int operator()(int) const { return 42; }
+};
+
+// clang-format off
+void test() {
+  std::function_ref<void()> f1(std::cw<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 '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void(A)> f2(std::cw<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 '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
+  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments and produces a result with a structural type}}
+
+  int i;
+  std::function_ref<void()> f3(std::cw<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 '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  A a;
+  std::function_ref<void()> f4(std::cw<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 '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void()> f5(std::cw<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 '__f.value != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void()> f6(std::cw<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 '__f.value != nullptr': the function pointer should not be a nullptr}}
+}
+// clang-format on
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
new file mode 100644
index 0000000000000..05756cfc3b3b8
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -0,0 +1,136 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Track number of moves
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+struct Small {};
+
+struct Big {
+  char c[128];
+};
+
+struct SmallButNonTrivial {
+  Small s;
+  SmallButNonTrivial() = default;
+  SmallButNonTrivial(const SmallButNonTrivial&) {}
+};
+
+// Need to inspect the internal state of function_ref, and our implementation friend-ed all the function_ref
+// specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
+template <>
+class std::function_ref<int> {
+  using storage = std::__storage_func_ref_t;
+  void test() {
+    {
+      // by value small argument
+      // the internal function should pass by value
+      using call = std::function_ref<void(Small)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small)>);
+    }
+    {
+      // by value big argument
+      // the internal function should pass by rvalue reference
+      using call = std::function_ref<void(Big)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Big&&)>);
+    }
+    {
+      // by value non-trivial small argument
+      // the internal function should pass by rvalue reference to avoid unnecessary copy/move
+      using call = std::function_ref<void(SmallButNonTrivial)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, SmallButNonTrivial&&)>);
+    }
+    {
+      // by lvalue reference argument
+      // the internal function should pass by lvalue reference
+      using call = std::function_ref<void(Small&)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small&)>);
+    }
+    {
+      // by rvalue reference argument
+      // the internal function should pass by rvalue reference
+      using call = std::function_ref<void(Small&&)>::__call_t;
+      static_assert(std::is_same_v<call, void (*)(storage, Small&&)>);
+    }
+  }
+};
+
+struct TrackCopyMove {
+  mutable int copy_count = 0;
+  int move_count         = 0;
+
+  TrackCopyMove() = default;
+  TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
+    ++copy_count;
+    ++other.copy_count;
+  }
+
+  TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
+    ++move_count;
+    ++other.move_count;
+  }
+  TrackCopyMove& operator=(const TrackCopyMove& other) {
+    ++copy_count;
+    ++other.copy_count;
+    return *this;
+  }
+  TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
+    ++move_count;
+    ++other.move_count;
+    return *this;
+  }
+};
+
+void test() {
+  {
+    // Arg type is an lvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&)> f = lambda;
+    f(t);
+  }
+  {
+    // Arg type is an rvalue reference, we should not copy or move the object
+    TrackCopyMove t;
+    auto lambda = [&t](TrackCopyMove&& tm) {
+      assert(&tm == &t);
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 0);
+    };
+    std::function_ref<void(TrackCopyMove&&)> f = lambda;
+    f(std::move(t));
+  }
+  {
+    // Arg type is a prvalue, we should move but not copy the object
+    // In this case, where the type is not trivially copyable, the object should be
+    // moved exactly once when passing into the lambda. The internal functions
+    // of function_ref should forward the argument without copying or moving it
+    auto lambda = [](TrackCopyMove tm) {
+      assert(tm.copy_count == 0);
+      assert(tm.move_count == 1);
+    };
+    std::function_ref<void(TrackCopyMove)> f = lambda;
+    f(TrackCopyMove{});
+  }
+}
+
+int main(int, char**) {
+  test();
+
+  return 0;
+}
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..bcc453880c7e1 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)
-#    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 because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_function_ref
+#    error "__cpp_lib_function_ref should be defined in c++26"
+#  endif
+#  if __cpp_lib_function_ref != 202603L
+#    error "__cpp_lib_function_ref should have the value 202603L 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 20c16700ef76a..f3603c7192a4f 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
@@ -7111,17 +7111,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
-#    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 because it is unimplemented in libc++!"
-#    endif
+#  ifndef __cpp_lib_function_ref
+#    error "__cpp_lib_function_ref should be defined in c++26"
+#  endif
+#  if __cpp_lib_function_ref != 202603L
+#    error "__cpp_lib_function_ref should have the value 202603L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_gcd_lcm
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
new file mode 100644
index 0000000000000..029cb19d54f6a
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.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
+
+#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_ref(int, float) & { return 42; }
+  int fn_mem_const(int, float) const { return 42; }
+  int fn_mem_const_ref(int, float) const& { 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 c, class F>
+  //  function_ref(constant_wrapper<c, F>) -> function_ref<...>;
+  {
+    std::function_ref fn_ref = std::constant_wrapper<fn>();
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    std::function_ref fn_ref = std::constant_wrapper<fn_noexcept>();
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+
+  // template<auto c, class F, class T>
+  //  function_ref(constant_wrapper<c, F>, T&&) -> function_ref<...>;
+  {
+    int arg                  = 0;
+    std::function_ref fn_ref = {std::constant_wrapper<fn>(), arg};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float)>>);
+  }
+  {
+    int arg                  = 0;
+    std::function_ref fn_ref = {std::constant_wrapper<fn_noexcept>(), arg};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float) noexcept>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::data_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int&() noexcept>>);
+  }
+  {
+    const S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::data_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int const&() noexcept>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&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::constant_wrapper<&S::fn_mem_const>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_ref>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_const_ref>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::constant_wrapper<&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/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..b4df4392a2ade
--- /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,115 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_wrapper.
+
+// 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_wrapper<[] {}>>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[](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_wrapper<[] { return 42; }>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::constant_wrapper<[](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_wrapper<[] noexcept {} >>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_wrapper<[](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_wrapper<[] noexcept {}>>);
+static_assert(
+    !std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_wrapper<[](int) noexcept {}>>);
+
+int forty_two() { return 42; }
+
+void test() {
+  {
+    std::function_ref<int()> f(std::cw<[] { return 41; }>);
+    f = std::function_ref<int()>(std::cw<[] { return 42; }>);
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int() > f(std::cw<[] { return 41; }>);
+    f = &forty_two;
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int() > f(std::cw<[] { return 41; }>);
+    f = std::cw<[] { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 41; }>);
+    f = std::cw<[] { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // noexcept
+    std::function_ref<int() noexcept> f(std::cw<[] noexcept { return 41; }>);
+    f = std::cw<[] noexcept { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // const noexcept
+    std::function_ref<int() const noexcept> f(std::cw<[] noexcept { return 41; }>);
+    f = std::cw<[] noexcept { return 42; }>;
+    assert(f() == 42);
+  }
+}
+
+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/constant_wrapper.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
new file mode 100644
index 0000000000000..4b425ee03e286
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper.pass.cpp
@@ -0,0 +1,175 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_wrapper<f>) noexcept;
+
+#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 NonConstInvocable {
+  void operator()() noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>>);
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int)>, std::constant_wrapper<l2>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int)>, std::constant_wrapper<l2>>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int) noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) const>, std::constant_wrapper<l2>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int) const>, std::constant_wrapper<l2>>);
+
+// const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<NonConstInvocable{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_noexcept>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_wrapper<l2_noexcept>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+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::cw<[] {}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    std::function_ref<void()> f = std::cw<[] {}>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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_wrapper_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
new file mode 100644
index 0000000000000..dd6e10e8a3289
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ptr.pass.cpp
@@ -0,0 +1,359 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_wrapper<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_wrapper<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, const int*>);
+
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::g>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A*>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::g_noexcept>, A*>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+static_assert(
+    std::
+        is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2_const>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2_const>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::g_const>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2_const>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A*>);
+
+// const noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_const_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>, int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_const>, int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<NonConstInvocable{}>,
+                                       int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const noexcept>,
+                                      std::constant_wrapper<l2_const_noexcept>,
+                                      int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_const_noexcept>, int*>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f>, A*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const>, A*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<&A::g_const_noexcept>,
+                                       A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+                                              std::constant_wrapper<l1_const_noexcept>,
+                                              int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const noexcept>,
+                                              std::constant_wrapper<l2_const_noexcept>,
+                                              int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+                                              std::constant_wrapper<&A::f_const_noexcept>,
+                                              A*>);
+
+double f1(const int* x, double y) noexcept { return *x + y; }
+
+struct M {
+  int i;
+  int f() { return i; }
+  int f_const() const { return i + 5; }
+  int f_noexcept() noexcept { return i + 7; }
+  int f_const_noexcept() const noexcept { return i + 9; }
+  int g(int& j) {
+    j = 42;
+    return i + j;
+  }
+  int g_const(int& j) const {
+    j = 42;
+    return i + j + 1;
+  }
+  int g_noexcept(int& j) noexcept {
+    j = 42;
+    return i + j + 2;
+  }
+  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 {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+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::cw<[](int*) {}>, &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    int i                       = 0;
+    std::function_ref<void()> f = {std::cw<[](int*) {}>, &i};
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // mutate
+    int i;
+    std::function_ref<double(double)> f(
+        std::cw<[](int* j, double d) {
+          *j = 5;
+          return *j + d;
+        }>,
+        &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(3.3) == 8.3);
+      assert(i == 5);
+    }
+  }
+  {
+    // const
+    int i = 5;
+    std::function_ref<int() const> f(std::cw<[](const int* pi) { return *pi + 42; }>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
+  {
+    // noexcept
+    int i = 5;
+    std::function_ref<double(double) noexcept> f(std::cw<&f1>, &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // const noexcept
+    int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // member ptr
+    M m{3};
+    std::function_ref<int()> f(std::cw<&M::f>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 3);
+    }
+
+    int j = 0;
+    std::function_ref<int(int&)> g(std::cw<&M::g>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g(j) == 45);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const> f_const(std::cw<&M::f_const>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const() == 8);
+    }
+
+    j = 0;
+    std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, &m);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const(j) == 46);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept() == 10);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_noexcept(j) == 47);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept() == 12);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, &m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const_noexcept(j) == 48);
+      assert(j == 42);
+    }
+  }
+  {
+    // with conversions
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
new file mode 100644
index 0000000000000..485fa95ca2998
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -0,0 +1,373 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_wrapper<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_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&&>);
+
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>, int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::g>, A&>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A&>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3>, const int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::g_noexcept>, A&>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+static_assert(
+    std::
+        is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, long&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l3>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l3>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::g_const>, A&>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A&>);
+
+// const noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>, int&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<NonConstInvocable{}>,
+                                       long&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3>, const int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3_noexcept>, const int&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f>, A&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const>, A&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<&A::g_const_noexcept>,
+                                       A&>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A&>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct M {
+  int i;
+  int f() { return i; }
+  int f_const() const { return i + 5; }
+  int f_noexcept() noexcept { return i + 7; }
+  int f_const_noexcept() const noexcept { return i + 9; }
+  int g(int& j) {
+    j = 42;
+    return i + j;
+  }
+  int g_const(int& j) const {
+    j = 42;
+    return i + j + 1;
+  }
+  int g_noexcept(int& j) noexcept {
+    j = 42;
+    return i + j + 2;
+  }
+  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 {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+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::cw<[](int) {}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    int i                       = 0;
+    std::function_ref<void()> f = {std::cw<[](int) {}>, i};
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // mutate
+    int i;
+    std::function_ref<double(double)> f(
+        std::cw<[](int& j, double d) {
+          j = 5;
+          return j + d;
+        }>,
+        i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(3.3) == 8.3);
+      assert(i == 5);
+    }
+  }
+  {
+    // const
+    int i = 5;
+    std::function_ref<int() const> f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
+  {
+    // noexcept
+    int i = 5;
+    std::function_ref<double(double) noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // const noexcept
+    int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // member ptr
+    M m{3};
+    std::function_ref<int()> f(std::cw<&M::f>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 3);
+    }
+
+    int j = 0;
+    std::function_ref<int(int&)> g(std::cw<&M::g>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g(j) == 45);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const> f_const(std::cw<&M::f_const>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const() == 8);
+    }
+
+    j = 0;
+    std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const(j) == 46);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_noexcept() == 10);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_noexcept(j) == 47);
+      assert(j == 42);
+    }
+
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_const_noexcept() == 12);
+    }
+
+    j = 0;
+    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, m);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(g_const_noexcept(j) == 48);
+      assert(j == 42);
+    }
+  }
+  {
+    // with conversions
+    int i = 1;
+
+    std::function_ref<Int(int, int)> f(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<NeedsConversion{}>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const> f2(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<&needs_conversion>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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/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..61b70a559d786
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
@@ -0,0 +1,131 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+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::cw<[] {}>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    auto f2 = f;
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    auto f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    auto f_copy = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    auto f2_copy = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    auto f3_copy = f3;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    auto f4_copy = f4;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    auto f_copy = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    auto f2_copy = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    auto f3_copy = f3;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    auto f4_copy = f4;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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..1a8b26f86c8be
--- /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,158 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>>);
+
+double plus(int x, double y) noexcept { return x + y; }
+double minus(int x, double y) noexcept { return x - y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+int zero(Int, Int, Int) noexcept { return 0; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    std::function_ref<void()> f2(std::cw<[] {}>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
+    f2 = f;
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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::cw<&plus>);
+    std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
+    f2 = f;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
+    f = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
+    f_const = f2_const;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
+    f_noexcept = f2_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
+    f_const_noexcept = f2_const_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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::cw<&zero>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
+    f = f2;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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::cw<&zero>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
+    f_const = f2_const;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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::cw<&zero>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
+    f_noexcept = f2_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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::cw<&zero>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
+    f_const_noexcept = f2_const_noexcept;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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/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..f142b81367ef1
--- /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,149 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_maythrow(int i, A a) { return i - a.i; }
+int fn_noexcept(int i, A a) noexcept { return i + a.i; }
+
+void foo(int) {}
+void bar(int) noexcept {}
+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
+    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 l = [](int x, int y, int z) { return x + y - z; };
+    std::function_ref<int(int, int, int) const> f(+l);
+    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);
+  }
+
+  {
+    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);
+
+    {
+      std::function_ref r1 = foo;
+      std::function_ref r2 = bar;
+      r1                   = r2; // ok
+    }
+  }
+}
+
+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/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..0f27bba821391
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
@@ -0,0 +1,129 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>>);
+
+double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+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::cw<[] {}>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    auto f2 = std::move(f);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
+    auto f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
+    auto f_copy = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
+    auto f2_copy = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
+    auto f3_copy = std::move(f3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
+    auto f4_copy = std::move(f4);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4_copy(1, 2, 3).i == 6);
+    }
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
+    auto f_copy = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
+    auto f2_copy = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
+    auto f3_copy = std::move(f3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3_copy(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
+    auto f4_copy = std::move(f4);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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..b40dfa9f2cf4b
--- /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,156 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>>);
+
+double plus(int x, double y) noexcept { return x + y; }
+double minus(int x, double y) noexcept { return x - y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+int zero(Int, Int, Int) noexcept { return 0; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::cw<[] {}>);
+    std::function_ref<void()> f2(std::cw<[] {}>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+      f2();
+    }
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
+    f2 = std::move(f);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+      assert(f2() == 42);
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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::cw<&plus>);
+    std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
+    f2 = std::move(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
+    f = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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::cw<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
+    f_const = std::move(f2_const);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
+    f_noexcept = std::move(f2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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::cw<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
+    f_const_noexcept = std::move(f2_const_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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::cw<&zero>);
+    std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
+    f = std::move(f2);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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::cw<&zero>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
+    f_const = std::move(f2_const);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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::cw<&zero>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
+    f_noexcept = std::move(f2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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::cw<&zero>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
+    f_const_noexcept = std::move(f2_const_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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/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..56d6dd3b7109b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -0,0 +1,316 @@
+//===----------------------------------------------------------------------===//
+//
+// 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;
+
+  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;
+  int operator()(auto&&...) { return 5 + i; }
+
+  int operator()(auto&&...) const { return 6 + i; }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int fn() { return 5; }
+constexpr auto fn_noexcept = []() noexcept { return 6; };
+
+const auto one = []() noexcept { return 1; };
+const auto two = []() noexcept { return 2; };
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(l1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit(false)
+    std::function_ref<void()> f = l1;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // noexcept
+    std::function_ref<A() noexcept> f(l2_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      auto a = f();
+      assert(a.i == 5);
+    }
+  }
+  {
+    // const
+    std::function_ref<double(int, double) const> f(l3);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2.0) == 3.0);
+    }
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(l3_noexcept);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      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);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 5);
+      assert(local() == 6);
+      assert(f() == 7);
+    }
+  }
+  {
+    // const correctness
+    F f{5};
+
+    std::function_ref<int()> f1(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f1() == 10);
+      assert(std::as_const(f1)() == 10);
+    }
+
+    std::function_ref<int() const> f2(f);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 11);
+      assert(std::as_const(f2)() == 11);
+    }
+  }
+  {
+    // with conversions
+    NeedsConversion c{};
+
+    std::function_ref<Int(int, int, int)> f(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const> f2(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) noexcept> f3(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f3(1, 2, 3).i == 6);
+    }
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(c);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f4(1, 2, 3).i == 6);
+    }
+  }
+
+  {
+    // noexcept conversion
+    std::function_ref<int() noexcept> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const conversion
+    std::function_ref<int() const> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept to noexcept conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int() noexcept> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // const noexcept to const conversion
+    std::function_ref<int() const noexcept> f1(fn_noexcept);
+    std::function_ref<int() const> f2(f1);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 6);
+    }
+  }
+
+  {
+    // P3961R1 Less double indirection in function_ref
+    // double unwrapping
+    std::function_ref<int() const noexcept> f1(std::cw<one>);
+    std::function_ref<int()> f2(f1);
+
+    f1 = std::cw<two>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 1 || f2() == 2);
+      LIBCPP_ASSERT(f2() == 1);
+    }
+  }
+
+  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..1cd0c769ce4d3
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -0,0 +1,362 @@
+//===----------------------------------------------------------------------===//
+//
+// 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; }
+};
+
+struct Big {
+  char c[128];
+};
+
+struct S3Ref {
+  char operator()(Big& b) const noexcept { return b.c[0]; }
+};
+
+struct S3Value {
+  char operator()(Big b) const noexcept { return b.c[0]; }
+};
+
+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::cw<[](int& i) -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly)> f(std::cw<[](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::cw<[](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);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&)> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big)> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+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::cw<[](int& i) -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) const> f(std::cw<[](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::cw<[](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);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) const> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) const> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+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::cw<[](int& i) noexcept -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) noexcept> f(
+        std::cw<[](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::cw<[](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);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) noexcept> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) noexcept> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+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::cw<[](int& i) noexcept -> int& { return i; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) const noexcept> f(
+        std::cw<[](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::cw<[](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);
+  }
+  {
+    // Big object passed
+    Big b{};
+    b.c[0] = 'a';
+    std::function_ref<char(Big&) const noexcept> f1(std::cw<S3Ref{}>);
+    assert(f1(b) == 'a');
+    std::function_ref<char(Big) const noexcept> f2(std::cw<S3Value{}>);
+    assert(f2(b) == 'a');
+  }
+}
+
+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/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>>);
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index d8fb575b97b2f..6ddd89695c1b1 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -703,10 +703,9 @@ def add_version_header(tc):
         {
             "name": "__cpp_lib_function_ref",
             "values": {
-                "c++26": 202306  # P0792R14 function_ref: a type-erased callable reference
+                "c++26": 202603  # P3948R1 constant_wrapper is the only tool needed for passing constant expressions via function arguments
             },
             "headers": ["functional"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_gcd_lcm",

>From 16dd0570f572bc6b0db37894071c4c4ebbd41102 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 25 Apr 2026 14:20:04 +0100
Subject: [PATCH 03/11] review comments

---
 .../__functional/function_ref_common.h        | 22 ++++++-------------
 .../include/__functional/function_ref_impl.h  |  2 +-
 .../internal_call_signature.pass.cpp          |  2 +-
 .../constant_wrapper_ref.pass.cpp             |  8 +++++++
 .../func.wrap.ref.ctor/ref.pass.cpp           | 12 ++++++++++
 5 files changed, 29 insertions(+), 17 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 61979231b56e8..72ded36d65107 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -16,6 +16,7 @@
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
 
@@ -37,21 +38,16 @@ struct __is_convertible_from_specialization : false_type {};
 // pointers
 // todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
 // supported
-union __storage_func_ref_t {
+union __function_ref_storage {
   void* __obj_ptr_;
-  void const* __obj_const_ptr_;
   void (*__fn_ptr_)();
 
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t() noexcept : __obj_ptr_(nullptr) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __function_ref_storage() noexcept : __obj_ptr_(nullptr) {}
 
   template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t(_Tp* __ptr) noexcept {
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __function_ref_storage(_Tp* __ptr) noexcept {
     if constexpr (is_object_v<_Tp>) {
-      if constexpr (is_const_v<_Tp>) {
-        __obj_const_ptr_ = __ptr;
-      } else {
-        __obj_ptr_ = __ptr;
-      }
+      __obj_ptr_ = const_cast<remove_cv_t<_Tp>*>(__ptr);
     } else {
       static_assert(is_function_v<_Tp>);
       __fn_ptr_ = reinterpret_cast<void (*)()>(__ptr);
@@ -59,13 +55,9 @@ union __storage_func_ref_t {
   }
 
   template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_func_ref_t __storage) {
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__function_ref_storage __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_);
-      }
+      return static_cast<_Tp*>(__storage.__obj_ptr_);
     } else {
       static_assert(is_function_v<_Tp>);
       return reinterpret_cast<_Tp*>(__storage.__fn_ptr_);
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index a4c98d3031602..36841d6745955 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -72,7 +72,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   template <class _Arg>
   using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
 
-  using __storage_t _LIBCPP_NODEBUG = __storage_func_ref_t;
+  using __storage_t _LIBCPP_NODEBUG = __function_ref_storage;
 
   using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, __arg_t<_ArgTypes>...) noexcept(__is_noexcept);
 
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
index 05756cfc3b3b8..1e5df6413daf0 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -31,7 +31,7 @@ struct SmallButNonTrivial {
 // specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
 template <>
 class std::function_ref<int> {
-  using storage = std::__storage_func_ref_t;
+  using storage = std::__function_ref_storage;
   void test() {
     {
       // by value small argument
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
index 485fa95ca2998..d3d5f6827193d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_wrapper_ref.pass.cpp
@@ -362,6 +362,14 @@ constexpr bool test() {
       assert(f4(2, 3).i == 6);
     }
   }
+  {
+    // volatile objects
+    volatile int i = 5;
+    std::function_ref<int() > f(std::cw<[](int j) { return j + 42; }>, i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
 
   return true;
 }
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 56d6dd3b7109b..9ab012b3a04e8 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
@@ -161,6 +161,10 @@ constexpr auto fn_noexcept = []() noexcept { return 6; };
 const auto one = []() noexcept { return 1; };
 const auto two = []() noexcept { return 2; };
 
+struct VolatileCallable {
+  int operator()() volatile const { return 42; }
+};
+
 constexpr bool test() {
   {
     std::function_ref<void()> f(l1);
@@ -305,6 +309,14 @@ constexpr bool test() {
       LIBCPP_ASSERT(f2() == 1);
     }
   }
+  {
+    // volatile objects
+    const volatile VolatileCallable vc{};
+    std::function_ref<int()> f(vc);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 42);
+    }
+  }
 
   return true;
 }

>From dcf00e60f179718975446d10247def2b24cf8b80 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 26 Apr 2026 09:41:10 +0100
Subject: [PATCH 04/11] remove stale comments

---
 libcxx/include/__functional/function_ref_common.h | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 72ded36d65107..c5adb8cbc7d52 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -34,10 +34,6 @@ 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
 union __function_ref_storage {
   void* __obj_ptr_;
   void (*__fn_ptr_)();

>From 2bd7e224b7b6584cb4f0e00aaaf2b9837af1e789 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 15 May 2026 14:06:05 +0100
Subject: [PATCH 05/11] fix bug

---
 .../__functional/function_ref_common.h        |  2 +-
 .../include/__functional/function_ref_impl.h  | 19 ++++++++++++-------
 .../internal_call_signature.pass.cpp          |  4 +++-
 .../func.wrap.ref.ctor/assign.delete.pass.cpp |  6 +++---
 .../func.wrap.ref.ctor/ref.pass.cpp           | 11 +++++++++++
 5 files changed, 30 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index c5adb8cbc7d52..60bc6ef87af4f 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -31,7 +31,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
-template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
+template <class _Fn1, class _Fn2>
 struct __is_convertible_from_specialization : false_type {};
 
 union __function_ref_storage {
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 36841d6745955..55755e9804c95 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -46,14 +46,19 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
-template <bool _NoExcept2, bool _NoExcept1, class _Rp, class... _ArgTypes>
+template <bool _NoExcept1, bool _NoExcept2, class _Rp, class... _ArgTypes>
 struct __is_convertible_from_specialization<
-    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept2)>,
-    _NoExcept1,
-    _Rp,
-    _ArgTypes...>
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept1)>,
+    function_ref<_Rp(_ArgTypes...) const noexcept(_NoExcept2)> >
     : is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)> {};
 
+template <bool _NoExcept1, bool _NoExcept2, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization<
+    function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept1)>,
+    function_ref<_Rp(_ArgTypes...) noexcept(_NoExcept2)> >
+    : _And<is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)>,
+           is_convertible<_LIBCPP_FUNCTION_REF_CV int&, int&>> {};
+
 template <class _Rp, class... _ArgTypes, bool __is_noexcept>
 class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
 private:
@@ -62,9 +67,9 @@ 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;
 
-  template <class _Fn>
+  template <class _Fn2>
   static constexpr bool __is_convertible_from_specialization_v =
-      __is_convertible_from_specialization<_Fn, __is_noexcept, _Rp, _ArgTypes...>::value;
+      __is_convertible_from_specialization<function_ref, _Fn2>::value;
 
   template <class... _Tp>
   friend class function_ref;
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
index 1e5df6413daf0..ebdf683791694 100644
--- a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/internal_call_signature.pass.cpp
@@ -8,7 +8,9 @@
 
 // REQUIRES: std-at-least-c++26
 
-// Track number of moves
+// In case the function signature is taking an argument by value,
+// when the type is small and trivial, we pass it internally by value,
+// otherwise, we pass it by rvalue reference
 
 #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
index b4df4392a2ade..cb84b3a183920 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
@@ -35,9 +35,9 @@ static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_wrap
 static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[](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() 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 (*)()>);
@@ -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/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 9ab012b3a04e8..6b8b7c71f0d08 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
@@ -309,6 +309,17 @@ constexpr bool test() {
       LIBCPP_ASSERT(f2() == 1);
     }
   }
+  {
+    // P3961R1 Less double indirection in function_ref
+    // is-convertible-from-specialization<remove_cv_t<T>> is false
+    std::function_ref<int()> f1(std::cw<one>);
+    std::function_ref<int() const> f2(f1);
+
+    f1 = std::cw<two>;
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2() == 2);
+    }
+  }
   {
     // volatile objects
     const volatile VolatileCallable vc{};

>From b43c2361ce96ea79b2267e4064931be7576d7038 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 15 May 2026 17:28:36 +0100
Subject: [PATCH 06/11] ci

---
 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 55755e9804c95..454b0ffb4afb3 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -15,6 +15,7 @@
 #include <__functional/invoke.h>
 #include <__memory/addressof.h>
 #include <__type_traits/conditional.h>
+#include <__type_traits/conjunction.h>
 #include <__type_traits/invoke.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_convertible.h>

>From 02fb0e83dd121d1efa26761ec935003ec321bfcf Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 23 May 2026 10:39:44 +0100
Subject: [PATCH 07/11] static operator

---
 .../include/__functional/function_ref_impl.h  | 23 +++++++++++----
 .../func.wrap.ref.ctor/ref.pass.cpp           | 28 +++++++++++++++++++
 2 files changed, 45 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 454b0ffb4afb3..ab65382782fb2 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -26,6 +26,7 @@
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_void.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_pointer.h>
 #include <__type_traits/remove_reference.h>
@@ -99,12 +100,22 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   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_(std::addressof(__obj)),
-        __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-          return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
-        }) {}
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept {
+    using _Dn = remove_cv_t<_Tp>;
+    if constexpr (requires(__arg_t<_ArgTypes>... __args) {
+                    _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+                  }) {
+      __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+        return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+      };
+    } else {
+      __storage_ = __storage_t(std::addressof(__obj)),
+      __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
+        _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
+        return __obj1(static_cast<__arg_t<_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> &&
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 6b8b7c71f0d08..2684ec3548f8e 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
@@ -328,6 +328,34 @@ constexpr bool test() {
       assert(f() == 42);
     }
   }
+  {
+    // static call operator
+    struct StaticCall {
+      static int operator()(int x, int y) { return x + y; }
+    };
+    StaticCall sc;
+    std::function_ref<int(int, int)> f(sc);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(1, 2) == 3);
+    }
+  }
+  {
+    // overload set with static and non-static call operator
+    struct OverloadSet {
+      static int operator()(int x) { return x + 1; }
+      int operator()(long x) const { return x + 2; }
+    };
+    OverloadSet os;
+    std::function_ref<int(int)> f1(os);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f1(1) == 2);
+    }
+
+    std::function_ref<int(long)> f2(os);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f2(1L) == 3);
+    }
+  }
 
   return true;
 }

>From 79595202684aae99418029b490ab40901e567202 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 23 May 2026 19:26:38 +0100
Subject: [PATCH 08/11] work around clang 22 bug

---
 libcxx/include/__functional/function_ref_common.h             | 4 ++++
 libcxx/include/__functional/function_ref_impl.h               | 4 +---
 .../func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp   | 1 +
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 60bc6ef87af4f..bfe3d938056f0 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -19,6 +19,7 @@
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
+#include <__utility/declval.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -107,6 +108,9 @@ struct __function_ref_arg_fwd<_Arg> {
   using type _LIBCPP_NODEBUG = _Arg;
 };
 
+template <class _Fn, class... _Args>
+concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
+
 #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 ab65382782fb2..7a173dee7fa4f 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -102,9 +102,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
              __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept {
     using _Dn = remove_cv_t<_Tp>;
-    if constexpr (requires(__arg_t<_ArgTypes>... __args) {
-                    _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
-                  }) {
+    if constexpr (__statically_callable<_Dn, __arg_t<_ArgTypes>...>) {
       __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
       };
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 2684ec3548f8e..11b993e4a3ec3 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
@@ -351,6 +351,7 @@ constexpr bool test() {
       assert(f1(1) == 2);
     }
 
+    LIBCPP_STATIC_ASSERT(!std::__statically_callable<OverloadSet, long>);
     std::function_ref<int(long)> f2(os);
     if (!TEST_IS_CONSTANT_EVALUATED) {
       assert(f2(1L) == 3);

>From c066447880e200824aa80d150814828987333620 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:12:34 +0100
Subject: [PATCH 09/11] review comments

---
 libcxx/include/__functional/function_ref_common.h | 15 +++++++--------
 libcxx/include/__functional/function_ref_impl.h   |  6 +++---
 .../constant_arg.mandates.verify.cpp              |  2 +-
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index bfe3d938056f0..0f40a07073cfc 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -15,7 +15,8 @@
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_reference.h>
-#include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/is_trivially_constructible.h>
+#include <__type_traits/is_trivially_destructible.h>
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/constant_wrapper.h>
@@ -98,15 +99,13 @@ template <auto _Value>
 constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
 
 template <class _Arg>
-struct __function_ref_arg_fwd {
-  using type _LIBCPP_NODEBUG = _Arg&&;
-};
+concept __itanium_trivial_for_calls =
+    is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
+    is_trivially_move_constructible_v<_Arg>;
 
 template <class _Arg>
-  requires(!is_reference_v<_Arg> && is_trivially_copyable_v<_Arg> && sizeof(_Arg) <= 16)
-struct __function_ref_arg_fwd<_Arg> {
-  using type _LIBCPP_NODEBUG = _Arg;
-};
+concept __register_passable =
+    !is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
 
 template <class _Fn, class... _Args>
 concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 7a173dee7fa4f..3424a99bdd174 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -77,7 +77,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   friend class function_ref;
 
   template <class _Arg>
-  using __arg_t _LIBCPP_NODEBUG = typename __function_ref_arg_fwd<_Arg>::type;
+  using __arg_t _LIBCPP_NODEBUG = _If<__register_passable<_Arg>, _Arg, _Arg&&>;
 
   using __storage_t _LIBCPP_NODEBUG = __function_ref_storage;
 
@@ -135,8 +135,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
           !requires {
             typename constant_wrapper<std::invoke(decltype(__f)::value, remove_cvref_t<_ArgTypes>::value...)>;
           },
-          "function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments "
-          "and produces a result with a structural type");
+          "cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref "
+          "constructed from cw would be ambiguous");
     }
   }
 
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
index 519957eec6878..63cfbc0e97c9b 100644
--- 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
@@ -41,7 +41,7 @@ void test() {
   // expected-error@*:* {{static assertion failed due to requirement '__f.value != nullptr': the function pointer should not be a nullptr}}
 
   std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
-  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments and produces a result with a structural type}}
+  // expected-error@*:* {{static assertion failed due to requirement '!requires { std::constant_wrapper<std::__cw_fixed_value<int>{42}, int>; }': cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
 
   int i;
   std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}

>From e2c9027b66368ef112163f45d2d9cb04580743bf Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:13:48 +0100
Subject: [PATCH 10/11] use std::forward instead of static_cast

---
 libcxx/include/__functional/function_ref_impl.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 3424a99bdd174..d276c78fd09e6 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -92,7 +92,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
       : __storage_(__fn_ptr),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return __storage_t::template __get<_Fp>(__storage)(static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return __storage_t::template __get<_Fp>(__storage)(std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
   }
@@ -104,13 +104,13 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     using _Dn = remove_cv_t<_Tp>;
     if constexpr (__statically_callable<_Dn, __arg_t<_ArgTypes>...>) {
       __call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-        return _Dn::operator()(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        return _Dn::operator()(std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     } else {
       __storage_ = __storage_t(std::addressof(__obj)),
       __call_    = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
         _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-        return __obj1(static_cast<__arg_t<_ArgTypes>>(__args)...);
+        return __obj1(std::forward<__arg_t<_ArgTypes>>(__args)...);
       };
     }
   }
@@ -125,7 +125,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     requires __is_invocable_using<const _Fn&>
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f) noexcept
       : __call_([](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
-          return std::invoke_r<_Rp>(decltype(__f)::value, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
@@ -146,7 +146,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       : __storage_(std::addressof(__obj)),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           _LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
-          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
@@ -160,7 +160,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       : __storage_(__obj_ptr_),
         __call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
           auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
-          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, static_cast<__arg_t<_ArgTypes>>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, std::forward<__arg_t<_ArgTypes>>(__args)...);
         }) {
     if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
       static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");

>From 887426bd2002840e89232c381b1129e5e76ee688 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 6 Jun 2026 13:14:02 +0100
Subject: [PATCH 11/11] format

---
 .../func.wrap.ref.ctor/constant_arg.mandates.verify.cpp      | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

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
index 63cfbc0e97c9b..c84cca2793bac 100644
--- 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
@@ -17,8 +17,9 @@
 
 // Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
 
-// For the first overload,
-// f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>... satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)> is not a valid type.
+// For the first overload, f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>...
+// satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)>
+// is not a valid type.
 
 #include <functional>
 #include <utility>



More information about the libcxx-commits mailing list