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

via libcxx-commits libcxx-commits at lists.llvm.org
Sat Apr 18 07:08:16 PDT 2026


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

>From 497e38b5fd140484689b2f216d76e86c0633acc3 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/26] [libcxx] Implement `std::constant_wrapper`

ci

ci

review comments

review comments

ci

review comments
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +
 libcxx/docs/ReleaseNotes/23.rst               |   2 +
 libcxx/docs/Status/Cxx2cIssues.csv            |   8 +-
 libcxx/docs/Status/Cxx2cPapers.csv            |   4 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__utility/constant_wrapper.h   | 338 +++++++++++
 libcxx/include/module.modulemap.in            |   1 +
 libcxx/include/span                           |   2 +-
 libcxx/include/utility                        |  17 +
 libcxx/include/version                        |   2 +
 .../views.span/span.cons/deduct.pass.cpp      |   8 +
 .../utility.version.compile.pass.cpp          |  27 +
 .../version.version.compile.pass.cpp          |  27 +
 .../const.wrap.class/adl.compile.pass.cpp     |  30 +
 .../const.wrap.class/assign.pass.cpp          |  84 +++
 .../const.wrap.class/binary_ops.pass.cpp      | 566 ++++++++++++++++++
 .../utilities/const.wrap.class/call.pass.cpp  | 238 ++++++++
 .../utilities/const.wrap.class/comma.pass.cpp |  62 ++
 .../utilities/const.wrap.class/comp.pass.cpp  | 410 +++++++++++++
 .../const.wrap.class/convert.pass.cpp         |  85 +++
 .../const.wrap.class/ctad.compile.pass.cpp    |  32 +
 .../utilities/const.wrap.class/cw.pass.cpp    |  71 +++
 .../cw_fixed.array.ctor.pass.cpp              |  86 +++
 .../const.wrap.class/cw_fixed.ctor.pass.cpp   |  70 +++
 .../const.wrap.class/general.pass.cpp         |  42 ++
 .../std/utilities/const.wrap.class/helpers.h  |  21 +
 .../const.wrap.class/mem_ptr.pass.cpp         | 114 ++++
 .../const.wrap.class/pseudo_mutators.pass.cpp | 440 ++++++++++++++
 .../const.wrap.class/subscript.pass.cpp       | 183 ++++++
 .../const.wrap.class/types.compile.pass.cpp   |  38 ++
 .../const.wrap.class/unary_ops.pass.cpp       | 246 ++++++++
 .../generate_feature_test_macro_components.py |   7 +
 32 files changed, 3257 insertions(+), 7 deletions(-)
 create mode 100644 libcxx/include/__utility/constant_wrapper.h
 create mode 100644 libcxx/test/std/utilities/const.wrap.class/adl.compile.pass.cpp
 create mode 100644 libcxx/test/std/utilities/const.wrap.class/assign.pass.cpp
 create mode 100644 libcxx/test/std/utilities/const.wrap.class/binary_ops.pass.cpp
 create mode 100644 libcxx/test/std/utilities/const.wrap.class/call.pass.cpp
 create mode 100644 libcxx/test/std/utilities/const.wrap.class/comma.pass.cpp
 create mode 100644 libcxx/test/std/utilities/const.wrap.class/comp.pass.cpp
 create mode 100644 libcxx/test/std/utilities/const.wrap.class/convert.pass.cpp
 create mode 100644 libcxx/test/std/utilities/const.wrap.class/ctad.compile.pass.cpp
 create mode 100644 libcxx/test/std/utilities/const.wrap.class/cw.pass.cpp
 create mode 100644 libcxx/test/std/utilities/const.wrap.class/cw_fixed.array.ctor.pass.cpp
 create mode 100644 libcxx/test/std/utilities/const.wrap.class/cw_fixed.ctor.pass.cpp
 create mode 100644 libcxx/test/std/utilities/const.wrap.class/general.pass.cpp
 create mode 100644 libcxx/test/std/utilities/const.wrap.class/helpers.h
 create mode 100644 libcxx/test/std/utilities/const.wrap.class/mem_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/const.wrap.class/pseudo_mutators.pass.cpp
 create mode 100644 libcxx/test/std/utilities/const.wrap.class/subscript.pass.cpp
 create mode 100644 libcxx/test/std/utilities/const.wrap.class/types.compile.pass.cpp
 create mode 100644 libcxx/test/std/utilities/const.wrap.class/unary_ops.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 94342eb4a4f0a..dcdf385ace6f6 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -426,6 +426,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_bitset``                                       ``202306L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_constant_wrapper``                             ``202603L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_constexpr_algorithms``                         ``202306L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_constexpr_flat_map``                           ``202502L``
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index c21b378f7db4b..e79c01e937836 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -42,6 +42,8 @@ Implemented Papers
 - P3936R1: Safer ``atomic_ref::address`` (`Github <https://llvm.org/PR189594>`__)
 - P3953R3: Rename ``std::runtime_format`` (`Github <https://llvm.org/PR189624>`__)
 - P4052R0: Renaming saturation arithmetic functions (`Github <https://llvm.org/PR189589>`__)
+- P2781R9: ``std::constant_wrapper`` (`Github <https://llvm.org/PR148179>`__)
+- P3978R3: ``constant_wrapper`` should unwrap on call and subscript (`Github <https://llvm.org/PR189605>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index a4afad4efa884..686e4d56bf21f 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -209,7 +209,7 @@
 "`LWG4376 <https://wg21.link/LWG4376>`__","ABI tag in return type of [simd.mask.unary] is overconstrained","2025-11 (Kona)","","","`#171367 <https://github.com/llvm/llvm-project/issues/171367>`__",""
 "`LWG4377 <https://wg21.link/LWG4377>`__","Misleading note about lock-free property of ``std::atomic_ref``","2025-11 (Kona)","","","`#171368 <https://github.com/llvm/llvm-project/issues/171368>`__",""
 "`LWG4382 <https://wg21.link/LWG4382>`__","The ``simd::basic_mask(bool)`` overload needs to be more constrained","2025-11 (Kona)","","","`#171369 <https://github.com/llvm/llvm-project/issues/171369>`__",""
-"`LWG4383 <https://wg21.link/LWG4383>`__","``constant_wrapper``\'s pseudo-mutators are underconstrained","2025-11 (Kona)","","","`#171370 <https://github.com/llvm/llvm-project/issues/171370>`__",""
+"`LWG4383 <https://wg21.link/LWG4383>`__","``constant_wrapper``\'s pseudo-mutators are underconstrained","2025-11 (Kona)","|Complete|","23","`#171370 <https://github.com/llvm/llvm-project/issues/171370>`__",""
 "`LWG4384 <https://wg21.link/LWG4384>`__","``flat_set::erase(iterator)`` is underconstrained","2025-11 (Kona)","","","`#171371 <https://github.com/llvm/llvm-project/issues/171371>`__",""
 "`LWG4388 <https://wg21.link/LWG4388>`__","Align new definition of ``va_start`` with C23","2025-11 (Kona)","","","`#171372 <https://github.com/llvm/llvm-project/issues/171372>`__",""
 "`LWG4396 <https://wg21.link/LWG4396>`__","Improve ``inplace_vector(from_range_t, R&& rg)``","2025-11 (Kona)","","","`#171373 <https://github.com/llvm/llvm-project/issues/171373>`__",""
@@ -293,7 +293,7 @@
 "`LWG4457 <https://wg21.link/LWG4457>`__","freestanding for ``stable_sort``, ``stable_partition`` and ``inplace_merge``","2026-03 (Croydon)","","","`#189835 <https://github.com/llvm/llvm-project/issues/189835>`__",""
 "`LWG4460 <https://wg21.link/LWG4460>`__","Missing *Throws:* for last ``variant`` constructor","2026-03 (Croydon)","","","`#189836 <https://github.com/llvm/llvm-project/issues/189836>`__",""
 "`LWG4467 <https://wg21.link/LWG4467>`__","``hive::splice`` can throw ``bad_alloc``","2026-03 (Croydon)","","","`#189837 <https://github.com/llvm/llvm-project/issues/189837>`__",""
-"`LWG4468 <https://wg21.link/LWG4468>`__","§[const.wrap.class] ""``operator decltype(auto)``"" is ill-formed","2026-03 (Croydon)","","","`#189838 <https://github.com/llvm/llvm-project/issues/189838>`__",""
+"`LWG4468 <https://wg21.link/LWG4468>`__","§[const.wrap.class] ""``operator decltype(auto)``"" is ill-formed","2026-03 (Croydon)","|Complete|","23","`#189838 <https://github.com/llvm/llvm-project/issues/189838>`__",""
 "`LWG4469 <https://wg21.link/LWG4469>`__","Names of parameters of addressable function shall remain unspecified","2026-03 (Croydon)","","","`#189839 <https://github.com/llvm/llvm-project/issues/189839>`__",""
 "`LWG4472 <https://wg21.link/LWG4472>`__","``std::atomic_ref<const T>`` can be constructed from temporaries","2026-03 (Croydon)","","","`#189840 <https://github.com/llvm/llvm-project/issues/189840>`__",""
 "`LWG4474 <https://wg21.link/LWG4474>`__","""``round_to_nearest``"" rounding mode is unclear","2026-03 (Croydon)","","","`#189841 <https://github.com/llvm/llvm-project/issues/189841>`__",""
@@ -311,7 +311,7 @@
 "`LWG4496 <https://wg21.link/LWG4496>`__","Precedes vs Reachable in [meta.reflection]","2026-03 (Croydon)","","","`#189853 <https://github.com/llvm/llvm-project/issues/189853>`__",""
 "`LWG4497 <https://wg21.link/LWG4497>`__","``std::nullopt_t`` should be comparable","2026-03 (Croydon)","","","`#189854 <https://github.com/llvm/llvm-project/issues/189854>`__",""
 "`LWG4499 <https://wg21.link/LWG4499>`__","``flat_set::insert_range`` specification may be problematic","2026-03 (Croydon)","","","`#189855 <https://github.com/llvm/llvm-project/issues/189855>`__",""
-"`LWG4500 <https://wg21.link/LWG4500>`__","``constant_wrapper`` wording problems","2026-03 (Croydon)","","","`#189856 <https://github.com/llvm/llvm-project/issues/189856>`__",""
+"`LWG4500 <https://wg21.link/LWG4500>`__","``constant_wrapper`` wording problems","2026-03 (Croydon)","|Complete|","23","`#189856 <https://github.com/llvm/llvm-project/issues/189856>`__",""
 "`LWG4504 <https://wg21.link/LWG4504>`__","Wording problem in ``{simple_}counting_scope``","2026-03 (Croydon)","","","`#189857 <https://github.com/llvm/llvm-project/issues/189857>`__",""
 "`LWG4506 <https://wg21.link/LWG4506>`__","``source_location`` is explicitly unspecified if is constexpr or not","2026-03 (Croydon)","","","`#189858 <https://github.com/llvm/llvm-project/issues/189858>`__",""
 "`LWG4510 <https://wg21.link/LWG4510>`__","Ambiguity of ``std::ranges::advance`` and ``std::ranges::next`` when the difference type is also a sentinel type","2026-03 (Croydon)","","","`#189859 <https://github.com/llvm/llvm-project/issues/189859>`__",""
@@ -320,7 +320,7 @@
 "`LWG4514 <https://wg21.link/LWG4514>`__","Missing absolute value of ``init`` in ``vector_two_norm`` and ``matrix_frob_norm``","2026-03 (Croydon)","","","`#189862 <https://github.com/llvm/llvm-project/issues/189862>`__",""
 "`LWG4517 <https://wg21.link/LWG4517>`__","``data_member_spec`` should throw for *cv*-qualified unnamed bit-fields","2026-03 (Croydon)","","","`#189863 <https://github.com/llvm/llvm-project/issues/189863>`__",""
 "`LWG4522 <https://wg21.link/LWG4522>`__","Clarify that ``std::format`` transcodes for ``std::wformat_string``\s","2026-03 (Croydon)","","","`#189864 <https://github.com/llvm/llvm-project/issues/189864>`__",""
-"`LWG4523 <https://wg21.link/LWG4523>`__","``constant_wrapper`` should assign to value","2026-03 (Croydon)","","","`#189865 <https://github.com/llvm/llvm-project/issues/189865>`__",""
+"`LWG4523 <https://wg21.link/LWG4523>`__","``constant_wrapper`` should assign to value","2026-03 (Croydon)","|Complete|","23","`#189865 <https://github.com/llvm/llvm-project/issues/189865>`__",""
 "`LWG4525 <https://wg21.link/LWG4525>`__","``task``'s ``final_suspend`` should move the result","2026-03 (Croydon)","","","`#189866 <https://github.com/llvm/llvm-project/issues/189866>`__",""
 "`LWG4527 <https://wg21.link/LWG4527>`__","``await_transform`` needs to use ``as_awaitable``","2026-03 (Croydon)","","","`#189867 <https://github.com/llvm/llvm-project/issues/189867>`__",""
 "`LWG4528 <https://wg21.link/LWG4528>`__","``task`` needs ``get_completion_signatures()``","2026-03 (Croydon)","","","`#189868 <https://github.com/llvm/llvm-project/issues/189868>`__",""
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 7adf5a2122e62..6b706ab036e01 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -152,7 +152,7 @@
 "`P3060R3 <https://wg21.link/P3060R3>`__","Add ``std::views::indices(n)``","2025-06 (Sofia)","|Complete|","22","`#148175 <https://github.com/llvm/llvm-project/issues/148175>`__",""
 "`P2319R5 <https://wg21.link/P2319R5>`__","Prevent ``path`` presentation problems","2025-06 (Sofia)","","","`#148177 <https://github.com/llvm/llvm-project/issues/148177>`__",""
 "`P3223R2 <https://wg21.link/P3223R2>`__","Making ``std::istream::ignore`` less surprising","2025-06 (Sofia)","|Complete|","22","`#148178 <https://github.com/llvm/llvm-project/issues/148178>`__",""
-"`P2781R9 <https://wg21.link/P2781R9>`__","``std::constant_wrapper``","2025-06 (Sofia)","","","`#148179 <https://github.com/llvm/llvm-project/issues/148179>`__",""
+"`P2781R9 <https://wg21.link/P2781R9>`__","``std::constant_wrapper``","2025-06 (Sofia)","|Complete|","23","`#148179 <https://github.com/llvm/llvm-project/issues/148179>`__",""
 "`P3697R1 <https://wg21.link/P3697R1>`__","Minor additions to C++26 standard library hardening","2025-06 (Sofia)","","","`#148180 <https://github.com/llvm/llvm-project/issues/148180>`__",""
 "`P3552R3 <https://wg21.link/P3552R3>`__","Add a Coroutine Task Type","2025-06 (Sofia)","","","`#148182 <https://github.com/llvm/llvm-project/issues/148182>`__",""
 "`P1317R2 <https://wg21.link/P1317R2>`__","Remove return type deduction in ``std::apply``","2025-06 (Sofia)","","","`#148183 <https://github.com/llvm/llvm-project/issues/148183>`__",""
@@ -193,7 +193,7 @@
 "`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>`__",""
-"`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","","","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
+"`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>`__",""
 "`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>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 53165f0336b2d..712ad8bf66593 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -926,6 +926,7 @@ set(files
   __utility/as_lvalue.h
   __utility/auto_cast.h
   __utility/cmp.h
+  __utility/constant_wrapper.h
   __utility/convert_to_integral.h
   __utility/declval.h
   __utility/default_three_way_comparator.h
diff --git a/libcxx/include/__utility/constant_wrapper.h b/libcxx/include/__utility/constant_wrapper.h
new file mode 100644
index 0000000000000..2f7f441eaeba2
--- /dev/null
+++ b/libcxx/include/__utility/constant_wrapper.h
@@ -0,0 +1,338 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___UTILITY_CONSTANT_WRAPPER_H
+#define _LIBCPP___UTILITY_CONSTANT_WRAPPER_H
+
+#include <__config>
+#include <__cstddef/size_t.h>
+#include <__functional/invoke.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <__utility/integer_sequence.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 _Tp>
+struct __cw_fixed_value {
+  using __type _LIBCPP_NODEBUG = _Tp;
+  _LIBCPP_HIDE_FROM_ABI constexpr __cw_fixed_value(__type __v) noexcept : __data(__v) {}
+  _Tp __data;
+};
+
+template <class _Tp, size_t _Extent>
+struct __cw_fixed_value<_Tp[_Extent]> {
+  using __type _LIBCPP_NODEBUG = _Tp[_Extent];
+  _Tp __data[_Extent];
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __cw_fixed_value(_Tp (&__arr)[_Extent]) noexcept
+      : __cw_fixed_value(__arr, make_index_sequence<_Extent>{}) {}
+
+private:
+  template <size_t... _Idxs>
+  _LIBCPP_HIDE_FROM_ABI constexpr __cw_fixed_value(_Tp (&__arr)[_Extent], index_sequence<_Idxs...>) noexcept
+      : __data{__arr[_Idxs]...} {}
+};
+
+template <class _Tp, size_t _Extent>
+__cw_fixed_value(_Tp (&)[_Extent]) -> __cw_fixed_value<_Tp[_Extent]>;
+
+template <__cw_fixed_value _Xp,
+#  ifdef _LIBCPP_COMPILER_GCC
+          // gcc bug:  https://gcc.gnu.org/PR117392
+          class = typename decltype(__cw_fixed_value(_Xp))::__type
+#  else
+          class = typename decltype(_Xp)::__type
+#  endif
+          >
+struct constant_wrapper;
+
+template <class _Tp>
+concept __constexpr_param = requires { typename constant_wrapper<_Tp::value>; };
+
+template <__cw_fixed_value _Xp>
+constexpr auto cw = constant_wrapper<_Xp>{};
+
+struct __cw_operators {
+  // unary operators
+  template <__constexpr_param _Tp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator+(_Tp) noexcept -> constant_wrapper<(+_Tp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Tp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator-(_Tp) noexcept -> constant_wrapper<(-_Tp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Tp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator~(_Tp) noexcept -> constant_wrapper<(~_Tp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Tp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator!(_Tp) noexcept -> constant_wrapper<(!_Tp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Tp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator&(_Tp) noexcept -> constant_wrapper<(&_Tp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Tp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator*(_Tp) noexcept -> constant_wrapper<(*_Tp::value)> {
+    return {};
+  }
+
+  // binary operators
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator+(_Lp, _Rp) noexcept
+      -> constant_wrapper<(_Lp::value + _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator-(_Lp, _Rp) noexcept
+      -> constant_wrapper<(_Lp::value - _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator*(_Lp, _Rp) noexcept
+      -> constant_wrapper<(_Lp::value * _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator/(_Lp, _Rp) noexcept
+      -> constant_wrapper<(_Lp::value / _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator%(_Lp, _Rp) noexcept
+      -> constant_wrapper<(_Lp::value % _Rp::value)> {
+    return {};
+  }
+
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<<(_Lp, _Rp) noexcept
+      -> constant_wrapper<(_Lp::value << _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator>>(_Lp, _Rp) noexcept
+      -> constant_wrapper<(_Lp::value >> _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator&(_Lp, _Rp) noexcept
+      -> constant_wrapper<(_Lp::value & _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator|(_Lp, _Rp) noexcept
+      -> constant_wrapper<(_Lp::value | _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator^(_Lp, _Rp) noexcept
+      -> constant_wrapper<(_Lp::value ^ _Rp::value)> {
+    return {};
+  }
+
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+    requires(!is_constructible_v<bool, decltype(_Lp::value)> || !is_constructible_v<bool, decltype(_Rp::value)>)
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator&&(_Lp, _Rp) noexcept
+      -> constant_wrapper<(_Lp::value && _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+    requires(!is_constructible_v<bool, decltype(_Lp::value)> || !is_constructible_v<bool, decltype(_Rp::value)>)
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator||(_Lp, _Rp) noexcept
+      -> constant_wrapper<(_Lp::value || _Rp::value)> {
+    return {};
+  }
+
+  // comparisons
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(_Lp, _Rp) noexcept
+      -> constant_wrapper<(_Lp::value <=> _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<(_Lp, _Rp) noexcept
+      -> constant_wrapper<(_Lp::value < _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=(_Lp, _Rp) noexcept
+      -> constant_wrapper<(_Lp::value <= _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator==(_Lp, _Rp) noexcept
+      -> constant_wrapper<(_Lp::value == _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator!=(_Lp, _Rp) noexcept
+      -> constant_wrapper<(_Lp::value != _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator>(_Lp, _Rp) noexcept
+      -> constant_wrapper<(_Lp::value > _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator>=(_Lp, _Rp) noexcept
+      -> constant_wrapper<(_Lp::value >= _Rp::value)> {
+    return {};
+  }
+
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator,(_Lp, _Rp) noexcept = delete;
+  template <__constexpr_param _Lp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator->*(_Lp, _Rp) noexcept
+      -> constant_wrapper<(_Lp::value->*_Rp::value)> {
+    return {};
+  }
+
+  // pseudo-mutators
+  template <__constexpr_param _Tp>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator++(this _Tp) noexcept -> constant_wrapper<(++_Tp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Tp>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator++(this _Tp, int) noexcept -> constant_wrapper<(_Tp::value++)> {
+    return {};
+  }
+  template <__constexpr_param _Tp>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator--(this _Tp) noexcept -> constant_wrapper<(--_Tp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Tp>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator--(this _Tp, int) noexcept -> constant_wrapper<(_Tp::value--)> {
+    return {};
+  }
+
+  template <__constexpr_param _Tp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator+=(this _Tp, _Rp) noexcept
+      -> constant_wrapper<(_Tp::value += _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Tp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator-=(this _Tp, _Rp) noexcept
+      -> constant_wrapper<(_Tp::value -= _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Tp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator*=(this _Tp, _Rp) noexcept
+      -> constant_wrapper<(_Tp::value *= _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Tp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator/=(this _Tp, _Rp) noexcept
+      -> constant_wrapper<(_Tp::value /= _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Tp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator%=(this _Tp, _Rp) noexcept
+      -> constant_wrapper<(_Tp::value %= _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Tp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator&=(this _Tp, _Rp) noexcept
+      -> constant_wrapper<(_Tp::value &= _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Tp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator|=(this _Tp, _Rp) noexcept
+      -> constant_wrapper<(_Tp::value |= _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Tp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator^=(this _Tp, _Rp) noexcept
+      -> constant_wrapper<(_Tp::value ^= _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Tp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator<<=(this _Tp, _Rp) noexcept
+      -> constant_wrapper<(_Tp::value <<= _Rp::value)> {
+    return {};
+  }
+  template <__constexpr_param _Tp, __constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator>>=(this _Tp, _Rp) noexcept
+      -> constant_wrapper<(_Tp::value >>= _Rp::value)> {
+    return {};
+  }
+};
+
+template <__cw_fixed_value _Xp, class>
+struct constant_wrapper : __cw_operators {
+  static constexpr const auto& value = _Xp.__data;
+  using type                         = constant_wrapper;
+  using value_type                   = decltype(_Xp)::__type;
+
+private:
+  template <class... _Args>
+    requires(__constexpr_param<remove_cvref_t<_Args>> && ...)
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __call(_Args&&...) noexcept
+      -> constant_wrapper<std::invoke(value, remove_cvref_t<_Args>::value...)> {
+    return {};
+  }
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  __call(_Args&&... __args) noexcept(noexcept(std::invoke(value, std::forward<_Args>(__args)...)))
+      -> decltype(std::invoke(value, std::forward<_Args>(__args)...)) {
+    return std::invoke(value, std::forward<_Args>(__args)...);
+  }
+
+  template <class... _Args>
+    requires(__constexpr_param<remove_cvref_t<_Args>> && ...)
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __subscript(_Args&&...) noexcept
+      -> constant_wrapper<value[remove_cvref_t<_Args>::value...]> {
+    return {};
+  }
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  __subscript(_Args&&... __args) noexcept(noexcept(value[std::forward<_Args>(__args)...]))
+      -> decltype(value[std::forward<_Args>(__args)...]) {
+    return value[std::forward<_Args>(__args)...];
+  }
+
+public:
+  template <__constexpr_param _Rp>
+  _LIBCPP_HIDE_FROM_ABI constexpr auto operator=(_Rp) const noexcept -> constant_wrapper<(value = _Rp::value)> {
+    return {};
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr operator decltype(value)() const noexcept { return value; }
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator()(_Args&&... __args) noexcept(noexcept(constant_wrapper::__call(std::forward<_Args>(__args)...)))
+      -> decltype(constant_wrapper::__call(std::forward<_Args>(__args)...)) {
+    return __call(std::forward<_Args>(__args)...);
+  }
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  operator[](_Args&&... __args) noexcept(noexcept(constant_wrapper::__subscript(std::forward<_Args>(__args)...)))
+      -> decltype(constant_wrapper::__subscript(std::forward<_Args>(__args)...)) {
+    return __subscript(std::forward<_Args>(__args)...);
+  }
+};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___UTILITY_CONSTANT_WRAPPER_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d6e8289b7c8b0..5748cc6f218a8 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2179,6 +2179,7 @@ module std [system] {
       export std_core.type_traits.decay // the macro expansion uses that trait
     }
     module cmp                             { header "__utility/cmp.h" }
+    module constant_wrapper                { header "__utility/constant_wrapper.h" }
     module convert_to_integral             { header "__utility/convert_to_integral.h" }
     module default_three_way_comparator    { header "__utility/default_three_way_comparator.h" }
     module element_count                   { header "__utility/element_count.h" }
diff --git a/libcxx/include/span b/libcxx/include/span
index 1911badd88cb1..b5d7844b0b7ee 100644
--- a/libcxx/include/span
+++ b/libcxx/include/span
@@ -614,7 +614,7 @@ template <class _Tp, size_t _Extent>
 #    if _LIBCPP_STD_VER >= 26
 template <class _Tp>
 concept __integral_constant_like =
-    is_integral_v<decltype(_Tp::value)> && !is_same_v<bool, remove_const_t<decltype(_Tp::value)>> &&
+    is_integral_v<remove_cvref_t<decltype(_Tp::value)>> && !is_same_v<bool, remove_const_t<decltype(_Tp::value)>> &&
     convertible_to<_Tp, decltype(_Tp::value)> && equality_comparable_with<_Tp, decltype(_Tp::value)> &&
     bool_constant<_Tp() == _Tp::value>::value &&
     bool_constant<static_cast<decltype(_Tp::value)>(_Tp()) == _Tp::value>::value;
diff --git a/libcxx/include/utility b/libcxx/include/utility
index 1b19243afca1b..24b3baac91b1d 100644
--- a/libcxx/include/utility
+++ b/libcxx/include/utility
@@ -195,6 +195,22 @@ template<class T1, class T2>
 template<class T1, class T2>
     constexpr const T1&& get(const pair<T2, T1>&&) noexcept; // C++14
 
+// [const.wrap.class], class template constant_wrapper
+template<class T>
+  struct cw-fixed-value;                                    // exposition only, since C++26
+
+template<cw-fixed-value X, class = typename decltype(X)::type>
+  struct constant_wrapper;                                  // since C++26
+
+template<class T>
+  concept constexpr-param =                                 // exposition only, since C++26
+    requires { typename constant_wrapper<T::value>; };
+
+struct cw-operators;                                        // exposition only, since C++26
+
+template<cw-fixed-value X>
+  constexpr auto cw = constant_wrapper<X>{};                // since C++26
+
 // C++14
 
 template<class T, T... I>
@@ -292,6 +308,7 @@ template <class T>
 #  endif
 
 #  if _LIBCPP_STD_VER >= 26
+#    include <__utility/constant_wrapper.h>
 #    include <__variant/monostate.h>
 #  endif
 
diff --git a/libcxx/include/version b/libcxx/include/version
index 395330df7f237..8901f8f146d52 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -64,6 +64,7 @@ __cpp_lib_common_reference                              202302L <type_traits>
 __cpp_lib_common_reference_wrapper                      202302L <functional>
 __cpp_lib_complex_udls                                  201309L <complex>
 __cpp_lib_concepts                                      202207L <concepts>
+__cpp_lib_constant_wrapper                              202603L <utility>
 __cpp_lib_constexpr_algorithms                          202306L <algorithm> <utility>
                                                         201806L // C++20
 __cpp_lib_constexpr_bitset                              202207L <bitset>
@@ -555,6 +556,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # undef  __cpp_lib_bind_front
 # define __cpp_lib_bind_front                           202306L
 # define __cpp_lib_bitset                               202306L
+# define __cpp_lib_constant_wrapper                     202603L
 # undef  __cpp_lib_constexpr_algorithms
 # define __cpp_lib_constexpr_algorithms                 202306L
 # define __cpp_lib_constexpr_flat_map                   202502L
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 28dda866371f5..1fbd288894191 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
@@ -30,6 +30,7 @@
 #include <iterator>
 #include <memory>
 #include <string>
+#include <utility>
 #include <type_traits>
 
 #include "test_macros.h"
@@ -57,6 +58,13 @@ void test_iterator_sentinel() {
     assert(s.size() == std::size(arr));
     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
 }
 
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.compile.pass.cpp
index 3f82018a15063..8cbd16d242f74 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.compile.pass.cpp
@@ -24,6 +24,10 @@
 #    error "__cpp_lib_as_const should not be defined before c++17"
 #  endif
 
+#  ifdef __cpp_lib_constant_wrapper
+#    error "__cpp_lib_constant_wrapper should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_constexpr_algorithms
 #    error "__cpp_lib_constexpr_algorithms should not be defined before c++20"
 #  endif
@@ -78,6 +82,10 @@
 #    error "__cpp_lib_as_const should not be defined before c++17"
 #  endif
 
+#  ifdef __cpp_lib_constant_wrapper
+#    error "__cpp_lib_constant_wrapper should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_constexpr_algorithms
 #    error "__cpp_lib_constexpr_algorithms should not be defined before c++20"
 #  endif
@@ -144,6 +152,10 @@
 #    error "__cpp_lib_as_const should have the value 201510L in c++17"
 #  endif
 
+#  ifdef __cpp_lib_constant_wrapper
+#    error "__cpp_lib_constant_wrapper should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_constexpr_algorithms
 #    error "__cpp_lib_constexpr_algorithms should not be defined before c++20"
 #  endif
@@ -210,6 +222,10 @@
 #    error "__cpp_lib_as_const should have the value 201510L in c++20"
 #  endif
 
+#  ifdef __cpp_lib_constant_wrapper
+#    error "__cpp_lib_constant_wrapper should not be defined before c++26"
+#  endif
+
 #  ifndef __cpp_lib_constexpr_algorithms
 #    error "__cpp_lib_constexpr_algorithms should be defined in c++20"
 #  endif
@@ -285,6 +301,10 @@
 #    error "__cpp_lib_as_const should have the value 201510L in c++23"
 #  endif
 
+#  ifdef __cpp_lib_constant_wrapper
+#    error "__cpp_lib_constant_wrapper should not be defined before c++26"
+#  endif
+
 #  ifndef __cpp_lib_constexpr_algorithms
 #    error "__cpp_lib_constexpr_algorithms should be defined in c++23"
 #  endif
@@ -381,6 +401,13 @@
 #    error "__cpp_lib_as_const should have the value 201510L in c++26"
 #  endif
 
+#  ifndef __cpp_lib_constant_wrapper
+#    error "__cpp_lib_constant_wrapper should be defined in c++26"
+#  endif
+#  if __cpp_lib_constant_wrapper != 202603L
+#    error "__cpp_lib_constant_wrapper should have the value 202603L in c++26"
+#  endif
+
 #  ifndef __cpp_lib_constexpr_algorithms
 #    error "__cpp_lib_constexpr_algorithms should be defined in c++26"
 #  endif
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index d1c571a9d1a8c..e342a7ba33f9e 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
@@ -180,6 +180,10 @@
 #    error "__cpp_lib_concepts should not be defined before c++20"
 #  endif
 
+#  ifdef __cpp_lib_constant_wrapper
+#    error "__cpp_lib_constant_wrapper should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_constexpr_algorithms
 #    error "__cpp_lib_constexpr_algorithms should not be defined before c++20"
 #  endif
@@ -1100,6 +1104,10 @@
 #    error "__cpp_lib_concepts should not be defined before c++20"
 #  endif
 
+#  ifdef __cpp_lib_constant_wrapper
+#    error "__cpp_lib_constant_wrapper should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_constexpr_algorithms
 #    error "__cpp_lib_constexpr_algorithms should not be defined before c++20"
 #  endif
@@ -2122,6 +2130,10 @@
 #    error "__cpp_lib_concepts should not be defined before c++20"
 #  endif
 
+#  ifdef __cpp_lib_constant_wrapper
+#    error "__cpp_lib_constant_wrapper should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_constexpr_algorithms
 #    error "__cpp_lib_constexpr_algorithms should not be defined before c++20"
 #  endif
@@ -3369,6 +3381,10 @@
 #    error "__cpp_lib_concepts should have the value 202207L in c++20"
 #  endif
 
+#  ifdef __cpp_lib_constant_wrapper
+#    error "__cpp_lib_constant_wrapper should not be defined before c++26"
+#  endif
+
 #  ifndef __cpp_lib_constexpr_algorithms
 #    error "__cpp_lib_constexpr_algorithms should be defined in c++20"
 #  endif
@@ -4832,6 +4848,10 @@
 #    error "__cpp_lib_concepts should have the value 202207L in c++23"
 #  endif
 
+#  ifdef __cpp_lib_constant_wrapper
+#    error "__cpp_lib_constant_wrapper should not be defined before c++26"
+#  endif
+
 #  ifndef __cpp_lib_constexpr_algorithms
 #    error "__cpp_lib_constexpr_algorithms should be defined in c++23"
 #  endif
@@ -6517,6 +6537,13 @@
 #    error "__cpp_lib_concepts should have the value 202207L in c++26"
 #  endif
 
+#  ifndef __cpp_lib_constant_wrapper
+#    error "__cpp_lib_constant_wrapper should be defined in c++26"
+#  endif
+#  if __cpp_lib_constant_wrapper != 202603L
+#    error "__cpp_lib_constant_wrapper should have the value 202603L in c++26"
+#  endif
+
 #  ifndef __cpp_lib_constexpr_algorithms
 #    error "__cpp_lib_constexpr_algorithms should be defined in c++26"
 #  endif
diff --git a/libcxx/test/std/utilities/const.wrap.class/adl.compile.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/adl.compile.pass.cpp
new file mode 100644
index 0000000000000..f711c15223749
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/adl.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constant_wrapper
+
+// [Note 1: The unnamed second template parameter to constant_wrapper is present
+// to aid argument-dependent lookup ([basic.lookup.argdep]) in finding overloads
+// for which constant_wrapper's wrapped value is a suitable argument, but for which
+// the constant_wrapper itself is not. - end note]
+
+#include <utility>
+
+namespace MyNamespace {
+struct MyType {};
+
+void adl_function(MyType) {}
+
+} // namespace MyNamespace
+
+void test() {
+  std::constant_wrapper<MyNamespace::MyType{}> cw_mt;
+  adl_function(cw_mt);
+}
diff --git a/libcxx/test/std/utilities/const.wrap.class/assign.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/assign.pass.cpp
new file mode 100644
index 0000000000000..718b9531bc220
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/assign.pass.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constant_wrapper
+
+// template<constexpr-param R>
+//   constexpr auto operator=(R) const noexcept
+//     -> constant_wrapper<value = R::value> { return {}; }
+
+#include <concepts>
+#include <type_traits>
+#include <utility>
+
+#include "helpers.h"
+
+struct WithOps {
+  int value;
+
+  constexpr WithOps(int v) : value(v) {}
+
+  constexpr WithOps operator=(int i) const { return WithOps{value + i}; }
+};
+
+struct OpsReturnNonStructural {
+  int value;
+
+  constexpr OpsReturnNonStructural(int v) : value(v) {}
+
+  constexpr NonStructural operator=(int i) const { return NonStructural{value + i}; }
+};
+
+template <class T, class R>
+concept HasAssign = requires(const T t, R r) {
+  { t = r };
+};
+
+template <class T, class R>
+concept HasNoexceptAssign = requires(const T t, R r) {
+  { t = r } noexcept;
+};
+
+static_assert(!HasAssign<std::constant_wrapper<5>, std::constant_wrapper<3>>);
+static_assert(!HasNoexceptAssign<std::constant_wrapper<5>, std::constant_wrapper<3>>);
+
+static_assert(HasAssign<std::constant_wrapper<WithOps{5}>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptAssign<std::constant_wrapper<WithOps{5}>, std::constant_wrapper<3>>);
+
+static_assert(!HasAssign<std::constant_wrapper<OpsReturnNonStructural{5}>, std::constant_wrapper<5>>);
+
+constexpr bool test() {
+  {
+    // WithOps assignment
+    const std::constant_wrapper<WithOps{5}> cwOps5;
+    std::constant_wrapper<3> cw3;
+
+    std::same_as<std::constant_wrapper<WithOps{8}>> decltype(auto) result = cwOps5 = cw3;
+    static_assert(result.value.value == 8);
+  }
+
+  {
+    // with integral_constant
+    const std::constant_wrapper<WithOps{5}> cwOps5;
+    std::integral_constant<int, 3> ic3;
+
+    std::same_as<std::constant_wrapper<WithOps{8}>> decltype(auto) result = cwOps5 = ic3;
+    static_assert(result.value.value == 8);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/const.wrap.class/binary_ops.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/binary_ops.pass.cpp
new file mode 100644
index 0000000000000..a90a4601e5549
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/binary_ops.pass.cpp
@@ -0,0 +1,566 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// ADDITIONAL_COMPILE_FLAGS: -Wno-constant-logical-operand
+
+// constant_wrapper
+
+//  template<constexpr-param L, constexpr-param R>
+//    friend constexpr auto operator+(L, R) noexcept -> constant_wrapper<(L::value + R::value)>
+//      { return {}; }
+//  template<constexpr-param L, constexpr-param R>
+//    friend constexpr auto operator-(L, R) noexcept -> constant_wrapper<(L::value - R::value)>
+//      { return {}; }
+//  template<constexpr-param L, constexpr-param R>
+//    friend constexpr auto operator*(L, R) noexcept -> constant_wrapper<(L::value * R::value)>
+//      { return {}; }
+//  template<constexpr-param L, constexpr-param R>
+//    friend constexpr auto operator/(L, R) noexcept -> constant_wrapper<(L::value / R::value)>
+//      { return {}; }
+//  template<constexpr-param L, constexpr-param R>
+//    friend constexpr auto operator%(L, R) noexcept -> constant_wrapper<(L::value % R::value)>
+//      { return {}; }
+
+//  template<constexpr-param L, constexpr-param R>
+//    friend constexpr auto operator<<(L, R) noexcept -> constant_wrapper<(L::value << R::value)>
+//      { return {}; }
+//  template<constexpr-param L, constexpr-param R>
+//    friend constexpr auto operator>>(L, R) noexcept -> constant_wrapper<(L::value >> R::value)>
+//      { return {}; }
+//  template<constexpr-param L, constexpr-param R>
+//    friend constexpr auto operator&(L, R) noexcept -> constant_wrapper<(L::value & R::value)>
+//      { return {}; }
+//  template<constexpr-param L, constexpr-param R>
+//    friend constexpr auto operator|(L, R) noexcept -> constant_wrapper<(L::value | R::value)>
+//      { return {}; }
+//  template<constexpr-param L, constexpr-param R>
+//    friend constexpr auto operator^(L, R) noexcept -> constant_wrapper<(L::value ^ R::value)>
+//      { return {}; }
+
+//  template<constexpr-param L, constexpr-param R>
+//    requires (!is_constructible_v<bool, decltype(L::value)> ||
+//              !is_constructible_v<bool, decltype(R::value)>)
+//      friend constexpr auto operator&&(L, R) noexcept
+//        -> constant_wrapper<(L::value && R::value)>
+//          { return {}; }
+//  template<constexpr-param L, constexpr-param R>
+//    requires (!is_constructible_v<bool, decltype(L::value)> ||
+//              !is_constructible_v<bool, decltype(R::value)>)
+//      friend constexpr auto operator||(L, R) noexcept
+//        -> constant_wrapper<(L::value || R::value)>
+//          { return {}; }
+
+#include <cassert>
+#include <concepts>
+#include <type_traits>
+#include <utility>
+
+#include "helpers.h"
+
+struct WithOps {
+  int value;
+
+  constexpr WithOps(int v) : value(v) {}
+
+  friend constexpr auto operator+(WithOps l, WithOps r) { return WithOps{l.value + r.value}; }
+  friend constexpr auto operator-(WithOps l, WithOps r) { return WithOps{l.value - r.value}; }
+  friend constexpr auto operator*(WithOps l, WithOps r) { return WithOps{l.value * r.value}; }
+  friend constexpr auto operator/(WithOps l, WithOps r) { return WithOps{l.value / r.value}; }
+  friend constexpr auto operator%(WithOps l, WithOps r) { return WithOps{l.value % r.value}; }
+  friend constexpr auto operator<<(WithOps l, WithOps r) { return WithOps{l.value << r.value}; }
+  friend constexpr auto operator>>(WithOps l, WithOps r) { return WithOps{l.value >> r.value}; }
+  friend constexpr auto operator&(WithOps l, WithOps r) { return WithOps{l.value & r.value}; }
+  friend constexpr auto operator|(WithOps l, WithOps r) { return WithOps{l.value | r.value}; }
+  friend constexpr auto operator^(WithOps l, WithOps r) { return WithOps{l.value ^ r.value}; }
+
+  friend constexpr auto operator&&(WithOps l, WithOps r) { return WithOps{l.value && r.value}; }
+  friend constexpr auto operator||(WithOps l, WithOps r) { return WithOps{l.value || r.value}; }
+};
+
+struct OpsReturnNonStructural {
+  int value;
+
+  constexpr OpsReturnNonStructural(int v) : value(v) {}
+
+  friend constexpr auto operator+(OpsReturnNonStructural l, OpsReturnNonStructural r) {
+    return NonStructural{l.value + r.value};
+  }
+  friend constexpr auto operator-(OpsReturnNonStructural l, OpsReturnNonStructural r) {
+    return NonStructural{l.value - r.value};
+  }
+  friend constexpr auto operator*(OpsReturnNonStructural l, OpsReturnNonStructural r) {
+    return NonStructural{l.value * r.value};
+  }
+  friend constexpr auto operator/(OpsReturnNonStructural l, OpsReturnNonStructural r) {
+    return NonStructural{l.value / r.value};
+  }
+  friend constexpr auto operator%(OpsReturnNonStructural l, OpsReturnNonStructural r) {
+    return NonStructural{l.value % r.value};
+  }
+  friend constexpr auto operator<<(OpsReturnNonStructural l, OpsReturnNonStructural r) {
+    return NonStructural{l.value << r.value};
+  }
+  friend constexpr auto operator>>(OpsReturnNonStructural l, OpsReturnNonStructural r) {
+    return NonStructural{l.value >> r.value};
+  }
+  friend constexpr auto operator&(OpsReturnNonStructural l, OpsReturnNonStructural r) {
+    return NonStructural{l.value & r.value};
+  }
+  friend constexpr auto operator|(OpsReturnNonStructural l, OpsReturnNonStructural r) {
+    return NonStructural{l.value | r.value};
+  }
+  friend constexpr auto operator^(OpsReturnNonStructural l, OpsReturnNonStructural r) {
+    return NonStructural{l.value ^ r.value};
+  }
+  friend constexpr auto operator&&(OpsReturnNonStructural l, OpsReturnNonStructural r) {
+    return NonStructural{l.value && r.value};
+  }
+  friend constexpr auto operator||(OpsReturnNonStructural l, OpsReturnNonStructural r) {
+    return NonStructural{l.value || r.value};
+  }
+};
+
+struct NoOps {};
+
+template <class L, class R>
+concept HasPlus = requires(L l, R r) {
+  { l + r };
+};
+
+template <class L, class R>
+concept HasMinus = requires(L l, R r) {
+  { l - r };
+};
+
+template <class L, class R>
+concept HasMultiply = requires(L l, R r) {
+  { l * r };
+};
+
+template <class L, class R>
+concept HasDivide = requires(L l, R r) {
+  { l / r };
+};
+
+template <class L, class R>
+concept HasModulo = requires(L l, R r) {
+  { l % r };
+};
+
+template <class L, class R>
+concept HasShiftLeft = requires(L l, R r) {
+  { l << r };
+};
+
+template <class L, class R>
+concept HasShiftRight = requires(L l, R r) {
+  { l >> r };
+};
+
+template <class L, class R>
+concept HasBitAnd = requires(L l, R r) {
+  { l & r };
+};
+
+template <class L, class R>
+concept HasBitOr = requires(L l, R r) {
+  { l | r };
+};
+
+template <class L, class R>
+concept HasBitXor = requires(L l, R r) {
+  { l ^ r };
+};
+
+template <class L, class R>
+concept HasLogicalAnd = requires(L l, R r) {
+  { l && r };
+};
+
+template <class L, class R>
+concept HasLogicalOr = requires(L l, R r) {
+  { l || r };
+};
+
+template <class L, class R>
+concept HasNoexceptPlus = requires(L l, R r) {
+  { l + r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptMinus = requires(L l, R r) {
+  { l - r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptMultiply = requires(L l, R r) {
+  { l * r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptDivide = requires(L l, R r) {
+  { l / r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptModulo = requires(L l, R r) {
+  { l % r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptShiftLeft = requires(L l, R r) {
+  { l << r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptShiftRight = requires(L l, R r) {
+  { l >> r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptBitAnd = requires(L l, R r) {
+  { l & r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptBitOr = requires(L l, R r) {
+  { l | r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptBitXor = requires(L l, R r) {
+  { l ^ r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptLogicalAnd = requires(L l, R r) {
+  { l && r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptLogicalOr = requires(L l, R r) {
+  { l || r } noexcept;
+};
+
+// Concept checks for int + int operations
+static_assert(HasPlus<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasMinus<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasMultiply<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasDivide<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasModulo<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasShiftLeft<std::constant_wrapper<6>, std::constant_wrapper<1>>);
+static_assert(HasShiftRight<std::constant_wrapper<6>, std::constant_wrapper<1>>);
+static_assert(HasBitAnd<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasBitOr<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasBitXor<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasLogicalAnd<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasLogicalOr<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+
+static_assert(HasNoexceptPlus<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptMinus<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptMultiply<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptDivide<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptModulo<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptShiftLeft<std::constant_wrapper<6>, std::constant_wrapper<1>>);
+static_assert(HasNoexceptShiftRight<std::constant_wrapper<6>, std::constant_wrapper<1>>);
+static_assert(HasNoexceptBitAnd<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptBitOr<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptBitXor<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptLogicalAnd<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptLogicalOr<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+
+// NoOps
+static_assert(!HasPlus<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasMinus<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasMultiply<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasDivide<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasModulo<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasShiftLeft<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasShiftRight<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasBitAnd<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasBitOr<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasBitXor<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasLogicalAnd<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasLogicalOr<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+
+// Concept checks for WithOps operations
+static_assert(HasNoexceptPlus<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptMinus<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptMultiply<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptDivide<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptModulo<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptShiftLeft<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{1}>>);
+static_assert(HasNoexceptShiftRight<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{1}>>);
+static_assert(HasNoexceptBitAnd<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptBitOr<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptBitXor<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptLogicalAnd<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptLogicalOr<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+
+// clang-format off
+// Non-structural types use implicit conversion to underlying type
+static_assert(HasPlus<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(HasMinus<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(HasMultiply<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(HasDivide<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(HasModulo<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(HasShiftLeft<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{1}>>);
+static_assert(HasShiftRight<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{1}>>);
+static_assert(HasBitAnd<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(HasBitOr<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(HasBitXor<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(HasLogicalAnd<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(HasLogicalOr<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+
+static_assert(!HasNoexceptPlus<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasNoexceptMinus<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasNoexceptMultiply<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasNoexceptDivide<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasNoexceptModulo<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasNoexceptShiftLeft<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{1}>>);
+static_assert(!HasNoexceptShiftRight<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{1}>>);
+static_assert(!HasNoexceptBitAnd<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasNoexceptBitOr<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasNoexceptBitXor<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasNoexceptLogicalAnd<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasNoexceptLogicalOr<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+// clang-format on
+
+constexpr bool test() {
+  {
+    // int + int
+    std::constant_wrapper<6> cw6;
+    std::constant_wrapper<3> cw3;
+
+    std::same_as<std::constant_wrapper<9>> decltype(auto) result = cw6 + cw3;
+    static_assert(result == 9);
+
+    std::same_as<std::constant_wrapper<3>> decltype(auto) result2 = cw6 - cw3;
+    static_assert(result2 == 3);
+
+    std::same_as<std::constant_wrapper<18>> decltype(auto) result3 = cw6 * cw3;
+    static_assert(result3 == 18);
+
+    std::same_as<std::constant_wrapper<2>> decltype(auto) result4 = cw6 / cw3;
+    static_assert(result4 == 2);
+
+    std::same_as<std::constant_wrapper<0>> decltype(auto) result5 = cw6 % cw3;
+    static_assert(result5 == 0);
+
+    std::same_as<std::constant_wrapper<2>> decltype(auto) result6 = cw6 & cw3;
+    static_assert(result6 == 2);
+
+    std::same_as<std::constant_wrapper<7>> decltype(auto) result7 = cw6 | cw3;
+    static_assert(result7 == 7);
+
+    std::same_as<std::constant_wrapper<5>> decltype(auto) result8 = cw6 ^ cw3;
+    static_assert(result8 == 5);
+
+    // Shift operations: 6 << 3 = 48, 6 >> 3 = 0
+    std::same_as<std::constant_wrapper<48>> decltype(auto) result9 = cw6 << cw3;
+    static_assert(result9 == 48);
+
+    std::same_as<std::constant_wrapper<0>> decltype(auto) result10 = cw6 >> cw3;
+    static_assert(result10 == 0);
+
+    // logical operations: int convertible to bool, so constant_wrapper overload is disabled
+    // They are implicitly converted to bool and use built-in operators, resulting in a bool
+    std::same_as<bool> decltype(auto) result11 = cw6 && cw3;
+    assert(result11 == true);
+
+    std::constant_wrapper<0> cw0;
+    std::same_as<bool> decltype(auto) result12 = cw0 || cw3;
+    assert(result12 == true);
+  }
+
+  {
+    // WithOps operations
+    std::constant_wrapper<WithOps{6}> cwWithOps6;
+    std::constant_wrapper<WithOps{3}> cwWithOps3;
+
+    std::same_as<std::constant_wrapper<WithOps{9}>> decltype(auto) result = cwWithOps6 + cwWithOps3;
+    static_assert(result.value.value == 9);
+
+    std::same_as<std::constant_wrapper<WithOps{3}>> decltype(auto) result2 = cwWithOps6 - cwWithOps3;
+    static_assert(result2.value.value == 3);
+
+    std::same_as<std::constant_wrapper<WithOps{18}>> decltype(auto) result3 = cwWithOps6 * cwWithOps3;
+    static_assert(result3.value.value == 18);
+
+    std::same_as<std::constant_wrapper<WithOps{2}>> decltype(auto) result4 = cwWithOps6 / cwWithOps3;
+    static_assert(result4.value.value == 2);
+
+    std::same_as<std::constant_wrapper<WithOps{0}>> decltype(auto) result5 = cwWithOps6 % cwWithOps3;
+    static_assert(result5.value.value == 0);
+
+    std::same_as<std::constant_wrapper<WithOps{2}>> decltype(auto) result6 = cwWithOps6 & cwWithOps3;
+    static_assert(result6.value.value == 2);
+
+    std::same_as<std::constant_wrapper<WithOps{7}>> decltype(auto) result7 = cwWithOps6 | cwWithOps3;
+    static_assert(result7.value.value == 7);
+
+    std::same_as<std::constant_wrapper<WithOps{5}>> decltype(auto) result8 = cwWithOps6 ^ cwWithOps3;
+    static_assert(result8.value.value == 5);
+
+    // Shift operations: 6 << 3 = 48, 6 >> 3 = 0
+    std::same_as<std::constant_wrapper<WithOps{48}>> decltype(auto) result9 = cwWithOps6 << cwWithOps3;
+    static_assert(result9.value.value == 48);
+
+    std::same_as<std::constant_wrapper<WithOps{0}>> decltype(auto) result10 = cwWithOps6 >> cwWithOps3;
+    static_assert(result10.value.value == 0);
+
+    std::same_as<std::constant_wrapper<WithOps{1}>> decltype(auto) result11 = cwWithOps6 && cwWithOps3;
+    static_assert(result11.value.value == 1);
+
+    std::same_as<std::constant_wrapper<WithOps{1}>> decltype(auto) result12 = cwWithOps6 || cwWithOps3;
+    static_assert(result12.value.value == 1);
+  }
+
+  {
+    // Non-structural return types use implicit conversion
+    std::constant_wrapper<OpsReturnNonStructural{6}> cwOpt6;
+    std::constant_wrapper<OpsReturnNonStructural{3}> cwOpt3;
+
+    std::same_as<NonStructural> decltype(auto) result = cwOpt6 + cwOpt3;
+    assert(result.get() == 9);
+
+    std::same_as<NonStructural> decltype(auto) result2 = cwOpt6 - cwOpt3;
+    assert(result2.get() == 3);
+
+    std::same_as<NonStructural> decltype(auto) result3 = cwOpt6 * cwOpt3;
+    assert(result3.get() == 18);
+
+    std::same_as<NonStructural> decltype(auto) result4 = cwOpt6 / cwOpt3;
+    assert(result4.get() == 2);
+
+    std::same_as<NonStructural> decltype(auto) result5 = cwOpt6 % cwOpt3;
+    assert(result5.get() == 0);
+
+    std::same_as<NonStructural> decltype(auto) result6 = cwOpt6 & cwOpt3;
+    assert(result6.get() == 2);
+
+    std::same_as<NonStructural> decltype(auto) result7 = cwOpt6 | cwOpt3;
+    assert(result7.get() == 7);
+
+    std::same_as<NonStructural> decltype(auto) result8 = cwOpt6 ^ cwOpt3;
+    assert(result8.get() == 5);
+
+    // Shift operations: 6 << 3 = 48, 6 >> 3 = 0
+    std::same_as<NonStructural> decltype(auto) result9 = cwOpt6 << cwOpt3;
+    assert(result9.get() == 48);
+
+    std::same_as<NonStructural> decltype(auto) result10 = cwOpt6 >> cwOpt3;
+    assert(result10.get() == 0);
+
+    std::same_as<NonStructural> decltype(auto) result11 = cwOpt6 && cwOpt3;
+    assert(result11.get() == 1);
+
+    std::same_as<NonStructural> decltype(auto) result12 = cwOpt6 || cwOpt3;
+    assert(result12.get() == 1);
+  }
+
+  {
+    // Mix with runtime param: these operators are not used
+    std::constant_wrapper<6> cw6;
+    int i = 3;
+
+    std::same_as<int> decltype(auto) result = cw6 + i;
+    assert(result == 9);
+
+    std::same_as<int> decltype(auto) result2 = cw6 - i;
+    assert(result2 == 3);
+
+    std::same_as<int> decltype(auto) result3 = cw6 * i;
+    assert(result3 == 18);
+
+    std::same_as<int> decltype(auto) result4 = cw6 / i;
+    assert(result4 == 2);
+
+    std::same_as<int> decltype(auto) result5 = cw6 % i;
+    assert(result5 == 0);
+
+    std::same_as<int> decltype(auto) result6 = cw6 & i;
+    assert(result6 == 2);
+
+    std::same_as<int> decltype(auto) result7 = cw6 | i;
+    assert(result7 == 7);
+
+    std::same_as<int> decltype(auto) result8 = cw6 ^ i;
+    assert(result8 == 5);
+
+    // Shift operations: 6 << 3 = 48, 6 >> 3 = 0
+    std::same_as<int> decltype(auto) result9 = cw6 << i;
+    assert(result9 == 48);
+
+    std::same_as<int> decltype(auto) result10 = cw6 >> i;
+    assert(result10 == 0);
+
+    std::same_as<bool> decltype(auto) result11 = cw6 && i;
+    assert(result11 == true);
+
+    std::constant_wrapper<0> cw0;
+    std::same_as<bool> decltype(auto) result12 = cw0 || i;
+    assert(result12 == true);
+  }
+
+  {
+    // with integral_constant
+    std::constant_wrapper<6> cw6;
+    std::integral_constant<int, 3> ic3;
+
+    std::same_as<std::constant_wrapper<9>> decltype(auto) result = cw6 + ic3;
+    static_assert(result == 9);
+
+    std::same_as<std::constant_wrapper<3>> decltype(auto) result2 = cw6 - ic3;
+    static_assert(result2 == 3);
+
+    std::same_as<std::constant_wrapper<18>> decltype(auto) result3 = cw6 * ic3;
+    static_assert(result3 == 18);
+
+    std::same_as<std::constant_wrapper<2>> decltype(auto) result4 = cw6 / ic3;
+    static_assert(result4 == 2);
+
+    std::same_as<std::constant_wrapper<0>> decltype(auto) result5 = cw6 % ic3;
+    static_assert(result5 == 0);
+
+    std::same_as<std::constant_wrapper<2>> decltype(auto) result6 = cw6 & ic3;
+    static_assert(result6 == 2);
+
+    std::same_as<std::constant_wrapper<7>> decltype(auto) result7 = cw6 | ic3;
+    static_assert(result7 == 7);
+
+    std::same_as<std::constant_wrapper<5>> decltype(auto) result8 = cw6 ^ ic3;
+    static_assert(result8 == 5);
+
+    // Shift operations: 6 << 3 = 48, 6 >> 3 = 0
+    std::same_as<std::constant_wrapper<48>> decltype(auto) result9 = cw6 << ic3;
+    static_assert(result9 == 48);
+
+    std::same_as<std::constant_wrapper<0>> decltype(auto) result10 = cw6 >> ic3;
+    static_assert(result10 == 0);
+
+    // logical operations: int convertible to bool, so constant_wrapper overload is disabled
+    // They are implicitly converted to bool and use built-in operators, resulting in a bool
+    std::same_as<bool> decltype(auto) result11 = cw6 && ic3;
+    assert(result11 == true);
+
+    std::constant_wrapper<0> cw0;
+    std::same_as<bool> decltype(auto) result12 = cw0 || ic3;
+    assert(result12 == true);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/const.wrap.class/call.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/call.pass.cpp
new file mode 100644
index 0000000000000..d63a92f62c6ff
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/call.pass.cpp
@@ -0,0 +1,238 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constant_wrapper
+
+// template<class... Args>
+// static constexpr decltype(auto) operator()(Args&&... args) noexcept(see below);
+
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "helpers.h"
+#include "MoveOnly.h"
+
+struct MoveOnlyFn {
+  constexpr MoveOnly operator()(const MoveOnly& m1, MoveOnly m2, MoveOnly&& m3) const {
+    return MoveOnly(m1.get() + m2.get() + m3.get());
+  }
+};
+
+constexpr bool fun_ptr(int i) { return i > 0; }
+
+struct OverloadSet {
+  constexpr int operator()(int) const { return 1; }
+
+  constexpr int operator()(std::constant_wrapper<42>) const { return 2; }
+};
+
+struct ReturnNonStructural {
+  constexpr NonStructural operator()(int i) const { return NonStructural{i}; }
+};
+
+struct CWOnly {
+  constexpr int operator()(std::constant_wrapper<42>) const { return 42; }
+};
+
+constexpr int nothrow_call(int) noexcept { return 42; }
+
+constexpr int throwing_call(int) { return 42; }
+
+struct S {
+  int member = 42;
+
+  constexpr int mem_fun(int i) const { return member + i; }
+};
+
+constexpr S s;
+
+// Let call-expr be constant_wrapper<INVOKE (value, remove_cvref_t<Args>::value...)>{} if all types
+// in remove_cvref_t<Args>... satisfy constexpr-param and constant_wrapper<INVOKE (value, remove_-
+// cvref_t<Args>::value...)> is a valid type, otherwise let call-expr be INVOKE (value, std::forward<Args>(args)...).
+//
+// Constraints: call-expr is a valid expression.
+// Remarks: The exception specification is equivalent to noexcept(call-expr).
+
+// clang-format off
+static_assert(std::is_invocable_v<std::constant_wrapper<[] { return 42; }>>);
+static_assert(!std::is_invocable_v<std::constant_wrapper<[] { return 42; }>, int>);
+static_assert(!std::is_invocable_v<std::constant_wrapper<5>>);
+
+static_assert(!std::is_invocable_v<std::constant_wrapper<std::plus<>{}>, int>);
+static_assert(std::is_nothrow_invocable_v<std::constant_wrapper<std::plus<>{}>, int, int>);
+static_assert(std::is_nothrow_invocable_v<std::constant_wrapper<std::plus<>{}>, std::constant_wrapper<42>, int>);
+static_assert(std::is_nothrow_invocable_v<std::constant_wrapper<std::plus<>{}>, std::constant_wrapper<42>, std::constant_wrapper<42>>);
+
+static_assert(std::is_nothrow_invocable_v<std::constant_wrapper<nothrow_call>, int>);
+static_assert(std::is_nothrow_invocable_v<std::constant_wrapper<nothrow_call>, std::constant_wrapper<42>>);
+
+static_assert(std::is_invocable_v<std::constant_wrapper<throwing_call>, int>);
+static_assert(!std::is_nothrow_invocable_v<std::constant_wrapper<throwing_call>, int>);
+static_assert(std::is_nothrow_invocable_v<std::constant_wrapper<throwing_call>, std::constant_wrapper<42>>,
+              "the call expression is still nothrow because the constexpr path is taken");
+// clang-format on
+
+constexpr bool test() {
+  {
+    // with runtime param
+    using T                                 = std::constant_wrapper<std::plus<>{}>;
+    std::same_as<int> decltype(auto) result = T::operator()(1, 2);
+    assert(result == 3);
+  }
+
+  {
+    // with runtime param and constexpr param
+    using T                                 = std::constant_wrapper<std::plus<>{}>;
+    std::same_as<int> decltype(auto) result = T::operator()(std::cw<1>, 2);
+    assert(result == 3);
+  }
+
+  {
+    // with only constexpr param
+    using T                                                      = std::constant_wrapper<std::plus<>{}>;
+    std::same_as<std::constant_wrapper<3>> decltype(auto) result = T::operator()(std::cw<1>, std::cw<2>);
+    static_assert(result == 3);
+  }
+
+  {
+    // nullary
+    using T                                                       = std::constant_wrapper<[] { return 42; }>;
+    std::same_as<std::constant_wrapper<42>> decltype(auto) result = T::operator()();
+    static_assert(result == 42);
+  }
+
+  {
+    // return void with runtime param
+    using T = std::constant_wrapper<[](int) {}>;
+    T::operator()(5);
+    static_assert(std::same_as<void, decltype(T::operator()(5))>);
+  }
+
+  {
+    // return void with constexpr param
+    using T = std::constant_wrapper<[](int) {}>;
+    T::operator()(std::cw<5>);
+    static_assert(std::same_as<void, decltype(T::operator()(std::cw<5>))>);
+  }
+
+  {
+    // nullary return void
+    using T = std::constant_wrapper<[] {}>;
+    T::operator()();
+    static_assert(std::same_as<void, decltype(T::operator()())>);
+  }
+
+  {
+    // move only
+    using T = std::constant_wrapper<MoveOnlyFn{}>;
+    MoveOnly m1(1), m2(2), m3(3);
+    std::same_as<MoveOnly> decltype(auto) result = T::operator()(m1, std::move(m2), std::move(m3));
+    assert(result.get() == 6);
+  }
+
+  {
+    // function pointer
+    using T                                  = std::constant_wrapper<fun_ptr>;
+    std::same_as<bool> decltype(auto) result = T::operator()(5);
+    assert(result);
+  }
+
+  {
+    // function pointer with constexpr param
+    using T                                                         = std::constant_wrapper<fun_ptr>;
+    std::same_as<std::constant_wrapper<true>> decltype(auto) result = T::operator()(std::cw<5>);
+    static_assert(result);
+  }
+  {
+    // member ptr with runtime param
+    using T = std::constant_wrapper<&S::member>;
+    S s1;
+    std::same_as<int&> decltype(auto) result = T::operator()(s1);
+    assert(result == 42);
+    assert(&result == &s1.member);
+  }
+  {
+    // member ptr with constexpr param
+    using T                                                       = std::constant_wrapper<&S::member>;
+    std::same_as<std::constant_wrapper<42>> decltype(auto) result = T::operator()(std::cw<&s>);
+    static_assert(result == 42);
+  }
+  {
+    // member function ptr with runtime param
+    using T = std::constant_wrapper<&S::mem_fun>;
+    S s1;
+    std::same_as<int> decltype(auto) result = T::operator()(s1, 8);
+    assert(result == 50);
+  }
+  {
+    // member function ptr with constexpr param
+    using T                                                       = std::constant_wrapper<&S::mem_fun>;
+    std::same_as<std::constant_wrapper<50>> decltype(auto) result = T::operator()(std::cw<&s>, std::cw<8>);
+    static_assert(result == 50);
+  }
+  {
+    // overload set
+    // will always unwrap the constexpr params and call the non-constexpr overload
+    using T                                  = std::constant_wrapper<OverloadSet{}>;
+    std::same_as<int> decltype(auto) result1 = T::operator()(42);
+    assert(result1 == 1);
+    std::same_as<std::constant_wrapper<1>> decltype(auto) result2 = T::operator()(std::cw<42>);
+    static_assert(result2 == 1);
+  }
+
+  {
+    // return non-structural type
+    using T                                           = std::constant_wrapper<ReturnNonStructural{}>;
+    std::same_as<NonStructural> decltype(auto) result = T::operator()(5);
+    assert(result.get() == 5);
+  }
+
+  {
+    // return non-structural type with constexpr param
+    using T                                           = std::constant_wrapper<ReturnNonStructural{}>;
+    std::same_as<NonStructural> decltype(auto) result = T::operator()(std::cw<5>);
+    assert(result.get() == 5);
+  }
+
+  {
+    // cw only
+    // the upwrapping case doesn't work so it falls back to the normal invoke path
+    using T                                 = std::constant_wrapper<CWOnly{}>;
+    std::same_as<int> decltype(auto) result = T::operator()(std::cw<42>);
+    assert(result == 42);
+  }
+
+  {
+    // just use the call operator
+    assert(std::cw<[](int i) { return i + 1; }>(42) == 43);
+    assert(std::cw<[](int i) { return i + 1; }>(std::cw<42>) == 43);
+  }
+
+  {
+    // with integral_constant, will still call the constexpr path
+    using T = std::constant_wrapper<std::plus<>{}>;
+    std::integral_constant<int, 1> ic1;
+    std::integral_constant<int, 2> ic2;
+    std::same_as<std::constant_wrapper<3>> decltype(auto) result = T::operator()(ic1, ic2);
+    static_assert(result == 3);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/const.wrap.class/comma.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/comma.pass.cpp
new file mode 100644
index 0000000000000..279b8e2004ea7
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/comma.pass.cpp
@@ -0,0 +1,62 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// ADDITIONAL_COMPILE_FLAGS: -Wno-unused-value
+
+// constant_wrapper
+
+// template<constexpr-param L, constexpr-param R>
+//   friend constexpr auto operator,(L, R) noexcept = delete;
+
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+struct WithOps {
+  int value;
+
+  constexpr WithOps(int v) : value(v) {}
+
+  friend constexpr auto operator,(const WithOps& /*l*/, WithOps r) { return WithOps{r.value}; }
+};
+
+struct NoOps {};
+
+template <class L, class R>
+concept HasComma = requires(L l, R r) {
+  { l, r };
+};
+
+// Comma operator is deleted for constant_wrapper operands
+static_assert(!HasComma<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(!HasComma<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(!HasComma<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+
+// Mixed operands - one constant_wrapper, one runtime type (uses built-in operator)
+static_assert(HasComma<std::constant_wrapper<42>, int>);
+static_assert(HasComma<int, std::constant_wrapper<42>>);
+
+constexpr bool test() {
+  {
+    // only mixed with runtime parameters
+    std::constant_wrapper<42> cw42;
+    int i                                     = 0;
+    std::same_as<int&> decltype(auto) result1 = (cw42, i);
+    assert(result1 == 0);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/const.wrap.class/comp.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/comp.pass.cpp
new file mode 100644
index 0000000000000..7bbb325d77f02
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/comp.pass.cpp
@@ -0,0 +1,410 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constant_wrapper
+
+// template<constexpr-param L, constexpr-param R>
+//   friend constexpr auto operator<=>(L, R) noexcept
+//     -> constant_wrapper<(L::value <=> R::value)>
+//       { return {}; }
+// template<constexpr-param L, constexpr-param R>
+//   friend constexpr auto operator<(L, R) noexcept -> constant_wrapper<(L::value < R::value)>
+//     { return {}; }
+// template<constexpr-param L, constexpr-param R>
+//   friend constexpr auto operator<=(L, R) noexcept -> constant_wrapper<(L::value <= R::value)>
+//     { return {}; }
+// template<constexpr-param L, constexpr-param R>
+//   friend constexpr auto operator==(L, R) noexcept -> constant_wrapper<(L::value == R::value)>
+//     { return {}; }
+// template<constexpr-param L, constexpr-param R>
+//   friend constexpr auto operator!=(L, R) noexcept -> constant_wrapper<(L::value != R::value)>
+//     { return {}; }
+// template<constexpr-param L, constexpr-param R>
+//   friend constexpr auto operator>(L, R) noexcept -> constant_wrapper<(L::value > R::value)>
+//     { return {}; }
+// template<constexpr-param L, constexpr-param R>
+//   friend constexpr auto operator>=(L, R) noexcept -> constant_wrapper<(L::value >= R::value)>
+//     { return {}; }
+
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "helpers.h"
+
+struct WithOps {
+  int value;
+
+  constexpr WithOps(int v) : value(v) {}
+
+  friend constexpr auto operator==(WithOps l, WithOps r) { return l.value == r.value; }
+  friend constexpr auto operator!=(WithOps l, WithOps r) { return l.value != r.value; }
+  friend constexpr auto operator<(WithOps l, WithOps r) { return l.value < r.value; }
+  friend constexpr auto operator<=(WithOps l, WithOps r) { return l.value <= r.value; }
+  friend constexpr auto operator>=(WithOps l, WithOps r) { return l.value >= r.value; }
+  friend constexpr auto operator>(WithOps l, WithOps r) { return l.value > r.value; }
+  friend constexpr auto operator<=>(WithOps l, WithOps r) { return l.value <=> r.value; }
+};
+
+struct OpsReturnNonStructural {
+  int value;
+
+  constexpr OpsReturnNonStructural(int v) : value(v) {}
+
+  friend constexpr auto operator==(OpsReturnNonStructural l, OpsReturnNonStructural r) {
+    return NonStructural{l.value == r.value ? 1 : 0};
+  }
+  friend constexpr auto operator!=(OpsReturnNonStructural l, OpsReturnNonStructural r) {
+    return NonStructural{l.value != r.value ? 1 : 0};
+  }
+  friend constexpr auto operator<(OpsReturnNonStructural l, OpsReturnNonStructural r) {
+    return NonStructural{l.value < r.value ? 1 : 0};
+  }
+  friend constexpr auto operator<=(OpsReturnNonStructural l, OpsReturnNonStructural r) {
+    return NonStructural{l.value <= r.value ? 1 : 0};
+  }
+  friend constexpr auto operator>=(OpsReturnNonStructural l, OpsReturnNonStructural r) {
+    return NonStructural{l.value >= r.value ? 1 : 0};
+  }
+  friend constexpr auto operator>(OpsReturnNonStructural l, OpsReturnNonStructural r) {
+    return NonStructural{l.value > r.value ? 1 : 0};
+  }
+  friend constexpr auto operator<=>(OpsReturnNonStructural l, OpsReturnNonStructural r) {
+    return NonStructural{(l.value < r.value) ? -1 : (l.value > r.value) ? 1 : 0};
+  }
+};
+
+struct NoOps {};
+
+template <class L, class R>
+concept HasEqual = requires(L l, R r) {
+  { l == r };
+};
+
+template <class L, class R>
+concept HasNotEqual = requires(L l, R r) {
+  { l != r };
+};
+
+template <class L, class R>
+concept HasLess = requires(L l, R r) {
+  { l < r };
+};
+
+template <class L, class R>
+concept HasLessEqual = requires(L l, R r) {
+  { l <= r };
+};
+
+template <class L, class R>
+concept HasGreater = requires(L l, R r) {
+  { l > r };
+};
+
+template <class L, class R>
+concept HasGreaterEqual = requires(L l, R r) {
+  { l >= r };
+};
+
+template <class L, class R>
+concept HasSpaceship = requires(L l, R r) {
+  { l <=> r };
+};
+
+template <class L, class R>
+concept HasNoexceptEqual = requires(L l, R r) {
+  { l == r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptNotEqual = requires(L l, R r) {
+  { l != r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptLess = requires(L l, R r) {
+  { l < r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptLessEqual = requires(L l, R r) {
+  { l <= r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptGreater = requires(L l, R r) {
+  { l > r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptGreaterEqual = requires(L l, R r) {
+  { l >= r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptSpaceship = requires(L l, R r) {
+  { l <=> r } noexcept;
+};
+
+// Concept checks for int comparisons
+static_assert(HasEqual<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNotEqual<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasLess<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasLessEqual<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasGreater<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasGreaterEqual<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasSpaceship<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+
+static_assert(HasNoexceptEqual<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptNotEqual<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptLess<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptLessEqual<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptGreater<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptGreaterEqual<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptSpaceship<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+
+// NoOps
+static_assert(!HasEqual<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasNotEqual<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasLess<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasLessEqual<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasGreater<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasGreaterEqual<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasSpaceship<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+
+// Concept checks for WithOps comparisons
+static_assert(HasNoexceptEqual<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptNotEqual<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptLess<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptLessEqual<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptGreater<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptGreaterEqual<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(!HasNoexceptSpaceship<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>,
+              "strong_ordering is not a structural type, so the call falls back to runtime implicit conversion and "
+              "operator<=>, which is noexcept(false)");
+
+// clang-format off
+// Non-structural types use implicit conversion to underlying type
+static_assert(HasEqual<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(HasNotEqual<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(HasLess<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(HasLessEqual<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(HasGreater<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(HasGreaterEqual<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+
+static_assert(!HasNoexceptEqual<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasNoexceptNotEqual<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasNoexceptLess<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasNoexceptLessEqual<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasNoexceptGreater<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasNoexceptGreaterEqual<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+// clang-format on
+
+constexpr bool test() {
+  {
+    // int comparisons: 6 vs 3 - returns constant_wrapper<bool_value>
+    std::constant_wrapper<6> cw6;
+    std::constant_wrapper<3> cw3;
+
+    std::same_as<std::constant_wrapper<false>> decltype(auto) equal = cw6 == cw3;
+    static_assert(!static_cast<bool>(equal));
+
+    std::same_as<std::constant_wrapper<true>> decltype(auto) not_equal = cw6 != cw3;
+    static_assert(static_cast<bool>(not_equal));
+
+    std::same_as<std::constant_wrapper<false>> decltype(auto) less = cw6 < cw3;
+    static_assert(!static_cast<bool>(less));
+
+    std::same_as<std::constant_wrapper<false>> decltype(auto) less_equal = cw6 <= cw3;
+    static_assert(!static_cast<bool>(less_equal));
+
+    std::same_as<std::constant_wrapper<true>> decltype(auto) greater = cw6 > cw3;
+    static_assert(static_cast<bool>(greater));
+
+    std::same_as<std::constant_wrapper<true>> decltype(auto) greater_equal = cw6 >= cw3;
+    static_assert(static_cast<bool>(greater_equal));
+
+    // strong_ordering is not a structural type
+    std::same_as<std::strong_ordering> decltype(auto) spaceship = cw6 <=> cw3;
+    assert(spaceship == std::strong_ordering::greater);
+  }
+
+  {
+    // int comparisons: equal values
+    std::constant_wrapper<3> cw3a;
+    std::constant_wrapper<3> cw3b;
+
+    std::same_as<std::constant_wrapper<true>> decltype(auto) equal = cw3a == cw3b;
+    static_assert(static_cast<bool>(equal));
+
+    std::same_as<std::constant_wrapper<false>> decltype(auto) not_equal = cw3a != cw3b;
+    static_assert(!static_cast<bool>(not_equal));
+
+    std::same_as<std::constant_wrapper<false>> decltype(auto) less = cw3a < cw3b;
+    static_assert(!static_cast<bool>(less));
+
+    std::same_as<std::constant_wrapper<true>> decltype(auto) less_equal = cw3a <= cw3b;
+    static_assert(static_cast<bool>(less_equal));
+
+    std::same_as<std::constant_wrapper<true>> decltype(auto) greater = cw3a >= cw3b;
+    static_assert(static_cast<bool>(greater));
+
+    std::same_as<std::constant_wrapper<false>> decltype(auto) greater_cmp = cw3a > cw3b;
+    static_assert(!static_cast<bool>(greater_cmp));
+
+    std::same_as<std::strong_ordering> decltype(auto) spaceship = cw3a <=> cw3b;
+    assert(spaceship == std::strong_ordering::equal);
+  }
+
+  {
+    // WithOps comparisons - returns constant_wrapper<bool_value>
+    std::constant_wrapper<WithOps{6}> cwWithOps6;
+    std::constant_wrapper<WithOps{3}> cwWithOps3;
+
+    std::same_as<std::constant_wrapper<false>> decltype(auto) equal = cwWithOps6 == cwWithOps3;
+    static_assert(!static_cast<bool>(equal));
+
+    std::same_as<std::constant_wrapper<true>> decltype(auto) not_equal = cwWithOps6 != cwWithOps3;
+    static_assert(static_cast<bool>(not_equal));
+
+    std::same_as<std::constant_wrapper<false>> decltype(auto) less = cwWithOps6 < cwWithOps3;
+    static_assert(!static_cast<bool>(less));
+
+    std::same_as<std::constant_wrapper<false>> decltype(auto) less_equal = cwWithOps6 <= cwWithOps3;
+    static_assert(!static_cast<bool>(less_equal));
+
+    std::same_as<std::constant_wrapper<true>> decltype(auto) greater = cwWithOps6 > cwWithOps3;
+    static_assert(static_cast<bool>(greater));
+
+    std::same_as<std::constant_wrapper<true>> decltype(auto) greater_equal = cwWithOps6 >= cwWithOps3;
+    static_assert(static_cast<bool>(greater_equal));
+
+    std::same_as<std::strong_ordering> decltype(auto) spaceship = cwWithOps6 <=> cwWithOps3;
+    assert(spaceship == std::strong_ordering::greater);
+  }
+
+  {
+    // WithOps comparisons: equal values
+    std::constant_wrapper<WithOps{3}> cwWithOps3a;
+    std::constant_wrapper<WithOps{3}> cwWithOps3b;
+
+    std::same_as<std::constant_wrapper<true>> decltype(auto) equal = cwWithOps3a == cwWithOps3b;
+    static_assert(static_cast<bool>(equal));
+
+    std::same_as<std::constant_wrapper<false>> decltype(auto) not_equal = cwWithOps3a != cwWithOps3b;
+    static_assert(!static_cast<bool>(not_equal));
+
+    std::same_as<std::constant_wrapper<false>> decltype(auto) less = cwWithOps3a < cwWithOps3b;
+    static_assert(!static_cast<bool>(less));
+
+    std::same_as<std::constant_wrapper<true>> decltype(auto) less_equal = cwWithOps3a <= cwWithOps3b;
+    static_assert(static_cast<bool>(less_equal));
+
+    std::same_as<std::constant_wrapper<true>> decltype(auto) greater_equal = cwWithOps3a >= cwWithOps3b;
+    static_assert(static_cast<bool>(greater_equal));
+
+    std::same_as<std::constant_wrapper<false>> decltype(auto) greater = cwWithOps3a > cwWithOps3b;
+    static_assert(!static_cast<bool>(greater));
+
+    std::same_as<std::strong_ordering> decltype(auto) spaceship = cwWithOps3a <=> cwWithOps3b;
+    assert(spaceship == std::strong_ordering::equal);
+  }
+
+  {
+    // Non-structural return types use implicit conversion
+    std::constant_wrapper<OpsReturnNonStructural{6}> cwOpt6;
+    std::constant_wrapper<OpsReturnNonStructural{3}> cwOpt3;
+
+    std::same_as<NonStructural> decltype(auto) equal = cwOpt6 == cwOpt3;
+    assert(equal.get() == 0);
+
+    std::same_as<NonStructural> decltype(auto) not_equal = cwOpt6 != cwOpt3;
+    assert(not_equal.get() == 1);
+
+    std::same_as<NonStructural> decltype(auto) less = cwOpt6 < cwOpt3;
+    assert(less.get() == 0);
+
+    std::same_as<NonStructural> decltype(auto) less_equal = cwOpt6 <= cwOpt3;
+    assert(less_equal.get() == 0);
+
+    std::same_as<NonStructural> decltype(auto) greater = cwOpt6 > cwOpt3;
+    assert(greater.get() == 1);
+
+    std::same_as<NonStructural> decltype(auto) greater_equal = cwOpt6 >= cwOpt3;
+    assert(greater_equal.get() == 1);
+
+    std::same_as<NonStructural> decltype(auto) spaceship = cwOpt6 <=> cwOpt3;
+    assert(spaceship.get() == 1);
+  }
+
+  {
+    // Mix with runtime param: these operators are not used (built-in operators)
+    std::constant_wrapper<6> cw6;
+    int i = 3;
+
+    std::same_as<bool> decltype(auto) equal = cw6 == i;
+    assert(!equal);
+
+    std::same_as<bool> decltype(auto) not_equal = cw6 != i;
+    assert(not_equal);
+
+    std::same_as<bool> decltype(auto) less = cw6 < i;
+    assert(!less);
+
+    std::same_as<bool> decltype(auto) less_equal = cw6 <= i;
+    assert(!less_equal);
+
+    std::same_as<bool> decltype(auto) greater = cw6 > i;
+    assert(greater);
+
+    std::same_as<bool> decltype(auto) greater_equal = cw6 >= i;
+    assert(greater_equal);
+
+    std::same_as<std::strong_ordering> decltype(auto) spaceship = cw6 <=> i;
+    assert(spaceship == std::strong_ordering::greater);
+  }
+
+  {
+    // with integral_constant
+    std::constant_wrapper<6> cw6;
+    std::integral_constant<int, 3> ic3;
+
+    std::same_as<std::constant_wrapper<false>> decltype(auto) equal = cw6 == ic3;
+    static_assert(!static_cast<bool>(equal));
+
+    std::same_as<std::constant_wrapper<true>> decltype(auto) not_equal = cw6 != ic3;
+    static_assert(static_cast<bool>(not_equal));
+
+    std::same_as<std::constant_wrapper<false>> decltype(auto) less = cw6 < ic3;
+    static_assert(!static_cast<bool>(less));
+
+    std::same_as<std::constant_wrapper<false>> decltype(auto) less_equal = cw6 <= ic3;
+    static_assert(!static_cast<bool>(less_equal));
+
+    std::same_as<std::constant_wrapper<true>> decltype(auto) greater = cw6 > ic3;
+    static_assert(static_cast<bool>(greater));
+
+    std::same_as<std::constant_wrapper<true>> decltype(auto) greater_equal = cw6 >= ic3;
+    static_assert(static_cast<bool>(greater_equal));
+
+    // strong_ordering is not a structural type
+    std::same_as<std::strong_ordering> decltype(auto) spaceship = cw6 <=> ic3;
+    assert(spaceship == std::strong_ordering::greater);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/const.wrap.class/convert.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/convert.pass.cpp
new file mode 100644
index 0000000000000..a9567cb08b009
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/convert.pass.cpp
@@ -0,0 +1,85 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constant_wrapper
+
+// constexpr operator decltype(value)() const noexcept { return value; }
+
+#include <cassert>
+#include <utility>
+
+struct S {
+  int value;
+
+  constexpr S(int v) : value(v) {}
+};
+
+constexpr void f1(const S&) {}
+
+constexpr bool test() {
+  {
+    // int conversion
+    std::constant_wrapper<6> cw6;
+    const int& result = cw6;
+    assert(result == 6);
+    assert(&result == &cw6.value);
+
+    static_assert(noexcept(static_cast<const int&>(cw6)));
+  }
+
+  {
+    // struct conversion
+    constexpr S s{42};
+    std::constant_wrapper<s> cws;
+    const S& result = cws;
+    assert(result.value == 42);
+    assert(&result == &cws.value);
+
+    static_assert(noexcept(static_cast<const S&>(cws)));
+  }
+
+  {
+    // array conversion
+    constexpr int arr[] = {1, 2, 3};
+    std::constant_wrapper<arr> cwArr;
+    const int (&result)[3] = cwArr;
+    assert(result[0] == 1);
+    assert(result[1] == 2);
+    assert(result[2] == 3);
+    assert(&result == &cwArr.value);
+
+    static_assert(noexcept(static_cast<const int (&)[3]>(cwArr)));
+  }
+
+  {
+    // function pointer conversion
+    constexpr int (*fptr)(int) = [](int x) constexpr { return x * 2; };
+    std::constant_wrapper<fptr> cwFptr;
+    int (*result)(int) = cwFptr;
+    assert(result(5) == 10);
+
+    static_assert(noexcept(static_cast<int (*)(int)>(cwFptr)));
+  }
+
+  {
+    // conversion is implicit
+    std::constant_wrapper<S{42}> cws;
+    f1(cws);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/const.wrap.class/ctad.compile.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/ctad.compile.pass.cpp
new file mode 100644
index 0000000000000..7210d7e2152d5
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/ctad.compile.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constant_wrapper
+
+// template<class T, size_t Extent>
+//   cw-fixed-value(T (&)[Extent]) -> cw-fixed-value<T[Extent]>;                   // exposition only
+
+#include <type_traits>
+#include <utility>
+
+constexpr int arr[] = {1, 2, 3};
+using T1            = std::constant_wrapper<arr>;
+static_assert(std::is_same_v<T1::value_type, const int[3]>);
+
+using T2 = std::constant_wrapper<"hello world">;
+static_assert(std::is_same_v<T2::value_type, const char[12]>);
+
+struct S {
+  int value;
+};
+
+constexpr S s[] = {{1}, {2}, {3}};
+using T3        = std::constant_wrapper<s>;
+static_assert(std::is_same_v<T3::value_type, const S[3]>);
diff --git a/libcxx/test/std/utilities/const.wrap.class/cw.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/cw.pass.cpp
new file mode 100644
index 0000000000000..4f660ee378239
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/cw.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constant_wrapper
+
+//   template<cw-fixed-value X>
+//    constexpr auto cw = constant_wrapper<X>{};
+
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+struct S {
+  int value;
+
+  constexpr S(int v) : value(v) {}
+
+  constexpr bool operator==(const S& other) const { return value == other.value; }
+};
+
+constexpr bool test() {
+  {
+    // int constant
+    std::same_as<const std::constant_wrapper<42>> decltype(auto) cw_val = std::cw<42>;
+    static_assert(cw_val == 42);
+  }
+
+  {
+    // struct constant
+    constexpr S s{13};
+    std::same_as<const std::constant_wrapper<s>> decltype(auto) cw_val = std::cw<s>;
+    static_assert(cw_val == s);
+  }
+
+  {
+    // array constant
+    constexpr int arr[] = {1, 2, 3};
+    // gcc complains that cw_val is unused
+    [[maybe_unused]] std::same_as<const std::constant_wrapper<arr>> decltype(auto) cw_val = std::cw<arr>;
+    static_assert(cw_val[0] == 1);
+    static_assert(cw_val[1] == 2);
+    static_assert(cw_val[2] == 3);
+  }
+
+  {
+    // string literals
+    [[maybe_unused]] std::same_as<const std::constant_wrapper<"hello">> decltype(auto) cw_val = std::cw<"hello">;
+    static_assert(cw_val[0] == 'h');
+    static_assert(cw_val[1] == 'e');
+    static_assert(cw_val[2] == 'l');
+    static_assert(cw_val[3] == 'l');
+    static_assert(cw_val[4] == 'o');
+    static_assert(cw_val[5] == '\0');
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/const.wrap.class/cw_fixed.array.ctor.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/cw_fixed.array.ctor.pass.cpp
new file mode 100644
index 0000000000000..8bccfcfaaaa43
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/cw_fixed.array.ctor.pass.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constant_wrapper
+
+// constexpr cw-fixed-value(T (&arr)[Extent]) noexcept;
+
+#include <cassert>
+#include <utility>
+
+template <auto v>
+auto helper(std::constant_wrapper<v>) -> decltype(v);
+
+template <class T>
+using cw_fixed_value = decltype(helper(std::constant_wrapper<T{}>{}));
+
+struct S {
+  int value;
+
+  constexpr S(int v = 0) : value(v) {}
+
+  constexpr bool operator==(const S& other) const { return value == other.value; }
+};
+
+constexpr bool test() {
+  {
+    // int array construction
+    // the conversion from int array to cw-fixed-value<int array> uses the constructor
+    constexpr int arr[] = {1, 2, 3};
+    std::constant_wrapper<arr> cw{};
+    assert(cw.value[0] == 1);
+    assert(cw.value[1] == 2);
+    assert(cw.value[2] == 3);
+  }
+
+  {
+    // struct array construction
+    constexpr S s[] = {{1}, {2}, {3}};
+    std::constant_wrapper<s> cw{};
+    assert(cw.value[0] == S{1});
+    assert(cw.value[1] == S{2});
+    assert(cw.value[2] == S{3});
+  }
+
+  {
+    // calling the constructor
+    constexpr int arr[] = {1, 2, 3, 4, 5};
+    constexpr cw_fixed_value<const int[5]> ci(arr);
+    std::constant_wrapper<ci> cw;
+    assert(cw.value[0] == 1);
+    assert(cw.value[1] == 2);
+    assert(cw.value[2] == 3);
+    assert(cw.value[3] == 4);
+    assert(cw.value[4] == 5);
+
+    static_assert(noexcept(cw_fixed_value<const int[5]>{arr}));
+  }
+
+  {
+    // the constructor is implicit
+    constexpr int arr[]                       = {1, 2, 3, 4, 5};
+    constexpr cw_fixed_value<const int[5]> ci = arr;
+    std::constant_wrapper<ci> cw;
+    assert(cw.value[0] == 1);
+    assert(cw.value[1] == 2);
+    assert(cw.value[2] == 3);
+    assert(cw.value[3] == 4);
+    assert(cw.value[4] == 5);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/const.wrap.class/cw_fixed.ctor.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/cw_fixed.ctor.pass.cpp
new file mode 100644
index 0000000000000..30c2952f57d4c
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/cw_fixed.ctor.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constant_wrapper
+
+// constexpr cw-fixed-value(type v) noexcept : data(v) {}
+
+#include <cassert>
+#include <utility>
+
+template <auto v>
+auto helper(std::constant_wrapper<v>) -> decltype(v);
+
+template <class T>
+using cw_fixed_value = decltype(helper(std::constant_wrapper<T{}>{}));
+
+struct S {
+  int value;
+
+  constexpr S(int v = 0) : value(v) {}
+
+  constexpr bool operator==(const S& other) const { return value == other.value; }
+};
+
+constexpr bool test() {
+  {
+    // int construction
+    // the conversion from int to cw-fixed-value<int> uses the constructor
+    std::constant_wrapper<42> cw{};
+    assert(cw.value == 42);
+  }
+
+  {
+    // struct construction
+    std::constant_wrapper<S{13}> cw{};
+    assert(cw.value == S{13});
+  }
+
+  {
+    // calling the constructor
+    constexpr cw_fixed_value<int> ci{42};
+    std::constant_wrapper<ci> cw;
+    assert(cw == 42);
+
+    static_assert(noexcept(cw_fixed_value<int>{42}));
+  }
+
+  {
+    // the constructor is implicit
+    constexpr cw_fixed_value<int> ci = 42;
+    std::constant_wrapper<ci> cw;
+    assert(cw == 42);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/const.wrap.class/general.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/general.pass.cpp
new file mode 100644
index 0000000000000..d6baa3ae84ba1
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/general.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constant_wrapper
+
+// The class template constant_wrapper aids in metaprogramming by ensuring that the
+// evaluation of expressions comprised entirely of constant_wrapper are core constant
+// expressions ([expr.const]), regardless of the context in which they appear. In particular,
+// this enables use of constant_wrapper values that are passed as arguments to constexpr
+// functions to be used in constant expressions.
+
+#include <utility>
+
+constexpr auto initial_phase(auto quantity_1, auto quantity_2) { return quantity_1 + quantity_2; }
+
+constexpr auto middle_phase(auto tbd) { return tbd; }
+
+constexpr void profit() {}
+
+void final_phase(auto gathered, auto available) {
+  if constexpr (gathered == available)
+    profit();
+}
+
+void impeccable_underground_planning() {
+  auto gathered_quantity = middle_phase(initial_phase(std::cw<42>, std::cw<13>));
+  static_assert(gathered_quantity == 55);
+  auto all_available = std::cw<55>;
+  final_phase(gathered_quantity, all_available);
+}
+
+int main(int, char**) {
+  impeccable_underground_planning();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/const.wrap.class/helpers.h b/libcxx/test/std/utilities/const.wrap.class/helpers.h
new file mode 100644
index 0000000000000..a1f15bbc4bebd
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/helpers.h
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_STD_UTILITIES_CONST_WRAP_CLASS_HELPERS_H
+#define TEST_STD_UTILITIES_CONST_WRAP_CLASS_HELPERS_H
+
+struct NonStructural {
+  constexpr NonStructural(int i) : value(i) {}
+
+  constexpr int get() const { return value; }
+
+private:
+  int value;
+};
+
+#endif // TEST_STD_UTILITIES_CONST_WRAP_CLASS_HELPERS_H
diff --git a/libcxx/test/std/utilities/const.wrap.class/mem_ptr.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/mem_ptr.pass.cpp
new file mode 100644
index 0000000000000..bd342ee597e1b
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/mem_ptr.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// ADDITIONAL_COMPILE_FLAGS: -Wno-unused-value
+
+// constant_wrapper
+
+// template<constexpr-param L, constexpr-param R>
+//   friend constexpr auto operator->*(L, R) noexcept -> constant_wrapper<L::value->*(R::value)>
+//     { return {}; }
+
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "helpers.h"
+
+struct S {
+  int member = 42;
+};
+
+constexpr S s;
+
+template <class L, class R>
+concept HasPtrToMem = requires(L l, R r) {
+  { l->*r };
+};
+
+template <class L, class R>
+concept HasNoexceptPtrToMem = requires(L l, R r) {
+  { l->*r } noexcept;
+};
+
+struct WithOps {
+  int value;
+  constexpr WithOps(int v) : value(v) {}
+
+  friend constexpr auto operator->*(WithOps w, int WithOps::* pm) { return w.value + (&w)->*pm; }
+};
+
+struct OpsReturnNonStructural {
+  int value;
+  constexpr OpsReturnNonStructural(int v) : value(v) {}
+
+  friend constexpr auto operator->*(OpsReturnNonStructural o, int OpsReturnNonStructural::* pm) {
+    return NonStructural{o.value + (&o)->*pm};
+  }
+};
+
+struct NoOps {};
+
+static_assert(HasPtrToMem<std::constant_wrapper<&s>, std::constant_wrapper<&S::member>>);
+static_assert(HasNoexceptPtrToMem<std::constant_wrapper<&s>, std::constant_wrapper<&S::member>>);
+
+static_assert(HasPtrToMem<std::constant_wrapper<&s>, int S::*>);
+static_assert(!HasPtrToMem<std::constant_wrapper<&s>, int>);
+
+constexpr bool test() {
+  {
+    // use builtin operator->*
+    std::constant_wrapper<(&s)> cwS;
+    std::constant_wrapper<&S::member> cwPM;
+    std::same_as<std::constant_wrapper<42>> decltype(auto) result1 = cwS->*cwPM;
+    static_assert(result1 == 42);
+  }
+
+  {
+    // mix runtime and constant_wrapper parameters, will use built-in operator
+    std::constant_wrapper<(&s)> cwS;
+    int S::* pm                                     = &S::member;
+    std::same_as<const int&> decltype(auto) result1 = cwS->*pm;
+    assert(result1 == 42);
+  }
+
+  {
+    // custom operator->*
+    std::constant_wrapper<WithOps{42}> cwWO;
+    std::constant_wrapper<&WithOps::value> cwPM;
+    std::same_as<std::constant_wrapper<84>> decltype(auto) result1 = cwWO->*cwPM;
+    static_assert(result1 == 84);
+  }
+
+  {
+    // Return non-structural type
+    // Will use underlying type's runtime operators
+    std::constant_wrapper<OpsReturnNonStructural{42}> cwORNS;
+    std::constant_wrapper<&OpsReturnNonStructural::value> cwPM;
+    std::same_as<NonStructural> decltype(auto) result1 = cwORNS->*cwPM;
+    assert(result1.get() == 84);
+  }
+
+  {
+    // integral_constant
+    std::constant_wrapper<(&s)> cwS;
+    std::integral_constant<int S::*, &S::member> icPM;
+    std::same_as<std::constant_wrapper<42>> decltype(auto) result1 = cwS->*icPM;
+    static_assert(result1 == 42);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/const.wrap.class/pseudo_mutators.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/pseudo_mutators.pass.cpp
new file mode 100644
index 0000000000000..032afb8ea8cc3
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/pseudo_mutators.pass.cpp
@@ -0,0 +1,440 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constant_wrapper pseudo-mutators
+
+// template<constexpr-param T>
+//   constexpr auto operator++(this T) noexcept
+//     -> constant_wrapper<++(T::value)> { return {}; }
+// template<constexpr-param T>
+//   constexpr auto operator++(this T, int) noexcept
+//     -> constant_wrapper<(T::value++)> { return {}; }
+// template<constexpr-param T>
+//   constexpr auto operator--(this T) noexcept
+//     -> constant_wrapper<--(T::value)> { return {}; }
+// template<constexpr-param T>
+//   constexpr auto operator--(this T, int) noexcept
+//     -> constant_wrapper<(T::value--)> { return {}; }
+
+// template<constexpr-param T, constexpr-param R>
+//   constexpr auto operator+=(this T, R) noexcept
+//     -> constant_wrapper<(T::value += R::value)> { return {}; }
+// template<constexpr-param T, constexpr-param R>
+//   constexpr auto operator-=(this T, R) noexcept
+//     -> constant_wrapper<(T::value -= R::value)> { return {}; }
+// template<constexpr-param T, constexpr-param R>
+//   constexpr auto operator*=(this T, R) noexcept
+//     -> constant_wrapper<(T::value *= R::value)> { return {}; }
+// template<constexpr-param T, constexpr-param R>
+//   constexpr auto operator/=(this T, R) noexcept
+//     -> constant_wrapper<(T::value /= R::value)> { return {}; }
+// template<constexpr-param T, constexpr-param R>
+//   constexpr auto operator%=(this T, R) noexcept
+//     -> constant_wrapper<(T::value %= R::value)> { return {}; }
+// template<constexpr-param T, constexpr-param R>
+//   constexpr auto operator&=(this T, R) noexcept
+//     -> constant_wrapper<(T::value &= R::value)> { return {}; }
+// template<constexpr-param T, constexpr-param R>
+//   constexpr auto operator|=(this T, R) noexcept
+//     -> constant_wrapper<(T::value |= R::value)> { return {}; }
+// template<constexpr-param T, constexpr-param R>
+//   constexpr auto operator^=(this T, R) noexcept
+//     -> constant_wrapper<(T::value ^= R::value)> { return {}; }
+// template<constexpr-param T, constexpr-param R>
+//   constexpr auto operator<<=(this T, R) noexcept
+//     -> constant_wrapper<(T::value <<= R::value)> { return {}; }
+// template<constexpr-param T, constexpr-param R>
+//   constexpr auto operator>>=(this T, R) noexcept
+//     -> constant_wrapper<(T::value >>= R::value)> { return {}; }
+
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "helpers.h"
+
+struct WithOps {
+  int value;
+
+  constexpr WithOps(int v) : value(v) {}
+
+  constexpr auto operator++() const { return WithOps{value + 1}; }
+  constexpr auto operator++(int) const { return WithOps{value + 1}; }
+  constexpr auto operator--() const { return WithOps{value - 1}; }
+  constexpr auto operator--(int) const { return WithOps{value - 1}; }
+
+  constexpr auto operator+=(WithOps r) const { return WithOps{value + r.value}; }
+  constexpr auto operator-=(WithOps r) const { return WithOps{value - r.value}; }
+  constexpr auto operator*=(WithOps r) const { return WithOps{value * r.value}; }
+  constexpr auto operator/=(WithOps r) const { return WithOps{value / r.value}; }
+  constexpr auto operator%=(WithOps r) const { return WithOps{value % r.value}; }
+  constexpr auto operator&=(WithOps r) const { return WithOps{value & r.value}; }
+  constexpr auto operator|=(WithOps r) const { return WithOps{value | r.value}; }
+  constexpr auto operator^=(WithOps r) const { return WithOps{value ^ r.value}; }
+  constexpr auto operator<<=(WithOps r) const { return WithOps{value << r.value}; }
+  constexpr auto operator>>=(WithOps r) const { return WithOps{value >> r.value}; }
+};
+
+struct OpsReturnNonStructural {
+  int value;
+
+  constexpr OpsReturnNonStructural(int v) : value(v) {}
+
+  constexpr auto operator++() const { return NonStructural{value + 1}; }
+  constexpr auto operator++(int) const { return NonStructural{value + 1}; }
+  constexpr auto operator--() const { return NonStructural{value - 1}; }
+  constexpr auto operator--(int) const { return NonStructural{value - 1}; }
+
+  constexpr auto operator+=(OpsReturnNonStructural r) const { return NonStructural{value + r.value}; }
+  constexpr auto operator-=(OpsReturnNonStructural r) const { return NonStructural{value - r.value}; }
+  constexpr auto operator*=(OpsReturnNonStructural r) const { return NonStructural{value * r.value}; }
+  constexpr auto operator/=(OpsReturnNonStructural r) const { return NonStructural{value / r.value}; }
+  constexpr auto operator%=(OpsReturnNonStructural r) const { return NonStructural{value % r.value}; }
+  constexpr auto operator&=(OpsReturnNonStructural r) const { return NonStructural{value & r.value}; }
+  constexpr auto operator|=(OpsReturnNonStructural r) const { return NonStructural{value | r.value}; }
+  constexpr auto operator^=(OpsReturnNonStructural r) const { return NonStructural{value ^ r.value}; }
+  constexpr auto operator<<=(OpsReturnNonStructural r) const { return NonStructural{value << r.value}; }
+  constexpr auto operator>>=(OpsReturnNonStructural r) const { return NonStructural{value >> r.value}; }
+};
+
+struct NoOps {};
+
+template <class T>
+concept HasPreIncrement = requires(T t) {
+  { ++t };
+};
+
+template <class T>
+concept HasPostIncrement = requires(T t) {
+  { t++ };
+};
+
+template <class T>
+concept HasPreDecrement = requires(T t) {
+  { --t };
+};
+
+template <class T>
+concept HasPostDecrement = requires(T t) {
+  { t-- };
+};
+
+template <class L, class R>
+concept HasPlusAssign = requires(L l, R r) {
+  { l += r };
+};
+
+template <class L, class R>
+concept HasMinusAssign = requires(L l, R r) {
+  { l -= r };
+};
+
+template <class L, class R>
+concept HasMultiplyAssign = requires(L l, R r) {
+  { l *= r };
+};
+
+template <class L, class R>
+concept HasDivideAssign = requires(L l, R r) {
+  { l /= r };
+};
+
+template <class L, class R>
+concept HasModuloAssign = requires(L l, R r) {
+  { l %= r };
+};
+
+template <class L, class R>
+concept HasBitAndAssign = requires(L l, R r) {
+  { l &= r };
+};
+
+template <class L, class R>
+concept HasBitOrAssign = requires(L l, R r) {
+  { l |= r };
+};
+
+template <class L, class R>
+concept HasBitXorAssign = requires(L l, R r) {
+  { l ^= r };
+};
+
+template <class L, class R>
+concept HasShiftLeftAssign = requires(L l, R r) {
+  { l <<= r };
+};
+
+template <class L, class R>
+concept HasShiftRightAssign = requires(L l, R r) {
+  { l >>= r };
+};
+
+template <class T>
+concept HasNoexceptPreIncrement = requires(T t) {
+  { ++t } noexcept;
+};
+
+template <class T>
+concept HasNoexceptPostIncrement = requires(T t) {
+  { t++ } noexcept;
+};
+
+template <class T>
+concept HasNoexceptPreDecrement = requires(T t) {
+  { --t } noexcept;
+};
+
+template <class T>
+concept HasNoexceptPostDecrement = requires(T t) {
+  { t-- } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptPlusAssign = requires(L l, R r) {
+  { l += r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptMinusAssign = requires(L l, R r) {
+  { l -= r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptMultiplyAssign = requires(L l, R r) {
+  { l *= r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptDivideAssign = requires(L l, R r) {
+  { l /= r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptModuloAssign = requires(L l, R r) {
+  { l %= r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptBitAndAssign = requires(L l, R r) {
+  { l &= r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptBitOrAssign = requires(L l, R r) {
+  { l |= r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptBitXorAssign = requires(L l, R r) {
+  { l ^= r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptShiftLeftAssign = requires(L l, R r) {
+  { l <<= r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptShiftRightAssign = requires(L l, R r) {
+  { l >>= r } noexcept;
+};
+
+// Pseudo-mutators does work with int as built-in types mutating operators are const
+static_assert(!HasPreIncrement<std::constant_wrapper<6>>);
+static_assert(!HasPostIncrement<std::constant_wrapper<6>>);
+static_assert(!HasPreDecrement<std::constant_wrapper<6>>);
+static_assert(!HasPostDecrement<std::constant_wrapper<6>>);
+
+static_assert(!HasPlusAssign<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(!HasMinusAssign<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(!HasMultiplyAssign<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(!HasDivideAssign<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(!HasModuloAssign<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(!HasBitAndAssign<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(!HasBitOrAssign<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(!HasBitXorAssign<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(!HasShiftLeftAssign<std::constant_wrapper<6>, std::constant_wrapper<1>>);
+static_assert(!HasShiftRightAssign<std::constant_wrapper<6>, std::constant_wrapper<1>>);
+
+// NoOps - pseudo-mutators shouldn't work without supporting operators
+static_assert(!HasPreIncrement<std::constant_wrapper<NoOps{}>>);
+static_assert(!HasPostIncrement<std::constant_wrapper<NoOps{}>>);
+static_assert(!HasPreDecrement<std::constant_wrapper<NoOps{}>>);
+static_assert(!HasPostDecrement<std::constant_wrapper<NoOps{}>>);
+
+static_assert(!HasPlusAssign<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasMinusAssign<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasMultiplyAssign<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasDivideAssign<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasModuloAssign<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasBitAndAssign<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasBitOrAssign<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasBitXorAssign<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasShiftLeftAssign<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasShiftRightAssign<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+
+// Pseudo-mutators work with WithOps types
+static_assert(HasNoexceptPreIncrement<std::constant_wrapper<WithOps{6}>>);
+static_assert(HasNoexceptPostIncrement<std::constant_wrapper<WithOps{6}>>);
+static_assert(HasNoexceptPreDecrement<std::constant_wrapper<WithOps{6}>>);
+static_assert(HasNoexceptPostDecrement<std::constant_wrapper<WithOps{6}>>);
+
+static_assert(HasNoexceptPlusAssign<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptMinusAssign<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptMultiplyAssign<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptDivideAssign<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptModuloAssign<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptBitAndAssign<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptBitOrAssign<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptBitXorAssign<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptShiftLeftAssign<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{1}>>);
+static_assert(HasNoexceptShiftRightAssign<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{1}>>);
+
+// clang-format off
+// Non-structural return types cannot use implicit conversions too because they are member functions and cannot be found through ADL
+static_assert(!HasPreIncrement<std::constant_wrapper<OpsReturnNonStructural{6}>>);
+static_assert(!HasPostIncrement<std::constant_wrapper<OpsReturnNonStructural{6}>>);
+static_assert(!HasPreDecrement<std::constant_wrapper<OpsReturnNonStructural{6}>>);
+static_assert(!HasPostDecrement<std::constant_wrapper<OpsReturnNonStructural{6}>>);
+
+static_assert(!HasPlusAssign<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasMinusAssign<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasMultiplyAssign<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasDivideAssign<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasModuloAssign<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasBitAndAssign<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasBitOrAssign<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasBitXorAssign<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{3}>>);
+static_assert(!HasShiftLeftAssign<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{1}>>);
+static_assert(!HasShiftRightAssign<std::constant_wrapper<OpsReturnNonStructural{6}>, std::constant_wrapper<OpsReturnNonStructural{1}>>);
+// clang-format on
+
+// LWG 4383. constant_wrapper's pseudo-mutators are underconstrained
+// https://cplusplus.github.io/LWG/issue4383
+constexpr void lwg4383_f(auto t) {
+  if constexpr (requires { +t; }) // ok
+    +t;
+  if constexpr (requires { -t; }) // ok
+    -t;
+  if constexpr (requires { ++t; }) // no hard error
+    ++t;
+  if constexpr (requires { --t; }) // no hard error
+    --t;
+}
+
+struct S {
+  /* constexpr */ int operator+() const { return 0; }
+  /* constexpr */ int operator++() { return 0; }
+  constexpr void operator-() const {}
+  constexpr void operator--() {}
+};
+
+constexpr void lwg4383() { lwg4383_f(std::cw<S{}>); }
+
+constexpr bool test() {
+  {
+    // WithOps increment/decrement
+    std::constant_wrapper<WithOps{5}> cwWithOps5;
+    std::same_as<std::constant_wrapper<WithOps{6}>> decltype(auto) result1 = ++cwWithOps5;
+    static_assert(result1.value.value == 6);
+
+    std::same_as<std::constant_wrapper<WithOps{6}>> decltype(auto) result2 = cwWithOps5++;
+    static_assert(result2.value.value == 6);
+
+    std::same_as<std::constant_wrapper<WithOps{4}>> decltype(auto) result3 = --cwWithOps5;
+    static_assert(result3.value.value == 4);
+
+    std::same_as<std::constant_wrapper<WithOps{4}>> decltype(auto) result4 = cwWithOps5--;
+    static_assert(result4.value.value == 4);
+  }
+
+  {
+    // WithOps compound assignments
+    std::constant_wrapper<WithOps{10}> cwWithOps10;
+    std::constant_wrapper<WithOps{3}> cwWithOps3;
+
+    std::same_as<std::constant_wrapper<WithOps{13}>> decltype(auto) result1 = cwWithOps10 += cwWithOps3;
+    static_assert(result1.value.value == 13);
+
+    std::same_as<std::constant_wrapper<WithOps{7}>> decltype(auto) result2 = cwWithOps10 -= cwWithOps3;
+    static_assert(result2.value.value == 7);
+
+    std::same_as<std::constant_wrapper<WithOps{30}>> decltype(auto) result3 = cwWithOps10 *= cwWithOps3;
+    static_assert(result3.value.value == 30);
+
+    std::same_as<std::constant_wrapper<WithOps{3}>> decltype(auto) result4 = cwWithOps10 /= cwWithOps3;
+    static_assert(result4.value.value == 3);
+
+    std::same_as<std::constant_wrapper<WithOps{1}>> decltype(auto) result5 = cwWithOps10 %= cwWithOps3;
+    static_assert(result5.value.value == 1);
+
+    std::same_as<std::constant_wrapper<WithOps{2}>> decltype(auto) result6 = cwWithOps10 &= cwWithOps3;
+    static_assert(result6.value.value == 2);
+
+    std::same_as<std::constant_wrapper<WithOps{11}>> decltype(auto) result7 = cwWithOps10 |= cwWithOps3;
+    static_assert(result7.value.value == 11);
+
+    std::same_as<std::constant_wrapper<WithOps{9}>> decltype(auto) result8 = cwWithOps10 ^= cwWithOps3;
+    static_assert(result8.value.value == 9);
+
+    std::same_as<std::constant_wrapper<WithOps{80}>> decltype(auto) result9 = cwWithOps10 <<= cwWithOps3;
+    static_assert(result9.value.value == 80);
+
+    std::same_as<std::constant_wrapper<WithOps{1}>> decltype(auto) result10 = cwWithOps10 >>= cwWithOps3;
+    static_assert(result10.value.value == 1);
+  }
+
+  {
+    // integral_constant compound assignments
+    std::constant_wrapper<WithOps{10}> cwWithOps10;
+    std::integral_constant<WithOps, WithOps{3}> icWithOps3;
+
+    std::same_as<std::constant_wrapper<WithOps{13}>> decltype(auto) result1 = cwWithOps10 += icWithOps3;
+    static_assert(result1.value.value == 13);
+
+    std::same_as<std::constant_wrapper<WithOps{7}>> decltype(auto) result2 = cwWithOps10 -= icWithOps3;
+    static_assert(result2.value.value == 7);
+
+    std::same_as<std::constant_wrapper<WithOps{30}>> decltype(auto) result3 = cwWithOps10 *= icWithOps3;
+    static_assert(result3.value.value == 30);
+
+    std::same_as<std::constant_wrapper<WithOps{3}>> decltype(auto) result4 = cwWithOps10 /= icWithOps3;
+    static_assert(result4.value.value == 3);
+
+    std::same_as<std::constant_wrapper<WithOps{1}>> decltype(auto) result5 = cwWithOps10 %= icWithOps3;
+    static_assert(result5.value.value == 1);
+
+    std::same_as<std::constant_wrapper<WithOps{2}>> decltype(auto) result6 = cwWithOps10 &= icWithOps3;
+    static_assert(result6.value.value == 2);
+
+    std::same_as<std::constant_wrapper<WithOps{11}>> decltype(auto) result7 = cwWithOps10 |= icWithOps3;
+    static_assert(result7.value.value == 11);
+
+    std::same_as<std::constant_wrapper<WithOps{9}>> decltype(auto) result8 = cwWithOps10 ^= icWithOps3;
+    static_assert(result8.value.value == 9);
+
+    std::same_as<std::constant_wrapper<WithOps{80}>> decltype(auto) result9 = cwWithOps10 <<= icWithOps3;
+    static_assert(result9.value.value == 80);
+
+    std::same_as<std::constant_wrapper<WithOps{1}>> decltype(auto) result10 = cwWithOps10 >>= icWithOps3;
+    static_assert(result10.value.value == 1);
+  }
+
+  lwg4383();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/const.wrap.class/subscript.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/subscript.pass.cpp
new file mode 100644
index 0000000000000..697f31cb1df04
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/subscript.pass.cpp
@@ -0,0 +1,183 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constant_wrapper
+
+// template<class... Args>
+// static constexpr decltype(auto) operator[](Args&&... args) noexcept(see below);
+
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "helpers.h"
+#include "MoveOnly.h"
+
+struct MoveOnlyIndex {
+  constexpr MoveOnly operator[](const MoveOnly& m1, MoveOnly m2, MoveOnly&& m3) const {
+    return MoveOnly(m1.get() + m2.get() + m3.get());
+  }
+};
+
+struct Nary {
+  constexpr int operator[](auto... args) const { return sizeof...(args); }
+};
+
+struct OverloadSet {
+  constexpr int operator[](int) const { return 1; }
+
+  constexpr int operator[](std::constant_wrapper<42>) const { return 2; }
+};
+
+struct ReturnNonStructural {
+  constexpr NonStructural operator[](int i) const { return NonStructural{i}; }
+};
+
+struct CWOnly {
+  constexpr int operator[](std::constant_wrapper<42>) const { return 42; }
+};
+
+struct ThrowingSubscript {
+  constexpr int operator[](int) const { return 42; }
+};
+
+struct NothrowSubscript {
+  constexpr int operator[](int) const noexcept { return 42; }
+};
+
+constexpr int arr[] = {1, 2, 3, 4};
+
+// Let subscr-expr be constant_wrapper<value[remove_cvref_t<Args>::value...]>{} if all types in remove_cvref_t<Args>... satisfy constexpr-param and constant_wrapper<value[remove_cvref_t<Args>::value...]>
+// is a valid type, otherwise let subscr-expr be value[std::forward<Args>(args)...].
+// - Constraints: subscr-expr is a valid expression.
+// - Remarks: The exception specification is equivalent to noexcept(subscr-expr).
+
+template <class T, class... Args>
+concept HasSubscript = requires(T t, Args&&... args) {
+  { t[std::forward<Args>(args)...] };
+};
+
+template <class T, class... Args>
+concept HasNothrowSubscript = requires(T t, Args&&... args) {
+  { t[std::forward<Args>(args)...] } noexcept;
+};
+
+static_assert(!HasSubscript<std::constant_wrapper<4>, std::constant_wrapper<1>>);
+
+static_assert(HasSubscript<std::constant_wrapper<arr>, int>);
+static_assert(HasSubscript<std::constant_wrapper<arr>, std::constant_wrapper<1>>);
+
+static_assert(HasNothrowSubscript<std::constant_wrapper<arr>, int>);
+static_assert(HasNothrowSubscript<std::constant_wrapper<arr>, std::constant_wrapper<1>>);
+
+static_assert(HasSubscript<std::constant_wrapper<NothrowSubscript{}>, int>);
+static_assert(HasNothrowSubscript<std::constant_wrapper<NothrowSubscript{}>, int>);
+
+static_assert(HasSubscript<std::constant_wrapper<ThrowingSubscript{}>, int>);
+static_assert(!HasNothrowSubscript<std::constant_wrapper<ThrowingSubscript{}>, int>);
+static_assert(HasNothrowSubscript<std::constant_wrapper<ThrowingSubscript{}>, std::constant_wrapper<1>>,
+              "the subscript expression is still nothrow because the constexpr path is taken");
+
+constexpr bool test() {
+  {
+    // with runtime param
+    using T                                        = std::constant_wrapper<arr>;
+    std::same_as<const int&> decltype(auto) result = T::operator[](1);
+    assert(result == 2);
+  }
+  {
+    // with constexpr param
+    using T                                                      = std::constant_wrapper<arr>;
+    std::same_as<std::constant_wrapper<2>> decltype(auto) result = T::operator[](std::cw<1>);
+    static_assert(result == 2);
+  }
+
+  {
+    // null-ary
+    using T                                                      = std::constant_wrapper<Nary{}>;
+    std::same_as<std::constant_wrapper<0>> decltype(auto) result = T::operator[]();
+    static_assert(result == 0);
+  }
+
+  {
+    // n-ary
+    using T                                                      = std::constant_wrapper<Nary{}>;
+    std::same_as<std::constant_wrapper<3>> decltype(auto) result = T::operator[](std::cw<1>, std::cw<2>, std::cw<3>);
+    static_assert(result == 3);
+  }
+
+  {
+    // mixing constexpr and runtime
+    using T                                 = std::constant_wrapper<Nary{}>;
+    std::same_as<int> decltype(auto) result = T::operator[](std::cw<1>, 2, std::cw<3>);
+    assert(result == 3);
+  }
+
+  {
+    // move only
+    using T = std::constant_wrapper<MoveOnlyIndex{}>;
+    MoveOnly m1(1), m2(2), m3(3);
+    std::same_as<MoveOnly> decltype(auto) result = T::operator[](m1, std::move(m2), std::move(m3));
+    assert(result.get() == 6);
+  }
+  {
+    // overload set
+    // will always unwrap the constexpr params and call the non-constexpr overload
+    using T                                  = std::constant_wrapper<OverloadSet{}>;
+    std::same_as<int> decltype(auto) result1 = T::operator[](42);
+    assert(result1 == 1);
+    std::same_as<std::constant_wrapper<1>> decltype(auto) result2 = T::operator[](std::cw<42>);
+    static_assert(result2 == 1);
+  }
+
+  {
+    // return non-structural type
+    using T                                           = std::constant_wrapper<ReturnNonStructural{}>;
+    std::same_as<NonStructural> decltype(auto) result = T::operator[](5);
+    assert(result.get() == 5);
+  }
+
+  {
+    // return non-structural type with constexpr param
+    using T                                           = std::constant_wrapper<ReturnNonStructural{}>;
+    std::same_as<NonStructural> decltype(auto) result = T::operator[](std::cw<5>);
+    assert(result.get() == 5);
+  }
+
+  {
+    // cw only
+    // the upwrapping case doesn't work so it falls back to the normal invoke path
+    using T                                 = std::constant_wrapper<CWOnly{}>;
+    std::same_as<int> decltype(auto) result = T::operator[](std::cw<42>);
+    assert(result == 42);
+  }
+
+  {
+    // just use the index operator
+    assert(std::cw<"abcd">[2] == 'c');
+    assert(std::cw<"abcd">[std::cw<3>] == 'd');
+  }
+
+  {
+    // integral_constant
+    using T                                                      = std::constant_wrapper<arr>;
+    std::same_as<std::constant_wrapper<2>> decltype(auto) result = T::operator[](std::integral_constant<int, 1>{});
+    static_assert(result == 2);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/const.wrap.class/types.compile.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/types.compile.pass.cpp
new file mode 100644
index 0000000000000..96dce9e055209
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/types.compile.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constant_wrapper
+
+// static constexpr const auto & value = X.data;
+// using type = constant_wrapper;
+// using value_type = decltype(X)::type;
+
+#include <algorithm>
+#include <concepts>
+#include <utility>
+
+static_assert(std::constant_wrapper<42>::value == 42);
+static_assert(std::same_as<decltype(std::constant_wrapper<42>::value), const int&>);
+static_assert(std::same_as<std::constant_wrapper<42>::type, std::constant_wrapper<42>>);
+static_assert(std::same_as<std::constant_wrapper<42>::value_type, int>);
+
+struct S {
+  int member = 42;
+};
+
+static_assert(std::constant_wrapper<S{5}>::value.member == 5);
+static_assert(std::same_as<decltype(std::constant_wrapper<S{5}>::value), const S&>);
+static_assert(std::same_as<std::constant_wrapper<S{5}>::type, std::constant_wrapper<S{5}>>);
+static_assert(std::same_as<std::constant_wrapper<S{5}>::value_type, S>);
+
+static_assert(std::ranges::equal(std::constant_wrapper<"abcd">::value, "abcd"));
+static_assert(std::same_as<decltype(std::constant_wrapper<"abcd">::value), const char (&)[5]>);
+static_assert(std::same_as<std::constant_wrapper<"abcd">::type, std::constant_wrapper<"abcd">>);
+static_assert(std::same_as<std::constant_wrapper<"abcd">::value_type, const char[5]>);
diff --git a/libcxx/test/std/utilities/const.wrap.class/unary_ops.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/unary_ops.pass.cpp
new file mode 100644
index 0000000000000..5a474265a1719
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/unary_ops.pass.cpp
@@ -0,0 +1,246 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constant_wrapper
+
+//  template<constexpr-param T>
+//    friend constexpr auto operator+(T) noexcept -> constant_wrapper<(+T::value)>
+//      { return {}; }
+//  template<constexpr-param T>
+//    friend constexpr auto operator-(T) noexcept -> constant_wrapper<(-T::value)>
+//      { return {}; }
+//  template<constexpr-param T>
+//    friend constexpr auto operator~(T) noexcept -> constant_wrapper<(~T::value)>
+//      { return {}; }
+//  template<constexpr-param T>
+//    friend constexpr auto operator!(T) noexcept -> constant_wrapper<(!T::value)>
+//      { return {}; }
+//  template<constexpr-param T>
+//    friend constexpr auto operator&(T) noexcept -> constant_wrapper<(&T::value)>
+//      { return {}; }
+//  template<constexpr-param T>
+//    friend constexpr auto operator*(T) noexcept -> constant_wrapper<(*T::value)>
+//      { return {}; }
+
+#include <cassert>
+#include <concepts>
+#include <utility>
+
+#include "helpers.h"
+
+struct WithOps {
+  int value;
+
+  constexpr WithOps(int v) : value(v) {}
+
+  friend constexpr auto operator+(WithOps w) { return WithOps{+w.value}; }
+  friend constexpr auto operator-(WithOps w) { return WithOps{-w.value}; }
+  friend constexpr auto operator~(WithOps w) { return WithOps{~w.value}; }
+  friend constexpr auto operator!(WithOps w) { return WithOps{!w.value}; }
+  friend constexpr auto operator&(WithOps w) { return WithOps{w.value + 42}; }
+  friend constexpr auto operator*(WithOps w) { return WithOps{w.value - 42}; }
+};
+
+struct OpsReturnNonStructural {
+  int value;
+
+  constexpr OpsReturnNonStructural(int v) : value(v) {}
+
+  friend constexpr auto operator+(OpsReturnNonStructural o) { return NonStructural{+o.value}; }
+  friend constexpr auto operator-(OpsReturnNonStructural o) { return NonStructural{-o.value}; }
+  friend constexpr auto operator~(OpsReturnNonStructural o) { return NonStructural{~o.value}; }
+  friend constexpr auto operator!(OpsReturnNonStructural o) { return NonStructural{!o.value}; }
+  friend constexpr auto operator&(OpsReturnNonStructural o) { return NonStructural{o.value + 42}; }
+  friend constexpr auto operator*(OpsReturnNonStructural o) { return NonStructural{o.value - 42}; }
+};
+
+struct NoOps {};
+
+template <class T>
+concept HasPlus = requires(T t) {
+  { +t };
+};
+
+template <class T>
+concept HasMinus = requires(T t) {
+  { -t };
+};
+
+template <class T>
+concept HasBitNot = requires(T t) {
+  { ~t };
+};
+
+template <class T>
+concept HasNot = requires(T t) {
+  { !t };
+};
+
+template <class T>
+concept HasBitAnd = requires(T t) {
+  { &t };
+};
+
+template <class T>
+concept HasDeref = requires(T t) {
+  { *t };
+};
+
+template <class T>
+concept HasNoexceptPlus = requires(T t) {
+  { +t } noexcept;
+};
+
+template <class T>
+concept HasNoexceptMinus = requires(T t) {
+  { -t } noexcept;
+};
+
+template <class T>
+concept HasNoexceptBitNot = requires(T t) {
+  { ~t } noexcept;
+};
+
+template <class T>
+concept HasNoexceptNot = requires(T t) {
+  { !t } noexcept;
+};
+
+template <class T>
+concept HasNoexceptBitAnd = requires(T t) {
+  { &t } noexcept;
+};
+
+template <class T>
+concept HasNoexceptDeref = requires(T t) {
+  { *t } noexcept;
+};
+
+static_assert(HasPlus<std::constant_wrapper<WithOps{42}>>);
+static_assert(HasMinus<std::constant_wrapper<WithOps{42}>>);
+static_assert(HasBitNot<std::constant_wrapper<WithOps{42}>>);
+static_assert(HasNot<std::constant_wrapper<WithOps{42}>>);
+static_assert(HasBitAnd<std::constant_wrapper<WithOps{42}>>);
+static_assert(HasDeref<std::constant_wrapper<WithOps{42}>>);
+
+static_assert(HasNoexceptPlus<std::constant_wrapper<WithOps{42}>>);
+static_assert(HasNoexceptMinus<std::constant_wrapper<WithOps{42}>>);
+static_assert(HasNoexceptBitNot<std::constant_wrapper<WithOps{42}>>);
+static_assert(HasNoexceptNot<std::constant_wrapper<WithOps{42}>>);
+static_assert(HasNoexceptBitAnd<std::constant_wrapper<WithOps{42}>>);
+static_assert(HasNoexceptDeref<std::constant_wrapper<WithOps{42}>>);
+
+static_assert(HasNoexceptPlus<std::constant_wrapper<42>>);
+static_assert(HasNoexceptMinus<std::constant_wrapper<42>>);
+static_assert(HasNoexceptBitNot<std::constant_wrapper<42>>);
+static_assert(HasNoexceptNot<std::constant_wrapper<42>>);
+static_assert(HasNoexceptBitAnd<std::constant_wrapper<42>>);
+static_assert(!HasDeref<std::constant_wrapper<42>>);
+
+static_assert(!HasPlus<std::constant_wrapper<NoOps{}>>);
+static_assert(!HasMinus<std::constant_wrapper<NoOps{}>>);
+static_assert(!HasBitNot<std::constant_wrapper<NoOps{}>>);
+static_assert(!HasNot<std::constant_wrapper<NoOps{}>>);
+static_assert(HasBitAnd<std::constant_wrapper<NoOps{}>>);
+static_assert(!HasDeref<std::constant_wrapper<NoOps{}>>);
+
+// The operators from constant_wrapper do not exist, but they can be implicited converted
+// to the underlying type and use its operators instead.
+static_assert(HasPlus<std::constant_wrapper<OpsReturnNonStructural{42}>>);
+static_assert(HasMinus<std::constant_wrapper<OpsReturnNonStructural{42}>>);
+static_assert(HasBitNot<std::constant_wrapper<OpsReturnNonStructural{42}>>);
+static_assert(HasNot<std::constant_wrapper<OpsReturnNonStructural{42}>>);
+static_assert(HasBitAnd<std::constant_wrapper<OpsReturnNonStructural{42}>>);
+static_assert(HasDeref<std::constant_wrapper<OpsReturnNonStructural{42}>>);
+
+static_assert(!HasNoexceptPlus<std::constant_wrapper<OpsReturnNonStructural{42}>>);
+static_assert(!HasNoexceptMinus<std::constant_wrapper<OpsReturnNonStructural{42}>>);
+static_assert(!HasNoexceptBitNot<std::constant_wrapper<OpsReturnNonStructural{42}>>);
+static_assert(!HasNoexceptNot<std::constant_wrapper<OpsReturnNonStructural{42}>>);
+static_assert(!HasNoexceptBitAnd<std::constant_wrapper<OpsReturnNonStructural{42}>>);
+static_assert(!HasNoexceptDeref<std::constant_wrapper<OpsReturnNonStructural{42}>>);
+
+constexpr bool test() {
+  {
+    // int
+    std::constant_wrapper<42> cw42;
+
+    std::same_as<std::constant_wrapper<42>> decltype(auto) result = +cw42;
+    static_assert(result == 42);
+
+    std::same_as<std::constant_wrapper<-42>> decltype(auto) result2 = -cw42;
+    static_assert(result2 == -42);
+
+    std::same_as<std::constant_wrapper<~42>> decltype(auto) result3 = ~cw42;
+    static_assert(result3 == ~42);
+
+    std::same_as<std::constant_wrapper<!42>> decltype(auto) result4 = !cw42;
+    static_assert(result4 == !42);
+
+    std::same_as<std::constant_wrapper<&cw42.value>> decltype(auto) result5 = &cw42;
+    static_assert(result5 == &cw42.value);
+  }
+
+  {
+    // WithOps
+    std::constant_wrapper<WithOps{42}> cwWithOps;
+
+    std::same_as<std::constant_wrapper<WithOps{42}>> decltype(auto) result = +cwWithOps;
+    static_assert(result.value.value == 42);
+
+    std::same_as<std::constant_wrapper<WithOps{-42}>> decltype(auto) result2 = -cwWithOps;
+    static_assert(result2.value.value == -42);
+
+    std::same_as<std::constant_wrapper<WithOps{~42}>> decltype(auto) result3 = ~cwWithOps;
+    static_assert(result3.value.value == ~42);
+
+    std::same_as<std::constant_wrapper<WithOps{!42}>> decltype(auto) result4 = !cwWithOps;
+    static_assert(result4.value.value == !42);
+
+    std::same_as<std::constant_wrapper<WithOps{84}>> decltype(auto) result5 = &cwWithOps;
+    static_assert(result5.value.value == 84);
+
+    std::same_as<std::constant_wrapper<WithOps{0}>> decltype(auto) result6 = *cwWithOps;
+    static_assert(result6.value.value == 0);
+  }
+
+  {
+    // Return non-structural type
+    // Will use underlying type's runtime operators
+    std::constant_wrapper<OpsReturnNonStructural{42}> cwOpsReturnNonStructural;
+
+    std::same_as<NonStructural> decltype(auto) result = +cwOpsReturnNonStructural;
+    assert(result.get() == 42);
+
+    std::same_as<NonStructural> decltype(auto) result2 = -cwOpsReturnNonStructural;
+    assert(result2.get() == -42);
+
+    std::same_as<NonStructural> decltype(auto) result3 = ~cwOpsReturnNonStructural;
+    assert(result3.get() == ~42);
+
+    std::same_as<NonStructural> decltype(auto) result4 = !cwOpsReturnNonStructural;
+    assert(result4.get() == !42);
+
+    std::same_as<NonStructural> decltype(auto) result5 = &cwOpsReturnNonStructural;
+    assert(result5.get() == 84);
+
+    std::same_as<NonStructural> decltype(auto) result6 = *cwOpsReturnNonStructural;
+    assert(result6.get() == 0);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 56a1d0c39c9fb..1ebd821c5b85e 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -334,6 +334,13 @@ def add_version_header(tc):
             "values": {"c++20": 202207},
             "headers": ["concepts"],
         },
+        {
+            "name": "__cpp_lib_constant_wrapper",
+            "values": {
+                "c++26": 202603,
+            },
+            "headers": ["utility"],
+        },
         {
             "name": "__cpp_lib_constexpr_algorithms",
             "values": {

>From 91818a6996ceac5f200a5b5eefea49051ed12c8e 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/26] [libc++] P0792R14: 'function_ref'

---
 libcxx/include/CMakeLists.txt                 |   4 +
 libcxx/include/__functional/function_ref.h    |  46 +++++
 .../__functional/function_ref_common.h        |  72 +++++++
 .../include/__functional/function_ref_impl.h  | 181 ++++++++++++++++++
 libcxx/include/__utility/nontype.h            |  34 ++++
 libcxx/include/functional                     |   4 +
 libcxx/include/module.modulemap.in            |   2 +
 libcxx/include/utility                        |   9 +
 .../func.wrap.ref/call/const.pass.cpp         | 124 ++++++++++++
 .../call/const_noexcept.pass.cpp              | 124 ++++++++++++
 .../func.wrap.ref/call/default.pass.cpp       | 119 ++++++++++++
 .../func.wrap.ref/call/noexcept.pass.cpp      | 119 ++++++++++++
 12 files changed, 838 insertions(+)
 create mode 100644 libcxx/include/__functional/function_ref.h
 create mode 100644 libcxx/include/__functional/function_ref_common.h
 create mode 100644 libcxx/include/__functional/function_ref_impl.h
 create mode 100644 libcxx/include/__utility/nontype.h
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const_noexcept.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/default.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/noexcept.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 712ad8bf66593..a1775f131e489 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -432,6 +432,9 @@ set(files
   __functional/compose.h
   __functional/default_searcher.h
   __functional/function.h
+  __functional/function_ref_common.h
+  __functional/function_ref_impl.h
+  __functional/function_ref.h
   __functional/hash.h
   __functional/identity.h
   __functional/invoke.h
@@ -942,6 +945,7 @@ set(files
   __utility/lazy_synth_three_way_comparator.h
   __utility/move.h
   __utility/no_destroy.h
+  __utility/nontype.h
   __utility/pair.h
   __utility/piecewise_construct.h
   __utility/priority_tag.h
diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
new file mode 100644
index 0000000000000..01cc22a64018e
--- /dev/null
+++ b/libcxx/include/__functional/function_ref.h
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+// NOLINTBEGIN(readability-duplicate-include)
+
+#define _LIBCPP_FUNCTION_REF_CV
+#define _LIBCPP_FUNCTION_REF_NOEXCEPT false
+#include <__functional/function_ref_impl.h>
+#undef _LIBCPP_FUNCTION_REF_CV
+#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
+
+#define _LIBCPP_FUNCTION_REF_CV
+#define _LIBCPP_FUNCTION_REF_NOEXCEPT true
+#include <__functional/function_ref_impl.h>
+#undef _LIBCPP_FUNCTION_REF_CV
+#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
+
+#define _LIBCPP_FUNCTION_REF_CV const
+#define _LIBCPP_FUNCTION_REF_NOEXCEPT false
+#include <__functional/function_ref_impl.h>
+#undef _LIBCPP_FUNCTION_REF_CV
+#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
+
+#define _LIBCPP_FUNCTION_REF_CV const
+#define _LIBCPP_FUNCTION_REF_NOEXCEPT true
+#include <__functional/function_ref_impl.h>
+#undef _LIBCPP_FUNCTION_REF_CV
+#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
+
+// NOLINTEND(readability-duplicate-include)
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
new file mode 100644
index 0000000000000..f7d5575ebaffd
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
+#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
+
+#include <__config>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/remove_pointer.h>
+#include <__utility/nontype.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <class>
+inline constexpr bool __is_function_ref = false;
+
+template <class _Rp, class... _ArgTypes>
+inline constexpr bool __is_function_ref<function_ref<_Rp, _ArgTypes...>> = true;
+
+template <class _Fp, class _Tp>
+struct __function_ref_bind;
+
+template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...), _Tp> {
+  using type = _Rp(_ArgTypes...);
+};
+
+template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept, _Tp> {
+  using type = _Rp(_ArgTypes...) noexcept;
+};
+
+template <class _Tp, class _Mp, class _Gp>
+  requires is_object_v<_Mp>
+struct __function_ref_bind<_Mp _Gp::*, _Tp> {
+  using type = invoke_result_t<_Mp _Gp::*, _Tp&>;
+};
+
+template <class _Fp, class _Tp>
+using __function_ref_bind_t = __function_ref_bind<_Fp, _Tp>::type;
+
+template <class _Fp>
+  requires is_function_v<_Fp>
+function_ref(_Fp*) -> function_ref<_Fp>;
+
+template <auto _Fn>
+  requires is_function_v<remove_pointer_t<decltype(_Fn)>>
+function_ref(nontype_t<_Fn>) -> function_ref<remove_pointer_t<decltype(_Fn)>>;
+
+template <auto _Fn, class _Tp>
+function_ref(nontype_t<_Fn>, _Tp&&) -> function_ref<__function_ref_bind_t<decltype(_Fn), _Tp&>>;
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
new file mode 100644
index 0000000000000..4e1cd13a02ae2
--- /dev/null
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -0,0 +1,181 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <__assert>
+#include <__config>
+#include <__functional/function_ref_common.h>
+#include <__functional/invoke.h>
+#include <__memory/addressof.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_function.h>
+#include <__type_traits/is_member_pointer.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_pointer.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/forward.h>
+#include <__utility/nontype.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <class...>
+class function_ref;
+
+template <class _Rp, class... _ArgTypes>
+class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT)> {
+private:
+#  if _LIBCPP_FUNCTION_REF_NOEXCEPT == true
+  template <class... _Tp>
+  static constexpr bool __is_invocable_using = is_nothrow_invocable_r_v<_Rp, _Tp..., _ArgTypes...>;
+#  else
+  template <class... _Tp>
+  static constexpr bool __is_invocable_using = is_invocable_r_v<_Rp, _Tp..., _ArgTypes...>;
+#  endif
+
+  union __storage_t {
+    void* __obj_ptr;
+    void const* __obj_const_ptr;
+    void (*__fn_ptr)();
+
+    constexpr explicit __storage_t() noexcept : __obj_ptr(nullptr) {};
+
+    template <class _Tp>
+    constexpr explicit __storage_t(_Tp* __ptr) noexcept {
+      if constexpr (is_object_v<_Tp>) {
+        if constexpr (is_const_v<_Tp>) {
+          __obj_const_ptr = __ptr;
+        } else {
+          __obj_ptr = __ptr;
+        }
+      } else {
+        static_assert(is_function_v<_Tp>);
+        __fn_ptr = reinterpret_cast<void (*)()>(__ptr);
+      }
+    }
+  } __storage_;
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_t __storage) {
+    if constexpr (is_object_v<_Tp>) {
+      if constexpr (is_const_v<_Tp>) {
+        return static_cast<_Tp*>(__storage.__obj_const_ptr);
+      } else {
+        return static_cast<_Tp*>(__storage.__obj_ptr);
+      }
+    } else {
+      static_assert(is_function_v<_Tp>);
+      return reinterpret_cast<_Tp*>(__storage.__fn_ptr);
+    }
+  }
+
+  using __vtable_call_t = _Rp (*)(__storage_t, _ArgTypes&&...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+  __vtable_call_t __vtable_call_;
+
+public:
+  template <class _Fp>
+    requires is_function_v<_Fp> && __is_invocable_using<_Fp>
+  _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
+      : __storage_(__fn_ptr),
+        __vtable_call_(
+            [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+              return __get<_Fp>(__storage)(std::forward<_ArgTypes>(__args)...);
+            }) {
+    _LIBCPP_ASSERT_UNCATEGORIZED(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
+  }
+
+  template <class _Fp, class _Tp = remove_reference_t<_Fp>>
+    requires(!__is_function_ref<remove_cvref_t<_Fp>> && !is_member_pointer_v<_Tp> &&
+             __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&>)
+  _LIBCPP_HIDE_FROM_ABI function_ref(_Fp&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
+        __vtable_call_(
+            [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+              _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
+              return __obj(std::forward<_ArgTypes>(__args)...);
+            }) {}
+
+  template <auto _Fn>
+    requires __is_invocable_using<decltype(_Fn)>
+  constexpr function_ref(nontype_t<_Fn>) noexcept
+      : __vtable_call_([](__storage_t, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+          return std::invoke_r<_Rp>(_Fn, std::forward<_ArgTypes>(__args)...);
+        }) {
+    if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
+      static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
+    }
+  }
+
+  template <auto _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
+    requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp&>
+  constexpr function_ref(nontype_t<_Fn>, _Up&& __obj) noexcept
+      : __storage_(std::addressof(__obj)),
+        __vtable_call_(
+            [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+              _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
+              return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
+            }) {
+    if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
+      static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
+    }
+  }
+
+  template <auto _Fn, class _Tp>
+    requires __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp*>
+  constexpr function_ref(nontype_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
+      : __storage_(__obj_ptr),
+        __vtable_call_(
+            [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+              auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
+              return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
+            }) {
+    if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
+      static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
+    }
+
+    if constexpr (is_member_pointer_v<decltype(_Fn)>) {
+      _LIBCPP_ASSERT_UNCATEGORIZED(__obj_ptr != nullptr, "the object pointer should not be a nullptr");
+    }
+  }
+
+  constexpr function_ref(const function_ref&) noexcept = default;
+
+  constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+  template <class _Tp>
+    requires(!__is_function_ref<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_nontype_t<_Tp>)
+  function_ref& operator=(_Tp) = delete;
+
+  constexpr _Rp operator()(_ArgTypes... __args) const noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) {
+    return __vtable_call_(__storage_, std::forward<_ArgTypes>(__args)...);
+  }
+};
+
+template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
+                           _Tp> {
+  using type = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+};
+
+template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
+                           _Tp> {
+  using type = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__utility/nontype.h b/libcxx/include/__utility/nontype.h
new file mode 100644
index 0000000000000..86893f17e2d96
--- /dev/null
+++ b/libcxx/include/__utility/nontype.h
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___UTILITY_NONTYPE_H
+#define _LIBCPP___UTILITY_NONTYPE_H
+
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 26
+
+template <auto _Vp>
+struct nontype_t {
+  explicit nontype_t() = default;
+};
+
+template <auto _Vp>
+inline constexpr nontype_t<_Vp> nontype{};
+
+#endif // _LIBCPP_STD_VER >= 26
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___UTILITY_NONTYPE_H
diff --git a/libcxx/include/functional b/libcxx/include/functional
index 9ebcd818ec840..dbc023ac637d7 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -569,6 +569,10 @@ POLICY:  For non-variadic implementations, the number of arguments is limited
 #    include <__type_traits/unwrap_ref.h>
 #  endif
 
+#  if _LIBCPP_STD_VER >= 26
+#    include <__functional/function_ref.h>
+#  endif
+
 #  include <version>
 
 #  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 5748cc6f218a8..48ab2eba1b734 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1433,6 +1433,7 @@ module std [system] {
     }
     module default_searcher             { header "__functional/default_searcher.h" }
     module function                     { header "__functional/function.h" }
+    module function_ref                 { header "__functional/function_ref.h" }
     module hash                         { header "__functional/hash.h" }
     module identity                     { header "__functional/identity.h" }
     module invoke                       { header "__functional/invoke.h" }
@@ -2195,6 +2196,7 @@ module std [system] {
     module lazy_synth_three_way_comparator { header "__utility/lazy_synth_three_way_comparator.h" }
     module move                            { header "__utility/move.h" }
     module no_destroy                      { header "__utility/no_destroy.h" }
+    module nontype                         { header "__utility/nontype.h" }
     module pair                            {
       header "__utility/pair.h"
       export std.utility.piecewise_construct
diff --git a/libcxx/include/utility b/libcxx/include/utility
index 24b3baac91b1d..f357f2705bf21 100644
--- a/libcxx/include/utility
+++ b/libcxx/include/utility
@@ -266,6 +266,15 @@ template <size_t I>
 template <size_t I>
   inline constexpr in_place_index_t<I> in_place_index{};
 
+// nontype argument tag
+template<auto V>
+  struct nontype_t {
+    explicit nontype_t() = default;
+  };
+template<auto V> constexpr nontype_t<V> nontype{};
+
+template<auto V> constexpr nontype_t<V> nontype{};
+
 // [utility.underlying], to_underlying
 template <class T>
     constexpr underlying_type_t<T> to_underlying( T value ) noexcept; // C++23
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const.pass.cpp
new file mode 100644
index 0000000000000..53d7a5922353d
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const.pass.cpp
@@ -0,0 +1,124 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_invocable_v<std::function_ref<void() const>&>);
+static_assert(std::is_invocable_v<std::function_ref<void() const>>);
+static_assert(std::is_invocable_v<std::function_ref<void() const>&&>);
+static_assert(std::is_invocable_v<std::function_ref<void() const> const&>);
+static_assert(std::is_invocable_v<std::function_ref<void() const> const>);
+static_assert(std::is_invocable_v<std::function_ref<void() const> const&&>);
+
+int fn() { return 42; }
+
+struct {
+  int operator()() const { return 42; }
+} fn_obj;
+
+void test() {
+  // template<class F> function_ref(F* f) noexcept;
+  {
+    // initialized from a function
+    std::function_ref<int() const> fn_ref = fn;
+    assert(fn_ref() == 42);
+  }
+  {
+    // initialized from a function pointer
+    std::function_ref<int() const> fn_ref = &fn;
+    assert(fn_ref() == 42);
+  }
+
+  // template<class F> constexpr function_ref(F&& f) noexcept;
+  {
+    // initialized from a function object
+    std::function_ref<int() const> fn_ref = fn_obj;
+    assert(fn_ref() == 42);
+  }
+}
+
+struct S {
+  int data_mem = 42;
+
+  int fn_mem() const { return 42; }
+};
+
+void test_nontype_t() {
+  // template<auto f> constexpr function_ref(nontype_t<f>) noexcept;
+  {
+    // initialized from a function through `nontype_t`
+    std::function_ref<int() const> fn_ref = std::nontype_t<fn>();
+    assert(fn_ref() == 42);
+  }
+  {
+    // initialized from a function pointer through `nontype_t`
+    std::function_ref<int() const> fn_ref = std::nontype_t<&fn>();
+    assert(fn_ref() == 42);
+  }
+  {
+    // initialized from a function object through `nontype_t`
+    std::function_ref<int() const> fn_ref = std::nontype_t<fn_obj>();
+    assert(fn_ref() == 42);
+  }
+  {
+    S s;
+    // initialized from a pointer to data member through `nontype_t`
+    std::function_ref<int(S) const> fn_ref = std::nontype_t<&S::data_mem>();
+    assert(fn_ref(s) == 42);
+  }
+  {
+    S s;
+    // initialized from a pointer to function member through `nontype_t`
+    std::function_ref<int(S) const> fn_ref = std::nontype_t<&S::fn_mem>();
+    assert(fn_ref(s) == 42);
+  }
+
+  // template<auto f, class U>
+  //   constexpr function_ref(nontype_t<f>, U&& obj) noexcept;
+  {
+    S s;
+    // initialized from a pointer to data member through `nontype_t` and bound to an object through a reference
+    std::function_ref<int() const> fn_ref = {std::nontype_t<&S::data_mem>(), s};
+    assert(fn_ref() == 42);
+  }
+  {
+    S s;
+    // initialized from a pointer to function member through `nontype_t` and bound to an object through a reference
+    std::function_ref<int() const> fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+    assert(fn_ref() == 42);
+  }
+
+  // template<auto f, class T>
+  //   constexpr function_ref(nontype_t<f>, cv T* obj) noexcept;
+  {
+    S s;
+    // initialized from a pointer to data member through `nontype_t` and bound to an object through a pointer
+    std::function_ref<int() const> fn_ref = {std::nontype_t<&S::data_mem>(), &s};
+    assert(fn_ref() == 42);
+  }
+  {
+    S s;
+    // initialized from a pointer to function member through `nontype_t` and bound to an object through a pointer
+    static_assert(std::is_same_v<decltype(&s), S*>);
+    std::function_ref<int() const> fn_ref = {std::nontype_t<&S::fn_mem>(), &s};
+    assert(fn_ref() == 42);
+  }
+}
+
+int main(int, char**) {
+  test();
+  test_nontype_t();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const_noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const_noexcept.pass.cpp
new file mode 100644
index 0000000000000..18d44e4b7ef0b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const_noexcept.pass.cpp
@@ -0,0 +1,124 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_invocable_v<std::function_ref<void() const noexcept>&>);
+static_assert(std::is_invocable_v<std::function_ref<void() const noexcept>>);
+static_assert(std::is_invocable_v<std::function_ref<void() const noexcept>&&>);
+static_assert(std::is_invocable_v<std::function_ref<void() const noexcept> const&>);
+static_assert(std::is_invocable_v<std::function_ref<void() const noexcept> const >);
+static_assert(std::is_invocable_v<std::function_ref<void() const noexcept> const&&>);
+
+int fn() noexcept { return 42; }
+
+struct {
+  int operator()() const noexcept { return 42; }
+} fn_obj;
+
+void test() {
+  // template<class F> function_ref(F* f) noexcept;
+  {
+    // initialized from a function
+    std::function_ref<int() const noexcept> fn_ref = fn;
+    assert(fn_ref() == 42);
+  }
+  {
+    // initialized from a function pointer
+    std::function_ref<int() const noexcept> fn_ref = &fn;
+    assert(fn_ref() == 42);
+  }
+
+  // template<class F> const noexceptexpr function_ref(F&& f) noexcept;
+  {
+    // initialized from a function object
+    std::function_ref<int() const noexcept> fn_ref = fn_obj;
+    assert(fn_ref() == 42);
+  }
+}
+
+struct S {
+  int data_mem = 42;
+
+  int fn_mem() const noexcept { return 42; }
+};
+
+void test_nontype_t() {
+  // template<auto f> const noexceptexpr function_ref(nontype_t<f>) noexcept;
+  {
+    // initialized from a function through `nontype_t`
+    std::function_ref<int() const noexcept> fn_ref = std::nontype_t<fn>();
+    assert(fn_ref() == 42);
+  }
+  {
+    // initialized from a function pointer through `nontype_t`
+    std::function_ref<int() const noexcept> fn_ref = std::nontype_t<&fn>();
+    assert(fn_ref() == 42);
+  }
+  {
+    // initialized from a function object through `nontype_t`
+    std::function_ref<int() const noexcept> fn_ref = std::nontype_t<fn_obj>();
+    assert(fn_ref() == 42);
+  }
+  {
+    S s;
+    // initialized from a pointer to data member through `nontype_t`
+    std::function_ref<int(S) const noexcept> fn_ref = std::nontype_t<&S::data_mem>();
+    assert(fn_ref(s) == 42);
+  }
+  {
+    S s;
+    // initialized from a pointer to function member through `nontype_t`
+    std::function_ref<int(S) const noexcept> fn_ref = std::nontype_t<&S::fn_mem>();
+    assert(fn_ref(s) == 42);
+  }
+
+  // template<auto f, class U>
+  //   const noexceptexpr function_ref(nontype_t<f>, U&& obj) noexcept;
+  {
+    S s;
+    // initialized from a pointer to data member through `nontype_t` and bound to an object through a reference
+    std::function_ref<int() const noexcept> fn_ref = {std::nontype_t<&S::data_mem>(), s};
+    assert(fn_ref() == 42);
+  }
+  {
+    S s;
+    // initialized from a pointer to function member through `nontype_t` and bound to an object through a reference
+    std::function_ref<int() const noexcept> fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+    assert(fn_ref() == 42);
+  }
+
+  // template<auto f, class T>
+  //   const noexceptexpr function_ref(nontype_t<f>, cv T* obj) noexcept;
+  {
+    S s;
+    // initialized from a pointer to data member through `nontype_t` and bound to an object through a pointer
+    std::function_ref<int() const noexcept> fn_ref = {std::nontype_t<&S::data_mem>(), &s};
+    assert(fn_ref() == 42);
+  }
+  {
+    S s;
+    // initialized from a pointer to function member through `nontype_t` and bound to an object through a pointer
+    static_assert(std::is_same_v<decltype(&s), S*>);
+    std::function_ref<int() const noexcept> fn_ref = {std::nontype_t<&S::fn_mem>(), &s};
+    assert(fn_ref() == 42);
+  }
+}
+
+int main(int, char**) {
+  test();
+  test_nontype_t();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/default.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/default.pass.cpp
new file mode 100644
index 0000000000000..ebc07a6c3520c
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/default.pass.cpp
@@ -0,0 +1,119 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_invocable_v<std::function_ref<void()>&>);
+static_assert(std::is_invocable_v<std::function_ref<void()>>);
+static_assert(std::is_invocable_v<std::function_ref<void()>&&>);
+static_assert(std::is_invocable_v<std::function_ref<void()> const&>);
+static_assert(std::is_invocable_v<std::function_ref<void()> const>);
+static_assert(std::is_invocable_v<std::function_ref<void()> const&&>);
+
+int fn() { return 42; }
+
+struct {
+  int operator()() { return 42; }
+} fn_obj;
+
+void test() {
+  // template<class F> function_ref(F* f) noexcept;
+  {
+    // initialized from a function
+    std::function_ref<int()> fn_ref = fn;
+    assert(fn_ref() == 42);
+  }
+  {
+    // initialized from a function pointer
+    std::function_ref<int()> fn_ref = &fn;
+    assert(fn_ref() == 42);
+  }
+
+  // template<class F> constexpr function_ref(F&& f) noexcept;
+  {
+    // initialized from a function object
+    std::function_ref<int()> fn_ref = fn_obj;
+    assert(fn_ref() == 42);
+  }
+}
+
+struct S {
+  int data_mem = 42;
+
+  int fn_mem() { return 42; }
+};
+
+void test_nontype_t() {
+  // template<auto f> constexpr function_ref(nontype_t<f>) noexcept;
+  {
+    // initialized from a function through `nontype_t`
+    std::function_ref<int()> fn_ref = std::nontype_t<fn>();
+    assert(fn_ref() == 42);
+  }
+  {
+    // initialized from a function pointer through `nontype_t`
+    std::function_ref<int()> fn_ref = std::nontype_t<&fn>();
+    assert(fn_ref() == 42);
+  }
+  {
+    S s;
+    // initialized from a pointer to data member through `nontype_t`
+    std::function_ref<int(S)> fn_ref = std::nontype_t<&S::data_mem>();
+    assert(fn_ref(s) == 42);
+  }
+  {
+    S s;
+    // initialized from a pointer to function member through `nontype_t`
+    std::function_ref<int(S)> fn_ref = std::nontype_t<&S::fn_mem>();
+    assert(fn_ref(s) == 42);
+  }
+
+  // template<auto f, class U>
+  //   constexpr function_ref(nontype_t<f>, U&& obj) noexcept;
+  {
+    S s;
+    // initialized from a pointer to data member through `nontype_t` and bound to an object through a reference
+    std::function_ref<int()> fn_ref = {std::nontype_t<&S::data_mem>(), s};
+    assert(fn_ref() == 42);
+  }
+  {
+    S s;
+    // initialized from a pointer to function member through `nontype_t` and bound to an object through a reference
+    std::function_ref<int()> fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+    assert(fn_ref() == 42);
+  }
+
+  // template<auto f, class T>
+  //   constexpr function_ref(nontype_t<f>, cv T* obj) noexcept;
+  {
+    S s;
+    // initialized from a pointer to data member through `nontype_t` and bound to an object through a pointer
+    std::function_ref<int()> fn_ref = {std::nontype_t<&S::data_mem>(), &s};
+    assert(fn_ref() == 42);
+  }
+  {
+    S s;
+    // initialized from a pointer to function member through `nontype_t` and bound to an object through a pointer
+    static_assert(std::is_same_v<decltype(&s), S*>);
+    std::function_ref<int()> fn_ref = {std::nontype_t<&S::fn_mem>(), &s};
+    assert(fn_ref() == 42);
+  }
+}
+
+int main(int, char**) {
+  test();
+  test_nontype_t();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/noexcept.pass.cpp
new file mode 100644
index 0000000000000..9b58e677ed6ba
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/noexcept.pass.cpp
@@ -0,0 +1,119 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_invocable_v<std::function_ref<void() noexcept>&>);
+static_assert(std::is_invocable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_invocable_v<std::function_ref<void() noexcept>&&>);
+static_assert(std::is_invocable_v<std::function_ref<void() noexcept> const&>);
+static_assert(std::is_invocable_v<std::function_ref<void() noexcept> const>);
+static_assert(std::is_invocable_v<std::function_ref<void() noexcept> const&&>);
+
+int fn() noexcept { return 42; }
+
+struct {
+  int operator()() noexcept { return 42; }
+} fn_obj;
+
+void test() {
+  // template<class F> function_ref(F* f) noexcept;
+  {
+    // initialized from a function
+    std::function_ref<int() noexcept> fn_ref = fn;
+    assert(fn_ref() == 42);
+  }
+  {
+    // initialized from a function pointer
+    std::function_ref<int() noexcept> fn_ref = &fn;
+    assert(fn_ref() == 42);
+  }
+
+  // template<class F> constexpr function_ref(F&& f) noexcept;
+  {
+    // initialized from a function object
+    std::function_ref<int() noexcept> fn_ref = fn_obj;
+    assert(fn_ref() == 42);
+  }
+}
+
+struct S {
+  int data_mem = 42;
+
+  int fn_mem() noexcept { return 42; }
+};
+
+void test_nontype_t() {
+  // template<auto f> constexpr function_ref(nontype_t<f>) noexcept;
+  {
+    // initialized from a function through `nontype_t`
+    std::function_ref<int() noexcept> fn_ref = std::nontype_t<fn>();
+    assert(fn_ref() == 42);
+  }
+  {
+    // initialized from a function pointer through `nontype_t`
+    std::function_ref<int() noexcept> fn_ref = std::nontype_t<&fn>();
+    assert(fn_ref() == 42);
+  }
+  {
+    S s;
+    // initialized from a pointer to data member through `nontype_t`
+    std::function_ref<int(S) noexcept> fn_ref = std::nontype_t<&S::data_mem>();
+    assert(fn_ref(s) == 42);
+  }
+  {
+    S s;
+    // initialized from a pointer to function member through `nontype_t`
+    std::function_ref<int(S) noexcept> fn_ref = std::nontype_t<&S::fn_mem>();
+    assert(fn_ref(s) == 42);
+  }
+
+  // template<auto f, class U>
+  //   constexpr function_ref(nontype_t<f>, U&& obj) noexcept;
+  {
+    S s;
+    // initialized from a pointer to data member through `nontype_t` and bound to an object through a reference
+    std::function_ref<int() noexcept> fn_ref = {std::nontype_t<&S::data_mem>(), s};
+    assert(fn_ref() == 42);
+  }
+  {
+    S s;
+    // initialized from a pointer to function member through `nontype_t` and bound to an object through a reference
+    std::function_ref<int() noexcept> fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+    assert(fn_ref() == 42);
+  }
+
+  // template<auto f, class T>
+  //   constexpr function_ref(nontype_t<f>, cv T* obj) noexcept;
+  {
+    S s;
+    // initialized from a pointer to data member through `nontype_t` and bound to an object through a pointer
+    std::function_ref<int() noexcept> fn_ref = {std::nontype_t<&S::data_mem>(), &s};
+    assert(fn_ref() == 42);
+  }
+  {
+    S s;
+    // initialized from a pointer to function member through `nontype_t` and bound to an object through a pointer
+    static_assert(std::is_same_v<decltype(&s), S*>);
+    std::function_ref<int() noexcept> fn_ref = {std::nontype_t<&S::fn_mem>(), &s};
+    assert(fn_ref() == 42);
+  }
+}
+
+int main(int, char**) {
+  test();
+  test_nontype_t();
+  return 0;
+}

>From 81e73dbfcfd020deb84de6e001298fddd435f584 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 8 Jun 2024 23:22:06 -0700
Subject: [PATCH 03/26] [libc++] P0792R14: 'function_ref'

---
 .../__functional/function_ref_common.h        |  8 +-
 .../include/__functional/function_ref_impl.h  | 48 +++++------
 .../func.wrap.ref/ctor/ctad.pass.cpp          | 84 +++++++++++++++++++
 .../{call => invoke}/const.pass.cpp           |  0
 .../{call => invoke}/const_noexcept.pass.cpp  |  0
 .../{call => invoke}/default.pass.cpp         |  0
 .../{call => invoke}/noexcept.pass.cpp        |  0
 7 files changed, 111 insertions(+), 29 deletions(-)
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp
 rename libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/{call => invoke}/const.pass.cpp (100%)
 rename libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/{call => invoke}/const_noexcept.pass.cpp (100%)
 rename libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/{call => invoke}/default.pass.cpp (100%)
 rename libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/{call => invoke}/noexcept.pass.cpp (100%)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index f7d5575ebaffd..d34a47286cf58 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -33,14 +33,14 @@ template <class _Rp, class... _ArgTypes>
 inline constexpr bool __is_function_ref<function_ref<_Rp, _ArgTypes...>> = true;
 
 template <class _Fp, class _Tp>
-struct __function_ref_bind;
+struct __function_ref_bind {};
 
-template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
 struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...), _Tp> {
   using type = _Rp(_ArgTypes...);
 };
 
-template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
 struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept, _Tp> {
   using type = _Rp(_ArgTypes...) noexcept;
 };
@@ -48,7 +48,7 @@ struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept, _Tp> {
 template <class _Tp, class _Mp, class _Gp>
   requires is_object_v<_Mp>
 struct __function_ref_bind<_Mp _Gp::*, _Tp> {
-  using type = invoke_result_t<_Mp _Gp::*, _Tp&>;
+  using type = invoke_result_t<_Mp _Gp::*, _Tp&>();
 };
 
 template <class _Fp, class _Tp>
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 4e1cd13a02ae2..2e6ed45f32b19 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -46,6 +46,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
   static constexpr bool __is_invocable_using = is_invocable_r_v<_Rp, _Tp..., _ArgTypes...>;
 #  endif
 
+  // use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
+  // pointers
   union __storage_t {
     void* __obj_ptr;
     void const* __obj_const_ptr;
@@ -82,18 +84,17 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
     }
   }
 
-  using __vtable_call_t = _Rp (*)(__storage_t, _ArgTypes&&...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
-  __vtable_call_t __vtable_call_;
+  using __call_t = _Rp (*)(__storage_t, _ArgTypes&&...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+  __call_t __call_;
 
 public:
   template <class _Fp>
     requires is_function_v<_Fp> && __is_invocable_using<_Fp>
   _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
       : __storage_(__fn_ptr),
-        __vtable_call_(
-            [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
-              return __get<_Fp>(__storage)(std::forward<_ArgTypes>(__args)...);
-            }) {
+        __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+          return __get<_Fp>(__storage)(std::forward<_ArgTypes>(__args)...);
+        }) {
     _LIBCPP_ASSERT_UNCATEGORIZED(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
   }
 
@@ -102,16 +103,15 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
              __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&>)
   _LIBCPP_HIDE_FROM_ABI function_ref(_Fp&& __obj) noexcept
       : __storage_(std::addressof(__obj)),
-        __vtable_call_(
-            [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
-              _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
-              return __obj(std::forward<_ArgTypes>(__args)...);
-            }) {}
+        __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
+          return __obj(std::forward<_ArgTypes>(__args)...);
+        }) {}
 
   template <auto _Fn>
     requires __is_invocable_using<decltype(_Fn)>
   constexpr function_ref(nontype_t<_Fn>) noexcept
-      : __vtable_call_([](__storage_t, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+      : __call_([](__storage_t, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
           return std::invoke_r<_Rp>(_Fn, std::forward<_ArgTypes>(__args)...);
         }) {
     if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
@@ -123,11 +123,10 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
     requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp&>
   constexpr function_ref(nontype_t<_Fn>, _Up&& __obj) noexcept
       : __storage_(std::addressof(__obj)),
-        __vtable_call_(
-            [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
-              _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
-              return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
-            }) {
+        __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
+          return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
+        }) {
     if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
       static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
     }
@@ -137,11 +136,10 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
     requires __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp*>
   constexpr function_ref(nontype_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
       : __storage_(__obj_ptr),
-        __vtable_call_(
-            [](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
-              auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
-              return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
-            }) {
+        __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+          auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
+          return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
+        }) {
     if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
       static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
     }
@@ -160,17 +158,17 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
   function_ref& operator=(_Tp) = delete;
 
   constexpr _Rp operator()(_ArgTypes... __args) const noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) {
-    return __vtable_call_(__storage_, std::forward<_ArgTypes>(__args)...);
+    return __call_(__storage_, std::forward<_ArgTypes>(__args)...);
   }
 };
 
-template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
 struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
                            _Tp> {
   using type = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
 };
 
-template <class _Tp, class _Rp, class _Gp, class _ArgTypes>
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
 struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
                            _Tp> {
   using type = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp
new file mode 100644
index 0000000000000..c7490fc0982e3
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+int fn(int, float) { return 42; }
+
+int fn_noexcept(int, float) noexcept { return 42; }
+
+struct S {
+  int data_mem = 42;
+
+  int fn_mem(int, float) { return 42; }
+  int fn_mem_noexcept(int, float) noexcept { return 42; }
+};
+
+void test() {
+  // template<class F>
+  //  function_ref(F*) -> function_ref<F>;
+  {
+    std::function_ref fn_ref = fn;
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    std::function_ref fn_ref = fn_noexcept;
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+
+  // template<auto f>
+  //  function_ref(nontype_t<f>) -> function_ref<...>;
+  {
+    std::function_ref fn_ref = std::nontype_t<fn>();
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    std::function_ref fn_ref = std::nontype_t<fn_noexcept>();
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+
+  // template<auto f, class T>
+  //  function_ref(nontype_t<f>, T&&) -> function_ref<...>;
+  {
+    int arg                  = 0;
+    std::function_ref fn_ref = {std::nontype_t<fn>(), arg};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::nontype_t<&S::data_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int&()>>);
+  }
+  {
+    const S s;
+    std::function_ref fn_ref = {std::nontype_t<&S::data_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int const&()>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
+  }
+  {
+    S s;
+    std::function_ref fn_ref = {std::nontype_t<&S::fn_mem_noexcept>(), s};
+    static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp
similarity index 100%
rename from libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const.pass.cpp
rename to libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const_noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp
similarity index 100%
rename from libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/const_noexcept.pass.cpp
rename to libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/default.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
similarity index 100%
rename from libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/default.pass.cpp
rename to libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp
similarity index 100%
rename from libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/call/noexcept.pass.cpp
rename to libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp

>From 30ad9ed17d32561749fa3817f397548819c47475 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 01:50:20 -0700
Subject: [PATCH 04/26] [libc++] P0792R14: 'function_ref'

---
 libcxx/modules/std/functional.inc | 4 ++++
 libcxx/modules/std/utility.inc    | 5 +++++
 2 files changed, 9 insertions(+)

diff --git a/libcxx/modules/std/functional.inc b/libcxx/modules/std/functional.inc
index 9ef8f584611fc..be804b19f75f8 100644
--- a/libcxx/modules/std/functional.inc
+++ b/libcxx/modules/std/functional.inc
@@ -95,6 +95,10 @@ export namespace std {
 
   using std::function;
 
+#if _LIBCPP_STD_VER >= 26
+  using std::function_ref;
+#endif // _LIBCPP_STD_VER >= 26
+
   using std::swap;
 
   using std::operator==;
diff --git a/libcxx/modules/std/utility.inc b/libcxx/modules/std/utility.inc
index 77c21b87640dd..28f8e36e30af9 100644
--- a/libcxx/modules/std/utility.inc
+++ b/libcxx/modules/std/utility.inc
@@ -89,6 +89,11 @@ export namespace std {
   using std::in_place_index;
   using std::in_place_index_t;
 
+#if _LIBCPP_STD_VER >= 26
+  using std::nontype;
+  using std::nontype_t;
+#endif // _LIBCPP_STD_VER >= 23
+
   // [depr.relops]
   namespace rel_ops {
     using rel_ops::operator!=;

>From 5c73eaf4d8d50d6e32341f8e4cba908ffcd08944 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 03:41:35 -0700
Subject: [PATCH 05/26] [libc++] P0792R14: 'function_ref'

---
 libcxx/include/CMakeLists.txt      | 2 +-
 libcxx/include/__utility/nontype.h | 5 +++++
 libcxx/include/module.modulemap.in | 2 ++
 3 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index a1775f131e489..bf24bf5f8bbb7 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -432,9 +432,9 @@ set(files
   __functional/compose.h
   __functional/default_searcher.h
   __functional/function.h
+  __functional/function_ref.h
   __functional/function_ref_common.h
   __functional/function_ref_impl.h
-  __functional/function_ref.h
   __functional/hash.h
   __functional/identity.h
   __functional/invoke.h
diff --git a/libcxx/include/__utility/nontype.h b/libcxx/include/__utility/nontype.h
index 86893f17e2d96..6a91d2cace162 100644
--- a/libcxx/include/__utility/nontype.h
+++ b/libcxx/include/__utility/nontype.h
@@ -27,6 +27,11 @@ struct nontype_t {
 template <auto _Vp>
 inline constexpr nontype_t<_Vp> nontype{};
 
+template <class>
+inline constexpr bool __is_nontype_t = false;
+template <auto _Vp>
+inline constexpr bool __is_nontype_t<nontype_t<_Vp>> = true;
+
 #endif // _LIBCPP_STD_VER >= 26
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 48ab2eba1b734..33ef4377d8de7 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1434,6 +1434,8 @@ module std [system] {
     module default_searcher             { header "__functional/default_searcher.h" }
     module function                     { header "__functional/function.h" }
     module function_ref                 { header "__functional/function_ref.h" }
+    module function_ref_common          { header "__functional/function_ref_common.h" }
+    module function_ref_impl            { header "__functional/function_ref_impl.h" }
     module hash                         { header "__functional/hash.h" }
     module identity                     { header "__functional/identity.h" }
     module invoke                       { header "__functional/invoke.h" }

>From 90b6b7faa280beb79c2aa4a1ab907218d9dbff03 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 03:50:12 -0700
Subject: [PATCH 06/26] [libc++] P0792R14: 'function_ref'

---
 .../include/__functional/function_ref_impl.h   | 18 +++++++++---------
 libcxx/include/__utility/nontype.h             |  2 +-
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 2e6ed45f32b19..261beb2d04dad 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -53,10 +53,10 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
     void const* __obj_const_ptr;
     void (*__fn_ptr)();
 
-    constexpr explicit __storage_t() noexcept : __obj_ptr(nullptr) {};
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t() noexcept : __obj_ptr(nullptr){};
 
     template <class _Tp>
-    constexpr explicit __storage_t(_Tp* __ptr) noexcept {
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t(_Tp* __ptr) noexcept {
       if constexpr (is_object_v<_Tp>) {
         if constexpr (is_const_v<_Tp>) {
           __obj_const_ptr = __ptr;
@@ -110,7 +110,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
 
   template <auto _Fn>
     requires __is_invocable_using<decltype(_Fn)>
-  constexpr function_ref(nontype_t<_Fn>) noexcept
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(nontype_t<_Fn>) noexcept
       : __call_([](__storage_t, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
           return std::invoke_r<_Rp>(_Fn, std::forward<_ArgTypes>(__args)...);
         }) {
@@ -121,7 +121,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
 
   template <auto _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
     requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp&>
-  constexpr function_ref(nontype_t<_Fn>, _Up&& __obj) noexcept
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(nontype_t<_Fn>, _Up&& __obj) noexcept
       : __storage_(std::addressof(__obj)),
         __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
           _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
@@ -134,7 +134,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
 
   template <auto _Fn, class _Tp>
     requires __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp*>
-  constexpr function_ref(nontype_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(nontype_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
       : __storage_(__obj_ptr),
         __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
           auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
@@ -149,15 +149,15 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
     }
   }
 
-  constexpr function_ref(const function_ref&) noexcept = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(const function_ref&) noexcept = default;
 
-  constexpr function_ref& operator=(const function_ref&) noexcept = default;
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref& operator=(const function_ref&) noexcept = default;
 
   template <class _Tp>
     requires(!__is_function_ref<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_nontype_t<_Tp>)
-  function_ref& operator=(_Tp) = delete;
+  _LIBCPP_HIDE_FROM_ABI function_ref& operator=(_Tp) = delete;
 
-  constexpr _Rp operator()(_ArgTypes... __args) const noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) {
+  _LIBCPP_HIDE_FROM_ABI constexpr _Rp operator()(_ArgTypes... __args) const noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) {
     return __call_(__storage_, std::forward<_ArgTypes>(__args)...);
   }
 };
diff --git a/libcxx/include/__utility/nontype.h b/libcxx/include/__utility/nontype.h
index 6a91d2cace162..3a10840154009 100644
--- a/libcxx/include/__utility/nontype.h
+++ b/libcxx/include/__utility/nontype.h
@@ -21,7 +21,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <auto _Vp>
 struct nontype_t {
-  explicit nontype_t() = default;
+  _LIBCPP_HIDE_FROM_ABI explicit nontype_t() = default;
 };
 
 template <auto _Vp>

>From 9fc2305fc1ecd10419cecfd3161eef2c407eff80 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 03:55:54 -0700
Subject: [PATCH 07/26] [libc++] P0792R14: 'function_ref'

---
 libcxx/include/__functional/function_ref_common.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index d34a47286cf58..b2d1864e582c5 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -11,6 +11,7 @@
 
 #include <__config>
 #include <__type_traits/invoke.h>
+#include <__type_traits/is_function.h>
 #include <__type_traits/is_object.h>
 #include <__type_traits/remove_pointer.h>
 #include <__utility/nontype.h>

>From 64b9f076800f8c0ea87ff81ec5309dee04e3e415 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 04:01:41 -0700
Subject: [PATCH 08/26] [libc++] P0792R14: 'function_ref'

---
 libcxx/include/__functional/function_ref_impl.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 261beb2d04dad..50951228c10a8 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -17,6 +17,7 @@
 #include <__type_traits/is_member_pointer.h>
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_pointer.h>
+#include <__type_traits/is_reference.h>
 #include <__type_traits/is_void.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_pointer.h>

>From 2b917d71fde6e7266511a0a6870af4cfb8aabd5d Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 04:41:50 -0700
Subject: [PATCH 09/26] [libc++] P0792R14: 'function_ref'

---
 libcxx/include/__functional/function_ref.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
index 01cc22a64018e..8fcebb8aa4825 100644
--- a/libcxx/include/__functional/function_ref.h
+++ b/libcxx/include/__functional/function_ref.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
 
 #include <__config>
+#include <__functional/function_ref_common.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header

>From 4b180b91c1428c921ea920cff12b8535f00befb5 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 05:10:54 -0700
Subject: [PATCH 10/26] [libc++] P0792R14: 'function_ref'

---
 libcxx/include/module.modulemap.in | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 33ef4377d8de7..21d2bb6ff241d 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1435,7 +1435,7 @@ module std [system] {
     module function                     { header "__functional/function.h" }
     module function_ref                 { header "__functional/function_ref.h" }
     module function_ref_common          { header "__functional/function_ref_common.h" }
-    module function_ref_impl            { header "__functional/function_ref_impl.h" }
+    module function_ref_impl            { textual header "__functional/function_ref_impl.h" }
     module hash                         { header "__functional/hash.h" }
     module identity                     { header "__functional/identity.h" }
     module invoke                       { header "__functional/invoke.h" }

>From 010236166a665776bbeca202113e1af01d1df753 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 11:18:14 -0700
Subject: [PATCH 11/26] [libc++] P0792R14: 'function_ref'

---
 libcxx/include/__functional/function_ref_impl.h | 1 -
 libcxx/include/module.modulemap.in              | 2 +-
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 50951228c10a8..d992d6661cf1b 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -8,7 +8,6 @@
 
 #include <__assert>
 #include <__config>
-#include <__functional/function_ref_common.h>
 #include <__functional/invoke.h>
 #include <__memory/addressof.h>
 #include <__type_traits/invoke.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 21d2bb6ff241d..a3d3d51090465 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1434,7 +1434,7 @@ module std [system] {
     module default_searcher             { header "__functional/default_searcher.h" }
     module function                     { header "__functional/function.h" }
     module function_ref                 { header "__functional/function_ref.h" }
-    module function_ref_common          { header "__functional/function_ref_common.h" }
+    module function_ref_common          { textual header "__functional/function_ref_common.h" }
     module function_ref_impl            { textual header "__functional/function_ref_impl.h" }
     module hash                         { header "__functional/hash.h" }
     module identity                     { header "__functional/identity.h" }

>From e70a2b5fe3b00a3672dd9e4fc699c75d305f8846 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 11:23:21 -0700
Subject: [PATCH 12/26] [libc++] P0792R14: 'function_ref'

---
 libcxx/include/functional | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libcxx/include/functional b/libcxx/include/functional
index dbc023ac637d7..14e29f58cca2e 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -485,6 +485,11 @@ template <class  R, class ... ArgTypes>
 template <class  R, class ... ArgTypes>
   void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept;
 
+// [func.wrap.ref], non-owning wrapper
+template<class... S> class function_ref;                              // freestanding, not defined, since C++26
+template<class R, class... ArgTypes>
+  class function_ref<R(ArgTypes...) cv noexcept(noex)>;               // freestanding, since C++26
+
 template <class T> struct hash;
 
 template <> struct hash<bool>;

>From 2c2118483f9a52b64bde15392edc49fd2fa1df18 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sun, 9 Jun 2024 11:58:22 -0700
Subject: [PATCH 13/26] [libc++] P0792R14: 'function_ref'

---
 libcxx/include/__functional/function_ref_impl.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index d992d6661cf1b..daae5c87d9470 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -53,7 +53,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
     void const* __obj_const_ptr;
     void (*__fn_ptr)();
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t() noexcept : __obj_ptr(nullptr){};
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t() noexcept : __obj_ptr(nullptr) {}
 
     template <class _Tp>
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t(_Tp* __ptr) noexcept {

>From cc15e79e410f3b5d825c3178b3a24124b5b9a181 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 8 Mar 2026 10:52:40 +0000
Subject: [PATCH 14/26] rebase + put in experimental + rename nontype to
 constant_arg

---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +-
 libcxx/docs/ReleaseNotes/23.rst               |  2 +
 libcxx/docs/Status/Cxx2cPapers.csv            |  2 +-
 libcxx/include/CMakeLists.txt                 |  1 -
 libcxx/include/__configuration/experimental.h |  1 +
 libcxx/include/__functional/function_ref.h    |  2 +
 .../__functional/function_ref_common.h        | 18 ++++----
 .../include/__functional/function_ref_impl.h  | 20 ++++----
 .../__utility/{nontype.h => constant_arg.h}   | 16 +++----
 libcxx/include/module.modulemap.in            |  1 -
 libcxx/include/utility                        | 10 ++--
 libcxx/include/version                        |  4 +-
 libcxx/modules/std/utility.inc                |  4 +-
 .../functional.version.compile.pass.cpp       |  4 +-
 .../version.version.compile.pass.cpp          |  4 +-
 .../func.wrap.ref/ctor/ctad.pass.cpp          | 18 ++++----
 .../func.wrap.ref/invoke/const.pass.cpp       | 46 +++++++++----------
 .../invoke/const_noexcept.pass.cpp            | 46 +++++++++----------
 .../func.wrap.ref/invoke/default.pass.cpp     | 42 ++++++++---------
 .../func.wrap.ref/invoke/noexcept.pass.cpp    | 42 ++++++++---------
 .../generate_feature_test_macro_components.py |  3 +-
 21 files changed, 146 insertions(+), 142 deletions(-)
 rename libcxx/include/__utility/{nontype.h => constant_arg.h} (63%)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index dcdf385ace6f6..937005bdd30fa 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -470,7 +470,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_fstream_native_handle``                        ``202306L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_function_ref``                                 *unimplemented*
+    ``__cpp_lib_function_ref``                                 ``202306L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_generate_random``                              *unimplemented*
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index e79c01e937836..a5acc569dc9ee 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -44,6 +44,8 @@ Implemented Papers
 - P4052R0: Renaming saturation arithmetic functions (`Github <https://llvm.org/PR189589>`__)
 - P2781R9: ``std::constant_wrapper`` (`Github <https://llvm.org/PR148179>`__)
 - P3978R3: ``constant_wrapper`` should unwrap on call and subscript (`Github <https://llvm.org/PR189605>`__)
+- P0792R14: ``function_ref`` : a type-erased callable reference  (`Github <https://llvm.org/PR105376>`__) This feature is currently experimental and
+  therefore requires ``-fexperimental-library``.
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 6b706ab036e01..9d5d4ce907802 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -14,7 +14,7 @@
 "`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
 "`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
 "`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","","","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
-"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","","","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
+"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","23","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
 "`P2874R2 <https://wg21.link/P2874R2>`__","P2874R2: Mandating Annex D Require No More","2023-06 (Varna)","|Complete|","12","`#105377 <https://github.com/llvm/llvm-project/issues/105377>`__",""
 "`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","2023-06 (Varna)","","","`#105378 <https://github.com/llvm/llvm-project/issues/105378>`__",""
 "`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","2023-06 (Varna)","|Complete|","19","`#105380 <https://github.com/llvm/llvm-project/issues/105380>`__","Change of ``__cpp_lib_variant`` is completed in LLVM 20. Change of ``__cpp_lib_format`` is blocked by `P2419R2 <https://wg21.link/P2419R2>`__."
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index bf24bf5f8bbb7..5fcf53a4f2070 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -945,7 +945,6 @@ set(files
   __utility/lazy_synth_three_way_comparator.h
   __utility/move.h
   __utility/no_destroy.h
-  __utility/nontype.h
   __utility/pair.h
   __utility/piecewise_construct.h
   __utility/priority_tag.h
diff --git a/libcxx/include/__configuration/experimental.h b/libcxx/include/__configuration/experimental.h
index bb38d8297c63d..1e12c50802895 100644
--- a/libcxx/include/__configuration/experimental.h
+++ b/libcxx/include/__configuration/experimental.h
@@ -34,5 +34,6 @@
 #define _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
 #define _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
 #define _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
+#define _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
 
 #endif // _LIBCPP___CONFIGURATION_EXPERIMENTAL_H
diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
index 8fcebb8aa4825..fbfb0d8a8b3a8 100644
--- a/libcxx/include/__functional/function_ref.h
+++ b/libcxx/include/__functional/function_ref.h
@@ -16,6 +16,7 @@
 #  pragma GCC system_header
 #endif
 
+#if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
 // NOLINTBEGIN(readability-duplicate-include)
 
 #define _LIBCPP_FUNCTION_REF_CV
@@ -43,5 +44,6 @@
 #undef _LIBCPP_FUNCTION_REF_NOEXCEPT
 
 // NOLINTEND(readability-duplicate-include)
+#endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
 
 #endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index b2d1864e582c5..f1d1ee2279d9c 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -14,7 +14,7 @@
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_object.h>
 #include <__type_traits/remove_pointer.h>
-#include <__utility/nontype.h>
+#include <__utility/constant_arg.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -22,7 +22,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 26
+#if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
 
 template <class...>
 class function_ref;
@@ -38,22 +38,22 @@ struct __function_ref_bind {};
 
 template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
 struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...), _Tp> {
-  using type = _Rp(_ArgTypes...);
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...);
 };
 
 template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
 struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept, _Tp> {
-  using type = _Rp(_ArgTypes...) noexcept;
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept;
 };
 
 template <class _Tp, class _Mp, class _Gp>
   requires is_object_v<_Mp>
 struct __function_ref_bind<_Mp _Gp::*, _Tp> {
-  using type = invoke_result_t<_Mp _Gp::*, _Tp&>();
+  using type _LIBCPP_NODEBUG = invoke_result_t<_Mp _Gp::*, _Tp&>();
 };
 
 template <class _Fp, class _Tp>
-using __function_ref_bind_t = __function_ref_bind<_Fp, _Tp>::type;
+using __function_ref_bind_t _LIBCPP_NODEBUG = __function_ref_bind<_Fp, _Tp>::type;
 
 template <class _Fp>
   requires is_function_v<_Fp>
@@ -61,12 +61,12 @@ function_ref(_Fp*) -> function_ref<_Fp>;
 
 template <auto _Fn>
   requires is_function_v<remove_pointer_t<decltype(_Fn)>>
-function_ref(nontype_t<_Fn>) -> function_ref<remove_pointer_t<decltype(_Fn)>>;
+function_ref(constant_arg_t<_Fn>) -> function_ref<remove_pointer_t<decltype(_Fn)>>;
 
 template <auto _Fn, class _Tp>
-function_ref(nontype_t<_Fn>, _Tp&&) -> function_ref<__function_ref_bind_t<decltype(_Fn), _Tp&>>;
+function_ref(constant_arg_t<_Fn>, _Tp&&) -> function_ref<__function_ref_bind_t<decltype(_Fn), _Tp&>>;
 
-#endif // _LIBCPP_STD_VER >= 26
+#endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
 
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index daae5c87d9470..4627fa6325305 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -21,8 +21,8 @@
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_pointer.h>
 #include <__type_traits/remove_reference.h>
+#include <__utility/constant_arg.h>
 #include <__utility/forward.h>
-#include <__utility/nontype.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -30,7 +30,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 26
+#if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
 
 template <class...>
 class function_ref;
@@ -84,7 +84,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
     }
   }
 
-  using __call_t = _Rp (*)(__storage_t, _ArgTypes&&...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+  using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, _ArgTypes&&...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
   __call_t __call_;
 
 public:
@@ -110,7 +110,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
 
   template <auto _Fn>
     requires __is_invocable_using<decltype(_Fn)>
-  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(nontype_t<_Fn>) noexcept
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>) noexcept
       : __call_([](__storage_t, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
           return std::invoke_r<_Rp>(_Fn, std::forward<_ArgTypes>(__args)...);
         }) {
@@ -121,7 +121,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
 
   template <auto _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
     requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp&>
-  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(nontype_t<_Fn>, _Up&& __obj) noexcept
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _Up&& __obj) noexcept
       : __storage_(std::addressof(__obj)),
         __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
           _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
@@ -134,7 +134,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
 
   template <auto _Fn, class _Tp>
     requires __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp*>
-  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(nontype_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
       : __storage_(__obj_ptr),
         __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
           auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
@@ -154,7 +154,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref& operator=(const function_ref&) noexcept = default;
 
   template <class _Tp>
-    requires(!__is_function_ref<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_nontype_t<_Tp>)
+    requires(!__is_function_ref<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_constant_arg_t<_Tp>)
   _LIBCPP_HIDE_FROM_ABI function_ref& operator=(_Tp) = delete;
 
   _LIBCPP_HIDE_FROM_ABI constexpr _Rp operator()(_ArgTypes... __args) const noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) {
@@ -165,15 +165,15 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
 template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
 struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
                            _Tp> {
-  using type = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
 };
 
 template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
 struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
                            _Tp> {
-  using type = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
 };
 
-#endif // _LIBCPP_STD_VER >= 26
+#endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__utility/nontype.h b/libcxx/include/__utility/constant_arg.h
similarity index 63%
rename from libcxx/include/__utility/nontype.h
rename to libcxx/include/__utility/constant_arg.h
index 3a10840154009..f299ec509ed5c 100644
--- a/libcxx/include/__utility/nontype.h
+++ b/libcxx/include/__utility/constant_arg.h
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef _LIBCPP___UTILITY_NONTYPE_H
-#define _LIBCPP___UTILITY_NONTYPE_H
+#ifndef _LIBCPP___UTILITY_CONSTANT_ARG_H
+#define _LIBCPP___UTILITY_CONSTANT_ARG_H
 
 #include <__config>
 
@@ -20,20 +20,20 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 #if _LIBCPP_STD_VER >= 26
 
 template <auto _Vp>
-struct nontype_t {
-  _LIBCPP_HIDE_FROM_ABI explicit nontype_t() = default;
+struct constant_arg_t {
+  _LIBCPP_HIDE_FROM_ABI explicit constant_arg_t() = default;
 };
 
 template <auto _Vp>
-inline constexpr nontype_t<_Vp> nontype{};
+inline constexpr constant_arg_t<_Vp> constant_arg{};
 
 template <class>
-inline constexpr bool __is_nontype_t = false;
+inline constexpr bool __is_constant_arg_t = false;
 template <auto _Vp>
-inline constexpr bool __is_nontype_t<nontype_t<_Vp>> = true;
+inline constexpr bool __is_constant_arg_t<constant_arg_t<_Vp>> = true;
 
 #endif // _LIBCPP_STD_VER >= 26
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP___UTILITY_NONTYPE_H
+#endif // _LIBCPP___UTILITY_CONSTANT_ARG_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index a3d3d51090465..82dbcf7dc791c 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2198,7 +2198,6 @@ module std [system] {
     module lazy_synth_three_way_comparator { header "__utility/lazy_synth_three_way_comparator.h" }
     module move                            { header "__utility/move.h" }
     module no_destroy                      { header "__utility/no_destroy.h" }
-    module nontype                         { header "__utility/nontype.h" }
     module pair                            {
       header "__utility/pair.h"
       export std.utility.piecewise_construct
diff --git a/libcxx/include/utility b/libcxx/include/utility
index f357f2705bf21..0cc3f7f4d69ea 100644
--- a/libcxx/include/utility
+++ b/libcxx/include/utility
@@ -266,14 +266,12 @@ template <size_t I>
 template <size_t I>
   inline constexpr in_place_index_t<I> in_place_index{};
 
-// nontype argument tag
+// constant_arg argument tag
 template<auto V>
-  struct nontype_t {
-    explicit nontype_t() = default;
+  struct constant_arg_t {
+    explicit constant_arg_t() = default;
   };
-template<auto V> constexpr nontype_t<V> nontype{};
-
-template<auto V> constexpr nontype_t<V> nontype{};
+template<auto V> constexpr constant_arg_t<V> constant_arg_t{};
 
 // [utility.underlying], to_underlying
 template <class T>
diff --git a/libcxx/include/version b/libcxx/include/version
index 8901f8f146d52..e3e4255122d30 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -583,7 +583,9 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # if _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
 #   define __cpp_lib_fstream_native_handle              202306L
 # endif
-// # define __cpp_lib_function_ref                         202306L
+# if _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
+#   define __cpp_lib_function_ref                       202306L
+# endif
 // # define __cpp_lib_generate_random                      202403L
 // # define __cpp_lib_hazard_pointer                       202306L
 // # define __cpp_lib_inplace_vector                       202406L
diff --git a/libcxx/modules/std/utility.inc b/libcxx/modules/std/utility.inc
index 28f8e36e30af9..aacab46e6df02 100644
--- a/libcxx/modules/std/utility.inc
+++ b/libcxx/modules/std/utility.inc
@@ -90,8 +90,8 @@ export namespace std {
   using std::in_place_index_t;
 
 #if _LIBCPP_STD_VER >= 26
-  using std::nontype;
-  using std::nontype_t;
+  using std::constant_arg;
+  using std::constant_arg_t;
 #endif // _LIBCPP_STD_VER >= 23
 
   // [depr.relops]
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
index b7b7d0334830a..eaab9bdd80ad8 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
@@ -494,7 +494,7 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
+#  if !defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
 #    ifndef __cpp_lib_function_ref
 #      error "__cpp_lib_function_ref should be defined in c++26"
 #    endif
@@ -503,7 +503,7 @@
 #    endif
 #  else
 #    ifdef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should not be defined because it is unimplemented in libc++!"
+#      error "__cpp_lib_function_ref should not be defined when the requirement '!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF' is not met!"
 #    endif
 #  endif
 
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index e342a7ba33f9e..2e86059e6e91f 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
@@ -7035,7 +7035,7 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION)
+#  if !defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
 #    ifndef __cpp_lib_function_ref
 #      error "__cpp_lib_function_ref should be defined in c++26"
 #    endif
@@ -7044,7 +7044,7 @@
 #    endif
 #  else
 #    ifdef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should not be defined because it is unimplemented in libc++!"
+#      error "__cpp_lib_function_ref should not be defined when the requirement '!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF' is not met!"
 #    endif
 #  endif
 
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp
index c7490fc0982e3..9ef7bd66a8953 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp
@@ -39,41 +39,41 @@ void test() {
   }
 
   // template<auto f>
-  //  function_ref(nontype_t<f>) -> function_ref<...>;
+  //  function_ref(constant_arg_t<f>) -> function_ref<...>;
   {
-    std::function_ref fn_ref = std::nontype_t<fn>();
+    std::function_ref fn_ref = std::constant_arg_t<fn>();
     static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
   }
   {
-    std::function_ref fn_ref = std::nontype_t<fn_noexcept>();
+    std::function_ref fn_ref = std::constant_arg_t<fn_noexcept>();
     static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
   }
 
   // template<auto f, class T>
-  //  function_ref(nontype_t<f>, T&&) -> function_ref<...>;
+  //  function_ref(constant_arg_t<f>, T&&) -> function_ref<...>;
   {
     int arg                  = 0;
-    std::function_ref fn_ref = {std::nontype_t<fn>(), arg};
+    std::function_ref fn_ref = {std::constant_arg_t<fn>(), arg};
     static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float)>>);
   }
   {
     S s;
-    std::function_ref fn_ref = {std::nontype_t<&S::data_mem>(), s};
+    std::function_ref fn_ref = {std::constant_arg_t<&S::data_mem>(), s};
     static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int&()>>);
   }
   {
     const S s;
-    std::function_ref fn_ref = {std::nontype_t<&S::data_mem>(), s};
+    std::function_ref fn_ref = {std::constant_arg_t<&S::data_mem>(), s};
     static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int const&()>>);
   }
   {
     S s;
-    std::function_ref fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+    std::function_ref fn_ref = {std::constant_arg_t<&S::fn_mem>(), s};
     static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
   }
   {
     S s;
-    std::function_ref fn_ref = {std::nontype_t<&S::fn_mem_noexcept>(), s};
+    std::function_ref fn_ref = {std::constant_arg_t<&S::fn_mem_noexcept>(), s};
     static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
   }
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp
index 53d7a5922353d..26a32e91adfd7 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp
@@ -55,70 +55,70 @@ struct S {
   int fn_mem() const { return 42; }
 };
 
-void test_nontype_t() {
-  // template<auto f> constexpr function_ref(nontype_t<f>) noexcept;
+void test_constant_arg_t() {
+  // template<auto f> constexpr function_ref(constant_arg_t<f>) noexcept;
   {
-    // initialized from a function through `nontype_t`
-    std::function_ref<int() const> fn_ref = std::nontype_t<fn>();
+    // initialized from a function through `constant_arg_t`
+    std::function_ref<int() const> fn_ref = std::constant_arg_t<fn>();
     assert(fn_ref() == 42);
   }
   {
-    // initialized from a function pointer through `nontype_t`
-    std::function_ref<int() const> fn_ref = std::nontype_t<&fn>();
+    // initialized from a function pointer through `constant_arg_t`
+    std::function_ref<int() const> fn_ref = std::constant_arg_t<&fn>();
     assert(fn_ref() == 42);
   }
   {
-    // initialized from a function object through `nontype_t`
-    std::function_ref<int() const> fn_ref = std::nontype_t<fn_obj>();
+    // initialized from a function object through `constant_arg_t`
+    std::function_ref<int() const> fn_ref = std::constant_arg_t<fn_obj>();
     assert(fn_ref() == 42);
   }
   {
     S s;
-    // initialized from a pointer to data member through `nontype_t`
-    std::function_ref<int(S) const> fn_ref = std::nontype_t<&S::data_mem>();
+    // initialized from a pointer to data member through `constant_arg_t`
+    std::function_ref<int(S) const> fn_ref = std::constant_arg_t<&S::data_mem>();
     assert(fn_ref(s) == 42);
   }
   {
     S s;
-    // initialized from a pointer to function member through `nontype_t`
-    std::function_ref<int(S) const> fn_ref = std::nontype_t<&S::fn_mem>();
+    // initialized from a pointer to function member through `constant_arg_t`
+    std::function_ref<int(S) const> fn_ref = std::constant_arg_t<&S::fn_mem>();
     assert(fn_ref(s) == 42);
   }
 
   // template<auto f, class U>
-  //   constexpr function_ref(nontype_t<f>, U&& obj) noexcept;
+  //   constexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
   {
     S s;
-    // initialized from a pointer to data member through `nontype_t` and bound to an object through a reference
-    std::function_ref<int() const> fn_ref = {std::nontype_t<&S::data_mem>(), s};
+    // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a reference
+    std::function_ref<int() const> fn_ref = {std::constant_arg_t<&S::data_mem>(), s};
     assert(fn_ref() == 42);
   }
   {
     S s;
-    // initialized from a pointer to function member through `nontype_t` and bound to an object through a reference
-    std::function_ref<int() const> fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+    // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a reference
+    std::function_ref<int() const> fn_ref = {std::constant_arg_t<&S::fn_mem>(), s};
     assert(fn_ref() == 42);
   }
 
   // template<auto f, class T>
-  //   constexpr function_ref(nontype_t<f>, cv T* obj) noexcept;
+  //   constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
   {
     S s;
-    // initialized from a pointer to data member through `nontype_t` and bound to an object through a pointer
-    std::function_ref<int() const> fn_ref = {std::nontype_t<&S::data_mem>(), &s};
+    // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a pointer
+    std::function_ref<int() const> fn_ref = {std::constant_arg_t<&S::data_mem>(), &s};
     assert(fn_ref() == 42);
   }
   {
     S s;
-    // initialized from a pointer to function member through `nontype_t` and bound to an object through a pointer
+    // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a pointer
     static_assert(std::is_same_v<decltype(&s), S*>);
-    std::function_ref<int() const> fn_ref = {std::nontype_t<&S::fn_mem>(), &s};
+    std::function_ref<int() const> fn_ref = {std::constant_arg_t<&S::fn_mem>(), &s};
     assert(fn_ref() == 42);
   }
 }
 
 int main(int, char**) {
   test();
-  test_nontype_t();
+  test_constant_arg_t();
   return 0;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp
index 18d44e4b7ef0b..26ace9c496b88 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp
@@ -55,70 +55,70 @@ struct S {
   int fn_mem() const noexcept { return 42; }
 };
 
-void test_nontype_t() {
-  // template<auto f> const noexceptexpr function_ref(nontype_t<f>) noexcept;
+void test_constant_arg_t() {
+  // template<auto f> const noexceptexpr function_ref(constant_arg_t<f>) noexcept;
   {
-    // initialized from a function through `nontype_t`
-    std::function_ref<int() const noexcept> fn_ref = std::nontype_t<fn>();
+    // initialized from a function through `constant_arg_t`
+    std::function_ref<int() const noexcept> fn_ref = std::constant_arg_t<fn>();
     assert(fn_ref() == 42);
   }
   {
-    // initialized from a function pointer through `nontype_t`
-    std::function_ref<int() const noexcept> fn_ref = std::nontype_t<&fn>();
+    // initialized from a function pointer through `constant_arg_t`
+    std::function_ref<int() const noexcept> fn_ref = std::constant_arg_t<&fn>();
     assert(fn_ref() == 42);
   }
   {
-    // initialized from a function object through `nontype_t`
-    std::function_ref<int() const noexcept> fn_ref = std::nontype_t<fn_obj>();
+    // initialized from a function object through `constant_arg_t`
+    std::function_ref<int() const noexcept> fn_ref = std::constant_arg_t<fn_obj>();
     assert(fn_ref() == 42);
   }
   {
     S s;
-    // initialized from a pointer to data member through `nontype_t`
-    std::function_ref<int(S) const noexcept> fn_ref = std::nontype_t<&S::data_mem>();
+    // initialized from a pointer to data member through `constant_arg_t`
+    std::function_ref<int(S) const noexcept> fn_ref = std::constant_arg_t<&S::data_mem>();
     assert(fn_ref(s) == 42);
   }
   {
     S s;
-    // initialized from a pointer to function member through `nontype_t`
-    std::function_ref<int(S) const noexcept> fn_ref = std::nontype_t<&S::fn_mem>();
+    // initialized from a pointer to function member through `constant_arg_t`
+    std::function_ref<int(S) const noexcept> fn_ref = std::constant_arg_t<&S::fn_mem>();
     assert(fn_ref(s) == 42);
   }
 
   // template<auto f, class U>
-  //   const noexceptexpr function_ref(nontype_t<f>, U&& obj) noexcept;
+  //   const noexceptexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
   {
     S s;
-    // initialized from a pointer to data member through `nontype_t` and bound to an object through a reference
-    std::function_ref<int() const noexcept> fn_ref = {std::nontype_t<&S::data_mem>(), s};
+    // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a reference
+    std::function_ref<int() const noexcept> fn_ref = {std::constant_arg_t<&S::data_mem>(), s};
     assert(fn_ref() == 42);
   }
   {
     S s;
-    // initialized from a pointer to function member through `nontype_t` and bound to an object through a reference
-    std::function_ref<int() const noexcept> fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+    // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a reference
+    std::function_ref<int() const noexcept> fn_ref = {std::constant_arg_t<&S::fn_mem>(), s};
     assert(fn_ref() == 42);
   }
 
   // template<auto f, class T>
-  //   const noexceptexpr function_ref(nontype_t<f>, cv T* obj) noexcept;
+  //   const noexceptexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
   {
     S s;
-    // initialized from a pointer to data member through `nontype_t` and bound to an object through a pointer
-    std::function_ref<int() const noexcept> fn_ref = {std::nontype_t<&S::data_mem>(), &s};
+    // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a pointer
+    std::function_ref<int() const noexcept> fn_ref = {std::constant_arg_t<&S::data_mem>(), &s};
     assert(fn_ref() == 42);
   }
   {
     S s;
-    // initialized from a pointer to function member through `nontype_t` and bound to an object through a pointer
+    // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a pointer
     static_assert(std::is_same_v<decltype(&s), S*>);
-    std::function_ref<int() const noexcept> fn_ref = {std::nontype_t<&S::fn_mem>(), &s};
+    std::function_ref<int() const noexcept> fn_ref = {std::constant_arg_t<&S::fn_mem>(), &s};
     assert(fn_ref() == 42);
   }
 }
 
 int main(int, char**) {
   test();
-  test_nontype_t();
+  test_constant_arg_t();
   return 0;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
index ebc07a6c3520c..ce9d7f8d73716 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
@@ -55,65 +55,65 @@ struct S {
   int fn_mem() { return 42; }
 };
 
-void test_nontype_t() {
-  // template<auto f> constexpr function_ref(nontype_t<f>) noexcept;
+void test_constant_arg_t() {
+  // template<auto f> constexpr function_ref(constant_arg_t<f>) noexcept;
   {
-    // initialized from a function through `nontype_t`
-    std::function_ref<int()> fn_ref = std::nontype_t<fn>();
+    // initialized from a function through `constant_arg_t`
+    std::function_ref<int()> fn_ref = std::constant_arg_t<fn>();
     assert(fn_ref() == 42);
   }
   {
-    // initialized from a function pointer through `nontype_t`
-    std::function_ref<int()> fn_ref = std::nontype_t<&fn>();
+    // initialized from a function pointer through `constant_arg_t`
+    std::function_ref<int()> fn_ref = std::constant_arg_t<&fn>();
     assert(fn_ref() == 42);
   }
   {
     S s;
-    // initialized from a pointer to data member through `nontype_t`
-    std::function_ref<int(S)> fn_ref = std::nontype_t<&S::data_mem>();
+    // initialized from a pointer to data member through `constant_arg_t`
+    std::function_ref<int(S)> fn_ref = std::constant_arg_t<&S::data_mem>();
     assert(fn_ref(s) == 42);
   }
   {
     S s;
-    // initialized from a pointer to function member through `nontype_t`
-    std::function_ref<int(S)> fn_ref = std::nontype_t<&S::fn_mem>();
+    // initialized from a pointer to function member through `constant_arg_t`
+    std::function_ref<int(S)> fn_ref = std::constant_arg_t<&S::fn_mem>();
     assert(fn_ref(s) == 42);
   }
 
   // template<auto f, class U>
-  //   constexpr function_ref(nontype_t<f>, U&& obj) noexcept;
+  //   constexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
   {
     S s;
-    // initialized from a pointer to data member through `nontype_t` and bound to an object through a reference
-    std::function_ref<int()> fn_ref = {std::nontype_t<&S::data_mem>(), s};
+    // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a reference
+    std::function_ref<int()> fn_ref = {std::constant_arg_t<&S::data_mem>(), s};
     assert(fn_ref() == 42);
   }
   {
     S s;
-    // initialized from a pointer to function member through `nontype_t` and bound to an object through a reference
-    std::function_ref<int()> fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+    // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a reference
+    std::function_ref<int()> fn_ref = {std::constant_arg_t<&S::fn_mem>(), s};
     assert(fn_ref() == 42);
   }
 
   // template<auto f, class T>
-  //   constexpr function_ref(nontype_t<f>, cv T* obj) noexcept;
+  //   constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
   {
     S s;
-    // initialized from a pointer to data member through `nontype_t` and bound to an object through a pointer
-    std::function_ref<int()> fn_ref = {std::nontype_t<&S::data_mem>(), &s};
+    // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a pointer
+    std::function_ref<int()> fn_ref = {std::constant_arg_t<&S::data_mem>(), &s};
     assert(fn_ref() == 42);
   }
   {
     S s;
-    // initialized from a pointer to function member through `nontype_t` and bound to an object through a pointer
+    // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a pointer
     static_assert(std::is_same_v<decltype(&s), S*>);
-    std::function_ref<int()> fn_ref = {std::nontype_t<&S::fn_mem>(), &s};
+    std::function_ref<int()> fn_ref = {std::constant_arg_t<&S::fn_mem>(), &s};
     assert(fn_ref() == 42);
   }
 }
 
 int main(int, char**) {
   test();
-  test_nontype_t();
+  test_constant_arg_t();
   return 0;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp
index 9b58e677ed6ba..7c947143ec0bd 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp
@@ -55,65 +55,65 @@ struct S {
   int fn_mem() noexcept { return 42; }
 };
 
-void test_nontype_t() {
-  // template<auto f> constexpr function_ref(nontype_t<f>) noexcept;
+void test_constant_arg_t() {
+  // template<auto f> constexpr function_ref(constant_arg_t<f>) noexcept;
   {
-    // initialized from a function through `nontype_t`
-    std::function_ref<int() noexcept> fn_ref = std::nontype_t<fn>();
+    // initialized from a function through `constant_arg_t`
+    std::function_ref<int() noexcept> fn_ref = std::constant_arg_t<fn>();
     assert(fn_ref() == 42);
   }
   {
-    // initialized from a function pointer through `nontype_t`
-    std::function_ref<int() noexcept> fn_ref = std::nontype_t<&fn>();
+    // initialized from a function pointer through `constant_arg_t`
+    std::function_ref<int() noexcept> fn_ref = std::constant_arg_t<&fn>();
     assert(fn_ref() == 42);
   }
   {
     S s;
-    // initialized from a pointer to data member through `nontype_t`
-    std::function_ref<int(S) noexcept> fn_ref = std::nontype_t<&S::data_mem>();
+    // initialized from a pointer to data member through `constant_arg_t`
+    std::function_ref<int(S) noexcept> fn_ref = std::constant_arg_t<&S::data_mem>();
     assert(fn_ref(s) == 42);
   }
   {
     S s;
-    // initialized from a pointer to function member through `nontype_t`
-    std::function_ref<int(S) noexcept> fn_ref = std::nontype_t<&S::fn_mem>();
+    // initialized from a pointer to function member through `constant_arg_t`
+    std::function_ref<int(S) noexcept> fn_ref = std::constant_arg_t<&S::fn_mem>();
     assert(fn_ref(s) == 42);
   }
 
   // template<auto f, class U>
-  //   constexpr function_ref(nontype_t<f>, U&& obj) noexcept;
+  //   constexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
   {
     S s;
-    // initialized from a pointer to data member through `nontype_t` and bound to an object through a reference
-    std::function_ref<int() noexcept> fn_ref = {std::nontype_t<&S::data_mem>(), s};
+    // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a reference
+    std::function_ref<int() noexcept> fn_ref = {std::constant_arg_t<&S::data_mem>(), s};
     assert(fn_ref() == 42);
   }
   {
     S s;
-    // initialized from a pointer to function member through `nontype_t` and bound to an object through a reference
-    std::function_ref<int() noexcept> fn_ref = {std::nontype_t<&S::fn_mem>(), s};
+    // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a reference
+    std::function_ref<int() noexcept> fn_ref = {std::constant_arg_t<&S::fn_mem>(), s};
     assert(fn_ref() == 42);
   }
 
   // template<auto f, class T>
-  //   constexpr function_ref(nontype_t<f>, cv T* obj) noexcept;
+  //   constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
   {
     S s;
-    // initialized from a pointer to data member through `nontype_t` and bound to an object through a pointer
-    std::function_ref<int() noexcept> fn_ref = {std::nontype_t<&S::data_mem>(), &s};
+    // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a pointer
+    std::function_ref<int() noexcept> fn_ref = {std::constant_arg_t<&S::data_mem>(), &s};
     assert(fn_ref() == 42);
   }
   {
     S s;
-    // initialized from a pointer to function member through `nontype_t` and bound to an object through a pointer
+    // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a pointer
     static_assert(std::is_same_v<decltype(&s), S*>);
-    std::function_ref<int() noexcept> fn_ref = {std::nontype_t<&S::fn_mem>(), &s};
+    std::function_ref<int() noexcept> fn_ref = {std::constant_arg_t<&S::fn_mem>(), &s};
     assert(fn_ref() == 42);
   }
 }
 
 int main(int, char**) {
   test();
-  test_nontype_t();
+  test_constant_arg_t();
   return 0;
 }
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 1ebd821c5b85e..5efaad14462e8 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -701,7 +701,8 @@ def add_version_header(tc):
                 "c++26": 202306  # P0792R14 function_ref: a type-erased callable reference
             },
             "headers": ["functional"],
-            "unimplemented": True,
+            "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF",
+            "libcxx_guard": "_LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF"
         },
         {
             "name": "__cpp_lib_gcd_lcm",

>From e79006c9f3595d98482107fd82f719346cbee599 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 13 Mar 2026 17:03:58 +0000
Subject: [PATCH 15/26] tests

---
 libcxx/include/__functional/function_ref.h    |  15 --
 .../__functional/function_ref_common.h        |   6 -
 .../include/__functional/function_ref_impl.h  |  62 +++---
 libcxx/include/module.modulemap.in            |   2 +-
 .../assert.constant_arg_ptr.pass.cpp          |  31 +++
 .../assert.function_ptr.pass.cpp              |  26 +++
 .../constant_arg.mandates.verify.cpp          |  48 +++++
 .../func.wrap.ref/{ctor => }/ctad.pass.cpp    |   0
 .../func.wrap.ref.ctor/constant_arg.pass.cpp  | 106 +++++++++
 .../func.wrap.ref.ctor/function_ptr.pass.cpp  | 119 ++++++++++
 .../func.wrap.ref.ctor/ref.pass.cpp           | 203 ++++++++++++++++++
 .../func.wrap.ref/invoke/default.pass.cpp     |   2 +-
 .../trivially_copyable.compile.pass.cpp       |  33 +++
 13 files changed, 601 insertions(+), 52 deletions(-)
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
 create mode 100644 libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
 rename libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/{ctor => }/ctad.pass.cpp (100%)
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp

diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
index fbfb0d8a8b3a8..e46706a1a8aaf 100644
--- a/libcxx/include/__functional/function_ref.h
+++ b/libcxx/include/__functional/function_ref.h
@@ -20,28 +20,13 @@
 // NOLINTBEGIN(readability-duplicate-include)
 
 #define _LIBCPP_FUNCTION_REF_CV
-#define _LIBCPP_FUNCTION_REF_NOEXCEPT false
 #include <__functional/function_ref_impl.h>
 #undef _LIBCPP_FUNCTION_REF_CV
-#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
 
-#define _LIBCPP_FUNCTION_REF_CV
-#define _LIBCPP_FUNCTION_REF_NOEXCEPT true
-#include <__functional/function_ref_impl.h>
-#undef _LIBCPP_FUNCTION_REF_CV
-#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
-
-#define _LIBCPP_FUNCTION_REF_CV const
-#define _LIBCPP_FUNCTION_REF_NOEXCEPT false
-#include <__functional/function_ref_impl.h>
-#undef _LIBCPP_FUNCTION_REF_CV
-#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
 
 #define _LIBCPP_FUNCTION_REF_CV const
-#define _LIBCPP_FUNCTION_REF_NOEXCEPT true
 #include <__functional/function_ref_impl.h>
 #undef _LIBCPP_FUNCTION_REF_CV
-#undef _LIBCPP_FUNCTION_REF_NOEXCEPT
 
 // NOLINTEND(readability-duplicate-include)
 #endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index f1d1ee2279d9c..f921787f951f5 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -27,12 +27,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
-template <class>
-inline constexpr bool __is_function_ref = false;
-
-template <class _Rp, class... _ArgTypes>
-inline constexpr bool __is_function_ref<function_ref<_Rp, _ArgTypes...>> = true;
-
 template <class _Fp, class _Tp>
 struct __function_ref_bind {};
 
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 4627fa6325305..65c9088b9c892 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -6,10 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
+// This header is unguarded on purpose. This header is an implementation detail of function_ref.h
+// and generates multiple versions of std::function_ref
+
 #include <__assert>
 #include <__config>
 #include <__functional/invoke.h>
 #include <__memory/addressof.h>
+#include <__type_traits/conditional.h>
 #include <__type_traits/invoke.h>
 #include <__type_traits/is_const.h>
 #include <__type_traits/is_function.h>
@@ -17,6 +21,7 @@
 #include <__type_traits/is_object.h>
 #include <__type_traits/is_pointer.h>
 #include <__type_traits/is_reference.h>
+#include <__type_traits/is_same.h>
 #include <__type_traits/is_void.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_pointer.h>
@@ -28,6 +33,10 @@
 #  pragma GCC system_header
 #endif
 
+#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
+#  error This header should only be included from function_ref.h
+#endif
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
@@ -35,16 +44,13 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
-template <class _Rp, class... _ArgTypes>
-class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT)> {
+template <class _Rp, class... _ArgTypes, bool __is_noexcept>
+class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
 private:
-#  if _LIBCPP_FUNCTION_REF_NOEXCEPT == true
-  template <class... _Tp>
-  static constexpr bool __is_invocable_using = is_nothrow_invocable_r_v<_Rp, _Tp..., _ArgTypes...>;
-#  else
   template <class... _Tp>
-  static constexpr bool __is_invocable_using = is_invocable_r_v<_Rp, _Tp..., _ArgTypes...>;
-#  endif
+  static constexpr bool __is_invocable_using =
+      _If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
+          value;
 
   // use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
   // pointers
@@ -84,7 +90,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
     }
   }
 
-  using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, _ArgTypes&&...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+  using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, _ArgTypes&&...) noexcept(__is_noexcept);
   __call_t __call_;
 
 public:
@@ -92,26 +98,26 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
     requires is_function_v<_Fp> && __is_invocable_using<_Fp>
   _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
       : __storage_(__fn_ptr),
-        __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+        __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
           return __get<_Fp>(__storage)(std::forward<_ArgTypes>(__args)...);
         }) {
-    _LIBCPP_ASSERT_UNCATEGORIZED(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
+    _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
   }
 
-  template <class _Fp, class _Tp = remove_reference_t<_Fp>>
-    requires(!__is_function_ref<remove_cvref_t<_Fp>> && !is_member_pointer_v<_Tp> &&
+  template <class _Fn, class _Tp = remove_reference_t<_Fn>>
+    requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
              __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&>)
-  _LIBCPP_HIDE_FROM_ABI function_ref(_Fp&& __obj) noexcept
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
       : __storage_(std::addressof(__obj)),
-        __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+        __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
           _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
           return __obj(std::forward<_ArgTypes>(__args)...);
         }) {}
 
   template <auto _Fn>
-    requires __is_invocable_using<decltype(_Fn)>
+    requires __is_invocable_using<const decltype(_Fn)&>
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>) noexcept
-      : __call_([](__storage_t, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+      : __call_([](__storage_t, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
           return std::invoke_r<_Rp>(_Fn, std::forward<_ArgTypes>(__args)...);
         }) {
     if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
@@ -123,7 +129,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
     requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp&>
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _Up&& __obj) noexcept
       : __storage_(std::addressof(__obj)),
-        __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+        __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
           _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
           return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
         }) {
@@ -136,7 +142,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
     requires __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp*>
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
       : __storage_(__obj_ptr),
-        __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) -> _Rp {
+        __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
           auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
           return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
         }) {
@@ -154,24 +160,22 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FU
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref& operator=(const function_ref&) noexcept = default;
 
   template <class _Tp>
-    requires(!__is_function_ref<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_constant_arg_t<_Tp>)
+    requires(!is_same_v<_Tp, function_ref>) && (!is_pointer_v<_Tp>) && (!__is_constant_arg_t<_Tp>)
   _LIBCPP_HIDE_FROM_ABI function_ref& operator=(_Tp) = delete;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _Rp operator()(_ArgTypes... __args) const noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT) {
+  _LIBCPP_HIDE_FROM_ABI constexpr _Rp operator()(_ArgTypes... __args) const noexcept(__is_noexcept) {
     return __call_(__storage_, std::forward<_ArgTypes>(__args)...);
   }
 };
 
-template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
-struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
-                           _Tp> {
-  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes, bool __is_noexcept>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
 };
 
-template <class _Tp, class _Rp, class _Gp, class... _ArgTypes>
-struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT),
-                           _Tp> {
-  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(_LIBCPP_FUNCTION_REF_NOEXCEPT);
+template <class _Tp, class _Rp, class _Gp, class... _ArgTypes, bool __is_noexcept>
+struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(__is_noexcept), _Tp> {
+  using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
 };
 
 #endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 82dbcf7dc791c..41c4cedccbba3 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1434,7 +1434,7 @@ module std [system] {
     module default_searcher             { header "__functional/default_searcher.h" }
     module function                     { header "__functional/function.h" }
     module function_ref                 { header "__functional/function_ref.h" }
-    module function_ref_common          { textual header "__functional/function_ref_common.h" }
+    module function_ref_common          { header "__functional/function_ref_common.h" }
     module function_ref_impl            { textual header "__functional/function_ref_impl.h" }
     module hash                         { header "__functional/hash.h" }
     module identity                     { header "__functional/identity.h" }
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp
new file mode 100644
index 0000000000000..085b09a4905ba
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.constant_arg_ptr.pass.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// template<auto f, class T>
+//   constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
+// Preconditions: If is_member_pointer_v<F> is true, obj is not a null pointer.
+
+#include <functional>
+
+#include "check_assertion.h"
+
+struct A {
+  void f() {}
+};
+
+int main(int, char**) {
+  TEST_LIBCPP_ASSERT_FAILURE(std::function_ref<void()>(std::constant_arg<&A::f>, static_cast<A*>(nullptr)),
+                             "the object pointer should not be a nullptr");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
new file mode 100644
index 0000000000000..371bf714f38c9
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assert.function_ptr.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// REQUIRES: std-at-least-c++26
+// UNSUPPORTED: libcpp-hardening-mode=none
+// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
+
+// template<class F> function_ref(F* f) noexcept;
+// Preconditions: f is not a null pointer.
+
+#include <functional>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  TEST_LIBCPP_ASSERT_FAILURE(
+      std::function_ref<void()>(static_cast<void (*)()>(nullptr)), "the function pointer should not be a nullptr");
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
new file mode 100644
index 0000000000000..4e26f940ff430
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.mandates.verify.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<auto f> constexpr function_ref(constant_arg_t<f>) noexcept;
+// template<auto f, class U>
+//   constexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
+// template<auto f, class T>
+//   constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
+
+// Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
+
+#include <functional>
+#include <utility>
+
+struct A {
+  void f();
+};
+
+// clang-format off
+void test() {
+  std::function_ref<void()> f1(std::constant_arg<static_cast<void (*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void(A)> f2(std::constant_arg<static_cast<void (A::*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': the function pointer should not be a nullptr}}
+
+  int i;
+  std::function_ref<void()> f3(std::constant_arg<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': the function pointer should not be a nullptr}}
+
+  A a;
+  std::function_ref<void()> f4(std::constant_arg<static_cast<void (A::*)()>(nullptr)>, a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void()> f5(std::constant_arg<static_cast<void (*)(int*)>(nullptr)>, &i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': the function pointer should not be a nullptr}}
+
+  std::function_ref<void()> f6(std::constant_arg<static_cast<void (A::*)()>(nullptr)>, &a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  // expected-error@*:* {{static assertion failed due to requirement 'nullptr != nullptr': the function pointer should not be a nullptr}}
+}
+// clang-format on
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
similarity index 100%
rename from libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctor/ctad.pass.cpp
rename to libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp
new file mode 100644
index 0000000000000..5e54af54cfe1f
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp
@@ -0,0 +1,106 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<auto f> constexpr function_ref(constant_arg_t<f>) noexcept;
+
+#include "__utility/constant_arg.h"
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints: is-invocable-using<const F&> is true.
+
+auto l1          = [] {};
+auto l1_noexcept = [] noexcept {};
+auto l2          = [](int) {};
+auto l2_noexcept = [](int) noexcept {};
+
+struct NonConst {
+  void operator()() noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<NonConst{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int)>, std::constant_arg_t<l2>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int)>, std::constant_arg_t<l2>>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1_noexcept>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<NonConst{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l2_noexcept>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) noexcept>, std::constant_arg_t<l2_noexcept>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1_noexcept>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int) noexcept>, std::constant_arg_t<l2_noexcept>>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<NonConst{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) const>, std::constant_arg_t<l2>>);
+
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int) const>, std::constant_arg_t<l2>>);
+
+// const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1_noexcept>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<NonConst{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l2_noexcept>>);
+static_assert(std::is_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_arg_t<l2_noexcept>>);
+
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1_noexcept>>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_arg_t<l2_noexcept>>);
+
+constexpr double f1(int x, double y) noexcept { return x + y; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::constant_arg<[] {}>);
+    f();
+  }
+  {
+    // explicit
+    std::function_ref<void()> f = std::constant_arg<[] {}>;
+    f();
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::constant_arg<[] { return 42; }>);
+    assert(f() == 42);
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::constant_arg<&f1>);
+    assert(f(1, 2.0) == 3.0);
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::constant_arg<&f1>);
+    assert(f(1, 2.0) == 3.0);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
new file mode 100644
index 0000000000000..8dccedc1f4199
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -0,0 +1,119 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<class F> function_ref(F* f) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - is_function_v<F> is true, and
+// - is-invocable-using<F> is true.
+
+struct A {
+  int i;
+  void f() {}
+  void operator()(auto...) const {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, void (*)()>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, void (*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (*)(int)>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*)>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, void (*)()>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, void (*)() noexcept>);
+
+// non-const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, A (*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, A (*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) noexcept>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, A (*)() noexcept>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double)>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, A)>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double)>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double) noexcept>);
+
+// const noexcept(true)
+static_assert(
+    std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, double) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, double)>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, A) noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                              void (*)(int, double) noexcept>);
+
+int fn() { return 42; }
+
+int fn_noexcept(int i, A a) noexcept { return i + a.i; }
+
+void test() {
+  {
+    // simple case
+    std::function_ref<int()> f(&fn);
+    assert(f() == 42);
+  }
+  {
+    // explicit(false)
+    std::function_ref<int()> f = &fn;
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int(int, A)> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+  {
+    // noexcept
+    std::function_ref<int(int, A) noexcept> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+  {
+    // const
+    auto x = [](int x, int y, int z) { return x + y - z; };
+    std::function_ref<int(int, int, int) const> f(+x);
+    assert(f(2, 3, 4) == 1);
+  }
+  {
+    // const noexcept
+    std::function_ref<int(int, A) const noexcept> f(&fn_noexcept);
+    assert(f(4, A{5}) == 9);
+  }
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
new file mode 100644
index 0000000000000..4657e7b129d2e
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -0,0 +1,203 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<class F> constexpr function_ref(F&&) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - remove_cvref_t<F> is not the same type as function_ref,
+// - is_member_pointer_v<T> is false, and
+// - is-invocable-using<cv T&> is true.
+
+struct A {
+  int i;
+  void f() {}
+  void operator()(auto...) const {}
+};
+
+constexpr auto l1 = [] {};
+using L1          = std::remove_cvref_t<decltype(l1)>;
+
+constexpr auto l2 = [] { return A{5}; };
+using L2          = std::remove_cvref_t<decltype(l2)>;
+
+constexpr auto l2_noexcept = [] noexcept { return A{5}; };
+using L2Noexcept           = std::remove_cvref_t<decltype(l2_noexcept)>;
+
+constexpr auto l3 = [](int x, double d) { return x + d; };
+using L3          = std::remove_cvref_t<decltype(l3)>;
+
+constexpr auto l3_noexcept = [](int x, double d) noexcept { return x + d; };
+using L3Noexcept           = std::remove_cvref_t<decltype(l3_noexcept)>;
+
+struct NonConstInvocable {
+  int i;
+
+  constexpr int operator()(int x, double y) {
+    ++i;
+    return x + y + i;
+  }
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1 const&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1&&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L1 const&&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, L2&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::function_ref<int()>&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*)>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1 const&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1 const&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::function_ref<int()>&>);
+
+// non-const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&&>);
+static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, std::function_ref<A&() noexcept>>);
+
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, L2&>);
+static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, L2 const&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) noexcept>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, std::function_ref<A&() noexcept>>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, L3&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, const L3&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, L3&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, const L3&&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void(int, double) const>, std::function_ref<int(int, double) const>>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(int, double)>, NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double)>, const NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, NonConstInvocable&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (A::*)()>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)()>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, L3&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, const L3&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, L3&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, const L3&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>,
+                                              std::function_ref<int(int, double) const>>);
+
+// const noexcept(true)
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&&>);
+static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                      std::function_ref<int(int, double) const noexcept>>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3&&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3&&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (A::*)() noexcept>);
+static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)() noexcept>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
+                                              std::function_ref<int(int, double) const noexcept>>);
+
+struct F {
+  int i;
+  constexpr int operator()(auto&&...) { return 5 + i; }
+
+  constexpr int operator()(auto&&...) const { return 6 + i; }
+};
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(l1);
+    f();
+  }
+  {
+    // explicit(false)
+    std::function_ref<void()> f = l1;
+    f();
+  }
+  {
+    // noexcept
+    std::function_ref<A() noexcept> f(l2_noexcept);
+    auto a = f();
+    assert(a.i == 5);
+  }
+  {
+    // const
+    std::function_ref<double(int, double) const> f(l3);
+    assert(f(1, 2.0) == 3.0);
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(l3_noexcept);
+    assert(f(1, 2.0) == 3.0);
+  }
+  {
+    // no copies of original callable
+    auto local = [i = 5] mutable { return i++; };
+    std::function_ref<int()> f(local);
+    assert(f() == 5);
+    assert(local() == 6);
+    assert(f() == 7);
+  }
+  {
+    // const correctness
+    F f{5};
+
+    std::function_ref<int()> f1(f);
+    assert(f1() == 10);
+    assert(std::as_const(f1)() == 10);
+
+    std::function_ref<int() const> f2(f);
+    assert(f2() == 11);
+    assert(std::as_const(f2)() == 11);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
index ce9d7f8d73716..e73e778313dec 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+// REQUIRES: std-at-least-c++26
 
 #include <cassert>
 #include <functional>
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp
new file mode 100644
index 0000000000000..72e26f13868ea
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/trivially_copyable.compile.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
+
+#include <concepts>
+#include <functional>
+#include <type_traits>
+
+struct A {
+  A() {}
+  A(const A&) {}
+  ~A() {}
+};
+
+static_assert(std::copyable<std::function_ref<A(const A&)>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&)>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) noexcept>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) noexcept>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) const>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) const>>);
+
+static_assert(std::copyable<std::function_ref<A(const A&) const noexcept>>);
+static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) const noexcept>>);

>From 05848c0d16e4405769764359baaacb0c174e7bed Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 14 Mar 2026 15:28:01 +0000
Subject: [PATCH 16/26] remove experimental

---
 libcxx/docs/ReleaseNotes/23.rst                  |  1 +
 libcxx/include/__configuration/experimental.h    |  1 -
 libcxx/include/__functional/function_ref.h       |  4 ++--
 .../include/__functional/function_ref_common.h   |  4 ++--
 libcxx/include/__functional/function_ref_impl.h  |  4 ++--
 libcxx/include/version                           |  4 +---
 .../functional.version.compile.pass.cpp          | 16 +++++-----------
 .../version.version.compile.pass.cpp             | 16 +++++-----------
 .../generate_feature_test_macro_components.py    |  2 --
 9 files changed, 18 insertions(+), 34 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index a5acc569dc9ee..b4c4fe4190814 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -46,6 +46,7 @@ Implemented Papers
 - P3978R3: ``constant_wrapper`` should unwrap on call and subscript (`Github <https://llvm.org/PR189605>`__)
 - P0792R14: ``function_ref`` : a type-erased callable reference  (`Github <https://llvm.org/PR105376>`__) This feature is currently experimental and
   therefore requires ``-fexperimental-library``.
+- P0792R14: ``function_ref`` : a type-erased callable reference  (`Github <https://llvm.org/PR105376>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/include/__configuration/experimental.h b/libcxx/include/__configuration/experimental.h
index 1e12c50802895..bb38d8297c63d 100644
--- a/libcxx/include/__configuration/experimental.h
+++ b/libcxx/include/__configuration/experimental.h
@@ -34,6 +34,5 @@
 #define _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
 #define _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
 #define _LIBCPP_HAS_EXPERIMENTAL_OPTIONAL_ITERATOR _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
-#define _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
 
 #endif // _LIBCPP___CONFIGURATION_EXPERIMENTAL_H
diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
index e46706a1a8aaf..864e921e37824 100644
--- a/libcxx/include/__functional/function_ref.h
+++ b/libcxx/include/__functional/function_ref.h
@@ -16,7 +16,7 @@
 #  pragma GCC system_header
 #endif
 
-#if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
+#if _LIBCPP_STD_VER >= 26
 // NOLINTBEGIN(readability-duplicate-include)
 
 #define _LIBCPP_FUNCTION_REF_CV
@@ -29,6 +29,6 @@
 #undef _LIBCPP_FUNCTION_REF_CV
 
 // NOLINTEND(readability-duplicate-include)
-#endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
+#endif // _LIBCPP_STD_VER >= 26
 
 #endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index f921787f951f5..a757d0d308bc5 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -22,7 +22,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
+#if _LIBCPP_STD_VER >= 26
 
 template <class...>
 class function_ref;
@@ -60,7 +60,7 @@ function_ref(constant_arg_t<_Fn>) -> function_ref<remove_pointer_t<decltype(_Fn)
 template <auto _Fn, class _Tp>
 function_ref(constant_arg_t<_Fn>, _Tp&&) -> function_ref<__function_ref_bind_t<decltype(_Fn), _Tp&>>;
 
-#endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
+#endif // _LIBCPP_STD_VER >= 26
 
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 65c9088b9c892..bcdd3d92bf7b9 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -39,7 +39,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
+#if _LIBCPP_STD_VER >= 26
 
 template <class...>
 class function_ref;
@@ -178,6 +178,6 @@ struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV &
   using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
 };
 
-#endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
+#endif // _LIBCPP_STD_VER >= 26
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/version b/libcxx/include/version
index e3e4255122d30..1a4de23fb45e1 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -583,9 +583,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # if _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
 #   define __cpp_lib_fstream_native_handle              202306L
 # endif
-# if _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
-#   define __cpp_lib_function_ref                       202306L
-# endif
+# define __cpp_lib_function_ref                         202306L
 // # define __cpp_lib_generate_random                      202403L
 // # define __cpp_lib_hazard_pointer                       202306L
 // # define __cpp_lib_inplace_vector                       202406L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
index eaab9bdd80ad8..eedb1eb8eb2a7 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
@@ -494,17 +494,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
-#    ifndef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should be defined in c++26"
-#    endif
-#    if __cpp_lib_function_ref != 202306L
-#      error "__cpp_lib_function_ref should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should not be defined when the requirement '!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF' is not met!"
-#    endif
+#  ifndef __cpp_lib_function_ref
+#    error "__cpp_lib_function_ref should be defined in c++26"
+#  endif
+#  if __cpp_lib_function_ref != 202306L
+#    error "__cpp_lib_function_ref should have the value 202306L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_invoke
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 2e86059e6e91f..4958ee42103f4 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
@@ -7035,17 +7035,11 @@
 #    endif
 #  endif
 
-#  if !defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF
-#    ifndef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should be defined in c++26"
-#    endif
-#    if __cpp_lib_function_ref != 202306L
-#      error "__cpp_lib_function_ref should have the value 202306L in c++26"
-#    endif
-#  else
-#    ifdef __cpp_lib_function_ref
-#      error "__cpp_lib_function_ref should not be defined when the requirement '!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF' is not met!"
-#    endif
+#  ifndef __cpp_lib_function_ref
+#    error "__cpp_lib_function_ref should be defined in c++26"
+#  endif
+#  if __cpp_lib_function_ref != 202306L
+#    error "__cpp_lib_function_ref should have the value 202306L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_gcd_lcm
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 5efaad14462e8..f290e35c594d0 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -701,8 +701,6 @@ def add_version_header(tc):
                 "c++26": 202306  # P0792R14 function_ref: a type-erased callable reference
             },
             "headers": ["functional"],
-            "test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF",
-            "libcxx_guard": "_LIBCPP_HAS_EXPERIMENTAL_FUNCTION_REF"
         },
         {
             "name": "__cpp_lib_gcd_lcm",

>From d71d82b02b1e0a7331bd16cfb055a6750bb576c1 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 14 Mar 2026 20:14:24 +0000
Subject: [PATCH 17/26] test for constant_arg_ref

---
 .../include/__functional/function_ref_impl.h  |   6 +-
 .../func.wrap.ref.ctor/constant_arg.pass.cpp  |  52 ++-
 .../constant_arg_ref.pass.cpp                 | 319 ++++++++++++++++++
 .../func.wrap.ref.ctor/function_ptr.pass.cpp  |  20 ++
 .../func.wrap.ref.ctor/ref.pass.cpp           |  25 ++
 5 files changed, 414 insertions(+), 8 deletions(-)
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index bcdd3d92bf7b9..7df579186d032 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -74,7 +74,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
         __fn_ptr = reinterpret_cast<void (*)()>(__ptr);
       }
     }
-  } __storage_;
+  };
 
   template <class _Tp>
   _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_t __storage) {
@@ -90,6 +90,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     }
   }
 
+  __storage_t __storage_;
+
   using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, _ArgTypes&&...) noexcept(__is_noexcept);
   __call_t __call_;
 
@@ -126,7 +128,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   }
 
   template <auto _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
-    requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp&>
+    requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<const decltype(_Fn)&, _LIBCPP_FUNCTION_REF_CV _Tp&>
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _Up&& __obj) noexcept
       : __storage_(std::addressof(__obj)),
         __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp
index 5e54af54cfe1f..7adc6108e9b50 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp
@@ -10,7 +10,6 @@
 
 // template<auto f> constexpr function_ref(constant_arg_t<f>) noexcept;
 
-#include "__utility/constant_arg.h"
 #include <cassert>
 #include <functional>
 #include <utility>
@@ -25,13 +24,13 @@ auto l1_noexcept = [] noexcept {};
 auto l2          = [](int) {};
 auto l2_noexcept = [](int) noexcept {};
 
-struct NonConst {
+struct NonConstInvocable {
   void operator()() noexcept {}
 };
 
 // non-const noexcept(false)
 static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>>);
-static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<NonConst{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<NonConstInvocable{}>>);
 static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l2>>);
 static_assert(std::is_constructible_v<std::function_ref<void(int)>, std::constant_arg_t<l2>>);
 
@@ -41,7 +40,7 @@ static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int)>, std:
 // non-const noexcept
 static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1_noexcept>>);
 static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1>>);
-static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<NonConst{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<NonConstInvocable{}>>);
 static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l2_noexcept>>);
 static_assert(std::is_constructible_v<std::function_ref<void(int) noexcept>, std::constant_arg_t<l2_noexcept>>);
 
@@ -50,7 +49,7 @@ static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int) noexce
 
 // const noexcept(false)
 static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1>>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<NonConst{}>>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<NonConstInvocable{}>>);
 static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l2>>);
 static_assert(std::is_constructible_v<std::function_ref<void(int) const>, std::constant_arg_t<l2>>);
 
@@ -60,7 +59,8 @@ static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int) const>
 // const noexcept
 static_assert(std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1_noexcept>>);
 static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1>>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<NonConst{}>>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<NonConstInvocable{}>>);
 static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l2_noexcept>>);
 static_assert(std::is_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_arg_t<l2_noexcept>>);
 
@@ -71,6 +71,17 @@ static_assert(
 
 constexpr double f1(int x, double y) noexcept { return x + y; }
 
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  constexpr int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
 constexpr bool test() {
   {
     std::function_ref<void()> f(std::constant_arg<[] {}>);
@@ -96,11 +107,40 @@ constexpr bool test() {
     std::function_ref<double(int, double) const noexcept> f(std::constant_arg<&f1>);
     assert(f(1, 2.0) == 3.0);
   }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::constant_arg<NeedsConversion{}>);
+    assert(f(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f2(std::constant_arg<NeedsConversion{}>);
+    assert(f2(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<NeedsConversion{}>);
+    assert(f3(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<NeedsConversion{}>);
+    assert(f4(1, 2, 3).i == 6);
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::constant_arg<&needs_conversion>);
+    assert(f(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f2(std::constant_arg<&needs_conversion>);
+    assert(f2(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<&needs_conversion>);
+    assert(f3(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<&needs_conversion>);
+    assert(f4(1, 2, 3).i == 6);
+  }
 
   return true;
 }
 
 int main(int, char**) {
   test();
+  static_assert(test());
   return 0;
 }
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp
new file mode 100644
index 0000000000000..3d77f59263496
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp
@@ -0,0 +1,319 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<auto f, class U>
+//   constexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - is_rvalue_reference_v<U&&> is false, and
+// - is-invocable-using<const F&, cv T&> is true.
+
+auto l1          = [](int) {};
+auto l1_noexcept = [](int) noexcept {};
+auto l2          = [](int, double) {};
+auto l2_noexcept = [](int, double) noexcept {};
+auto l3          = [](int&) {};
+auto l3_noexcept = [](int&) noexcept {};
+
+struct NonConstInvocable {
+  void operator()(long) noexcept {}
+};
+
+struct A {
+  int i;
+  void f() {}
+  void f_const() const {}
+  void f_noexcept() noexcept {}
+  void f_const_noexcept() const noexcept {}
+  void g(int&) {}
+  void g_const(int&) const {}
+  void g_noexcept(int&) noexcept {}
+  void g_const_noexcept(int&) const noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>, int&&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<NonConstInvocable{}>, long&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_arg_t<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l2>, int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l3>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l3>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<&A::g>, A&>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_arg_t<l2>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_arg_t<l3>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_arg_t<&A::f>, A&>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1_noexcept>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1_noexcept>, int&&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<NonConstInvocable{}>, long&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_arg_t<l2_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_arg_t<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l2_noexcept>, int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l3_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l3>, const int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l3_noexcept>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<&A::f_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<&A::g_noexcept>, A&>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1_noexcept>, int&>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_arg_t<l2_noexcept>, int&>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l3_noexcept>, int&>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<&A::f_noexcept>, A&>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1>, int&&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<NonConstInvocable{}>, long&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_arg_t<l2>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l2>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l3>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l3>, const int&>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::f_const>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::g_const>, A&>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_arg_t<l2>, int&>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::f_const>, A&>);
+
+// const noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1_noexcept>, int&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1>, int&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1_noexcept>, int&&>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_arg_t<NonConstInvocable{}>,
+                                       long&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_arg_t<l2_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_arg_t<l2>, int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l2_noexcept>, int&>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l3_noexcept>, int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l3>, const int&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l3_noexcept>, const int&>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f_const_noexcept>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f>, A&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f_const>, A&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f_noexcept>, A&>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::g_const_noexcept>, A&>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1_noexcept>, int&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_arg_t<l2_noexcept>, int&>);
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f_const_noexcept>, A&>);
+
+constexpr double f1(int x, double y) noexcept { return x + y; }
+
+struct M {
+  int i;
+  constexpr int f() { return i; }
+  constexpr int f_const() const { return i + 5; }
+  constexpr int f_noexcept() noexcept { return i + 7; }
+  constexpr int f_const_noexcept() const noexcept { return i + 9; }
+  constexpr int g(int& j) {
+    j = 42;
+    return i + j;
+  }
+  constexpr int g_const(int& j) const {
+    j = 42;
+    return i + j + 1;
+  }
+  constexpr int g_noexcept(int& j) noexcept {
+    j = 42;
+    return i + j + 2;
+  }
+  constexpr int g_const_noexcept(int& j) const noexcept {
+    j = 42;
+    return i + j + 3;
+  }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  constexpr int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    int i = 0;
+    std::function_ref<void()> f(std::constant_arg<[](int) {}>, i);
+    f();
+  }
+  {
+    // explicit
+    int i                       = 0;
+    std::function_ref<void()> f = {std::constant_arg<[](int) {}>, i};
+    f();
+  }
+  {
+    // mutate
+    int i;
+    std::function_ref<double(double)> f(
+        std::constant_arg<[](int& j, double d) {
+          j = 5;
+          return j + d;
+        }>,
+        i);
+    assert(f(3.3) == 8.3);
+    assert(i == 5);
+  }
+  {
+    // const
+    int i = 5;
+    std::function_ref<int() const> f(std::constant_arg<[](int i) { return i + 42; }>, i);
+    assert(f() == 47);
+  }
+  {
+    // noexcept
+    int i = 5;
+    std::function_ref<double(double) noexcept> f(std::constant_arg<&f1>, i);
+    assert(f(2.0) == 7.0);
+  }
+  {
+    // const noexcept
+    int i = 5;
+    std::function_ref<double( double) const noexcept> f(std::constant_arg<&f1>, i);
+    assert(f(2.0) == 7.0);
+  }
+  {
+    // member ptr
+    M m{3};
+    std::function_ref<int()> f(std::constant_arg<&M::f>, m);
+    assert(f() == 3);
+
+    int j = 0;
+    std::function_ref<int(int&)> g(std::constant_arg<&M::g>, m);
+    assert(g(j) == 45);
+    assert(j == 42);
+
+    std::function_ref<int() const> f_const(std::constant_arg<&M::f_const>, m);
+    assert(f_const() == 8);
+
+    j = 0;
+    std::function_ref<int(int&)> g_const(std::constant_arg<&M::g_const>, m);
+    assert(g_const(j) == 46);
+    assert(j == 42);
+
+    std::function_ref<int() noexcept> f_noexcept(std::constant_arg<&M::f_noexcept>, m);
+    assert(f_noexcept() == 10);
+
+    j = 0;
+    std::function_ref<int(int&) noexcept> g_noexcept(std::constant_arg<&M::g_noexcept>, m);
+    assert(g_noexcept(j) == 47);
+    assert(j == 42);
+
+    std::function_ref<int() const noexcept> f_const_noexcept(std::constant_arg<&M::f_const_noexcept>, m);
+    assert(f_const_noexcept() == 12);
+
+    j = 0;
+    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::constant_arg<&M::g_const_noexcept>, m);
+    assert(g_const_noexcept(j) == 48);
+    assert(j == 42);
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::constant_arg<NeedsConversion{}>);
+    assert(f(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f2(std::constant_arg<NeedsConversion{}>);
+    assert(f2(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<NeedsConversion{}>);
+    assert(f3(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<NeedsConversion{}>);
+    assert(f4(1, 2, 3).i == 6);
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::constant_arg<&needs_conversion>);
+    assert(f(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f2(std::constant_arg<&needs_conversion>);
+    assert(f2(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<&needs_conversion>);
+    assert(f3(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<&needs_conversion>);
+    assert(f4(1, 2, 3).i == 6);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
index 8dccedc1f4199..22835c48ebcf9 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -80,6 +80,13 @@ int fn() { return 42; }
 
 int fn_noexcept(int i, A a) noexcept { return i + a.i; }
 
+struct Int {
+  int i;
+  Int(int ii) noexcept : i(ii) {}
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
 void test() {
   {
     // simple case
@@ -111,6 +118,19 @@ void test() {
     std::function_ref<int(int, A) const noexcept> f(&fn_noexcept);
     assert(f(4, A{5}) == 9);
   }
+  {
+    std::function_ref<Int(int, int, int)> f(&needs_conversion);
+    assert(f(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f2(&needs_conversion);
+    assert(f2(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f3(&needs_conversion);
+    assert(f3(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(&needs_conversion);
+    assert(f4(1, 2, 3).i == 6);
+  }
 }
 
 int main(int, char**) {
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index 4657e7b129d2e..f0f6411521f6d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -146,6 +146,15 @@ struct F {
   constexpr int operator()(auto&&...) const { return 6 + i; }
 };
 
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  constexpr int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
 constexpr bool test() {
   {
     std::function_ref<void()> f(l1);
@@ -192,6 +201,22 @@ constexpr bool test() {
     assert(f2() == 11);
     assert(std::as_const(f2)() == 11);
   }
+  {
+    // with conversions
+    NeedsConversion c{};
+
+    std::function_ref<Int(int, int, int)> f(c);
+    assert(f(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f2(c);
+    assert(f2(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f3(c);
+    assert(f3(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(c);
+    assert(f4(1, 2, 3).i == 6);
+  }
 
   return true;
 }

>From f7296533322601c106d5c43373d21beebd2bbb4d Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 14 Mar 2026 20:43:59 +0000
Subject: [PATCH 18/26] test for constant_arg_ptr

---
 .../include/__functional/function_ref_impl.h  |   2 +-
 .../constant_arg_ptr.pass.cpp                 | 302 ++++++++++++++++++
 .../constant_arg_ref.pass.cpp                 |  37 ++-
 3 files changed, 323 insertions(+), 18 deletions(-)
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ptr.pass.cpp

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 7df579186d032..bc3078ed47660 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -141,7 +141,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   }
 
   template <auto _Fn, class _Tp>
-    requires __is_invocable_using<decltype(_Fn), _LIBCPP_FUNCTION_REF_CV _Tp*>
+    requires __is_invocable_using<const decltype(_Fn)&, _LIBCPP_FUNCTION_REF_CV _Tp*>
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
       : __storage_(__obj_ptr),
         __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ptr.pass.cpp
new file mode 100644
index 0000000000000..20b0ca855f08d
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ptr.pass.cpp
@@ -0,0 +1,302 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<auto f, class T>
+//   constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints: is-invocable-using<const F&, cv T*> is true.
+
+auto l1                = [](int*) {};
+auto l1_const          = [](const int*) {};
+auto l1_noexcept       = [](int*) noexcept {};
+auto l1_const_noexcept = [](const int*) noexcept {};
+auto l2                = [](int*, double) {};
+auto l2_const          = [](const int*, double) {};
+auto l2_noexcept       = [](int*, double) noexcept {};
+auto l2_const_noexcept = [](const int*, double) noexcept {};
+
+struct NonConstInvocable {
+  void operator()(int*) noexcept {}
+};
+
+struct A {
+  int i;
+  void f() {}
+  void f_const() const {}
+  void f_noexcept() noexcept {}
+  void f_const_noexcept() const noexcept {}
+  void g(int&) {}
+  void g_const(int&) const {}
+  void g_noexcept(int&) noexcept {}
+  void g_const_noexcept(int&) const noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>, const int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_arg_t<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l2>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<&A::g>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_arg_t<l2>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_arg_t<&A::f>, A*>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1_noexcept>, int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<NonConstInvocable{}>, int*>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_arg_t<l2_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_arg_t<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l2_noexcept>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<&A::f_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<&A::g_noexcept>, A*>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1_noexcept>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_arg_t<l2_noexcept>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<&A::f_noexcept>, A*>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1_const>, int*>);
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1_const>, const int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_arg_t<l2_const>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l2_const>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::f_const>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::g_const>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1_const>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1_const>, const int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_arg_t<l2_const>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::f_const>, A*>);
+
+// const noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1_const_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1_const>, int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1_noexcept>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const noexcept>,
+                                      std::constant_arg_t<l2_const_noexcept>,
+                                      int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l2_const_noexcept>, int*>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f_const_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f_const>, A*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f_noexcept>, A*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::g_const_noexcept>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+                                              std::constant_arg_t<l1_const_noexcept>,
+                                              int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const noexcept>,
+                                              std::constant_arg_t<l2_const_noexcept>,
+                                              int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+                                              std::constant_arg_t<&A::f_const_noexcept>,
+                                              A*>);
+
+constexpr double f1(const int* x, double y) noexcept { return *x + y; }
+
+struct M {
+  int i;
+  constexpr int f() { return i; }
+  constexpr int f_const() const { return i + 5; }
+  constexpr int f_noexcept() noexcept { return i + 7; }
+  constexpr int f_const_noexcept() const noexcept { return i + 9; }
+  constexpr int g(int& j) {
+    j = 42;
+    return i + j;
+  }
+  constexpr int g_const(int& j) const {
+    j = 42;
+    return i + j + 1;
+  }
+  constexpr int g_noexcept(int& j) noexcept {
+    j = 42;
+    return i + j + 2;
+  }
+  constexpr int g_const_noexcept(int& j) const noexcept {
+    j = 42;
+    return i + j + 3;
+  }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  constexpr int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    int i = 0;
+    std::function_ref<void()> f(std::constant_arg<[](int*) {}>, &i);
+    f();
+  }
+  {
+    // explicit
+    int i                       = 0;
+    std::function_ref<void()> f = {std::constant_arg<[](int*) {}>, &i};
+    f();
+  }
+  {
+    // mutate
+    int i;
+    std::function_ref<double(double)> f(
+        std::constant_arg<[](int* j, double d) {
+          *j = 5;
+          return *j + d;
+        }>,
+        &i);
+    assert(f(3.3) == 8.3);
+    assert(i == 5);
+  }
+  {
+    // const
+    int i = 5;
+    std::function_ref<int() const> f(std::constant_arg<[](const int* i) { return *i + 42; }>, &i);
+    assert(f() == 47);
+  }
+  {
+    // noexcept
+    int i = 5;
+    std::function_ref<double(double) noexcept> f(std::constant_arg<&f1>, &i);
+    assert(f(2.0) == 7.0);
+  }
+  {
+    // const noexcept
+    int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::constant_arg<&f1>, &i);
+    assert(f(2.0) == 7.0);
+  }
+  {
+    // member ptr
+    M m{3};
+    std::function_ref<int()> f(std::constant_arg<&M::f>, &m);
+    assert(f() == 3);
+
+    int j = 0;
+    std::function_ref<int(int&)> g(std::constant_arg<&M::g>, &m);
+    assert(g(j) == 45);
+    assert(j == 42);
+
+    std::function_ref<int() const> f_const(std::constant_arg<&M::f_const>, &m);
+    assert(f_const() == 8);
+
+    j = 0;
+    std::function_ref<int(int&)> g_const(std::constant_arg<&M::g_const>, &m);
+    assert(g_const(j) == 46);
+    assert(j == 42);
+
+    std::function_ref<int() noexcept> f_noexcept(std::constant_arg<&M::f_noexcept>, &m);
+    assert(f_noexcept() == 10);
+
+    j = 0;
+    std::function_ref<int(int&) noexcept> g_noexcept(std::constant_arg<&M::g_noexcept>, &m);
+    assert(g_noexcept(j) == 47);
+    assert(j == 42);
+
+    std::function_ref<int() const noexcept> f_const_noexcept(std::constant_arg<&M::f_const_noexcept>, &m);
+    assert(f_const_noexcept() == 12);
+
+    j = 0;
+    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::constant_arg<&M::g_const_noexcept>, &m);
+    assert(g_const_noexcept(j) == 48);
+    assert(j == 42);
+  }
+  {
+    // with conversions
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::constant_arg<NeedsConversion{}>, i);
+    assert(f(2, 3).i == 6);
+
+    std::function_ref<Int(int, int) const> f2(std::constant_arg<NeedsConversion{}>, i);
+    assert(f2(2, 3).i == 6);
+
+    std::function_ref<Int(int, int) noexcept> f3(std::constant_arg<NeedsConversion{}>, i);
+    assert(f3(2, 3).i == 6);
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::constant_arg<NeedsConversion{}>, i);
+    assert(f4(2, 3).i == 6);
+  }
+  {
+    // with conversions function pointer
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::constant_arg<&needs_conversion>, i);
+    assert(f(2, 3).i == 6);
+
+    std::function_ref<Int(int, int) const> f2(std::constant_arg<&needs_conversion>, i);
+    assert(f2(2, 3).i == 6);
+
+    std::function_ref<Int(int, int) noexcept> f3(std::constant_arg<&needs_conversion>, i);
+    assert(f3(2, 3).i == 6);
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::constant_arg<&needs_conversion>, i);
+    assert(f4(2, 3).i == 6);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp
index 3d77f59263496..43eb5133c76fd 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp
@@ -242,7 +242,7 @@ constexpr bool test() {
   {
     // const noexcept
     int i = 5;
-    std::function_ref<double( double) const noexcept> f(std::constant_arg<&f1>, i);
+    std::function_ref<double(double) const noexcept> f(std::constant_arg<&f1>, i);
     assert(f(2.0) == 7.0);
   }
   {
@@ -282,31 +282,34 @@ constexpr bool test() {
   }
   {
     // with conversions
-    std::function_ref<Int(int, int, int)> f(std::constant_arg<NeedsConversion{}>);
-    assert(f(1, 2, 3).i == 6);
+    int i = 1;
 
-    std::function_ref<Int(int, int, int) const> f2(std::constant_arg<NeedsConversion{}>);
-    assert(f2(1, 2, 3).i == 6);
+    std::function_ref<Int(int, int)> f(std::constant_arg<NeedsConversion{}>, i);
+    assert(f(2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<NeedsConversion{}>);
-    assert(f3(1, 2, 3).i == 6);
+    std::function_ref<Int(int, int) const> f2(std::constant_arg<NeedsConversion{}>, i);
+    assert(f2(2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<NeedsConversion{}>);
-    assert(f4(1, 2, 3).i == 6);
+    std::function_ref<Int(int, int) noexcept> f3(std::constant_arg<NeedsConversion{}>, i);
+    assert(f3(2, 3).i == 6);
+
+    std::function_ref<Int(int, int) const noexcept> f4(std::constant_arg<NeedsConversion{}>, i);
+    assert(f4(2, 3).i == 6);
   }
   {
     // with conversions function pointer
-    std::function_ref<Int(int, int, int)> f(std::constant_arg<&needs_conversion>);
-    assert(f(1, 2, 3).i == 6);
+    int i = 1;
+    std::function_ref<Int(int, int)> f(std::constant_arg<&needs_conversion>, i);
+    assert(f(2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const> f2(std::constant_arg<&needs_conversion>);
-    assert(f2(1, 2, 3).i == 6);
+    std::function_ref<Int(int, int) const> f2(std::constant_arg<&needs_conversion>, i);
+    assert(f2(2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<&needs_conversion>);
-    assert(f3(1, 2, 3).i == 6);
+    std::function_ref<Int(int, int) noexcept> f3(std::constant_arg<&needs_conversion>, i);
+    assert(f3(2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<&needs_conversion>);
-    assert(f4(1, 2, 3).i == 6);
+    std::function_ref<Int(int, int) const noexcept> f4(std::constant_arg<&needs_conversion>, i);
+    assert(f4(2, 3).i == 6);
   }
 
   return true;

>From 5e7d39a20e3217d5af3c39d9891c3892334f7fc4 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 14 Mar 2026 20:49:05 +0000
Subject: [PATCH 19/26] remove fun_ptr member

---
 .../include/__functional/function_ref_impl.h  | 26 +++++++++----------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index bc3078ed47660..f0c7c40765013 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -54,24 +54,24 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
 
   // use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
   // pointers
+  // todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be supported
   union __storage_t {
-    void* __obj_ptr;
-    void const* __obj_const_ptr;
-    void (*__fn_ptr)();
+    void* __obj_ptr_;
+    void const* __obj_const_ptr_;
 
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t() noexcept : __obj_ptr(nullptr) {}
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t() noexcept : __obj_ptr_(nullptr) {}
 
     template <class _Tp>
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t(_Tp* __ptr) noexcept {
       if constexpr (is_object_v<_Tp>) {
         if constexpr (is_const_v<_Tp>) {
-          __obj_const_ptr = __ptr;
+          __obj_const_ptr_ = __ptr;
         } else {
-          __obj_ptr = __ptr;
+          __obj_ptr_ = __ptr;
         }
       } else {
         static_assert(is_function_v<_Tp>);
-        __fn_ptr = reinterpret_cast<void (*)()>(__ptr);
+        __obj_ptr_ = reinterpret_cast<void*>(__ptr);
       }
     }
   };
@@ -80,13 +80,13 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_t __storage) {
     if constexpr (is_object_v<_Tp>) {
       if constexpr (is_const_v<_Tp>) {
-        return static_cast<_Tp*>(__storage.__obj_const_ptr);
+        return static_cast<_Tp*>(__storage.__obj_const_ptr_);
       } else {
-        return static_cast<_Tp*>(__storage.__obj_ptr);
+        return static_cast<_Tp*>(__storage.__obj_ptr_);
       }
     } else {
       static_assert(is_function_v<_Tp>);
-      return reinterpret_cast<_Tp*>(__storage.__fn_ptr);
+      return reinterpret_cast<_Tp*>(__storage.__obj_ptr_);
     }
   }
 
@@ -142,8 +142,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
 
   template <auto _Fn, class _Tp>
     requires __is_invocable_using<const decltype(_Fn)&, _LIBCPP_FUNCTION_REF_CV _Tp*>
-  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
-      : __storage_(__obj_ptr),
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr_) noexcept
+      : __storage_(__obj_ptr_),
         __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
           auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
           return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
@@ -153,7 +153,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     }
 
     if constexpr (is_member_pointer_v<decltype(_Fn)>) {
-      _LIBCPP_ASSERT_UNCATEGORIZED(__obj_ptr != nullptr, "the object pointer should not be a nullptr");
+      _LIBCPP_ASSERT_UNCATEGORIZED(__obj_ptr_ != nullptr, "the object pointer should not be a nullptr");
     }
   }
 

>From e65a234081db15a018546c0b3a5f6bb4835dc9f2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 15 Mar 2026 14:43:35 +0000
Subject: [PATCH 20/26] invoke

---
 .../func.wrap/func.wrap.ref/ctad.pass.cpp     |   2 +-
 .../func.wrap.ref.ctor/assign.delete.pass.cpp | 117 +++++++
 .../func.wrap.ref.ctor/copy.pass.cpp          | 107 ++++++
 .../func.wrap.ref.ctor/copy_assign.pass.cpp   | 135 ++++++++
 .../func.wrap.ref.ctor/move.pass.cpp          | 105 ++++++
 .../func.wrap.ref.ctor/move_assign.pass.cpp   | 133 ++++++++
 .../func.wrap.ref.inv/invoke.pass.cpp         | 315 ++++++++++++++++++
 .../func.wrap.ref/invoke/const.pass.cpp       | 124 -------
 .../invoke/const_noexcept.pass.cpp            | 124 -------
 .../func.wrap.ref/invoke/default.pass.cpp     | 119 -------
 .../func.wrap.ref/invoke/noexcept.pass.cpp    | 119 -------
 11 files changed, 913 insertions(+), 487 deletions(-)
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
 delete mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp
 delete mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp
 delete mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
 delete mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp

diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
index 9ef7bd66a8953..591c397fee2d8 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+// REQUIRES: std-at-least-c++26
 
 #include <cassert>
 #include <functional>
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
new file mode 100644
index 0000000000000..956b34695f71a
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -0,0 +1,117 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// template<class T> function_ref& operator=(T) = delete;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints:
+// - T is not the same type as function_ref,
+// - is_pointer_v<T> is false, and
+// - T is not a specialization of constant_arg_t.
+
+// non const noexcept(false)
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const>>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() noexcept>>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void()>, void (*)()>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, void (*)(int)>);
+
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_arg_t<[] {}>>);
+static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_arg_t<[](int) {}>>);
+
+// const noexcept(false)
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const>, void (*)()>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, void (*)(int)>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::constant_arg_t<[] { return 42; }>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::constant_arg_t<[](int) { return 42; }>>);
+
+// non-const noexcept(true)
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() noexcept>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, void (*)() noexcept>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_arg_t<[] noexcept {} >>);
+static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_arg_t<[](int) noexcept {}>>);
+
+// const noexcept(true)
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void()>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const>>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const noexcept>>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)() noexcept>);
+static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)(int) noexcept>);
+
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_arg_t<[] noexcept {}>>);
+static_assert(
+    !std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_arg_t<[](int) noexcept {}>>);
+
+constexpr int forty_two() { return 42; }
+
+constexpr bool test() {
+  {
+    std::function_ref<int()> f(std::constant_arg<[] { return 41; }>);
+    f = std::function_ref<int()>(std::constant_arg<[] { return 42; }>);
+    assert(f() == 42);
+  }
+  if !consteval {
+    std::function_ref<int() > f(std::constant_arg<[] { return 41; }>);
+    f = &forty_two;
+    assert(f() == 42);
+  }
+  {
+    std::function_ref<int() > f(std::constant_arg<[] { return 41; }>);
+    f = std::constant_arg<[] { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::constant_arg<[] { return 41; }>);
+    f = std::constant_arg<[] { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // noexcept
+    std::function_ref<int() noexcept> f(std::constant_arg<[] noexcept { return 41; }>);
+    f = std::constant_arg<[] noexcept { return 42; }>;
+    assert(f() == 42);
+  }
+  {
+    // const noexcept
+    std::function_ref<int() const noexcept> f(std::constant_arg<[] noexcept { return 41; }>);
+    f = std::constant_arg<[] noexcept { return 42; }>;
+    assert(f() == 42);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
new file mode 100644
index 0000000000000..5339d75660d4b
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr function_ref(const function_ref&) noexcept = default;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_copy_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_copy_constructible_v<std::function_ref<void() const noexcept>>);
+
+constexpr double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  constexpr int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::constant_arg<[] {}>);
+    auto f2 = f;
+    f2();
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::constant_arg<[] { return 42; }>);
+    auto f2 = f;
+
+    assert(f2() == 42);
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::constant_arg<&f1>);
+    auto f2 = f;
+    assert(f2(1, 2.0) == 3.0);
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::constant_arg<&f1>);
+    auto f2 = f;
+    assert(f2(1, 2.0) == 3.0);
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::constant_arg<NeedsConversion{}>);
+    auto f_copy = f;
+    assert(f_copy(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f2(std::constant_arg<NeedsConversion{}>);
+    auto f2_copy = f2;
+    assert(f2_copy(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<NeedsConversion{}>);
+    auto f3_copy = f3;
+    assert(f3_copy(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<NeedsConversion{}>);
+    auto f4_copy = f4;
+    assert(f4_copy(1, 2, 3).i == 6);
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::constant_arg<&needs_conversion>);
+    auto f_copy = f;
+    assert(f_copy(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f2(std::constant_arg<&needs_conversion>);
+    auto f2_copy = f2;
+    assert(f2_copy(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<&needs_conversion>);
+    auto f3_copy = f3;
+    assert(f3_copy(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<&needs_conversion>);
+    auto f4_copy = f4;
+    assert(f4_copy(1, 2, 3).i == 6);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
new file mode 100644
index 0000000000000..c91da893db377
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/copy_assign.pass.cpp
@@ -0,0 +1,135 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// constexpr function_ref& operator=(const function_ref&) noexcept = default;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_copy_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_copy_assignable_v<std::function_ref<void() const noexcept>>);
+
+constexpr double plus(int x, double y) noexcept { return x + y; }
+constexpr double minus(int x, double y) noexcept { return x - y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  constexpr int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+constexpr int zero(Int, Int, Int) noexcept { return 0; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::constant_arg<[] {}>);
+    std::function_ref<void()> f2(std::constant_arg<[] {}>);
+    f2 = f;
+    f();
+    f2();
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::constant_arg<[] { return 42; }>);
+    std::function_ref<int() const> f2(std::constant_arg<[] { return 41; }>);
+    f2 = f;
+
+    assert(f() == 42);
+    assert(f2() == 42);
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::constant_arg<&plus>);
+    std::function_ref<double(int, double) noexcept> f2(std::constant_arg<&minus>);
+    f2 = f;
+    assert(f(1, 2.0) == 3.0);
+    assert(f2(1, 2.0) == 3.0);
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::constant_arg<&plus>);
+    std::function_ref<double(int, double) const noexcept> f2(std::constant_arg<&minus>);
+    f2 = f;
+    assert(f(1, 2.0) == 3.0);
+    assert(f2(1, 2.0) == 3.0);
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::constant_arg<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int)> f2(std::constant_arg<NeedsConversion{}>);
+    f = f2;
+    assert(f(1, 2, 3).i == 6);
+    assert(f2(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f_const(std::constant_arg<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::constant_arg<NeedsConversion{}>);
+    f_const = f2_const;
+    assert(f_const(1, 2, 3).i == 6);
+    assert(f2_const(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(
+        std::constant_arg<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::constant_arg<NeedsConversion{}>);
+    f_noexcept = f2_noexcept;
+    assert(f_noexcept(1, 2, 3).i == 6);
+    assert(f2_noexcept(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
+        std::constant_arg<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::constant_arg<NeedsConversion{}>);
+    f_const_noexcept = f2_const_noexcept;
+    assert(f_const_noexcept(1, 2, 3).i == 6);
+    assert(f2_const_noexcept(1, 2, 3).i == 6);
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::constant_arg<&zero>);
+    std::function_ref<Int(int, int, int)> f2(std::constant_arg<&needs_conversion>);
+    f = f2;
+    assert(f(1, 2, 3).i == 6);
+    assert(f2(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f_const(std::constant_arg<&zero>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::constant_arg<&needs_conversion>);
+    f_const = f2_const;
+    assert(f_const(1, 2, 3).i == 6);
+    assert(f2_const(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::constant_arg<&zero>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::constant_arg<&needs_conversion>);
+    f_noexcept = f2_noexcept;
+    assert(f_noexcept(1, 2, 3).i == 6);
+    assert(f2_noexcept(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::constant_arg<&zero>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::constant_arg<&needs_conversion>);
+    f_const_noexcept = f2_const_noexcept;
+    assert(f_const_noexcept(1, 2, 3).i == 6);
+    assert(f2_const_noexcept(1, 2, 3).i == 6);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
new file mode 100644
index 0000000000000..56d81cb997329
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move.pass.cpp
@@ -0,0 +1,105 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_move_constructible_v<std::function_ref<void()>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() const>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_move_constructible_v<std::function_ref<void() const noexcept>>);
+
+constexpr double f1(int x, double y) noexcept { return x + y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  constexpr int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::constant_arg<[] {}>);
+    auto f2 = std::move(f);
+    f2();
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::constant_arg<[] { return 42; }>);
+    auto f2 = std::move(f);
+
+    assert(f2() == 42);
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::constant_arg<&f1>);
+    auto f2 = std::move(f);
+    assert(f2(1, 2.0) == 3.0);
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::constant_arg<&f1>);
+    auto f2 = std::move(f);
+    assert(f2(1, 2.0) == 3.0);
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::constant_arg<NeedsConversion{}>);
+    auto f_copy = std::move(f);
+    assert(f_copy(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f2(std::constant_arg<NeedsConversion{}>);
+    auto f2_copy = std::move(f2);
+    assert(f2_copy(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<NeedsConversion{}>);
+    auto f3_copy = std::move(f3);
+    assert(f3_copy(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<NeedsConversion{}>);
+    auto f4_copy = std::move(f4);
+    assert(f4_copy(1, 2, 3).i == 6);
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::constant_arg<&needs_conversion>);
+    auto f_copy = std::move(f);
+    assert(f_copy(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f2(std::constant_arg<&needs_conversion>);
+    auto f2_copy = std::move(f2);
+    assert(f2_copy(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<&needs_conversion>);
+    auto f3_copy = std::move(f3);
+    assert(f3_copy(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<&needs_conversion>);
+    auto f4_copy = std::move(f4);
+    assert(f4_copy(1, 2, 3).i == 6);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
new file mode 100644
index 0000000000000..45e6323dd2e52
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/move_assign.pass.cpp
@@ -0,0 +1,133 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+static_assert(std::is_move_assignable_v<std::function_ref<void()>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() const>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() noexcept>>);
+static_assert(std::is_move_assignable_v<std::function_ref<void() const noexcept>>);
+
+constexpr double plus(int x, double y) noexcept { return x + y; }
+constexpr double minus(int x, double y) noexcept { return x - y; }
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  constexpr int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+constexpr int zero(Int, Int, Int) noexcept { return 0; }
+
+constexpr bool test() {
+  {
+    std::function_ref<void()> f(std::constant_arg<[] {}>);
+    std::function_ref<void()> f2(std::constant_arg<[] {}>);
+    f2 = std::move(f);
+    f();
+    f2();
+  }
+  {
+    // const
+    std::function_ref<int() const> f(std::constant_arg<[] { return 42; }>);
+    std::function_ref<int() const> f2(std::constant_arg<[] { return 41; }>);
+    f2 = std::move(f);
+
+    assert(f() == 42);
+    assert(f2() == 42);
+  }
+  {
+    // noexcept
+    std::function_ref<double(int, double) noexcept> f(std::constant_arg<&plus>);
+    std::function_ref<double(int, double) noexcept> f2(std::constant_arg<&minus>);
+    f2 = std::move(f);
+    assert(f(1, 2.0) == 3.0);
+    assert(f2(1, 2.0) == 3.0);
+  }
+  {
+    // const noexcept
+    std::function_ref<double(int, double) const noexcept> f(std::constant_arg<&plus>);
+    std::function_ref<double(int, double) const noexcept> f2(std::constant_arg<&minus>);
+    f2 = std::move(f);
+    assert(f(1, 2.0) == 3.0);
+    assert(f2(1, 2.0) == 3.0);
+  }
+  {
+    // with conversions
+    std::function_ref<Int(int, int, int)> f(std::constant_arg<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int)> f2(std::constant_arg<NeedsConversion{}>);
+    f = std::move(f2);
+    assert(f(1, 2, 3).i == 6);
+    assert(f2(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f_const(std::constant_arg<[](int, int, int) { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::constant_arg<NeedsConversion{}>);
+    f_const = std::move(f2_const);
+    assert(f_const(1, 2, 3).i == 6);
+    assert(f2_const(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(
+        std::constant_arg<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::constant_arg<NeedsConversion{}>);
+    f_noexcept = std::move(f2_noexcept);
+    assert(f_noexcept(1, 2, 3).i == 6);
+    assert(f2_noexcept(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
+        std::constant_arg<[](int, int, int) noexcept { return Int{1}; }>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::constant_arg<NeedsConversion{}>);
+    f_const_noexcept = std::move(f2_const_noexcept);
+    assert(f_const_noexcept(1, 2, 3).i == 6);
+    assert(f2_const_noexcept(1, 2, 3).i == 6);
+  }
+  {
+    // with conversions function pointer
+    std::function_ref<Int(int, int, int)> f(std::constant_arg<&zero>);
+    std::function_ref<Int(int, int, int)> f2(std::constant_arg<&needs_conversion>);
+    f = std::move(f2);
+    assert(f(1, 2, 3).i == 6);
+    assert(f2(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const> f_const(std::constant_arg<&zero>);
+    std::function_ref<Int(int, int, int) const> f2_const(std::constant_arg<&needs_conversion>);
+    f_const = std::move(f2_const);
+    assert(f_const(1, 2, 3).i == 6);
+    assert(f2_const(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::constant_arg<&zero>);
+    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::constant_arg<&needs_conversion>);
+    f_noexcept = std::move(f2_noexcept);
+    assert(f_noexcept(1, 2, 3).i == 6);
+    assert(f2_noexcept(1, 2, 3).i == 6);
+
+    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::constant_arg<&zero>);
+    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::constant_arg<&needs_conversion>);
+    f_const_noexcept = std::move(f2_const_noexcept);
+    assert(f_const_noexcept(1, 2, 3).i == 6);
+    assert(f2_const_noexcept(1, 2, 3).i == 6);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
new file mode 100644
index 0000000000000..6b656c2d74c5d
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -0,0 +1,315 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// R operator()(ArgTypes... args) const noexcept(noex);
+
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "MoveOnly.h"
+
+template <class T, class... Args>
+concept ConstInvocable = requires(const T t, Args... args) {
+  { t(std::forward<Args>(args)...) };
+};
+
+template <class T, class... Args>
+concept ConstNoexceptInvocable = requires(const T t, Args... args) {
+  { t(std::forward<Args>(args)...) } noexcept;
+};
+
+static_assert(ConstInvocable<std::function_ref<void()>>);
+static_assert(!ConstNoexceptInvocable<std::function_ref<void()>>);
+
+static_assert(ConstInvocable<std::function_ref<void() noexcept>>);
+static_assert(ConstNoexceptInvocable<std::function_ref<void() noexcept>>);
+
+static_assert(ConstInvocable<std::function_ref<void() const>>);
+static_assert(!ConstNoexceptInvocable<std::function_ref<void() const>>);
+
+static_assert(ConstInvocable<std::function_ref<void() const noexcept>>);
+static_assert(ConstNoexceptInvocable<std::function_ref<void() const noexcept>>);
+
+struct S {
+  int data = 42;
+  int operator()(int& x) const noexcept { return data + x; }
+  int operator()(const int& x) const noexcept { return data + x + 1; }
+  int operator()(int&& x) const noexcept { return data + x + 2; }
+  int operator()(const int&& x) const noexcept { return data + x + 3; }
+};
+
+struct S2 {
+  double operator()(int x, int y, int z) noexcept { return x + y + z; }
+
+  double operator()(int x, int y, int z) const noexcept { return x + y + z + 1; }
+};
+
+void test_default() {
+  {
+    std::function_ref<void()> f = [] {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&)> f(std::constant_arg<[](int& x) -> int& { return x; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly)> f(std::constant_arg<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int)> f(std::constant_arg<[](int x) -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&)> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&)> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&)> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&)> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int)> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 6.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 6.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int)> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+}
+
+void test_const() {
+  {
+    std::function_ref<void() const> f = [] {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) const> f(std::constant_arg<[](int& x) -> int& { return x; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) const> f(
+        std::constant_arg<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) const> f(std::constant_arg<[](int x) -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) const> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) const> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) const> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) const> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) const> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 7.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 7.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) const> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+}
+
+void test_noexcept() {
+  {
+    std::function_ref<void() noexcept> f = [] noexcept {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) noexcept> f(std::constant_arg<[](int& x) noexcept -> int& { return x; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) noexcept> f(
+        std::constant_arg<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) noexcept> f(std::constant_arg<[](int x) noexcept -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) noexcept> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) noexcept> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) noexcept> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) noexcept> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) noexcept> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 6.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 6.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) noexcept> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+}
+
+void test_const_noexcept() {
+  {
+    std::function_ref<void() const noexcept> f = [] noexcept {};
+    f();
+    static_assert(std::is_void_v<decltype(f())>);
+  }
+  {
+    // reference
+    int x = 42;
+    std::function_ref<int&(int&) const noexcept> f(std::constant_arg<[](int& x) noexcept -> int& { return x; }>);
+    std::same_as<int&> decltype(auto) r = f(x);
+    assert(&r == &x);
+  }
+  {
+    // Move only
+    std::function_ref<MoveOnly(MoveOnly) const noexcept> f(
+        std::constant_arg<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
+    std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
+    assert(r.get() == 6);
+  }
+  {
+    // different return type
+    std::function_ref<int(int) const noexcept> f(std::constant_arg<[](int x) noexcept -> double { return x + 0.1; }>);
+    std::same_as<int> decltype(auto) r = f(5);
+    assert(r == 5);
+  }
+  {
+    // Args overload resolution
+    S s;
+    int x = 1;
+    std::function_ref<int(int&) const noexcept> f(s);
+    assert(f(x) == 43);
+
+    std::function_ref<int(const int&) const noexcept> g(s);
+    assert(g(x) == 44);
+
+    std::function_ref<int(int&&) const noexcept> h(s);
+    assert(h(std::move(x)) == 45);
+
+    std::function_ref<int(const int&&) const noexcept> i(s);
+    assert(i(std::move(x)) == 46);
+  }
+  {
+    // const overload
+    S2 s;
+    std::function_ref<double(int, int, int) const noexcept> f(s);
+    std::same_as<double> decltype(auto) r = f(1, 2, 3);
+    assert(r == 7.0);
+
+    std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
+    assert(r2 == 7.0);
+
+    const S2 s2;
+    std::function_ref<double(int, int, int) const noexcept> f2(s2);
+    std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
+    assert(r3 == 7.0);
+
+    std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
+    assert(r4 == 7.0);
+  }
+}
+
+void test() {
+  test_default();
+  test_const();
+  test_noexcept();
+  test_const_noexcept();
+}
+
+int main(int, char**) {
+  test();
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp
deleted file mode 100644
index 26a32e91adfd7..0000000000000
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const.pass.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-#include "test_macros.h"
-
-static_assert(std::is_invocable_v<std::function_ref<void() const>&>);
-static_assert(std::is_invocable_v<std::function_ref<void() const>>);
-static_assert(std::is_invocable_v<std::function_ref<void() const>&&>);
-static_assert(std::is_invocable_v<std::function_ref<void() const> const&>);
-static_assert(std::is_invocable_v<std::function_ref<void() const> const>);
-static_assert(std::is_invocable_v<std::function_ref<void() const> const&&>);
-
-int fn() { return 42; }
-
-struct {
-  int operator()() const { return 42; }
-} fn_obj;
-
-void test() {
-  // template<class F> function_ref(F* f) noexcept;
-  {
-    // initialized from a function
-    std::function_ref<int() const> fn_ref = fn;
-    assert(fn_ref() == 42);
-  }
-  {
-    // initialized from a function pointer
-    std::function_ref<int() const> fn_ref = &fn;
-    assert(fn_ref() == 42);
-  }
-
-  // template<class F> constexpr function_ref(F&& f) noexcept;
-  {
-    // initialized from a function object
-    std::function_ref<int() const> fn_ref = fn_obj;
-    assert(fn_ref() == 42);
-  }
-}
-
-struct S {
-  int data_mem = 42;
-
-  int fn_mem() const { return 42; }
-};
-
-void test_constant_arg_t() {
-  // template<auto f> constexpr function_ref(constant_arg_t<f>) noexcept;
-  {
-    // initialized from a function through `constant_arg_t`
-    std::function_ref<int() const> fn_ref = std::constant_arg_t<fn>();
-    assert(fn_ref() == 42);
-  }
-  {
-    // initialized from a function pointer through `constant_arg_t`
-    std::function_ref<int() const> fn_ref = std::constant_arg_t<&fn>();
-    assert(fn_ref() == 42);
-  }
-  {
-    // initialized from a function object through `constant_arg_t`
-    std::function_ref<int() const> fn_ref = std::constant_arg_t<fn_obj>();
-    assert(fn_ref() == 42);
-  }
-  {
-    S s;
-    // initialized from a pointer to data member through `constant_arg_t`
-    std::function_ref<int(S) const> fn_ref = std::constant_arg_t<&S::data_mem>();
-    assert(fn_ref(s) == 42);
-  }
-  {
-    S s;
-    // initialized from a pointer to function member through `constant_arg_t`
-    std::function_ref<int(S) const> fn_ref = std::constant_arg_t<&S::fn_mem>();
-    assert(fn_ref(s) == 42);
-  }
-
-  // template<auto f, class U>
-  //   constexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
-  {
-    S s;
-    // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a reference
-    std::function_ref<int() const> fn_ref = {std::constant_arg_t<&S::data_mem>(), s};
-    assert(fn_ref() == 42);
-  }
-  {
-    S s;
-    // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a reference
-    std::function_ref<int() const> fn_ref = {std::constant_arg_t<&S::fn_mem>(), s};
-    assert(fn_ref() == 42);
-  }
-
-  // template<auto f, class T>
-  //   constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
-  {
-    S s;
-    // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a pointer
-    std::function_ref<int() const> fn_ref = {std::constant_arg_t<&S::data_mem>(), &s};
-    assert(fn_ref() == 42);
-  }
-  {
-    S s;
-    // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a pointer
-    static_assert(std::is_same_v<decltype(&s), S*>);
-    std::function_ref<int() const> fn_ref = {std::constant_arg_t<&S::fn_mem>(), &s};
-    assert(fn_ref() == 42);
-  }
-}
-
-int main(int, char**) {
-  test();
-  test_constant_arg_t();
-  return 0;
-}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp
deleted file mode 100644
index 26ace9c496b88..0000000000000
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/const_noexcept.pass.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-#include "test_macros.h"
-
-static_assert(std::is_invocable_v<std::function_ref<void() const noexcept>&>);
-static_assert(std::is_invocable_v<std::function_ref<void() const noexcept>>);
-static_assert(std::is_invocable_v<std::function_ref<void() const noexcept>&&>);
-static_assert(std::is_invocable_v<std::function_ref<void() const noexcept> const&>);
-static_assert(std::is_invocable_v<std::function_ref<void() const noexcept> const >);
-static_assert(std::is_invocable_v<std::function_ref<void() const noexcept> const&&>);
-
-int fn() noexcept { return 42; }
-
-struct {
-  int operator()() const noexcept { return 42; }
-} fn_obj;
-
-void test() {
-  // template<class F> function_ref(F* f) noexcept;
-  {
-    // initialized from a function
-    std::function_ref<int() const noexcept> fn_ref = fn;
-    assert(fn_ref() == 42);
-  }
-  {
-    // initialized from a function pointer
-    std::function_ref<int() const noexcept> fn_ref = &fn;
-    assert(fn_ref() == 42);
-  }
-
-  // template<class F> const noexceptexpr function_ref(F&& f) noexcept;
-  {
-    // initialized from a function object
-    std::function_ref<int() const noexcept> fn_ref = fn_obj;
-    assert(fn_ref() == 42);
-  }
-}
-
-struct S {
-  int data_mem = 42;
-
-  int fn_mem() const noexcept { return 42; }
-};
-
-void test_constant_arg_t() {
-  // template<auto f> const noexceptexpr function_ref(constant_arg_t<f>) noexcept;
-  {
-    // initialized from a function through `constant_arg_t`
-    std::function_ref<int() const noexcept> fn_ref = std::constant_arg_t<fn>();
-    assert(fn_ref() == 42);
-  }
-  {
-    // initialized from a function pointer through `constant_arg_t`
-    std::function_ref<int() const noexcept> fn_ref = std::constant_arg_t<&fn>();
-    assert(fn_ref() == 42);
-  }
-  {
-    // initialized from a function object through `constant_arg_t`
-    std::function_ref<int() const noexcept> fn_ref = std::constant_arg_t<fn_obj>();
-    assert(fn_ref() == 42);
-  }
-  {
-    S s;
-    // initialized from a pointer to data member through `constant_arg_t`
-    std::function_ref<int(S) const noexcept> fn_ref = std::constant_arg_t<&S::data_mem>();
-    assert(fn_ref(s) == 42);
-  }
-  {
-    S s;
-    // initialized from a pointer to function member through `constant_arg_t`
-    std::function_ref<int(S) const noexcept> fn_ref = std::constant_arg_t<&S::fn_mem>();
-    assert(fn_ref(s) == 42);
-  }
-
-  // template<auto f, class U>
-  //   const noexceptexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
-  {
-    S s;
-    // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a reference
-    std::function_ref<int() const noexcept> fn_ref = {std::constant_arg_t<&S::data_mem>(), s};
-    assert(fn_ref() == 42);
-  }
-  {
-    S s;
-    // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a reference
-    std::function_ref<int() const noexcept> fn_ref = {std::constant_arg_t<&S::fn_mem>(), s};
-    assert(fn_ref() == 42);
-  }
-
-  // template<auto f, class T>
-  //   const noexceptexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
-  {
-    S s;
-    // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a pointer
-    std::function_ref<int() const noexcept> fn_ref = {std::constant_arg_t<&S::data_mem>(), &s};
-    assert(fn_ref() == 42);
-  }
-  {
-    S s;
-    // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a pointer
-    static_assert(std::is_same_v<decltype(&s), S*>);
-    std::function_ref<int() const noexcept> fn_ref = {std::constant_arg_t<&S::fn_mem>(), &s};
-    assert(fn_ref() == 42);
-  }
-}
-
-int main(int, char**) {
-  test();
-  test_constant_arg_t();
-  return 0;
-}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
deleted file mode 100644
index e73e778313dec..0000000000000
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/default.pass.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++26
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-#include "test_macros.h"
-
-static_assert(std::is_invocable_v<std::function_ref<void()>&>);
-static_assert(std::is_invocable_v<std::function_ref<void()>>);
-static_assert(std::is_invocable_v<std::function_ref<void()>&&>);
-static_assert(std::is_invocable_v<std::function_ref<void()> const&>);
-static_assert(std::is_invocable_v<std::function_ref<void()> const>);
-static_assert(std::is_invocable_v<std::function_ref<void()> const&&>);
-
-int fn() { return 42; }
-
-struct {
-  int operator()() { return 42; }
-} fn_obj;
-
-void test() {
-  // template<class F> function_ref(F* f) noexcept;
-  {
-    // initialized from a function
-    std::function_ref<int()> fn_ref = fn;
-    assert(fn_ref() == 42);
-  }
-  {
-    // initialized from a function pointer
-    std::function_ref<int()> fn_ref = &fn;
-    assert(fn_ref() == 42);
-  }
-
-  // template<class F> constexpr function_ref(F&& f) noexcept;
-  {
-    // initialized from a function object
-    std::function_ref<int()> fn_ref = fn_obj;
-    assert(fn_ref() == 42);
-  }
-}
-
-struct S {
-  int data_mem = 42;
-
-  int fn_mem() { return 42; }
-};
-
-void test_constant_arg_t() {
-  // template<auto f> constexpr function_ref(constant_arg_t<f>) noexcept;
-  {
-    // initialized from a function through `constant_arg_t`
-    std::function_ref<int()> fn_ref = std::constant_arg_t<fn>();
-    assert(fn_ref() == 42);
-  }
-  {
-    // initialized from a function pointer through `constant_arg_t`
-    std::function_ref<int()> fn_ref = std::constant_arg_t<&fn>();
-    assert(fn_ref() == 42);
-  }
-  {
-    S s;
-    // initialized from a pointer to data member through `constant_arg_t`
-    std::function_ref<int(S)> fn_ref = std::constant_arg_t<&S::data_mem>();
-    assert(fn_ref(s) == 42);
-  }
-  {
-    S s;
-    // initialized from a pointer to function member through `constant_arg_t`
-    std::function_ref<int(S)> fn_ref = std::constant_arg_t<&S::fn_mem>();
-    assert(fn_ref(s) == 42);
-  }
-
-  // template<auto f, class U>
-  //   constexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
-  {
-    S s;
-    // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a reference
-    std::function_ref<int()> fn_ref = {std::constant_arg_t<&S::data_mem>(), s};
-    assert(fn_ref() == 42);
-  }
-  {
-    S s;
-    // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a reference
-    std::function_ref<int()> fn_ref = {std::constant_arg_t<&S::fn_mem>(), s};
-    assert(fn_ref() == 42);
-  }
-
-  // template<auto f, class T>
-  //   constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
-  {
-    S s;
-    // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a pointer
-    std::function_ref<int()> fn_ref = {std::constant_arg_t<&S::data_mem>(), &s};
-    assert(fn_ref() == 42);
-  }
-  {
-    S s;
-    // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a pointer
-    static_assert(std::is_same_v<decltype(&s), S*>);
-    std::function_ref<int()> fn_ref = {std::constant_arg_t<&S::fn_mem>(), &s};
-    assert(fn_ref() == 42);
-  }
-}
-
-int main(int, char**) {
-  test();
-  test_constant_arg_t();
-  return 0;
-}
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp
deleted file mode 100644
index 7c947143ec0bd..0000000000000
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/invoke/noexcept.pass.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
-
-#include <cassert>
-#include <functional>
-#include <utility>
-#include <type_traits>
-
-#include "test_macros.h"
-
-static_assert(std::is_invocable_v<std::function_ref<void() noexcept>&>);
-static_assert(std::is_invocable_v<std::function_ref<void() noexcept>>);
-static_assert(std::is_invocable_v<std::function_ref<void() noexcept>&&>);
-static_assert(std::is_invocable_v<std::function_ref<void() noexcept> const&>);
-static_assert(std::is_invocable_v<std::function_ref<void() noexcept> const>);
-static_assert(std::is_invocable_v<std::function_ref<void() noexcept> const&&>);
-
-int fn() noexcept { return 42; }
-
-struct {
-  int operator()() noexcept { return 42; }
-} fn_obj;
-
-void test() {
-  // template<class F> function_ref(F* f) noexcept;
-  {
-    // initialized from a function
-    std::function_ref<int() noexcept> fn_ref = fn;
-    assert(fn_ref() == 42);
-  }
-  {
-    // initialized from a function pointer
-    std::function_ref<int() noexcept> fn_ref = &fn;
-    assert(fn_ref() == 42);
-  }
-
-  // template<class F> constexpr function_ref(F&& f) noexcept;
-  {
-    // initialized from a function object
-    std::function_ref<int() noexcept> fn_ref = fn_obj;
-    assert(fn_ref() == 42);
-  }
-}
-
-struct S {
-  int data_mem = 42;
-
-  int fn_mem() noexcept { return 42; }
-};
-
-void test_constant_arg_t() {
-  // template<auto f> constexpr function_ref(constant_arg_t<f>) noexcept;
-  {
-    // initialized from a function through `constant_arg_t`
-    std::function_ref<int() noexcept> fn_ref = std::constant_arg_t<fn>();
-    assert(fn_ref() == 42);
-  }
-  {
-    // initialized from a function pointer through `constant_arg_t`
-    std::function_ref<int() noexcept> fn_ref = std::constant_arg_t<&fn>();
-    assert(fn_ref() == 42);
-  }
-  {
-    S s;
-    // initialized from a pointer to data member through `constant_arg_t`
-    std::function_ref<int(S) noexcept> fn_ref = std::constant_arg_t<&S::data_mem>();
-    assert(fn_ref(s) == 42);
-  }
-  {
-    S s;
-    // initialized from a pointer to function member through `constant_arg_t`
-    std::function_ref<int(S) noexcept> fn_ref = std::constant_arg_t<&S::fn_mem>();
-    assert(fn_ref(s) == 42);
-  }
-
-  // template<auto f, class U>
-  //   constexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
-  {
-    S s;
-    // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a reference
-    std::function_ref<int() noexcept> fn_ref = {std::constant_arg_t<&S::data_mem>(), s};
-    assert(fn_ref() == 42);
-  }
-  {
-    S s;
-    // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a reference
-    std::function_ref<int() noexcept> fn_ref = {std::constant_arg_t<&S::fn_mem>(), s};
-    assert(fn_ref() == 42);
-  }
-
-  // template<auto f, class T>
-  //   constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
-  {
-    S s;
-    // initialized from a pointer to data member through `constant_arg_t` and bound to an object through a pointer
-    std::function_ref<int() noexcept> fn_ref = {std::constant_arg_t<&S::data_mem>(), &s};
-    assert(fn_ref() == 42);
-  }
-  {
-    S s;
-    // initialized from a pointer to function member through `constant_arg_t` and bound to an object through a pointer
-    static_assert(std::is_same_v<decltype(&s), S*>);
-    std::function_ref<int() noexcept> fn_ref = {std::constant_arg_t<&S::fn_mem>(), &s};
-    assert(fn_ref() == 42);
-  }
-}
-
-int main(int, char**) {
-  test();
-  test_constant_arg_t();
-  return 0;
-}

>From d8bc187b51e6d2e6e2272ad8b9ce870e526a454e Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 15 Mar 2026 14:45:09 +0000
Subject: [PATCH 21/26] format

---
 libcxx/include/__functional/function_ref.h      | 13 ++++++-------
 libcxx/include/__functional/function_ref_impl.h |  3 ++-
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__functional/function_ref.h b/libcxx/include/__functional/function_ref.h
index 864e921e37824..b7fd3a4a2c79f 100644
--- a/libcxx/include/__functional/function_ref.h
+++ b/libcxx/include/__functional/function_ref.h
@@ -19,14 +19,13 @@
 #if _LIBCPP_STD_VER >= 26
 // NOLINTBEGIN(readability-duplicate-include)
 
-#define _LIBCPP_FUNCTION_REF_CV
-#include <__functional/function_ref_impl.h>
-#undef _LIBCPP_FUNCTION_REF_CV
+#  define _LIBCPP_FUNCTION_REF_CV
+#  include <__functional/function_ref_impl.h>
+#  undef _LIBCPP_FUNCTION_REF_CV
 
-
-#define _LIBCPP_FUNCTION_REF_CV const
-#include <__functional/function_ref_impl.h>
-#undef _LIBCPP_FUNCTION_REF_CV
+#  define _LIBCPP_FUNCTION_REF_CV const
+#  include <__functional/function_ref_impl.h>
+#  undef _LIBCPP_FUNCTION_REF_CV
 
 // NOLINTEND(readability-duplicate-include)
 #endif // _LIBCPP_STD_VER >= 26
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index f0c7c40765013..97bc18baae3ee 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -54,7 +54,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
 
   // use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
   // pointers
-  // todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be supported
+  // todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
+  // supported
   union __storage_t {
     void* __obj_ptr_;
     void const* __obj_const_ptr_;

>From e221d5629e6c914abb7a3e39b639945178d8fc41 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 15 Mar 2026 18:42:30 +0000
Subject: [PATCH 22/26] constexpr function ptr

---
 .../include/__functional/function_ref_impl.h  | 32 +++++++++++++++++--
 .../func.wrap.ref.ctor/function_ptr.pass.cpp  | 17 +++++++---
 2 files changed, 41 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 97bc18baae3ee..d6a963644926f 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -59,6 +59,8 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   union __storage_t {
     void* __obj_ptr_;
     void const* __obj_const_ptr_;
+    _Rp (*__fn_ptr_)(_ArgTypes...);
+    _Rp (*__fn_ptr_noexcept_)(_ArgTypes...) noexcept;
 
     _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t() noexcept : __obj_ptr_(nullptr) {}
 
@@ -72,7 +74,19 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
         }
       } else {
         static_assert(is_function_v<_Tp>);
-        __obj_ptr_ = reinterpret_cast<void*>(__ptr);
+        if constexpr (is_nothrow_invocable_v<_Tp, _ArgTypes...>) {
+          if constexpr (requires { __fn_ptr_noexcept_ = __ptr; }) {
+            __fn_ptr_noexcept_ = __ptr;
+          } else {
+            __obj_ptr_ = reinterpret_cast<void*>(__ptr);
+          }
+        } else {
+          if constexpr (requires { __fn_ptr_ = __ptr; }) {
+            __fn_ptr_ = __ptr;
+          } else {
+            __obj_ptr_ = reinterpret_cast<void*>(__ptr);
+          }
+        }
       }
     }
   };
@@ -87,7 +101,19 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       }
     } else {
       static_assert(is_function_v<_Tp>);
-      return reinterpret_cast<_Tp*>(__storage.__obj_ptr_);
+      if constexpr (is_nothrow_invocable_v<_Tp, _ArgTypes...>) {
+        if constexpr (requires(_Tp* __ptr) { __storage.__fn_ptr_noexcept_ = __ptr; }) {
+          return __storage.__fn_ptr_noexcept_;
+        } else {
+          return reinterpret_cast<_Tp*>(__storage.__obj_ptr_);
+        }
+      } else {
+        if constexpr (requires(_Tp* __ptr) { __storage.__fn_ptr_ = __ptr; }) {
+          return __storage.__fn_ptr_;
+        } else {
+          return reinterpret_cast<_Tp*>(__storage.__obj_ptr_);
+        }
+      }
     }
   }
 
@@ -99,7 +125,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
 public:
   template <class _Fp>
     requires is_function_v<_Fp> && __is_invocable_using<_Fp>
-  _LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fp* __fn_ptr) noexcept
       : __storage_(__fn_ptr),
         __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
           return __get<_Fp>(__storage)(std::forward<_ArgTypes>(__args)...);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
index 22835c48ebcf9..3c7053709458f 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -76,18 +76,18 @@ static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (
 static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
                                               void (*)(int, double) noexcept>);
 
-int fn() { return 42; }
+constexpr int fn() { return 42; }
 
-int fn_noexcept(int i, A a) noexcept { return i + a.i; }
+constexpr int fn_noexcept(int i, A a) noexcept { return i + a.i; }
 
 struct Int {
   int i;
-  Int(int ii) noexcept : i(ii) {}
+  constexpr Int(int ii) noexcept : i(ii) {}
 };
 
-int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
 
-void test() {
+constexpr bool test() {
   {
     // simple case
     std::function_ref<int()> f(&fn);
@@ -118,6 +118,11 @@ void test() {
     std::function_ref<int(int, A) const noexcept> f(&fn_noexcept);
     assert(f(4, A{5}) == 9);
   }
+
+  return true;
+}
+
+void non_constexpr_test() {
   {
     std::function_ref<Int(int, int, int)> f(&needs_conversion);
     assert(f(1, 2, 3).i == 6);
@@ -135,5 +140,7 @@ void test() {
 
 int main(int, char**) {
   test();
+  non_constexpr_test();
+  static_assert(test());
   return 0;
 }

>From 6f1e9fac3061b16d89f23ce3c072a8b4ee1ab0ee Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Tue, 24 Mar 2026 07:31:20 +0000
Subject: [PATCH 23/26] double unwrapping

---
 .../__functional/function_ref_common.h        | 73 ++++++++++++++
 .../include/__functional/function_ref_impl.h  | 94 +++++--------------
 .../func.wrap.ref.ctor/assign.delete.pass.cpp | 16 ++--
 .../func.wrap.ref.ctor/function_ptr.pass.cpp  |  9 ++
 .../func.wrap.ref.ctor/ref.pass.cpp           | 37 ++++++++
 5 files changed, 152 insertions(+), 77 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index a757d0d308bc5..0b1aa207ca589 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -11,6 +11,7 @@
 
 #include <__config>
 #include <__type_traits/invoke.h>
+#include <__type_traits/is_const.h>
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_object.h>
 #include <__type_traits/remove_pointer.h>
@@ -27,6 +28,78 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
+template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization : false_type {};
+
+  // use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
+  // pointers
+  // todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
+  // supported
+  template <class _Rp, class... _ArgTypes>
+  union __storage_func_ref_t {
+    void* __obj_ptr_;
+    void const* __obj_const_ptr_;
+    _Rp (*__fn_ptr_)(_ArgTypes...);
+    _Rp (*__fn_ptr_noexcept_)(_ArgTypes...) noexcept;
+    void(* __fn_ptr_type_erased_)();
+
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t() noexcept : __obj_ptr_(nullptr) {}
+
+    template <class _Tp>
+    _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t(_Tp* __ptr) noexcept {
+      if constexpr (is_object_v<_Tp>) {
+        if constexpr (is_const_v<_Tp>) {
+          __obj_const_ptr_ = __ptr;
+        } else {
+          __obj_ptr_ = __ptr;
+        }
+      } else {
+        static_assert(is_function_v<_Tp>);
+        if constexpr (is_nothrow_invocable_v<_Tp, _ArgTypes...>) {
+          if constexpr (requires { __fn_ptr_noexcept_ = __ptr; }) {
+            __fn_ptr_noexcept_ = __ptr;
+          } else {
+            __fn_ptr_type_erased_ = reinterpret_cast<void(*)()>(__ptr);
+          }
+        } else {
+          if constexpr (requires { __fn_ptr_ = __ptr; }) {
+            __fn_ptr_ = __ptr;
+          } else {
+            __fn_ptr_type_erased_ = reinterpret_cast<void(*)()>(__ptr);
+          }
+        }
+      }
+    }
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_func_ref_t __storage) {
+    if constexpr (is_object_v<_Tp>) {
+      if constexpr (is_const_v<_Tp>) {
+        return static_cast<_Tp*>(__storage.__obj_const_ptr_);
+      } else {
+        return static_cast<_Tp*>(__storage.__obj_ptr_);
+      }
+    } else {
+      static_assert(is_function_v<_Tp>);
+      if constexpr (is_nothrow_invocable_v<_Tp, _ArgTypes...>) {
+        if constexpr (requires(_Tp* __ptr) { __storage.__fn_ptr_noexcept_ = __ptr; }) {
+          return __storage.__fn_ptr_noexcept_;
+        } else {
+          return reinterpret_cast<_Tp*>(__storage.__fn_ptr_type_erased_);
+        }
+      } else {
+        if constexpr (requires(_Tp* __ptr) { __storage.__fn_ptr_ = __ptr; }) {
+          return __storage.__fn_ptr_;
+        } else {
+          return reinterpret_cast<_Tp*>(__storage.__fn_ptr_type_erased_);
+        }
+      }
+    }
+  }
+  };
+
+
+
 template <class _Fp, class _Tp>
 struct __function_ref_bind {};
 
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index d6a963644926f..78c5b6a357357 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -9,6 +9,7 @@
 // This header is unguarded on purpose. This header is an implementation detail of function_ref.h
 // and generates multiple versions of std::function_ref
 
+#include "__functional/function_ref_common.h"
 #include <__assert>
 #include <__config>
 #include <__functional/invoke.h>
@@ -28,6 +29,7 @@
 #include <__type_traits/remove_reference.h>
 #include <__utility/constant_arg.h>
 #include <__utility/forward.h>
+#include <type_traits>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -44,6 +46,10 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
+template <bool _NoExcept2,  bool _NoExcept1, class _Rp, class... _ArgTypes>
+struct __is_convertible_from_specialization<function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept2)>, _NoExcept1, _Rp, _ArgTypes...>
+ : is_convertible<_Rp(&)(_ArgTypes...) noexcept(_NoExcept2), _Rp(&)(_ArgTypes...) noexcept(_NoExcept1)> {};
+
 template <class _Rp, class... _ArgTypes, bool __is_noexcept>
 class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
 private:
@@ -52,70 +58,13 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       _If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
           value;
 
-  // use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
-  // pointers
-  // todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
-  // supported
-  union __storage_t {
-    void* __obj_ptr_;
-    void const* __obj_const_ptr_;
-    _Rp (*__fn_ptr_)(_ArgTypes...);
-    _Rp (*__fn_ptr_noexcept_)(_ArgTypes...) noexcept;
-
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t() noexcept : __obj_ptr_(nullptr) {}
-
-    template <class _Tp>
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_t(_Tp* __ptr) noexcept {
-      if constexpr (is_object_v<_Tp>) {
-        if constexpr (is_const_v<_Tp>) {
-          __obj_const_ptr_ = __ptr;
-        } else {
-          __obj_ptr_ = __ptr;
-        }
-      } else {
-        static_assert(is_function_v<_Tp>);
-        if constexpr (is_nothrow_invocable_v<_Tp, _ArgTypes...>) {
-          if constexpr (requires { __fn_ptr_noexcept_ = __ptr; }) {
-            __fn_ptr_noexcept_ = __ptr;
-          } else {
-            __obj_ptr_ = reinterpret_cast<void*>(__ptr);
-          }
-        } else {
-          if constexpr (requires { __fn_ptr_ = __ptr; }) {
-            __fn_ptr_ = __ptr;
-          } else {
-            __obj_ptr_ = reinterpret_cast<void*>(__ptr);
-          }
-        }
-      }
-    }
-  };
+  template <class _Fn>
+  static constexpr bool __is_convertible_from_specialization_v = __is_convertible_from_specialization<_Fn, __is_noexcept, _Rp, _ArgTypes...>::value;
 
-  template <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_t __storage) {
-    if constexpr (is_object_v<_Tp>) {
-      if constexpr (is_const_v<_Tp>) {
-        return static_cast<_Tp*>(__storage.__obj_const_ptr_);
-      } else {
-        return static_cast<_Tp*>(__storage.__obj_ptr_);
-      }
-    } else {
-      static_assert(is_function_v<_Tp>);
-      if constexpr (is_nothrow_invocable_v<_Tp, _ArgTypes...>) {
-        if constexpr (requires(_Tp* __ptr) { __storage.__fn_ptr_noexcept_ = __ptr; }) {
-          return __storage.__fn_ptr_noexcept_;
-        } else {
-          return reinterpret_cast<_Tp*>(__storage.__obj_ptr_);
-        }
-      } else {
-        if constexpr (requires(_Tp* __ptr) { __storage.__fn_ptr_ = __ptr; }) {
-          return __storage.__fn_ptr_;
-        } else {
-          return reinterpret_cast<_Tp*>(__storage.__obj_ptr_);
-        }
-      }
-    }
-  }
+  template<class... _Tp>
+  friend class function_ref;
+
+  using __storage_t _LIBCPP_NODEBUG = __storage_func_ref_t<_Rp, _ArgTypes...>;
 
   __storage_t __storage_;
 
@@ -128,21 +77,28 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fp* __fn_ptr) noexcept
       : __storage_(__fn_ptr),
         __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
-          return __get<_Fp>(__storage)(std::forward<_ArgTypes>(__args)...);
+          return __storage_t::template __get<_Fp>(__storage)(std::forward<_ArgTypes>(__args)...);
         }) {
     _LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
   }
 
   template <class _Fn, class _Tp = remove_reference_t<_Fn>>
     requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
-             __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&>)
+             __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
       : __storage_(std::addressof(__obj)),
         __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
-          _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__storage_t::template __get<_Tp>(__storage);
           return __obj(std::forward<_ArgTypes>(__args)...);
         }) {}
 
+  template <class _Fn, class _Tp = remove_reference_t<_Fn>>
+    requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
+             __is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && __is_convertible_from_specialization_v<_Tp>)
+  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
+      : __storage_(__obj.__storage_),
+        __call_(__obj.__call_) {}
+
   template <auto _Fn>
     requires __is_invocable_using<const decltype(_Fn)&>
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>) noexcept
@@ -159,7 +115,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _Up&& __obj) noexcept
       : __storage_(std::addressof(__obj)),
         __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
-          _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__get<_Tp>(__storage);
+          _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__storage_t::template __get<_Tp>(__storage);
           return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
         }) {
     if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
@@ -172,7 +128,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr_) noexcept
       : __storage_(__obj_ptr_),
         __call_([](__storage_t __storage, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
-          auto __obj = __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
+          auto __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
           return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
         }) {
     if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
@@ -189,7 +145,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref& operator=(const function_ref&) noexcept = default;
 
   template <class _Tp>
-    requires(!is_same_v<_Tp, function_ref>) && (!is_pointer_v<_Tp>) && (!__is_constant_arg_t<_Tp>)
+    requires(!__is_convertible_from_specialization_v<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_constant_arg_t<_Tp>)
   _LIBCPP_HIDE_FROM_ABI function_ref& operator=(_Tp) = delete;
 
   _LIBCPP_HIDE_FROM_ABI constexpr _Rp operator()(_ArgTypes... __args) const noexcept(__is_noexcept) {
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
index 956b34695f71a..81d1e79a5499f 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/assign.delete.pass.cpp
@@ -24,9 +24,9 @@
 
 // non const noexcept(false)
 static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void()>>);
-static_assert(!std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const>>);
-static_assert(!std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() noexcept>>);
-static_assert(!std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const noexcept>>);
 
 static_assert(std::is_assignable_v<std::function_ref<void()>, void (*)()>);
 static_assert(!std::is_assignable_v<std::function_ref<void()>, void (*)(int)>);
@@ -35,10 +35,10 @@ static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_arg_
 static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_arg_t<[](int) {}>>);
 
 // const noexcept(false)
-static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const>>);
-static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
-static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
 
 static_assert(std::is_assignable_v<std::function_ref<void() const>, void (*)()>);
 static_assert(!std::is_assignable_v<std::function_ref<void() const>, void (*)(int)>);
@@ -50,7 +50,7 @@ static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::consta
 static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void()>>);
 static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const>>);
 static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() noexcept>>);
-static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const noexcept>>);
 
 static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, void (*)() noexcept>);
 static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
@@ -61,7 +61,7 @@ static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::con
 // const noexcept(true)
 static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void()>>);
 static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const>>);
-static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
 static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const noexcept>>);
 
 static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)() noexcept>);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
index 3c7053709458f..f28f26f707d3b 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/function_ptr.pass.cpp
@@ -78,8 +78,11 @@ static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double
 
 constexpr int fn() { return 42; }
 
+constexpr int fn_maythrow(int i, A a) { return i - a.i; }
 constexpr int fn_noexcept(int i, A a) noexcept { return i + a.i; }
 
+constexpr void foo(int) {}
+constexpr void bar(int) noexcept {}
 struct Int {
   int i;
   constexpr Int(int ii) noexcept : i(ii) {}
@@ -135,6 +138,12 @@ void non_constexpr_test() {
 
     std::function_ref<Int(int, int, int) const noexcept> f4(&needs_conversion);
     assert(f4(1, 2, 3).i == 6);
+
+    {
+      std::function_ref r1 = foo;
+      std::function_ref r2 = bar;
+      r1                   = r2; // ok
+    }
   }
 }
 
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
index f0f6411521f6d..abf118cb66a30 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/ref.pass.cpp
@@ -155,6 +155,12 @@ struct NeedsConversion {
   constexpr int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
 };
 
+constexpr int fn() { return 5; }
+constexpr int fn_noexcept() noexcept { return 6; }
+
+constexpr int one() noexcept { return 1; }
+constexpr int two() noexcept { return 2; }
+
 constexpr bool test() {
   {
     std::function_ref<void()> f(l1);
@@ -218,6 +224,37 @@ constexpr bool test() {
     assert(f4(1, 2, 3).i == 6);
   }
 
+  {
+    // noexcept conversion
+    std::function_ref<int() noexcept> f1(&fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    assert(f2() == 6);
+  }
+
+  {
+    // const conversion
+    std::function_ref<int() const> f1(&fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    assert(f2() == 6);
+  }
+
+  {
+    // const noexcept conversion
+    std::function_ref<int() const noexcept> f1(&fn_noexcept);
+    std::function_ref<int()> f2(f1);
+    assert(f2() == 6);
+  }
+
+  {
+    // double unwrapping
+    std::function_ref<int() const noexcept> f1(&one);
+    std::function_ref<int()> f2(f1);
+
+    f1 = &two;
+    assert(f2() == 1 || f2() == 2);
+    LIBCPP_ASSERT(f2() == 1);
+  }
+
   return true;
 }
 

>From 4ef2f9a6d38692a6a6415af27db51eabb510bc45 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 18 Apr 2026 13:43:57 +0100
Subject: [PATCH 24/26] P3948R1

---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +-
 .../__functional/function_ref_common.h        |  86 ++++-----
 .../include/__functional/function_ref_impl.h  |  72 ++++----
 libcxx/include/__utility/constant_arg.h       |  39 ----
 libcxx/include/utility                        |   7 -
 libcxx/include/version                        |   4 +-
 libcxx/modules/std/utility.inc                |   4 +-
 .../assert.constant_arg_ptr.pass.cpp          |   4 +-
 .../constant_arg.mandates.verify.cpp          |  18 +-
 .../functional.version.compile.pass.cpp       |   4 +-
 .../version.version.compile.pass.cpp          |   4 +-
 .../func.wrap/func.wrap.ref/ctad.pass.cpp     |  22 +--
 .../func.wrap.ref.ctor/assign.delete.pass.cpp |  40 ++---
 .../func.wrap.ref.ctor/constant_arg.pass.cpp  |  80 ++++-----
 .../constant_arg_ptr.pass.cpp                 | 146 +++++++--------
 .../constant_arg_ref.pass.cpp                 | 168 +++++++++---------
 .../func.wrap.ref.ctor/copy.pass.cpp          |  24 +--
 .../func.wrap.ref.ctor/copy_assign.pass.cpp   |  48 ++---
 .../func.wrap.ref.ctor/move.pass.cpp          |  24 +--
 .../func.wrap.ref.ctor/move_assign.pass.cpp   |  48 ++---
 .../func.wrap.ref.inv/invoke.pass.cpp         |  24 +--
 .../generate_feature_test_macro_components.py |   2 +-
 22 files changed, 420 insertions(+), 450 deletions(-)
 delete mode 100644 libcxx/include/__utility/constant_arg.h

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 937005bdd30fa..4824188f9a6a1 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -470,7 +470,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_fstream_native_handle``                        ``202306L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_function_ref``                                 ``202306L``
+    ``__cpp_lib_function_ref``                                 ``202603L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_generate_random``                              *unimplemented*
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/include/__functional/function_ref_common.h b/libcxx/include/__functional/function_ref_common.h
index 0b1aa207ca589..13188111b0b34 100644
--- a/libcxx/include/__functional/function_ref_common.h
+++ b/libcxx/include/__functional/function_ref_common.h
@@ -15,7 +15,7 @@
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_object.h>
 #include <__type_traits/remove_pointer.h>
-#include <__utility/constant_arg.h>
+#include <__utility/constant_wrapper.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -31,45 +31,45 @@ class function_ref;
 template <class _Fn, bool _NoExcept1, class _Rp, class... _ArgTypes>
 struct __is_convertible_from_specialization : false_type {};
 
-  // use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
-  // pointers
-  // todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
-  // supported
-  template <class _Rp, class... _ArgTypes>
-  union __storage_func_ref_t {
-    void* __obj_ptr_;
-    void const* __obj_const_ptr_;
-    _Rp (*__fn_ptr_)(_ArgTypes...);
-    _Rp (*__fn_ptr_noexcept_)(_ArgTypes...) noexcept;
-    void(* __fn_ptr_type_erased_)();
-
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t() noexcept : __obj_ptr_(nullptr) {}
-
-    template <class _Tp>
-    _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t(_Tp* __ptr) noexcept {
-      if constexpr (is_object_v<_Tp>) {
-        if constexpr (is_const_v<_Tp>) {
-          __obj_const_ptr_ = __ptr;
+// use a union instead of a plain `void*` to avoid dropping const qualifiers and casting function pointers to data
+// pointers
+// todo: libstdc++ does not support volatile objects. shall we support it? the standard does not say it should not be
+// supported
+template <class _Rp, class... _ArgTypes>
+union __storage_func_ref_t {
+  void* __obj_ptr_;
+  void const* __obj_const_ptr_;
+  _Rp (*__fn_ptr_)(_ArgTypes...);
+  _Rp (*__fn_ptr_noexcept_)(_ArgTypes...) noexcept;
+  void (*__fn_ptr_type_erased_)();
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t() noexcept : __obj_ptr_(nullptr) {}
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __storage_func_ref_t(_Tp* __ptr) noexcept {
+    if constexpr (is_object_v<_Tp>) {
+      if constexpr (is_const_v<_Tp>) {
+        __obj_const_ptr_ = __ptr;
+      } else {
+        __obj_ptr_ = __ptr;
+      }
+    } else {
+      static_assert(is_function_v<_Tp>);
+      if constexpr (is_nothrow_invocable_v<_Tp, _ArgTypes...>) {
+        if constexpr (requires { __fn_ptr_noexcept_ = __ptr; }) {
+          __fn_ptr_noexcept_ = __ptr;
         } else {
-          __obj_ptr_ = __ptr;
+          __fn_ptr_type_erased_ = reinterpret_cast<void (*)()>(__ptr);
         }
       } else {
-        static_assert(is_function_v<_Tp>);
-        if constexpr (is_nothrow_invocable_v<_Tp, _ArgTypes...>) {
-          if constexpr (requires { __fn_ptr_noexcept_ = __ptr; }) {
-            __fn_ptr_noexcept_ = __ptr;
-          } else {
-            __fn_ptr_type_erased_ = reinterpret_cast<void(*)()>(__ptr);
-          }
+        if constexpr (requires { __fn_ptr_ = __ptr; }) {
+          __fn_ptr_ = __ptr;
         } else {
-          if constexpr (requires { __fn_ptr_ = __ptr; }) {
-            __fn_ptr_ = __ptr;
-          } else {
-            __fn_ptr_type_erased_ = reinterpret_cast<void(*)()>(__ptr);
-          }
+          __fn_ptr_type_erased_ = reinterpret_cast<void (*)()>(__ptr);
         }
       }
     }
+  }
 
   template <class _Tp>
   _LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__storage_func_ref_t __storage) {
@@ -96,9 +96,7 @@ struct __is_convertible_from_specialization : false_type {};
       }
     }
   }
-  };
-
-
+};
 
 template <class _Fp, class _Tp>
 struct __function_ref_bind {};
@@ -126,12 +124,18 @@ template <class _Fp>
   requires is_function_v<_Fp>
 function_ref(_Fp*) -> function_ref<_Fp>;
 
-template <auto _Fn>
-  requires is_function_v<remove_pointer_t<decltype(_Fn)>>
-function_ref(constant_arg_t<_Fn>) -> function_ref<remove_pointer_t<decltype(_Fn)>>;
+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 _Fn, class _Tp>
-function_ref(constant_arg_t<_Fn>, _Tp&&) -> function_ref<__function_ref_bind_t<decltype(_Fn), _Tp&>>;
+template <auto _Value>
+constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
 
 #endif // _LIBCPP_STD_VER >= 26
 
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index 78c5b6a357357..d9c57ac173ef0 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -9,7 +9,8 @@
 // This header is unguarded on purpose. This header is an implementation detail of function_ref.h
 // and generates multiple versions of std::function_ref
 
-#include "__functional/function_ref_common.h"
+#include <__functional/function_ref_common.h>
+
 #include <__assert>
 #include <__config>
 #include <__functional/invoke.h>
@@ -17,6 +18,7 @@
 #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>
@@ -27,9 +29,8 @@
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_pointer.h>
 #include <__type_traits/remove_reference.h>
-#include <__utility/constant_arg.h>
+#include <__utility/constant_wrapper.h>
 #include <__utility/forward.h>
-#include <type_traits>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -46,9 +47,13 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class...>
 class function_ref;
 
-template <bool _NoExcept2,  bool _NoExcept1, class _Rp, class... _ArgTypes>
-struct __is_convertible_from_specialization<function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept2)>, _NoExcept1, _Rp, _ArgTypes...>
- : is_convertible<_Rp(&)(_ArgTypes...) noexcept(_NoExcept2), _Rp(&)(_ArgTypes...) noexcept(_NoExcept1)> {};
+template <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)> {
@@ -59,9 +64,10 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
           value;
 
   template <class _Fn>
-  static constexpr bool __is_convertible_from_specialization_v = __is_convertible_from_specialization<_Fn, __is_noexcept, _Rp, _ArgTypes...>::value;
+  static constexpr bool __is_convertible_from_specialization_v =
+      __is_convertible_from_specialization<_Fn, __is_noexcept, _Rp, _ArgTypes...>::value;
 
-  template<class... _Tp>
+  template <class... _Tp>
   friend class function_ref;
 
   using __storage_t _LIBCPP_NODEBUG = __storage_func_ref_t<_Rp, _ArgTypes...>;
@@ -96,46 +102,52 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
     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_) {}
+      : __storage_(__obj.__storage_), __call_(__obj.__call_) {}
 
-  template <auto _Fn>
-    requires __is_invocable_using<const decltype(_Fn)&>
-  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>) noexcept
+  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, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
-          return std::invoke_r<_Rp>(_Fn, std::forward<_ArgTypes>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, std::forward<_ArgTypes>(__args)...);
         }) {
-    if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
-      static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
+    if constexpr (is_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 will unwrap the callable from cw<f>, and it cannot produce a constant_wrapper result");
     }
   }
 
-  template <auto _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
-    requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<const decltype(_Fn)&, _LIBCPP_FUNCTION_REF_CV _Tp&>
-  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _Up&& __obj) noexcept
+  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, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
           _LIBCPP_FUNCTION_REF_CV _Tp& __obj = *__storage_t::template __get<_Tp>(__storage);
-          return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, std::forward<_ArgTypes>(__args)...);
         }) {
-    if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
-      static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
+    if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
+      static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
     }
   }
 
-  template <auto _Fn, class _Tp>
-    requires __is_invocable_using<const decltype(_Fn)&, _LIBCPP_FUNCTION_REF_CV _Tp*>
-  _LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_arg_t<_Fn>, _LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr_) noexcept
+  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, _ArgTypes&&... __args) static noexcept(__is_noexcept) -> _Rp {
           auto __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
-          return std::invoke_r<_Rp>(_Fn, __obj, std::forward<_ArgTypes>(__args)...);
+          return std::invoke_r<_Rp>(decltype(__f)::value, __obj, std::forward<_ArgTypes>(__args)...);
         }) {
-    if constexpr (is_pointer_v<decltype(_Fn)> || is_member_pointer_v<decltype(_Fn)>) {
-      static_assert(_Fn != nullptr, "the function pointer should not be a nullptr");
+    if constexpr (is_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<decltype(_Fn)>) {
+    if constexpr (is_member_pointer_v<_Fn>) {
       _LIBCPP_ASSERT_UNCATEGORIZED(__obj_ptr_ != nullptr, "the object pointer should not be a nullptr");
     }
   }
@@ -145,7 +157,7 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
   _LIBCPP_HIDE_FROM_ABI constexpr function_ref& operator=(const function_ref&) noexcept = default;
 
   template <class _Tp>
-    requires(!__is_convertible_from_specialization_v<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_constant_arg_t<_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 constexpr _Rp operator()(_ArgTypes... __args) const noexcept(__is_noexcept) {
diff --git a/libcxx/include/__utility/constant_arg.h b/libcxx/include/__utility/constant_arg.h
deleted file mode 100644
index f299ec509ed5c..0000000000000
--- a/libcxx/include/__utility/constant_arg.h
+++ /dev/null
@@ -1,39 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBCPP___UTILITY_CONSTANT_ARG_H
-#define _LIBCPP___UTILITY_CONSTANT_ARG_H
-
-#include <__config>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#  pragma GCC system_header
-#endif
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-#if _LIBCPP_STD_VER >= 26
-
-template <auto _Vp>
-struct constant_arg_t {
-  _LIBCPP_HIDE_FROM_ABI explicit constant_arg_t() = default;
-};
-
-template <auto _Vp>
-inline constexpr constant_arg_t<_Vp> constant_arg{};
-
-template <class>
-inline constexpr bool __is_constant_arg_t = false;
-template <auto _Vp>
-inline constexpr bool __is_constant_arg_t<constant_arg_t<_Vp>> = true;
-
-#endif // _LIBCPP_STD_VER >= 26
-
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP___UTILITY_CONSTANT_ARG_H
diff --git a/libcxx/include/utility b/libcxx/include/utility
index 0cc3f7f4d69ea..24b3baac91b1d 100644
--- a/libcxx/include/utility
+++ b/libcxx/include/utility
@@ -266,13 +266,6 @@ template <size_t I>
 template <size_t I>
   inline constexpr in_place_index_t<I> in_place_index{};
 
-// constant_arg argument tag
-template<auto V>
-  struct constant_arg_t {
-    explicit constant_arg_t() = default;
-  };
-template<auto V> constexpr constant_arg_t<V> constant_arg_t{};
-
 // [utility.underlying], to_underlying
 template <class T>
     constexpr underlying_type_t<T> to_underlying( T value ) noexcept; // C++23
diff --git a/libcxx/include/version b/libcxx/include/version
index 1a4de23fb45e1..c96bcccfcf808 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -129,7 +129,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>
@@ -583,7 +583,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/utility.inc b/libcxx/modules/std/utility.inc
index aacab46e6df02..47c30c9022ade 100644
--- a/libcxx/modules/std/utility.inc
+++ b/libcxx/modules/std/utility.inc
@@ -90,8 +90,8 @@ export namespace std {
   using std::in_place_index_t;
 
 #if _LIBCPP_STD_VER >= 26
-  using std::constant_arg;
-  using std::constant_arg_t;
+  using std::cw;
+  using std::constant_wrapper;
 #endif // _LIBCPP_STD_VER >= 23
 
   // [depr.relops]
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
index 085b09a4905ba..dcaa2c4dfb70d 100644
--- 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
@@ -12,7 +12,7 @@
 // XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
 
 // template<auto f, class T>
-//   constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
+//   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>
@@ -24,7 +24,7 @@ struct A {
 };
 
 int main(int, char**) {
-  TEST_LIBCPP_ASSERT_FAILURE(std::function_ref<void()>(std::constant_arg<&A::f>, static_cast<A*>(nullptr)),
+  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/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 4e26f940ff430..47a5a514e6052 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
@@ -8,11 +8,11 @@
 
 // REQUIRES: std-at-least-c++26
 
-// template<auto f> constexpr function_ref(constant_arg_t<f>) noexcept;
+// template<auto f> constexpr function_ref(constant_wrapper<f>) noexcept;
 // template<auto f, class U>
-//   constexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
+//   constexpr function_ref(constant_wrapper<f>, U&& obj) noexcept;
 // template<auto f, class T>
-//   constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
+//   constexpr function_ref(constant_wrapper<f>, cv T* obj) noexcept;
 
 // Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
 
@@ -25,24 +25,24 @@ struct A {
 
 // clang-format off
 void test() {
-  std::function_ref<void()> f1(std::constant_arg<static_cast<void (*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  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 'nullptr != nullptr': the function pointer should not be a nullptr}}
 
-  std::function_ref<void(A)> f2(std::constant_arg<static_cast<void (A::*)()>(nullptr)>); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  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 'nullptr != nullptr': the function pointer should not be a nullptr}}
 
   int i;
-  std::function_ref<void()> f3(std::constant_arg<static_cast<void (*)(int)>(nullptr)>, i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  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 'nullptr != nullptr': the function pointer should not be a nullptr}}
 
   A a;
-  std::function_ref<void()> f4(std::constant_arg<static_cast<void (A::*)()>(nullptr)>, a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  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 'nullptr != nullptr': the function pointer should not be a nullptr}}
 
-  std::function_ref<void()> f5(std::constant_arg<static_cast<void (*)(int*)>(nullptr)>, &i); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  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 'nullptr != nullptr': the function pointer should not be a nullptr}}
 
-  std::function_ref<void()> f6(std::constant_arg<static_cast<void (A::*)()>(nullptr)>, &a); // expected-note-re{{in instantiation of function template specialization 'std::function_ref{{.*}}' requested here}}
+  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 'nullptr != nullptr': the function pointer should not be a nullptr}}
 }
 // clang-format on
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 eedb1eb8eb2a7..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
@@ -497,8 +497,8 @@
 #  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"
+#  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 4958ee42103f4..a48c2e116948a 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
@@ -7038,8 +7038,8 @@
 #  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"
+#  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
index 591c397fee2d8..e5adbe84b08e7 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/ctad.pass.cpp
@@ -38,42 +38,42 @@ void test() {
     static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
   }
 
-  // template<auto f>
-  //  function_ref(constant_arg_t<f>) -> function_ref<...>;
+  // template<auto c, class F>
+  //  function_ref(constant_wrapper<c, F>) -> function_ref<...>;
   {
-    std::function_ref fn_ref = std::constant_arg_t<fn>();
+    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_arg_t<fn_noexcept>();
+    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 f, class T>
-  //  function_ref(constant_arg_t<f>, T&&) -> function_ref<...>;
+  // 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_arg_t<fn>(), arg};
+    std::function_ref fn_ref = {std::constant_wrapper<fn>(), arg};
     static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float)>>);
   }
   {
     S s;
-    std::function_ref fn_ref = {std::constant_arg_t<&S::data_mem>(), 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 S s;
-    std::function_ref fn_ref = {std::constant_arg_t<&S::data_mem>(), 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&()>>);
   }
   {
     S s;
-    std::function_ref fn_ref = {std::constant_arg_t<&S::fn_mem>(), 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_arg_t<&S::fn_mem_noexcept>(), 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>>);
   }
 }
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 81d1e79a5499f..1ad82be2f8913 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
@@ -20,7 +20,7 @@
 // Constraints:
 // - T is not the same type as function_ref,
 // - is_pointer_v<T> is false, and
-// - T is not a specialization of constant_arg_t.
+// - 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()>>);
@@ -31,8 +31,8 @@ static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<
 static_assert(std::is_assignable_v<std::function_ref<void()>, void (*)()>);
 static_assert(!std::is_assignable_v<std::function_ref<void()>, void (*)(int)>);
 
-static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_arg_t<[] {}>>);
-static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_arg_t<[](int) {}>>);
+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()>>);
@@ -43,8 +43,8 @@ static_assert(std::is_assignable_v<std::function_ref<void() const>, std::functio
 static_assert(std::is_assignable_v<std::function_ref<void() const>, void (*)()>);
 static_assert(!std::is_assignable_v<std::function_ref<void() const>, void (*)(int)>);
 
-static_assert(std::is_assignable_v<std::function_ref<void() const>, std::constant_arg_t<[] { return 42; }>>);
-static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::constant_arg_t<[](int) { return 42; }>>);
+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()>>);
@@ -55,8 +55,8 @@ static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::func
 static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, void (*)() noexcept>);
 static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
 
-static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_arg_t<[] noexcept {} >>);
-static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_arg_t<[](int) noexcept {}>>);
+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()>>);
@@ -67,44 +67,44 @@ static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std
 static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)() noexcept>);
 static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)(int) noexcept>);
 
-static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_arg_t<[] noexcept {}>>);
+static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_wrapper<[] noexcept {}>>);
 static_assert(
-    !std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_arg_t<[](int) noexcept {}>>);
+    !std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_wrapper<[](int) noexcept {}>>);
 
 constexpr int forty_two() { return 42; }
 
 constexpr bool test() {
   {
-    std::function_ref<int()> f(std::constant_arg<[] { return 41; }>);
-    f = std::function_ref<int()>(std::constant_arg<[] { return 42; }>);
+    std::function_ref<int()> f(std::cw<[] { return 41; }>);
+    f = std::function_ref<int()>(std::cw<[] { return 42; }>);
     assert(f() == 42);
   }
   if !consteval {
-    std::function_ref<int() > f(std::constant_arg<[] { return 41; }>);
+    std::function_ref<int() > f(std::cw<[] { return 41; }>);
     f = &forty_two;
     assert(f() == 42);
   }
   {
-    std::function_ref<int() > f(std::constant_arg<[] { return 41; }>);
-    f = std::constant_arg<[] { return 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::constant_arg<[] { return 41; }>);
-    f = std::constant_arg<[] { return 42; }>;
+    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::constant_arg<[] noexcept { return 41; }>);
-    f = std::constant_arg<[] noexcept { return 42; }>;
+    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::constant_arg<[] noexcept { return 41; }>);
-    f = std::constant_arg<[] noexcept { return 42; }>;
+    std::function_ref<int() const noexcept> f(std::cw<[] noexcept { return 41; }>);
+    f = std::cw<[] noexcept { return 42; }>;
     assert(f() == 42);
   }
   return true;
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp
index 7adc6108e9b50..9875996025b11 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp
@@ -8,7 +8,7 @@
 
 // REQUIRES: std-at-least-c++26
 
-// template<auto f> constexpr function_ref(constant_arg_t<f>) noexcept;
+// template<auto f> constexpr function_ref(constant_wrapper<f>) noexcept;
 
 #include <cassert>
 #include <functional>
@@ -29,45 +29,45 @@ struct NonConstInvocable {
 };
 
 // non-const noexcept(false)
-static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>>);
-static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<NonConstInvocable{}>>);
-static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l2>>);
-static_assert(std::is_constructible_v<std::function_ref<void(int)>, std::constant_arg_t<l2>>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>>);
+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_arg_t<l1>>);
-static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int)>, std::constant_arg_t<l2>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_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_arg_t<l1_noexcept>>);
-static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l1>>);
-static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<NonConstInvocable{}>>);
-static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l2_noexcept>>);
-static_assert(std::is_constructible_v<std::function_ref<void(int) noexcept>, std::constant_arg_t<l2_noexcept>>);
+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_arg_t<l1_noexcept>>);
-static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int) noexcept>, std::constant_arg_t<l2_noexcept>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_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_arg_t<l1>>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<NonConstInvocable{}>>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l2>>);
-static_assert(std::is_constructible_v<std::function_ref<void(int) const>, std::constant_arg_t<l2>>);
+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_arg_t<l1>>);
-static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int) const>, std::constant_arg_t<l2>>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_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_arg_t<l1_noexcept>>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1>>);
+static_assert(std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_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_arg_t<NonConstInvocable{}>>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l2_noexcept>>);
-static_assert(std::is_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_arg_t<l2_noexcept>>);
+    !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_arg_t<l1_noexcept>>);
+    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_arg_t<l2_noexcept>>);
+    std::is_nothrow_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_wrapper<l2_noexcept>>);
 
 constexpr double f1(int x, double y) noexcept { return x + y; }
 
@@ -84,55 +84,55 @@ constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i
 
 constexpr bool test() {
   {
-    std::function_ref<void()> f(std::constant_arg<[] {}>);
+    std::function_ref<void()> f(std::cw<[] {}>);
     f();
   }
   {
     // explicit
-    std::function_ref<void()> f = std::constant_arg<[] {}>;
+    std::function_ref<void()> f = std::cw<[] {}>;
     f();
   }
   {
     // const
-    std::function_ref<int() const> f(std::constant_arg<[] { return 42; }>);
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
     assert(f() == 42);
   }
   {
     // noexcept
-    std::function_ref<double(int, double) noexcept> f(std::constant_arg<&f1>);
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
     assert(f(1, 2.0) == 3.0);
   }
   {
     // const noexcept
-    std::function_ref<double(int, double) const noexcept> f(std::constant_arg<&f1>);
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
     assert(f(1, 2.0) == 3.0);
   }
   {
     // with conversions
-    std::function_ref<Int(int, int, int)> f(std::constant_arg<NeedsConversion{}>);
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
     assert(f(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const> f2(std::constant_arg<NeedsConversion{}>);
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
     assert(f2(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<NeedsConversion{}>);
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
     assert(f3(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<NeedsConversion{}>);
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
     assert(f4(1, 2, 3).i == 6);
   }
   {
     // with conversions function pointer
-    std::function_ref<Int(int, int, int)> f(std::constant_arg<&needs_conversion>);
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
     assert(f(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const> f2(std::constant_arg<&needs_conversion>);
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
     assert(f2(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<&needs_conversion>);
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
     assert(f3(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<&needs_conversion>);
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
     assert(f4(1, 2, 3).i == 6);
   }
 
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ptr.pass.cpp
index 20b0ca855f08d..3573d2a38413d 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ptr.pass.cpp
@@ -9,7 +9,7 @@
 // REQUIRES: std-at-least-c++26
 
 // template<auto f, class T>
-//   constexpr function_ref(constant_arg_t<f>, cv T* obj) noexcept;
+//   constexpr function_ref(constant_wrapper<f>, cv T* obj) noexcept;
 
 #include <cassert>
 #include <functional>
@@ -46,105 +46,105 @@ struct A {
 };
 
 // non-const noexcept(false)
-static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>, int*>);
-static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>, const int*>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, const int*>);
 
-static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<NonConstInvocable{}>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, int*>);
 
-static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_arg_t<l2>, int*>);
-static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l2>, int*>);
+static_assert(std::is_constructible_v<std::function_ref<void(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_arg_t<&A::f>, A*>);
-static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<&A::g>, A*>);
+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_arg_t<l1>, int*>);
-static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_arg_t<l2>, int*>);
-static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_arg_t<&A::f>, A*>);
+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_arg_t<l1_noexcept>, 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_arg_t<l1>, 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_arg_t<NonConstInvocable{}>, int*>);
+    !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_arg_t<l2_noexcept>, int*>);
-static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_arg_t<l2>, int*>);
-static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l2_noexcept>, int*>);
+    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_arg_t<&A::f_noexcept>, A*>);
-static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<&A::f>, A*>);
-static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<&A::g_noexcept>, A*>);
+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_arg_t<l1_noexcept>, int*>);
+    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_arg_t<l2_noexcept>, int*>);
+    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_arg_t<&A::f_noexcept>, A*>);
+    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_arg_t<l1_const>, int*>);
-static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1_const>, const int*>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l1>, int*>);
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_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_arg_t<NonConstInvocable{}>, int*>);
+    !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_arg_t<l2_const>, int*>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l2_const>, int*>);
+static_assert(std::is_constructible_v<std::function_ref<void(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_arg_t<&A::f_const>, A*>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::f>, A*>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::g_const>, A*>);
+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_arg_t<l1_const>, int*>);
+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_arg_t<l1_const>, const int*>);
+    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_arg_t<l2_const>, int*>);
-static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::f_const>, A*>);
+    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_arg_t<l1_const_noexcept>, int*>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1>, int*>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l1_const>, int*>);
+    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_arg_t<l1_noexcept>, int*>);
+    !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_arg_t<NonConstInvocable{}>, int*>);
+    !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_arg_t<l2_const_noexcept>,
+                                      std::constant_wrapper<l2_const_noexcept>,
                                       int*>);
 static_assert(
-    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l2_const_noexcept>, int*>);
+    !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_arg_t<&A::f_const_noexcept>, A*>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f>, A*>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f_const>, A*>);
+    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_arg_t<&A::f_noexcept>, A*>);
+    !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_arg_t<&A::g_const_noexcept>, A*>);
+    !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_arg_t<l1_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_arg_t<l2_const_noexcept>,
+                                              std::constant_wrapper<l2_const_noexcept>,
                                               int*>);
 static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
-                                              std::constant_arg_t<&A::f_const_noexcept>,
+                                              std::constant_wrapper<&A::f_const_noexcept>,
                                               A*>);
 
 constexpr double f1(const int* x, double y) noexcept { return *x + y; }
@@ -187,20 +187,20 @@ constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i
 constexpr bool test() {
   {
     int i = 0;
-    std::function_ref<void()> f(std::constant_arg<[](int*) {}>, &i);
+    std::function_ref<void()> f(std::cw<[](int*) {}>, &i);
     f();
   }
   {
     // explicit
     int i                       = 0;
-    std::function_ref<void()> f = {std::constant_arg<[](int*) {}>, &i};
+    std::function_ref<void()> f = {std::cw<[](int*) {}>, &i};
     f();
   }
   {
     // mutate
     int i;
     std::function_ref<double(double)> f(
-        std::constant_arg<[](int* j, double d) {
+        std::cw<[](int* j, double d) {
           *j = 5;
           return *j + d;
         }>,
@@ -211,84 +211,84 @@ constexpr bool test() {
   {
     // const
     int i = 5;
-    std::function_ref<int() const> f(std::constant_arg<[](const int* i) { return *i + 42; }>, &i);
+    std::function_ref<int() const> f(std::cw<[](const int* i) { return *i + 42; }>, &i);
     assert(f() == 47);
   }
   {
     // noexcept
     int i = 5;
-    std::function_ref<double(double) noexcept> f(std::constant_arg<&f1>, &i);
+    std::function_ref<double(double) noexcept> f(std::cw<&f1>, &i);
     assert(f(2.0) == 7.0);
   }
   {
     // const noexcept
     int i = 5;
-    std::function_ref<double(double) const noexcept> f(std::constant_arg<&f1>, &i);
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, &i);
     assert(f(2.0) == 7.0);
   }
   {
     // member ptr
     M m{3};
-    std::function_ref<int()> f(std::constant_arg<&M::f>, &m);
+    std::function_ref<int()> f(std::cw<&M::f>, &m);
     assert(f() == 3);
 
     int j = 0;
-    std::function_ref<int(int&)> g(std::constant_arg<&M::g>, &m);
+    std::function_ref<int(int&)> g(std::cw<&M::g>, &m);
     assert(g(j) == 45);
     assert(j == 42);
 
-    std::function_ref<int() const> f_const(std::constant_arg<&M::f_const>, &m);
+    std::function_ref<int() const> f_const(std::cw<&M::f_const>, &m);
     assert(f_const() == 8);
 
     j = 0;
-    std::function_ref<int(int&)> g_const(std::constant_arg<&M::g_const>, &m);
+    std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, &m);
     assert(g_const(j) == 46);
     assert(j == 42);
 
-    std::function_ref<int() noexcept> f_noexcept(std::constant_arg<&M::f_noexcept>, &m);
+    std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, &m);
     assert(f_noexcept() == 10);
 
     j = 0;
-    std::function_ref<int(int&) noexcept> g_noexcept(std::constant_arg<&M::g_noexcept>, &m);
+    std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, &m);
     assert(g_noexcept(j) == 47);
     assert(j == 42);
 
-    std::function_ref<int() const noexcept> f_const_noexcept(std::constant_arg<&M::f_const_noexcept>, &m);
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, &m);
     assert(f_const_noexcept() == 12);
 
     j = 0;
-    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::constant_arg<&M::g_const_noexcept>, &m);
+    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, &m);
     assert(g_const_noexcept(j) == 48);
     assert(j == 42);
   }
   {
     // with conversions
     int i = 1;
-    std::function_ref<Int(int, int)> f(std::constant_arg<NeedsConversion{}>, i);
+    std::function_ref<Int(int, int)> f(std::cw<NeedsConversion{}>, i);
     assert(f(2, 3).i == 6);
 
-    std::function_ref<Int(int, int) const> f2(std::constant_arg<NeedsConversion{}>, i);
+    std::function_ref<Int(int, int) const> f2(std::cw<NeedsConversion{}>, i);
     assert(f2(2, 3).i == 6);
 
-    std::function_ref<Int(int, int) noexcept> f3(std::constant_arg<NeedsConversion{}>, i);
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<NeedsConversion{}>, i);
     assert(f3(2, 3).i == 6);
 
-    std::function_ref<Int(int, int) const noexcept> f4(std::constant_arg<NeedsConversion{}>, i);
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<NeedsConversion{}>, i);
     assert(f4(2, 3).i == 6);
   }
   {
     // with conversions function pointer
     int i = 1;
-    std::function_ref<Int(int, int)> f(std::constant_arg<&needs_conversion>, i);
+    std::function_ref<Int(int, int)> f(std::cw<&needs_conversion>, i);
     assert(f(2, 3).i == 6);
 
-    std::function_ref<Int(int, int) const> f2(std::constant_arg<&needs_conversion>, i);
+    std::function_ref<Int(int, int) const> f2(std::cw<&needs_conversion>, i);
     assert(f2(2, 3).i == 6);
 
-    std::function_ref<Int(int, int) noexcept> f3(std::constant_arg<&needs_conversion>, i);
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<&needs_conversion>, i);
     assert(f3(2, 3).i == 6);
 
-    std::function_ref<Int(int, int) const noexcept> f4(std::constant_arg<&needs_conversion>, i);
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<&needs_conversion>, i);
     assert(f4(2, 3).i == 6);
   }
 
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp
index 43eb5133c76fd..8b7995d0d3c25 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp
@@ -9,7 +9,7 @@
 // REQUIRES: std-at-least-c++26
 
 // template<auto f, class U>
-//   constexpr function_ref(constant_arg_t<f>, U&& obj) noexcept;
+//   constexpr function_ref(constant_wrapper<f>, U&& obj) noexcept;
 
 #include <cassert>
 #include <functional>
@@ -46,125 +46,125 @@ struct A {
 };
 
 // non-const noexcept(false)
-static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>, int&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&>);
 
-static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l1>, int&&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&&>);
 
-static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<NonConstInvocable{}>, long&>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, long&>);
 
-static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_arg_t<l2>, int&>);
-static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l2>, int&>);
+static_assert(std::is_constructible_v<std::function_ref<void(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_arg_t<l3>, int&>);
-static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<l3>, const int&>);
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_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_arg_t<&A::f>, A&>);
-static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_arg_t<&A::g>, A&>);
+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_arg_t<l1>, int&>);
-static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_arg_t<l2>, int&>);
-static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_arg_t<l3>, int&>);
-static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_arg_t<&A::f>, A&>);
+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_arg_t<l1_noexcept>, 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_arg_t<l1>, 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_arg_t<l1_noexcept>, 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_arg_t<NonConstInvocable{}>, long&>);
+    !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_arg_t<l2_noexcept>, int&>);
-static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_arg_t<l2>, int&>);
-static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l2_noexcept>, int&>);
+    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_arg_t<l3_noexcept>, int&>);
-static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<l3>, const int&>);
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_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_arg_t<l3_noexcept>, const int&>);
+    !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_arg_t<&A::f_noexcept>, A&>);
-static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<&A::f>, A&>);
-static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_arg_t<&A::g_noexcept>, A&>);
+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_arg_t<l1_noexcept>, int&>);
+    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_arg_t<l2_noexcept>, int&>);
+    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_arg_t<l3_noexcept>, int&>);
+    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_arg_t<&A::f_noexcept>, A&>);
+    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_arg_t<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_arg_t<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_arg_t<NonConstInvocable{}>, long&>);
+    !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_arg_t<l2>, int&>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l2>, int&>);
+static_assert(std::is_constructible_v<std::function_ref<void(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_arg_t<l3>, int&>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<l3>, const int&>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_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_arg_t<&A::f_const>, A&>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::f>, A&>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::g_const>, A&>);
+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_arg_t<l1>, int&>);
-static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_arg_t<l2>, int&>);
-static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_arg_t<&A::f_const>, A&>);
+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_arg_t<l1_noexcept>, int&>);
+    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_arg_t<l1>, 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_arg_t<l1_noexcept>, int&&>);
+    !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_arg_t<NonConstInvocable{}>,
+                                       std::constant_wrapper<NonConstInvocable{}>,
                                        long&>);
 
 static_assert(
-    std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_arg_t<l2_noexcept>, int&>);
-static_assert(!std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_arg_t<l2>, int&>);
+    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_arg_t<l2_noexcept>, int&>);
+    !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_arg_t<l3_noexcept>, int&>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<l3>, const int&>);
+    !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_arg_t<l3_noexcept>, const int&>);
+    !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_arg_t<&A::f_const_noexcept>, A&>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f>, A&>);
-static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_arg_t<&A::f_const>, A&>);
+    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_arg_t<&A::f_noexcept>, A&>);
+    !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_arg_t<&A::g_const_noexcept>, A&>);
+    !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_arg_t<l1_noexcept>, int&>);
+    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_arg_t<l2_noexcept>, int&>);
+    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_arg_t<&A::f_const_noexcept>, A&>);
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A&>);
 
 constexpr double f1(int x, double y) noexcept { return x + y; }
 
@@ -206,20 +206,20 @@ constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i
 constexpr bool test() {
   {
     int i = 0;
-    std::function_ref<void()> f(std::constant_arg<[](int) {}>, i);
+    std::function_ref<void()> f(std::cw<[](int) {}>, i);
     f();
   }
   {
     // explicit
     int i                       = 0;
-    std::function_ref<void()> f = {std::constant_arg<[](int) {}>, i};
+    std::function_ref<void()> f = {std::cw<[](int) {}>, i};
     f();
   }
   {
     // mutate
     int i;
     std::function_ref<double(double)> f(
-        std::constant_arg<[](int& j, double d) {
+        std::cw<[](int& j, double d) {
           j = 5;
           return j + d;
         }>,
@@ -230,53 +230,53 @@ constexpr bool test() {
   {
     // const
     int i = 5;
-    std::function_ref<int() const> f(std::constant_arg<[](int i) { return i + 42; }>, i);
+    std::function_ref<int() const> f(std::cw<[](int i) { return i + 42; }>, i);
     assert(f() == 47);
   }
   {
     // noexcept
     int i = 5;
-    std::function_ref<double(double) noexcept> f(std::constant_arg<&f1>, i);
+    std::function_ref<double(double) noexcept> f(std::cw<&f1>, i);
     assert(f(2.0) == 7.0);
   }
   {
     // const noexcept
     int i = 5;
-    std::function_ref<double(double) const noexcept> f(std::constant_arg<&f1>, i);
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, i);
     assert(f(2.0) == 7.0);
   }
   {
     // member ptr
     M m{3};
-    std::function_ref<int()> f(std::constant_arg<&M::f>, m);
+    std::function_ref<int()> f(std::cw<&M::f>, m);
     assert(f() == 3);
 
     int j = 0;
-    std::function_ref<int(int&)> g(std::constant_arg<&M::g>, m);
+    std::function_ref<int(int&)> g(std::cw<&M::g>, m);
     assert(g(j) == 45);
     assert(j == 42);
 
-    std::function_ref<int() const> f_const(std::constant_arg<&M::f_const>, m);
+    std::function_ref<int() const> f_const(std::cw<&M::f_const>, m);
     assert(f_const() == 8);
 
     j = 0;
-    std::function_ref<int(int&)> g_const(std::constant_arg<&M::g_const>, m);
+    std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, m);
     assert(g_const(j) == 46);
     assert(j == 42);
 
-    std::function_ref<int() noexcept> f_noexcept(std::constant_arg<&M::f_noexcept>, m);
+    std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, m);
     assert(f_noexcept() == 10);
 
     j = 0;
-    std::function_ref<int(int&) noexcept> g_noexcept(std::constant_arg<&M::g_noexcept>, m);
+    std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, m);
     assert(g_noexcept(j) == 47);
     assert(j == 42);
 
-    std::function_ref<int() const noexcept> f_const_noexcept(std::constant_arg<&M::f_const_noexcept>, m);
+    std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, m);
     assert(f_const_noexcept() == 12);
 
     j = 0;
-    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::constant_arg<&M::g_const_noexcept>, m);
+    std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, m);
     assert(g_const_noexcept(j) == 48);
     assert(j == 42);
   }
@@ -284,31 +284,31 @@ constexpr bool test() {
     // with conversions
     int i = 1;
 
-    std::function_ref<Int(int, int)> f(std::constant_arg<NeedsConversion{}>, i);
+    std::function_ref<Int(int, int)> f(std::cw<NeedsConversion{}>, i);
     assert(f(2, 3).i == 6);
 
-    std::function_ref<Int(int, int) const> f2(std::constant_arg<NeedsConversion{}>, i);
+    std::function_ref<Int(int, int) const> f2(std::cw<NeedsConversion{}>, i);
     assert(f2(2, 3).i == 6);
 
-    std::function_ref<Int(int, int) noexcept> f3(std::constant_arg<NeedsConversion{}>, i);
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<NeedsConversion{}>, i);
     assert(f3(2, 3).i == 6);
 
-    std::function_ref<Int(int, int) const noexcept> f4(std::constant_arg<NeedsConversion{}>, i);
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<NeedsConversion{}>, i);
     assert(f4(2, 3).i == 6);
   }
   {
     // with conversions function pointer
     int i = 1;
-    std::function_ref<Int(int, int)> f(std::constant_arg<&needs_conversion>, i);
+    std::function_ref<Int(int, int)> f(std::cw<&needs_conversion>, i);
     assert(f(2, 3).i == 6);
 
-    std::function_ref<Int(int, int) const> f2(std::constant_arg<&needs_conversion>, i);
+    std::function_ref<Int(int, int) const> f2(std::cw<&needs_conversion>, i);
     assert(f2(2, 3).i == 6);
 
-    std::function_ref<Int(int, int) noexcept> f3(std::constant_arg<&needs_conversion>, i);
+    std::function_ref<Int(int, int) noexcept> f3(std::cw<&needs_conversion>, i);
     assert(f3(2, 3).i == 6);
 
-    std::function_ref<Int(int, int) const noexcept> f4(std::constant_arg<&needs_conversion>, i);
+    std::function_ref<Int(int, int) const noexcept> f4(std::cw<&needs_conversion>, i);
     assert(f4(2, 3).i == 6);
   }
 
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
index 5339d75660d4b..882c5ccba26a2 100644
--- 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
@@ -37,62 +37,62 @@ constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i
 
 constexpr bool test() {
   {
-    std::function_ref<void()> f(std::constant_arg<[] {}>);
+    std::function_ref<void()> f(std::cw<[] {}>);
     auto f2 = f;
     f2();
   }
   {
     // const
-    std::function_ref<int() const> f(std::constant_arg<[] { return 42; }>);
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
     auto f2 = f;
 
     assert(f2() == 42);
   }
   {
     // noexcept
-    std::function_ref<double(int, double) noexcept> f(std::constant_arg<&f1>);
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
     auto f2 = f;
     assert(f2(1, 2.0) == 3.0);
   }
   {
     // const noexcept
-    std::function_ref<double(int, double) const noexcept> f(std::constant_arg<&f1>);
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
     auto f2 = f;
     assert(f2(1, 2.0) == 3.0);
   }
   {
     // with conversions
-    std::function_ref<Int(int, int, int)> f(std::constant_arg<NeedsConversion{}>);
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
     auto f_copy = f;
     assert(f_copy(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const> f2(std::constant_arg<NeedsConversion{}>);
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
     auto f2_copy = f2;
     assert(f2_copy(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<NeedsConversion{}>);
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
     auto f3_copy = f3;
     assert(f3_copy(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<NeedsConversion{}>);
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
     auto f4_copy = f4;
     assert(f4_copy(1, 2, 3).i == 6);
   }
   {
     // with conversions function pointer
-    std::function_ref<Int(int, int, int)> f(std::constant_arg<&needs_conversion>);
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
     auto f_copy = f;
     assert(f_copy(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const> f2(std::constant_arg<&needs_conversion>);
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
     auto f2_copy = f2;
     assert(f2_copy(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<&needs_conversion>);
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
     auto f3_copy = f3;
     assert(f3_copy(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<&needs_conversion>);
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
     auto f4_copy = f4;
     assert(f4_copy(1, 2, 3).i == 6);
   }
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
index c91da893db377..d449e4b2fbce6 100644
--- 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
@@ -39,16 +39,16 @@ constexpr int zero(Int, Int, Int) noexcept { return 0; }
 
 constexpr bool test() {
   {
-    std::function_ref<void()> f(std::constant_arg<[] {}>);
-    std::function_ref<void()> f2(std::constant_arg<[] {}>);
+    std::function_ref<void()> f(std::cw<[] {}>);
+    std::function_ref<void()> f2(std::cw<[] {}>);
     f2 = f;
     f();
     f2();
   }
   {
     // const
-    std::function_ref<int() const> f(std::constant_arg<[] { return 42; }>);
-    std::function_ref<int() const> f2(std::constant_arg<[] { return 41; }>);
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
     f2 = f;
 
     assert(f() == 42);
@@ -56,70 +56,70 @@ constexpr bool test() {
   }
   {
     // noexcept
-    std::function_ref<double(int, double) noexcept> f(std::constant_arg<&plus>);
-    std::function_ref<double(int, double) noexcept> f2(std::constant_arg<&minus>);
+    std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
+    std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
     f2 = f;
     assert(f(1, 2.0) == 3.0);
     assert(f2(1, 2.0) == 3.0);
   }
   {
     // const noexcept
-    std::function_ref<double(int, double) const noexcept> f(std::constant_arg<&plus>);
-    std::function_ref<double(int, double) const noexcept> f2(std::constant_arg<&minus>);
+    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;
     assert(f(1, 2.0) == 3.0);
     assert(f2(1, 2.0) == 3.0);
   }
   {
     // with conversions
-    std::function_ref<Int(int, int, int)> f(std::constant_arg<[](int, int, int) { return Int{1}; }>);
-    std::function_ref<Int(int, int, int)> f2(std::constant_arg<NeedsConversion{}>);
+    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;
     assert(f(1, 2, 3).i == 6);
     assert(f2(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const> f_const(std::constant_arg<[](int, int, int) { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) const> f2_const(std::constant_arg<NeedsConversion{}>);
+    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;
     assert(f_const(1, 2, 3).i == 6);
     assert(f2_const(1, 2, 3).i == 6);
 
     std::function_ref<Int(int, int, int) noexcept> f_noexcept(
-        std::constant_arg<[](int, int, int) noexcept { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::constant_arg<NeedsConversion{}>);
+        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;
     assert(f_noexcept(1, 2, 3).i == 6);
     assert(f2_noexcept(1, 2, 3).i == 6);
 
     std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
-        std::constant_arg<[](int, int, int) noexcept { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::constant_arg<NeedsConversion{}>);
+        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;
     assert(f_const_noexcept(1, 2, 3).i == 6);
     assert(f2_const_noexcept(1, 2, 3).i == 6);
   }
   {
     // with conversions function pointer
-    std::function_ref<Int(int, int, int)> f(std::constant_arg<&zero>);
-    std::function_ref<Int(int, int, int)> f2(std::constant_arg<&needs_conversion>);
+    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;
     assert(f(1, 2, 3).i == 6);
     assert(f2(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const> f_const(std::constant_arg<&zero>);
-    std::function_ref<Int(int, int, int) const> f2_const(std::constant_arg<&needs_conversion>);
+    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;
     assert(f_const(1, 2, 3).i == 6);
     assert(f2_const(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::constant_arg<&zero>);
-    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::constant_arg<&needs_conversion>);
+    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;
     assert(f_noexcept(1, 2, 3).i == 6);
     assert(f2_noexcept(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::constant_arg<&zero>);
-    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::constant_arg<&needs_conversion>);
+    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;
     assert(f_const_noexcept(1, 2, 3).i == 6);
     assert(f2_const_noexcept(1, 2, 3).i == 6);
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
index 56d81cb997329..81673a1e1c1cf 100644
--- 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
@@ -35,62 +35,62 @@ constexpr int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i
 
 constexpr bool test() {
   {
-    std::function_ref<void()> f(std::constant_arg<[] {}>);
+    std::function_ref<void()> f(std::cw<[] {}>);
     auto f2 = std::move(f);
     f2();
   }
   {
     // const
-    std::function_ref<int() const> f(std::constant_arg<[] { return 42; }>);
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
     auto f2 = std::move(f);
 
     assert(f2() == 42);
   }
   {
     // noexcept
-    std::function_ref<double(int, double) noexcept> f(std::constant_arg<&f1>);
+    std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
     auto f2 = std::move(f);
     assert(f2(1, 2.0) == 3.0);
   }
   {
     // const noexcept
-    std::function_ref<double(int, double) const noexcept> f(std::constant_arg<&f1>);
+    std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
     auto f2 = std::move(f);
     assert(f2(1, 2.0) == 3.0);
   }
   {
     // with conversions
-    std::function_ref<Int(int, int, int)> f(std::constant_arg<NeedsConversion{}>);
+    std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
     auto f_copy = std::move(f);
     assert(f_copy(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const> f2(std::constant_arg<NeedsConversion{}>);
+    std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
     auto f2_copy = std::move(f2);
     assert(f2_copy(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<NeedsConversion{}>);
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
     auto f3_copy = std::move(f3);
     assert(f3_copy(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<NeedsConversion{}>);
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
     auto f4_copy = std::move(f4);
     assert(f4_copy(1, 2, 3).i == 6);
   }
   {
     // with conversions function pointer
-    std::function_ref<Int(int, int, int)> f(std::constant_arg<&needs_conversion>);
+    std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
     auto f_copy = std::move(f);
     assert(f_copy(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const> f2(std::constant_arg<&needs_conversion>);
+    std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
     auto f2_copy = std::move(f2);
     assert(f2_copy(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) noexcept> f3(std::constant_arg<&needs_conversion>);
+    std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
     auto f3_copy = std::move(f3);
     assert(f3_copy(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const noexcept> f4(std::constant_arg<&needs_conversion>);
+    std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
     auto f4_copy = std::move(f4);
     assert(f4_copy(1, 2, 3).i == 6);
   }
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
index 45e6323dd2e52..95bbb7d2d0f25 100644
--- 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
@@ -37,16 +37,16 @@ constexpr int zero(Int, Int, Int) noexcept { return 0; }
 
 constexpr bool test() {
   {
-    std::function_ref<void()> f(std::constant_arg<[] {}>);
-    std::function_ref<void()> f2(std::constant_arg<[] {}>);
+    std::function_ref<void()> f(std::cw<[] {}>);
+    std::function_ref<void()> f2(std::cw<[] {}>);
     f2 = std::move(f);
     f();
     f2();
   }
   {
     // const
-    std::function_ref<int() const> f(std::constant_arg<[] { return 42; }>);
-    std::function_ref<int() const> f2(std::constant_arg<[] { return 41; }>);
+    std::function_ref<int() const> f(std::cw<[] { return 42; }>);
+    std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
     f2 = std::move(f);
 
     assert(f() == 42);
@@ -54,70 +54,70 @@ constexpr bool test() {
   }
   {
     // noexcept
-    std::function_ref<double(int, double) noexcept> f(std::constant_arg<&plus>);
-    std::function_ref<double(int, double) noexcept> f2(std::constant_arg<&minus>);
+    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);
     assert(f(1, 2.0) == 3.0);
     assert(f2(1, 2.0) == 3.0);
   }
   {
     // const noexcept
-    std::function_ref<double(int, double) const noexcept> f(std::constant_arg<&plus>);
-    std::function_ref<double(int, double) const noexcept> f2(std::constant_arg<&minus>);
+    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);
     assert(f(1, 2.0) == 3.0);
     assert(f2(1, 2.0) == 3.0);
   }
   {
     // with conversions
-    std::function_ref<Int(int, int, int)> f(std::constant_arg<[](int, int, int) { return Int{1}; }>);
-    std::function_ref<Int(int, int, int)> f2(std::constant_arg<NeedsConversion{}>);
+    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);
     assert(f(1, 2, 3).i == 6);
     assert(f2(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const> f_const(std::constant_arg<[](int, int, int) { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) const> f2_const(std::constant_arg<NeedsConversion{}>);
+    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);
     assert(f_const(1, 2, 3).i == 6);
     assert(f2_const(1, 2, 3).i == 6);
 
     std::function_ref<Int(int, int, int) noexcept> f_noexcept(
-        std::constant_arg<[](int, int, int) noexcept { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::constant_arg<NeedsConversion{}>);
+        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);
     assert(f_noexcept(1, 2, 3).i == 6);
     assert(f2_noexcept(1, 2, 3).i == 6);
 
     std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
-        std::constant_arg<[](int, int, int) noexcept { return Int{1}; }>);
-    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::constant_arg<NeedsConversion{}>);
+        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);
     assert(f_const_noexcept(1, 2, 3).i == 6);
     assert(f2_const_noexcept(1, 2, 3).i == 6);
   }
   {
     // with conversions function pointer
-    std::function_ref<Int(int, int, int)> f(std::constant_arg<&zero>);
-    std::function_ref<Int(int, int, int)> f2(std::constant_arg<&needs_conversion>);
+    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);
     assert(f(1, 2, 3).i == 6);
     assert(f2(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const> f_const(std::constant_arg<&zero>);
-    std::function_ref<Int(int, int, int) const> f2_const(std::constant_arg<&needs_conversion>);
+    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);
     assert(f_const(1, 2, 3).i == 6);
     assert(f2_const(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::constant_arg<&zero>);
-    std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::constant_arg<&needs_conversion>);
+    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);
     assert(f_noexcept(1, 2, 3).i == 6);
     assert(f2_noexcept(1, 2, 3).i == 6);
 
-    std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::constant_arg<&zero>);
-    std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::constant_arg<&needs_conversion>);
+    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);
     assert(f_const_noexcept(1, 2, 3).i == 6);
     assert(f2_const_noexcept(1, 2, 3).i == 6);
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
index 6b656c2d74c5d..d41f7145eaf02 100644
--- 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
@@ -64,19 +64,19 @@ void test_default() {
   {
     // reference
     int x = 42;
-    std::function_ref<int&(int&)> f(std::constant_arg<[](int& x) -> int& { return x; }>);
+    std::function_ref<int&(int&)> f(std::cw<[](int& x) -> int& { return x; }>);
     std::same_as<int&> decltype(auto) r = f(x);
     assert(&r == &x);
   }
   {
     // Move only
-    std::function_ref<MoveOnly(MoveOnly)> f(std::constant_arg<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+    std::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::constant_arg<[](int x) -> double { return x + 0.1; }>);
+    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);
   }
@@ -125,20 +125,20 @@ void test_const() {
   {
     // reference
     int x = 42;
-    std::function_ref<int&(int&) const> f(std::constant_arg<[](int& x) -> int& { return x; }>);
+    std::function_ref<int&(int&) const> f(std::cw<[](int& x) -> int& { return x; }>);
     std::same_as<int&> decltype(auto) r = f(x);
     assert(&r == &x);
   }
   {
     // Move only
     std::function_ref<MoveOnly(MoveOnly) const> f(
-        std::constant_arg<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+        std::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::constant_arg<[](int x) -> double { return x + 0.1; }>);
+    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);
   }
@@ -187,20 +187,20 @@ void test_noexcept() {
   {
     // reference
     int x = 42;
-    std::function_ref<int&(int&) noexcept> f(std::constant_arg<[](int& x) noexcept -> int& { return x; }>);
+    std::function_ref<int&(int&) noexcept> f(std::cw<[](int& x) noexcept -> int& { return x; }>);
     std::same_as<int&> decltype(auto) r = f(x);
     assert(&r == &x);
   }
   {
     // Move only
     std::function_ref<MoveOnly(MoveOnly) noexcept> f(
-        std::constant_arg<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
+        std::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::constant_arg<[](int x) noexcept -> double { return x + 0.1; }>);
+    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);
   }
@@ -249,20 +249,20 @@ void test_const_noexcept() {
   {
     // reference
     int x = 42;
-    std::function_ref<int&(int&) const noexcept> f(std::constant_arg<[](int& x) noexcept -> int& { return x; }>);
+    std::function_ref<int&(int&) const noexcept> f(std::cw<[](int& x) noexcept -> int& { return x; }>);
     std::same_as<int&> decltype(auto) r = f(x);
     assert(&r == &x);
   }
   {
     // Move only
     std::function_ref<MoveOnly(MoveOnly) const noexcept> f(
-        std::constant_arg<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
+        std::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::constant_arg<[](int x) noexcept -> double { return x + 0.1; }>);
+    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);
   }
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f290e35c594d0..73fade73993b4 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -698,7 +698,7 @@ 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"],
         },

>From 828d85bc38ad63da44c930e6492dd8ce2dbd0819 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 18 Apr 2026 15:05:22 +0100
Subject: [PATCH 25/26] tests for new papers

---
 .../include/__functional/function_ref_impl.h  |  4 ++-
 .../constant_arg.mandates.verify.cpp          | 34 +++++++++++++------
 .../func.wrap.ref.ctor/ref.pass.cpp           |  1 +
 3 files changed, 27 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index d9c57ac173ef0..b4e848c7c005d 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -117,7 +117,9 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
       static_assert(
           !requires {
             typename constant_wrapper<std::invoke(decltype(__f)::value, remove_cvref_t<_ArgTypes>::value...)>;
-          }, "function_ref will unwrap the callable from cw<f>, and it cannot produce a constant_wrapper result");
+          },
+          "function_ref argument types are all constexpr-param, and callable can be invoked with unwrapped arguments "
+          "and produces a result with a structural type");
     }
   }
 
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 47a5a514e6052..519957eec6878 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
@@ -8,14 +8,18 @@
 
 // REQUIRES: std-at-least-c++26
 
-// template<auto f> constexpr function_ref(constant_wrapper<f>) noexcept;
-// template<auto f, class U>
-//   constexpr function_ref(constant_wrapper<f>, U&& obj) noexcept;
-// template<auto f, class T>
-//   constexpr function_ref(constant_wrapper<f>, cv T* obj) noexcept;
+// 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>
 
@@ -23,26 +27,34 @@ 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 'nullptr != nullptr': the function pointer should not be a nullptr}}
+  // 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 'nullptr != nullptr': the function pointer should not be a nullptr}}
+  // 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 'nullptr != nullptr': the function pointer should not be a nullptr}}
+  // 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 'nullptr != nullptr': the function pointer should not be a nullptr}}
+  // 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 'nullptr != nullptr': the function pointer should not be a nullptr}}
+  // 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 'nullptr != nullptr': the function pointer should not be a nullptr}}
+  // 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/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 abf118cb66a30..d100c879b05ba 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
@@ -246,6 +246,7 @@ constexpr bool test() {
   }
 
   {
+    // P3961R1 Less double indirection in function_ref
     // double unwrapping
     std::function_ref<int() const noexcept> f1(&one);
     std::function_ref<int()> f2(f1);

>From 17ff44daafeed8e2f4997f6c93c0f1ec1e9d00cc Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 18 Apr 2026 15:07:50 +0100
Subject: [PATCH 26/26] format

---
 libcxx/modules/std/utility.inc                |  2 +-
 .../func.wrap.ref.ctor/constant_arg.pass.cpp  |  3 ++-
 .../constant_arg_ptr.pass.cpp                 | 19 ++++++++++++-------
 .../constant_arg_ref.pass.cpp                 | 17 +++++++++++------
 .../func.wrap.ref.ctor/copy_assign.pass.cpp   |  3 +--
 .../func.wrap.ref.ctor/move_assign.pass.cpp   |  3 +--
 .../func.wrap.ref.inv/invoke.pass.cpp         |  3 +--
 7 files changed, 29 insertions(+), 21 deletions(-)

diff --git a/libcxx/modules/std/utility.inc b/libcxx/modules/std/utility.inc
index 47c30c9022ade..62400c2b15179 100644
--- a/libcxx/modules/std/utility.inc
+++ b/libcxx/modules/std/utility.inc
@@ -90,8 +90,8 @@ export namespace std {
   using std::in_place_index_t;
 
 #if _LIBCPP_STD_VER >= 26
-  using std::cw;
   using std::constant_wrapper;
+  using std::cw;
 #endif // _LIBCPP_STD_VER >= 23
 
   // [depr.relops]
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp
index 9875996025b11..bccaa5cf71ff6 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg.pass.cpp
@@ -45,7 +45,8 @@ static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::
 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>>);
+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>>);
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ptr.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ptr.pass.cpp
index 3573d2a38413d..186549a3be98e 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ptr.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ptr.pass.cpp
@@ -83,7 +83,8 @@ static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::
 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*>);
+    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*>);
 
@@ -114,12 +115,14 @@ static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, s
 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_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() 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>,
@@ -130,11 +133,13 @@ static_assert(
 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*>);
+    !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::g_const_noexcept>, A*>);
+    !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>,
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp
index 8b7995d0d3c25..c901985f41cfc 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.ctor/constant_arg_ref.pass.cpp
@@ -95,7 +95,8 @@ static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::
 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&>);
+    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(
@@ -139,24 +140,28 @@ static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
 
 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(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>, 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&>);
+    !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::g_const_noexcept>, A&>);
+    !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(
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
index d449e4b2fbce6..19f4066664bd1 100644
--- 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
@@ -84,8 +84,7 @@ constexpr bool test() {
     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> 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;
     assert(f_noexcept(1, 2, 3).i == 6);
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
index 95bbb7d2d0f25..673879772d55a 100644
--- 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
@@ -82,8 +82,7 @@ constexpr bool test() {
     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> 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);
     assert(f_noexcept(1, 2, 3).i == 6);
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
index d41f7145eaf02..2dfff1ede8a18 100644
--- 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
@@ -131,8 +131,7 @@ void test_const() {
   }
   {
     // Move only
-    std::function_ref<MoveOnly(MoveOnly) const> f(
-        std::cw<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
+    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);
   }



More information about the libcxx-commits mailing list