diff --git a/stl/inc/mdspan b/stl/inc/mdspan index bd7420639aa..65bcadb4cb6 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -14,6 +14,7 @@ _EMIT_STL_WARNING(STL4038, "The contents of are available only with C++ #include #include #include +#include #include #pragma pack(push, _CRT_PACKING) @@ -74,15 +75,12 @@ private: static constexpr array _Dynamic_indices_inv = _Make_dynamic_indices_inv(); + struct _Construct_from_tuple { + constexpr explicit _Construct_from_tuple() noexcept = default; + }; + struct _Static_extents_only { constexpr explicit _Static_extents_only() noexcept = default; - - template - constexpr explicit _Static_extents_only(_Args&&...) noexcept {} - - _NODISCARD constexpr index_type* begin() const noexcept { - return nullptr; - } }; conditional_t<_Rank_dynamic != 0, array, _Static_extents_only> _Dynamic_extents{}; @@ -97,12 +95,16 @@ public: } _NODISCARD static constexpr size_t static_extent(const rank_type _Idx) noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Idx < _Rank, "Index must be less than rank() (N4950 [mdspan.extents.obs]/1)"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 return _Static_extents[_Idx]; } _NODISCARD constexpr index_type extent(const rank_type _Idx) const noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Idx < _Rank, "Index must be less than rank() (N4950 [mdspan.extents.obs]/3)"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 if constexpr (rank_dynamic() == 0) { return static_cast(_Static_extents[_Idx]); } else if constexpr (rank_dynamic() == rank()) { @@ -118,88 +120,110 @@ public: constexpr extents() noexcept = default; + template + requires (sizeof...(_OtherExtents) == rank()) + && ((_OtherExtents == dynamic_extent || _Extents == dynamic_extent || _OtherExtents == _Extents) && ...) + constexpr explicit extents( + const extents<_OtherIndexType, _OtherExtents...>& _Other, index_sequence<_Indices...>) noexcept + : _Dynamic_extents{static_cast(_Other.extent(_Dynamic_indices_inv[_Indices]))...} { +#if _CONTAINER_DEBUG_LEVEL > 0 + if constexpr (rank() > 0) { + for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) { + if constexpr (rank() != rank_dynamic()) { + _STL_VERIFY(_Static_extents[_Idx] == dynamic_extent + || _STD cmp_equal(_Static_extents[_Idx], _Other.extent(_Idx)), + "Value of other.extent(r) must be equal to extent(r) for each r for which extent(r) is a " + "static extent (N4950 [mdspan.extents.cons]/2.1)"); + } + _STL_VERIFY(_STD in_range(_Other.extent(_Idx)), + "Value of other.extent(r) must be representable as a value of type index_type for every rank index " + "r (N4950 [mdspan.extents.cons]/2.2)"); + } + } +#endif // _CONTAINER_DEBUG_LEVEL > 0 + } + template requires (sizeof...(_OtherExtents) == rank()) && ((_OtherExtents == dynamic_extent || _Extents == dynamic_extent || _OtherExtents == _Extents) && ...) constexpr explicit(((_Extents != dynamic_extent && _OtherExtents == dynamic_extent) || ...) || (numeric_limits::max)() < (numeric_limits<_OtherIndexType>::max)()) - extents(const extents<_OtherIndexType, _OtherExtents...>& _Other) noexcept { - auto _It = _Dynamic_extents.begin(); - for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) { - _STL_VERIFY( - _Static_extents[_Idx] == dynamic_extent || _STD cmp_equal(_Static_extents[_Idx], _Other.extent(_Idx)), - "Value of other.extent(r) must be equal to extent(r) for each r for which extent(r) is a static extent " - "(N4950 [mdspan.extents.cons]/2.1)"); - _STL_VERIFY(_STD in_range(_Other.extent(_Idx)), - "Value of other.extent(r) must be representable as a value of type index_type for every rank index r " - "(N4950 [mdspan.extents.cons]/2.2)"); - - if (_Static_extents[_Idx] == dynamic_extent) { - *_It = static_cast(_Other.extent(_Idx)); - ++_It; - } - } + extents(const extents<_OtherIndexType, _OtherExtents...>& _Other) noexcept + : extents(_Other, make_index_sequence{}) {} + + template + requires (tuple_size_v<_ExtsTuple> == rank_dynamic()) + constexpr explicit extents(_Construct_from_tuple, _ExtsTuple _Tpl, index_sequence<_Indices...>) noexcept + : _Dynamic_extents{static_cast(_STD move(_STD get<_Indices>(_Tpl)))...} {} + + template + requires (tuple_size_v<_ExtsTuple> != rank_dynamic()) + constexpr explicit extents(_Construct_from_tuple, _ExtsTuple _Tpl, index_sequence<_DynIndices...>) noexcept + : _Dynamic_extents{static_cast(_STD move(_STD get<_Dynamic_indices_inv[_DynIndices]>(_Tpl)))...} { +#if _CONTAINER_DEBUG_LEVEL > 0 + [&](index_sequence<_MixedIndices...>) { + _STL_VERIFY(((_Static_extents[_MixedIndices] == dynamic_extent + || _STD cmp_equal(_Static_extents[_MixedIndices], + static_cast(_STD move(_STD get<_MixedIndices>(_Tpl))))) + && ...), + "Value of exts_arr[r] must be equal to extent(r) for each r for which extent(r) is a static extent " + "(N4950 [mdspan.extents.cons]/7.1)"); + }(make_index_sequence{}); +#endif // _CONTAINER_DEBUG_LEVEL > 0 } template requires (is_convertible_v<_OtherIndexTypes, index_type> && ...) && (is_nothrow_constructible_v && ...) && (sizeof...(_OtherIndexTypes) == rank_dynamic() || sizeof...(_OtherIndexTypes) == rank()) - constexpr explicit extents(_OtherIndexTypes... _Exts) noexcept { - if constexpr ((_Is_standard_integer<_OtherIndexTypes> && ...)) { - _STL_VERIFY(sizeof...(_Exts) == 0 || ((_Exts >= 0 && _STD in_range(_Exts)) && ...), - "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 (N4950 [mdspan.extents.cons]/7.2)"); - } - - if constexpr (sizeof...(_Exts) == rank_dynamic()) { - _Dynamic_extents = {static_cast(_STD move(_Exts))...}; - } else { - array _Exts_arr{static_cast(_STD move(_Exts))...}; - auto _It = _Dynamic_extents.begin(); - for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) { - _STL_VERIFY( - _Static_extents[_Idx] == dynamic_extent || _STD cmp_equal(_Static_extents[_Idx], _Exts_arr[_Idx]), - "Value of exts_arr[r] must be equal to extent(r) for each r for which extent(r) is a static extent " - "(N4950 [mdspan.extents.cons]/7.1)"); - if (_Static_extents[_Idx] == dynamic_extent) { - *_It = _Exts_arr[_Idx]; - ++_It; - } + constexpr explicit extents(_OtherIndexTypes... _Exts) noexcept + : extents(_Construct_from_tuple{}, _STD tie(_Exts...), make_index_sequence{}) { +#if _CONTAINER_DEBUG_LEVEL > 0 + auto _Check_extent = [](const _Ty& _Ext) { + if constexpr (_Is_standard_integer<_Ty>) { + return _Ext >= 0 && _STD in_range(_Ext); + } else { + return true; // NB: We cannot check preconditions } - } + }; + _STL_VERIFY((_Check_extent(_Exts) && ...), + "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 (N4950 [mdspan.extents.cons]/7.2)"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 } template requires is_convertible_v - && is_nothrow_constructible_v && (_Size != rank()) - constexpr explicit extents(span<_OtherIndexType, _Size> _Exts, index_sequence<_Indices...>) noexcept - : _Dynamic_extents{static_cast(_STD as_const(_Exts[_Indices]))...} { + && is_nothrow_constructible_v && (_Size == rank_dynamic()) + constexpr explicit extents(span<_OtherIndexType, _Size> _Dynamic_exts, index_sequence<_Indices...>) noexcept + : _Dynamic_extents{static_cast(_STD as_const(_Dynamic_exts[_Indices]))...} { +#if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (_Is_standard_integer<_OtherIndexType> && _Size != 0) { - for (_OtherIndexType _Ext : _Exts) { - _STL_VERIFY(_Ext >= 0 && _STD in_range(_Ext), - "Either N must be zero or exts[r] must be nonnegative and must be representable as value of type " - "index_type for every rank index r (N4950 [mdspan.extents.cons]/10.2)"); - } + _STL_VERIFY(((_Dynamic_exts[_Indices] >= 0 && _STD in_range(_Dynamic_exts[_Indices])) && ...), + "Either N must be zero or exts[r] must be nonnegative and must be representable as value of type " + "index_type for every rank index r (N4950 [mdspan.extents.cons]/10.2)"); } +#endif // _CONTAINER_DEBUG_LEVEL > 0 } template requires is_convertible_v - && is_nothrow_constructible_v && (_Size == rank()) - constexpr explicit extents(span<_OtherIndexType, _Size> _Exts, index_sequence<_Indices...>) noexcept - : _Dynamic_extents{static_cast(_STD as_const(_Exts[_Dynamic_indices_inv[_Indices]]))...} { + && is_nothrow_constructible_v && (_Size != rank_dynamic()) + constexpr explicit extents(span<_OtherIndexType, _Size> _Mixed_exts, index_sequence<_Indices...>) noexcept + : _Dynamic_extents{static_cast(_STD as_const(_Mixed_exts[_Dynamic_indices_inv[_Indices]]))...} { +#if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (_Is_standard_integer<_OtherIndexType>) { for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) { _STL_VERIFY( - _Static_extents[_Idx] == dynamic_extent || _STD cmp_equal(_Static_extents[_Idx], _Exts[_Idx]), + _Static_extents[_Idx] == dynamic_extent || _STD cmp_equal(_Static_extents[_Idx], _Mixed_exts[_Idx]), "Value of exts[r] must be equal to extent(r) for each r for which extent(r) is a static extent " "(N4950 [mdspan.extents.cons]/10.1)"); - _STL_VERIFY(_Exts[_Idx] >= 0 && _STD in_range(_Exts[_Idx]), + _STL_VERIFY(_Mixed_exts[_Idx] >= 0 && _STD in_range(_Mixed_exts[_Idx]), "Either N must be zero or exts[r] must be nonnegative and must be representable as value of type " "index_type for every rank index r (N4950 [mdspan.extents.cons]/10.2)"); } } +#endif // _CONTAINER_DEBUG_LEVEL > 0 } template @@ -280,6 +304,7 @@ template class _Fwd_prod_of_extents { public: _NODISCARD static constexpr _Extents::index_type _Calculate(const _Extents& _Exts, const size_t _Idx) noexcept { + _STL_INTERNAL_CHECK(_Idx <= _Extents::_Rank); if constexpr (_Extents::rank() == 0) { return 1; } else { @@ -293,7 +318,7 @@ public: }; template - requires ((_Extents != dynamic_extent) && ...) + requires (sizeof...(_Extents) > 0) && ((_Extents != dynamic_extent) && ...) class _Fwd_prod_of_extents> { private: using _Ty = extents<_IndexType, _Extents...>; @@ -301,8 +326,8 @@ private: _NODISCARD static consteval auto _Make_prods() noexcept { array _Result; _Result.front() = 1; - for (size_t _Dim = 1; _Dim < _Ty::_Rank + 1; ++_Dim) { - _Result[_Dim] = static_cast<_Ty::index_type>(_Result[_Dim - 1] * _Ty::static_extent(_Dim - 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)); } return _Result; } @@ -311,6 +336,7 @@ private: public: _NODISCARD static constexpr _Ty::index_type _Calculate(const _Ty&, const size_t _Idx) noexcept { + _STL_INTERNAL_CHECK(_Idx <= _Ty::_Rank); return _Cache[_Idx]; } }; @@ -320,6 +346,7 @@ template class _Rev_prod_of_extents { public: _NODISCARD static constexpr _Extents::index_type _Calculate(const _Extents& _Exts, const size_t _Idx) noexcept { + _STL_INTERNAL_CHECK(_Idx < _Extents::_Rank); typename _Extents::index_type _Result = 1; for (size_t _Dim = _Idx + 1; _Dim < _Extents::_Rank; ++_Dim) { _Result *= _Exts.extent(_Dim); @@ -337,8 +364,8 @@ private: _NODISCARD static consteval auto _Make_prods() noexcept { array _Result; _Result.back() = 1; - for (size_t _Dim = _Ty::_Rank; _Dim-- > 1;) { - _Result[_Dim - 1] = static_cast<_Ty::index_type>(_Result[_Dim] * _Ty::static_extent(_Dim)); + for (size_t _Idx = _Ty::_Rank; _Idx-- > 1;) { + _Result[_Idx - 1] = static_cast<_Ty::index_type>(_Result[_Idx] * _Ty::static_extent(_Idx)); } return _Result; } @@ -347,6 +374,7 @@ private: public: _NODISCARD static constexpr _Ty::index_type _Calculate(const _Ty&, const size_t _Idx) noexcept { + _STL_INTERNAL_CHECK(_Idx < _Ty::_Rank); return _Cache[_Idx]; } }; diff --git a/tests/std/include/test_mdspan_support.hpp b/tests/std/include/test_mdspan_support.hpp index b653d74268e..c6c1f034fa0 100644 --- a/tests/std/include/test_mdspan_support.hpp +++ b/tests/std/include/test_mdspan_support.hpp @@ -177,7 +177,7 @@ namespace details { template constexpr void check_members_with_mixed_extents(Fn&& fn) { auto select_extent = [](size_t e) consteval { - return e == std::dynamic_extent ? std::min(sizeof...(Extents), size_t{3}) : e; + return e == std::dynamic_extent ? (std::min)(sizeof...(Extents), size_t{3}) : e; }; // Check signed integers @@ -198,7 +198,7 @@ namespace details { template constexpr void check_members_with_various_extents_impl(Fn&& fn, std::index_sequence) { auto static_or_dynamic = [](size_t i) consteval { - return i == 0 ? std::dynamic_extent : std::min(sizeof...(Seq), size_t{3}); + return i == 0 ? std::dynamic_extent : (std::min)(sizeof...(Seq), size_t{3}); }; if constexpr (sizeof...(Seq) <= 1) { diff --git a/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp b/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp index 2c80d249ab3..78e3e9ad2ad 100644 --- a/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp @@ -1,12 +1,15 @@ // 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; @@ -39,12 +42,18 @@ void test_construction_from_pack_with_invalid_values() { [[maybe_unused]] extents e{1, 1}; } -void test_construction_from_pack_with_unrepresentable_as_index_type_values() { +void test_construction_from_pack_with_unrepresentable_as_index_type_values_1() { // 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{1, 256}; } +void test_construction_from_pack_with_unrepresentable_as_index_type_values_2() { + // 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{ConvertibleToInt{.val = 1}, 256}; +} + void test_construction_from_span_with_invalid_values() { int vals[] = {1, 2}; span s{vals}; @@ -82,7 +91,8 @@ int main(int argc, char* argv[]) { test_construction_from_other_extents_with_invalid_values, test_construction_from_other_extents_with_unrepresentable_as_index_type_values, test_construction_from_pack_with_invalid_values, - test_construction_from_pack_with_unrepresentable_as_index_type_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_span_with_invalid_values, test_construction_from_span_with_unrepresentable_as_index_type_values, test_construction_from_array_with_invalid_values,