Index: libcxx/include/CMakeLists.txt =================================================================== --- libcxx/include/CMakeLists.txt +++ libcxx/include/CMakeLists.txt @@ -482,6 +482,7 @@ __mdspan/extents.h __mdspan/layout_left.h __mdspan/layout_right.h + __mdspan/mdspan.h __memory/addressof.h __memory/align.h __memory/aligned_alloc.h Index: libcxx/include/__mdspan/layout_left.h =================================================================== --- libcxx/include/__mdspan/layout_left.h +++ libcxx/include/__mdspan/layout_left.h @@ -159,7 +159,7 @@ } private: - extents_type __extents_{}; // exposition only + _LIBCPP_NO_UNIQUE_ADDRESS extents_type __extents_{}; // exposition only }; #endif // _LIBCPP_STD_VER >= 23 Index: libcxx/include/__mdspan/layout_right.h =================================================================== --- libcxx/include/__mdspan/layout_right.h +++ libcxx/include/__mdspan/layout_right.h @@ -156,7 +156,7 @@ } private: - extents_type __extents_{}; // exposition only + _LIBCPP_NO_UNIQUE_ADDRESS extents_type __extents_{}; // exposition only }; #endif // _LIBCPP_STD_VER >= 23 Index: libcxx/include/__mdspan/mdspan.h =================================================================== --- /dev/null +++ libcxx/include/__mdspan/mdspan.h @@ -0,0 +1,316 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +//===---------------------------------------------------------------------===// + +#ifndef _LIBCPP___MDSPAN_MDSPAN_H +#define _LIBCPP___MDSPAN_MDSPAN_H + +#include <__assert> +#include <__config> +#include <__fwd/mdspan.h> +#include <__mdspan/default_accessor.h> +#include <__mdspan/extents.h> +#include <__type_traits/extent.h> +#include <__type_traits/is_abstract.h> +#include <__type_traits/is_array.h> +#include <__type_traits/is_constructible.h> +#include <__type_traits/is_convertible.h> +#include <__type_traits/is_default_constructible.h> +#include <__type_traits/is_nothrow_constructible.h> +#include <__type_traits/is_pointer.h> +#include <__type_traits/is_same.h> +#include <__type_traits/rank.h> +#include <__type_traits/remove_all_extents.h> +#include <__type_traits/remove_cv.h> +#include <__type_traits/remove_pointer.h> +#include <__type_traits/remove_reference.h> +#include <__utility/integer_sequence.h> +#include +#include +#include +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 23 + +// Helper for lightweight test checking that one did pass a layout policy as LayoutPolicy template argument +namespace __mdspan_detail { +template +concept __has_invalid_mapping = !requires { typename _Layout::template mapping<_Extents>; }; +} // namespace __mdspan_detail + +template < class _ElementType, + class _Extents, + class _LayoutPolicy = layout_right, + class _AccessorPolicy = default_accessor<_ElementType> > +class mdspan { +private: + static_assert(__mdspan_detail::__is_extents_v<_Extents>, + "mdspan: Extents template parameter must be a specialization of extents."); + static_assert(!is_array_v<_ElementType>, "mdspan: ElementType template parameter may not be an array type"); + static_assert(!is_abstract_v<_ElementType>, "mdspan: ElementType template parameter may not be an abstract class"); + static_assert(is_same_v<_ElementType, typename _AccessorPolicy::element_type>, + "mdspan: ElementType template parameter must match AccessorPolicy::element_type"); + static_assert(!__mdspan_detail::__has_invalid_mapping<_LayoutPolicy, _Extents>, + "mdspan: LayoutPolicy template parameter is invalid. A common mistake is to pass a layout mapping " + "instead of a layout policy"); + + // Helper class for index sequence fold operations + template > + struct __idx_fold; + + template + struct __idx_fold> { + _LIBCPP_HIDE_FROM_ABI static constexpr typename _Extents::size_type __size(mdspan const& __self) noexcept { + return ((__self.__map_.extents().extent(_Idxs)) * ... * typename _Extents::size_type(1)); + } + _LIBCPP_HIDE_FROM_ABI static constexpr bool __empty(mdspan const& __self) noexcept { + return (__self.rank() > 0) && ((__self.__map_.extents().extent(_Idxs) == index_type(0)) || ... || false); + } + template + _LIBCPP_HIDE_FROM_ABI static constexpr _ReferenceType + __callop(mdspan const& __self, const array<_OtherIndexType, _Size>& __indices) noexcept { + return __self.__acc_.access(__self.__ptr_, __self.__map_(__indices[_Idxs]...)); + } + _LIBCPP_HIDE_FROM_ABI static constexpr bool __size_is_representable(mdspan const& __self) noexcept { + typename _Extents::size_type __prod = 1; + return (__builtin_mul_overflow(__prod, __self.extent(_Idxs), &__prod) || ... || false); + } + }; + +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; + + _LIBCPP_HIDE_FROM_ABI static constexpr rank_type rank() noexcept { return extents_type::rank(); } + _LIBCPP_HIDE_FROM_ABI static constexpr rank_type rank_dynamic() noexcept { return extents_type::rank_dynamic(); } + _LIBCPP_HIDE_FROM_ABI static constexpr size_t static_extent(rank_type __r) noexcept { + return extents_type::static_extent(__r); + } + _LIBCPP_HIDE_FROM_ABI constexpr index_type extent(rank_type __r) const noexcept { return __map_.extents().extent(__r); }; + +public: + //-------------------------------------------------------------------------------- + // [mdspan.basic.cons], mdspan constructors, assignment, and destructor + + _LIBCPP_HIDE_FROM_ABI constexpr mdspan() + requires( + // nvhpc has a bug where using just rank_dynamic() here doesn't work ... + (extents_type::rank_dynamic() > 0) && is_default_constructible_v && + is_default_constructible_v && is_default_constructible_v) + = default; + _LIBCPP_HIDE_FROM_ABI constexpr mdspan(const mdspan&) = default; + _LIBCPP_HIDE_FROM_ABI constexpr mdspan(mdspan&&) = default; + + template + requires((is_convertible_v<_OtherIndexTypes, index_type> && ...) && + (is_nothrow_constructible_v && ...) && + ((sizeof...(_OtherIndexTypes) == rank()) || (sizeof...(_OtherIndexTypes) == rank_dynamic())) && + is_constructible_v && is_default_constructible_v) + _LIBCPP_HIDE_FROM_ABI explicit constexpr mdspan(data_handle_type __p, _OtherIndexTypes... __exts) + : __ptr_(std::move(__p)), __map_(extents_type(static_cast(std::move(__exts))...)), __acc_{} {} + + template + requires(is_convertible_v && + is_nothrow_constructible_v && + ((_Size == rank()) || (_Size == rank_dynamic())) && is_constructible_v && + is_default_constructible_v) + explicit(_Size != rank_dynamic()) + _LIBCPP_HIDE_FROM_ABI constexpr mdspan(data_handle_type __p, const array<_OtherIndexType, _Size>& __exts) + : __ptr_(std::move(__p)), __map_(extents_type(__exts)), __acc_{} {} + + template < class _OtherIndexType, size_t _Size> + requires(is_convertible_v<_OtherIndexType, index_type> && is_nothrow_constructible_v && + ((_Size == rank()) || (_Size == rank_dynamic())) && is_constructible_v && + is_default_constructible_v) + explicit(_Size != rank_dynamic()) + _LIBCPP_HIDE_FROM_ABI constexpr mdspan(data_handle_type __p, span<_OtherIndexType, _Size> __exts) + : __ptr_(std::move(__p)), __map_(extents_type(__exts)), __acc_{} {} + + _LIBCPP_HIDE_FROM_ABI constexpr mdspan(data_handle_type __p, const extents_type& __exts) + requires(is_default_constructible_v && is_constructible_v) + : __ptr_(std::move(__p)), __map_(__exts), __acc_{} {} + + _LIBCPP_HIDE_FROM_ABI constexpr mdspan(data_handle_type __p, const mapping_type& __m) + requires(is_default_constructible_v) + : __ptr_(std::move(__p)), __map_(__m), __acc_{} {} + + _LIBCPP_HIDE_FROM_ABI constexpr mdspan(data_handle_type __p, const mapping_type& __m, const accessor_type& __a) + : __ptr_(std::move(__p)), __map_(__m), __acc_(__a) {} + + template < class _OtherElementType, class _OtherExtents, class _OtherLayoutPolicy, class _OtherAccessor> + requires(is_constructible_v> && + is_constructible_v) + explicit(!is_convertible_v&, mapping_type> || + !is_convertible_v) + _LIBCPP_HIDE_FROM_ABI constexpr mdspan( + const mdspan<_OtherElementType, _OtherExtents, _OtherLayoutPolicy, _OtherAccessor>& __other) + : __ptr_(__other.__ptr_), __map_(__other.__map_), __acc_(__other.__acc_) { + static_assert(is_constructible_v, + "mdspan: incompatible data_handle_type for mdspan construction"); + static_assert( + is_constructible_v, "mdspan: incompatible extents for mdspan construction"); + + // The following precondition is part of the standard, but is unlikely to be triggered. + // The extents constructor checkes this and the mapping must be storing the extents, since + // its extents() function returns a const reference to extents_type. + // The only way this can be triggered is if the mapping conversion constructor would for example + // construct its extents() always only from the dynamic extents, instead of from the other extents. + if constexpr (rank() > 0) { + for (size_t __r = 0; __r < rank(); __r++) { + _LIBCPP_ASSERT_UNCATEGORIZED( + (static_extent(__r) == dynamic_extent) || + (static_cast(__other.extent(__r)) == static_cast(static_extent(__r))), + "mdspan: conversion mismatch of source dynamic extents with static extents"); + } + } + } + + _LIBCPP_HIDE_FROM_ABI constexpr mdspan& operator=(const mdspan&) = default; + _LIBCPP_HIDE_FROM_ABI constexpr mdspan& operator=(mdspan&&) = default; + + //-------------------------------------------------------------------------------- + // [mdspan.basic.mapping], mdspan mapping domain multidimensional index to access codomain element + + template < class... _OtherIndexTypes> + requires((is_convertible_v<_OtherIndexTypes, index_type> && ...) && + (is_nothrow_constructible_v && ...) && + (rank() == sizeof...(_OtherIndexTypes))) + _LIBCPP_HIDE_FROM_ABI constexpr reference operator[](_OtherIndexTypes... __indices) const { + _LIBCPP_ASSERT(__mdspan_detail::__is_multidimensional_index_in(extents(), __indices...), + "mdspan: operator[] out of bounds access"); + return __acc_.access(__ptr_, __map_(static_cast(std::move(__indices))...)); + } + + template < class _OtherIndexType> + requires(is_convertible_v<_OtherIndexType, index_type> && is_nothrow_constructible_v) + _LIBCPP_HIDE_FROM_ABI constexpr reference operator[](const array< _OtherIndexType, rank()>& __indices) const { + return __idx_fold<>::template __callop(*this, __indices); + } + + template < class _OtherIndexType> + requires(is_convertible_v<_OtherIndexType, index_type> && is_nothrow_constructible_v) + _LIBCPP_HIDE_FROM_ABI constexpr reference operator[](span<_OtherIndexType, rank()> __indices) const { + return __idx_fold<>::template __callop(*this, __indices); + } + + _LIBCPP_HIDE_FROM_ABI constexpr size_type size() const noexcept { + _LIBCPP_ASSERT_UNCATEGORIZED( + __idx_fold<>::__size_is_representable(*this), "mdspan: size() is not representable as size_type"); + return __idx_fold<>::__size(*this); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool empty() const noexcept { return __idx_fold<>::__empty(*this); } + + _LIBCPP_HIDE_FROM_ABI friend constexpr void swap(mdspan& __x, mdspan& __y) noexcept { + swap(__x.__ptr_, __y.__ptr_); + swap(__x.__map_, __y.__map_); + swap(__x.__acc_, __y.__acc_); + } + + //-------------------------------------------------------------------------------- + // [mdspan.basic.domobs], mdspan observers of the domain multidimensional index space + + _LIBCPP_HIDE_FROM_ABI constexpr const extents_type& extents() const noexcept { return __map_.extents(); }; + _LIBCPP_HIDE_FROM_ABI constexpr const data_handle_type& data_handle() const noexcept { return __ptr_; }; + _LIBCPP_HIDE_FROM_ABI constexpr const mapping_type& mapping() const noexcept { return __map_; }; + _LIBCPP_HIDE_FROM_ABI constexpr const accessor_type& accessor() const noexcept { return __acc_; }; + + //-------------------------------------------------------------------------------- + // [mdspan.basic.obs], mdspan observers of the mapping + + _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_unique() { return mapping_type::is_always_unique(); }; + _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_exhaustive() { return mapping_type::is_always_exhaustive(); }; + _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_strided() { return mapping_type::is_always_strided(); }; + + _LIBCPP_HIDE_FROM_ABI constexpr bool is_unique() const { return __map_.is_unique(); }; + _LIBCPP_HIDE_FROM_ABI constexpr bool is_exhaustive() const { return __map_.is_exhaustive(); }; + _LIBCPP_HIDE_FROM_ABI constexpr bool is_strided() const { return __map_.is_strided(); }; + _LIBCPP_HIDE_FROM_ABI constexpr index_type stride(rank_type __r) const { return __map_.stride(__r); }; + +private: + _LIBCPP_NO_UNIQUE_ADDRESS data_handle_type __ptr_{}; + _LIBCPP_NO_UNIQUE_ADDRESS mapping_type __map_{}; + _LIBCPP_NO_UNIQUE_ADDRESS accessor_type __acc_{}; + + template + friend class mdspan; +}; + +template < class _ElementType, class... _OtherIndexTypes> + requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0)) +explicit mdspan(_ElementType*, _OtherIndexTypes...) + -> mdspan<_ElementType, dextents>; + +template < class Pointer> + requires(is_pointer_v>) +mdspan(Pointer&&) -> mdspan>, extents>; + +template < class CArray> + requires(is_array_v && (rank_v == 1)) +mdspan(CArray&) -> mdspan, extents>>; + +template +mdspan(_ElementType*, const array<_OtherIndexType, _Size>&) -> mdspan<_ElementType, dextents>; + +template +mdspan(_ElementType*, span<_OtherIndexType, _Size>) -> mdspan<_ElementType, dextents>; + +// This one is necessary because all the constructors take `data_handle_type`s, not +// `_ElementType*`s, and `data_handle_type` is taken from `accessor_type::data_handle_type`, which +// seems to throw off automatic deduction guides. +template +mdspan(_ElementType*, const extents<_OtherIndexType, _ExtentsPack...>&) + -> mdspan<_ElementType, extents<_OtherIndexType, _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; + +#endif // _LIBCPP_STD_VER >= 23 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___MDSPAN_MDSPAN_H Index: libcxx/include/mdspan =================================================================== --- libcxx/include/mdspan +++ libcxx/include/mdspan @@ -208,6 +208,135 @@ }; } +// mdspan synopsis + +namespace std { + template> + class mdspan { + public: + 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; + + static constexpr rank_type rank() noexcept { return extents_type::rank(); } + static constexpr rank_type rank_dynamic() noexcept { return extents_type::rank_dynamic(); } + static constexpr size_t static_extent(rank_type r) noexcept + { return extents_type::static_extent(r); } + constexpr index_type extent(rank_type r) const noexcept { return extents().extent(r); } + + // [mdspan.mdspan.cons], constructors + constexpr mdspan(); + constexpr mdspan(const mdspan& rhs) = default; + constexpr mdspan(mdspan&& rhs) = default; + + template + constexpr explicit mdspan(data_handle_type ptr, OtherIndexTypes... exts); + template + constexpr explicit(N != rank_dynamic()) + mdspan(data_handle_type p, span exts); + template + constexpr explicit(N != rank_dynamic()) + mdspan(data_handle_type p, const array& exts); + constexpr mdspan(data_handle_type p, const extents_type& ext); + constexpr mdspan(data_handle_type p, const mapping_type& m); + constexpr mdspan(data_handle_type p, const mapping_type& m, const accessor_type& a); + + template + constexpr explicit(see below) + mdspan(const mdspan& other); + + constexpr mdspan& operator=(const mdspan& rhs) = default; + constexpr mdspan& operator=(mdspan&& rhs) = default; + + // [mdspan.mdspan.members], members + template + constexpr reference operator[](OtherIndexTypes... indices) const; + template + constexpr reference operator[](span indices) const; + template + constexpr reference operator[](const array& indices) const; + + constexpr size_type size() const noexcept; + [[nodiscard]] constexpr bool empty() const noexcept; + + friend constexpr void swap(mdspan& x, mdspan& y) noexcept; + + constexpr const extents_type& extents() const noexcept { return map_.extents(); } + constexpr const data_handle_type& data_handle() const noexcept { return ptr_; } + constexpr const mapping_type& mapping() const noexcept { return map_; } + constexpr const accessor_type& accessor() const noexcept { return acc_; } + + static constexpr bool is_always_unique() + { return mapping_type::is_always_unique(); } + static constexpr bool is_always_exhaustive() + { return mapping_type::is_always_exhaustive(); } + static constexpr bool is_always_strided() + { return mapping_type::is_always_strided(); } + + constexpr bool is_unique() const + { return map_.is_unique(); } + constexpr bool is_exhaustive() const + { return map_.is_exhaustive(); } + constexpr bool is_strided() const + { return map_.is_strided(); } + constexpr index_type stride(rank_type r) const + { return map_.stride(r); } + + private: + accessor_type acc_; // exposition only + mapping_type map_; // exposition only + data_handle_type ptr_; // exposition only + }; + + template + requires(is_array_v && rank_v == 1) + mdspan(CArray&) + -> mdspan, extents>>; + + template + requires(is_pointer_v>) + mdspan(Pointer&&) + -> mdspan>, extents>; + + template + requires((is_convertible_v && ...) && sizeof...(Integrals) > 0) + explicit mdspan(ElementType*, Integrals...) + -> mdspan>; + + template + mdspan(ElementType*, span) + -> mdspan>; + + template + mdspan(ElementType*, const array&) + -> mdspan>; + + template + mdspan(ElementType*, const extents&) + -> mdspan>; + + template + mdspan(ElementType*, const MappingType&) + -> mdspan; + + template + mdspan(const typename AccessorType::data_handle_type&, const MappingType&, + const AccessorType&) + -> mdspan; +} */ #ifndef _LIBCPP_MDSPAN @@ -219,6 +348,7 @@ #include <__mdspan/extents.h> #include <__mdspan/layout_left.h> #include <__mdspan/layout_right.h> +#include <__mdspan/mdspan.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header Index: libcxx/include/module.modulemap.in =================================================================== --- libcxx/include/module.modulemap.in +++ libcxx/include/module.modulemap.in @@ -1247,6 +1247,7 @@ } module layout_left { private header "__mdspan/layout_left.h" } module layout_right { private header "__mdspan/layout_right.h" } + module mdspan { private header "__mdspan/mdspan.h" } module mdspan_fwd { private header "__fwd/mdspan.h" } } } Index: libcxx/modules/std/mdspan.cppm =================================================================== --- libcxx/modules/std/mdspan.cppm +++ libcxx/modules/std/mdspan.cppm @@ -27,5 +27,5 @@ using std::default_accessor; // [mdspan.mdspan], class template mdspan - // using std::mdspan; + using std::mdspan; } // namespace std Index: libcxx/test/std/containers/views/mdspan/mdspan/CustomTestAccessors.h =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/CustomTestAccessors.h @@ -0,0 +1,105 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +//===---------------------------------------------------------------------===// + +#ifndef TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_ACCESSORS_H +#define TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_ACCESSORS_H + +#include +#include + +// non default constructible data handle +template +struct no_default_ctor_handle { + T* ptr; + no_default_ctor_handle() = delete; + constexpr no_default_ctor_handle(T* ptr_) : ptr(ptr_) {} +}; + +// non-default constructible accessor +// when instantiated with double, also use non-default constructible, not T* data_handle +template +struct checked_accessor { + size_t N; + using offset_policy = std::default_accessor<_ElementType>; + using element_type = _ElementType; + using reference = _ElementType&; + using data_handle_type = _ElementType*; + + constexpr checked_accessor(size_t N_) : N(N_) {} + template + requires(std::is_convertible_v<_OtherElementType (*)[], element_type (*)[]>) + explicit _LIBCPP_HIDE_FROM_ABI constexpr checked_accessor(const checked_accessor<_OtherElementType>& other) noexcept { + N = other.N; + } + + _LIBCPP_HIDE_FROM_ABI constexpr reference access(data_handle_type __p, size_t __i) const noexcept { + assert(__i < N); + return __p[__i]; + } + _LIBCPP_HIDE_FROM_ABI constexpr data_handle_type offset(data_handle_type __p, size_t __i) const noexcept { + assert(__i < N); + return __p + __i; + } +}; + +template <> +struct checked_accessor { + size_t N; + using offset_policy = std::default_accessor; + using element_type = double; + using reference = double&; + using data_handle_type = no_default_ctor_handle; + + constexpr checked_accessor(size_t N_) : N(N_) {} + template + requires(std::is_convertible_v<_OtherElementType (*)[], element_type (*)[]>) + _LIBCPP_HIDE_FROM_ABI constexpr checked_accessor(const checked_accessor<_OtherElementType>& other) noexcept { + N = other.N; + } + + _LIBCPP_HIDE_FROM_ABI constexpr reference access(data_handle_type __p, size_t __i) const noexcept { + assert(__i < N); + return __p.ptr[__i]; + } + _LIBCPP_HIDE_FROM_ABI constexpr data_handle_type offset(data_handle_type __p, size_t __i) const noexcept { + assert(__i < N); + return __p.ptr + __i; + } +}; + +template <> +struct checked_accessor { + size_t N; + using offset_policy = std::default_accessor; + using element_type = const double; + using reference = const double&; + using data_handle_type = const double*; + + constexpr checked_accessor() : N(0) {} + constexpr checked_accessor(size_t N_) : N(N_) {} + constexpr checked_accessor(const checked_accessor& acc) : N(acc.N) {} + + _LIBCPP_HIDE_FROM_ABI constexpr reference access(data_handle_type __p, size_t __i) const noexcept { + assert(__i < N); + return __p[__i]; + } + _LIBCPP_HIDE_FROM_ABI constexpr data_handle_type offset(data_handle_type __p, size_t __i) const noexcept { + assert(__i < N); + return __p + __i; + } +}; + +#endif // TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_ACCESSORS_H Index: libcxx/test/std/containers/views/mdspan/mdspan/CustomTestLayouts.h =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/CustomTestLayouts.h @@ -0,0 +1,208 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +//===---------------------------------------------------------------------===// + +#ifndef TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_LAYOUTS_H +#define TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_LAYOUTS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Layout that wraps indices to test some idiosyncratic behavior +// - only accepts integers as indices +// - is_always_strided and is_always_unique are false +// - is_strided and is_unique are true if all extents are smaller than _Wrap +// - not default constructible +// - not extents constructible +// - not trivally copyable +// - does not check dynamic to static extent conversion in converting ctor +// - check via sideeffects that mdspan::swap calls mappings swap via ADL + +struct not_extents_constructible_tag {}; + +template +class layout_wrapping_integral { +public: + template + class mapping; +}; + +template +template +class layout_wrapping_integral<_WrapArg>::mapping { + static constexpr _Extents::index_type _Wrap = static_cast<_Extents::index_type>(_WrapArg); + +public: + static_assert(std::__mdspan_detail::__is_extents<_Extents>::value, + "layout_left::mapping template argument must be a specialization of extents."); + + 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 layout_type = layout_wrapping_integral<_Wrap>; + +private: + _LIBCPP_HIDE_FROM_ABI static constexpr bool __required_span_size_is_representable(const extents_type& __ext) { + if constexpr (extents_type::rank() == 0) + return true; + + index_type __prod = __ext.extent(0); + for (rank_type __r = 1; __r < extents_type::rank(); __r++) { + bool __overflowed = __builtin_mul_overflow(__prod, __ext.extent(__r), &__prod); + if (__overflowed) + return false; + } + return true; + } + + static_assert((extents_type::rank_dynamic() > 0) || __required_span_size_is_representable(extents_type()), + "layout_left::mapping product of static extents must be representable as index_type."); + +public: + // [mdspan.layout.left.cons], constructors + _LIBCPP_HIDE_FROM_ABI constexpr mapping() noexcept = delete; + _LIBCPP_HIDE_FROM_ABI constexpr mapping(const mapping& other) noexcept : __extents_(other.extents()){}; + _LIBCPP_HIDE_FROM_ABI constexpr mapping(const extents_type& __ext, not_extents_constructible_tag) noexcept + : __extents_(__ext) { + _LIBCPP_ASSERT(__required_span_size_is_representable(__ext), + "layout_left::mapping extents ctor: product of extents must be representable as index_type."); + } + + template + requires(std::is_constructible_v) + _LIBCPP_HIDE_FROM_ABI constexpr explicit(!std::is_convertible_v<_OtherExtents, extents_type>) + mapping(const mapping<_OtherExtents>& __other) noexcept { + _LIBCPP_ASSERT( + std::__mdspan_detail::__is_representable_as(__other.required_span_size()), + "layout_left::mapping converting ctor: other.required_span_size() must be representable as index_type."); + std::array dyn_extents; + rank_type count = 0; + for (rank_type r = 0; r < extents_type::rank(); r++) { + if (extents_type::static_extent(r) == std::dynamic_extent) { + dyn_extents[count++] = __other.extents().extent(r); + } + } + __extents_ = extents_type(dyn_extents); + } + + _LIBCPP_HIDE_FROM_ABI constexpr mapping& operator=(const mapping&) noexcept = default; + + // [mdspan.layout.left.obs], observers + _LIBCPP_HIDE_FROM_ABI constexpr const extents_type& extents() const noexcept { return __extents_; } + + _LIBCPP_HIDE_FROM_ABI constexpr index_type required_span_size() const noexcept { + index_type __size = 1; + for (size_t __r = 0; __r < extents_type::rank(); __r++) + __size *= __extents_.extent(__r) < _Wrap ? __extents_.extent(__r) : _Wrap; + return __size; + } + + template + requires((sizeof...(_Indices) == extents_type::rank()) && (std::is_convertible_v<_Indices, index_type> && ...) && + (std::is_nothrow_constructible_v && ...)) + _LIBCPP_HIDE_FROM_ABI constexpr index_type operator()(_Indices... __idx) const noexcept { + _LIBCPP_ASSERT(std::__mdspan_detail::__is_multidimensional_index_in(__extents_, __idx...), + "layout_left::mapping: out of bounds indexing"); + std::array __idx_a{ + static_cast(static_cast(__idx) % _Wrap)...}; + return [&](std::index_sequence<_Pos...>) { + index_type __res = 0; + ((__res = __idx_a[extents_type::rank() - 1 - _Pos] + + (__extents_.extent(extents_type::rank() - 1 - _Pos) < _Wrap + ? __extents_.extent(extents_type::rank() - 1 - _Pos) + : _Wrap) * + __res), + ...); + return __res; + }(std::make_index_sequence()); + } + + _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_unique() noexcept { return false; } + _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_exhaustive() noexcept { return true; } + _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_strided() noexcept { return false; } + + _LIBCPP_HIDE_FROM_ABI constexpr bool is_unique() const noexcept { + for (rank_type r = 0; r < extents_type::rank(); r++) { + if (__extents_.extent(r) > _Wrap) + return false; + } + return true; + } + _LIBCPP_HIDE_FROM_ABI static constexpr bool is_exhaustive() noexcept { return true; } + _LIBCPP_HIDE_FROM_ABI constexpr bool is_strided() const noexcept { + for (rank_type r = 0; r < extents_type::rank(); r++) { + if (__extents_.extent(r) > _Wrap) + return false; + } + return true; + } + + _LIBCPP_HIDE_FROM_ABI constexpr index_type stride(rank_type __r) const noexcept + requires(extents_type::rank() > 0) + { + _LIBCPP_ASSERT(__r < extents_type::rank(), "layout_left::mapping::stride(): invalid rank index"); + index_type __s = 1; + for (rank_type __i = extents_type::rank() - 1; __i > __r; __i--) + __s *= __extents_.extent(__i); + return __s; + } + + template + requires(_OtherExtents::rank() == extents_type::rank()) + _LIBCPP_HIDE_FROM_ABI friend constexpr bool + operator==(const mapping& __lhs, const mapping<_OtherExtents>& __rhs) noexcept { + return __lhs.extents() == __rhs.extents(); + } + + friend constexpr void swap(mapping& x, mapping& y) noexcept { + swap(x.__extents_, y.__extents_); + if !consteval { + swap_counter()++; + } + } + + static int& swap_counter() { + static int value = 0; + return value; + } + +private: + extents_type __extents_{}; // exposition only +}; + +template +constexpr auto construct_mapping(std::layout_left, Extents exts) { + return std::layout_left::mapping(exts); +} + +template +constexpr auto construct_mapping(std::layout_right, Extents exts) { + return std::layout_right::mapping(exts); +} + +template +constexpr auto construct_mapping(layout_wrapping_integral<_Wraps>, Extents exts) { + return typename layout_wrapping_integral<_Wraps>::template mapping(exts, not_extents_constructible_tag{}); +} + +#endif // TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_LAYOUTS_H Index: libcxx/test/std/containers/views/mdspan/mdspan/assert.conversion.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/assert.conversion.pass.cpp @@ -0,0 +1,66 @@ +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// REQUIRES: has-unix-headers +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// XFAIL: availability-verbose_abort-missing +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1 + +// + +// template +// constexpr explicit(see below) +// mdspan(const mdspan& other); +// +// Constraints: +// - is_constructible_v&> is true, and +// - is_constructible_v is true. +// Mandates: +// - is_constructible_v is +// - is_constructible_v is true. +// +// Preconditions: +// - For each rank index r of extents_type, static_extent(r) == dynamic_extent || static_extent(r) == other.extent(r) is true. +// - [0, map_.required_span_size()) is an accessible range of ptr_ and acc_ for values of ptr_, map_, and acc_ after the invocation of this constructor. +// +// Effects: +// - Direct-non-list-initializes ptr_ with other.ptr_, +// - direct-non-list-initializes map_ with other.map_, and +// - direct-non-list-initializes acc_ with other.acc_. +// +// Remarks: The expression inside explicit is equivalent to: +// !is_convertible_v&, mapping_type> +// || !is_convertible_v + +#include +#include +#include + +#include "check_assertion.h" +#include "CustomTestLayouts.h" + +// We use a funky mapping in this test that doesn't check the dynamic/static extents mismatch itself +int main(int, char**) { + constexpr size_t D = std::dynamic_extent; + std::array data; + typename layout_wrapping_integral<4>::template mapping> src_map( + std::dextents(5, 2), not_extents_constructible_tag()); + std::mdspan, layout_wrapping_integral<4>> arg(data.data(), src_map); + + // working case + { + [[maybe_unused]] std::mdspan, layout_wrapping_integral<4>> m(arg); // should work + } + // mismatch of static extent + { + TEST_LIBCPP_ASSERT_FAILURE( + ([=] { std::mdspan, layout_wrapping_integral<4>> m(arg); }()), + "mdspan: conversion mismatch of source dynamic extents with static extents"); + } + return 0; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/assert.index_operator.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/assert.index_operator.pass.cpp @@ -0,0 +1,90 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: has-unix-headers +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// XFAIL: availability-verbose_abort-missing +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1 + +// + +// template +// constexpr reference operator[](OtherIndexTypes... indices) const; +// Constraints: +// - (is_convertible_v && ...) is true, +// - (is_nothrow_constructible_v && ...) is true, and +// - sizeof...(OtherIndexTypes) == rank() is true. +// +// Let I be extents_type::index-cast(std::move(indices)). +// +// Preconditions: I is a multidimensional index in extents(). +// Note 1: This implies that map_(I) < map_.required_span_size() is true. +// +// Effects: Equivalent to: +// return acc_.access(ptr_, map_(static_cast(std::move(indices))...)); + +#include +#include + +#include "check_assertion.h" + +int main(int, char**) { + float data[1024]; + // value out of range + { + std::mdspan m(data, std::extents()); + TEST_LIBCPP_ASSERT_FAILURE(m[-1], "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE(m[-130], "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE(m[5], "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE(m[1000], "mdspan: operator[] out of bounds access"); + } + { + std::mdspan m(data, std::extents()); + TEST_LIBCPP_ASSERT_FAILURE(m[-1], "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE(m[-130], "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE(m[5], "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE(m[1000], "mdspan: operator[] out of bounds access"); + } + { + std::mdspan m(data, std::dextents(5)); + TEST_LIBCPP_ASSERT_FAILURE(m[-1], "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE(m[-130], "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE(m[5], "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE(m[1000], "mdspan: operator[] out of bounds access"); + } + { + std::mdspan m(data, std::dextents(5)); + TEST_LIBCPP_ASSERT_FAILURE(m[-1], "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE(m[-130], "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE(m[5], "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE(m[1000], "mdspan: operator[] out of bounds access"); + } + { + std::mdspan m(data, std::dextents(5, 7, 9)); + TEST_LIBCPP_ASSERT_FAILURE((m[-1, -1, -1]), "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE((m[-1, 0, 0]), "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE((m[0, -1, 0]), "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE((m[0, 0, -1]), "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE((m[5, 3, 3]), "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE((m[3, 7, 3]), "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE((m[3, 3, 9]), "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE((m[5, 7, 9]), "mdspan: operator[] out of bounds access"); + } + { + std::mdspan m(data, std::dextents(5, 7, 9)); + TEST_LIBCPP_ASSERT_FAILURE((m[-1, -1, -1]), "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE((m[-1, 0, 0]), "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE((m[0, -1, 0]), "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE((m[0, 0, -1]), "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE((m[5, 3, 3]), "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE((m[3, 7, 3]), "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE((m[3, 3, 9]), "mdspan: operator[] out of bounds access"); + TEST_LIBCPP_ASSERT_FAILURE((m[5, 7, 9]), "mdspan: operator[] out of bounds access"); + } + return 0; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/assert.size.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/assert.size.pass.cpp @@ -0,0 +1,40 @@ +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// REQUIRES: has-unix-headers +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// XFAIL: availability-verbose_abort-missing +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1 + +// + +// constexpr size_type size() const noexcept; +// +// Preconditions: The size of the multidimensional index space extents() is representable as a value of type size_type ([basic.fundamental]). +// +// Returns: extents().fwd-prod-of-extents(rank()). + +#include +#include +#include + +#include "check_assertion.h" +#include "CustomTestLayouts.h" + +// We use a funky mapping in this test where required_span_size is much smaller than the size of the index space +int main(int, char**) { + std::array data; + typename layout_wrapping_integral<4>::template mapping> map( + std::dextents(100, 6), not_extents_constructible_tag()); + std::mdspan, layout_wrapping_integral<4>> mds(data.data(), map); + // sanity check + assert(map.required_span_size() == char(16)); + // mismatch of static extent + { + TEST_LIBCPP_ASSERT_FAILURE(([=] { mds.size(); }()), "mdspan: size() is not representable as size_type"); + } + return 0; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/assign.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/assign.pass.cpp @@ -0,0 +1,105 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr mdspan(const mdspan&) = default; +// +// A specialization of mdspan is a trivially copyable type if its accessor_type, mapping_type, and data_handle_type are trivially copyable types. + +#include +#include +#include +#include + +#include "test_macros.h" + +#include "../MinimalElementType.h" +#include "CustomTestLayouts.h" +#include "CustomTestAccessors.h" + +template +constexpr void test_mdspan_types(const H& handle, const M& map, const A& acc) { + using MDS = std::mdspan; + + MDS m_org(handle, map, acc); + MDS m(handle, map, acc); + + // The defaulted assignment operator seems to be deprecated because: + // error: definition of implicit copy assignment operator for 'checked_accessor' is deprecated + // because it has a user-provided copy constructor [-Werror,-Wdeprecated-copy-with-user-provided-copy] + if constexpr (!std::is_same_v>) + m = m_org; + // even though the following checks out: + static_assert(std::copyable>); + static_assert(std::is_assignable_v, checked_accessor>); + + static_assert(noexcept(m = m_org)); + assert(m.extents() == map.extents()); + if constexpr (std::equality_comparable) + assert(m.data_handle() == handle); + if constexpr (std::equality_comparable) + assert(m.mapping() == map); + if constexpr (std::equality_comparable) + assert(m.accessor() == acc); +} + +template +constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) { + constexpr size_t D = std::dynamic_extent; + test_mdspan_types(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(7)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(2, 3)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(0, 3)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(1, 2, 3, 2)), acc); +} + +template +constexpr void mixin_layout(const H& handle, const A& acc) { + // make sure we test a trivially copyable mapping + static_assert(std::is_trivially_copyable_v>>); + mixin_extents(handle, std::layout_left(), acc); + mixin_extents(handle, std::layout_right(), acc); + // make sure we test a not trivially copyable mapping + static_assert( + !std::is_trivially_copyable_v::template mapping>>); + mixin_extents(handle, layout_wrapping_integral<4>(), acc); +} + +template +constexpr void mixin_accessor() { + ElementPool elements; + // make sure we test trivially constructable accessor and data_handle + static_assert(std::is_trivially_copyable_v>); + static_assert(std::is_trivially_copyable_v::data_handle_type>); + mixin_layout(elements.get_ptr(), std::default_accessor()); + + // Using weird accessor/data_handle + // Make sure they actually got the properties we want to test + // checked_accessor is noexcept copy constructible except for const double + checked_accessor acc(1024); + static_assert(noexcept(checked_accessor(acc)) != std::is_same_v); + mixin_layout(typename checked_accessor::data_handle_type(elements.get_ptr()), acc); +} + +constexpr bool test() { + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + return true; +} +int main(int, char**) { + (void)test(); + static_assert(test()); + return 0; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/conversion.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/conversion.pass.cpp @@ -0,0 +1,277 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// template +// constexpr explicit(see below) +// mdspan(const mdspan& other); +// +// Constraints: +// - is_constructible_v&> is true, and +// - is_constructible_v is true. +// Mandates: +// - is_constructible_v is +// - is_constructible_v is true. +// +// Preconditions: +// - For each rank index r of extents_type, static_extent(r) == dynamic_extent || static_extent(r) == other.extent(r) is true. +// - [0, map_.required_span_size()) is an accessible range of ptr_ and acc_ for values of ptr_, map_, and acc_ after the invocation of this constructor. +// +// Effects: +// - Direct-non-list-initializes ptr_ with other.ptr_, +// - direct-non-list-initializes map_ with other.map_, and +// - direct-non-list-initializes acc_ with other.acc_. +// +// Remarks: The expression inside explicit is equivalent to: +// !is_convertible_v&, mapping_type> +// || !is_convertible_v + +#include +#include +#include +#include + +#include "test_macros.h" + +#include "../MinimalElementType.h" +#include "CustomTestLayouts.h" +#include "CustomTestAccessors.h" + +// some weird things we rely on in cornercases: +// - pointer of element types would be convertible but accessors of those are not +static_assert(!std::is_constructible_v, checked_accessor>); +// - dynamic to static is not convertible +static_assert(!std::is_convertible_v, std::extents>); +// - checked_accessor is idiosyncratic: its never convertible between different types +static_assert(std::is_constructible_v, checked_accessor>); +static_assert(!std::is_convertible_v, checked_accessor>); +// - checked_accessor is idiosyncratic: its not constructible from const to non-const for double +static_assert(!std::is_constructible_v, checked_accessor>); +static_assert(!std::is_convertible_v, checked_accessor>); + +template +constexpr void test_implicit_conversion(ToMDS to_mds, FromMDS from_mds) { + assert(to_mds.extents() == from_mds.extents()); + if constexpr (std::equality_comparable_with) + assert(to_mds.data_handle() == from_mds.data_handle()); + if constexpr (std::equality_comparable_with) + assert(to_mds.mapping() == from_mds.mapping()); + if constexpr (std::equality_comparable_with) + assert(to_mds.accessor() == from_mds.accessor()); +} + +template +constexpr void test_conversion(FromMDS from_mds) { + using ToMDS = + std::mdspan; + + if constexpr (constructible) { + ToMDS to_mds(from_mds); + assert(to_mds.extents() == from_mds.extents()); + if constexpr (std::equality_comparable_with) + assert(to_mds.data_handle() == from_mds.data_handle()); + if constexpr (std::equality_comparable_with) + assert(to_mds.mapping() == from_mds.mapping()); + if constexpr (std::equality_comparable_with) + assert(to_mds.accessor() == from_mds.accessor()); + if constexpr (convertible) { + test_implicit_conversion(from_mds, from_mds); + } else { + static_assert(!std::is_convertible_v); + } + } else { + static_assert(!std::is_constructible_v); + } +} + +template +constexpr void mixin_tomds_extents(FromMDS m) { + using FromMapping = typename FromMDS::mapping_type; + constexpr size_t D = std::dynamic_extent; + { + using ToMapping = decltype(construct_mapping(L(), std::extents())); + test_conversion, + convertible && std::is_convertible_v, + ToMapping, + ToA>(m); + } + { + using ToMapping = decltype(construct_mapping(L(), std::extents())); + test_conversion, + convertible && std::is_convertible_v, + ToMapping, + ToA>(m); + } + { + using ToMapping = decltype(construct_mapping(L(), std::extents(3))); + test_conversion, + convertible && std::is_convertible_v, + ToMapping, + ToA>(m); + } + { + using ToMapping = decltype(construct_mapping(L(), std::extents())); + test_conversion, + convertible && std::is_convertible_v, + ToMapping, + ToA>(m); + } + { + using ToMapping = decltype(construct_mapping(L(), std::extents(3))); + test_conversion, + convertible && std::is_convertible_v, + ToMapping, + ToA>(m); + } + { + using ToMapping = decltype(construct_mapping(L(), std::extents())); + test_conversion, + convertible && std::is_convertible_v, + ToMapping, + ToA>(m); + } + { + using ToMapping = decltype(construct_mapping(L(), std::extents(3))); + test_conversion, + convertible && std::is_convertible_v, + ToMapping, + ToA>(m); + } + { + using ToMapping = decltype(construct_mapping(L(), std::extents(3, 5))); + test_conversion, + convertible && std::is_convertible_v, + ToMapping, + ToA>(m); + } + { + using ToMapping = decltype(construct_mapping(L(), std::extents(3, 5))); + test_conversion, + convertible && std::is_convertible_v, + ToMapping, + ToA>(m); + } + { + using ToMapping = decltype(construct_mapping(L(), std::extents(5))); + test_conversion, + convertible && std::is_convertible_v, + ToMapping, + ToA>(m); + } +} + +// defer more info on constructible/convertible until we got the mapping +template +constexpr void mixin_tomds_layout(FromMDS m) { + mixin_tomds_extents(m); + mixin_tomds_extents(m); + mixin_tomds_extents, ToA>(m); +} + +template +constexpr void mixin_tomds_accessor(FromMDS m) { + using FromA = typename FromMDS::accessor_type; + mixin_tomds_layout, const FromA&>, + std::is_convertible_v>, + std::default_accessor>(m); + + // Using weird accessor/data_handle + // Make sure they actually got the properties we want to test + // checked_accessor is not default constructible except for const double, where it is not noexcept + static_assert(std::is_default_constructible_v> == std::is_same_v); + // checked_accessor's data handle type is not default constructible for double + static_assert( + std::is_default_constructible_v::data_handle_type> != std::is_same_v); + mixin_tomds_layout, const FromA&>, + std::is_convertible_v>, + checked_accessor>(m); +} + +template +constexpr void mixin_tomds_element_type(FromMDS m) { + using T = typename FromMDS::element_type; + { + constexpr bool constructible = std::is_same_v; + mixin_tomds_accessor(m); + } + { + constexpr bool constructible = std::is_same_v || std::is_same_v; + mixin_tomds_accessor(m); + } + { + constexpr bool constructible = std::is_same_v; + mixin_tomds_accessor(m); + } + { + constexpr bool constructible = std::is_same_v || std::is_same_v; + mixin_tomds_accessor(m); + } + { + constexpr bool constructible = std::is_same_v; + mixin_tomds_accessor(m); + } + { + constexpr bool constructible = std::is_same_v || std::is_same_v; + mixin_tomds_accessor(m); + } +} + +template +constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) { + constexpr size_t D = std::dynamic_extent; + mixin_tomds_element_type(std::mdspan(handle, construct_mapping(layout, std::extents()), acc)); + mixin_tomds_element_type(std::mdspan(handle, construct_mapping(layout, std::extents(3)), acc)); + mixin_tomds_element_type(std::mdspan(handle, construct_mapping(layout, std::extents()), acc)); + mixin_tomds_element_type(std::mdspan(handle, construct_mapping(layout, std::extents(3, 5)), acc)); + mixin_tomds_element_type(std::mdspan(handle, construct_mapping(layout, std::extents(3, 5)), acc)); +} + +template +constexpr void mixin_layout(const H& handle, const A& acc) { + mixin_extents(handle, std::layout_left(), acc); + mixin_extents(handle, std::layout_right(), acc); + mixin_extents(handle, layout_wrapping_integral<4>(), acc); +} + +template +constexpr void mixin_accessor() { + ElementPool elements; + mixin_layout(elements.get_ptr(), std::default_accessor()); + mixin_layout(typename checked_accessor::data_handle_type(elements.get_ptr()), checked_accessor(1024)); +} + +constexpr bool test() { + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + return true; +} +int main(int, char**) { + (void)test(); + static_assert(test()); + return 0; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/ctor.copy.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/ctor.copy.pass.cpp @@ -0,0 +1,98 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr mdspan(const mdspan&) = default; +// +// A specialization of mdspan is a trivially copyable type if its accessor_type, mapping_type, and data_handle_type are trivially copyable types. + +#include +#include +#include +#include + +#include "test_macros.h" + +#include "../MinimalElementType.h" +#include "CustomTestLayouts.h" +#include "CustomTestAccessors.h" + +template +constexpr void test_mdspan_types(const H& handle, const M& map, const A& acc) { + using MDS = std::mdspan; + + MDS m_org(handle, map, acc); + MDS m(m_org); + static_assert(noexcept(MDS(m_org)) == (noexcept(H(handle))&& noexcept(M(map))&& noexcept(A(acc)))); + static_assert( + std::is_trivially_copyable_v == + (std::is_trivially_copyable_v && std::is_trivially_copyable_v && std::is_trivially_copyable_v)); + assert(m.extents() == map.extents()); + if constexpr (std::equality_comparable) + assert(m.data_handle() == handle); + if constexpr (std::equality_comparable) + assert(m.mapping() == map); + if constexpr (std::equality_comparable) + assert(m.accessor() == acc); +} + +template +constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) { + constexpr size_t D = std::dynamic_extent; + test_mdspan_types(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(7)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(2, 3)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(0, 3)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(1, 2, 3, 2)), acc); +} + +template +constexpr void mixin_layout(const H& handle, const A& acc) { + // make sure we test a trivially copyable mapping + static_assert(std::is_trivially_copyable_v>>); + mixin_extents(handle, std::layout_left(), acc); + mixin_extents(handle, std::layout_right(), acc); + // make sure we test a not trivially copyable mapping + static_assert( + !std::is_trivially_copyable_v::template mapping>>); + mixin_extents(handle, layout_wrapping_integral<4>(), acc); +} + +template +constexpr void mixin_accessor() { + ElementPool elements; + // make sure we test trivially constructable accessor and data_handle + static_assert(std::is_trivially_copyable_v>); + static_assert(std::is_trivially_copyable_v::data_handle_type>); + mixin_layout(elements.get_ptr(), std::default_accessor()); + + // Using weird accessor/data_handle + // Make sure they actually got the properties we want to test + // checked_accessor is noexcept copy constructible except for const double + checked_accessor acc(1024); + static_assert(noexcept(checked_accessor(acc)) != std::is_same_v); + mixin_layout(typename checked_accessor::data_handle_type(elements.get_ptr()), acc); +} + +constexpr bool test() { + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + return true; +} +int main(int, char**) { + (void)test(); + static_assert(test()); + return 0; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/ctor.default.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/ctor.default.pass.cpp @@ -0,0 +1,106 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr mdspan(); +// Constraints: +// - rank_dynamic() > 0 is true. +// - is_default_constructible_v is true. +// - is_default_constructible_v is true. +// - is_default_constructible_v is true. +// +// Preconditions: [0, map_.required_span_size()) is an accessible range of ptr_ +// and acc_ for the values of map_ and acc_ after the invocation of this constructor. +// +// Effects: Value-initializes ptr_, map_, and acc_. + +#include +#include +#include +#include + +#include "test_macros.h" + +#include "../MinimalElementType.h" +#include "CustomTestLayouts.h" +#include "CustomTestAccessors.h" + +template +constexpr void test_mdspan_types(const H&, const M&, const A&) { + using MDS = std::mdspan; + + if constexpr (MDS::rank_dynamic() > 0 && std::is_default_constructible_v && std::is_default_constructible_v && + std::is_default_constructible_v) { + MDS m; + static_assert(noexcept(MDS()) == (noexcept(H())&& noexcept(M())&& noexcept(A()))); + assert(m.extents() == typename MDS::extents_type()); + if constexpr (std::equality_comparable) + assert(m.data_handle() == H()); + if constexpr (std::equality_comparable) + assert(m.mapping() == M()); + if constexpr (std::equality_comparable) + assert(m.accessor() == A()); + } else { + static_assert(!std::is_default_constructible_v); + } +} + +template +constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) { + constexpr size_t D = std::dynamic_extent; + test_mdspan_types(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(7)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(2, 3)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(0, 3)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(1, 2, 3, 2)), acc); +} + +template +constexpr void mixin_layout(const H& handle, const A& acc) { + mixin_extents(handle, std::layout_left(), acc); + mixin_extents(handle, std::layout_right(), acc); + + // Use weird layout, make sure it has the properties we want to test + constexpr size_t D = std::dynamic_extent; + static_assert( + !std::is_default_constructible_v< typename layout_wrapping_integral<4>::template mapping>>); + mixin_extents(handle, layout_wrapping_integral<4>(), acc); +} + +template +constexpr void mixin_accessor() { + ElementPool elements; + mixin_layout(elements.get_ptr(), std::default_accessor()); + + // Using weird accessor/data_handle + // Make sure they actually got the properties we want to test + // checked_accessor is not default constructible except for const double, where it is not noexcept + static_assert(std::is_default_constructible_v> == std::is_same_v); + // checked_accessor's data handle type is not default constructible for double + static_assert( + std::is_default_constructible_v::data_handle_type> != std::is_same_v); + mixin_layout(typename checked_accessor::data_handle_type(elements.get_ptr()), checked_accessor(1024)); +} + +constexpr bool test() { + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + return true; +} +int main(int, char**) { + (void)test(); + static_assert(test()); + return 0; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_array.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_array.pass.cpp @@ -0,0 +1,141 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// template +// constexpr explicit(N != rank_dynamic()) +// mdspan(data_handle_type p, const array& exts); +// +// Constraints: +// - is_convertible_v is true, +// - (is_nothrow_constructible && ...) is true, +// - N == rank() || N == rank_dynamic() is true, +// - is_constructible_v is true, and +// - is_default_constructible_v is true. +// +// Preconditions: [0, map_.required_span_size()) is an accessible range of p and acc_ +// for the values of map_ and acc_ after the invocation of this constructor. +// +// Effects: +// - Direct-non-list-initializes ptr_ with std::move(p), +// - direct-non-list-initializes map_ with extents_type(exts), and +// - value-initializes acc_. + +#include +#include +#include +#include +#include + +#include "test_macros.h" + +#include "../MinimalElementType.h" +#include "CustomTestLayouts.h" +#include "CustomTestAccessors.h" + +template +constexpr auto array_from_extents(const Extents& exts, std::index_sequence) { + return std::array{exts.extent(Idxs)...}; +} + +template +concept check_mdspan_ctor_implicit = requires(MDS m, typename MDS::data_handle_type h, const Exts& exts) { + m = {h, exts}; +}; + +template +constexpr void +test_mdspan_ctor_array(const H& handle, const M& map, const A&, std::array exts) { + using MDS = std::mdspan; + MDS m(handle, exts); + + static_assert(!noexcept(MDS(handle, exts))); + + static_assert(check_mdspan_ctor_implicit == (N == MDS::rank_dynamic())); + + assert(m.extents() == map.extents()); + if constexpr (std::equality_comparable) + assert(m.data_handle() == handle); + if constexpr (std::equality_comparable) + assert(m.mapping() == map); + if constexpr (std::equality_comparable) + assert(m.accessor() == A()); +} + +template +constexpr void test_mdspan_ctor(const H& handle, const M& map, const A& acc) { + using MDS = std::mdspan; + + if constexpr (std::is_constructible_v && std::is_default_constructible_v) { + // test from all extents + auto exts = array_from_extents(map.extents(), std::make_index_sequence()); + test_mdspan_ctor_array(handle, map, acc, exts); + + // test from dynamic extents + std::array exts_dynamic{}; + size_t r_dyn = 0; + for (size_t r = 0; r < MDS::rank(); r++) { + if (MDS::static_extent(r) == std::dynamic_extent) + exts_dynamic[r_dyn++] = exts[r]; + } + test_mdspan_ctor_array(handle, map, acc, exts_dynamic); + } else { + static_assert(!std::is_constructible_v); + } +} + +template +constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) { + constexpr size_t D = std::dynamic_extent; + test_mdspan_ctor(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_ctor(handle, construct_mapping(layout, std::extents(7)), acc); + test_mdspan_ctor(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_ctor(handle, construct_mapping(layout, std::extents(2, 3)), acc); + test_mdspan_ctor(handle, construct_mapping(layout, std::extents(0, 3)), acc); + test_mdspan_ctor(handle, construct_mapping(layout, std::extents(1, 2, 3, 2)), acc); +} + +template +constexpr void mixin_layout(const H& handle, const A& acc) { + mixin_extents(handle, std::layout_left(), acc); + mixin_extents(handle, std::layout_right(), acc); + + // Sanity check that this layouts mapping is not constructible from extents + static_assert(!std::is_constructible_v::template mapping>, + std::extents>); + mixin_extents(handle, layout_wrapping_integral<4>(), acc); +} + +template +constexpr void mixin_accessor() { + ElementPool elements; + mixin_layout(elements.get_ptr(), std::default_accessor()); + + // Using weird accessor/data_handle + // Make sure they actually got the properties we want to test + // checked_accessor is not default constructible except for const double, where it is not noexcept + static_assert(std::is_default_constructible_v> == std::is_same_v); + mixin_layout(typename checked_accessor::data_handle_type(elements.get_ptr()), checked_accessor(1024)); +} + +constexpr bool test() { + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + return true; +} +int main(int, char**) { + (void)test(); + static_assert(test()); + return 0; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_extents.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_extents.pass.cpp @@ -0,0 +1,108 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr mdspan(data_handle_type p, const extents_type& ext); +// +// Constraints: +// - is_constructible_v is true, and +// - is_default_constructible_v is true. +// +// Preconditions: [0, map_.required_span_size()) is an accessible range of p and acc_ +// for the values of map_ and acc_ after the invocation of this constructor. +// +// Effects: +// - Direct-non-list-initializes ptr_ with std::move(p), +// - direct-non-list-initializes map_ with ext, and +// - value-initializes acc_. + +#include +#include +#include +#include + +#include "test_macros.h" + +#include "../MinimalElementType.h" +#include "CustomTestLayouts.h" +#include "CustomTestAccessors.h" + +template +constexpr void test_mdspan_types(const H& handle, const M& map, const A&) { + using MDS = std::mdspan; + + if constexpr (std::is_constructible_v && std::is_default_constructible_v) { + // use formulation of constructor which tests that its not explicit + MDS m = {handle, map.extents()}; + static_assert(!noexcept(MDS(handle, map.extents()))); + assert(m.extents() == map.extents()); + if constexpr (std::equality_comparable) + assert(m.data_handle() == handle); + if constexpr (std::equality_comparable) + assert(m.mapping() == map); + if constexpr (std::equality_comparable) + assert(m.accessor() == A()); + } else { + static_assert(!std::is_constructible_v); + } +} + +template +constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) { + constexpr size_t D = std::dynamic_extent; + test_mdspan_types(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(7)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(2, 3)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(0, 3)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(1, 2, 3, 2)), acc); +} + +template +constexpr void mixin_layout(const H& handle, const A& acc) { + mixin_extents(handle, std::layout_left(), acc); + mixin_extents(handle, std::layout_right(), acc); + + // Use weird layout, make sure it has the properties we want to test + constexpr size_t D = std::dynamic_extent; + static_assert( + !std::is_default_constructible_v< typename layout_wrapping_integral<4>::template mapping>>); + mixin_extents(handle, layout_wrapping_integral<4>(), acc); +} + +template +constexpr void mixin_accessor() { + ElementPool elements; + mixin_layout(elements.get_ptr(), std::default_accessor()); + + // Using weird accessor/data_handle + // Make sure they actually got the properties we want to test + // checked_accessor is not default constructible except for const double, where it is not noexcept + static_assert(std::is_default_constructible_v> == std::is_same_v); + // checked_accessor's data handle type is not default constructible for double + static_assert( + std::is_default_constructible_v::data_handle_type> != std::is_same_v); + mixin_layout(typename checked_accessor::data_handle_type(elements.get_ptr()), checked_accessor(1024)); +} + +constexpr bool test() { + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + return true; +} +int main(int, char**) { + (void)test(); + static_assert(test()); + return 0; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_integers.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_integers.pass.cpp @@ -0,0 +1,131 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// template +// constexpr explicit mdspan(data_handle_type p, OtherIndexTypes... exts); +// +// Let N be sizeof...(OtherIndexTypes). +// +// Constraints: +// - (is_convertible_v && ...) is true, +// - (is_nothrow_constructible && ...) is true, +// - N == rank() || N == rank_dynamic() is true, +// - is_constructible_v is true, and +// - is_default_constructible_v is true. +// +// Preconditions: [0, map_.required_span_size()) is an accessible range of p and acc_ +// for the values of map_ and acc_ after the invocation of this constructor. +// +// Effects: +// - Direct-non-list-initializes ptr_ with std::move(p), +// - direct-non-list-initializes map_ with extents_type(static_cast(std::move(exts))...), and +// - value-initializes acc_. + +#include +#include +#include +#include +#include + +#include "test_macros.h" + +#include "../MinimalElementType.h" +#include "CustomTestLayouts.h" +#include "CustomTestAccessors.h" + +template +concept check_mdspan_ctor_implicit = requires(MDS m, Args... args) { m = {args...}; }; + +template +constexpr void test_mdspan_types(const H& handle, const M& map, const A&, Idxs... idxs) { + using MDS = std::mdspan; + + if constexpr (std::is_constructible_v && std::is_default_constructible_v) { + MDS m(handle, idxs...); + + // sanity check that concept works + static_assert(check_mdspan_ctor_implicit>); + // check that the constructor from integral is explicit + static_assert(!check_mdspan_ctor_implicit); + + assert(m.extents() == map.extents()); + if constexpr (std::equality_comparable) + assert(m.data_handle() == handle); + if constexpr (std::equality_comparable) + assert(m.mapping() == map); + if constexpr (std::equality_comparable) + assert(m.accessor() == A()); + } else { + static_assert(!std::is_constructible_v); + } +} + +template +constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) { + constexpr size_t D = std::dynamic_extent; + // construct from just dynamic extents + test_mdspan_types(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(7)), acc, 7); + test_mdspan_types(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(2, 3)), acc, 2, 3); + test_mdspan_types(handle, construct_mapping(layout, std::extents(0, 3)), acc, 0, 3); + test_mdspan_types( + handle, construct_mapping(layout, std::extents(1, 2, 3, 2)), acc, 1, 2, 3, 2); + + // construct from all extents + test_mdspan_types(handle, construct_mapping(layout, std::extents()), acc, 7); + test_mdspan_types(handle, construct_mapping(layout, std::extents(2, 3)), acc, 2, 4, 3); + test_mdspan_types(handle, construct_mapping(layout, std::extents(0, 3)), acc, 0, 7, 3); + test_mdspan_types( + handle, construct_mapping(layout, std::extents(1, 2, 3, 2)), acc, 1, 7, 2, 4, 3, 2); +} + +template +constexpr void mixin_layout(const H& handle, const A& acc) { + mixin_extents(handle, std::layout_left(), acc); + mixin_extents(handle, std::layout_right(), acc); + + // Sanity check that this layouts mapping is not constructible from extents + static_assert(!std::is_constructible_v::template mapping>, + std::extents>); + mixin_extents(handle, layout_wrapping_integral<4>(), acc); +} + +template +constexpr void mixin_accessor() { + ElementPool elements; + mixin_layout(elements.get_ptr(), std::default_accessor()); + + // Using weird accessor/data_handle + // Make sure they actually got the properties we want to test + // checked_accessor is not default constructible except for const double, where it is not noexcept + static_assert(std::is_default_constructible_v> == std::is_same_v); + // checked_accessor's data handle type is not default constructible for double + static_assert( + std::is_default_constructible_v::data_handle_type> != std::is_same_v); + mixin_layout(typename checked_accessor::data_handle_type(elements.get_ptr()), checked_accessor(1024)); +} + +constexpr bool test() { + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + return true; +} + +int main(int, char**) { + (void)test(); + static_assert(test()); + return 0; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map.pass.cpp @@ -0,0 +1,106 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr mdspan(data_handle_type p, const mapping_type& m); +// +// Constraints: is_default_constructible_v is true. +// +// Preconditions: [0, m.required_span_size()) is an accessible range of p and acc_ +// for the value of acc_ after the invocation of this constructor. +// +// Effects: +// - Direct-non-list-initializes ptr_ with std::move(p), +// - direct-non-list-initializes map_ with m, and +// - value-initializes acc_. + +#include +#include +#include +#include + +#include "test_macros.h" + +#include "../MinimalElementType.h" +#include "CustomTestLayouts.h" +#include "CustomTestAccessors.h" + +template +constexpr void test_mdspan_types(const H& handle, const M& map, const A&) { + using MDS = std::mdspan; + + if constexpr (std::is_default_constructible_v) { + // use formulation of constructor which tests that it is not explicit + MDS m = {handle, map}; + static_assert(!noexcept(MDS(handle, map))); + assert(m.extents() == map.extents()); + if constexpr (std::equality_comparable) + assert(m.data_handle() == handle); + if constexpr (std::equality_comparable) + assert(m.mapping() == map); + if constexpr (std::equality_comparable) + assert(m.accessor() == A()); + } else { + static_assert(!std::is_constructible_v); + } +} + +template +constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) { + constexpr size_t D = std::dynamic_extent; + test_mdspan_types(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(7)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(2, 3)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(0, 3)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(1, 2, 3, 2)), acc); +} + +template +constexpr void mixin_layout(const H& handle, const A& acc) { + mixin_extents(handle, std::layout_left(), acc); + mixin_extents(handle, std::layout_right(), acc); + + // Use weird layout, make sure it has the properties we want to test + constexpr size_t D = std::dynamic_extent; + static_assert( + !std::is_default_constructible_v< typename layout_wrapping_integral<4>::template mapping>>); + mixin_extents(handle, layout_wrapping_integral<4>(), acc); +} + +template +constexpr void mixin_accessor() { + ElementPool elements; + mixin_layout(elements.get_ptr(), std::default_accessor()); + + // Using weird accessor/data_handle + // Make sure they actually got the properties we want to test + // checked_accessor is not default constructible except for const double, where it is not noexcept + static_assert(std::is_default_constructible_v> == std::is_same_v); + // checked_accessor's data handle type is not default constructible for double + static_assert( + std::is_default_constructible_v::data_handle_type> != std::is_same_v); + mixin_layout(typename checked_accessor::data_handle_type(elements.get_ptr()), checked_accessor(1024)); +} + +constexpr bool test() { + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + return true; +} +int main(int, char**) { + (void)test(); + static_assert(test()); + return 0; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map_acc.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map_acc.pass.cpp @@ -0,0 +1,99 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// constexpr mdspan(data_handle_type p, const mapping_type& m, const accessor_type& a); +// +// Preconditions: [0, m.required_span_size()) is an accessible range of p and a. +// +// 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. + +#include +#include +#include +#include + +#include "test_macros.h" + +#include "../MinimalElementType.h" +#include "CustomTestLayouts.h" +#include "CustomTestAccessors.h" + +template +constexpr void test_mdspan_types(const H& handle, const M& map, const A& acc) { + using MDS = std::mdspan; + + // use formulation of constructor which tests that it is not explicit + MDS m = {handle, map, acc}; + static_assert(!noexcept(MDS(handle, map, acc))); + assert(m.extents() == map.extents()); + if constexpr (std::equality_comparable) + assert(m.data_handle() == handle); + if constexpr (std::equality_comparable) + assert(m.mapping() == map); + if constexpr (std::equality_comparable) + assert(m.accessor() == acc); +} + +template +constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) { + constexpr size_t D = std::dynamic_extent; + test_mdspan_types(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(7)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(2, 3)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(0, 3)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(1, 2, 3, 2)), acc); +} + +template +constexpr void mixin_layout(const H& handle, const A& acc) { + mixin_extents(handle, std::layout_left(), acc); + mixin_extents(handle, std::layout_right(), acc); + + // Use weird layout, make sure it has the properties we want to test + constexpr size_t D = std::dynamic_extent; + static_assert( + !std::is_default_constructible_v< typename layout_wrapping_integral<4>::template mapping>>); + mixin_extents(handle, layout_wrapping_integral<4>(), acc); +} + +template +constexpr void mixin_accessor() { + ElementPool elements; + mixin_layout(elements.get_ptr(), std::default_accessor()); + + // Using weird accessor/data_handle + // Make sure they actually got the properties we want to test + // checked_accessor is not default constructible except for const double, where it is not noexcept + static_assert(std::is_default_constructible_v> == std::is_same_v); + // checked_accessor's data handle type is not default constructible for double + static_assert( + std::is_default_constructible_v::data_handle_type> != std::is_same_v); + mixin_layout(typename checked_accessor::data_handle_type(elements.get_ptr()), checked_accessor(1024)); +} + +constexpr bool test() { + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + return true; +} +int main(int, char**) { + (void)test(); + static_assert(test()); + return 0; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_span.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_span.pass.cpp @@ -0,0 +1,141 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// template +// constexpr explicit(N != rank_dynamic()) +// mdspan(data_handle_type p, const span& exts); +// +// Constraints: +// - is_convertible_v is true, +// - (is_nothrow_constructible && ...) is true, +// - N == rank() || N == rank_dynamic() is true, +// - is_constructible_v is true, and +// - is_default_constructible_v is true. +// +// Preconditions: [0, map_.required_span_size()) is an accessible range of p and acc_ +// for the values of map_ and acc_ after the invocation of this constructor. +// +// Effects: +// - Direct-non-list-initializes ptr_ with std::move(p), +// - direct-non-list-initializes map_ with extents_type(exts), and +// - value-initializes acc_. + +#include +#include +#include +#include +#include + +#include "test_macros.h" + +#include "../MinimalElementType.h" +#include "CustomTestLayouts.h" +#include "CustomTestAccessors.h" + +template +constexpr auto array_from_extents(const Extents& exts, std::index_sequence) { + return std::array{exts.extent(Idxs)...}; +} + +template +concept check_mdspan_ctor_implicit = requires(MDS m, typename MDS::data_handle_type h, const Exts& exts) { + m = {h, exts}; +}; + +template +constexpr void +test_mdspan_ctor_array(const H& handle, const M& map, const A&, std::span exts) { + using MDS = std::mdspan; + MDS m(handle, exts); + + static_assert(!noexcept(MDS(handle, exts))); + + static_assert(check_mdspan_ctor_implicit == (N == MDS::rank_dynamic())); + + assert(m.extents() == map.extents()); + if constexpr (std::equality_comparable) + assert(m.data_handle() == handle); + if constexpr (std::equality_comparable) + assert(m.mapping() == map); + if constexpr (std::equality_comparable) + assert(m.accessor() == A()); +} + +template +constexpr void test_mdspan_ctor(const H& handle, const M& map, const A& acc) { + using MDS = std::mdspan; + + if constexpr (std::is_constructible_v && std::is_default_constructible_v) { + // test from all extents + auto exts = array_from_extents(map.extents(), std::make_index_sequence()); + test_mdspan_ctor_array(handle, map, acc, std::span(exts)); + + // test from dynamic extents + std::array exts_dynamic{}; + size_t r_dyn = 0; + for (size_t r = 0; r < MDS::rank(); r++) { + if (MDS::static_extent(r) == std::dynamic_extent) + exts_dynamic[r_dyn++] = exts[r]; + } + test_mdspan_ctor_array(handle, map, acc, std::span(exts_dynamic)); + } else { + static_assert(!std::is_constructible_v); + } +} + +template +constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) { + constexpr size_t D = std::dynamic_extent; + test_mdspan_ctor(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_ctor(handle, construct_mapping(layout, std::extents(7)), acc); + test_mdspan_ctor(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_ctor(handle, construct_mapping(layout, std::extents(2, 3)), acc); + test_mdspan_ctor(handle, construct_mapping(layout, std::extents(0, 3)), acc); + test_mdspan_ctor(handle, construct_mapping(layout, std::extents(1, 2, 3, 2)), acc); +} + +template +constexpr void mixin_layout(const H& handle, const A& acc) { + mixin_extents(handle, std::layout_left(), acc); + mixin_extents(handle, std::layout_right(), acc); + + // Sanity check that this layouts mapping is not constructible from extents + static_assert(!std::is_constructible_v::template mapping>, + std::extents>); + mixin_extents(handle, layout_wrapping_integral<4>(), acc); +} + +template +constexpr void mixin_accessor() { + ElementPool elements; + mixin_layout(elements.get_ptr(), std::default_accessor()); + + // Using weird accessor/data_handle + // Make sure they actually got the properties we want to test + // checked_accessor is not default constructible except for const double, where it is not noexcept + static_assert(std::is_default_constructible_v> == std::is_same_v); + mixin_layout(typename checked_accessor::data_handle_type(elements.get_ptr()), checked_accessor(1024)); +} + +constexpr bool test() { + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + return true; +} +int main(int, char**) { + (void)test(); + static_assert(test()); + return 0; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp @@ -0,0 +1,159 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// template +// requires(is_array_v && rank_v == 1) +// mdspan(CArray&) +// -> mdspan, extents>>; +// +// template +// requires(is_pointer_v>) +// mdspan(Pointer&&) +// -> mdspan>, extents>; +// +// template +// requires((is_convertible_v && ...) && sizeof...(Integrals) > 0) +// explicit mdspan(ElementType*, Integrals...) +// -> mdspan>; +// +// template +// mdspan(ElementType*, span) +// -> mdspan>; +// +// template +// mdspan(ElementType*, const array&) +// -> mdspan>; +// +// template +// mdspan(ElementType*, const extents&) +// -> mdspan>; +// +// template +// mdspan(ElementType*, const MappingType&) +// -> mdspan; +// +// template +// mdspan(const typename AccessorType::data_handle_type&, const MappingType&, +// const AccessorType&) +// -> mdspan; + +#include +#include +#include +#include + +#include "test_macros.h" + +#include "../MinimalElementType.h" +#include "CustomTestLayouts.h" +#include "CustomTestAccessors.h" + +template +constexpr void test_mdspan_types(const H& handle, const M& map, const A& acc) { + using MDS = std::mdspan; + + // deduction from data_handle_type (including non-pointer), mapping and accessor + ASSERT_SAME_TYPE(decltype(std::mdspan(handle, map, acc)), MDS); + + if constexpr (std::is_same_v>) { + // deduction from pointer and mapping + // non-pointer data-handle-types have other accessor + ASSERT_SAME_TYPE(decltype(std::mdspan(handle, map)), MDS); + if constexpr (std::is_same_v) { + // deduction from pointer and extents + ASSERT_SAME_TYPE(decltype(std::mdspan(handle, map.extents())), MDS); + } + } +} + +template +constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) { + constexpr size_t D = std::dynamic_extent; + test_mdspan_types(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(7)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(2, 3)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(0, 3)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(1, 2, 3, 2)), acc); +} + +struct SizeTIntType { + size_t val; + constexpr SizeTIntType(size_t val_) : val(val_) {} + constexpr operator size_t() const noexcept { return size_t(val); } +}; + +template + requires(sizeof(decltype(std::mdspan(std::declval(), 10))) > 0) +constexpr bool test_no_layout_deduction_guides(const H& handle, const A&) { + using T = typename A::element_type; + // deduction from pointer alone + ASSERT_SAME_TYPE(decltype(std::mdspan(handle)), std::mdspan>); + // deduction from pointer and integral like + ASSERT_SAME_TYPE(decltype(std::mdspan(handle, 5, SizeTIntType(6))), std::mdspan>); + + std::array exts; + // deduction from pointer and array + ASSERT_SAME_TYPE(decltype(std::mdspan(handle, exts)), std::mdspan>); + // deduction from pointer and span + ASSERT_SAME_TYPE(decltype(std::mdspan(handle, std::span(exts))), std::mdspan>); + return true; +} + +template +constexpr bool test_no_layout_deduction_guides(const H&, const A&) { + return false; +} + +template +constexpr void mixin_layout(const H& handle, const A& acc) { + mixin_extents(handle, std::layout_left(), acc); + mixin_extents(handle, std::layout_right(), acc); + mixin_extents(handle, layout_wrapping_integral<4>(), acc); + + // checking that there is no deduction happen for non-pointer handle type + assert((test_no_layout_deduction_guides(handle, acc) == std::is_same_v)); +} + +template +constexpr void mixin_accessor() { + ElementPool elements; + mixin_layout(elements.get_ptr(), std::default_accessor()); + + // Using weird accessor/data_handle + // Make sure they actually got the properties we want to test + // checked_accessor is noexcept copy constructible except for const double + checked_accessor acc(1024); + static_assert(noexcept(checked_accessor(acc)) != std::is_same_v); + mixin_layout(typename checked_accessor::data_handle_type(elements.get_ptr()), acc); +} + +constexpr bool test() { + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + + // deduction from array alone + float a[12]; + ASSERT_SAME_TYPE(decltype(std::mdspan(a)), std::mdspan>); + + return true; +} +int main(int, char**) { + (void)test(); + static_assert(test()); + return 0; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/element_type.verify.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/element_type.verify.cpp @@ -0,0 +1,39 @@ +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// template +// class mdspan; +// +// Mandates: +// - ElementType is a complete object type that is neither an abstract class type nor an array type. +// - is_same_v is true. + +#include + +class AbstractClass { +public: + virtual void method() = 0; +}; + +void not_abstract_class() { + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}mdspan: ElementType template parameter may not be an abstract class}} + [[maybe_unused]] std::mdspan> m; +} + +void not_array_type() { + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}mdspan: ElementType template parameter may not be an array type}} + [[maybe_unused]] std::mdspan> m; +} + +void element_type_mismatch() { + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}mdspan: ElementType template parameter must match AccessorPolicy::element_type}} + [[maybe_unused]] std::mdspan, std::layout_right, std::default_accessor> m; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/extents.verify.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/extents.verify.cpp @@ -0,0 +1,23 @@ +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// template +// class mdspan; +// +// Mandates: +// - Extents is a specialization of extents + +#include + +void not_extents() { + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}mdspan: Extents template parameter must be a specialization of extents.}} + [[maybe_unused]] std::mdspan m; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/index_operator.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/index_operator.pass.cpp @@ -0,0 +1,182 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// Test default iteration: +// +// template +// constexpr reference operator[](Indices...) const noexcept; +// +// Constraints: +// * sizeof...(Indices) == extents_type::rank() is true, +// * (is_convertible_v && ...) is true, and +// * (is_nothrow_constructible_v && ...) is true. +// +// Preconditions: +// * extents_type::index-cast(i) is a multidimensional index in extents_. + +// GCC warns about comma operator changing its meaning inside [] in C++23 +#if defined(__GNUC__) && !defined(__clang_major__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcomma-subscript" +#endif + +#include +#include +#include + +#include "test_macros.h" + +#include "../ConvertibleToIntegral.h" +#include "CustomTestLayouts.h" + +// Clang 15 and 16 do not support argument packs as input to operator [] +#if defined(__clang_major__) && __clang_major__ < 17 +template +constexpr auto& access(MDS mds) { + return mds[]; +} +template +constexpr auto& access(MDS mds, int64_t i0) { + return mds[i0]; +} +template +constexpr auto& access(MDS mds, int64_t i0, int64_t i1) { + return mds[i0, i1]; +} +template +constexpr auto& access(MDS mds, int64_t i0, int64_t i1, int64_t i2) { + return mds[i0, i1, i2]; +} +template +constexpr auto& access(MDS mds, int64_t i0, int64_t i1, int64_t i2, int64_t i3) { + return mds[i0, i1, i2, i3]; +} +#endif + +template +concept operator_constraints = requires(MDS m, Indices... idxs) { + { std::is_same_v }; +}; + +template + requires(operator_constraints) +constexpr bool check_operator_constraints(MDS m, Indices... idxs) { + (void)m[idxs...]; + return true; +} + +template +constexpr bool check_operator_constraints(MDS, Indices...) { + return false; +} + +template +constexpr void iterate(MDS mds, Args... args) { + constexpr int r = static_cast(MDS::extents_type::rank()) - 1 - static_cast(sizeof...(Args)); + if constexpr (-1 == r) { +#if defined(__clang_major__) && __clang_major__ < 17 + int* ptr1 = &access(mds, args...); +#else + int* ptr1 = &mds[args...]; +#endif + int* ptr2 = &(mds.accessor().access(mds.data_handle(), mds.mapping()(args...))); + assert(ptr1 == ptr2); + } else { + for (typename MDS::index_type i = 0; i < mds.extents().extent(r); i++) { + iterate(mds, i, args...); + } + } +} + +template +constexpr void test_iteration(Mapping m) { + std::array data; + using MDS = std::mdspan; + MDS mds(data.data(), m); + + iterate(mds); +} + +template +constexpr void test_layout() { + constexpr size_t D = std::dynamic_extent; + test_iteration(construct_mapping(Layout(), std::extents())); + test_iteration(construct_mapping(Layout(), std::extents(1))); + test_iteration(construct_mapping(Layout(), std::extents(7))); + test_iteration(construct_mapping(Layout(), std::extents())); + test_iteration(construct_mapping(Layout(), std::extents())); + test_iteration(construct_mapping(Layout(), std::extents(1, 1, 1, 1))); + +// TODO enable for GCC 13, when the CI pipeline is switched, doesn't work with GCC 12 +#if defined(__clang_major__) && __clang_major__ > 16 + int data[1]; + // Check operator constraint for number of arguments + static_assert(check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents(1))), 0)); + static_assert( + !check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents(1))), 0, 0)); + + // Check operator constraint for convertibility of arguments to index_type + static_assert( + check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents(1))), IntType(0))); + static_assert(!check_operator_constraints( + std::mdspan(data, construct_mapping(Layout(), std::extents(1))), IntType(0))); + + // Check operator constraint for no-throw-constructibility of index_type from arguments + static_assert(!check_operator_constraints( + std::mdspan(data, construct_mapping(Layout(), std::extents(1))), IntType(0))); + + // Check that mixed integrals work: note the second one tests that mdspan casts: layout_wrapping_integral does not accept IntType + static_assert(check_operator_constraints( + std::mdspan(data, construct_mapping(Layout(), std::extents(1, 1))), int(0), size_t(0))); + static_assert(check_operator_constraints( + std::mdspan(data, construct_mapping(Layout(), std::extents(1, 1))), unsigned(0), IntType(0))); +#endif +} + +template +constexpr void test_layout_large() { + constexpr size_t D = std::dynamic_extent; + test_iteration(construct_mapping(Layout(), std::extents(3, 5, 6))); + test_iteration(construct_mapping(Layout(), std::extents(3, 6))); +} + +// mdspan::operator[] casts to index_type before calling mapping +// mapping requirements only require the index operator to mixed integer types not anything convertible to index_type +constexpr void test_index_cast_happens() {} + +constexpr bool test() { + test_layout(); + test_layout(); + test_layout>(); + return true; +} + +constexpr bool test_large() { + test_layout_large(); + test_layout_large(); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + // The large test iterates over ~10k loop indices. + // With assertions enabled this triggered the maximum default limit + // for steps in consteval expressions. Assertions roughly double the + // total number of instructions, so this was already close to the maximum. + test_large(); + return 0; +} +#if defined(__GNUC__) && !defined(__clang_major__) +# pragma GCC diagnostic pop +#endif Index: libcxx/test/std/containers/views/mdspan/mdspan/mapping.verify.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/mapping.verify.cpp @@ -0,0 +1,23 @@ +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// template +// class mdspan; +// +// Mandates: +// - LayoutPolicy shall meet the layout mapping policy requirements ([mdspan.layout.policy.reqmts]) + +#include + +void not_layout_policy() { + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}mdspan: LayoutPolicy template parameter is invalid. A common mistake is to pass a layout mapping instead of a layout policy}} + [[maybe_unused]] std::mdspan, std::layout_left::template mapping>> m; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/properties.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/properties.pass.cpp @@ -0,0 +1,214 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// template> +// class mdspan { +// public: +// static constexpr rank_type rank() noexcept { return extents_type::rank(); } +// static constexpr rank_type rank_dynamic() noexcept { return extents_type::rank_dynamic(); } +// static constexpr size_t static_extent(rank_type r) noexcept +// { return extents_type::static_extent(r); } +// constexpr index_type extent(rank_type r) const noexcept { return extents().extent(r); } +// +// constexpr size_type size() const noexcept; +// [[nodiscard]] constexpr bool empty() const noexcept; +// +// +// constexpr const extents_type& extents() const noexcept { return map_.extents(); } +// constexpr const data_handle_type& data_handle() const noexcept { return ptr_; } +// constexpr const mapping_type& mapping() const noexcept { return map_; } +// constexpr const accessor_type& accessor() const noexcept { return acc_; } +// static constexpr bool is_always_unique() +// { return mapping_type::is_always_unique(); } +// static constexpr bool is_always_exhaustive() +// { return mapping_type::is_always_exhaustive(); } +// static constexpr bool is_always_strided() +// { return mapping_type::is_always_strided(); } +// +// constexpr bool is_unique() const +// { return map_.is_unique(); } +// constexpr bool is_exhaustive() const +// { return map_.is_exhaustive(); } +// constexpr bool is_strided() const +// { return map_.is_strided(); } +// constexpr index_type stride(rank_type r) const +// { return map_.stride(r); } +// }; +// +// Each specialization MDS of mdspan models copyable and +// - is_nothrow_move_constructible_v is true, +// - is_nothrow_move_assignable_v is true, and +// - is_nothrow_swappable_v is true. +// A specialization of mdspan is a trivially copyable type if its accessor_type, mapping_type, and data_handle_type are trivially copyable types. + +#include +#include +#include +#include + +#include "test_macros.h" + +#include "../MinimalElementType.h" +#include "CustomTestLayouts.h" + +template +constexpr void test_mdspan_types(const H& handle, const M& map, const A& acc) { + using MDS = std::mdspan; + MDS m(handle, map, acc); + + // ===================================== + // Traits for every mdspan + // ===================================== + 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); + + // ===================================== + // Invariants coming from data handle + // ===================================== + // data_handle() + ASSERT_SAME_TYPE(decltype(m.data_handle()), const H&); + ASSERT_NOEXCEPT(m.data_handle()); + if constexpr (std::equality_comparable) { + assert(m.data_handle() == handle); + } + + // ===================================== + // Invariants coming from extents + // ===================================== + + // extents() + ASSERT_SAME_TYPE(decltype(m.extents()), const typename MDS::extents_type&); + ASSERT_NOEXCEPT(m.extents()); + assert(m.extents() == map.extents()); + + // rank() + ASSERT_SAME_TYPE(decltype(m.rank()), typename MDS::rank_type); + ASSERT_NOEXCEPT(m.rank()); + static_assert(MDS::rank() == MDS::extents_type::rank()); + + // rank_dynamic() + ASSERT_SAME_TYPE(decltype(m.rank_dynamic()), typename MDS::rank_type); + ASSERT_NOEXCEPT(m.rank_dynamic()); + static_assert(MDS::rank_dynamic() == MDS::extents_type::rank_dynamic()); + + // extent(r), static_extent(r), size() + if constexpr (MDS::rank() > 0) { + typename MDS::size_type size = 1; + for (typename MDS::rank_type r = 0; r < MDS::rank(); r++) { + ASSERT_SAME_TYPE(decltype(MDS::static_extent(r)), size_t); + ASSERT_NOEXCEPT(MDS::static_extent(r)); + assert(MDS::static_extent(r) == MDS::extents_type::static_extent(r)); + ASSERT_SAME_TYPE(decltype(m.extent(r)), typename MDS::index_type); + ASSERT_NOEXCEPT(m.extent(r)); + assert(m.extent(r) == m.extents().extent(r)); + size *= m.extent(r); + } + assert(m.size() == size); + } else { + assert(m.size() == 1); + } + ASSERT_SAME_TYPE(decltype(m.size()), typename MDS::size_type); + ASSERT_NOEXCEPT(m.size()); + + // empty() + ASSERT_SAME_TYPE(decltype(m.empty()), bool); + ASSERT_NOEXCEPT(m.empty()); + assert(m.empty() == (m.size() == 0)); + + // ===================================== + // Invariants coming from mapping + // ===================================== + + // mapping() + ASSERT_SAME_TYPE(decltype(m.mapping()), const M&); + ASSERT_NOEXCEPT(m.mapping()); + + // is_[always_]unique/exhaustive/strided() + ASSERT_SAME_TYPE(decltype(MDS::is_always_unique()), bool); + ASSERT_SAME_TYPE(decltype(MDS::is_always_exhaustive()), bool); + ASSERT_SAME_TYPE(decltype(MDS::is_always_strided()), bool); + ASSERT_SAME_TYPE(decltype(m.is_unique()), bool); + ASSERT_SAME_TYPE(decltype(m.is_exhaustive()), bool); + ASSERT_SAME_TYPE(decltype(m.is_strided()), bool); + assert(!noexcept(MDS::is_always_unique())); + assert(!noexcept(MDS::is_always_exhaustive())); + assert(!noexcept(MDS::is_always_strided())); + assert(!noexcept(m.is_unique())); + assert(!noexcept(m.is_exhaustive())); + assert(!noexcept(m.is_strided())); + static_assert(MDS::is_always_unique() == M::is_always_unique()); + static_assert(MDS::is_always_exhaustive() == M::is_always_exhaustive()); + static_assert(MDS::is_always_strided() == M::is_always_strided()); + assert(m.is_unique() == map.is_unique()); + assert(m.is_exhaustive() == map.is_exhaustive()); + assert(m.is_strided() == map.is_strided()); + + // stride(r) + if constexpr (MDS::rank() > 0) { + if (m.is_strided()) { + for (typename MDS::rank_type r = 0; r < MDS::rank(); r++) { + ASSERT_SAME_TYPE(decltype(m.stride(r)), typename MDS::index_type); + assert(!noexcept(m.stride(r))); + assert(m.stride(r) == map.stride(r)); + } + } + } + + // ===================================== + // Invariants coming from accessor + // ===================================== + + // accessor() + ASSERT_SAME_TYPE(decltype(m.accessor()), const A&); + ASSERT_NOEXCEPT(m.accessor()); +} + +template +constexpr void mixin_extents(const H& handle, const L& layout, const A& acc) { + constexpr size_t D = std::dynamic_extent; + test_mdspan_types(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(7)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents()), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(2, 3)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(0, 3)), acc); + test_mdspan_types(handle, construct_mapping(layout, std::extents(1, 2, 3, 2)), acc); +} + +template +constexpr void mixin_layout(const H& handle, const A& acc) { + mixin_extents(handle, std::layout_left(), acc); + mixin_extents(handle, std::layout_right(), acc); + mixin_extents(handle, layout_wrapping_integral<4>(), acc); +} + +template +constexpr void mixin_accessor() { + ElementPool elements; + mixin_layout(elements.get_ptr(), std::default_accessor()); +} + +constexpr bool test() { + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + return true; +} +int main(int, char**) { + (void)test(); + static_assert(test()); + return 0; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/swap.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/swap.pass.cpp @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// +// +// friend constexpr void swap(mdspan& x, mdspan& y) noexcept; +// +// Effects: Equivalent to: +// swap(x.ptr_, y.ptr_); +// swap(x.map_, y.map_); +// swap(x.acc_, y.acc_); + +#include +#include +#include +#include + +#include "test_macros.h" + +#include "../MinimalElementType.h" +#include "CustomTestLayouts.h" + +template +constexpr void test_swap(MDS a, MDS b) { + auto org_a = a; + auto org_b = b; + swap(a, b); + assert(a.extents() == org_b.extents()); + assert(b.extents() == org_a.extents()); + if constexpr (std::equality_comparable) { + assert(a.mapping() == org_b.mapping()); + assert(b.mapping() == org_a.mapping()); + } + if constexpr (std::equality_comparable) { + assert(a.data_handle() == org_b.data_handle()); + assert(b.data_handle() == org_a.data_handle()); + } + // This check uses a side effect of layout_wrapping_integral::swap to make sure + // mdspan calls the underlying components' swap via ADL + if !consteval { + if constexpr (std::is_same_v>) { + assert(MDS::mapping_type::swap_counter() > 0); + } + } +} + +constexpr bool test() { + using extents_t = std::extents; + float data_a[1024]; + float data_b[1024]; + { + std::mdspan a(data_a, extents_t(12)); + std::mdspan b(data_b, extents_t(5)); + test_swap(a, b); + } + { + layout_wrapping_integral<4>::template mapping map_a(extents_t(12), not_extents_constructible_tag()), + map_b(extents_t(5), not_extents_constructible_tag()); + std::mdspan a(data_a, map_a); + std::mdspan b(data_b, map_b); + test_swap(a, b); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/types.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/types.pass.cpp @@ -0,0 +1,99 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// +// +// template> +// class mdspan { +// public: +// 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; +// ... +// }; + +#include +#include +#include +#include + +#include "test_macros.h" + +#include "../MinimalElementType.h" +#include "CustomTestLayouts.h" + +template +void test_mdspan_types() { + using MDS = std::mdspan; + ASSERT_SAME_TYPE(typename MDS::extents_type, E); + ASSERT_SAME_TYPE(typename MDS::layout_type, L); + ASSERT_SAME_TYPE(typename MDS::accessor_type, A); + ASSERT_SAME_TYPE(typename MDS::mapping_type, typename L::template mapping); + ASSERT_SAME_TYPE(typename MDS::element_type, T); + ASSERT_SAME_TYPE(typename MDS::value_type, std::remove_cv_t); + ASSERT_SAME_TYPE(typename MDS::index_type, typename E::index_type); + ASSERT_SAME_TYPE(typename MDS::size_type, typename E::size_type); + ASSERT_SAME_TYPE(typename MDS::rank_type, typename E::rank_type); + ASSERT_SAME_TYPE(typename MDS::data_handle_type, typename A::data_handle_type); + ASSERT_SAME_TYPE(typename MDS::reference, typename A::reference); + + // check the size of mdspan + if constexpr (std::is_same_v || std::is_same_v) { + constexpr size_t sizeof_dht = sizeof(typename MDS::data_handle_type); + // we expect only a data_handle_type + the dynamic extents, however we need to take into account alignment + constexpr size_t expected_size = + sizeof_dht + + ((MDS::rank_dynamic() * sizeof(typename MDS::index_type) + sizeof_dht - 1) / sizeof_dht) * sizeof_dht; + LIBCPP_STATIC_ASSERT(sizeof(MDS) == expected_size); + } +} + +template +void mixin_extents() { + constexpr size_t D = std::dynamic_extent; + test_mdspan_types, L, A>(); + test_mdspan_types, L, A>(); + test_mdspan_types, L, A>(); + test_mdspan_types, L, A>(); + test_mdspan_types, L, A>(); + test_mdspan_types, L, A>(); + test_mdspan_types, L, A>(); + test_mdspan_types, L, A>(); +} + +template +void mixin_layout() { + mixin_extents(); + mixin_extents(); + mixin_extents, A>(); +} + +template +void mixin_accessor() { + mixin_layout>(); +} + +int main(int, char**) { + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + return 0; +}