From cb6bccc33f46cba5bbdba5f3883acde1a1f4f83e Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Mon, 13 Jul 2026 22:38:17 +0800 Subject: [PATCH] [libc++] Backport P3029R1 Better `mdspan`'s CTAD to C++20/23 (#208132) Also backport - support for `T::value` of a reference type in `integral-constant-like` from P2781R9 `std::constant_wrapper`, and - LWG4351 _`integral-constant-like`_ needs more `remove_cvref_t`. P3029R1 (and its further fixes) should be treated as a Defect Report against C++20/23 to avoid behavior difference in different modes. Other implementations will do or are already doing the same. --- libcxx/docs/Status/Cxx26Papers.csv | 2 +- libcxx/include/__mdspan/extents.h | 6 ----- libcxx/include/__mdspan/mdspan.h | 7 ----- libcxx/include/mdspan | 6 +---- libcxx/include/span | 17 ++++-------- .../views/mdspan/extents/ctad.pass.cpp | 13 ++++++--- .../views/mdspan/mdspan/deduction.pass.cpp | 19 ++++++++----- .../views.span/span.cons/deduct.pass.cpp | 27 ++++++++++++++----- 8 files changed, 49 insertions(+), 48 deletions(-) diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv index efc98495d1d5..2522dd98e4b9 100644 --- a/libcxx/docs/Status/Cxx26Papers.csv +++ b/libcxx/docs/Status/Cxx26Papers.csv @@ -61,7 +61,7 @@ "`P1068R11 `__","Vector API for random number generation","2024-03 (Tokyo)","","","`#105423 `__","" "`P2944R3 `__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Complete|","22","`#105424 `__","The changes to ``tuple``'s equality overload from P2165R4 are not yet implemented." "`P2642R6 `__","Padded ``mdspan`` layouts","2024-03 (Tokyo)","","","`#105425 `__","" -"`P3029R1 `__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 `__","" +"`P3029R1 `__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 `__","Implemented as a DR against C++20 since LLVM 23." "","","","","","","" "`P2747R2 `__","``constexpr`` placement new","2024-06 (St. Louis)","|Complete|","20","`#105427 `__","" "`P2997R1 `__","Removing the common reference requirement from the indirectly invocable concepts","2024-06 (St. Louis)","|Complete|","19","`#105428 `__","Implemented as a DR against C++20. (MSVC STL and libstdc++ will do the same.)" diff --git a/libcxx/include/__mdspan/extents.h b/libcxx/include/__mdspan/extents.h index bc4490aa5b47..99366b9ca9cc 100644 --- a/libcxx/include/__mdspan/extents.h +++ b/libcxx/include/__mdspan/extents.h @@ -466,15 +466,9 @@ using dims = dextents<_IndexType, _Rank>; # endif // Deduction guide for extents -# if _LIBCPP_STD_VER >= 26 template requires(is_convertible_v<_IndexTypes, size_t> && ...) explicit extents(_IndexTypes...) -> extents...>; -# else -template - requires(is_convertible_v<_IndexTypes, size_t> && ...) -explicit extents(_IndexTypes...) -> extents; -# endif namespace __mdspan_detail { diff --git a/libcxx/include/__mdspan/mdspan.h b/libcxx/include/__mdspan/mdspan.h index bcfa302231cd..3b78f792770f 100644 --- a/libcxx/include/__mdspan/mdspan.h +++ b/libcxx/include/__mdspan/mdspan.h @@ -312,17 +312,10 @@ private: # endif }; -# if _LIBCPP_STD_VER >= 26 template requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0)) explicit mdspan(_ElementType*, _OtherIndexTypes...) -> mdspan<_ElementType, extents...>>; -# else -template - requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0)) -explicit mdspan(_ElementType*, _OtherIndexTypes...) - -> mdspan<_ElementType, dextents>; -# endif template requires(is_pointer_v>) diff --git a/libcxx/include/mdspan b/libcxx/include/mdspan index 2f0e1f9b8775..6313c03f041a 100644 --- a/libcxx/include/mdspan +++ b/libcxx/include/mdspan @@ -411,14 +411,10 @@ namespace std { mdspan(Pointer&&) -> mdspan>, extents>; - template - requires ((is_convertible_v && ...) && sizeof...(Integrals) > 0) - explicit mdspan(ElementType*, Integrals...) - -> mdspan>; // until C++26 template requires((is_convertible_v && ...) && sizeof...(Integrals) > 0) explicit mdspan(ElementType*, Integrals...) - -> mdspan...>>; // since C++26 + -> mdspan...>>; template mdspan(ElementType*, span) diff --git a/libcxx/include/span b/libcxx/include/span index e39b811f9b1e..b05a953829b5 100644 --- a/libcxx/include/span +++ b/libcxx/include/span @@ -19,16 +19,16 @@ namespace std { inline constexpr size_t dynamic_extent = numeric_limits::max(); template - concept integral-constant-like = // exposition only, since C++26 - is_integral_v && - !is_same_v> && + concept integral-constant-like = // exposition only + is_integral_v> && + !is_same_v> && convertible_to && equality_comparable_with && bool_constant::value && bool_constant(T()) == T::value>::value; template - constexpr size_t maybe-static-ext = dynamic_extent; // exposition only, since C++26 + constexpr size_t maybe-static-ext = dynamic_extent; // exposition only template constexpr size_t maybe-static-ext = {T::value}; @@ -123,9 +123,7 @@ private: }; template - span(It, EndOrSize) -> span>>; // until C++26 -template - span(It, EndOrSize) -> span>, maybe-static-ext>; // since C++26 + span(It, EndOrSize) -> span>, maybe-static-ext>; template span(T (&)[N]) -> span; @@ -597,7 +595,6 @@ template return __s.__as_writable_bytes(); } -# if _LIBCPP_STD_VER >= 26 template concept __integral_constant_like = is_integral_v> && !is_same_v> && @@ -613,10 +610,6 @@ inline constexpr size_t __maybe_static_ext<_Tp> = {_Tp::value}; template span(_It, _EndOrSize) -> span>, __maybe_static_ext<_EndOrSize>>; -# else -template -span(_It, _EndOrSize) -> span>>; -# endif template span(_Tp (&)[_Sz]) -> span<_Tp, _Sz>; diff --git a/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp index b72c3e050b1f..e34113f91ad0 100644 --- a/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp +++ b/libcxx/test/std/containers/views/mdspan/extents/ctad.pass.cpp @@ -13,8 +13,7 @@ // explicit extents(Integrals...) -> see below; // Constraints: (is_convertible_v && ...) is true. // -// Remarks: The deduced type is dextents. // until C++26 -// Remarks: The deduced type is extents...>. // since C++26 +// Remarks: The deduced type is extents...>. #include #include @@ -31,6 +30,9 @@ struct NoDefaultCtorIndex { constexpr operator size_t() const noexcept { return value; } }; +template +using size_ref_constant = std::integral_constant::value>; + template constexpr void test(E e, Expected expected) { ASSERT_SAME_TYPE(E, Expected); @@ -47,13 +49,16 @@ constexpr bool test() { std::extents(1, 2u, 3, 4, 5, 6, 7, 8, 9)); test(std::extents(NoDefaultCtorIndex{1}, NoDefaultCtorIndex{2}), std::extents(1, 2)); -#if _LIBCPP_STD_VER >= 26 // P3029R1: deduction from `integral_constant` test(std::extents(std::integral_constant{}), std::extents()); test(std::extents(std::integral_constant{}, 6), std::extents(6)); test(std::extents(std::integral_constant{}, 6, std::integral_constant{}), std::extents(6)); -#endif + // support for `T::value` of a reference type from P2781R9 `std::constant_wrapper` + test(std::extents(size_ref_constant<5>{}), std::extents()); + test(std::extents(size_ref_constant<5>{}, 6), std::extents(6)); + test(std::extents(std::integral_constant{}, 6, size_ref_constant<7>{}), + std::extents(6)); return true; } diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp index 762841016e29..6c12ccb7fd86 100644 --- a/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp +++ b/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp @@ -22,11 +22,7 @@ // template // requires((is_convertible_v && ...) && sizeof...(Integrals) > 0) // explicit mdspan(ElementType*, Integrals...) -// -> mdspan>; // until C++26 -// template -// requires((is_convertible_v && ...) && sizeof...(Integrals) > 0) -// explicit mdspan(ElementType*, Integrals...) -// -> mdspan...>>; // since C++26 +// -> mdspan...>>; // // template // mdspan(ElementType*, span) @@ -53,6 +49,7 @@ #include #include +#include #include #include // dynamic_extent #include @@ -98,6 +95,9 @@ struct SizeTIntType { constexpr operator size_t() const noexcept { return size_t(val); } }; +template +using size_ref_constant = std::integral_constant::value>; + template requires(sizeof(decltype(std::mdspan(std::declval(), 10))) > 0) constexpr bool test_no_layout_deduction_guides(const H& handle, const A&) { @@ -107,7 +107,6 @@ constexpr bool test_no_layout_deduction_guides(const H& handle, const A&) { // deduction from pointer and integral like ASSERT_SAME_TYPE(decltype(std::mdspan(handle, 5, SizeTIntType(6))), std::mdspan>); -#if _LIBCPP_STD_VER >= 26 // P3029R1: deduction from `integral_constant` ASSERT_SAME_TYPE( decltype(std::mdspan(handle, std::integral_constant{})), std::mdspan>); @@ -117,7 +116,13 @@ constexpr bool test_no_layout_deduction_guides(const H& handle, const A&) { decltype(std::mdspan( handle, std::integral_constant{}, std::dynamic_extent, std::integral_constant{})), std::mdspan>); -#endif + // support for `T::value` of a reference type from P2781R9 `std::constant_wrapper` + ASSERT_SAME_TYPE(decltype(std::mdspan(handle, size_ref_constant<5>{})), std::mdspan>); + ASSERT_SAME_TYPE(decltype(std::mdspan(handle, size_ref_constant<5>{}, std::dynamic_extent)), + std::mdspan>); + ASSERT_SAME_TYPE(decltype(std::mdspan( + handle, std::integral_constant{}, std::dynamic_extent, size_ref_constant<7>{})), + std::mdspan>); std::array exts; // deduction from pointer and array diff --git a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp index 9e41afad2258..35d9b36f824a 100644 --- a/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp +++ b/libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp @@ -11,10 +11,7 @@ // // template -// span(It, EndOrSize) -> span>>; // until C++26 -// template -// span(It, EndOrSize) -> span>, -// maybe-static-ext>; // since C++26 +// span(It, EndOrSize) -> span>, maybe-static-ext>; // template // span(T (&)[N]) -> span; // template @@ -27,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -50,7 +48,6 @@ void test_iterator_sentinel() { assert(s.data() == std::data(arr)); } -#if TEST_STD_VER >= 26 // P3029R1: deduction from `integral_constant` { std::span s{std::begin(arr), std::integral_constant{}}; @@ -58,15 +55,33 @@ void test_iterator_sentinel() { assert(s.size() == 3); assert(s.data() == std::data(arr)); } - + // support for `T::value` of a reference type from P2781R9 `std::constant_wrapper` + { + using size_3_ref_type = std::integral_constant::value>; + std::span s{std::begin(arr), size_3_ref_type{}}; + ASSERT_SAME_TYPE(decltype(s), std::span); + assert(s.size() == 3); + assert(s.data() == std::data(arr)); + } +#if TEST_STD_VER >= 26 { std::span s{std::begin(arr), std::cw<3>}; ASSERT_SAME_TYPE(decltype(s), std::span); assert(s.size() == 3); assert(s.data() == std::data(arr)); } +#endif { // LWG4351 integral-constant-like needs more remove_cvref_t + using true_ref_type = std::integral_constant; + LIBCPP_STATIC_ASSERT(!std::__integral_constant_like); + std::span s(std::begin(arr), true_ref_type{}); + ASSERT_SAME_TYPE(decltype(s), std::span); + assert(s.size() == 1); + assert(s.data() == std::data(arr)); + } +#if TEST_STD_VER >= 26 + { LIBCPP_STATIC_ASSERT(!std::__integral_constant_like)>); std::span s(std::begin(arr), std::cw); ASSERT_SAME_TYPE(decltype(s), std::span);