[libc++] Implement P4206R0 Revert string support in std::constant_wrapper#203338
Conversation
|
@llvm/pr-subscribers-libcxx Author: Yihan Wang (yronglin) ChangesFixes #203336 Full diff: https://github.com/llvm/llvm-project/pull/203338.diff 12 Files Affected:
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 2132e80251657..c85c025ae88ab 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -216,3 +216,4 @@
"`P4159R0 <https://wg21.link/P4159R0>`__","Make ``sender_in`` and ``receiver_of`` exposition-only","2026-03 (Croydon)","","","`#189632 <https://github.com/llvm/llvm-project/issues/189632>`__",""
"`P4154R0 <https://wg21.link/P4154R0>`__","Renaming various execution things","2026-03 (Croydon)","","","`#189633 <https://github.com/llvm/llvm-project/issues/189633>`__",""
"","","","","","",""
+"`P3726R2 <https://wg21.link/4206R0>`__","Revert string support in ``std::constant_wrapper``","2026-06 (Brno)","|Complete|","23","`<TBD>`__",""
diff --git a/libcxx/include/__utility/constant_wrapper.h b/libcxx/include/__utility/constant_wrapper.h
index 6bae95fc1878a..3f490be6f8bf5 100644
--- a/libcxx/include/__utility/constant_wrapper.h
+++ b/libcxx/include/__utility/constant_wrapper.h
@@ -14,6 +14,7 @@
#include <__functional/invoke.h>
#include <__type_traits/invoke.h>
#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_same.h>
#include <__type_traits/remove_cvref.h>
#include <__utility/declval.h>
#include <__utility/forward.h>
@@ -27,36 +28,12 @@ _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,
+template <auto _Xp,
# ifdef _LIBCPP_COMPILER_GCC
- // gcc bug: https://gcc.gnu.org/PR117392
- class = typename decltype(__cw_fixed_value(_Xp))::__type
+ // gcc bug: https://gcc.gnu.org/PR117392
+ class = remove_cvref_t<decltype(_Xp)>,
# else
- class = typename decltype(_Xp)::__type
+ class = decltype(_Xp)
# endif
>
struct constant_wrapper;
@@ -64,7 +41,7 @@ struct constant_wrapper;
template <class _Tp>
concept __constexpr_param = requires { typename constant_wrapper<_Tp::value>; };
-template <__cw_fixed_value _Xp>
+template <auto _Xp>
constexpr auto cw = constant_wrapper<_Xp>{};
struct __cw_operators {
@@ -288,11 +265,20 @@ concept __constexpr_indexable = (__constexpr_param<remove_cvref_t<_Args>> && ...
typename constant_wrapper<_Obj[remove_cvref_t<_Args>::value...]>;
};
-template <__cw_fixed_value _Xp, class>
+template <auto _Xp, class _Tp>
struct constant_wrapper : __cw_operators {
- static constexpr const auto& value = _Xp.__data;
+# ifdef _LIB_CPP_COMPILER_GCC
+ // gcc bug: https://gcc.gnu.org/PR125188
+ static constexpr decltype((_Xp)) value = (_Xp);
+# else
+ static constexpr decltype(auto) value = (_Xp);
+# endif
+
using type = constant_wrapper;
- using value_type = decltype(_Xp)::__type;
+ using value_type = decltype(_Xp);
+
+ static_assert(is_same_v<_Tp, value_type>,
+ "the second template parameter of std::constant_wrapper must be its value_type");
template <__constexpr_param _Rp>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator=(_Rp) const noexcept
diff --git a/libcxx/include/utility b/libcxx/include/utility
index e0c97efc6424d..940dc4f354984 100644
--- a/libcxx/include/utility
+++ b/libcxx/include/utility
@@ -196,10 +196,7 @@ 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>
+template<auto X, class = decltype(X)>
struct constant_wrapper; // since C++26
template<class T>
@@ -208,7 +205,7 @@ template<class T>
struct cw-operators; // exposition only, since C++26
-template<cw-fixed-value X>
+template<auto X>
constexpr auto cw = constant_wrapper<X>{}; // since C++26
// C++14
diff --git a/libcxx/test/std/utilities/const.wrap.class/convert.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/convert.pass.cpp
index a9567cb08b009..f7c59401dcd2a 100644
--- a/libcxx/test/std/utilities/const.wrap.class/convert.pass.cpp
+++ b/libcxx/test/std/utilities/const.wrap.class/convert.pass.cpp
@@ -27,11 +27,10 @@ constexpr bool test() {
{
// int conversion
std::constant_wrapper<6> cw6;
- const int& result = cw6;
+ int result = cw6;
assert(result == 6);
- assert(&result == &cw6.value);
- static_assert(noexcept(static_cast<const int&>(cw6)));
+ static_assert(noexcept(static_cast<int>(cw6)));
}
{
@@ -45,19 +44,6 @@ constexpr bool test() {
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; };
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
deleted file mode 100644
index 7210d7e2152d5..0000000000000
--- a/libcxx/test/std/utilities/const.wrap.class/ctad.compile.pass.cpp
+++ /dev/null
@@ -1,32 +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
-
-// 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
index 4f660ee378239..8327076c337ec 100644
--- a/libcxx/test/std/utilities/const.wrap.class/cw.pass.cpp
+++ b/libcxx/test/std/utilities/const.wrap.class/cw.pass.cpp
@@ -10,12 +10,13 @@
// constant_wrapper
-// template<cw-fixed-value X>
+// template<auto X>
// constexpr auto cw = constant_wrapper<X>{};
#include <cassert>
#include <concepts>
#include <utility>
+#include <cstddef>
struct S {
int value;
@@ -41,7 +42,7 @@ constexpr bool test() {
{
// array constant
- constexpr int arr[] = {1, 2, 3};
+ constexpr static 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);
@@ -49,16 +50,6 @@ constexpr bool test() {
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;
}
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
deleted file mode 100644
index 8bccfcfaaaa43..0000000000000
--- a/libcxx/test/std/utilities/const.wrap.class/cw_fixed.array.ctor.pass.cpp
+++ /dev/null
@@ -1,86 +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
-
-// 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
deleted file mode 100644
index 30c2952f57d4c..0000000000000
--- a/libcxx/test/std/utilities/const.wrap.class/cw_fixed.ctor.pass.cpp
+++ /dev/null
@@ -1,70 +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
-
-// 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/subscript.pass.cpp b/libcxx/test/std/utilities/const.wrap.class/subscript.pass.cpp
index 7efefa85fed95..bfa77a4662afc 100644
--- a/libcxx/test/std/utilities/const.wrap.class/subscript.pass.cpp
+++ b/libcxx/test/std/utilities/const.wrap.class/subscript.pass.cpp
@@ -171,12 +171,6 @@ constexpr bool test() {
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>;
diff --git a/libcxx/test/std/utilities/const.wrap.class/template.verify.cpp b/libcxx/test/std/utilities/const.wrap.class/template.verify.cpp
new file mode 100644
index 0000000000000..f8902212299ec
--- /dev/null
+++ b/libcxx/test/std/utilities/const.wrap.class/template.verify.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+#include <utility>
+
+// expected-error@+1 {{pointer to subobject of string literal is not allowed in a template argument}}
+std::constant_wrapper<"hello"> string_literal;
+
+// expected-error-re@*:* {{static assertion failed{{.*}}the second template parameter of std::constant_wrapper must be its value_type}}
+std::constant_wrapper<1, float> wrong_type1; // expected-note {{in instantiation of template class}}
+// expected-error-re@*:* {{static assertion failed{{.*}}the second template parameter of std::constant_wrapper must be its value_type}}
+std::constant_wrapper<1.0, int> wrong_type2; // expected-note {{in instantiation of template class}}
+// expected-error-re@*:* {{static assertion failed{{.*}}the second template parameter of std::constant_wrapper must be its value_type}}
+std::constant_wrapper<1, const int> wrong_type3; // expected-note {{in instantiation of template class}}
+// expected-error-re@*:* {{static assertion failed{{.*}}the second template parameter of std::constant_wrapper must be its value_type}}
+std::constant_wrapper<1, const int&> wrong_type4; // expected-note {{in instantiation of template class}}
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
index 96dce9e055209..cfaf2223875c3 100644
--- a/libcxx/test/std/utilities/const.wrap.class/types.compile.pass.cpp
+++ b/libcxx/test/std/utilities/const.wrap.class/types.compile.pass.cpp
@@ -10,16 +10,15 @@
// constant_wrapper
-// static constexpr const auto & value = X.data;
+// static constexpr decltype(auto) value = (X);
// using type = constant_wrapper;
-// using value_type = decltype(X)::type;
+// using value_type = decltype(X);
-#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<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>);
@@ -32,7 +31,15 @@ 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]>);
+template <auto V>
+consteval bool value_ref_to_template_parameter_object() {
+ return &V == &std::constant_wrapper<V>::value;
+}
+
+static_assert(value_ref_to_template_parameter_object<S{5}>());
+
+constexpr int arr[] = {1, 2, 3, 4, 5};
+
+static_assert(std::constant_wrapper<arr>::value == arr);
+static_assert(std::same_as<std::constant_wrapper<arr>::type, std::constant_wrapper<arr>>);
+static_assert(std::same_as<std::constant_wrapper<arr>::value_type, const int *>);
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index d8fb575b97b2f..64e1f1cb7f9a7 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -337,7 +337,7 @@ def add_version_header(tc):
{
"name": "__cpp_lib_constant_wrapper",
"values": {
- "c++26": 202603,
+ "c++26": 202606,
},
"headers": ["utility"],
},
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
…pper Signed-off-by: yronglin <yronglin777@gmail.com>
|
The AIX pipeline failed due to a clang bug: #202693, it's use clang-21 which not include this fix. |
Signed-off-by: yronglin <yronglin777@gmail.com>
Signed-off-by: yronglin <yronglin777@gmail.com>
|
It doesn't seem possible to work around bug #151531 (fixed in Clang 22) for Clang 21, which is very unfortunate. A possible resolution might be temporarily skipping this line for Clang 21: Per https://libcxx.llvm.org/#platform-and-compiler-support, perhaps we don't need to support Clang 21 when LLVM 23 is released, but I'm not very sure. I still want to land this in LLVM 23. |
|
|
||
| template <__cw_fixed_value _Xp, | ||
| template <auto _Xp, | ||
| # ifdef _LIBCPP_COMPILER_GCC |
There was a problem hiding this comment.
i think the original gcc bug is due to the missing CTAD conversion to cw-fixed-value. since we are removing it, I think it is very likely we don't need this branch anymore
There was a problem hiding this comment.
Yeah. The first _LIBCPP_COMPILER_GCC branch can be removed. Although the second is still necessary.
There was a problem hiding this comment.
Yeah, I remove all the 2 special GCC patch. But need to change decltype(auto) value = (_Xp) to decltype((_Xp)) value = (_Xp).
Signed-off-by: yronglin <yronglin777@gmail.com>
Hmm, I missed this one, let me add it. |
Signed-off-by: yronglin <yronglin777@gmail.com>
Signed-off-by: yronglin <yronglin777@gmail.com>
Signed-off-by: yronglin <yronglin777@gmail.com>
Signed-off-by: yronglin <yronglin777@gmail.com>
Signed-off-by: yronglin <yronglin777@gmail.com>
frederick-vs-ja
left a comment
There was a problem hiding this comment.
LGTM with nits, assuming CI passes on macOS. CI failures on Windows were unrelated.
Co-authored-by: A. Jiang <de34@live.cn>
Fixes #203336