-
Notifications
You must be signed in to change notification settings - Fork 13.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[libc++] Re-implement LWG2770 again * 2 #132598
Conversation
@llvm/pr-subscribers-libcxx Author: A. Jiang (frederick-vs-ja) Changes1013fe3 used to implement LWG2770, but cb0d4df 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:
Full diff: https://github.com/llvm/llvm-project/pull/132598.diff 5 Files Affected:
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;
}
|
1013fe3 used to implement LWG2770, but cb0d4df 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.
75ded26
to
cd71231
Compare
LIBCPP_ASSERT(p == -1); | ||
} | ||
|
||
#if TEST_STD_VER >= 26 && __cpp_structured_bindings >= 202411L |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#if TEST_STD_VER >= 26 && __cpp_structured_bindings >= 202411L | |
#if TEST_STD_VER >= 26 |
We normally just assume that TEST_STD_VER >= 26
implies that the language feature is available. Is there a reason to stray from that here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GCC hasn't implemented this feature yet, while I think it's worth testing empty structured bindings.
1013fe3 used to implement LWG2770, but cb0d4df 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: