From ff64bc8fa301b96c15aae32edc3ed57ff96027ac Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Wed, 21 Jun 2023 11:44:47 +0200 Subject: [PATCH 01/29] Improve `mdspan::empty` --- stl/inc/mdspan | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 65bcadb4cb6..e35a288888e 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -41,6 +41,7 @@ public: static constexpr rank_type _Rank = sizeof...(_Extents); static constexpr rank_type _Rank_dynamic = (static_cast(_Extents == dynamic_extent) + ... + 0); + static constexpr bool _Multidim_index_space_size_is_always_zero = ((_Extents == 0) || ...); private: static constexpr array _Static_extents = {_Extents...}; @@ -1052,12 +1053,17 @@ public: } _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 { From 8d7468c6e6b51573ed5ee2a3a6ebb5589dcd8c4e Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Wed, 21 Jun 2023 12:14:07 +0200 Subject: [PATCH 02/29] Improve `_Is_index_space_size_representable` and rename it to `_Is_static_multidim_index_space_size_representable` --- stl/inc/mdspan | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index e35a288888e..70d5046b0a5 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -256,11 +256,17 @@ public: } } - _NODISCARD static consteval bool _Is_index_space_size_representable() { - if constexpr (rank_dynamic() == 0 && rank() > 0) { - return _STD in_range((_Extents * ...)); - } else { + _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-??? + const bool _Overflow = (_Mul_overflow(static_cast(_Extents), _Result, _Result) || ...); +#pragma warning(pop) + return !_Overflow; } } @@ -410,7 +416,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)."); @@ -545,7 +552,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)."); @@ -692,7 +700,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)."); From 3e791db6f326af72978b9af04d9272c555113243 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Wed, 21 Jun 2023 12:22:31 +0200 Subject: [PATCH 03/29] `layout_left`: Guard checks with `#if _CONTAINER_DEBUG_LEVEL > 0` --- stl/inc/mdspan | 10 ++++++++++ .../tests/P0009R18_mdspan_layout_left_death/test.cpp | 2 ++ 2 files changed, 12 insertions(+) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 70d5046b0a5..75c9b5571ec 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -433,9 +433,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 @@ -443,15 +445,18 @@ 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; @@ -467,6 +472,7 @@ public: _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; @@ -483,10 +489,12 @@ 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 { +#if _CONTAINER_DEBUG_LEVEL > 0 _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)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 return _Index_impl(make_index_sequence{}, static_cast(_Indices)...); } @@ -517,8 +525,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); } 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 4ffb2d8d6b0..248bf78ed10 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 From 936352612b7aa77339f8ea75d67160c589bd489e Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Wed, 21 Jun 2023 12:30:08 +0200 Subject: [PATCH 04/29] `layout_left`: Implement `mapping(const extents_type&)` constructor's precondition --- stl/inc/mdspan | 30 ++++++++++++++++++- .../test.cpp | 15 ++++++++-- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 75c9b5571ec..938d5bbcdbc 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -270,6 +270,28 @@ public: } } + _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 { + index_type _Result{1}; + bool _Overflow = false; + for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) { + const index_type _Ext = extent(_Idx); + if (_Ext == 0) { + return true; + } + + if (!_Overflow) { + _Overflow = _Mul_overflow(extent(_Idx), _Result, _Result); + } + } + + return !_Overflow; + } + } + template _NODISCARD constexpr bool _Contains_multidimensional_index( index_sequence<_Seq...>, _IndexTypes... _Indices) const noexcept { @@ -425,7 +447,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 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 248bf78ed10..8a5fbff150b 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_left_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_left_death/test.cpp @@ -12,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}}; @@ -55,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, From 7c29805ffb81699abb31b597730c43501f145dc5 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Wed, 21 Jun 2023 12:35:17 +0200 Subject: [PATCH 05/29] `layout_right`: Guard checks with `#if _CONTAINER_DEBUG_LEVEL > 0` --- stl/inc/mdspan | 10 ++++++++++ .../tests/P0009R18_mdspan_layout_right_death/test.cpp | 2 ++ 2 files changed, 12 insertions(+) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 938d5bbcdbc..4cdec17cea2 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -607,9 +607,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 @@ -617,9 +619,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 @@ -627,6 +631,7 @@ 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); @@ -643,6 +648,7 @@ public: _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; @@ -659,10 +665,12 @@ 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 { +#if _CONTAINER_DEBUG_LEVEL > 0 _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)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 return _Index_impl(make_index_sequence{}, static_cast(_Indices)...); } @@ -693,8 +701,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); } 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 254ea565805..ff7ac7f41eb 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 From e416aef7dd06bba3c8b9b28a4cb4e757c4279a80 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Wed, 21 Jun 2023 12:36:00 +0200 Subject: [PATCH 06/29] `layout_right`: Implement `mapping(const extents_type&)` constructor's precondition --- stl/inc/mdspan | 8 +++++++- .../P0009R18_mdspan_layout_right_death/test.cpp | 15 +++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 4cdec17cea2..3c263c1f1e5 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -599,7 +599,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 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 ff7ac7f41eb..e5412bbd782 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_right_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_right_death/test.cpp @@ -12,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}}; @@ -55,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, From 6cd745ba9b159bc2c2a4d5d6920447dd2ff74602 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Wed, 21 Jun 2023 12:44:09 +0200 Subject: [PATCH 07/29] `layout_stride`: Guard checks with `#if _CONTAINER_DEBUG_LEVEL > 0` --- stl/inc/mdspan | 8 ++++++++ .../tests/P0009R18_mdspan_layout_stride_death/test.cpp | 2 ++ 2 files changed, 10 insertions(+) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 3c263c1f1e5..d43f4af5bcd 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -777,11 +777,13 @@ 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]))...} { +#if _CONTAINER_DEBUG_LEVEL > 0 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)."); } +#endif // _CONTAINER_DEBUG_LEVEL > 0 } #ifndef __clang__ // TRANSITION, DevCom-10360833 @@ -821,15 +823,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); } } @@ -866,10 +872,12 @@ 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 { +#if _CONTAINER_DEBUG_LEVEL > 0 _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)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 return _Index_impl(make_index_sequence{}, static_cast(_Indices)...); } 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 2ad0eda002b..7379f74ab92 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride_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 From e59d020ba1bf6b9150c035eb8888745e3e4283f8 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Wed, 21 Jun 2023 12:58:37 +0200 Subject: [PATCH 08/29] `layout_left`: Use `for` loop instead of huge lambda expression in precondition check --- stl/inc/mdspan | 23 ++++++++----------- .../test.cpp | 2 +- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index d43f4af5bcd..a81d7d9f78c 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -486,20 +486,17 @@ public: : _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 } 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 8a5fbff150b..ae12da21424 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_left_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_left_death/test.cpp @@ -38,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}; From e1b35120c20f39cfba344c7a667796fbccaa3a41 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Wed, 21 Jun 2023 13:11:49 +0200 Subject: [PATCH 09/29] `layout_right`: Use `for` loop instead of huge lambda expression in precondition check --- stl/inc/mdspan | 24 ++++++++----------- .../test.cpp | 2 +- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index a81d7d9f78c..8b7edb106bb 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -636,21 +636,17 @@ public: : _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 } 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 e5412bbd782..4e643353f18 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_right_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_right_death/test.cpp @@ -38,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}; From 63e652b31aec19f0742b8a2aae854f31a584d659 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Wed, 21 Jun 2023 15:10:32 +0200 Subject: [PATCH 10/29] `layout_stride`: implement default constructor's preconditions --- stl/inc/mdspan | 11 ++++++++++- .../P0009R18_mdspan_layout_stride_death/test.cpp | 9 ++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 8b7edb106bb..d95d31b7e94 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -756,8 +756,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 } } } 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 7379f74ab92..92ae72a3189 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp @@ -12,6 +12,13 @@ using namespace std; +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() { // 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}}; @@ -38,7 +45,7 @@ void test_call_operator() { int main(int argc, char* argv[]) { std_testing::death_test_executive exec; exec.add_death_tests({ - // TRANSITION more tests + test_default_construction, test_construction_from_extents_and_array, test_construction_from_extents_and_span, test_construction_from_strided_layout_mapping, From 1e5341e5e62d2ab264d1392f87899412138122df Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Wed, 21 Jun 2023 17:47:31 +0200 Subject: [PATCH 11/29] `layout_stride`: implement `mapping(extents_type, array/span)` constructor's preconditions --- stl/inc/mdspan | 25 ++++++++++++++--- .../test.cpp | 28 +++++++++++++++---- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index d95d31b7e94..47951c16c15 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -780,10 +780,27 @@ public: index_sequence<_Indices...>) noexcept : _Exts(_Exts_), _Strides{static_cast(_STD as_const(_Strides_[_Indices]))...} { #if _CONTAINER_DEBUG_LEVEL > 0 - 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 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 } 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 92ae72a3189..5ff04bca436 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp @@ -9,6 +9,7 @@ #include #include +#include using namespace std; @@ -19,15 +20,30 @@ void test_default_construction() { [[maybe_unused]] layout_stride::mapping m{}; // NB: strides are [140, 35, 7, 1] } -void test_construction_from_extents_and_array() { +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() { @@ -46,8 +62,10 @@ int main(int argc, char* argv[]) { std_testing::death_test_executive exec; exec.add_death_tests({ test_default_construction, - test_construction_from_extents_and_array, - test_construction_from_extents_and_span, + 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, }); From 6ce8adcd9983b2307c76995c957f47d2fff2441f Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 22 Jun 2023 17:01:11 +0200 Subject: [PATCH 12/29] `extents`: Improve `extents(exts...)` constructor's preconditions --- stl/inc/mdspan | 3 +++ tests/std/tests/P0009R18_mdspan_extents_death/test.cpp | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 47951c16c15..8900062935b 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -183,6 +183,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 } diff --git a/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp b/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp index 78e3e9ad2ad..4a506e77c03 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, From f6aed8ded5e2517cd992623af212a19ce9f8cbed Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 22 Jun 2023 17:14:28 +0200 Subject: [PATCH 13/29] `mdspan`: Implement `mdspan(other mdspan)` constructor's preconditions --- stl/inc/mdspan | 8 ++++++ tests/std/test.lst | 1 + .../P0009R18_mdspan_mdspan_death/env.lst | 4 +++ .../P0009R18_mdspan_mdspan_death/test.cpp | 28 +++++++++++++++++++ 4 files changed, 41 insertions(+) create mode 100644 tests/std/tests/P0009R18_mdspan_mdspan_death/env.lst create mode 100644 tests/std/tests/P0009R18_mdspan_mdspan_death/test.cpp diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 8900062935b..764750afd03 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -1111,6 +1111,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 = static_extent(_Idx); + _STL_VERIFY(_Static_ext == dynamic_extent || _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; diff --git a/tests/std/test.lst b/tests/std/test.lst index ccec8ad7194..6faeebe1b42 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_mdspan_death/env.lst b/tests/std/tests/P0009R18_mdspan_mdspan_death/env.lst new file mode 100644 index 00000000000..18e2d7c71ec --- /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 00000000000..08110f0886c --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_mdspan_death/test.cpp @@ -0,0 +1,28 @@ +// 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}; +} + +int main(int argc, char* argv[]) { + std_testing::death_test_executive exec; + exec.add_death_tests({ + test_construction_from_other_mdspan, + }); + return exec.run(argc, argv); +} From b3e4c4e0a6a1b7a255391ca551f59e89b6f5a389 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 22 Jun 2023 17:18:58 +0200 Subject: [PATCH 14/29] `extents`: expose `_Static_extents` array --- stl/inc/mdspan | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 764750afd03..375bf721ad1 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -41,11 +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; @@ -359,7 +358,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; } @@ -397,7 +396,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; } @@ -1113,7 +1112,7 @@ public: "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 = static_extent(_Idx); + const auto _Static_ext = extents_type::_Static_extents[_Idx]; _STL_VERIFY(_Static_ext == dynamic_extent || _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)."); From 360d1f7fd09c494b9e2da4845097625aa4c18786 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 22 Jun 2023 17:35:38 +0200 Subject: [PATCH 15/29] `mdspan`: Implement precondition check in `operator[]` --- stl/inc/mdspan | 7 +++++++ .../P0009R18_mdspan_mdspan_death/test.cpp | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 375bf721ad1..7762fcb1c4c 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -1129,6 +1129,13 @@ public: && (is_nothrow_constructible_v && ...) && (sizeof...(_OtherIndexTypes) == rank()) _NODISCARD constexpr reference operator[](_OtherIndexTypes... _Indices) const { +#if _CONTAINER_DEBUG_LEVEL > 0 + if constexpr ((integral<_OtherIndexTypes> && ...)) { + _STL_VERIFY(_Map.extents()._Contains_multidimensional_index( + make_index_sequence{}, static_cast(_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(static_cast(_STD move(_Indices))...))); } #endif // __cpp_multidimensional_subscript diff --git a/tests/std/tests/P0009R18_mdspan_mdspan_death/test.cpp b/tests/std/tests/P0009R18_mdspan_mdspan_death/test.cpp index 08110f0886c..0eceac9d9e9 100644 --- a/tests/std/tests/P0009R18_mdspan_mdspan_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_mdspan_death/test.cpp @@ -19,10 +19,28 @@ void test_construction_from_other_mdspan() { [[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}]; +} + 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, }); return exec.run(argc, argv); } From 76e0498b5462080366a0ba950ee9e13902e5692a Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 22 Jun 2023 18:43:10 +0200 Subject: [PATCH 16/29] `mdspan`: Implement `mdspan::size`'s preconditions --- stl/inc/mdspan | 14 +++++++++++--- .../tests/P0009R18_mdspan_mdspan_death/test.cpp | 16 +++++++++++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 7762fcb1c4c..49d9fdd3f22 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -272,21 +272,22 @@ public: } } + 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 { - index_type _Result{1}; bool _Overflow = false; + _Ty _Result = 1; for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) { - const index_type _Ext = extent(_Idx); + const auto _Ext = static_cast<_Ty>(extent(_Idx)); if (_Ext == 0) { return true; } if (!_Overflow) { - _Overflow = _Mul_overflow(extent(_Idx), _Result, _Result); + _Overflow = _Mul_overflow(_Ext, _Result, _Result); } } @@ -1155,6 +1156,13 @@ public: } _NODISCARD constexpr size_type size() const noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 + if constexpr (rank_dynamic() != 0) { + _STL_VERIFY(_Map.extents()._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)); } diff --git a/tests/std/tests/P0009R18_mdspan_mdspan_death/test.cpp b/tests/std/tests/P0009R18_mdspan_mdspan_death/test.cpp index 0eceac9d9e9..76398b5cc53 100644 --- a/tests/std/tests/P0009R18_mdspan_mdspan_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_mdspan_death/test.cpp @@ -10,7 +10,7 @@ using namespace std; -constexpr array some_ints{}; +constexpr array some_ints{}; void test_construction_from_other_mdspan() { auto mds1 = mdspan{some_ints.data(), 8, 2, 8}; @@ -33,6 +33,18 @@ void test_access_with_invalid_multidimensional_index_2() { (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({ @@ -41,6 +53,8 @@ int main(int argc, char* argv[]) { 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); } From 1a04c332e30e3a3452d7352102e6d7d292d10e39 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 22 Jun 2023 19:15:01 +0200 Subject: [PATCH 17/29] `extents`: Document DevCom-10398426 workaround --- stl/inc/mdspan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 49d9fdd3f22..a92af15c619 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -265,7 +265,7 @@ public: } else { index_type _Result{1}; #pragma warning(push) -#pragma warning(disable : 6287) // TRANSITION, DevCom-??? +#pragma warning(disable : 6287) // TRANSITION, DevCom-10398426 const bool _Overflow = (_Mul_overflow(static_cast(_Extents), _Result, _Result) || ...); #pragma warning(pop) return !_Overflow; From 6044c692a5abaad0cd75ff07177422ddd8d1ee78 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 22 Jun 2023 19:17:53 +0200 Subject: [PATCH 18/29] layouts: fix `_Contains_multidimensional_index` checks --- stl/inc/mdspan | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index a92af15c619..20cfb7f3e0e 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -518,10 +518,12 @@ public: && (is_nothrow_constructible_v && ...) _NODISCARD constexpr index_type operator()(_IndexTypes... _Indices) const noexcept { #if _CONTAINER_DEBUG_LEVEL > 0 - _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)."); + if constexpr ((integral<_IndexTypes> && ...)) { + _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)."); + } #endif // _CONTAINER_DEBUG_LEVEL > 0 return _Index_impl(make_index_sequence{}, static_cast(_Indices)...); } @@ -668,10 +670,12 @@ public: && (is_nothrow_constructible_v && ...) _NODISCARD constexpr index_type operator()(_IndexTypes... _Indices) const noexcept { #if _CONTAINER_DEBUG_LEVEL > 0 - _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)."); + if constexpr ((integral<_IndexTypes> && ...)) { + _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)."); + } #endif // _CONTAINER_DEBUG_LEVEL > 0 return _Index_impl(make_index_sequence{}, static_cast(_Indices)...); } @@ -895,10 +899,12 @@ public: && (is_nothrow_constructible_v && ...) _NODISCARD constexpr index_type operator()(_IndexTypes... _Indices) const noexcept { #if _CONTAINER_DEBUG_LEVEL > 0 - _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)."); + if constexpr ((integral<_IndexTypes> && ...)) { + _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)."); + } #endif // _CONTAINER_DEBUG_LEVEL > 0 return _Index_impl(make_index_sequence{}, static_cast(_Indices)...); } From 77c4a0b150326b268b660af40f9b7c2e1abc8acb Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 22 Jun 2023 19:39:18 +0200 Subject: [PATCH 19/29] `layout_stride`: fix death test --- tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 5ff04bca436..c8a0b9b1d11 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp @@ -39,7 +39,7 @@ void test_construction_from_extents_and_span_1() { } void test_construction_from_extents_and_span_2() { - using Ext = extents; + 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 From 0a3b516cd0dbe04f4db8320d0c01ab8f0d1b3403 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 22 Jun 2023 20:02:18 +0200 Subject: [PATCH 20/29] Those invisible... --- stl/inc/mdspan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 20cfb7f3e0e..310b7d61425 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -765,7 +765,7 @@ public: for (rank_type _Idx = extents_type::_Rank - 1; _Idx-- > 0;) { #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 + // 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, From f65dd87beb706a9b0b940b810ebdb2ad0a362a96 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 22 Jun 2023 20:26:59 +0200 Subject: [PATCH 21/29] Add missing `template` keyword --- stl/inc/mdspan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 310b7d61425..10e0ee4146d 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -1164,7 +1164,7 @@ public: _NODISCARD constexpr size_type size() const noexcept { #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (rank_dynamic() != 0) { - _STL_VERIFY(_Map.extents()._Is_dynamic_multidim_index_space_size_representable(), + _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)."); } From 172f2d5f4c3bbe3297504745249ea8beb02fae7b Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 22 Jun 2023 20:28:53 +0200 Subject: [PATCH 22/29] `mdspan`: Use `cmp_equal` instead of regular `==` in precondition check --- stl/inc/mdspan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 10e0ee4146d..dd04f46cadc 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -1120,7 +1120,7 @@ public: #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(_Static_ext == dynamic_extent || _Static_ext == _Other.extent(_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)."); } From eb1ee89552460ceb557b348319a4012f773c311e Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 22 Jun 2023 23:46:48 +0200 Subject: [PATCH 23/29] Silence Clang's constexpr limit error in `layout_(left|right)`'s tests Drive-by: * Remove unnecessary `[[maybe_unused]]` attributes * Simplify calls to `check_members_with_various_extents` --- tests/std/tests/P0009R18_mdspan_layout_left/test.cpp | 10 +++++----- tests/std/tests/P0009R18_mdspan_layout_right/test.cpp | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp index 9946cf40f8e..be1ae796561 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_right/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp index a5e58742cbe..f7d9f801378 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(); } From ca14e4b989266c2b36fdfa73336859b6c352929c Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Fri, 23 Jun 2023 14:19:16 +0200 Subject: [PATCH 24/29] Drive-by: use `ranges::adjacent_find` instead of `views::pairwise_transform` in `get_mapping_properties` --- tests/std/include/test_mdspan_support.hpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/std/include/test_mdspan_support.hpp b/tests/std/include/test_mdspan_support.hpp index c6c1f034fa0..0a86d302c29 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 From a7f66dfe69c9d8190670bd1cd030df9cb2d1cdac Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Fri, 23 Jun 2023 14:21:20 +0200 Subject: [PATCH 25/29] Drive-by: fix exhaustiveness check in `get_mapping_properties`. Previously, non-unique exhaustive mappings were rejected. --- tests/std/include/test_mdspan_support.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/include/test_mdspan_support.hpp b/tests/std/include/test_mdspan_support.hpp index 0a86d302c29..60b9729e78c 100644 --- a/tests/std/include/test_mdspan_support.hpp +++ b/tests/std/include/test_mdspan_support.hpp @@ -294,7 +294,7 @@ MappingProperties get_mapping_properties(const Mapping& mapping) { // 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(); + 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 From 72d2ff89af25778614fce84a9508441d4207bcde Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Sun, 2 Jul 2023 15:02:22 +0200 Subject: [PATCH 26/29] `layouts`: Improve preconditions checks in `operator()` --- stl/inc/mdspan | 51 ++++++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index dd04f46cadc..c2b4a61f43e 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -517,14 +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 { -#if _CONTAINER_DEBUG_LEVEL > 0 - if constexpr ((integral<_IndexTypes> && ...)) { - _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)."); - } -#endif // _CONTAINER_DEBUG_LEVEL > 0 return _Index_impl(make_index_sequence{}, static_cast(_Indices)...); } @@ -572,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...> _IndexSeq, _IndexTypes... _Indices) const noexcept { _STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...)); +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Exts._Contains_multidimensional_index(_IndexSeq, _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))), ...); @@ -669,14 +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 { -#if _CONTAINER_DEBUG_LEVEL > 0 - if constexpr ((integral<_IndexTypes> && ...)) { - _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)."); - } -#endif // _CONTAINER_DEBUG_LEVEL > 0 return _Index_impl(make_index_sequence{}, static_cast(_Indices)...); } @@ -724,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...> _IndexSeq, _IndexTypes... _Indices) const noexcept { _STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...)); +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Exts._Contains_multidimensional_index(_IndexSeq, _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; @@ -898,14 +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 { -#if _CONTAINER_DEBUG_LEVEL > 0 - if constexpr ((integral<_IndexTypes> && ...)) { - _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)."); - } -#endif // _CONTAINER_DEBUG_LEVEL > 0 return _Index_impl(make_index_sequence{}, static_cast(_Indices)...); } @@ -981,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...> _IndexSeq, _IndexTypes... _Indices) const noexcept { _STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...)); +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Exts._Contains_multidimensional_index(_IndexSeq, _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)); } }; From fbee069ee601ead0f97331aeb347fc2aea25204f Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Sun, 2 Jul 2023 15:07:13 +0200 Subject: [PATCH 27/29] `mdspan`: Manually inline call to `extents_type::static_extent` in `static_extent` and add precondition check --- stl/inc/mdspan | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index c2b4a61f43e..1ceb7cadf80 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -1050,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 { From 5fa6e1838e6f49661f03d6193c90aec1507f5314 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Sun, 2 Jul 2023 16:44:51 +0200 Subject: [PATCH 28/29] `layouts`: `_IndexSeq` -> `_Index_seq` --- stl/inc/mdspan | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 1ceb7cadf80..eb165f3b931 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -565,10 +565,10 @@ private: template _NODISCARD constexpr index_type _Index_impl( - [[maybe_unused]] index_sequence<_Seq...> _IndexSeq, _IndexTypes... _Indices) const noexcept { + [[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(_IndexSeq, _Indices...), + _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 @@ -716,10 +716,10 @@ private: template _NODISCARD constexpr index_type _Index_impl( - [[maybe_unused]] index_sequence<_Seq...> _IndexSeq, _IndexTypes... _Indices) const noexcept { + [[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(_IndexSeq, _Indices...), + _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 @@ -972,10 +972,10 @@ private: template _NODISCARD constexpr index_type _Index_impl( - [[maybe_unused]] index_sequence<_Seq...> _IndexSeq, _IndexTypes... _Indices) const noexcept { + [[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(_IndexSeq, _Indices...), + _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 From d847e1a8cfc30d0ab3dd501999e38f556f6b24c0 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Sun, 2 Jul 2023 20:57:52 +0200 Subject: [PATCH 29/29] `mdspan`: Improve precondition in `mdspan::operator[]` --- stl/inc/mdspan | 49 ++++++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index eb165f3b931..332cf5ff9df 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -1136,14 +1136,7 @@ public: && (is_nothrow_constructible_v && ...) && (sizeof...(_OtherIndexTypes) == rank()) _NODISCARD constexpr reference operator[](_OtherIndexTypes... _Indices) const { -#if _CONTAINER_DEBUG_LEVEL > 0 - if constexpr ((integral<_OtherIndexTypes> && ...)) { - _STL_VERIFY(_Map.extents()._Contains_multidimensional_index( - make_index_sequence{}, static_cast(_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(static_cast(_STD move(_Indices))...))); + return _Access_impl(static_cast(_STD move(_Indices))...); } #endif // __cpp_multidimensional_subscript @@ -1151,14 +1144,26 @@ 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 { @@ -1242,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{};