Skip to content

Commit af26799

Browse files
[libc++] Re-implement LWG2770 again * 2 (#132598)
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.
1 parent 134cb88 commit af26799

File tree

5 files changed

+102
-15
lines changed

5 files changed

+102
-15
lines changed

libcxx/docs/Status/Cxx17Issues.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@
252252
"`LWG2760 <https://wg21.link/LWG2760>`__","non-const basic_string::data should not invalidate iterators","2016-11 (Issaquah)","|Complete|","",""
253253
"`LWG2765 <https://wg21.link/LWG2765>`__","Did LWG 1123 go too far?","2016-11 (Issaquah)","|Complete|","",""
254254
"`LWG2767 <https://wg21.link/LWG2767>`__","not_fn call_wrapper can form invalid types","2016-11 (Issaquah)","|Complete|","",""
255+
"`LWG2770 <https://wg21.link/LWG2770>`__","``tuple_size<const T>`` specialization is not SFINAE compatible and breaks decomposition declarations","2016-11 (Issaquah)","|Complete|","21",""
255256
"`LWG2771 <https://wg21.link/LWG2771>`__","Broken Effects of some basic_string::compare functions in terms of basic_string_view","2016-11 (Issaquah)","|Complete|","",""
256257
"`LWG2773 <https://wg21.link/LWG2773>`__","Making std::ignore constexpr","2016-11 (Issaquah)","|Complete|","",""
257258
"`LWG2777 <https://wg21.link/LWG2777>`__","basic_string_view::copy should use char_traits::copy","2016-11 (Issaquah)","|Complete|","",""

libcxx/include/__tuple/tuple_size.h

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,17 @@ template <class _Tp, class...>
3232
using __enable_if_tuple_size_imp _LIBCPP_NODEBUG = _Tp;
3333

3434
template <class _Tp>
35-
struct _LIBCPP_TEMPLATE_VIS tuple_size<__enable_if_tuple_size_imp< const _Tp,
36-
__enable_if_t<!is_volatile<_Tp>::value>,
37-
integral_constant<size_t, sizeof(tuple_size<_Tp>)>>>
35+
struct _LIBCPP_TEMPLATE_VIS tuple_size<
36+
__enable_if_tuple_size_imp<const _Tp, __enable_if_t<!is_volatile<_Tp>::value>, decltype(tuple_size<_Tp>::value)>>
3837
: public integral_constant<size_t, tuple_size<_Tp>::value> {};
3938

4039
template <class _Tp>
41-
struct _LIBCPP_TEMPLATE_VIS tuple_size<__enable_if_tuple_size_imp< volatile _Tp,
42-
__enable_if_t<!is_const<_Tp>::value>,
43-
integral_constant<size_t, sizeof(tuple_size<_Tp>)>>>
40+
struct _LIBCPP_TEMPLATE_VIS tuple_size<
41+
__enable_if_tuple_size_imp<volatile _Tp, __enable_if_t<!is_const<_Tp>::value>, decltype(tuple_size<_Tp>::value)>>
4442
: public integral_constant<size_t, tuple_size<_Tp>::value> {};
4543

4644
template <class _Tp>
47-
struct _LIBCPP_TEMPLATE_VIS
48-
tuple_size<__enable_if_tuple_size_imp<const volatile _Tp, integral_constant<size_t, sizeof(tuple_size<_Tp>)>>>
45+
struct _LIBCPP_TEMPLATE_VIS tuple_size<__enable_if_tuple_size_imp<const volatile _Tp, decltype(tuple_size<_Tp>::value)>>
4946
: public integral_constant<size_t, tuple_size<_Tp>::value> {};
5047

5148
#else

libcxx/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_incomplete.pass.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,11 @@ void test_complete() {
4545
template <class T>
4646
void test_incomplete() {
4747
static_assert(!is_complete<T>(), "");
48-
static_assert(!is_complete<const T>(), "");
49-
static_assert(!is_complete<volatile T>(), "");
50-
static_assert(!is_complete<const volatile T>(), "");
48+
// https://cplusplus.github.io/LWG/issue4040
49+
// It is controversial whether these specializations are incomplete.
50+
LIBCPP_STATIC_ASSERT(!is_complete<const T>(), "");
51+
LIBCPP_STATIC_ASSERT(!is_complete<volatile T>(), "");
52+
LIBCPP_STATIC_ASSERT(!is_complete<const volatile T>(), "");
5153
}
5254

5355

libcxx/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_incomplete.verify.cpp

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
struct Dummy1 {};
2424
struct Dummy2 {};
2525
struct Dummy3 {};
26+
struct Dummy4 {};
27+
struct Dummy5 {};
2628

2729
template <>
2830
struct std::tuple_size<Dummy1> {
@@ -39,6 +41,16 @@ struct std::tuple_size<Dummy2> {
3941
template <>
4042
struct std::tuple_size<Dummy3> {};
4143

44+
template <>
45+
struct std::tuple_size<Dummy4> {
46+
void value();
47+
};
48+
49+
template <>
50+
struct std::tuple_size<Dummy5> {
51+
size_t value;
52+
};
53+
4254
void f() {
4355
// Test that tuple_size<const T> is not incomplete when tuple_size<T>::value
4456
// is well-formed but not a constant expression.
@@ -53,9 +65,21 @@ void f() {
5365
(void)std::tuple_size<const Dummy2>::value; // expected-note {{here}}
5466
}
5567
// Test that tuple_size<const T> generates an error when tuple_size<T> is
56-
// complete but ::value isn't a constant expression convertible to size_t.
68+
// complete but has no ::value member.
69+
{
70+
// expected-error@*:* 1 {{implicit instantiation of undefined template}}
71+
(void)std::tuple_size<const Dummy3>::value;
72+
}
73+
// Test that tuple_size<const T> generates an error when tuple_size<T> has
74+
// the ::value member but tuple_size<T>::value is ill-formed.
75+
{
76+
// expected-error@*:* 1 {{implicit instantiation of undefined template}}
77+
(void)std::tuple_size<const Dummy4>::value;
78+
}
79+
// Test that tuple_size<const T> generates an error when tuple_size<T> has
80+
// the ::value member which is non-static.
5781
{
58-
// expected-error@*:* 1 {{no member named 'value'}}
59-
(void)std::tuple_size<const Dummy3>::value; // expected-note {{here}}
82+
// expected-error@*:* 1 {{invalid use of non-static data member 'value'}}
83+
(void)std::tuple_size<const Dummy5>::value; // expected-note {{here}}
6084
}
6185
}

libcxx/test/std/utilities/tuple/tuple.tuple/tuple.helper/tuple_size_structured_bindings.pass.cpp

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,36 @@ void test_decomp_array() {
109109
}
110110
}
111111

112+
struct TestLWG2770 {
113+
int n;
114+
};
115+
116+
template <>
117+
struct std::tuple_size<TestLWG2770> {};
118+
119+
void test_lwg_2770() {
120+
{
121+
auto [n] = TestLWG2770{42};
122+
assert(n == 42);
123+
}
124+
{
125+
const auto [n] = TestLWG2770{42};
126+
assert(n == 42);
127+
}
128+
{
129+
TestLWG2770 s{42};
130+
auto& [n] = s;
131+
assert(n == 42);
132+
assert(&n == &s.n);
133+
}
134+
{
135+
const TestLWG2770 s{42};
136+
auto& [n] = s;
137+
assert(n == 42);
138+
assert(&n == &s.n);
139+
}
140+
}
141+
112142
struct Test {
113143
int x;
114144
};
@@ -136,16 +166,49 @@ struct std::tuple_size<Test> {
136166
void test_after_tuple_size_specialization() {
137167
Test const t{99};
138168
auto& [p] = t;
139-
assert(p == -1);
169+
// https://cplusplus.github.io/LWG/issue4040
170+
// It is controversial whether std::tuple_size<const Test> is instantiated here or before.
171+
(void)p;
172+
LIBCPP_ASSERT(p == -1);
173+
}
174+
175+
#if TEST_STD_VER >= 26 && __cpp_structured_bindings >= 202411L
176+
struct InvalidWhenNoCv1 {};
177+
178+
template <>
179+
struct std::tuple_size<InvalidWhenNoCv1> {};
180+
181+
struct InvalidWhenNoCv2 {};
182+
183+
template <>
184+
struct std::tuple_size<InvalidWhenNoCv2> {
185+
void value();
186+
};
187+
188+
template <class = void>
189+
void test_decomp_as_empty_pack() {
190+
{
191+
const auto [... pack] = InvalidWhenNoCv1{};
192+
static_assert(sizeof...(pack) == 0);
193+
}
194+
{
195+
const auto [... pack] = InvalidWhenNoCv2{};
196+
static_assert(sizeof...(pack) == 0);
197+
}
140198
}
199+
#endif
141200

142201
int main(int, char**) {
143202
test_decomp_user_type();
144203
test_decomp_tuple();
145204
test_decomp_pair();
146205
test_decomp_array();
206+
test_lwg_2770();
147207
test_before_tuple_size_specialization();
148208
test_after_tuple_size_specialization();
209+
#if TEST_STD_VER >= 26 && __cpp_structured_bindings >= 202411L
210+
test_decomp_as_empty_pack();
211+
#endif
149212

150213
return 0;
151214
}

0 commit comments

Comments
 (0)