[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.
This commit is contained in:
A. Jiang 2026-07-13 22:38:17 +08:00 committed by GitHub
parent c4cb2c0e76
commit cb6bccc33f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 49 additions and 48 deletions

View file

@ -61,7 +61,7 @@
"`P1068R11 <https://wg21.link/P1068R11>`__","Vector API for random number generation","2024-03 (Tokyo)","","","`#105423 <https://github.com/llvm/llvm-project/issues/105423>`__",""
"`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Complete|","22","`#105424 <https://github.com/llvm/llvm-project/issues/105424>`__","The changes to ``tuple``'s equality overload from P2165R4 are not yet implemented."
"`P2642R6 <https://wg21.link/P2642R6>`__","Padded ``mdspan`` layouts","2024-03 (Tokyo)","","","`#105425 <https://github.com/llvm/llvm-project/issues/105425>`__",""
"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__",""
"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","`#105426 <https://github.com/llvm/llvm-project/issues/105426>`__","Implemented as a DR against C++20 since LLVM 23."
"","","","","","",""
"`P2747R2 <https://wg21.link/P2747R2>`__","``constexpr`` placement new","2024-06 (St. Louis)","|Complete|","20","`#105427 <https://github.com/llvm/llvm-project/issues/105427>`__",""
"`P2997R1 <https://wg21.link/P2997R1>`__","Removing the common reference requirement from the indirectly invocable concepts","2024-06 (St. Louis)","|Complete|","19","`#105428 <https://github.com/llvm/llvm-project/issues/105428>`__","Implemented as a DR against C++20. (MSVC STL and libstdc++ will do the same.)"

1 Paper # Paper Name Meeting Status First released version GitHub issue Notes
61 `P1068R11 <https://wg21.link/P1068R11>`__ Vector API for random number generation 2024-03 (Tokyo) `#105423 <https://github.com/llvm/llvm-project/issues/105423>`__
62 `P2944R3 <https://wg21.link/P2944R3>`__ Comparisons for ``reference_wrapper`` 2024-03 (Tokyo) |Complete| 22 `#105424 <https://github.com/llvm/llvm-project/issues/105424>`__ The changes to ``tuple``'s equality overload from P2165R4 are not yet implemented.
63 `P2642R6 <https://wg21.link/P2642R6>`__ Padded ``mdspan`` layouts 2024-03 (Tokyo) `#105425 <https://github.com/llvm/llvm-project/issues/105425>`__
64 `P3029R1 <https://wg21.link/P3029R1>`__ Better ``mdspan``'s CTAD 2024-03 (Tokyo) |Complete| 19 `#105426 <https://github.com/llvm/llvm-project/issues/105426>`__ Implemented as a DR against C++20 since LLVM 23.
65
66 `P2747R2 <https://wg21.link/P2747R2>`__ ``constexpr`` placement new 2024-06 (St. Louis) |Complete| 20 `#105427 <https://github.com/llvm/llvm-project/issues/105427>`__
67 `P2997R1 <https://wg21.link/P2997R1>`__ Removing the common reference requirement from the indirectly invocable concepts 2024-06 (St. Louis) |Complete| 19 `#105428 <https://github.com/llvm/llvm-project/issues/105428>`__ Implemented as a DR against C++20. (MSVC STL and libstdc++ will do the same.)

View file

@ -466,15 +466,9 @@ using dims = dextents<_IndexType, _Rank>;
# endif
// Deduction guide for extents
# if _LIBCPP_STD_VER >= 26
template <class... _IndexTypes>
requires(is_convertible_v<_IndexTypes, size_t> && ...)
explicit extents(_IndexTypes...) -> extents<size_t, __maybe_static_ext<_IndexTypes>...>;
# else
template <class... _IndexTypes>
requires(is_convertible_v<_IndexTypes, size_t> && ...)
explicit extents(_IndexTypes...) -> extents<size_t, size_t(((void)sizeof(_IndexTypes), dynamic_extent))...>;
# endif
namespace __mdspan_detail {

View file

@ -312,17 +312,10 @@ private:
# endif
};
# if _LIBCPP_STD_VER >= 26
template <class _ElementType, class... _OtherIndexTypes>
requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0))
explicit mdspan(_ElementType*, _OtherIndexTypes...)
-> mdspan<_ElementType, extents<size_t, __maybe_static_ext<_OtherIndexTypes>...>>;
# else
template <class _ElementType, class... _OtherIndexTypes>
requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0))
explicit mdspan(_ElementType*, _OtherIndexTypes...)
-> mdspan<_ElementType, dextents<size_t, sizeof...(_OtherIndexTypes)>>;
# endif
template <class _Pointer>
requires(is_pointer_v<remove_reference_t<_Pointer>>)

