[libcxx-commits] [libcxx] [libcxx] Implement `std::constant_wrapper` (PR #191695)
via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Apr 12 02:34:23 PDT 2026
https://github.com/huixie90 created https://github.com/llvm/llvm-project/pull/191695
None
>From c7078d8bdeb0c5d3359941e842df8dd5ee5670d3 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] [libcxx] Implement `std::constant_wrapper`
---
libcxx/docs/FeatureTestMacroTable.rst | 2 +
libcxx/docs/ReleaseNotes/23.rst | 2 +
libcxx/docs/Status/Cxx2cIssues.csv | 6 +-
libcxx/docs/Status/Cxx2cPapers.csv | 4 +-
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__utility/constant_wrapper.h | 326 ++++++++++++++++++
libcxx/include/module.modulemap.in | 1 +
libcxx/include/span | 2 +-
libcxx/include/utility | 17 +
libcxx/include/version | 2 +
.../utility.version.compile.pass.cpp | 27 ++
.../version.version.compile.pass.cpp | 27 ++
.../utilities/const.wrap.class/adl.pass.cpp | 0
.../utilities/const.wrap.class/call.pass.cpp | 196 +++++++++++
.../std/utilities/const.wrap.class/helpers.h | 23 ++
.../const.wrap.class/unary_ops.pass.cpp | 249 +++++++++++++
.../generate_feature_test_macro_components.py | 7 +
17 files changed, 886 insertions(+), 6 deletions(-)
create mode 100644 libcxx/include/__utility/constant_wrapper.h
create mode 100644 libcxx/test/std/utilities/const.wrap.class/adl.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/helpers.h
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 0f65770a4fa14..cbe55ffddb93b 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 aeabfeedfbc5e..13cf86eb25621 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -39,6 +39,8 @@ Implemented Papers
------------------
- P2440R1: ``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right`` (`Github <https://llvm.org/PR105184>`__)
+- 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 e17992f899b3b..d130e643658d5 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>`__",""
@@ -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 6eda9128fda48..28e8a3f87c12b 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..849c12385c9c5
--- /dev/null
+++ b/libcxx/include/__utility/constant_wrapper.h
@@ -0,0 +1,326 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <__algorithm/copy.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>
+
+#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;
+
+template <__cw_fixed_value _Xp, class = typename decltype(_Xp)::__type>
+struct constant_wrapper;
+
+template <class _Tp>
+concept __constexpr_param = requires { typename constant_wrapper<_Tp::value>; };
+
+struct __cw_operators;
+
+template <__cw_fixed_value _Xp>
+constexpr auto cw = constant_wrapper<_Xp>{};
+
+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];
+ _LIBCPP_HIDE_FROM_ABI constexpr __cw_fixed_value(_Tp (&__arr)[_Extent]) noexcept {
+ std::copy(__arr, __arr + _Extent, __data);
+ }
+ _Tp __data[_Extent];
+};
+
+template <class _Tp, size_t _Extent>
+__cw_fixed_value(_Tp (&)[_Extent]) -> __cw_fixed_value<_Tp[_Extent]>;
+
+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 = 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&&... __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&&... __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(auto)() 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 c43d36e569efb..7001adfef7f42 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/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 a1c8755af4ad9..e3e667d815774 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.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/adl.pass.cpp
new file mode 100644
index 0000000000000..e69de29bb2d1d
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..0cabc616c4771
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/call.pass.cpp
@@ -0,0 +1,196 @@
+//===----------------------------------------------------------------------===//
+//
+// 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"
+#include "test_macros.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; }
+
+// 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).
+
+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");
+
+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>);
+ assert(result == 3);
+ }
+
+ {
+ // nullary
+ using T = std::constant_wrapper<[] { return 42; }>;
+ std::same_as<std::constant_wrapper<42>> decltype(auto) result = T::operator()();
+ 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>);
+ assert(result);
+ }
+
+ {
+ // 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>);
+ 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);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ 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..f94d604731331
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/helpers.h
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/unary_ops.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/unary_ops.pass.cpp
new file mode 100644
index 0000000000000..0228cd41ddb49
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/unary_ops.pass.cpp
@@ -0,0 +1,249 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <functional>
+#include <type_traits>
+#include <utility>
+
+#include "helpers.h"
+#include "test_macros.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 OptsReturnNonStructural {
+ int value;
+
+ constexpr OptsReturnNonStructural(int v) : value(v) {}
+
+ friend constexpr auto operator+(OptsReturnNonStructural o) { return NonStructural{+o.value}; }
+ friend constexpr auto operator-(OptsReturnNonStructural o) { return NonStructural{-o.value}; }
+ friend constexpr auto operator~(OptsReturnNonStructural o) { return NonStructural{~o.value}; }
+ friend constexpr auto operator!(OptsReturnNonStructural o) { return NonStructural{!o.value}; }
+ friend constexpr auto operator&(OptsReturnNonStructural o) { return NonStructural{o.value + 42}; }
+ friend constexpr auto operator*(OptsReturnNonStructural 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<OptsReturnNonStructural{42}>>);
+static_assert(HasMinus<std::constant_wrapper<OptsReturnNonStructural{42}>>);
+static_assert(HasBitNot<std::constant_wrapper<OptsReturnNonStructural{42}>>);
+static_assert(HasNot<std::constant_wrapper<OptsReturnNonStructural{42}>>);
+static_assert(HasBitAnd<std::constant_wrapper<OptsReturnNonStructural{42}>>);
+static_assert(HasDeref<std::constant_wrapper<OptsReturnNonStructural{42}>>);
+
+static_assert(!HasNoexceptPlus<std::constant_wrapper<OptsReturnNonStructural{42}>>);
+static_assert(!HasNoexceptMinus<std::constant_wrapper<OptsReturnNonStructural{42}>>);
+static_assert(!HasNoexceptBitNot<std::constant_wrapper<OptsReturnNonStructural{42}>>);
+static_assert(!HasNoexceptNot<std::constant_wrapper<OptsReturnNonStructural{42}>>);
+static_assert(!HasNoexceptBitAnd<std::constant_wrapper<OptsReturnNonStructural{42}>>);
+static_assert(!HasNoexceptDeref<std::constant_wrapper<OptsReturnNonStructural{42}>>);
+
+constexpr bool test() {
+ {
+ // int
+ std::constant_wrapper<42> cw42;
+
+ std::same_as<std::constant_wrapper<42>> decltype(auto) result = +cw42;
+ assert(result == 42);
+
+ std::same_as<std::constant_wrapper<-42>> decltype(auto) result2 = -cw42;
+ assert(result2 == -42);
+
+ std::same_as<std::constant_wrapper<~42>> decltype(auto) result3 = ~cw42;
+ assert(result3 == ~42);
+
+ std::same_as<std::constant_wrapper<!42>> decltype(auto) result4 = !cw42;
+ assert(result4 == !42);
+
+ std::same_as<std::constant_wrapper<&cw42.value>> decltype(auto) result5 = &cw42;
+ assert(result5 == &cw42.value);
+ }
+
+ {
+ // WithOps
+ std::constant_wrapper<WithOps{42}> cwWithOps;
+
+ std::same_as<std::constant_wrapper<WithOps{42}>> decltype(auto) result = +cwWithOps;
+ assert(result.value.value == 42);
+
+ std::same_as<std::constant_wrapper<WithOps{-42}>> decltype(auto) result2 = -cwWithOps;
+ assert(result2.value.value == -42);
+
+ std::same_as<std::constant_wrapper<WithOps{~42}>> decltype(auto) result3 = ~cwWithOps;
+ assert(result3.value.value == ~42);
+
+ std::same_as<std::constant_wrapper<WithOps{!42}>> decltype(auto) result4 = !cwWithOps;
+ assert(result4.value.value == !42);
+
+ std::same_as<std::constant_wrapper<WithOps{84}>> decltype(auto) result5 = &cwWithOps;
+ assert(result5.value.value == 84);
+
+ std::same_as<std::constant_wrapper<WithOps{0}>> decltype(auto) result6 = *cwWithOps;
+ assert(result6.value.value == 0);
+ }
+
+ {
+ // Return non-structural type
+ // Will use underlying type's runtime operators
+ std::constant_wrapper<OptsReturnNonStructural{42}> cwOptsReturnNonStructural;
+
+ std::same_as<NonStructural> decltype(auto) result = +cwOptsReturnNonStructural;
+ assert(result.get() == 42);
+
+ std::same_as<NonStructural> decltype(auto) result2 = -cwOptsReturnNonStructural;
+ assert(result2.get() == -42);
+
+ std::same_as<NonStructural> decltype(auto) result3 = ~cwOptsReturnNonStructural;
+ assert(result3.get() == ~42);
+
+ std::same_as<NonStructural> decltype(auto) result4 = !cwOptsReturnNonStructural;
+ assert(result4.get() == !42);
+
+ std::same_as<NonStructural> decltype(auto) result5 = &cwOptsReturnNonStructural;
+ assert(result5.get() == 84);
+
+ std::same_as<NonStructural> decltype(auto) result6 = *cwOptsReturnNonStructural;
+ 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 c5f81ca172f5a..cc57cc8993f51 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": {
More information about the libcxx-commits
mailing list