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..77dcd034001aa 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 && __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; }