diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 3a26234f87ed..9fe3ad2feda1 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1062,6 +1062,16 @@ ftms = { cxxmin = 26; extra_cond = "__glibcxx_assume_aligned " "&& __glibcxx_is_sufficiently_aligned"; + }; +}; + +// Purely internal macro padded layouts. +ftms = { + name = padded_layouts; + no_stdname = true; // internal + values = { + v = 1; + cxxmin = 26; }; }; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 46e4c1121e7a..d9bf5c8145a0 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -1193,6 +1193,15 @@ #endif /* !defined(__cpp_lib_aligned_accessor) && defined(__glibcxx_want_aligned_accessor) */ #undef __glibcxx_want_aligned_accessor +#if !defined(__cpp_lib_padded_layouts) +# if (__cplusplus > 202302L) +# define __glibcxx_padded_layouts 1L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_padded_layouts) +# endif +# endif +#endif /* !defined(__cpp_lib_padded_layouts) && defined(__glibcxx_want_padded_layouts) */ +#undef __glibcxx_want_padded_layouts + #if !defined(__cpp_lib_ssize) # if (__cplusplus >= 202002L) # define __glibcxx_ssize 201902L diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan index d9b5152b6938..7f14ca613f33 100644 --- a/libstdc++-v3/include/std/mdspan +++ b/libstdc++-v3/include/std/mdspan @@ -71,6 +71,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return true; } + template + constexpr _IndexType + __index_type_cast(_OIndexType&& __other) + { + if constexpr (std::is_integral_v<_OIndexType>) + { + __glibcxx_assert(cmp_less_equal(__other, + __gnu_cxx::__int_traits<_IndexType>::__max)); + if constexpr (std::is_signed_v<_OIndexType>) + __glibcxx_assert(__other >= 0); + return std::move(__other); + } + else + { + auto __ret = static_cast<_IndexType>(std::move(__other)); + if constexpr (std::is_signed_v<_IndexType>) + __glibcxx_assert(__ret >= 0); + return __ret; + } + } + template class _StaticExtents { @@ -261,6 +282,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __static_extents() noexcept { return _Extents::_Storage::_S_static_extents(); } + template + constexpr span + __static_extents(size_t __begin, size_t __end) noexcept + { + const auto& __sta_exts = __static_extents<_Extents>(); + return span(__sta_exts.data() + __begin, __end - __begin); + } + // Pre-compute: \prod_{i = 0}^r _Extents[i], for r = 0,..., n (exclusive) template constexpr auto __fwd_partial_prods = [] consteval @@ -476,6 +505,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } // Preconditions: _r < _Extents::rank() + template + constexpr typename _Extents::index_type + __fwd_prod(const _Extents& __exts, size_t __begin, size_t __end) noexcept + { + size_t __sta_prod = [__begin, __end] { + span __sta_exts = __static_extents<_Extents>(__begin, __end); + size_t __ret = 1; + for(auto __ext : __sta_exts) + if (__ext != dynamic_extent) + __ret *= __ext; + return __ret; + }(); + return __extents_prod(__exts, __sta_prod, __begin, __end); + } + template constexpr typename _Extents::index_type __fwd_prod(const _Extents& __exts, size_t __r) noexcept @@ -567,6 +611,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION class mapping; }; +#ifdef __glibcxx_padded_layouts + template + struct layout_left_padded + { + template + class mapping; + }; + + template + struct layout_right_padded + { + template + class mapping; + }; +#endif + namespace __mdspan { template @@ -669,10 +729,34 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION is_same_v, _Mapping>; + template typename _Layout, typename _Mapping> + concept __padded_mapping_of = __mapping_of< + _Layout<_Mapping::padding_value>, _Mapping>; + +#ifdef __glibcxx_padded_layouts + template + constexpr bool __is_left_padded_mapping = __padded_mapping_of< + layout_left_padded, _Mapping>; + + template + constexpr bool __is_right_padded_mapping = __padded_mapping_of< + layout_right_padded, _Mapping>; +#endif + + template + consteval size_t + __get_static_stride() + { return _PaddedMapping::_PaddedStorage::_S_static_stride; } + template concept __standardized_mapping = __mapping_of || __mapping_of - || __mapping_of; + || __mapping_of +#ifdef __glibcxx_padded_layouts + || __is_left_padded_mapping<_Mapping> + || __is_right_padded_mapping<_Mapping> +#endif + ; // A tag type to create internal ctors. class __internal_ctor @@ -726,6 +810,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : mapping(__other.extents(), __mdspan::__internal_ctor{}) { __glibcxx_assert(*this == __other); } +#if __glibcxx_padded_layouts + template + requires __mdspan::__is_left_padded_mapping<_LeftpadMapping> + && is_constructible_v + constexpr + explicit(!is_convertible_v) + mapping(const _LeftpadMapping& __other) noexcept + : mapping(__other.extents(), __mdspan::__internal_ctor{}) + { + constexpr size_t __ostride_sta = __mdspan::__get_static_stride< + _LeftpadMapping>(); + + if constexpr (extents_type::rank() > 1) + { + if constexpr (extents_type::static_extent(0) != dynamic_extent + && __ostride_sta != dynamic_extent) + static_assert(extents_type::static_extent(0) == __ostride_sta); + else + __glibcxx_assert(__other.stride(1) + == __other.extents().extent(0)); + } + } +#endif // __glibcxx_padded_layouts + constexpr mapping& operator=(const mapping&) noexcept = default; @@ -1173,6 +1283,550 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION [[no_unique_address]] _Strides _M_strides; }; +#ifdef __glibcxx_padded_layouts + namespace __mdspan + { + constexpr size_t + __least_multiple(size_t __x, size_t __y) + { + if (__x <= 1) + return __y; + return (__y / __x + (__y % __x != 0)) * __x ; + } + + template + constexpr bool + __is_representable_least_multiple(size_t __x, size_t __y) + { + constexpr auto __y_max = __gnu_cxx::__int_traits<_IndexType>::__max; + if(std::cmp_greater(__y, __y_max)) + return false; + + if(__x <= 1) + return true; + + auto __max_delta = __y_max - static_cast<_IndexType>(__y); + auto __y_mod_x = __y % __x; + auto __delta = (__y_mod_x == 0) ? size_t(0) : (__x - __y_mod_x); + return std::cmp_less_equal(__delta, __max_delta); + } + + template + concept __valid_static_stride = (_Extents::rank() <= 1) + || (_PaddingValue == dynamic_extent) + || (_Extents::static_extent(_LayoutTraits::_S_ext_idx) == dynamic_extent) + || (__is_representable_least_multiple( + _PaddingValue, _Extents::static_extent(_LayoutTraits::_S_ext_idx))); + + template + consteval bool + __is_representable_padded_size() + { + using _IndexType = typename _Extents::index_type; + auto __sta_exts = __static_extents<_Extents>( + _LayoutTraits::_S_unpad_begin, _LayoutTraits::_S_unpad_end); + size_t __max = __gnu_cxx::__int_traits<_IndexType>::__max; + return __static_quotient(__sta_exts, __max / _PaddedStride) != 0; + } + + template + concept __valid_padded_size = (_Rank <= 1) + || (_PaddedStride == dynamic_extent) + || (!__all_static(__static_extents<_Extents>())) + || (__contains_zero(__static_extents<_Extents>())) + || (__is_representable_padded_size<_PaddedStride, _Extents, + _LayoutTraits>()); + + template + constexpr typename _Extents::index_type + __linear_index_leftpad(const _Extents& __exts, _Stride __stride, + _Indices... __indices) + { + // i0 + stride*(i1 + extents.extent(1)*...) + using _IndexType = typename _Extents::index_type; + _IndexType __res = 0; + if constexpr (sizeof...(__indices) > 0) + { + _IndexType __mult = 1; + + auto __update_rest = [&, __pos = 1u](_IndexType __idx) mutable + { + __res += __idx * __mult; + __mult *= __exts.extent(__pos); + ++__pos; + }; + + auto __update = [&](_IndexType __idx, auto... __rest) + { + __res += __idx; + __mult = __stride.extent(0); + (__update_rest(__rest), ...); + }; + __update(__indices...); + } + return __res; + } + + template + struct _LeftPaddedLayoutTraits + { + using _LayoutSame = layout_left; + using _LayoutOther = layout_right; + + constexpr static const size_t _S_ext_idx = 0; + constexpr static const size_t _S_stride_idx = 1; + constexpr static const size_t _S_unpad_begin = 1; + constexpr static const size_t _S_unpad_end = _Rank; + + template + constexpr static auto _S_make_padded_extent( + extents<_IndexType, _StaticStride> __stride, + const extents<_IndexType, _Extents...>& __exts) + { + auto __impl = [&](integer_sequence) + { + return extents<_IndexType, _StaticStride, + (_Extents...[_Is + 1])...>{ + __stride.extent(0), __exts.extent(_Is + 1)...}; + }; + return __impl(make_index_sequence()); + } + }; + + template + class _PaddedStorage + { + using _LayoutSame = typename _LayoutTraits::_LayoutSame; + + public: + using _IndexType = typename _Extents::index_type; + constexpr static size_t _S_rank = _Extents::rank(); + + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4372. Weaken Mandates: for dynamic padding values in padded layouts + static_assert((_PaddingValue == dynamic_extent) + || (cmp_less_equal(_PaddingValue, + __gnu_cxx::__int_traits<_IndexType>::__max)), + "padding_value must be representable as index_type"); + + static_assert(__representable_size<_Extents, _IndexType>, + "The size of extents_type must be representable as index_type"); + + static_assert(__valid_static_stride<_Extents, _PaddingValue, + _LayoutTraits>, + "The padded stride must be representable as size_t"); + + static constexpr size_t _S_static_stride = [] consteval + { + constexpr size_t __rank = _Extents::rank(); + if constexpr (__rank <= 1) + return 0; + else + { + constexpr size_t __ext_idx = _LayoutTraits::_S_ext_idx; + constexpr size_t __sta_ext = _Extents::static_extent(__ext_idx); + if constexpr (__sta_ext == 0) + return size_t(0); + else if constexpr (_PaddingValue == dynamic_extent + || __sta_ext == dynamic_extent) + return dynamic_extent; + else + return __least_multiple(_PaddingValue, __sta_ext); + } + }(); + + static_assert(_S_static_stride == dynamic_extent + || cmp_less_equal(_S_static_stride, + __gnu_cxx::__int_traits<_IndexType>::__max), + "Padded stride must be representable as index_type"); + + static_assert(__valid_padded_size<_Extents, _S_static_stride, + _LayoutTraits>); + + constexpr + _PaddedStorage() noexcept + { + if constexpr (_S_rank > 1) + if constexpr (_S_static_stride == dynamic_extent + && _S_static_padextent() != dynamic_extent) + _M_stride = _Stride{_S_static_padextent()}; + } + + constexpr explicit + _PaddedStorage(const _Extents& __exts) + : _M_extents(__exts) + { + if constexpr (!__all_static(__static_extents<_Extents>())) + __glibcxx_assert(__is_representable_extents(_M_extents)); + + if constexpr (_S_rank > 1) + { + _IndexType __stride; + if constexpr (_PaddingValue == dynamic_extent) + __stride = _M_padextent(); + else if constexpr (_S_static_padextent() != dynamic_extent) + return; + else + { + __glibcxx_assert( + __is_representable_least_multiple<_IndexType>( + _PaddingValue, _M_padextent())); + + __stride = static_cast<_IndexType>( + __least_multiple(_PaddingValue, _M_padextent())); + + __glibcxx_assert(__is_representable_extents( + _LayoutTraits::_S_make_padded_extent( + std::dextents<_IndexType, 1>{__stride}, + _M_extents))); + } + _M_stride = _Stride{__stride}; + } + } + + constexpr explicit + _PaddedStorage(const _Extents& __exts, _IndexType __pad) + : _M_extents(__exts) + { + if constexpr (_PaddingValue != dynamic_extent) + __glibcxx_assert(cmp_equal(_PaddingValue, __pad)); + if constexpr (_S_rank > 1 && _S_static_stride == dynamic_extent) + { + __glibcxx_assert( + __is_representable_least_multiple<_IndexType>( + __pad, _M_padextent())); + + _M_stride = _Stride{static_cast<_IndexType>( + __least_multiple(__pad, _M_padextent()))}; + + __glibcxx_assert(__is_representable_extents( + _LayoutTraits::_S_make_padded_extent( + _M_stride, _M_extents))); + } + } + + template + constexpr explicit + _PaddedStorage(const typename _LayoutSame::mapping<_OExtents>& + __other) + : _PaddedStorage(_Extents(__other.extents())) + { + constexpr size_t __stride_idx = _LayoutTraits::_S_stride_idx; + constexpr size_t __ext_idx = _LayoutTraits::_S_ext_idx; + if constexpr (_S_rank > 1 && _PaddingValue != dynamic_extent) + { + static_assert(_S_static_stride == dynamic_extent + || _OExtents::static_extent(__ext_idx) == dynamic_extent + || _S_static_stride == _OExtents::static_extent(__ext_idx), + "The padded stride must be compatible with other"); + + if constexpr (_S_static_stride == dynamic_extent + || _OExtents::static_extent(__stride_idx) == dynamic_extent) + __glibcxx_assert(std::cmp_equal(_M_padstride(), + _M_padextent())); + } + } + + template + constexpr explicit + _PaddedStorage(const typename layout_stride::mapping<_OExtents>& + __other) + : _M_extents(__other.extents()) + { + __glibcxx_assert(cmp_less_equal(__other.required_span_size(), + __gnu_cxx::__int_traits<_IndexType> + ::__max)); + + constexpr size_t __stride_idx = _LayoutTraits::_S_stride_idx; + if constexpr (_S_rank > 1) + { + if constexpr (_PaddingValue != dynamic_extent) + __glibcxx_assert(cmp_equal(__other.stride(__stride_idx), + _M_calc_padstride()) + && "The padded stride must be compatible with other"); + if constexpr (_S_static_stride == dynamic_extent) + _M_stride = _Stride{__other.stride(__stride_idx)}; + } + } + + template + constexpr explicit + _PaddedStorage(_LayoutTraits::_LayoutSame, + const _SamePaddedMapping& __other) + : _M_extents(__other.extents()) + { + if constexpr (_S_rank > 1) + { + static_assert(_PaddingValue == dynamic_extent + || _SamePaddedMapping::padding_value == dynamic_extent + || _PaddingValue == _SamePaddedMapping::padding_value, + "If neither PaddingValue is dynamic_extent, then they must " + "be equal"); + + constexpr size_t __stride_idx = _LayoutTraits::_S_stride_idx; + if constexpr (_PaddingValue != dynamic_extent) + __glibcxx_assert(cmp_equal(__other.stride(__stride_idx), + _M_calc_padstride()) + && "The padded stride must be compatible with other"); + if constexpr (_S_static_stride == dynamic_extent) + _M_stride = _Stride{__other.stride(__stride_idx)}; + } + __glibcxx_assert(cmp_less_equal(__other.required_span_size(), + __gnu_cxx::__int_traits<_IndexType>::__max)); + } + + template + constexpr explicit + _PaddedStorage(_LayoutTraits::_LayoutOther, + const _OtherPaddedMapping& __other) noexcept + : _M_extents(__other.extents()) + { + __glibcxx_assert(cmp_less_equal(__other.required_span_size(), + __gnu_cxx::__int_traits<_IndexType>::__max)); + } + + static constexpr bool + _M_is_always_exhaustive() noexcept + { + if constexpr (_S_rank <= 1) + return true; + else + return _S_static_padextent() != dynamic_extent + && _S_static_stride != dynamic_extent + && _S_static_padextent() == _S_static_stride; + } + + constexpr bool + _M_is_exhaustive() const noexcept + { + if constexpr (_M_is_always_exhaustive()) + return true; + else + return cmp_equal(_M_padextent(), _M_padstride()); + } + + constexpr static size_t + _S_static_padextent() noexcept + { return _Extents::static_extent(_LayoutTraits::_S_ext_idx); } + + constexpr _IndexType + _M_padextent() const noexcept + { return _M_extents.extent(_LayoutTraits::_S_ext_idx); } + + constexpr _IndexType + _M_calc_padstride() const noexcept + { + if constexpr (_S_static_stride != dynamic_extent) + return _S_static_stride; + else if constexpr (_PaddingValue != dynamic_extent) + return __least_multiple(_PaddingValue, _M_padextent()); + else + return _M_padextent(); + } + + constexpr _IndexType + _M_padstride() const noexcept + { return _M_stride.extent(0); } + + constexpr _IndexType + _M_required_span_size() const noexcept + { + if constexpr (_S_rank == 0) + return 1; + else if (__mdspan::__empty(_M_extents)) + return 0; + else + { + size_t __stride = static_cast(_M_padstride()); + size_t __prod_rest = __mdspan::__fwd_prod(_M_extents, + _LayoutTraits::_S_unpad_begin, _LayoutTraits::_S_unpad_end); + size_t __delta = _M_padstride() - _M_padextent(); + return static_cast<_IndexType>(__stride * __prod_rest - __delta); + } + } + + template + constexpr bool + _M_equal(const _SamePaddedMapping& __other) const noexcept + { + return _M_extents == __other.extents() + && (_S_rank < 2 + || cmp_equal(_M_stride.extent(0), + __other.stride(_LayoutTraits::_S_stride_idx))); + } + + using _Stride = std::extents<_IndexType, _S_static_stride>; + [[no_unique_address]] _Stride _M_stride; + [[no_unique_address]] _Extents _M_extents; + }; + } + + template + template + class layout_left_padded<_PaddingValue>::mapping + { + public: + static constexpr size_t padding_value = _PaddingValue; + + using extents_type = _Extents; + using index_type = typename extents_type::index_type; + using size_type = typename extents_type::size_type; + using rank_type = typename extents_type::rank_type; + using layout_type = layout_left_padded; + + private: + static constexpr size_t _S_rank = extents_type::rank(); + using _PaddedStorage = __mdspan::_PaddedStorage<_PaddingValue, + _Extents, __mdspan::_LeftPaddedLayoutTraits<_S_rank>>; + [[no_unique_address]] _PaddedStorage _M_storage; + + consteval friend size_t + __mdspan::__get_static_stride(); + + constexpr index_type + _M_extent(size_t __r) const noexcept + { return _M_storage._M_extents.extent(__r); } + + constexpr index_type + _M_padstride() const noexcept + { return _M_storage._M_stride.extent(0); } + + public: + constexpr + mapping() noexcept + { } + + constexpr + mapping(const mapping&) noexcept = default; + + constexpr + mapping(const extents_type& __exts) + : _M_storage(__exts) + { } + + template<__mdspan::__valid_index_type _OIndexType> + constexpr mapping(const extents_type& __exts, _OIndexType __pad) + : _M_storage(__exts, + __mdspan::__index_type_cast(std::move(__pad))) + { } + + template + requires is_constructible_v + constexpr explicit(!is_convertible_v<_OExtents, extents_type>) + mapping(const layout_left::mapping<_OExtents>& __other) + : _M_storage(__other) + { } + + template + requires is_constructible_v<_OExtents, extents_type> + constexpr explicit(_OExtents::rank() > 0) + mapping(const typename layout_stride::mapping<_OExtents>& __other) + : _M_storage(__other) + { __glibcxx_assert(*this == __other); } + + template + requires __mdspan::__is_left_padded_mapping<_LeftpadMapping> + && is_constructible_v + constexpr explicit(_S_rank > 1 && (padding_value != dynamic_extent + || _LeftpadMapping::padding_value == dynamic_extent)) + mapping(const _LeftpadMapping& __other) + : _M_storage(layout_left{}, __other) + { } + + template + requires (__mdspan::__is_right_padded_mapping<_RightPaddedMapping> + || __mdspan::__mapping_of) + && (_S_rank <= 1) + && is_constructible_v + constexpr explicit(!is_convertible_v< + typename _RightPaddedMapping::extents_type, extents_type>) + mapping(const _RightPaddedMapping& __other) noexcept + : _M_storage(layout_right{}, __other) + { } + + constexpr mapping& + operator=(const mapping&) noexcept = default; + + constexpr const extents_type& + extents() const noexcept { return _M_storage._M_extents; } + + constexpr array + strides() const noexcept + { + array __ret; + if constexpr (_S_rank > 0) + __ret[0] = 1; + if constexpr (_S_rank > 1) + __ret[1] = _M_padstride(); + if constexpr (_S_rank > 2) + for(size_t __i = 2; __i < _S_rank; ++__i) + __ret[__i] = __ret[__i - 1] * _M_extent(__i - 1); + return __ret; + } + + constexpr index_type + required_span_size() const noexcept + { return _M_storage._M_required_span_size(); } + + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 4314. Missing move in mdspan layout mapping::operator() + template<__mdspan::__valid_index_type... _Indices> + requires (sizeof...(_Indices) == _S_rank) + constexpr index_type + operator()(_Indices... __indices) const noexcept + { + return __mdspan::__linear_index_leftpad( + extents(), _M_storage._M_stride, + static_cast(std::move(__indices))...); + } + + static constexpr bool + is_always_exhaustive() noexcept + { return _PaddedStorage::_M_is_always_exhaustive(); } + + constexpr bool + is_exhaustive() noexcept + { return _M_storage._M_is_exhaustive(); } + + static constexpr bool + is_always_unique() noexcept { return true; } + + static constexpr bool + is_always_strided() noexcept { return true; } + + static constexpr bool + is_unique() noexcept { return true; } + + static constexpr bool + is_strided() noexcept { return true; } + + constexpr index_type + stride(rank_type __r) const noexcept + { + __glibcxx_assert(__r < _S_rank); + if (__r == 0) + return 1; + else + return static_cast( + static_cast(_M_padstride()) * + static_cast(__mdspan::__fwd_prod(extents(), 1, __r))); + } + + template + requires(__mdspan::__is_left_padded_mapping<_LeftpadMapping> + && _LeftpadMapping::extents_type::rank() == _S_rank) + friend constexpr bool + operator==(const mapping& __self, const _LeftpadMapping& __other) + noexcept + { return __self._M_storage._M_equal(__other); } + }; +#endif // __glibcxx_padded_layouts + template struct default_accessor { @@ -1370,7 +2024,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION mdspan(data_handle_type __handle, const mapping_type& __mapping, const accessor_type& __accessor) : _M_accessor(__accessor), _M_mapping(__mapping), - _M_handle(std::move(__handle)) + _M_handle(std::move(__handle)) { } template(); // { dg-error "require auto b7 = B<7, std::layout_stride, std::layout_stride>(); // { dg-error "required from" } // { dg-prune-output "must be representable as index_type" } +// { dg-prune-output "static assertion failed" } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc index 23c0a55dae19..8cba8094abcc 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc @@ -1,6 +1,7 @@ // { dg-do run { target c++23 } } #include +#include "padded_traits.h" #include #include @@ -27,7 +28,6 @@ template VERIFY(std::cmp_equal(m.stride(i), other.stride(i))); } - template constexpr void verify_convertible(From from) @@ -40,7 +40,10 @@ template constexpr void verify_nothrow_convertible(From from) { - static_assert(std::is_nothrow_constructible_v); + if constexpr (is_padded_layout) + static_assert(std::is_constructible_v); + else + static_assert(std::is_nothrow_constructible_v); verify_convertible(from); } @@ -57,7 +60,10 @@ template constexpr void verify_nothrow_constructible(From from) { - static_assert(std::is_nothrow_constructible_v); + if constexpr (is_padded_layout) + static_assert(std::is_constructible_v); + else + static_assert(std::is_nothrow_constructible_v); verify_constructible(from); } @@ -196,6 +202,16 @@ namespace from_extents // ctor: mapping(mapping) namespace from_same_layout { + template + constexpr void + verify_convertible(OExtents exts) + { + using Mapping = typename Layout::mapping; + using OMapping = typename Layout::mapping; + + ::verify_convertible(OMapping(exts)); + } + template constexpr void verify_nothrow_convertible(OExtents exts) @@ -223,8 +239,12 @@ namespace from_same_layout verify_nothrow_convertible>( std::extents{}); - verify_nothrow_constructible>( - std::extents{}); + if constexpr (!is_padded_layout) + verify_nothrow_constructible>( + std::extents{}); + else + verify_convertible>( + std::extents{}); assert_not_constructible< typename Layout::mapping>, @@ -234,8 +254,12 @@ namespace from_same_layout typename Layout::mapping>, typename Layout::mapping>>(); - verify_nothrow_constructible>( - std::extents{1}); + if constexpr (!is_padded_layout) + verify_nothrow_constructible>( + std::extents{1}); + else + verify_convertible>( + std::extents{1}); verify_nothrow_convertible>( std::extents{}); @@ -247,8 +271,12 @@ namespace from_same_layout verify_nothrow_constructible>( std::extents{1}); - verify_nothrow_convertible>( - std::extents{}); + if constexpr (!is_padded_layout) + verify_nothrow_convertible>( + std::extents{}); + else + verify_nothrow_constructible>( + std::extents{}); return true; } @@ -424,11 +452,24 @@ template from_stride::test_all(); } +template typename Layout> + constexpr void + test_padded_all() + { + test_all>(); + test_all>(); + test_all>(); + test_all>(); + } + int main() { test_all(); test_all(); +#if __cplusplus > 202302L + test_padded_all(); +#endif from_left_or_right::test_all(); from_left_or_right::test_all(); diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc index cbc425f6c159..05188432f144 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc @@ -35,7 +35,8 @@ template { constexpr Int n1 = std::numeric_limits::max(); constexpr size_t n2 = std::dynamic_extent - 1; - constexpr size_t n = std::cmp_less(n1, n2) ? size_t(n1) : n2; + // Allow some room for padding. + constexpr size_t n = (std::cmp_less(n1, n2) ? size_t(n1) : n2) - 4; verify_all(typename Layout::mapping>{}); verify_all(typename Layout::mapping>{}); @@ -73,7 +74,8 @@ template { constexpr Int n1 = std::numeric_limits::max(); constexpr size_t n2 = std::dynamic_extent - 1; - constexpr Int n = std::cmp_less(n1, n2) ? n1 : Int(n2); + // Allow some room for padding. + constexpr Int n = (std::cmp_less(n1, n2) ? n1 : Int(n2)) - 4; verify_all(make_mapping( std::extents{n, n, n, n})); @@ -121,11 +123,25 @@ template return true; } +template typename Layout> + constexpr bool + test_padded_all() + { + static_assert(test_all>()); + static_assert(test_all>()); + static_assert(test_all>()); + static_assert(test_all>()); + return true; + } + int main() { static_assert(test_all()); static_assert(test_all()); static_assert(test_all()); +#if __cplusplus > 202302L + static_assert(test_padded_all()); +#endif return 0; } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc index db15e2a48f34..10ce622523d7 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc @@ -2,6 +2,7 @@ #include #include "../int_like.h" +#include "padded_traits.h" #include #include @@ -370,6 +371,37 @@ template<> } }; +#if __cplusplus > 202302L +template + requires is_left_padded + struct TestStride2D + { + static constexpr void + run() + { + using Traits = LayoutTraits()>; + using Extents = typename Traits::extents_type>; + using Mapping = typename Layout::mapping; + constexpr size_t padding_value = Mapping::padding_value; + + Mapping m; + size_t effective_pad = (padding_value == 0 || padding_value == dyn) + ? size_t(1) : padding_value; + + constexpr auto i0 = is_left_padded ? 0 : 1; + VERIFY(m.stride(i0) == 1); + + // The next multiple of padding_value, that's greater or equal + // to exts.extent(0) is the unique value in the range: + // [exts.extent(0), exts.extent(0) + padding_value) + // that is divisible by padding_value. + auto stride = Traits::padded_stride(m); + VERIFY((stride % effective_pad) == 0); + VERIFY(3 <= stride && std::cmp_less(stride, 3 + effective_pad)); + } + }; +#endif + template constexpr void test_stride_2d() @@ -423,6 +455,40 @@ template<> } }; +#if __cplusplus > 202302L +template + requires is_left_padded + struct TestStride3D + { + static constexpr void + run() + { + using Traits = LayoutTraits()>; + using Extents = typename Traits::extents_type>; + using Mapping = typename Layout::mapping; + constexpr size_t padding_value = Mapping::padding_value; + + Mapping m; + size_t effective_pad = (padding_value == 0 || padding_value == dyn) + ? size_t(1) : padding_value; + + constexpr auto i0 = is_left_padded ? 0 : 2; + VERIFY(m.stride(i0) == 1); + + // The next multiple of padding_value, that's greater or equal + // to exts.extent(0) is the unique value in the range: + // [exts.extent(0), exts.extent(0) + padding_value) + // that is divisible by padding_value. + auto stride = Traits::padded_stride(m); + VERIFY((stride % effective_pad) == 0); + VERIFY(3 <= stride && std::cmp_less(stride, 3 + effective_pad)); + + constexpr auto i2 = is_left_padded ? 2 : 0; + VERIFY(stride * 5 == m.stride(i2)); + } + }; +#endif + template constexpr void test_stride_3d() @@ -451,7 +517,8 @@ template test_has_stride_0d() { using Mapping = typename Layout::mapping>; - constexpr bool expected = std::is_same_v; + constexpr bool expected = !(std::is_same_v + || std::is_same_v); static_assert(has_stride == expected); } @@ -595,16 +662,54 @@ template test_has_op_eq(); } +#if __cplusplus > 202302L +template typename Layout> + constexpr bool + test_padded_all() + { + test_all>(); + test_all>(); + test_all>(); + test_all>(); + test_all>(); + return true; + } + +template typename Layout> + constexpr bool + test_padded_has_op_eq() + { + using Traits = LayoutTraits()>; + test_has_op_eq, false>(); + test_has_op_eq, false>(); + test_has_op_eq, false>(); + // The next one looks strange, because it's neither. Somehow, the + // conversion rules seem to be playing a critical role again. + // test_has_op_eq, false>(); + + test_has_op_eq, Layout<6>, true>(); + test_has_op_eq, Layout, true>(); + return true; + } +#endif + int main() { test_all(); test_all(); test_all(); +#if __cplusplus > 202302L + test_padded_all(); +#endif test_has_op_eq(); test_has_op_eq(); test_has_op_eq(); +#if __cplusplus > 202302L + test_padded_has_op_eq(); +#endif + test_has_op_eq_peculiar(); return 0; } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc new file mode 100644 index 000000000000..d43b84ef875b --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc @@ -0,0 +1,673 @@ +// { dg-do run { target c++26 } } +#include + +#include +#include "../int_like.h" +#include "padded_traits.h" +#include + +constexpr size_t dyn = std::dynamic_extent; + +template typename Layout> + constexpr bool + test_representable_padded_size() + { + using Traits = LayoutTraits()>; + { + using E = typename Traits::extents_type>; + [[maybe_unused]] typename Layout<1>::mapping m1; + } + + { + using E = typename Traits::extents_type>; + [[maybe_unused]] typename Layout<0>::mapping m1; + [[maybe_unused]] typename Layout<1>::mapping m2; + [[maybe_unused]] typename Layout<128>::mapping m3; + [[maybe_unused]] typename Layout<255>::mapping m4; + } + + { + using E = typename Traits::extents_type>; + [[maybe_unused]] typename Layout::mapping m1(E{}, 0); + [[maybe_unused]] typename Layout::mapping m2(E{}, 1); + [[maybe_unused]] typename Layout::mapping m3(E{}, 128); + [[maybe_unused]] typename Layout::mapping m4(E{}, 255); + } + + { + using E = typename Traits::extents_type>; + [[maybe_unused]] typename Layout<0>::mapping m1; + [[maybe_unused]] typename Layout<1>::mapping m2; + [[maybe_unused]] typename Layout<128>::mapping m3; + [[maybe_unused]] typename Layout<255>::mapping m4; + } + return true; + } + +template + constexpr void + test_default_ctor_single(auto canonical_strides) + { + using Traits = LayoutTraits()>; + using E = typename Traits::extents_type; + auto strides = Traits::make_array(canonical_strides); + + typename Layout::template mapping msta; + VERIFY(msta.stride(0) == strides[0]); + VERIFY(msta.stride(1) == strides[1]); + } + + +template typename Layout> + constexpr bool + test_default_ctor() + { + using E1 = std::extents; + test_default_ctor_single, E1>(std::array{1, 4}); + test_default_ctor_single, E1>(std::array{1, 3}); + + using E2 = std::extents; + test_default_ctor_single, E2>(std::array{1, 0}); + test_default_ctor_single, E2>(std::array{1, 0}); + return true; + } + +template typename Layout> + constexpr bool + test_from_exts() + { + using Traits = LayoutTraits()>; + auto exts = Traits::make_extents(std::dextents{3, 5}); + + typename Layout<0>::mapping m0_sta(exts); + VERIFY(Traits::padded_stride(m0_sta) == Traits::padded_extent(exts)); + + typename Layout<1>::mapping m1_sta(exts); + VERIFY(Traits::padded_stride(m1_sta) == Traits::padded_extent(exts)); + + typename Layout<2>::mapping m2_sta(exts); + VERIFY(Traits::padded_stride(m2_sta) == 4); + + typename Layout::mapping mdyn(exts); + VERIFY(Traits::padded_stride(mdyn) == Traits::padded_extent(exts)); + return true; + } + +template + constexpr bool + test_from_pad_single() + { + using Traits = LayoutTraits()>; + auto pad = 3; + auto exts = Traits::make_extents(std::dextents{pad + 1, 5, 7}); + typename Layout::mapping m(exts, CustomPadType{pad}); + VERIFY(std::cmp_equal(Traits::padded_stride(m), 2*pad)); + VERIFY(m.extents() == exts); + return true; + } + +template + constexpr void + test_from_pad() + { + test_from_pad_single(); + static_assert(test_from_pad_single()); + + test_from_pad_single(); + test_from_pad_single(); + test_from_pad_single(); + + using Extents = std::dims<3>; + using Mapping = Layout::template mapping; + static_assert(!std::is_constructible_v); + static_assert(!std::is_constructible_v); + } + +template typename Layout> + constexpr void + test_from_pad_all() + { + test_from_pad>(); + test_from_pad>(); + } + +constexpr bool +is_same_mapping(const auto& lhs, const auto& rhs) +{ + if (lhs.extents().rank() != rhs.extents().rank()) + return false; + + if (lhs.extents() != rhs.extents()) + return false; + + for (size_t i = 0; i < lhs.extents().rank(); ++i) + if (lhs.stride(i) != rhs.stride(i)) + return false; + return true; +} + +enum class ConversionRule +{ + Never, + Always, + Regular +}; + +template +consteval bool +should_convert(auto rule) +{ + if constexpr (rule == ConversionRule::Never) + return false; + if constexpr (rule == ConversionRule::Always) + return true; + else + return std::is_convertible_v; +} + +template + constexpr void + check_convertible(const From& m, auto conversion_rule) + { + VERIFY(is_same_mapping(m, To(m))); + constexpr bool expected = should_convert(conversion_rule); + static_assert(std::is_convertible_v == expected); + } + +template + constexpr void + check_convertible_variants(auto msta, auto conversion_rule) + { + using LayoutFrom = decltype(msta)::layout_type; + constexpr auto cregular = std::cw; + + // There's a twist when both mappings are left-padded. There's two distinct + // ctors: a defaulted copy ctor and a constrained template that enables + // construction from left-padded mappings even if their layout_type is + // different. The two ctors have different rules regarding conversion. + + if constexpr (!std::same_as) + check_convertible>(msta, conversion_rule); + else + check_convertible>(msta, cregular); + + check_convertible>(msta, conversion_rule); + + auto mdyn = typename LayoutFrom::mapping(msta); + check_convertible>(mdyn, conversion_rule); + if constexpr (!std::same_as) + check_convertible>(mdyn, conversion_rule); + else + check_convertible>(mdyn, cregular); + + static_assert(!std::is_constructible_v< + typename LayoutTo::mapping, typename LayoutFrom::mapping>); + }; + +template + constexpr void + test_from_same_1d() + { + using E1 = std::extents; + using E2 = std::extents; + using E3 = std::extents; + constexpr auto cr = std::cw; + + using Traits = LayoutTraits()>; + auto msta = typename Traits::layout_same::mapping(E1{}); + check_convertible_variants(msta, cr); + } + +template + constexpr void + test_from_same_2d() + { + using Traits = LayoutTraits()>; + using E1 = typename Traits::extents_type>; + using E2 = typename Traits::extents_type>; + using E3 = typename Traits::extents_type>; + constexpr auto cr = std::cw; + + auto msta = typename Traits::layout_same::mapping(E1{}); + check_convertible_variants(msta, cr); + } + +template typename Layout> +constexpr bool +test_from_same() +{ + auto check = [](PaddedLayout) + { + test_from_same_1d(); + test_from_same_2d(); + }; + + check(Layout<0>{}); + check(Layout<1>{}); + check(Layout<2>{}); + check(Layout<6>{}); + check(Layout{}); + + // rank == 1 is more permissive: + test_from_same_1d>(); + return true; +} + +template typename Layout, typename E1_, typename E2_, + typename E3_> + constexpr bool + test_from_stride_nd(auto strides_) + { + using Traits = LayoutTraits()>; + using E1 = typename Traits::extents_type; + using E2 = typename Traits::extents_type; + using E3 = typename Traits::extents_type; + auto strides = Traits::make_array(strides_); + + auto check = [&strides](PaddedLayout) + { + auto exts = E1{}; + constexpr auto cr = std::cw; + + auto m = std::layout_stride::mapping(exts, strides); + check_convertible_variants(m, cr); + }; + + check(Layout<0>{}); + check(Layout<1>{}); + check(Layout<2>{}); + check(Layout<6>{}); + check(Layout{}); + return true; + } + +template typename Layout> + constexpr bool + test_from_stride_2d() + { + using E1 = std::extents; + using E2 = std::dims<2>; + using E3 = std::extents; + + auto strides = std::array{1, 6}; + test_from_stride_nd(strides); + return true; + } + +template typename Layout> + constexpr bool + test_from_stride_3d() + { + using E1 = std::extents; + using E2 = std::dims<3>; + using E3 = std::extents; + + auto strides = std::array{1, 6, 6*5}; + test_from_stride_nd(strides); + return true; + } + +template typename Layout> + constexpr bool + test_from_stride() + { + test_from_stride_2d(); + test_from_stride_3d(); + return true; + } + +template typename Layout> + constexpr void + test_from_samepad_0d() + { + using E1 = std::extents; + using E2 = std::extents; + using E3 = std::extents; + + typename Layout<6>::mapping msta{E1{}}; + + auto check = [](To, auto m) + { + constexpr auto cr = std::cw; + check_convertible_variants(m, cr); + }; + + check(Layout<6>{}, msta); + check(Layout{}, msta); + } + +template typename Layout> + constexpr void + test_from_samepad_1d() + { + using E1 = std::extents; + using E2 = std::extents; + using E3 = std::extents; + + typename Layout<6>::mapping msta{E1{}}; + typename Layout::mapping mdyn{E1{}}; + + auto check = [](To, auto m) + { + constexpr auto cr = std::cw; + check_convertible_variants(m, cr); + }; + + // Remember, for rank <= 1 the padding_value is irrelevant. + check(Layout<6>{}, msta); + check(Layout<6>{}, mdyn); + check(Layout{}, msta); + check(Layout{}, mdyn); + } + +template typename Layout> + constexpr void + test_from_samepad_2d() + { + using Traits = LayoutTraits()>; + using E1 = typename Traits::extents_type>; + using E2 = typename Traits::extents_type>; + using E3 = typename Traits::extents_type>; + + typename Layout<6>::mapping msta{E1{}}; + typename Layout::mapping mdyn{E1{}}; + + constexpr auto calways = std::cw; + constexpr auto cnever = std::cw; + + auto check = [](To, auto m, auto cr) + { check_convertible_variants(m, cr); }; + + check(Layout<6>{}, msta, cnever); + check(Layout<6>{}, mdyn, cnever); + check(Layout{}, msta, calways); + check(Layout{}, mdyn, cnever); + } + +template typename Layout> + constexpr bool + test_from_samepad() + { + test_from_samepad_0d(); + test_from_samepad_1d(); + test_from_samepad_2d(); + return true; + } + +template typename Layout> + constexpr bool + test_from_other() + { + using Traits = LayoutTraits()>; + using E1 = std::extents; + using E2 = std::dims<1>; + using E3 = std::extents; + + auto check = [](PaddedLayout) + { + constexpr auto cr = std::cw; + + using layout_other = typename Traits::layout_other; + auto msta = typename layout_other::mapping(E1{}); + check_convertible_variants(msta, cr); + }; + + + // Remember, the padding_value has no effect for rank <= 1. + check(Layout<0>{}); + check(Layout<1>{}); + check(Layout<2>{}); + check(Layout<5>{}); + check(Layout<6>{}); + check(Layout{}); + return true; + } + +template typename Layout> + constexpr bool + test_to_same() + { + using Traits = LayoutTraits()>; + using E1 = typename Traits::extents_type>; + using E2 = typename Traits::extents_type>; + using E3 = typename Traits::extents_type>; + + auto check = [](auto msta) + { + constexpr auto cr = std::cw; + using LayoutSame = typename Traits::layout_same; + check_convertible_variants(msta, cr); + }; + + check(typename Layout<0>::mapping(E1{})); + check(typename Layout<2>::mapping(E1{})); + check(typename Layout<6>::mapping(E1{})); + check(typename Layout::mapping(E1{}, 0)); + check(typename Layout::mapping(E1{}, 2)); + check(typename Layout::mapping(E1{}, 6)); + return true; + } + +template typename Layout> + constexpr bool + test_never_to_other() + { + using Traits = LayoutTraits()>; + using E1 = std::extents; + using E2 = std::dims<1>; + + auto check = [](PaddedLayout, auto exts) + { + using LayoutOther = typename Traits::layout_other; + auto mr = typename LayoutOther::mapping(exts); + auto mlp = typename PaddedLayout::mapping{mr}; + static_assert(!std::is_constructible_v); + }; + + check(Layout<2>{}, E1{}); + check(Layout<2>{}, E2{E1{}}); + return true; + } + +template + constexpr void + test_strides() + { + auto check = [](auto exts) + { + auto m = typename Layout::mapping(exts); + using IndexType = typename decltype(m)::index_type; + constexpr size_t rank = decltype(m)::extents_type::rank(); + + auto strides = m.strides(); + static_assert(std::same_as>); + VERIFY(strides.size() == rank); + for (size_t i = 0; i < strides.size(); ++i) + VERIFY(strides[i] == m.stride(i)); + }; + + check(std::extents()); + check(std::extents(0)); + check(std::extents(3)); + check(std::extents(3, 5, 7)); + check(std::extents(3, 0, 7)); + } + +template typename Layout> + constexpr bool + test_strides_all() + { + test_strides>(); + test_strides>(); + test_strides>(); + test_strides>(); + return true; + } + +template typename Layout> + constexpr void + test_exhaustive_0d() + { + auto exts = std::extents{}; + + auto check = [](auto m) + { + static_assert(m.is_always_exhaustive()); + VERIFY(m.is_exhaustive()); + }; + + check(typename Layout<0>::mapping(exts)); + check(typename Layout<1>::mapping(exts)); + check(typename Layout<2>::mapping(exts)); + check(typename Layout::mapping(exts)); + } + +template typename Layout> +constexpr void + test_exhaustive_1d() + { + auto check_dyn_and_sta = [](PaddedLayout) + { + auto check = [](auto exts) + { + auto m = typename PaddedLayout::mapping(exts); + static_assert(m.is_always_exhaustive()); + VERIFY(m.is_exhaustive()); + }; + + check(std::extents(4)); + check(std::extents{}); + }; + + check_dyn_and_sta(Layout<1>{}); + check_dyn_and_sta(Layout<2>{}); + check_dyn_and_sta(Layout<6>{}); + check_dyn_and_sta(Layout{}); + } + +template typename Layout> + constexpr void + test_exhaustive_3d() + { + using Traits = LayoutTraits()>; + auto exts_dyn = Traits::make_extents(std::extents(4, 5, 7)); + auto exts_sta = Traits::make_extents(std::extents{}); + auto ctrue = std::cw; + auto cfalse= std::cw; + + auto check = [](auto m, auto static_expected, auto runtime_expected) + { + static_assert(m.is_always_exhaustive() == static_expected); + VERIFY(m.is_exhaustive() == runtime_expected); + }; + + check(typename Layout<0>::mapping(exts_sta), ctrue, true); + check(typename Layout<0>::mapping(exts_dyn), cfalse, true); + check(typename Layout<1>::mapping(exts_sta), ctrue, true); + check(typename Layout<1>::mapping(exts_dyn), cfalse, true); + check(typename Layout<2>::mapping(exts_sta), ctrue, true); + check(typename Layout<2>::mapping(exts_dyn), cfalse, true); + check(typename Layout<6>::mapping(exts_dyn), cfalse, false); + check(typename Layout<6>::mapping(exts_sta), cfalse, false); + check(typename Layout::mapping(exts_sta), cfalse, true); + check(typename Layout::mapping(exts_dyn, 2), cfalse, true); + check(typename Layout::mapping(exts_dyn, 3), cfalse, false); + } + +template typename Layout> + constexpr bool + test_exhaustive() + { + test_exhaustive_0d(); + test_exhaustive_1d(); + test_exhaustive_3d(); + return true; + } + +template typename Layout> + constexpr bool + test_op_eq() + { + // The generic cases are handled in layouts/mapping.cc. Here we check + // special cases related to non exhaustive layouts. + using Traits = LayoutTraits()>; + + auto exts_sta = Traits::make_extents(std::extents{}); + auto exts_dyn = std::dims<3>(exts_sta); + auto exts_other = Traits::make_extents(std::extents{}); + + auto m1 = typename Layout<0>::mapping(exts_sta); + auto m2 = typename Layout<7>::mapping(exts_sta); + + VERIFY(m1 == typename Layout<0>::mapping(exts_sta)); + VERIFY(m1 == typename Layout<1>::mapping(exts_sta)); + VERIFY(m1 == typename Layout<2>::mapping(exts_sta)); + VERIFY(m1 == typename Layout<6>::mapping(exts_sta)); + VERIFY(m1 != typename Layout<7>::mapping(exts_sta)); + VERIFY(m1 == typename Layout::mapping(exts_sta)); + VERIFY(m1 != typename Layout::mapping(exts_sta, 7)); + + VERIFY(m1 == typename Layout<0>::mapping(exts_dyn)); + VERIFY(m1 == typename Layout<1>::mapping(exts_dyn)); + VERIFY(m1 == typename Layout<2>::mapping(exts_dyn)); + VERIFY(m1 == typename Layout<6>::mapping(exts_dyn)); + VERIFY(m1 != typename Layout<7>::mapping(exts_dyn)); + VERIFY(m1 == typename Layout::mapping(exts_dyn)); + VERIFY(m1 != typename Layout::mapping(exts_dyn, 7)); + + VERIFY(m2 == typename Layout<7>::mapping(exts_sta)); + VERIFY(m2 == typename Layout::mapping(exts_sta, 7)); + VERIFY(m2 == typename Layout<7>::mapping(exts_dyn)); + VERIFY(m2 == typename Layout::mapping(exts_dyn, 7)); + + VERIFY(m2 != typename Layout<7>::mapping(exts_other)); + VERIFY(m2 != typename Layout::mapping(exts_other, 7)); + return true; + } + +template typename Layout> + constexpr bool + test_required_span_size_overflow() + { + using Traits = LayoutTraits()>; + using Extents = std::dextents; + auto exts = Traits::make_extents(Extents{64, 2}); + auto strides = Traits::make_array(std::array{1, 128}); + auto ms = std::layout_stride::mapping(exts, strides); + auto m = typename Layout::mapping(ms); + VERIFY(is_same_mapping(m, ms)); + VERIFY(m.required_span_size() == ms.required_span_size()); + return true; + } + +template typename Layout> + constexpr bool + test_all() + { + test_representable_padded_size(); + test_default_ctor(); + test_from_exts(); + test_from_stride(); + test_from_samepad(); + test_from_same(); + test_from_other(); + test_to_same(); + test_never_to_other(); + test_strides_all(); + test_exhaustive(); + test_op_eq(); + test_required_span_size_overflow(); + return true; + } + +int +main() +{ + test_all(); + static_assert(test_all()); + + test_from_pad_all(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc new file mode 100644 index 000000000000..f0dbfe9d9c62 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc @@ -0,0 +1,324 @@ +// { dg-do compile { target c++26 } } +#include + +#include "padded_traits.h" +#include + +constexpr size_t dyn = std::dynamic_extent; + +template typename Layout> + constexpr bool + test_from_extens_representable_sta() + { + using E1 = std::extents; + auto m = typename Layout::mapping(E1{}); // { dg-error "required from" } + return true; + } +static_assert(test_from_extens_representable_sta()); // { dg-error "from here" } + +template typename Layout> + constexpr bool + test_from_extents_representable_padded_size() + { + using E1 = std::extents; + using E2 = std::dextents; + + auto m = typename Layout::mapping(E2{E1{}}); // { dg-error "expansion of" } + (void) m; + return true; + } +static_assert(test_from_extents_representable_padded_size()); // { dg-error "expansion of" } + +template typename Layout> + constexpr bool + test_from_extents_representable_stride() + { + using Traits = LayoutTraits()>; + using E1 = typename Traits::extents_type>; + auto m = typename Layout<128>::mapping(E1{129}); // { dg-error "expansion of" } + (void) m; + return true; + } +static_assert(test_from_extents_representable_stride()); // { dg-error "expansion of" } + +template typename Layout> + constexpr bool + test_from_pad_representable_stride() + { + using Traits = LayoutTraits()>; + auto exts = Traits::make_extents(std::dextents(129, 2)); + auto m = typename Layout::mapping(exts, 128); // { dg-error "expansion of" } + return true; + } +static_assert(test_from_pad_representable_stride()); // { dg-error "expansion of" } + +template typename Layout> + constexpr bool + test_from_pad_representable_padded_size() + { + using Traits = LayoutTraits()>; + auto exts = Traits::make_extents(std::dextents(64, 2)); + auto m = typename Layout::mapping(exts, 128); // { dg-error "expansion of" } + return true; + } +static_assert(test_from_pad_representable_padded_size()); // { dg-error "expansion of" } + +template typename Layout> + constexpr bool + test_from_left() + { + using Traits = LayoutTraits()>; + using LayoutSame = typename Traits::layout_same; + auto exts = Traits::make_extents(std::extents{4}); + auto ml = typename LayoutSame::mapping(exts); + + typename Layout<4>::mapping m(ml); // { dg-error "expansion of" } + return true; + } +static_assert(test_from_left()); // { dg-error "required from here" } + +template typename Layout> + constexpr bool + test_from_left_bad_runtime_stride() + { + using Traits = LayoutTraits()>; + auto exts = Traits::make_extents(std::extents{6, 4}); + auto ml = typename Traits::layout_same::mapping(exts); + + typename Layout<4>::mapping m(ml); // { dg-error "expansion of" } + (void) m; + return true; + } +static_assert(test_from_left_bad_runtime_stride()); // { dg-error "expansion of" } + +template typename Layout> + constexpr bool + test_from_left_representable_extents() + { + using Traits = LayoutTraits()>; + auto exts = Traits::make_extents(std::extents{8, 128}); + auto ml = typename Traits::layout_same::mapping(exts); + + typename Layout<8>::mapping> m(ml); // { dg-error "expansion of" } + return true; + } +static_assert(test_from_left_representable_extents()); // { dg-error "expansion of" } + +template typename Layout, size_t PaddingValue> + constexpr bool + test_pad_overflow() + { + auto exts = std::extents{4}; + auto n = size_t(1) << 9; + auto m = typename Layout::mapping(exts, n); + (void) m; + return true; + } +static_assert(test_pad_overflow()); // { dg-error "expansion of" } +static_assert(test_pad_overflow()); // { dg-error "expansion of" } + +template typename Layout, size_t PaddingValue> + constexpr bool + test_from_pad_negative() + { + auto exts = std::extents(4); + auto m = typename Layout::mapping(exts, -1); + (void) m; + return true; + } +static_assert(test_from_pad_negative()); // { dg-error "expansion of" } +static_assert(test_from_pad_negative()); // { dg-error "expansion of" } + +template typename Layout, size_t Pad> + constexpr bool + test_static_pad_same() + { + using Extents = std::extents; + using Mapping = typename Layout::mapping; + auto exts = Extents{4}; + auto m = Mapping(exts, Pad + 1); // { dg-error "expansion of" } + (void) m; + return true; + } +static_assert(test_static_pad_same()); // { dg-error "expansion of" } +static_assert(test_static_pad_same()); // { dg-error "expansion of" } + +template typename Layout> + constexpr bool + test_from_stride_wrong_stride0() + { + using Traits = LayoutTraits()>; + auto e = Traits::make_extents(std::extents{3, 5}); + auto s = Traits::make_array(std::array{2, 7}); + auto ms = std::layout_stride::mapping(e, s); + auto m = typename Layout::mapping(ms); // { dg-error "expansion of" } + (void) m; + return true; + } +static_assert(test_from_stride_wrong_stride0()); // { dg-error "expansion of" } + +template typename Layout> + constexpr bool + test_from_stride_wrong_stride1() + { + using Traits = LayoutTraits()>; + auto e = Traits::make_extents(std::extents(3, 5)); + auto s = Traits::make_array(std::array{1, 3}); + auto ms = std::layout_stride::mapping(e, s); + auto m = typename Layout<2>::mapping(ms); // { dg-error "expansion of" } + (void) m; + return true; + } +static_assert(test_from_stride_wrong_stride1()); // { dg-error "expansion of" } + +template typename Layout> + constexpr bool + test_from_stride_wrong_stride2() + { + using Traits = LayoutTraits()>; + auto e = Traits::make_extents(std::extents(3, 5, 7)); + auto s = Traits::make_array(std::array{1, 4, 21}); + auto ms = std::layout_stride::mapping(e, s); + auto m = typename Layout::mapping(ms); // here (not implemented) + (void) m; + return true; + } +static_assert(test_from_stride_wrong_stride2()); + +template typename Layout> + constexpr bool + test_from_stride_oversized() + { + using Traits = LayoutTraits()>; + auto exts = Traits::make_extents(std::extents{3, 6}); + auto s = Traits::make_array(std::array{1, 128}); + auto ms = std::layout_stride::mapping(exts, s); + + using Mapping = typename Layout::mapping>; + Mapping m(ms); // { dg-error "expansion of" } + (void) m; + return true; + } +static_assert(test_from_stride_oversized()); // { dg-error "expansion of" } + +template typename Layout> + constexpr bool + test_from_samepad_dyn() + { + using Traits = LayoutTraits()>; + auto e = Traits::make_extents(std::extents(3, 5)); + auto mlp = typename Layout::mapping(e); + auto m = typename Layout<2>::mapping(mlp); // { dg-error "expansion of" } + (void) m; + return true; + } +static_assert(test_from_samepad_dyn()); // { dg-error "expansion of" } + +template typename Layout> + constexpr bool + test_from_samepad_sta() + { + using Traits = LayoutTraits()>; + auto e = Traits::make_extents(std::extents{3, 5}); + auto mlp = typename Layout<3>::mapping(e); + auto m = typename Layout<2>::mapping(mlp); // { dg-error "expansion of" } + (void) m; + return true; + } +static_assert(test_from_samepad_sta()); // { dg-error "expansion of" } + +template typename Layout> + constexpr bool + test_from_samepad_oversized() + { + using Traits = LayoutTraits()>; + using E1 = typename Traits::extents_type>; + using E2 = typename Traits::extents_type>; + auto mlp = typename Layout::mapping(E1{}); + auto m = typename Layout::mapping(mlp); // { dg-error "expansion of" } + (void) m; + return true; + } +static_assert(test_from_samepad_oversized()); // { dg-error "expansion of" } + +template typename Layout, size_t RunId> + constexpr bool + test_to_same_not_exhaustive() + { + using Traits = LayoutTraits()>; + using E1 = typename Traits::extents_type>; + using E2 = typename Traits::extents_type>; + + [[maybe_unused]] auto msta = typename Layout<7>::mapping(E1{}); + if constexpr (RunId == 0) + { + auto m = typename Traits::layout_same::mapping(msta); // { dg-error "required from" } + (void) m; + } + if constexpr (RunId == 1) + { + auto m = typename Traits::layout_same::mapping(msta); // { dg-error "expansion of" } + (void) m; + } + + [[maybe_unused]] auto mdyn = typename Layout::mapping(E2{E1{}}, 7); + if constexpr (RunId == 2) + { + auto m = typename Traits::layout_same::mapping(mdyn); // { dg-error "expansion of" } + (void) m; + } + if constexpr (RunId == 3) + { + auto m = typename Traits::layout_same::mapping(mdyn); // { dg-error "expansion of" } + (void) m; + } + return true; + } +static_assert(test_to_same_not_exhaustive()); // { dg-error "expansion of" } +static_assert(test_to_same_not_exhaustive()); // { dg-error "expansion of" } +static_assert(test_to_same_not_exhaustive()); // { dg-error "expansion of" } +static_assert(test_to_same_not_exhaustive()); // { dg-error "expansion of" } + +template typename Layout> + constexpr bool + test_statically_bad_padding_value1() + { + using Traits = LayoutTraits()>; + constexpr auto N = std::numeric_limits::max() - 1; + using Extents = typename Traits::extents_type>; + typename Layout<10>::mapping m; // { dg-error "required from" } + return true; + } +static_assert(test_statically_bad_padding_value1()); // { dg-error "required from" } + +template typename Layout> + constexpr bool + test_statically_bad_padding_value2() + { + using Traits = LayoutTraits()>; + using Extents = typename Traits::extents_type>; + typename Layout<2>::mapping m; // { dg-error "required from" } + return true; + } +static_assert(test_statically_bad_padding_value2()); // { dg-error "required from" } + +template typename Layout> + constexpr bool + test_statically_oversized() + { + using Traits = LayoutTraits()>; + using Extents = typename Traits::extents_type>; + typename Layout<2>::mapping m; // { dg-error "required from" } + return true; + } +static_assert(test_statically_oversized()); // { dg-error "from here" } + +// { dg-prune-output "padding_value must be representable as index_type" } +// { dg-prune-output "non-constant condition for static assertion" } +// { dg-prune-output "called in a constant expression" } +// { dg-prune-output "no matching function" } +// { dg-prune-output "static assertion failed" } +// { dg-prune-output "__glibcxx_assert_fail()" } +// { dg-prune-output "must be compatible with other.stride" } +// { dg-prune-output "padding_value is dynamic_extent" } +// { dg-prune-output "_S_rank <= 1" } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h new file mode 100644 index 000000000000..9171d8c1ce1d --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h @@ -0,0 +1,73 @@ +#ifndef TEST_MDSPAN_PADDED_TRAITS_H +#define TEST_MDSPAN_PADDED_TRAITS_H + +#include + +template + constexpr static bool is_left_padded = false; + +#if __cplusplus > 202302L +template + constexpr static bool is_left_padded> + = true; +#endif + +template + constexpr bool + is_padded_layout = is_left_padded; + +#if __cplusplus > 202302L + +enum class PaddingSide +{ + Left +}; + +struct DeducePaddingSide +{ + template typename Layout> + constexpr static PaddingSide + from_template() + { return PaddingSide::Left; } + + template + constexpr static PaddingSide + from_typename() + { return PaddingSide::Left; } +}; + +template + struct LayoutTraits; + +template<> + struct LayoutTraits + { + using layout_same = std::layout_left; + using layout_other = std::layout_right; + + template + using extents_type = Extents; + + template + constexpr static extents_type + make_extents(const Extents& exts) + { return exts; } + + template + constexpr static std::array + make_array(const std::array& expected) + { return expected; } + + template + constexpr static auto + padded_stride(const Mapping& m) + { return m.stride(1); } + + template + constexpr static auto + padded_extent(const Extents& exts) + { return exts.extent(0); } + }; + +#endif +#endif