[libcxx-commits] [libcxx] [libc++] Re-implement LWG2770 again * 2 (PR #132598)
A. Jiang via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Mar 23 01:02:40 PDT 2025
https://github.com/frederick-vs-ja created https://github.com/llvm/llvm-project/pull/132598
1013fe3c0cfd7582e94ef2d4bfd79da7ea1a1289 used to implement LWG2770, but cb0d4df97490ec2d2b1cdf7574d26b1bc4063599 made LWG2770 unimplemented again because of CWG2386.
This patch re-implements LWG2770, while keeping the libc++-specific implementation strategy (which is controversial as noted in LWG4040).
Drive-by:
- Make the test coverage for the controversial part noted in LWG4040 libc++-only.
- Add the previously missed entry for LWG2770 to the documentation.
>From 75ded26412c7bf419ba4ad8a0ac362f1373667bc Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Sun, 23 Mar 2025 15:59:56 +0800
Subject: [PATCH] [libc++] Re-implement LWG2770 again * 2
1013fe3c0cfd7582e94ef2d4bfd79da7ea1a1289 used to implement LWG2770, but
cb0d4df97490ec2d2b1cdf7574d26b1bc4063599 made LWG2770 unimplemened
again because of CWG2386.
This patch re-implements LWG2770, while keeping the libc++-speific
implementation strategy (which is controversial as noted in LWG4040).
Drive-by:
- Make the test coverage for the controversial part noted in LWG4040
libc++-only.
- Add the previously missed entry for LWG2770 to the documentation.
---
libcxx/docs/Status/Cxx17Issues.csv | 1 +
libcxx/include/__tuple/tuple_size.h | 13 ++--
.../tuple_size_incomplete.pass.cpp | 8 ++-
.../tuple_size_incomplete.verify.cpp | 30 ++++++++-
.../tuple_size_structured_bindings.pass.cpp | 65 ++++++++++++++++++-
5 files changed, 102 insertions(+), 15 deletions(-)
diff --git a/libcxx/docs/Status/Cxx17Issues.csv b/libcxx/docs/Status/Cxx17Issues.csv
index d09b37f1d3900..3ca3e22845cfd 100644
--- a/libcxx/docs/Status/Cxx17Issues.csv
+++ b/libcxx/docs/Status/Cxx17Issues.csv
@@ -252,6 +252,7 @@
"`LWG2760 <https://wg21.link/LWG2760>`__","non-const basic_string::data should not invalidate iterators","2016-11 (Issaquah)","|Complete|","",""
"`LWG2765 <https://wg21.link/LWG2765>`__","Did LWG 1123 go too far?","2016-11 (Issaquah)","|Complete|","",""
"`LWG2767 <https://wg21.link/LWG2767>`__","not_fn call_wrapper can form invalid types","2016-11 (Issaquah)","|Complete|","",""
+"`LWG2770 <https://wg21.link/LWG2770>`__","``tuple_size<const T>`` specialization is not SFINAE compatible and breaks decomposition declarations","2016-11 (Issaquah)","|Complete|","21",""
"`LWG2771 <https://wg21.link/LWG2771>`__","Broken Effects of some basic_string::compare functions in terms of basic_string_view","2016-11 (Issaquah)","|Complete|","",""
"`LWG2773 <https://wg21.link/LWG2773>`__","Making std::ignore constexpr","2016-11 (Issaquah)","|Complete|","",""
"`LWG2777 <https://wg21.link/LWG2777>`__","basic_string_view::copy should use char_traits::copy","2016-11 (Issaquah)","|Complete|","",""
diff --git a/libcxx/include/__tuple/tuple_size.h b/libcxx/include/__tuple/tuple_size.h
index 27d57eb56ba6b..035ffa3655bc1 100644
--- a/libcxx/include/__tuple/tuple_size.h
+++ b/libcxx/include/__tuple/tuple_size.h
@@ -32,20 +32,17 @@ template <class _Tp, class...>
using __enable_if_tuple_size_imp _LIBCPP_NODEBUG = _Tp;
template <class _Tp>
-struct _LIBCPP_TEMPLATE_VIS tuple_size<__enable_if_tuple_size_imp< const _Tp,
- __enable_if_t<!is_volatile<_Tp>::value>,
- integral_constant<size_t, sizeof(tuple_size<_Tp>)>>>
+struct _LIBCPP_TEMPLATE_VIS tuple_size<
+ __enable_if_tuple_size_imp<const _Tp, __enable_if_t<!is_volatile<_Tp>::value>, decltype(tuple_size<_Tp>::value)>>
: public integral_constant<size_t, tuple_size<_Tp>::value> {};
template <class _Tp>
-struct _LIBCPP_TEMPLATE_VIS tuple_size<__enable_if_tuple_size_imp< volatile _Tp,
- __enable_if_t<!is_const<_Tp>::value>,
- integral_constant<size_t, sizeof(tuple_size<_Tp>)>>>
+struct _LIBCPP_TEMPLATE_VIS tuple_size<
+ __enable_if_tuple_size_imp<volatile _Tp, __enable_if_t<!is_const<_Tp>::value>, decltype(tuple_size<_Tp>::value)>>
: public integral_constant<size_t, tuple_size<_Tp>::value> {};
template <class _Tp>
-struct _LIBCPP_TEMPLATE_VIS
-tuple_size<__enable_if_tuple_size_imp<const volatile _Tp, integral_constant<size_t, sizeof(tuple_size<_Tp>)>>>
+struct _LIBCPP_TEMPLATE_VIS tuple_size<__enable_if_tuple_size_imp<const volatile _Tp, decltype(tuple_size<_Tp>::value)>>
: public integral_constant<size_t, tuple_size<_Tp>::value> {};
#else
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_incomplete.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_incomplete.pass.cpp
index a2cb2e989a571..bef4486d586b3 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_incomplete.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_incomplete.pass.cpp
@@ -45,9 +45,11 @@ void test_complete() {
template <class T>
void test_incomplete() {
static_assert(!is_complete<T>(), "");
- static_assert(!is_complete<const T>(), "");
- static_assert(!is_complete<volatile T>(), "");
- static_assert(!is_complete<const volatile T>(), "");
+ // https://cplusplus.github.io/LWG/issue4040
+ // It is controversial whether these specializations are incomplete.
+ LIBCPP_STATIC_ASSERT(!is_complete<const T>(), "");
+ LIBCPP_STATIC_ASSERT(!is_complete<volatile T>(), "");
+ LIBCPP_STATIC_ASSERT(!is_complete<const volatile T>(), "");
}
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_incomplete.verify.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_incomplete.verify.cpp
index 532ccd730ffc1..cc6af820c930b 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_incomplete.verify.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_incomplete.verify.cpp
@@ -23,6 +23,8 @@
struct Dummy1 {};
struct Dummy2 {};
struct Dummy3 {};
+struct Dummy4 {};
+struct Dummy5 {};
template <>
struct std::tuple_size<Dummy1> {
@@ -39,6 +41,16 @@ struct std::tuple_size<Dummy2> {
template <>
struct std::tuple_size<Dummy3> {};
+template <>
+struct std::tuple_size<Dummy4> {
+ void value();
+};
+
+template <>
+struct std::tuple_size<Dummy5> {
+ size_t value;
+};
+
void f() {
// Test that tuple_size<const T> is not incomplete when tuple_size<T>::value
// is well-formed but not a constant expression.
@@ -53,9 +65,21 @@ void f() {
(void)std::tuple_size<const Dummy2>::value; // expected-note {{here}}
}
// Test that tuple_size<const T> generates an error when tuple_size<T> is
- // complete but ::value isn't a constant expression convertible to size_t.
+ // complete but has no ::value member.
+ {
+ // expected-error@*:* 1 {{implicit instantiation of undefined template}}
+ (void)std::tuple_size<const Dummy3>::value;
+ }
+ // Test that tuple_size<const T> generates an error when tuple_size<T> has
+ // the ::value member but tuple_size<T>::value is ill-formed.
+ {
+ // expected-error@*:* 1 {{implicit instantiation of undefined template}}
+ (void)std::tuple_size<const Dummy4>::value;
+ }
+ // Test that tuple_size<const T> generates an error when tuple_size<T> has
+ // the ::value member which is non-static.
{
- // expected-error@*:* 1 {{no member named 'value'}}
- (void)std::tuple_size<const Dummy3>::value; // expected-note {{here}}
+ // expected-error@*:* 1 {{invalid use of non-static data member 'value'}}
+ (void)std::tuple_size<const Dummy5>::value; // expected-note {{here}}
}
}
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_structured_bindings.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_structured_bindings.pass.cpp
index 2324da3ad5f3c..9016421da15eb 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_structured_bindings.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_structured_bindings.pass.cpp
@@ -109,6 +109,36 @@ void test_decomp_array() {
}
}
+struct TestLWG2770 {
+ int n;
+};
+
+template <>
+struct std::tuple_size<TestLWG2770> {};
+
+void test_lwg_2770() {
+ {
+ auto [n] = TestLWG2770{42};
+ assert(n == 42);
+ }
+ {
+ const auto [n] = TestLWG2770{42};
+ assert(n == 42);
+ }
+ {
+ TestLWG2770 s{42};
+ auto& [n] = s;
+ assert(n == 42);
+ assert(&n == &s.n);
+ }
+ {
+ const TestLWG2770 s{42};
+ auto& [n] = s;
+ assert(n == 42);
+ assert(&n == &s.n);
+ }
+}
+
struct Test {
int x;
};
@@ -136,16 +166,49 @@ struct std::tuple_size<Test> {
void test_after_tuple_size_specialization() {
Test const t{99};
auto& [p] = t;
- assert(p == -1);
+ // https://cplusplus.github.io/LWG/issue4040
+ // It is controversial whether std::tuple_size<const Test> is instantiated here or before.
+ (void)p;
+ LIBCPP_ASSERT(p == -1);
+}
+
+#if TEST_STD_VER >= 26
+struct InvalidWhenNoCv1 {};
+
+template <>
+struct std::tuple_size<InvalidWhenNoCv1> {};
+
+struct InvalidWhenNoCv2 {};
+
+template <>
+struct std::tuple_size<InvalidWhenNoCv2> {
+ void value();
+};
+
+template <class = void>
+void test_decomp_as_empty_pack() {
+ {
+ const auto [... pack] = InvalidWhenNoCv1{};
+ static_assert(sizeof...(pack) == 0);
+ }
+ {
+ const auto [... pack] = InvalidWhenNoCv2{};
+ static_assert(sizeof...(pack) == 0);
+ }
}
+#endif
int main(int, char**) {
test_decomp_user_type();
test_decomp_tuple();
test_decomp_pair();
test_decomp_array();
+ test_lwg_2770();
test_before_tuple_size_specialization();
test_after_tuple_size_specialization();
+#if TEST_STD_VER >= 26
+ test_decomp_as_empty_pack();
+#endif
return 0;
}
More information about the libcxx-commits
mailing list