From 69751addedffe90d7a9c0d91a4e4587c30d6b0b2 Mon Sep 17 00:00:00 2001 From: Matt Stephanson <68978048+MattStephanson@users.noreply.github.com> Date: Fri, 24 Feb 2023 22:15:22 -0800 Subject: [PATCH 01/57] Implement P0009R18 `` et al. (#3502) * First cut of `mdspan`, known to be incomplete. --- stl/CMakeLists.txt | 1 + stl/inc/__msvc_all_public_headers.hpp | 1 + stl/inc/header-units.json | 1 + stl/inc/mdspan | 901 +++++++++++++ stl/inc/yvals_core.h | 6 + stl/modules/std.ixx | 1 + tests/std/test.lst | 1 + tests/std/tests/P0009R18_mdspan/env.lst | 4 + tests/std/tests/P0009R18_mdspan/test.cpp | 1117 +++++++++++++++++ .../importable_cxx_library_headers.jsonc | 1 + .../test.cpp | 1 + .../test.compile.pass.cpp | 14 + 12 files changed, 2049 insertions(+) create mode 100644 stl/inc/mdspan create mode 100644 tests/std/tests/P0009R18_mdspan/env.lst create mode 100644 tests/std/tests/P0009R18_mdspan/test.cpp diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 4bc93c646c..cdac12a038 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -174,6 +174,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/list ${CMAKE_CURRENT_LIST_DIR}/inc/locale ${CMAKE_CURRENT_LIST_DIR}/inc/map + ${CMAKE_CURRENT_LIST_DIR}/inc/mdspan ${CMAKE_CURRENT_LIST_DIR}/inc/memory ${CMAKE_CURRENT_LIST_DIR}/inc/memory_resource ${CMAKE_CURRENT_LIST_DIR}/inc/mutex diff --git a/stl/inc/__msvc_all_public_headers.hpp b/stl/inc/__msvc_all_public_headers.hpp index 1734fa07b2..11950e6310 100644 --- a/stl/inc/__msvc_all_public_headers.hpp +++ b/stl/inc/__msvc_all_public_headers.hpp @@ -106,6 +106,7 @@ #include #include #include +#include #include #include #include diff --git a/stl/inc/header-units.json b/stl/inc/header-units.json index 612fae9502..270dd1b25e 100644 --- a/stl/inc/header-units.json +++ b/stl/inc/header-units.json @@ -80,6 +80,7 @@ "list", "locale", "map", + "mdspan", "memory", "memory_resource", "mutex", diff --git a/stl/inc/mdspan b/stl/inc/mdspan new file mode 100644 index 0000000000..4724eff0d7 --- /dev/null +++ b/stl/inc/mdspan @@ -0,0 +1,901 @@ +// mdspan standard header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef _MDSPAN_ +#define _MDSPAN_ +#include +#if _STL_COMPILER_PREPROCESSOR +#if !_HAS_CXX23 +_EMIT_STL_WARNING(STL4038, "The contents of are available only with C++23 or later."); +#else // ^^^ !_HAS_CXX23 / _HAS_CXX23 vvv +#include +#include +#include +#include +#include + +#pragma pack(push, _CRT_PACKING) +#pragma warning(push, _STL_WARNING_LEVEL) +#pragma warning(disable : _STL_DISABLED_WARNINGS) +_STL_DISABLE_CLANG_WARNINGS +#pragma push_macro("new") +#undef new + +_STD_BEGIN + +template +struct _Mdspan_extent_type { + using index_type = _IndexType; + + index_type _Dynamic_extents[_Rank_dynamic] = {}; + static constexpr size_t _Static_extents[sizeof...(_Extents)] = {_Extents...}; + static constexpr array _Dynamic_indexes = []() constexpr { + array result; + size_t _Counter = 0; + for (size_t i = 0; i < sizeof...(_Extents); ++i) { + result[i] = _Counter; + if (_Static_extents[i] == dynamic_extent) { + ++_Counter; + } + } + return result; + }(); + + constexpr _Mdspan_extent_type() noexcept = default; + + template && ...), + int> = 0> + constexpr _Mdspan_extent_type(_OtherIndexTypes... _OtherExtents) noexcept : _Dynamic_extents{_OtherExtents...} {} + + template = 0> + constexpr _Mdspan_extent_type(span<_OtherIndexType, _Size> _Data, index_sequence<_Idx...>) noexcept + : _Dynamic_extents{static_cast(_STD as_const(_Data[_Idx]))...} {} + + + template && ...), + int> = 0> + constexpr _Mdspan_extent_type(_OtherIndexTypes... _OtherExtents) noexcept { + auto _It = _Dynamic_extents; + ((_Extents == dynamic_extent ? void(*_It++ = _OtherExtents) : void(_OtherExtents)), ...); + } + + template = 0> + constexpr _Mdspan_extent_type(span<_OtherIndexType, _Size> _Data, index_sequence<_Idx...>) noexcept + : _Dynamic_extents{{static_cast(_STD as_const(_Data[_Dynamic_indexes[_Idx]]))...}} {} + + constexpr index_type* _Begin_dynamic_extents() noexcept { + return _Dynamic_extents; + } + + constexpr const index_type* _Begin_dynamic_extents() const noexcept { + return _Dynamic_extents; + } +}; + +template +struct _Mdspan_extent_type<_IndexType, 0, _Extents...> { + using index_type = _IndexType; + + static constexpr size_t _Static_extents[sizeof...(_Extents)] = {_Extents...}; + + constexpr _Mdspan_extent_type() noexcept = default; + + template = 0> + constexpr _Mdspan_extent_type(_IndexTypes... /*_OtherExtents*/) noexcept {} + + template = 0> + constexpr _Mdspan_extent_type(span<_OtherIndexType, _Size>, index_sequence<_Idx...>) noexcept {} + + constexpr index_type* _Begin_dynamic_extents() noexcept { + return nullptr; + } + + constexpr const index_type* _Begin_dynamic_extents() const noexcept { + return nullptr; + } +}; + +template +struct _Mdspan_extent_type<_IndexType, 0> { + using index_type = _IndexType; + + constexpr index_type* _Begin_dynamic_extents() { + return nullptr; + } + + constexpr const index_type* _Begin_dynamic_extents() const noexcept { + return nullptr; + } +}; + +template +class extents : private _Mdspan_extent_type<_IndexType, ((_Extents == dynamic_extent) + ... + 0), _Extents...> { +public: + using _Mybase = _Mdspan_extent_type<_IndexType, ((_Extents == dynamic_extent) + ... + 0), _Extents...>; + using index_type = typename _Mybase::index_type; + using size_type = make_unsigned_t; + using rank_type = size_t; + + // TRANSITION: doesn't account for extended integer types + static_assert(_Is_any_of_v, signed char, unsigned char, short, unsigned short, int, + unsigned int, long, unsigned long, long long, unsigned long long>, + "N4928 [mdspan.extents.overview]/2 " + "requires that extents::index_type be a signed or unsigned integer type."); + + static_assert(((_Extents == dynamic_extent || _Extents <= (numeric_limits<_IndexType>::max)()) && ...)); + + _NODISCARD static constexpr rank_type rank() noexcept { + return sizeof...(_Extents); + } + + _NODISCARD static constexpr rank_type rank_dynamic() noexcept { + return ((_Extents == dynamic_extent) + ... + 0); + } + + _NODISCARD static constexpr size_t static_extent(const rank_type _Idx) noexcept { + return _Mybase::_Static_extents[_Idx]; + } + + _NODISCARD constexpr index_type extent(const rank_type _Idx) const noexcept { + if constexpr (rank_dynamic() == 0) { + return static_cast(_Mybase::_Static_extents[_Idx]); + } else if constexpr (rank_dynamic() == rank()) { + return _Mybase::_Dynamic_extents[_Idx]; + } else { + const auto _Static_extent = _Mybase::_Static_extents[_Idx]; + if (_Static_extent == dynamic_extent) { + return _Mybase::_Dynamic_extents[_Mybase::_Dynamic_indexes[_Idx]]; + } else { + return static_cast(_Static_extent); + } + } + } + + constexpr extents() noexcept = default; + + template = 0> + explicit((((_Extents != dynamic_extent) && (_OtherExtents == dynamic_extent)) || ...) + || numeric_limits::max() + < numeric_limits<_OtherIndexType>::max()) constexpr extents(const extents<_OtherIndexType, + _OtherExtents...>& _Other) noexcept { + auto _Dynamic_it = _Mybase::_Begin_dynamic_extents(); + for (rank_type _Dim = 0; _Dim < sizeof...(_Extents); ++_Dim) { + if (_Mybase::_Static_extents[_Dim] == dynamic_extent) { + *_Dynamic_it++ = _Other.extent(_Dim); + } + } + } + + template && ...) + && (is_nothrow_constructible_v && ...) + && (sizeof...(_OtherIndexTypes) == rank_dynamic() || sizeof...(_OtherIndexTypes) == rank()), + int> = 0> + explicit constexpr extents(_OtherIndexTypes... _Exts) noexcept + : _Mybase{static_cast(_STD move(_Exts))...} {} + + template + && is_nothrow_constructible_v + && (_Size == rank_dynamic() || _Size == rank()), + int> = 0> + explicit(_Size != rank_dynamic()) constexpr extents(const array<_OtherIndexType, _Size>& _Exts) noexcept + : _Mybase{span{_Exts}, _STD make_index_sequence{}} {} + + template + && is_nothrow_constructible_v + && (_Size == rank_dynamic() || _Size == rank()), + int> = 0> + explicit(_Size != rank_dynamic()) constexpr extents(span<_OtherIndexType, _Size> _Exts) noexcept + : _Mybase{_Exts, _STD make_index_sequence{}} {} + + + template + _NODISCARD_FRIEND constexpr bool operator==( + const extents& _Lhs, const extents<_OtherIndexType, _OtherExtents...>& _Rhs) noexcept { + if constexpr (sizeof...(_Extents) != sizeof...(_OtherExtents)) { + return false; + } + + for (size_t _Dim = 0; _Dim < sizeof...(_Extents); ++_Dim) { + if (_Lhs.extent(_Dim) != _Rhs.extent(_Dim)) { + return false; + } + } + + return true; + } + + constexpr void _Fill_extents(index_type* _Out) const noexcept { + auto _Dynamic_it = _Mybase::_Begin_dynamic_extents(); + for (size_t _Dim = 0; _Dim < sizeof...(_Extents); ++_Dim) { + if (_Mybase::_Static_extents[_Dim] == dynamic_extent) { + *_Out++ = *_Dynamic_it++; + } else { + *_Out++ = static_cast(_Mybase::_Static_extents[_Dim]); + } + } + } +}; + +template +using dextents = + decltype([](const _IndexType2, const index_sequence<_Seq...>) constexpr { + return extents<_IndexType2, ((void) _Seq, dynamic_extent)...>{}; + }(_IndexType{0}, make_index_sequence<_Rank>{})); + +// TRANSITION: why not `((void) _Ext, dynamic_extent)...`?! +template && ...), int> = 0> +extents(_Integrals... _Ext) + -> extents, _Integrals>::value...>; + + +template +constexpr bool _Is_extents = false; + +template +constexpr bool _Is_extents> = true; + + +template +struct _Layout_mapping_alike_helper : false_type {}; + +template +struct _Layout_mapping_alike_helper<_Mapping, + void_t, + is_same, + is_same, bool_constant<_Mapping::is_always_strided()>, + bool_constant<_Mapping::is_always_exhaustive()>, bool_constant<_Mapping::is_always_unique()>>> + : bool_constant<_Is_extents> {}; + +template +struct _Layout_mapping_alike : bool_constant<_Layout_mapping_alike_helper<_Mapping>::value> {}; + + +template +constexpr bool _Is_mapping_of = + is_same_v, _Mapping>; + + +struct layout_left { + template + class mapping; +}; + +struct layout_right { + template + class mapping; +}; + +struct layout_stride { + template + class mapping; +}; + +template +class layout_left::mapping { +public: + using extents_type = _Extents; + using index_type = typename _Extents::index_type; + using size_type = typename _Extents::size_type; + using rank_type = typename _Extents::rank_type; + using layout_type = layout_left; + + constexpr mapping() noexcept = default; + constexpr mapping(const mapping&) noexcept = default; + + constexpr mapping(const _Extents& e) noexcept : _Myext(e){}; + + template , int> = 0> + explicit(!is_convertible_v<_OtherExtents, _Extents>) constexpr mapping( + const mapping<_OtherExtents>& _Other) noexcept + : _Myext{_Other.extents()} {}; + + template , int> = 0> + explicit(!is_convertible_v<_OtherExtents, _Extents>) constexpr mapping( + const layout_right::mapping<_OtherExtents>& _Other) noexcept + : _Myext{_Other.extents()} {} + + template , int> = 0> + explicit(_Extents::rank() > 0) constexpr mapping(const layout_stride::template mapping<_OtherExtents>& _Other) + : _Myext{_Other.extents()} {} + + constexpr mapping& operator=(const mapping&) noexcept = default; + + _NODISCARD constexpr const extents_type& extents() const noexcept { + return _Myext; + } + + _NODISCARD constexpr index_type required_span_size() const noexcept { + index_type _Result = 1; + for (rank_type _Dim = 0; _Dim < _Extents::rank(); ++_Dim) { + _Result *= _Myext.extent(_Dim); + } + + return _Result; + } + + template && ...) + && (is_nothrow_constructible_v && ...), + int> = 0> + _NODISCARD constexpr index_type operator()(_Indices... _Idx) const noexcept { + return _Index_impl...>( + static_cast(_Idx)..., make_index_sequence<_Extents::rank()>{}); + } + + _NODISCARD static constexpr bool is_always_unique() noexcept { + return true; + } + _NODISCARD static constexpr bool is_always_exhaustive() noexcept { + return true; + } + _NODISCARD static constexpr bool is_always_strided() noexcept { + return true; + } + + _NODISCARD constexpr bool is_unique() const noexcept { + return true; + } + _NODISCARD constexpr bool is_exhaustive() const noexcept { + return true; + } + _NODISCARD constexpr bool is_strided() const noexcept { + return true; + } + + template 0), int> = 0> + _NODISCARD constexpr index_type stride(const rank_type _Rank) const noexcept { + index_type _Result = 1; + for (rank_type _Dim = 0; _Dim < _Rank; ++_Dim) { + _Result *= _Myext.extent(_Dim); + } + + return _Result; + } + + template + _NODISCARD friend constexpr bool operator==(const mapping& _Lhs, const mapping& _Rhs) noexcept { + return _Lhs.extents() == _Rhs.extents(); + } + +private: + _Extents _Myext{}; + + template + constexpr index_type _Index_impl(_IndexType... _Idx, index_sequence<_Seq...>) const noexcept { + // return _Extents::rank() > 0 ? ((_Idx * stride(_Seq)) + ... + 0) : 0; + index_type _Stride = 1; + index_type _Result = 0; + (((_Result += _Idx * _Stride), (void) (_Stride *= _Myext.extent(_Seq))), ...); + return _Result; + } +}; + +template +class layout_right::mapping { +public: + using extents_type = _Extents; + using index_type = typename _Extents::index_type; + using size_type = typename _Extents::size_type; + using rank_type = typename _Extents::rank_type; + using layout_type = layout_right; + + constexpr mapping() noexcept = default; + constexpr mapping(const mapping&) noexcept = default; + + constexpr mapping(const _Extents& e) noexcept : _Myext(e){}; + + template , int> = 0> + explicit(!is_convertible_v<_OtherExtents, _Extents>) constexpr mapping( + const mapping<_OtherExtents>& _Other) noexcept + : _Myext{_Other.extents()} {}; + + template , int> = 0> + explicit(!is_convertible_v<_OtherExtents, _Extents>) constexpr mapping( + const layout_left::mapping<_OtherExtents>& _Other) noexcept + : _Myext{_Other.extents()} {} + + template , int> = 0> + explicit(_Extents::rank() > 0) constexpr mapping(const layout_stride::template mapping<_OtherExtents>& _Other) + : _Myext{_Other.extents()} {} + + + constexpr mapping& operator=(const mapping&) noexcept = default; + + _NODISCARD constexpr const extents_type& extents() const noexcept { + return _Myext; + } + + _NODISCARD constexpr index_type required_span_size() const noexcept { + index_type _Result = 1; + for (rank_type _Dim = 0; _Dim < _Extents::rank(); ++_Dim) { + _Result *= _Myext.extent(_Dim); + } + + return _Result; + } + + template && ...) + && (is_nothrow_constructible_v && ...), + int> = 0> + _NODISCARD constexpr index_type operator()(_Indices... _Idx) const noexcept { + return _Index_impl...>( + static_cast(_Idx)..., make_index_sequence<_Extents::rank()>{}); + } + + _NODISCARD static constexpr bool is_always_unique() noexcept { + return true; + } + _NODISCARD static constexpr bool is_always_exhaustive() noexcept { + return true; + } + _NODISCARD static constexpr bool is_always_strided() noexcept { + return true; + } + + _NODISCARD constexpr bool is_unique() const noexcept { + return true; + } + _NODISCARD constexpr bool is_exhaustive() const noexcept { + return true; + } + _NODISCARD constexpr bool is_strided() const noexcept { + return true; + } + + template 0), int> = 0> + _NODISCARD constexpr index_type stride(const rank_type _Rank) const noexcept { + index_type _Result = 1; + for (rank_type _Dim = _Rank + 1; _Dim < _Extents::rank(); ++_Dim) { + _Result *= _Myext.extent(_Dim); + } + + return _Result; + } + + template + _NODISCARD friend constexpr bool operator==(const mapping& _Lhs, const mapping& _Rhs) noexcept { + return _Lhs.extents() == _Rhs.extents(); + } + +private: + _Extents _Myext{}; + + static constexpr size_t _Multiply(size_t _X, size_t _Y) { + return _X * _Y; + } + + template + constexpr index_type _Index_impl(_IndexType... _Idx, index_sequence<_Seq...>) const noexcept { + index_type _Result = 0; + ((void) (_Result = _Idx + _Myext.extent(_Seq) * _Result), ...); + return _Result; + } +}; + + +template +class layout_stride::mapping { +public: + using extents_type = _Extents; + using index_type = typename _Extents::index_type; + using size_type = typename _Extents::size_type; + using rank_type = typename _Extents::rank_type; + using layout_type = layout_stride; + + constexpr mapping() noexcept = default; + constexpr mapping(const mapping&) noexcept = default; + + template , int> = 0> + constexpr mapping(const _Extents& _E_, const array<_OtherIndexType, _Extents::rank()>& _S_) noexcept : _Myext{_E_} { + for (rank_type _Idx = 0; _Idx < _Extents::rank(); ++_Idx) { + _Mystrides[_Idx] = _S_[_Idx]; + } + }; + + template , int> = 0> + constexpr mapping(const _Extents& _E_, const span<_OtherIndexType, _Extents::rank()> _S_) noexcept : _Myext{_E_} { + for (rank_type _Idx = 0; _Idx < _Extents::rank(); ++_Idx) { + _Mystrides[_Idx] = _S_[_Idx]; + } + }; + + template , int> = 0> + explicit(!is_convertible_v<_OtherExtents, _Extents>) constexpr mapping( + const mapping<_OtherExtents>& _Other) noexcept + : _Myext{_Other.extents()}, _Mystrides{_Other.strides()} { + for (rank_type _Idx = 0; _Idx < _Extents::rank(); ++_Idx) { + _Mystrides[_Idx] = _Other.stride(_Idx); + } + } + + template ::value + && is_constructible_v + && _StridedLayoutMapping::is_always_unique() && _StridedLayoutMapping::is_always_strided(), + int> = 0> + explicit( + !is_convertible_v + && (_Is_mapping_of || _Is_mapping_of + || _Is_mapping_of) ) constexpr mapping(const _StridedLayoutMapping& + _Other) noexcept + : _Myext(_Other.extents()) { + for (rank_type _Dim = 0; _Dim < _Extents::rank(); ++_Dim) { + _Mystrides[_Dim] = _Other.stride(_Dim); + } + } + + constexpr mapping& operator=(const mapping&) noexcept = default; + + _NODISCARD constexpr const extents_type& extents() const noexcept { + return _Myext; + } + + _NODISCARD constexpr array strides() const noexcept { + return _Mystrides; + } + + _NODISCARD constexpr index_type required_span_size() const noexcept { + if (_Extents::rank() > 0) { + index_type _Result = 1; + for (rank_type _Dim = 0; _Dim < _Extents::rank(); ++_Dim) { + const auto _Ext = _Myext.extent(_Dim); + if (_Ext == 0) { + return 0; + } + + _Result += (_Ext - 1) * _Mystrides[_Dim]; + } + + return _Result; + } else { + return 1; + } + } + + template && ...) + && (is_nothrow_constructible_v && ...), + int> = 0> + _NODISCARD constexpr index_type operator()(_Indices... _Idx) const noexcept { + return _Index_impl...>( + static_cast(_Idx)..., make_index_sequence<_Extents::rank()>{}); + } + + _NODISCARD static constexpr bool is_always_unique() noexcept { + return true; + } + + _NODISCARD static constexpr bool is_always_exhaustive() noexcept { + return false; + } + + _NODISCARD static constexpr bool is_always_strided() noexcept { + return true; + } + + _NODISCARD constexpr bool is_unique() const noexcept { + return true; + } + + _NODISCARD constexpr bool is_exhaustive() const noexcept { + index_type _Ext_size = 1; + for (rank_type _Dim = 0; _Dim < _Extents::rank(); ++_Dim) { + _Ext_size *= _Myext.extent(_Dim); + } + + return required_span_size() == _Ext_size; + } + + _NODISCARD constexpr bool is_strided() const noexcept { + return true; + } + + template 0), int> = 0> + _NODISCARD constexpr index_type stride(const rank_type _Idx) const noexcept { + return _Mystrides[_Idx]; + } + + template ::value + && extents_type::rank() == _OtherMapping::extents_type::rank() + && _OtherMapping::is_always_strided(), + int> = 0> + _NODISCARD friend constexpr bool operator==(const mapping& _Lhs, const _OtherMapping& _Rhs) noexcept { + if (_Lhs.extents() != _Rhs.extents()) { + return false; + } + + constexpr rank_type _Rank = extents_type::rank(); + for (rank_type _Dim = 0; _Dim < _Rank; ++_Dim) { + if (_Lhs.stride(_Dim) != _Rhs.stride(_Dim)) { + return false; + } + } + + index_type _Offset; + if constexpr (_Rank == 0) { + _Offset = _Rhs(); + } else { + bool _Is_empty = false; + for (rank_type _Dim = 0; _Dim < _Rank; ++_Dim) { + if (_Lhs.extents().extent(_Dim) == 0) { + _Is_empty = true; + break; + } + } + + if (_Is_empty) { + _Offset = 0; + } else { + _Offset = [&_Rhs](index_sequence<_Idx...>) { + return _Rhs(((void) _Idx, 0)...); + } + (make_index_sequence<_Rank>{}); + } + + return _Offset == 0; + } + } + +private: + _Extents _Myext{}; + array _Mystrides = {}; + + template + constexpr index_type _Index_impl(_IndexType... _Idx, index_sequence<_Seq...>) const noexcept { + return ((_Idx * _Mystrides[_Seq]) + ...); + } +}; + +template +struct default_accessor { + using offset_policy = default_accessor; + using element_type = _ElementType; + using reference = _ElementType&; + using data_handle_type = _ElementType*; + + constexpr default_accessor() noexcept = default; + + template ::element_type (*)[], _ElementType (*)[]>, + int> = 0> + constexpr default_accessor(default_accessor<_OtherElementType>) noexcept {} + + _NODISCARD constexpr data_handle_type offset(data_handle_type _Ptr, size_t _Idx) const noexcept { + return _Ptr + _Idx; + } + + _NODISCARD constexpr reference access(data_handle_type _Ptr, size_t _Idx) const noexcept { + return _Ptr[_Idx]; + } +}; + +template > +class mdspan { +public: + // Domain and codomain types + using extents_type = _Extents; + using layout_type = _LayoutPolicy; + using accessor_type = _AccessorPolicy; + using mapping_type = typename layout_type::template mapping; + using element_type = _ElementType; + using value_type = remove_cv_t; + using index_type = typename extents_type::index_type; + using size_type = typename extents_type::size_type; + using rank_type = typename extents_type::rank_type; + using data_handle_type = typename accessor_type::data_handle_type; + using reference = typename accessor_type::reference; + + _NODISCARD static constexpr rank_type rank() noexcept { + return _Extents::rank(); + } + _NODISCARD static constexpr rank_type rank_dynamic() noexcept { + return _Extents::rank_dynamic(); + } + _NODISCARD static constexpr size_t static_extent(const rank_type r) noexcept { + return _Extents::static_extent(r); + } + + template 0) && is_default_constructible_v + && is_default_constructible_v<_Mapping> && is_default_constructible_v, + int> = 0> + constexpr mdspan() {} + constexpr mdspan(const mdspan& rhs) = default; + constexpr mdspan(mdspan&& rhs) = default; + + template && is_default_constructible_v, + int> = 0> + explicit constexpr mdspan(data_handle_type _Ptr_) : _Ptr{_STD move(_Ptr_)}, _Map{extents_type{}} {} + + template 0)) && (is_convertible_v<_OtherIndexTypes, index_type> && ...) + && (is_nothrow_constructible_v && ...) + //&& (sizeof...(_OtherIndexTypes) == rank() || sizeof...(_OtherIndexTypes) == rank_dynamic()) + && is_constructible_v && is_default_constructible_v, + int> = 0> + explicit constexpr mdspan(data_handle_type _Ptr_, _OtherIndexTypes... _Exts) + : _Ptr{_STD move(_Ptr_)}, _Map{extents_type{static_cast(_STD move(_Exts))...}} {} + + template + && is_nothrow_constructible_v + && (_Size == rank() || _Size == rank_dynamic()) + && is_constructible_v && is_default_constructible_v, + int> = 0> + explicit(_Size != rank_dynamic()) constexpr mdspan(data_handle_type _Ptr_, span<_OtherIndexType, _Size>& _Exts) + : _Ptr{_Ptr_}, _Map{_Extents{_Exts}} {} + + template + && is_nothrow_constructible_v + && (_Size == rank() || _Size == rank_dynamic()) + && is_constructible_v && is_default_constructible_v, + int> = 0> + explicit(_Size != rank_dynamic()) constexpr mdspan( + data_handle_type _Ptr_, const array<_OtherIndexType, _Size>& _Exts) + : _Ptr{_Ptr_}, _Map{_Extents{_Exts}} {} + + + template && is_default_constructible_v, int> = 0> + constexpr mdspan(data_handle_type _Ptr_, const _Extents& _Ext) : _Ptr{_Ptr_}, _Map{_Ext} {} + + template , int> = 0> + constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_) : _Ptr{_Ptr_}, _Map{_Map_} {} + + constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_, const accessor_type& _Acc_) + : _Ptr{_Ptr_}, _Map{_Map_}, _Acc{_Acc_} {} + + template &> + && is_constructible_v, + int> = 0> + constexpr explicit( + !is_convertible_v&, mapping_type> + || !is_convertible_v) + mdspan(const mdspan<_OtherElementType, _OtherExtents, _OtherLayoutPolicy, _OtherAccessor>& _Other) + : _Ptr{_Other._Ptr}, _Map{_Other._Map}, _Acc{_Other._Acc} { + static_assert(is_constructible_v); + static_assert(is_constructible_v); + } + + constexpr mdspan& operator=(const mdspan& rhs) = default; + constexpr mdspan& operator=(mdspan&& rhs) = default; + + // TRANSITION operator[](const _OtherIndexTypes... _Indices) + template && ...) + && (is_nothrow_constructible_v && ...), + /*&& sizeof...(_OtherIndexTypes) == rank(),*/ + int> = 0> + _NODISCARD constexpr reference operator()(const _OtherIndexTypes... _Indices) const { + return _Acc.access(_Ptr, _Map(static_cast(_STD move(_Indices))...)); + } + + template , int> = 0> + _NODISCARD constexpr reference operator[](span<_OtherIndexType, rank()> _Indices) const { + return _Index_impl(_Indices, make_index_sequence{}); + } + + template , int> = 0> + _NODISCARD constexpr reference operator[](const array<_OtherIndexType, rank()>& _Indices) const { + return _Index_impl(_Indices, make_index_sequence{}); + } + + _NODISCARD constexpr const extents_type& extents() const noexcept { + return _Map.extents(); + } + + _NODISCARD constexpr const data_handle_type& data_handle() const noexcept { + return _Ptr; + } + + _NODISCARD constexpr const mapping_type& mapping() const noexcept { + return _Map; + } + + _NODISCARD constexpr const accessor_type& accessor() const noexcept { + return _Acc; + } + + _NODISCARD constexpr index_type extent(const rank_type r) const noexcept { + const auto& _Ext = _Map.extents(); + return _Ext.extent(r); + } + + _NODISCARD constexpr size_type size() const noexcept { + const auto& _Ext = _Map.extents(); + size_type _Result = 1; + for (rank_type _Dim = 0; _Dim < rank(); ++_Dim) { + _Result *= _Ext.extent(_Dim); + } + return _Result; + } + + _NODISCARD constexpr bool empty() const noexcept { + for (rank_type _Dim = 0; _Dim < rank(); ++_Dim) { + if (_Map.extents().extent(_Dim) == 0) { + return true; + } + } + + return false; + } + + friend constexpr void swap(mdspan& _Lhs, mdspan& _Rhs) noexcept { + swap(_Lhs._Ptr, _Rhs._Ptr); + swap(_Lhs._Map, _Rhs._Map); + swap(_Lhs._Acc, _Rhs._Acc); + } + + _NODISCARD static constexpr bool is_always_unique() { + return mapping_type::is_always_unique(); + } + + _NODISCARD static constexpr bool is_always_exhaustive() { + return mapping_type::is_always_exhaustive(); + } + + _NODISCARD static constexpr bool is_always_strided() { + return mapping_type::is_always_strided(); + } + + _NODISCARD constexpr bool is_unique() const { + return _Map.is_unique(); + } + + _NODISCARD constexpr bool is_exhaustive() const { + return _Map.is_exhaustive(); + } + + _NODISCARD constexpr bool is_strided() const { + return _Map.is_strided(); + } + + _NODISCARD constexpr index_type stride(const size_t _Dim) const { + return _Map.stride(_Dim); + } + +private: + template + _NODISCARD constexpr reference _Index_impl(_IndexContainer&& _Indices, index_sequence<_Idx...>) const { + return _Acc.access(_Ptr, _Map(_STD as_const(_STD forward<_IndexContainer>(_Indices)[_Idx])...)); + } + + data_handle_type _Ptr{}; + mapping_type _Map{}; + accessor_type _Acc{}; +}; + +_STD_END + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) +#endif // _HAS_CXX23 +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _MDSPAN_ diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 85bebe3ffb..f1304b2f1d 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -299,6 +299,7 @@ // Other C++20 deprecation warnings // _HAS_CXX23 directly controls: +// P0009R18 // P0288R9 move_only_function // P0323R12 // P0401R6 Providing Size Feedback In The Allocator Interface @@ -352,6 +353,10 @@ // P2499R0 string_view Range Constructor Should Be explicit // P2505R5 Monadic Functions For expected // P2549R1 unexpected::error() +// P2599R2 mdspan: index_type, size_type +// P2604R0 mdspan: data_handle_type, data_handle(), exhaustive +// P2613R1 mdspan: empty() +// P2763R1 Fixing layout_stride's Default Constructor For Fully Static Extents // _HAS_CXX23 and _SILENCE_ALL_CXX23_DEPRECATION_WARNINGS control: // P1413R3 Deprecate aligned_storage And aligned_union @@ -1707,6 +1712,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #define __cpp_lib_invoke_r 202106L #define __cpp_lib_ios_noreplace 202207L #define __cpp_lib_is_scoped_enum 202011L +#define __cpp_lib_mdspan 202207L #if !defined(__clang__) && !defined(__EDG__) // TRANSITION, Clang and EDG support for modules #define __cpp_lib_modules 202207L diff --git a/stl/modules/std.ixx b/stl/modules/std.ixx index dc43b5722e..e56d32f850 100644 --- a/stl/modules/std.ixx +++ b/stl/modules/std.ixx @@ -76,6 +76,7 @@ export module std; #include #include #include +#include #include #include #include diff --git a/tests/std/test.lst b/tests/std/test.lst index 22e1b45d8f..26d1288ae7 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -227,6 +227,7 @@ tests\LWG3422_seed_seq_ctors tests\LWG3480_directory_iterator_range tests\LWG3545_pointer_traits_sfinae tests\LWG3610_iota_view_size_and_integer_class +tests\P0009R18_mdspan tests\P0019R8_atomic_ref tests\P0024R2_parallel_algorithms_adjacent_difference tests\P0024R2_parallel_algorithms_adjacent_find diff --git a/tests/std/tests/P0009R18_mdspan/env.lst b/tests/std/tests/P0009R18_mdspan/env.lst new file mode 100644 index 0000000000..642f530ffa --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P0009R18_mdspan/test.cpp b/tests/std/tests/P0009R18_mdspan/test.cpp new file mode 100644 index 0000000000..1da04b34d5 --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan/test.cpp @@ -0,0 +1,1117 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +using namespace std; + +#ifdef __cpp_lib_concepts +// A type that's regular and trivially copyable, and also maximally nothrow. +template +using is_regular_trivial_nothrow = std::conjunction>, is_trivially_copyable, + is_nothrow_default_constructible, is_nothrow_copy_constructible, is_nothrow_move_constructible, + is_nothrow_copy_assignable, is_nothrow_move_assignable, is_nothrow_swappable>; + +template +inline constexpr bool is_regular_trivial_nothrow_v = is_regular_trivial_nothrow::value; +#endif // __cpp_lib_concepts + +struct Constructible { + // noexcept constructible for size_t, but not convertible + explicit operator size_t() noexcept; +}; + +struct Convertible { + // convertible, but not noexcept constructible + operator size_t(); +}; + +struct ConstructibleAndConvertible { + // convertible and noexcept constuctible + constexpr operator size_t() noexcept { + return size_t{0}; + }; +}; + +struct ConstructibleAndConvertibleConst { + // convertible and noexcept constuctible + constexpr operator size_t() const noexcept { + return size_t{0}; + }; +}; + + +void extent_tests_traits() { +#ifdef __cpp_lib_concepts + static_assert(is_regular_trivial_nothrow_v>); + static_assert(is_regular_trivial_nothrow_v>); + static_assert(is_regular_trivial_nothrow_v>); + static_assert(is_regular_trivial_nothrow_v>); + static_assert(is_regular_trivial_nothrow_v>); +#endif // __cpp_lib_concepts + + static_assert(is_same_v, extents>); + static_assert(is_same_v, extents>); + static_assert(is_same_v, extents>); + static_assert(is_same_v, extents>); + + constexpr extents e(2, 3); + static_assert(is_same_v, dextents>); + static_assert(e.static_extent(0) == dynamic_extent); + static_assert(e.static_extent(1) == dynamic_extent); + static_assert(e.extent(0) == 2); + static_assert(e.extent(1) == 3); + + constexpr dextents ex0(1); + (void) ex0; + static_assert(!is_constructible_v, int*>); + + extents(); + extents(); + extents(); + extents(); + extents(); + extents(); + extents(); + extents(); + extents(); + extents(); +} + +void extent_tests_rank() { + static_assert(extents::rank() == 0); + static_assert(extents::rank_dynamic() == 0); + + static_assert(extents::rank() == 1); + static_assert(extents::rank_dynamic() == 0); + + static_assert(extents::rank() == 1); + static_assert(extents::rank_dynamic() == 1); + + static_assert(extents::rank() == 2); + static_assert(extents::rank_dynamic() == 0); + + static_assert(extents::rank() == 2); + static_assert(extents::rank_dynamic() == 1); + + static_assert(extents::rank() == 2); + static_assert(extents::rank_dynamic() == 1); + + static_assert(extents::rank() == 2); + static_assert(extents::rank_dynamic() == 2); +} + +void extent_tests_static_extent() { + static_assert(extents::static_extent(0) == 2); + static_assert(extents::static_extent(1) == 3); + + static_assert(extents::static_extent(0) == 2); + static_assert(extents::static_extent(1) == dynamic_extent); + + static_assert(extents::static_extent(0) == dynamic_extent); + static_assert(extents::static_extent(1) == 3); + + static_assert(extents::static_extent(0) == dynamic_extent); + static_assert(extents::static_extent(1) == dynamic_extent); +} + +void extent_tests_extent() { + constexpr extents e_23; + static_assert(e_23.extent(0) == 2); + static_assert(e_23.extent(1) == 3); + + constexpr extents e_2d{3}; + static_assert(e_2d.extent(0) == 2); + static_assert(e_2d.extent(1) == 3); + + constexpr extents e_dd{2, 3}; + static_assert(e_dd.extent(0) == 2); + static_assert(e_dd.extent(1) == 3); + + constexpr extents e_2dd{3, 5}; + static_assert(e_2dd.extent(0) == 2); + static_assert(e_2dd.extent(1) == 3); + static_assert(e_2dd.extent(2) == 5); +} + +void extent_tests_ctor_other_sizes() { + static_assert(!is_constructible_v, Constructible>); + static_assert(!is_constructible_v, Convertible>); + // static_assert(is_constructible_v, ConstructibleAndConvertible>); + constexpr extents ex0{ConstructibleAndConvertible{}}; + // static_assert(is_constructible_v, ConstructibleAndConvertibleConst>); + constexpr extents ex1{ConstructibleAndConvertibleConst{}}; + + // static_assert(is_constructible_v, int>); + constexpr extents ex2(1); + static_assert(!is_constructible_v, int, int>); + // static_assert(is_constructible_v, int, int, int>); + extents ex3(1, 2, 3); + + (void) ex0; + (void) ex1; + (void) ex2; + (void) ex3; + + extents e0; + assert(e0.extent(0) == 2); + assert(e0.extent(1) == 3); + + extents e1(5); + assert(e1.extent(0) == 2); + assert(e1.extent(1) == 5); + + extents e2(5); + assert(e2.extent(0) == 5); + assert(e2.extent(1) == 3); + + extents e3(5, 7); + assert(e3.extent(0) == 5); + assert(e3.extent(1) == 7); +} + +void extent_tests_copy_ctor_other() { + // Rank and value of static extents must match. + static_assert(!is_constructible_v, extents>); + static_assert(!is_constructible_v, extents>); + static_assert(!is_constructible_v, extents>); + + // Static extents are constuctible, but not convertible, from dynamic extents. + // static_assert(is_constructible_v, extents>); + constexpr extents ex0{extents{}}; + (void) ex0; + static_assert(!is_convertible_v, extents>); + + // Dynamic extents are constuctible and convertible from static extents. + static_assert(is_constructible_v, extents>); + extents{extents{}}; + static_assert(is_convertible_v, extents>); + + // Can implicitly convert from narrower to wider size_type, but not vice-versa. + static_assert(is_convertible_v, extents>); + static_assert(!is_convertible_v, extents>); + + extents e_dyn(3); + extents e(e_dyn); + (void) e; + + using E = extents; + + extents e0{extents{}}; + E e1(extents(2u)); + extents e2{extents{3u}}; + extents e3{extents{2u, 3u}}; + + (void) e0; + (void) e1; + (void) e2; + (void) e3; +} + +template +struct is_array_cons_avail : std::false_type {}; + +template +struct is_array_cons_avail>()}), T>::value>> : std::true_type { +}; + +template +constexpr bool is_array_cons_avail_v = is_array_cons_avail::value; + +void extent_tests_ctor_array() { + static_assert(!is_constructible_v, array>); + static_assert(!is_constructible_v, array>); + static_assert(!is_constructible_v, array>); + static_assert(is_constructible_v, array>); + constexpr extents ex0{array{}}; + (void) ex0; + + static_assert(is_constructible_v, array>); + constexpr extents ex1{array{}}; + static_assert(!is_constructible_v, array>); + static_assert(is_constructible_v, array>); + constexpr extents ex2{array{1, 2, 2}}; + (void) ex1; + (void) ex2; + + static_assert(is_constructible_v, array>); + constexpr extents ex3{array{}}; + static_assert(is_constructible_v, array>); + constexpr extents ex4{array{}}; + static_assert(!is_constructible_v, array>); + (void) ex3; + (void) ex4; + + extents e0; + assert(e0.extent(0) == 2u); + assert(e0.extent(1) == 3u); + + // native extent::size_type + extents e1(to_array({5})); + assert(e1.extent(0) == 2u); + assert(e1.extent(1) == 5u); + + extents e2(to_array({5})); + assert(e2.extent(0) == 5u); + assert(e2.extent(1) == 3u); + + extents e3(to_array({5, 7})); + assert(e3.extent(0) == 5u); + assert(e3.extent(1) == 7u); + + // convertible size type + extents e4(to_array({5})); + assert(e4.extent(0) == 2u); + assert(e4.extent(1) == 5u); + + extents e5(to_array({5})); + assert(e5.extent(0) == 5u); + assert(e5.extent(1) == 3u); + + extents e6(to_array({5, 7})); + assert(e6.extent(0) == 5u); + assert(e6.extent(1) == 7u); +} + +void extent_tests_ctor_span() { + static_assert(!is_constructible_v, span>); + static_assert(!is_constructible_v, span>); + static_assert(!is_constructible_v, span>); + static_assert(is_constructible_v, span>); + ConstructibleAndConvertibleConst arr0[1] = {{}}; + constexpr extents ex0{span{arr0}}; + (void) ex0; + + static_assert(is_constructible_v, span>); + constexpr int arr1[1] = {1}; + constexpr extents ex1{span{arr1}}; + static_assert(!is_constructible_v, span>); + static_assert(is_constructible_v, span>); + constexpr int arr2[3] = {3, 2, 2}; + constexpr extents ex2{span{arr2}}; + (void) ex1; + (void) ex2; + + + extents e0; + assert(e0.extent(0) == 2u); + assert(e0.extent(1) == 3u); + + // native extent::size_type + constexpr int one_int[] = {5}; + constexpr int two_int[] = {5, 7}; + extents e1(span{one_int}); + assert(e1.extent(0) == 2); + assert(e1.extent(1) == 5); + + extents e2(span{one_int}); + assert(e2.extent(0) == 5); + assert(e2.extent(1) == 3); + + extents e3(span{two_int}); + assert(e3.extent(0) == 5); + assert(e3.extent(1) == 7); + + // convertible size type + constexpr size_t one_sizet[] = {5}; + constexpr size_t two_sizet[] = {5, 7}; + extents e4(span{one_sizet}); + assert(e4.extent(0) == 2); + assert(e4.extent(1) == 5); + + extents e5(span{one_sizet}); + assert(e5.extent(0) == 5); + assert(e5.extent(1) == 3); + + extents e6(span{two_sizet}); + assert(e6.extent(0) == 5); + assert(e6.extent(1) == 7); +} + +void extent_tests_equality() { + static_assert(extents{} == extents{}); + static_assert(extents{} != extents{}); + static_assert(extents{} != extents{}); + + extents e_23; + extents e_2d{3}; + extents e_d3{2}; + extents e_dd{2, 3}; + + assert(e_23 == e_2d); + assert(e_23 == e_d3); + assert(e_23 == e_dd); + assert(e_2d == e_d3); +} + +template = 0> +void TestMapping(const Mapping& map) { + using IndexT = typename Mapping::index_type; + using RankT = typename Mapping::rank_type; + static_assert(is_same_v()(IndexT{0}, IndexT{0}))>); + static_assert(is_same_v().stride(RankT{0}))>); + + array s; + const auto& e = map.extents(); + size_t num_entries = 1; + for (size_t i = 0; i < Mapping::extents_type::rank(); ++i) { + num_entries *= e.extent(i); + s[i] = map.stride(i); + } + + vector indices; + indices.reserve(num_entries); + + for (IndexT i = 0; i < e.extent(0); ++i) { + for (IndexT j = 0; j < e.extent(1); ++j) { + const auto idx = i * s[0] + j * s[1]; + assert(map(i, j) == idx); + indices.push_back(idx); + } + } + + bool is_unique = true; + bool is_exhaust = true; + sort(indices.begin(), indices.end()); + for (size_t i = 1; i < indices.size(); ++i) { + const auto diff = indices[i] - indices[i - 1]; + if (diff == 0) { + is_unique = false; + } else if (diff != 1) { + is_exhaust = false; + } + } + + assert(map.is_unique() == is_unique); + assert(map.is_exhaustive() == is_exhaust); + assert(map.required_span_size() == indices.back() + 1); +} + +template = 0> +void TestMapping(const Mapping& map) { + using IndexT = typename Mapping::index_type; + using RankT = typename Mapping::rank_type; + static_assert(is_same_v()(IndexT{0}, IndexT{0}, IndexT{0}))>); + static_assert(is_same_v().stride(RankT{0}))>); + + array s; + const auto& e = map.extents(); + size_t num_entries = 1; + for (size_t i = 0; i < Mapping::extents_type::rank(); ++i) { + num_entries *= e.extent(i); + s[i] = map.stride(i); + } + + vector indices; + indices.reserve(num_entries); + + for (IndexT i = 0; i < e.extent(0); ++i) { + for (IndexT j = 0; j < e.extent(1); ++j) { + for (IndexT k = 0; k < e.extent(2); ++k) { + const auto idx = i * s[0] + j * s[1] + k * s[2]; + assert(map(i, j, k) == idx); + indices.push_back(idx); + } + } + } + + bool is_unique = true; + bool is_exhaust = true; + sort(indices.begin(), indices.end()); + for (size_t i = 1; i < indices.size(); ++i) { + const auto diff = indices[i] - indices[i - 1]; + if (diff == 0) { + is_unique = false; + } else if (diff != 1) { + is_exhaust = false; + } + } + + assert(map.is_unique() == is_unique); + assert(map.is_exhaustive() == is_exhaust); + assert(map.required_span_size() == indices.back() + 1); +} + +void layout_left_tests_traits() { +#ifdef __cpp_lib_concepts + static_assert(is_regular_trivial_nothrow_v>>); + static_assert(is_regular_trivial_nothrow_v>>); + static_assert(is_regular_trivial_nothrow_v>>); + static_assert(is_regular_trivial_nothrow_v>>); +#endif // __cpp_lib_concepts + + using E = extents; + static_assert(is_same_v::extents_type, E>); + static_assert(is_same_v::index_type, E::index_type>); + static_assert(is_same_v::size_type, E::size_type>); + static_assert(is_same_v::rank_type, E::rank_type>); + static_assert(is_same_v::layout_type, layout_left>); +} + +void layout_right_tests_traits() { +#ifdef __cpp_lib_concepts + static_assert(is_regular_trivial_nothrow_v>>); + static_assert(is_regular_trivial_nothrow_v>>); + static_assert(is_regular_trivial_nothrow_v>>); + static_assert(is_regular_trivial_nothrow_v>>); +#endif // __cpp_lib_concepts + + using E = extents; + static_assert(is_same_v::extents_type, E>); + static_assert(is_same_v::index_type, E::index_type>); + static_assert(is_same_v::size_type, E::size_type>); + static_assert(is_same_v::rank_type, E::rank_type>); + static_assert(is_same_v::layout_type, layout_right>); +} + +void layout_stride_tests_traits() { +#ifdef __cpp_lib_concepts + static_assert(is_regular_trivial_nothrow_v>>); + static_assert(is_regular_trivial_nothrow_v>>); + static_assert(is_regular_trivial_nothrow_v>>); + static_assert( + is_regular_trivial_nothrow_v>>); +#endif // __cpp_lib_concepts + + using E = extents; + static_assert(is_same_v::extents_type, E>); + static_assert(is_same_v::index_type, E::index_type>); + static_assert(is_same_v::size_type, E::size_type>); + static_assert(is_same_v::rank_type, E::rank_type>); + static_assert(is_same_v::layout_type, layout_stride>); +} + +void layout_left_tests_properties() { + constexpr layout_left::mapping> map{}; + static_assert(map.is_unique() == true); + static_assert(map.is_exhaustive() == true); + static_assert(map.is_strided() == true); + + static_assert(decltype(map)::is_always_unique() == true); + static_assert(decltype(map)::is_always_exhaustive() == true); + static_assert(decltype(map)::is_always_strided() == true); +} + +void layout_right_tests_properties() { + constexpr layout_right::mapping> map{}; + static_assert(map.is_unique() == true); + static_assert(map.is_exhaustive() == true); + static_assert(map.is_strided() == true); + + static_assert(decltype(map)::is_always_unique() == true); + static_assert(decltype(map)::is_always_exhaustive() == true); + static_assert(decltype(map)::is_always_strided() == true); +} + +void layout_stride_tests_properties() { + constexpr layout_stride::mapping> map{}; + static_assert(map.is_unique() == true); + static_assert(map.is_strided() == true); + + static_assert(decltype(map)::is_always_unique() == true); + static_assert(decltype(map)::is_always_exhaustive() == false); + static_assert(decltype(map)::is_always_strided() == true); +} + +void layout_left_tests_extents_ctor() { + constexpr extents e1; + constexpr extents e2{7}; + constexpr extents e3{11}; + constexpr extents e4{17, 19}; + + constexpr layout_left::mapping m1{e1}; + static_assert(m1.extents() == e1); + + constexpr layout_left::mapping m2{e2}; + static_assert(m2.extents() == e2); + + constexpr layout_left::mapping m3{e3}; + static_assert(m3.extents() == e3); + + constexpr layout_left::mapping m4{e4}; + static_assert(m4.extents() == e4); +} + +void layout_right_tests_extents_ctor() { + constexpr extents e1; + constexpr extents e2{7}; + constexpr extents e3{11}; + constexpr extents e4{17, 19}; + + constexpr layout_right::mapping m1{e1}; + static_assert(m1.extents() == e1); + + constexpr layout_right::mapping m2{e2}; + static_assert(m2.extents() == e2); + + constexpr layout_right::mapping m3{e3}; + static_assert(m3.extents() == e3); + + constexpr layout_right::mapping m4{e4}; + static_assert(m4.extents() == e4); +} + +void layout_stride_tests_extents_ctor() { + constexpr extents e1; + constexpr array s1{1, 2}; + + constexpr layout_stride::mapping m1{e1, s1}; + static_assert(m1.extents() == e1); + static_assert(m1.strides() == s1); + + constexpr extents e2{7}; + constexpr array s2{7, 1}; + + constexpr layout_stride::mapping m2{e2, s2}; + static_assert(m2.extents() == e2); + static_assert(m2.strides() == s2); +} + +template +void copy_ctor_helper_left(const Extents& e) { + const layout_left::mapping m1{e}; + const layout_left::mapping m2{m1}; + assert(m1 == m2); +} + +template +void copy_ctor_helper_right(const Extents& e) { + const layout_right::mapping m1{e}; + const layout_right::mapping m2{m1}; + assert(m1 == m2); +} + +void layout_left_tests_copy_ctor() { + copy_ctor_helper_left(extents{}); + copy_ctor_helper_left(extents{7}); + copy_ctor_helper_left(extents{11}); + copy_ctor_helper_left(extents{17, 19}); +} + +void layout_right_tests_copy_ctor() { + copy_ctor_helper_right(extents{}); + copy_ctor_helper_right(extents{7}); + copy_ctor_helper_right(extents{11}); + copy_ctor_helper_right(extents{17, 19}); +} + +void layout_left_tests_copy_other_extent() { + using E1 = extents; + using E2 = extents; + constexpr E1 e1; + constexpr E2 e2{3}; + constexpr layout_left::mapping m1(static_cast(e2)); + constexpr layout_left::mapping m2(e1); + + static_assert(m1.extents() == e1); + static_assert(m2.extents() == e1); + static_assert(m1.extents() == e2); + static_assert(m2.extents() == e2); +} + +void layout_right_tests_copy_ctor_other() { + using E1 = extents; + using E2 = extents; + constexpr E1 e1; + constexpr E2 e2{3}; + constexpr layout_right::mapping m1(static_cast(e2)); + constexpr layout_right::mapping m2(e1); + + static_assert(m1.extents() == e1); + static_assert(m2.extents() == e1); + static_assert(m1.extents() == e2); + static_assert(m2.extents() == e2); +} + +template +void assign_helper_left(const Extents& e) { + const layout_left::mapping m1{e}; + layout_left::mapping m2; + m2 = m1; + assert(m1 == m2); +} + +template +void assign_helper_right(const Extents& e) { + const layout_right::mapping m1{e}; + layout_right::mapping m2; + m2 = m1; + assert(m1 == m2); +} + +void layout_left_tests_assign() { + assign_helper_left(extents{}); + assign_helper_left(extents{7}); + assign_helper_left(extents{11}); + assign_helper_left(extents{17, 19}); +} + +void layout_right_tests_assign() { + assign_helper_right(extents{}); + assign_helper_right(extents{7}); + assign_helper_right(extents{11}); + assign_helper_right(extents{17, 19}); +} + +void layout_left_tests_ctor_other_layout() { + using E = extents; + + // from layout_left + using OE1 = extents; + static_assert(is_nothrow_constructible_v, layout_right::mapping>); + static_assert(is_nothrow_convertible_v, layout_left::mapping>); + + using OE2 = extents; // not convertible + static_assert(is_nothrow_constructible_v, layout_right::mapping>); + static_assert(!is_convertible_v, layout_left::mapping>); + + using OE3 = extents; // not constructible, rank > 1 + static_assert(!is_constructible_v, layout_right::mapping>); + static_assert(!is_convertible_v, layout_left::mapping>); + + static_assert(!is_constructible_v, layout_right::mapping>); + static_assert(!is_convertible_v, layout_left::mapping>); + + // from layout_stride + static_assert(is_constructible_v>, layout_stride::mapping>>); + static_assert(is_convertible_v>, layout_left::mapping>>); + + static_assert(is_constructible_v, layout_stride::mapping>); + static_assert(!is_convertible_v, layout_left::mapping>); + + static_assert(!is_constructible_v, layout_stride::mapping>); + static_assert(!is_convertible_v, layout_left::mapping>); +} + +void layout_right_tests_ctor_other_layout() { + using E = extents; + + // from layout_left + using OE1 = extents; + static_assert(is_nothrow_constructible_v, layout_left::mapping>); + static_assert(is_nothrow_convertible_v, layout_right::mapping>); + + using OE2 = extents; // not convertible + static_assert(is_nothrow_constructible_v, layout_left::mapping>); + static_assert(!is_convertible_v, layout_right::mapping>); + + using OE3 = extents; // not constructible, rank > 1 + static_assert(!is_constructible_v, layout_left::mapping>); + static_assert(!is_convertible_v, layout_right::mapping>); + + static_assert(!is_constructible_v, layout_left::mapping>); + static_assert(!is_convertible_v, layout_right::mapping>); + + // from layout_stride + static_assert(is_constructible_v>, layout_stride::mapping>>); + static_assert(is_convertible_v>, layout_right::mapping>>); + + static_assert(is_constructible_v, layout_stride::mapping>); + static_assert(!is_convertible_v, layout_right::mapping>); + + static_assert(!is_constructible_v, layout_stride::mapping>); + static_assert(!is_convertible_v, layout_right::mapping>); +} + +void layout_left_tests_strides() { + using E = extents; + layout_left::mapping map; + static_assert(map.stride(0) == 1); + static_assert(map.stride(1) == 2); + static_assert(map.stride(2) == 2 * 3); + static_assert(map.stride(3) == 2 * 3 * 5); +} + +void layout_right_tests_strides() { + using E = extents; + layout_right::mapping map; + static_assert(map.stride(0) == 7 * 5 * 3); + static_assert(map.stride(1) == 7 * 5); + static_assert(map.stride(2) == 7); + static_assert(map.stride(3) == 1); +} + +void layout_stride_tests_strides() { + using E = extents; + constexpr array s{1, 3}; + constexpr layout_stride::mapping map{E{}, s}; + static_assert(map.stride(0) == s[0]); + static_assert(map.stride(1) == s[1]); + static_assert(map.strides() == s); +} + +void layout_left_tests_indexing() { + static_assert(layout_left::mapping>{}() == 0); + TestMapping(layout_left::mapping>{}); + TestMapping(layout_left::mapping>{}); +} + +void layout_right_tests_indexing() { + static_assert(layout_right::mapping>{}() == 0); + TestMapping(layout_right::mapping>{}); + TestMapping(layout_right::mapping>{}); +} + +template +void copy_ctor_helper_stride(const Extents& e) { + const layout_stride::mapping m1{layout_right::mapping{e}}; + const layout_stride::mapping m2{m1}; + assert(m1 == m2); +} + +void layout_stride_tests_copy_ctor() { + copy_ctor_helper_stride(extents{}); + copy_ctor_helper_stride(extents{7}); + copy_ctor_helper_stride(extents{11}); + copy_ctor_helper_stride(extents{17, 19}); +} + +void layout_stride_tests_ctor_other_extents() { + constexpr extents e1; + constexpr extents e2{3}; + constexpr array s{3, 1}; + + constexpr layout_stride::mapping m1(e1, s); + constexpr layout_stride::mapping m2(m1); + + static_assert(m2.extents() == e1); + static_assert(m2.strides() == s); +} + +template +void other_mapping_helper() { + constexpr LayoutMapping other; + constexpr layout_stride::mapping map{other}; + static_assert(map.extents() == other.extents()); + for (size_t i = 0; i < LayoutMapping::extents_type::rank(); ++i) { + assert(map.stride(i) == other.stride(i)); + } +} +void layout_stride_tests_ctor_other_mapping() { + using E = extents; + other_mapping_helper>(); + other_mapping_helper>(); +} + +template +void assign_helper_stride(const Extents& e) { + const layout_stride::mapping m1{layout_right::mapping{e}}; + layout_stride::mapping m2; + m2 = m1; + assert(m1 == m2); +} + +void layout_stride_tests_assign() { + assign_helper_stride(extents{}); + assign_helper_stride(extents{7}); + assign_helper_stride(extents{11}); + assign_helper_stride(extents{17, 19}); +} + +void layout_stride_tests_indexing_static() { + using E = extents; + TestMapping(layout_stride::mapping{E{}, array{1, 2}}); + TestMapping(layout_stride::mapping{E{}, array{3, 1}}); + + // non-exhaustive mappings + TestMapping(layout_stride::mapping{E{}, array{1, 3}}); + TestMapping(layout_stride::mapping{E{}, array{4, 1}}); + TestMapping(layout_stride::mapping{E{}, array{2, 3}}); + + // exhaustive mappings with singleton dimensions + using E1 = extents; + TestMapping(layout_stride::mapping{E1{}, array{3, 1, 1}}); + TestMapping(layout_stride::mapping{E1{}, array{3, 7, 1}}); + + using E2 = extents; + TestMapping(layout_stride::mapping{E2{}, array{3, 1, 1}}); + TestMapping(layout_stride::mapping{E2{}, array{3, 1, 11}}); + + using E3 = extents; + TestMapping(layout_stride::mapping{E3{}, array{1, 3, 1}}); + TestMapping(layout_stride::mapping{E3{}, array{1, 3, 13}}); +} + + +void layout_stride_tests_equality() { + using E = extents; + constexpr layout_stride::mapping map1(layout_right::mapping{}); + constexpr layout_stride::mapping map2(layout_right::mapping{}); + static_assert(map1 == map2); + + constexpr layout_stride::mapping map3{layout_left::mapping{}}; + static_assert(map1 != map3); + + using ED = extents; + constexpr layout_stride::mapping map4{ED{2, 3}, map1.strides()}; + static_assert(map1 == map4); +} + +void accessor_tests_general() { + default_accessor a; + double arr[4] = {}; + static_assert(a.offset(arr, 3) == &arr[3]); + + a.access(arr, 2) = 42; + assert(arr[2] == 42); + + static_assert(is_constructible_v, default_accessor>); + static_assert(!is_constructible_v, default_accessor>); +} + +namespace Pathological { + + struct Empty {}; + + struct Extents { + using index_type = int; + using size_type = std::make_unsigned_t; + using rank_type = size_t; + + explicit Extents(Empty) {} + + template + explicit Extents(const array&) {} + + static constexpr size_t rank() { + return 0; + } + + static constexpr size_t rank_dynamic() { + return 0; + } + }; + + struct Layout { + template + struct mapping { + using extents_type = E; + using layout_type = Layout; + mapping(Extents) {} + }; + }; + + struct Accessor { + using data_handle_type = int*; + using reference = int&; + Accessor(int) {} + }; +} // namespace Pathological + +void mdspan_tests_traits() { + using M = mdspan>; + static_assert(is_trivially_copyable_v /*&& is_default_constructible_v*/ + && is_copy_constructible_v && is_move_constructible_v && is_copy_assignable_v + && is_move_assignable_v); + + static_assert(is_same_v>); + static_assert(is_same_v); + static_assert(is_same_v>); + static_assert(is_same_v>>); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); +} + +void mdspan_tests_ctor_sizes() { + static constexpr int arr[6] = {}; + constexpr mdspan> mds1(arr, 2); + static_assert(mds1.data_handle() == arr); + static_assert((mds1.extents() == extents{})); + static_assert(mds1.is_exhaustive()); + + static_assert(!is_constructible_v, int*, + Pathological::Empty>); // Empty not convertible to size_type + + // TRANSITION: this assert should be true + // static_assert(!is_constructible_v, int*, + // int>); // Pathological::Extents not constructible from int + + static_assert(!is_constructible_v, Pathological::Layout>, int*, + int>); // Pathological::Layout not constructible from extents + + static_assert( + !is_constructible_v, layout_right, Pathological::Accessor>, int*, + int>); // Pathological::Accessor not default constructible +} + +void mdspan_tests_ctor_array() { + static constexpr int arr[6] = {}; + constexpr mdspan> mds1(arr, array{2}); + static_assert(mds1.data_handle() == arr); + static_assert(mds1.extents() == extents{}); + + static_assert(!is_constructible_v, int*, + array>); // Empty not convertible to size_type + + static_assert(!is_constructible_v, int*, + array>); // Pathological::Extents not constructible from int + + static_assert(!is_constructible_v, Pathological::Layout>, int*, + array>); // Pathological::Layout not constructible from extents + + static_assert( + !is_constructible_v, layout_right, Pathological::Accessor>, int*, + array>); // Pathological::Accessor not default constructible +} + +void mdspan_tests_ctor_extents() { + static constexpr int arr[6] = {}; + constexpr mdspan> mds1(arr, extents{2}); + static_assert(mds1.data_handle() == arr); + static_assert(mds1.extents() == extents{}); + + static_assert(!is_constructible_v, Pathological::Layout>, int*, + extents>); // Pathological::Layout not constructible from extents + + static_assert( + !is_constructible_v, layout_right, Pathological::Accessor>, int*, + extents>); // Pathological::Accessor not default constructible +} + +void mdspan_tests_ctor_mapping() { + static constexpr int arr[6] = {}; + using E = extents; + constexpr layout_left::mapping> map(extents{}); + + constexpr mdspan mds1(arr, map); + static_assert(mds1.data_handle() == arr); + static_assert(mds1.extents() == extents{}); + static_assert(mds1.mapping() == map); + + static_assert( + !is_constructible_v, layout_right, Pathological::Accessor>, int*, + extents>); // Pathological::Accessor not default constructible +} + +template +struct stateful_accessor { + using data_handle_type = Type*; + using reference = Type&; + + constexpr stateful_accessor(int i_) : i(i_){}; + int i = 0; +}; + +void mdspan_tests_ctor_accessor() { + static constexpr int arr[6] = {}; + using E = extents; + constexpr layout_left::mapping> map(extents{}); + constexpr stateful_accessor acc(1); + + constexpr mdspan> mds1(arr, map, acc); + static_assert(mds1.data_handle() == arr); + static_assert(mds1.extents() == extents{}); + static_assert(mds1.mapping() == map); + static_assert(mds1.accessor().i == 1); + + static_assert( + !is_constructible_v, layout_right, Pathological::Accessor>, int*, + extents>); // Pathological::Accessor not default constructible +} + +void mdspan_tests_assign() { + using E2 = extents; + int arr[6] = {}; + mdspan> mds1(arr, 2); + mdspan> mds2(nullptr, 3); + mds2 = mds1; + assert(mds2.data_handle() == arr); + assert(mds2.extents() == E2{}); + assert(mds2.mapping() == mds1.mapping()); +} + +void mdspan_tests_observers() { + using E = extents; + static constexpr int arr[] = {0, 1, 2, 3, 4, 5, 6, 7}; + constexpr mdspan mds{arr, layout_stride::mapping{E{2}, array{1, 3}}}; + + static_assert(mds.rank() == 2); + static_assert(mds.rank_dynamic() == 1); + + static_assert(mds.static_extent(0) == dynamic_extent); + static_assert(mds.static_extent(1) == 3); + static_assert(mds.extent(0) == 2); + static_assert(mds.extent(1) == 3); + static_assert(mds.size() == 6); + + static_assert(mds.stride(0) == 1); + static_assert(mds.stride(1) == 3); + + static_assert(mds.is_always_unique()); + static_assert(!mds.is_always_exhaustive()); + static_assert(mds.is_always_strided()); + + static_assert(mds.is_unique()); + static_assert(!mds.is_exhaustive()); + static_assert(mds.is_strided()); + + static_assert(mds(1, 0) == 1); + static_assert(mds(1, 2) == 7); + + static_assert(mds[array{0, 1}] == 3); + static_assert(mds[array{1, 1}] == 4); +} + +int main() { + extent_tests_rank(); + extent_tests_static_extent(); + extent_tests_extent(); + extent_tests_ctor_other_sizes(); + extent_tests_copy_ctor_other(); + extent_tests_ctor_array(); + extent_tests_ctor_span(); + extent_tests_equality(); + + layout_left_tests_traits(); + layout_left_tests_properties(); + layout_left_tests_extents_ctor(); + layout_left_tests_copy_ctor(); + layout_left_tests_copy_other_extent(); + layout_left_tests_assign(); + layout_left_tests_ctor_other_layout(); + layout_left_tests_strides(); + layout_left_tests_indexing(); + + layout_right_tests_traits(); + layout_right_tests_properties(); + layout_right_tests_extents_ctor(); + layout_right_tests_copy_ctor(); + layout_right_tests_copy_ctor_other(); + layout_right_tests_assign(); + layout_right_tests_ctor_other_layout(); + layout_right_tests_strides(); + layout_right_tests_indexing(); + + layout_stride_tests_traits(); + layout_stride_tests_properties(); + layout_stride_tests_extents_ctor(); + layout_stride_tests_strides(); + layout_stride_tests_copy_ctor(); + layout_stride_tests_ctor_other_extents(); + layout_stride_tests_ctor_other_mapping(); + layout_stride_tests_assign(); + layout_stride_tests_indexing_static(); + layout_stride_tests_equality(); + + accessor_tests_general(); + + mdspan_tests_traits(); + mdspan_tests_ctor_sizes(); + mdspan_tests_ctor_array(); + mdspan_tests_ctor_extents(); + mdspan_tests_ctor_mapping(); + mdspan_tests_ctor_accessor(); + mdspan_tests_assign(); + mdspan_tests_observers(); + + return 0; +} diff --git a/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc b/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc index ad8b6c8ab9..65d0cc77e4 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc +++ b/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc @@ -40,6 +40,7 @@ "list", "locale", "map", + "mdspan", "memory", "memory_resource", "mutex", diff --git a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp index e9ec1a36cf..cd9d33717e 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -46,6 +46,7 @@ import ; import ; import ; import ; +import ; import ; import ; import ; diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 7dedee3a9d..1a08c884d5 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -1322,6 +1322,20 @@ STATIC_ASSERT(__cpp_lib_math_special_functions == 201603L); #endif #endif +#if _HAS_CXX23 +#ifndef __cpp_lib_mdspan +#error __cpp_lib_mdspan is not defined +#elif __cpp_lib_mdspan != 202207L +#error __cpp_lib_mdspan is not 202207L +#else +STATIC_ASSERT(__cpp_lib_mdspan == 202207L); +#endif +#else +#ifdef __cpp_lib_mdspan +#error __cpp_lib_mdspan is defined +#endif +#endif + #if _HAS_CXX17 #ifndef __cpp_lib_memory_resource #error __cpp_lib_memory_resource is not defined From 02b0ecb91d4737a859386da4bbd96975406bb0fe Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Sat, 4 Mar 2023 00:07:54 +0100 Subject: [PATCH 02/57] ``: Various cleanups (#3534) --- stl/inc/mdspan | 136 +++++++++++++++++++++++++------------------------ 1 file changed, 69 insertions(+), 67 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 4724eff0d7..7c057582d7 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -30,19 +30,21 @@ template struct _Mdspan_extent_type { using index_type = _IndexType; - index_type _Dynamic_extents[_Rank_dynamic] = {}; - static constexpr size_t _Static_extents[sizeof...(_Extents)] = {_Extents...}; - static constexpr array _Dynamic_indexes = []() constexpr { - array result; + _NODISCARD static constexpr auto _Get_dynamic_indices() noexcept { // TRANSITION consteval? + array _Result; size_t _Counter = 0; - for (size_t i = 0; i < sizeof...(_Extents); ++i) { - result[i] = _Counter; - if (_Static_extents[i] == dynamic_extent) { + for (size_t _Ix = 0; _Ix < sizeof...(_Extents); ++_Ix) { + _Result[_Ix] = _Counter; + if (_Static_extents[_Ix] == dynamic_extent) { ++_Counter; } } - return result; - }(); + return _Result; + } + + index_type _Dynamic_extents[_Rank_dynamic] = {}; + static constexpr size_t _Static_extents[sizeof...(_Extents)] = {_Extents...}; + static constexpr array _Dynamic_indices = _Get_dynamic_indices(); constexpr _Mdspan_extent_type() noexcept = default; @@ -55,7 +57,6 @@ struct _Mdspan_extent_type { constexpr _Mdspan_extent_type(span<_OtherIndexType, _Size> _Data, index_sequence<_Idx...>) noexcept : _Dynamic_extents{static_cast(_STD as_const(_Data[_Idx]))...} {} - template && ...), @@ -68,7 +69,7 @@ struct _Mdspan_extent_type { template = 0> constexpr _Mdspan_extent_type(span<_OtherIndexType, _Size> _Data, index_sequence<_Idx...>) noexcept - : _Dynamic_extents{{static_cast(_STD as_const(_Data[_Dynamic_indexes[_Idx]]))...}} {} + : _Dynamic_extents{{static_cast(_STD as_const(_Data[_Dynamic_indices[_Idx]]))...}} {} constexpr index_type* _Begin_dynamic_extents() noexcept { return _Dynamic_extents; @@ -152,7 +153,7 @@ public: } else { const auto _Static_extent = _Mybase::_Static_extents[_Idx]; if (_Static_extent == dynamic_extent) { - return _Mybase::_Dynamic_extents[_Mybase::_Dynamic_indexes[_Idx]]; + return _Mybase::_Dynamic_extents[_Mybase::_Dynamic_indices[_Idx]]; } else { return static_cast(_Static_extent); } @@ -166,10 +167,9 @@ public: && ((_OtherExtents == dynamic_extent || _Extents == dynamic_extent || _OtherExtents == _Extents) && ...), int> = 0> - explicit((((_Extents != dynamic_extent) && (_OtherExtents == dynamic_extent)) || ...) - || numeric_limits::max() - < numeric_limits<_OtherIndexType>::max()) constexpr extents(const extents<_OtherIndexType, - _OtherExtents...>& _Other) noexcept { + constexpr explicit((((_Extents != dynamic_extent) && (_OtherExtents == dynamic_extent)) || ...) + || numeric_limits::max() < numeric_limits<_OtherIndexType>::max()) + extents(const extents<_OtherIndexType, _OtherExtents...>& _Other) noexcept { auto _Dynamic_it = _Mybase::_Begin_dynamic_extents(); for (rank_type _Dim = 0; _Dim < sizeof...(_Extents); ++_Dim) { if (_Mybase::_Static_extents[_Dim] == dynamic_extent) { @@ -183,7 +183,7 @@ public: && (is_nothrow_constructible_v && ...) && (sizeof...(_OtherIndexTypes) == rank_dynamic() || sizeof...(_OtherIndexTypes) == rank()), int> = 0> - explicit constexpr extents(_OtherIndexTypes... _Exts) noexcept + constexpr explicit extents(_OtherIndexTypes... _Exts) noexcept : _Mybase{static_cast(_STD move(_Exts))...} {} template && (_Size == rank_dynamic() || _Size == rank()), int> = 0> - explicit(_Size != rank_dynamic()) constexpr extents(const array<_OtherIndexType, _Size>& _Exts) noexcept - : _Mybase{span{_Exts}, _STD make_index_sequence{}} {} + constexpr explicit(_Size != rank_dynamic()) extents(span<_OtherIndexType, _Size> _Exts) noexcept + : _Mybase{_Exts, _STD make_index_sequence{}} {} template && is_nothrow_constructible_v && (_Size == rank_dynamic() || _Size == rank()), int> = 0> - explicit(_Size != rank_dynamic()) constexpr extents(span<_OtherIndexType, _Size> _Exts) noexcept - : _Mybase{_Exts, _STD make_index_sequence{}} {} - + constexpr explicit(_Size != rank_dynamic()) extents(const array<_OtherIndexType, _Size>& _Exts) noexcept + : _Mybase{span{_Exts}, _STD make_index_sequence{}} {} template _NODISCARD_FRIEND constexpr bool operator==( - const extents& _Lhs, const extents<_OtherIndexType, _OtherExtents...>& _Rhs) noexcept { + const extents& _Left, const extents<_OtherIndexType, _OtherExtents...>& _Right) noexcept { if constexpr (sizeof...(_Extents) != sizeof...(_OtherExtents)) { return false; } for (size_t _Dim = 0; _Dim < sizeof...(_Extents); ++_Dim) { - if (_Lhs.extent(_Dim) != _Rhs.extent(_Dim)) { + if (_Left.extent(_Dim) != _Right.extent(_Dim)) { return false; } } @@ -242,14 +241,12 @@ template extents(_Integrals... _Ext) -> extents, _Integrals>::value...>; - template constexpr bool _Is_extents = false; template constexpr bool _Is_extents> = true; - template struct _Layout_mapping_alike_helper : false_type {}; @@ -264,12 +261,10 @@ struct _Layout_mapping_alike_helper<_Mapping, template struct _Layout_mapping_alike : bool_constant<_Layout_mapping_alike_helper<_Mapping>::value> {}; - template constexpr bool _Is_mapping_of = is_same_v, _Mapping>; - struct layout_left { template class mapping; @@ -300,18 +295,18 @@ public: constexpr mapping(const _Extents& e) noexcept : _Myext(e){}; template , int> = 0> - explicit(!is_convertible_v<_OtherExtents, _Extents>) constexpr mapping( - const mapping<_OtherExtents>& _Other) noexcept + constexpr explicit(!is_convertible_v<_OtherExtents, _Extents>) + mapping(const mapping<_OtherExtents>& _Other) noexcept : _Myext{_Other.extents()} {}; template , int> = 0> - explicit(!is_convertible_v<_OtherExtents, _Extents>) constexpr mapping( - const layout_right::mapping<_OtherExtents>& _Other) noexcept + constexpr explicit(!is_convertible_v<_OtherExtents, _Extents>) + mapping(const layout_right::mapping<_OtherExtents>& _Other) noexcept : _Myext{_Other.extents()} {} template , int> = 0> - explicit(_Extents::rank() > 0) constexpr mapping(const layout_stride::template mapping<_OtherExtents>& _Other) + constexpr explicit(_Extents::rank() > 0) mapping(const layout_stride::template mapping<_OtherExtents>& _Other) : _Myext{_Other.extents()} {} constexpr mapping& operator=(const mapping&) noexcept = default; @@ -341,9 +336,11 @@ public: _NODISCARD static constexpr bool is_always_unique() noexcept { return true; } + _NODISCARD static constexpr bool is_always_exhaustive() noexcept { return true; } + _NODISCARD static constexpr bool is_always_strided() noexcept { return true; } @@ -351,9 +348,11 @@ public: _NODISCARD constexpr bool is_unique() const noexcept { return true; } + _NODISCARD constexpr bool is_exhaustive() const noexcept { return true; } + _NODISCARD constexpr bool is_strided() const noexcept { return true; } @@ -369,8 +368,8 @@ public: } template - _NODISCARD friend constexpr bool operator==(const mapping& _Lhs, const mapping& _Rhs) noexcept { - return _Lhs.extents() == _Rhs.extents(); + _NODISCARD friend constexpr bool operator==(const mapping& _Left, const mapping& _Right) noexcept { + return _Left.extents() == _Right.extents(); } private: @@ -401,21 +400,20 @@ public: constexpr mapping(const _Extents& e) noexcept : _Myext(e){}; template , int> = 0> - explicit(!is_convertible_v<_OtherExtents, _Extents>) constexpr mapping( - const mapping<_OtherExtents>& _Other) noexcept + constexpr explicit(!is_convertible_v<_OtherExtents, _Extents>) + mapping(const mapping<_OtherExtents>& _Other) noexcept : _Myext{_Other.extents()} {}; template , int> = 0> - explicit(!is_convertible_v<_OtherExtents, _Extents>) constexpr mapping( - const layout_left::mapping<_OtherExtents>& _Other) noexcept + constexpr explicit(!is_convertible_v<_OtherExtents, _Extents>) + mapping(const layout_left::mapping<_OtherExtents>& _Other) noexcept : _Myext{_Other.extents()} {} template , int> = 0> - explicit(_Extents::rank() > 0) constexpr mapping(const layout_stride::template mapping<_OtherExtents>& _Other) + constexpr explicit(_Extents::rank() > 0) mapping(const layout_stride::template mapping<_OtherExtents>& _Other) : _Myext{_Other.extents()} {} - constexpr mapping& operator=(const mapping&) noexcept = default; _NODISCARD constexpr const extents_type& extents() const noexcept { @@ -443,9 +441,11 @@ public: _NODISCARD static constexpr bool is_always_unique() noexcept { return true; } + _NODISCARD static constexpr bool is_always_exhaustive() noexcept { return true; } + _NODISCARD static constexpr bool is_always_strided() noexcept { return true; } @@ -453,9 +453,11 @@ public: _NODISCARD constexpr bool is_unique() const noexcept { return true; } + _NODISCARD constexpr bool is_exhaustive() const noexcept { return true; } + _NODISCARD constexpr bool is_strided() const noexcept { return true; } @@ -471,8 +473,8 @@ public: } template - _NODISCARD friend constexpr bool operator==(const mapping& _Lhs, const mapping& _Rhs) noexcept { - return _Lhs.extents() == _Rhs.extents(); + _NODISCARD friend constexpr bool operator==(const mapping& _Left, const mapping& _Right) noexcept { + return _Left.extents() == _Right.extents(); } private: @@ -490,7 +492,6 @@ private: } }; - template class layout_stride::mapping { public: @@ -518,8 +519,8 @@ public: }; template , int> = 0> - explicit(!is_convertible_v<_OtherExtents, _Extents>) constexpr mapping( - const mapping<_OtherExtents>& _Other) noexcept + constexpr explicit(!is_convertible_v<_OtherExtents, _Extents>) + mapping(const mapping<_OtherExtents>& _Other) noexcept : _Myext{_Other.extents()}, _Mystrides{_Other.strides()} { for (rank_type _Idx = 0; _Idx < _Extents::rank(); ++_Idx) { _Mystrides[_Idx] = _Other.stride(_Idx); @@ -531,11 +532,11 @@ public: && is_constructible_v && _StridedLayoutMapping::is_always_unique() && _StridedLayoutMapping::is_always_strided(), int> = 0> - explicit( + constexpr explicit( !is_convertible_v && (_Is_mapping_of || _Is_mapping_of - || _Is_mapping_of) ) constexpr mapping(const _StridedLayoutMapping& - _Other) noexcept + || _Is_mapping_of) ) + mapping(const _StridedLayoutMapping& _Other) noexcept : _Myext(_Other.extents()) { for (rank_type _Dim = 0; _Dim < _Extents::rank(); ++_Dim) { _Mystrides[_Dim] = _Other.stride(_Dim); @@ -617,25 +618,25 @@ public: && extents_type::rank() == _OtherMapping::extents_type::rank() && _OtherMapping::is_always_strided(), int> = 0> - _NODISCARD friend constexpr bool operator==(const mapping& _Lhs, const _OtherMapping& _Rhs) noexcept { - if (_Lhs.extents() != _Rhs.extents()) { + _NODISCARD friend constexpr bool operator==(const mapping& _Left, const _OtherMapping& _Right) noexcept { + if (_Left.extents() != _Right.extents()) { return false; } constexpr rank_type _Rank = extents_type::rank(); for (rank_type _Dim = 0; _Dim < _Rank; ++_Dim) { - if (_Lhs.stride(_Dim) != _Rhs.stride(_Dim)) { + if (_Left.stride(_Dim) != _Right.stride(_Dim)) { return false; } } index_type _Offset; if constexpr (_Rank == 0) { - _Offset = _Rhs(); + _Offset = _Right(); } else { bool _Is_empty = false; for (rank_type _Dim = 0; _Dim < _Rank; ++_Dim) { - if (_Lhs.extents().extent(_Dim) == 0) { + if (_Left.extents().extent(_Dim) == 0) { _Is_empty = true; break; } @@ -644,8 +645,8 @@ public: if (_Is_empty) { _Offset = 0; } else { - _Offset = [&_Rhs](index_sequence<_Idx...>) { - return _Rhs(((void) _Idx, 0)...); + _Offset = [&_Right](index_sequence<_Idx...>) { + return _Right(((void) _Idx, 0)...); } (make_index_sequence<_Rank>{}); } @@ -692,7 +693,6 @@ template > class mdspan { public: - // Domain and codomain types using extents_type = _Extents; using layout_type = _LayoutPolicy; using accessor_type = _AccessorPolicy; @@ -708,9 +708,11 @@ public: _NODISCARD static constexpr rank_type rank() noexcept { return _Extents::rank(); } + _NODISCARD static constexpr rank_type rank_dynamic() noexcept { return _Extents::rank_dynamic(); } + _NODISCARD static constexpr size_t static_extent(const rank_type r) noexcept { return _Extents::static_extent(r); } @@ -720,6 +722,7 @@ public: && is_default_constructible_v<_Mapping> && is_default_constructible_v, int> = 0> constexpr mdspan() {} + constexpr mdspan(const mdspan& rhs) = default; constexpr mdspan(mdspan&& rhs) = default; @@ -727,7 +730,7 @@ public: enable_if_t<(rank() == 0 || rank_dynamic() == 0) && is_constructible_v<_Mapping, extents_type> && is_default_constructible_v, int> = 0> - explicit constexpr mdspan(data_handle_type _Ptr_) : _Ptr{_STD move(_Ptr_)}, _Map{extents_type{}} {} + constexpr explicit mdspan(data_handle_type _Ptr_) : _Ptr{_STD move(_Ptr_)}, _Map{extents_type{}} {} template 0)) && (is_convertible_v<_OtherIndexTypes, index_type> && ...) @@ -735,7 +738,7 @@ public: //&& (sizeof...(_OtherIndexTypes) == rank() || sizeof...(_OtherIndexTypes) == rank_dynamic()) && is_constructible_v && is_default_constructible_v, int> = 0> - explicit constexpr mdspan(data_handle_type _Ptr_, _OtherIndexTypes... _Exts) + constexpr explicit mdspan(data_handle_type _Ptr_, _OtherIndexTypes... _Exts) : _Ptr{_STD move(_Ptr_)}, _Map{extents_type{static_cast(_STD move(_Exts))...}} {} template && is_default_constructible_v, int> = 0> - explicit(_Size != rank_dynamic()) constexpr mdspan(data_handle_type _Ptr_, span<_OtherIndexType, _Size>& _Exts) + constexpr explicit(_Size != rank_dynamic()) mdspan(data_handle_type _Ptr_, span<_OtherIndexType, _Size>& _Exts) : _Ptr{_Ptr_}, _Map{_Extents{_Exts}} {} template && is_default_constructible_v, int> = 0> - explicit(_Size != rank_dynamic()) constexpr mdspan( - data_handle_type _Ptr_, const array<_OtherIndexType, _Size>& _Exts) + constexpr explicit(_Size != rank_dynamic()) + mdspan(data_handle_type _Ptr_, const array<_OtherIndexType, _Size>& _Exts) : _Ptr{_Ptr_}, _Map{_Extents{_Exts}} {} - template && is_default_constructible_v, int> = 0> constexpr mdspan(data_handle_type _Ptr_, const _Extents& _Ext) : _Ptr{_Ptr_}, _Map{_Ext} {} @@ -845,10 +847,10 @@ public: return false; } - friend constexpr void swap(mdspan& _Lhs, mdspan& _Rhs) noexcept { - swap(_Lhs._Ptr, _Rhs._Ptr); - swap(_Lhs._Map, _Rhs._Map); - swap(_Lhs._Acc, _Rhs._Acc); + friend constexpr void swap(mdspan& _Left, mdspan& _Right) noexcept { + swap(_Left._Ptr, _Right._Ptr); + swap(_Left._Map, _Right._Map); + swap(_Left._Acc, _Right._Acc); } _NODISCARD static constexpr bool is_always_unique() { From 0314b17a6f4362320d9c8cb11460fd9a4cd7fd65 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Sat, 4 Mar 2023 16:14:27 +0100 Subject: [PATCH 03/57] ``: Implement *Mandates* clauses (#3535) Co-authored-by: Matt Stephanson <68978048+MattStephanson@users.noreply.github.com> --- stl/inc/mdspan | 98 +++++++++++++++++------- tests/std/tests/P0009R18_mdspan/test.cpp | 31 ++++---- 2 files changed, 87 insertions(+), 42 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 7c057582d7..83dbe8a9c1 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -125,13 +125,11 @@ public: using size_type = make_unsigned_t; using rank_type = size_t; - // TRANSITION: doesn't account for extended integer types - static_assert(_Is_any_of_v, signed char, unsigned char, short, unsigned short, int, - unsigned int, long, unsigned long, long long, unsigned long long>, - "N4928 [mdspan.extents.overview]/2 " - "requires that extents::index_type be a signed or unsigned integer type."); - - static_assert(((_Extents == dynamic_extent || _Extents <= (numeric_limits<_IndexType>::max)()) && ...)); + static_assert(_Is_standard_integer<_IndexType>, + "IndexType must be a signed or unsigned integer type (N4928 [mdspan.extents.overview]/1.1)."); + static_assert(((_Extents == dynamic_extent || _STD in_range<_IndexType>(_Extents)) && ...), + "Each element of Extents must be either equal to dynamic_extent, or must be representable as a value of type " + "IndexType (N4928 [mdspan.extents.overview]/1.2)."); _NODISCARD static constexpr rank_type rank() noexcept { return sizeof...(_Extents); @@ -228,6 +226,14 @@ public: } } } + + _NODISCARD static constexpr bool _Is_index_space_size_representable() { + if constexpr (rank_dynamic() == 0 && rank() > 0) { + return _STD in_range((_Extents * ...)); + } else { + return true; + } + } }; template @@ -242,10 +248,10 @@ extents(_Integrals... _Ext) -> extents, _Integrals>::value...>; template -constexpr bool _Is_extents = false; +inline constexpr bool _Is_extents = false; template -constexpr bool _Is_extents> = true; +inline constexpr bool _Is_extents> = true; template struct _Layout_mapping_alike_helper : false_type {}; @@ -262,7 +268,7 @@ template struct _Layout_mapping_alike : bool_constant<_Layout_mapping_alike_helper<_Mapping>::value> {}; template -constexpr bool _Is_mapping_of = +inline constexpr bool _Is_mapping_of = is_same_v, _Mapping>; struct layout_left { @@ -289,15 +295,21 @@ public: using rank_type = typename _Extents::rank_type; using layout_type = layout_left; + static_assert( + _Is_extents<_Extents>, "Extents must be a specialization of extents (N4928 [mdspan.layout.left.overview]/2)."); + static_assert(_Extents::_Is_index_space_size_representable(), + "If Extends::rank_dynamic() == 0 is true, then the size of the multidimensional index space Extents() is " + "representable as a value of type typename Extends::index_type."); + constexpr mapping() noexcept = default; constexpr mapping(const mapping&) noexcept = default; - constexpr mapping(const _Extents& e) noexcept : _Myext(e){}; + constexpr mapping(const _Extents& _Ext) noexcept : _Myext(_Ext) {} template , int> = 0> constexpr explicit(!is_convertible_v<_OtherExtents, _Extents>) mapping(const mapping<_OtherExtents>& _Other) noexcept - : _Myext{_Other.extents()} {}; + : _Myext{_Other.extents()} {} template , int> = 0> @@ -367,8 +379,8 @@ public: return _Result; } - template - _NODISCARD friend constexpr bool operator==(const mapping& _Left, const mapping& _Right) noexcept { + template + _NODISCARD_FRIEND constexpr bool operator==(const mapping& _Left, const mapping<_OtherExtents>& _Right) noexcept { return _Left.extents() == _Right.extents(); } @@ -394,15 +406,21 @@ public: using rank_type = typename _Extents::rank_type; using layout_type = layout_right; + static_assert( + _Is_extents<_Extents>, "Extents must be a specialization of extents (N4928 [mdspan.layout.right.overview]/2)."); + static_assert(_Extents::_Is_index_space_size_representable(), + "If Extends::rank_dynamic() == 0 is true, then the size of the multidimensional index space Extents() is " + "representable as a value of type typename Extends::index_type."); + constexpr mapping() noexcept = default; constexpr mapping(const mapping&) noexcept = default; - constexpr mapping(const _Extents& e) noexcept : _Myext(e){}; + constexpr mapping(const _Extents& _Ext) noexcept : _Myext(_Ext) {} template , int> = 0> constexpr explicit(!is_convertible_v<_OtherExtents, _Extents>) mapping(const mapping<_OtherExtents>& _Other) noexcept - : _Myext{_Other.extents()} {}; + : _Myext{_Other.extents()} {} template , int> = 0> @@ -472,8 +490,8 @@ public: return _Result; } - template - _NODISCARD friend constexpr bool operator==(const mapping& _Left, const mapping& _Right) noexcept { + template + _NODISCARD_FRIEND constexpr bool operator==(const mapping& _Left, const mapping<_OtherExtents>& _Right) noexcept { return _Left.extents() == _Right.extents(); } @@ -501,6 +519,12 @@ public: using rank_type = typename _Extents::rank_type; using layout_type = layout_stride; + static_assert(_Is_extents<_Extents>, + "Extents must be a specialization of extents (N4928 [mdspan.layout.stride.overview]/2)."); + static_assert(_Extents::_Is_index_space_size_representable(), + "If Extends::rank_dynamic() == 0 is true, then the size of the multidimensional index space Extents() is " + "representable as a value of type typename Extends::index_type."); + constexpr mapping() noexcept = default; constexpr mapping(const mapping&) noexcept = default; @@ -618,7 +642,7 @@ public: && extents_type::rank() == _OtherMapping::extents_type::rank() && _OtherMapping::is_always_strided(), int> = 0> - _NODISCARD friend constexpr bool operator==(const mapping& _Left, const _OtherMapping& _Right) noexcept { + _NODISCARD_FRIEND constexpr bool operator==(const mapping& _Left, const _OtherMapping& _Right) noexcept { if (_Left.extents() != _Right.extents()) { return false; } @@ -705,6 +729,18 @@ public: using data_handle_type = typename accessor_type::data_handle_type; using reference = typename accessor_type::reference; + static_assert( + sizeof(_ElementType) > 0, "ElementType must be a complete type (N4928 [mdspan.mdspan.overview]/2.1)."); + static_assert( + !is_abstract_v<_ElementType>, "ElementType cannot be an abstract type (N4928 [mdspan.mdspan.overview]/2.1)."); + static_assert( + !is_array_v<_ElementType>, "ElementType cannot be an array type (N4928 [mdspan.mdspan.overview]/2.1)."); + static_assert( + _Is_extents<_Extents>, "Extents must be a specialization of extents (N4928 [mdspan.mdspan.overview]/2.2)."); + static_assert(is_same_v<_ElementType, typename _AccessorPolicy::element_type>, + "Expression is_same_v must be true (N4928 " + "[mdspan.mdspan.overview]/2.3)."); + _NODISCARD static constexpr rank_type rank() noexcept { return _Extents::rank(); } @@ -713,8 +749,8 @@ public: return _Extents::rank_dynamic(); } - _NODISCARD static constexpr size_t static_extent(const rank_type r) noexcept { - return _Extents::static_extent(r); + _NODISCARD static constexpr size_t static_extent(const rank_type _Rank) noexcept { + return _Extents::static_extent(_Rank); } template = 0> constexpr mdspan() {} - constexpr mdspan(const mdspan& rhs) = default; - constexpr mdspan(mdspan&& rhs) = default; + constexpr mdspan(const mdspan&) = default; + constexpr mdspan(mdspan&&) = default; template ) mdspan(const mdspan<_OtherElementType, _OtherExtents, _OtherLayoutPolicy, _OtherAccessor>& _Other) : _Ptr{_Other._Ptr}, _Map{_Other._Map}, _Acc{_Other._Acc} { - static_assert(is_constructible_v); - static_assert(is_constructible_v); + static_assert(is_constructible_v, + "Expression is_constructible_v must " + "be true (N4928 [mdspan.mdspan.cons]/20.1)."); + static_assert(is_constructible_v, + "Expression is_constructible_v must be true (N4928 " + "[mdspan.mdspan.cons]/20.2)."); } - constexpr mdspan& operator=(const mdspan& rhs) = default; - constexpr mdspan& operator=(mdspan&& rhs) = default; + constexpr mdspan& operator=(const mdspan&) = default; + constexpr mdspan& operator=(mdspan&&) = default; // TRANSITION operator[](const _OtherIndexTypes... _Indices) template , extents>); static_assert(!is_constructible_v, extents>); - // Static extents are constuctible, but not convertible, from dynamic extents. + // Static extents are constructible, but not convertible, from dynamic extents. // static_assert(is_constructible_v, extents>); constexpr extents ex0{extents{}}; (void) ex0; static_assert(!is_convertible_v, extents>); - // Dynamic extents are constuctible and convertible from static extents. + // Dynamic extents are constructible and convertible from static extents. static_assert(is_constructible_v, extents>); extents{extents{}}; static_assert(is_convertible_v, extents>); @@ -776,8 +776,8 @@ void layout_stride_tests_ctor_other_extents() { constexpr extents e2{3}; constexpr array s{3, 1}; - constexpr layout_stride::mapping m1(e1, s); - constexpr layout_stride::mapping m2(m1); + constexpr layout_stride::mapping> m1(e1, s); + constexpr layout_stride::mapping> m2(m1); static_assert(m2.extents() == e1); static_assert(m2.strides() == s); @@ -897,6 +897,7 @@ namespace Pathological { }; struct Accessor { + using element_type = int; using data_handle_type = int*; using reference = int&; Accessor(int) {} @@ -927,10 +928,11 @@ void mdspan_tests_ctor_sizes() { static_assert((mds1.extents() == extents{})); static_assert(mds1.is_exhaustive()); - static_assert(!is_constructible_v, int*, - Pathological::Empty>); // Empty not convertible to size_type + // TRANSITION: fix with concepts -> this is hard error per [mdspan.mdspan.overview]/2.2 + // static_assert(!is_constructible_v, int*, + // Pathological::Empty>); // Empty not convertible to size_type - // TRANSITION: this assert should be true + // TRANSITION: fix with concepts -> this is hard error per [mdspan.mdspan.overview]/2.2 // static_assert(!is_constructible_v, int*, // int>); // Pathological::Extents not constructible from int @@ -948,11 +950,13 @@ void mdspan_tests_ctor_array() { static_assert(mds1.data_handle() == arr); static_assert(mds1.extents() == extents{}); - static_assert(!is_constructible_v, int*, - array>); // Empty not convertible to size_type + // TRANSITION: fix with concepts -> this is hard error per [mdspan.mdspan.overview]/2.2 + // static_assert(!is_constructible_v, int*, + // array>); // Empty not convertible to size_type - static_assert(!is_constructible_v, int*, - array>); // Pathological::Extents not constructible from int + // TRANSITION: fix with concepts -> this is hard error per [mdspan.mdspan.overview]/2.2 + // static_assert(!is_constructible_v, int*, + // array>); // Pathological::Extents not constructible from int static_assert(!is_constructible_v, Pathological::Layout>, int*, array>); // Pathological::Layout not constructible from extents @@ -994,6 +998,7 @@ void mdspan_tests_ctor_mapping() { template struct stateful_accessor { + using element_type = Type; using data_handle_type = Type*; using reference = Type&; From 790187689803d3913b39f62313c3b5bfff49a545 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Sun, 12 Mar 2023 22:11:54 +0100 Subject: [PATCH 04/57] ``: Require concepts (#3560) --- stl/inc/mdspan | 105 +++++++++++------------ tests/std/tests/P0009R18_mdspan/env.lst | 2 +- tests/std/tests/P0009R18_mdspan/test.cpp | 10 --- 3 files changed, 51 insertions(+), 66 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 83dbe8a9c1..ba2207ff25 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -8,9 +8,9 @@ #define _MDSPAN_ #include #if _STL_COMPILER_PREPROCESSOR -#if !_HAS_CXX23 +#if !_HAS_CXX23 || !defined(__cpp_lib_concepts) // TRANSITION, GH-395 _EMIT_STL_WARNING(STL4038, "The contents of are available only with C++23 or later."); -#else // ^^^ !_HAS_CXX23 / _HAS_CXX23 vvv +#else // ^^^ not supported / supported language mode vvv #include #include #include @@ -48,26 +48,25 @@ struct _Mdspan_extent_type { constexpr _Mdspan_extent_type() noexcept = default; - template && ...), - int> = 0> + template + requires (sizeof...(_OtherIndexTypes) == _Rank_dynamic) && (is_same_v<_OtherIndexTypes, index_type> && ...) constexpr _Mdspan_extent_type(_OtherIndexTypes... _OtherExtents) noexcept : _Dynamic_extents{_OtherExtents...} {} - template = 0> + template + requires (_Size == _Rank_dynamic) constexpr _Mdspan_extent_type(span<_OtherIndexType, _Size> _Data, index_sequence<_Idx...>) noexcept : _Dynamic_extents{static_cast(_STD as_const(_Data[_Idx]))...} {} - template && ...), - int> = 0> + template + requires (sizeof...(_OtherIndexTypes) == sizeof...(_Extents)) && (sizeof...(_Extents) != _Rank_dynamic) + && (is_same_v<_OtherIndexTypes, index_type> && ...) constexpr _Mdspan_extent_type(_OtherIndexTypes... _OtherExtents) noexcept { auto _It = _Dynamic_extents; ((_Extents == dynamic_extent ? void(*_It++ = _OtherExtents) : void(_OtherExtents)), ...); } - template = 0> + template + requires (_Size == sizeof...(_Extents)) && (sizeof...(_Extents) != _Rank_dynamic) constexpr _Mdspan_extent_type(span<_OtherIndexType, _Size> _Data, index_sequence<_Idx...>) noexcept : _Dynamic_extents{{static_cast(_STD as_const(_Data[_Dynamic_indices[_Idx]]))...}} {} @@ -88,11 +87,12 @@ struct _Mdspan_extent_type<_IndexType, 0, _Extents...> { constexpr _Mdspan_extent_type() noexcept = default; - template = 0> + template + requires (sizeof...(_IndexTypes) == sizeof...(_Extents)) constexpr _Mdspan_extent_type(_IndexTypes... /*_OtherExtents*/) noexcept {} - template = 0> + template + requires (_Size == sizeof...(_Extents)) || (_Size == 0) constexpr _Mdspan_extent_type(span<_OtherIndexType, _Size>, index_sequence<_Idx...>) noexcept {} constexpr index_type* _Begin_dynamic_extents() noexcept { @@ -160,13 +160,11 @@ public: constexpr extents() noexcept = default; - template = 0> + template + requires (sizeof...(_OtherExtents) == sizeof...(_Extents)) + && ((_OtherExtents == dynamic_extent || _Extents == dynamic_extent || _OtherExtents == _Extents) && ...) constexpr explicit((((_Extents != dynamic_extent) && (_OtherExtents == dynamic_extent)) || ...) - || numeric_limits::max() < numeric_limits<_OtherIndexType>::max()) + || (numeric_limits::max)() < (numeric_limits<_OtherIndexType>::max)()) extents(const extents<_OtherIndexType, _OtherExtents...>& _Other) noexcept { auto _Dynamic_it = _Mybase::_Begin_dynamic_extents(); for (rank_type _Dim = 0; _Dim < sizeof...(_Extents); ++_Dim) { @@ -176,27 +174,24 @@ public: } } - template && ...) - && (is_nothrow_constructible_v && ...) - && (sizeof...(_OtherIndexTypes) == rank_dynamic() || sizeof...(_OtherIndexTypes) == rank()), - int> = 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 : _Mybase{static_cast(_STD move(_Exts))...} {} - template - && is_nothrow_constructible_v - && (_Size == rank_dynamic() || _Size == rank()), - int> = 0> + template + requires is_convertible_v + && is_nothrow_constructible_v + && (_Size == rank_dynamic() || _Size == rank()) constexpr explicit(_Size != rank_dynamic()) extents(span<_OtherIndexType, _Size> _Exts) noexcept : _Mybase{_Exts, _STD make_index_sequence{}} {} - template - && is_nothrow_constructible_v - && (_Size == rank_dynamic() || _Size == rank()), - int> = 0> + template + requires is_convertible_v + && is_nothrow_constructible_v + && (_Size == rank_dynamic() || _Size == rank()) constexpr explicit(_Size != rank_dynamic()) extents(const array<_OtherIndexType, _Size>& _Exts) noexcept : _Mybase{span{_Exts}, _STD make_index_sequence{}} {} @@ -243,7 +238,8 @@ using dextents = }(_IndexType{0}, make_index_sequence<_Rank>{})); // TRANSITION: why not `((void) _Ext, dynamic_extent)...`?! -template && ...), int> = 0> +template + requires (is_convertible_v<_Integrals, size_t> && ...) extents(_Integrals... _Ext) -> extents, _Integrals>::value...>; @@ -295,11 +291,11 @@ public: using rank_type = typename _Extents::rank_type; using layout_type = layout_left; - static_assert( - _Is_extents<_Extents>, "Extents must be a specialization of extents (N4928 [mdspan.layout.left.overview]/2)."); + static_assert(_Is_extents<_Extents>, + "Extents must be a specialization of std::extents (N4928 [mdspan.layout.left.overview]/2)."); static_assert(_Extents::_Is_index_space_size_representable(), - "If Extends::rank_dynamic() == 0 is true, then the size of the multidimensional index space Extents() is " - "representable as a value of type typename Extends::index_type."); + "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."); constexpr mapping() noexcept = default; constexpr mapping(const mapping&) noexcept = default; @@ -406,11 +402,11 @@ public: using rank_type = typename _Extents::rank_type; using layout_type = layout_right; - static_assert( - _Is_extents<_Extents>, "Extents must be a specialization of extents (N4928 [mdspan.layout.right.overview]/2)."); + static_assert(_Is_extents<_Extents>, + "Extents must be a specialization of std::extents (N4928 [mdspan.layout.right.overview]/2)."); static_assert(_Extents::_Is_index_space_size_representable(), - "If Extends::rank_dynamic() == 0 is true, then the size of the multidimensional index space Extents() is " - "representable as a value of type typename Extends::index_type."); + "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."); constexpr mapping() noexcept = default; constexpr mapping(const mapping&) noexcept = default; @@ -520,10 +516,10 @@ public: using layout_type = layout_stride; static_assert(_Is_extents<_Extents>, - "Extents must be a specialization of extents (N4928 [mdspan.layout.stride.overview]/2)."); + "Extents must be a specialization of std::extents (N4928 [mdspan.layout.stride.overview]/2)."); static_assert(_Extents::_Is_index_space_size_representable(), - "If Extends::rank_dynamic() == 0 is true, then the size of the multidimensional index space Extents() is " - "representable as a value of type typename Extends::index_type."); + "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."); constexpr mapping() noexcept = default; constexpr mapping(const mapping&) noexcept = default; @@ -735,10 +731,10 @@ public: !is_abstract_v<_ElementType>, "ElementType cannot be an abstract type (N4928 [mdspan.mdspan.overview]/2.1)."); static_assert( !is_array_v<_ElementType>, "ElementType cannot be an array type (N4928 [mdspan.mdspan.overview]/2.1)."); - static_assert( - _Is_extents<_Extents>, "Extents must be a specialization of extents (N4928 [mdspan.mdspan.overview]/2.2)."); + static_assert(_Is_extents<_Extents>, + "Extents must be a specialization of std::extents (N4928 [mdspan.mdspan.overview]/2.2)."); static_assert(is_same_v<_ElementType, typename _AccessorPolicy::element_type>, - "Expression is_same_v must be true (N4928 " + "ElementType and typename AccessorPolicy::element_type must be the same type (N4928 " "[mdspan.mdspan.overview]/2.3)."); _NODISCARD static constexpr rank_type rank() noexcept { @@ -817,11 +813,10 @@ public: mdspan(const mdspan<_OtherElementType, _OtherExtents, _OtherLayoutPolicy, _OtherAccessor>& _Other) : _Ptr{_Other._Ptr}, _Map{_Other._Map}, _Acc{_Other._Acc} { static_assert(is_constructible_v, - "Expression is_constructible_v must " - "be true (N4928 [mdspan.mdspan.cons]/20.1)."); + "The data_handle_type must be constructible from const typename OtherAccessor::data_handle_type& (N4928 " + "[mdspan.mdspan.cons]/20.1)."); static_assert(is_constructible_v, - "Expression is_constructible_v must be true (N4928 " - "[mdspan.mdspan.cons]/20.2)."); + "The extents_type must be constructible from OtherExtents (N4928 [mdspan.mdspan.cons]/20.2)."); } constexpr mdspan& operator=(const mdspan&) = default; diff --git a/tests/std/tests/P0009R18_mdspan/env.lst b/tests/std/tests/P0009R18_mdspan/env.lst index 642f530ffa..18e2d7c71e 100644 --- a/tests/std/tests/P0009R18_mdspan/env.lst +++ b/tests/std/tests/P0009R18_mdspan/env.lst @@ -1,4 +1,4 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -RUNALL_INCLUDE ..\usual_latest_matrix.lst +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P0009R18_mdspan/test.cpp b/tests/std/tests/P0009R18_mdspan/test.cpp index 6dd899092a..b653696e29 100644 --- a/tests/std/tests/P0009R18_mdspan/test.cpp +++ b/tests/std/tests/P0009R18_mdspan/test.cpp @@ -9,7 +9,6 @@ using namespace std; -#ifdef __cpp_lib_concepts // A type that's regular and trivially copyable, and also maximally nothrow. template using is_regular_trivial_nothrow = std::conjunction>, is_trivially_copyable, @@ -18,7 +17,6 @@ using is_regular_trivial_nothrow = std::conjunction inline constexpr bool is_regular_trivial_nothrow_v = is_regular_trivial_nothrow::value; -#endif // __cpp_lib_concepts struct Constructible { // noexcept constructible for size_t, but not convertible @@ -46,13 +44,11 @@ struct ConstructibleAndConvertibleConst { void extent_tests_traits() { -#ifdef __cpp_lib_concepts static_assert(is_regular_trivial_nothrow_v>); static_assert(is_regular_trivial_nothrow_v>); static_assert(is_regular_trivial_nothrow_v>); static_assert(is_regular_trivial_nothrow_v>); static_assert(is_regular_trivial_nothrow_v>); -#endif // __cpp_lib_concepts static_assert(is_same_v, extents>); static_assert(is_same_v, extents>); @@ -438,12 +434,10 @@ void TestMapping(const Mapping& map) { } void layout_left_tests_traits() { -#ifdef __cpp_lib_concepts static_assert(is_regular_trivial_nothrow_v>>); static_assert(is_regular_trivial_nothrow_v>>); static_assert(is_regular_trivial_nothrow_v>>); static_assert(is_regular_trivial_nothrow_v>>); -#endif // __cpp_lib_concepts using E = extents; static_assert(is_same_v::extents_type, E>); @@ -454,12 +448,10 @@ void layout_left_tests_traits() { } void layout_right_tests_traits() { -#ifdef __cpp_lib_concepts static_assert(is_regular_trivial_nothrow_v>>); static_assert(is_regular_trivial_nothrow_v>>); static_assert(is_regular_trivial_nothrow_v>>); static_assert(is_regular_trivial_nothrow_v>>); -#endif // __cpp_lib_concepts using E = extents; static_assert(is_same_v::extents_type, E>); @@ -470,13 +462,11 @@ void layout_right_tests_traits() { } void layout_stride_tests_traits() { -#ifdef __cpp_lib_concepts static_assert(is_regular_trivial_nothrow_v>>); static_assert(is_regular_trivial_nothrow_v>>); static_assert(is_regular_trivial_nothrow_v>>); static_assert( is_regular_trivial_nothrow_v>>); -#endif // __cpp_lib_concepts using E = extents; static_assert(is_same_v::extents_type, E>); From 5c2e59b5507e2547ec5b399cdcd22498f2a19f3d Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sun, 12 Mar 2023 20:26:19 -0700 Subject: [PATCH 05/57] ``: Add `_EXPORT_STD`, tiny cleanups (#3563) --- stl/inc/mdspan | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index ba2207ff25..669ba378ec 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -117,6 +117,7 @@ struct _Mdspan_extent_type<_IndexType, 0> { } }; +_EXPORT_STD template class extents : private _Mdspan_extent_type<_IndexType, ((_Extents == dynamic_extent) + ... + 0), _Extents...> { public: @@ -186,14 +187,14 @@ public: && is_nothrow_constructible_v && (_Size == rank_dynamic() || _Size == rank()) constexpr explicit(_Size != rank_dynamic()) extents(span<_OtherIndexType, _Size> _Exts) noexcept - : _Mybase{_Exts, _STD make_index_sequence{}} {} + : _Mybase{_Exts, make_index_sequence{}} {} template requires is_convertible_v && is_nothrow_constructible_v && (_Size == rank_dynamic() || _Size == rank()) constexpr explicit(_Size != rank_dynamic()) extents(const array<_OtherIndexType, _Size>& _Exts) noexcept - : _Mybase{span{_Exts}, _STD make_index_sequence{}} {} + : _Mybase{span{_Exts}, make_index_sequence{}} {} template _NODISCARD_FRIEND constexpr bool operator==( @@ -231,6 +232,7 @@ public: } }; +_EXPORT_STD template using dextents = decltype([](const _IndexType2, const index_sequence<_Seq...>) constexpr { @@ -267,16 +269,19 @@ template inline constexpr bool _Is_mapping_of = is_same_v, _Mapping>; +_EXPORT_STD struct layout_left { template class mapping; }; +_EXPORT_STD struct layout_right { template class mapping; }; +_EXPORT_STD struct layout_stride { template class mapping; @@ -494,10 +499,6 @@ public: private: _Extents _Myext{}; - static constexpr size_t _Multiply(size_t _X, size_t _Y) { - return _X * _Y; - } - template constexpr index_type _Index_impl(_IndexType... _Idx, index_sequence<_Seq...>) const noexcept { index_type _Result = 0; @@ -685,6 +686,7 @@ private: } }; +_EXPORT_STD template struct default_accessor { using offset_policy = default_accessor; @@ -709,6 +711,7 @@ struct default_accessor { } }; +_EXPORT_STD template > class mdspan { From 187a036681484dc81501e76bfcbb4bf9b0ed0c46 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 14 Mar 2023 01:36:41 +0100 Subject: [PATCH 06/57] ``: Use concepts everywhere and add `mdspan` deduction guides (#3564) --- stl/inc/mdspan | 264 +++++++++++++---------- tests/std/tests/P0009R18_mdspan/test.cpp | 1 + 2 files changed, 146 insertions(+), 119 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 669ba378ec..5bc6c74f0f 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -11,11 +11,8 @@ #if !_HAS_CXX23 || !defined(__cpp_lib_concepts) // TRANSITION, GH-395 _EMIT_STL_WARNING(STL4038, "The contents of are available only with C++23 or later."); #else // ^^^ not supported / supported language mode vvv -#include #include #include -#include -#include #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) @@ -251,20 +248,6 @@ inline constexpr bool _Is_extents = false; template inline constexpr bool _Is_extents> = true; -template -struct _Layout_mapping_alike_helper : false_type {}; - -template -struct _Layout_mapping_alike_helper<_Mapping, - void_t, - is_same, - is_same, bool_constant<_Mapping::is_always_strided()>, - bool_constant<_Mapping::is_always_exhaustive()>, bool_constant<_Mapping::is_always_unique()>>> - : bool_constant<_Is_extents> {}; - -template -struct _Layout_mapping_alike : bool_constant<_Layout_mapping_alike_helper<_Mapping>::value> {}; - template inline constexpr bool _Is_mapping_of = is_same_v, _Mapping>; @@ -307,18 +290,20 @@ public: constexpr mapping(const _Extents& _Ext) noexcept : _Myext(_Ext) {} - template , int> = 0> + template + requires is_constructible_v<_Extents, _OtherExtents> constexpr explicit(!is_convertible_v<_OtherExtents, _Extents>) mapping(const mapping<_OtherExtents>& _Other) noexcept : _Myext{_Other.extents()} {} - template , int> = 0> + template + requires (_Extents::rank() <= 1) && is_constructible_v<_Extents, _OtherExtents> constexpr explicit(!is_convertible_v<_OtherExtents, _Extents>) mapping(const layout_right::mapping<_OtherExtents>& _Other) noexcept : _Myext{_Other.extents()} {} - template , int> = 0> + template + requires is_constructible_v<_Extents, _OtherExtents> constexpr explicit(_Extents::rank() > 0) mapping(const layout_stride::template mapping<_OtherExtents>& _Other) : _Myext{_Other.extents()} {} @@ -337,10 +322,9 @@ public: return _Result; } - template && ...) - && (is_nothrow_constructible_v && ...), - int> = 0> + 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<_Extents::rank()>{}); @@ -370,8 +354,9 @@ public: return true; } - template 0), int> = 0> - _NODISCARD constexpr index_type stride(const rank_type _Rank) const noexcept { + _NODISCARD constexpr index_type stride(const rank_type _Rank) const noexcept + requires (extents_type::rank() > 0) + { index_type _Result = 1; for (rank_type _Dim = 0; _Dim < _Rank; ++_Dim) { _Result *= _Myext.extent(_Dim); @@ -381,6 +366,7 @@ public: } 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(); } @@ -418,18 +404,20 @@ public: constexpr mapping(const _Extents& _Ext) noexcept : _Myext(_Ext) {} - template , int> = 0> + template + requires is_constructible_v constexpr explicit(!is_convertible_v<_OtherExtents, _Extents>) mapping(const mapping<_OtherExtents>& _Other) noexcept : _Myext{_Other.extents()} {} - template , int> = 0> + template + requires (extents_type::rank() <= 1) && is_constructible_v constexpr explicit(!is_convertible_v<_OtherExtents, _Extents>) mapping(const layout_left::mapping<_OtherExtents>& _Other) noexcept : _Myext{_Other.extents()} {} - template , int> = 0> + template + requires is_constructible_v constexpr explicit(_Extents::rank() > 0) mapping(const layout_stride::template mapping<_OtherExtents>& _Other) : _Myext{_Other.extents()} {} @@ -448,10 +436,9 @@ public: return _Result; } - template && ...) - && (is_nothrow_constructible_v && ...), - int> = 0> + 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<_Extents::rank()>{}); @@ -481,8 +468,9 @@ public: return true; } - template 0), int> = 0> - _NODISCARD constexpr index_type stride(const rank_type _Rank) const noexcept { + _NODISCARD constexpr index_type stride(const rank_type _Rank) const noexcept + requires (extents_type::rank() > 0) + { index_type _Result = 1; for (rank_type _Dim = _Rank + 1; _Dim < _Extents::rank(); ++_Dim) { _Result *= _Myext.extent(_Dim); @@ -492,6 +480,7 @@ public: } 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(); } @@ -507,6 +496,17 @@ private: } }; +template +concept _Layout_mapping_alike = requires { + requires _Is_extents; + { _Mp::is_always_strided() } -> same_as; + { _Mp::is_always_exhaustive() } -> same_as; + { _Mp::is_always_unique() } -> same_as; + bool_constant<_Mp::is_always_strided()>::value; + bool_constant<_Mp::is_always_exhaustive()>::value; + bool_constant<_Mp::is_always_unique()>::value; + }; + template class layout_stride::mapping { public: @@ -525,38 +525,45 @@ public: constexpr mapping() noexcept = default; constexpr mapping(const mapping&) noexcept = default; - template , int> = 0> - constexpr mapping(const _Extents& _E_, const array<_OtherIndexType, _Extents::rank()>& _S_) noexcept : _Myext{_E_} { +#ifndef __clang__ // TRANSITION, MSVC messes up CTAD when concepts are used here (needs further investigation) + template + && is_nothrow_constructible_v, + int> = 0> +#else // ^^^ workaround / no workaround vvv + template + requires is_convertible_v + && is_nothrow_constructible_v +#endif // ^^^ no workaround ^^^ + constexpr mapping(const _Extents& _E_, const span<_OtherIndexType, _Extents::rank()> _S_) noexcept : _Myext{_E_} { for (rank_type _Idx = 0; _Idx < _Extents::rank(); ++_Idx) { _Mystrides[_Idx] = _S_[_Idx]; } }; - template , int> = 0> - constexpr mapping(const _Extents& _E_, const span<_OtherIndexType, _Extents::rank()> _S_) noexcept : _Myext{_E_} { +#ifndef __clang__ // TRANSITION, MSVC messes up CTAD when concepts are used here (needs further investigation) + template + && is_nothrow_constructible_v, + int> = 0> +#else // ^^^ workaround / no workaround vvv + template + requires is_convertible_v + && is_nothrow_constructible_v +#endif // ^^^ no workaround ^^^ + constexpr mapping(const extents_type& _E_, const array<_OtherIndexType, extents_type::rank()>& _S_) noexcept + : _Myext{_E_} { for (rank_type _Idx = 0; _Idx < _Extents::rank(); ++_Idx) { _Mystrides[_Idx] = _S_[_Idx]; } }; - template , int> = 0> - constexpr explicit(!is_convertible_v<_OtherExtents, _Extents>) - mapping(const mapping<_OtherExtents>& _Other) noexcept - : _Myext{_Other.extents()}, _Mystrides{_Other.strides()} { - for (rank_type _Idx = 0; _Idx < _Extents::rank(); ++_Idx) { - _Mystrides[_Idx] = _Other.stride(_Idx); - } - } - - template ::value - && is_constructible_v - && _StridedLayoutMapping::is_always_unique() && _StridedLayoutMapping::is_always_strided(), - int> = 0> - constexpr explicit( - !is_convertible_v + template + requires _Layout_mapping_alike<_StridedLayoutMapping> + && is_constructible_v + && (_StridedLayoutMapping::is_always_unique()) && (_StridedLayoutMapping::is_always_strided()) + constexpr explicit(!( + is_convertible_v && (_Is_mapping_of || _Is_mapping_of - || _Is_mapping_of) ) + || _Is_mapping_of) )) mapping(const _StridedLayoutMapping& _Other) noexcept : _Myext(_Other.extents()) { for (rank_type _Dim = 0; _Dim < _Extents::rank(); ++_Dim) { @@ -592,10 +599,9 @@ public: } } - template && ...) - && (is_nothrow_constructible_v && ...), - int> = 0> + 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<_Extents::rank()>{}); @@ -630,15 +636,13 @@ public: return true; } - template 0), int> = 0> _NODISCARD constexpr index_type stride(const rank_type _Idx) const noexcept { return _Mystrides[_Idx]; } - template ::value - && extents_type::rank() == _OtherMapping::extents_type::rank() - && _OtherMapping::is_always_strided(), - int> = 0> + template + requires _Layout_mapping_alike<_OtherMapping> && (extents_type::rank() == _OtherMapping::extents_type::rank()) + && (_OtherMapping::is_always_strided()) _NODISCARD_FRIEND constexpr bool operator==(const mapping& _Left, const _OtherMapping& _Right) noexcept { if (_Left.extents() != _Right.extents()) { return false; @@ -696,10 +700,8 @@ struct default_accessor { constexpr default_accessor() noexcept = default; - template ::element_type (*)[], _ElementType (*)[]>, - int> = 0> + template + requires is_convertible_v<_OtherElementType (*)[], element_type (*)[]> constexpr default_accessor(default_accessor<_OtherElementType>) noexcept {} _NODISCARD constexpr data_handle_type offset(data_handle_type _Ptr, size_t _Idx) const noexcept { @@ -752,64 +754,54 @@ public: return _Extents::static_extent(_Rank); } - template 0) && is_default_constructible_v - && is_default_constructible_v<_Mapping> && is_default_constructible_v, - int> = 0> - constexpr mdspan() {} + constexpr mdspan() + requires (rank_dynamic() > 0) && is_default_constructible_v + && is_default_constructible_v && is_default_constructible_v + {} constexpr mdspan(const mdspan&) = default; constexpr mdspan(mdspan&&) = default; - template && is_default_constructible_v, - int> = 0> - constexpr explicit mdspan(data_handle_type _Ptr_) : _Ptr{_STD move(_Ptr_)}, _Map{extents_type{}} {} - - template 0)) && (is_convertible_v<_OtherIndexTypes, index_type> && ...) - && (is_nothrow_constructible_v && ...) - //&& (sizeof...(_OtherIndexTypes) == rank() || sizeof...(_OtherIndexTypes) == rank_dynamic()) - && is_constructible_v && is_default_constructible_v, - int> = 0> + 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) : _Ptr{_STD move(_Ptr_)}, _Map{extents_type{static_cast(_STD move(_Exts))...}} {} - template - && is_nothrow_constructible_v - && (_Size == rank() || _Size == rank_dynamic()) - && is_constructible_v && is_default_constructible_v, - int> = 0> + template + requires is_convertible_v<_OtherIndexType, index_type> + && is_nothrow_constructible_v + && (_Size == rank() || _Size == rank_dynamic()) + && is_constructible_v && is_default_constructible_v constexpr explicit(_Size != rank_dynamic()) mdspan(data_handle_type _Ptr_, span<_OtherIndexType, _Size>& _Exts) : _Ptr{_Ptr_}, _Map{_Extents{_Exts}} {} - template - && is_nothrow_constructible_v - && (_Size == rank() || _Size == rank_dynamic()) - && is_constructible_v && is_default_constructible_v, - int> = 0> + template + requires is_convertible_v + && is_nothrow_constructible_v + && (_Size == rank() || _Size == rank_dynamic()) + && is_constructible_v && is_default_constructible_v constexpr explicit(_Size != rank_dynamic()) mdspan(data_handle_type _Ptr_, const array<_OtherIndexType, _Size>& _Exts) : _Ptr{_Ptr_}, _Map{_Extents{_Exts}} {} - template && is_default_constructible_v, int> = 0> - constexpr mdspan(data_handle_type _Ptr_, const _Extents& _Ext) : _Ptr{_Ptr_}, _Map{_Ext} {} + constexpr mdspan(data_handle_type _Ptr_, const extents_type& _Ext) + requires is_constructible_v && is_default_constructible_v + : _Ptr{_Ptr_}, _Map{_Ext} {} - template , int> = 0> - constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_) : _Ptr{_Ptr_}, _Map{_Map_} {} + constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_) + requires is_default_constructible_v + : _Ptr{_Ptr_}, _Map{_Map_} {} constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_, const accessor_type& _Acc_) : _Ptr{_Ptr_}, _Map{_Map_}, _Acc{_Acc_} {} - template &> - && is_constructible_v, - int> = 0> + template + requires is_constructible_v&> + && is_constructible_v constexpr explicit( !is_convertible_v&, mapping_type> || !is_convertible_v) @@ -825,22 +817,25 @@ public: constexpr mdspan& operator=(const mdspan&) = default; constexpr mdspan& operator=(mdspan&&) = default; - // TRANSITION operator[](const _OtherIndexTypes... _Indices) - template && ...) - && (is_nothrow_constructible_v && ...), - /*&& sizeof...(_OtherIndexTypes) == rank(),*/ - int> = 0> + // TRANSITION, P2128R6 (Multidimensional subscript operator) + template + 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))...)); } - template , int> = 0> + template + requires is_convertible_v + && is_nothrow_constructible_v _NODISCARD constexpr reference operator[](span<_OtherIndexType, rank()> _Indices) const { return _Index_impl(_Indices, make_index_sequence{}); } - template , int> = 0> + template + requires is_convertible_v + && is_nothrow_constructible_v _NODISCARD constexpr reference operator[](const array<_OtherIndexType, rank()>& _Indices) const { return _Index_impl(_Indices, make_index_sequence{}); } @@ -930,12 +925,43 @@ private: accessor_type _Acc{}; }; +template + requires (is_array_v<_CArray> && rank_v<_CArray> == 1) +mdspan(_CArray&) -> mdspan, extents>>; + +template + requires (is_pointer_v>) +mdspan(_Pointer&&) -> mdspan>, extents>; + +template + requires ((is_convertible_v<_Integrals, size_t> && ...) && sizeof...(_Integrals) > 0) +explicit mdspan(_ElementType*, _Integrals...) -> mdspan<_ElementType, dextents>; + +template +mdspan(_ElementType*, span<_OtherIndexType, _Nx>) -> mdspan<_ElementType, dextents>; + +template +mdspan(_ElementType*, const array<_OtherIndexType, _Nx>&) -> mdspan<_ElementType, dextents>; + +template +mdspan(_ElementType*, const extents<_IndexType, _ExtentsPack...>&) + -> mdspan<_ElementType, extents<_IndexType, _ExtentsPack...>>; + +template +mdspan(_ElementType*, const _MappingType&) + -> mdspan<_ElementType, typename _MappingType::extents_type, typename _MappingType::layout_type>; + +template +mdspan(const typename _AccessorType::data_handle_type&, const _MappingType&, const _AccessorType&) + -> mdspan; + _STD_END #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) #pragma pack(pop) -#endif // _HAS_CXX23 +#endif // ^^^ supported language mode ^^^ #endif // _STL_COMPILER_PREPROCESSOR #endif // _MDSPAN_ diff --git a/tests/std/tests/P0009R18_mdspan/test.cpp b/tests/std/tests/P0009R18_mdspan/test.cpp index b653696e29..6e0ad59568 100644 --- a/tests/std/tests/P0009R18_mdspan/test.cpp +++ b/tests/std/tests/P0009R18_mdspan/test.cpp @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#include #include #include #include From 404605b40c39d19ef5ff98175204731d8b24e1dc Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Fri, 17 Mar 2023 22:55:10 +0100 Subject: [PATCH 07/57] ``: Add separate tests for `extents` and `default_accessor` (#3580) --- stl/inc/mdspan | 3 +- tests/std/test.lst | 2 + .../P0009R18_mdspan_default_accessor/env.lst | 4 + .../P0009R18_mdspan_default_accessor/test.cpp | 66 ++++ .../std/tests/P0009R18_mdspan_extents/env.lst | 4 + .../tests/P0009R18_mdspan_extents/test.cpp | 295 ++++++++++++++++++ 6 files changed, 373 insertions(+), 1 deletion(-) create mode 100644 tests/std/tests/P0009R18_mdspan_default_accessor/env.lst create mode 100644 tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp create mode 100644 tests/std/tests/P0009R18_mdspan_extents/env.lst create mode 100644 tests/std/tests/P0009R18_mdspan_extents/test.cpp diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 5bc6c74f0f..adbe8e4891 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -13,6 +13,7 @@ _EMIT_STL_WARNING(STL4038, "The contents of are available only with C++ #else // ^^^ not supported / supported language mode vvv #include #include +#include #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) @@ -201,7 +202,7 @@ public: } for (size_t _Dim = 0; _Dim < sizeof...(_Extents); ++_Dim) { - if (_Left.extent(_Dim) != _Right.extent(_Dim)) { + if (_STD cmp_not_equal(_Left.extent(_Dim), _Right.extent(_Dim))) { return false; } } diff --git a/tests/std/test.lst b/tests/std/test.lst index a70c5617cb..b6b81ee110 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -228,6 +228,8 @@ tests\LWG3480_directory_iterator_range tests\LWG3545_pointer_traits_sfinae tests\LWG3610_iota_view_size_and_integer_class tests\P0009R18_mdspan +tests\P0009R18_mdspan_default_accessor +tests\P0009R18_mdspan_extents tests\P0019R8_atomic_ref tests\P0024R2_parallel_algorithms_adjacent_difference tests\P0024R2_parallel_algorithms_adjacent_find diff --git a/tests/std/tests/P0009R18_mdspan_default_accessor/env.lst b/tests/std/tests/P0009R18_mdspan_default_accessor/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_default_accessor/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_default_accessor/test.cpp b/tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp new file mode 100644 index 0000000000..21379295e7 --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +template +constexpr void test_one(array elems) { + using Accessor = default_accessor; + + // Check modeled concepts + static_assert(is_nothrow_move_constructible_v); + static_assert(is_nothrow_move_assignable_v); + static_assert(is_nothrow_swappable_v); + static_assert(is_trivially_copyable_v); + static_assert(semiregular); + + // Check nested types + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + + // Check default constructor + Accessor accessor; + static_assert(is_nothrow_default_constructible_v); + + { // Check converting constructor from other accessor + [[maybe_unused]] default_accessor const_accessor = accessor; + static_assert(is_nothrow_constructible_v, Accessor>); + static_assert(!is_constructible_v>); + } + + { // Check 'access' member function + same_as decltype(auto) accessed_elem = accessor.access(elems.data(), 1); + assert(accessed_elem == elems[1]); + static_assert(noexcept(accessor.access(elems.data(), 0))); + } + + { // Check 'offset' member function + same_as auto ptr = accessor.offset(elems.data(), 1); + assert(ptr == elems.data() + 1); + static_assert(noexcept(accessor.offset(elems.data(), 0))); + } +} + +constexpr bool test() { + test_one({'a', 'b', 'c'}); + test_one({1, 2, 3}); + test_one({1.1, 2.2, 3.3}); + test_one({L"1", L"2", L"3"}); + test_one({3, 2, 1}); + return true; +} + +int main() { + static_assert(test()); + test(); +} diff --git a/tests/std/tests/P0009R18_mdspan_extents/env.lst b/tests/std/tests/P0009R18_mdspan_extents/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_extents/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_extents/test.cpp b/tests/std/tests/P0009R18_mdspan_extents/test.cpp new file mode 100644 index 0000000000..b73415b842 --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_extents/test.cpp @@ -0,0 +1,295 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#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}; + } +}; + +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 + +template +constexpr void do_check_members(index_sequence) { + using Ext = extents; + + // Each specialization of extents models regular and is trivially copyable + static_assert(regular); + static_assert(is_trivially_copyable_v); + + // Check member types + static_assert(same_as); + static_assert(same_as>); + static_assert(same_as); + + // Check static observers + static_assert(Ext::rank() == sizeof...(Extents)); + static_assert(Ext::rank_dynamic() == ((Extents == dynamic_extent) + ... + 0)); + static_assert(((Ext::static_extent(Indices) == Extents) && ...)); + + // Check noexceptness of static observers + static_assert(noexcept(Ext::rank())); + static_assert(noexcept(Ext::rank_dynamic())); + static_assert(noexcept(Ext::static_extent(0))); + + // Check default constructor + Ext ext; + static_assert(is_nothrow_default_constructible_v); + + // Check 'extent' observer + assert((((ext.extent(Indices) == Extents && Extents != dynamic_extent) || ext.extent(Indices) == 0) && ...)); + + using OtherIndexType = conditional_t, long long, unsigned long long>; + using Ext2 = extents; + + { // Check construction from other extents + Ext2 ext2{ext}; + assert(((ext.extent(Indices) == ext2.extent(Indices)) && ...)); + assert(ext == ext2); + static_assert(is_nothrow_constructible_v); + // Other tests are defined in 'check_construction_from_other_extents' function + } + + { // Check construction from extents pack + Ext2 ext2{ext.extent(Indices)...}; + assert(((ext.extent(Indices) == ext2.extent(Indices)) && ...)); + assert(ext == ext2); + static_assert(is_nothrow_constructible_v); + // Other tests are defined in 'check_construction_from_extents_pack' function + } + + { // Check construction from array and span + auto arr = to_array({ext.extent(Indices)...}); + Ext2 ext2a{arr}; + assert(((ext.extent(Indices) == ext2a.extent(Indices)) && ...)); + assert(ext == ext2a); + static_assert(is_nothrow_constructible_v); + + span s{arr}; + Ext2 ext2b{s}; + assert(((ext.extent(Indices) == ext2b.extent(Indices)) && ...)); + assert(ext == ext2b); + static_assert(is_nothrow_constructible_v); + // Other tests are defined in 'check_construction_from_array_and_span' function + } +} + +template +constexpr void check_members() { + do_check_members(make_index_sequence{}); +} + +constexpr void check_construction_from_other_extents() { + { // Check construction from too big or too small other extents + using Ext = extents; + static_assert(!is_constructible_v); + static_assert(!is_constructible_v>); + } + + { // Check construction with different values + static_assert(is_nothrow_constructible_v, extents>); + static_assert(is_nothrow_constructible_v, extents>); + static_assert(is_nothrow_constructible_v, extents>); + static_assert(is_nothrow_constructible_v, extents>); + static_assert(!is_constructible_v, extents>); + } + + { // Check postconditions + extents ext{4, 4}; + extents ext2{ext}; + assert(ext == ext2); + assert(ext2.extent(0) == 4); + assert(ext2.extent(1) == 4); + + extents ext3{ext}; + assert(ext == ext3); + assert(ext3.extent(0) == 4); + assert(ext3.extent(1) == 4); + } + + { // Check implicit conversions + static_assert(!NotImplicitlyConstructibleFrom, extents>); + static_assert(NotImplicitlyConstructibleFrom, extents>); + static_assert(NotImplicitlyConstructibleFrom, extents>); + static_assert(NotImplicitlyConstructibleFrom, extents>); + } +} + +constexpr void check_construction_from_extents_pack() { + { // Check construction from various types + using Ext = extents; + static_assert(is_nothrow_constructible_v); + static_assert(!is_constructible_v); + static_assert(is_nothrow_constructible_v); + } + + { // Check construction from types (not) convertible to index_type + using Ext = extents; + static_assert(is_nothrow_constructible_v>); + static_assert(!is_constructible_v); + static_assert(is_nothrow_constructible_v>); + static_assert(!is_constructible_v); + } + + { // Check construction from types that may throw during conversion to index_type + using Ext = extents; + static_assert(!is_constructible_v>); + static_assert(!is_constructible_v>); + } + +#if 0 // FIXME Bug in array/span constructor? + { // Check postconditions [FIXME] + using Ext = extents; + array arr = {4, 4, 4}; + Ext ext{arr}; + Ext ext2{4, 4, 4}; + assert(ext == ext2); + } +#endif // Bug? + + { // Check implicit conversions + static_assert(NotImplicitlyConstructibleFrom, unsigned long long>); + static_assert(NotImplicitlyConstructibleFrom, long, long>); + static_assert(NotImplicitlyConstructibleFrom, char, signed char, unsigned char>); + } +} + +constexpr void check_construction_from_array_and_span() { + { // Check construction from arrays/spans with elements (not) convertible to index_type + using Ext = extents; + + array arr1 = {4, 5}; + Ext ext1a{arr1}; + span s1{arr1}; + Ext ext1b{s1}; + assert(ext1a == ext1b); + static_assert(is_nothrow_constructible_v); + static_assert(is_nothrow_constructible_v); + + array, 2> arr2; + Ext ext2a{arr2}; + span s2{arr2}; + Ext ext2b{s2}; + assert(ext2a == ext2b); + static_assert(is_nothrow_constructible_v); + static_assert(is_nothrow_constructible_v); + + static_assert(!is_constructible_v>); + static_assert(!is_constructible_v>); + } + + { // Check construction from arrays/spans with elements that may throw during conversion to index_type + using Ext = extents; + static_assert(!is_constructible_v, 2>>); + static_assert(!is_constructible_v, 2>>); + } + + { // Check construction from arrays/spans with invalid size + using Ext = extents; + static_assert(!is_constructible_v>); + static_assert(!is_constructible_v>); + static_assert(!is_constructible_v>); + static_assert(!is_constructible_v>); + static_assert(!is_constructible_v>); + static_assert(!is_constructible_v>); + } + + { // Check implicit conversions + static_assert(!NotImplicitlyConstructibleFrom, array>); + static_assert(NotImplicitlyConstructibleFrom, array>); + static_assert(!NotImplicitlyConstructibleFrom, span>); + static_assert(NotImplicitlyConstructibleFrom, span>); + } +} + +constexpr void check_equality_operator() { + { // All extents are static + extents e1; + extents e2; + extents e3; + assert(e1 != e2); + assert(e2 != e3); + assert(e1 == e3); + } + + { // Some extents are static, some dynamic + extents e1{1}; + extents e2{2}; + extents e3{3}; + assert(e1 != e2); + assert(e2 == e3); + assert(e1 != e2); + } + + { // All extents are dynamic + dextents e1{1, 2}; + dextents e2{1, 2}; + dextents e3{1, 3}; + assert(e1 == e2); + assert(e2 != e3); + assert(e1 != e3); + } +} + +constexpr bool test() { + // check_members(); // FIXME Definitely a bug. + check_members(); + check_members(); + // check_members(); // FIXME Bug in array/span constructor? + check_members(); + check_construction_from_other_extents(); + check_construction_from_extents_pack(); + check_construction_from_array_and_span(); + check_equality_operator(); + return true; +} + +template +constexpr bool all_extents_dynamic = false; + +template +constexpr bool all_extents_dynamic, ExpectedRank> = + ((Extents == dynamic_extent) && ...) && (sizeof...(Extents) == ExpectedRank); + +template +concept CanDeduceExtents = requires(Args&&... args) { extents{forward(args)...}; }; + +// Check deduction guide +using DG = decltype(extents{'1', 2, 3u, 4ll, ConvertibleToInt{}}); +static_assert(all_extents_dynamic); +static_assert(same_as); +static_assert(!CanDeduceExtents); + +// Check dextents +static_assert(all_extents_dynamic, 0>); +static_assert(all_extents_dynamic, 2>); +static_assert(all_extents_dynamic, 3>); +static_assert(all_extents_dynamic, 5>); + +int main() { + static_assert(test()); + test(); +} From b0a6a97272f0ac01873ce83da7ad9a6645e2db2c Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Fri, 24 Mar 2023 22:45:25 +0100 Subject: [PATCH 08/57] ``: Completely rework `std::extents` (#3586) --- stl/inc/mdspan | 300 ++++++++++-------- tests/std/test.lst | 1 + tests/std/tests/P0009R18_mdspan/test.cpp | 47 +-- .../tests/P0009R18_mdspan_extents/test.cpp | 87 ++++- .../P0009R18_mdspan_extents_death/env.lst | 4 + .../P0009R18_mdspan_extents_death/test.cpp | 92 ++++++ 6 files changed, 346 insertions(+), 185 deletions(-) create mode 100644 tests/std/tests/P0009R18_mdspan_extents_death/env.lst create mode 100644 tests/std/tests/P0009R18_mdspan_extents_death/test.cpp diff --git a/stl/inc/mdspan b/stl/inc/mdspan index adbe8e4891..236b217afa 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -24,135 +24,101 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN -template -struct _Mdspan_extent_type { +_EXPORT_STD template +class extents { +public: using index_type = _IndexType; + using size_type = make_unsigned_t; + using rank_type = size_t; + + _NODISCARD static constexpr rank_type rank() noexcept { + return sizeof...(_Extents); + } + + static_assert(_Is_standard_integer, + "IndexType must be a signed or unsigned integer type (N4928 [mdspan.extents.overview]/1.1)."); + static_assert(((_Extents == dynamic_extent || _STD in_range(_Extents)) && ...), + "Each element of Extents must be either equal to dynamic_extent, or must be representable as a value of type " + "IndexType (N4928 [mdspan.extents.overview]/1.2)."); - _NODISCARD static constexpr auto _Get_dynamic_indices() noexcept { // TRANSITION consteval? - array _Result; - size_t _Counter = 0; - for (size_t _Ix = 0; _Ix < sizeof...(_Extents); ++_Ix) { - _Result[_Ix] = _Counter; - if (_Static_extents[_Ix] == dynamic_extent) { +private: + _NODISCARD static _CONSTEVAL auto _Make_dynamic_indices() noexcept { +#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 + array _Result{}; + rank_type _Counter = 0; + for (rank_type _Idx = 0; _Idx < rank(); ++_Idx) { + _Result[_Idx] = _Counter; + if (_Static_extents[_Idx] == dynamic_extent) { ++_Counter; } } + _Result[rank()] = _Counter; return _Result; +#pragma warning(pop) // TRANSITION, "/analyze:only" BUG? } - index_type _Dynamic_extents[_Rank_dynamic] = {}; - static constexpr size_t _Static_extents[sizeof...(_Extents)] = {_Extents...}; - static constexpr array _Dynamic_indices = _Get_dynamic_indices(); + static constexpr array _Static_extents = {_Extents...}; + static constexpr array _Dynamic_indices = _Make_dynamic_indices(); - constexpr _Mdspan_extent_type() noexcept = default; - - template - requires (sizeof...(_OtherIndexTypes) == _Rank_dynamic) && (is_same_v<_OtherIndexTypes, index_type> && ...) - constexpr _Mdspan_extent_type(_OtherIndexTypes... _OtherExtents) noexcept : _Dynamic_extents{_OtherExtents...} {} - - template - requires (_Size == _Rank_dynamic) - constexpr _Mdspan_extent_type(span<_OtherIndexType, _Size> _Data, index_sequence<_Idx...>) noexcept - : _Dynamic_extents{static_cast(_STD as_const(_Data[_Idx]))...} {} - - template - requires (sizeof...(_OtherIndexTypes) == sizeof...(_Extents)) && (sizeof...(_Extents) != _Rank_dynamic) - && (is_same_v<_OtherIndexTypes, index_type> && ...) - constexpr _Mdspan_extent_type(_OtherIndexTypes... _OtherExtents) noexcept { - auto _It = _Dynamic_extents; - ((_Extents == dynamic_extent ? void(*_It++ = _OtherExtents) : void(_OtherExtents)), ...); + _NODISCARD static constexpr rank_type _Dynamic_index(rank_type _Idx) noexcept { + return _Dynamic_indices[_Idx]; } - template - requires (_Size == sizeof...(_Extents)) && (sizeof...(_Extents) != _Rank_dynamic) - constexpr _Mdspan_extent_type(span<_OtherIndexType, _Size> _Data, index_sequence<_Idx...>) noexcept - : _Dynamic_extents{{static_cast(_STD as_const(_Data[_Dynamic_indices[_Idx]]))...}} {} - - constexpr index_type* _Begin_dynamic_extents() noexcept { - return _Dynamic_extents; - } - - constexpr const index_type* _Begin_dynamic_extents() const noexcept { - return _Dynamic_extents; + _NODISCARD static _CONSTEVAL auto _Make_dynamic_indices_inv() noexcept { + array _Result{}; + for (rank_type _Idx = 0; _Idx < rank(); ++_Idx) { + for (rank_type _Rdx = 0; _Rdx < rank(); ++_Rdx) { + if (_Dynamic_index(_Rdx + 1) == _Idx + 1) { + _Result[_Idx] = _Rdx; + break; + } + } + } + return _Result; } -}; - -template -struct _Mdspan_extent_type<_IndexType, 0, _Extents...> { - using index_type = _IndexType; - - static constexpr size_t _Static_extents[sizeof...(_Extents)] = {_Extents...}; - constexpr _Mdspan_extent_type() noexcept = default; + static constexpr array _Dynamic_indices_inv = _Make_dynamic_indices_inv(); - template - requires (sizeof...(_IndexTypes) == sizeof...(_Extents)) - constexpr _Mdspan_extent_type(_IndexTypes... /*_OtherExtents*/) noexcept {} - - template - requires (_Size == sizeof...(_Extents)) || (_Size == 0) - constexpr _Mdspan_extent_type(span<_OtherIndexType, _Size>, index_sequence<_Idx...>) noexcept {} - - constexpr index_type* _Begin_dynamic_extents() noexcept { - return nullptr; + _NODISCARD static constexpr rank_type _Dynamic_index_inv(rank_type _Idx) noexcept { + return _Dynamic_indices_inv[_Idx]; } - constexpr const index_type* _Begin_dynamic_extents() const noexcept { - return nullptr; - } -}; + struct _Static_extents_only { + constexpr explicit _Static_extents_only() noexcept = default; -template -struct _Mdspan_extent_type<_IndexType, 0> { - using index_type = _IndexType; + template + constexpr explicit _Static_extents_only(_Args&&...) noexcept {} - constexpr index_type* _Begin_dynamic_extents() { - return nullptr; - } + _NODISCARD constexpr index_type* begin() const noexcept { + return nullptr; + } + }; - constexpr const index_type* _Begin_dynamic_extents() const noexcept { - return nullptr; - } -}; + static constexpr rank_type _Rank_dynamic = _Dynamic_index(rank()); + conditional_t<_Rank_dynamic != 0, array, _Static_extents_only> _Dynamic_extents{}; -_EXPORT_STD -template -class extents : private _Mdspan_extent_type<_IndexType, ((_Extents == dynamic_extent) + ... + 0), _Extents...> { public: - using _Mybase = _Mdspan_extent_type<_IndexType, ((_Extents == dynamic_extent) + ... + 0), _Extents...>; - using index_type = typename _Mybase::index_type; - using size_type = make_unsigned_t; - using rank_type = size_t; - - static_assert(_Is_standard_integer<_IndexType>, - "IndexType must be a signed or unsigned integer type (N4928 [mdspan.extents.overview]/1.1)."); - static_assert(((_Extents == dynamic_extent || _STD in_range<_IndexType>(_Extents)) && ...), - "Each element of Extents must be either equal to dynamic_extent, or must be representable as a value of type " - "IndexType (N4928 [mdspan.extents.overview]/1.2)."); - - _NODISCARD static constexpr rank_type rank() noexcept { - return sizeof...(_Extents); - } - _NODISCARD static constexpr rank_type rank_dynamic() noexcept { - return ((_Extents == dynamic_extent) + ... + 0); + return _Rank_dynamic; } _NODISCARD static constexpr size_t static_extent(const rank_type _Idx) noexcept { - return _Mybase::_Static_extents[_Idx]; + _STL_VERIFY(_Idx < rank(), "Index must be less than rank() (N4928 [mdspan.extents.obs]/1)"); + return _Static_extents[_Idx]; } _NODISCARD constexpr index_type extent(const rank_type _Idx) const noexcept { + _STL_VERIFY(_Idx < rank(), "Index must be less than rank() (N4928 [mdspan.extents.obs]/3)"); if constexpr (rank_dynamic() == 0) { - return static_cast(_Mybase::_Static_extents[_Idx]); + return static_cast(static_extent(_Idx)); } else if constexpr (rank_dynamic() == rank()) { - return _Mybase::_Dynamic_extents[_Idx]; + return _Dynamic_extents[_Idx]; } else { - const auto _Static_extent = _Mybase::_Static_extents[_Idx]; - if (_Static_extent == dynamic_extent) { - return _Mybase::_Dynamic_extents[_Mybase::_Dynamic_indices[_Idx]]; + if (static_extent(_Idx) == dynamic_extent) { + return _Dynamic_extents[_Dynamic_index(_Idx)]; } else { - return static_cast(_Static_extent); + return static_cast(static_extent(_Idx)); } } } @@ -160,15 +126,24 @@ public: constexpr extents() noexcept = default; template - requires (sizeof...(_OtherExtents) == sizeof...(_Extents)) + 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 _Dynamic_it = _Mybase::_Begin_dynamic_extents(); - for (rank_type _Dim = 0; _Dim < sizeof...(_Extents); ++_Dim) { - if (_Mybase::_Static_extents[_Dim] == dynamic_extent) { - *_Dynamic_it++ = _Other.extent(_Dim); + auto _It = _Dynamic_extents.begin(); + for (rank_type _Idx = 0; _Idx < rank(); ++_Idx) { + _STL_VERIFY( + static_extent(_Idx) == dynamic_extent || _STD cmp_equal(static_extent(_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 " + "(N4928 [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 " + "(N4928 [mdspan.extents.cons]/2.2)"); + + if (static_extent(_Idx) == dynamic_extent) { + *_It = static_cast(_Other.extent(_Idx)); + ++_It; } } } @@ -177,51 +152,95 @@ public: requires (is_convertible_v<_OtherIndexTypes, index_type> && ...) && (is_nothrow_constructible_v && ...) && (sizeof...(_OtherIndexTypes) == rank_dynamic() || sizeof...(_OtherIndexTypes) == rank()) - constexpr explicit extents(_OtherIndexTypes... _Exts) noexcept - : _Mybase{static_cast(_STD move(_Exts))...} {} + constexpr explicit extents(_OtherIndexTypes... _Exts) noexcept { +#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 + 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 (N4928 [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_extent(_Idx) == dynamic_extent || _STD cmp_equal(static_extent(_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 " + "(N4928 [mdspan.extents.cons]/7.1)"); + if (static_extent(_Idx) == dynamic_extent) { + *_It = _Exts_arr[_Idx]; + ++_It; + } + } + } +#pragma warning(pop) // TRANSITION, "/analyze:only" BUG? + } + + 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]))...} { + 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 (N4928 [mdspan.extents.cons]/10.2)"); + } + } + } + + 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_index_inv(_Indices)]))...} { + if constexpr (_Is_standard_integer<_OtherIndexType>) { + for (rank_type _Idx = 0; _Idx < rank(); ++_Idx) { + _STL_VERIFY(static_extent(_Idx) == dynamic_extent || _STD cmp_equal(static_extent(_Idx), _Exts[_Idx]), + "Value of exts[r] must be equal to extent(r) for each r for which extent(r) is a static extent " + "(N4928 [mdspan.extents.cons]/10.1)"); + _STL_VERIFY(_Exts[_Idx] >= 0 && _STD in_range(_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 (N4928 [mdspan.extents.cons]/10.2)"); + } + } + } template requires is_convertible_v && is_nothrow_constructible_v && (_Size == rank_dynamic() || _Size == rank()) constexpr explicit(_Size != rank_dynamic()) extents(span<_OtherIndexType, _Size> _Exts) noexcept - : _Mybase{_Exts, make_index_sequence{}} {} + : extents(_Exts, make_index_sequence{}) {} template requires is_convertible_v && is_nothrow_constructible_v && (_Size == rank_dynamic() || _Size == rank()) constexpr explicit(_Size != rank_dynamic()) extents(const array<_OtherIndexType, _Size>& _Exts) noexcept - : _Mybase{span{_Exts}, make_index_sequence{}} {} + : extents(span{_Exts}, make_index_sequence{}) {} template _NODISCARD_FRIEND constexpr bool operator==( const extents& _Left, const extents<_OtherIndexType, _OtherExtents...>& _Right) noexcept { - if constexpr (sizeof...(_Extents) != sizeof...(_OtherExtents)) { + if constexpr (rank() != sizeof...(_OtherExtents)) { return false; - } - - for (size_t _Dim = 0; _Dim < sizeof...(_Extents); ++_Dim) { - if (_STD cmp_not_equal(_Left.extent(_Dim), _Right.extent(_Dim))) { - return false; - } - } - - return true; - } - - constexpr void _Fill_extents(index_type* _Out) const noexcept { - auto _Dynamic_it = _Mybase::_Begin_dynamic_extents(); - for (size_t _Dim = 0; _Dim < sizeof...(_Extents); ++_Dim) { - if (_Mybase::_Static_extents[_Dim] == dynamic_extent) { - *_Out++ = *_Dynamic_it++; - } else { - *_Out++ = static_cast(_Mybase::_Static_extents[_Dim]); + } else { + for (rank_type _Idx = 0; _Idx < rank(); ++_Idx) { + if (_STD cmp_not_equal(_Left.extent(_Idx), _Right.extent(_Idx))) { + return false; + } } + return true; } } - _NODISCARD static constexpr bool _Is_index_space_size_representable() { + _NODISCARD static _CONSTEVAL bool _Is_index_space_size_representable() { if constexpr (rank_dynamic() == 0 && rank() > 0) { return _STD in_range((_Extents * ...)); } else { @@ -230,20 +249,29 @@ public: } }; -_EXPORT_STD -template -using dextents = - decltype([](const _IndexType2, const index_sequence<_Seq...>) constexpr { - return extents<_IndexType2, ((void) _Seq, dynamic_extent)...>{}; - }(_IndexType{0}, make_index_sequence<_Rank>{})); - -// TRANSITION: why not `((void) _Ext, dynamic_extent)...`?! +#if defined(__clang__) || defined(__EDG__) // TRANSITION, REQUIRES REPORT (ICE) template requires (is_convertible_v<_Integrals, size_t> && ...) -extents(_Integrals... _Ext) +extents(_Integrals... _Exts) -> extents; +#else // ^^^ no workaround / workaround vvv +template + requires (is_convertible_v<_Integrals, size_t> && ...) +extents(_Integrals...) -> extents, _Integrals>::value...>; +#endif // ^^^ workaround ^^^ + +template +struct _Dextents_impl; + +template +struct _Dextents_impl<_IndexType, index_sequence<_Indices...>> { + using type = extents<_IndexType, ((void) _Indices, dynamic_extent)...>; +}; + +_EXPORT_STD template +using dextents = typename _Dextents_impl<_IndexType, make_index_sequence<_Rank>>::type; -template +template inline constexpr bool _Is_extents = false; template diff --git a/tests/std/test.lst b/tests/std/test.lst index b6b81ee110..fb4b5f3f4d 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -230,6 +230,7 @@ tests\LWG3610_iota_view_size_and_integer_class tests\P0009R18_mdspan tests\P0009R18_mdspan_default_accessor tests\P0009R18_mdspan_extents +tests\P0009R18_mdspan_extents_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/test.cpp b/tests/std/tests/P0009R18_mdspan/test.cpp index 6e0ad59568..91ed592d09 100644 --- a/tests/std/tests/P0009R18_mdspan/test.cpp +++ b/tests/std/tests/P0009R18_mdspan/test.cpp @@ -32,14 +32,14 @@ struct Convertible { struct ConstructibleAndConvertible { // convertible and noexcept constructible constexpr operator size_t() noexcept { - return size_t{0}; + return size_t{2}; }; }; struct ConstructibleAndConvertibleConst { // convertible and noexcept constructible constexpr operator size_t() const noexcept { - return size_t{0}; + return size_t{2}; }; }; @@ -138,21 +138,16 @@ void extent_tests_extent() { void extent_tests_ctor_other_sizes() { static_assert(!is_constructible_v, Constructible>); static_assert(!is_constructible_v, Convertible>); - // static_assert(is_constructible_v, ConstructibleAndConvertible>); - constexpr extents ex0{ConstructibleAndConvertible{}}; - // static_assert(is_constructible_v, ConstructibleAndConvertibleConst>); - constexpr extents ex1{ConstructibleAndConvertibleConst{}}; + static_assert(is_constructible_v, ConstructibleAndConvertible>); + [[maybe_unused]] constexpr extents ex0{ConstructibleAndConvertible{}}; + static_assert(is_constructible_v, ConstructibleAndConvertibleConst>); + [[maybe_unused]] constexpr extents ex1{ConstructibleAndConvertibleConst{}}; - // static_assert(is_constructible_v, int>); - constexpr extents ex2(1); + static_assert(is_constructible_v, int>); + [[maybe_unused]] constexpr extents ex2(1); static_assert(!is_constructible_v, int, int>); - // static_assert(is_constructible_v, int, int, int>); - extents ex3(1, 2, 3); - - (void) ex0; - (void) ex1; - (void) ex2; - (void) ex3; + static_assert(is_constructible_v, int, int, int>); + [[maybe_unused]] extents ex3(1, 2, 3); extents e0; assert(e0.extent(0) == 2); @@ -178,8 +173,8 @@ void extent_tests_copy_ctor_other() { static_assert(!is_constructible_v, extents>); // Static extents are constructible, but not convertible, from dynamic extents. - // static_assert(is_constructible_v, extents>); - constexpr extents ex0{extents{}}; + static_assert(is_constructible_v, extents>); + constexpr extents ex0{extents{3}}; (void) ex0; static_assert(!is_convertible_v, extents>); @@ -239,7 +234,7 @@ void extent_tests_ctor_array() { static_assert(is_constructible_v, array>); constexpr extents ex3{array{}}; static_assert(is_constructible_v, array>); - constexpr extents ex4{array{}}; + constexpr extents ex4{array{10}}; static_assert(!is_constructible_v, array>); (void) ex3; (void) ex4; @@ -919,14 +914,6 @@ void mdspan_tests_ctor_sizes() { static_assert((mds1.extents() == extents{})); static_assert(mds1.is_exhaustive()); - // TRANSITION: fix with concepts -> this is hard error per [mdspan.mdspan.overview]/2.2 - // static_assert(!is_constructible_v, int*, - // Pathological::Empty>); // Empty not convertible to size_type - - // TRANSITION: fix with concepts -> this is hard error per [mdspan.mdspan.overview]/2.2 - // static_assert(!is_constructible_v, int*, - // int>); // Pathological::Extents not constructible from int - static_assert(!is_constructible_v, Pathological::Layout>, int*, int>); // Pathological::Layout not constructible from extents @@ -941,14 +928,6 @@ void mdspan_tests_ctor_array() { static_assert(mds1.data_handle() == arr); static_assert(mds1.extents() == extents{}); - // TRANSITION: fix with concepts -> this is hard error per [mdspan.mdspan.overview]/2.2 - // static_assert(!is_constructible_v, int*, - // array>); // Empty not convertible to size_type - - // TRANSITION: fix with concepts -> this is hard error per [mdspan.mdspan.overview]/2.2 - // static_assert(!is_constructible_v, int*, - // array>); // Pathological::Extents not constructible from int - static_assert(!is_constructible_v, Pathological::Layout>, int*, array>); // Pathological::Layout not constructible from extents diff --git a/tests/std/tests/P0009R18_mdspan_extents/test.cpp b/tests/std/tests/P0009R18_mdspan_extents/test.cpp index b73415b842..380aad6340 100644 --- a/tests/std/tests/P0009R18_mdspan_extents/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_extents/test.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -60,7 +61,8 @@ constexpr void do_check_members(index_sequence) { static_assert(is_nothrow_default_constructible_v); // Check 'extent' observer - assert((((ext.extent(Indices) == Extents && Extents != dynamic_extent) || ext.extent(Indices) == 0) && ...)); + assert( + (((cmp_equal(ext.extent(Indices), Extents) && Extents != dynamic_extent) || ext.extent(Indices) == 0) && ...)); using OtherIndexType = conditional_t, long long, unsigned long long>; using Ext2 = extents; @@ -82,7 +84,7 @@ constexpr void do_check_members(index_sequence) { } { // Check construction from array and span - auto arr = to_array({ext.extent(Indices)...}); + array arr = {ext.extent(Indices)...}; Ext2 ext2a{arr}; assert(((ext.extent(Indices) == ext2a.extent(Indices)) && ...)); assert(ext == ext2a); @@ -160,15 +162,26 @@ constexpr void check_construction_from_extents_pack() { static_assert(!is_constructible_v>); } -#if 0 // FIXME Bug in array/span constructor? - { // Check postconditions [FIXME] - using Ext = extents; - array arr = {4, 4, 4}; - Ext ext{arr}; - Ext ext2{4, 4, 4}; - assert(ext == ext2); + { // Check postconditions + using Ext = extents; + Ext ext1a{4, ConvertibleToInt{}, 4}; + Ext ext1b{4, 1, 4}; + assert(ext1a == ext1b); + + Ext ext2a{4, ConvertibleToInt{}}; + Ext ext2b{4, 1}; + assert(ext2a == ext2b); + } + + { // Check construciton with integers with mismatched signs + using Ext = extents; + (void) Ext{4ull}; + } + + { // Check narrowing conversions + using Ext = extents; + (void) Ext{4ll}; } -#endif // Bug? { // Check implicit conversions static_assert(NotImplicitlyConstructibleFrom, unsigned long long>); @@ -178,10 +191,10 @@ constexpr void check_construction_from_extents_pack() { } constexpr void check_construction_from_array_and_span() { - { // Check construction from arrays/spans with elements (not) convertible to index_type - using Ext = extents; + { // Check construction from arrays/spans where [array/span].size() is equal to rank() + using Ext = extents; - array arr1 = {4, 5}; + array arr1 = {1, 5}; Ext ext1a{arr1}; span s1{arr1}; Ext ext1b{s1}; @@ -201,6 +214,49 @@ constexpr void check_construction_from_array_and_span() { static_assert(!is_constructible_v>); } + { // // Check construction from arrays/spans where [array/span].size() is equal to rank_dynamic() + using Ext = extents; + + array arr1 = {4, 4}; + Ext ext1a{arr1}; + span s1{arr1}; + Ext ext1b{s1}; + assert(ext1a == ext1b); + static_assert(is_nothrow_constructible_v); + static_assert(is_nothrow_constructible_v); + + array, 2> arr2; + Ext ext2a{arr2}; + span s2{arr2}; + Ext ext2b{s2}; + assert(ext2a == ext2b); + static_assert(is_nothrow_constructible_v); + static_assert(is_nothrow_constructible_v); + + static_assert(!is_constructible_v>); + static_assert(!is_constructible_v>); + } + + { // Check construciton with integers with mismatched signs + using Ext = extents; + + array arr = {4ull}; + (void) Ext{arr}; + + span s{arr}; + (void) Ext{s}; + } + + { // Check narrowing conversions + using Ext = extents; + + array arr = {4ll}; + (void) Ext{arr}; + + span s{arr}; + (void) Ext{s}; + } + { // Check construction from arrays/spans with elements that may throw during conversion to index_type using Ext = extents; static_assert(!is_constructible_v, 2>>); @@ -255,10 +311,11 @@ constexpr void check_equality_operator() { } constexpr bool test() { - // check_members(); // FIXME Definitely a bug. + check_members(); check_members(); + check_members(); check_members(); - // check_members(); // FIXME Bug in array/span constructor? + check_members(); check_members(); check_construction_from_other_extents(); check_construction_from_extents_pack(); diff --git a/tests/std/tests/P0009R18_mdspan_extents_death/env.lst b/tests/std/tests/P0009R18_mdspan_extents_death/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_extents_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_extents_death/test.cpp b/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp new file mode 100644 index 0000000000..2c80d249ab --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +#include + +using namespace std; + +void test_static_extent_function_with_invalid_index() { + using E = extents; + // Index must be less than rank() + (void) E::static_extent(1); +} + +void test_extent_function_with_invalid_index() { + extents e; + // Index must be less than rank() + (void) e.extent(1); +} + +void test_construction_from_other_extents_with_invalid_values() { + extents e1{1, 2}; + // Value of other.extent(r) must be equal to extent(r) for each r for which extent(r) is a static extent + [[maybe_unused]] extents e2{e1}; +} + +void test_construction_from_other_extents_with_unrepresentable_as_index_type_values() { + extents e1{256}; + // Value of other.extent(r) must be representable as a value of type index_type for every rank index r + [[maybe_unused]] extents e2{e1}; +} + +void test_construction_from_pack_with_invalid_values() { + // Value of exts_arr[r] must be equal to extent(r) for each r for which extent(r) is a static extent + [[maybe_unused]] extents e{1, 1}; +} + +void test_construction_from_pack_with_unrepresentable_as_index_type_values() { + // 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_span_with_invalid_values() { + int vals[] = {1, 2}; + span s{vals}; + // Value of other.extent(r) must be equal to extent(r) for each r for which extent(r) is a static extent + [[maybe_unused]] extents e{s}; +} + +void test_construction_from_span_with_unrepresentable_as_index_type_values() { + int vals[] = {1, 2}; + span s{vals}; + // Value of other.extent(r) must be equal to extent(r) for each r for which extent(r) is a static extent + [[maybe_unused]] extents e{s}; +} + +void test_construction_from_array_with_invalid_values() { + int vals[] = {256}; + span s{vals}; + // 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 + [[maybe_unused]] extents e{s}; +} + +void test_construction_from_array_with_unrepresentable_as_index_type_values() { + array a = {256}; + // 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 + [[maybe_unused]] extents e{a}; +} + +int main(int argc, char* argv[]) { + std_testing::death_test_executive exec; + exec.add_death_tests({ + test_static_extent_function_with_invalid_index, + test_extent_function_with_invalid_index, + 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_span_with_invalid_values, + test_construction_from_span_with_unrepresentable_as_index_type_values, + test_construction_from_array_with_invalid_values, + test_construction_from_array_with_unrepresentable_as_index_type_values, + }); + return exec.run(argc, argv); +} From 3ea5312fce262705822650ee3dd0d34d69b5dc10 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Mon, 27 Mar 2023 09:54:59 +0200 Subject: [PATCH 09/57] ``: Various cleanups (mostly renames) (#3593) --- stl/inc/mdspan | 231 +++++++++--------- stl/inc/yvals_core.h | 5 +- .../tests/P0009R18_mdspan_extents/test.cpp | 4 +- .../test.compile.pass.cpp | 2 +- 4 files changed, 120 insertions(+), 122 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 236b217afa..6a357a01b6 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -36,10 +36,10 @@ public: } static_assert(_Is_standard_integer, - "IndexType must be a signed or unsigned integer type (N4928 [mdspan.extents.overview]/1.1)."); + "IndexType must be a signed or unsigned integer type (N4944 [mdspan.extents.overview]/1.1)."); static_assert(((_Extents == dynamic_extent || _STD in_range(_Extents)) && ...), "Each element of Extents must be either equal to dynamic_extent, or must be representable as a value of type " - "IndexType (N4928 [mdspan.extents.overview]/1.2)."); + "IndexType (N4944 [mdspan.extents.overview]/1.2)."); private: _NODISCARD static _CONSTEVAL auto _Make_dynamic_indices() noexcept { @@ -68,9 +68,9 @@ private: _NODISCARD static _CONSTEVAL auto _Make_dynamic_indices_inv() noexcept { array _Result{}; for (rank_type _Idx = 0; _Idx < rank(); ++_Idx) { - for (rank_type _Rdx = 0; _Rdx < rank(); ++_Rdx) { - if (_Dynamic_index(_Rdx + 1) == _Idx + 1) { - _Result[_Idx] = _Rdx; + for (rank_type _Idx_inv = 0; _Idx_inv < rank(); ++_Idx_inv) { + if (_Dynamic_index(_Idx_inv + 1) == _Idx + 1) { + _Result[_Idx] = _Idx_inv; break; } } @@ -104,12 +104,12 @@ public: } _NODISCARD static constexpr size_t static_extent(const rank_type _Idx) noexcept { - _STL_VERIFY(_Idx < rank(), "Index must be less than rank() (N4928 [mdspan.extents.obs]/1)"); + _STL_VERIFY(_Idx < rank(), "Index must be less than rank() (N4944 [mdspan.extents.obs]/1)"); return _Static_extents[_Idx]; } _NODISCARD constexpr index_type extent(const rank_type _Idx) const noexcept { - _STL_VERIFY(_Idx < rank(), "Index must be less than rank() (N4928 [mdspan.extents.obs]/3)"); + _STL_VERIFY(_Idx < rank(), "Index must be less than rank() (N4944 [mdspan.extents.obs]/3)"); if constexpr (rank_dynamic() == 0) { return static_cast(static_extent(_Idx)); } else if constexpr (rank_dynamic() == rank()) { @@ -128,7 +128,7 @@ public: template requires (sizeof...(_OtherExtents) == rank()) && ((_OtherExtents == dynamic_extent || _Extents == dynamic_extent || _OtherExtents == _Extents) && ...) - constexpr explicit((((_Extents != dynamic_extent) && (_OtherExtents == dynamic_extent)) || ...) + 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(); @@ -136,10 +136,10 @@ public: _STL_VERIFY( static_extent(_Idx) == dynamic_extent || _STD cmp_equal(static_extent(_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 " - "(N4928 [mdspan.extents.cons]/2.1)"); + "(N4944 [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 " - "(N4928 [mdspan.extents.cons]/2.2)"); + "(N4944 [mdspan.extents.cons]/2.2)"); if (static_extent(_Idx) == dynamic_extent) { *_It = static_cast(_Other.extent(_Idx)); @@ -158,7 +158,7 @@ public: 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 (N4928 [mdspan.extents.cons]/7.2)"); + "representable as value of type index_type (N4944 [mdspan.extents.cons]/7.2)"); } if constexpr (sizeof...(_Exts) == rank_dynamic()) { @@ -170,7 +170,7 @@ public: _STL_VERIFY( static_extent(_Idx) == dynamic_extent || _STD cmp_equal(static_extent(_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 " - "(N4928 [mdspan.extents.cons]/7.1)"); + "(N4944 [mdspan.extents.cons]/7.1)"); if (static_extent(_Idx) == dynamic_extent) { *_It = _Exts_arr[_Idx]; ++_It; @@ -189,7 +189,7 @@ public: 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 (N4928 [mdspan.extents.cons]/10.2)"); + "index_type for every rank index r (N4944 [mdspan.extents.cons]/10.2)"); } } } @@ -203,10 +203,10 @@ public: for (rank_type _Idx = 0; _Idx < rank(); ++_Idx) { _STL_VERIFY(static_extent(_Idx) == dynamic_extent || _STD cmp_equal(static_extent(_Idx), _Exts[_Idx]), "Value of exts[r] must be equal to extent(r) for each r for which extent(r) is a static extent " - "(N4928 [mdspan.extents.cons]/10.1)"); + "(N4944 [mdspan.extents.cons]/10.1)"); _STL_VERIFY(_Exts[_Idx] >= 0 && _STD in_range(_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 (N4928 [mdspan.extents.cons]/10.2)"); + "index_type for every rank index r (N4944 [mdspan.extents.cons]/10.2)"); } } } @@ -281,20 +281,17 @@ template inline constexpr bool _Is_mapping_of = is_same_v, _Mapping>; -_EXPORT_STD -struct layout_left { +_EXPORT_STD struct layout_left { template class mapping; }; -_EXPORT_STD -struct layout_right { +_EXPORT_STD struct layout_right { template class mapping; }; -_EXPORT_STD -struct layout_stride { +_EXPORT_STD struct layout_stride { template class mapping; }; @@ -303,51 +300,50 @@ template class layout_left::mapping { public: using extents_type = _Extents; - using index_type = typename _Extents::index_type; - using size_type = typename _Extents::size_type; - using rank_type = typename _Extents::rank_type; + using index_type = typename extents_type::index_type; + using size_type = typename extents_type::size_type; + using rank_type = typename extents_type::rank_type; using layout_type = layout_left; - static_assert(_Is_extents<_Extents>, - "Extents must be a specialization of std::extents (N4928 [mdspan.layout.left.overview]/2)."); - static_assert(_Extents::_Is_index_space_size_representable(), + static_assert(_Is_extents, + "Extents must be a specialization of std::extents (N4944 [mdspan.layout.left.overview]/2)."); + static_assert(extents_type::_Is_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."); + "representable as a value of type typename Extents::index_type (N4944 [mdspan.layout.left.overview]/4)."); constexpr mapping() noexcept = default; constexpr mapping(const mapping&) noexcept = default; - constexpr mapping(const _Extents& _Ext) noexcept : _Myext(_Ext) {} + constexpr mapping(const extents_type& _Exts_) noexcept : _Exts(_Exts_) {} template - requires is_constructible_v<_Extents, _OtherExtents> - constexpr explicit(!is_convertible_v<_OtherExtents, _Extents>) + requires is_constructible_v + constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const mapping<_OtherExtents>& _Other) noexcept - : _Myext{_Other.extents()} {} + : _Exts(_Other.extents()) {} template - requires (_Extents::rank() <= 1) && is_constructible_v<_Extents, _OtherExtents> - constexpr explicit(!is_convertible_v<_OtherExtents, _Extents>) + requires (extents_type::rank() <= 1) && is_constructible_v + constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const layout_right::mapping<_OtherExtents>& _Other) noexcept - : _Myext{_Other.extents()} {} + : _Exts(_Other.extents()) {} template - requires is_constructible_v<_Extents, _OtherExtents> - constexpr explicit(_Extents::rank() > 0) mapping(const layout_stride::template mapping<_OtherExtents>& _Other) - : _Myext{_Other.extents()} {} + requires is_constructible_v + constexpr explicit(extents_type::rank() > 0) mapping(const layout_stride::template mapping<_OtherExtents>& _Other) + : _Exts(_Other.extents()) {} constexpr mapping& operator=(const mapping&) noexcept = default; _NODISCARD constexpr const extents_type& extents() const noexcept { - return _Myext; + return _Exts; } _NODISCARD constexpr index_type required_span_size() const noexcept { index_type _Result = 1; - for (rank_type _Dim = 0; _Dim < _Extents::rank(); ++_Dim) { - _Result *= _Myext.extent(_Dim); + for (rank_type _Dim = 0; _Dim < extents_type::rank(); ++_Dim) { + _Result *= _Exts.extent(_Dim); } - return _Result; } @@ -356,7 +352,7 @@ public: && (is_nothrow_constructible_v && ...) _NODISCARD constexpr index_type operator()(_Indices... _Idx) const noexcept { return _Index_impl...>( - static_cast(_Idx)..., make_index_sequence<_Extents::rank()>{}); + static_cast(_Idx)..., make_index_sequence{}); } _NODISCARD static constexpr bool is_always_unique() noexcept { @@ -388,7 +384,7 @@ public: { index_type _Result = 1; for (rank_type _Dim = 0; _Dim < _Rank; ++_Dim) { - _Result *= _Myext.extent(_Dim); + _Result *= _Exts.extent(_Dim); } return _Result; @@ -401,14 +397,14 @@ public: } private: - _Extents _Myext{}; + 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; index_type _Stride = 1; index_type _Result = 0; - (((_Result += _Idx * _Stride), (void) (_Stride *= _Myext.extent(_Seq))), ...); + (((_Result += _Idx * _Stride), (void) (_Stride *= _Exts.extent(_Seq))), ...); return _Result; } }; @@ -417,51 +413,50 @@ template class layout_right::mapping { public: using extents_type = _Extents; - using index_type = typename _Extents::index_type; - using size_type = typename _Extents::size_type; - using rank_type = typename _Extents::rank_type; + using index_type = typename extents_type::index_type; + using size_type = typename extents_type::size_type; + using rank_type = typename extents_type::rank_type; using layout_type = layout_right; - static_assert(_Is_extents<_Extents>, - "Extents must be a specialization of std::extents (N4928 [mdspan.layout.right.overview]/2)."); - static_assert(_Extents::_Is_index_space_size_representable(), + static_assert(_Is_extents, + "Extents must be a specialization of std::extents (N4944 [mdspan.layout.right.overview]/2)."); + static_assert(extents_type::_Is_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."); + "representable as a value of type typename Extents::index_type (N4944 [mdspan.layout.right.overview]/4)."); constexpr mapping() noexcept = default; constexpr mapping(const mapping&) noexcept = default; - constexpr mapping(const _Extents& _Ext) noexcept : _Myext(_Ext) {} + constexpr mapping(const extents_type& _Exts_) noexcept : _Exts(_Exts_) {} template requires is_constructible_v - constexpr explicit(!is_convertible_v<_OtherExtents, _Extents>) + constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const mapping<_OtherExtents>& _Other) noexcept - : _Myext{_Other.extents()} {} + : _Exts(_Other.extents()) {} template requires (extents_type::rank() <= 1) && is_constructible_v - constexpr explicit(!is_convertible_v<_OtherExtents, _Extents>) + constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const layout_left::mapping<_OtherExtents>& _Other) noexcept - : _Myext{_Other.extents()} {} + : _Exts(_Other.extents()) {} template requires is_constructible_v - constexpr explicit(_Extents::rank() > 0) mapping(const layout_stride::template mapping<_OtherExtents>& _Other) - : _Myext{_Other.extents()} {} + constexpr explicit(extents_type::rank() > 0) mapping(const layout_stride::template mapping<_OtherExtents>& _Other) + : _Exts(_Other.extents()) {} constexpr mapping& operator=(const mapping&) noexcept = default; _NODISCARD constexpr const extents_type& extents() const noexcept { - return _Myext; + return _Exts; } _NODISCARD constexpr index_type required_span_size() const noexcept { index_type _Result = 1; - for (rank_type _Dim = 0; _Dim < _Extents::rank(); ++_Dim) { - _Result *= _Myext.extent(_Dim); + for (rank_type _Dim = 0; _Dim < extents_type::rank(); ++_Dim) { + _Result *= _Exts.extent(_Dim); } - return _Result; } @@ -470,7 +465,7 @@ public: && (is_nothrow_constructible_v && ...) _NODISCARD constexpr index_type operator()(_Indices... _Idx) const noexcept { return _Index_impl...>( - static_cast(_Idx)..., make_index_sequence<_Extents::rank()>{}); + static_cast(_Idx)..., make_index_sequence{}); } _NODISCARD static constexpr bool is_always_unique() noexcept { @@ -501,8 +496,8 @@ public: requires (extents_type::rank() > 0) { index_type _Result = 1; - for (rank_type _Dim = _Rank + 1; _Dim < _Extents::rank(); ++_Dim) { - _Result *= _Myext.extent(_Dim); + for (rank_type _Dim = _Rank + 1; _Dim < extents_type::rank(); ++_Dim) { + _Result *= _Exts.extent(_Dim); } return _Result; @@ -515,12 +510,12 @@ public: } private: - _Extents _Myext{}; + extents_type _Exts{}; template constexpr index_type _Index_impl(_IndexType... _Idx, index_sequence<_Seq...>) const noexcept { index_type _Result = 0; - ((void) (_Result = _Idx + _Myext.extent(_Seq) * _Result), ...); + ((void) (_Result = _Idx + _Exts.extent(_Seq) * _Result), ...); return _Result; } }; @@ -540,16 +535,16 @@ template class layout_stride::mapping { public: using extents_type = _Extents; - using index_type = typename _Extents::index_type; - using size_type = typename _Extents::size_type; - using rank_type = typename _Extents::rank_type; + using index_type = typename extents_type::index_type; + using size_type = typename extents_type::size_type; + using rank_type = typename extents_type::rank_type; using layout_type = layout_stride; - static_assert(_Is_extents<_Extents>, - "Extents must be a specialization of std::extents (N4928 [mdspan.layout.stride.overview]/2)."); - static_assert(_Extents::_Is_index_space_size_representable(), + static_assert(_Is_extents, + "Extents must be a specialization of std::extents (N4944 [mdspan.layout.stride.overview]/2)."); + static_assert(extents_type::_Is_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."); + "representable as a value of type typename Extents::index_type (N4944 [mdspan.layout.stride.overview]/4)."); constexpr mapping() noexcept = default; constexpr mapping(const mapping&) noexcept = default; @@ -563,9 +558,10 @@ public: requires is_convertible_v && is_nothrow_constructible_v #endif // ^^^ no workaround ^^^ - constexpr mapping(const _Extents& _E_, const span<_OtherIndexType, _Extents::rank()> _S_) noexcept : _Myext{_E_} { - for (rank_type _Idx = 0; _Idx < _Extents::rank(); ++_Idx) { - _Mystrides[_Idx] = _S_[_Idx]; + constexpr mapping(const extents_type& _Exts_, const span<_OtherIndexType, extents_type::rank()> _Strides_) noexcept + : _Exts{_Exts_} { + for (rank_type _Idx = 0; _Idx < extents_type::rank(); ++_Idx) { + _Strides[_Idx] = _Strides_[_Idx]; } }; @@ -578,10 +574,11 @@ public: requires is_convertible_v && is_nothrow_constructible_v #endif // ^^^ no workaround ^^^ - constexpr mapping(const extents_type& _E_, const array<_OtherIndexType, extents_type::rank()>& _S_) noexcept - : _Myext{_E_} { - for (rank_type _Idx = 0; _Idx < _Extents::rank(); ++_Idx) { - _Mystrides[_Idx] = _S_[_Idx]; + constexpr mapping( + const extents_type& _Exts_, const array<_OtherIndexType, extents_type::rank()>& _Strides_) noexcept + : _Exts{_Exts_} { + for (rank_type _Idx = 0; _Idx < extents_type::rank(); ++_Idx) { + _Strides[_Idx] = _Strides_[_Idx]; } }; @@ -594,32 +591,32 @@ public: && (_Is_mapping_of || _Is_mapping_of || _Is_mapping_of) )) mapping(const _StridedLayoutMapping& _Other) noexcept - : _Myext(_Other.extents()) { - for (rank_type _Dim = 0; _Dim < _Extents::rank(); ++_Dim) { - _Mystrides[_Dim] = _Other.stride(_Dim); + : _Exts(_Other.extents()) { + for (rank_type _Dim = 0; _Dim < extents_type::rank(); ++_Dim) { + _Strides[_Dim] = _Other.stride(_Dim); } } constexpr mapping& operator=(const mapping&) noexcept = default; _NODISCARD constexpr const extents_type& extents() const noexcept { - return _Myext; + return _Exts; } - _NODISCARD constexpr array strides() const noexcept { - return _Mystrides; + _NODISCARD constexpr array strides() const noexcept { + return _Strides; } _NODISCARD constexpr index_type required_span_size() const noexcept { - if (_Extents::rank() > 0) { + if (extents_type::rank() > 0) { index_type _Result = 1; - for (rank_type _Dim = 0; _Dim < _Extents::rank(); ++_Dim) { - const auto _Ext = _Myext.extent(_Dim); + for (rank_type _Dim = 0; _Dim < extents_type::rank(); ++_Dim) { + const auto _Ext = _Exts.extent(_Dim); if (_Ext == 0) { return 0; } - _Result += (_Ext - 1) * _Mystrides[_Dim]; + _Result += (_Ext - 1) * _Strides[_Dim]; } return _Result; @@ -633,7 +630,7 @@ public: && (is_nothrow_constructible_v && ...) _NODISCARD constexpr index_type operator()(_Indices... _Idx) const noexcept { return _Index_impl...>( - static_cast(_Idx)..., make_index_sequence<_Extents::rank()>{}); + static_cast(_Idx)..., make_index_sequence{}); } _NODISCARD static constexpr bool is_always_unique() noexcept { @@ -654,8 +651,8 @@ public: _NODISCARD constexpr bool is_exhaustive() const noexcept { index_type _Ext_size = 1; - for (rank_type _Dim = 0; _Dim < _Extents::rank(); ++_Dim) { - _Ext_size *= _Myext.extent(_Dim); + for (rank_type _Dim = 0; _Dim < extents_type::rank(); ++_Dim) { + _Ext_size *= _Exts.extent(_Dim); } return required_span_size() == _Ext_size; @@ -666,7 +663,7 @@ public: } _NODISCARD constexpr index_type stride(const rank_type _Idx) const noexcept { - return _Mystrides[_Idx]; + return _Strides[_Idx]; } template @@ -710,17 +707,16 @@ public: } private: - _Extents _Myext{}; - array _Mystrides = {}; + extents_type _Exts{}; + array _Strides{}; template constexpr index_type _Index_impl(_IndexType... _Idx, index_sequence<_Seq...>) const noexcept { - return ((_Idx * _Mystrides[_Seq]) + ...); + return ((_Idx * _Strides[_Seq]) + ...); } }; -_EXPORT_STD -template +_EXPORT_STD template struct default_accessor { using offset_policy = default_accessor; using element_type = _ElementType; @@ -742,8 +738,7 @@ struct default_accessor { } }; -_EXPORT_STD -template > class mdspan { public: @@ -760,27 +755,27 @@ public: using reference = typename accessor_type::reference; static_assert( - sizeof(_ElementType) > 0, "ElementType must be a complete type (N4928 [mdspan.mdspan.overview]/2.1)."); + sizeof(element_type) > 0, "ElementType must be a complete type (N4944 [mdspan.mdspan.overview]/2.1)."); static_assert( - !is_abstract_v<_ElementType>, "ElementType cannot be an abstract type (N4928 [mdspan.mdspan.overview]/2.1)."); + !is_abstract_v, "ElementType cannot be an abstract type (N4944 [mdspan.mdspan.overview]/2.1)."); static_assert( - !is_array_v<_ElementType>, "ElementType cannot be an array type (N4928 [mdspan.mdspan.overview]/2.1)."); - static_assert(_Is_extents<_Extents>, - "Extents must be a specialization of std::extents (N4928 [mdspan.mdspan.overview]/2.2)."); - static_assert(is_same_v<_ElementType, typename _AccessorPolicy::element_type>, - "ElementType and typename AccessorPolicy::element_type must be the same type (N4928 " + !is_array_v, "ElementType cannot be an array type (N4944 [mdspan.mdspan.overview]/2.1)."); + static_assert(_Is_extents, + "Extents must be a specialization of std::extents (N4944 [mdspan.mdspan.overview]/2.2)."); + static_assert(is_same_v, + "ElementType and typename AccessorPolicy::element_type must be the same type (N4944 " "[mdspan.mdspan.overview]/2.3)."); _NODISCARD static constexpr rank_type rank() noexcept { - return _Extents::rank(); + return extents_type::rank(); } _NODISCARD static constexpr rank_type rank_dynamic() noexcept { - return _Extents::rank_dynamic(); + return extents_type::rank_dynamic(); } _NODISCARD static constexpr size_t static_extent(const rank_type _Rank) noexcept { - return _Extents::static_extent(_Rank); + return extents_type::static_extent(_Rank); } constexpr mdspan() @@ -806,7 +801,7 @@ public: && (_Size == rank() || _Size == rank_dynamic()) && is_constructible_v && is_default_constructible_v constexpr explicit(_Size != rank_dynamic()) mdspan(data_handle_type _Ptr_, span<_OtherIndexType, _Size>& _Exts) - : _Ptr{_Ptr_}, _Map{_Extents{_Exts}} {} + : _Ptr{_Ptr_}, _Map{extents_type{_Exts}} {} template requires is_convertible_v @@ -815,7 +810,7 @@ public: && is_constructible_v && is_default_constructible_v constexpr explicit(_Size != rank_dynamic()) mdspan(data_handle_type _Ptr_, const array<_OtherIndexType, _Size>& _Exts) - : _Ptr{_Ptr_}, _Map{_Extents{_Exts}} {} + : _Ptr{_Ptr_}, _Map{extents_type{_Exts}} {} constexpr mdspan(data_handle_type _Ptr_, const extents_type& _Ext) requires is_constructible_v && is_default_constructible_v @@ -837,10 +832,10 @@ public: mdspan(const mdspan<_OtherElementType, _OtherExtents, _OtherLayoutPolicy, _OtherAccessor>& _Other) : _Ptr{_Other._Ptr}, _Map{_Other._Map}, _Acc{_Other._Acc} { static_assert(is_constructible_v, - "The data_handle_type must be constructible from const typename OtherAccessor::data_handle_type& (N4928 " + "The data_handle_type must be constructible from const typename OtherAccessor::data_handle_type& (N4944 " "[mdspan.mdspan.cons]/20.1)."); static_assert(is_constructible_v, - "The extents_type must be constructible from OtherExtents (N4928 [mdspan.mdspan.cons]/20.2)."); + "The extents_type must be constructible from OtherExtents (N4944 [mdspan.mdspan.cons]/20.2)."); } constexpr mdspan& operator=(const mdspan&) = default; diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 7964ccc538..e1793282dd 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -1724,7 +1724,10 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #define __cpp_lib_invoke_r 202106L #define __cpp_lib_ios_noreplace 202207L #define __cpp_lib_is_scoped_enum 202011L -#define __cpp_lib_mdspan 202207L + +#ifdef __cpp_lib_concepts +#define __cpp_lib_mdspan 202207L +#endif // __cpp_lib_concepts #if !defined(__clang__) && !defined(__EDG__) // TRANSITION, Clang and EDG support for modules #define __cpp_lib_modules 202207L diff --git a/tests/std/tests/P0009R18_mdspan_extents/test.cpp b/tests/std/tests/P0009R18_mdspan_extents/test.cpp index 380aad6340..bd6c2de05f 100644 --- a/tests/std/tests/P0009R18_mdspan_extents/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_extents/test.cpp @@ -173,7 +173,7 @@ constexpr void check_construction_from_extents_pack() { assert(ext2a == ext2b); } - { // Check construciton with integers with mismatched signs + { // Check construction from integers with mismatched signs using Ext = extents; (void) Ext{4ull}; } @@ -214,7 +214,7 @@ constexpr void check_construction_from_array_and_span() { static_assert(!is_constructible_v>); } - { // // Check construction from arrays/spans where [array/span].size() is equal to rank_dynamic() + { // Check construction from arrays/spans where [array/span].size() is equal to rank_dynamic() using Ext = extents; array arr1 = {4, 4}; diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 9928b8a8f9..01a43c2f1d 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -1350,7 +1350,7 @@ STATIC_ASSERT(__cpp_lib_math_special_functions == 201603L); #endif #endif -#if _HAS_CXX23 +#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 #ifndef __cpp_lib_mdspan #error __cpp_lib_mdspan is not defined #elif __cpp_lib_mdspan != 202207L From 53d73eee9f27fa8173b31fcfec4070cf31220d84 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Sat, 1 Apr 2023 00:46:45 +0200 Subject: [PATCH 10/57] ``: `layout_left` improvements (#3603) --- stl/inc/mdspan | 98 +++-- tests/std/include/test_mdspan_support.hpp | 116 ++++++ tests/std/test.lst | 2 + .../tests/P0009R18_mdspan_extents/test.cpp | 25 +- .../tests/P0009R18_mdspan_layout_left/env.lst | 4 + .../P0009R18_mdspan_layout_left/test.cpp | 372 ++++++++++++++++++ .../P0009R18_mdspan_layout_left_death/env.lst | 4 + .../test.cpp | 61 +++ 8 files changed, 625 insertions(+), 57 deletions(-) create mode 100644 tests/std/include/test_mdspan_support.hpp create mode 100644 tests/std/tests/P0009R18_mdspan_layout_left/env.lst create mode 100644 tests/std/tests/P0009R18_mdspan_layout_left/test.cpp create mode 100644 tests/std/tests/P0009R18_mdspan_layout_left_death/env.lst create mode 100644 tests/std/tests/P0009R18_mdspan_layout_left_death/test.cpp diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 6a357a01b6..8f86197b54 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 0000000000..16d6647417 --- /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 06841dee53..7e133eec9d 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 bd6c2de05f..83cf926a70 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 0000000000..18e2d7c71e --- /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 0000000000..f9f74fed95 --- /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 0000000000..18e2d7c71e --- /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 0000000000..64fd0f7c72 --- /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); +} From 74a6e2fa02d121826667b60950490afc8b3c960e Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 4 Apr 2023 02:12:11 +0200 Subject: [PATCH 11/57] ``: `layout_right` improvements (#3615) --- stl/inc/mdspan | 97 +++-- tests/std/test.lst | 2 + .../P0009R18_mdspan_layout_left/test.cpp | 43 +- .../P0009R18_mdspan_layout_right/env.lst | 4 + .../P0009R18_mdspan_layout_right/test.cpp | 390 ++++++++++++++++++ .../env.lst | 4 + .../test.cpp | 61 +++ 7 files changed, 548 insertions(+), 53 deletions(-) create mode 100644 tests/std/tests/P0009R18_mdspan_layout_right/env.lst create mode 100644 tests/std/tests/P0009R18_mdspan_layout_right/test.cpp create mode 100644 tests/std/tests/P0009R18_mdspan_layout_right_death/env.lst create mode 100644 tests/std/tests/P0009R18_mdspan_layout_right_death/test.cpp diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 8f86197b54..fa7ed45da9 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -249,6 +249,15 @@ public: return _Result; } + // TRANSITION, LWG ISSUE? I believe that this function should return 'index_type' + _NODISCARD constexpr index_type _Rev_prod_of_extents(const rank_type _Idx) const noexcept { + index_type _Result = 1; + for (rank_type _Dim = _Idx + 1; _Dim < rank(); ++_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 * ...)); @@ -354,12 +363,11 @@ public: 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))))) - && ...); + 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 " @@ -456,24 +464,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.right.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.right.cons]/4)."); + } template requires (extents_type::rank() <= 1) && is_constructible_v constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const layout_left::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.right.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 = 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) (N4944 [mdspan.layout.right.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.right.cons]/10.2)."); + } constexpr mapping& operator=(const mapping&) noexcept = default; @@ -482,19 +518,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 { @@ -509,27 +540,24 @@ 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 = _Rank + 1; _Dim < extents_type::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.right.obs]/6)."); + return _Exts._Rev_prod_of_extents(_Idx); } template @@ -541,10 +569,11 @@ public: private: extents_type _Exts{}; - template - constexpr index_type _Index_impl(_IndexType... _Idx, index_sequence<_Seq...>) const noexcept { + 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 _Result = 0; - ((void) (_Result = _Idx + _Exts.extent(_Seq) * _Result), ...); + ((_Result = static_cast(_Indices + _Exts.extent(_Seq) * _Result)), ...); return _Result; } }; diff --git a/tests/std/test.lst b/tests/std/test.lst index 7e133eec9d..a6bbfee080 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -235,6 +235,8 @@ tests\P0009R18_mdspan_extents tests\P0009R18_mdspan_extents_death tests\P0009R18_mdspan_layout_left tests\P0009R18_mdspan_layout_left_death +tests\P0009R18_mdspan_layout_right +tests\P0009R18_mdspan_layout_right_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_layout_left/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp index f9f74fed95..4322e1c864 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp @@ -13,11 +13,6 @@ 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; @@ -83,9 +78,12 @@ constexpr void do_check_members(const extents& ext, index #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)); + array strides{}; + if constexpr (Ext::rank() > 0) { + strides.front() = 1; + for (size_t i = 1; i < Ext::rank(); ++i) { + strides[i] = static_cast(strides[i - 1] * ext.extent(i - 1)); + } } using StrideMapping = layout_stride::mapping; @@ -141,6 +139,7 @@ constexpr void do_check_members(const extents& ext, index { // Check comparisons assert(m == m); + assert(!(m != m)); // Other tests are defined in 'check_comparisons' function } } @@ -215,6 +214,12 @@ constexpr void check_construction_from_other_stride_mapping() { static_assert(!is_constructible_v>>); } + { // Check correctness + using Ext = extents; + layout_stride::mapping stride_mapping{Ext{}, array{1, 4, 12, 24, 72}}; + [[maybe_unused]] layout_left::mapping m{stride_mapping}; + } + { // Check implicit conversions static_assert( !NotImplicitlyConstructibleFrom>, layout_stride::mapping>>); @@ -230,25 +235,25 @@ constexpr void check_construction_from_other_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); + static_assert(!CheckCallOperatorOfLayoutMapping); + static_assert(!CheckCallOperatorOfLayoutMapping); + static_assert(CheckCallOperatorOfLayoutMapping); + static_assert(!CheckCallOperatorOfLayoutMapping); } { // 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); + static_assert(CheckCallOperatorOfLayoutMapping); + static_assert(CheckCallOperatorOfLayoutMapping); + static_assert(CheckCallOperatorOfLayoutMapping>); + static_assert(CheckCallOperatorOfLayoutMapping>); + static_assert(!CheckCallOperatorOfLayoutMapping); } { // Check call with types that might throw during conversion using Mapping = layout_left::mapping>; - static_assert(CanInvokeCallOperatorOfMapping>); - static_assert(!CanInvokeCallOperatorOfMapping>); + static_assert(CheckCallOperatorOfLayoutMapping>); + static_assert(!CheckCallOperatorOfLayoutMapping>); } { // Check various mappings diff --git a/tests/std/tests/P0009R18_mdspan_layout_right/env.lst b/tests/std/tests/P0009R18_mdspan_layout_right/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_layout_right/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_right/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp new file mode 100644 index 0000000000..620183f2ac --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp @@ -0,0 +1,390 @@ +// 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 +constexpr void do_check_members(const extents& ext, index_sequence) { + using Ext = extents; + using Mapping = layout_right::mapping; + + // layout_right meets the layout mapping policy requirements and is a trivial type + static_assert(check_layout_mapping_policy_requirements()); + static_assert(is_trivial_v); + + // layout_right::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_right::mapping; + + { // Check construction from other layout_right::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_right_mapping' function + } + + { // Check construction from layout_left::mapping + using LeftMapping = layout_left::mapping; + if constexpr (Ext::rank() <= 1) { + LeftMapping left_mapping{ext}; + [[maybe_unused]] Mapping m1{left_mapping}; + [[maybe_unused]] Mapping2 m2{left_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_left_mapping' function + } + + { // Check construction from layout_stride::mapping + array strides{}; + if constexpr (Ext::rank() > 0) { + strides.back() = 1; + for (size_t i = Ext::rank() - 1; i-- > 0;) { + strides[i] = static_cast(strides[i + 1] * ext.extent(i + 1)); + } + } + + 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 + } + + 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_stride0 = static_cast((ext.extent(Indices) * ... * 1) / ext.extent(0)); + assert(m.stride(0) == expected_stride0); + assert(m.stride(Ext::rank() - 1) == 1); + static_assert(noexcept(m.stride(Ext::rank() - 1))); + static_assert(noexcept(m.stride(0))); + } else { + static_assert(!CheckStrideMemberFunction); + } + + { // Check comparisons + assert(m == m); + 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_right_mapping() { + { // Check invalid construction + using Mapping = layout_right::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>>); + static_assert(NotImplicitlyConstructibleFrom>, + layout_right::mapping>>); + } +} + +constexpr void check_construction_from_other_left_mapping() { + { // Check construction from layout_left::mapping with various values of E::rank() + static_assert( + is_constructible_v>, layout_left::mapping>>); + static_assert( + is_constructible_v>, layout_left::mapping>>); + static_assert( + !is_constructible_v>, layout_left::mapping>>); + static_assert( + !is_constructible_v>, layout_left::mapping>>); + } + + { // Check construction from layout_left::mapping when E is invalid + using Mapping = layout_right::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>>); + } +} + +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_right::mapping>; + static_assert(!is_constructible_v>>); + static_assert(!is_constructible_v>>); + } + + { // Check correctness + using Ext = extents; + layout_stride::mapping stride_mapping{Ext{}, array{72, 24, 12, 4, 1}}; + [[maybe_unused]] layout_right::mapping m{stride_mapping}; + } + + { // 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_right::mapping>; + static_assert(!CheckCallOperatorOfLayoutMapping); + static_assert(!CheckCallOperatorOfLayoutMapping); + static_assert(CheckCallOperatorOfLayoutMapping); + static_assert(!CheckCallOperatorOfLayoutMapping); + } + + { // Check call with invalid types + using Mapping = layout_right::mapping>; + static_assert(CheckCallOperatorOfLayoutMapping); + static_assert(CheckCallOperatorOfLayoutMapping); + static_assert(CheckCallOperatorOfLayoutMapping>); + static_assert(CheckCallOperatorOfLayoutMapping>); + static_assert(!CheckCallOperatorOfLayoutMapping); + } + + { // Check call with types that might throw during conversion + using Mapping = layout_right::mapping>; + static_assert(CheckCallOperatorOfLayoutMapping>); + static_assert(!CheckCallOperatorOfLayoutMapping>); + } + + { // Check various mappings + layout_right::mapping> m1; + assert(m1() == 0); + + layout_right::mapping> m2{dextents{4}}; + assert(m2(0) == 0); + assert(m2(1) == 1); + assert(m2(2) == 2); + assert(m2(3) == 3); + + layout_right::mapping> m3; + assert(m3(0, 0) == 0); + assert(m3(0, 1) == 1); + assert(m3(1, 0) == 5); + assert(m3(1, 1) == 6); + assert(m3(1, 2) == 7); + assert(m3(2, 1) == 11); + assert(m3(2, 2) == 12); + assert(m3(3, 4) == 19); + } +} + +constexpr void check_comparisons() { + using StaticMapping = layout_right::mapping>; + using DynamicMapping = layout_right::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{4, 4}}; + DynamicMapping m3{dextents{2, 2}}; + assert(m1 == m2); + assert(m2 != m3); + assert(m1 != m3); + } +} + +constexpr void check_correctness() { + { // empty extents + const array vals{}; + mdspan, layout_right> nothing{vals.data()}; + assert(nothing.size() == 1); + } + + { // regular vector + const array vals{2, 1, 0}; + mdspan, layout_right> vec{vals.data()}; + + // TRANSITION, use operator[] + assert(vec(0) == 2); + assert(vec(1) == 1); + assert(vec(2) == 0); + } + + { // 4x3 matrix with row-major order + const array vals{11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; + mdspan, layout_right> matrix{vals.data()}; + + // TRANSITION, use operator[] + assert(matrix(0, 0) == 11); + assert(matrix(0, 2) == 9); + assert(matrix(1, 1) == 7); + assert(matrix(2, 0) == 5); + assert(matrix(2, 2) == 3); + assert(matrix(3, 1) == 1); + } + + { // 4x3x2 tensor + const array vals{23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; + mdspan, layout_right> tensor{vals.data(), 4, 3, 2}; + + // TRANSITION, use operator[] + assert(tensor(0, 0, 0) == 23); + assert(tensor(0, 0, 1) == 22); + assert(tensor(0, 1, 0) == 21); + assert(tensor(0, 1, 1) == 20); + assert(tensor(1, 0, 0) == 17); + assert(tensor(1, 0, 1) == 16); + assert(tensor(1, 1, 0) == 15); + assert(tensor(1, 1, 1) == 14); + assert(tensor(2, 2, 1) == 6); + assert(tensor(3, 2, 1) == 0); + } + + { // 3x2x3x2 tensor + const array vals{35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, + 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; + mdspan, layout_right> tensor{vals.data(), 2, 2}; + + // TRANSITION, use operator[] + assert(tensor(0, 0, 0, 0) == 35); + assert(tensor(0, 0, 0, 1) == 34); + assert(tensor(0, 0, 1, 0) == 33); + assert(tensor(0, 0, 1, 1) == 32); + assert(tensor(0, 1, 0, 0) == 29); + assert(tensor(0, 1, 0, 1) == 28); + assert(tensor(0, 1, 1, 0) == 27); + assert(tensor(0, 1, 1, 1) == 26); + assert(tensor(1, 0, 0, 0) == 23); + assert(tensor(1, 0, 0, 1) == 22); + assert(tensor(1, 0, 1, 0) == 21); + assert(tensor(1, 0, 1, 1) == 20); + assert(tensor(1, 1, 0, 0) == 17); + assert(tensor(1, 1, 0, 1) == 16); + assert(tensor(1, 1, 1, 0) == 15); + assert(tensor(1, 1, 1, 1) == 14); + assert(tensor(2, 0, 2, 0) == 7); + assert(tensor(2, 1, 2, 1) == 0); + } +} + +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_right_mapping(); + check_construction_from_other_left_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_right_death/env.lst b/tests/std/tests/P0009R18_mdspan_layout_right_death/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_layout_right_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_right_death/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_right_death/test.cpp new file mode 100644 index 0000000000..449227a714 --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_layout_right_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_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_right::mapping> m2{m1}; +} + +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_right::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{3, 1}}; + // 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}; +} + +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_right::mapping> m2{m1}; +} +#pragma warning(pop) // TRANSITION, "/analyze:only" BUG? + +void test_stride_function() { + layout_right::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_right_mapping, + test_construction_from_other_left_mapping, + test_construction_from_other_stride_mapping_1, + test_construction_from_other_stride_mapping_2, + test_stride_function, + }); + return exec.run(argc, argv); +} From 32853a6c87f9d589f636ae39965afda0a93f0ad1 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 4 Apr 2023 02:18:01 +0200 Subject: [PATCH 12/57] ``: Improve accessor testing (#3616) --- stl/inc/mdspan | 7 + tests/std/include/test_mdspan_support.hpp | 132 ++++++++++++------ .../P0009R18_mdspan_default_accessor/test.cpp | 5 + 3 files changed, 99 insertions(+), 45 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index fa7ed45da9..9594bf14ce 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -781,6 +781,13 @@ struct default_accessor { using reference = _ElementType&; using data_handle_type = _ElementType*; + static_assert( + sizeof(element_type) > 0, "ElementType must be a complete type (N4944 [mdspan.accessor.default.overview]/2)."); + static_assert(!is_abstract_v, + "ElementType cannot be an abstract type (N4944 [mdspan.accessor.default.overview]/2)."); + static_assert( + !is_array_v, "ElementType cannot be an array type (N4944 [mdspan.accessor.default.overview]/2)."); + constexpr default_accessor() noexcept = default; template diff --git a/tests/std/include/test_mdspan_support.hpp b/tests/std/include/test_mdspan_support.hpp index 16d6647417..842065b905 100644 --- a/tests/std/include/test_mdspan_support.hpp +++ b/tests/std/include/test_mdspan_support.hpp @@ -11,7 +11,7 @@ enum class IsNothrow : bool { no, yes }; -template +template struct ConvertibleToInt { constexpr operator Int() const noexcept(std::to_underlying(Nothrow)) { return Int{1}; @@ -20,53 +20,57 @@ struct ConvertibleToInt { struct NonConvertibleToAnything {}; -template -constexpr void check_implicit_conversion(T); // not defined +namespace detail { + 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)...}); }; + && !requires(Args&&... args) { detail::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; - }; +namespace detail { + 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 = is_extents_v + && std::same_as + && std::same_as + && is_mapping_of_v; + + // clang-format off + 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; + }; + // clang-format on + + template + concept CheckStaticFunctionsOfLayoutMapping = requires { + { 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; + }; +} // namespace detail // clang-format off template @@ -89,9 +93,9 @@ constexpr bool check_layout_mapping_requirements() { 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); + static_assert(detail::CheckNestedTypesOfLayoutMapping); + static_assert(detail::CheckMemberFunctionsOfLayoutMapping); + static_assert(detail::CheckStaticFunctionsOfLayoutMapping); [](std::index_sequence) { static_assert(CheckCallOperatorOfLayoutMapping); @@ -106,7 +110,7 @@ constexpr bool check_layout_mapping_requirements() { } template - requires is_extents_v + requires detail::is_extents_v constexpr bool check_layout_mapping_policy_requirements() { using X = typename MP::template mapping; static_assert(check_layout_mapping_requirements()); @@ -114,3 +118,41 @@ constexpr bool check_layout_mapping_policy_requirements() { static_assert(std::same_as); return true; } + +template +constexpr bool check_accessor_policy_requirements(); + +namespace detail { + template + concept CheckNestedTypesOfAccessorPolicy = + sizeof(typename A::element_type) > 0 + && (!std::is_abstract_v) && std::copyable + && std::is_nothrow_move_constructible_v + && std::is_nothrow_move_assignable_v + && std::is_nothrow_swappable_v + && std::common_reference_with + && (std::same_as + || check_accessor_policy_requirements()) + && std::constructible_from + && std::is_same_v; + + // clang-format off + template + concept CheckMemberFunctionsOfAccessorPolicy = + requires(const A a, const typename A::data_handle_type p, size_t i) { + { a.access(p, i) } -> std::same_as; + { a.offset(p, i) } -> std::same_as; + }; + // clang-format on +} // namespace detail + +template +constexpr bool check_accessor_policy_requirements() { + static_assert(std::copyable); + 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(detail::CheckNestedTypesOfAccessorPolicy); + static_assert(detail::CheckMemberFunctionsOfAccessorPolicy); + return true; +} diff --git a/tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp b/tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp index 21379295e7..9036a910c7 100644 --- a/tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp @@ -9,12 +9,17 @@ #include #include +#include + using namespace std; template constexpr void test_one(array elems) { using Accessor = default_accessor; + // default_accessor meets the accessor policy requirements + static_assert(check_accessor_policy_requirements()); + // Check modeled concepts static_assert(is_nothrow_move_constructible_v); static_assert(is_nothrow_move_assignable_v); From f4934c682695a7010f7ceda343d29a0088e18a59 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Fri, 7 Apr 2023 03:00:19 +0200 Subject: [PATCH 13/57] ``: Cleanup `extents`'s deduction guide (#3632) --- stl/inc/mdspan | 12 ++++-------- .../std/tests/P0009R18_mdspan_layout_right/test.cpp | 10 ++++------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 9594bf14ce..5506590b7c 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -267,16 +267,12 @@ public: } }; -#if defined(__clang__) || defined(__EDG__) // TRANSITION, REQUIRES REPORT (ICE) -template - requires (is_convertible_v<_Integrals, size_t> && ...) -extents(_Integrals... _Exts) -> extents; -#else // ^^^ no workaround / workaround vvv +template +inline constexpr size_t _Repeat_dynamic_extent = dynamic_extent; + template requires (is_convertible_v<_Integrals, size_t> && ...) -extents(_Integrals...) - -> extents, _Integrals>::value...>; -#endif // ^^^ workaround ^^^ +extents(_Integrals...) -> extents...>; template struct _Dextents_impl; diff --git a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp index 620183f2ac..4810d8a362 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp @@ -183,12 +183,10 @@ constexpr void check_construction_from_other_left_mapping() { } { // Check implicit conversions - static_assert(!NotImplicitlyConstructibleFrom>, - layout_left::mapping>>); - static_assert(NotImplicitlyConstructibleFrom>, - layout_left::mapping>>); - static_assert(NotImplicitlyConstructibleFrom>, - layout_left::mapping>>); + using Mapping = layout_right::mapping>; + static_assert(!NotImplicitlyConstructibleFrom>>); + static_assert(NotImplicitlyConstructibleFrom>>); + static_assert(NotImplicitlyConstructibleFrom>>); } } From 34fce78136b532fa51f674655e8b8ad35f901cf6 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 7 Apr 2023 15:12:59 -0700 Subject: [PATCH 14/57] `` needs `` for `numeric_limits`. --- stl/inc/mdspan | 1 + 1 file changed, 1 insertion(+) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 5506590b7c..2fb443e92e 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -12,6 +12,7 @@ _EMIT_STL_WARNING(STL4038, "The contents of are available only with C++23 or later."); #else // ^^^ not supported / supported language mode vvv #include +#include #include #include From 7899889a8d9eed750e70bd7349f1eb4b04daa6f1 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Wed, 26 Apr 2023 21:57:42 +0200 Subject: [PATCH 15/57] ``: Cleanup `mdspan` class a little bit (#3672) --- stl/inc/mdspan | 91 ++++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 47 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 2fb443e92e..c68a2ce82e 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -836,8 +836,12 @@ public: return extents_type::rank_dynamic(); } - _NODISCARD static constexpr size_t static_extent(const rank_type _Rank) noexcept { - return extents_type::static_extent(_Rank); + _NODISCARD static constexpr size_t static_extent(const rank_type _Idx) noexcept { + return extents_type::static_extent(_Idx); + } + + _NODISCARD constexpr index_type extent(const rank_type _Idx) const noexcept { + return _Map.extents().extent(_Idx); } constexpr mdspan() @@ -854,7 +858,7 @@ public: && (sizeof...(_OtherIndexTypes) == rank() || sizeof...(_OtherIndexTypes) == rank_dynamic()) && is_constructible_v && is_default_constructible_v constexpr explicit mdspan(data_handle_type _Ptr_, _OtherIndexTypes... _Exts) - : _Ptr{_STD move(_Ptr_)}, _Map{extents_type{static_cast(_STD move(_Exts))...}} {} + : _Ptr(_STD move(_Ptr_)), _Map(extents_type{static_cast(_STD move(_Exts))...}), _Acc() {} template requires is_convertible_v<_OtherIndexType, index_type> @@ -862,7 +866,7 @@ public: && (_Size == rank() || _Size == rank_dynamic()) && is_constructible_v && is_default_constructible_v constexpr explicit(_Size != rank_dynamic()) mdspan(data_handle_type _Ptr_, span<_OtherIndexType, _Size>& _Exts) - : _Ptr{_Ptr_}, _Map{extents_type{_Exts}} {} + : _Ptr(_STD move(_Ptr_)), _Map(extents_type{_Exts}), _Acc() {} template requires is_convertible_v @@ -871,18 +875,18 @@ public: && is_constructible_v && is_default_constructible_v constexpr explicit(_Size != rank_dynamic()) mdspan(data_handle_type _Ptr_, const array<_OtherIndexType, _Size>& _Exts) - : _Ptr{_Ptr_}, _Map{extents_type{_Exts}} {} + : _Ptr(_STD move(_Ptr_)), _Map(extents_type{_Exts}), _Acc() {} constexpr mdspan(data_handle_type _Ptr_, const extents_type& _Ext) requires is_constructible_v && is_default_constructible_v - : _Ptr{_Ptr_}, _Map{_Ext} {} + : _Ptr(_STD move(_Ptr_)), _Map(_Ext), _Acc() {} constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_) requires is_default_constructible_v - : _Ptr{_Ptr_}, _Map{_Map_} {} + : _Ptr(_STD move(_Ptr_)), _Map(_Map_), _Acc() {} constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_, const accessor_type& _Acc_) - : _Ptr{_Ptr_}, _Map{_Map_}, _Acc{_Acc_} {} + : _Ptr(_STD move(_Ptr_)), _Map(_Map_), _Acc(_Acc_) {} template requires is_constructible_v&> @@ -891,7 +895,7 @@ public: !is_convertible_v&, mapping_type> || !is_convertible_v) mdspan(const mdspan<_OtherElementType, _OtherExtents, _OtherLayoutPolicy, _OtherAccessor>& _Other) - : _Ptr{_Other._Ptr}, _Map{_Other._Map}, _Acc{_Other._Acc} { + : _Ptr(_Other._Ptr), _Map(_Other._Map), _Acc(_Other._Acc) { static_assert(is_constructible_v, "The data_handle_type must be constructible from const typename OtherAccessor::data_handle_type& (N4944 " "[mdspan.mdspan.cons]/20.1)."); @@ -925,43 +929,16 @@ public: return _Index_impl(_Indices, make_index_sequence{}); } - _NODISCARD constexpr const extents_type& extents() const noexcept { - return _Map.extents(); - } - - _NODISCARD constexpr const data_handle_type& data_handle() const noexcept { - return _Ptr; - } - - _NODISCARD constexpr const mapping_type& mapping() const noexcept { - return _Map; - } - - _NODISCARD constexpr const accessor_type& accessor() const noexcept { - return _Acc; - } - - _NODISCARD constexpr index_type extent(const rank_type _Rank) const noexcept { - const auto& _Ext = _Map.extents(); - return _Ext.extent(_Rank); - } - _NODISCARD constexpr size_type size() const noexcept { - const auto& _Ext = _Map.extents(); - size_type _Result = 1; - for (rank_type _Dim = 0; _Dim < rank(); ++_Dim) { - _Result *= _Ext.extent(_Dim); - } - return _Result; + return static_cast(_Map.extents()._Fwd_prod_of_extents(rank())); } _NODISCARD constexpr bool empty() const noexcept { - for (rank_type _Dim = 0; _Dim < rank(); ++_Dim) { - if (_Map.extents().extent(_Dim) == 0) { + for (rank_type _Idx = 0; _Idx < rank(); ++_Idx) { + if (_Map.extents().extent(_Idx) == 0) { return true; } } - return false; } @@ -971,32 +948,52 @@ public: swap(_Left._Acc, _Right._Acc); } - _NODISCARD static constexpr bool is_always_unique() { + _NODISCARD constexpr const extents_type& extents() const noexcept { + return _Map.extents(); + } + + _NODISCARD constexpr const data_handle_type& data_handle() const noexcept { + return _Ptr; + } + + _NODISCARD constexpr const mapping_type& mapping() const noexcept { + return _Map; + } + + _NODISCARD constexpr const accessor_type& accessor() const noexcept { + return _Acc; + } + + _NODISCARD static constexpr bool is_always_unique() noexcept( + noexcept(mapping_type::is_always_unique())) /* strengthened */ { return mapping_type::is_always_unique(); } - _NODISCARD static constexpr bool is_always_exhaustive() { + _NODISCARD static constexpr bool is_always_exhaustive() noexcept( + noexcept(mapping_type::is_always_exhaustive())) /* strengthened */ { return mapping_type::is_always_exhaustive(); } - _NODISCARD static constexpr bool is_always_strided() { + _NODISCARD static constexpr bool is_always_strided() noexcept( + noexcept(mapping_type::is_always_strided())) /* strengthened */ { return mapping_type::is_always_strided(); } - _NODISCARD constexpr bool is_unique() const { + _NODISCARD constexpr bool is_unique() const noexcept(noexcept(_Map.is_unique())) /* strengthened */ { return _Map.is_unique(); } - _NODISCARD constexpr bool is_exhaustive() const { + _NODISCARD constexpr bool is_exhaustive() const noexcept(noexcept(_Map.is_exhaustive())) /* strengthened */ { return _Map.is_exhaustive(); } - _NODISCARD constexpr bool is_strided() const { + _NODISCARD constexpr bool is_strided() const noexcept(noexcept(_Map.is_strided())) /* strengthened */ { return _Map.is_strided(); } - _NODISCARD constexpr index_type stride(const size_t _Dim) const { - return _Map.stride(_Dim); + _NODISCARD constexpr index_type stride(const rank_type _Idx) const + noexcept(noexcept(_Map.stride(_Idx))) /* strengthened */ { + return _Map.stride(_Idx); } private: From ea8995d7f48dd26386e246f495a0357f957a9d18 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 27 Apr 2023 01:08:09 +0200 Subject: [PATCH 16/57] ``: `layout_stride` improvements (#3673) --- stl/inc/mdspan | 154 ++++++++++-------- tests/std/test.lst | 2 + .../P0009R18_mdspan_layout_stride/env.lst | 4 + .../P0009R18_mdspan_layout_stride/test.cpp | 150 +++++++++++++++++ .../env.lst | 4 + .../test.cpp | 42 +++++ 6 files changed, 292 insertions(+), 64 deletions(-) create mode 100644 tests/std/tests/P0009R18_mdspan_layout_stride/env.lst create mode 100644 tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp create mode 100644 tests/std/tests/P0009R18_mdspan_layout_stride_death/env.lst create mode 100644 tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp diff --git a/stl/inc/mdspan b/stl/inc/mdspan index c68a2ce82e..48d7237292 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -601,9 +601,34 @@ public: "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 (N4944 [mdspan.layout.stride.overview]/4)."); - constexpr mapping() noexcept = default; +#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 + constexpr mapping() noexcept : _Exts(extents_type{}) { + 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 + _Strides[_Idx] = _Strides[_Idx + 1] * _Exts.extent(_Idx + 1); + } + } + } +#pragma warning(pop) // TRANSITION, "/analyze:only" BUG? + constexpr mapping(const mapping&) noexcept = default; + template + requires is_convertible_v + && is_nothrow_constructible_v + constexpr mapping(const extents_type& _Exts_, span<_OtherIndexType, extents_type::rank()> _Strides_, + index_sequence<_Indices...>) noexcept + : _Exts(_Exts_), _Strides{static_cast(_STD as_const(_Strides_[_Indices]))...} { + for (rank_type _Idx = 0; _Idx < extents_type::rank(); ++_Idx) { + // TRANSITION CHECK [mdspan.layout.stride.cons]/4.2 (REQUIRES `_Multiply_with_overflow_check`) + _STL_VERIFY(_Strides[_Idx] > 0, "Value of s[i] must be greater than 0 for all i in the range [0, rank_) " + "(N4944 [mdspan.layout.stride.cons]/4.1)."); + } + } + #ifndef __clang__ // TRANSITION, MSVC messes up CTAD when concepts are used here (needs further investigation) template && is_nothrow_constructible_v, @@ -613,12 +638,9 @@ public: requires is_convertible_v && is_nothrow_constructible_v #endif // ^^^ no workaround ^^^ - constexpr mapping(const extents_type& _Exts_, const span<_OtherIndexType, extents_type::rank()> _Strides_) noexcept - : _Exts(_Exts_) { - for (rank_type _Idx = 0; _Idx < extents_type::rank(); ++_Idx) { - _Strides[_Idx] = _Strides_[_Idx]; - } - }; + constexpr mapping(const extents_type& _Exts_, span<_OtherIndexType, extents_type::rank()> _Strides_) noexcept + : mapping(_Exts_, _Strides_, make_index_sequence{}) { + } #ifndef __clang__ // TRANSITION, MSVC messes up CTAD when concepts are used here (needs further investigation) template @@ -631,11 +653,8 @@ public: #endif // ^^^ no workaround ^^^ constexpr mapping( const extents_type& _Exts_, const array<_OtherIndexType, extents_type::rank()>& _Strides_) noexcept - : _Exts(_Exts_) { - for (rank_type _Idx = 0; _Idx < extents_type::rank(); ++_Idx) { - _Strides[_Idx] = _Strides_[_Idx]; - } - }; + : mapping(_Exts_, span{_Strides_}, make_index_sequence{}) { + } template requires _Layout_mapping_alike<_StridedLayoutMapping> @@ -647,8 +666,16 @@ public: || _Is_mapping_of) )) mapping(const _StridedLayoutMapping& _Other) noexcept : _Exts(_Other.extents()) { - for (rank_type _Dim = 0; _Dim < extents_type::rank(); ++_Dim) { - _Strides[_Dim] = _Other.stride(_Dim); + _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.stride.cons]/7.3)."); + _STL_VERIFY( + _Offset(_Other) == 0, "Value of OFFSET(other) must be equal to 0 (N4944 [mdspan.layout.stride.cons]/7.4)."); + for (rank_type _Idx = 0; _Idx < extents_type::rank(); ++_Idx) { + const auto _Stride = _Other.stride(_Idx); + _STL_VERIFY(_Stride > 0, "Value of other.stride(r) must be greater than 0 for every rank index r of " + "extents() (N4944 [mdspan.layout.stride.cons]/7.2)."); + _Strides[_Idx] = static_cast(_Stride); } } @@ -662,30 +689,32 @@ public: return _Strides; } +#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 _NODISCARD constexpr index_type required_span_size() const noexcept { - if (extents_type::rank() > 0) { + if constexpr (extents_type::rank() == 0) { + return 1; + } else { index_type _Result = 1; - for (rank_type _Dim = 0; _Dim < extents_type::rank(); ++_Dim) { - const auto _Ext = _Exts.extent(_Dim); + for (rank_type _Idx = 0; _Idx < extents_type::rank(); ++_Idx) { + const index_type _Ext = _Exts.extent(_Idx); if (_Ext == 0) { return 0; } - _Result += (_Ext - 1) * _Strides[_Dim]; + _Result += (_Ext - 1) * _Strides[_Idx]; } return _Result; - } else { - return 1; } } +#pragma warning(pop) // TRANSITION, "/analyze:only" BUG? - 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 { @@ -700,20 +729,19 @@ 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 { - index_type _Ext_size = 1; - for (rank_type _Dim = 0; _Dim < extents_type::rank(); ++_Dim) { - _Ext_size *= _Exts.extent(_Dim); + if constexpr (extents_type::rank() == 0) { + return true; + } else { + return required_span_size() == _Exts._Fwd_prod_of_extents(extents_type::rank()); } - - return required_span_size() == _Ext_size; } - _NODISCARD constexpr bool is_strided() const noexcept { + _NODISCARD static constexpr bool is_strided() noexcept { return true; } @@ -725,49 +753,47 @@ public: requires _Layout_mapping_alike<_OtherMapping> && (extents_type::rank() == _OtherMapping::extents_type::rank()) && (_OtherMapping::is_always_strided()) _NODISCARD_FRIEND constexpr bool operator==(const mapping& _Left, const _OtherMapping& _Right) noexcept { - if (_Left.extents() != _Right.extents()) { - return false; - } - - constexpr rank_type _Rank = extents_type::rank(); - for (rank_type _Dim = 0; _Dim < _Rank; ++_Dim) { - if (_Left.stride(_Dim) != _Right.stride(_Dim)) { + if constexpr (extents_type::rank() != 0) { + if (_Left.extents() != _Right.extents()) { return false; } - } - - index_type _Offset; - if constexpr (_Rank == 0) { - _Offset = _Right(); - } else { - bool _Is_empty = false; - for (rank_type _Dim = 0; _Dim < _Rank; ++_Dim) { - if (_Left.extents().extent(_Dim) == 0) { - _Is_empty = true; - break; - } - } - if (_Is_empty) { - _Offset = 0; - } else { - _Offset = [&_Right](index_sequence<_Idx...>) { - return _Right(((void) _Idx, 0)...); + for (rank_type _Idx = 0; _Idx < extents_type::rank(); ++_Idx) { + if (_Left.stride(_Idx) != _Right.stride(_Idx)) { + return false; } - (make_index_sequence<_Rank>{}); } - - return _Offset == 0; } + + return _Offset(_Right) == 0; } private: extents_type _Exts{}; array _Strides{}; - template - constexpr index_type _Index_impl(_IndexType... _Idx, index_sequence<_Seq...>) const noexcept { - return ((_Idx * _Strides[_Seq]) + ...); + template + _NODISCARD static constexpr typename _OtherMapping::index_type _Offset(_OtherMapping& _Mapping) noexcept { + if constexpr (extents_type::rank() == 0) { + return _Mapping(); + } else { + for (rank_type _Idx = 0; _Idx < extents_type::rank(); ++_Idx) { + if (_Mapping.extents().extent(_Idx) == 0) { + return 0; + } + } + + return [&](index_sequence<_Indices...>) { + return _Mapping(((void) _Indices, 0)...); + } + (make_index_sequence{}); + } + } + + template + _NODISCARD constexpr index_type _Index_impl(index_sequence<_Seq...>, _IndexTypes... _Indices) const noexcept { + _STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...)); + return ((_Indices * _Strides[_Seq]) + ... + 0); } }; diff --git a/tests/std/test.lst b/tests/std/test.lst index 88d477d877..b447d8a54e 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -238,6 +238,8 @@ tests\P0009R18_mdspan_layout_left tests\P0009R18_mdspan_layout_left_death tests\P0009R18_mdspan_layout_right tests\P0009R18_mdspan_layout_right_death +tests\P0009R18_mdspan_layout_stride +tests\P0009R18_mdspan_layout_stride_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_layout_stride/env.lst b/tests/std/tests/P0009R18_mdspan_layout_stride/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_layout_stride/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_stride/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp new file mode 100644 index 0000000000..57683fd893 --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp @@ -0,0 +1,150 @@ +// 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 +constexpr void do_check_members(const extents& ext, + const array strs, index_sequence) { + using Ext = extents; + using Strides = array; + using Mapping = layout_stride::mapping; + + // layout_stride meets the layout mapping policy requirements and is a trivial type + static_assert(check_layout_mapping_policy_requirements()); + static_assert(is_trivial_v); + + // layout_stride::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); + +#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 default and copy constructor + Mapping m; + const Mapping cpy = m; + const layout_right::mapping right_mapping; + assert(m == right_mapping); + assert(cpy == m); + static_assert(is_nothrow_default_constructible_v); + static_assert(is_nothrow_copy_constructible_v); + } + + { // Check construction from extents_type and array + Mapping m{ext, strs}; + assert(m.extents() == ext); + assert(m.strides() == strs); + static_assert(is_nothrow_constructible_v); + // Other tests are defined in 'check_construction_from_extents_and_array' function [FIXME] + } + + { // Check construction from extents_type and span + using Span = span; + Mapping m{ext, Span{strs}}; + assert(m.extents() == ext); + assert(m.strides() == strs); + static_assert(is_nothrow_constructible_v); + // Other tests are defined in 'check_construction_from_extents_and_array' function [FIXME] + } + + using OtherIndexType = long long; + using Ext2 = extents; + using Mapping2 = layout_stride::mapping; + + { // Check construction from other mappings + Mapping m1{ext, strs}; + Mapping2 m2{m1}; + assert(m1 == m2); + static_assert(is_nothrow_constructible_v); + // Other tests are defined in 'check_construction_from_other_mappings' function [FIXME] + } + + Mapping m{ext, strs}; // For later use + + { // Check 'extents' function + same_as decltype(auto) ext2 = m.extents(); + assert(ext2 == ext); + static_assert(noexcept(m.extents())); + } + + { // Check 'strides' function + same_as decltype(auto) strs2 = m.strides(); + assert(strs2 == strs); + static_assert(noexcept(m.strides())); + } + + // Function 'required_span_size' is tested in 'check_required_span_size' function[FIXME] + { // Check 'required_span_size' function[FIXME] + if (((ext.extent(Indices) == 0) || ...)) { + assert(m.required_span_size() == 0); + } else { + const IndexType expected_value = + static_cast((((ext.extent(Indices) - 1) * strs[Indices]) + ... + 1)); + assert(m.required_span_size() == expected_value); + } + static_assert(noexcept(m.required_span_size())); + } + + // Call operator() is tested 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/strided]' functions + static_assert(Mapping::is_unique()); + static_assert(Mapping::is_strided()); + // Tests of 'is_exhaustive' are defined in 'check_is_exhaustive' function [FIXME] + } + + { // Check 'stride' function (intentionally not if constexpr) + for (size_t i = 0; i < strs.size(); ++i) { + same_as decltype(auto) s = m.stride(i); + assert(strs[i] == s); + } + } + + { // Check comparisons + assert(m == m); + assert(!(m != m)); + // Other tests are defined in 'check_comparisons' function [FIXME] + } +#pragma warning(pop) // TRANSITION, "/analyze:only" BUG? +} + +template +constexpr void check_members(extents ext, const array strides) { + do_check_members(ext, strides, make_index_sequence{}); +} + +constexpr bool test() { + check_members(extents{}, array{}); + check_members(extents{}, array{1}); + check_members(extents{3}, array{1, 3}); + // TRANSITION more tests + return true; +} + +int main() { + static_assert(test()); + test(); +} diff --git a/tests/std/tests/P0009R18_mdspan_layout_stride_death/env.lst b/tests/std/tests/P0009R18_mdspan_layout_stride_death/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_layout_stride_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_stride_death/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp new file mode 100644 index 0000000000..4d66c20b0e --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +#include + +using namespace std; + +#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_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}}; +} + +void test_construction_from_extents_and_span() { + 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}}; +} + +void test_construction_from_strided_layout_mapping() { + layout_right::mapping> m1; + // Value of other.required_span_size() must be representable as a value of type index_type + [[maybe_unused]] layout_stride::mapping> m2{m1}; +} +#pragma warning(pop) // TRANSITION, "/analyze:only" BUG? + +int main(int argc, char* argv[]) { + std_testing::death_test_executive exec; + exec.add_death_tests({ + // TRANSITION more tests + test_construction_from_extents_and_array, + test_construction_from_extents_and_span, + test_construction_from_strided_layout_mapping, + }); + return exec.run(argc, argv); +} From e4fb8fadd543b77324dbc8b5f684d8ac1b43e54d Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 27 Apr 2023 02:22:37 +0200 Subject: [PATCH 17/57] ``: Reduce amount of function calls in non-optimized debug mode (#3674) --- stl/inc/mdspan | 113 ++++++++---------- .../P0009R18_mdspan_layout_stride/test.cpp | 6 +- 2 files changed, 56 insertions(+), 63 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 48d7237292..ff1766909f 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -32,45 +32,38 @@ public: using size_type = make_unsigned_t; using rank_type = size_t; - _NODISCARD static constexpr rank_type rank() noexcept { - return sizeof...(_Extents); - } - static_assert(_Is_standard_integer, "IndexType must be a signed or unsigned integer type (N4944 [mdspan.extents.overview]/1.1)."); static_assert(((_Extents == dynamic_extent || _STD in_range(_Extents)) && ...), "Each element of Extents must be either equal to dynamic_extent, or must be representable as a value of type " "IndexType (N4944 [mdspan.extents.overview]/1.2)."); + static constexpr rank_type _Rank = sizeof...(_Extents); + static constexpr rank_type _Rank_dynamic = (static_cast(_Extents == dynamic_extent) + ... + 0); + private: + static constexpr array _Static_extents = {_Extents...}; + _NODISCARD static _CONSTEVAL auto _Make_dynamic_indices() noexcept { -#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 - array _Result{}; + array _Result{}; rank_type _Counter = 0; - for (rank_type _Idx = 0; _Idx < rank(); ++_Idx) { + for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) { _Result[_Idx] = _Counter; if (_Static_extents[_Idx] == dynamic_extent) { ++_Counter; } } - _Result[rank()] = _Counter; + _Result[_Rank] = _Counter; return _Result; -#pragma warning(pop) // TRANSITION, "/analyze:only" BUG? } - static constexpr array _Static_extents = {_Extents...}; - static constexpr array _Dynamic_indices = _Make_dynamic_indices(); - - _NODISCARD static constexpr rank_type _Dynamic_index(rank_type _Idx) noexcept { - return _Dynamic_indices[_Idx]; - } + static constexpr array _Dynamic_indices = _Make_dynamic_indices(); _NODISCARD static _CONSTEVAL auto _Make_dynamic_indices_inv() noexcept { - array _Result{}; - for (rank_type _Idx = 0; _Idx < rank(); ++_Idx) { - for (rank_type _Idx_inv = 0; _Idx_inv < rank(); ++_Idx_inv) { - if (_Dynamic_index(_Idx_inv + 1) == _Idx + 1) { + array _Result{}; + for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) { + for (rank_type _Idx_inv = 0; _Idx_inv < _Rank; ++_Idx_inv) { + if (_Dynamic_indices[_Idx_inv + 1] == _Idx + 1) { _Result[_Idx] = _Idx_inv; break; } @@ -79,11 +72,7 @@ private: return _Result; } - static constexpr array _Dynamic_indices_inv = _Make_dynamic_indices_inv(); - - _NODISCARD static constexpr rank_type _Dynamic_index_inv(rank_type _Idx) noexcept { - return _Dynamic_indices_inv[_Idx]; - } + static constexpr array _Dynamic_indices_inv = _Make_dynamic_indices_inv(); struct _Static_extents_only { constexpr explicit _Static_extents_only() noexcept = default; @@ -96,30 +85,33 @@ private: } }; - static constexpr rank_type _Rank_dynamic = _Dynamic_index(rank()); conditional_t<_Rank_dynamic != 0, array, _Static_extents_only> _Dynamic_extents{}; public: + _NODISCARD static constexpr rank_type rank() noexcept { + return _Rank; + } + _NODISCARD static constexpr rank_type rank_dynamic() noexcept { return _Rank_dynamic; } _NODISCARD static constexpr size_t static_extent(const rank_type _Idx) noexcept { - _STL_VERIFY(_Idx < rank(), "Index must be less than rank() (N4944 [mdspan.extents.obs]/1)"); + _STL_VERIFY(_Idx < _Rank, "Index must be less than rank() (N4944 [mdspan.extents.obs]/1)"); return _Static_extents[_Idx]; } _NODISCARD constexpr index_type extent(const rank_type _Idx) const noexcept { - _STL_VERIFY(_Idx < rank(), "Index must be less than rank() (N4944 [mdspan.extents.obs]/3)"); + _STL_VERIFY(_Idx < _Rank, "Index must be less than rank() (N4944 [mdspan.extents.obs]/3)"); if constexpr (rank_dynamic() == 0) { - return static_cast(static_extent(_Idx)); + return static_cast(_Static_extents[_Idx]); } else if constexpr (rank_dynamic() == rank()) { return _Dynamic_extents[_Idx]; } else { - if (static_extent(_Idx) == dynamic_extent) { - return _Dynamic_extents[_Dynamic_index(_Idx)]; + if (_Static_extents[_Idx] == dynamic_extent) { + return _Dynamic_extents[_Dynamic_indices[_Idx]]; } else { - return static_cast(static_extent(_Idx)); + return static_cast(_Static_extents[_Idx]); } } } @@ -133,16 +125,16 @@ public: || (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) { + for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) { _STL_VERIFY( - static_extent(_Idx) == dynamic_extent || _STD cmp_equal(static_extent(_Idx), _Other.extent(_Idx)), + _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 " "(N4944 [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 " "(N4944 [mdspan.extents.cons]/2.2)"); - if (static_extent(_Idx) == dynamic_extent) { + if (_Static_extents[_Idx] == dynamic_extent) { *_It = static_cast(_Other.extent(_Idx)); ++_It; } @@ -167,12 +159,12 @@ public: } else { array _Exts_arr{static_cast(_STD move(_Exts))...}; auto _It = _Dynamic_extents.begin(); - for (rank_type _Idx = 0; _Idx < rank(); ++_Idx) { + for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) { _STL_VERIFY( - static_extent(_Idx) == dynamic_extent || _STD cmp_equal(static_extent(_Idx), _Exts_arr[_Idx]), + _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 " "(N4944 [mdspan.extents.cons]/7.1)"); - if (static_extent(_Idx) == dynamic_extent) { + if (_Static_extents[_Idx] == dynamic_extent) { *_It = _Exts_arr[_Idx]; ++_It; } @@ -199,10 +191,11 @@ public: 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_index_inv(_Indices)]))...} { + : _Dynamic_extents{static_cast(_STD as_const(_Exts[_Dynamic_indices_inv[_Indices]]))...} { if constexpr (_Is_standard_integer<_OtherIndexType>) { - for (rank_type _Idx = 0; _Idx < rank(); ++_Idx) { - _STL_VERIFY(static_extent(_Idx) == dynamic_extent || _STD cmp_equal(static_extent(_Idx), _Exts[_Idx]), + for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) { + _STL_VERIFY( + _Static_extents[_Idx] == dynamic_extent || _STD cmp_equal(_Static_extents[_Idx], _Exts[_Idx]), "Value of exts[r] must be equal to extent(r) for each r for which extent(r) is a static extent " "(N4944 [mdspan.extents.cons]/10.1)"); _STL_VERIFY(_Exts[_Idx] >= 0 && _STD in_range(_Exts[_Idx]), @@ -232,7 +225,7 @@ public: if constexpr (rank() != sizeof...(_OtherExtents)) { return false; } else { - for (rank_type _Idx = 0; _Idx < rank(); ++_Idx) { + for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) { if (_STD cmp_not_equal(_Left.extent(_Idx), _Right.extent(_Idx))) { return false; } @@ -253,7 +246,7 @@ public: // TRANSITION, LWG ISSUE? I believe that this function should return 'index_type' _NODISCARD constexpr index_type _Rev_prod_of_extents(const rank_type _Idx) const noexcept { index_type _Result = 1; - for (rank_type _Dim = _Idx + 1; _Dim < rank(); ++_Dim) { + for (rank_type _Dim = _Idx + 1; _Dim < _Rank; ++_Dim) { _Result *= extent(_Dim); } return _Result; @@ -361,7 +354,7 @@ public: const bool _Verify = [&](index_sequence<_Indices...>) { index_type _Prod = 1; return ((_Other.stride(_Indices) - == (_Indices == extents_type::rank() - 1 + == (_Indices == extents_type::_Rank - 1 ? _Prod : _STD exchange(_Prod, static_cast(_Prod * _Exts.extent(_Indices))))) && ...); @@ -382,7 +375,7 @@ public: } _NODISCARD constexpr index_type required_span_size() const noexcept { - return _Exts._Fwd_prod_of_extents(extents_type::rank()); + return _Exts._Fwd_prod_of_extents(extents_type::_Rank); } template @@ -419,7 +412,7 @@ public: _NODISCARD constexpr index_type stride(const rank_type _Idx) const noexcept requires (extents_type::rank() > 0) { - _STL_VERIFY(_Idx < extents_type::rank(), + _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); } @@ -494,7 +487,7 @@ public: index_type _Prod = stride(0); return ( (_Other.stride(_Indices) - == (_Indices == extents_type::rank() - 1 + == (_Indices == extents_type::_Rank - 1 ? _Prod : _STD exchange(_Prod, static_cast(_Prod / _Exts.extent(_Indices + 1))))) && ...); @@ -515,7 +508,7 @@ public: } _NODISCARD constexpr index_type required_span_size() const noexcept { - return _Exts._Fwd_prod_of_extents(extents_type::rank()); + return _Exts._Fwd_prod_of_extents(extents_type::_Rank); } template @@ -552,7 +545,7 @@ public: _NODISCARD constexpr index_type stride(const rank_type _Idx) const noexcept requires (extents_type::rank() > 0) { - _STL_VERIFY(_Idx < extents_type::rank(), + _STL_VERIFY(_Idx < extents_type::_Rank, "Value of i must be less than extents_type::rank() (N4944 [mdspan.layout.right.obs]/6)."); return _Exts._Rev_prod_of_extents(_Idx); } @@ -606,7 +599,7 @@ public: constexpr mapping() noexcept : _Exts(extents_type{}) { if constexpr (extents_type::rank() != 0) { _Strides.back() = 1; - for (rank_type _Idx = extents_type::rank() - 1; _Idx-- > 0;) { + for (rank_type _Idx = extents_type::_Rank - 1; _Idx-- > 0;) { // TRANSITION USE `_Multiply_with_overflow_check` IN DEBUG MODE _Strides[_Idx] = _Strides[_Idx + 1] * _Exts.extent(_Idx + 1); } @@ -622,7 +615,7 @@ public: constexpr mapping(const extents_type& _Exts_, span<_OtherIndexType, extents_type::rank()> _Strides_, index_sequence<_Indices...>) noexcept : _Exts(_Exts_), _Strides{static_cast(_STD as_const(_Strides_[_Indices]))...} { - for (rank_type _Idx = 0; _Idx < extents_type::rank(); ++_Idx) { + 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_) " "(N4944 [mdspan.layout.stride.cons]/4.1)."); @@ -671,7 +664,7 @@ public: "[mdspan.layout.stride.cons]/7.3)."); _STL_VERIFY( _Offset(_Other) == 0, "Value of OFFSET(other) must be equal to 0 (N4944 [mdspan.layout.stride.cons]/7.4)."); - for (rank_type _Idx = 0; _Idx < extents_type::rank(); ++_Idx) { + for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { const auto _Stride = _Other.stride(_Idx); _STL_VERIFY(_Stride > 0, "Value of other.stride(r) must be greater than 0 for every rank index r of " "extents() (N4944 [mdspan.layout.stride.cons]/7.2)."); @@ -696,7 +689,7 @@ public: return 1; } else { index_type _Result = 1; - for (rank_type _Idx = 0; _Idx < extents_type::rank(); ++_Idx) { + for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { const index_type _Ext = _Exts.extent(_Idx); if (_Ext == 0) { return 0; @@ -737,7 +730,7 @@ public: if constexpr (extents_type::rank() == 0) { return true; } else { - return required_span_size() == _Exts._Fwd_prod_of_extents(extents_type::rank()); + return required_span_size() == _Exts._Fwd_prod_of_extents(extents_type::_Rank); } } @@ -758,7 +751,7 @@ public: return false; } - for (rank_type _Idx = 0; _Idx < extents_type::rank(); ++_Idx) { + for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { if (_Left.stride(_Idx) != _Right.stride(_Idx)) { return false; } @@ -777,7 +770,7 @@ private: if constexpr (extents_type::rank() == 0) { return _Mapping(); } else { - for (rank_type _Idx = 0; _Idx < extents_type::rank(); ++_Idx) { + for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { if (_Mapping.extents().extent(_Idx) == 0) { return 0; } @@ -855,11 +848,11 @@ public: "[mdspan.mdspan.overview]/2.3)."); _NODISCARD static constexpr rank_type rank() noexcept { - return extents_type::rank(); + return extents_type::_Rank; } _NODISCARD static constexpr rank_type rank_dynamic() noexcept { - return extents_type::rank_dynamic(); + return extents_type::_Rank_dynamic; } _NODISCARD static constexpr size_t static_extent(const rank_type _Idx) noexcept { @@ -956,11 +949,11 @@ public: } _NODISCARD constexpr size_type size() const noexcept { - return static_cast(_Map.extents()._Fwd_prod_of_extents(rank())); + return static_cast(_Map.extents()._Fwd_prod_of_extents(extents_type::_Rank)); } _NODISCARD constexpr bool empty() const noexcept { - for (rank_type _Idx = 0; _Idx < rank(); ++_Idx) { + for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { if (_Map.extents().extent(_Idx) == 0) { return true; } diff --git a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp index 57683fd893..960c7fe4e6 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp @@ -15,7 +15,7 @@ using namespace std; template constexpr void do_check_members(const extents& ext, - const array strs, index_sequence) { + const array& strs, index_sequence) { using Ext = extents; using Strides = array; using Mapping = layout_stride::mapping; @@ -116,7 +116,7 @@ constexpr void do_check_members(const extents& ext, // Tests of 'is_exhaustive' are defined in 'check_is_exhaustive' function [FIXME] } - { // Check 'stride' function (intentionally not if constexpr) + { // Check 'stride' function for (size_t i = 0; i < strs.size(); ++i) { same_as decltype(auto) s = m.stride(i); assert(strs[i] == s); @@ -132,7 +132,7 @@ constexpr void do_check_members(const extents& ext, } template -constexpr void check_members(extents ext, const array strides) { +constexpr void check_members(extents ext, const array& strides) { do_check_members(ext, strides, make_index_sequence{}); } From 2bb397cc36d8111dfa76712e61d37c708a739b99 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 11 May 2023 06:45:13 +0200 Subject: [PATCH 18/57] ``: Diversify integer types used in tests (#3682) Co-authored-by: Stephan T. Lavavej --- stl/inc/mdspan | 6 +- tests/std/include/test_mdspan_support.hpp | 63 +++++++++++++++++++ .../tests/P0009R18_mdspan_extents/test.cpp | 17 ++--- .../P0009R18_mdspan_layout_left/test.cpp | 18 ++---- .../P0009R18_mdspan_layout_right/test.cpp | 21 +++---- .../P0009R18_mdspan_layout_stride/test.cpp | 63 +++++++++++++++---- 6 files changed, 137 insertions(+), 51 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index ff1766909f..638943cb4c 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -601,7 +601,7 @@ public: _Strides.back() = 1; for (rank_type _Idx = extents_type::_Rank - 1; _Idx-- > 0;) { // TRANSITION USE `_Multiply_with_overflow_check` IN DEBUG MODE - _Strides[_Idx] = _Strides[_Idx + 1] * _Exts.extent(_Idx + 1); + _Strides[_Idx] = static_cast(_Strides[_Idx + 1] * _Exts.extent(_Idx + 1)); } } } @@ -752,7 +752,7 @@ public: } for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { - if (_Left.stride(_Idx) != _Right.stride(_Idx)) { + if (_STD cmp_not_equal(_Left.stride(_Idx), _Right.stride(_Idx))) { return false; } } @@ -786,7 +786,7 @@ private: template _NODISCARD constexpr index_type _Index_impl(index_sequence<_Seq...>, _IndexTypes... _Indices) const noexcept { _STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...)); - return ((_Indices * _Strides[_Seq]) + ... + 0); + return static_cast(((_Indices * _Strides[_Seq]) + ... + 0)); } }; diff --git a/tests/std/include/test_mdspan_support.hpp b/tests/std/include/test_mdspan_support.hpp index 842065b905..4fd9306bcf 100644 --- a/tests/std/include/test_mdspan_support.hpp +++ b/tests/std/include/test_mdspan_support.hpp @@ -3,9 +3,11 @@ #pragma once +#include #include #include #include +#include #include #include @@ -156,3 +158,64 @@ constexpr bool check_accessor_policy_requirements() { static_assert(detail::CheckMemberFunctionsOfAccessorPolicy); return true; } + +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; + }; + + // Check signed integers + fn(std::extents{select_extent(Extents)...}); + fn(std::extents{select_extent(Extents)...}); + fn(std::extents{select_extent(Extents)...}); + fn(std::extents{select_extent(Extents)...}); + fn(std::extents{select_extent(Extents)...}); + + // Check unsigned integers + fn(std::extents{select_extent(Extents)...}); + fn(std::extents{select_extent(Extents)...}); + fn(std::extents{select_extent(Extents)...}); + fn(std::extents{select_extent(Extents)...}); + fn(std::extents{select_extent(Extents)...}); + } + + 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}); + }; + + if constexpr (sizeof...(Seq) <= 1) { + check_members_with_mixed_extents<>(std::forward(fn)); + } else if constexpr (sizeof...(Seq) <= 2) { + (check_members_with_mixed_extents(std::forward(fn)), ...); + } else if constexpr (sizeof...(Seq) <= 4) { + (check_members_with_mixed_extents( + std::forward(fn)), + ...); + } else if constexpr (sizeof...(Seq) <= 8) { + (check_members_with_mixed_extents(std::forward(fn)), + ...); + } else if constexpr (sizeof...(Seq) <= 16) { + (check_members_with_mixed_extents(std::forward(fn)), + ...); + } else { + static_assert(sizeof...(Seq) <= 16, "We don't need more testing."); + } + } +} // namespace details + +template +constexpr void check_members_with_various_extents(Fn&& fn) { + details::check_members_with_various_extents_impl(std::forward(fn), std::make_index_sequence<1>{}); + details::check_members_with_various_extents_impl(std::forward(fn), std::make_index_sequence<2>{}); + details::check_members_with_various_extents_impl(std::forward(fn), std::make_index_sequence<4>{}); + details::check_members_with_various_extents_impl(std::forward(fn), std::make_index_sequence<8>{}); +#ifndef _PREFAST_ + details::check_members_with_various_extents_impl(std::forward(fn), std::make_index_sequence<16>{}); +#endif // _PREFAST_ +} diff --git a/tests/std/tests/P0009R18_mdspan_extents/test.cpp b/tests/std/tests/P0009R18_mdspan_extents/test.cpp index 83cf926a70..e7fa010988 100644 --- a/tests/std/tests/P0009R18_mdspan_extents/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_extents/test.cpp @@ -15,7 +15,7 @@ using namespace std; template -constexpr void do_check_members(index_sequence) { +constexpr void check_members(index_sequence) { using Ext = extents; // Each specialization of extents models regular and is trivially copyable @@ -80,11 +80,6 @@ constexpr void do_check_members(index_sequence) { } } -template -constexpr void check_members() { - do_check_members(make_index_sequence{}); -} - constexpr void check_construction_from_other_extents() { { // Check construction from too big or too small other extents using Ext = extents; @@ -292,16 +287,14 @@ constexpr void check_equality_operator() { } constexpr bool test() { - check_members(); - check_members(); - check_members(); - check_members(); - check_members(); - check_members(); + check_members_with_various_extents([](const extents&) { + check_members(make_index_sequence{}); + }); check_construction_from_other_extents(); check_construction_from_extents_pack(); check_construction_from_array_and_span(); check_equality_operator(); + return true; } diff --git a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp index 4322e1c864..fdae36b755 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp @@ -14,7 +14,7 @@ using namespace std; template -constexpr void do_check_members(const extents& ext, index_sequence) { +constexpr void check_members(const extents& ext, index_sequence) { using Ext = extents; using Mapping = layout_left::mapping; @@ -144,11 +144,6 @@ constexpr void do_check_members(const extents& ext, index } } -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>; @@ -356,18 +351,17 @@ constexpr void check_correctness() { } 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_members_with_various_extents( + [](const extents& ext) { + check_members(ext, make_index_sequence{}); + }); 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; } diff --git a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp index 4810d8a362..64f2e77f2c 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp @@ -14,7 +14,7 @@ using namespace std; template -constexpr void do_check_members(const extents& ext, index_sequence) { +constexpr void check_members(const extents& ext, index_sequence) { using Ext = extents; using Mapping = layout_right::mapping; @@ -51,6 +51,8 @@ constexpr void do_check_members(const extents& ext, index using Ext2 = extents; using Mapping2 = layout_right::mapping; +#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 other layout_right::mapping Mapping m1{ext}; Mapping2 m2{m1}; @@ -138,11 +140,7 @@ constexpr void do_check_members(const extents& ext, index 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{}); +#pragma warning(pop) // TRANSITION, "/analyze:only" BUG? } constexpr void check_construction_from_other_right_mapping() { @@ -367,18 +365,17 @@ constexpr void check_correctness() { } 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_members_with_various_extents( + [](const extents& ext) { + check_members(ext, make_index_sequence{}); + }); check_construction_from_other_right_mapping(); check_construction_from_other_left_mapping(); check_construction_from_other_stride_mapping(); check_call_operator(); check_comparisons(); check_correctness(); + return true; } diff --git a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp index 960c7fe4e6..7900fa7c80 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#include #include #include #include @@ -13,9 +14,16 @@ using namespace std; -template +struct CmpEqual { + template + [[nodiscard]] constexpr bool operator()(T t, U u) const noexcept { + return cmp_equal(t, u); + } +}; + +template constexpr void do_check_members(const extents& ext, - const array& strs, index_sequence) { + const array& strs, index_sequence) { using Ext = extents; using Strides = array; using Mapping = layout_stride::mapping; @@ -50,16 +58,16 @@ constexpr void do_check_members(const extents& ext, { // Check construction from extents_type and array Mapping m{ext, strs}; assert(m.extents() == ext); - assert(m.strides() == strs); + assert(ranges::equal(m.strides(), strs, CmpEqual{})); static_assert(is_nothrow_constructible_v); // Other tests are defined in 'check_construction_from_extents_and_array' function [FIXME] } { // Check construction from extents_type and span - using Span = span; + using Span = span; Mapping m{ext, Span{strs}}; assert(m.extents() == ext); - assert(m.strides() == strs); + assert(ranges::equal(m.strides(), strs, CmpEqual{})); static_assert(is_nothrow_constructible_v); // Other tests are defined in 'check_construction_from_extents_and_array' function [FIXME] } @@ -86,7 +94,7 @@ constexpr void do_check_members(const extents& ext, { // Check 'strides' function same_as decltype(auto) strs2 = m.strides(); - assert(strs2 == strs); + assert(ranges::equal(strs2, strs, CmpEqual{})); static_assert(noexcept(m.strides())); } @@ -119,7 +127,7 @@ constexpr void do_check_members(const extents& ext, { // Check 'stride' function for (size_t i = 0; i < strs.size(); ++i) { same_as decltype(auto) s = m.stride(i); - assert(strs[i] == s); + assert(cmp_equal(strs[i], s)); } } @@ -131,15 +139,46 @@ constexpr void do_check_members(const extents& ext, #pragma warning(pop) // TRANSITION, "/analyze:only" BUG? } +template +constexpr void check_members_with_different_strides_index_type( + extents ext, const array& strides) { + array test_strides; + ranges::transform(strides, test_strides.begin(), [](auto i) { return static_cast(i); }); + do_check_members(ext, test_strides, make_index_sequence{}); +} + template -constexpr void check_members(extents ext, const array& strides) { - do_check_members(ext, strides, make_index_sequence{}); +constexpr void check_members(extents ext, const array& strides) { + // Check signed strides + check_members_with_different_strides_index_type(ext, strides); + check_members_with_different_strides_index_type(ext, strides); + check_members_with_different_strides_index_type(ext, strides); + check_members_with_different_strides_index_type(ext, strides); + check_members_with_different_strides_index_type(ext, strides); + + // Check unsigned strides + check_members_with_different_strides_index_type(ext, strides); + check_members_with_different_strides_index_type(ext, strides); + check_members_with_different_strides_index_type(ext, strides); + check_members_with_different_strides_index_type(ext, strides); + check_members_with_different_strides_index_type(ext, strides); } constexpr bool test() { - check_members(extents{}, array{}); - check_members(extents{}, array{1}); - check_members(extents{3}, array{1, 3}); + // Check signed integers + check_members(extents{5}, array{1}); + check_members(extents{}, array{1, 6}); + check_members(extents{3}, array{1, 3}); + check_members(extents{}, array{1}); + check_members(extents{3}, array{1, 3, 6}); + + // Check unsigned integers + check_members(extents{5}, array{1}); + check_members(extents{}, array{1, 6}); + check_members(extents{3}, array{1, 3}); + check_members(extents{}, array{1}); + check_members(extents{3}, array{1, 3, 6}); + // TRANSITION more tests return true; } From 1d70c09c571a7b27f23678d38923f2972bdf2836 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 11 May 2023 06:45:33 +0200 Subject: [PATCH 19/57] ``: Add modules test (#3688) Co-authored-by: Stephan T. Lavavej --- tests/std/include/test_header_units_and_modules.hpp | 12 ++++++++++++ tests/std/tests/include_each_header_alone_matrix.lst | 1 + 2 files changed, 13 insertions(+) diff --git a/tests/std/include/test_header_units_and_modules.hpp b/tests/std/include/test_header_units_and_modules.hpp index 33fc4115a7..8e00ddd90b 100644 --- a/tests/std/include/test_header_units_and_modules.hpp +++ b/tests/std/include/test_header_units_and_modules.hpp @@ -389,6 +389,17 @@ void test_map() { assert(m[30] == 33); } +void test_mdspan() { + using namespace std; + puts("Testing ."); + int arr[] = {10, 0, 0, 0, 20, 0, 0, 0, 30}; + layout_right::mapping> mp; + assert(arr[mp(0, 0)] == 10); + assert(arr[mp(1, 1)] == 20); + assert(arr[mp(2, 2)] == 30); + // TRANSITION, test std::mdspan too (DevCom-10359857) +} + void test_memory() { using namespace std; puts("Testing ."); @@ -1097,6 +1108,7 @@ void all_cpp_header_tests() { test_list(); test_locale(); test_map(); + test_mdspan(); test_memory(); test_memory_resource(); test_mutex(); diff --git a/tests/std/tests/include_each_header_alone_matrix.lst b/tests/std/tests/include_each_header_alone_matrix.lst index 9b041e051e..4d07f7d8bb 100644 --- a/tests/std/tests/include_each_header_alone_matrix.lst +++ b/tests/std/tests/include_each_header_alone_matrix.lst @@ -44,6 +44,7 @@ PM_CL="/DMEOW_HEADER=limits" PM_CL="/DMEOW_HEADER=list" PM_CL="/DMEOW_HEADER=locale" PM_CL="/DMEOW_HEADER=map" +PM_CL="/DMEOW_HEADER=mdspan" PM_CL="/DMEOW_HEADER=memory" PM_CL="/DMEOW_HEADER=memory_resource" PM_CL="/DMEOW_HEADER=mutex" From 4bd089bd7b227519159d68c6d08a6a8b4025815d Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 11 May 2023 07:53:02 +0200 Subject: [PATCH 20/57] ``: Remove some C28020 warning suppressions (#3690) Co-authored-by: Stephan T. Lavavej --- stl/inc/mdspan | 9 --------- tests/std/tests/P0009R18_mdspan_layout_left/test.cpp | 6 +++--- .../std/tests/P0009R18_mdspan_layout_left_death/test.cpp | 3 --- .../tests/P0009R18_mdspan_layout_right_death/test.cpp | 3 --- tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp | 6 +++--- .../tests/P0009R18_mdspan_layout_stride_death/test.cpp | 3 --- 6 files changed, 6 insertions(+), 24 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 638943cb4c..0c188a582a 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -146,8 +146,6 @@ public: && (is_nothrow_constructible_v && ...) && (sizeof...(_OtherIndexTypes) == rank_dynamic() || sizeof...(_OtherIndexTypes) == rank()) constexpr explicit extents(_OtherIndexTypes... _Exts) noexcept { -#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 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 " @@ -170,7 +168,6 @@ public: } } } -#pragma warning(pop) // TRANSITION, "/analyze:only" BUG? } template @@ -594,8 +591,6 @@ public: "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 (N4944 [mdspan.layout.stride.overview]/4)."); -#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 constexpr mapping() noexcept : _Exts(extents_type{}) { if constexpr (extents_type::rank() != 0) { _Strides.back() = 1; @@ -605,7 +600,6 @@ public: } } } -#pragma warning(pop) // TRANSITION, "/analyze:only" BUG? constexpr mapping(const mapping&) noexcept = default; @@ -682,8 +676,6 @@ public: return _Strides; } -#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 _NODISCARD constexpr index_type required_span_size() const noexcept { if constexpr (extents_type::rank() == 0) { return 1; @@ -701,7 +693,6 @@ public: return _Result; } } -#pragma warning(pop) // TRANSITION, "/analyze:only" BUG? template requires (sizeof...(_IndexTypes) == extents_type::rank()) && (is_convertible_v<_IndexTypes, index_type> && ...) diff --git a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp index fdae36b755..5b77ab1760 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp @@ -75,14 +75,15 @@ constexpr void check_members(const extents& ext, index_se // 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{}; if constexpr (Ext::rank() > 0) { strides.front() = 1; for (size_t i = 1; i < Ext::rank(); ++i) { +#pragma warning(push) +#pragma warning(disable : 28020) // TRANSITION, DevCom-923103 strides[i] = static_cast(strides[i - 1] * ext.extent(i - 1)); +#pragma warning(pop) } } @@ -91,7 +92,6 @@ constexpr void check_members(const extents& ext, index_se [[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 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 64fd0f7c72..90425880ec 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_left_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_left_death/test.cpp @@ -24,8 +24,6 @@ void test_construction_from_other_right_mapping() { 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}}; @@ -39,7 +37,6 @@ void test_construction_from_other_stride_mapping_2() { // 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; 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 449227a714..aed180045f 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_right_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_right_death/test.cpp @@ -24,8 +24,6 @@ void test_construction_from_other_left_mapping() { layout_right::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{3, 1}}; @@ -39,7 +37,6 @@ void test_construction_from_other_stride_mapping_2() { // Value of other.required_span_size() must be representable as a value of type index_type layout_right::mapping> m2{m1}; } -#pragma warning(pop) // TRANSITION, "/analyze:only" BUG? void test_stride_function() { layout_right::mapping> m; diff --git a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp index 7900fa7c80..4f98daa7b9 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp @@ -43,8 +43,6 @@ constexpr void do_check_members(const extents& ext, static_assert(same_as); static_assert(same_as); -#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 default and copy constructor Mapping m; const Mapping cpy = m; @@ -127,7 +125,10 @@ constexpr void do_check_members(const extents& ext, { // Check 'stride' function for (size_t i = 0; i < strs.size(); ++i) { same_as decltype(auto) s = m.stride(i); +#pragma warning(push) +#pragma warning(disable : 28020) // TRANSITION, DevCom-923103 assert(cmp_equal(strs[i], s)); +#pragma warning(pop) } } @@ -136,7 +137,6 @@ constexpr void do_check_members(const extents& ext, assert(!(m != m)); // Other tests are defined in 'check_comparisons' function [FIXME] } -#pragma warning(pop) // TRANSITION, "/analyze:only" BUG? } template 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 4d66c20b0e..eadd0d3a49 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp @@ -10,8 +10,6 @@ using namespace std; -#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_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}}; @@ -28,7 +26,6 @@ void test_construction_from_strided_layout_mapping() { // Value of other.required_span_size() must be representable as a value of type index_type [[maybe_unused]] layout_stride::mapping> m2{m1}; } -#pragma warning(pop) // TRANSITION, "/analyze:only" BUG? int main(int argc, char* argv[]) { std_testing::death_test_executive exec; From 98a3f6a04f25ad3d946fd1420bfd9d5488fe1e3f Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 11 May 2023 20:32:52 +0200 Subject: [PATCH 21/57] ``: Document CTAD workaround (#3693) --- stl/inc/mdspan | 4 ++-- tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 0c188a582a..0d2fa374ad 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -616,7 +616,7 @@ public: } } -#ifndef __clang__ // TRANSITION, MSVC messes up CTAD when concepts are used here (needs further investigation) +#ifndef __clang__ // TRANSITION, DevCom-10360833 template && is_nothrow_constructible_v, int> = 0> @@ -629,7 +629,7 @@ public: : mapping(_Exts_, _Strides_, make_index_sequence{}) { } -#ifndef __clang__ // TRANSITION, MSVC messes up CTAD when concepts are used here (needs further investigation) +#ifndef __clang__ // TRANSITION, DevCom-10360833 template && is_nothrow_constructible_v, int> = 0> diff --git a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp index 4f98daa7b9..e18d918656 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp @@ -164,6 +164,14 @@ constexpr void check_members(extents ext, const array(ext, strides); } +constexpr void check_ctad() { + extents e; + array s{1, 2}; + layout_stride::mapping m{e, s}; + assert(m.extents() == e); + assert(m.strides() == s); +} + constexpr bool test() { // Check signed integers check_members(extents{5}, array{1}); @@ -179,6 +187,8 @@ constexpr bool test() { check_members(extents{}, array{1}); check_members(extents{3}, array{1, 3, 6}); + check_ctad(); + // TRANSITION more tests return true; } From 8eb2c9e3e7184b312629b0f041849b3351d7a178 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Sat, 13 May 2023 21:00:59 +0200 Subject: [PATCH 22/57] ``: Cite N4950 instead of N4944 (#3698) --- stl/inc/mdspan | 82 +++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 0d2fa374ad..670f641c47 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -33,10 +33,10 @@ public: using rank_type = size_t; static_assert(_Is_standard_integer, - "IndexType must be a signed or unsigned integer type (N4944 [mdspan.extents.overview]/1.1)."); + "IndexType must be a signed or unsigned integer type (N4950 [mdspan.extents.overview]/1.1)."); static_assert(((_Extents == dynamic_extent || _STD in_range(_Extents)) && ...), "Each element of Extents must be either equal to dynamic_extent, or must be representable as a value of type " - "IndexType (N4944 [mdspan.extents.overview]/1.2)."); + "IndexType (N4950 [mdspan.extents.overview]/1.2)."); static constexpr rank_type _Rank = sizeof...(_Extents); static constexpr rank_type _Rank_dynamic = (static_cast(_Extents == dynamic_extent) + ... + 0); @@ -97,12 +97,12 @@ public: } _NODISCARD static constexpr size_t static_extent(const rank_type _Idx) noexcept { - _STL_VERIFY(_Idx < _Rank, "Index must be less than rank() (N4944 [mdspan.extents.obs]/1)"); + _STL_VERIFY(_Idx < _Rank, "Index must be less than rank() (N4950 [mdspan.extents.obs]/1)"); return _Static_extents[_Idx]; } _NODISCARD constexpr index_type extent(const rank_type _Idx) const noexcept { - _STL_VERIFY(_Idx < _Rank, "Index must be less than rank() (N4944 [mdspan.extents.obs]/3)"); + _STL_VERIFY(_Idx < _Rank, "Index must be less than rank() (N4950 [mdspan.extents.obs]/3)"); if constexpr (rank_dynamic() == 0) { return static_cast(_Static_extents[_Idx]); } else if constexpr (rank_dynamic() == rank()) { @@ -129,10 +129,10 @@ public: _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 " - "(N4944 [mdspan.extents.cons]/2.1)"); + "(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 " - "(N4944 [mdspan.extents.cons]/2.2)"); + "(N4950 [mdspan.extents.cons]/2.2)"); if (_Static_extents[_Idx] == dynamic_extent) { *_It = static_cast(_Other.extent(_Idx)); @@ -149,7 +149,7 @@ public: 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 (N4944 [mdspan.extents.cons]/7.2)"); + "representable as value of type index_type (N4950 [mdspan.extents.cons]/7.2)"); } if constexpr (sizeof...(_Exts) == rank_dynamic()) { @@ -161,7 +161,7 @@ public: _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 " - "(N4944 [mdspan.extents.cons]/7.1)"); + "(N4950 [mdspan.extents.cons]/7.1)"); if (_Static_extents[_Idx] == dynamic_extent) { *_It = _Exts_arr[_Idx]; ++_It; @@ -179,7 +179,7 @@ public: 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 (N4944 [mdspan.extents.cons]/10.2)"); + "index_type for every rank index r (N4950 [mdspan.extents.cons]/10.2)"); } } } @@ -194,10 +194,10 @@ public: _STL_VERIFY( _Static_extents[_Idx] == dynamic_extent || _STD cmp_equal(_Static_extents[_Idx], _Exts[_Idx]), "Value of exts[r] must be equal to extent(r) for each r for which extent(r) is a static extent " - "(N4944 [mdspan.extents.cons]/10.1)"); + "(N4950 [mdspan.extents.cons]/10.1)"); _STL_VERIFY(_Exts[_Idx] >= 0 && _STD in_range(_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 (N4944 [mdspan.extents.cons]/10.2)"); + "index_type for every rank index r (N4950 [mdspan.extents.cons]/10.2)"); } } } @@ -311,10 +311,10 @@ public: using layout_type = layout_left; static_assert(_Is_extents, - "Extents must be a specialization of std::extents (N4944 [mdspan.layout.left.overview]/2)."); + "Extents must be a specialization of std::extents (N4950 [mdspan.layout.left.overview]/2)."); static_assert(extents_type::_Is_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 (N4944 [mdspan.layout.left.overview]/4)."); + "representable as a value of type typename Extents::index_type (N4950 [mdspan.layout.left.overview]/4)."); constexpr mapping() noexcept = default; constexpr mapping(const mapping&) noexcept = default; @@ -329,7 +329,7 @@ public: mapping(const mapping<_OtherExtents>& _Other) noexcept : _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 " + "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " "[mdspan.layout.left.cons]/4)."); } @@ -339,7 +339,7 @@ public: mapping(const layout_right::mapping<_OtherExtents>& _Other) noexcept : _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 " + "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " "[mdspan.layout.left.cons]/7)."); } @@ -358,10 +358,10 @@ public: } (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)."); + "extents().fwd-prod-of-extents(r) (N4950 [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 " + "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " "[mdspan.layout.left.cons]/10.2)."); } @@ -410,7 +410,7 @@ public: requires (extents_type::rank() > 0) { _STL_VERIFY(_Idx < extents_type::_Rank, - "Value of i must be less than extents_type::rank() (N4944 [mdspan.layout.left.obs]/6)."); + "Value of i must be less than extents_type::rank() (N4950 [mdspan.layout.left.obs]/6)."); return _Exts._Fwd_prod_of_extents(_Idx); } @@ -443,10 +443,10 @@ public: using layout_type = layout_right; static_assert(_Is_extents, - "Extents must be a specialization of std::extents (N4944 [mdspan.layout.right.overview]/2)."); + "Extents must be a specialization of std::extents (N4950 [mdspan.layout.right.overview]/2)."); static_assert(extents_type::_Is_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 (N4944 [mdspan.layout.right.overview]/4)."); + "representable as a value of type typename Extents::index_type (N4950 [mdspan.layout.right.overview]/4)."); constexpr mapping() noexcept = default; constexpr mapping(const mapping&) noexcept = default; @@ -461,7 +461,7 @@ public: mapping(const mapping<_OtherExtents>& _Other) noexcept : _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 " + "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " "[mdspan.layout.right.cons]/4)."); } @@ -471,7 +471,7 @@ public: mapping(const layout_left::mapping<_OtherExtents>& _Other) noexcept : _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 " + "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " "[mdspan.layout.right.cons]/7)."); } @@ -491,10 +491,10 @@ public: } (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) (N4944 [mdspan.layout.right.cons]/10.1)."); + "extents().rev-prod-of-extents(r) (N4950 [mdspan.layout.right.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 " + "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " "[mdspan.layout.right.cons]/10.2)."); } @@ -543,7 +543,7 @@ public: requires (extents_type::rank() > 0) { _STL_VERIFY(_Idx < extents_type::_Rank, - "Value of i must be less than extents_type::rank() (N4944 [mdspan.layout.right.obs]/6)."); + "Value of i must be less than extents_type::rank() (N4950 [mdspan.layout.right.obs]/6)."); return _Exts._Rev_prod_of_extents(_Idx); } @@ -586,10 +586,10 @@ public: using layout_type = layout_stride; static_assert(_Is_extents, - "Extents must be a specialization of std::extents (N4944 [mdspan.layout.stride.overview]/2)."); + "Extents must be a specialization of std::extents (N4950 [mdspan.layout.stride.overview]/2)."); static_assert(extents_type::_Is_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 (N4944 [mdspan.layout.stride.overview]/4)."); + "representable as a value of type typename Extents::index_type (N4950 [mdspan.layout.stride.overview]/4)."); constexpr mapping() noexcept : _Exts(extents_type{}) { if constexpr (extents_type::rank() != 0) { @@ -612,7 +612,7 @@ public: 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_) " - "(N4944 [mdspan.layout.stride.cons]/4.1)."); + "(N4950 [mdspan.layout.stride.cons]/4.1)."); } } @@ -654,14 +654,14 @@ public: mapping(const _StridedLayoutMapping& _Other) noexcept : _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 " + "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 (N4944 [mdspan.layout.stride.cons]/7.4)."); + _Offset(_Other) == 0, "Value of OFFSET(other) must be equal to 0 (N4950 [mdspan.layout.stride.cons]/7.4)."); for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { const auto _Stride = _Other.stride(_Idx); _STL_VERIFY(_Stride > 0, "Value of other.stride(r) must be greater than 0 for every rank index r of " - "extents() (N4944 [mdspan.layout.stride.cons]/7.2)."); + "extents() (N4950 [mdspan.layout.stride.cons]/7.2)."); _Strides[_Idx] = static_cast(_Stride); } } @@ -789,11 +789,11 @@ struct default_accessor { using data_handle_type = _ElementType*; static_assert( - sizeof(element_type) > 0, "ElementType must be a complete type (N4944 [mdspan.accessor.default.overview]/2)."); + sizeof(element_type) > 0, "ElementType must be a complete type (N4950 [mdspan.accessor.default.overview]/2)."); static_assert(!is_abstract_v, - "ElementType cannot be an abstract type (N4944 [mdspan.accessor.default.overview]/2)."); + "ElementType cannot be an abstract type (N4950 [mdspan.accessor.default.overview]/2)."); static_assert( - !is_array_v, "ElementType cannot be an array type (N4944 [mdspan.accessor.default.overview]/2)."); + !is_array_v, "ElementType cannot be an array type (N4950 [mdspan.accessor.default.overview]/2)."); constexpr default_accessor() noexcept = default; @@ -827,15 +827,15 @@ public: using reference = typename accessor_type::reference; static_assert( - sizeof(element_type) > 0, "ElementType must be a complete type (N4944 [mdspan.mdspan.overview]/2.1)."); + sizeof(element_type) > 0, "ElementType must be a complete type (N4950 [mdspan.mdspan.overview]/2.1)."); static_assert( - !is_abstract_v, "ElementType cannot be an abstract type (N4944 [mdspan.mdspan.overview]/2.1)."); + !is_abstract_v, "ElementType cannot be an abstract type (N4950 [mdspan.mdspan.overview]/2.1)."); static_assert( - !is_array_v, "ElementType cannot be an array type (N4944 [mdspan.mdspan.overview]/2.1)."); + !is_array_v, "ElementType cannot be an array type (N4950 [mdspan.mdspan.overview]/2.1)."); static_assert(_Is_extents, - "Extents must be a specialization of std::extents (N4944 [mdspan.mdspan.overview]/2.2)."); + "Extents must be a specialization of std::extents (N4950 [mdspan.mdspan.overview]/2.2)."); static_assert(is_same_v, - "ElementType and typename AccessorPolicy::element_type must be the same type (N4944 " + "ElementType and typename AccessorPolicy::element_type must be the same type (N4950 " "[mdspan.mdspan.overview]/2.3)."); _NODISCARD static constexpr rank_type rank() noexcept { @@ -907,10 +907,10 @@ public: mdspan(const mdspan<_OtherElementType, _OtherExtents, _OtherLayoutPolicy, _OtherAccessor>& _Other) : _Ptr(_Other._Ptr), _Map(_Other._Map), _Acc(_Other._Acc) { static_assert(is_constructible_v, - "The data_handle_type must be constructible from const typename OtherAccessor::data_handle_type& (N4944 " + "The data_handle_type must be constructible from const typename OtherAccessor::data_handle_type& (N4950 " "[mdspan.mdspan.cons]/20.1)."); static_assert(is_constructible_v, - "The extents_type must be constructible from OtherExtents (N4944 [mdspan.mdspan.cons]/20.2)."); + "The extents_type must be constructible from OtherExtents (N4950 [mdspan.mdspan.cons]/20.2)."); } constexpr mdspan& operator=(const mdspan&) = default; From cdac55bf4531e1d6529f7a4b765e528c962c8bba Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Mon, 15 May 2023 17:38:27 +0200 Subject: [PATCH 23/57] ``: Improve `layout_stride` tests (#3701) --- .../P0009R18_mdspan_layout_stride/test.cpp | 381 +++++++++++++++++- 1 file changed, 368 insertions(+), 13 deletions(-) diff --git a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp index e18d918656..99dcce276d 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp @@ -58,7 +58,7 @@ constexpr void do_check_members(const extents& ext, assert(m.extents() == ext); assert(ranges::equal(m.strides(), strs, CmpEqual{})); static_assert(is_nothrow_constructible_v); - // Other tests are defined in 'check_construction_from_extents_and_array' function [FIXME] + // Other tests are defined in 'check_construction_from_extents_and_array' function } { // Check construction from extents_type and span @@ -67,7 +67,7 @@ constexpr void do_check_members(const extents& ext, assert(m.extents() == ext); assert(ranges::equal(m.strides(), strs, CmpEqual{})); static_assert(is_nothrow_constructible_v); - // Other tests are defined in 'check_construction_from_extents_and_array' function [FIXME] + // Other tests are defined in 'check_construction_from_extents_and_array' function } using OtherIndexType = long long; @@ -79,7 +79,7 @@ constexpr void do_check_members(const extents& ext, Mapping2 m2{m1}; assert(m1 == m2); static_assert(is_nothrow_constructible_v); - // Other tests are defined in 'check_construction_from_other_mappings' function [FIXME] + // Other tests are defined in 'check_construction_from_other_mappings' function } Mapping m{ext, strs}; // For later use @@ -96,8 +96,7 @@ constexpr void do_check_members(const extents& ext, static_assert(noexcept(m.strides())); } - // Function 'required_span_size' is tested in 'check_required_span_size' function[FIXME] - { // Check 'required_span_size' function[FIXME] + { // Check 'required_span_size' function if (((ext.extent(Indices) == 0) || ...)) { assert(m.required_span_size() == 0); } else { @@ -106,6 +105,7 @@ constexpr void do_check_members(const extents& ext, assert(m.required_span_size() == expected_value); } static_assert(noexcept(m.required_span_size())); + // Other tests are defined in 'check_required_span_size' function } // Call operator() is tested in 'check_call_operator' function @@ -119,7 +119,7 @@ constexpr void do_check_members(const extents& ext, { // Check 'is_[unique/strided]' functions static_assert(Mapping::is_unique()); static_assert(Mapping::is_strided()); - // Tests of 'is_exhaustive' are defined in 'check_is_exhaustive' function [FIXME] + // Tests of 'is_exhaustive' are defined in 'check_is_exhaustive' function } { // Check 'stride' function @@ -135,7 +135,7 @@ constexpr void do_check_members(const extents& ext, { // Check comparisons assert(m == m); assert(!(m != m)); - // Other tests are defined in 'check_comparisons' function [FIXME] + // Other tests are defined in 'check_comparisons' function } } @@ -164,12 +164,361 @@ constexpr void check_members(extents ext, const array(ext, strides); } +constexpr void check_construction_from_extents_and_array() { + // Check invalid construction + using Mapping = layout_stride::mapping>; + static_assert(!is_constructible_v, array>); + static_assert(!is_constructible_v, array>); + static_assert(!is_constructible_v, array>); + static_assert(!is_constructible_v, array>); + static_assert(!is_constructible_v, span>); + static_assert(!is_constructible_v, span>); + static_assert(!is_constructible_v, span>); + static_assert(!is_constructible_v, span>); + static_assert(!is_constructible_v, array>); + static_assert(!is_constructible_v, span>); + static_assert(!is_constructible_v, array, 2>>); + static_assert(!is_constructible_v, span, 2>>); +} + +constexpr void check_construction_from_other_mappings() { + { // Check construction + using Mapping = layout_stride::mapping>; + static_assert(is_constructible_v>>); + static_assert(is_constructible_v>>); + static_assert(is_constructible_v>>); + static_assert(is_constructible_v>>); + static_assert(is_constructible_v>>); + } + + { // Check invalid construction + using Mapping = layout_stride::mapping>; + static_assert(!is_constructible_v>>); + static_assert(!is_constructible_v>>); + static_assert(!is_constructible_v>>); + static_assert(!is_constructible_v>>); + static_assert(!is_constructible_v>>); + // TRANSITION, Check other kinds of invalid construction (requires new helper types) + } + + { // Check construction from layout_left::mapping + layout_left::mapping> left_mapping{dextents{4, 3, 2}}; + layout_stride::mapping> strided_mapping{left_mapping}; + assert(ranges::equal(strided_mapping.strides(), array{1, 4, 12}, CmpEqual{})); + } + + { // Check construction from layout_right::mapping + layout_right::mapping> right_mapping{dextents{4, 3, 2}}; + layout_stride::mapping> strided_mapping{right_mapping}; + assert(ranges::equal(strided_mapping.strides(), array{6, 2, 1}, CmpEqual{})); + } +} + +constexpr void check_required_span_size() { + { // Check [mdspan.layout.stride.expo]/1.1: Ext::rank() == 0 + using M1 = layout_stride::mapping>; + static_assert(M1{}.required_span_size() == 1); + + layout_stride::mapping> m2; + assert(m2.required_span_size() == 1); + } + + { // Check [mdspan.layout.stride.expo]/1.2: size of the multidimensional index space e is 0 + using M1 = layout_stride::mapping>; + static_assert(M1{}.required_span_size() == 0); + + layout_stride::mapping> m2{dextents{3, 0, 3, 3}, array{1, 3, 1, 1}}; + assert(m2.required_span_size() == 0); + } + + { // Check [mdspan.layout.stride.expo]/1.3: final case + using M1 = layout_stride::mapping>; + static_assert(M1{}.required_span_size() == 36); + + layout_stride::mapping> m2{dextents{4, 3, 4}, array{1, 4, 12}}; + assert(m2.required_span_size() == 48); + } +} + +constexpr void check_is_exhaustive() { + { // Check exhaustive mappings (all possibilities) + using E = extents; + assert((layout_stride::mapping{E{}, array{1, 2, 6}}.is_exhaustive())); + assert((layout_stride::mapping{E{}, array{1, 10, 2}}.is_exhaustive())); + assert((layout_stride::mapping{E{}, array{3, 1, 6}}.is_exhaustive())); + assert((layout_stride::mapping{E{}, array{15, 1, 3}}.is_exhaustive())); + assert((layout_stride::mapping{E{}, array{5, 10, 1}}.is_exhaustive())); + assert((layout_stride::mapping{E{}, array{15, 5, 1}}.is_exhaustive())); + } + + { // Check non-exhaustive mappings + using E = extents; + assert((!layout_stride::mapping{E{}, array{1, 2, 12}}.is_exhaustive())); + assert((!layout_stride::mapping{E{}, array{8, 18, 1}}.is_exhaustive())); + assert((!layout_stride::mapping{E{}, array{5, 1, 12}}.is_exhaustive())); + } +} + +constexpr void check_call_operator() { + { // Check call with invalid amount of indices + using Mapping = layout_stride::mapping>; + static_assert(!CheckCallOperatorOfLayoutMapping); + static_assert(!CheckCallOperatorOfLayoutMapping); + static_assert(CheckCallOperatorOfLayoutMapping); + static_assert(!CheckCallOperatorOfLayoutMapping); + } + + { // Check call with invalid types + using Mapping = layout_stride::mapping>; + static_assert(CheckCallOperatorOfLayoutMapping); + static_assert(CheckCallOperatorOfLayoutMapping); + static_assert(CheckCallOperatorOfLayoutMapping>); + static_assert(CheckCallOperatorOfLayoutMapping>); + static_assert(!CheckCallOperatorOfLayoutMapping); + } + + { // Check call with types that might throw during conversion + using Mapping = layout_stride::mapping>; + static_assert(CheckCallOperatorOfLayoutMapping>); + static_assert(!CheckCallOperatorOfLayoutMapping>); + } + + { // Check various mappings + layout_stride::mapping> m1; + assert(m1() == 0); + + layout_stride::mapping> m2{dextents{4}, array{1}}; + assert(m2(0) == 0); + assert(m2(1) == 1); + assert(m2(2) == 2); + assert(m2(3) == 3); + + layout_stride::mapping> m3{{}, array{1, 5}}; // non-exhaustive mapping + assert(!m3.is_exhaustive()); + assert(m3(0, 0) == 0); + assert(m3(0, 1) == 5); + assert(m3(0, 2) == 10); + assert(m3(0, 3) == 15); + assert(m3(0, 4) == 20); + assert(m3(1, 0) == 1); + assert(m3(1, 1) == 6); + assert(m3(1, 2) == 11); + assert(m3(1, 3) == 16); + assert(m3(1, 4) == 21); + assert(m3(2, 0) == 2); + assert(m3(2, 1) == 7); + assert(m3(2, 2) == 12); + assert(m3(2, 3) == 17); + assert(m3(3, 0) == 3); + assert(m3(3, 1) == 8); + assert(m3(3, 2) == 13); + assert(m3(3, 4) == 23); + + layout_stride::mapping> m4{{}, array{15, 1, 3}}; // exhaustive mapping + assert(m4.is_exhaustive()); + assert(m4(0, 0, 0) == 0); + assert(m4(0, 0, 1) == 3); + assert(m4(0, 1, 0) == 1); + assert(m4(0, 1, 1) == 4); + assert(m4(1, 0, 0) == 15); + assert(m4(1, 0, 1) == 18); + assert(m4(1, 1, 0) == 16); + assert(m4(1, 1, 1) == 19); + assert(m4(1, 2, 4) == 29); + } +} + +constexpr void check_comparisons() { + using E = extents; + using StaticStrideMapping = layout_stride::mapping; + using DynamicStrideMapping = layout_stride::mapping>; + using RightMapping = layout_right::mapping; + using LeftMapping = layout_left::mapping; + + { // Check equality_comparable_with concept + static_assert(equality_comparable_with); + static_assert(equality_comparable_with); + static_assert(equality_comparable_with); + static_assert(equality_comparable_with); + static_assert(equality_comparable_with); + static_assert(!equality_comparable_with>>); + static_assert(!equality_comparable_with>>); + static_assert(!equality_comparable_with>>); + static_assert(!equality_comparable_with>>); + // TRANSITION, Check other constraints: [mdspan.layout.stride.obs]/6.1, 6.3 + } + + { // Check correctness: layout_stride::mapping with layout_stride::mapping + StaticStrideMapping m1{E{}, array{3, 1}}; + DynamicStrideMapping m2{dextents{2, 3}, array{3, 1}}; + assert(m1 == m2); // extents are equal, OFFSET(rhs) == 0, strides are equal + + DynamicStrideMapping m3{dextents{2, 3}, array{1, 2}}; + assert(m1 != m3); // extents are equal, OFFSET(rhs) == 0, strides are not equal + assert(m2 != m3); // ditto + + DynamicStrideMapping m4{dextents{1, 3}, array{3, 1}}; + assert(m1 != m4); // extents are not equal, OFFSET(rhs) == 0, strides are equal + assert(m2 != m4); // ditto + assert(m3 != m4); // extents are not equal, OFFSET(rhs) == 0, strides are not equal + + // NB: OFFSET(layout_stride::mapping) is always equal to 0 + } + + { // Check correctness: layout_stride::mapping with layout_left::mapping + LeftMapping m1; + StaticStrideMapping m2{E{}, array{1, 2}}; + assert(m1 == m2); // extents are equal, OFFSET(rhs) == 0, strides are equal + + DynamicStrideMapping m3{dextents{2, 3}, array{3, 1}}; + assert(m1 != m3); // extents are equal, OFFSET(rhs) == 0, strides are not equal + assert(m2 != m3); // ditto + + DynamicStrideMapping m4{dextents{2, 1}, array{1, 2}}; + assert(m1 != m4); // extents are not equal, OFFSET(rhs) == 0, strides are equal + assert(m2 != m4); // ditto + assert(m3 != m4); // extents are not equal, OFFSET(rhs) == 0, strides are not equal + + // NB: OFFSET(layout_left::mapping) is always equal to 0 + } + + { // Check correctness: layout_stride::mapping with layout_right::mapping + RightMapping m1; + StaticStrideMapping m2{E{}, array{3, 1}}; + assert(m1 == m2); // extents are equal, OFFSET(rhs) == 0, strides are equal + + DynamicStrideMapping m3{dextents{2, 3}, array{1, 2}}; + assert(m1 != m3); // extents are equal, OFFSET(rhs) == 0, strides are not equal + assert(m2 != m3); // ditto + + DynamicStrideMapping m4{dextents{1, 3}, array{3, 1}}; + assert(m1 != m4); // extents are not equal, OFFSET(rhs) == 0, strides are equal + assert(m2 != m4); // ditto + assert(m3 != m4); // extents are not equal, OFFSET(rhs) == 0, strides are not equal + + // NB: OFFSET(layout_right::mapping) is always equal to 0 + } + + // TRANSITION, Check comparisons with custom layout mapping +} + +constexpr void check_correctness() { + { // empty extents + const array vals{}; + mdspan, layout_stride> nothing{vals.data(), {}}; + assert(nothing.size() == 1); + } + + { // regular vector + using E = extents; + const array vals{1, 2, 3}; + layout_stride::mapping m{E{}, array{1}}; + mdspan, layout_stride> vec{vals.data(), m}; + + // TRANSITION, use operator[] + assert(vec(0) == 1); + assert(vec(1) == 2); + assert(vec(2) == 3); + } + + { // 2x3 matrix with row-major order + using E = extents; + const array vals{1, 2, 3, 4, 5, 6}; + layout_stride::mapping m{E{}, array{3, 1}}; + mdspan matrix{vals.data(), m}; + + // TRANSITION, use operator[] + assert(matrix(0, 0) == 1); + assert(matrix(0, 1) == 2); + assert(matrix(0, 2) == 3); + assert(matrix(1, 0) == 4); + assert(matrix(1, 1) == 5); + assert(matrix(1, 2) == 6); + } + + { // 3x2x2 tensor + using E = extents; + const array vals{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + layout_stride::mapping m{E{}, array{8, 1, 6}}; // non-exhaustive mapping + assert(!m.is_exhaustive()); + mdspan tensor{vals.data(), m}; + + // TRANSITION, use operator[] + assert(tensor(0, 0, 0) == 0); + assert(tensor(0, 0, 1) == 6); + assert(tensor(0, 1, 0) == 1); + assert(tensor(0, 1, 1) == 7); + assert(tensor(1, 0, 0) == 8); + assert(tensor(1, 0, 1) == 14); + assert(tensor(1, 1, 0) == 9); + assert(tensor(1, 1, 1) == 15); + assert(tensor(2, 0, 0) == 16); + assert(tensor(2, 0, 1) == 22); + assert(tensor(2, 1, 0) == 17); + assert(tensor(2, 1, 1) == 23); + } + + { // 2x3x3x2 tensor + using E = extents; + const array vals{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}; + layout_stride::mapping m{E{}, array{18, 1, 3, 9}}; // exhaustive mapping + assert(m.is_exhaustive()); + mdspan tensor{vals.data(), m}; + + // TRANSITION, use operator[] + assert(tensor(0, 0, 0, 0) == 0); + assert(tensor(0, 0, 0, 1) == 9); + assert(tensor(0, 0, 1, 0) == 3); + assert(tensor(0, 0, 1, 1) == 12); + assert(tensor(0, 1, 0, 0) == 1); + assert(tensor(0, 1, 0, 1) == 10); + assert(tensor(0, 1, 1, 0) == 4); + assert(tensor(0, 1, 1, 1) == 13); + assert(tensor(1, 0, 0, 0) == 18); + assert(tensor(1, 0, 0, 1) == 27); + assert(tensor(1, 0, 1, 0) == 21); + assert(tensor(1, 0, 1, 1) == 30); + assert(tensor(1, 1, 0, 0) == 19); + assert(tensor(1, 1, 0, 1) == 28); + assert(tensor(1, 1, 1, 0) == 22); + assert(tensor(1, 1, 1, 1) == 31); + assert(tensor(0, 2, 2, 0) == 8); + assert(tensor(1, 2, 2, 1) == 35); + } +} + constexpr void check_ctad() { - extents e; - array s{1, 2}; - layout_stride::mapping m{e, s}; - assert(m.extents() == e); - assert(m.strides() == s); + using E = extents; + E e; + + { // E::index_type and array::value_type are the same + array a{1, 2}; + layout_stride::mapping m1{e, a}; + static_assert(same_as>); + assert(m1.extents() == e); + assert(m1.strides() == a); + + span s{a}; + layout_stride::mapping m2{e, s}; + static_assert(same_as>); + assert(m2.extents() == e); + assert(m2.strides() == a); + } + + { // E::index_type and array::value_type are different + array a{1, 2}; + layout_stride::mapping m1{e, a}; + static_assert(same_as>); + assert(m1.extents() == e); + assert(ranges::equal(m1.strides(), a, CmpEqual{})); + + span s{a}; + layout_stride::mapping m2{e, s}; + static_assert(same_as>); + assert(m2.extents() == e); + assert(ranges::equal(m2.strides(), a, CmpEqual{})); + } } constexpr bool test() { @@ -187,9 +536,15 @@ constexpr bool test() { check_members(extents{}, array{1}); check_members(extents{3}, array{1, 3, 6}); + check_construction_from_extents_and_array(); + check_construction_from_other_mappings(); + check_required_span_size(); + check_is_exhaustive(); + check_call_operator(); + check_comparisons(); + check_correctness(); check_ctad(); - // TRANSITION more tests return true; } From 67b9723b0acd7da0d141f46449557c7dd8036b42 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Mon, 15 May 2023 17:46:19 +0200 Subject: [PATCH 24/57] ``: Add new debug checks to layout mappings (#3702) --- stl/inc/mdspan | 23 +++++++++++++++++++ .../P0009R18_mdspan_layout_left/test.cpp | 2 +- .../test.cpp | 7 ++++++ .../P0009R18_mdspan_layout_right/test.cpp | 8 ++++--- .../test.cpp | 7 ++++++ .../test.cpp | 7 ++++++ 6 files changed, 50 insertions(+), 4 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 670f641c47..fbd4574c0e 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -256,6 +256,17 @@ public: return true; } } + + template + _NODISCARD constexpr bool _Contains_multidimensional_index( + index_sequence<_Seq...>, _IndexTypes... _Indices) const noexcept { + _STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...)); + if constexpr (unsigned_integral) { + return ((_Indices < extent(_Seq)) && ...); + } else { + return ((0 <= _Indices && _Indices < extent(_Seq)) && ...); + } + } }; template @@ -379,6 +390,10 @@ public: requires (sizeof...(_IndexTypes) == extents_type::rank()) && (is_convertible_v<_IndexTypes, index_type> && ...) && (is_nothrow_constructible_v && ...) _NODISCARD constexpr index_type operator()(_IndexTypes... _Indices) const noexcept { + _STL_VERIFY(_Exts._Contains_multidimensional_index( + make_index_sequence{}, static_cast(_Indices)...), + "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " + "[mdspan.layout.left.obs]/3)."); return _Index_impl(make_index_sequence{}, static_cast(_Indices)...); } @@ -512,6 +527,10 @@ public: requires (sizeof...(_IndexTypes) == extents_type::rank()) && (is_convertible_v<_IndexTypes, index_type> && ...) && (is_nothrow_constructible_v && ...) _NODISCARD constexpr index_type operator()(_IndexTypes... _Indices) const noexcept { + _STL_VERIFY(_Exts._Contains_multidimensional_index( + make_index_sequence{}, static_cast(_Indices)...), + "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " + "[mdspan.layout.right.obs]/3)."); return _Index_impl(make_index_sequence{}, static_cast(_Indices)...); } @@ -698,6 +717,10 @@ public: requires (sizeof...(_IndexTypes) == extents_type::rank()) && (is_convertible_v<_IndexTypes, index_type> && ...) && (is_nothrow_constructible_v && ...) _NODISCARD constexpr index_type operator()(_IndexTypes... _Indices) const noexcept { + _STL_VERIFY(_Exts._Contains_multidimensional_index( + make_index_sequence{}, static_cast(_Indices)...), + "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " + "[mdspan.layout.stride.obs]/3)."); return _Index_impl(make_index_sequence{}, static_cast(_Indices)...); } diff --git a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp index 5b77ab1760..1930471031 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp @@ -330,7 +330,7 @@ constexpr void check_correctness() { 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, 2) == 17); assert(tensor(2, 1, 3) == 23); } 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 90425880ec..4ffb2d8d6b 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_left_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_left_death/test.cpp @@ -38,6 +38,12 @@ void test_construction_from_other_stride_mapping_2() { layout_left::mapping> m2{m1}; } +void test_call_operator() { + layout_left::mapping> m; + // Value of extents_type::index-cast(i) must be a multidimensional index in extents_ + (void) m(2, 3, 5); +} + void test_stride_function() { layout_left::mapping> m; // Value of i must be less than extents_type::rank() @@ -52,6 +58,7 @@ int main(int argc, char* argv[]) { test_construction_from_other_right_mapping, test_construction_from_other_stride_mapping_1, test_construction_from_other_stride_mapping_2, + test_call_operator, test_stride_function, }); return exec.run(argc, argv); diff --git a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp index 64f2e77f2c..a80d1b8080 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp @@ -51,8 +51,7 @@ constexpr void check_members(const extents& ext, index_se using Ext2 = extents; using Mapping2 = layout_right::mapping; -#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 +#ifndef __clang__ // FIXME, Clang suddenly cannot digest this { // Check construction from other layout_right::mapping Mapping m1{ext}; Mapping2 m2{m1}; @@ -60,6 +59,7 @@ constexpr void check_members(const extents& ext, index_se static_assert(is_nothrow_constructible_v); // Other tests are defined in 'check_construction_from_other_right_mapping' function } +#endif { // Check construction from layout_left::mapping using LeftMapping = layout_left::mapping; @@ -82,7 +82,10 @@ constexpr void check_members(const extents& ext, index_se if constexpr (Ext::rank() > 0) { strides.back() = 1; for (size_t i = Ext::rank() - 1; i-- > 0;) { +#pragma warning(push) +#pragma warning(disable : 28020) // TRANSITION, DevCom-923103 strides[i] = static_cast(strides[i + 1] * ext.extent(i + 1)); +#pragma warning(pop) } } @@ -140,7 +143,6 @@ constexpr void check_members(const extents& ext, index_se assert(!(m != m)); // Other tests are defined in 'check_comparisons' function } -#pragma warning(pop) // TRANSITION, "/analyze:only" BUG? } constexpr void check_construction_from_other_right_mapping() { 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 aed180045f..254ea56580 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_right_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_right_death/test.cpp @@ -38,6 +38,12 @@ void test_construction_from_other_stride_mapping_2() { layout_right::mapping> m2{m1}; } +void test_call_operator() { + layout_right::mapping> m; + // Value of extents_type::index-cast(i) must be a multidimensional index in extents_ + (void) m(4, 3, 3); +} + void test_stride_function() { layout_right::mapping> m; // Value of i must be less than extents_type::rank() @@ -52,6 +58,7 @@ int main(int argc, char* argv[]) { test_construction_from_other_left_mapping, test_construction_from_other_stride_mapping_1, test_construction_from_other_stride_mapping_2, + test_call_operator, test_stride_function, }); return exec.run(argc, argv); 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 eadd0d3a49..2532506730 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp @@ -27,6 +27,12 @@ void test_construction_from_strided_layout_mapping() { [[maybe_unused]] layout_stride::mapping> m2{m1}; } +void test_call_operator() { + layout_stride::mapping> m; + // Value of extents_type::index-cast(i) must be a multidimensional index in extents_ + (void) m(4, 3, 3); +} + int main(int argc, char* argv[]) { std_testing::death_test_executive exec; exec.add_death_tests({ @@ -34,6 +40,7 @@ int main(int argc, char* argv[]) { test_construction_from_extents_and_array, test_construction_from_extents_and_span, test_construction_from_strided_layout_mapping, + test_call_operator, }); return exec.run(argc, argv); } From 42dedfd734cce3f7e77e7d7b193b03b02391fa00 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 25 May 2023 04:23:13 +0800 Subject: [PATCH 25/57] ``: Cleanup for `_CONSTEVAL` (#3638) --- stl/inc/mdspan | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 69512d2e89..4196ff3b33 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -44,7 +44,7 @@ public: private: static constexpr array _Static_extents = {_Extents...}; - _NODISCARD static _CONSTEVAL auto _Make_dynamic_indices() noexcept { + _NODISCARD static consteval auto _Make_dynamic_indices() noexcept { array _Result{}; rank_type _Counter = 0; for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) { @@ -59,7 +59,7 @@ private: static constexpr array _Dynamic_indices = _Make_dynamic_indices(); - _NODISCARD static _CONSTEVAL auto _Make_dynamic_indices_inv() noexcept { + _NODISCARD static consteval auto _Make_dynamic_indices_inv() noexcept { array _Result{}; for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) { for (rank_type _Idx_inv = 0; _Idx_inv < _Rank; ++_Idx_inv) { @@ -249,7 +249,7 @@ public: return _Result; } - _NODISCARD static _CONSTEVAL bool _Is_index_space_size_representable() { + _NODISCARD static consteval bool _Is_index_space_size_representable() { if constexpr (rank_dynamic() == 0 && rank() > 0) { return _STD in_range((_Extents * ...)); } else { From c600a08671ba620a6dd0568fe01005c6bb031f81 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Wed, 24 May 2023 22:37:09 +0200 Subject: [PATCH 26/57] ``: Implement multidimensional subscript operator (#3689) --- stl/inc/mdspan | 17 ++- tests/std/tests/P0009R18_mdspan/test.cpp | 6 +- .../P0009R18_mdspan_layout_left/test.cpp | 82 ++++++---- .../P0009R18_mdspan_layout_right/test.cpp | 129 ++++++++++------ .../P0009R18_mdspan_layout_stride/test.cpp | 141 ++++++++++++------ .../test.cpp | 2 +- 6 files changed, 253 insertions(+), 124 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 4196ff3b33..f8cd52ff9b 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -935,14 +935,15 @@ public: constexpr mdspan& operator=(const mdspan&) = default; constexpr mdspan& operator=(mdspan&&) = default; - // TRANSITION, P2128R6 (Multidimensional subscript operator) +#ifdef __clang__ // TRANSITION, P2128R6 template requires (is_convertible_v<_OtherIndexTypes, index_type> && ...) && (is_nothrow_constructible_v && ...) && (sizeof...(_OtherIndexTypes) == rank()) - _NODISCARD constexpr reference operator()(_OtherIndexTypes... _Indices) const { + _NODISCARD constexpr reference operator[](_OtherIndexTypes... _Indices) const { return _Acc.access(_Ptr, static_cast(_Map(static_cast(_STD move(_Indices))...))); } +#endif // __clang__ template requires is_convertible_v @@ -955,7 +956,7 @@ public: requires is_convertible_v && is_nothrow_constructible_v _NODISCARD constexpr reference operator[](const array<_OtherIndexType, rank()>& _Indices) const { - return _Index_impl(_Indices, make_index_sequence{}); + return _Index_impl(span{_Indices}, make_index_sequence{}); } _NODISCARD constexpr size_type size() const noexcept { @@ -1026,9 +1027,13 @@ public: } private: - template - _NODISCARD constexpr reference _Index_impl(_IndexContainer&& _Indices, index_sequence<_Idx...>) const { - return _Acc.access(_Ptr, _Map(_STD as_const(_STD forward<_IndexContainer>(_Indices)[_Idx])...)); + template + _NODISCARD constexpr reference _Index_impl(span<_OtherIndexType, rank()> _Indices, index_sequence<_Seq...>) const { +#ifdef __clang__ // TRANSITION, P2128R6 + return this->operator[](_STD as_const(_Indices[_Seq])...); +#else // ^^^ defined(__clang__) / !defined(__clang__) vvv + return _Acc.access(_Ptr, static_cast(_Map(static_cast(_STD as_const(_Indices[_Seq]))...))); +#endif // ^^^ !defined(__clang__) ^^^ } data_handle_type _Ptr{}; diff --git a/tests/std/tests/P0009R18_mdspan/test.cpp b/tests/std/tests/P0009R18_mdspan/test.cpp index ad11337a65..dcd3274a72 100644 --- a/tests/std/tests/P0009R18_mdspan/test.cpp +++ b/tests/std/tests/P0009R18_mdspan/test.cpp @@ -1029,8 +1029,10 @@ void mdspan_tests_observers() { static_assert(!mds.is_exhaustive()); static_assert(mds.is_strided()); - static_assert(mds(1, 0) == 1); - static_assert(mds(1, 2) == 7); +#ifdef __clang__ // TRANSITION, P2128R6 + static_assert(mds[1, 0] == 1); + static_assert(mds[1, 2] == 7); +#endif // __clang__ static_assert(mds[array{0, 1}] == 3); static_assert(mds[array{1, 1}] == 4); diff --git a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp index 1930471031..4e2e247b86 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp @@ -302,36 +302,57 @@ constexpr void check_correctness() { 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); +#ifdef __clang__ // TRANSITION, P2128R6 + assert(vec[0] == 0); + assert(vec[1] == 1); + assert(vec[2] == 2); +#else // ^^^ defined(__clang__) / !defined(__clang__) vvv + assert(vec[array{0}] == 0); + assert(vec[array{1}] == 1); + assert(vec[array{2}] == 2); +#endif // ^^^ !defined(__clang__) ^^^ } { // 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); +#ifdef __clang__ // TRANSITION, P2128R6 + 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)); +#else // ^^^ defined(__clang__) / !defined(__clang__) vvv + assert((matrix[array{0, 0}] == 0)); + assert((matrix[array{1, 0}] == 1)); + assert((matrix[array{2, 0}] == 2)); + assert((matrix[array{0, 1}] == 3)); + assert((matrix[array{1, 1}] == 4)); + assert((matrix[array{2, 1}] == 5)); +#endif // ^^^ !defined(__clang__) ^^^ } { // 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, 1, 2) == 17); - assert(tensor(2, 1, 3) == 23); +#ifdef __clang__ // TRANSITION, P2128R6 + 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, 1, 2] == 17)); + assert((tensor[2, 1, 3] == 23)); +#else // ^^^ defined(__clang__) / !defined(__clang__) vvv + assert((tensor[array{0, 0, 0}] == 0)); + assert((tensor[array{2, 0, 0}] == 2)); + assert((tensor[array{1, 1, 1}] == 10)); + assert((tensor[array{0, 0, 3}] == 18)); + assert((tensor[array{2, 1, 2}] == 17)); + assert((tensor[array{2, 1, 3}] == 23)); +#endif // ^^^ !defined(__clang__) ^^^ } { // 2x3x2x3 tensor @@ -339,14 +360,23 @@ constexpr void check_correctness() { 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); +#ifdef __clang__ // TRANSITION, P2128R6 + 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)); +#else // ^^^ defined(__clang__) / !defined(__clang__) vvv + assert((tensor[array{0, 0, 0, 0}] == 0)); + assert((tensor[array{1, 0, 0, 0}] == 1)); + assert((tensor[array{0, 1, 1, 0}] == 8)); + assert((tensor[array{0, 0, 0, 1}] == 12)); + assert((tensor[array{0, 0, 0, 2}] == 24)); + assert((tensor[array{0, 2, 0, 2}] == 28)); + assert((tensor[array{1, 2, 1, 2}] == 35)); +#endif // ^^^ !defined(__clang__) ^^^ } } diff --git a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp index a80d1b8080..67530019c1 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp @@ -59,7 +59,7 @@ constexpr void check_members(const extents& ext, index_se static_assert(is_nothrow_constructible_v); // Other tests are defined in 'check_construction_from_other_right_mapping' function } -#endif +#endif // __clang__ { // Check construction from layout_left::mapping using LeftMapping = layout_left::mapping; @@ -303,40 +303,65 @@ constexpr void check_correctness() { const array vals{2, 1, 0}; mdspan, layout_right> vec{vals.data()}; - // TRANSITION, use operator[] - assert(vec(0) == 2); - assert(vec(1) == 1); - assert(vec(2) == 0); +#ifdef __clang__ // TRANSITION, P2128R6 + assert(vec[0] == 2); + assert(vec[1] == 1); + assert(vec[2] == 0); +#else // ^^^ defined(__clang__) / !defined(__clang__) vvv + assert(vec[array{0}] == 2); + assert(vec[array{1}] == 1); + assert(vec[array{2}] == 0); +#endif // ^^^ !defined(__clang__) ^^^ } { // 4x3 matrix with row-major order const array vals{11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; mdspan, layout_right> matrix{vals.data()}; - // TRANSITION, use operator[] - assert(matrix(0, 0) == 11); - assert(matrix(0, 2) == 9); - assert(matrix(1, 1) == 7); - assert(matrix(2, 0) == 5); - assert(matrix(2, 2) == 3); - assert(matrix(3, 1) == 1); +#ifdef __clang__ // TRANSITION, P2128R6 + assert((matrix[0, 0] == 11)); + assert((matrix[0, 2] == 9)); + assert((matrix[1, 1] == 7)); + assert((matrix[2, 0] == 5)); + assert((matrix[2, 2] == 3)); + assert((matrix[3, 1] == 1)); +#else // ^^^ defined(__clang__) / !defined(__clang__) vvv + assert((matrix[array{0, 0}] == 11)); + assert((matrix[array{0, 2}] == 9)); + assert((matrix[array{1, 1}] == 7)); + assert((matrix[array{2, 0}] == 5)); + assert((matrix[array{2, 2}] == 3)); + assert((matrix[array{3, 1}] == 1)); +#endif // ^^^ !defined(__clang__) ^^^ } { // 4x3x2 tensor const array vals{23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; mdspan, layout_right> tensor{vals.data(), 4, 3, 2}; - // TRANSITION, use operator[] - assert(tensor(0, 0, 0) == 23); - assert(tensor(0, 0, 1) == 22); - assert(tensor(0, 1, 0) == 21); - assert(tensor(0, 1, 1) == 20); - assert(tensor(1, 0, 0) == 17); - assert(tensor(1, 0, 1) == 16); - assert(tensor(1, 1, 0) == 15); - assert(tensor(1, 1, 1) == 14); - assert(tensor(2, 2, 1) == 6); - assert(tensor(3, 2, 1) == 0); +#ifdef __clang__ // TRANSITION, P2128R6 + assert((tensor[0, 0, 0] == 23)); + assert((tensor[0, 0, 1] == 22)); + assert((tensor[0, 1, 0] == 21)); + assert((tensor[0, 1, 1] == 20)); + assert((tensor[1, 0, 0] == 17)); + assert((tensor[1, 0, 1] == 16)); + assert((tensor[1, 1, 0] == 15)); + assert((tensor[1, 1, 1] == 14)); + assert((tensor[2, 2, 1] == 6)); + assert((tensor[3, 2, 1] == 0)); +#else // ^^^ defined(__clang__) / !defined(__clang__) vvv + assert((tensor[array{0, 0, 0}] == 23)); + assert((tensor[array{0, 0, 1}] == 22)); + assert((tensor[array{0, 1, 0}] == 21)); + assert((tensor[array{0, 1, 1}] == 20)); + assert((tensor[array{1, 0, 0}] == 17)); + assert((tensor[array{1, 0, 1}] == 16)); + assert((tensor[array{1, 1, 0}] == 15)); + assert((tensor[array{1, 1, 1}] == 14)); + assert((tensor[array{2, 2, 1}] == 6)); + assert((tensor[array{3, 2, 1}] == 0)); +#endif // ^^^ !defined(__clang__) ^^^ } { // 3x2x3x2 tensor @@ -344,25 +369,45 @@ constexpr void check_correctness() { 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; mdspan, layout_right> tensor{vals.data(), 2, 2}; - // TRANSITION, use operator[] - assert(tensor(0, 0, 0, 0) == 35); - assert(tensor(0, 0, 0, 1) == 34); - assert(tensor(0, 0, 1, 0) == 33); - assert(tensor(0, 0, 1, 1) == 32); - assert(tensor(0, 1, 0, 0) == 29); - assert(tensor(0, 1, 0, 1) == 28); - assert(tensor(0, 1, 1, 0) == 27); - assert(tensor(0, 1, 1, 1) == 26); - assert(tensor(1, 0, 0, 0) == 23); - assert(tensor(1, 0, 0, 1) == 22); - assert(tensor(1, 0, 1, 0) == 21); - assert(tensor(1, 0, 1, 1) == 20); - assert(tensor(1, 1, 0, 0) == 17); - assert(tensor(1, 1, 0, 1) == 16); - assert(tensor(1, 1, 1, 0) == 15); - assert(tensor(1, 1, 1, 1) == 14); - assert(tensor(2, 0, 2, 0) == 7); - assert(tensor(2, 1, 2, 1) == 0); +#ifdef __clang__ // TRANSITION, P2128R6 + assert((tensor[0, 0, 0, 0] == 35)); + assert((tensor[0, 0, 0, 1] == 34)); + assert((tensor[0, 0, 1, 0] == 33)); + assert((tensor[0, 0, 1, 1] == 32)); + assert((tensor[0, 1, 0, 0] == 29)); + assert((tensor[0, 1, 0, 1] == 28)); + assert((tensor[0, 1, 1, 0] == 27)); + assert((tensor[0, 1, 1, 1] == 26)); + assert((tensor[1, 0, 0, 0] == 23)); + assert((tensor[1, 0, 0, 1] == 22)); + assert((tensor[1, 0, 1, 0] == 21)); + assert((tensor[1, 0, 1, 1] == 20)); + assert((tensor[1, 1, 0, 0] == 17)); + assert((tensor[1, 1, 0, 1] == 16)); + assert((tensor[1, 1, 1, 0] == 15)); + assert((tensor[1, 1, 1, 1] == 14)); + assert((tensor[2, 0, 2, 0] == 7)); + assert((tensor[2, 1, 2, 1] == 0)); +#else // ^^^ defined(__clang__) / !defined(__clang__) vvv + assert((tensor[array{0, 0, 0, 0}] == 35)); + assert((tensor[array{0, 0, 0, 1}] == 34)); + assert((tensor[array{0, 0, 1, 0}] == 33)); + assert((tensor[array{0, 0, 1, 1}] == 32)); + assert((tensor[array{0, 1, 0, 0}] == 29)); + assert((tensor[array{0, 1, 0, 1}] == 28)); + assert((tensor[array{0, 1, 1, 0}] == 27)); + assert((tensor[array{0, 1, 1, 1}] == 26)); + assert((tensor[array{1, 0, 0, 0}] == 23)); + assert((tensor[array{1, 0, 0, 1}] == 22)); + assert((tensor[array{1, 0, 1, 0}] == 21)); + assert((tensor[array{1, 0, 1, 1}] == 20)); + assert((tensor[array{1, 1, 0, 0}] == 17)); + assert((tensor[array{1, 1, 0, 1}] == 16)); + assert((tensor[array{1, 1, 1, 0}] == 15)); + assert((tensor[array{1, 1, 1, 1}] == 14)); + assert((tensor[array{2, 0, 2, 0}] == 7)); + assert((tensor[array{2, 1, 2, 1}] == 0)); +#endif // ^^^ !defined(__clang__) ^^^ } } diff --git a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp index 99dcce276d..435d637177 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp @@ -215,7 +215,7 @@ constexpr void check_construction_from_other_mappings() { } constexpr void check_required_span_size() { - { // Check [mdspan.layout.stride.expo]/1.1: Ext::rank() == 0 + { // Check N4950 [mdspan.layout.stride.expo]/1.1: Ext::rank() == 0 using M1 = layout_stride::mapping>; static_assert(M1{}.required_span_size() == 1); @@ -223,7 +223,7 @@ constexpr void check_required_span_size() { assert(m2.required_span_size() == 1); } - { // Check [mdspan.layout.stride.expo]/1.2: size of the multidimensional index space e is 0 + { // Check N4950 [mdspan.layout.stride.expo]/1.2: size of the multidimensional index space e is 0 using M1 = layout_stride::mapping>; static_assert(M1{}.required_span_size() == 0); @@ -231,7 +231,7 @@ constexpr void check_required_span_size() { assert(m2.required_span_size() == 0); } - { // Check [mdspan.layout.stride.expo]/1.3: final case + { // Check N4950 [mdspan.layout.stride.expo]/1.3: final case using M1 = layout_stride::mapping>; static_assert(M1{}.required_span_size() == 36); @@ -345,7 +345,7 @@ constexpr void check_comparisons() { static_assert(!equality_comparable_with>>); static_assert(!equality_comparable_with>>); static_assert(!equality_comparable_with>>); - // TRANSITION, Check other constraints: [mdspan.layout.stride.obs]/6.1, 6.3 + // TRANSITION, Check other constraints: N4950 [mdspan.layout.stride.obs]/6.1, 6.3 } { // Check correctness: layout_stride::mapping with layout_stride::mapping @@ -415,10 +415,15 @@ constexpr void check_correctness() { layout_stride::mapping m{E{}, array{1}}; mdspan, layout_stride> vec{vals.data(), m}; - // TRANSITION, use operator[] - assert(vec(0) == 1); - assert(vec(1) == 2); - assert(vec(2) == 3); +#ifdef __clang__ // TRANSITION, P2128R6 + assert((vec[0] == 1)); + assert((vec[1] == 2)); + assert((vec[2] == 3)); +#else // ^^^ defined(__clang__) / !defined(__clang__) vvv + assert((vec[array{0}] == 1)); + assert((vec[array{1}] == 2)); + assert((vec[array{2}] == 3)); +#endif // ^^^ !defined(__clang__) ^^^ } { // 2x3 matrix with row-major order @@ -427,13 +432,21 @@ constexpr void check_correctness() { layout_stride::mapping m{E{}, array{3, 1}}; mdspan matrix{vals.data(), m}; - // TRANSITION, use operator[] - assert(matrix(0, 0) == 1); - assert(matrix(0, 1) == 2); - assert(matrix(0, 2) == 3); - assert(matrix(1, 0) == 4); - assert(matrix(1, 1) == 5); - assert(matrix(1, 2) == 6); +#ifdef __clang__ // TRANSITION, P2128R6 + assert((matrix[0, 0] == 1)); + assert((matrix[0, 1] == 2)); + assert((matrix[0, 2] == 3)); + assert((matrix[1, 0] == 4)); + assert((matrix[1, 1] == 5)); + assert((matrix[1, 2] == 6)); +#else // ^^^ defined(__clang__) / !defined(__clang__) vvv + assert((matrix[array{0, 0}] == 1)); + assert((matrix[array{0, 1}] == 2)); + assert((matrix[array{0, 2}] == 3)); + assert((matrix[array{1, 0}] == 4)); + assert((matrix[array{1, 1}] == 5)); + assert((matrix[array{1, 2}] == 6)); +#endif // ^^^ !defined(__clang__) ^^^ } { // 3x2x2 tensor @@ -443,19 +456,33 @@ constexpr void check_correctness() { assert(!m.is_exhaustive()); mdspan tensor{vals.data(), m}; - // TRANSITION, use operator[] - assert(tensor(0, 0, 0) == 0); - assert(tensor(0, 0, 1) == 6); - assert(tensor(0, 1, 0) == 1); - assert(tensor(0, 1, 1) == 7); - assert(tensor(1, 0, 0) == 8); - assert(tensor(1, 0, 1) == 14); - assert(tensor(1, 1, 0) == 9); - assert(tensor(1, 1, 1) == 15); - assert(tensor(2, 0, 0) == 16); - assert(tensor(2, 0, 1) == 22); - assert(tensor(2, 1, 0) == 17); - assert(tensor(2, 1, 1) == 23); +#ifdef __clang__ // TRANSITION, P2128R6 + assert((tensor[0, 0, 0] == 0)); + assert((tensor[0, 0, 1] == 6)); + assert((tensor[0, 1, 0] == 1)); + assert((tensor[0, 1, 1] == 7)); + assert((tensor[1, 0, 0] == 8)); + assert((tensor[1, 0, 1] == 14)); + assert((tensor[1, 1, 0] == 9)); + assert((tensor[1, 1, 1] == 15)); + assert((tensor[2, 0, 0] == 16)); + assert((tensor[2, 0, 1] == 22)); + assert((tensor[2, 1, 0] == 17)); + assert((tensor[2, 1, 1] == 23)); +#else // ^^^ defined(__clang__) / !defined(__clang__) vvv + assert((tensor[array{0, 0, 0}] == 0)); + assert((tensor[array{0, 0, 1}] == 6)); + assert((tensor[array{0, 1, 0}] == 1)); + assert((tensor[array{0, 1, 1}] == 7)); + assert((tensor[array{1, 0, 0}] == 8)); + assert((tensor[array{1, 0, 1}] == 14)); + assert((tensor[array{1, 1, 0}] == 9)); + assert((tensor[array{1, 1, 1}] == 15)); + assert((tensor[array{2, 0, 0}] == 16)); + assert((tensor[array{2, 0, 1}] == 22)); + assert((tensor[array{2, 1, 0}] == 17)); + assert((tensor[array{2, 1, 1}] == 23)); +#endif // ^^^ !defined(__clang__) ^^^ } { // 2x3x3x2 tensor @@ -466,25 +493,45 @@ constexpr void check_correctness() { assert(m.is_exhaustive()); mdspan tensor{vals.data(), m}; - // TRANSITION, use operator[] - assert(tensor(0, 0, 0, 0) == 0); - assert(tensor(0, 0, 0, 1) == 9); - assert(tensor(0, 0, 1, 0) == 3); - assert(tensor(0, 0, 1, 1) == 12); - assert(tensor(0, 1, 0, 0) == 1); - assert(tensor(0, 1, 0, 1) == 10); - assert(tensor(0, 1, 1, 0) == 4); - assert(tensor(0, 1, 1, 1) == 13); - assert(tensor(1, 0, 0, 0) == 18); - assert(tensor(1, 0, 0, 1) == 27); - assert(tensor(1, 0, 1, 0) == 21); - assert(tensor(1, 0, 1, 1) == 30); - assert(tensor(1, 1, 0, 0) == 19); - assert(tensor(1, 1, 0, 1) == 28); - assert(tensor(1, 1, 1, 0) == 22); - assert(tensor(1, 1, 1, 1) == 31); - assert(tensor(0, 2, 2, 0) == 8); - assert(tensor(1, 2, 2, 1) == 35); +#ifdef __clang__ // TRANSITION, P2128R6 + assert((tensor[0, 0, 0, 0] == 0)); + assert((tensor[0, 0, 0, 1] == 9)); + assert((tensor[0, 0, 1, 0] == 3)); + assert((tensor[0, 0, 1, 1] == 12)); + assert((tensor[0, 1, 0, 0] == 1)); + assert((tensor[0, 1, 0, 1] == 10)); + assert((tensor[0, 1, 1, 0] == 4)); + assert((tensor[0, 1, 1, 1] == 13)); + assert((tensor[1, 0, 0, 0] == 18)); + assert((tensor[1, 0, 0, 1] == 27)); + assert((tensor[1, 0, 1, 0] == 21)); + assert((tensor[1, 0, 1, 1] == 30)); + assert((tensor[1, 1, 0, 0] == 19)); + assert((tensor[1, 1, 0, 1] == 28)); + assert((tensor[1, 1, 1, 0] == 22)); + assert((tensor[1, 1, 1, 1] == 31)); + assert((tensor[0, 2, 2, 0] == 8)); + assert((tensor[1, 2, 2, 1] == 35)); +#else // ^^^ defined(__clang__) / !defined(__clang__) vvv + assert((tensor[array{0, 0, 0, 0}] == 0)); + assert((tensor[array{0, 0, 0, 1}] == 9)); + assert((tensor[array{0, 0, 1, 0}] == 3)); + assert((tensor[array{0, 0, 1, 1}] == 12)); + assert((tensor[array{0, 1, 0, 0}] == 1)); + assert((tensor[array{0, 1, 0, 1}] == 10)); + assert((tensor[array{0, 1, 1, 0}] == 4)); + assert((tensor[array{0, 1, 1, 1}] == 13)); + assert((tensor[array{1, 0, 0, 0}] == 18)); + assert((tensor[array{1, 0, 0, 1}] == 27)); + assert((tensor[array{1, 0, 1, 0}] == 21)); + assert((tensor[array{1, 0, 1, 1}] == 30)); + assert((tensor[array{1, 1, 0, 0}] == 19)); + assert((tensor[array{1, 1, 0, 1}] == 28)); + assert((tensor[array{1, 1, 1, 0}] == 22)); + assert((tensor[array{1, 1, 1, 1}] == 31)); + assert((tensor[array{0, 2, 2, 0}] == 8)); + assert((tensor[array{1, 2, 2, 1}] == 35)); +#endif // ^^^ !defined(__clang__) ^^^ } } 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 2532506730..2ad0eda002 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp @@ -28,7 +28,7 @@ void test_construction_from_strided_layout_mapping() { } void test_call_operator() { - layout_stride::mapping> m; + layout_stride::mapping> m; // Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (void) m(4, 3, 3); } From ec12ba7e8f713cc07711add4ca06c437a9ee95cc Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 25 May 2023 00:24:44 +0200 Subject: [PATCH 27/57] ``: Down with `typename`! (#3694) --- stl/inc/mdspan | 34 +++++++++++------------ tests/std/include/test_mdspan_support.hpp | 10 +++---- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index f8cd52ff9b..53adcb772a 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -285,7 +285,7 @@ struct _Dextents_impl<_IndexType, index_sequence<_Indices...>> { }; _EXPORT_STD template -using dextents = typename _Dextents_impl<_IndexType, make_index_sequence<_Rank>>::type; +using dextents = _Dextents_impl<_IndexType, make_index_sequence<_Rank>>::type; template inline constexpr bool _Is_extents = false; @@ -316,9 +316,9 @@ template class layout_left::mapping { public: using extents_type = _Extents; - using index_type = typename extents_type::index_type; - using size_type = typename extents_type::size_type; - using rank_type = typename extents_type::rank_type; + using index_type = extents_type::index_type; + using size_type = extents_type::size_type; + using rank_type = extents_type::rank_type; using layout_type = layout_left; static_assert(_Is_extents, @@ -451,9 +451,9 @@ template class layout_right::mapping { public: using extents_type = _Extents; - using index_type = typename extents_type::index_type; - using size_type = typename extents_type::size_type; - using rank_type = typename extents_type::rank_type; + using index_type = extents_type::index_type; + using size_type = extents_type::size_type; + using rank_type = extents_type::rank_type; using layout_type = layout_right; static_assert(_Is_extents, @@ -597,9 +597,9 @@ template class layout_stride::mapping { public: using extents_type = _Extents; - using index_type = typename extents_type::index_type; - using size_type = typename extents_type::size_type; - using rank_type = typename extents_type::rank_type; + using index_type = extents_type::index_type; + using size_type = extents_type::size_type; + using rank_type = extents_type::rank_type; using layout_type = layout_stride; static_assert(_Is_extents, @@ -778,7 +778,7 @@ private: array _Strides{}; template - _NODISCARD static constexpr typename _OtherMapping::index_type _Offset(_OtherMapping& _Mapping) noexcept { + _NODISCARD static constexpr _OtherMapping::index_type _Offset(_OtherMapping& _Mapping) noexcept { if constexpr (extents_type::rank() == 0) { return _Mapping(); } else { @@ -836,14 +836,14 @@ public: using extents_type = _Extents; using layout_type = _LayoutPolicy; using accessor_type = _AccessorPolicy; - using mapping_type = typename layout_type::template mapping; + using mapping_type = layout_type::template mapping; using element_type = _ElementType; using value_type = remove_cv_t; - using index_type = typename extents_type::index_type; - using size_type = typename extents_type::size_type; - using rank_type = typename extents_type::rank_type; - using data_handle_type = typename accessor_type::data_handle_type; - using reference = typename accessor_type::reference; + using index_type = extents_type::index_type; + using size_type = extents_type::size_type; + using rank_type = extents_type::rank_type; + using data_handle_type = accessor_type::data_handle_type; + using reference = accessor_type::reference; static_assert( sizeof(element_type) > 0, "ElementType must be a complete type (N4950 [mdspan.mdspan.overview]/2.1)."); diff --git a/tests/std/include/test_mdspan_support.hpp b/tests/std/include/test_mdspan_support.hpp index 9945b56ff9..9ac8383480 100644 --- a/tests/std/include/test_mdspan_support.hpp +++ b/tests/std/include/test_mdspan_support.hpp @@ -79,12 +79,12 @@ template concept CheckCallOperatorOfLayoutMapping = requires(const M m, Indices... i) { { m(i...) } -> std::same_as; - { m(i...) == m(static_cast(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) { +concept CheckStrideMemberFunction = requires(M mapping, M::rank_type i) { { mapping.stride(i) } -> std::same_as; }; @@ -103,7 +103,7 @@ constexpr bool check_layout_mapping_requirements() { static_assert(CheckCallOperatorOfLayoutMapping); }(std::make_index_sequence{}); - if constexpr (requires(M m, typename M::rank_type i) { m.stride(i); }) { + if constexpr (requires(M m, M::rank_type i) { m.stride(i); }) { static_assert(CheckStrideMemberFunction); } @@ -113,7 +113,7 @@ constexpr bool check_layout_mapping_requirements() { template requires detail::is_extents_v constexpr bool check_layout_mapping_policy_requirements() { - using X = typename MP::template mapping; + using X = MP::template mapping; static_assert(check_layout_mapping_requirements()); static_assert(std::same_as); static_assert(std::same_as); @@ -139,7 +139,7 @@ namespace detail { // clang-format off template concept CheckMemberFunctionsOfAccessorPolicy = - requires(const A a, const typename A::data_handle_type p, size_t i) { + requires(const A a, const A::data_handle_type p, size_t i) { { a.access(p, i) } -> std::same_as; { a.offset(p, i) } -> std::same_as; }; From 5e7a0f4ae48ce0445de3683d27103981735217c2 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Thu, 25 May 2023 00:30:35 +0200 Subject: [PATCH 28/57] ``: Cache `fwd-prod-of-extents` and `rev-prod-of-extents` when all extents are static (#3715) --- stl/inc/mdspan | 107 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 83 insertions(+), 24 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 53adcb772a..50b0b055fb 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -231,24 +231,6 @@ 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; - } - - // TRANSITION, LWG ISSUE? I believe that this function should return 'index_type' - _NODISCARD constexpr index_type _Rev_prod_of_extents(const rank_type _Idx) const noexcept { - index_type _Result = 1; - for (rank_type _Dim = _Idx + 1; _Dim < _Rank; ++_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 * ...)); @@ -293,6 +275,82 @@ inline constexpr bool _Is_extents = false; template inline constexpr bool _Is_extents> = true; +template + requires _Is_extents<_Extents> +class _Fwd_prod_of_extents { +public: + _NODISCARD static constexpr _Extents::index_type _Calculate(const _Extents& _Exts, const size_t _Idx) noexcept { + if constexpr (_Extents::rank() == 0) { + return 1; + } else { + typename _Extents::index_type _Result = 1; + for (size_t _Dim = 0; _Dim < _Idx; ++_Dim) { + _Result *= _Exts.extent(_Dim); + } + return _Result; + } + } +}; + +template + requires ((_Extents != dynamic_extent) && ...) +class _Fwd_prod_of_extents> { +private: + using _Ty = extents<_IndexType, _Extents...>; + + _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)); + } + return _Result; + } + + static constexpr array _Cache = _Make_prods(); + +public: + _NODISCARD static constexpr _Ty::index_type _Calculate(const _Ty&, const size_t _Idx) noexcept { + return _Cache[_Idx]; + } +}; + +template + requires _Is_extents<_Extents> && (_Extents::rank() > 0) +class _Rev_prod_of_extents { +public: + _NODISCARD static constexpr _Extents::index_type _Calculate(const _Extents& _Exts, const size_t _Idx) noexcept { + typename _Extents::index_type _Result = 1; + for (size_t _Dim = _Idx + 1; _Dim < _Extents::_Rank; ++_Dim) { + _Result *= _Exts.extent(_Dim); + } + return _Result; + } +}; + +template + requires ((_Extents != dynamic_extent) && ...) +class _Rev_prod_of_extents> { +private: + using _Ty = extents<_IndexType, _Extents...>; + + _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)); + } + return _Result; + } + + static constexpr array _Cache = _Make_prods(); + +public: + _NODISCARD static constexpr _Ty::index_type _Calculate(const _Ty&, const size_t _Idx) noexcept { + return _Cache[_Idx]; + } +}; + template inline constexpr bool _Is_mapping_of = is_same_v, _Mapping>; @@ -382,7 +440,7 @@ public: } _NODISCARD constexpr index_type required_span_size() const noexcept { - return _Exts._Fwd_prod_of_extents(extents_type::_Rank); + return _Fwd_prod_of_extents::_Calculate(_Exts, extents_type::_Rank); } template @@ -425,7 +483,7 @@ public: { _STL_VERIFY(_Idx < extents_type::_Rank, "Value of i must be less than extents_type::rank() (N4950 [mdspan.layout.left.obs]/6)."); - return _Exts._Fwd_prod_of_extents(_Idx); + return _Fwd_prod_of_extents::_Calculate(_Exts, _Idx); } template @@ -518,7 +576,7 @@ public: } _NODISCARD constexpr index_type required_span_size() const noexcept { - return _Exts._Fwd_prod_of_extents(extents_type::_Rank); + return _Fwd_prod_of_extents::_Calculate(_Exts, extents_type::_Rank); } template @@ -561,7 +619,7 @@ public: { _STL_VERIFY(_Idx < extents_type::_Rank, "Value of i must be less than extents_type::rank() (N4950 [mdspan.layout.right.obs]/6)."); - return _Exts._Rev_prod_of_extents(_Idx); + return _Rev_prod_of_extents::_Calculate(_Exts, _Idx); } template @@ -742,7 +800,7 @@ public: if constexpr (extents_type::rank() == 0) { return true; } else { - return required_span_size() == _Exts._Fwd_prod_of_extents(extents_type::_Rank); + return required_span_size() == _Fwd_prod_of_extents::_Calculate(_Exts, extents_type::_Rank); } } @@ -960,7 +1018,8 @@ public: } _NODISCARD constexpr size_type size() const noexcept { - return static_cast(_Map.extents()._Fwd_prod_of_extents(extents_type::_Rank)); + return static_cast( + _Fwd_prod_of_extents::_Calculate(_Map.extents(), extents_type::_Rank)); } _NODISCARD constexpr bool empty() const noexcept { From ab7bb2375dec29d33c2f7add04f8ba3a53fea430 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Sat, 10 Jun 2023 18:22:43 +0200 Subject: [PATCH 29/57] ``: Test `mdspan` (#3749) Co-authored-by: Stephan T. Lavavej --- stl/inc/mdspan | 31 +- tests/std/include/test_mdspan_support.hpp | 60 +- tests/std/test.lst | 1 + tests/std/tests/P0009R18_mdspan/test.cpp | 4 +- .../P0009R18_mdspan_layout_left/test.cpp | 24 +- .../P0009R18_mdspan_layout_right/test.cpp | 24 +- .../P0009R18_mdspan_layout_stride/test.cpp | 24 +- .../std/tests/P0009R18_mdspan_mdspan/env.lst | 4 + .../std/tests/P0009R18_mdspan_mdspan/test.cpp | 1352 +++++++++++++++++ 9 files changed, 1439 insertions(+), 85 deletions(-) create mode 100644 tests/std/tests/P0009R18_mdspan_mdspan/env.lst create mode 100644 tests/std/tests/P0009R18_mdspan_mdspan/test.cpp diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 50b0b055fb..2a0f9b289a 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -952,7 +952,7 @@ public: && is_nothrow_constructible_v && (_Size == rank() || _Size == rank_dynamic()) && is_constructible_v && is_default_constructible_v - constexpr explicit(_Size != rank_dynamic()) mdspan(data_handle_type _Ptr_, span<_OtherIndexType, _Size>& _Exts) + constexpr explicit(_Size != rank_dynamic()) mdspan(data_handle_type _Ptr_, span<_OtherIndexType, _Size> _Exts) : _Ptr(_STD move(_Ptr_)), _Map(extents_type{_Exts}), _Acc() {} template @@ -982,7 +982,7 @@ public: !is_convertible_v&, mapping_type> || !is_convertible_v) mdspan(const mdspan<_OtherElementType, _OtherExtents, _OtherLayoutPolicy, _OtherAccessor>& _Other) - : _Ptr(_Other._Ptr), _Map(_Other._Map), _Acc(_Other._Acc) { + : _Ptr(_Other.data_handle()), _Map(_Other.mapping()), _Acc(_Other.accessor()) { static_assert(is_constructible_v, "The data_handle_type must be constructible from const typename OtherAccessor::data_handle_type& (N4950 " "[mdspan.mdspan.cons]/20.1)."); @@ -993,7 +993,7 @@ public: constexpr mdspan& operator=(const mdspan&) = default; constexpr mdspan& operator=(mdspan&&) = default; -#ifdef __clang__ // TRANSITION, P2128R6 +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 template requires (is_convertible_v<_OtherIndexTypes, index_type> && ...) && (is_nothrow_constructible_v && ...) @@ -1001,7 +1001,7 @@ public: _NODISCARD constexpr reference operator[](_OtherIndexTypes... _Indices) const { return _Acc.access(_Ptr, static_cast(_Map(static_cast(_STD move(_Indices))...))); } -#endif // __clang__ +#endif // __cpp_multidimensional_subscript template requires is_convertible_v @@ -1032,9 +1032,9 @@ public: } friend constexpr void swap(mdspan& _Left, mdspan& _Right) noexcept { - swap(_Left._Ptr, _Right._Ptr); - swap(_Left._Map, _Right._Map); - swap(_Left._Acc, _Right._Acc); + swap(_Left._Ptr, _Right._Ptr); // intentional ADL + swap(_Left._Map, _Right._Map); // intentional ADL + swap(_Left._Acc, _Right._Acc); // intentional ADL } _NODISCARD constexpr const extents_type& extents() const noexcept { @@ -1088,13 +1088,20 @@ public: private: template _NODISCARD constexpr reference _Index_impl(span<_OtherIndexType, rank()> _Indices, index_sequence<_Seq...>) const { -#ifdef __clang__ // TRANSITION, P2128R6 - return this->operator[](_STD as_const(_Indices[_Seq])...); -#else // ^^^ defined(__clang__) / !defined(__clang__) vvv - return _Acc.access(_Ptr, static_cast(_Map(static_cast(_STD as_const(_Indices[_Seq]))...))); -#endif // ^^^ !defined(__clang__) ^^^ +#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))...))); + } +#endif // __cpp_multidimensional_subscript + data_handle_type _Ptr{}; mapping_type _Map{}; accessor_type _Acc{}; diff --git a/tests/std/include/test_mdspan_support.hpp b/tests/std/include/test_mdspan_support.hpp index 9ac8383480..23b8902dc4 100644 --- a/tests/std/include/test_mdspan_support.hpp +++ b/tests/std/include/test_mdspan_support.hpp @@ -27,12 +27,10 @@ namespace detail { constexpr void check_implicit_conversion(T); // not defined } -// clang-format off template -concept NotImplicitlyConstructibleFrom = - std::constructible_from - && !requires(Args&&... args) { detail::check_implicit_conversion({std::forward(args)...}); }; -// clang-format on +concept NotImplicitlyConstructibleFrom = std::constructible_from && !requires(Args&&... args) { + detail::check_implicit_conversion({std::forward(args)...}); +}; namespace detail { template @@ -51,17 +49,14 @@ namespace detail { && std::same_as && is_mapping_of_v; - // clang-format off 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; - }; - // clang-format on + 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 { @@ -74,14 +69,11 @@ namespace detail { }; } // namespace detail -// 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 +concept CheckCallOperatorOfLayoutMapping = requires(const M m, Indices... i) { + { m(i...) } -> std::same_as; + { m(i...) == m(static_cast(i)...) } -> std::same_as; +}; template concept CheckStrideMemberFunction = requires(M mapping, M::rank_type i) { @@ -127,23 +119,21 @@ namespace detail { template concept CheckNestedTypesOfAccessorPolicy = sizeof(typename A::element_type) > 0 - && (!std::is_abstract_v) &&std::copyable&& std:: - is_nothrow_move_constructible_v&& std::is_nothrow_move_assignable_v< - typename A::data_handle_type>&& std::is_nothrow_swappable_v&& std:: - common_reference_with + && !std::is_abstract_v && std::copyable + && std::is_nothrow_move_constructible_v + && std::is_nothrow_move_assignable_v + && std::is_nothrow_swappable_v + && std::common_reference_with && (std::same_as || check_accessor_policy_requirements()) - && std::constructible_from&& std::is_same_v; + && std::constructible_from + && std::is_same_v; - // clang-format off template - concept CheckMemberFunctionsOfAccessorPolicy = - requires(const A a, const A::data_handle_type p, size_t i) { - { a.access(p, i) } -> std::same_as; - { a.offset(p, i) } -> std::same_as; - }; - // clang-format on + concept CheckMemberFunctionsOfAccessorPolicy = requires(const A a, const A::data_handle_type p, size_t i) { + { a.access(p, i) } -> std::same_as; + { a.offset(p, i) } -> std::same_as; + }; } // namespace detail template diff --git a/tests/std/test.lst b/tests/std/test.lst index da65c04701..018e7f0a00 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -243,6 +243,7 @@ tests\P0009R18_mdspan_layout_right tests\P0009R18_mdspan_layout_right_death tests\P0009R18_mdspan_layout_stride tests\P0009R18_mdspan_layout_stride_death +tests\P0009R18_mdspan_mdspan tests\P0019R8_atomic_ref tests\P0024R2_parallel_algorithms_adjacent_difference tests\P0024R2_parallel_algorithms_adjacent_find diff --git a/tests/std/tests/P0009R18_mdspan/test.cpp b/tests/std/tests/P0009R18_mdspan/test.cpp index dcd3274a72..28ea7b5aca 100644 --- a/tests/std/tests/P0009R18_mdspan/test.cpp +++ b/tests/std/tests/P0009R18_mdspan/test.cpp @@ -1029,10 +1029,10 @@ void mdspan_tests_observers() { static_assert(!mds.is_exhaustive()); static_assert(mds.is_strided()); -#ifdef __clang__ // TRANSITION, P2128R6 +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 static_assert(mds[1, 0] == 1); static_assert(mds[1, 2] == 7); -#endif // __clang__ +#endif // __cpp_multidimensional_subscript static_assert(mds[array{0, 1}] == 3); static_assert(mds[array{1, 1}] == 4); diff --git a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp index 4e2e247b86..d585c455b1 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp @@ -302,57 +302,57 @@ constexpr void check_correctness() { const array values{0, 1, 2}; mdspan, layout_left> vec{values.data()}; -#ifdef __clang__ // TRANSITION, P2128R6 +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 assert(vec[0] == 0); assert(vec[1] == 1); assert(vec[2] == 2); -#else // ^^^ defined(__clang__) / !defined(__clang__) vvv +#else // ^^^ defined(__cpp_multidimensional_subscript) / !defined(__cpp_multidimensional_subscript) vvv assert(vec[array{0}] == 0); assert(vec[array{1}] == 1); assert(vec[array{2}] == 2); -#endif // ^^^ !defined(__clang__) ^^^ +#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ } { // 3x2 matrix with column-major order const array values{0, 1, 2, 3, 4, 5}; mdspan, layout_left> matrix{values.data()}; -#ifdef __clang__ // TRANSITION, P2128R6 +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 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)); -#else // ^^^ defined(__clang__) / !defined(__clang__) vvv +#else // ^^^ defined(__cpp_multidimensional_subscript) / !defined(__cpp_multidimensional_subscript) vvv assert((matrix[array{0, 0}] == 0)); assert((matrix[array{1, 0}] == 1)); assert((matrix[array{2, 0}] == 2)); assert((matrix[array{0, 1}] == 3)); assert((matrix[array{1, 1}] == 4)); assert((matrix[array{2, 1}] == 5)); -#endif // ^^^ !defined(__clang__) ^^^ +#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ } { // 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}; -#ifdef __clang__ // TRANSITION, P2128R6 +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 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, 1, 2] == 17)); assert((tensor[2, 1, 3] == 23)); -#else // ^^^ defined(__clang__) / !defined(__clang__) vvv +#else // ^^^ defined(__cpp_multidimensional_subscript) / !defined(__cpp_multidimensional_subscript) vvv assert((tensor[array{0, 0, 0}] == 0)); assert((tensor[array{2, 0, 0}] == 2)); assert((tensor[array{1, 1, 1}] == 10)); assert((tensor[array{0, 0, 3}] == 18)); assert((tensor[array{2, 1, 2}] == 17)); assert((tensor[array{2, 1, 3}] == 23)); -#endif // ^^^ !defined(__clang__) ^^^ +#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ } { // 2x3x2x3 tensor @@ -360,7 +360,7 @@ constexpr void check_correctness() { 26, 27, 28, 29, 30, 31, 32, 33, 34, 35}; mdspan, layout_left> tensor{values.data(), 2, 3}; -#ifdef __clang__ // TRANSITION, P2128R6 +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 assert((tensor[0, 0, 0, 0] == 0)); assert((tensor[1, 0, 0, 0] == 1)); assert((tensor[0, 1, 1, 0] == 8)); @@ -368,7 +368,7 @@ constexpr void check_correctness() { assert((tensor[0, 0, 0, 2] == 24)); assert((tensor[0, 2, 0, 2] == 28)); assert((tensor[1, 2, 1, 2] == 35)); -#else // ^^^ defined(__clang__) / !defined(__clang__) vvv +#else // ^^^ defined(__cpp_multidimensional_subscript) / !defined(__cpp_multidimensional_subscript) vvv assert((tensor[array{0, 0, 0, 0}] == 0)); assert((tensor[array{1, 0, 0, 0}] == 1)); assert((tensor[array{0, 1, 1, 0}] == 8)); @@ -376,7 +376,7 @@ constexpr void check_correctness() { assert((tensor[array{0, 0, 0, 2}] == 24)); assert((tensor[array{0, 2, 0, 2}] == 28)); assert((tensor[array{1, 2, 1, 2}] == 35)); -#endif // ^^^ !defined(__clang__) ^^^ +#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ } } diff --git a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp index 67530019c1..dd67099a7a 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp @@ -303,43 +303,43 @@ constexpr void check_correctness() { const array vals{2, 1, 0}; mdspan, layout_right> vec{vals.data()}; -#ifdef __clang__ // TRANSITION, P2128R6 +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 assert(vec[0] == 2); assert(vec[1] == 1); assert(vec[2] == 0); -#else // ^^^ defined(__clang__) / !defined(__clang__) vvv +#else // ^^^ defined(__cpp_multidimensional_subscript) / !defined(__cpp_multidimensional_subscript) vvv assert(vec[array{0}] == 2); assert(vec[array{1}] == 1); assert(vec[array{2}] == 0); -#endif // ^^^ !defined(__clang__) ^^^ +#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ } { // 4x3 matrix with row-major order const array vals{11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; mdspan, layout_right> matrix{vals.data()}; -#ifdef __clang__ // TRANSITION, P2128R6 +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 assert((matrix[0, 0] == 11)); assert((matrix[0, 2] == 9)); assert((matrix[1, 1] == 7)); assert((matrix[2, 0] == 5)); assert((matrix[2, 2] == 3)); assert((matrix[3, 1] == 1)); -#else // ^^^ defined(__clang__) / !defined(__clang__) vvv +#else // ^^^ defined(__cpp_multidimensional_subscript) / !defined(__cpp_multidimensional_subscript) vvv assert((matrix[array{0, 0}] == 11)); assert((matrix[array{0, 2}] == 9)); assert((matrix[array{1, 1}] == 7)); assert((matrix[array{2, 0}] == 5)); assert((matrix[array{2, 2}] == 3)); assert((matrix[array{3, 1}] == 1)); -#endif // ^^^ !defined(__clang__) ^^^ +#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ } { // 4x3x2 tensor const array vals{23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; mdspan, layout_right> tensor{vals.data(), 4, 3, 2}; -#ifdef __clang__ // TRANSITION, P2128R6 +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 assert((tensor[0, 0, 0] == 23)); assert((tensor[0, 0, 1] == 22)); assert((tensor[0, 1, 0] == 21)); @@ -350,7 +350,7 @@ constexpr void check_correctness() { assert((tensor[1, 1, 1] == 14)); assert((tensor[2, 2, 1] == 6)); assert((tensor[3, 2, 1] == 0)); -#else // ^^^ defined(__clang__) / !defined(__clang__) vvv +#else // ^^^ defined(__cpp_multidimensional_subscript) / !defined(__cpp_multidimensional_subscript) vvv assert((tensor[array{0, 0, 0}] == 23)); assert((tensor[array{0, 0, 1}] == 22)); assert((tensor[array{0, 1, 0}] == 21)); @@ -361,7 +361,7 @@ constexpr void check_correctness() { assert((tensor[array{1, 1, 1}] == 14)); assert((tensor[array{2, 2, 1}] == 6)); assert((tensor[array{3, 2, 1}] == 0)); -#endif // ^^^ !defined(__clang__) ^^^ +#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ } { // 3x2x3x2 tensor @@ -369,7 +369,7 @@ constexpr void check_correctness() { 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; mdspan, layout_right> tensor{vals.data(), 2, 2}; -#ifdef __clang__ // TRANSITION, P2128R6 +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 assert((tensor[0, 0, 0, 0] == 35)); assert((tensor[0, 0, 0, 1] == 34)); assert((tensor[0, 0, 1, 0] == 33)); @@ -388,7 +388,7 @@ constexpr void check_correctness() { assert((tensor[1, 1, 1, 1] == 14)); assert((tensor[2, 0, 2, 0] == 7)); assert((tensor[2, 1, 2, 1] == 0)); -#else // ^^^ defined(__clang__) / !defined(__clang__) vvv +#else // ^^^ defined(__cpp_multidimensional_subscript) / !defined(__cpp_multidimensional_subscript) vvv assert((tensor[array{0, 0, 0, 0}] == 35)); assert((tensor[array{0, 0, 0, 1}] == 34)); assert((tensor[array{0, 0, 1, 0}] == 33)); @@ -407,7 +407,7 @@ constexpr void check_correctness() { assert((tensor[array{1, 1, 1, 1}] == 14)); assert((tensor[array{2, 0, 2, 0}] == 7)); assert((tensor[array{2, 1, 2, 1}] == 0)); -#endif // ^^^ !defined(__clang__) ^^^ +#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ } } diff --git a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp index 435d637177..ec4ea0711a 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp @@ -415,15 +415,15 @@ constexpr void check_correctness() { layout_stride::mapping m{E{}, array{1}}; mdspan, layout_stride> vec{vals.data(), m}; -#ifdef __clang__ // TRANSITION, P2128R6 +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 assert((vec[0] == 1)); assert((vec[1] == 2)); assert((vec[2] == 3)); -#else // ^^^ defined(__clang__) / !defined(__clang__) vvv +#else // ^^^ defined(__cpp_multidimensional_subscript) / !defined(__cpp_multidimensional_subscript) vvv assert((vec[array{0}] == 1)); assert((vec[array{1}] == 2)); assert((vec[array{2}] == 3)); -#endif // ^^^ !defined(__clang__) ^^^ +#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ } { // 2x3 matrix with row-major order @@ -432,21 +432,21 @@ constexpr void check_correctness() { layout_stride::mapping m{E{}, array{3, 1}}; mdspan matrix{vals.data(), m}; -#ifdef __clang__ // TRANSITION, P2128R6 +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 assert((matrix[0, 0] == 1)); assert((matrix[0, 1] == 2)); assert((matrix[0, 2] == 3)); assert((matrix[1, 0] == 4)); assert((matrix[1, 1] == 5)); assert((matrix[1, 2] == 6)); -#else // ^^^ defined(__clang__) / !defined(__clang__) vvv +#else // ^^^ defined(__cpp_multidimensional_subscript) / !defined(__cpp_multidimensional_subscript) vvv assert((matrix[array{0, 0}] == 1)); assert((matrix[array{0, 1}] == 2)); assert((matrix[array{0, 2}] == 3)); assert((matrix[array{1, 0}] == 4)); assert((matrix[array{1, 1}] == 5)); assert((matrix[array{1, 2}] == 6)); -#endif // ^^^ !defined(__clang__) ^^^ +#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ } { // 3x2x2 tensor @@ -456,7 +456,7 @@ constexpr void check_correctness() { assert(!m.is_exhaustive()); mdspan tensor{vals.data(), m}; -#ifdef __clang__ // TRANSITION, P2128R6 +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 assert((tensor[0, 0, 0] == 0)); assert((tensor[0, 0, 1] == 6)); assert((tensor[0, 1, 0] == 1)); @@ -469,7 +469,7 @@ constexpr void check_correctness() { assert((tensor[2, 0, 1] == 22)); assert((tensor[2, 1, 0] == 17)); assert((tensor[2, 1, 1] == 23)); -#else // ^^^ defined(__clang__) / !defined(__clang__) vvv +#else // ^^^ defined(__cpp_multidimensional_subscript) / !defined(__cpp_multidimensional_subscript) vvv assert((tensor[array{0, 0, 0}] == 0)); assert((tensor[array{0, 0, 1}] == 6)); assert((tensor[array{0, 1, 0}] == 1)); @@ -482,7 +482,7 @@ constexpr void check_correctness() { assert((tensor[array{2, 0, 1}] == 22)); assert((tensor[array{2, 1, 0}] == 17)); assert((tensor[array{2, 1, 1}] == 23)); -#endif // ^^^ !defined(__clang__) ^^^ +#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ } { // 2x3x3x2 tensor @@ -493,7 +493,7 @@ constexpr void check_correctness() { assert(m.is_exhaustive()); mdspan tensor{vals.data(), m}; -#ifdef __clang__ // TRANSITION, P2128R6 +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 assert((tensor[0, 0, 0, 0] == 0)); assert((tensor[0, 0, 0, 1] == 9)); assert((tensor[0, 0, 1, 0] == 3)); @@ -512,7 +512,7 @@ constexpr void check_correctness() { assert((tensor[1, 1, 1, 1] == 31)); assert((tensor[0, 2, 2, 0] == 8)); assert((tensor[1, 2, 2, 1] == 35)); -#else // ^^^ defined(__clang__) / !defined(__clang__) vvv +#else // ^^^ defined(__cpp_multidimensional_subscript) / !defined(__cpp_multidimensional_subscript) vvv assert((tensor[array{0, 0, 0, 0}] == 0)); assert((tensor[array{0, 0, 0, 1}] == 9)); assert((tensor[array{0, 0, 1, 0}] == 3)); @@ -531,7 +531,7 @@ constexpr void check_correctness() { assert((tensor[array{1, 1, 1, 1}] == 31)); assert((tensor[array{0, 2, 2, 0}] == 8)); assert((tensor[array{1, 2, 2, 1}] == 35)); -#endif // ^^^ !defined(__clang__) ^^^ +#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ } } diff --git a/tests/std/tests/P0009R18_mdspan_mdspan/env.lst b/tests/std/tests/P0009R18_mdspan_mdspan/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_mdspan/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/test.cpp b/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp new file mode 100644 index 0000000000..a0e16d6339 --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp @@ -0,0 +1,1352 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +class ActionTracker { +public: + constexpr explicit ActionTracker(int id_) noexcept : id{id_} {} + + constexpr ActionTracker(const ActionTracker& other) noexcept : id{other.id}, copy_constructed{true} {} + + constexpr ActionTracker(ActionTracker&& other) noexcept : id{exchange(other.id, -1)}, move_constructed{true} {} + + constexpr ActionTracker& operator=(const ActionTracker& other) noexcept { + id = other.id; + copy_assigned = true; + return *this; + } + + constexpr ActionTracker& operator=(ActionTracker&& other) noexcept { + id = exchange(other.id, -1); + move_assigned = true; + return *this; + } + + constexpr int get_id() const noexcept { + return id; + } + + constexpr bool is_copy_constructed() const noexcept { + return copy_constructed; + } + + constexpr bool is_move_constructed() const noexcept { + return move_constructed; + } + + constexpr bool is_copy_assigned() const noexcept { + return copy_assigned; + } + + constexpr bool is_move_assigned() const noexcept { + return move_assigned; + } + + constexpr bool is_swapped() const noexcept { + return swapped; + } + + friend constexpr void swap(ActionTracker& left, ActionTracker& right) noexcept { + left.swapped = true; + right.swapped = true; + swap(left.id, right.id); + // leave the other members alone + } + +private: + int id; + bool copy_constructed = false; + bool move_constructed = false; + bool copy_assigned = false; + bool move_assigned = false; + bool swapped = false; +}; + +enum class RequireId { no, yes }; + +template +struct TrackingLayout { + template + requires constructible_from, Extents> + && (check_layout_mapping_policy_requirements()) + class mapping : public ActionTracker { + public: + using extents_type = Extents; + using index_type = extents_type::index_type; + using rank_type = extents_type::rank_type; + using layout_type = TrackingLayout; + using underlying_mapping = MpPolicy::template mapping; + + constexpr explicit mapping(int id) + requires default_initializable + : ActionTracker(id), mp() {} + + constexpr mapping(const extents_type& e, int id) : ActionTracker(id), mp(e) {} + + constexpr mapping(const extents_type& e) + requires (!to_underlying(ReqId)) + : ActionTracker(-1), constructed_with_extents_only{true}, mp(e) {} + + template + mapping(initializer_list) = delete; // we should never use list-initialization in + + constexpr mapping(const mapping& other) : ActionTracker(other), mp(other.mp) {} + + constexpr mapping(mapping&& other) noexcept : ActionTracker(move(other)), mp(move(other.mp)) {} + + // NB: special constructor for check_construction_from_other_mdspan's effects test + template + requires is_constructible_v + constexpr mapping(const mapping& other) : ActionTracker(other), mp(other.get_underlying()) {} + + constexpr mapping& operator=(const mapping&) = default; + constexpr mapping& operator=(mapping&&) = default; + + constexpr const extents_type& extents() const { + return mp.extents(); + } + + template + constexpr index_type operator()(IndexTypes... indices) const { + return mp(indices...); + } + + constexpr index_type required_span_size() const { + return mp.required_span_size(); + } + + constexpr bool is_unique() const { + return mp.is_unique(); + } + + constexpr bool is_exhaustive() const { + return mp.is_exhaustive(); + } + + constexpr bool is_strided() const { + return mp.is_strided(); + } + + constexpr index_type stride() const { + return mp.stride(); + } + + static constexpr bool is_always_unique() { + return underlying_mapping::is_always_unique(); + } + + static constexpr bool is_always_exhaustive() { + return underlying_mapping::is_always_exhaustive(); + } + + static constexpr bool is_always_strided() { + return underlying_mapping::is_always_strided(); + } + + constexpr bool operator==(const mapping& other) const { + return mp == other.mp; + } + + constexpr bool is_constructed_with_extents_only() const noexcept + requires (!to_underlying(ReqId)) + { + return constructed_with_extents_only; + } + + constexpr const underlying_mapping& get_underlying() const noexcept { + return mp; + } + + friend constexpr void swap(mapping& left, mapping& right) noexcept { + swap(static_cast(left), static_cast(right)); + swap(left.mp, right.mp); + } + + private: + bool constructed_with_extents_only = false; + underlying_mapping mp; + }; +}; + +static_assert(check_layout_mapping_policy_requirements, dextents>()); +static_assert(check_layout_mapping_policy_requirements, dextents>()); + +struct VectorBoolAccessor { + using offset_policy = VectorBoolAccessor; + using element_type = bool; + using reference = vector::reference; + using data_handle_type = vector::iterator; + + constexpr reference access(data_handle_type handle, size_t off) const { + return handle[static_cast(off)]; + } + + constexpr data_handle_type offset(data_handle_type handle, size_t off) const { + return handle + static_cast(off); + } +}; + +static_assert(check_accessor_policy_requirements()); + +template +class TrackingDataHandle : public ActionTracker { +public: + using data_handle_type = ElementType*; + + constexpr explicit TrackingDataHandle(int id, data_handle_type ptr_) noexcept : ActionTracker(id), ptr{ptr_} {} + + // NB: special constructor for check_construction_from_other_mdspan's effects test + template + requires is_convertible_v + constexpr TrackingDataHandle(const TrackingDataHandle& other) : ActionTracker(other) {} + + template + TrackingDataHandle(initializer_list) = delete; // we should never use list-initialization in + + constexpr TrackingDataHandle(const TrackingDataHandle& other) noexcept : ActionTracker(other), ptr{other.ptr} {} + + constexpr TrackingDataHandle(TrackingDataHandle&& other) noexcept + : ActionTracker(move(other)), ptr{exchange(other.ptr, nullptr)} {} + + constexpr TrackingDataHandle& operator=(const TrackingDataHandle&) noexcept = default; + constexpr TrackingDataHandle& operator=(TrackingDataHandle&&) noexcept = default; + + constexpr data_handle_type get_ptr() const noexcept { + return ptr; + } + + friend constexpr void swap(TrackingDataHandle& left, TrackingDataHandle& right) noexcept { + swap(static_cast(left), static_cast(right)); + swap(left.ptr, right.ptr); + } + +private: + data_handle_type ptr; +}; + +template +class AccessorWithTrackingDataHandle { +public: + using offset_policy = AccessorWithTrackingDataHandle; + using element_type = ElementType; + using reference = ElementType&; + using data_handle_type = TrackingDataHandle; + + constexpr reference access(data_handle_type handle, size_t off) const { + return handle.get_ptr()[off]; + } + + constexpr data_handle_type offset(data_handle_type handle, size_t off) const { + return TrackingDataHandle{handle.get_id(), handle.get_ptr() + off}; + } +}; + +template +class TrackingAccessor : public ActionTracker { +public: + using offset_policy = TrackingAccessor; + using element_type = ElementType; + using reference = ElementType&; + using data_handle_type = TrackingDataHandle; + + constexpr explicit TrackingAccessor(int id) noexcept : ActionTracker(id) {} + + // NB: special constructor for check_construction_from_other_mdspan's effects test + template + requires is_convertible_v + constexpr TrackingAccessor(const TrackingAccessor& other) : ActionTracker(other) {} + + constexpr reference access(data_handle_type handle, size_t off) const { + return handle.get_ptr()[off]; + } + + constexpr data_handle_type offset(data_handle_type handle, size_t off) const { + return data_handle_type{handle.get_id(), handle.get_ptr() + off}; + } + + friend constexpr void swap(TrackingAccessor& left, TrackingAccessor& right) noexcept { + swap(static_cast(left), static_cast(right)); + } +}; + +static_assert(check_accessor_policy_requirements>()); + +template +struct AccessorWithCustomOffsetPolicy { + using offset_policy = default_accessor; + using element_type = offset_policy::element_type; + using reference = offset_policy::reference; + using data_handle_type = offset_policy::data_handle_type; + + AccessorWithCustomOffsetPolicy() = default; + + // NB: special constructor for check_construction_from_other_mdspan's explicitness test + template + requires is_convertible_v + constexpr explicit AccessorWithCustomOffsetPolicy(AccessorWithCustomOffsetPolicy) noexcept {} + + constexpr operator const offset_policy&() const { + return offpol; + } + + constexpr reference access(data_handle_type handle, size_t off) const { + return offpol(handle, off); + } + + constexpr data_handle_type offset(data_handle_type handle, size_t off) const { + return offpol.offset(handle, off); + } + +private: + offset_policy offpol; +}; + +static_assert(check_accessor_policy_requirements>()); + +template +struct TrivialAccessor { + using offset_policy = TrivialAccessor; + using element_type = ElementType; + using reference = ElementType&; + using data_handle_type = ElementType*; + + constexpr reference access(data_handle_type handle, size_t off) const noexcept { + return handle[off]; + } + + constexpr data_handle_type offset(data_handle_type handle, size_t off) const noexcept { + return handle + off; + } + + int member; +}; + +static_assert(check_accessor_policy_requirements>()); +static_assert(is_trivial_v>); + +constexpr void check_modeled_concepts() { + using Mds = mdspan, TrackingLayout, + AccessorWithCustomOffsetPolicy>; + static_assert(copyable); + static_assert(is_nothrow_move_constructible_v); + static_assert(is_nothrow_move_assignable_v); + static_assert(is_nothrow_swappable_v); + static_assert( + is_trivially_copyable_v + == (is_trivially_copyable_v && is_trivially_copyable_v + && is_trivially_copyable_v) ); +} + +constexpr void check_member_types() { + using Ext = extents; + using Layout = layout_stride; + using Accessor = TrivialAccessor; + using Mds = mdspan; + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + static_assert(same_as>); + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); +} + +constexpr void check_observers() { + using Ext = extents; + using Mds = mdspan>; + + { // Check results + static_assert(Mds::rank() == Ext::rank()); + static_assert(Mds::rank_dynamic() == Ext::rank_dynamic()); + static_assert(Mds::static_extent(0) == Ext::static_extent(0)); + static_assert(Mds::static_extent(1) == Ext::static_extent(1)); + static_assert(Mds::static_extent(2) == Ext::static_extent(2)); + static_assert(Mds::static_extent(3) == Ext::static_extent(3)); + static_assert(Mds::static_extent(4) == Ext::static_extent(4)); + } + + { // Check return types + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + } + + { // Check noexceptness + static_assert(noexcept(Mds::rank())); + static_assert(noexcept(Mds::rank_dynamic())); + static_assert(noexcept(Mds::static_extent(0))); + } +} + +constexpr void check_default_constructor() { + { // Check constraint: 'rank_dynamic() > 0' + static_assert(is_default_constructible_v>>); + static_assert(!is_default_constructible_v>>); + static_assert(!is_default_constructible_v>>); + } + + { // Check constraints: 'is_default_constructible_v' + static_assert(is_default_constructible_v, layout_right, VectorBoolAccessor>>); + static_assert( + !is_default_constructible_v, layout_right, TrackingAccessor>>); + } + + { // Check constraint: 'is_default_constructible_v' + static_assert(!is_default_constructible_v, layout_stride>>); + static_assert(!is_default_constructible_v, TrackingLayout<>>>); + } + + { // Check constraint: 'is_default_constructible_v' + static_assert(is_default_constructible_v< + mdspan, layout_right, AccessorWithCustomOffsetPolicy>>); + static_assert( + !is_default_constructible_v, layout_right, TrackingAccessor>>); + } + + { // Check effects + mdspan, layout_stride, TrivialAccessor> mds{}; + assert(mds.data_handle() == nullptr); + assert((mds.mapping().strides() == array{0, 0, 1})); + assert(mds.accessor().member == 0); + } +} + +constexpr void check_defaulted_copy_and_move_constructors() { + using Ext = extents; + using Mds = mdspan, TrackingAccessor>; + short bits_of_218[] = {1, 1, 0, 1, 1, 0, 1, 0}; + + { // Check defaulted copy constructor + Mds mds1{ + TrackingDataHandle{2, bits_of_218}, TrackingLayout<>::mapping(4), TrackingAccessor{8}}; + Mds mds2{mds1}; + assert(mds2.data_handle().is_copy_constructed()); + assert(mds2.mapping().is_copy_constructed()); + assert(mds2.accessor().is_copy_constructed()); + } + + { // Check defaulted move constructor + Mds mds1{ + TrackingDataHandle{2, bits_of_218}, TrackingLayout<>::mapping(4), TrackingAccessor{8}}; + Mds mds2{move(mds1)}; + assert(mds2.data_handle().is_move_constructed()); + assert(mds2.mapping().is_move_constructed()); + assert(mds2.accessor().is_move_constructed()); + } +} + +constexpr void check_data_handle_and_indices_pack_constructor() { + { // Check constraint: '(is_convertible_v && ...)' + using Mds = mdspan>; + static_assert(is_constructible_v); + static_assert(is_constructible_v); + static_assert(is_constructible_v); + static_assert(is_constructible_v>); + static_assert(!is_constructible_v); + } + + { // Check constraint: '(is_nothrow_constructible && ...)' + using Mds = mdspan>; + static_assert(is_constructible_v>); + static_assert(!is_constructible_v>); + } + + { // Check constraint: 'N == rank() || N == rank_dynamic()' + using Mds = mdspan>; + static_assert(!is_constructible_v); + static_assert(is_constructible_v); + static_assert(!is_constructible_v); + static_assert(is_constructible_v); + } + + { // Check constraint: 'is_constructible_v' + static_assert(is_constructible_v, layout_left>, int* const, int, int>); + static_assert(!is_constructible_v, layout_stride>, int*, int, int>); + static_assert(!is_constructible_v, TrackingLayout<>>, int*, int, int>); + } + + { // Check constraint: 'is_default_constructible_v' + static_assert(is_constructible_v, layout_right, VectorBoolAccessor>, + vector::iterator, int, int>); + static_assert( + !is_constructible_v, layout_right, TrackingAccessor>, int*, int, int>); + } + + { // Check explicitness + using Mds = mdspan>; + static_assert(NotImplicitlyConstructibleFrom); + static_assert(NotImplicitlyConstructibleFrom); + static_assert(!NotImplicitlyConstructibleFrom); + } + + { // Check effects: 'direct-non-list-initializes ptr_ with std::move(p)' + int ints[4] = {1, 2, 3, 4}; + mdspan, layout_right, AccessorWithTrackingDataHandle> mds{ + TrackingDataHandle{1, ints}, 2, 2}; + assert(mds.data_handle().is_move_constructed()); + } + + { // Check effects: 'direct-non-list-initializes map_ with + // extents_type(static_cast(std::move(exts))...)' + using Ext = dextents; + struct FunnyIndex { + constexpr operator Ext::index_type() const& noexcept { + return 1; + } + + constexpr operator integral auto() && noexcept { + return 1; + } + + constexpr operator Ext::index_type() && noexcept { + return 3; + } + }; + + char digits[9] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'}; + FunnyIndex i; + mdspan mds{digits, i, i}; + assert(mds.extent(0) == 3); + assert(mds.extent(1) == 3); + } + + { // Check effects: 'value-initializes acc_' + int ints[4] = {2, 4, 8, 16}; + mdspan, layout_left, TrivialAccessor> mds{ints, 2, 2}; + assert(mds.accessor().member == 0); + } +} + +constexpr void check_data_handle_and_span_array_constructors() { + { // Check constraint: 'is_convertible_v' + using Mds = mdspan>; + static_assert(is_constructible_v>); + static_assert(is_constructible_v>); + static_assert(is_constructible_v, 3>>); + static_assert(is_constructible_v, 3>>); + static_assert(!is_constructible_v>); + static_assert(!is_constructible_v>); + } + + { // Check constraint: 'is_nothrow_constructible' + using Mds = mdspan>; + static_assert(is_constructible_v, 2>>); + static_assert(is_constructible_v, 2>>); + static_assert(!is_constructible_v, 2>>); + static_assert(!is_constructible_v, 2>>); + } + + { // Check constraint: 'N == rank() || N == rank_dynamic()' + using Mds = mdspan>; + static_assert(!is_constructible_v>); + static_assert(!is_constructible_v>); + static_assert(is_constructible_v>); + static_assert(is_constructible_v>); + static_assert(!is_constructible_v>); + static_assert(!is_constructible_v>); + static_assert(is_constructible_v>); + static_assert(is_constructible_v>); + } + + { // Check constraint: 'is_constructible_v' + static_assert(is_constructible_v, layout_left>, int* const, span>); + static_assert(is_constructible_v, layout_left>, int* const, array>); + static_assert( + is_constructible_v, TrackingLayout>, + int* const, span>); + static_assert( + is_constructible_v, TrackingLayout>, + int* const, array>); + static_assert(!is_constructible_v, layout_stride>, int*, span>); + static_assert(!is_constructible_v, layout_stride>, int*, array>); + static_assert(!is_constructible_v, TrackingLayout<>>, int*, span>); + static_assert(!is_constructible_v, TrackingLayout<>>, int*, array>); + } + + { // Check constraint: 'is_default_constructible_v' + static_assert(is_constructible_v, layout_right, VectorBoolAccessor>, + vector::iterator, span>); + static_assert(is_constructible_v, layout_right, VectorBoolAccessor>, + vector::iterator, array>); + static_assert(!is_constructible_v, layout_right, TrackingAccessor>, int*, + span>); + static_assert(!is_constructible_v, layout_right, TrackingAccessor>, int*, + array>); + } + + { // Check explicitness + using Mds = mdspan>; + static_assert(NotImplicitlyConstructibleFrom>); + static_assert(NotImplicitlyConstructibleFrom>); + static_assert(!NotImplicitlyConstructibleFrom>); + static_assert(!NotImplicitlyConstructibleFrom>); + static_assert(NotImplicitlyConstructibleFrom, 3>>); + static_assert(NotImplicitlyConstructibleFrom, 3>>); + static_assert(!NotImplicitlyConstructibleFrom, 1>>); + static_assert(!NotImplicitlyConstructibleFrom, 1>>); + } + + { // Check effects: 'direct-non-list-initializes ptr_ with std::move(p)' + int ints[4] = {1, 2, 3, 4}; + array indices{2, 2}; + mdspan, layout_right, AccessorWithTrackingDataHandle> mds1{ + TrackingDataHandle{1, ints}, indices}; + assert(mds1.data_handle().is_move_constructed()); + span s{indices}; + mdspan, layout_right, AccessorWithTrackingDataHandle> mds2{ + TrackingDataHandle{1, ints}, s}; + assert(mds2.data_handle().is_move_constructed()); + } + + { // Check effects: 'direct-non-list-initializes map_ with extents_type(exts)' + using Ext = dextents; + struct FunnyIndex { + constexpr operator integral auto() & noexcept { + return 1; + } + + constexpr operator integral auto() const& noexcept { + return 1; + } + + constexpr operator Ext::index_type() const& noexcept { + return 3; + } + + constexpr operator integral auto() && noexcept { + return 1; + } + }; + + char digits[9] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'}; + array indices; + mdspan> mds1{digits, indices}; + assert(mds1.extent(0) == 3); + span s{indices}; + mdspan mds2{digits, s}; + assert(mds2.extent(0) == 3); + assert(mds2.extent(1) == 3); + } + + { // Check effects: 'value-initializes acc_' + int ints[4] = {1, 3, 7, 15}; + array indices{2, 2}; + mdspan, layout_left, TrivialAccessor> mds1{ints, indices}; + assert(mds1.accessor().member == 0); + span s{indices}; + mdspan, layout_left, TrivialAccessor> mds2{ints, s}; + assert(mds2.accessor().member == 0); + } +} + +constexpr void check_data_handle_and_extents_constructor() { + { // Check constraint: 'is_constructible_v' + static_assert(is_constructible_v>, int*, dextents>); + static_assert(is_constructible_v>, int*, dextents>); + static_assert(!is_constructible_v, layout_stride>, int*, dextents>); + static_assert(!is_constructible_v>, int*, dextents>); + static_assert(!is_constructible_v>, int*, extents>); + } + + { // Check constraint: is_default_constructible_v + static_assert(is_constructible_v, layout_right, VectorBoolAccessor>, + vector::iterator, dextents>); + static_assert(is_constructible_v, layout_right, VectorBoolAccessor>, + vector::iterator, extents>); + static_assert(!is_constructible_v, layout_right, TrackingAccessor>, int*, + dextents>); + static_assert(!is_constructible_v, layout_right, TrackingAccessor>, int*, + extents>); + } + + { // Check effects: 'direct-non-list-initializes ptr_ with std::move(p)' + char physics[4] = {'s', 't', 'v', 'a'}; + mdspan, layout_right, AccessorWithTrackingDataHandle> mds{ + TrackingDataHandle{1, physics}, extents{}}; + assert(mds.data_handle().is_move_constructed()); + } + + { // Check effects: "direct-non-list-initializes map_ with ext" + short lucky_numbers[6] = {2, 15, 17, 31, 34, 35}; + mdspan, TrackingLayout> mds{ + lucky_numbers, extents{}}; + assert(mds.mapping().is_constructed_with_extents_only()); + } + + { // Check effects: 'value-initializes acc_' + int ints[4] = {1, 22, 333, 4444}; + mdspan, layout_left, TrivialAccessor> mds{ints, extents{}}; + assert(mds.accessor().member == 0); + } +} + +constexpr void check_data_handle_and_mapping_constructor() { + { // Check constraint: 'is_default_constructible_v' + static_assert(is_constructible_v, layout_left, VectorBoolAccessor>, + vector::iterator, layout_left::mapping>>); + static_assert(is_constructible_v, layout_left>, int* const, + layout_left::mapping>>); + static_assert(!is_constructible_v< + mdspan, extents, TrackingLayout<>, TrackingAccessor>>, + vector*, TrackingLayout<>::mapping>>); + static_assert(!is_constructible_v< + mdspan, extents, TrackingLayout<>, TrackingAccessor>>, + deque* const, TrackingLayout<>::mapping>>); + } + + { // Check effect: 'direct-non-list-initializes ptr_ with std::move(p)' + using Ext = extents; + char banana[6] = {'b', 'a', 'n', 'a', 'n', 'a'}; + mdspan> mds{ + TrackingDataHandle{1, banana}, layout_stride::mapping{Ext{}, array{3, 6, 1}}}; + assert(mds.data_handle().is_move_constructed()); + } + + { // Check effect: 'direct-non-list-initializes map_ with m' + using Ext = extents; + char x[9] = {'\\', ' ', '/', ' ', 'X', ' ', '/', ' ', '\\'}; + mdspan> mds{x, TrackingLayout<>::mapping{Ext{}, 23}}; + assert(mds.mapping().is_copy_constructed()); + } + + { // Check effect: 'value-initializes acc_' + using Ext = extents; + int twelve[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + mdspan> mds{ + twelve, layout_stride::mapping{Ext{}, array{2, 1, 4}}}; + assert(mds.accessor().member == 0); + } +} + +constexpr void check_data_handle_and_mapping_and_accessor_constructor() { + using Ext = extents; + using Mds = mdspan, TrackingAccessor>; + + unsigned int identity_matrix[] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; + Mds mds{TrackingDataHandle{16, identity_matrix}, TrackingLayout<>::mapping(17), + TrackingAccessor{18}}; + + // Check effects: + // - Direct-non-list-initializes ptr_ with std::move(p), + // - direct-non-list-initializes map_ with m, and + // - direct-non-list-initializes acc_ with a. + assert(mds.data_handle().is_move_constructed()); + assert(mds.data_handle().get_id() == 16); + assert(mds.mapping().is_copy_constructed()); + assert(mds.mapping().get_id() == 17); + assert(mds.accessor().is_copy_constructed()); + assert(mds.accessor().get_id() == 18); +} + +constexpr void check_construction_from_other_mdspan() { + { // Check constraint: 'is_constructible_v&>' + static_assert(is_constructible_v, layout_stride>, + mdspan, layout_right>>); + static_assert(!is_constructible_v, layout_left>, + mdspan, layout_right>>); + static_assert(!is_constructible_v, layout_left>, + mdspan, layout_left>>); + } + + { // Check constraint: 'is_constructible_v' + using Ext = extents; + static_assert(is_constructible_v>, + mdspan>>); + static_assert(!is_constructible_v>, + mdspan>>); + } + + { // Check explicitness + static_assert(NotImplicitlyConstructibleFrom>, mdspan>>); + static_assert(NotImplicitlyConstructibleFrom, layout_left>, + mdspan, layout_stride>>); + static_assert(!NotImplicitlyConstructibleFrom, layout_stride>, + mdspan, layout_left>>); + static_assert(NotImplicitlyConstructibleFrom< + mdspan, layout_left, AccessorWithCustomOffsetPolicy>, + mdspan, layout_left, AccessorWithCustomOffsetPolicy>>); + static_assert(!NotImplicitlyConstructibleFrom< + mdspan, layout_left, default_accessor>, + mdspan, layout_left, default_accessor>>); + } + + { // Check effects + int data[] = { + 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}; + + using Ext = dextents; + mdspan, TrackingAccessor> mds1{TrackingDataHandle{4, data}, + TrackingLayout<>::mapping{Ext{3, 3, 3}, 4}, TrackingAccessor{4}}; + mdspan, TrackingLayout<>, TrackingAccessor> mds2{mds1}; + assert(mds2.data_handle().is_copy_constructed()); + assert(mds2.mapping().is_copy_constructed()); + assert(mds2.accessor().is_copy_constructed()); + } +} + +constexpr void check_defaulted_copy_and_move_assignment_operators() { + using Ext = extents; + using Mds = mdspan, TrackingAccessor>; + short bits_of_218[] = {1, 1, 0, 1, 1, 0, 1, 0}; + short bits_of_248[] = {1, 1, 1, 1, 1, 0, 0, 0}; + + { // Check defaulted copy assignment operator + Mds mds1{ + TrackingDataHandle{2, bits_of_218}, TrackingLayout<>::mapping(4), TrackingAccessor{8}}; + Mds mds2{ + TrackingDataHandle{3, bits_of_248}, TrackingLayout<>::mapping(5), TrackingAccessor{9}}; + mds1 = mds2; + assert(mds1.data_handle().is_copy_assigned()); + assert(mds1.mapping().is_copy_assigned()); + assert(mds1.accessor().is_copy_assigned()); + } + + { // Check defaulted move assignment operator + Mds mds1{ + TrackingDataHandle{2, bits_of_218}, TrackingLayout<>::mapping(4), TrackingAccessor{8}}; + Mds mds2{ + TrackingDataHandle{3, bits_of_248}, TrackingLayout<>::mapping(5), TrackingAccessor{9}}; + mds1 = move(mds2); + assert(mds1.data_handle().is_move_assigned()); + assert(mds1.mapping().is_move_assigned()); + assert(mds1.accessor().is_move_assigned()); + } +} + +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 +template +concept CanCallMultidimSubscriptOp = requires(const Mds& mds, IndexTypes... indices) { + { mds[indices...] } -> same_as; +}; + +constexpr void check_multidimensional_subscript_operator() { + { // Check constraint: '(is_convertible_v && ...)' + using Mds = mdspan>; + static_assert(CanCallMultidimSubscriptOp); + static_assert(CanCallMultidimSubscriptOp); + static_assert(CanCallMultidimSubscriptOp); + static_assert(CanCallMultidimSubscriptOp); + static_assert(CanCallMultidimSubscriptOp, int, int, int, int>); +#ifndef __clang__ // TRANSITION, Clang 17 + static_assert(!CanCallSubscriptOperator); +#endif // __clang__ + } + + { // Check constraint: '(is_nothrow_constructible_v && ...)' + using Mds = mdspan>; + static_assert(CanCallMultidimSubscriptOp, int>); +#ifndef __clang__ // TRANSITION, Clang 17 + static_assert(!CanCallSubscriptOperator, int>); +#endif // __clang__ + } + + { // Check constraint: 'sizeof...(OtherIndexTypes) == rank()' + using Mds = mdspan>; + static_assert(CanCallMultidimSubscriptOp); +#ifndef __clang__ // TRANSITION, Clang 17 + static_assert(!CanCallSubscriptOperator); + static_assert(!CanCallSubscriptOperator); +#endif // __clang__ + } + + { // Check correctness + using Ext = extents; + vector bools{true, false, false, true}; + mdspan, VectorBoolAccessor> mds{ + bools.begin(), TrackingLayout::mapping(5)}; + same_as::reference> decltype(auto) r1 = mds[1, 1]; + assert(r1); + same_as::reference> decltype(auto) r2 = as_const(mds)[0, 1]; + assert(!r2); + } + + { // Check that indices are moved and then casted to 'index_type' + using Ext = dextents; + struct FunnyIndex { + constexpr operator integral auto() const& noexcept { + return 0; + } + + constexpr operator integral auto() && noexcept { + return 0; + } + + constexpr operator Ext::index_type() && noexcept { + return 1; + } + }; + + int mat2x2[4] = {0, 0, 0, 1}; + mdspan mds{mat2x2, 2, 2}; + FunnyIndex i; + assert((mds[i, i] == 1)); + } + + { // Check that indices are passed by value + struct WeirdIndex { + WeirdIndex() = default; + constexpr WeirdIndex(const WeirdIndex&) : val{1} {} + constexpr WeirdIndex(WeirdIndex&&) : val{2} {} + + constexpr operator int() const noexcept { + return val; + } + + int val = 0; + }; + + int ten2x2x3[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; + mdspan> mds{ten2x2x3}; + WeirdIndex i; + assert((mds[i, i, move(i)] == 1)); + } +} +#endif // __cpp_multidimensional_subscript + +template +concept CanCallSubscriptOp = requires(const Mds& mds, span s, const array& a) { + { mds[s] } -> same_as; + { mds[a] } -> same_as; +}; + +constexpr void check_span_array_subscript_operator() { + { // Check constraint: 'is_convertible_v + using Mds = mdspan>; + static_assert(CanCallSubscriptOp); + static_assert(CanCallSubscriptOp); + static_assert(CanCallSubscriptOp); + static_assert(CanCallSubscriptOp); + static_assert(CanCallSubscriptOp); + static_assert(CanCallSubscriptOp); + static_assert(CanCallSubscriptOp); + static_assert(CanCallSubscriptOp); + static_assert(CanCallSubscriptOp); + static_assert(CanCallSubscriptOp); + static_assert(CanCallSubscriptOp>); + static_assert(!CanCallSubscriptOp); + } + + { // Check constraint: 'is_nothrow_constructible_v' + using Mds = mdspan>; + static_assert(CanCallSubscriptOp>); + static_assert(!CanCallSubscriptOp>); + } + + { // Check function argument: '(span|array)::size() == rank()' + using Mds = mdspan>; + static_assert(!CanCallSubscriptOp); + static_assert(!CanCallSubscriptOp); + } + + { // Check correctness + using Ext = extents; + vector bools{true, true, true, true, false, true, true, true, true}; + mdspan, VectorBoolAccessor> mds{bools.begin(), TrackingLayout<>::mapping(99)}; + + array a1{1, 1}; + same_as::reference> decltype(auto) ar1 = mds[a1]; + assert(!ar1); + span s1{a1}; + same_as::reference> decltype(auto) sr1 = mds[s1]; + assert(!sr1); + + array a2{2, 2}; + same_as::reference> decltype(auto) ar2 = as_const(mds)[a2]; + assert(ar2); + span s2{a2}; + same_as::reference> decltype(auto) sr2 = as_const(mds)[s2]; + assert(sr2); + } + + { // Check that indices are expanded in as_const function and passed to multidimensional subscript operator + using Ext = dextents; + struct FunkyIndex { + FunkyIndex() = default; + FunkyIndex(FunkyIndex&) = delete; + FunkyIndex(const FunkyIndex&) = default; + + constexpr operator integral auto() const& noexcept { + return 0; + } + + constexpr operator integral auto() && noexcept { + return 0; + } + + constexpr operator Ext::index_type() && noexcept { + return 1; + } + }; + + char alpha[4] = {'a', 'b', 'c', 'd'}; + mdspan mds{alpha, 2, 2}; + array a; + assert(mds[a] == 'd'); + span s{a}; + assert(mds[s] == 'd'); + } +} + +constexpr void check_size() { + const int some_data[60] = {}; + + { // Not empty mdspan + mdspan mds1{some_data, extents{}}; + assert(mds1.size() == 60); + mdspan mds2{some_data, extents{12}}; + assert(mds2.size() == 60); + mdspan mds3{some_data, dextents{2, 3, 5}}; + assert(mds3.size() == 30); + } + + { // Empty mdspan + mdspan mds1{some_data, extents{}}; + assert(mds1.size() == 0); + mdspan mds2{some_data, extents{0}}; + assert(mds2.size() == 0); + mdspan mds3{some_data, extents{0}}; + assert(mds3.size() == 0); + } + + { // Mdspan with 'rank() == 0' + mdspan mds{some_data, extents{}}; + assert(mds.size() == 1); + } + + { // Mdspan whose index space size would not be representable as index_type if 0 wasn't there + mdspan mds1{some_data, extents{}}; + assert(mds1.size() == 0); + mdspan mds2{some_data, dextents{32767, 3, 0}}; + assert(mds2.size() == 0); + } + + { // Other properties + mdspan mds{some_data, extents{}}; + same_as decltype(auto) s1 = mds.size(); + assert(s1 == 60); + static_assert(noexcept(mds.size())); + same_as decltype(auto) s2 = as_const(mds).size(); + assert(s2 == 60); + static_assert(noexcept(as_const(mds).size())); + } +} + +constexpr void check_empty() { + const int some_data[24] = {}; + + { // Not empty mdspan + mdspan mds1{some_data, extents{}}; + assert(!mds1.empty()); + mdspan mds2{some_data, extents{3}}; + assert(!mds2.empty()); + mdspan mds3{some_data, dextents{3, 3, 4}}; + assert(!mds3.empty()); + } + + { // Empty mdspan + mdspan mds1{some_data, extents{}}; + assert(mds1.empty()); + mdspan mds2{some_data, extents{0}}; + assert(mds2.empty()); + mdspan mds3{some_data, extents{0}}; + assert(mds3.empty()); + } + + { // Mdspan with 'rank() == 0' + mdspan mds{some_data, extents{}}; + assert(!mds.empty()); + } + + { // Mdspan whose index space size would not be representable as index_type if 0 wasn't there + mdspan mds1{some_data, extents{}}; + assert(mds1.empty()); + mdspan mds2{some_data, dextents{32767, 3, 0}}; + assert(mds2.empty()); + } + + { // Other properties + mdspan mds{some_data, extents{}}; + same_as decltype(auto) b1 = mds.empty(); + assert(b1); + static_assert(noexcept(mds.empty())); + same_as decltype(auto) b2 = as_const(mds).empty(); + assert(b2); + static_assert(noexcept(as_const(mds).empty())); + } +} + +constexpr void check_swap() { + using E = extents; + using Mds = mdspan, TrackingAccessor>; + static_assert(is_nothrow_swappable_v); + static_assert(!is_swappable_v); + + int a1[9] = {1, 0, 0, 0, 1, 0, 0, 0, 1}; + Mds mds1{TrackingDataHandle{1, a1}, TrackingLayout<>::mapping(1), TrackingAccessor{1}}; + int a2[9] = {3, 0, 0, 0, 3, 0, 0, 0, 3}; + Mds mds2{TrackingDataHandle{3, a2}, TrackingLayout<>::mapping(3), TrackingAccessor{3}}; + swap(mds1, mds2); + static_assert(is_void_v); + + assert(mds1.data_handle().get_id() == 3); + assert(mds1.data_handle().is_swapped()); + assert(mds1.mapping().get_id() == 3); + assert(mds1.mapping().is_swapped()); + assert(mds1.accessor().get_id() == 3); + assert(mds1.accessor().is_swapped()); + assert((mds1[array{1, 1}] == 3)); + assert((mds1[array{0, 1}] == 0)); + + assert(mds2.data_handle().get_id() == 1); + assert(mds2.data_handle().is_swapped()); + assert(mds2.mapping().get_id() == 1); + assert(mds2.mapping().is_swapped()); + assert(mds2.accessor().get_id() == 1); + assert(mds2.accessor().is_swapped()); + assert((mds2[array{1, 1}] == 1)); + assert((mds2[array{0, 1}] == 0)); +} + +constexpr void check_getters() { + int data[6] = {1, 2, 3, 4, 5, 6}; + auto mds = mdspan(data, 2, 3); + + { // Check 'extents()' + same_as&> decltype(auto) e = mds.extents(); + assert((e == dextents{2, 3})); + assert(&e == &mds.mapping().extents()); + static_assert(noexcept(mds.extents())); + same_as&> decltype(auto) ce = as_const(mds).extents(); + assert(&ce == &e); + static_assert(noexcept(as_const(mds).extents())); + } + + { // Check 'data_handle()' + same_as decltype(auto) dh = mds.data_handle(); + assert(dh == data); + static_assert(noexcept(mds.data_handle())); + same_as decltype(auto) cdh = as_const(mds).data_handle(); + assert(&cdh == &dh); + static_assert(noexcept(as_const(mds).data_handle())); + } + + { // Check 'mapping()' + using E = dextents; + + same_as&> decltype(auto) mp = mds.mapping(); + assert((mp == layout_stride::mapping{E{2, 3}, array{3, 1}})); + static_assert(noexcept(mds.mapping())); + same_as&> decltype(auto) cmp = as_const(mds).mapping(); + assert(&cmp == &mp); + static_assert(noexcept(as_const(mds).mapping())); + } + + { // Check 'accessor()' + same_as&> decltype(auto) acc = mds.accessor(); + static_assert(noexcept(mds.accessor())); + same_as&> decltype(auto) cacc = as_const(mds).accessor(); + assert(&cacc == &acc); + static_assert(noexcept(as_const(mds).accessor())); + } +} + +constexpr void check_is_always_functions() { + using Mds = mdspan, layout_stride, TrivialAccessor>; + + { // Check results + static_assert(Mds::is_always_unique() == Mds::mapping_type::is_always_unique()); + static_assert(Mds::is_always_exhaustive() == Mds::mapping_type::is_always_exhaustive()); + static_assert(Mds::is_always_strided() == Mds::mapping_type::is_always_strided()); + } + + { // Check types + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + } + + { // Check noexceptness (strengthened) + static_assert(noexcept(Mds::is_always_unique()) == noexcept(Mds::mapping_type::is_always_unique())); + static_assert(noexcept(Mds::is_always_exhaustive()) == noexcept(Mds::mapping_type::is_always_exhaustive())); + static_assert(noexcept(Mds::is_always_strided()) == noexcept(Mds::mapping_type::is_always_strided())); + } +} + +constexpr void check_is_functions() { + using E = extents; + vector bools = {true, false, true, true, true, true, true, false, true, false, false, false}; + mdspan mds{ + bools.begin(), layout_stride::mapping{E{}, array{6, 1, 2}}}; + + { // Check results + assert(mds.is_unique() == mds.mapping().is_unique()); + assert(mds.is_exhaustive() == mds.mapping().is_exhaustive()); + assert(mds.is_strided() == mds.mapping().is_strided()); + } + + { // Check types + static_assert(same_as); + static_assert(same_as); + static_assert(same_as); + } + + { // Check noexceptness (strengthened) + static_assert(noexcept(mds.is_unique()) == noexcept(mds.mapping().is_unique())); + static_assert(noexcept(mds.is_exhaustive()) == noexcept(mds.mapping().is_exhaustive())); + static_assert(noexcept(mds.is_strided()) == noexcept(mds.mapping().is_strided())); + } +} + +constexpr void check_stride_function() { + using E = extents; + using Mds = mdspan; + + int data[] = { + 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}; + Mds mds{data, layout_stride::mapping{E{}, array{15, 1, 3}}}; + same_as decltype(auto) s1 = mds.stride(0); + assert(s1 == 15); + same_as decltype(auto) s2 = as_const(mds).stride(1); + assert(s2 == 1); + + struct ConvertibleToRankType { + constexpr operator integral auto() const { + return 1; + } + + constexpr operator Mds::rank_type() const { + return 2; + } + }; + + same_as decltype(auto) s3 = as_const(mds).stride(ConvertibleToRankType{}); + assert(s3 == 3); +} + +constexpr void check_deduction_guides() { + { // CArray + int carray[10]{}; + mdspan mds{carray}; + static_assert(same_as>>); + } + + { // Pointer&& + float carray[20]{}; + float* ptr = carray; + mdspan mds{ptr}; + static_assert(same_as>>); + } + + { // ElementType*, Integrals... + byte carray[30]{}; + byte* ptr = carray; + mdspan mds1{ptr, 6, 5}; + static_assert(same_as>>); + mdspan mds2{ptr, 2, 3, 5}; + static_assert(same_as>>); + } + + { // ElementType*, span + const int carray[40]{}; + const int* ptr = carray; + const int exts1[] = {2, 4, 5}; + span s1{exts1}; + mdspan mds1{ptr, s1}; + static_assert(same_as>>); + const long exts2[] = {2, 2, 5, 2}; + span s2{exts2}; + mdspan mds2{ptr, s2}; + static_assert(same_as>>); + } + + { // ElementType*, const array& + const char carray[50]{}; + const char* ptr = carray; + array a1{5, 10}; + mdspan mds1{ptr, a1}; + static_assert(same_as>>); + array a2{2, 5, 5}; + mdspan mds2{ptr, a2}; + static_assert(same_as>>); + } + + { // ElementType*, const extents& + const double carray[60]{}; + const double* ptr = carray; + extents exts1; + mdspan mds1{ptr, exts1}; + static_assert(same_as>>); + extents exts2{4}; + mdspan mds2{ptr, exts2}; + static_assert(same_as>>); + } + + { // ElementType*, const MappingType& + const long carray[70]{}; + const long* ptr = carray; + mdspan mds1{ptr, layout_left::mapping>{}}; + static_assert(same_as, layout_left>>); + mdspan mds2{ptr, layout_stride::mapping>{dextents{2, 3, 5}, array{3, 1, 6}}}; + static_assert(same_as, layout_stride>>); + } + + { // const typename AccessorType::data_handle_type&, const MappingType&, const AccessorType& + vector bools = {true, false, true, false}; + mdspan mds{bools.begin(), TrackingLayout<>::mapping>(1), VectorBoolAccessor{}}; + static_assert(same_as, TrackingLayout<>, VectorBoolAccessor>>); + } +} + +constexpr bool test() { + check_modeled_concepts(); + check_member_types(); + check_observers(); + check_default_constructor(); + check_defaulted_copy_and_move_constructors(); + check_data_handle_and_indices_pack_constructor(); + check_data_handle_and_span_array_constructors(); + check_data_handle_and_extents_constructor(); + check_data_handle_and_mapping_constructor(); + check_data_handle_and_mapping_and_accessor_constructor(); + check_construction_from_other_mdspan(); + check_defaulted_copy_and_move_assignment_operators(); +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 + check_multidimensional_subscript_operator(); +#endif // __cpp_multidimensional_subscript + check_span_array_subscript_operator(); + check_size(); + check_empty(); + check_swap(); + check_getters(); + check_is_always_functions(); + check_is_functions(); + check_stride_function(); + check_deduction_guides(); + return true; +} + +int main() { + static_assert(test()); + test(); +} From 34405b3fa1d489e23fe9e5e82e4d7fecfcf27088 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Sat, 17 Jun 2023 04:57:20 +0200 Subject: [PATCH 30/57] ``: Remove old tests (#3767) Co-authored-by: Stephan T. Lavavej --- stl/inc/mdspan | 3 +- tests/std/include/test_mdspan_support.hpp | 131 +- tests/std/test.lst | 1 - tests/std/tests/P0009R18_mdspan/env.lst | 4 - tests/std/tests/P0009R18_mdspan/test.cpp | 1094 ----------------- .../tests/P0009R18_mdspan_extents/test.cpp | 394 ++++-- .../P0009R18_mdspan_layout_left/test.cpp | 142 ++- .../P0009R18_mdspan_layout_right/test.cpp | 154 ++- .../P0009R18_mdspan_layout_stride/test.cpp | 250 +++- .../std/tests/P0009R18_mdspan_mdspan/test.cpp | 22 +- 10 files changed, 896 insertions(+), 1299 deletions(-) delete mode 100644 tests/std/tests/P0009R18_mdspan/env.lst delete mode 100644 tests/std/tests/P0009R18_mdspan/test.cpp diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 2a0f9b289a..bd7420639a 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -549,7 +549,8 @@ public: template requires is_constructible_v - constexpr explicit(extents_type::rank() > 0) mapping(const layout_stride::template mapping<_OtherExtents>& _Other) + constexpr explicit(extents_type::rank() > 0) + mapping(const layout_stride::template mapping<_OtherExtents>& _Other) noexcept : _Exts(_Other.extents()) { if constexpr (extents_type::rank() > 0) { const bool _Verify = [&](index_sequence<_Indices...>) { diff --git a/tests/std/include/test_mdspan_support.hpp b/tests/std/include/test_mdspan_support.hpp index 23b8902dc4..b653d74268 100644 --- a/tests/std/include/test_mdspan_support.hpp +++ b/tests/std/include/test_mdspan_support.hpp @@ -4,22 +4,48 @@ #pragma once #include +#include #include #include +#include #include +#include +#include #include +#include #include #include +#include +enum class IsExplicit : bool { no, yes }; enum class IsNothrow : bool { no, yes }; -template +template struct ConvertibleToInt { - constexpr operator Int() const noexcept(std::to_underlying(Nothrow)) { - return Int{1}; + Int val = 1; + + constexpr explicit(std::to_underlying(Explicit)) operator Int() const noexcept(std::to_underlying(Nothrow)) { + return val; } }; +static_assert(std::is_aggregate_v>); +static_assert(std::is_convertible_v, int>); +static_assert(std::is_nothrow_convertible_v, int>); +static_assert(!std::is_nothrow_convertible_v, int>); +static_assert(std::is_convertible_v, int>); +static_assert(!std::is_convertible_v, int>); + +template +struct NonConstConvertibleToInt { + constexpr operator Int() noexcept; // not defined +}; + +static_assert(std::is_convertible_v, int>); +static_assert(!std::is_convertible_v, int>); +static_assert(std::is_nothrow_convertible_v, int>); +static_assert(!std::is_nothrow_convertible_v, int>); + struct NonConvertibleToAnything {}; namespace detail { @@ -207,3 +233,102 @@ constexpr void check_members_with_various_extents(Fn&& fn) { details::check_members_with_various_extents_impl(std::forward(fn), std::make_index_sequence<16>{}); #endif // _PREFAST_ } + +namespace details { + static constexpr bool permissive() { + return false; + } + + template + struct PermissiveTestBase { + static constexpr bool permissive() { + return true; + } + }; + + template + struct PermissiveTest : PermissiveTestBase { + static constexpr bool test() { + return permissive(); + } + }; +} // namespace details + +inline constexpr bool is_permissive = details::PermissiveTest::test(); + +template +struct MappingProperties { + typename Mapping::index_type req_span_size; + bool uniqueness; + bool exhaustiveness; + bool strideness; +}; + +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(); + 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))...); + }(rank_indices); + + auto map_index = [&](const auto& tpl) { return std::apply([&](auto... i) { return mapping(i...); }, tpl); }; + auto mapped_indices = multidim_indices | std::views::transform(map_index) | std::ranges::to(); + std::ranges::sort(mapped_indices); + + 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)) { + 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); + + { // 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 strided? (N4950 [mdspan.layout.reqmts]/18) + props.strideness = true; // assumption + for (auto r : std::views::iota(0u, rank)) { + std::optional sr; + for (auto i : multidim_indices) { + const auto i_plus_dr = [&](std::index_sequence) { + return std::array{static_cast(std::get(i) + (Indices == r ? 1 : 0))...}; + }(rank_indices); + + if (i_plus_dr[r] < get_extent(r)) { + const auto diff = static_cast(map_index(i_plus_dr) - map_index(i)); + if (!sr.has_value()) { + sr = diff; + } else if (*sr != diff) { + props.strideness = false; + break; + } + } + } + + if (!props.strideness) { + break; + } + } + } + + return props; +} + +template + requires (details::PermissiveTest::test()) +constexpr MappingProperties get_mapping_properties(const Mapping&) { + return {}; // we cannot get properties in '/permissive' mode +} diff --git a/tests/std/test.lst b/tests/std/test.lst index 6c28937ec7..ccec8ad719 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -236,7 +236,6 @@ tests\LWG3422_seed_seq_ctors tests\LWG3480_directory_iterator_range tests\LWG3545_pointer_traits_sfinae tests\LWG3610_iota_view_size_and_integer_class -tests\P0009R18_mdspan tests\P0009R18_mdspan_default_accessor tests\P0009R18_mdspan_extents tests\P0009R18_mdspan_extents_death diff --git a/tests/std/tests/P0009R18_mdspan/env.lst b/tests/std/tests/P0009R18_mdspan/env.lst deleted file mode 100644 index 18e2d7c71e..0000000000 --- a/tests/std/tests/P0009R18_mdspan/env.lst +++ /dev/null @@ -1,4 +0,0 @@ -# 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/test.cpp b/tests/std/tests/P0009R18_mdspan/test.cpp deleted file mode 100644 index 28ea7b5aca..0000000000 --- a/tests/std/tests/P0009R18_mdspan/test.cpp +++ /dev/null @@ -1,1094 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include -#include -#include -#include -#include -#include - -using namespace std; - -// A type that's regular and trivially copyable, and also maximally nothrow. -template -using is_regular_trivial_nothrow = std::conjunction>, is_trivially_copyable, - is_nothrow_default_constructible, is_nothrow_copy_constructible, is_nothrow_move_constructible, - is_nothrow_copy_assignable, is_nothrow_move_assignable, is_nothrow_swappable>; - -template -inline constexpr bool is_regular_trivial_nothrow_v = is_regular_trivial_nothrow::value; - -struct Constructible { - // noexcept constructible for size_t, but not convertible - explicit operator size_t() noexcept; -}; - -struct Convertible { - // convertible, but not noexcept constructible - operator size_t(); -}; - -struct ConstructibleAndConvertible { - // convertible and noexcept constructible - constexpr operator size_t() noexcept { - return size_t{2}; - } -}; - -struct ConstructibleAndConvertibleConst { - // convertible and noexcept constructible - constexpr operator size_t() const noexcept { - return size_t{2}; - } -}; - - -void extent_tests_traits() { - static_assert(is_regular_trivial_nothrow_v>); - static_assert(is_regular_trivial_nothrow_v>); - static_assert(is_regular_trivial_nothrow_v>); - static_assert(is_regular_trivial_nothrow_v>); - static_assert(is_regular_trivial_nothrow_v>); - - static_assert(is_same_v, extents>); - static_assert(is_same_v, extents>); - static_assert(is_same_v, extents>); - static_assert(is_same_v, extents>); - - constexpr extents e(2, 3); - static_assert(is_same_v, dextents>); - static_assert(e.static_extent(0) == dynamic_extent); - static_assert(e.static_extent(1) == dynamic_extent); - static_assert(e.extent(0) == 2); - static_assert(e.extent(1) == 3); - - constexpr dextents ex0(1); - (void) ex0; - static_assert(!is_constructible_v, int*>); - - extents(); - extents(); - extents(); - extents(); - extents(); - extents(); - extents(); - extents(); - extents(); - extents(); -} - -void extent_tests_rank() { - static_assert(extents::rank() == 0); - static_assert(extents::rank_dynamic() == 0); - - static_assert(extents::rank() == 1); - static_assert(extents::rank_dynamic() == 0); - - static_assert(extents::rank() == 1); - static_assert(extents::rank_dynamic() == 1); - - static_assert(extents::rank() == 2); - static_assert(extents::rank_dynamic() == 0); - - static_assert(extents::rank() == 2); - static_assert(extents::rank_dynamic() == 1); - - static_assert(extents::rank() == 2); - static_assert(extents::rank_dynamic() == 1); - - static_assert(extents::rank() == 2); - static_assert(extents::rank_dynamic() == 2); -} - -void extent_tests_static_extent() { - static_assert(extents::static_extent(0) == 2); - static_assert(extents::static_extent(1) == 3); - - static_assert(extents::static_extent(0) == 2); - static_assert(extents::static_extent(1) == dynamic_extent); - - static_assert(extents::static_extent(0) == dynamic_extent); - static_assert(extents::static_extent(1) == 3); - - static_assert(extents::static_extent(0) == dynamic_extent); - static_assert(extents::static_extent(1) == dynamic_extent); -} - -void extent_tests_extent() { - constexpr extents e_23; - static_assert(e_23.extent(0) == 2); - static_assert(e_23.extent(1) == 3); - - constexpr extents e_2d{3}; - static_assert(e_2d.extent(0) == 2); - static_assert(e_2d.extent(1) == 3); - - constexpr extents e_dd{2, 3}; - static_assert(e_dd.extent(0) == 2); - static_assert(e_dd.extent(1) == 3); - - constexpr extents e_2dd{3, 5}; - static_assert(e_2dd.extent(0) == 2); - static_assert(e_2dd.extent(1) == 3); - static_assert(e_2dd.extent(2) == 5); -} - -void extent_tests_ctor_other_sizes() { - static_assert(!is_constructible_v, Constructible>); - static_assert(!is_constructible_v, Convertible>); - static_assert(is_constructible_v, ConstructibleAndConvertible>); - [[maybe_unused]] constexpr extents ex0{ConstructibleAndConvertible{}}; - static_assert(is_constructible_v, ConstructibleAndConvertibleConst>); - [[maybe_unused]] constexpr extents ex1{ConstructibleAndConvertibleConst{}}; - - static_assert(is_constructible_v, int>); - [[maybe_unused]] constexpr extents ex2(1); - static_assert(!is_constructible_v, int, int>); - static_assert(is_constructible_v, int, int, int>); - [[maybe_unused]] extents ex3(1, 2, 3); - - extents e0; - assert(e0.extent(0) == 2); - assert(e0.extent(1) == 3); - - extents e1(5); - assert(e1.extent(0) == 2); - assert(e1.extent(1) == 5); - - extents e2(5); - assert(e2.extent(0) == 5); - assert(e2.extent(1) == 3); - - extents e3(5, 7); - assert(e3.extent(0) == 5); - assert(e3.extent(1) == 7); -} - -void extent_tests_copy_ctor_other() { - // Rank and value of static extents must match. - static_assert(!is_constructible_v, extents>); - static_assert(!is_constructible_v, extents>); - static_assert(!is_constructible_v, extents>); - - // Static extents are constructible, but not convertible, from dynamic extents. - static_assert(is_constructible_v, extents>); - constexpr extents ex0{extents{3}}; - (void) ex0; - static_assert(!is_convertible_v, extents>); - - // Dynamic extents are constructible and convertible from static extents. - static_assert(is_constructible_v, extents>); - extents{extents{}}; - static_assert(is_convertible_v, extents>); - - // Can implicitly convert from narrower to wider size_type, but not vice-versa. - static_assert(is_convertible_v, extents>); - static_assert(!is_convertible_v, extents>); - - extents e_dyn(3); - extents e(e_dyn); - (void) e; - - using E = extents; - - extents e0{extents{}}; - E e1(extents(2u)); - extents e2{extents{3u}}; - extents e3{extents{2u, 3u}}; - - (void) e0; - (void) e1; - (void) e2; - (void) e3; -} - -template -struct is_array_cons_avail : std::false_type {}; - -template -struct is_array_cons_avail>()}), T>::value>> : std::true_type { -}; - -template -constexpr bool is_array_cons_avail_v = is_array_cons_avail::value; - -void extent_tests_ctor_array() { - static_assert(!is_constructible_v, array>); - static_assert(!is_constructible_v, array>); - static_assert(!is_constructible_v, array>); - static_assert(is_constructible_v, array>); - constexpr extents ex0{array{}}; - (void) ex0; - - static_assert(is_constructible_v, array>); - constexpr extents ex1{array{}}; - static_assert(!is_constructible_v, array>); - static_assert(is_constructible_v, array>); - constexpr extents ex2{array{1, 2, 2}}; - (void) ex1; - (void) ex2; - - static_assert(is_constructible_v, array>); - constexpr extents ex3{array{}}; - static_assert(is_constructible_v, array>); - constexpr extents ex4{array{10}}; - static_assert(!is_constructible_v, array>); - (void) ex3; - (void) ex4; - - extents e0; - assert(e0.extent(0) == 2u); - assert(e0.extent(1) == 3u); - - // native extent::size_type - extents e1(to_array({5})); - assert(e1.extent(0) == 2u); - assert(e1.extent(1) == 5u); - - extents e2(to_array({5})); - assert(e2.extent(0) == 5u); - assert(e2.extent(1) == 3u); - - extents e3(to_array({5, 7})); - assert(e3.extent(0) == 5u); - assert(e3.extent(1) == 7u); - - // convertible size type - extents e4(to_array({5})); - assert(e4.extent(0) == 2u); - assert(e4.extent(1) == 5u); - - extents e5(to_array({5})); - assert(e5.extent(0) == 5u); - assert(e5.extent(1) == 3u); - - extents e6(to_array({5, 7})); - assert(e6.extent(0) == 5u); - assert(e6.extent(1) == 7u); -} - -void extent_tests_ctor_span() { - static_assert(!is_constructible_v, span>); - static_assert(!is_constructible_v, span>); - static_assert(!is_constructible_v, span>); - static_assert(is_constructible_v, span>); - ConstructibleAndConvertibleConst arr0[1] = {{}}; - constexpr extents ex0{span{arr0}}; - (void) ex0; - - static_assert(is_constructible_v, span>); - constexpr int arr1[1] = {1}; - constexpr extents ex1{span{arr1}}; - static_assert(!is_constructible_v, span>); - static_assert(is_constructible_v, span>); - constexpr int arr2[3] = {3, 2, 2}; - constexpr extents ex2{span{arr2}}; - (void) ex1; - (void) ex2; - - - extents e0; - assert(e0.extent(0) == 2u); - assert(e0.extent(1) == 3u); - - // native extent::size_type - constexpr int one_int[] = {5}; - constexpr int two_int[] = {5, 7}; - extents e1(span{one_int}); - assert(e1.extent(0) == 2); - assert(e1.extent(1) == 5); - - extents e2(span{one_int}); - assert(e2.extent(0) == 5); - assert(e2.extent(1) == 3); - - extents e3(span{two_int}); - assert(e3.extent(0) == 5); - assert(e3.extent(1) == 7); - - // convertible size type - constexpr size_t one_sizet[] = {5}; - constexpr size_t two_sizet[] = {5, 7}; - extents e4(span{one_sizet}); - assert(e4.extent(0) == 2); - assert(e4.extent(1) == 5); - - extents e5(span{one_sizet}); - assert(e5.extent(0) == 5); - assert(e5.extent(1) == 3); - - extents e6(span{two_sizet}); - assert(e6.extent(0) == 5); - assert(e6.extent(1) == 7); -} - -void extent_tests_equality() { - static_assert(extents{} == extents{}); - static_assert(extents{} != extents{}); - static_assert(extents{} != extents{}); - - extents e_23; - extents e_2d{3}; - extents e_d3{2}; - extents e_dd{2, 3}; - - assert(e_23 == e_2d); - assert(e_23 == e_d3); - assert(e_23 == e_dd); - assert(e_2d == e_d3); -} - -template = 0> -void TestMapping(const Mapping& map) { - using IndexT = typename Mapping::index_type; - using RankT = typename Mapping::rank_type; - static_assert(is_same_v()(IndexT{0}, IndexT{0}))>); - static_assert(is_same_v().stride(RankT{0}))>); - - array s; - const auto& e = map.extents(); - size_t num_entries = 1; - for (size_t i = 0; i < Mapping::extents_type::rank(); ++i) { - num_entries *= e.extent(i); - s[i] = map.stride(i); - } - - vector indices; - indices.reserve(num_entries); - - for (IndexT i = 0; i < e.extent(0); ++i) { - for (IndexT j = 0; j < e.extent(1); ++j) { - const auto idx = i * s[0] + j * s[1]; - assert(map(i, j) == idx); - indices.push_back(idx); - } - } - - bool is_unique = true; - bool is_exhaust = true; - sort(indices.begin(), indices.end()); - for (size_t i = 1; i < indices.size(); ++i) { - const auto diff = indices[i] - indices[i - 1]; - if (diff == 0) { - is_unique = false; - } else if (diff != 1) { - is_exhaust = false; - } - } - - assert(map.is_unique() == is_unique); - assert(map.is_exhaustive() == is_exhaust); - assert(map.required_span_size() == indices.back() + 1); -} - -template = 0> -void TestMapping(const Mapping& map) { - using IndexT = typename Mapping::index_type; - using RankT = typename Mapping::rank_type; - static_assert(is_same_v()(IndexT{0}, IndexT{0}, IndexT{0}))>); - static_assert(is_same_v().stride(RankT{0}))>); - - array s; - const auto& e = map.extents(); - size_t num_entries = 1; - for (size_t i = 0; i < Mapping::extents_type::rank(); ++i) { - num_entries *= e.extent(i); - s[i] = map.stride(i); - } - - vector indices; - indices.reserve(num_entries); - - for (IndexT i = 0; i < e.extent(0); ++i) { - for (IndexT j = 0; j < e.extent(1); ++j) { - for (IndexT k = 0; k < e.extent(2); ++k) { - const auto idx = i * s[0] + j * s[1] + k * s[2]; - assert(map(i, j, k) == idx); - indices.push_back(idx); - } - } - } - - bool is_unique = true; - bool is_exhaust = true; - sort(indices.begin(), indices.end()); - for (size_t i = 1; i < indices.size(); ++i) { - const auto diff = indices[i] - indices[i - 1]; - if (diff == 0) { - is_unique = false; - } else if (diff != 1) { - is_exhaust = false; - } - } - - assert(map.is_unique() == is_unique); - assert(map.is_exhaustive() == is_exhaust); - assert(map.required_span_size() == indices.back() + 1); -} - -void layout_left_tests_traits() { - static_assert(is_regular_trivial_nothrow_v>>); - static_assert(is_regular_trivial_nothrow_v>>); - static_assert(is_regular_trivial_nothrow_v>>); - static_assert(is_regular_trivial_nothrow_v>>); - - using E = extents; - static_assert(is_same_v::extents_type, E>); - static_assert(is_same_v::index_type, E::index_type>); - static_assert(is_same_v::size_type, E::size_type>); - static_assert(is_same_v::rank_type, E::rank_type>); - static_assert(is_same_v::layout_type, layout_left>); -} - -void layout_right_tests_traits() { - static_assert(is_regular_trivial_nothrow_v>>); - static_assert(is_regular_trivial_nothrow_v>>); - static_assert(is_regular_trivial_nothrow_v>>); - static_assert(is_regular_trivial_nothrow_v>>); - - using E = extents; - static_assert(is_same_v::extents_type, E>); - static_assert(is_same_v::index_type, E::index_type>); - static_assert(is_same_v::size_type, E::size_type>); - static_assert(is_same_v::rank_type, E::rank_type>); - static_assert(is_same_v::layout_type, layout_right>); -} - -void layout_stride_tests_traits() { - static_assert(is_regular_trivial_nothrow_v>>); - static_assert(is_regular_trivial_nothrow_v>>); - static_assert(is_regular_trivial_nothrow_v>>); - static_assert( - is_regular_trivial_nothrow_v>>); - - using E = extents; - static_assert(is_same_v::extents_type, E>); - static_assert(is_same_v::index_type, E::index_type>); - static_assert(is_same_v::size_type, E::size_type>); - static_assert(is_same_v::rank_type, E::rank_type>); - static_assert(is_same_v::layout_type, layout_stride>); -} - -void layout_left_tests_properties() { - constexpr layout_left::mapping> map{}; - static_assert(map.is_unique() == true); - static_assert(map.is_exhaustive() == true); - static_assert(map.is_strided() == true); - - static_assert(decltype(map)::is_always_unique() == true); - static_assert(decltype(map)::is_always_exhaustive() == true); - static_assert(decltype(map)::is_always_strided() == true); -} - -void layout_right_tests_properties() { - constexpr layout_right::mapping> map{}; - static_assert(map.is_unique() == true); - static_assert(map.is_exhaustive() == true); - static_assert(map.is_strided() == true); - - static_assert(decltype(map)::is_always_unique() == true); - static_assert(decltype(map)::is_always_exhaustive() == true); - static_assert(decltype(map)::is_always_strided() == true); -} - -void layout_stride_tests_properties() { - constexpr layout_stride::mapping> map{}; - static_assert(map.is_unique() == true); - static_assert(map.is_strided() == true); - - static_assert(decltype(map)::is_always_unique() == true); - static_assert(decltype(map)::is_always_exhaustive() == false); - static_assert(decltype(map)::is_always_strided() == true); -} - -void layout_left_tests_extents_ctor() { - constexpr extents e1; - constexpr extents e2{7}; - constexpr extents e3{11}; - constexpr extents e4{17, 19}; - - constexpr layout_left::mapping m1{e1}; - static_assert(m1.extents() == e1); - - constexpr layout_left::mapping m2{e2}; - static_assert(m2.extents() == e2); - - constexpr layout_left::mapping m3{e3}; - static_assert(m3.extents() == e3); - - constexpr layout_left::mapping m4{e4}; - static_assert(m4.extents() == e4); -} - -void layout_right_tests_extents_ctor() { - constexpr extents e1; - constexpr extents e2{7}; - constexpr extents e3{11}; - constexpr extents e4{17, 19}; - - constexpr layout_right::mapping m1{e1}; - static_assert(m1.extents() == e1); - - constexpr layout_right::mapping m2{e2}; - static_assert(m2.extents() == e2); - - constexpr layout_right::mapping m3{e3}; - static_assert(m3.extents() == e3); - - constexpr layout_right::mapping m4{e4}; - static_assert(m4.extents() == e4); -} - -void layout_stride_tests_extents_ctor() { - constexpr extents e1; - constexpr array s1{1, 2}; - - constexpr layout_stride::mapping m1{e1, s1}; - static_assert(m1.extents() == e1); - static_assert(m1.strides() == s1); - - constexpr extents e2{7}; - constexpr array s2{7, 1}; - - constexpr layout_stride::mapping m2{e2, s2}; - static_assert(m2.extents() == e2); - static_assert(m2.strides() == s2); -} - -template -void copy_ctor_helper_left(const Extents& e) { - const layout_left::mapping m1{e}; - const layout_left::mapping m2{m1}; - assert(m1 == m2); -} - -template -void copy_ctor_helper_right(const Extents& e) { - const layout_right::mapping m1{e}; - const layout_right::mapping m2{m1}; - assert(m1 == m2); -} - -void layout_left_tests_copy_ctor() { - copy_ctor_helper_left(extents{}); - copy_ctor_helper_left(extents{7}); - copy_ctor_helper_left(extents{11}); - copy_ctor_helper_left(extents{17, 19}); -} - -void layout_right_tests_copy_ctor() { - copy_ctor_helper_right(extents{}); - copy_ctor_helper_right(extents{7}); - copy_ctor_helper_right(extents{11}); - copy_ctor_helper_right(extents{17, 19}); -} - -void layout_left_tests_copy_other_extent() { - using E1 = extents; - using E2 = extents; - constexpr E1 e1; - constexpr E2 e2{3}; - constexpr layout_left::mapping m1(static_cast(e2)); - constexpr layout_left::mapping m2(e1); - - static_assert(m1.extents() == e1); - static_assert(m2.extents() == e1); - static_assert(m1.extents() == e2); - static_assert(m2.extents() == e2); -} - -void layout_right_tests_copy_ctor_other() { - using E1 = extents; - using E2 = extents; - constexpr E1 e1; - constexpr E2 e2{3}; - constexpr layout_right::mapping m1(static_cast(e2)); - constexpr layout_right::mapping m2(e1); - - static_assert(m1.extents() == e1); - static_assert(m2.extents() == e1); - static_assert(m1.extents() == e2); - static_assert(m2.extents() == e2); -} - -template -void assign_helper_left(const Extents& e) { - const layout_left::mapping m1{e}; - layout_left::mapping m2; - m2 = m1; - assert(m1 == m2); -} - -template -void assign_helper_right(const Extents& e) { - const layout_right::mapping m1{e}; - layout_right::mapping m2; - m2 = m1; - assert(m1 == m2); -} - -void layout_left_tests_assign() { - assign_helper_left(extents{}); - assign_helper_left(extents{7}); - assign_helper_left(extents{11}); - assign_helper_left(extents{17, 19}); -} - -void layout_right_tests_assign() { - assign_helper_right(extents{}); - assign_helper_right(extents{7}); - assign_helper_right(extents{11}); - assign_helper_right(extents{17, 19}); -} - -void layout_left_tests_ctor_other_layout() { - using E = extents; - - // from layout_left - using OE1 = extents; - static_assert(is_nothrow_constructible_v, layout_right::mapping>); - static_assert(is_nothrow_convertible_v, layout_left::mapping>); - - using OE2 = extents; // not convertible - static_assert(is_nothrow_constructible_v, layout_right::mapping>); - static_assert(!is_convertible_v, layout_left::mapping>); - - using OE3 = extents; // not constructible, rank > 1 - static_assert(!is_constructible_v, layout_right::mapping>); - static_assert(!is_convertible_v, layout_left::mapping>); - - static_assert(!is_constructible_v, layout_right::mapping>); - static_assert(!is_convertible_v, layout_left::mapping>); - - // from layout_stride - static_assert(is_constructible_v>, layout_stride::mapping>>); - static_assert(is_convertible_v>, layout_left::mapping>>); - - static_assert(is_constructible_v, layout_stride::mapping>); - static_assert(!is_convertible_v, layout_left::mapping>); - - static_assert(!is_constructible_v, layout_stride::mapping>); - static_assert(!is_convertible_v, layout_left::mapping>); -} - -void layout_right_tests_ctor_other_layout() { - using E = extents; - - // from layout_left - using OE1 = extents; - static_assert(is_nothrow_constructible_v, layout_left::mapping>); - static_assert(is_nothrow_convertible_v, layout_right::mapping>); - - using OE2 = extents; // not convertible - static_assert(is_nothrow_constructible_v, layout_left::mapping>); - static_assert(!is_convertible_v, layout_right::mapping>); - - using OE3 = extents; // not constructible, rank > 1 - static_assert(!is_constructible_v, layout_left::mapping>); - static_assert(!is_convertible_v, layout_right::mapping>); - - static_assert(!is_constructible_v, layout_left::mapping>); - static_assert(!is_convertible_v, layout_right::mapping>); - - // from layout_stride - static_assert(is_constructible_v>, layout_stride::mapping>>); - static_assert(is_convertible_v>, layout_right::mapping>>); - - static_assert(is_constructible_v, layout_stride::mapping>); - static_assert(!is_convertible_v, layout_right::mapping>); - - static_assert(!is_constructible_v, layout_stride::mapping>); - static_assert(!is_convertible_v, layout_right::mapping>); -} - -void layout_left_tests_strides() { - using E = extents; - layout_left::mapping map; - static_assert(map.stride(0) == 1); - static_assert(map.stride(1) == 2); - static_assert(map.stride(2) == 2 * 3); - static_assert(map.stride(3) == 2 * 3 * 5); -} - -void layout_right_tests_strides() { - using E = extents; - layout_right::mapping map; - static_assert(map.stride(0) == 7 * 5 * 3); - static_assert(map.stride(1) == 7 * 5); - static_assert(map.stride(2) == 7); - static_assert(map.stride(3) == 1); -} - -void layout_stride_tests_strides() { - using E = extents; - constexpr array s{1, 3}; - constexpr layout_stride::mapping map{E{}, s}; - static_assert(map.stride(0) == s[0]); - static_assert(map.stride(1) == s[1]); - static_assert(map.strides() == s); -} - -void layout_left_tests_indexing() { - static_assert(layout_left::mapping>{}() == 0); - TestMapping(layout_left::mapping>{}); - TestMapping(layout_left::mapping>{}); -} - -void layout_right_tests_indexing() { - static_assert(layout_right::mapping>{}() == 0); - TestMapping(layout_right::mapping>{}); - TestMapping(layout_right::mapping>{}); -} - -template -void copy_ctor_helper_stride(const Extents& e) { - const layout_stride::mapping m1{layout_right::mapping{e}}; - const layout_stride::mapping m2{m1}; - assert(m1 == m2); -} - -void layout_stride_tests_copy_ctor() { - copy_ctor_helper_stride(extents{}); - copy_ctor_helper_stride(extents{7}); - copy_ctor_helper_stride(extents{11}); - copy_ctor_helper_stride(extents{17, 19}); -} - -void layout_stride_tests_ctor_other_extents() { - constexpr extents e1; - constexpr extents e2{3}; - constexpr array s{3, 1}; - - constexpr layout_stride::mapping> m1(e1, s); - constexpr layout_stride::mapping> m2(m1); - - static_assert(m2.extents() == e1); - static_assert(m2.strides() == s); -} - -template -void other_mapping_helper() { - constexpr LayoutMapping other; - constexpr layout_stride::mapping map{other}; - static_assert(map.extents() == other.extents()); - for (size_t i = 0; i < LayoutMapping::extents_type::rank(); ++i) { - assert(map.stride(i) == other.stride(i)); - } -} -void layout_stride_tests_ctor_other_mapping() { - using E = extents; - other_mapping_helper>(); - other_mapping_helper>(); -} - -template -void assign_helper_stride(const Extents& e) { - const layout_stride::mapping m1{layout_right::mapping{e}}; - layout_stride::mapping m2; - m2 = m1; - assert(m1 == m2); -} - -void layout_stride_tests_assign() { - assign_helper_stride(extents{}); - assign_helper_stride(extents{7}); - assign_helper_stride(extents{11}); - assign_helper_stride(extents{17, 19}); -} - -void layout_stride_tests_indexing_static() { - using E = extents; - TestMapping(layout_stride::mapping{E{}, array{1, 2}}); - TestMapping(layout_stride::mapping{E{}, array{3, 1}}); - - // non-exhaustive mappings - TestMapping(layout_stride::mapping{E{}, array{1, 3}}); - TestMapping(layout_stride::mapping{E{}, array{4, 1}}); - TestMapping(layout_stride::mapping{E{}, array{2, 3}}); - - // exhaustive mappings with singleton dimensions - using E1 = extents; - TestMapping(layout_stride::mapping{E1{}, array{3, 1, 1}}); - TestMapping(layout_stride::mapping{E1{}, array{3, 7, 1}}); - - using E2 = extents; - TestMapping(layout_stride::mapping{E2{}, array{3, 1, 1}}); - TestMapping(layout_stride::mapping{E2{}, array{3, 1, 11}}); - - using E3 = extents; - TestMapping(layout_stride::mapping{E3{}, array{1, 3, 1}}); - TestMapping(layout_stride::mapping{E3{}, array{1, 3, 13}}); -} - - -void layout_stride_tests_equality() { - using E = extents; - constexpr layout_stride::mapping map1(layout_right::mapping{}); - constexpr layout_stride::mapping map2(layout_right::mapping{}); - static_assert(map1 == map2); - - constexpr layout_stride::mapping map3{layout_left::mapping{}}; - static_assert(map1 != map3); - - using ED = extents; - constexpr layout_stride::mapping map4{ED{2, 3}, map1.strides()}; - static_assert(map1 == map4); -} - -void accessor_tests_general() { - default_accessor a; - double arr[4] = {}; - static_assert(a.offset(arr, 3) == &arr[3]); - - a.access(arr, 2) = 42; - assert(arr[2] == 42); - - static_assert(is_constructible_v, default_accessor>); - static_assert(!is_constructible_v, default_accessor>); -} - -namespace Pathological { - - struct Empty {}; - - struct Extents { - using index_type = int; - using size_type = std::make_unsigned_t; - using rank_type = size_t; - - explicit Extents(Empty) {} - - template - explicit Extents(const array&) {} - - static constexpr size_t rank() { - return 0; - } - - static constexpr size_t rank_dynamic() { - return 0; - } - }; - - struct Layout { - template - struct mapping { - using extents_type = E; - using layout_type = Layout; - mapping(Extents) {} - }; - }; - - struct Accessor { - using element_type = int; - using data_handle_type = int*; - using reference = int&; - Accessor(int) {} - }; -} // namespace Pathological - -void mdspan_tests_traits() { - using M = mdspan>; - static_assert(is_trivially_copyable_v /*&& is_default_constructible_v*/ - && is_copy_constructible_v && is_move_constructible_v && is_copy_assignable_v - && is_move_assignable_v); - - static_assert(is_same_v>); - static_assert(is_same_v); - static_assert(is_same_v>); - static_assert(is_same_v>>); - static_assert(is_same_v); - static_assert(is_same_v); - static_assert(is_same_v); - static_assert(is_same_v); - static_assert(is_same_v); -} - -void mdspan_tests_ctor_sizes() { - static constexpr int arr[6] = {}; - constexpr mdspan> mds1(arr, 2); - static_assert(mds1.data_handle() == arr); - static_assert((mds1.extents() == extents{})); - static_assert(mds1.is_exhaustive()); - - static_assert(!is_constructible_v, Pathological::Layout>, int*, - int>); // Pathological::Layout not constructible from extents - - static_assert( - !is_constructible_v, layout_right, Pathological::Accessor>, int*, - int>); // Pathological::Accessor not default constructible -} - -void mdspan_tests_ctor_array() { - static constexpr int arr[6] = {}; - constexpr mdspan> mds1(arr, array{2}); - static_assert(mds1.data_handle() == arr); - static_assert(mds1.extents() == extents{}); - - static_assert(!is_constructible_v, Pathological::Layout>, int*, - array>); // Pathological::Layout not constructible from extents - - static_assert( - !is_constructible_v, layout_right, Pathological::Accessor>, int*, - array>); // Pathological::Accessor not default constructible -} - -void mdspan_tests_ctor_extents() { - static constexpr int arr[6] = {}; - constexpr mdspan> mds1(arr, extents{2}); - static_assert(mds1.data_handle() == arr); - static_assert(mds1.extents() == extents{}); - - static_assert(!is_constructible_v, Pathological::Layout>, int*, - extents>); // Pathological::Layout not constructible from extents - - static_assert( - !is_constructible_v, layout_right, Pathological::Accessor>, int*, - extents>); // Pathological::Accessor not default constructible -} - -void mdspan_tests_ctor_mapping() { - static constexpr int arr[6] = {}; - using E = extents; - constexpr layout_left::mapping> map(extents{}); - - constexpr mdspan mds1(arr, map); - static_assert(mds1.data_handle() == arr); - static_assert(mds1.extents() == extents{}); - static_assert(mds1.mapping() == map); - - static_assert( - !is_constructible_v, layout_right, Pathological::Accessor>, int*, - extents>); // Pathological::Accessor not default constructible -} - -template -struct stateful_accessor { - using element_type = Type; - using data_handle_type = Type*; - using reference = Type&; - - constexpr stateful_accessor(int i_) : i(i_){}; - int i = 0; -}; - -void mdspan_tests_ctor_accessor() { - static constexpr int arr[6] = {}; - using E = extents; - constexpr layout_left::mapping> map(extents{}); - constexpr stateful_accessor acc(1); - - constexpr mdspan> mds1(arr, map, acc); - static_assert(mds1.data_handle() == arr); - static_assert(mds1.extents() == extents{}); - static_assert(mds1.mapping() == map); - static_assert(mds1.accessor().i == 1); - - static_assert( - !is_constructible_v, layout_right, Pathological::Accessor>, int*, - extents>); // Pathological::Accessor not default constructible -} - -void mdspan_tests_assign() { - using E2 = extents; - int arr[6] = {}; - mdspan> mds1(arr, 2); - mdspan> mds2(nullptr, 3); - mds2 = mds1; - assert(mds2.data_handle() == arr); - assert(mds2.extents() == E2{}); - assert(mds2.mapping() == mds1.mapping()); -} - -void mdspan_tests_observers() { - using E = extents; - static constexpr int arr[] = {0, 1, 2, 3, 4, 5, 6, 7}; - constexpr mdspan mds{arr, layout_stride::mapping{E{2}, array{1, 3}}}; - - static_assert(mds.rank() == 2); - static_assert(mds.rank_dynamic() == 1); - - static_assert(mds.static_extent(0) == dynamic_extent); - static_assert(mds.static_extent(1) == 3); - static_assert(mds.extent(0) == 2); - static_assert(mds.extent(1) == 3); - static_assert(mds.size() == 6); - - static_assert(mds.stride(0) == 1); - static_assert(mds.stride(1) == 3); - - static_assert(mds.is_always_unique()); - static_assert(!mds.is_always_exhaustive()); - static_assert(mds.is_always_strided()); - - static_assert(mds.is_unique()); - static_assert(!mds.is_exhaustive()); - static_assert(mds.is_strided()); - -#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 - static_assert(mds[1, 0] == 1); - static_assert(mds[1, 2] == 7); -#endif // __cpp_multidimensional_subscript - - static_assert(mds[array{0, 1}] == 3); - static_assert(mds[array{1, 1}] == 4); -} - -int main() { - extent_tests_rank(); - extent_tests_static_extent(); - extent_tests_extent(); - extent_tests_ctor_other_sizes(); - extent_tests_copy_ctor_other(); - extent_tests_ctor_array(); - extent_tests_ctor_span(); - extent_tests_equality(); - - layout_left_tests_traits(); - layout_left_tests_properties(); - layout_left_tests_extents_ctor(); - layout_left_tests_copy_ctor(); - layout_left_tests_copy_other_extent(); - layout_left_tests_assign(); - layout_left_tests_ctor_other_layout(); - layout_left_tests_strides(); - layout_left_tests_indexing(); - - layout_right_tests_traits(); - layout_right_tests_properties(); - layout_right_tests_extents_ctor(); - layout_right_tests_copy_ctor(); - layout_right_tests_copy_ctor_other(); - layout_right_tests_assign(); - layout_right_tests_ctor_other_layout(); - layout_right_tests_strides(); - layout_right_tests_indexing(); - - layout_stride_tests_traits(); - layout_stride_tests_properties(); - layout_stride_tests_extents_ctor(); - layout_stride_tests_strides(); - layout_stride_tests_copy_ctor(); - layout_stride_tests_ctor_other_extents(); - layout_stride_tests_ctor_other_mapping(); - layout_stride_tests_assign(); - layout_stride_tests_indexing_static(); - layout_stride_tests_equality(); - - accessor_tests_general(); - - mdspan_tests_traits(); - mdspan_tests_ctor_sizes(); - mdspan_tests_ctor_array(); - mdspan_tests_ctor_extents(); - mdspan_tests_ctor_mapping(); - mdspan_tests_ctor_accessor(); - mdspan_tests_assign(); - mdspan_tests_observers(); - - return 0; -} diff --git a/tests/std/tests/P0009R18_mdspan_extents/test.cpp b/tests/std/tests/P0009R18_mdspan_extents/test.cpp index e7fa010988..3ff5424078 100644 --- a/tests/std/tests/P0009R18_mdspan_extents/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_extents/test.cpp @@ -10,7 +10,7 @@ #include #include -#include "test_mdspan_support.hpp" +#include using namespace std; @@ -22,6 +22,13 @@ constexpr void check_members(index_sequence) { static_assert(regular); static_assert(is_trivially_copyable_v); + // Check implicit properties + static_assert(is_nothrow_copy_constructible_v); + static_assert(is_nothrow_move_constructible_v); + static_assert(is_nothrow_copy_assignable_v); + static_assert(is_nothrow_move_assignable_v); + static_assert(is_nothrow_swappable_v); + // Check member types static_assert(same_as); static_assert(same_as>); @@ -80,10 +87,42 @@ constexpr void check_members(index_sequence) { } } +constexpr void check_defaulted_default_constructor() { + { // All extents are static + using Ext = extents; + static_assert(is_nothrow_default_constructible_v); + + Ext ext; + assert(ext.extent(0) == 3); + assert(ext.extent(1) == 5); + assert(ext.extent(2) == 7); + } + + { // Some extents are static, some dynamic + using Ext = extents; + static_assert(is_nothrow_default_constructible_v); + + Ext ext; + assert(ext.extent(0) == 0); + assert(ext.extent(1) == 0); + assert(ext.extent(2) == 4); + } + + { // All extents are dynamic + using Ext = dextents; + static_assert(is_nothrow_default_constructible_v); + + Ext ext; + assert(ext.extent(0) == 0); + assert(ext.extent(1) == 0); + assert(ext.extent(2) == 0); + } +} + constexpr void check_construction_from_other_extents() { { // Check construction from too big or too small other extents using Ext = extents; - static_assert(!is_constructible_v); + static_assert(!is_constructible_v>); static_assert(!is_constructible_v>); } @@ -95,17 +134,44 @@ constexpr void check_construction_from_other_extents() { static_assert(!is_constructible_v, extents>); } - { // Check postconditions - extents ext{4, 4}; - extents ext2{ext}; - assert(ext == ext2); - assert(ext2.extent(0) == 4); + { // Check postconditions: static extents from static extents + extents ext; + extents ext2 = ext; + assert(ext2.extent(0) == 3); assert(ext2.extent(1) == 4); + assert(ext == ext2); + } - extents ext3{ext}; - assert(ext == ext3); - assert(ext3.extent(0) == 4); - assert(ext3.extent(1) == 4); + { // Check postconditions: dynamic extents from static extents + extents ext; + dextents ext2 = ext; + assert(ext2.extent(0) == 2); + assert(ext2.extent(1) == 3); + assert(ext == ext2); + } + + { // Check postconditions: dynamic extents from dynamic extents + dextents ext{5, 10}; + dextents ext2 = ext; + assert(ext2.extent(0) == 5); + assert(ext2.extent(1) == 10); + assert(ext == ext2); + } + + { // Check postconditions: static extents from dynamic extents + dextents ext{9, 6}; + extents ext2{ext}; // NB: explicit constructor + assert(ext2.extent(0) == 9); + assert(ext2.extent(1) == 6); + assert(ext == ext2); + } + + { // Check postconditions: wider index type to narrower index type + dextents ext{3, 5}; + dextents ext2{ext}; // NB: explicit constructor + assert(ext2.extent(0) == 3); + assert(ext2.extent(1) == 5); + assert(ext == ext2); } { // Check implicit conversions @@ -118,7 +184,7 @@ constexpr void check_construction_from_other_extents() { constexpr void check_construction_from_extents_pack() { { // Check construction from various types - using Ext = extents; + using Ext = extents; static_assert(is_nothrow_constructible_v); static_assert(!is_constructible_v); static_assert(is_nothrow_constructible_v); @@ -127,8 +193,10 @@ constexpr void check_construction_from_extents_pack() { { // Check construction from types (not) convertible to index_type using Ext = extents; static_assert(is_nothrow_constructible_v>); + static_assert(!is_nothrow_constructible_v>); static_assert(!is_constructible_v); static_assert(is_nothrow_constructible_v>); + static_assert(!is_nothrow_constructible_v>); static_assert(!is_constructible_v); } @@ -138,15 +206,47 @@ constexpr void check_construction_from_extents_pack() { static_assert(!is_constructible_v>); } - { // Check postconditions + { // Check postconditions when 'sizeof...(pack) == rank()' using Ext = extents; - Ext ext1a{4, ConvertibleToInt{}, 4}; - Ext ext1b{4, 1, 4}; - assert(ext1a == ext1b); + Ext ext{4, ConvertibleToInt{.val = 4}, 4}; + assert(ext.extent(0) == 4); + assert(ext.extent(1) == 4); + assert(ext.extent(2) == 4); + Ext ext2{4, 4, 4}; + assert(ext == ext2); + } - Ext ext2a{4, ConvertibleToInt{}}; - Ext ext2b{4, 1}; - assert(ext2a == ext2b); + { // Check postconditions when 'sizeof...(pack) == rank_dynamic()' + using Ext = extents; + Ext ext{3, ConvertibleToInt{.val = 3}}; + assert(ext.extent(0) == 3); + assert(ext.extent(1) == 3); + assert(ext.extent(2) == 5); + Ext ext2{3, 3}; + assert(ext == ext2); + } + + { // Check that elements from pack are passed through 'move' + using Ext = dextents; + struct FancyIndex { + constexpr operator integral auto() const& noexcept { + return 3; + } + + constexpr operator integral auto() const&& noexcept { + return 3; + } + + constexpr operator Ext::index_type() && noexcept { + return 4; + } + }; + + FancyIndex i; + Ext ext{FancyIndex{}, i, as_const(i)}; + assert(ext.extent(0) == 4); + assert(ext.extent(1) == 4); + assert(ext.extent(2) == 4); } { // Check construction from integers with mismatched signs @@ -167,48 +267,136 @@ constexpr void check_construction_from_extents_pack() { } constexpr void check_construction_from_array_and_span() { - { // Check construction from arrays/spans where [array/span].size() is equal to rank() - using Ext = extents; - - array arr1 = {1, 5}; - Ext ext1a{arr1}; - span s1{arr1}; - Ext ext1b{s1}; - assert(ext1a == ext1b); - static_assert(is_nothrow_constructible_v); - static_assert(is_nothrow_constructible_v); - - array, 2> arr2; - Ext ext2a{arr2}; - span s2{arr2}; - Ext ext2b{s2}; - assert(ext2a == ext2b); - static_assert(is_nothrow_constructible_v); - static_assert(is_nothrow_constructible_v); + { // Check construction from array/span where 'size()' is equal to 'rank()' and OtherIndexType models 'integral' + using Ext = extents; + array arr = {1, 5}; + Ext ext{arr}; + assert(ext.extent(0) == 1); + assert(ext.extent(1) == 5); + + Ext ext2{span{arr}}; + assert(ext == ext2); + + static_assert(is_nothrow_constructible_v); + static_assert(is_nothrow_constructible_v>); + } + + { // Check construction from array/span where 'size()' is equal to 'rank()' and OtherIndexType is class type + using Ext = extents; + array, 2> arr{{{.val = 3}, {.val = 5}}}; + Ext ext{arr}; + assert(ext.extent(0) == 3); + assert(ext.extent(1) == 5); + + const span s{arr}; + Ext ext2{s}; + assert(ext == ext2); + + static_assert(is_nothrow_constructible_v); + static_assert(is_nothrow_constructible_v); + } + + { // Check construction from array/span where 'size()' is equal to 'rank()' and OtherIndexType is "special" + using Ext = extents; + struct SpecialIndex { + constexpr operator integral auto() noexcept { + return 3; + } + constexpr operator integral auto() const noexcept { + return 5; + } + }; + + // Elements of 'arr' and 's' should be passed through 'as_const' + array arr; + Ext ext{arr}; + assert(ext.extent(0) == 5); + assert(ext.extent(1) == 5); + + span s{arr}; + Ext ext2{s}; + assert(ext == ext2); + } + + { // Check invalid construction from array/span where 'size()' is equal to 'rank()' + using Ext = extents; + static_assert(!is_constructible_v, 2>>); + static_assert(!is_constructible_v, 2>>); + static_assert(!is_constructible_v, 2>>); + static_assert(!is_constructible_v, 2>>); + static_assert(!is_constructible_v, 2>>); + static_assert(!is_constructible_v, 2>>); static_assert(!is_constructible_v>); static_assert(!is_constructible_v>); } - { // Check construction from arrays/spans where [array/span].size() is equal to rank_dynamic() - using Ext = extents; + { // Check construction from array/span where 'size()' is equal to 'rank_dynamic()' and OtherIndexType models + // 'integral' + using Ext = extents; + array arr = {4, 4}; + Ext ext{arr}; + assert(ext.extent(0) == 3); + assert(ext.extent(1) == 4); + assert(ext.extent(2) == 3); + assert(ext.extent(3) == 4); + + Ext ext2{span{arr}}; + assert(ext == ext2); + + static_assert(is_nothrow_constructible_v); + static_assert(is_nothrow_constructible_v>); + } - array arr1 = {4, 4}; - Ext ext1a{arr1}; - span s1{arr1}; - Ext ext1b{s1}; - assert(ext1a == ext1b); - static_assert(is_nothrow_constructible_v); - static_assert(is_nothrow_constructible_v); + { // Check construction from array/span where 'size()' is equal to 'rank_dynamic()' and OtherIndexType is class type + using Ext = extents; + array, 2> arr{{{.val = 2}, {.val = 2}}}; + Ext ext{arr}; + assert(ext.extent(0) == 3); + assert(ext.extent(1) == 2); + assert(ext.extent(2) == 3); + assert(ext.extent(3) == 2); - array, 2> arr2; - Ext ext2a{arr2}; - span s2{arr2}; - Ext ext2b{s2}; - assert(ext2a == ext2b); - static_assert(is_nothrow_constructible_v); - static_assert(is_nothrow_constructible_v); + span s{arr}; + Ext ext2{s}; + assert(ext == ext2); + static_assert(is_nothrow_constructible_v); + static_assert(is_nothrow_constructible_v); + } + + { // Check construction from array/span where 'size()' is equal to 'rank_dynamic()' and OtherIndexType is "special" + using Ext = extents; + struct SpecialIndex { + constexpr operator integral auto() noexcept { + return 5; + } + + constexpr operator integral auto() const noexcept { + return 3; + } + }; + + // Elements of 'arr' and 's' should be passed through 'as_const' + array arr; + Ext ext{arr}; + assert(ext.extent(0) == 3); + assert(ext.extent(1) == 3); + assert(ext.extent(2) == 3); + + span s{arr}; + Ext ext2{s}; + assert(ext == ext2); + } + + { // Check invalid construction from array/span where 'size()' is equal to 'rank_dynamic()' + using Ext = extents; + static_assert(!is_constructible_v, 2>>); + static_assert(!is_constructible_v, 2>>); + static_assert(!is_constructible_v, 2>>); + static_assert(!is_constructible_v, 2>>); + static_assert(!is_constructible_v, 2>>); + static_assert(!is_constructible_v, 2>>); static_assert(!is_constructible_v>); static_assert(!is_constructible_v>); } @@ -228,15 +416,7 @@ constexpr void check_construction_from_array_and_span() { array arr = {4ll}; (void) Ext{arr}; - - span s{arr}; - (void) Ext{s}; - } - - { // Check construction from arrays/spans with elements that may throw during conversion to index_type - using Ext = extents; - static_assert(!is_constructible_v, 2>>); - static_assert(!is_constructible_v, 2>>); + (void) Ext{span{arr}}; } { // Check construction from arrays/spans with invalid size @@ -262,27 +442,81 @@ constexpr void check_equality_operator() { extents e1; extents e2; extents e3; - assert(e1 != e2); + + same_as decltype(auto) cond = e1 != e2; + assert(cond); assert(e2 != e3); assert(e1 == e3); + + static_assert(noexcept(e1 != e2)); + static_assert(noexcept(e1 == e3)); } { // Some extents are static, some dynamic extents e1{1}; extents e2{2}; extents e3{3}; - assert(e1 != e2); + + same_as decltype(auto) cond = e1 != e2; + assert(cond); assert(e2 == e3); assert(e1 != e2); + + static_assert(noexcept(e1 != e2)); + static_assert(noexcept(e2 == e3)); } { // All extents are dynamic dextents e1{1, 2}; dextents e2{1, 2}; dextents e3{1, 3}; - assert(e1 == e2); + + same_as decltype(auto) cond = e1 == e2; + assert(cond); assert(e2 != e3); assert(e1 != e3); + + static_assert(noexcept(e1 == e2)); + static_assert(noexcept(e2 != e3)); + } + + { // Different ranks + static_assert(extents{} != extents{}); + static_assert(extents{} != extents{}); + static_assert(noexcept(extents{} != extents{})); + static_assert(noexcept(extents{} != extents{})); + } +} + +template +concept CanDeduceExtents = requires(Args&&... args) { extents{forward(args)...}; }; + +template +constexpr bool all_extents_dynamic = false; + +template +constexpr bool all_extents_dynamic, ExpectedRank> = + ((Extents == dynamic_extent) && ...) && (sizeof...(Extents) == ExpectedRank); + +constexpr void check_deduction_guide() { + { // Check 'CanDeduceExtents' concept + static_assert(CanDeduceExtents); + static_assert(CanDeduceExtents); + static_assert(CanDeduceExtents, int>); + static_assert(!CanDeduceExtents, int>); + static_assert(!CanDeduceExtents); + } + + { // Check correctness + extents ext{'1', 2, 3u, 4ll, ConvertibleToInt{.val = 5}}; + assert(ext.extent(0) == '1'); + assert(ext.extent(1) == 2); + assert(ext.extent(2) == 3); + assert(ext.extent(3) == 4); + assert(ext.extent(4) == 5); + + static_assert(all_extents_dynamic); + static_assert(same_as); } } @@ -290,35 +524,27 @@ constexpr bool test() { check_members_with_various_extents([](const extents&) { check_members(make_index_sequence{}); }); + check_defaulted_default_constructor(); check_construction_from_other_extents(); check_construction_from_extents_pack(); check_construction_from_array_and_span(); check_equality_operator(); + check_deduction_guide(); return true; } -template -constexpr bool all_extents_dynamic = false; - -template -constexpr bool all_extents_dynamic, ExpectedRank> = - ((Extents == dynamic_extent) && ...) && (sizeof...(Extents) == ExpectedRank); - -template -concept CanDeduceExtents = requires(Args&&... args) { extents{forward(args)...}; }; - -// Check deduction guide -using DG = decltype(extents{'1', 2, 3u, 4ll, ConvertibleToInt{}}); -static_assert(all_extents_dynamic); -static_assert(same_as); -static_assert(!CanDeduceExtents); - // Check dextents static_assert(all_extents_dynamic, 0>); -static_assert(all_extents_dynamic, 2>); -static_assert(all_extents_dynamic, 3>); -static_assert(all_extents_dynamic, 5>); +static_assert(all_extents_dynamic, 1>); +static_assert(all_extents_dynamic, 2>); +static_assert(all_extents_dynamic, 3>); +static_assert(all_extents_dynamic, 4>); +static_assert(all_extents_dynamic, 5>); +static_assert(all_extents_dynamic, 6>); +static_assert(all_extents_dynamic, 7>); +static_assert(all_extents_dynamic, 8>); +static_assert(all_extents_dynamic, 9>); int main() { static_assert(test()); diff --git a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp index d585c455b1..9946cf40f8 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp @@ -9,7 +9,7 @@ #include #include -#include "test_mdspan_support.hpp" +#include using namespace std; @@ -34,17 +34,26 @@ constexpr void check_members(const extents& ext, index_se static_assert(same_as); { // Check default and copy constructor - Mapping m; + const Mapping m; Mapping cpy = m; assert(cpy == m); static_assert(is_nothrow_default_constructible_v); static_assert(is_nothrow_copy_constructible_v); } + { // Check copy assignment operator + const Mapping m; + Mapping cpy; + cpy = m; + assert(cpy == m); + static_assert(is_nothrow_copy_assignable_v); + } + { // Check construction from extents_type Mapping m{ext}; assert(m.extents() == ext); static_assert(is_nothrow_constructible_v); + // Other tests are defined in 'check_construction_from_extents' function } using OtherIndexType = long long; @@ -75,6 +84,9 @@ constexpr void check_members(const extents& ext, index_se // Other tests are defined in 'check_construction_from_other_right_mapping' function } +#ifdef __clang__ + if (!is_constant_evaluated()) // FIXME clang hits constexpr limit here +#endif { // Check construction from layout_stride::mapping array strides{}; if constexpr (Ext::rank() > 0) { @@ -87,9 +99,9 @@ constexpr void check_members(const extents& ext, index_se } } - using StrideMapping = layout_stride::mapping; - StrideMapping stride_mapping{ext, strides}; - [[maybe_unused]] Mapping m{stride_mapping}; + layout_stride::mapping m1{ext, span{strides}}; + Mapping m2{m1}; + assert(m1.extents() == m2.extents()); // Other tests are defined in 'check_construction_from_other_stride_mapping' function } @@ -101,9 +113,10 @@ constexpr void check_members(const extents& ext, index_se } { // Check 'required_span_size' function - const IndexType expected_value = static_cast((ext.extent(Indices) * ... * 1)); - assert(m.required_span_size() == expected_value); + same_as decltype(auto) rss = m.required_span_size(); + assert(rss == static_cast((ext.extent(Indices) * ... * 1))); static_assert(noexcept(m.required_span_size())); + // Other tests are defined in 'check_mapping_properties' } { // Check operator() @@ -124,6 +137,7 @@ constexpr void check_members(const extents& ext, index_se static_assert(Mapping::is_unique()); static_assert(Mapping::is_exhaustive()); static_assert(Mapping::is_strided()); + // Other tests are defined in 'check_mapping_properties' } if constexpr (Ext::rank() > 0) { // Check 'stride' function @@ -133,6 +147,7 @@ constexpr void check_members(const extents& ext, index_se assert(m.stride(0) == 1); static_assert(noexcept(m.stride(Ext::rank() - 1))); static_assert(noexcept(m.stride(0))); + // Other tests are defined in 'check_stride_function' } else { static_assert(!CheckStrideMemberFunction); } @@ -144,6 +159,44 @@ constexpr void check_members(const extents& ext, index_se } } +void check_mapping_properties() { + if constexpr (!is_permissive) { + auto check = []([[maybe_unused]] const auto& mapping) { + const auto props = get_mapping_properties(mapping); + assert(props.req_span_size == mapping.required_span_size()); + assert(props.uniqueness); + assert(props.exhaustiveness); + assert(props.strideness); + }; + + using M1 = layout_left::mapping>; + check(M1{}); + + using M2 = layout_left::mapping>; + check(M2{M2::extents_type{6}}); + + using M3 = layout_left::mapping>; + check(M3{M3::extents_type{3, 5, 4, 2}}); + } +} + +constexpr void check_construction_from_extents() { + using Ext = extents; + Ext ext; + + { // Check construction from 'extents_type' + layout_left::mapping mp(ext); + assert(mp.extents() == ext); + static_assert(is_nothrow_constructible_v); + } + + { // Check construction from other extents + layout_left::mapping> mp(ext); + assert(mp.extents() == ext); + static_assert(is_nothrow_constructible_v); + } +} + constexpr void check_construction_from_other_left_mapping() { { // Check invalid construction using Mapping = layout_left::mapping>; @@ -161,14 +214,23 @@ constexpr void check_construction_from_other_left_mapping() { static_assert(NotImplicitlyConstructibleFrom>, layout_left::mapping>>); } + + { // Check effects + layout_left::mapping> m1; + layout_left::mapping> m2{m1}; + assert(m2.extents().extent(0) == 5); + assert(m2.extents().extent(1) == 3); + assert(m2.extents().extent(2) == 5); + assert(m1.extents() == m2.extents()); + } } 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_nothrow_constructible_v>, + layout_right::mapping>>); + static_assert(is_nothrow_constructible_v>, + layout_right::mapping>>); static_assert( !is_constructible_v>, layout_right::mapping>>); static_assert( @@ -189,6 +251,12 @@ constexpr void check_construction_from_other_right_mapping() { static_assert(NotImplicitlyConstructibleFrom>, layout_right::mapping>>); } + + { // Check effects + layout_right::mapping> m1; + layout_left::mapping> m2{m1}; + assert(m2.extents().extent(0) == 8); + } } constexpr void check_construction_from_other_stride_mapping() { @@ -211,8 +279,13 @@ constexpr void check_construction_from_other_stride_mapping() { { // Check correctness using Ext = extents; - layout_stride::mapping stride_mapping{Ext{}, array{1, 4, 12, 24, 72}}; - [[maybe_unused]] layout_left::mapping m{stride_mapping}; + layout_stride::mapping m1{Ext{}, array{1, 4, 12, 24, 72}}; + layout_left::mapping m2{m1}; + assert(m2.extents().extent(0) == 4); + assert(m2.extents().extent(1) == 3); + assert(m2.stride(2) == 12); + assert(m2.stride(3) == 24); + assert(m2.extents() == m1.extents()); } { // Check implicit conversions @@ -271,6 +344,35 @@ constexpr void check_call_operator() { } } +constexpr void check_stride_function() { + layout_left::mapping> prime_mapping; + + { // Check return type + same_as decltype(auto) s = prime_mapping.stride(0); + assert(s == 1); + } + + { // Check that argument is 'rank_type' + struct ConvertibleToRankType { + constexpr operator integral auto() const { + return 0; + } + + constexpr operator size_t() const { // NB: 'rank_type' is always 'size_t' + return 1; + } + }; + + assert(prime_mapping.stride(ConvertibleToRankType{}) == 2); // 1 * 2 + } + + { // Check other strides + assert(prime_mapping.stride(2) == 6); + assert(prime_mapping.stride(3) == 30); + assert(prime_mapping.stride(4) == 210); + } +} + constexpr void check_comparisons() { using StaticMapping = layout_left::mapping>; using DynamicMapping = layout_left::mapping>; @@ -291,6 +393,14 @@ constexpr void check_comparisons() { } } +constexpr void check_ctad() { + using Ext = extents; + layout_left::mapping m{Ext{}}; + static_assert(same_as>); + assert(m.extents().extent(0) == 3); + assert(m.stride(1) == 3); +} + constexpr void check_correctness() { { // empty extents const array values{}; @@ -385,11 +495,17 @@ constexpr bool test() { [](const extents& ext) { check_members(ext, make_index_sequence{}); }); + if (!is_constant_evaluated()) { // too heavy for compile time + check_mapping_properties(); + } + check_construction_from_extents(); check_construction_from_other_left_mapping(); check_construction_from_other_right_mapping(); check_construction_from_other_stride_mapping(); check_call_operator(); + check_stride_function(); check_comparisons(); + check_ctad(); check_correctness(); return true; diff --git a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp index dd67099a7a..a5e58742cb 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp @@ -9,7 +9,7 @@ #include #include -#include "test_mdspan_support.hpp" +#include using namespace std; @@ -34,24 +34,35 @@ constexpr void check_members(const extents& ext, index_se static_assert(same_as); { // Check default and copy constructor - Mapping m; + const Mapping m; Mapping cpy = m; assert(cpy == m); static_assert(is_nothrow_default_constructible_v); static_assert(is_nothrow_copy_constructible_v); } + { // Check copy assignment operator + const Mapping m; + Mapping cpy; + cpy = m; + assert(cpy == m); + static_assert(is_nothrow_copy_assignable_v); + } + { // Check construction from extents_type Mapping m{ext}; assert(m.extents() == ext); static_assert(is_nothrow_constructible_v); + // Other tests are defined in 'check_construction_from_extents' function } using OtherIndexType = long long; using Ext2 = extents; using Mapping2 = layout_right::mapping; -#ifndef __clang__ // FIXME, Clang suddenly cannot digest this +#ifdef __clang__ + if (!is_constant_evaluated()) // FIXME clang hits constexpr limit here +#endif { // Check construction from other layout_right::mapping Mapping m1{ext}; Mapping2 m2{m1}; @@ -59,7 +70,6 @@ constexpr void check_members(const extents& ext, index_se static_assert(is_nothrow_constructible_v); // Other tests are defined in 'check_construction_from_other_right_mapping' function } -#endif // __clang__ { // Check construction from layout_left::mapping using LeftMapping = layout_left::mapping; @@ -89,9 +99,9 @@ constexpr void check_members(const extents& ext, index_se } } - using StrideMapping = layout_stride::mapping; - StrideMapping stride_mapping{ext, strides}; - [[maybe_unused]] Mapping m{stride_mapping}; + layout_stride::mapping m1{ext, strides}; + Mapping m2{m1}; + assert(m1.extents() == m2.extents()); // Other tests are defined in 'check_construction_from_other_stride_mapping' function } @@ -103,9 +113,10 @@ constexpr void check_members(const extents& ext, index_se } { // Check 'required_span_size' function - const IndexType expected_value = static_cast((ext.extent(Indices) * ... * 1)); - assert(m.required_span_size() == expected_value); + same_as decltype(auto) rss = m.required_span_size(); + assert(rss == static_cast((ext.extent(Indices) * ... * 1))); static_assert(noexcept(m.required_span_size())); + // Other tests are defined in 'check_mapping_properties' } { // Check operator() @@ -126,6 +137,7 @@ constexpr void check_members(const extents& ext, index_se static_assert(Mapping::is_unique()); static_assert(Mapping::is_exhaustive()); static_assert(Mapping::is_strided()); + // Other tests are defined in 'check_mapping_properties' } if constexpr (Ext::rank() > 0) { // Check 'stride' function @@ -134,6 +146,7 @@ constexpr void check_members(const extents& ext, index_se assert(m.stride(Ext::rank() - 1) == 1); static_assert(noexcept(m.stride(Ext::rank() - 1))); static_assert(noexcept(m.stride(0))); + // Other tests are defined in 'check_stride_function' } else { static_assert(!CheckStrideMemberFunction); } @@ -145,6 +158,44 @@ constexpr void check_members(const extents& ext, index_se } } +void check_mapping_properties() { + if constexpr (!is_permissive) { + auto check = []([[maybe_unused]] const auto& mapping) { + const auto props = get_mapping_properties(mapping); + assert(props.req_span_size == mapping.required_span_size()); + assert(props.uniqueness); + assert(props.exhaustiveness); + assert(props.strideness); + }; + + using M1 = layout_right::mapping>; + check(M1{}); + + using M2 = layout_right::mapping>; + check(M2{M2::extents_type{2, 6}}); + + using M3 = layout_right::mapping>; + check(M3{M3::extents_type{4, 3, 5, 4}}); + } +} + +constexpr void check_construction_from_extents() { + using Ext = extents; + Ext ext; + + { // Check construction from 'extents_type' + layout_right::mapping mp(ext); + assert(mp.extents() == ext); + static_assert(is_nothrow_constructible_v); + } + + { // Check construction from other extents + layout_right::mapping> mp(ext); + assert(mp.extents() == ext); + static_assert(is_nothrow_constructible_v); + } +} + constexpr void check_construction_from_other_right_mapping() { { // Check invalid construction using Mapping = layout_right::mapping>; @@ -166,10 +217,10 @@ constexpr void check_construction_from_other_right_mapping() { constexpr void check_construction_from_other_left_mapping() { { // Check construction from layout_left::mapping with various values of E::rank() - static_assert( - is_constructible_v>, layout_left::mapping>>); - static_assert( - is_constructible_v>, layout_left::mapping>>); + static_assert(is_nothrow_constructible_v>, + layout_left::mapping>>); + static_assert(is_nothrow_constructible_v>, + layout_left::mapping>>); static_assert( !is_constructible_v>, layout_left::mapping>>); static_assert( @@ -188,18 +239,27 @@ constexpr void check_construction_from_other_left_mapping() { static_assert(NotImplicitlyConstructibleFrom>>); static_assert(NotImplicitlyConstructibleFrom>>); } + + { // Check effects + layout_right::mapping> m1; + layout_right::mapping> m2{m1}; + assert(m2.extents().extent(0) == 6); + assert(m2.extents().extent(1) == 2); + assert(m2.extents().extent(2) == 6); + assert(m1.extents() == m2.extents()); + } } 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>>); + static_assert(is_nothrow_constructible_v>, + layout_stride::mapping>>); + static_assert(is_nothrow_constructible_v>, + layout_stride::mapping>>); + static_assert(is_nothrow_constructible_v>, + layout_stride::mapping>>); + static_assert(is_nothrow_constructible_v>, + layout_stride::mapping>>); } { // Check construction from layout_stride::mapping when E is invalid @@ -210,8 +270,13 @@ constexpr void check_construction_from_other_stride_mapping() { { // Check correctness using Ext = extents; - layout_stride::mapping stride_mapping{Ext{}, array{72, 24, 12, 4, 1}}; - [[maybe_unused]] layout_right::mapping m{stride_mapping}; + layout_stride::mapping m1{Ext{}, array{72, 24, 12, 4, 1}}; + layout_right::mapping m2{m1}; + assert(m2.extents().extent(0) == 4); + assert(m2.extents().extent(1) == 3); + assert(m2.stride(2) == 12); + assert(m2.stride(3) == 4); + assert(m2.extents() == m1.extents()); } { // Check implicit conversions @@ -272,6 +337,35 @@ constexpr void check_call_operator() { } } +constexpr void check_stride_function() { + layout_right::mapping> prime_mapping; + + { // Check return type + same_as decltype(auto) s = prime_mapping.stride(0); + assert(s == 1155); // 11 * 7 * 5 * 3 + } + + { // Check that argument is 'rank_type' + struct ConvertibleToRankType { + constexpr operator integral auto() const { + return 0; + } + + constexpr operator size_t() const { // NB: 'rank_type' is always 'size_t' + return 1; + } + }; + + assert(prime_mapping.stride(ConvertibleToRankType{}) == 385); // 11 * 7 * 5 + } + + { // Check other strides + assert(prime_mapping.stride(2) == 77); + assert(prime_mapping.stride(3) == 11); + assert(prime_mapping.stride(4) == 1); + } +} + constexpr void check_comparisons() { using StaticMapping = layout_right::mapping>; using DynamicMapping = layout_right::mapping>; @@ -292,6 +386,14 @@ constexpr void check_comparisons() { } } +constexpr void check_ctad() { + using Ext = extents; + layout_right::mapping m{Ext{}}; + static_assert(same_as>); + assert(m.extents().extent(0) == 5); + assert(m.stride(1) == 1); +} + constexpr void check_correctness() { { // empty extents const array vals{}; @@ -416,11 +518,17 @@ constexpr bool test() { [](const extents& ext) { check_members(ext, make_index_sequence{}); }); + if (!is_constant_evaluated()) { // too heavy for compile time + check_mapping_properties(); + } + check_construction_from_extents(); check_construction_from_other_right_mapping(); check_construction_from_other_left_mapping(); check_construction_from_other_stride_mapping(); check_call_operator(); + check_stride_function(); check_comparisons(); + check_ctad(); check_correctness(); return true; diff --git a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp index ec4ea0711a..4c39707b54 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp @@ -10,7 +10,7 @@ #include #include -#include "test_mdspan_support.hpp" +#include using namespace std; @@ -44,7 +44,7 @@ constexpr void do_check_members(const extents& ext, static_assert(same_as); { // Check default and copy constructor - Mapping m; + const Mapping m; const Mapping cpy = m; const layout_right::mapping right_mapping; assert(m == right_mapping); @@ -53,12 +53,20 @@ constexpr void do_check_members(const extents& ext, static_assert(is_nothrow_copy_constructible_v); } + { // Check copy assignment operator + const Mapping m; + Mapping cpy; + cpy = m; + assert(cpy == m); + static_assert(is_nothrow_copy_assignable_v); + } + { // Check construction from extents_type and array Mapping m{ext, strs}; assert(m.extents() == ext); assert(ranges::equal(m.strides(), strs, CmpEqual{})); static_assert(is_nothrow_constructible_v); - // Other tests are defined in 'check_construction_from_extents_and_array' function + // Other tests are defined in 'check_construction_from_extents_and_array_or_span' function } { // Check construction from extents_type and span @@ -67,7 +75,7 @@ constexpr void do_check_members(const extents& ext, assert(m.extents() == ext); assert(ranges::equal(m.strides(), strs, CmpEqual{})); static_assert(is_nothrow_constructible_v); - // Other tests are defined in 'check_construction_from_extents_and_array' function + // Other tests are defined in 'check_construction_from_extents_and_array_or_span' function } using OtherIndexType = long long; @@ -94,6 +102,7 @@ constexpr void do_check_members(const extents& ext, same_as decltype(auto) strs2 = m.strides(); assert(ranges::equal(strs2, strs, CmpEqual{})); static_assert(noexcept(m.strides())); + // Other tests are defined in 'check_stride_function' } { // Check 'required_span_size' function @@ -105,7 +114,7 @@ constexpr void do_check_members(const extents& ext, assert(m.required_span_size() == expected_value); } static_assert(noexcept(m.required_span_size())); - // Other tests are defined in 'check_required_span_size' function + // Other tests are defined in 'check_required_span_size' and 'check_mapping_properties' functions } // Call operator() is tested in 'check_call_operator' function @@ -120,6 +129,7 @@ constexpr void do_check_members(const extents& ext, static_assert(Mapping::is_unique()); static_assert(Mapping::is_strided()); // Tests of 'is_exhaustive' are defined in 'check_is_exhaustive' function + // Other tests are defined in 'check_mapping_properties' } { // Check 'stride' function @@ -164,35 +174,99 @@ constexpr void check_members(extents ext, const array(ext, strides); } -constexpr void check_construction_from_extents_and_array() { - // Check invalid construction - using Mapping = layout_stride::mapping>; - static_assert(!is_constructible_v, array>); - static_assert(!is_constructible_v, array>); - static_assert(!is_constructible_v, array>); - static_assert(!is_constructible_v, array>); - static_assert(!is_constructible_v, span>); - static_assert(!is_constructible_v, span>); - static_assert(!is_constructible_v, span>); - static_assert(!is_constructible_v, span>); - static_assert(!is_constructible_v, array>); - static_assert(!is_constructible_v, span>); - static_assert(!is_constructible_v, array, 2>>); - static_assert(!is_constructible_v, span, 2>>); +void check_mapping_properties() { + if constexpr (!is_permissive) { + auto check = [](const auto& mapping, const bool expected_exhaustiveness) { + const auto props = get_mapping_properties(mapping); + assert(props.req_span_size == mapping.required_span_size()); + assert(props.uniqueness); + assert(props.exhaustiveness == expected_exhaustiveness); + assert(props.exhaustiveness == mapping.is_exhaustive()); + assert(props.strideness); + }; + + { // Check exhaustive mappings + using M1 = layout_stride::mapping>; + check(M1{M1::extents_type{}, array{6, 1, 2}}, true); + + using M2 = layout_stride::mapping>; + check(M2{M2::extents_type{6, 7}, array{1, 48, 6}}, true); + + using M3 = layout_stride::mapping>; + check(M3{M3::extents_type{3, 5, 2, 4}, array{20, 1, 60, 5}}, true); + } + + { // Check non-exhaustive mappings + using M1 = layout_stride::mapping>; + check(M1{M1::extents_type{}, array{9, 18, 1}}, false); + + using M2 = layout_stride::mapping>; + check(M2{M2::extents_type{4, 3}, array{12, 36, 1}}, false); + + using M3 = layout_stride::mapping>; + check(M3{M3::extents_type{4, 3, 2}, array{8, 32, 2}}, false); + } + } +} + +constexpr void check_construction_from_extents_and_array_or_span() { + { // Check invalid construction + using Mapping = layout_stride::mapping>; + static_assert(!is_constructible_v, array>); + static_assert(!is_constructible_v, span>); + static_assert(!is_constructible_v, array>); + static_assert(!is_constructible_v, span>); + static_assert(!is_constructible_v, array>); + static_assert(!is_constructible_v, span>); + static_assert(!is_constructible_v, array>); + static_assert(!is_constructible_v, span>); + static_assert(!is_constructible_v, array>); + static_assert(!is_constructible_v, span>); + static_assert(!is_constructible_v, array, 2>>); + static_assert(!is_constructible_v, span, 2>>); + } + + using Ext = extents; + Ext ext; + + { // Check construction from 'extents_type' + array strs{24, 1, 4}; + layout_stride::mapping m1(ext, strs); + assert(m1.extents() == ext); + static_assert(is_nothrow_constructible_v); + + span s{strs}; + layout_stride::mapping m2(ext, s); + assert(m2.extents() == ext); + static_assert(is_nothrow_constructible_v); + } + + { // Check construction from other extents + using Ext2 = extents; + + const array strs{1, 12, 2}; + layout_stride::mapping m1(ext, strs); + assert(m1.extents() == ext); + static_assert(is_nothrow_constructible_v); + + const span s{strs}; + layout_stride::mapping m2(ext, s); + static_assert(is_nothrow_constructible_v); + } } constexpr void check_construction_from_other_mappings() { - { // Check construction + { // Check valid construction using Mapping = layout_stride::mapping>; - static_assert(is_constructible_v>>); - static_assert(is_constructible_v>>); - static_assert(is_constructible_v>>); - static_assert(is_constructible_v>>); - static_assert(is_constructible_v>>); + static_assert(is_nothrow_constructible_v>>); + static_assert(is_nothrow_constructible_v>>); + static_assert(is_nothrow_constructible_v>>); + static_assert(is_nothrow_constructible_v>>); + static_assert(is_nothrow_constructible_v>>); } { // Check invalid construction - using Mapping = layout_stride::mapping>; + using Mapping = layout_stride::mapping>; static_assert(!is_constructible_v>>); static_assert(!is_constructible_v>>); static_assert(!is_constructible_v>>); @@ -328,6 +402,36 @@ constexpr void check_call_operator() { } } +constexpr void check_stride_function() { + using Ext = extents; + layout_stride::mapping even_mapping{Ext{}, array{80, 160, 640, 1, 8}}; + + { // Check return type + same_as decltype(auto) s = even_mapping.stride(0); + assert(s == 80); + } + + { // Check that argument is 'rank_type' + struct ConvertibleToRankType { + constexpr operator integral auto() const { + return 0; + } + + constexpr operator size_t() const { // NB: 'rank_type' is always 'size_t' + return 1; + } + }; + + assert(even_mapping.stride(ConvertibleToRankType{}) == 160); + } + + { // Check other strides + assert(even_mapping.stride(2) == 640); + assert(even_mapping.stride(3) == 1); + assert(even_mapping.stride(4) == 8); + } +} + constexpr void check_comparisons() { using E = extents; using StaticStrideMapping = layout_stride::mapping; @@ -351,7 +455,8 @@ constexpr void check_comparisons() { { // Check correctness: layout_stride::mapping with layout_stride::mapping StaticStrideMapping m1{E{}, array{3, 1}}; DynamicStrideMapping m2{dextents{2, 3}, array{3, 1}}; - assert(m1 == m2); // extents are equal, OFFSET(rhs) == 0, strides are equal + same_as decltype(auto) cond = m1 == m2; + assert(cond); // extents are equal, OFFSET(rhs) == 0, strides are equal DynamicStrideMapping m3{dextents{2, 3}, array{1, 2}}; assert(m1 != m3); // extents are equal, OFFSET(rhs) == 0, strides are not equal @@ -363,12 +468,16 @@ constexpr void check_comparisons() { assert(m3 != m4); // extents are not equal, OFFSET(rhs) == 0, strides are not equal // NB: OFFSET(layout_stride::mapping) is always equal to 0 + + static_assert(noexcept(m1 == m2)); + static_assert(noexcept(m1 != m3)); } { // Check correctness: layout_stride::mapping with layout_left::mapping LeftMapping m1; StaticStrideMapping m2{E{}, array{1, 2}}; - assert(m1 == m2); // extents are equal, OFFSET(rhs) == 0, strides are equal + same_as decltype(auto) cond = m1 == m2; + assert(cond); // extents are equal, OFFSET(rhs) == 0, strides are equal DynamicStrideMapping m3{dextents{2, 3}, array{3, 1}}; assert(m1 != m3); // extents are equal, OFFSET(rhs) == 0, strides are not equal @@ -380,12 +489,16 @@ constexpr void check_comparisons() { assert(m3 != m4); // extents are not equal, OFFSET(rhs) == 0, strides are not equal // NB: OFFSET(layout_left::mapping) is always equal to 0 + + static_assert(noexcept(m1 == m2)); + static_assert(noexcept(m1 != m3)); } { // Check correctness: layout_stride::mapping with layout_right::mapping RightMapping m1; StaticStrideMapping m2{E{}, array{3, 1}}; - assert(m1 == m2); // extents are equal, OFFSET(rhs) == 0, strides are equal + same_as decltype(auto) cond = m1 == m2; + assert(cond); // extents are equal, OFFSET(rhs) == 0, strides are equal DynamicStrideMapping m3{dextents{2, 3}, array{1, 2}}; assert(m1 != m3); // extents are equal, OFFSET(rhs) == 0, strides are not equal @@ -397,11 +510,47 @@ constexpr void check_comparisons() { assert(m3 != m4); // extents are not equal, OFFSET(rhs) == 0, strides are not equal // NB: OFFSET(layout_right::mapping) is always equal to 0 + + static_assert(noexcept(m1 == m2)); + static_assert(noexcept(m1 != m3)); } // TRANSITION, Check comparisons with custom layout mapping } +constexpr void check_ctad() { + using E = extents; + E e; + + { // E::index_type and array::value_type are the same + array a{1, 2}; + layout_stride::mapping m1{e, a}; + static_assert(same_as>); + assert(m1.extents() == e); + assert(m1.strides() == a); + + span s{a}; + layout_stride::mapping m2{e, s}; + static_assert(same_as>); + assert(m2.extents() == e); + assert(m2.strides() == a); + } + + { // E::index_type and array::value_type are different + array a{1, 2}; + layout_stride::mapping m1{e, a}; + static_assert(same_as>); + assert(m1.extents() == e); + assert(ranges::equal(m1.strides(), a, CmpEqual{})); + + span s{a}; + layout_stride::mapping m2{e, s}; + static_assert(same_as>); + assert(m2.extents() == e); + assert(ranges::equal(m2.strides(), a, CmpEqual{})); + } +} + constexpr void check_correctness() { { // empty extents const array vals{}; @@ -535,39 +684,6 @@ constexpr void check_correctness() { } } -constexpr void check_ctad() { - using E = extents; - E e; - - { // E::index_type and array::value_type are the same - array a{1, 2}; - layout_stride::mapping m1{e, a}; - static_assert(same_as>); - assert(m1.extents() == e); - assert(m1.strides() == a); - - span s{a}; - layout_stride::mapping m2{e, s}; - static_assert(same_as>); - assert(m2.extents() == e); - assert(m2.strides() == a); - } - - { // E::index_type and array::value_type are different - array a{1, 2}; - layout_stride::mapping m1{e, a}; - static_assert(same_as>); - assert(m1.extents() == e); - assert(ranges::equal(m1.strides(), a, CmpEqual{})); - - span s{a}; - layout_stride::mapping m2{e, s}; - static_assert(same_as>); - assert(m2.extents() == e); - assert(ranges::equal(m2.strides(), a, CmpEqual{})); - } -} - constexpr bool test() { // Check signed integers check_members(extents{5}, array{1}); @@ -583,14 +699,18 @@ constexpr bool test() { check_members(extents{}, array{1}); check_members(extents{3}, array{1, 3, 6}); - check_construction_from_extents_and_array(); + if (!is_constant_evaluated()) { // too heavy for compile time + check_mapping_properties(); + } + check_construction_from_extents_and_array_or_span(); check_construction_from_other_mappings(); check_required_span_size(); check_is_exhaustive(); check_call_operator(); + check_stride_function(); check_comparisons(); - check_correctness(); check_ctad(); + check_correctness(); return true; } diff --git a/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp b/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp index a0e16d6339..44580c94dd 100644 --- a/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp @@ -339,9 +339,12 @@ struct TrivialAccessor { static_assert(check_accessor_policy_requirements>()); static_assert(is_trivial_v>); -constexpr void check_modeled_concepts() { - using Mds = mdspan, TrackingLayout, - AccessorWithCustomOffsetPolicy>; +template class AccessorTemplate> +constexpr void check_modeled_concepts_and_member_types() { + using Accessor = AccessorTemplate; + using Mds = mdspan; + + // Check modeled concepts static_assert(copyable); static_assert(is_nothrow_move_constructible_v); static_assert(is_nothrow_move_assignable_v); @@ -350,13 +353,8 @@ constexpr void check_modeled_concepts() { is_trivially_copyable_v == (is_trivially_copyable_v && is_trivially_copyable_v && is_trivially_copyable_v) ); -} -constexpr void check_member_types() { - using Ext = extents; - using Layout = layout_stride; - using Accessor = TrivialAccessor; - using Mds = mdspan; + // Check member types static_assert(same_as); static_assert(same_as); static_assert(same_as); @@ -1319,8 +1317,10 @@ constexpr void check_deduction_guides() { } constexpr bool test() { - check_modeled_concepts(); - check_member_types(); + check_modeled_concepts_and_member_types, layout_stride, TrivialAccessor>(); + check_modeled_concepts_and_member_types, TrackingLayout<>, + AccessorWithTrackingDataHandle>(); + check_modeled_concepts_and_member_types, layout_left, TrackingAccessor>(); check_observers(); check_default_constructor(); check_defaulted_copy_and_move_constructors(); From 652e2b070e23e7b359f999239bad83b31eff0f3b Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Wed, 21 Jun 2023 04:07:30 +0200 Subject: [PATCH 31/57] ``: `extents` improvements (#3786) --- stl/inc/mdspan | 154 +++++++++++------- tests/std/include/test_mdspan_support.hpp | 4 +- .../P0009R18_mdspan_extents_death/test.cpp | 14 +- 3 files changed, 105 insertions(+), 67 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index bd7420639a..65bcadb4cb 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 b653d74268..c6c1f034fa 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 2c80d249ab..78e3e9ad2a 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, From 1984147cbd880268201ccdcb2bc296e8eabdc4f1 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 4 Jul 2023 00:10:01 +0200 Subject: [PATCH 32/57] ``: Improve debug checks (#3821) --- stl/inc/mdspan | 290 +++++++++++++----- tests/std/include/test_mdspan_support.hpp | 18 +- tests/std/test.lst | 1 + .../P0009R18_mdspan_extents_death/test.cpp | 8 + .../P0009R18_mdspan_layout_left/test.cpp | 10 +- .../test.cpp | 19 +- .../P0009R18_mdspan_layout_right/test.cpp | 10 +- .../test.cpp | 19 +- .../test.cpp | 39 ++- .../P0009R18_mdspan_mdspan_death/env.lst | 4 + .../P0009R18_mdspan_mdspan_death/test.cpp | 60 ++++ 11 files changed, 368 insertions(+), 110 deletions(-) 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 65bcadb4cb..332cf5ff9d 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -41,10 +41,10 @@ public: static constexpr rank_type _Rank = sizeof...(_Extents); static constexpr rank_type _Rank_dynamic = (static_cast(_Extents == dynamic_extent) + ... + 0); + static constexpr array _Static_extents = {_Extents...}; + static constexpr bool _Multidim_index_space_size_is_always_zero = ((_Extents == 0) || ...); private: - static constexpr array _Static_extents = {_Extents...}; - _NODISCARD static consteval auto _Make_dynamic_indices() noexcept { array _Result{}; rank_type _Counter = 0; @@ -182,6 +182,9 @@ public: auto _Check_extent = [](const _Ty& _Ext) { if constexpr (_Is_standard_integer<_Ty>) { return _Ext >= 0 && _STD in_range(_Ext); + } else if constexpr (integral<_Ty> && !same_as<_Ty, bool>) { // NB: character types + const auto _Integer_ext = static_cast(_Ext); + return _Integer_ext >= 0 && _STD in_range(_Integer_ext); } else { return true; // NB: We cannot check preconditions } @@ -255,11 +258,40 @@ public: } } - _NODISCARD static consteval bool _Is_index_space_size_representable() { - if constexpr (rank_dynamic() == 0 && rank() > 0) { - return _STD in_range((_Extents * ...)); + _NODISCARD static consteval bool _Is_static_multidim_index_space_size_representable() noexcept { + // Pre: rank_dynamic() == 0 + if constexpr (_Multidim_index_space_size_is_always_zero) { + return true; } else { + index_type _Result{1}; +#pragma warning(push) +#pragma warning(disable : 6287) // TRANSITION, DevCom-10398426 + const bool _Overflow = (_Mul_overflow(static_cast(_Extents), _Result, _Result) || ...); +#pragma warning(pop) + return !_Overflow; + } + } + + template + _NODISCARD constexpr bool _Is_dynamic_multidim_index_space_size_representable() const noexcept { + // Pre: rank_dynamic() != 0 + if constexpr (_Multidim_index_space_size_is_always_zero) { return true; + } else { + bool _Overflow = false; + _Ty _Result = 1; + for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) { + const auto _Ext = static_cast<_Ty>(extent(_Idx)); + if (_Ext == 0) { + return true; + } + + if (!_Overflow) { + _Overflow = _Mul_overflow(_Ext, _Result, _Result); + } + } + + return !_Overflow; } } @@ -327,7 +359,7 @@ private: array _Result; _Result.front() = 1; for (size_t _Idx = 1; _Idx < _Ty::_Rank + 1; ++_Idx) { - _Result[_Idx] = static_cast<_Ty::index_type>(_Result[_Idx - 1] * _Ty::static_extent(_Idx - 1)); + _Result[_Idx] = static_cast<_Ty::index_type>(_Result[_Idx - 1] * _Ty::_Static_extents[_Idx - 1]); } return _Result; } @@ -365,7 +397,7 @@ private: array _Result; _Result.back() = 1; for (size_t _Idx = _Ty::_Rank; _Idx-- > 1;) { - _Result[_Idx - 1] = static_cast<_Ty::index_type>(_Result[_Idx] * _Ty::static_extent(_Idx)); + _Result[_Idx - 1] = static_cast<_Ty::index_type>(_Result[_Idx] * _Ty::_Static_extents[_Idx]); } return _Result; } @@ -409,7 +441,8 @@ public: static_assert(_Is_extents, "Extents must be a specialization of std::extents (N4950 [mdspan.layout.left.overview]/2)."); - static_assert(extents_type::_Is_index_space_size_representable(), + static_assert( + extents_type::rank_dynamic() != 0 || extents_type::_Is_static_multidim_index_space_size_representable(), "If Extents::rank_dynamic() == 0 is true, then the size of the multidimensional index space Extents() must be " "representable as a value of type typename Extents::index_type (N4950 [mdspan.layout.left.overview]/4)."); @@ -417,7 +450,13 @@ public: constexpr mapping(const mapping&) noexcept = default; constexpr mapping(const extents_type& _Exts_) noexcept : _Exts(_Exts_) { - // TRANSITION, CHECK [mdspan.layout.left.cons]/1 (REQUIRES '_Multiply_with_overflow_check' FROM #3561) +#if _CONTAINER_DEBUG_LEVEL > 0 + if constexpr (extents_type::rank_dynamic() != 0) { + _STL_VERIFY(_Exts_._Is_dynamic_multidim_index_space_size_representable(), + "The size of the multidimensional index space e must be representable as a value of type index_type " + "(N4950 [mdspan.layout.left.cons]/1)."); + } +#endif // _CONTAINER_DEBUG_LEVEL > 0 } template @@ -425,9 +464,11 @@ public: constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const mapping<_OtherExtents>& _Other) noexcept : _Exts(_Other.extents()) { +#if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_STD in_range(_Other.required_span_size()), "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " "[mdspan.layout.left.cons]/4)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 } template @@ -435,30 +476,31 @@ public: constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const layout_right::mapping<_OtherExtents>& _Other) noexcept : _Exts(_Other.extents()) { +#if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_STD in_range(_Other.required_span_size()), "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " "[mdspan.layout.left.cons]/7)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 } template requires is_constructible_v constexpr explicit(extents_type::rank() > 0) mapping(const layout_stride::template mapping<_OtherExtents>& _Other) : _Exts(_Other.extents()) { +#if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (extents_type::rank() > 0) { - const bool _Verify = [&](index_sequence<_Indices...>) { - index_type _Prod = 1; - return ((_Other.stride(_Indices) - == (_Indices == extents_type::_Rank - 1 - ? _Prod - : _STD exchange(_Prod, static_cast(_Prod * _Exts.extent(_Indices))))) - && ...); - }(make_index_sequence{}); - _STL_VERIFY(_Verify, "For all r in the range [0, extents_type::rank()), other.stride(r) must be equal to " - "extents().fwd-prod-of-extents(r) (N4950 [mdspan.layout.left.cons]/10.1)."); + index_type _Prod = 1; + for (size_t _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { + _STL_VERIFY(_Other.stride(_Idx) == _Prod, + "For all r in the range [0, extents_type::rank()), other.stride(r) must be equal to " + "extents().fwd-prod-of-extents(r) (N4950 [mdspan.layout.left.cons]/10.1)."); + _Prod = static_cast(_Prod * _Exts.extent(_Idx)); + } + _STL_VERIFY(_STD in_range(_Other.required_span_size()), + "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " + "[mdspan.layout.left.cons]/10.2)."); } - _STL_VERIFY(_STD in_range(_Other.required_span_size()), - "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " - "[mdspan.layout.left.cons]/10.2)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 } constexpr mapping& operator=(const mapping&) noexcept = default; @@ -475,10 +517,6 @@ public: requires (sizeof...(_IndexTypes) == extents_type::rank()) && (is_convertible_v<_IndexTypes, index_type> && ...) && (is_nothrow_constructible_v && ...) _NODISCARD constexpr index_type operator()(_IndexTypes... _Indices) const noexcept { - _STL_VERIFY(_Exts._Contains_multidimensional_index( - make_index_sequence{}, static_cast(_Indices)...), - "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " - "[mdspan.layout.left.obs]/3)."); return _Index_impl(make_index_sequence{}, static_cast(_Indices)...); } @@ -509,8 +547,10 @@ public: _NODISCARD constexpr index_type stride(const rank_type _Idx) const noexcept requires (extents_type::rank() > 0) { +#if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Idx < extents_type::_Rank, "Value of i must be less than extents_type::rank() (N4950 [mdspan.layout.left.obs]/6)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 return _Fwd_prod_of_extents::_Calculate(_Exts, _Idx); } @@ -524,8 +564,15 @@ private: extents_type _Exts{}; template - _NODISCARD constexpr index_type _Index_impl(index_sequence<_Seq...>, _IndexTypes... _Indices) const noexcept { + _NODISCARD constexpr index_type _Index_impl( + [[maybe_unused]] index_sequence<_Seq...> _Index_seq, _IndexTypes... _Indices) const noexcept { _STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...)); +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Exts._Contains_multidimensional_index(_Index_seq, _Indices...), + "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " + "[mdspan.layout.left.obs]/3)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + index_type _Stride = 1; index_type _Result = 0; (((_Result += _Indices * _Stride), (_Stride *= _Exts.extent(_Seq))), ...); @@ -544,7 +591,8 @@ public: static_assert(_Is_extents, "Extents must be a specialization of std::extents (N4950 [mdspan.layout.right.overview]/2)."); - static_assert(extents_type::_Is_index_space_size_representable(), + static_assert( + extents_type::rank_dynamic() != 0 || extents_type::_Is_static_multidim_index_space_size_representable(), "If Extents::rank_dynamic() == 0 is true, then the size of the multidimensional index space Extents() must be " "representable as a value of type typename Extents::index_type (N4950 [mdspan.layout.right.overview]/4)."); @@ -552,7 +600,13 @@ public: constexpr mapping(const mapping&) noexcept = default; constexpr mapping(const extents_type& _Exts_) noexcept : _Exts(_Exts_) { - // TRANSITION, CHECK [mdspan.layout.right.cons]/1 (REQUIRES '_Multiply_with_overflow_check' FROM #3561) +#if _CONTAINER_DEBUG_LEVEL > 0 + if constexpr (extents_type::rank_dynamic() != 0) { + _STL_VERIFY(_Exts_._Is_dynamic_multidim_index_space_size_representable(), + "The size of the multidimensional index space e must be representable as a value of type index_type " + "(N4950 [mdspan.layout.right.cons]/1)."); + } +#endif // _CONTAINER_DEBUG_LEVEL > 0 } template @@ -560,9 +614,11 @@ public: constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const mapping<_OtherExtents>& _Other) noexcept : _Exts(_Other.extents()) { +#if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_STD in_range(_Other.required_span_size()), "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " "[mdspan.layout.right.cons]/4)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 } template @@ -570,9 +626,11 @@ public: constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const layout_left::mapping<_OtherExtents>& _Other) noexcept : _Exts(_Other.extents()) { +#if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_STD in_range(_Other.required_span_size()), "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " "[mdspan.layout.right.cons]/7)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 } template @@ -580,22 +638,20 @@ public: constexpr explicit(extents_type::rank() > 0) mapping(const layout_stride::template mapping<_OtherExtents>& _Other) noexcept : _Exts(_Other.extents()) { +#if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (extents_type::rank() > 0) { - const bool _Verify = [&](index_sequence<_Indices...>) { - index_type _Prod = stride(0); - return ( - (_Other.stride(_Indices) - == (_Indices == extents_type::_Rank - 1 - ? _Prod - : _STD exchange(_Prod, static_cast(_Prod / _Exts.extent(_Indices + 1))))) - && ...); - }(make_index_sequence{}); - _STL_VERIFY(_Verify, "For all r in the range [0, extents_type::rank()), other.stride(r) must be equal to " - "extents().rev-prod-of-extents(r) (N4950 [mdspan.layout.right.cons]/10.1)."); + index_type _Prod = 1; + for (size_t _Idx = extents_type::_Rank; _Idx-- > 0;) { + _STL_VERIFY(_Prod == _Other.stride(_Idx), + "For all r in the range [0, extents_type::rank()), other.stride(r) must be equal to " + "extents().rev-prod-of-extents(r) (N4950 [mdspan.layout.right.cons]/10.1)."); + _Prod = static_cast(_Prod * _Exts.extent(_Idx)); + } + _STL_VERIFY(_STD in_range(_Other.required_span_size()), + "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " + "[mdspan.layout.right.cons]/10.2)."); } - _STL_VERIFY(_STD in_range(_Other.required_span_size()), - "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " - "[mdspan.layout.right.cons]/10.2)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 } constexpr mapping& operator=(const mapping&) noexcept = default; @@ -612,10 +668,6 @@ public: requires (sizeof...(_IndexTypes) == extents_type::rank()) && (is_convertible_v<_IndexTypes, index_type> && ...) && (is_nothrow_constructible_v && ...) _NODISCARD constexpr index_type operator()(_IndexTypes... _Indices) const noexcept { - _STL_VERIFY(_Exts._Contains_multidimensional_index( - make_index_sequence{}, static_cast(_Indices)...), - "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " - "[mdspan.layout.right.obs]/3)."); return _Index_impl(make_index_sequence{}, static_cast(_Indices)...); } @@ -646,8 +698,10 @@ public: _NODISCARD constexpr index_type stride(const rank_type _Idx) const noexcept requires (extents_type::rank() > 0) { +#if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Idx < extents_type::_Rank, "Value of i must be less than extents_type::rank() (N4950 [mdspan.layout.right.obs]/6)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 return _Rev_prod_of_extents::_Calculate(_Exts, _Idx); } @@ -661,8 +715,15 @@ private: extents_type _Exts{}; template - _NODISCARD constexpr index_type _Index_impl(index_sequence<_Seq...>, _IndexTypes... _Indices) const noexcept { + _NODISCARD constexpr index_type _Index_impl( + [[maybe_unused]] index_sequence<_Seq...> _Index_seq, _IndexTypes... _Indices) const noexcept { _STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...)); +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Exts._Contains_multidimensional_index(_Index_seq, _Indices...), + "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " + "[mdspan.layout.right.obs]/3)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + index_type _Result = 0; ((_Result = static_cast(_Indices + _Exts.extent(_Seq) * _Result)), ...); return _Result; @@ -691,7 +752,8 @@ public: static_assert(_Is_extents, "Extents must be a specialization of std::extents (N4950 [mdspan.layout.stride.overview]/2)."); - static_assert(extents_type::_Is_index_space_size_representable(), + static_assert( + extents_type::rank_dynamic() != 0 || extents_type::_Is_static_multidim_index_space_size_representable(), "If Extents::rank_dynamic() == 0 is true, then the size of the multidimensional index space Extents() must be " "representable as a value of type typename Extents::index_type (N4950 [mdspan.layout.stride.overview]/4)."); @@ -699,8 +761,17 @@ public: if constexpr (extents_type::rank() != 0) { _Strides.back() = 1; for (rank_type _Idx = extents_type::_Rank - 1; _Idx-- > 0;) { - // TRANSITION USE `_Multiply_with_overflow_check` IN DEBUG MODE +#if _CONTAINER_DEBUG_LEVEL > 0 + const bool _Overflow = _Mul_overflow(_Strides[_Idx + 1], _Exts.extent(_Idx + 1), _Strides[_Idx]); + // NB: N4950 requires value of 'layout_right::mapping().required_span_size()' to be + // representable as value of type 'index_type', but this is not enough. We need to require every single + // stride to be representable as value of type 'index_type', so we can get desired effects. + _STL_VERIFY(!_Overflow, + "Value of layout_right::mapping().required_span_size() must be " + "representable as a value of type index_type (N4950 [mdspan.layout.stride.cons]/1)."); +#else // ^^^ _CONTAINER_DEBUG_LEVEL > 0 / _CONTAINER_DEBUG_LEVEL == 0 vvv _Strides[_Idx] = static_cast(_Strides[_Idx + 1] * _Exts.extent(_Idx + 1)); +#endif // _CONTAINER_DEBUG_LEVEL > 0 } } } @@ -713,11 +784,30 @@ public: constexpr mapping(const extents_type& _Exts_, span<_OtherIndexType, extents_type::rank()> _Strides_, index_sequence<_Indices...>) noexcept : _Exts(_Exts_), _Strides{static_cast(_STD as_const(_Strides_[_Indices]))...} { - for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { - // TRANSITION CHECK [mdspan.layout.stride.cons]/4.2 (REQUIRES `_Multiply_with_overflow_check`) - _STL_VERIFY(_Strides[_Idx] > 0, "Value of s[i] must be greater than 0 for all i in the range [0, rank_) " - "(N4950 [mdspan.layout.stride.cons]/4.1)."); +#if _CONTAINER_DEBUG_LEVEL > 0 + if constexpr (extents_type::rank() != 0) { + bool _Found_zero = false; + bool _Overflow = false; + index_type _Req_span_size = 0; + for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { + const index_type _Stride = _Strides[_Idx]; + _STL_VERIFY(_Stride > 0, "Value of s[i] must be greater than 0 for all i in the range [0, rank_) " + "(N4950 [mdspan.layout.stride.cons]/4.1)."); + const index_type _Ext = _Exts.extent(_Idx); + if (_Ext == 0) { + _Found_zero = true; + } + + if (!_Found_zero && !_Overflow) { + index_type _Prod; + _Overflow = _Mul_overflow(static_cast(_Ext - 1), _Stride, _Prod) + || _Add_overflow(_Req_span_size, _Prod, _Req_span_size); + } + } + _STL_VERIFY(_Found_zero || !_Overflow, "REQUIRED-SPAN-SIZE(e, s) must be representable as a value of type " + "index_type (N4950 [mdspan.layout.stride.cons]/4.2)."); } +#endif // _CONTAINER_DEBUG_LEVEL > 0 } #ifndef __clang__ // TRANSITION, DevCom-10360833 @@ -757,15 +847,19 @@ public: || _Is_mapping_of) )) mapping(const _StridedLayoutMapping& _Other) noexcept : _Exts(_Other.extents()) { +#if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_STD in_range(_Other.required_span_size()), "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " "[mdspan.layout.stride.cons]/7.3)."); _STL_VERIFY( _Offset(_Other) == 0, "Value of OFFSET(other) must be equal to 0 (N4950 [mdspan.layout.stride.cons]/7.4)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { const auto _Stride = _Other.stride(_Idx); +#if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Stride > 0, "Value of other.stride(r) must be greater than 0 for every rank index r of " "extents() (N4950 [mdspan.layout.stride.cons]/7.2)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 _Strides[_Idx] = static_cast(_Stride); } } @@ -802,10 +896,6 @@ public: requires (sizeof...(_IndexTypes) == extents_type::rank()) && (is_convertible_v<_IndexTypes, index_type> && ...) && (is_nothrow_constructible_v && ...) _NODISCARD constexpr index_type operator()(_IndexTypes... _Indices) const noexcept { - _STL_VERIFY(_Exts._Contains_multidimensional_index( - make_index_sequence{}, static_cast(_Indices)...), - "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " - "[mdspan.layout.stride.obs]/3)."); return _Index_impl(make_index_sequence{}, static_cast(_Indices)...); } @@ -881,8 +971,15 @@ private: } template - _NODISCARD constexpr index_type _Index_impl(index_sequence<_Seq...>, _IndexTypes... _Indices) const noexcept { + _NODISCARD constexpr index_type _Index_impl( + [[maybe_unused]] index_sequence<_Seq...> _Index_seq, _IndexTypes... _Indices) const noexcept { _STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...)); +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Exts._Contains_multidimensional_index(_Index_seq, _Indices...), + "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " + "[mdspan.layout.stride.obs]/3)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return static_cast(((_Indices * _Strides[_Seq]) + ... + 0)); } }; @@ -953,7 +1050,10 @@ public: } _NODISCARD static constexpr size_t static_extent(const rank_type _Idx) noexcept { - return extents_type::static_extent(_Idx); +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Idx < extents_type::_Rank, "Index must be less than rank() (N4950 [mdspan.extents.obs]/1)"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return extents_type::_Static_extents[_Idx]; } _NODISCARD constexpr index_type extent(const rank_type _Idx) const noexcept { @@ -1017,6 +1117,14 @@ public: "[mdspan.mdspan.cons]/20.1)."); static_assert(is_constructible_v, "The extents_type must be constructible from OtherExtents (N4950 [mdspan.mdspan.cons]/20.2)."); +#if _CONTAINER_DEBUG_LEVEL > 0 + for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { + const auto _Static_ext = extents_type::_Static_extents[_Idx]; + _STL_VERIFY(_STD cmp_equal(_Static_ext, dynamic_extent) || _STD cmp_equal(_Static_ext, _Other.extent(_Idx)), + "For each rank index r of extents_type, static_extent(r) == dynamic_extent || static_extent(r) == " + "other.extent(r) must be true (N4950 [mdspan.mdspan.cons]/21.1)."); + } +#endif // _CONTAINER_DEBUG_LEVEL > 0 } constexpr mdspan& operator=(const mdspan&) = default; @@ -1028,7 +1136,7 @@ public: && (is_nothrow_constructible_v && ...) && (sizeof...(_OtherIndexTypes) == rank()) _NODISCARD constexpr reference operator[](_OtherIndexTypes... _Indices) const { - return _Acc.access(_Ptr, static_cast(_Map(static_cast(_STD move(_Indices))...))); + return _Access_impl(static_cast(_STD move(_Indices))...); } #endif // __cpp_multidimensional_subscript @@ -1036,28 +1144,52 @@ public: requires is_convertible_v && is_nothrow_constructible_v _NODISCARD constexpr reference operator[](span<_OtherIndexType, rank()> _Indices) const { - return _Index_impl(_Indices, make_index_sequence{}); + return [&](index_sequence<_Seq...>) -> reference { +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 + return operator[](_STD as_const(_Indices[_Seq])...); +#else // ^^^ defined(__cpp_multidimensional_subscript) / !defined(__cpp_multidimensional_subscript) vvv + return _Multidimensional_access(_STD as_const(_Indices[_Seq])...); +#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ + }(make_index_sequence{}); } template requires is_convertible_v && is_nothrow_constructible_v _NODISCARD constexpr reference operator[](const array<_OtherIndexType, rank()>& _Indices) const { - return _Index_impl(span{_Indices}, make_index_sequence{}); + return [&](index_sequence<_Seq...>) -> reference { +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 + return operator[](_Indices[_Seq]...); +#else // ^^^ defined(__cpp_multidimensional_subscript) / !defined(__cpp_multidimensional_subscript) vvv + return _Multidimensional_access(_Indices[_Seq]...); +#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ + }(make_index_sequence{}); } _NODISCARD constexpr size_type size() const noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 + if constexpr (rank_dynamic() != 0) { + _STL_VERIFY(_Map.extents().template _Is_dynamic_multidim_index_space_size_representable(), + "The size of the multidimensional index space extents() must be representable as a value of type " + "size_type (N4950 [mdspan.mdspan.members]/7)."); + } +#endif // _CONTAINER_DEBUG_LEVEL > 0 return static_cast( _Fwd_prod_of_extents::_Calculate(_Map.extents(), extents_type::_Rank)); } _NODISCARD constexpr bool empty() const noexcept { - for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { - if (_Map.extents().extent(_Idx) == 0) { - return true; + if constexpr (extents_type::_Multidim_index_space_size_is_always_zero) { + return true; + } else { + const extents_type& _Exts = _Map.extents(); + for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { + if (_Exts.extent(_Idx) == 0) { + return true; + } } + return false; } - return false; } friend constexpr void swap(mdspan& _Left, mdspan& _Right) noexcept { @@ -1115,21 +1247,23 @@ public: } private: - template - _NODISCARD constexpr reference _Index_impl(span<_OtherIndexType, rank()> _Indices, index_sequence<_Seq...>) const { -#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 - return operator[](_STD as_const(_Indices[_Seq])...); -#else // ^^^ defined(__cpp_multidimensional_subscript) / !defined(__cpp_multidimensional_subscript) vvv - return _Multidimensional_access(_STD as_const(_Indices[_Seq])...); -#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ - } - #ifndef __cpp_multidimensional_subscript // TRANSITION, P2128R6 template _NODISCARD constexpr reference _Multidimensional_access(_OtherIndexTypes... _Indices) const { - return _Acc.access(_Ptr, static_cast(_Map(static_cast(_STD move(_Indices))...))); + return _Access_impl(static_cast(_STD move(_Indices))...); + } +#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ + + template + _NODISCARD constexpr reference _Access_impl(_OtherIndexTypes... _Indices) const { + _STL_INTERNAL_STATIC_ASSERT((same_as<_OtherIndexTypes, index_type> && ...)); +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Map.extents()._Contains_multidimensional_index(make_index_sequence{}, _Indices...), + "I must be a multidimensional index in extents() (N4950 [mdspan.mdspan.members]/3)."); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + + return _Acc.access(_Ptr, static_cast(_Map(_Indices...))); } -#endif // __cpp_multidimensional_subscript data_handle_type _Ptr{}; mapping_type _Map{}; diff --git a/tests/std/include/test_mdspan_support.hpp b/tests/std/include/test_mdspan_support.hpp index c6c1f034fa..60b9729e78 100644 --- a/tests/std/include/test_mdspan_support.hpp +++ b/tests/std/include/test_mdspan_support.hpp @@ -267,14 +267,13 @@ struct MappingProperties { template requires (!details::PermissiveTest::test()) MappingProperties get_mapping_properties(const Mapping& mapping) { - using IndexType = typename Mapping::index_type; - constexpr IndexType zero = 0; - constexpr auto rank = Mapping::extents_type::rank(); + using IndexType = typename Mapping::index_type; + constexpr auto rank = Mapping::extents_type::rank(); constexpr std::make_index_sequence rank_indices; auto get_extent = [&](size_t i) { return mapping.extents().extent(i); }; auto multidim_indices = [&](std::index_sequence) { - return std::views::cartesian_product(std::views::iota(zero, get_extent(Indices))...); + return std::views::cartesian_product(std::views::iota(IndexType{0}, get_extent(Indices))...); }(rank_indices); auto map_index = [&](const auto& tpl) { return std::apply([&](auto... i) { return mapping(i...); }, tpl); }; @@ -284,19 +283,18 @@ MappingProperties get_mapping_properties(const Mapping& mapping) { MappingProperties props{}; // Find required span size (N4950 [mdspan.layout.reqmts]/12) - if (std::ranges::contains(std::views::iota(0u, rank) | std::views::transform(get_extent), zero)) { + if (std::ranges::contains(std::views::iota(0u, rank) | std::views::transform(get_extent), IndexType{0})) { props.req_span_size = 0; } else { props.req_span_size = static_cast(1 + mapped_indices.back()); } // Is mapping unique? (N4950 [mdspan.layout.reqmts]/14) - props.uniqueness = !std::ranges::contains(std::views::pairwise_transform(mapped_indices, std::minus{}), zero); + props.uniqueness = std::ranges::adjacent_find(mapped_indices) == mapped_indices.end(); - { // Is mapping exhaustive? (N4950 [mdspan.layout.reqmts]/16) - const auto diffs = std::views::pairwise_transform(mapped_indices, [](auto x, auto y) { return y - x; }); - props.exhaustiveness = std::ranges::find_if_not(diffs, [](auto x) { return x == 1; }) == diffs.end(); - } + // Is mapping exhaustive? (N4950 [mdspan.layout.reqmts]/16) + props.exhaustiveness = + std::ranges::adjacent_find(mapped_indices, [](auto x, auto y) { return y - x > 1; }) == mapped_indices.end(); { // Is mapping strided? (N4950 [mdspan.layout.reqmts]/18) props.strideness = true; // assumption diff --git a/tests/std/test.lst b/tests/std/test.lst index ccec8ad719..6faeebe1b4 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -246,6 +246,7 @@ tests\P0009R18_mdspan_layout_right_death tests\P0009R18_mdspan_layout_stride tests\P0009R18_mdspan_layout_stride_death tests\P0009R18_mdspan_mdspan +tests\P0009R18_mdspan_mdspan_death tests\P0019R8_atomic_ref tests\P0024R2_parallel_algorithms_adjacent_difference tests\P0024R2_parallel_algorithms_adjacent_find diff --git a/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp b/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp index 78e3e9ad2a..4a506e77c0 100644 --- a/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp @@ -54,6 +54,13 @@ void test_construction_from_pack_with_unrepresentable_as_index_type_values_2() { [[maybe_unused]] extents e{ConvertibleToInt{.val = 1}, 256}; } +void test_construction_from_pack_with_unrepresentable_as_index_type_values_3() { + static_assert(signed_integral, "char is not signed integral"); + // Either sizeof...(exts) must be equal to 0 or each element of exts must be nonnegative and must be representable + // as value of type index_type + [[maybe_unused]] extents e{static_cast(-1)}; +} + void test_construction_from_span_with_invalid_values() { int vals[] = {1, 2}; span s{vals}; @@ -93,6 +100,7 @@ int main(int argc, char* argv[]) { test_construction_from_pack_with_invalid_values, test_construction_from_pack_with_unrepresentable_as_index_type_values_1, test_construction_from_pack_with_unrepresentable_as_index_type_values_2, + test_construction_from_pack_with_unrepresentable_as_index_type_values_3, test_construction_from_span_with_invalid_values, test_construction_from_span_with_unrepresentable_as_index_type_values, test_construction_from_array_with_invalid_values, diff --git a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp index 9946cf40f8..be1ae79656 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp @@ -161,7 +161,7 @@ constexpr void check_members(const extents& ext, index_se void check_mapping_properties() { if constexpr (!is_permissive) { - auto check = []([[maybe_unused]] const auto& mapping) { + auto check = [](const auto& mapping) { const auto props = get_mapping_properties(mapping); assert(props.req_span_size == mapping.required_span_size()); assert(props.uniqueness); @@ -465,6 +465,9 @@ constexpr void check_correctness() { #endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ } +#ifdef __clang__ + if (!is_constant_evaluated()) // FIXME clang hits constexpr limit here +#endif { // 2x3x2x3 tensor const array values{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35}; @@ -491,10 +494,7 @@ constexpr void check_correctness() { } constexpr bool test() { - check_members_with_various_extents( - [](const extents& ext) { - check_members(ext, make_index_sequence{}); - }); + check_members_with_various_extents([](const E& e) { check_members(e, make_index_sequence{}); }); if (!is_constant_evaluated()) { // too heavy for compile time check_mapping_properties(); } diff --git a/tests/std/tests/P0009R18_mdspan_layout_left_death/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_left_death/test.cpp index 4ffb2d8d6b..ae12da2142 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_left_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_left_death/test.cpp @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#define _CONTAINER_DEBUG_LEVEL 1 + #include #include #include @@ -10,7 +12,17 @@ using namespace std; -// TRANSITION, Test Construction From extents_type +void test_construction_from_extents_type_with_signed_index_type() { + using Ext = dextents; + // The size of the multidimensional index space e must be representable as a value of type index_type + [[maybe_unused]] layout_left::mapping m{Ext{5, 4, 7}}; +} + +void test_construction_from_extents_type_with_unsigned_index_type() { + using Ext = dextents; + // The size of the multidimensional index space e must be representable as a value of type index_type + [[maybe_unused]] layout_left::mapping m{Ext{5, 10, 6}}; +} void test_construction_from_other_left_mapping() { layout_left::mapping> m1{dextents{256}}; @@ -26,7 +38,7 @@ void test_construction_from_other_right_mapping() { void test_construction_from_other_stride_mapping_1() { using Ext = extents; - layout_stride::mapping m1{Ext{}, array{1, 1}}; + layout_stride::mapping m1{Ext{}, array{4, 1}}; // For all r in the range [0, extents_type::rank()), other.stride(r) must be equal to // extents().fwd-prod-of-extents(r) layout_left::mapping m2{m1}; @@ -53,7 +65,8 @@ void test_stride_function() { int main(int argc, char* argv[]) { std_testing::death_test_executive exec; exec.add_death_tests({ - // TRANSITION Construction From extents_type + test_construction_from_extents_type_with_signed_index_type, + test_construction_from_extents_type_with_unsigned_index_type, test_construction_from_other_left_mapping, test_construction_from_other_right_mapping, test_construction_from_other_stride_mapping_1, diff --git a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp index a5e58742cb..f7d9f80137 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp @@ -33,6 +33,9 @@ constexpr void check_members(const extents& ext, index_se static_assert(same_as); static_assert(same_as); +#ifdef __clang__ + if (!is_constant_evaluated()) // FIXME clang hits constexpr limit here +#endif { // Check default and copy constructor const Mapping m; Mapping cpy = m; @@ -160,7 +163,7 @@ constexpr void check_members(const extents& ext, index_se void check_mapping_properties() { if constexpr (!is_permissive) { - auto check = []([[maybe_unused]] const auto& mapping) { + auto check = [](const auto& mapping) { const auto props = get_mapping_properties(mapping); assert(props.req_span_size == mapping.required_span_size()); assert(props.uniqueness); @@ -514,10 +517,7 @@ constexpr void check_correctness() { } constexpr bool test() { - check_members_with_various_extents( - [](const extents& ext) { - check_members(ext, make_index_sequence{}); - }); + check_members_with_various_extents([](const E& e) { check_members(e, make_index_sequence{}); }); if (!is_constant_evaluated()) { // too heavy for compile time check_mapping_properties(); } diff --git a/tests/std/tests/P0009R18_mdspan_layout_right_death/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_right_death/test.cpp index 254ea56580..4e643353f1 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_right_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_right_death/test.cpp @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#define _CONTAINER_DEBUG_LEVEL 1 + #include #include #include @@ -10,7 +12,17 @@ using namespace std; -// TRANSITION, Test Construction From extents_type +void test_construction_from_extents_type_with_signed_index_type() { + using Ext = dextents; + // The size of the multidimensional index space e must be representable as a value of type index_type + [[maybe_unused]] layout_right::mapping m{Ext{5, 4, 7}}; +} + +void test_construction_from_extents_type_with_unsigned_index_type() { + using Ext = dextents; + // The size of the multidimensional index space e must be representable as a value of type index_type + [[maybe_unused]] layout_right::mapping m{Ext{5, 10, 6}}; +} void test_construction_from_other_right_mapping() { layout_right::mapping> m1{dextents{256}}; @@ -26,7 +38,7 @@ void test_construction_from_other_left_mapping() { void test_construction_from_other_stride_mapping_1() { using Ext = extents; - layout_stride::mapping m1{Ext{}, array{3, 1}}; + layout_stride::mapping m1{Ext{}, array{1, 2}}; // For all r in the range [0, extents_type::rank()), other.stride(r) must be equal to // extents().rev-prod-of-extents(r) layout_right::mapping m2{m1}; @@ -53,7 +65,8 @@ void test_stride_function() { int main(int argc, char* argv[]) { std_testing::death_test_executive exec; exec.add_death_tests({ - // TRANSITION Construction From extents_type + test_construction_from_extents_type_with_signed_index_type, + test_construction_from_extents_type_with_unsigned_index_type, test_construction_from_other_right_mapping, test_construction_from_other_left_mapping, test_construction_from_other_stride_mapping_1, diff --git a/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp index 2ad0eda002..c8a0b9b1d1 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp @@ -1,24 +1,49 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#define _CONTAINER_DEBUG_LEVEL 1 + #include #include #include #include #include +#include using namespace std; -void test_construction_from_extents_and_array() { +void test_default_construction() { + using Ext = extents; + // Value of layout_right::mapping().required_span_size() must be + // representable as a value of type index_type + [[maybe_unused]] layout_stride::mapping m{}; // NB: strides are [140, 35, 7, 1] +} + +void test_construction_from_extents_and_array_1() { // Value of s[i] must be greater than 0 for all i in the range [0, rank_) [[maybe_unused]] layout_stride::mapping> m1{extents{}, array{-1}}; } -void test_construction_from_extents_and_span() { +void test_construction_from_extents_and_array_2() { + using Ext = extents; + // REQUIRED-SPAN-SIZE(e, s) must be representable as a value of type index_type + [[maybe_unused]] layout_stride::mapping m{Ext{}, array{2}}; +} + + +void test_construction_from_extents_and_span_1() { array s{-1}; // Value of s[i] must be greater than 0 for all i in the range [0, rank_) - [[maybe_unused]] layout_stride::mapping> m1{extents{}, span{s}}; + [[maybe_unused]] layout_stride::mapping> m{extents{}, span{s}}; +} + +void test_construction_from_extents_and_span_2() { + using Ext = extents; + array, 1> a{{{.val = 2}}}; + const span s{a}; + // REQUIRED-SPAN-SIZE(e, s) must be representable as a value of type index_type + [[maybe_unused]] layout_stride::mapping m{Ext{}, s}; } void test_construction_from_strided_layout_mapping() { @@ -36,9 +61,11 @@ void test_call_operator() { int main(int argc, char* argv[]) { std_testing::death_test_executive exec; exec.add_death_tests({ - // TRANSITION more tests - test_construction_from_extents_and_array, - test_construction_from_extents_and_span, + test_default_construction, + test_construction_from_extents_and_array_1, + test_construction_from_extents_and_array_2, + test_construction_from_extents_and_span_1, + test_construction_from_extents_and_span_2, test_construction_from_strided_layout_mapping, test_call_operator, }); diff --git a/tests/std/tests/P0009R18_mdspan_mdspan_death/env.lst b/tests/std/tests/P0009R18_mdspan_mdspan_death/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_mdspan_death/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P0009R18_mdspan_mdspan_death/test.cpp b/tests/std/tests/P0009R18_mdspan_mdspan_death/test.cpp new file mode 100644 index 0000000000..76398b5cc5 --- /dev/null +++ b/tests/std/tests/P0009R18_mdspan_mdspan_death/test.cpp @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#define _CONTAINER_DEBUG_LEVEL 1 + +#include +#include + +#include + +using namespace std; + +constexpr array some_ints{}; + +void test_construction_from_other_mdspan() { + auto mds1 = mdspan{some_ints.data(), 8, 2, 8}; + // For each rank index r of extents_type, static_extent(r) == dynamic_extent || static_extent(r) == other.extent(r) + // must be true + [[maybe_unused]] mdspan> mds2{mds1}; +} + +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 +void test_access_with_invalid_multidimensional_index_1() { + auto mds = mdspan{some_ints.data(), 4, 4}; + // I must be a multidimensional index in extents() + (void) mds[3, 4]; +} +#endif // __cpp_multidimensional_subscript + +void test_access_with_invalid_multidimensional_index_2() { + auto mds = mdspan{some_ints.data(), 5, 5}; + // I must be a multidimensional index in extents() + (void) mds[array{4, 5}]; +} + +void test_size_when_index_type_is_signed() { + auto mds = mdspan{some_ints.data(), dextents{8, 8, 4}}; + // The size of the multidimensional index space extents() must be representable as a value of type size_type + (void) mds.size(); +} + +void test_size_when_index_type_is_unsigned() { + auto mds = mdspan{some_ints.data(), dextents{8, 8, 4}}; + // The size of the multidimensional index space extents() must be representable as a value of type size_type + (void) mds.size(); +} + +int main(int argc, char* argv[]) { + std_testing::death_test_executive exec; + exec.add_death_tests({ + test_construction_from_other_mdspan, +#ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 + test_access_with_invalid_multidimensional_index_1, +#endif // __cpp_multidimensional_subscript + test_access_with_invalid_multidimensional_index_2, + test_size_when_index_type_is_signed, + test_size_when_index_type_is_unsigned, + }); + return exec.run(argc, argv); +} From 09b128673c148deed15b38b8befcd0b738a34b7c Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 4 Jul 2023 00:25:00 +0200 Subject: [PATCH 33/57] ``: Finish `layout_stride`'s tests (#3829) --- tests/std/include/test_mdspan_support.hpp | 3 +- .../P0009R18_mdspan_layout_stride/test.cpp | 145 +++++++++++++++++- 2 files changed, 143 insertions(+), 5 deletions(-) diff --git a/tests/std/include/test_mdspan_support.hpp b/tests/std/include/test_mdspan_support.hpp index 60b9729e78..80ac1e4daf 100644 --- a/tests/std/include/test_mdspan_support.hpp +++ b/tests/std/include/test_mdspan_support.hpp @@ -302,7 +302,8 @@ MappingProperties get_mapping_properties(const Mapping& mapping) { std::optional sr; for (auto i : multidim_indices) { const auto i_plus_dr = [&](std::index_sequence) { - return std::array{static_cast(std::get(i) + (Indices == r ? 1 : 0))...}; + return std::array{ + static_cast(std::get(i) + (Indices == r ? 1 : 0))...}; }(rank_indices); if (i_plus_dr[r] < get_extent(r)) { diff --git a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp index 4c39707b54..ab20ad5c87 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -21,6 +22,82 @@ struct CmpEqual { } }; +struct NotLayoutMappingAlikeAtAll { + template + class mapping : public layout_right::mapping { + public: + using layout_type = NotLayoutMappingAlikeAtAll; + + private: + using layout_right::mapping::is_always_exhaustive; + }; +}; + +static_assert(!_Layout_mapping_alike>>); + +enum class AlwaysUnique { no, yes }; +enum class AlwaysStrided { no, yes }; + +template +struct LyingLayout { + template + class mapping : public layout_left::mapping { + public: + using layout_type = LyingLayout; + + constexpr bool is_unique() const { + return is_unique(); + } + + constexpr bool is_exhaustive() const { + return is_exhaustive(); + } + + constexpr bool is_strided() const { + return is_strided(); + } + + static constexpr bool is_always_unique() { + return to_underlying( + Unique); // might be a lie, allowed by the standard (N4950 [mdspan.layout.reqmts]/23 Note 5) + } + + static constexpr bool is_always_exhaustive() { + return layout_right::mapping::is_always_exhaustive(); + } + + static constexpr bool is_always_strided() { + return to_underlying( + Strided); // might be a lie, allowed by the standard (N4950 [mdspan.layout.reqmts]/27 Note 7) + } + }; +}; + +static_assert( + check_layout_mapping_policy_requirements, extents>()); +static_assert( + check_layout_mapping_policy_requirements, dextents>()); + +struct HollowLayout { + template + requires (Extents::rank() == 0) + class mapping : public layout_right::mapping { + public: + using index_type = Extents::index_type; + using layout_type = HollowLayout; + + constexpr index_type operator()() const noexcept { + return 1; // NB: used by 'check_comparisons' (OFFSET(*this) != 0) + } + + constexpr index_type required_span_size() const noexcept { + return 2; + } + }; +}; + +static_assert(check_layout_mapping_policy_requirements>()); + template constexpr void do_check_members(const extents& ext, const array& strs, index_sequence) { @@ -263,16 +340,24 @@ constexpr void check_construction_from_other_mappings() { static_assert(is_nothrow_constructible_v>>); static_assert(is_nothrow_constructible_v>>); static_assert(is_nothrow_constructible_v>>); + static_assert(is_nothrow_constructible_v::mapping>>); } { // Check invalid construction using Mapping = layout_stride::mapping>; + static_assert(!is_constructible_v>>); static_assert(!is_constructible_v>>); static_assert(!is_constructible_v>>); static_assert(!is_constructible_v>>); static_assert(!is_constructible_v>>); static_assert(!is_constructible_v>>); - // TRANSITION, Check other kinds of invalid construction (requires new helper types) + static_assert(!is_constructible_v::mapping>>); + static_assert(!is_constructible_v::mapping>>); + static_assert(!is_constructible_v::mapping>>); } { // Check construction from layout_left::mapping @@ -439,17 +524,29 @@ constexpr void check_comparisons() { using RightMapping = layout_right::mapping; using LeftMapping = layout_left::mapping; - { // Check equality_comparable_with concept + { // Check equality_comparable_with concept (correct comparisons) static_assert(equality_comparable_with); static_assert(equality_comparable_with); static_assert(equality_comparable_with); static_assert(equality_comparable_with); static_assert(equality_comparable_with); + } + + { // Check equality_comparable_with concept (incorrect comparisons) + static_assert( + !equality_comparable_with>>); static_assert(!equality_comparable_with>>); static_assert(!equality_comparable_with>>); static_assert(!equality_comparable_with>>); static_assert(!equality_comparable_with>>); - // TRANSITION, Check other constraints: N4950 [mdspan.layout.stride.obs]/6.1, 6.3 + static_assert(!equality_comparable_with::mapping>>); + static_assert(!equality_comparable_with::mapping>>); + static_assert(!equality_comparable_with::mapping>>); + static_assert(!equality_comparable_with::mapping>>); } { // Check correctness: layout_stride::mapping with layout_stride::mapping @@ -515,7 +612,47 @@ constexpr void check_comparisons() { static_assert(noexcept(m1 != m3)); } - // TRANSITION, Check comparisons with custom layout mapping + { // Check correctness: layout_stride::mapping with LyingLayout::mapping + using CustomMapping = LyingLayout::mapping; + CustomMapping m1; + StaticStrideMapping m2{E{}, array{1, 2}}; + same_as decltype(auto) cond = m1 == m2; + assert(cond); // extents are equal, OFFSET(rhs) == 0, strides are equal + + DynamicStrideMapping m3{dextents{2, 3}, array{3, 1}}; + assert(m1 != m3); // extents are equal, OFFSET(rhs) == 0, strides are not equal + assert(m2 != m3); // ditto + + DynamicStrideMapping m4{dextents{1, 3}, array{1, 2}}; + assert(m1 != m4); // extents are not equal, OFFSET(rhs) == 0, strides are equal + assert(m2 != m4); // ditto + assert(m3 != m4); // extents are not equal, OFFSET(rhs) == 0, strides are not equal + + // NB: OFFSET(CustomMapping) is always equal to 0 + + static_assert(noexcept(m1 == m2)); + static_assert(noexcept(m1 != m3)); + } + + { // Check correctness: layout_stride::mapping with HollowLayout::mapping + HollowLayout::mapping> m1; + if constexpr (!is_permissive) { + if (!is_constant_evaluated()) { // too heavy for compile time + const auto props = get_mapping_properties(m1); + assert(props.req_span_size == m1.required_span_size()); + assert(props.uniqueness); + assert(props.exhaustiveness); + assert(props.strideness); + } + } + + layout_stride::mapping> m2; + same_as decltype(auto) cond = m1 == m2; + assert(!cond); // extents are equal, OFFSET(rhs) != 0, strides are equal + + static_assert(noexcept(m1 == m2)); + static_assert(noexcept(m1 != m2)); + } } constexpr void check_ctad() { From 2a12d44e44e89f98b12ef7dd999f609ce8245f58 Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Tue, 4 Jul 2023 01:02:54 +0200 Subject: [PATCH 34/57] ``: Make `extents` truly empty when `rank_dynamic() == 0` (#3825) --- stl/inc/mdspan | 38 +++++++++++-------- .../tests/P0009R18_mdspan_extents/test.cpp | 6 +++ 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 332cf5ff9d..c8a1669c2b 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -25,9 +25,19 @@ _STL_DISABLE_CLANG_WARNINGS #undef new _STD_BEGIN +template +struct _Maybe_empty_array { + array<_IndexType, _Size> _Array{}; +}; + +template +struct _Maybe_empty_array<_IndexType, 0> {}; + +template +inline constexpr size_t _Calculate_rank_dynamic = ((_Extents == dynamic_extent) + ... + 0); _EXPORT_STD template -class extents { +class extents : private _Maybe_empty_array<_IndexType, _Calculate_rank_dynamic<_Extents...>> { public: using index_type = _IndexType; using size_type = make_unsigned_t; @@ -39,8 +49,8 @@ public: "Each element of Extents must be either equal to dynamic_extent, or must be representable as a value of type " "IndexType (N4950 [mdspan.extents.overview]/1.2)."); - static constexpr rank_type _Rank = sizeof...(_Extents); - static constexpr rank_type _Rank_dynamic = (static_cast(_Extents == dynamic_extent) + ... + 0); + static constexpr rank_type _Rank = sizeof...(_Extents); + static constexpr rank_type _Rank_dynamic = _Calculate_rank_dynamic<_Extents...>; static constexpr array _Static_extents = {_Extents...}; static constexpr bool _Multidim_index_space_size_is_always_zero = ((_Extents == 0) || ...); @@ -76,14 +86,10 @@ 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; + explicit _Construct_from_tuple() = default; }; - conditional_t<_Rank_dynamic != 0, array, _Static_extents_only> _Dynamic_extents{}; + using _Base = _Maybe_empty_array<_IndexType, _Rank_dynamic>; public: _NODISCARD static constexpr rank_type rank() noexcept { @@ -108,10 +114,10 @@ public: if constexpr (rank_dynamic() == 0) { return static_cast(_Static_extents[_Idx]); } else if constexpr (rank_dynamic() == rank()) { - return _Dynamic_extents[_Idx]; + return this->_Array[_Idx]; } else { if (_Static_extents[_Idx] == dynamic_extent) { - return _Dynamic_extents[_Dynamic_indices[_Idx]]; + return this->_Array[_Dynamic_indices[_Idx]]; } else { return static_cast(_Static_extents[_Idx]); } @@ -125,7 +131,7 @@ public: && ((_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]))...} { + : _Base{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) { @@ -154,12 +160,12 @@ public: 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)))...} {} + : _Base{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)))...} { + : _Base{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 @@ -199,7 +205,7 @@ public: requires is_convertible_v && 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]))...} { + : _Base{static_cast(_STD as_const(_Dynamic_exts[_Indices]))...} { #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (_Is_standard_integer<_OtherIndexType> && _Size != 0) { _STL_VERIFY(((_Dynamic_exts[_Indices] >= 0 && _STD in_range(_Dynamic_exts[_Indices])) && ...), @@ -213,7 +219,7 @@ public: requires is_convertible_v && 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]]))...} { + : _Base{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) { diff --git a/tests/std/tests/P0009R18_mdspan_extents/test.cpp b/tests/std/tests/P0009R18_mdspan_extents/test.cpp index 3ff5424078..58fd6ff5ac 100644 --- a/tests/std/tests/P0009R18_mdspan_extents/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_extents/test.cpp @@ -546,6 +546,12 @@ static_assert(all_extents_dynamic, 7>); static_assert(all_extents_dynamic, 8>); static_assert(all_extents_dynamic, 9>); +// When 'E::rank_dynamic()' is equal to 0 then 'is_empty_v' should be true (MSVC STL specific behavior) +static_assert(!is_empty_v>); +static_assert(!is_empty_v>); +static_assert(is_empty_v>); +static_assert(is_empty_v>); + int main() { static_assert(test()); test(); From 7943441b130148f1b76ae26539d48ebc95758b5f Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Fri, 7 Jul 2023 00:26:58 +0200 Subject: [PATCH 35/57] ``: More EBO (#3847) --- stl/inc/mdspan | 287 +++++++++++++----- .../test.compile.pass.cpp | 5 + .../P0009R18_mdspan_default_accessor/test.cpp | 3 + .../P0009R18_mdspan_layout_left/test.cpp | 13 + .../P0009R18_mdspan_layout_right/test.cpp | 6 + .../P0009R18_mdspan_layout_stride/test.cpp | 6 + .../std/tests/P0009R18_mdspan_mdspan/test.cpp | 99 ++++-- 7 files changed, 309 insertions(+), 110 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index c8a1669c2b..2fad249ec3 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -24,6 +24,10 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new +// TRANSITION, non-_Ugly attribute tokens +#pragma push_macro("empty_bases") +#undef empty_bases + _STD_BEGIN template struct _Maybe_empty_array { @@ -34,7 +38,7 @@ template struct _Maybe_empty_array<_IndexType, 0> {}; template -inline constexpr size_t _Calculate_rank_dynamic = ((_Extents == dynamic_extent) + ... + 0); +inline constexpr size_t _Calculate_rank_dynamic = (static_cast(_Extents == dynamic_extent) + ... + 0); _EXPORT_STD template class extents : private _Maybe_empty_array<_IndexType, _Calculate_rank_dynamic<_Extents...>> { @@ -437,7 +441,36 @@ _EXPORT_STD struct layout_stride { }; template -class layout_left::mapping { +struct _Maybe_fully_static_extents { + _STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>); + + constexpr _Maybe_fully_static_extents() noexcept = default; + + template + constexpr explicit _Maybe_fully_static_extents(const _OtherExtents& _Exts_) : _Exts(_Exts_) {} + + _Extents _Exts{}; +}; + +template + requires (_Extents::rank_dynamic() == 0) +struct _Maybe_fully_static_extents<_Extents> { + _STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>); + + constexpr _Maybe_fully_static_extents() noexcept = default; + + template + constexpr explicit _Maybe_fully_static_extents([[maybe_unused]] const _OtherExtents& _Exts_) { +#if _CONTAINER_DEBUG_LEVEL > 0 + (void) _Extents{_Exts_}; // NB: temporary created for preconditions check +#endif // _CONTAINER_DEBUG_LEVEL > 0 + } + + static constexpr _Extents _Exts{}; +}; + +template +class layout_left::mapping : private _Maybe_fully_static_extents<_Extents> { public: using extents_type = _Extents; using index_type = extents_type::index_type; @@ -445,6 +478,9 @@ public: using rank_type = extents_type::rank_type; using layout_type = layout_left; +private: + using _Base = _Maybe_fully_static_extents; + static_assert(_Is_extents, "Extents must be a specialization of std::extents (N4950 [mdspan.layout.left.overview]/2)."); static_assert( @@ -452,10 +488,11 @@ public: "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)."); +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 : _Base(_Exts_) { #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (extents_type::rank_dynamic() != 0) { _STL_VERIFY(_Exts_._Is_dynamic_multidim_index_space_size_representable(), @@ -469,7 +506,7 @@ public: requires is_constructible_v constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const mapping<_OtherExtents>& _Other) noexcept - : _Exts(_Other.extents()) { + : _Base(_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 " @@ -481,7 +518,7 @@ public: 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()) { + : _Base(_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 " @@ -492,7 +529,7 @@ public: template requires is_constructible_v constexpr explicit(extents_type::rank() > 0) mapping(const layout_stride::template mapping<_OtherExtents>& _Other) - : _Exts(_Other.extents()) { + : _Base(_Other.extents()) { #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (extents_type::rank() > 0) { index_type _Prod = 1; @@ -500,7 +537,7 @@ public: _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)); + _Prod = static_cast(_Prod * this->_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 " @@ -512,11 +549,11 @@ public: constexpr mapping& operator=(const mapping&) noexcept = default; _NODISCARD constexpr const extents_type& extents() const noexcept { - return _Exts; + return this->_Exts; } _NODISCARD constexpr index_type required_span_size() const noexcept { - return _Fwd_prod_of_extents::_Calculate(_Exts, extents_type::_Rank); + return _Fwd_prod_of_extents::_Calculate(this->_Exts, extents_type::_Rank); } template @@ -557,7 +594,7 @@ public: _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); + return _Fwd_prod_of_extents::_Calculate(this->_Exts, _Idx); } template @@ -567,27 +604,25 @@ public: } private: - extents_type _Exts{}; - template _NODISCARD constexpr index_type _Index_impl( [[maybe_unused]] index_sequence<_Seq...> _Index_seq, _IndexTypes... _Indices) const noexcept { _STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...)); #if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Exts._Contains_multidimensional_index(_Index_seq, _Indices...), + _STL_VERIFY(this->_Exts._Contains_multidimensional_index(_Index_seq, _Indices...), "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " "[mdspan.layout.left.obs]/3)."); #endif // _CONTAINER_DEBUG_LEVEL > 0 index_type _Stride = 1; index_type _Result = 0; - (((_Result += _Indices * _Stride), (_Stride *= _Exts.extent(_Seq))), ...); + (((_Result += _Indices * _Stride), (_Stride *= this->_Exts.extent(_Seq))), ...); return _Result; } }; template -class layout_right::mapping { +class layout_right::mapping : private _Maybe_fully_static_extents<_Extents> { public: using extents_type = _Extents; using index_type = extents_type::index_type; @@ -595,6 +630,9 @@ public: using rank_type = extents_type::rank_type; using layout_type = layout_right; +private: + using _Base = _Maybe_fully_static_extents; + static_assert(_Is_extents, "Extents must be a specialization of std::extents (N4950 [mdspan.layout.right.overview]/2)."); static_assert( @@ -602,10 +640,11 @@ public: "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)."); +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 : _Base(_Exts_) { #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (extents_type::rank_dynamic() != 0) { _STL_VERIFY(_Exts_._Is_dynamic_multidim_index_space_size_representable(), @@ -619,7 +658,7 @@ public: requires is_constructible_v constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const mapping<_OtherExtents>& _Other) noexcept - : _Exts(_Other.extents()) { + : _Base(_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 " @@ -631,7 +670,7 @@ public: requires (extents_type::rank() <= 1) && is_constructible_v constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const layout_left::mapping<_OtherExtents>& _Other) noexcept - : _Exts(_Other.extents()) { + : _Base(_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 " @@ -643,7 +682,7 @@ public: requires is_constructible_v constexpr explicit(extents_type::rank() > 0) mapping(const layout_stride::template mapping<_OtherExtents>& _Other) noexcept - : _Exts(_Other.extents()) { + : _Base(_Other.extents()) { #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (extents_type::rank() > 0) { index_type _Prod = 1; @@ -651,7 +690,7 @@ public: _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)); + _Prod = static_cast(_Prod * this->_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 " @@ -663,11 +702,11 @@ public: constexpr mapping& operator=(const mapping&) noexcept = default; _NODISCARD constexpr const extents_type& extents() const noexcept { - return _Exts; + return this->_Exts; } _NODISCARD constexpr index_type required_span_size() const noexcept { - return _Fwd_prod_of_extents::_Calculate(_Exts, extents_type::_Rank); + return _Fwd_prod_of_extents::_Calculate(this->_Exts, extents_type::_Rank); } template @@ -708,30 +747,28 @@ public: _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); + return _Rev_prod_of_extents::_Calculate(this->_Exts, _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 _NODISCARD constexpr index_type _Index_impl( [[maybe_unused]] index_sequence<_Seq...> _Index_seq, _IndexTypes... _Indices) const noexcept { _STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...)); #if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Exts._Contains_multidimensional_index(_Index_seq, _Indices...), + _STL_VERIFY(this->_Exts._Contains_multidimensional_index(_Index_seq, _Indices...), "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " "[mdspan.layout.right.obs]/3)."); #endif // _CONTAINER_DEBUG_LEVEL > 0 index_type _Result = 0; - ((_Result = static_cast(_Indices + _Exts.extent(_Seq) * _Result)), ...); + ((_Result = static_cast(_Indices + this->_Exts.extent(_Seq) * _Result)), ...); return _Result; } }; @@ -748,7 +785,8 @@ concept _Layout_mapping_alike = requires { }; template -class layout_stride::mapping { +class layout_stride::mapping : private _Maybe_fully_static_extents<_Extents>, + private _Maybe_empty_array { public: using extents_type = _Extents; using index_type = extents_type::index_type; @@ -756,6 +794,10 @@ public: using rank_type = extents_type::rank_type; using layout_type = layout_stride; +private: + using _Extents_base = _Maybe_fully_static_extents; + using _Strides_base = _Maybe_empty_array; + static_assert(_Is_extents, "Extents must be a specialization of std::extents (N4950 [mdspan.layout.stride.overview]/2)."); static_assert( @@ -763,12 +805,14 @@ public: "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)."); - constexpr mapping() noexcept : _Exts(extents_type{}) { +public: + constexpr mapping() noexcept : _Extents_base(extents_type{}) { if constexpr (extents_type::rank() != 0) { - _Strides.back() = 1; + this->_Array.back() = 1; 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]); + const bool _Overflow = + _Mul_overflow(this->_Array[_Idx + 1], this->_Exts.extent(_Idx + 1), this->_Array[_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. @@ -776,7 +820,7 @@ public: "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)); + this->_Array[_Idx] = static_cast(this->_Array[_Idx + 1] * this->_Exts.extent(_Idx + 1)); #endif // _CONTAINER_DEBUG_LEVEL > 0 } } @@ -789,17 +833,17 @@ public: && is_nothrow_constructible_v 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]))...} { + : _Extents_base(_Exts_), _Strides_base{static_cast(_STD as_const(_Strides_[_Indices]))...} { #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (extents_type::rank() != 0) { bool _Found_zero = false; bool _Overflow = false; index_type _Req_span_size = 0; for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { - const index_type _Stride = _Strides[_Idx]; + const index_type _Stride = this->_Array[_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); + const index_type _Ext = this->_Exts.extent(_Idx); if (_Ext == 0) { _Found_zero = true; } @@ -852,7 +896,7 @@ public: && (_Is_mapping_of || _Is_mapping_of || _Is_mapping_of) )) mapping(const _StridedLayoutMapping& _Other) noexcept - : _Exts(_Other.extents()) { + : _Extents_base(_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 " @@ -866,18 +910,18 @@ public: _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); + this->_Array[_Idx] = static_cast(_Stride); } } constexpr mapping& operator=(const mapping&) noexcept = default; _NODISCARD constexpr const extents_type& extents() const noexcept { - return _Exts; + return this->_Exts; } _NODISCARD constexpr array strides() const noexcept { - return _Strides; + return this->_Array; } _NODISCARD constexpr index_type required_span_size() const noexcept { @@ -886,12 +930,12 @@ public: } else { index_type _Result = 1; for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { - const index_type _Ext = _Exts.extent(_Idx); + const index_type _Ext = this->_Exts.extent(_Idx); if (_Ext == 0) { return 0; } - _Result += (_Ext - 1) * _Strides[_Idx]; + _Result += (_Ext - 1) * this->_Array[_Idx]; } return _Result; @@ -925,7 +969,8 @@ public: if constexpr (extents_type::rank() == 0) { return true; } else { - return required_span_size() == _Fwd_prod_of_extents::_Calculate(_Exts, extents_type::_Rank); + return required_span_size() + == _Fwd_prod_of_extents::_Calculate(this->_Exts, extents_type::_Rank); } } @@ -934,7 +979,7 @@ public: } _NODISCARD constexpr index_type stride(const rank_type _Idx) const noexcept { - return _Strides[_Idx]; + return this->_Array[_Idx]; } template @@ -957,9 +1002,6 @@ public: } private: - extents_type _Exts{}; - array _Strides{}; - template _NODISCARD static constexpr _OtherMapping::index_type _Offset(_OtherMapping& _Mapping) noexcept { if constexpr (extents_type::rank() == 0) { @@ -981,12 +1023,12 @@ private: [[maybe_unused]] index_sequence<_Seq...> _Index_seq, _IndexTypes... _Indices) const noexcept { _STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...)); #if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Exts._Contains_multidimensional_index(_Index_seq, _Indices...), + _STL_VERIFY(this->_Exts._Contains_multidimensional_index(_Index_seq, _Indices...), "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " "[mdspan.layout.stride.obs]/3)."); #endif // _CONTAINER_DEBUG_LEVEL > 0 - return static_cast(((_Indices * _Strides[_Seq]) + ... + 0)); + return static_cast(((_Indices * this->_Array[_Seq]) + ... + 0)); } }; @@ -1019,9 +1061,81 @@ struct default_accessor { } }; +template +concept _Elidable_layout_mapping = + (_Is_any_of_v<_LayoutPolicy, layout_left, layout_right> && _Extents::rank_dynamic() == 0) + || (same_as<_LayoutPolicy, layout_stride> && _Extents::rank() == 0); + +template +struct _Mdspan_mapping_base { + _STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>); + + using _Mapping = _LayoutPolicy::template mapping<_Extents>; + + constexpr _Mdspan_mapping_base() noexcept = default; + + constexpr explicit _Mdspan_mapping_base(const _Extents& _Exts) : _Map(_Exts) {} + + template + constexpr explicit _Mdspan_mapping_base(const _OtherMapping& _Map_) : _Map(_Map_) {} + + _Mapping _Map{}; +}; + +template _LayoutPolicy> +struct _Mdspan_mapping_base<_Extents, _LayoutPolicy> { + _STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>); + + using _Mapping = _LayoutPolicy::template mapping<_Extents>; + + constexpr _Mdspan_mapping_base() noexcept = default; + + constexpr explicit _Mdspan_mapping_base(const _Extents&) noexcept {} + + template + constexpr explicit _Mdspan_mapping_base(const _OtherMapping& _Map_) { + // NB: Constructing _Mapping from _OtherMapping may have side effects - we should create a temporary. + if constexpr (!_Elidable_layout_mapping) { + (void) _Mapping{_Map_}; + } + } + + static constexpr _Mapping _Map{}; +}; + +template +concept _Elidable_accessor_policy = _Is_specialization_v<_AccessorPolicy, default_accessor>; + +template +struct _Mdspan_accessor_base { + constexpr _Mdspan_accessor_base() noexcept = default; + + template + constexpr explicit _Mdspan_accessor_base(const _OtherAccessorPolicy& _Acc_) : _Acc(_Acc_) {} + + _AccessorPolicy _Acc{}; +}; + +template <_Elidable_accessor_policy _AccessorPolicy> +struct _Mdspan_accessor_base<_AccessorPolicy> { + constexpr _Mdspan_accessor_base() noexcept = default; + + template + constexpr explicit _Mdspan_accessor_base(const _OtherAccessorPolicy& _Acc_) { + // NB: Constructing _AccessorPolicy from _OtherAccessorPolicy may have side effects - we should create a + // temporary. + if constexpr (!_Elidable_accessor_policy<_OtherAccessorPolicy>) { + (void) _AccessorPolicy{_Acc_}; + } + } + + static constexpr _AccessorPolicy _Acc{}; +}; + _EXPORT_STD template > -class mdspan { +class __declspec(empty_bases) mdspan : private _Mdspan_mapping_base<_Extents, _LayoutPolicy>, + private _Mdspan_accessor_base<_AccessorPolicy> { public: using extents_type = _Extents; using layout_type = _LayoutPolicy; @@ -1035,6 +1149,10 @@ public: using data_handle_type = accessor_type::data_handle_type; using reference = accessor_type::reference; +private: + using _Mapping_base = _Mdspan_mapping_base; + using _Accessor_base = _Mdspan_accessor_base; + static_assert( sizeof(element_type) > 0, "ElementType must be a complete type (N4950 [mdspan.mdspan.overview]/2.1)."); static_assert( @@ -1047,6 +1165,7 @@ public: "ElementType and typename AccessorPolicy::element_type must be the same type (N4950 " "[mdspan.mdspan.overview]/2.3)."); +public: _NODISCARD static constexpr rank_type rank() noexcept { return extents_type::_Rank; } @@ -1063,7 +1182,7 @@ public: } _NODISCARD constexpr index_type extent(const rank_type _Idx) const noexcept { - return _Map.extents().extent(_Idx); + return this->_Map.extents().extent(_Idx); } constexpr mdspan() @@ -1080,7 +1199,8 @@ public: && (sizeof...(_OtherIndexTypes) == rank() || sizeof...(_OtherIndexTypes) == rank_dynamic()) && is_constructible_v && is_default_constructible_v constexpr explicit mdspan(data_handle_type _Ptr_, _OtherIndexTypes... _Exts) - : _Ptr(_STD move(_Ptr_)), _Map(extents_type{static_cast(_STD move(_Exts))...}), _Acc() {} + : _Mapping_base(extents_type{static_cast(_STD move(_Exts))...}), _Accessor_base(), + _Ptr(_STD move(_Ptr_)) {} template requires is_convertible_v<_OtherIndexType, index_type> @@ -1088,7 +1208,7 @@ public: && (_Size == rank() || _Size == rank_dynamic()) && is_constructible_v && is_default_constructible_v constexpr explicit(_Size != rank_dynamic()) mdspan(data_handle_type _Ptr_, span<_OtherIndexType, _Size> _Exts) - : _Ptr(_STD move(_Ptr_)), _Map(extents_type{_Exts}), _Acc() {} + : _Mapping_base(extents_type{_Exts}), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {} template requires is_convertible_v @@ -1097,18 +1217,18 @@ public: && is_constructible_v && is_default_constructible_v constexpr explicit(_Size != rank_dynamic()) mdspan(data_handle_type _Ptr_, const array<_OtherIndexType, _Size>& _Exts) - : _Ptr(_STD move(_Ptr_)), _Map(extents_type{_Exts}), _Acc() {} + : _Mapping_base(extents_type{_Exts}), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {} - constexpr mdspan(data_handle_type _Ptr_, const extents_type& _Ext) + constexpr mdspan(data_handle_type _Ptr_, const extents_type& _Exts) requires is_constructible_v && is_default_constructible_v - : _Ptr(_STD move(_Ptr_)), _Map(_Ext), _Acc() {} + : _Mapping_base(_Exts), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {} constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_) requires is_default_constructible_v - : _Ptr(_STD move(_Ptr_)), _Map(_Map_), _Acc() {} + : _Mapping_base(_Map_), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {} constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_, const accessor_type& _Acc_) - : _Ptr(_STD move(_Ptr_)), _Map(_Map_), _Acc(_Acc_) {} + : _Mapping_base(_Map_), _Accessor_base(_Acc_), _Ptr(_STD move(_Ptr_)) {} template requires is_constructible_v&> @@ -1117,7 +1237,7 @@ public: !is_convertible_v&, mapping_type> || !is_convertible_v) mdspan(const mdspan<_OtherElementType, _OtherExtents, _OtherLayoutPolicy, _OtherAccessor>& _Other) - : _Ptr(_Other.data_handle()), _Map(_Other.mapping()), _Acc(_Other.accessor()) { + : _Mapping_base(_Other.mapping()), _Accessor_base(_Other.accessor()), _Ptr(_Other.data_handle()) { static_assert(is_constructible_v, "The data_handle_type must be constructible from const typename OtherAccessor::data_handle_type& (N4950 " "[mdspan.mdspan.cons]/20.1)."); @@ -1175,20 +1295,20 @@ public: _NODISCARD constexpr size_type size() const noexcept { #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (rank_dynamic() != 0) { - _STL_VERIFY(_Map.extents().template _Is_dynamic_multidim_index_space_size_representable(), + _STL_VERIFY(this->_Map.extents().template _Is_dynamic_multidim_index_space_size_representable(), "The size of the multidimensional index space extents() must be representable as a value of type " "size_type (N4950 [mdspan.mdspan.members]/7)."); } #endif // _CONTAINER_DEBUG_LEVEL > 0 return static_cast( - _Fwd_prod_of_extents::_Calculate(_Map.extents(), extents_type::_Rank)); + _Fwd_prod_of_extents::_Calculate(this->_Map.extents(), extents_type::_Rank)); } _NODISCARD constexpr bool empty() const noexcept { if constexpr (extents_type::_Multidim_index_space_size_is_always_zero) { return true; } else { - const extents_type& _Exts = _Map.extents(); + const extents_type& _Exts = this->_Map.extents(); for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { if (_Exts.extent(_Idx) == 0) { return true; @@ -1200,12 +1320,18 @@ public: friend constexpr void swap(mdspan& _Left, mdspan& _Right) noexcept { swap(_Left._Ptr, _Right._Ptr); // intentional ADL - swap(_Left._Map, _Right._Map); // intentional ADL - swap(_Left._Acc, _Right._Acc); // intentional ADL + + if constexpr (!_Elidable_layout_mapping) { + swap(_Left._Map, _Right._Map); // intentional ADL + } + + if constexpr (!_Elidable_accessor_policy) { + swap(_Left._Acc, _Right._Acc); // intentional ADL + } } _NODISCARD constexpr const extents_type& extents() const noexcept { - return _Map.extents(); + return this->_Map.extents(); } _NODISCARD constexpr const data_handle_type& data_handle() const noexcept { @@ -1213,11 +1339,11 @@ public: } _NODISCARD constexpr const mapping_type& mapping() const noexcept { - return _Map; + return this->_Map; } _NODISCARD constexpr const accessor_type& accessor() const noexcept { - return _Acc; + return this->_Acc; } _NODISCARD static constexpr bool is_always_unique() noexcept( @@ -1235,21 +1361,21 @@ public: return mapping_type::is_always_strided(); } - _NODISCARD constexpr bool is_unique() const noexcept(noexcept(_Map.is_unique())) /* strengthened */ { - return _Map.is_unique(); + _NODISCARD constexpr bool is_unique() const noexcept(noexcept(this->_Map.is_unique())) /* strengthened */ { + return this->_Map.is_unique(); } - _NODISCARD constexpr bool is_exhaustive() const noexcept(noexcept(_Map.is_exhaustive())) /* strengthened */ { - return _Map.is_exhaustive(); + _NODISCARD constexpr bool is_exhaustive() const noexcept(noexcept(this->_Map.is_exhaustive())) /* strengthened */ { + return this->_Map.is_exhaustive(); } - _NODISCARD constexpr bool is_strided() const noexcept(noexcept(_Map.is_strided())) /* strengthened */ { - return _Map.is_strided(); + _NODISCARD constexpr bool is_strided() const noexcept(noexcept(this->_Map.is_strided())) /* strengthened */ { + return this->_Map.is_strided(); } _NODISCARD constexpr index_type stride(const rank_type _Idx) const - noexcept(noexcept(_Map.stride(_Idx))) /* strengthened */ { - return _Map.stride(_Idx); + noexcept(noexcept(this->_Map.stride(_Idx))) /* strengthened */ { + return this->_Map.stride(_Idx); } private: @@ -1264,16 +1390,14 @@ private: _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...), + _STL_VERIFY(this->_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...))); + return this->_Acc.access(_Ptr, static_cast(this->_Map(_Indices...))); } - data_handle_type _Ptr{}; - mapping_type _Map{}; - accessor_type _Acc{}; + /* [[no_unique_address]] */ data_handle_type _Ptr{}; }; template @@ -1309,6 +1433,9 @@ mdspan(const typename _AccessorType::data_handle_type&, const _MappingType&, con _STD_END +// TRANSITION, non-_Ugly attribute tokens +#pragma pop_macro("empty_bases") + #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) diff --git a/tests/std/tests/GH_002206_unreserved_names/test.compile.pass.cpp b/tests/std/tests/GH_002206_unreserved_names/test.compile.pass.cpp index d5534e2234..7bed2d659c 100644 --- a/tests/std/tests/GH_002206_unreserved_names/test.compile.pass.cpp +++ b/tests/std/tests/GH_002206_unreserved_names/test.compile.pass.cpp @@ -13,6 +13,7 @@ #define intrinsic 3 #define lifetimebound 4 #define noop_dtor 5 +#define empty_bases 6 #include <__msvc_all_public_headers.hpp> @@ -35,3 +36,7 @@ #if noop_dtor != 5 #error bad macro expansion #endif // noop_dtor != 5 + +#if empty_bases != 6 +#error bad macro expansion +#endif // noop_dtor != 6 diff --git a/tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp b/tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp index 9036a910c7..5402186a9f 100644 --- a/tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp @@ -27,6 +27,9 @@ constexpr void test_one(array elems) { static_assert(is_trivially_copyable_v); static_assert(semiregular); + // Check if default_accessor is empty + static_assert(std::is_empty_v); + // Check nested types static_assert(same_as); static_assert(same_as); diff --git a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp index be1ae79656..4c53b23742 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp @@ -423,6 +423,10 @@ constexpr void check_correctness() { #endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ } + +#ifdef __clang__ + if (!is_constant_evaluated()) // FIXME clang hits constexpr limit here +#endif { // 3x2 matrix with column-major order const array values{0, 1, 2, 3, 4, 5}; mdspan, layout_left> matrix{values.data()}; @@ -444,6 +448,9 @@ constexpr void check_correctness() { #endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ } +#ifdef __clang__ + if (!is_constant_evaluated()) // FIXME clang hits constexpr limit here +#endif { // 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}; @@ -493,6 +500,12 @@ constexpr void check_correctness() { } } +// When 'M::extents_type::rank_dynamic()' is equal to 0 then 'is_empty_v' should be true (MSVC STL specific behavior) +static_assert(!is_empty_v>>); +static_assert(!is_empty_v>>); +static_assert(is_empty_v>>); +static_assert(is_empty_v>>); + constexpr bool test() { check_members_with_various_extents([](const E& e) { check_members(e, make_index_sequence{}); }); if (!is_constant_evaluated()) { // too heavy for compile time diff --git a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp index f7d9f80137..273a461ecf 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp @@ -516,6 +516,12 @@ constexpr void check_correctness() { } } +// When 'M::extents_type::rank_dynamic()' is equal to 0 then 'is_empty_v' should be true (MSVC STL specific behavior) +static_assert(!is_empty_v>>); +static_assert(!is_empty_v>>); +static_assert(is_empty_v>>); +static_assert(is_empty_v>>); + constexpr bool test() { check_members_with_various_extents([](const E& e) { check_members(e, make_index_sequence{}); }); if (!is_constant_evaluated()) { // too heavy for compile time diff --git a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp index ab20ad5c87..13f6ea3032 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp @@ -821,6 +821,12 @@ constexpr void check_correctness() { } } +// When 'M::extents_type::rank()' is equal to 0 then 'is_empty_v' should be true (MSVC STL specific behavior) +static_assert(!is_empty_v>>); +static_assert(!is_empty_v>>); +static_assert(!is_empty_v>>); +static_assert(is_empty_v>>); + constexpr bool test() { // Check signed integers check_members(extents{5}, array{1}); diff --git a/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp b/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp index 44580c94dd..aa4915c84c 100644 --- a/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp @@ -1095,35 +1095,52 @@ constexpr void check_empty() { } constexpr void check_swap() { - using E = extents; - using Mds = mdspan, TrackingAccessor>; - static_assert(is_nothrow_swappable_v); - static_assert(!is_swappable_v); - - int a1[9] = {1, 0, 0, 0, 1, 0, 0, 0, 1}; - Mds mds1{TrackingDataHandle{1, a1}, TrackingLayout<>::mapping(1), TrackingAccessor{1}}; - int a2[9] = {3, 0, 0, 0, 3, 0, 0, 0, 3}; - Mds mds2{TrackingDataHandle{3, a2}, TrackingLayout<>::mapping(3), TrackingAccessor{3}}; - swap(mds1, mds2); - static_assert(is_void_v); - - assert(mds1.data_handle().get_id() == 3); - assert(mds1.data_handle().is_swapped()); - assert(mds1.mapping().get_id() == 3); - assert(mds1.mapping().is_swapped()); - assert(mds1.accessor().get_id() == 3); - assert(mds1.accessor().is_swapped()); - assert((mds1[array{1, 1}] == 3)); - assert((mds1[array{0, 1}] == 0)); - - assert(mds2.data_handle().get_id() == 1); - assert(mds2.data_handle().is_swapped()); - assert(mds2.mapping().get_id() == 1); - assert(mds2.mapping().is_swapped()); - assert(mds2.accessor().get_id() == 1); - assert(mds2.accessor().is_swapped()); - assert((mds2[array{1, 1}] == 1)); - assert((mds2[array{0, 1}] == 0)); + { // Check swapping with tracking types + using E = extents; + using Mds = mdspan, TrackingAccessor>; + static_assert(is_nothrow_swappable_v); + static_assert(!is_swappable_v); + + int a1[9] = {1, 0, 0, 0, 1, 0, 0, 0, 1}; + Mds mds1{TrackingDataHandle{1, a1}, TrackingLayout<>::mapping(1), TrackingAccessor{1}}; + int a2[9] = {3, 0, 0, 0, 3, 0, 0, 0, 3}; + Mds mds2{TrackingDataHandle{3, a2}, TrackingLayout<>::mapping(3), TrackingAccessor{3}}; + swap(mds1, mds2); + static_assert(is_void_v); + + assert(mds1.data_handle().get_id() == 3); + assert(mds1.data_handle().is_swapped()); + assert(mds1.mapping().get_id() == 3); + assert(mds1.mapping().is_swapped()); + assert(mds1.accessor().get_id() == 3); + assert(mds1.accessor().is_swapped()); + assert((mds1[array{1, 1}] == 3)); + assert((mds1[array{0, 1}] == 0)); + + assert(mds2.data_handle().get_id() == 1); + assert(mds2.data_handle().is_swapped()); + assert(mds2.mapping().get_id() == 1); + assert(mds2.mapping().is_swapped()); + assert(mds2.accessor().get_id() == 1); + assert(mds2.accessor().is_swapped()); + assert((mds2[array{1, 1}] == 1)); + assert((mds2[array{0, 1}] == 0)); + } + + { // Check swapping with standard layout and accessor + using Mds = mdspan>; + static_assert(is_nothrow_swappable_v); + static_assert(!is_swappable_v); + + int diag[] = {1, 0, 0, 1}; + Mds mds1{diag}; + int revdiag[] = {0, 1, 1, 0}; + Mds mds2{revdiag}; + + swap(mds1, mds2); + assert(mds1.data_handle() == revdiag); + assert(mds2.data_handle() == diag); + } } constexpr void check_getters() { @@ -1316,11 +1333,33 @@ constexpr void check_deduction_guides() { } } +// When +// * 'Mds::accessor_type' is specialization of 'default_accesor', and +// * 'Mds::layout_type' is +// * 'layout_left' or 'layout_right' and 'Mds::extents_type::rank_dynamic() == 0', or +// * 'layout_stride' and 'Mds::extents_type::rank() == 0' +// then 'sizeof(Mds) == sizeof(void*)' (MSVC STL specific behavior). +static_assert(sizeof(mdspan, layout_left>) == sizeof(void*)); +static_assert(sizeof(mdspan, layout_left>) > sizeof(void*)); +static_assert(sizeof(mdspan, layout_left, TrivialAccessor>) > sizeof(void*)); + +static_assert(sizeof(mdspan, layout_right>) == sizeof(void*)); +static_assert(sizeof(mdspan, layout_right>) > sizeof(void*)); +static_assert(sizeof(mdspan, layout_right, TrivialAccessor>) > sizeof(void*)); + +static_assert(sizeof(mdspan, layout_stride>) == sizeof(void*)); +static_assert(sizeof(mdspan, layout_stride>) > sizeof(void*)); +static_assert(sizeof(mdspan, layout_stride>) > sizeof(void*)); +static_assert(sizeof(mdspan, layout_stride, TrivialAccessor>) > sizeof(void*)); + constexpr bool test() { + check_modeled_concepts_and_member_types, layout_left, default_accessor>(); + check_modeled_concepts_and_member_types, layout_right, default_accessor>(); + check_modeled_concepts_and_member_types, layout_stride, default_accessor>(); + check_modeled_concepts_and_member_types, layout_left, TrackingAccessor>(); check_modeled_concepts_and_member_types, layout_stride, TrivialAccessor>(); check_modeled_concepts_and_member_types, TrackingLayout<>, AccessorWithTrackingDataHandle>(); - check_modeled_concepts_and_member_types, layout_left, TrackingAccessor>(); check_observers(); check_default_constructor(); check_defaulted_copy_and_move_constructors(); From 2c495f5316eaa1e67c9db588d20f06c9c7ca5e0d Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Sat, 8 Jul 2023 04:11:28 +0200 Subject: [PATCH 36/57] ``: Final cleanups (#3852) Co-authored-by: Stephan T. Lavavej --- stl/inc/mdspan | 258 ++++++++++-------- tests/std/include/test_mdspan_support.hpp | 6 +- .../test.compile.pass.cpp | 2 +- .../P0009R18_mdspan_extents_death/test.cpp | 2 +- .../P0009R18_mdspan_layout_left/test.cpp | 29 +- .../P0009R18_mdspan_layout_right/test.cpp | 6 - .../test.cpp | 2 +- .../std/tests/P0009R18_mdspan_mdspan/test.cpp | 159 ++++++----- .../P0009R18_mdspan_mdspan_death/test.cpp | 10 +- 9 files changed, 258 insertions(+), 216 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 2fad249ec3..7e584e4c40 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -75,63 +75,24 @@ private: static constexpr array _Dynamic_indices = _Make_dynamic_indices(); _NODISCARD static consteval auto _Make_dynamic_indices_inv() noexcept { - array _Result{}; + array _Result{}; + rank_type _Counter = 0; for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) { - for (rank_type _Idx_inv = 0; _Idx_inv < _Rank; ++_Idx_inv) { - if (_Dynamic_indices[_Idx_inv + 1] == _Idx + 1) { - _Result[_Idx] = _Idx_inv; - break; - } + if (_Static_extents[_Idx] == dynamic_extent) { + _Analysis_assume_(_Counter < _Rank_dynamic); // TRANSITION, DevCom-923103 + _Result[_Counter] = _Idx; + ++_Counter; } } return _Result; } - static constexpr array _Dynamic_indices_inv = _Make_dynamic_indices_inv(); - - struct _Construct_from_tuple { - explicit _Construct_from_tuple() = default; - }; + static constexpr array _Dynamic_indices_inv = _Make_dynamic_indices_inv(); using _Base = _Maybe_empty_array<_IndexType, _Rank_dynamic>; -public: - _NODISCARD static constexpr rank_type rank() noexcept { - return _Rank; - } - - _NODISCARD static constexpr rank_type rank_dynamic() noexcept { - return _Rank_dynamic; - } - - _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()) { - return this->_Array[_Idx]; - } else { - if (_Static_extents[_Idx] == dynamic_extent) { - return this->_Array[_Dynamic_indices[_Idx]]; - } else { - return static_cast(_Static_extents[_Idx]); - } - } - } - - constexpr extents() noexcept = default; - template - requires (sizeof...(_OtherExtents) == rank()) + requires (sizeof...(_OtherExtents) == _Rank) && ((_OtherExtents == dynamic_extent || _Extents == dynamic_extent || _OtherExtents == _Extents) && ...) constexpr explicit extents( const extents<_OtherIndexType, _OtherExtents...>& _Other, index_sequence<_Indices...>) noexcept @@ -153,21 +114,17 @@ public: #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 - : extents(_Other, make_index_sequence{}) {} + struct _Construct_from_tuple { + explicit _Construct_from_tuple() = default; + }; template - requires (tuple_size_v<_ExtsTuple> == rank_dynamic()) + requires (tuple_size_v<_ExtsTuple> == _Rank_dynamic) constexpr explicit extents(_Construct_from_tuple, _ExtsTuple _Tpl, index_sequence<_Indices...>) noexcept : _Base{static_cast(_STD move(_STD get<_Indices>(_Tpl)))...} {} template - requires (tuple_size_v<_ExtsTuple> != rank_dynamic()) + requires (tuple_size_v<_ExtsTuple> != _Rank_dynamic) constexpr explicit extents(_Construct_from_tuple, _ExtsTuple _Tpl, index_sequence<_DynIndices...>) noexcept : _Base{static_cast(_STD move(_STD get<_Dynamic_indices_inv[_DynIndices]>(_Tpl)))...} { #if _CONTAINER_DEBUG_LEVEL > 0 @@ -182,32 +139,9 @@ public: #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 - : 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 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 - } - }; - _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_dynamic()) + && is_nothrow_constructible_v && (_Size == _Rank_dynamic) constexpr explicit extents(span<_OtherIndexType, _Size> _Dynamic_exts, index_sequence<_Indices...>) noexcept : _Base{static_cast(_STD as_const(_Dynamic_exts[_Indices]))...} { #if _CONTAINER_DEBUG_LEVEL > 0 @@ -221,7 +155,7 @@ public: template requires is_convertible_v - && is_nothrow_constructible_v && (_Size != rank_dynamic()) + && is_nothrow_constructible_v && (_Size != _Rank_dynamic) constexpr explicit extents(span<_OtherIndexType, _Size> _Mixed_exts, index_sequence<_Indices...>) noexcept : _Base{static_cast(_STD as_const(_Mixed_exts[_Dynamic_indices_inv[_Indices]]))...} { #if _CONTAINER_DEBUG_LEVEL > 0 @@ -239,6 +173,72 @@ public: #endif // _CONTAINER_DEBUG_LEVEL > 0 } +public: + _NODISCARD static constexpr rank_type rank() noexcept { + return _Rank; + } + + _NODISCARD static constexpr rank_type rank_dynamic() noexcept { + return _Rank_dynamic; + } + + _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()) { + return this->_Array[_Idx]; + } else { + if (_Static_extents[_Idx] == dynamic_extent) { + return this->_Array[_Dynamic_indices[_Idx]]; + } else { + return static_cast(_Static_extents[_Idx]); + } + } + } + + constexpr extents() noexcept = default; + + 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 + : extents(_Other, make_index_sequence{}) {} + + 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 + : 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 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 + } + }; + _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 @@ -342,9 +342,10 @@ template inline constexpr bool _Is_extents> = true; template - requires _Is_extents<_Extents> class _Fwd_prod_of_extents { public: + _STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>); + _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) { @@ -359,34 +360,36 @@ public: } }; -template - requires (sizeof...(_Extents) > 0) && ((_Extents != dynamic_extent) && ...) -class _Fwd_prod_of_extents> { +template + requires (_Extents::rank() > 0) && (_Extents::rank_dynamic() == 0) +class _Fwd_prod_of_extents<_Extents> { private: - using _Ty = extents<_IndexType, _Extents...>; + _STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>); _NODISCARD static consteval auto _Make_prods() noexcept { - array _Result; + 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_extents[_Idx - 1]); + for (size_t _Idx = 1; _Idx < _Extents::_Rank + 1; ++_Idx) { + _Result[_Idx] = static_cast<_Extents::index_type>(_Result[_Idx - 1] * _Extents::_Static_extents[_Idx - 1]); } return _Result; } - static constexpr array _Cache = _Make_prods(); + static constexpr array _Cache = _Make_prods(); public: - _NODISCARD static constexpr _Ty::index_type _Calculate(const _Ty&, const size_t _Idx) noexcept { - _STL_INTERNAL_CHECK(_Idx <= _Ty::_Rank); + _NODISCARD static constexpr _Extents::index_type _Calculate(const _Extents&, const size_t _Idx) noexcept { + _STL_INTERNAL_CHECK(_Idx <= _Extents::_Rank); return _Cache[_Idx]; } }; template - requires _Is_extents<_Extents> && (_Extents::rank() > 0) class _Rev_prod_of_extents { public: + _STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>); + _STL_INTERNAL_STATIC_ASSERT(_Extents::rank() > 0); + _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; @@ -397,26 +400,27 @@ public: } }; -template - requires ((_Extents != dynamic_extent) && ...) -class _Rev_prod_of_extents> { +template + requires (_Extents::rank_dynamic() == 0) +class _Rev_prod_of_extents<_Extents> { private: - using _Ty = extents<_IndexType, _Extents...>; + _STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>); + _STL_INTERNAL_STATIC_ASSERT(_Extents::rank() > 0); _NODISCARD static consteval auto _Make_prods() noexcept { - array _Result; + 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_extents[_Idx]); + for (size_t _Idx = _Extents::_Rank; _Idx-- > 1;) { + _Result[_Idx - 1] = static_cast<_Extents::index_type>(_Result[_Idx] * _Extents::_Static_extents[_Idx]); } return _Result; } - static constexpr array _Cache = _Make_prods(); + static constexpr array _Cache = _Make_prods(); public: - _NODISCARD static constexpr _Ty::index_type _Calculate(const _Ty&, const size_t _Idx) noexcept { - _STL_INTERNAL_CHECK(_Idx < _Ty::_Rank); + _NODISCARD static constexpr _Extents::index_type _Calculate(const _Extents&, const size_t _Idx) noexcept { + _STL_INTERNAL_CHECK(_Idx < _Extents::_Rank); return _Cache[_Idx]; } }; @@ -528,7 +532,8 @@ public: template requires is_constructible_v - constexpr explicit(extents_type::rank() > 0) mapping(const layout_stride::template mapping<_OtherExtents>& _Other) + constexpr explicit(extents_type::rank() > 0) + mapping(const layout_stride::template mapping<_OtherExtents>& _Other) noexcept // strengthened : _Base(_Other.extents()) { #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (extents_type::rank() > 0) { @@ -814,8 +819,8 @@ public: const bool _Overflow = _Mul_overflow(this->_Array[_Idx + 1], this->_Exts.extent(_Idx + 1), this->_Array[_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. + // representable as a value of type 'index_type', but this is not enough. We need to require every + // single stride to be representable as a 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)."); @@ -1185,7 +1190,9 @@ public: return this->_Map.extents().extent(_Idx); } - constexpr mdspan() + constexpr mdspan() noexcept( + is_nothrow_default_constructible_v&& is_nothrow_default_constructible_v&& + is_nothrow_default_constructible_v) // strengthened requires (rank_dynamic() > 0) && is_default_constructible_v && is_default_constructible_v && is_default_constructible_v {} @@ -1198,7 +1205,9 @@ public: && (is_nothrow_constructible_v && ...) && (sizeof...(_OtherIndexTypes) == rank() || sizeof...(_OtherIndexTypes) == rank_dynamic()) && is_constructible_v && is_default_constructible_v - constexpr explicit mdspan(data_handle_type _Ptr_, _OtherIndexTypes... _Exts) + constexpr explicit mdspan(data_handle_type _Ptr_, _OtherIndexTypes... _Exts) noexcept( + is_nothrow_move_constructible_v&& is_nothrow_constructible_v&& + is_nothrow_default_constructible_v) // strengthened : _Mapping_base(extents_type{static_cast(_STD move(_Exts))...}), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {} @@ -1207,7 +1216,10 @@ public: && is_nothrow_constructible_v && (_Size == rank() || _Size == rank_dynamic()) && is_constructible_v && is_default_constructible_v - constexpr explicit(_Size != rank_dynamic()) mdspan(data_handle_type _Ptr_, span<_OtherIndexType, _Size> _Exts) + constexpr explicit(_Size != rank_dynamic()) + mdspan(data_handle_type _Ptr_, span<_OtherIndexType, _Size> _Exts) noexcept( + is_nothrow_move_constructible_v&& is_nothrow_constructible_v&& + is_nothrow_default_constructible_v) // strengthened : _Mapping_base(extents_type{_Exts}), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {} template @@ -1216,18 +1228,26 @@ public: && (_Size == rank() || _Size == rank_dynamic()) && is_constructible_v && is_default_constructible_v constexpr explicit(_Size != rank_dynamic()) - mdspan(data_handle_type _Ptr_, const array<_OtherIndexType, _Size>& _Exts) + mdspan(data_handle_type _Ptr_, const array<_OtherIndexType, _Size>& _Exts) noexcept( + is_nothrow_move_constructible_v&& is_nothrow_constructible_v&& + is_nothrow_default_constructible_v) // strengthened : _Mapping_base(extents_type{_Exts}), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {} - constexpr mdspan(data_handle_type _Ptr_, const extents_type& _Exts) + constexpr mdspan(data_handle_type _Ptr_, const extents_type& _Exts) noexcept( + is_nothrow_move_constructible_v&& is_nothrow_constructible_v&& is_nothrow_default_constructible_v) // strengthened requires is_constructible_v && is_default_constructible_v : _Mapping_base(_Exts), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {} - constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_) + constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_) noexcept( + is_nothrow_move_constructible_v&& is_nothrow_copy_constructible_v&& + is_nothrow_default_constructible_v) // strengthened requires is_default_constructible_v : _Mapping_base(_Map_), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {} - constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_, const accessor_type& _Acc_) + constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_, const accessor_type& _Acc_) noexcept( + is_nothrow_move_constructible_v&& is_nothrow_copy_constructible_v&& + is_nothrow_copy_constructible_v) // strengthened : _Mapping_base(_Map_), _Accessor_base(_Acc_), _Ptr(_STD move(_Ptr_)) {} template @@ -1236,7 +1256,11 @@ public: constexpr explicit( !is_convertible_v&, mapping_type> || !is_convertible_v) - mdspan(const mdspan<_OtherElementType, _OtherExtents, _OtherLayoutPolicy, _OtherAccessor>& _Other) + mdspan(const mdspan<_OtherElementType, _OtherExtents, _OtherLayoutPolicy, _OtherAccessor>& _Other) noexcept( + is_nothrow_constructible_v&& + is_nothrow_constructible_v&>&& + is_nothrow_constructible_v) // strengthened : _Mapping_base(_Other.mapping()), _Accessor_base(_Other.accessor()), _Ptr(_Other.data_handle()) { static_assert(is_constructible_v, "The data_handle_type must be constructible from const typename OtherAccessor::data_handle_type& (N4950 " @@ -1261,7 +1285,8 @@ public: requires (is_convertible_v<_OtherIndexTypes, index_type> && ...) && (is_nothrow_constructible_v && ...) && (sizeof...(_OtherIndexTypes) == rank()) - _NODISCARD constexpr reference operator[](_OtherIndexTypes... _Indices) const { + _NODISCARD constexpr reference operator[](_OtherIndexTypes... _Indices) const + noexcept(noexcept(_Access_impl(static_cast(_STD move(_Indices))...))) /* strengthened */ { return _Access_impl(static_cast(_STD move(_Indices))...); } #endif // __cpp_multidimensional_subscript @@ -1387,7 +1412,8 @@ private: #endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ template - _NODISCARD constexpr reference _Access_impl(_OtherIndexTypes... _Indices) const { + _NODISCARD constexpr reference _Access_impl(_OtherIndexTypes... _Indices) const + noexcept(noexcept(this->_Acc.access(_Ptr, static_cast(this->_Map(_Indices...))))) /* strengthened */ { _STL_INTERNAL_STATIC_ASSERT((same_as<_OtherIndexTypes, index_type> && ...)); #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(this->_Map.extents()._Contains_multidimensional_index(make_index_sequence{}, _Indices...), diff --git a/tests/std/include/test_mdspan_support.hpp b/tests/std/include/test_mdspan_support.hpp index 80ac1e4daf..9a49ebe6d4 100644 --- a/tests/std/include/test_mdspan_support.hpp +++ b/tests/std/include/test_mdspan_support.hpp @@ -229,9 +229,9 @@ constexpr void check_members_with_various_extents(Fn&& fn) { details::check_members_with_various_extents_impl(std::forward(fn), std::make_index_sequence<2>{}); details::check_members_with_various_extents_impl(std::forward(fn), std::make_index_sequence<4>{}); details::check_members_with_various_extents_impl(std::forward(fn), std::make_index_sequence<8>{}); -#ifndef _PREFAST_ - details::check_members_with_various_extents_impl(std::forward(fn), std::make_index_sequence<16>{}); -#endif // _PREFAST_ + if (!std::is_constant_evaluated()) { + details::check_members_with_various_extents_impl(std::forward(fn), std::make_index_sequence<16>{}); + } } namespace details { diff --git a/tests/std/tests/GH_002206_unreserved_names/test.compile.pass.cpp b/tests/std/tests/GH_002206_unreserved_names/test.compile.pass.cpp index 7bed2d659c..c57279e236 100644 --- a/tests/std/tests/GH_002206_unreserved_names/test.compile.pass.cpp +++ b/tests/std/tests/GH_002206_unreserved_names/test.compile.pass.cpp @@ -39,4 +39,4 @@ #if empty_bases != 6 #error bad macro expansion -#endif // noop_dtor != 6 +#endif // empty_bases != 6 diff --git a/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp b/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp index 4a506e77c0..30c743c087 100644 --- a/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp @@ -55,7 +55,7 @@ void test_construction_from_pack_with_unrepresentable_as_index_type_values_2() { } void test_construction_from_pack_with_unrepresentable_as_index_type_values_3() { - static_assert(signed_integral, "char is not signed integral"); + static_assert(signed_integral, "This test assumes that it isn't being compiled with /J"); // 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)}; diff --git a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp index 4c53b23742..0073f773cc 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp @@ -84,9 +84,6 @@ constexpr void check_members(const extents& ext, index_se // Other tests are defined in 'check_construction_from_other_right_mapping' function } -#ifdef __clang__ - if (!is_constant_evaluated()) // FIXME clang hits constexpr limit here -#endif { // Check construction from layout_stride::mapping array strides{}; if constexpr (Ext::rank() > 0) { @@ -261,14 +258,14 @@ constexpr void check_construction_from_other_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>>); + static_assert(is_nothrow_constructible_v>, + layout_stride::mapping>>); // strengthened + static_assert(is_nothrow_constructible_v>, + layout_stride::mapping>>); // strengthened + static_assert(is_nothrow_constructible_v>, + layout_stride::mapping>>); // strengthened + static_assert(is_nothrow_constructible_v>, + layout_stride::mapping>>); // strengthened } { // Check construction from layout_stride::mapping when E is invalid @@ -423,10 +420,6 @@ constexpr void check_correctness() { #endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ } - -#ifdef __clang__ - if (!is_constant_evaluated()) // FIXME clang hits constexpr limit here -#endif { // 3x2 matrix with column-major order const array values{0, 1, 2, 3, 4, 5}; mdspan, layout_left> matrix{values.data()}; @@ -448,9 +441,6 @@ constexpr void check_correctness() { #endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ } -#ifdef __clang__ - if (!is_constant_evaluated()) // FIXME clang hits constexpr limit here -#endif { // 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}; @@ -472,9 +462,6 @@ 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}; diff --git a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp index 273a461ecf..87babc454c 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp @@ -33,9 +33,6 @@ 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; @@ -63,9 +60,6 @@ constexpr void check_members(const extents& ext, index_se using Ext2 = extents; using Mapping2 = layout_right::mapping; -#ifdef __clang__ - if (!is_constant_evaluated()) // FIXME clang hits constexpr limit here -#endif { // Check construction from other layout_right::mapping Mapping m1{ext}; Mapping2 m2{m1}; 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 c8a0b9b1d1..ed2e78c199 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp @@ -15,7 +15,7 @@ using namespace std; void test_default_construction() { using Ext = extents; - // Value of layout_right::mapping().required_span_size() must be + // Value of layout_stride::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] } diff --git a/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp b/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp index aa4915c84c..48a2e2d07e 100644 --- a/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp @@ -397,13 +397,14 @@ constexpr void check_observers() { constexpr void check_default_constructor() { { // Check constraint: 'rank_dynamic() > 0' - static_assert(is_default_constructible_v>>); + static_assert(is_nothrow_default_constructible_v>>); // strengthened static_assert(!is_default_constructible_v>>); static_assert(!is_default_constructible_v>>); } { // Check constraints: 'is_default_constructible_v' - static_assert(is_default_constructible_v, layout_right, VectorBoolAccessor>>); + static_assert(is_nothrow_default_constructible_v< + mdspan, layout_right, VectorBoolAccessor>>); // strengthened static_assert( !is_default_constructible_v, layout_right, TrackingAccessor>>); } @@ -414,8 +415,8 @@ constexpr void check_default_constructor() { } { // Check constraint: 'is_default_constructible_v' - static_assert(is_default_constructible_v< - mdspan, layout_right, AccessorWithCustomOffsetPolicy>>); + static_assert(is_nothrow_default_constructible_v< + mdspan, layout_right, AccessorWithCustomOffsetPolicy>>); // strengthened static_assert( !is_default_constructible_v, layout_right, TrackingAccessor>>); } @@ -455,36 +456,40 @@ constexpr void check_defaulted_copy_and_move_constructors() { constexpr void check_data_handle_and_indices_pack_constructor() { { // Check constraint: '(is_convertible_v && ...)' using Mds = mdspan>; - static_assert(is_constructible_v); - static_assert(is_constructible_v); - static_assert(is_constructible_v); - static_assert(is_constructible_v>); + static_assert(is_nothrow_constructible_v); // strengthened + static_assert(is_nothrow_constructible_v); // strengthened + static_assert( + is_nothrow_constructible_v); // strengthened + static_assert(is_nothrow_constructible_v>); // strengthened static_assert(!is_constructible_v); } { // Check constraint: '(is_nothrow_constructible && ...)' using Mds = mdspan>; - static_assert(is_constructible_v>); + static_assert(is_nothrow_constructible_v>); // strengthened static_assert(!is_constructible_v>); } { // Check constraint: 'N == rank() || N == rank_dynamic()' using Mds = mdspan>; static_assert(!is_constructible_v); - static_assert(is_constructible_v); + static_assert(is_nothrow_constructible_v); // strengthened static_assert(!is_constructible_v); - static_assert(is_constructible_v); + static_assert(is_nothrow_constructible_v); // strengthened } { // Check constraint: 'is_constructible_v' - static_assert(is_constructible_v, layout_left>, int* const, int, int>); + static_assert(is_nothrow_constructible_v, layout_left>, int* const, int, + int>); // strengthened static_assert(!is_constructible_v, layout_stride>, int*, int, int>); static_assert(!is_constructible_v, TrackingLayout<>>, int*, int, int>); } { // Check constraint: 'is_default_constructible_v' - static_assert(is_constructible_v, layout_right, VectorBoolAccessor>, - vector::iterator, int, int>); + static_assert(is_nothrow_constructible_v, layout_right, VectorBoolAccessor>, + vector::iterator, int, int>); // strengthened static_assert( !is_constructible_v, layout_right, TrackingAccessor>, int*, int, int>); } @@ -537,18 +542,20 @@ constexpr void check_data_handle_and_indices_pack_constructor() { constexpr void check_data_handle_and_span_array_constructors() { { // Check constraint: 'is_convertible_v' using Mds = mdspan>; - static_assert(is_constructible_v>); - static_assert(is_constructible_v>); - static_assert(is_constructible_v, 3>>); - static_assert(is_constructible_v, 3>>); + static_assert(is_nothrow_constructible_v>); // strengthened + static_assert(is_nothrow_constructible_v>); // strengthened + static_assert(is_nothrow_constructible_v, 3>>); // strengthened + static_assert(is_nothrow_constructible_v, 3>>); // strengthened static_assert(!is_constructible_v>); static_assert(!is_constructible_v>); } { // Check constraint: 'is_nothrow_constructible' using Mds = mdspan>; - static_assert(is_constructible_v, 2>>); - static_assert(is_constructible_v, 2>>); + static_assert(is_nothrow_constructible_v, 2>>); // strengthened + static_assert(is_nothrow_constructible_v, 2>>); // strengthened static_assert(!is_constructible_v, 2>>); static_assert(!is_constructible_v, 2>>); } @@ -557,23 +564,31 @@ constexpr void check_data_handle_and_span_array_constructors() { using Mds = mdspan>; static_assert(!is_constructible_v>); static_assert(!is_constructible_v>); - static_assert(is_constructible_v>); - static_assert(is_constructible_v>); + static_assert(is_nothrow_constructible_v>); // strengthened + static_assert(is_nothrow_constructible_v>); // strengthened static_assert(!is_constructible_v>); static_assert(!is_constructible_v>); - static_assert(is_constructible_v>); - static_assert(is_constructible_v>); + static_assert(is_nothrow_constructible_v>); // strengthened + static_assert(is_nothrow_constructible_v>); // strengthened } { // Check constraint: 'is_constructible_v' - static_assert(is_constructible_v, layout_left>, int* const, span>); - static_assert(is_constructible_v, layout_left>, int* const, array>); + static_assert(is_nothrow_constructible_v, layout_left>, int* const, + span>); // strengthened + static_assert(is_nothrow_constructible_v, layout_left>, int* const, + array>); // strengthened static_assert( is_constructible_v, TrackingLayout>, int* const, span>); + static_assert( + !is_nothrow_constructible_v, TrackingLayout>, + int* const, span>); // strengthened static_assert( is_constructible_v, TrackingLayout>, int* const, array>); + static_assert( + !is_nothrow_constructible_v, TrackingLayout>, + int* const, array>); // strengthened static_assert(!is_constructible_v, layout_stride>, int*, span>); static_assert(!is_constructible_v, layout_stride>, int*, array>); static_assert(!is_constructible_v, TrackingLayout<>>, int*, span>); @@ -581,10 +596,10 @@ constexpr void check_data_handle_and_span_array_constructors() { } { // Check constraint: 'is_default_constructible_v' - static_assert(is_constructible_v, layout_right, VectorBoolAccessor>, - vector::iterator, span>); - static_assert(is_constructible_v, layout_right, VectorBoolAccessor>, - vector::iterator, array>); + static_assert(is_nothrow_constructible_v, layout_right, VectorBoolAccessor>, + vector::iterator, span>); // strengthened + static_assert(is_nothrow_constructible_v, layout_right, VectorBoolAccessor>, + vector::iterator, array>); // strengthened static_assert(!is_constructible_v, layout_right, TrackingAccessor>, int*, span>); static_assert(!is_constructible_v, layout_right, TrackingAccessor>, int*, @@ -658,18 +673,20 @@ constexpr void check_data_handle_and_span_array_constructors() { constexpr void check_data_handle_and_extents_constructor() { { // Check constraint: 'is_constructible_v' - static_assert(is_constructible_v>, int*, dextents>); - static_assert(is_constructible_v>, int*, dextents>); + static_assert( + is_nothrow_constructible_v>, int*, dextents>); // strengthened + static_assert( + is_nothrow_constructible_v>, int*, dextents>); // strengthened static_assert(!is_constructible_v, layout_stride>, int*, dextents>); static_assert(!is_constructible_v>, int*, dextents>); static_assert(!is_constructible_v>, int*, extents>); } { // Check constraint: is_default_constructible_v - static_assert(is_constructible_v, layout_right, VectorBoolAccessor>, - vector::iterator, dextents>); - static_assert(is_constructible_v, layout_right, VectorBoolAccessor>, - vector::iterator, extents>); + static_assert(is_nothrow_constructible_v, layout_right, VectorBoolAccessor>, + vector::iterator, dextents>); // strengthened + static_assert(is_nothrow_constructible_v, layout_right, VectorBoolAccessor>, + vector::iterator, extents>); // strengthened static_assert(!is_constructible_v, layout_right, TrackingAccessor>, int*, dextents>); static_assert(!is_constructible_v, layout_right, TrackingAccessor>, int*, @@ -699,10 +716,10 @@ constexpr void check_data_handle_and_extents_constructor() { constexpr void check_data_handle_and_mapping_constructor() { { // Check constraint: 'is_default_constructible_v' - static_assert(is_constructible_v, layout_left, VectorBoolAccessor>, - vector::iterator, layout_left::mapping>>); - static_assert(is_constructible_v, layout_left>, int* const, - layout_left::mapping>>); + static_assert(is_nothrow_constructible_v, layout_left, VectorBoolAccessor>, + vector::iterator, layout_left::mapping>>); // strengthened + static_assert(is_nothrow_constructible_v, layout_left>, int* const, + layout_left::mapping>>); // strengthened static_assert(!is_constructible_v< mdspan, extents, TrackingLayout<>, TrackingAccessor>>, vector*, TrackingLayout<>::mapping>>); @@ -736,30 +753,42 @@ constexpr void check_data_handle_and_mapping_constructor() { } constexpr void check_data_handle_and_mapping_and_accessor_constructor() { - using Ext = extents; - using Mds = mdspan, TrackingAccessor>; - - unsigned int identity_matrix[] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; - Mds mds{TrackingDataHandle{16, identity_matrix}, TrackingLayout<>::mapping(17), - TrackingAccessor{18}}; - - // Check effects: - // - Direct-non-list-initializes ptr_ with std::move(p), - // - direct-non-list-initializes map_ with m, and - // - direct-non-list-initializes acc_ with a. - assert(mds.data_handle().is_move_constructed()); - assert(mds.data_handle().get_id() == 16); - assert(mds.mapping().is_copy_constructed()); - assert(mds.mapping().get_id() == 17); - assert(mds.accessor().is_copy_constructed()); - assert(mds.accessor().get_id() == 18); + { // Check effects + using Ext = extents; + using Mds = mdspan, TrackingAccessor>; + + unsigned int identity_matrix[] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; + Mds mds{TrackingDataHandle{16, identity_matrix}, TrackingLayout<>::mapping(17), + TrackingAccessor{18}}; + + // Effects: + // - Direct-non-list-initializes ptr_ with std::move(p), + // - direct-non-list-initializes map_ with m, and + // - direct-non-list-initializes acc_ with a. + assert(mds.data_handle().is_move_constructed()); + assert(mds.data_handle().get_id() == 16); + assert(mds.mapping().is_copy_constructed()); + assert(mds.mapping().get_id() == 17); + assert(mds.accessor().is_copy_constructed()); + assert(mds.accessor().get_id() == 18); + } + + { // Check noexceptness (strengthened) + using Mds1 = mdspan>; + static_assert(is_nothrow_constructible_v); + + using Mds2 = mdspan, TrackingLayout<>>; + static_assert(!is_nothrow_constructible_v); + } } constexpr void check_construction_from_other_mdspan() { { // Check constraint: 'is_constructible_v&>' - static_assert(is_constructible_v, layout_stride>, - mdspan, layout_right>>); + static_assert(is_nothrow_constructible_v, layout_stride>, + mdspan, layout_right>>); // strengthened static_assert(!is_constructible_v, layout_left>, mdspan, layout_right>>); static_assert(!is_constructible_v, layout_left>, @@ -768,8 +797,9 @@ constexpr void check_construction_from_other_mdspan() { { // Check constraint: 'is_constructible_v' using Ext = extents; - static_assert(is_constructible_v>, - mdspan>>); + static_assert( + is_nothrow_constructible_v>, + mdspan>>); // strengthened static_assert(!is_constructible_v>, mdspan>>); } @@ -877,6 +907,11 @@ constexpr void check_multidimensional_subscript_operator() { assert(r1); same_as::reference> decltype(auto) r2 = as_const(mds)[0, 1]; assert(!r2); + +#ifndef __clang__ // TRANSITION, Clang 17 + static_assert(noexcept(mds[1, 1])); // strengthened + static_assert(noexcept(as_const(mds)[0, 1])); // strengthened +#endif // __clang__ } { // Check that indices are moved and then casted to 'index_type' @@ -1334,7 +1369,7 @@ constexpr void check_deduction_guides() { } // When -// * 'Mds::accessor_type' is specialization of 'default_accesor', and +// * 'Mds::accessor_type' is a specialization of 'default_accessor', and // * 'Mds::layout_type' is // * 'layout_left' or 'layout_right' and 'Mds::extents_type::rank_dynamic() == 0', or // * 'layout_stride' and 'Mds::extents_type::rank() == 0' diff --git a/tests/std/tests/P0009R18_mdspan_mdspan_death/test.cpp b/tests/std/tests/P0009R18_mdspan_mdspan_death/test.cpp index 76398b5cc5..f4c072422c 100644 --- a/tests/std/tests/P0009R18_mdspan_mdspan_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_mdspan_death/test.cpp @@ -13,7 +13,7 @@ using namespace std; constexpr array some_ints{}; void test_construction_from_other_mdspan() { - auto mds1 = mdspan{some_ints.data(), 8, 2, 8}; + mdspan mds1{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}; @@ -21,26 +21,26 @@ void test_construction_from_other_mdspan() { #ifdef __cpp_multidimensional_subscript // TRANSITION, P2128R6 void test_access_with_invalid_multidimensional_index_1() { - auto mds = mdspan{some_ints.data(), 4, 4}; + mdspan mds{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}; + mdspan mds{some_ints.data(), 5, 5}; // I must be a multidimensional index in extents() (void) mds[array{4, 5}]; } void test_size_when_index_type_is_signed() { - auto mds = mdspan{some_ints.data(), dextents{8, 8, 4}}; + mdspan mds{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}}; + mdspan mds{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(); } From ec37cf5e20c444705ecf2b70a60b7094a2530667 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 17 Jul 2023 14:59:15 -0700 Subject: [PATCH 37/57] Replace workaround for DevCom-10360833. --- stl/inc/mdspan | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 7e584e4c40..c79cfa1783 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -865,32 +865,21 @@ public: #endif // _CONTAINER_DEBUG_LEVEL > 0 } -#ifndef __clang__ // TRANSITION, DevCom-10360833 - template - && is_nothrow_constructible_v, - int> = 0> -#else // ^^^ workaround / no workaround vvv + // TRANSITION, VSO-1852021: `_Extents::rank()` should be `extents_type::rank()` + // TRANSITION, VSO-1852030: `typename _Extents::index_type` should be `index_type` template - requires is_convertible_v - && is_nothrow_constructible_v -#endif // ^^^ no workaround ^^^ - constexpr mapping(const extents_type& _Exts_, span<_OtherIndexType, extents_type::rank()> _Strides_) noexcept - : mapping(_Exts_, _Strides_, make_index_sequence{}) { - } + requires is_convertible_v + && is_nothrow_constructible_v + constexpr mapping(const extents_type& _Exts_, span<_OtherIndexType, _Extents::rank()> _Strides_) noexcept + : mapping(_Exts_, _Strides_, make_index_sequence{}) {} -#ifndef __clang__ // TRANSITION, DevCom-10360833 - template - && is_nothrow_constructible_v, - int> = 0> -#else // ^^^ workaround / no workaround vvv + // TRANSITION, VSO-1852021: `_Extents::rank()` should be `extents_type::rank()` + // TRANSITION, VSO-1852030: `typename _Extents::index_type` should be `index_type` template - requires is_convertible_v - && is_nothrow_constructible_v -#endif // ^^^ no workaround ^^^ - constexpr mapping( - const extents_type& _Exts_, const array<_OtherIndexType, extents_type::rank()>& _Strides_) noexcept - : mapping(_Exts_, span{_Strides_}, make_index_sequence{}) { - } + requires is_convertible_v + && is_nothrow_constructible_v + constexpr mapping(const extents_type& _Exts_, const array<_OtherIndexType, _Extents::rank()>& _Strides_) noexcept + : mapping(_Exts_, span{_Strides_}, make_index_sequence{}) {} template requires _Layout_mapping_alike<_StridedLayoutMapping> From 1f9ecbf56b2b5c9a5d6c98d916499dcfd7996405 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 27 Jul 2023 03:15:39 +0800 Subject: [PATCH 38/57] Drop `#pragma once` from `` (#3901) --- stl/inc/mdspan | 1 - 1 file changed, 1 deletion(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index c79cfa1783..e06ca1a4d2 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -3,7 +3,6 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#pragma once #ifndef _MDSPAN_ #define _MDSPAN_ #include From 74e82a6cac55c74c702538e020e4980ed51519d3 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Thu, 10 Aug 2023 21:50:47 +0300 Subject: [PATCH 39/57] mdspan STL's review comments - easy part (#3944) --- tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp | 2 +- tests/std/tests/P0009R18_mdspan_extents/test.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp b/tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp index 5402186a9f..aa0e829edb 100644 --- a/tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp @@ -28,7 +28,7 @@ constexpr void test_one(array elems) { static_assert(semiregular); // Check if default_accessor is empty - static_assert(std::is_empty_v); + static_assert(is_empty_v); // Check nested types static_assert(same_as); diff --git a/tests/std/tests/P0009R18_mdspan_extents/test.cpp b/tests/std/tests/P0009R18_mdspan_extents/test.cpp index 58fd6ff5ac..9252e7b4a8 100644 --- a/tests/std/tests/P0009R18_mdspan_extents/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_extents/test.cpp @@ -36,7 +36,7 @@ constexpr void check_members(index_sequence) { // Check static observers static_assert(Ext::rank() == sizeof...(Extents)); - static_assert(Ext::rank_dynamic() == ((Extents == dynamic_extent) + ... + 0)); + static_assert(Ext::rank_dynamic() == (size_t{Extents == dynamic_extent} + ... + 0)); static_assert(((Ext::static_extent(Indices) == Extents) && ...)); // Check noexceptness of static observers @@ -67,7 +67,7 @@ constexpr void check_members(index_sequence) { Ext2 ext2{ext.extent(Indices)...}; assert(((ext.extent(Indices) == ext2.extent(Indices)) && ...)); assert(ext == ext2); - static_assert(is_nothrow_constructible_v); + static_assert(is_nothrow_constructible_v); // Other tests are defined in 'check_construction_from_extents_pack' function } From 425317d183eabd6c4280e0993e53bf2a59225c3a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 12 Aug 2023 02:07:12 -0700 Subject: [PATCH 40/57] Final `mdspan` test fixes (#3954) --- .../tests/P0009R18_mdspan_extents/test.cpp | 1 - .../P0009R18_mdspan_extents_death/test.cpp | 15 ++++++------- .../P0009R18_mdspan_layout_left/test.cpp | 15 +++++++------ .../P0009R18_mdspan_layout_right/test.cpp | 21 +++++++++++++----- .../P0009R18_mdspan_layout_stride/test.cpp | 12 +++++----- .../test.cpp | 4 ++-- .../std/tests/P0009R18_mdspan_mdspan/test.cpp | 22 +++++++++---------- 7 files changed, 50 insertions(+), 40 deletions(-) diff --git a/tests/std/tests/P0009R18_mdspan_extents/test.cpp b/tests/std/tests/P0009R18_mdspan_extents/test.cpp index 9252e7b4a8..2dedead9cc 100644 --- a/tests/std/tests/P0009R18_mdspan_extents/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_extents/test.cpp @@ -426,7 +426,6 @@ constexpr void check_construction_from_array_and_span() { static_assert(!is_constructible_v>); static_assert(!is_constructible_v>); static_assert(!is_constructible_v>); - static_assert(!is_constructible_v>); } { // Check implicit conversions diff --git a/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp b/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp index 30c743c087..6565c43abe 100644 --- a/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_extents_death/test.cpp @@ -64,18 +64,11 @@ void test_construction_from_pack_with_unrepresentable_as_index_type_values_3() { void test_construction_from_span_with_invalid_values() { int vals[] = {1, 2}; span s{vals}; - // Value of other.extent(r) must be equal to extent(r) for each r for which extent(r) is a static extent + // Value of exts[r] must be equal to extent(r) for each r for which extent(r) is a static extent [[maybe_unused]] extents e{s}; } void test_construction_from_span_with_unrepresentable_as_index_type_values() { - int vals[] = {1, 2}; - span s{vals}; - // Value of other.extent(r) must be equal to extent(r) for each r for which extent(r) is a static extent - [[maybe_unused]] extents e{s}; -} - -void test_construction_from_array_with_invalid_values() { int vals[] = {256}; span s{vals}; // Either N must be zero or exts[r] must be nonnegative and must be representable as value of type index_type for @@ -83,6 +76,12 @@ void test_construction_from_array_with_invalid_values() { [[maybe_unused]] extents e{s}; } +void test_construction_from_array_with_invalid_values() { + array a = {1, 2}; + // Value of exts[r] must be equal to extent(r) for each r for which extent(r) is a static extent + [[maybe_unused]] extents e{a}; +} + void test_construction_from_array_with_unrepresentable_as_index_type_values() { array a = {256}; // Either N must be zero or exts[r] must be nonnegative and must be representable as value of type index_type for diff --git a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp index 0073f773cc..3d3d511a9e 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -96,7 +97,7 @@ constexpr void check_members(const extents& ext, index_se } } - layout_stride::mapping m1{ext, span{strides}}; + layout_stride::mapping m1{ext, strides}; Mapping m2{m1}; assert(m1.extents() == m2.extents()); // Other tests are defined in 'check_construction_from_other_stride_mapping' function @@ -152,6 +153,8 @@ constexpr void check_members(const extents& ext, index_se { // Check comparisons assert(m == m); assert(!(m != m)); + static_assert(noexcept(m == m)); + static_assert(noexcept(m != m)); // Other tests are defined in 'check_comparisons' function } } @@ -241,12 +244,10 @@ constexpr void check_construction_from_other_right_mapping() { } { // Check implicit conversions - static_assert(!NotImplicitlyConstructibleFrom>, - layout_right::mapping>>); - static_assert(NotImplicitlyConstructibleFrom>, - layout_right::mapping>>); - static_assert(NotImplicitlyConstructibleFrom>, - layout_right::mapping>>); + using Mapping = layout_left::mapping>; + static_assert(!NotImplicitlyConstructibleFrom>>); + static_assert(NotImplicitlyConstructibleFrom>>); + static_assert(NotImplicitlyConstructibleFrom>>); } { // Check effects diff --git a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp index 87babc454c..c16cff0366 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -151,6 +152,8 @@ constexpr void check_members(const extents& ext, index_se { // Check comparisons assert(m == m); assert(!(m != m)); + static_assert(noexcept(m == m)); + static_assert(noexcept(m != m)); // Other tests are defined in 'check_comparisons' function } } @@ -210,6 +213,15 @@ constexpr void check_construction_from_other_right_mapping() { static_assert(NotImplicitlyConstructibleFrom>, layout_right::mapping>>); } + + { // Check effects + layout_right::mapping> m1; + layout_right::mapping> m2{m1}; + assert(m2.extents().extent(0) == 6); + assert(m2.extents().extent(1) == 2); + assert(m2.extents().extent(2) == 6); + assert(m1.extents() == m2.extents()); + } } constexpr void check_construction_from_other_left_mapping() { @@ -238,12 +250,9 @@ constexpr void check_construction_from_other_left_mapping() { } { // Check effects - layout_right::mapping> m1; - layout_right::mapping> m2{m1}; - assert(m2.extents().extent(0) == 6); - assert(m2.extents().extent(1) == 2); - assert(m2.extents().extent(2) == 6); - assert(m1.extents() == m2.extents()); + layout_left::mapping> m1; + layout_right::mapping> m2{m1}; + assert(m2.extents().extent(0) == 8); } } diff --git a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp index 13f6ea3032..b495de4327 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp @@ -35,8 +35,8 @@ struct NotLayoutMappingAlikeAtAll { static_assert(!_Layout_mapping_alike>>); -enum class AlwaysUnique { no, yes }; -enum class AlwaysStrided { no, yes }; +enum class AlwaysUnique : bool { no, yes }; +enum class AlwaysStrided : bool { no, yes }; template struct LyingLayout { @@ -63,7 +63,7 @@ struct LyingLayout { } static constexpr bool is_always_exhaustive() { - return layout_right::mapping::is_always_exhaustive(); + return layout_left::mapping::is_always_exhaustive(); } static constexpr bool is_always_strided() { @@ -222,6 +222,8 @@ constexpr void do_check_members(const extents& ext, { // Check comparisons assert(m == m); assert(!(m != m)); + static_assert(noexcept(m == m)); + static_assert(noexcept(m != m)); // Other tests are defined in 'check_comparisons' function } } @@ -378,8 +380,8 @@ constexpr void check_required_span_size() { using M1 = layout_stride::mapping>; static_assert(M1{}.required_span_size() == 1); - layout_stride::mapping> m2; - assert(m2.required_span_size() == 1); + M1 m1; + assert(m1.required_span_size() == 1); } { // Check N4950 [mdspan.layout.stride.expo]/1.2: size of the multidimensional index space e is 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 ed2e78c199..d0e111bb69 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp @@ -33,9 +33,9 @@ void test_construction_from_extents_and_array_2() { void test_construction_from_extents_and_span_1() { - array s{-1}; + array a{-1}; // Value of s[i] must be greater than 0 for all i in the range [0, rank_) - [[maybe_unused]] layout_stride::mapping> m{extents{}, span{s}}; + [[maybe_unused]] layout_stride::mapping> m{extents{}, span{a}}; } void test_construction_from_extents_and_span_2() { diff --git a/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp b/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp index 48a2e2d07e..542fda4ecf 100644 --- a/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp @@ -37,27 +37,27 @@ class ActionTracker { return *this; } - constexpr int get_id() const noexcept { + [[nodiscard]] constexpr int get_id() const noexcept { return id; } - constexpr bool is_copy_constructed() const noexcept { + [[nodiscard]] constexpr bool is_copy_constructed() const noexcept { return copy_constructed; } - constexpr bool is_move_constructed() const noexcept { + [[nodiscard]] constexpr bool is_move_constructed() const noexcept { return move_constructed; } - constexpr bool is_copy_assigned() const noexcept { + [[nodiscard]] constexpr bool is_copy_assigned() const noexcept { return copy_assigned; } - constexpr bool is_move_assigned() const noexcept { + [[nodiscard]] constexpr bool is_move_assigned() const noexcept { return move_assigned; } - constexpr bool is_swapped() const noexcept { + [[nodiscard]] constexpr bool is_swapped() const noexcept { return swapped; } @@ -77,7 +77,7 @@ class ActionTracker { bool swapped = false; }; -enum class RequireId { no, yes }; +enum class RequireId : bool { no, yes }; template struct TrackingLayout { @@ -1062,12 +1062,12 @@ constexpr void check_size() { assert(mds3.size() == 0); } - { // Mdspan with 'rank() == 0' + { // mdspan with 'rank() == 0' mdspan mds{some_data, extents{}}; assert(mds.size() == 1); } - { // Mdspan whose index space size would not be representable as index_type if 0 wasn't there + { // mdspan whose index space size would not be representable as index_type if 0 wasn't there mdspan mds1{some_data, extents{}}; assert(mds1.size() == 0); mdspan mds2{some_data, dextents{32767, 3, 0}}; @@ -1106,12 +1106,12 @@ constexpr void check_empty() { assert(mds3.empty()); } - { // Mdspan with 'rank() == 0' + { // mdspan with 'rank() == 0' mdspan mds{some_data, extents{}}; assert(!mds.empty()); } - { // Mdspan whose index space size would not be representable as index_type if 0 wasn't there + { // mdspan whose index space size would not be representable as index_type if 0 wasn't there mdspan mds1{some_data, extents{}}; assert(mds1.empty()); mdspan mds2{some_data, dextents{32767, 3, 0}}; From b60ac58d968dbedac6a281d58d3c190d708cefe1 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 15 Aug 2023 16:59:43 -0700 Subject: [PATCH 41/57] ``: Product code review (#3971) --- stl/inc/mdspan | 93 ++++++++++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index e06ca1a4d2..18bfbd3242 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -304,7 +304,7 @@ public: } } - template + template _NODISCARD constexpr bool _Contains_multidimensional_index( index_sequence<_Seq...>, _IndexTypes... _Indices) const noexcept { _STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...)); @@ -321,7 +321,7 @@ inline constexpr size_t _Repeat_dynamic_extent = dynamic_extent; template requires (is_convertible_v<_Integrals, size_t> && ...) -extents(_Integrals...) -> extents...>; +explicit extents(_Integrals...) -> extents...>; template struct _Dextents_impl; @@ -532,7 +532,7 @@ public: template requires is_constructible_v constexpr explicit(extents_type::rank() > 0) - mapping(const layout_stride::template mapping<_OtherExtents>& _Other) noexcept // strengthened + mapping(const layout_stride::mapping<_OtherExtents>& _Other) noexcept // strengthened : _Base(_Other.extents()) { #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (extents_type::rank() > 0) { @@ -684,8 +684,7 @@ public: template requires is_constructible_v - constexpr explicit(extents_type::rank() > 0) - mapping(const layout_stride::template mapping<_OtherExtents>& _Other) noexcept + constexpr explicit(extents_type::rank() > 0) mapping(const layout_stride::mapping<_OtherExtents>& _Other) noexcept : _Base(_Other.extents()) { #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (extents_type::rank() > 0) { @@ -1045,13 +1044,13 @@ struct default_accessor { requires is_convertible_v<_OtherElementType (*)[], element_type (*)[]> constexpr default_accessor(default_accessor<_OtherElementType>) noexcept {} - _NODISCARD constexpr data_handle_type offset(data_handle_type _Ptr, size_t _Idx) const noexcept { - return _Ptr + _Idx; - } - _NODISCARD constexpr reference access(data_handle_type _Ptr, size_t _Idx) const noexcept { return _Ptr[_Idx]; } + + _NODISCARD constexpr data_handle_type offset(data_handle_type _Ptr, size_t _Idx) const noexcept { + return _Ptr + _Idx; + } }; template @@ -1200,7 +1199,7 @@ public: _Ptr(_STD move(_Ptr_)) {} template - requires is_convertible_v<_OtherIndexType, index_type> + requires is_convertible_v && is_nothrow_constructible_v && (_Size == rank() || _Size == rank_dynamic()) && is_constructible_v && is_default_constructible_v @@ -1277,32 +1276,45 @@ public: noexcept(noexcept(_Access_impl(static_cast(_STD move(_Indices))...))) /* strengthened */ { return _Access_impl(static_cast(_STD move(_Indices))...); } -#endif // __cpp_multidimensional_subscript +private: + template + _NODISCARD constexpr reference _Multidimensional_subscript(span<_OtherIndexType, rank()> _Indices, + index_sequence<_Seq...>) const noexcept(noexcept(operator[](_STD as_const(_Indices[_Seq])...))) { + return operator[](_STD as_const(_Indices[_Seq])...); + } +#else // ^^^ defined(__cpp_multidimensional_subscript) / !defined(__cpp_multidimensional_subscript) vvv +private: + template + _NODISCARD constexpr reference _Multidimensional_access(_OtherIndexTypes... _Indices) const + noexcept(noexcept(_Access_impl(static_cast(_STD move(_Indices))...))) { + return _Access_impl(static_cast(_STD move(_Indices))...); + } + + template + _NODISCARD constexpr reference _Multidimensional_subscript(span<_OtherIndexType, rank()> _Indices, + index_sequence<_Seq...>) const noexcept(noexcept(_Multidimensional_access(_STD as_const(_Indices[_Seq])...))) { + return _Multidimensional_access(_STD as_const(_Indices[_Seq])...); + } +#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ + +public: template requires is_convertible_v && is_nothrow_constructible_v - _NODISCARD constexpr reference operator[](span<_OtherIndexType, rank()> _Indices) const { - 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{}); + _NODISCARD constexpr reference operator[](span<_OtherIndexType, rank()> _Indices) const + noexcept(noexcept(_Multidimensional_subscript(_Indices, make_index_sequence{}))) /* strengthened */ + { + return _Multidimensional_subscript(_Indices, make_index_sequence{}); } template requires is_convertible_v && is_nothrow_constructible_v - _NODISCARD constexpr reference operator[](const array<_OtherIndexType, rank()>& _Indices) const { - 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 reference operator[](const array<_OtherIndexType, rank()>& _Indices) const noexcept( + noexcept(_Multidimensional_subscript(span{_Indices}, make_index_sequence{}))) /* strengthened */ + { + return _Multidimensional_subscript(span{_Indices}, make_index_sequence{}); } _NODISCARD constexpr size_type size() const noexcept { @@ -1359,19 +1371,19 @@ public: return this->_Acc; } - _NODISCARD static constexpr bool is_always_unique() noexcept( - noexcept(mapping_type::is_always_unique())) /* strengthened */ { - return mapping_type::is_always_unique(); + _NODISCARD static constexpr bool is_always_unique() noexcept /* strengthened */ { + constexpr bool _Result = mapping_type::is_always_unique(); + return _Result; } - _NODISCARD static constexpr bool is_always_exhaustive() noexcept( - noexcept(mapping_type::is_always_exhaustive())) /* strengthened */ { - return mapping_type::is_always_exhaustive(); + _NODISCARD static constexpr bool is_always_exhaustive() noexcept /* strengthened */ { + constexpr bool _Result = mapping_type::is_always_exhaustive(); + return _Result; } - _NODISCARD static constexpr bool is_always_strided() noexcept( - noexcept(mapping_type::is_always_strided())) /* strengthened */ { - return mapping_type::is_always_strided(); + _NODISCARD static constexpr bool is_always_strided() noexcept /* strengthened */ { + constexpr bool _Result = mapping_type::is_always_strided(); + return _Result; } _NODISCARD constexpr bool is_unique() const noexcept(noexcept(this->_Map.is_unique())) /* strengthened */ { @@ -1392,16 +1404,9 @@ public: } private: -#ifndef __cpp_multidimensional_subscript // TRANSITION, P2128R6 - template - _NODISCARD constexpr reference _Multidimensional_access(_OtherIndexTypes... _Indices) const { - return _Access_impl(static_cast(_STD move(_Indices))...); - } -#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ - template _NODISCARD constexpr reference _Access_impl(_OtherIndexTypes... _Indices) const - noexcept(noexcept(this->_Acc.access(_Ptr, static_cast(this->_Map(_Indices...))))) /* strengthened */ { + noexcept(noexcept(this->_Acc.access(_Ptr, static_cast(this->_Map(_Indices...))))) { _STL_INTERNAL_STATIC_ASSERT((same_as<_OtherIndexTypes, index_type> && ...)); #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(this->_Map.extents()._Contains_multidimensional_index(make_index_sequence{}, _Indices...), From e2298333955e3bf0ba8b7236f58ef5dd0be8f473 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 15 Aug 2023 17:09:29 -0700 Subject: [PATCH 42/57] Update `#endif` comment for consistency. --- stl/inc/yvals_core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 082711a68b..05976ffb03 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -1794,7 +1794,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #ifdef __cpp_lib_concepts #define __cpp_lib_mdspan 202207L -#endif // __cpp_lib_concepts +#endif // defined(__cpp_lib_concepts) #if !defined(__clang__) && !defined(__EDG__) // TRANSITION, Clang and EDG support for modules #define __cpp_lib_modules 202207L From 015f590aecbcb06f69d156da0514fdf5a5affa3e Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 5 Sep 2023 16:53:16 -0700 Subject: [PATCH 43/57] Fix stealth merge conflict with GH 3977. --- stl/modules/std.ixx | 2 ++ tests/std/include/test_header_units_and_modules.hpp | 4 ++++ .../std/tests/P1502R1_standard_library_header_units/test.cpp | 2 ++ 3 files changed, 8 insertions(+) diff --git a/stl/modules/std.ixx b/stl/modules/std.ixx index ef14a79936..7a5fdbf7ce 100644 --- a/stl/modules/std.ixx +++ b/stl/modules/std.ixx @@ -78,7 +78,9 @@ export module std; #include #include #include +#if _HAS_CXX23 #include +#endif // _HAS_CXX23 #include #include #include diff --git a/tests/std/include/test_header_units_and_modules.hpp b/tests/std/include/test_header_units_and_modules.hpp index b35391d5a4..828a3e9bbc 100644 --- a/tests/std/include/test_header_units_and_modules.hpp +++ b/tests/std/include/test_header_units_and_modules.hpp @@ -391,6 +391,7 @@ void test_map() { assert(m[30] == 33); } +#if TEST_STANDARD >= 23 void test_mdspan() { using namespace std; puts("Testing ."); @@ -401,6 +402,7 @@ void test_mdspan() { assert(arr[mp(2, 2)] == 30); // TRANSITION, test std::mdspan too (DevCom-10359857) } +#endif // TEST_STANDARD >= 23 void test_memory() { using namespace std; @@ -1122,7 +1124,9 @@ void all_cpp_header_tests() { test_list(); test_locale(); test_map(); +#if TEST_STANDARD >= 23 test_mdspan(); +#endif // TEST_STANDARD >= 23 test_memory(); test_memory_resource(); test_mutex(); diff --git a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp index 6d19049e7b..1617b5e285 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -48,7 +48,9 @@ import ; import ; import ; import ; +#if TEST_STANDARD >= 23 import ; +#endif // TEST_STANDARD >= 23 import ; import ; import ; From d7fb94e8e987c31620ba508afc4fdaa33016bd04 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 11 Sep 2023 22:12:57 -0700 Subject: [PATCH 44/57] Fix Clang 17 failures in P0009R18_mdspan_mdspan --- tests/std/tests/P0009R18_mdspan_mdspan/test.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp b/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp index 542fda4ecf..5bf7c4d12f 100644 --- a/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp @@ -122,7 +122,7 @@ struct TrackingLayout { } template - constexpr index_type operator()(IndexTypes... indices) const { + constexpr index_type operator()(IndexTypes... indices) const noexcept(noexcept(mp(indices...))) { return mp(indices...); } @@ -192,7 +192,7 @@ struct VectorBoolAccessor { using reference = vector::reference; using data_handle_type = vector::iterator; - constexpr reference access(data_handle_type handle, size_t off) const { + constexpr reference access(data_handle_type handle, size_t off) const noexcept { return handle[static_cast(off)]; } @@ -877,7 +877,7 @@ constexpr void check_multidimensional_subscript_operator() { static_assert(CanCallMultidimSubscriptOp); static_assert(CanCallMultidimSubscriptOp, int, int, int, int>); #ifndef __clang__ // TRANSITION, Clang 17 - static_assert(!CanCallSubscriptOperator); + static_assert(!CanCallMultidimSubscriptOp); #endif // __clang__ } @@ -885,7 +885,7 @@ constexpr void check_multidimensional_subscript_operator() { using Mds = mdspan>; static_assert(CanCallMultidimSubscriptOp, int>); #ifndef __clang__ // TRANSITION, Clang 17 - static_assert(!CanCallSubscriptOperator, int>); + static_assert(!CanCallMultidimSubscriptOp, int>); #endif // __clang__ } @@ -893,8 +893,8 @@ constexpr void check_multidimensional_subscript_operator() { using Mds = mdspan>; static_assert(CanCallMultidimSubscriptOp); #ifndef __clang__ // TRANSITION, Clang 17 - static_assert(!CanCallSubscriptOperator); - static_assert(!CanCallSubscriptOperator); + static_assert(!CanCallMultidimSubscriptOp); + static_assert(!CanCallMultidimSubscriptOp); #endif // __clang__ } From a25bbfea22b62059a6b1845fdf7f186f53f299ff Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 11 Sep 2023 22:21:54 -0700 Subject: [PATCH 45/57] Clarify error messages --- stl/inc/mdspan | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 18bfbd3242..9174d2d7e8 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -49,7 +49,7 @@ public: static_assert(_Is_standard_integer, "IndexType must be a signed or unsigned integer type (N4950 [mdspan.extents.overview]/1.1)."); static_assert(((_Extents == dynamic_extent || _STD in_range(_Extents)) && ...), - "Each element of Extents must be either equal to dynamic_extent, or must be representable as a value of type " + "Each element of Extents must either be equal to dynamic_extent, or be representable as a value of type " "IndexType (N4950 [mdspan.extents.overview]/1.2)."); static constexpr rank_type _Rank = sizeof...(_Extents); @@ -146,8 +146,8 @@ private: #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (_Is_standard_integer<_OtherIndexType> && _Size != 0) { _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)"); + "exts[r] must be representable as a nonnegative value of type index_type for every rank index r " + "(N4950 [mdspan.extents.cons]/10.2)"); } #endif // _CONTAINER_DEBUG_LEVEL > 0 } @@ -165,8 +165,8 @@ private: "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(_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)"); + "exts[r] must be representable as a nonnegative value of type index_type for every rank index r " + "(N4950 [mdspan.extents.cons]/10.2)"); } } #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -232,9 +232,8 @@ public: 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)"); + _STL_VERIFY((_Check_extent(_Exts) && ...), "Each argument must be representable as a nonnegative value of type " + "index_type (N4950 [mdspan.extents.cons]/7.2)"); #endif // _CONTAINER_DEBUG_LEVEL > 0 } From 89e75062cb7e29696435f992af25fe5491740279 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 11 Sep 2023 22:36:30 -0700 Subject: [PATCH 46/57] Remove unnecessary _Size template parameter from extents constructor --- stl/inc/mdspan | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 9174d2d7e8..932119200d 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -138,13 +138,13 @@ private: #endif // _CONTAINER_DEBUG_LEVEL > 0 } - template + template requires is_convertible_v - && is_nothrow_constructible_v && (_Size == _Rank_dynamic) - constexpr explicit extents(span<_OtherIndexType, _Size> _Dynamic_exts, index_sequence<_Indices...>) noexcept + && is_nothrow_constructible_v + constexpr explicit extents(span<_OtherIndexType, _Rank_dynamic> _Dynamic_exts, index_sequence<_Indices...>) noexcept : _Base{static_cast(_STD as_const(_Dynamic_exts[_Indices]))...} { #if _CONTAINER_DEBUG_LEVEL > 0 - if constexpr (_Is_standard_integer<_OtherIndexType> && _Size != 0) { + if constexpr (_Is_standard_integer<_OtherIndexType> && _Rank_dynamic != 0) { _STL_VERIFY(((_Dynamic_exts[_Indices] >= 0 && _STD in_range(_Dynamic_exts[_Indices])) && ...), "exts[r] must be representable as a nonnegative value of type index_type for every rank index r " "(N4950 [mdspan.extents.cons]/10.2)"); From f2b4b5d9dd45ad5443a075726e5082286f0b50e9 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 11 Sep 2023 22:37:19 -0700 Subject: [PATCH 47/57] Convert private extents constructor constraints to _STL_INTERNAL_STATIC_ASSERTs --- stl/inc/mdspan | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 932119200d..9f27d9d50d 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -91,11 +91,12 @@ private: using _Base = _Maybe_empty_array<_IndexType, _Rank_dynamic>; 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 : _Base{static_cast(_Other.extent(_Dynamic_indices_inv[_Indices]))...} { + _STL_INTERNAL_STATIC_ASSERT(sizeof...(_OtherExtents) == _Rank); + _STL_INTERNAL_STATIC_ASSERT( + ((_OtherExtents == dynamic_extent || _Extents == dynamic_extent || _OtherExtents == _Extents) && ...)); #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (rank() > 0) { for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) { @@ -139,10 +140,10 @@ private: } template - requires is_convertible_v - && is_nothrow_constructible_v constexpr explicit extents(span<_OtherIndexType, _Rank_dynamic> _Dynamic_exts, index_sequence<_Indices...>) noexcept : _Base{static_cast(_STD as_const(_Dynamic_exts[_Indices]))...} { + _STL_INTERNAL_STATIC_ASSERT(is_convertible_v + && is_nothrow_constructible_v); #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (_Is_standard_integer<_OtherIndexType> && _Rank_dynamic != 0) { _STL_VERIFY(((_Dynamic_exts[_Indices] >= 0 && _STD in_range(_Dynamic_exts[_Indices])) && ...), @@ -153,10 +154,11 @@ private: } template - requires is_convertible_v - && is_nothrow_constructible_v && (_Size != _Rank_dynamic) constexpr explicit extents(span<_OtherIndexType, _Size> _Mixed_exts, index_sequence<_Indices...>) noexcept : _Base{static_cast(_STD as_const(_Mixed_exts[_Dynamic_indices_inv[_Indices]]))...} { + _STL_INTERNAL_STATIC_ASSERT(_Size != _Rank_dynamic); + _STL_INTERNAL_STATIC_ASSERT(is_convertible_v + && is_nothrow_constructible_v); #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (_Is_standard_integer<_OtherIndexType>) { for (rank_type _Idx = 0; _Idx < _Rank; ++_Idx) { From ea8ea4acee3bc34ea335ba91ecf6a225da6e516a Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 11 Sep 2023 22:39:13 -0700 Subject: [PATCH 48/57] Extract _Construct_from_tuple from extents and rename to _Extents_from_tuple --- stl/inc/mdspan | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 9f27d9d50d..a45da1734f 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -39,6 +39,10 @@ struct _Maybe_empty_array<_IndexType, 0> {}; template inline constexpr size_t _Calculate_rank_dynamic = (static_cast(_Extents == dynamic_extent) + ... + 0); +struct _Extents_from_tuple { + explicit _Extents_from_tuple() = default; +}; + _EXPORT_STD template class extents : private _Maybe_empty_array<_IndexType, _Calculate_rank_dynamic<_Extents...>> { public: @@ -114,18 +118,14 @@ private: #endif // _CONTAINER_DEBUG_LEVEL > 0 } - struct _Construct_from_tuple { - explicit _Construct_from_tuple() = default; - }; - template requires (tuple_size_v<_ExtsTuple> == _Rank_dynamic) - constexpr explicit extents(_Construct_from_tuple, _ExtsTuple _Tpl, index_sequence<_Indices...>) noexcept + constexpr explicit extents(_Extents_from_tuple, _ExtsTuple _Tpl, index_sequence<_Indices...>) noexcept : _Base{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 + constexpr explicit extents(_Extents_from_tuple, _ExtsTuple _Tpl, index_sequence<_DynIndices...>) noexcept : _Base{static_cast(_STD move(_STD get<_Dynamic_indices_inv[_DynIndices]>(_Tpl)))...} { #if _CONTAINER_DEBUG_LEVEL > 0 [&](index_sequence<_MixedIndices...>) { @@ -222,7 +222,7 @@ public: && (is_nothrow_constructible_v && ...) && (sizeof...(_OtherIndexTypes) == rank_dynamic() || sizeof...(_OtherIndexTypes) == rank()) constexpr explicit extents(_OtherIndexTypes... _Exts) noexcept - : extents(_Construct_from_tuple{}, _STD tie(_Exts...), make_index_sequence{}) { + : extents(_Extents_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>) { From a3e66ade7af10e99d17a00113071d3c093c368bc Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 11 Sep 2023 22:49:36 -0700 Subject: [PATCH 49/57] Check layout_(left|right)::mapping constructor precondition everywhere the Standard mandates it --- stl/inc/mdspan | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index a45da1734f..421b0160f7 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -544,10 +544,10 @@ public: "extents().fwd-prod-of-extents(r) (N4950 [mdspan.layout.left.cons]/10.1)."); _Prod = static_cast(_Prod * this->_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 } @@ -696,10 +696,10 @@ public: "extents().rev-prod-of-extents(r) (N4950 [mdspan.layout.right.cons]/10.1)."); _Prod = static_cast(_Prod * this->_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 } From 946ff56cb801d4214800c488807558c1e4bbffce Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 11 Sep 2023 22:54:32 -0700 Subject: [PATCH 50/57] "Helper" constructor of layout_stride::mapping should be private --- stl/inc/mdspan | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 421b0160f7..5579c3b0c7 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -809,35 +809,12 @@ private: "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)."); -public: - constexpr mapping() noexcept : _Extents_base(extents_type{}) { - if constexpr (extents_type::rank() != 0) { - this->_Array.back() = 1; - for (rank_type _Idx = extents_type::_Rank - 1; _Idx-- > 0;) { -#if _CONTAINER_DEBUG_LEVEL > 0 - const bool _Overflow = - _Mul_overflow(this->_Array[_Idx + 1], this->_Exts.extent(_Idx + 1), this->_Array[_Idx]); - // NB: N4950 requires value of 'layout_right::mapping().required_span_size()' to be - // representable as a value of type 'index_type', but this is not enough. We need to require every - // single stride to be representable as a 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 - this->_Array[_Idx] = static_cast(this->_Array[_Idx + 1] * this->_Exts.extent(_Idx + 1)); -#endif // _CONTAINER_DEBUG_LEVEL > 0 - } - } - } - - constexpr mapping(const mapping&) noexcept = default; - template - requires is_convertible_v - && is_nothrow_constructible_v constexpr mapping(const extents_type& _Exts_, span<_OtherIndexType, extents_type::rank()> _Strides_, index_sequence<_Indices...>) noexcept : _Extents_base(_Exts_), _Strides_base{static_cast(_STD as_const(_Strides_[_Indices]))...} { + _STL_INTERNAL_STATIC_ASSERT(is_convertible_v + && is_nothrow_constructible_v); #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (extents_type::rank() != 0) { bool _Found_zero = false; @@ -864,6 +841,29 @@ public: #endif // _CONTAINER_DEBUG_LEVEL > 0 } +public: + constexpr mapping() noexcept : _Extents_base(extents_type{}) { + if constexpr (extents_type::rank() != 0) { + this->_Array.back() = 1; + for (rank_type _Idx = extents_type::_Rank - 1; _Idx-- > 0;) { +#if _CONTAINER_DEBUG_LEVEL > 0 + const bool _Overflow = + _Mul_overflow(this->_Array[_Idx + 1], this->_Exts.extent(_Idx + 1), this->_Array[_Idx]); + // NB: N4950 requires value of 'layout_right::mapping().required_span_size()' to be + // representable as a value of type 'index_type', but this is not enough. We need to require every + // single stride to be representable as a 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 + this->_Array[_Idx] = static_cast(this->_Array[_Idx + 1] * this->_Exts.extent(_Idx + 1)); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + } + } + } + + constexpr mapping(const mapping&) noexcept = default; + // TRANSITION, VSO-1852021: `_Extents::rank()` should be `extents_type::rank()` // TRANSITION, VSO-1852030: `typename _Extents::index_type` should be `index_type` template From 29ed185cfff3d82b0121f38b54a58e8281764c0a Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 11 Sep 2023 23:29:06 -0700 Subject: [PATCH 51/57] test coverage for layout_stride::mapping with empty extents and fixes for the resulting failures. --- stl/inc/mdspan | 24 +++++++++++++------ .../P0009R18_mdspan_layout_stride/test.cpp | 4 ++++ .../test.cpp | 7 ++++++ 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 5579c3b0c7..09e2db8441 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -897,13 +897,15 @@ public: _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 constexpr (extents_type::_Rank != 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)."); + _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 - this->_Array[_Idx] = static_cast(_Stride); + this->_Array[_Idx] = static_cast(_Stride); + } } } @@ -914,7 +916,11 @@ public: } _NODISCARD constexpr array strides() const noexcept { - return this->_Array; + if constexpr (extents_type::rank() == 0) { + return {}; + } else { + return this->_Array; + } } _NODISCARD constexpr index_type required_span_size() const noexcept { @@ -972,7 +978,11 @@ public: } _NODISCARD constexpr index_type stride(const rank_type _Idx) const noexcept { - return this->_Array[_Idx]; + if constexpr (extents_type::rank() == 0) { + _STL_VERIFY(false, "The argument to stride must be nonnegative and less than extents_type::rank()."); + } else { + return this->_Array[_Idx]; + } } template diff --git a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp index b495de4327..82cdd5d2ca 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp @@ -844,6 +844,10 @@ constexpr bool test() { check_members(extents{}, array{1}); check_members(extents{3}, array{1, 3, 6}); + // Check degenerate extents + check_members(extents{}, array{}); + check_members(extents{}, array{}); + if (!is_constant_evaluated()) { // too heavy for compile time check_mapping_properties(); } 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 d0e111bb69..3663f784d4 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride_death/test.cpp @@ -58,6 +58,12 @@ void test_call_operator() { (void) m(4, 3, 3); } +void test_stride_with_empty_extents() { + layout_stride::mapping> m; + // The argument to stride must be nonnegative and less than extents_type::rank() + (void) m.stride(0); +} + int main(int argc, char* argv[]) { std_testing::death_test_executive exec; exec.add_death_tests({ @@ -68,6 +74,7 @@ int main(int argc, char* argv[]) { test_construction_from_extents_and_span_2, test_construction_from_strided_layout_mapping, test_call_operator, + test_stride_with_empty_extents, }); return exec.run(argc, argv); } From 8a5c26d328f4ba72da50aa7ccd7e1b1ba66196e4 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 11 Sep 2023 23:30:10 -0700 Subject: [PATCH 52/57] Defend against non-`{}`-constructible types --- stl/inc/mdspan | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 09e2db8441..44f0a38230 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -1082,7 +1082,7 @@ struct _Mdspan_mapping_base { template constexpr explicit _Mdspan_mapping_base(const _OtherMapping& _Map_) : _Map(_Map_) {} - _Mapping _Map{}; + _Mapping _Map = _Mapping(); }; template _LayoutPolicy> @@ -1116,7 +1116,7 @@ struct _Mdspan_accessor_base { template constexpr explicit _Mdspan_accessor_base(const _OtherAccessorPolicy& _Acc_) : _Acc(_Acc_) {} - _AccessorPolicy _Acc{}; + _AccessorPolicy _Acc = _AccessorPolicy(); }; template <_Elidable_accessor_policy _AccessorPolicy> From 0b4c7b1f4602d727cdefb798f6d96a413743f0a2 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 11 Sep 2023 23:34:04 -0700 Subject: [PATCH 53/57] data_handle_type is always nothrow movable ... per the accessor policy requirements. --- stl/inc/mdspan | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 44f0a38230..c8ee5c5de0 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -1204,7 +1204,7 @@ public: && (sizeof...(_OtherIndexTypes) == rank() || sizeof...(_OtherIndexTypes) == rank_dynamic()) && is_constructible_v && is_default_constructible_v constexpr explicit mdspan(data_handle_type _Ptr_, _OtherIndexTypes... _Exts) noexcept( - is_nothrow_move_constructible_v&& is_nothrow_constructible_v&& + is_nothrow_constructible_v&& is_nothrow_default_constructible_v) // strengthened : _Mapping_base(extents_type{static_cast(_STD move(_Exts))...}), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {} @@ -1214,10 +1214,9 @@ public: && is_nothrow_constructible_v && (_Size == rank() || _Size == rank_dynamic()) && is_constructible_v && is_default_constructible_v - constexpr explicit(_Size != rank_dynamic()) - mdspan(data_handle_type _Ptr_, span<_OtherIndexType, _Size> _Exts) noexcept( - is_nothrow_move_constructible_v&& is_nothrow_constructible_v&& - is_nothrow_default_constructible_v) // strengthened + constexpr explicit(_Size != rank_dynamic()) mdspan(data_handle_type _Ptr_, + span<_OtherIndexType, _Size> _Exts) noexcept(is_nothrow_constructible_v&& + is_nothrow_default_constructible_v) // strengthened : _Mapping_base(extents_type{_Exts}), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {} template @@ -1225,27 +1224,25 @@ public: && is_nothrow_constructible_v && (_Size == rank() || _Size == rank_dynamic()) && is_constructible_v && is_default_constructible_v - constexpr explicit(_Size != rank_dynamic()) - mdspan(data_handle_type _Ptr_, const array<_OtherIndexType, _Size>& _Exts) noexcept( - is_nothrow_move_constructible_v&& is_nothrow_constructible_v&& - is_nothrow_default_constructible_v) // strengthened + constexpr explicit(_Size != rank_dynamic()) mdspan(data_handle_type _Ptr_, + const array<_OtherIndexType, _Size>& _Exts) noexcept(is_nothrow_constructible_v&& + is_nothrow_default_constructible_v) // strengthened : _Mapping_base(extents_type{_Exts}), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {} constexpr mdspan(data_handle_type _Ptr_, const extents_type& _Exts) noexcept( - is_nothrow_move_constructible_v&& is_nothrow_constructible_v&& is_nothrow_default_constructible_v) // strengthened requires is_constructible_v && is_default_constructible_v : _Mapping_base(_Exts), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {} constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_) noexcept( - is_nothrow_move_constructible_v&& is_nothrow_copy_constructible_v&& + is_nothrow_copy_constructible_v&& is_nothrow_default_constructible_v) // strengthened requires is_default_constructible_v : _Mapping_base(_Map_), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {} constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_, const accessor_type& _Acc_) noexcept( - is_nothrow_move_constructible_v&& is_nothrow_copy_constructible_v&& - is_nothrow_copy_constructible_v) // strengthened + is_nothrow_copy_constructible_v&& is_nothrow_copy_constructible_v) // strengthened : _Mapping_base(_Map_), _Accessor_base(_Acc_), _Ptr(_STD move(_Ptr_)) {} template From 4c4a04b661a2e862194c65540cc9573e6b2cb2a3 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 11 Sep 2023 23:45:32 -0700 Subject: [PATCH 54/57] Merge _Multidimensional_subscript overloads and avoid "detouring" through `operator[]` so we don't copy `OtherIndexType`s, which we aren't allowed to do. Fix test case that was expecting this bogosity. --- stl/inc/mdspan | 22 +++++-------------- .../std/tests/P0009R18_mdspan_mdspan/test.cpp | 5 ++--- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index c8ee5c5de0..4ce7f96256 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -1284,27 +1284,15 @@ public: noexcept(noexcept(_Access_impl(static_cast(_STD move(_Indices))...))) /* strengthened */ { return _Access_impl(static_cast(_STD move(_Indices))...); } +#endif // ^^^ defined(__cpp_multidimensional_subscript) ^^^ private: template - _NODISCARD constexpr reference _Multidimensional_subscript(span<_OtherIndexType, rank()> _Indices, - index_sequence<_Seq...>) const noexcept(noexcept(operator[](_STD as_const(_Indices[_Seq])...))) { - return operator[](_STD as_const(_Indices[_Seq])...); + _NODISCARD constexpr reference _Multidimensional_subscript( + span<_OtherIndexType, rank()> _Indices, index_sequence<_Seq...>) const + noexcept(noexcept(_Access_impl(static_cast(_STD as_const(_Indices[_Seq]))...))) { + return _Access_impl(static_cast(_STD as_const(_Indices[_Seq]))...); } -#else // ^^^ defined(__cpp_multidimensional_subscript) / !defined(__cpp_multidimensional_subscript) vvv -private: - template - _NODISCARD constexpr reference _Multidimensional_access(_OtherIndexTypes... _Indices) const - noexcept(noexcept(_Access_impl(static_cast(_STD move(_Indices))...))) { - return _Access_impl(static_cast(_STD move(_Indices))...); - } - - template - _NODISCARD constexpr reference _Multidimensional_subscript(span<_OtherIndexType, rank()> _Indices, - index_sequence<_Seq...>) const noexcept(noexcept(_Multidimensional_access(_STD as_const(_Indices[_Seq])...))) { - return _Multidimensional_access(_STD as_const(_Indices[_Seq])...); - } -#endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ public: template diff --git a/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp b/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp index 5bf7c4d12f..2997a5f9a7 100644 --- a/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp @@ -1016,8 +1016,7 @@ constexpr void check_span_array_subscript_operator() { using Ext = dextents; struct FunkyIndex { FunkyIndex() = default; - FunkyIndex(FunkyIndex&) = delete; - FunkyIndex(const FunkyIndex&) = default; + FunkyIndex(const FunkyIndex&) = delete; constexpr operator integral auto() const& noexcept { return 0; @@ -1027,7 +1026,7 @@ constexpr void check_span_array_subscript_operator() { return 0; } - constexpr operator Ext::index_type() && noexcept { + constexpr operator Ext::index_type() const& noexcept { return 1; } }; From 3382da9e85e867bc2adb549f444ddb8d175f4758 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 11 Sep 2023 23:50:44 -0700 Subject: [PATCH 55/57] Detach comments to correct strange wrapping in P0009R18_mdspan_layout_stride --- tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp index 82cdd5d2ca..d276b22e7f 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp @@ -58,8 +58,8 @@ struct LyingLayout { } static constexpr bool is_always_unique() { - return to_underlying( - Unique); // might be a lie, allowed by the standard (N4950 [mdspan.layout.reqmts]/23 Note 5) + // might be a lie, allowed by the standard (N4950 [mdspan.layout.reqmts]/23 Note 5) + return to_underlying(Unique); } static constexpr bool is_always_exhaustive() { @@ -67,8 +67,8 @@ struct LyingLayout { } static constexpr bool is_always_strided() { - return to_underlying( - Strided); // might be a lie, allowed by the standard (N4950 [mdspan.layout.reqmts]/27 Note 7) + // might be a lie, allowed by the standard (N4950 [mdspan.layout.reqmts]/27 Note 7) + return to_underlying(Strided); } }; }; From 90fb129fe31883a10fa1fd40142e9343b1e2411e Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 11 Sep 2023 23:50:58 -0700 Subject: [PATCH 56/57] Remove `constexpr` from undefined function declarations --- tests/std/include/test_mdspan_support.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/include/test_mdspan_support.hpp b/tests/std/include/test_mdspan_support.hpp index 9a49ebe6d4..12b94e4bb7 100644 --- a/tests/std/include/test_mdspan_support.hpp +++ b/tests/std/include/test_mdspan_support.hpp @@ -38,7 +38,7 @@ static_assert(!std::is_convertible_v struct NonConstConvertibleToInt { - constexpr operator Int() noexcept; // not defined + operator Int() noexcept; // not defined }; static_assert(std::is_convertible_v, int>); @@ -50,7 +50,7 @@ struct NonConvertibleToAnything {}; namespace detail { template - constexpr void check_implicit_conversion(T); // not defined + void check_implicit_conversion(T); // not defined } template From 7fe7858d0e0043d3737101deab74887e756e2625 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 12 Sep 2023 17:01:52 -0700 Subject: [PATCH 57/57] `_Ptr{}` => `_Ptr = data_handle_type()` --- stl/inc/mdspan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 4ce7f96256..ed037824dc 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -1412,7 +1412,7 @@ private: return this->_Acc.access(_Ptr, static_cast(this->_Map(_Indices...))); } - /* [[no_unique_address]] */ data_handle_type _Ptr{}; + /* [[no_unique_address]] */ data_handle_type _Ptr = data_handle_type(); }; template