diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 6a357a01b6c..8f86197b54a 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -240,6 +240,15 @@ public: } } + // TRANSITION, LWG ISSUE? I believe that this function should return 'index_type' + _NODISCARD constexpr index_type _Fwd_prod_of_extents(const rank_type _Idx) const noexcept { + index_type _Result = 1; + for (rank_type _Dim = 0; _Dim < _Idx; ++_Dim) { + _Result *= extent(_Dim); + } + return _Result; + } + _NODISCARD static _CONSTEVAL bool _Is_index_space_size_representable() { if constexpr (rank_dynamic() == 0 && rank() > 0) { return _STD in_range((_Extents * ...)); @@ -314,24 +323,52 @@ public: constexpr mapping() noexcept = default; constexpr mapping(const mapping&) noexcept = default; - constexpr mapping(const extents_type& _Exts_) noexcept : _Exts(_Exts_) {} + constexpr mapping(const extents_type& _Exts_) noexcept : _Exts(_Exts_) { + // TRANSITION, CHECK [mdspan.layout.left.cons]/1 (REQUIRES '_Multiply_with_overflow_check' FROM #3561) + } template requires is_constructible_v constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const mapping<_OtherExtents>& _Other) noexcept - : _Exts(_Other.extents()) {} + : _Exts(_Other.extents()) { + _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 (N4944 " + "[mdspan.layout.left.cons]/4)."); + } template requires (extents_type::rank() <= 1) && is_constructible_v constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const layout_right::mapping<_OtherExtents>& _Other) noexcept - : _Exts(_Other.extents()) {} + : _Exts(_Other.extents()) { + _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 (N4944 " + "[mdspan.layout.left.cons]/7)."); + } template requires is_constructible_v constexpr explicit(extents_type::rank() > 0) mapping(const layout_stride::template mapping<_OtherExtents>& _Other) - : _Exts(_Other.extents()) {} + : _Exts(_Other.extents()) { + if constexpr (extents_type::rank() > 0) { + const bool _Verify = [&](index_sequence<_Indices...>) { + index_type _Prod = 1; + return ( + (_Other.stride(_Indices) + == (_Indices + 1 == extents_type::rank() + ? _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().fwd-prod-of-extents(r) (N4944 [mdspan.layout.left.cons]/10.1)."); + } + _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 (N4944 " + "[mdspan.layout.left.cons]/10.2)."); + } constexpr mapping& operator=(const mapping&) noexcept = default; @@ -340,19 +377,14 @@ public: } _NODISCARD constexpr index_type required_span_size() const noexcept { - index_type _Result = 1; - for (rank_type _Dim = 0; _Dim < extents_type::rank(); ++_Dim) { - _Result *= _Exts.extent(_Dim); - } - return _Result; + return _Exts._Fwd_prod_of_extents(extents_type::rank()); } - template - requires (sizeof...(_Indices) == extents_type::rank()) && (is_convertible_v<_Indices, index_type> && ...) - && (is_nothrow_constructible_v && ...) - _NODISCARD constexpr index_type operator()(_Indices... _Idx) const noexcept { - return _Index_impl...>( - static_cast(_Idx)..., make_index_sequence{}); + template + 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 { + return _Index_impl(make_index_sequence{}, static_cast(_Indices)...); } _NODISCARD static constexpr bool is_always_unique() noexcept { @@ -367,44 +399,41 @@ public: return true; } - _NODISCARD constexpr bool is_unique() const noexcept { + _NODISCARD static constexpr bool is_unique() noexcept { return true; } - _NODISCARD constexpr bool is_exhaustive() const noexcept { + _NODISCARD static constexpr bool is_exhaustive() noexcept { return true; } - _NODISCARD constexpr bool is_strided() const noexcept { + _NODISCARD static constexpr bool is_strided() noexcept { return true; } - _NODISCARD constexpr index_type stride(const rank_type _Rank) const noexcept + _NODISCARD constexpr index_type stride(const rank_type _Idx) const noexcept requires (extents_type::rank() > 0) { - index_type _Result = 1; - for (rank_type _Dim = 0; _Dim < _Rank; ++_Dim) { - _Result *= _Exts.extent(_Dim); - } - - return _Result; + _STL_VERIFY(_Idx < extents_type::rank(), + "Value of i must be less than extents_type::rank() (N4944 [mdspan.layout.left.obs]/6)."); + return _Exts._Fwd_prod_of_extents(_Idx); } template requires (extents_type::rank() == _OtherExtents::rank()) _NODISCARD_FRIEND constexpr bool operator==(const mapping& _Left, const mapping<_OtherExtents>& _Right) noexcept { - return _Left.extents() == _Right.extents(); + return _Left._Exts == _Right.extents(); } private: extents_type _Exts{}; - template - constexpr index_type _Index_impl(_IndexType... _Idx, index_sequence<_Seq...>) const noexcept { - // return _Extents::rank() > 0 ? ((_Idx * stride(_Seq)) + ... + 0) : 0; + template + _NODISCARD constexpr index_type _Index_impl(index_sequence<_Seq...>, _IndexTypes... _Indices) const noexcept { + _STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...)); index_type _Stride = 1; index_type _Result = 0; - (((_Result += _Idx * _Stride), (void) (_Stride *= _Exts.extent(_Seq))), ...); + (((_Result += _Indices * _Stride), (_Stride *= _Exts.extent(_Seq))), ...); return _Result; } }; @@ -559,7 +588,7 @@ public: && is_nothrow_constructible_v #endif // ^^^ no workaround ^^^ constexpr mapping(const extents_type& _Exts_, const span<_OtherIndexType, extents_type::rank()> _Strides_) noexcept - : _Exts{_Exts_} { + : _Exts(_Exts_) { for (rank_type _Idx = 0; _Idx < extents_type::rank(); ++_Idx) { _Strides[_Idx] = _Strides_[_Idx]; } @@ -576,7 +605,7 @@ public: #endif // ^^^ no workaround ^^^ constexpr mapping( const extents_type& _Exts_, const array<_OtherIndexType, extents_type::rank()>& _Strides_) noexcept - : _Exts{_Exts_} { + : _Exts(_Exts_) { for (rank_type _Idx = 0; _Idx < extents_type::rank(); ++_Idx) { _Strides[_Idx] = _Strides_[_Idx]; } @@ -789,7 +818,6 @@ public: template requires (is_convertible_v<_OtherIndexTypes, index_type> && ...) && (is_nothrow_constructible_v && ...) - && (sizeof...(_OtherIndexTypes) > 0) && (sizeof...(_OtherIndexTypes) == rank() || sizeof...(_OtherIndexTypes) == rank_dynamic()) && is_constructible_v && is_default_constructible_v constexpr explicit mdspan(data_handle_type _Ptr_, _OtherIndexTypes... _Exts) @@ -846,8 +874,8 @@ public: requires (is_convertible_v<_OtherIndexTypes, index_type> && ...) && (is_nothrow_constructible_v && ...) && (sizeof...(_OtherIndexTypes) == rank()) - _NODISCARD constexpr reference operator()(const _OtherIndexTypes... _Indices) const { - return _Acc.access(_Ptr, _Map(static_cast(_STD move(_Indices))...)); + _NODISCARD constexpr reference operator()(_OtherIndexTypes... _Indices) const { + return _Acc.access(_Ptr, static_cast(_Map(static_cast(_STD move(_Indices))...))); } template diff --git a/tests/std/include/test_mdspan_support.hpp b/tests/std/include/test_mdspan_support.hpp new file mode 100644 index 00000000000..16d66474179 --- /dev/null +++ b/tests/std/include/test_mdspan_support.hpp @@ -0,0 +1,116 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once + +#include +#include +#include +#include +#include + +enum class IsNothrow : bool { no, yes }; + +template +struct ConvertibleToInt { + constexpr operator Int() const noexcept(std::to_underlying(Nothrow)) { + return Int{1}; + } +}; + +struct NonConvertibleToAnything {}; + +template +constexpr void check_implicit_conversion(T); // not defined + +// clang-format off +template +concept NotImplicitlyConstructibleFrom = + std::constructible_from + && !requires(Args&&... args) { check_implicit_conversion({std::forward(args)...}); }; +// clang-format on + +template +inline constexpr bool is_extents_v = false; + +template +inline constexpr bool is_extents_v> = true; + +template +inline constexpr bool is_mapping_of_v = + std::is_same_v, Mapping>; + +template +concept CheckNestedTypesOfLayoutMapping = + requires { + requires is_extents_v; + requires std::same_as; + requires std::same_as; + requires is_mapping_of_v; + }; + +template +concept CheckMemberFunctionsOfLayoutMapping = requires(const M m) { + { m.extents() } -> std::same_as; + { m.required_span_size() } -> std::same_as; + { m.is_unique() } -> std::same_as; + { m.is_exhaustive() } -> std::same_as; + { m.is_strided() } -> std::same_as; + }; + +template +concept CheckStaticFunctionsOfLayoutMapping = requires(const M m) { + { M::is_always_strided() } -> std::same_as; + { M::is_always_exhaustive() } -> std::same_as; + { M::is_always_unique() } -> std::same_as; + std::bool_constant::value; + std::bool_constant::value; + std::bool_constant::value; + }; + +// clang-format off +template +concept CheckCallOperatorOfLayoutMapping = + requires(const M m, Indices... i) { + { m(i...) } -> std::same_as; + { m(i...) == m(static_cast(i)...) } -> std::same_as; + }; +// clang-format on + +template +concept CheckStrideMemberFunction = requires(M mapping, typename M::rank_type i) { + { mapping.stride(i) } -> std::same_as; + }; + +template +constexpr bool check_layout_mapping_requirements() { + static_assert(std::copyable); + static_assert(std::equality_comparable); + static_assert(std::is_nothrow_move_constructible_v); + static_assert(std::is_nothrow_move_assignable_v); + static_assert(std::is_nothrow_swappable_v); + static_assert(CheckNestedTypesOfLayoutMapping); + static_assert(CheckMemberFunctionsOfLayoutMapping); + static_assert(CheckStaticFunctionsOfLayoutMapping); + + [](std::index_sequence) { + static_assert(CheckCallOperatorOfLayoutMapping); + } + (std::make_index_sequence{}); + + if constexpr (requires(M m, typename M::rank_type i) { m.stride(i); }) { + static_assert(CheckStrideMemberFunction); + } + + return true; +} + +template + requires is_extents_v +constexpr bool check_layout_mapping_policy_requirements() { + using X = typename MP::template mapping; + static_assert(check_layout_mapping_requirements()); + static_assert(std::same_as); + static_assert(std::same_as); + return true; +} diff --git a/tests/std/test.lst b/tests/std/test.lst index 06841dee535..7e133eec9d8 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -233,6 +233,8 @@ tests\P0009R18_mdspan tests\P0009R18_mdspan_default_accessor tests\P0009R18_mdspan_extents tests\P0009R18_mdspan_extents_death +tests\P0009R18_mdspan_layout_left +tests\P0009R18_mdspan_layout_left_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/test.cpp b/tests/std/tests/P0009R18_mdspan_extents/test.cpp index bd6c2de05f5..83cf926a70a 100644 --- a/tests/std/tests/P0009R18_mdspan_extents/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_extents/test.cpp @@ -10,28 +10,9 @@ #include #include -using namespace std; - -enum class IsNothrow : bool { no, yes }; - -template -struct ConvertibleToInt { - constexpr operator Int() const noexcept(to_underlying(Nothrow)) { - return Int{1}; - } -}; +#include "test_mdspan_support.hpp" -struct NonConvertibleToAnything {}; - -template -constexpr void check_implicit_conversion(T); // not defined - -// clang-format off -template -concept NotImplicitlyConstructibleFrom = - constructible_from - && !requires(Args&&... args) { check_implicit_conversion({forward(args)...}); }; -// clang-format on +using namespace std; template constexpr void do_check_members(index_sequence) { @@ -237,7 +218,7 @@ constexpr void check_construction_from_array_and_span() { static_assert(!is_constructible_v>); } - { // Check construciton with integers with mismatched signs + { // Check construction with integers with mismatched signs using Ext = extents; array arr = {4ull}; diff --git a/tests/std/tests/P0009R18_mdspan_layout_left/env.lst b/tests/std/tests/P0009R18_mdspan_layout_left/env.lst new file mode 100644 index 00000000000..18e2d7c71ec --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_layout_left/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_layout_left/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp new file mode 100644 index 00000000000..f9f74fed954 --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp @@ -0,0 +1,372 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +#include "test_mdspan_support.hpp" + +using namespace std; + +template +concept CanInvokeCallOperatorOfMapping = requires(Mapping m, Indices... i) { + { m(i...) } -> same_as; + }; + +template +constexpr void do_check_members(const extents& ext, index_sequence) { + using Ext = extents; + using Mapping = layout_left::mapping; + + // layout_left meets the layout mapping policy requirements and is a trivial type + static_assert(check_layout_mapping_policy_requirements()); + static_assert(is_trivial_v); + + // layout_left::mapping is a trivially copyable type that models regular for each Ext + static_assert(is_trivially_copyable_v); + static_assert(regular); + + // Check member types + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + + { // Check default and copy constructor + Mapping m; + Mapping cpy = m; + assert(cpy == m); + static_assert(is_nothrow_default_constructible_v); + static_assert(is_nothrow_copy_constructible_v); + } + + { // Check construction from extents_type + Mapping m{ext}; + assert(m.extents() == ext); + static_assert(is_nothrow_constructible_v); + } + + using OtherIndexType = long long; + using Ext2 = extents; + using Mapping2 = layout_left::mapping; + + { // Check construction from other layout_left::mapping + Mapping m1{ext}; + Mapping2 m2{m1}; + assert(m1 == m2); + static_assert(is_nothrow_constructible_v); + // Other tests are defined in 'check_construction_from_other_left_mapping' function + } + + { // Check construction from layout_right::mapping + using RightMapping = layout_right::mapping; + if constexpr (Ext::rank() <= 1) { + RightMapping right_mapping{ext}; + [[maybe_unused]] Mapping m1{right_mapping}; + [[maybe_unused]] Mapping2 m2{right_mapping}; + assert(m1 == m2); + static_assert(is_nothrow_constructible_v); + static_assert(is_nothrow_constructible_v); + } else { + static_assert(!is_constructible_v); + static_assert(!is_constructible_v); + } + // Other tests are defined in 'check_construction_from_other_right_mapping' function + } + +#pragma warning(push) // TRANSITION, "/analyze:only" BUG? +#pragma warning(disable : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + { // Check construction from layout_stride::mapping + array strides{1}; + for (size_t i = 1; i < Ext::rank(); ++i) { + strides[i] = static_cast(strides[i - 1] * ext.extent(i)); + } + + using StrideMapping = layout_stride::mapping; + StrideMapping stride_mapping{ext, strides}; + [[maybe_unused]] Mapping m{stride_mapping}; + // Other tests are defined in 'check_construction_from_other_stride_mapping' function + } +#pragma warning(pop) // TRANSITION, "/analyze:only" BUG? + + Mapping m{ext}; // For later use + + { // Check 'extents' function + assert(m.extents() == ext); + static_assert(noexcept(m.extents())); + } + + { // Check 'required_span_size' function + const IndexType expected_value = static_cast((ext.extent(Indices) * ... * 1)); + assert(m.required_span_size() == expected_value); + static_assert(noexcept(m.required_span_size())); + } + + { // Check operator() + assert(m(((void) Indices, 0)...) == 0); + assert(m((ext.extent(Indices) - 1)...) == static_cast((ext.extent(Indices) * ... * 1)) - 1); + static_assert(noexcept(m(((void) Indices, 0)...))); + static_assert(noexcept(m((ext.extent(Indices) - 1)...))); + // Other tests are defined in 'check_call_operator' function + } + + { // Check 'is_always_[unique/exhaustive/strided]' functions + static_assert(Mapping::is_always_unique()); + static_assert(Mapping::is_always_exhaustive()); + static_assert(Mapping::is_always_strided()); + } + + { // Check 'is_[unique/exhaustive/strided]' functions + static_assert(Mapping::is_unique()); + static_assert(Mapping::is_exhaustive()); + static_assert(Mapping::is_strided()); + } + + if constexpr (Ext::rank() > 0) { // Check 'stride' function + const IndexType expected_value = + static_cast((ext.extent(Indices) * ... * 1) / ext.extent(Ext::rank() - 1)); + assert(m.stride(Ext::rank() - 1) == expected_value); + assert(m.stride(0) == 1); + static_assert(noexcept(m.stride(Ext::rank() - 1))); + static_assert(noexcept(m.stride(0))); + } else { + static_assert(!CheckStrideMemberFunction); + } + + { // Check comparisons + assert(m == m); + // Other tests are defined in 'check_comparisons' function + } +} + +template +constexpr void check_members(extents ext) { + do_check_members(ext, make_index_sequence{}); +} + +constexpr void check_construction_from_other_left_mapping() { + { // Check invalid construction + using Mapping = layout_left::mapping>; + static_assert(!is_constructible_v>>); + static_assert(!is_constructible_v>>); + } + + { // Check implicit conversions + static_assert(!NotImplicitlyConstructibleFrom>, + layout_left::mapping>>); + static_assert(NotImplicitlyConstructibleFrom>, + layout_left::mapping>>); + static_assert(NotImplicitlyConstructibleFrom>, + layout_left::mapping>>); + static_assert(NotImplicitlyConstructibleFrom>, + layout_left::mapping>>); + } +} + +constexpr void check_construction_from_other_right_mapping() { + { // Check construction from layout_right::mapping with various values of E::rank() + static_assert( + is_constructible_v>, layout_right::mapping>>); + static_assert( + is_constructible_v>, layout_right::mapping>>); + static_assert( + !is_constructible_v>, layout_right::mapping>>); + static_assert( + !is_constructible_v>, layout_right::mapping>>); + } + + { // Check construction from layout_right::mapping when E is invalid + using Mapping = layout_left::mapping>; + static_assert(!is_constructible_v>>); + static_assert(!is_constructible_v>>); + } + + { // Check implicit conversions + static_assert(!NotImplicitlyConstructibleFrom>, + layout_right::mapping>>); + static_assert(NotImplicitlyConstructibleFrom>, + layout_right::mapping>>); + static_assert(NotImplicitlyConstructibleFrom>, + layout_right::mapping>>); + } +} + +constexpr void check_construction_from_other_stride_mapping() { + { // Check construction from layout_stride::mapping with various values of E::rank() + static_assert( + is_constructible_v>, layout_stride::mapping>>); + static_assert( + is_constructible_v>, layout_stride::mapping>>); + static_assert( + is_constructible_v>, layout_stride::mapping>>); + static_assert( + is_constructible_v>, layout_stride::mapping>>); + } + + { // Check construction from layout_stride::mapping when E is invalid + using Mapping = layout_left::mapping>; + static_assert(!is_constructible_v>>); + static_assert(!is_constructible_v>>); + } + + { // Check implicit conversions + static_assert( + !NotImplicitlyConstructibleFrom>, layout_stride::mapping>>); + static_assert(NotImplicitlyConstructibleFrom>, + layout_stride::mapping>>); + static_assert(NotImplicitlyConstructibleFrom>, + layout_stride::mapping>>); + static_assert(NotImplicitlyConstructibleFrom>, + layout_stride::mapping>>); + } +} + +constexpr void check_call_operator() { + { // Check call with invalid amount of indices + using Mapping = layout_left::mapping>; + static_assert(!CanInvokeCallOperatorOfMapping); + static_assert(!CanInvokeCallOperatorOfMapping); + static_assert(CanInvokeCallOperatorOfMapping); + static_assert(!CanInvokeCallOperatorOfMapping); + } + + { // Check call with invalid types + using Mapping = layout_left::mapping>; + static_assert(CanInvokeCallOperatorOfMapping); + static_assert(CanInvokeCallOperatorOfMapping); + static_assert(CanInvokeCallOperatorOfMapping>); + static_assert(CanInvokeCallOperatorOfMapping>); + static_assert(!CanInvokeCallOperatorOfMapping); + } + + { // Check call with types that might throw during conversion + using Mapping = layout_left::mapping>; + static_assert(CanInvokeCallOperatorOfMapping>); + static_assert(!CanInvokeCallOperatorOfMapping>); + } + + { // Check various mappings + layout_left::mapping> m1; + assert(m1() == 0); + + layout_left::mapping> m2; + assert(m2(0) == 0); + assert(m2(1) == 1); + assert(m2(2) == 2); + + layout_left::mapping> m3{dextents{5, 6}}; + assert(m3(0, 0) == 0); + assert(m3(1, 0) == 1); + assert(m3(0, 1) == 5); + assert(m3(1, 1) == 6); + assert(m3(2, 1) == 7); + assert(m3(1, 2) == 11); + assert(m3(4, 5) == 29); + } +} + +constexpr void check_comparisons() { + using StaticMapping = layout_left::mapping>; + using DynamicMapping = layout_left::mapping>; + + { // Check equality_comparable_with concept + static_assert(equality_comparable_with); + static_assert(!equality_comparable_with>>); + static_assert(!equality_comparable_with>>); + } + + { // Check correctness + StaticMapping m1; + DynamicMapping m2{dextents{3}}; + DynamicMapping m3{dextents{2}}; + assert(m1 == m2); + assert(m2 != m3); + assert(m1 != m3); + } +} + +constexpr void check_correctness() { + { // empty extents + const array values{}; + mdspan, layout_left> nothing{values.data()}; + assert(nothing.size() == 1); + } + + { // regular vector + const array values{0, 1, 2}; + mdspan, layout_left> vec{values.data()}; + + // TRANSITION, use operator[] + assert(vec(0) == 0); + assert(vec(1) == 1); + assert(vec(2) == 2); + } + + { // 3x2 matrix with column-major order + const array values{0, 1, 2, 3, 4, 5}; + mdspan, layout_left> matrix{values.data()}; + + // TRANSITION, use operator[] + assert(matrix(0, 0) == 0); + assert(matrix(1, 0) == 1); + assert(matrix(2, 0) == 2); + assert(matrix(0, 1) == 3); + assert(matrix(1, 1) == 4); + assert(matrix(2, 1) == 5); + } + + { // 3x2x4 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}; + mdspan, layout_left> tensor{values.data(), 3, 2, 4}; + + // TRANSITION, use operator[] + assert(tensor(0, 0, 0) == 0); + assert(tensor(2, 0, 0) == 2); + assert(tensor(1, 1, 1) == 10); + assert(tensor(0, 0, 3) == 18); + assert(tensor(2, 2, 2) == 20); + assert(tensor(2, 1, 3) == 23); + } + + { // 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}; + mdspan, layout_left> tensor{values.data(), 2, 3}; + + // TRANSITION, use operator[] + assert(tensor(0, 0, 0, 0) == 0); + assert(tensor(1, 0, 0, 0) == 1); + assert(tensor(0, 1, 1, 0) == 8); + assert(tensor(0, 0, 0, 1) == 12); + assert(tensor(0, 0, 0, 2) == 24); + assert(tensor(0, 2, 0, 2) == 28); + assert(tensor(1, 2, 1, 2) == 35); + } +} + +constexpr bool test() { + check_members(extents{}); + check_members(extents{}); + check_members(extents{}); + check_members(extents{3}); + check_members(extents{4, 5}); + check_members(extents{3, 3, 3}); + check_construction_from_other_left_mapping(); + check_construction_from_other_right_mapping(); + check_construction_from_other_stride_mapping(); + check_call_operator(); + check_comparisons(); + check_correctness(); + return true; +} + +int main() { + static_assert(test()); + test(); +} diff --git a/tests/std/tests/P0009R18_mdspan_layout_left_death/env.lst b/tests/std/tests/P0009R18_mdspan_layout_left_death/env.lst new file mode 100644 index 00000000000..18e2d7c71ec --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_layout_left_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_layout_left_death/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_left_death/test.cpp new file mode 100644 index 00000000000..64fd0f7c728 --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_layout_left_death/test.cpp @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +#include + +using namespace std; + +// TRANSITION, Test Construction From extents_type + +void test_construction_from_other_left_mapping() { + layout_left::mapping> m1{dextents{256}}; + // Value of other.required_span_size() must be representable as a value of type index_type + layout_left::mapping> m2{m1}; +} + +void test_construction_from_other_right_mapping() { + layout_right::mapping> m1{dextents{256}}; + // Value of other.required_span_size() must be representable as a value of type index_type + layout_left::mapping> m2{m1}; +} + +#pragma warning(push) // TRANSITION, "/analyze:only" BUG? +#pragma warning(disable : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call +void test_construction_from_other_stride_mapping_1() { + using Ext = extents; + layout_stride::mapping m1{Ext{}, array{1, 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}; +} + +void test_construction_from_other_stride_mapping_2() { + layout_stride::mapping> m1{dextents{256}, array{1}}; + // Value of other.required_span_size() must be representable as a value of type index_type + layout_left::mapping> m2{m1}; +} +#pragma warning(pop) // TRANSITION, "/analyze:only" BUG? + +void test_stride_function() { + layout_left::mapping> m; + // Value of i must be less than extents_type::rank() + (void) m.stride(1); +} + +int main(int argc, char* argv[]) { + std_testing::death_test_executive exec; + exec.add_death_tests({ + // TRANSITION Construction From extents_type + test_construction_from_other_left_mapping, + test_construction_from_other_right_mapping, + test_construction_from_other_stride_mapping_1, + test_construction_from_other_stride_mapping_2, + test_stride_function, + }); + return exec.run(argc, argv); +}