diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 65bcadb4cb..332cf5ff9d 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -41,10 +41,10 @@ public: static constexpr rank_type _Rank = sizeof...(_Extents); static constexpr rank_type _Rank_dynamic = (static_cast(_Extents == dynamic_extent) + ... + 0); + static constexpr array _Static_extents = {_Extents...}; + static constexpr bool _Multidim_index_space_size_is_always_zero = ((_Extents == 0) || ...); private: - static constexpr array _Static_extents = {_Extents...}; - _NODISCARD static consteval auto _Make_dynamic_indices() noexcept { array _Result{}; rank_type _Counter = 0; @@ -182,6 +182,9 @@ public: auto _Check_extent = [](const _Ty& _Ext) { if constexpr (_Is_standard_integer<_Ty>) { return _Ext >= 0 && _STD in_range(_Ext); + } else if constexpr (integral<_Ty> && !same_as<_Ty, bool>) { // NB: character types + const auto _Integer_ext = static_cast(_Ext); + return _Integer_ext >= 0 && _STD in_range(_Integer_ext); } else { return true; // NB: We cannot check preconditions } @@ -255,11 +258,40 @@ public: } } - _NODISCARD static consteval bool _Is_index_space_size_representable() { - if constexpr (rank_dynamic() == 0 && rank() > 0) { - return _STD in_range((_Extents * ...)); + _NODISCARD static consteval bool _Is_static_multidim_index_space_size_representable() noexcept { + // Pre: rank_dynamic() == 0 + if constexpr (_Multidim_index_space_size_is_always_zero) { + return true; } else { + index_type _Result{1}; +#pragma warning(push) +#pragma warning(disable : 6287) // TRANSITION, DevCom-10398426 + const bool _Overflow = (_Mul_overflow(static_cast(_Extents), _Result, _Result) || ...); +#pragma warning(pop) + return !_Overflow; + } + } + + template + _NODISCARD constexpr bool _Is_dynamic_multidim_index_space_size_representable() const noexcept { + // Pre: rank_dynamic() != 0 + if constexpr (_Multidim_index_space_size_is_always_zero) { return true; + } else { + bool _Overflow = false; + _Ty _Result = 1; + for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) { + const auto _Ext = static_cast<_Ty>(extent(_Idx)); + if (_Ext == 0) { + return true; + } + + if (!_Overflow) { + _Overflow = _Mul_overflow(_Ext, _Result, _Result); + } + } + + return !_Overflow; } } @@ -327,7 +359,7 @@ private: array _Result; _Result.front() = 1; for (size_t _Idx = 1; _Idx < _Ty::_Rank + 1; ++_Idx) { - _Result[_Idx] = static_cast<_Ty::index_type>(_Result[_Idx - 1] * _Ty::static_extent(_Idx - 1)); + _Result[_Idx] = static_cast<_Ty::index_type>(_Result[_Idx - 1] * _Ty::_Static_extents[_Idx - 1]); } return _Result; } @@ -365,7 +397,7 @@ private: array _Result; _Result.back() = 1; for (size_t _Idx = _Ty::_Rank; _Idx-- > 1;) { - _Result[_Idx - 1] = static_cast<_Ty::index_type>(_Result[_Idx] * _Ty::static_extent(_Idx)); + _Result[_Idx - 1] = static_cast<_Ty::index_type>(_Result[_Idx] * _Ty::_Static_extents[_Idx]); } return _Result; } @@ -409,7 +441,8 @@ public: static_assert(_Is_extents, "Extents must be a specialization of std::extents (N4950 [mdspan.layout.left.overview]/2)."); - static_assert(extents_type::_Is_index_space_size_representable(), + static_assert( + extents_type::rank_dynamic() != 0 || extents_type::_Is_static_multidim_index_space_size_representable(), "If Extents::rank_dynamic() == 0 is true, then the size of the multidimensional index space Extents() must be " "representable as a value of type typename Extents::index_type (N4950 [mdspan.layout.left.overview]/4)."); @@ -417,7 +450,13 @@ public: constexpr mapping(const mapping&) noexcept = default; constexpr mapping(const extents_type& _Exts_) noexcept : _Exts(_Exts_) { - // TRANSITION, CHECK [mdspan.layout.left.cons]/1 (REQUIRES '_Multiply_with_overflow_check' FROM #3561) +#if _CONTAINER_DEBUG_LEVEL > 0 + if constexpr (extents_type::rank_dynamic() != 0) { + _STL_VERIFY(_Exts_._Is_dynamic_multidim_index_space_size_representable(), + "The size of the multidimensional index space e must be representable as a value of type index_type " + "(N4950 [mdspan.layout.left.cons]/1)."); + } +#endif // _CONTAINER_DEBUG_LEVEL > 0 } template @@ -425,9 +464,11 @@ public: constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const mapping<_OtherExtents>& _Other) noexcept : _Exts(_Other.extents()) { +#if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_STD in_range(_Other.required_span_size()), "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " "[mdspan.layout.left.cons]/4)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 } template @@ -435,30 +476,31 @@ public: constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const layout_right::mapping<_OtherExtents>& _Other) noexcept : _Exts(_Other.extents()) { +#if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_STD in_range(_Other.required_span_size()), "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " "[mdspan.layout.left.cons]/7)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 } template requires is_constructible_v constexpr explicit(extents_type::rank() > 0) mapping(const layout_stride::template mapping<_OtherExtents>& _Other) : _Exts(_Other.extents()) { +#if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (extents_type::rank() > 0) { - const bool _Verify = [&](index_sequence<_Indices...>) { - index_type _Prod = 1; - return ((_Other.stride(_Indices) - == (_Indices == extents_type::_Rank - 1 - ? _Prod - : _STD exchange(_Prod, static_cast(_Prod * _Exts.extent(_Indices))))) - && ...); - }(make_index_sequence{}); - _STL_VERIFY(_Verify, "For all r in the range [0, extents_type::rank()), other.stride(r) must be equal to " - "extents().fwd-prod-of-extents(r) (N4950 [mdspan.layout.left.cons]/10.1)."); + index_type _Prod = 1; + for (size_t _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { + _STL_VERIFY(_Other.stride(_Idx) == _Prod, + "For all r in the range [0, extents_type::rank()), other.stride(r) must be equal to " + "extents().fwd-prod-of-extents(r) (N4950 [mdspan.layout.left.cons]/10.1)."); + _Prod = static_cast(_Prod * _Exts.extent(_Idx)); + } + _STL_VERIFY(_STD in_range(_Other.required_span_size()), + "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " + "[mdspan.layout.left.cons]/10.2)."); } - _STL_VERIFY(_STD in_range(_Other.required_span_size()), - "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " - "[mdspan.layout.left.cons]/10.2)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 } constexpr mapping& operator=(const mapping&) noexcept = default; @@ -475,10 +517,6 @@ public: requires (sizeof...(_IndexTypes) == extents_type::rank()) && (is_convertible_v<_IndexTypes, index_type> && ...) && (is_nothrow_constructible_v && ...) _NODISCARD constexpr index_type operator()(_IndexTypes... _Indices) const noexcept { - _STL_VERIFY(_Exts._Contains_multidimensional_index( - make_index_sequence{}, static_cast(_Indices)...), - "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " - "[mdspan.layout.left.obs]/3)."); return _Index_impl(make_index_sequence{}, static_cast(_Indices)...); } @@ -509,8 +547,10 @@ public: _NODISCARD constexpr index_type stride(const rank_type _Idx) const noexcept requires (extents_type::rank() > 0) { +#if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Idx < extents_type::_Rank, "Value of i must be less than extents_type::rank() (N4950 [mdspan.layout.left.obs]/6)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 return _Fwd_prod_of_extents::_Calculate(_Exts, _Idx); } @@ -524,8 +564,15 @@ private: extents_type _Exts{}; template - _NODISCARD constexpr index_type _Index_impl(index_sequence<_Seq...>, _IndexTypes... _Indices) const noexcept { + _NODISCARD constexpr index_type _Index_impl( + [[maybe_unused]] index_sequence<_Seq...> _Index_seq, _IndexTypes... _Indices) const noexcept { _STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...)); +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Exts._Contains_multidimensional_index(_Index_seq, _Indices...), + "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " + "[mdspan.layout.left.obs]/3)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + index_type _Stride = 1; index_type _Result = 0; (((_Result += _Indices * _Stride), (_Stride *= _Exts.extent(_Seq))), ...); @@ -544,7 +591,8 @@ public: static_assert(_Is_extents, "Extents must be a specialization of std::extents (N4950 [mdspan.layout.right.overview]/2)."); - static_assert(extents_type::_Is_index_space_size_representable(), + static_assert( + extents_type::rank_dynamic() != 0 || extents_type::_Is_static_multidim_index_space_size_representable(), "If Extents::rank_dynamic() == 0 is true, then the size of the multidimensional index space Extents() must be " "representable as a value of type typename Extents::index_type (N4950 [mdspan.layout.right.overview]/4)."); @@ -552,7 +600,13 @@ public: constexpr mapping(const mapping&) noexcept = default; constexpr mapping(const extents_type& _Exts_) noexcept : _Exts(_Exts_) { - // TRANSITION, CHECK [mdspan.layout.right.cons]/1 (REQUIRES '_Multiply_with_overflow_check' FROM #3561) +#if _CONTAINER_DEBUG_LEVEL > 0 + if constexpr (extents_type::rank_dynamic() != 0) { + _STL_VERIFY(_Exts_._Is_dynamic_multidim_index_space_size_representable(), + "The size of the multidimensional index space e must be representable as a value of type index_type " + "(N4950 [mdspan.layout.right.cons]/1)."); + } +#endif // _CONTAINER_DEBUG_LEVEL > 0 } template @@ -560,9 +614,11 @@ public: constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const mapping<_OtherExtents>& _Other) noexcept : _Exts(_Other.extents()) { +#if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_STD in_range(_Other.required_span_size()), "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " "[mdspan.layout.right.cons]/4)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 } template @@ -570,9 +626,11 @@ public: constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const layout_left::mapping<_OtherExtents>& _Other) noexcept : _Exts(_Other.extents()) { +#if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_STD in_range(_Other.required_span_size()), "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " "[mdspan.layout.right.cons]/7)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 } template @@ -580,22 +638,20 @@ public: constexpr explicit(extents_type::rank() > 0) mapping(const layout_stride::template mapping<_OtherExtents>& _Other) noexcept : _Exts(_Other.extents()) { +#if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (extents_type::rank() > 0) { - const bool _Verify = [&](index_sequence<_Indices...>) { - index_type _Prod = stride(0); - return ( - (_Other.stride(_Indices) - == (_Indices == extents_type::_Rank - 1 - ? _Prod - : _STD exchange(_Prod, static_cast(_Prod / _Exts.extent(_Indices + 1))))) - && ...); - }(make_index_sequence{}); - _STL_VERIFY(_Verify, "For all r in the range [0, extents_type::rank()), other.stride(r) must be equal to " - "extents().rev-prod-of-extents(r) (N4950 [mdspan.layout.right.cons]/10.1)."); + index_type _Prod = 1; + for (size_t _Idx = extents_type::_Rank; _Idx-- > 0;) { + _STL_VERIFY(_Prod == _Other.stride(_Idx), + "For all r in the range [0, extents_type::rank()), other.stride(r) must be equal to " + "extents().rev-prod-of-extents(r) (N4950 [mdspan.layout.right.cons]/10.1)."); + _Prod = static_cast(_Prod * _Exts.extent(_Idx)); + } + _STL_VERIFY(_STD in_range(_Other.required_span_size()), + "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " + "[mdspan.layout.right.cons]/10.2)."); } - _STL_VERIFY(_STD in_range(_Other.required_span_size()), - "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " - "[mdspan.layout.right.cons]/10.2)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 } constexpr mapping& operator=(const mapping&) noexcept = default; @@ -612,10 +668,6 @@ public: requires (sizeof...(_IndexTypes) == extents_type::rank()) && (is_convertible_v<_IndexTypes, index_type> && ...) && (is_nothrow_constructible_v && ...) _NODISCARD constexpr index_type operator()(_IndexTypes... _Indices) const noexcept { - _STL_VERIFY(_Exts._Contains_multidimensional_index( - make_index_sequence{}, static_cast(_Indices)...), - "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " - "[mdspan.layout.right.obs]/3)."); return _Index_impl(make_index_sequence{}, static_cast(_Indices)...); } @@ -646,8 +698,10 @@ public: _NODISCARD constexpr index_type stride(const rank_type _Idx) const noexcept requires (extents_type::rank() > 0) { +#if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Idx < extents_type::_Rank, "Value of i must be less than extents_type::rank() (N4950 [mdspan.layout.right.obs]/6)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 return _Rev_prod_of_extents::_Calculate(_Exts, _Idx); } @@ -661,8 +715,15 @@ private: extents_type _Exts{}; template - _NODISCARD constexpr index_type _Index_impl(index_sequence<_Seq...>, _IndexTypes... _Indices) const noexcept { + _NODISCARD constexpr index_type _Index_impl( + [[maybe_unused]] index_sequence<_Seq...> _Index_seq, _IndexTypes... _Indices) const noexcept { _STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...)); +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Exts._Contains_multidimensional_index(_Index_seq, _Indices...), + "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " + "[mdspan.layout.right.obs]/3)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + index_type _Result = 0; ((_Result = static_cast(_Indices + _Exts.extent(_Seq) * _Result)), ...); return _Result; @@ -691,7 +752,8 @@ public: static_assert(_Is_extents, "Extents must be a specialization of std::extents (N4950 [mdspan.layout.stride.overview]/2)."); - static_assert(extents_type::_Is_index_space_size_representable(), + static_assert( + extents_type::rank_dynamic() != 0 || extents_type::_Is_static_multidim_index_space_size_representable(), "If Extents::rank_dynamic() == 0 is true, then the size of the multidimensional index space Extents() must be " "representable as a value of type typename Extents::index_type (N4950 [mdspan.layout.stride.overview]/4)."); @@ -699,8 +761,17 @@ public: if constexpr (extents_type::rank() != 0) { _Strides.back() = 1; for (rank_type _Idx = extents_type::_Rank - 1; _Idx-- > 0;) { - // TRANSITION USE `_Multiply_with_overflow_check` IN DEBUG MODE +#if _CONTAINER_DEBUG_LEVEL > 0 + const bool _Overflow = _Mul_overflow(_Strides[_Idx + 1], _Exts.extent(_Idx + 1), _Strides[_Idx]); + // NB: N4950 requires value of 'layout_right::mapping().required_span_size()' to be + // representable as value of type 'index_type', but this is not enough. We need to require every single + // stride to be representable as value of type 'index_type', so we can get desired effects. + _STL_VERIFY(!_Overflow, + "Value of layout_right::mapping().required_span_size() must be " + "representable as a value of type index_type (N4950 [mdspan.layout.stride.cons]/1)."); +#else // ^^^ _CONTAINER_DEBUG_LEVEL > 0 / _CONTAINER_DEBUG_LEVEL == 0 vvv _Strides[_Idx] = static_cast(_Strides[_Idx + 1] * _Exts.extent(_Idx + 1)); +#endif // _CONTAINER_DEBUG_LEVEL > 0 } } } @@ -713,11 +784,30 @@ public: constexpr mapping(const extents_type& _Exts_, span<_OtherIndexType, extents_type::rank()> _Strides_, index_sequence<_Indices...>) noexcept : _Exts(_Exts_), _Strides{static_cast(_STD as_const(_Strides_[_Indices]))...} { - for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { - // TRANSITION CHECK [mdspan.layout.stride.cons]/4.2 (REQUIRES `_Multiply_with_overflow_check`) - _STL_VERIFY(_Strides[_Idx] > 0, "Value of s[i] must be greater than 0 for all i in the range [0, rank_) " - "(N4950 [mdspan.layout.stride.cons]/4.1)."); +#if _CONTAINER_DEBUG_LEVEL > 0 + if constexpr (extents_type::rank() != 0) { + bool _Found_zero = false; + bool _Overflow = false; + index_type _Req_span_size = 0; + for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { + const index_type _Stride = _Strides[_Idx]; + _STL_VERIFY(_Stride > 0, "Value of s[i] must be greater than 0 for all i in the range [0, rank_) " + "(N4950 [mdspan.layout.stride.cons]/4.1)."); + const index_type _Ext = _Exts.extent(_Idx); + if (_Ext == 0) { + _Found_zero = true; + } + + if (!_Found_zero && !_Overflow) { + index_type _Prod; + _Overflow = _Mul_overflow(static_cast(_Ext - 1), _Stride, _Prod) + || _Add_overflow(_Req_span_size, _Prod, _Req_span_size); + } + } + _STL_VERIFY(_Found_zero || !_Overflow, "REQUIRED-SPAN-SIZE(e, s) must be representable as a value of type " + "index_type (N4950 [mdspan.layout.stride.cons]/4.2)."); } +#endif // _CONTAINER_DEBUG_LEVEL > 0 } #ifndef __clang__ // TRANSITION, DevCom-10360833 @@ -757,15 +847,19 @@ public: || _Is_mapping_of) )) mapping(const _StridedLayoutMapping& _Other) noexcept : _Exts(_Other.extents()) { +#if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_STD in_range(_Other.required_span_size()), "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " "[mdspan.layout.stride.cons]/7.3)."); _STL_VERIFY( _Offset(_Other) == 0, "Value of OFFSET(other) must be equal to 0 (N4950 [mdspan.layout.stride.cons]/7.4)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { const auto _Stride = _Other.stride(_Idx); +#if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Stride > 0, "Value of other.stride(r) must be greater than 0 for every rank index r of " "extents() (N4950 [mdspan.layout.stride.cons]/7.2)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 _Strides[_Idx] = static_cast(_Stride); } } @@ -802,10 +896,6 @@ public: requires (sizeof...(_IndexTypes) == extents_type::rank()) && (is_convertible_v<_IndexTypes, index_type> && ...) && (is_nothrow_constructible_v && ...) _NODISCARD constexpr index_type operator()(_IndexTypes... _Indices) const noexcept { - _STL_VERIFY(_Exts._Contains_multidimensional_index( - make_index_sequence{}, static_cast(_Indices)...), - "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " - "[mdspan.layout.stride.obs]/3)."); return _Index_impl(make_index_sequence{}, static_cast(_Indices)...); } @@ -881,8 +971,15 @@ private: } template - _NODISCARD constexpr index_type _Index_impl(index_sequence<_Seq...>, _IndexTypes... _Indices) const noexcept { + _NODISCARD constexpr index_type _Index_impl( + [[maybe_unused]] index_sequence<_Seq...> _Index_seq, _IndexTypes... _Indices) const noexcept { _STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...)); +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Exts._Contains_multidimensional_index(_Index_seq, _Indices...), + "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " + "[mdspan.layout.stride.obs]/3)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return static_cast(((_Indices * _Strides[_Seq]) + ... + 0)); } }; @@ -953,7 +1050,10 @@ public: } _NODISCARD static constexpr size_t static_extent(const rank_type _Idx) noexcept { - return extents_type::static_extent(_Idx); +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Idx < extents_type::_Rank, "Index must be less than rank() (N4950 [mdspan.extents.obs]/1)"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return extents_type::_Static_extents[_Idx]; } _NODISCARD constexpr index_type extent(const rank_type _Idx) const noexcept { @@ -1017,6 +1117,14 @@ public: "[mdspan.mdspan.cons]/20.1)."); static_assert(is_constructible_v, "The extents_type must be constructible from OtherExtents (N4950 [mdspan.mdspan.cons]/20.2)."); +#if _CONTAINER_DEBUG_LEVEL > 0 + for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { + const auto _Static_ext = extents_type::_Static_extents[_Idx]; + _STL_VERIFY(_STD cmp_equal(_Static_ext, dynamic_extent) || _STD cmp_equal(_Static_ext, _Other.extent(_Idx)), + "For each rank index r of extents_type, static_extent(r) == dynamic_extent || static_extent(r) == " + "other.extent(r) must be true (N4950 [mdspan.mdspan.cons]/21.1)."); + } +#endif // _CONTAINER_DEBUG_LEVEL > 0 } constexpr mdspan& operator=(const mdspan&) = default; @@ -1028,7 +1136,7 @@ public: && (is_nothrow_constructible_v && ...) && (sizeof...(_OtherIndexTypes) == rank()) _NODISCARD constexpr reference operator[](_OtherIndexTypes... _Indices) const { - return _Acc.access(_Ptr, static_cast(_Map(static_cast(_STD move(_Indices))...))); + return _Access_impl(static_cast(_STD move(_Indices))...); } #endif // __cpp_multidimensional_subscript @@ -1036,28 +1144,52 @@ public: requires is_convertible_v && is_nothrow_constructible_v _NODISCARD constexpr reference operator[](span<_OtherIndexType, rank()> _Indices) const { - return _Index_impl(_Indices, make_index_sequence{}); + return [&](index_sequence<_Seq...>) -> reference { +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 + return operator[](_STD as_const(_Indices[_Seq])...); +#else // ^^^ defined(__cpp_multidimensional_subscript) / !defined(__cpp_multidimensional_subscript) vvv + return _Multidimensional_access(_STD as_const(_Indices[_Seq])...); +#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ + }(make_index_sequence{}); } template requires is_convertible_v && is_nothrow_constructible_v _NODISCARD constexpr reference operator[](const array<_OtherIndexType, rank()>& _Indices) const { - return _Index_impl(span{_Indices}, make_index_sequence{}); + return [&](index_sequence<_Seq...>) -> reference { +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 + return operator[](_Indices[_Seq]...); +#else // ^^^ defined(__cpp_multidimensional_subscript) / !defined(__cpp_multidimensional_subscript) vvv + return _Multidimensional_access(_Indices[_Seq]...); +#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ + }(make_index_sequence{}); } _NODISCARD constexpr size_type size() const noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 + if constexpr (rank_dynamic() != 0) { + _STL_VERIFY(_Map.extents().template _Is_dynamic_multidim_index_space_size_representable(), + "The size of the multidimensional index space extents() must be representable as a value of type " + "size_type (N4950 [mdspan.mdspan.members]/7)."); + } +#endif // _CONTAINER_DEBUG_LEVEL > 0 return static_cast( _Fwd_prod_of_extents::_Calculate(_Map.extents(), extents_type::_Rank)); } _NODISCARD constexpr bool empty() const noexcept { - for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { - if (_Map.extents().extent(_Idx) == 0) { - return true; + if constexpr (extents_type::_Multidim_index_space_size_is_always_zero) { + return true; + } else { + const extents_type& _Exts = _Map.extents(); + for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { + if (_Exts.extent(_Idx) == 0) { + return true; + } } + return false; } - return false; } friend constexpr void swap(mdspan& _Left, mdspan& _Right) noexcept { @@ -1115,21 +1247,23 @@ public: } private: - template - _NODISCARD constexpr reference _Index_impl(span<_OtherIndexType, rank()> _Indices, index_sequence<_Seq...>) const { -#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 - return operator[](_STD as_const(_Indices[_Seq])...); -#else // ^^^ defined(__cpp_multidimensional_subscript) / !defined(__cpp_multidimensional_subscript) vvv - return _Multidimensional_access(_STD as_const(_Indices[_Seq])...); -#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ - } - #ifndef __cpp_multidimensional_subscript // TRANSITION, P2128R6 template _NODISCARD constexpr reference _Multidimensional_access(_OtherIndexTypes... _Indices) const { - return _Acc.access(_Ptr, static_cast(_Map(static_cast(_STD move(_Indices))...))); + return _Access_impl(static_cast(_STD move(_Indices))...); + } +#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ + + template + _NODISCARD constexpr reference _Access_impl(_OtherIndexTypes... _Indices) const { + _STL_INTERNAL_STATIC_ASSERT((same_as<_OtherIndexTypes, index_type> && ...)); +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Map.extents()._Contains_multidimensional_index(make_index_sequence{}, _Indices...), + "I must be a multidimensional index in extents() (N4950 [mdspan.mdspan.members]/3)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + + return _Acc.access(_Ptr, static_cast(_Map(_Indices...))); } -#endif // __cpp_multidimensional_subscript data_handle_type _Ptr{}; mapping_type _Map{}; diff --git a/tests/std/include/test_mdspan_support.hpp b/tests/std/include/test_mdspan_support.hpp index c6c1f034fa..60b9729e78 100644 --- a/tests/std/include/test_mdspan_support.hpp +++ b/tests/std/include/test_mdspan_support.hpp @@ -267,14 +267,13 @@ struct MappingProperties { template requires (!details::PermissiveTest::test()) MappingProperties get_mapping_properties(const Mapping& mapping) { - using IndexType = typename Mapping::index_type; - constexpr IndexType zero = 0; - constexpr auto rank = Mapping::extents_type::rank(); + using IndexType = typename Mapping::index_type; + constexpr auto rank = Mapping::extents_type::rank(); constexpr std::make_index_sequence rank_indices; auto get_extent = [&](size_t i) { return mapping.extents().extent(i); }; auto multidim_indices = [&](std::index_sequence) { - return std::views::cartesian_product(std::views::iota(zero, get_extent(Indices))...); + return std::views::cartesian_product(std::views::iota(IndexType{0}, get_extent(Indices))...); }(rank_indices); auto map_index = [&](const auto& tpl) { return std::apply([&](auto... i) { return mapping(i...); }, tpl); }; @@ -284,19 +283,18 @@ MappingProperties get_mapping_properties(const Mapping& mapping) { MappingProperties props{}; // Find required span size (N4950 [mdspan.layout.reqmts]/12) - if (std::ranges::contains(std::views::iota(0u, rank) | std::views::transform(get_extent), zero)) { + if (std::ranges::contains(std::views::iota(0u, rank) | std::views::transform(get_extent), IndexType{0})) { props.req_span_size = 0; } else { props.req_span_size = static_cast(1 + mapped_indices.back()); } // Is mapping unique? (N4950 [mdspan.layout.reqmts]/14) - props.uniqueness = !std::ranges::contains(std::views::pairwise_transform(mapped_indices, std::minus{}), zero); + props.uniqueness = std::ranges::adjacent_find(mapped_indices) == mapped_indices.end(); - { // Is mapping exhaustive? (N4950 [mdspan.layout.reqmts]/16) - const auto diffs = std::views::pairwise_transform(mapped_indices, [](auto x, auto y) { return y - x; }); - props.exhaustiveness = std::ranges::find_if_not(diffs, [](auto x) { return x == 1; }) == diffs.end(); - } + // Is mapping exhaustive? (N4950 [mdspan.layout.reqmts]/16) + props.exhaustiveness = + std::ranges::adjacent_find(mapped_indices, [](auto x, auto y) { return y - x > 1; }) == mapped_indices.end(); { // Is mapping strided? (N4950 [mdspan.layout.reqmts]/18) props.strideness = true; // assumption diff --git a/tests/std/test.lst b/tests/std/test.lst index ccec8ad719..6faeebe1b4 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -246,6 +246,7 @@ tests\P0009R18_mdspan_layout_right_death tests\P0009R18_mdspan_layout_stride tests\P0009R18_mdspan_layout_stride_death tests\P0009R18_mdspan_mdspan +tests\P0009R18_mdspan_mdspan_death tests\P0019R8_atomic_ref tests\P0024R2_parallel_algorithms_adjacent_difference tests\P0024R2_parallel_algorithms_adjacent_find diff --git a/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp b/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp index 78e3e9ad2a..4a506e77c0 100644 --- a/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp @@ -54,6 +54,13 @@ void test_construction_from_pack_with_unrepresentable_as_index_type_values_2() { [[maybe_unused]] extents e{ConvertibleToInt{.val = 1}, 256}; } +void test_construction_from_pack_with_unrepresentable_as_index_type_values_3() { + static_assert(signed_integral, "char is not signed integral"); + // Either sizeof...(exts) must be equal to 0 or each element of exts must be nonnegative and must be representable + // as value of type index_type + [[maybe_unused]] extents e{static_cast(-1)}; +} + void test_construction_from_span_with_invalid_values() { int vals[] = {1, 2}; span s{vals}; @@ -93,6 +100,7 @@ int main(int argc, char* argv[]) { test_construction_from_pack_with_invalid_values, test_construction_from_pack_with_unrepresentable_as_index_type_values_1, test_construction_from_pack_with_unrepresentable_as_index_type_values_2, + test_construction_from_pack_with_unrepresentable_as_index_type_values_3, test_construction_from_span_with_invalid_values, test_construction_from_span_with_unrepresentable_as_index_type_values, test_construction_from_array_with_invalid_values, diff --git a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp index 9946cf40f8..be1ae79656 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp @@ -161,7 +161,7 @@ constexpr void check_members(const extents& ext, index_se void check_mapping_properties() { if constexpr (!is_permissive) { - auto check = []([[maybe_unused]] const auto& mapping) { + auto check = [](const auto& mapping) { const auto props = get_mapping_properties(mapping); assert(props.req_span_size == mapping.required_span_size()); assert(props.uniqueness); @@ -465,6 +465,9 @@ constexpr void check_correctness() { #endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ } +#ifdef __clang__ + if (!is_constant_evaluated()) // FIXME clang hits constexpr limit here +#endif { // 2x3x2x3 tensor const array values{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35}; @@ -491,10 +494,7 @@ constexpr void check_correctness() { } constexpr bool test() { - check_members_with_various_extents( - [](const extents& ext) { - check_members(ext, make_index_sequence{}); - }); + check_members_with_various_extents([](const E& e) { check_members(e, make_index_sequence{}); }); if (!is_constant_evaluated()) { // too heavy for compile time check_mapping_properties(); } diff --git a/tests/std/tests/P0009R18_mdspan_layout_left_death/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_left_death/test.cpp index 4ffb2d8d6b..ae12da2142 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_left_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_left_death/test.cpp @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#define _CONTAINER_DEBUG_LEVEL 1 + #include #include #include @@ -10,7 +12,17 @@ using namespace std; -// TRANSITION, Test Construction From extents_type +void test_construction_from_extents_type_with_signed_index_type() { + using Ext = dextents; + // The size of the multidimensional index space e must be representable as a value of type index_type + [[maybe_unused]] layout_left::mapping m{Ext{5, 4, 7}}; +} + +void test_construction_from_extents_type_with_unsigned_index_type() { + using Ext = dextents; + // The size of the multidimensional index space e must be representable as a value of type index_type + [[maybe_unused]] layout_left::mapping m{Ext{5, 10, 6}}; +} void test_construction_from_other_left_mapping() { layout_left::mapping> m1{dextents{256}}; @@ -26,7 +38,7 @@ void test_construction_from_other_right_mapping() { void test_construction_from_other_stride_mapping_1() { using Ext = extents; - layout_stride::mapping m1{Ext{}, array{1, 1}}; + layout_stride::mapping m1{Ext{}, array{4, 1}}; // For all r in the range [0, extents_type::rank()), other.stride(r) must be equal to // extents().fwd-prod-of-extents(r) layout_left::mapping m2{m1}; @@ -53,7 +65,8 @@ void test_stride_function() { int main(int argc, char* argv[]) { std_testing::death_test_executive exec; exec.add_death_tests({ - // TRANSITION Construction From extents_type + test_construction_from_extents_type_with_signed_index_type, + test_construction_from_extents_type_with_unsigned_index_type, test_construction_from_other_left_mapping, test_construction_from_other_right_mapping, test_construction_from_other_stride_mapping_1, diff --git a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp index a5e58742cb..f7d9f80137 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp @@ -33,6 +33,9 @@ constexpr void check_members(const extents& ext, index_se static_assert(same_as); static_assert(same_as); +#ifdef __clang__ + if (!is_constant_evaluated()) // FIXME clang hits constexpr limit here +#endif { // Check default and copy constructor const Mapping m; Mapping cpy = m; @@ -160,7 +163,7 @@ constexpr void check_members(const extents& ext, index_se void check_mapping_properties() { if constexpr (!is_permissive) { - auto check = []([[maybe_unused]] const auto& mapping) { + auto check = [](const auto& mapping) { const auto props = get_mapping_properties(mapping); assert(props.req_span_size == mapping.required_span_size()); assert(props.uniqueness); @@ -514,10 +517,7 @@ constexpr void check_correctness() { } constexpr bool test() { - check_members_with_various_extents( - [](const extents& ext) { - check_members(ext, make_index_sequence{}); - }); + check_members_with_various_extents([](const E& e) { check_members(e, make_index_sequence{}); }); if (!is_constant_evaluated()) { // too heavy for compile time check_mapping_properties(); } diff --git a/tests/std/tests/P0009R18_mdspan_layout_right_death/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_right_death/test.cpp index 254ea56580..4e643353f1 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_right_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_right_death/test.cpp @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#define _CONTAINER_DEBUG_LEVEL 1 + #include #include #include @@ -10,7 +12,17 @@ using namespace std; -// TRANSITION, Test Construction From extents_type +void test_construction_from_extents_type_with_signed_index_type() { + using Ext = dextents; + // The size of the multidimensional index space e must be representable as a value of type index_type + [[maybe_unused]] layout_right::mapping m{Ext{5, 4, 7}}; +} + +void test_construction_from_extents_type_with_unsigned_index_type() { + using Ext = dextents; + // The size of the multidimensional index space e must be representable as a value of type index_type + [[maybe_unused]] layout_right::mapping m{Ext{5, 10, 6}}; +} void test_construction_from_other_right_mapping() { layout_right::mapping> m1{dextents{256}}; @@ -26,7 +38,7 @@ void test_construction_from_other_left_mapping() { void test_construction_from_other_stride_mapping_1() { using Ext = extents; - layout_stride::mapping m1{Ext{}, array{3, 1}}; + layout_stride::mapping m1{Ext{}, array{1, 2}}; // For all r in the range [0, extents_type::rank()), other.stride(r) must be equal to // extents().rev-prod-of-extents(r) layout_right::mapping m2{m1}; @@ -53,7 +65,8 @@ void test_stride_function() { int main(int argc, char* argv[]) { std_testing::death_test_executive exec; exec.add_death_tests({ - // TRANSITION Construction From extents_type + test_construction_from_extents_type_with_signed_index_type, + test_construction_from_extents_type_with_unsigned_index_type, test_construction_from_other_right_mapping, test_construction_from_other_left_mapping, test_construction_from_other_stride_mapping_1, diff --git a/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp index 2ad0eda002..c8a0b9b1d1 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp @@ -1,24 +1,49 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#define _CONTAINER_DEBUG_LEVEL 1 + #include #include #include #include #include +#include using namespace std; -void test_construction_from_extents_and_array() { +void test_default_construction() { + using Ext = extents; + // Value of layout_right::mapping().required_span_size() must be + // representable as a value of type index_type + [[maybe_unused]] layout_stride::mapping m{}; // NB: strides are [140, 35, 7, 1] +} + +void test_construction_from_extents_and_array_1() { // Value of s[i] must be greater than 0 for all i in the range [0, rank_) [[maybe_unused]] layout_stride::mapping> m1{extents{}, array{-1}}; } -void test_construction_from_extents_and_span() { +void test_construction_from_extents_and_array_2() { + using Ext = extents; + // REQUIRED-SPAN-SIZE(e, s) must be representable as a value of type index_type + [[maybe_unused]] layout_stride::mapping m{Ext{}, array{2}}; +} + + +void test_construction_from_extents_and_span_1() { array s{-1}; // Value of s[i] must be greater than 0 for all i in the range [0, rank_) - [[maybe_unused]] layout_stride::mapping> m1{extents{}, span{s}}; + [[maybe_unused]] layout_stride::mapping> m{extents{}, span{s}}; +} + +void test_construction_from_extents_and_span_2() { + using Ext = extents; + array, 1> a{{{.val = 2}}}; + const span s{a}; + // REQUIRED-SPAN-SIZE(e, s) must be representable as a value of type index_type + [[maybe_unused]] layout_stride::mapping m{Ext{}, s}; } void test_construction_from_strided_layout_mapping() { @@ -36,9 +61,11 @@ void test_call_operator() { int main(int argc, char* argv[]) { std_testing::death_test_executive exec; exec.add_death_tests({ - // TRANSITION more tests - test_construction_from_extents_and_array, - test_construction_from_extents_and_span, + test_default_construction, + test_construction_from_extents_and_array_1, + test_construction_from_extents_and_array_2, + test_construction_from_extents_and_span_1, + test_construction_from_extents_and_span_2, test_construction_from_strided_layout_mapping, test_call_operator, }); diff --git a/tests/std/tests/P0009R18_mdspan_mdspan_death/env.lst b/tests/std/tests/P0009R18_mdspan_mdspan_death/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_mdspan_death/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P0009R18_mdspan_mdspan_death/test.cpp b/tests/std/tests/P0009R18_mdspan_mdspan_death/test.cpp new file mode 100644 index 0000000000..76398b5cc5 --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_mdspan_death/test.cpp @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#define _CONTAINER_DEBUG_LEVEL 1 + +#include +#include + +#include + +using namespace std; + +constexpr array some_ints{}; + +void test_construction_from_other_mdspan() { + auto mds1 = mdspan{some_ints.data(), 8, 2, 8}; + // For each rank index r of extents_type, static_extent(r) == dynamic_extent || static_extent(r) == other.extent(r) + // must be true + [[maybe_unused]] mdspan> mds2{mds1}; +} + +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 +void test_access_with_invalid_multidimensional_index_1() { + auto mds = mdspan{some_ints.data(), 4, 4}; + // I must be a multidimensional index in extents() + (void) mds[3, 4]; +} +#endif // __cpp_multidimensional_subscript + +void test_access_with_invalid_multidimensional_index_2() { + auto mds = mdspan{some_ints.data(), 5, 5}; + // I must be a multidimensional index in extents() + (void) mds[array{4, 5}]; +} + +void test_size_when_index_type_is_signed() { + auto mds = mdspan{some_ints.data(), dextents{8, 8, 4}}; + // The size of the multidimensional index space extents() must be representable as a value of type size_type + (void) mds.size(); +} + +void test_size_when_index_type_is_unsigned() { + auto mds = mdspan{some_ints.data(), dextents{8, 8, 4}}; + // The size of the multidimensional index space extents() must be representable as a value of type size_type + (void) mds.size(); +} + +int main(int argc, char* argv[]) { + std_testing::death_test_executive exec; + exec.add_death_tests({ + test_construction_from_other_mdspan, +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 + test_access_with_invalid_multidimensional_index_1, +#endif // __cpp_multidimensional_subscript + test_access_with_invalid_multidimensional_index_2, + test_size_when_index_type_is_signed, + test_size_when_index_type_is_unsigned, + }); + return exec.run(argc, argv); +}