View file

@ -411,14 +411,10 @@ namespace std {
mdspan(Pointer&&)
-> mdspan<remove_pointer_t<remove_reference_t<Pointer>>, extents<size_t>>;
template<class ElementType, class... Integrals>
requires ((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
explicit mdspan(ElementType*, Integrals...)
-> mdspan<ElementType, dextents<size_t, sizeof...(Integrals)>>; // until C++26
template<class ElementType, class... Integrals>
requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
explicit mdspan(ElementType*, Integrals...)
-> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>; // since C++26
-> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>;
template<class ElementType, class OtherIndexType, size_t N>
mdspan(ElementType*, span<OtherIndexType, N>)

View file

@ -19,16 +19,16 @@ namespace std {
inline constexpr size_t dynamic_extent = numeric_limits<size_t>::max();
template<class T>
concept integral-constant-like = // exposition only, since C++26
is_integral_v<decltype(T::value)> &&
!is_same_v<bool, remove_const_t<decltype(T::value)>> &&
concept integral-constant-like = // exposition only
is_integral_v<remove_cvref_t<decltype(T::value)>> &&
!is_same_v<bool, remove_cvref_t<decltype(T::value)>> &&
convertible_to<T, decltype(T::value)> &&
equality_comparable_with<T, decltype(T::value)> &&
bool_constant<T() == T::value>::value &&
bool_constant<static_cast<decltype(T::value)>(T()) == T::value>::value;
template<class T>
constexpr size_t maybe-static-ext = dynamic_extent; // exposition only, since C++26
constexpr size_t maybe-static-ext = dynamic_extent; // exposition only
template<integral-constant-like T>
constexpr size_t maybe-static-ext<T> = {T::value};
@ -123,9 +123,7 @@ private:
};
template<class It, class EndOrSize>
span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>>; // until C++26
template<class It, class EndOrSize>
span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>, maybe-static-ext<EndOrSize>>; // since C++26
span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>, maybe-static-ext<EndOrSize>>;
template<class T, size_t N>
span(T (&)[N]) -> span<T, N>;
@ -597,7 +595,6 @@ template <class _Tp, size_t _Extent>
return __s.__as_writable_bytes();
}
# if _LIBCPP_STD_VER >= 26
template <class _Tp>
concept __integral_constant_like =
is_integral_v<remove_cvref_t<decltype(_Tp::value)>> && !is_same_v<bool, remove_cvref_t<decltype(_Tp::value)>> &&
@ -613,10 +610,6 @@ inline constexpr size_t __maybe_static_ext<_Tp> = {_Tp::value};
template <contiguous_iterator _It, class _EndOrSize>
span(_It, _EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>, __maybe_static_ext<_EndOrSize>>;
# else
template <contiguous_iterator _It, class _EndOrSize>
span(_It, _EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>>;
# endif
template <class _Tp, size_t _Sz>
span(_Tp (&)[_Sz]) -> span<_Tp, _Sz>;

View file

@ -13,8 +13,7 @@
// explicit extents(Integrals...) -> see below;
// Constraints: (is_convertible_v<Integrals, size_t> && ...) is true.
//
// Remarks: The deduced type is dextents<size_t, sizeof...(Integrals)>. // until C++26
// Remarks: The deduced type is extents<size_t, maybe-static-ext<Integrals>...>. // since C++26
// Remarks: The deduced type is extents<size_t, maybe-static-ext<Integrals>...>.
#include <cassert>
#include <cstddef>
@ -31,6 +30,9 @@ struct NoDefaultCtorIndex {
constexpr operator size_t() const noexcept { return value; }
};
template <std::size_t N>
using size_ref_constant = std::integral_constant<const std::size_t&, std::integral_constant<std::size_t, N>::value>;
template <class E, class Expected>
constexpr void test(E e, Expected expected) {
ASSERT_SAME_TYPE(E, Expected);
@ -47,13 +49,16 @@ constexpr bool test() {
std::extents<std::size_t, D, D, D, D, D, D, D, D, D>(1, 2u, 3, 4, 5, 6, 7, 8, 9));
test(std::extents(NoDefaultCtorIndex{1}, NoDefaultCtorIndex{2}), std::extents<std::size_t, D, D>(1, 2));
#if _LIBCPP_STD_VER >= 26
// P3029R1: deduction from `integral_constant`
test(std::extents(std::integral_constant<size_t, 5>{}), std::extents<std::size_t, 5>());
test(std::extents(std::integral_constant<size_t, 5>{}, 6), std::extents<std::size_t, 5, std::dynamic_extent>(6));
test(std::extents(std::integral_constant<size_t, 5>{}, 6, std::integral_constant<size_t, 7>{}),
std::extents<std::size_t, 5, std::dynamic_extent, 7>(6));
#endif
// support for `T::value` of a reference type from P2781R9 `std::constant_wrapper`
test(std::extents(size_ref_constant<5>{}), std::extents<std::size_t, 5>());
test(std::extents(size_ref_constant<5>{}, 6), std::extents<std::size_t, 5, std::dynamic_extent>(6));
test(std::extents(std::integral_constant<size_t, 5>{}, 6, size_ref_constant<7>{}),
std::extents<std::size_t, 5, std::dynamic_extent, 7>(6));
return true;
}

View file

@ -22,11 +22,7 @@
// template<class ElementType, class... Integrals>
// requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
// explicit mdspan(ElementType*, Integrals...)
// -> mdspan<ElementType, dextents<size_t, sizeof...(Integrals)>>; // until C++26
// template<class ElementType, class... Integrals>
// requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
// explicit mdspan(ElementType*, Integrals...)
// -> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>; // since C++26
// -> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>;
//
// template<class ElementType, class OtherIndexType, size_t N>
// mdspan(ElementType*, span<OtherIndexType, N>)
@ -53,6 +49,7 @@
#include <mdspan>
#include <cassert>
#include <cstddef>
#include <concepts>
#include <span> // dynamic_extent
#include <type_traits>
@ -98,6 +95,9 @@ struct SizeTIntType {
constexpr operator size_t() const noexcept { return size_t(val); }
};
template <std::size_t N>
using size_ref_constant = std::integral_constant<const std::size_t&, std::integral_constant<std::size_t, N>::value>;
template <class H, class A>
requires(sizeof(decltype(std::mdspan(std::declval<H>(), 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<T, std::dextents<size_t, 2>>);
#if _LIBCPP_STD_VER >= 26
// P3029R1: deduction from `integral_constant`
ASSERT_SAME_TYPE(
decltype(std::mdspan(handle, std::integral_constant<size_t, 5>{})), std::mdspan<T, std::extents<size_t, 5>>);
@ -117,7 +116,13 @@ constexpr bool test_no_layout_deduction_guides(const H& handle, const A&) {
decltype(std::mdspan(
handle, std::integral_constant<size_t, 5>{}, std::dynamic_extent, std::integral_constant<size_t, 7>{})),
std::mdspan<T, std::extents<size_t, 5, std::dynamic_extent, 7>>);
#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<T, std::extents<std::size_t, 5>>);
ASSERT_SAME_TYPE(decltype(std::mdspan(handle, size_ref_constant<5>{}, std::dynamic_extent)),
std::mdspan<T, std::extents<std::size_t, 5, std::dynamic_extent>>);
ASSERT_SAME_TYPE(decltype(std::mdspan(
handle, std::integral_constant<std::size_t, 5>{}, std::dynamic_extent, size_ref_constant<7>{})),
std::mdspan<T, std::extents<std::size_t, 5, std::dynamic_extent, 7>>);
std::array<char, 3> exts;
// deduction from pointer and array

View file

@ -11,10 +11,7 @@
// <span>
// template<class It, class EndOrSize>
// span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>>; // until C++26
// template<class It, class EndOrSize>
// span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>,
// maybe-static-ext<EndOrSize>>; // since C++26
// span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>, maybe-static-ext<EndOrSize>>;
// template<class T, size_t N>
// span(T (&)[N]) -> span<T, N>;
// template<class T, size_t N>
@ -27,6 +24,7 @@
#include <span>
#include <array>
#include <cassert>
#include <cstddef>
#include <iterator>
#include <memory>
#include <string>
@ -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<size_t, 3>{}};
@ -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<const std::size_t&, std::integral_constant<std::size_t, 3>::value>;
std::span s{std::begin(arr), size_3_ref_type{}};
ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
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<int, 3>);
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<const bool&, std::true_type::value>;
LIBCPP_STATIC_ASSERT(!std::__integral_constant_like<true_ref_type>);
std::span s(std::begin(arr), true_ref_type{});
ASSERT_SAME_TYPE(decltype(s), std::span<int, std::dynamic_extent>);
assert(s.size() == 1);
assert(s.data() == std::data(arr));
}
#if TEST_STD_VER >= 26
{
LIBCPP_STATIC_ASSERT(!std::__integral_constant_like<decltype(std::cw<true>)>);
std::span s(std::begin(arr), std::cw<true>);
ASSERT_SAME_TYPE(decltype(s), std::span<int, std::dynamic_extent>);