[libc++] Re-implement LWG2770 again * 2 (#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.
This commit is contained in:
parent
134cb8877e
commit
af267993a7
@ -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|","",""
|
||||
|
||||
|
@ -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
|
||||
|
||||
@ -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>(), "");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -23,6 +23,8 @@
|
||||
struct Dummy1 {};
|
||||
struct Dummy2 {};
|
||||
struct Dummy3 {};
|
||||
struct Dummy4 {};
|
||||
struct Dummy5 {};
|
||||
|
||||
template <>
|
||||
struct std::tuple_size<Dummy1> {
|
||||
@ -39,6 +41,16 @@ public:
|
||||
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 {{no member named 'value'}}
|
||||
(void)std::tuple_size<const Dummy3>::value; // expected-note {{here}}
|
||||
// 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 {{invalid use of non-static data member 'value'}}
|
||||
(void)std::tuple_size<const Dummy5>::value; // expected-note {{here}}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 @@ public:
|
||||
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 && __cpp_structured_bindings >= 202411L
|
||||
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 && __cpp_structured_bindings >= 202411L
|
||||
test_decomp_as_empty_pack();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user