Index: libcxx/docs/ReleaseNotes/17.rst =================================================================== --- libcxx/docs/ReleaseNotes/17.rst +++ libcxx/docs/ReleaseNotes/17.rst @@ -69,6 +69,7 @@ - P2585R0 - Improving default container formatting - P0408R7 - Efficient Access to ``basic_stringbuf``'s Buffer - P2474R2 - ``views::repeat`` +- P0009R18 - ``mdspan`` (``layout_stride`` is not done yet) Improvements and New Features ----------------------------- Index: libcxx/include/CMakeLists.txt =================================================================== --- libcxx/include/CMakeLists.txt +++ libcxx/include/CMakeLists.txt @@ -484,6 +484,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/extents.h =================================================================== --- libcxx/include/__mdspan/extents.h +++ libcxx/include/__mdspan/extents.h @@ -315,7 +315,8 @@ } template - requires(is_convertible_v<_OtherIndexType, index_type> && is_nothrow_constructible_v && + requires(is_convertible_v && + is_nothrow_constructible_v && (_Size == __rank_ || _Size == __rank_dynamic_)) explicit(_Size != __rank_dynamic_) _LIBCPP_HIDE_FROM_ABI constexpr extents(const array<_OtherIndexType, _Size>& __exts) noexcept @@ -325,7 +326,8 @@ } template - requires(is_convertible_v<_OtherIndexType, index_type> && is_nothrow_constructible_v && + requires(is_convertible_v && + is_nothrow_constructible_v && (_Size == __rank_ || _Size == __rank_dynamic_)) explicit(_Size != __rank_dynamic_) _LIBCPP_HIDE_FROM_ABI constexpr extents(const span<_OtherIndexType, _Size>& __exts) noexcept Index: libcxx/include/__mdspan/layout_left.h =================================================================== --- libcxx/include/__mdspan/layout_left.h +++ libcxx/include/__mdspan/layout_left.h @@ -75,8 +75,9 @@ _LIBCPP_HIDE_FROM_ABI constexpr mapping() noexcept = default; _LIBCPP_HIDE_FROM_ABI constexpr mapping(const mapping&) noexcept = default; _LIBCPP_HIDE_FROM_ABI constexpr mapping(const extents_type& __ext) 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."); + _LIBCPP_ASSERT_UNCATEGORIZED( + __required_span_size_is_representable(__ext), + "layout_left::mapping extents ctor: product of extents must be representable as index_type."); } template @@ -84,7 +85,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const mapping<_OtherExtents>& __other) noexcept : __extents_(__other.extents()) { - _LIBCPP_ASSERT( + _LIBCPP_ASSERT_UNCATEGORIZED( __mdspan_detail::__is_representable_as(__other.required_span_size()), "layout_left::mapping converting ctor: other.required_span_size() must be representable as index_type."); } @@ -94,7 +95,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const layout_right::mapping<_OtherExtents>& __other) noexcept : __extents_(__other.extents()) { - _LIBCPP_ASSERT( + _LIBCPP_ASSERT_UNCATEGORIZED( __mdspan_detail::__is_representable_as(__other.required_span_size()), "layout_left::mapping converting ctor: other.required_span_size() must be representable as index_type."); } @@ -122,8 +123,8 @@ requires((sizeof...(_Indices) == extents_type::rank()) && (is_convertible_v<_Indices, index_type> && ...) && (is_nothrow_constructible_v && ...)) _LIBCPP_HIDE_FROM_ABI constexpr index_type operator()(_Indices... __idx) const noexcept { - _LIBCPP_ASSERT(__mdspan_detail::__is_multidimensional_index_in(__extents_, __idx...), - "layout_left::mapping: out of bounds indexing"); + _LIBCPP_ASSERT_UNCATEGORIZED(__mdspan_detail::__is_multidimensional_index_in(__extents_, __idx...), + "layout_left::mapping: out of bounds indexing"); array __idx_a{static_cast(__idx)...}; return [&](index_sequence<_Pos...>) { index_type __res = 0; @@ -144,7 +145,7 @@ _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"); + _LIBCPP_ASSERT_UNCATEGORIZED(__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); @@ -159,7 +160,7 @@ } private: - extents_type __extents_{}; // exposition only + _LIBCPP_NO_UNIQUE_ADDRESS extents_type __extents_{}; }; #endif // _LIBCPP_STD_VER >= 23 Index: libcxx/include/__mdspan/layout_right.h =================================================================== --- libcxx/include/__mdspan/layout_right.h +++ libcxx/include/__mdspan/layout_right.h @@ -74,8 +74,9 @@ _LIBCPP_HIDE_FROM_ABI constexpr mapping() noexcept = default; _LIBCPP_HIDE_FROM_ABI constexpr mapping(const mapping&) noexcept = default; _LIBCPP_HIDE_FROM_ABI constexpr mapping(const extents_type& __ext) noexcept : __extents_(__ext) { - _LIBCPP_ASSERT(__required_span_size_is_representable(__ext), - "layout_right::mapping extents ctor: product of extents must be representable as index_type."); + _LIBCPP_ASSERT_UNCATEGORIZED( + __required_span_size_is_representable(__ext), + "layout_right::mapping extents ctor: product of extents must be representable as index_type."); } template @@ -83,7 +84,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const mapping<_OtherExtents>& __other) noexcept : __extents_(__other.extents()) { - _LIBCPP_ASSERT( + _LIBCPP_ASSERT_UNCATEGORIZED( __mdspan_detail::__is_representable_as(__other.required_span_size()), "layout_right::mapping converting ctor: other.required_span_size() must be representable as index_type."); } @@ -93,7 +94,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const layout_left::mapping<_OtherExtents>& __other) noexcept : __extents_(__other.extents()) { - _LIBCPP_ASSERT( + _LIBCPP_ASSERT_UNCATEGORIZED( __mdspan_detail::__is_representable_as(__other.required_span_size()), "layout_right::mapping converting ctor: other.required_span_size() must be representable as index_type."); } @@ -121,8 +122,8 @@ requires((sizeof...(_Indices) == extents_type::rank()) && (is_convertible_v<_Indices, index_type> && ...) && (is_nothrow_constructible_v && ...)) _LIBCPP_HIDE_FROM_ABI constexpr index_type operator()(_Indices... __idx) const noexcept { - _LIBCPP_ASSERT(__mdspan_detail::__is_multidimensional_index_in(__extents_, __idx...), - "layout_right::mapping: out of bounds indexing"); + _LIBCPP_ASSERT_UNCATEGORIZED(__mdspan_detail::__is_multidimensional_index_in(__extents_, __idx...), + "layout_right::mapping: out of bounds indexing"); return [&](index_sequence<_Pos...>) { index_type __res = 0; ((__res = static_cast(__idx) + __extents_.extent(_Pos) * __res), ...); @@ -141,7 +142,7 @@ _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_right::mapping::stride(): invalid rank index"); + _LIBCPP_ASSERT_UNCATEGORIZED(__r < extents_type::rank(), "layout_right::mapping::stride(): invalid rank index"); index_type __s = 1; for (rank_type __i = extents_type::rank() - 1; __i > __r; __i--) __s *= __extents_.extent(__i); @@ -156,7 +157,7 @@ } private: - extents_type __extents_{}; // exposition only + _LIBCPP_NO_UNIQUE_ADDRESS extents_type __extents_{}; }; #endif // _LIBCPP_STD_VER >= 23 Index: libcxx/include/__mdspan/mdspan.h =================================================================== --- /dev/null +++ libcxx/include/__mdspan/mdspan.h @@ -0,0 +1,310 @@ +// -*- 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 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"); + +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; + + _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.mdspan.cons], mdspan constructors, assignment, and destructor + + _LIBCPP_HIDE_FROM_ABI constexpr mdspan() + requires((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 + 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, 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 + 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 checks 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 + // always construct its extents() 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.mdspan.members], members + + template + requires((is_convertible_v<_OtherIndexTypes, index_type> && ...) && + (is_nothrow_constructible_v && ...) && + (sizeof...(_OtherIndexTypes) == rank())) + _LIBCPP_HIDE_FROM_ABI constexpr reference operator[](_OtherIndexTypes... __indices) const { + _LIBCPP_ASSERT_UNCATEGORIZED(__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 + requires(is_convertible_v && + is_nothrow_constructible_v) + _LIBCPP_HIDE_FROM_ABI constexpr reference operator[](const array< _OtherIndexType, rank()>& __indices) const { + return __acc_.access(__ptr_, [&](index_sequence<_Idxs...>) { + return __map_(__indices[_Idxs]...); + }(make_index_sequence())); + } + + template + requires(is_convertible_v && + is_nothrow_constructible_v) + _LIBCPP_HIDE_FROM_ABI constexpr reference operator[](span<_OtherIndexType, rank()> __indices) const { + return __acc_.access(__ptr_, [&](index_sequence<_Idxs...>) { + return __map_(__indices[_Idxs]...); + }(make_index_sequence())); + } + + _LIBCPP_HIDE_FROM_ABI constexpr size_type size() const noexcept { + _LIBCPP_ASSERT_UNCATEGORIZED( + false == ([&](index_sequence<_Idxs...>) { + size_type __prod = 1; + return (__builtin_mul_overflow(__prod, extent(_Idxs), &__prod) || ... || false); + }(make_index_sequence())), + "mdspan: size() is not representable as size_type"); + return [&](index_sequence<_Idxs...>) { + return ((static_cast(__map_.extents().extent(_Idxs))) * ... * size_type(1)); + }(make_index_sequence()); + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool empty() const noexcept { + return [&](index_sequence<_Idxs...>) { + return (rank() > 0) && ((__map_.extents().extent(_Idxs) == index_type(0)) || ... || false); + }(make_index_sequence()); + } + + _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_); + } + + _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_; }; + + _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: +// TODO: fix when mingw build passes conversion test with no-unique-address enabled +// apparently there is a bug where the no-unique-address thing, makes the conversion constructor +// in the case where mapping and accessor the last byte of the data handle.. +# ifndef _WIN32 + _LIBCPP_NO_UNIQUE_ADDRESS data_handle_type __ptr_{}; + _LIBCPP_NO_UNIQUE_ADDRESS mapping_type __map_{}; + _LIBCPP_NO_UNIQUE_ADDRESS accessor_type __acc_{}; +# else + data_handle_type __ptr_{}; + mapping_type __map_{}; + accessor_type __acc_{}; +# endif + + template + friend class mdspan; +}; + +template + requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0)) +explicit mdspan(_ElementType*, _OtherIndexTypes...) + -> mdspan<_ElementType, dextents>; + +template + requires(is_pointer_v>) +mdspan(Pointer&&) -> mdspan>, extents>; + +template + 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 @@ -1516,6 +1516,7 @@ } module std_private_mdspan_layout_left [system] { header "__mdspan/layout_left.h" } module std_private_mdspan_layout_right [system] { header "__mdspan/layout_right.h" } +module std_private_mdspan_mdspan [system] { header "__mdspan/mdspan.h" } module std_private_mdspan_mdspan_fwd [system] { header "__fwd/mdspan.h" } module std_private_memory_addressof [system] { header "__memory/addressof.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/ConvertibleToIntegral.h =================================================================== --- libcxx/test/std/containers/views/mdspan/ConvertibleToIntegral.h +++ libcxx/test/std/containers/views/mdspan/ConvertibleToIntegral.h @@ -20,4 +20,25 @@ constexpr operator char() const noexcept { return val; } }; +// only non-const convertible +struct IntTypeNC { + int val; + constexpr IntTypeNC() = default; + constexpr IntTypeNC(int v) noexcept : val(v){}; + + constexpr bool operator==(const IntType& rhs) const { return val == rhs.val; } + constexpr operator int() noexcept { return val; } + constexpr operator unsigned() { return val; } + constexpr operator char() noexcept { return val; } +}; + +// weird configurability of convertibility to int +template +struct IntConfig { + int val; + constexpr explicit IntConfig(int val_):val(val_){} + constexpr operator int() noexcept(ctor_nt_nc) requires(conv_nc) { return val; } + constexpr operator int() const noexcept(ctor_nt_c) requires(conv_c) { return val; } +}; + #endif // TEST_STD_CONTAINERS_VIEWS_MDSPAN_CONVERTIBLE_TO_INTEGRAL_H Index: libcxx/test/std/containers/views/mdspan/extents/assert.obs.pass.cpp =================================================================== --- libcxx/test/std/containers/views/mdspan/extents/assert.obs.pass.cpp +++ libcxx/test/std/containers/views/mdspan/extents/assert.obs.pass.cpp @@ -6,7 +6,7 @@ //===----------------------------------------------------------------------===// // REQUIRES: has-unix-headers // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 -// UNSUPPORTED: !libcpp-has-hardened-mode && !libcpp-has-debug-mode +// UNSUPPORTED: !libcpp-has-debug-mode // XFAIL: availability-verbose_abort-missing // Index: libcxx/test/std/containers/views/mdspan/extents/ctor_from_array.pass.cpp =================================================================== --- libcxx/test/std/containers/views/mdspan/extents/ctor_from_array.pass.cpp +++ libcxx/test/std/containers/views/mdspan/extents/ctor_from_array.pass.cpp @@ -82,5 +82,17 @@ static_assert(std::is_convertible_v, "Test helper IntType unexpectedly not convertible to int"); static_assert(!std::is_constructible_v< std::extents, std::array>, "extents constructible from illegal arguments"); + + // index_type is not nothrow constructible + static_assert(std::is_convertible_v); + static_assert(std::is_convertible_v); + static_assert(!std::is_nothrow_constructible_v); + static_assert(!std::is_constructible_v, std::array>); + + // convertible from non-const to index_type but not from const + static_assert(std::is_convertible_v); + static_assert(!std::is_convertible_v); + static_assert(std::is_nothrow_constructible_v); + static_assert(!std::is_constructible_v, std::array>); return 0; } Index: libcxx/test/std/containers/views/mdspan/extents/ctor_from_span.pass.cpp =================================================================== --- libcxx/test/std/containers/views/mdspan/extents/ctor_from_span.pass.cpp +++ libcxx/test/std/containers/views/mdspan/extents/ctor_from_span.pass.cpp @@ -84,5 +84,17 @@ static_assert(std::is_convertible_v, "Test helper IntType unexpectedly not convertible to int"); static_assert(!std::is_constructible_v< std::extents, std::span>, "extents constructible from illegal arguments"); + + // index_type is not nothrow constructible + static_assert(std::is_convertible_v); + static_assert(std::is_convertible_v); + static_assert(!std::is_nothrow_constructible_v); + static_assert(!std::is_constructible_v, std::span>); + + // convertible from non-const to index_type but not from const + static_assert(std::is_convertible_v); + static_assert(!std::is_convertible_v); + static_assert(std::is_nothrow_constructible_v); + static_assert(!std::is_constructible_v, std::span>); return 0; } Index: libcxx/test/std/containers/views/mdspan/layout_left/assert.ctor.extents.pass.cpp =================================================================== --- libcxx/test/std/containers/views/mdspan/layout_left/assert.ctor.extents.pass.cpp +++ libcxx/test/std/containers/views/mdspan/layout_left/assert.ctor.extents.pass.cpp @@ -8,7 +8,7 @@ // REQUIRES: has-unix-headers // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 -// UNSUPPORTED: !libcpp-has-hardened-mode && !libcpp-has-debug-mode +// UNSUPPORTED: !libcpp-has-debug-mode // XFAIL: availability-verbose_abort-missing // Index: libcxx/test/std/containers/views/mdspan/layout_left/assert.index_operator.pass.cpp =================================================================== --- libcxx/test/std/containers/views/mdspan/layout_left/assert.index_operator.pass.cpp +++ libcxx/test/std/containers/views/mdspan/layout_left/assert.index_operator.pass.cpp @@ -8,7 +8,7 @@ // REQUIRES: has-unix-headers // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 -// UNSUPPORTED: !libcpp-has-hardened-mode && !libcpp-has-debug-mode +// UNSUPPORTED: !libcpp-has-debug-mode // XFAIL: availability-verbose_abort-missing // Index: libcxx/test/std/containers/views/mdspan/layout_left/assert.stride.pass.cpp =================================================================== --- libcxx/test/std/containers/views/mdspan/layout_left/assert.stride.pass.cpp +++ libcxx/test/std/containers/views/mdspan/layout_left/assert.stride.pass.cpp @@ -8,7 +8,7 @@ // REQUIRES: has-unix-headers // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 -// UNSUPPORTED: !libcpp-has-hardened-mode && !libcpp-has-debug-mode +// UNSUPPORTED: !libcpp-has-debug-mode // XFAIL: availability-verbose_abort-missing // Index: libcxx/test/std/containers/views/mdspan/layout_right/assert.ctor.extents.pass.cpp =================================================================== --- libcxx/test/std/containers/views/mdspan/layout_right/assert.ctor.extents.pass.cpp +++ libcxx/test/std/containers/views/mdspan/layout_right/assert.ctor.extents.pass.cpp @@ -8,7 +8,7 @@ // REQUIRES: has-unix-headers // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 -// UNSUPPORTED: !libcpp-has-hardened-mode && !libcpp-has-debug-mode +// UNSUPPORTED: !libcpp-has-debug-mode // XFAIL: availability-verbose_abort-missing // Index: libcxx/test/std/containers/views/mdspan/layout_right/assert.index_operator.pass.cpp =================================================================== --- libcxx/test/std/containers/views/mdspan/layout_right/assert.index_operator.pass.cpp +++ libcxx/test/std/containers/views/mdspan/layout_right/assert.index_operator.pass.cpp @@ -8,7 +8,7 @@ // REQUIRES: has-unix-headers // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 -// UNSUPPORTED: !libcpp-has-hardened-mode && !libcpp-has-debug-mode +// UNSUPPORTED: !libcpp-has-debug-mode // XFAIL: availability-verbose_abort-missing // Index: libcxx/test/std/containers/views/mdspan/layout_right/assert.stride.pass.cpp =================================================================== --- libcxx/test/std/containers/views/mdspan/layout_right/assert.stride.pass.cpp +++ libcxx/test/std/containers/views/mdspan/layout_right/assert.stride.pass.cpp @@ -8,7 +8,7 @@ // REQUIRES: has-unix-headers // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 -// UNSUPPORTED: !libcpp-has-hardened-mode && !libcpp-has-debug-mode +// UNSUPPORTED: !libcpp-has-debug-mode // XFAIL: availability-verbose_abort-missing // Index: libcxx/test/std/containers/views/mdspan/mdspan/CustomTestAccessors.h =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/CustomTestAccessors.h @@ -0,0 +1,314 @@ +// -*- 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 +#include + +// This contains a bunch of accessors and handles which have different properties +// regarding constructibility and convertibility in order to test mdspan constraints + +// 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_) {} +}; + +// handle that can't convert from T to const T +template +struct not_const_convertible_handle { + T* ptr; + constexpr not_const_convertible_handle() : ptr(nullptr) {} + constexpr not_const_convertible_handle(T* ptr_) : ptr(ptr_) {} + + constexpr T& operator[](size_t i) const { return ptr[i]; } +}; + +// handle where move has side effects +template +struct move_counted_handle { + T* ptr; + constexpr move_counted_handle() = default; + constexpr move_counted_handle(const move_counted_handle&) = default; + template + requires(std::is_constructible_v) + constexpr move_counted_handle(const move_counted_handle& other) : ptr(other.ptr){}; + constexpr move_counted_handle(move_counted_handle&& other) { + ptr = other.ptr; + if !consteval { + move_counter()++; + } + } + constexpr move_counted_handle(T* ptr_) : ptr(ptr_) {} + + constexpr move_counted_handle& operator=(const move_counted_handle&) = default; + + constexpr T& operator[](size_t i) const { return ptr[i]; } + + static int& move_counter() { + static int c = 0; + return c; + } +}; + +// non-default constructible accessor with a bunch of different data handles +template +struct checked_accessor { + size_t N; + using offset_policy = std::default_accessor; + using element_type = ElementType; + using reference = ElementType&; + using data_handle_type = move_counted_handle; + + constexpr checked_accessor(size_t N_) : N(N_) {} + template + requires(std::is_convertible_v) + explicit constexpr checked_accessor(const checked_accessor& other) noexcept { + N = other.N; + } + + constexpr reference access(data_handle_type p, size_t i) const noexcept { + assert(i < N); + return p[i]; + } + constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept { + assert(i < N); + return data_handle_type(p.ptr + i); + } +}; + +static_assert(std::is_constructible_v, const checked_accessor&>); +static_assert(!std::is_convertible_v&, checked_accessor>); + +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) + constexpr checked_accessor(checked_accessor&& other) noexcept { + N = other.N; + } + + constexpr reference access(data_handle_type p, size_t i) const noexcept { + assert(i < N); + return p.ptr[i]; + } + 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 = unsigned; + using reference = unsigned; + using data_handle_type = not_const_convertible_handle; + + constexpr checked_accessor() : N(0) {} + constexpr checked_accessor(size_t N_) : N(N_) {} + constexpr checked_accessor(const checked_accessor& acc) : N(acc.N) {} + + constexpr reference access(data_handle_type p, size_t i) const noexcept { + assert(i < N); + return p[i]; + } + constexpr auto 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 unsigned; + using reference = unsigned; + using data_handle_type = not_const_convertible_handle; + + constexpr checked_accessor() : N(0) {} + constexpr checked_accessor(size_t N_) : N(N_) {} + constexpr checked_accessor(const checked_accessor& acc) : N(acc.N) {} + + template + constexpr explicit(std::is_const_v) checked_accessor(OtherACC&& acc) : N(acc.N) {} + + constexpr reference access(data_handle_type p, size_t i) const noexcept { + assert(i < N); + return p[i]; + } + constexpr auto 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 float; + using reference = const float&; + using data_handle_type = move_counted_handle; + + constexpr checked_accessor() : N(0) {} + constexpr checked_accessor(size_t N_) : N(N_) {} + constexpr checked_accessor(const checked_accessor& acc) : N(acc.N) {} + + constexpr checked_accessor(checked_accessor&& acc) : N(acc.N) {} + + constexpr reference access(data_handle_type p, size_t i) const noexcept { + assert(i < N); + return p[i]; + } + constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept { + assert(i < N); + return data_handle_type(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 = move_counted_handle; + + constexpr checked_accessor() : N(0) {} + constexpr checked_accessor(size_t N_) : N(N_) {} + constexpr checked_accessor(const checked_accessor& acc) : N(acc.N) {} + + constexpr reference access(data_handle_type p, size_t i) const noexcept { + assert(i < N); + return p[i]; + } + constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept { + assert(i < N); + return data_handle_type(p.ptr + i); + } +}; + +// Data handle pair which has configurable conversion properties +// bool template parameters are used to enable/disable ctors and assignment +// the c is the one for const T the nc for non-const (so we can convert mdspan) +// Note both take non-const T as template parameter though +template +struct conv_test_accessor_c; + +template +struct conv_test_accessor_nc { + using offset_policy = std::default_accessor; + using element_type = T; + using reference = T&; + using data_handle_type = T*; + + constexpr conv_test_accessor_nc() = default; + constexpr conv_test_accessor_nc(const conv_test_accessor_nc&) = default; + + template + constexpr operator conv_test_accessor_c() + requires(conv_nc) + { + return conv_test_accessor_c{}; + } + template + constexpr operator conv_test_accessor_c() const + requires(conv_c) + { + return conv_test_accessor_c{}; + } + + constexpr reference access(data_handle_type p, size_t i) const noexcept { return p[i]; } + constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept { return p + i; } +}; + +template +struct conv_test_accessor_c { + using offset_policy = std::default_accessor; + using element_type = const T; + using reference = const T&; + using data_handle_type = const T*; + + constexpr conv_test_accessor_c() = default; + constexpr conv_test_accessor_c(const conv_test_accessor_c&) = default; + + template + constexpr conv_test_accessor_c(const conv_test_accessor_nc&) + requires(ctor_c) + {} + template + constexpr conv_test_accessor_c(conv_test_accessor_nc&&) + requires(ctor_mv) + {} + template + constexpr conv_test_accessor_c& operator=(const conv_test_accessor_nc&) + requires(assign_c) + { + return {}; + } + template + constexpr conv_test_accessor_c& operator=(conv_test_accessor_nc&&) + requires(assign_mv) + { + return {}; + } + + constexpr reference access(data_handle_type p, size_t i) const noexcept { return p[i]; } + constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept { return p + i; } +}; + +template +struct convertible_accessor_but_not_handle { + size_t N; + using offset_policy = std::default_accessor; + using element_type = ElementType; + using reference = ElementType&; + using data_handle_type = not_const_convertible_handle; + + constexpr convertible_accessor_but_not_handle() = default; + template + requires(std::is_convertible_v) + explicit constexpr convertible_accessor_but_not_handle( + const convertible_accessor_but_not_handle& other) noexcept { + N = other.N; + } + + constexpr reference access(data_handle_type p, size_t i) const noexcept { + assert(i < N); + return p[i]; + } + constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept { + assert(i < N); + return data_handle_type(p.ptr + 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,325 @@ +// -*- 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 +#include + +// Layout that wraps indices to test some idiosyncratic behavior +// - basically it is a layout_left where indicies are first wrapped i.e. i%Wrap +// - 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 side-effects 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::mapping { + static constexpr typename Extents::index_type Wrap = static_cast(WrapArg); + +public: + using extents_type = Extents; + using index_type = typename extents_type::index_type; + using size_type = typename extents_type::size_type; + using rank_type = typename extents_type::rank_type; + using layout_type = layout_wrapping_integral; + +private: + 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, std::min(ext.extent(r), Wrap), &prod); + if (overflowed) + return false; + } + return true; + } + +public: + constexpr mapping() noexcept = delete; + constexpr mapping(const mapping& other) noexcept : extents_(other.extents()){}; + constexpr mapping(extents_type&& ext) noexcept + requires(Wrap == 8) + : extents_(ext) {} + constexpr mapping(const extents_type& ext, not_extents_constructible_tag) noexcept : extents_(ext) {} + + template + requires(std::is_constructible_v && (Wrap != 8)) + constexpr explicit(!std::is_convertible_v) + mapping(const mapping& other) noexcept { + 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); + } + template + requires(std::is_constructible_v && (Wrap == 8)) + constexpr explicit(!std::is_convertible_v) + mapping(mapping&& other) noexcept { + 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); + } + + constexpr mapping& operator=(const mapping& other) noexcept { + extents_ = other.extents_; + return *this; + }; + + constexpr const extents_type& extents() const noexcept { return extents_; } + + 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 && ...) && + (std::is_nothrow_constructible_v && ...)) + constexpr index_type operator()(Indices... idx) const noexcept { + std::array idx_a{static_cast(static_cast(idx) % Wrap)...}; + return [&](std::index_sequence) { + 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()); + } + + static constexpr bool is_always_unique() noexcept { return false; } + static constexpr bool is_always_exhaustive() noexcept { return true; } + static constexpr bool is_always_strided() noexcept { return false; } + + 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; + } + static constexpr bool is_exhaustive() noexcept { return true; } + 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; + } + + constexpr index_type stride(rank_type r) const noexcept + requires(extents_type::rank() > 0) + { + 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()) + friend constexpr bool operator==(const mapping& lhs, const mapping& 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_{}; +}; + +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, Extents exts) { + return typename layout_wrapping_integral::template mapping(exts, not_extents_constructible_tag{}); +} + + +// This layout does not check convertibility of extents for its conversion ctor +// Allows triggering mdspan's ctor static assertion on convertibility of extents +class always_convertible_layout { +public: + template + class mapping; +}; + +template +class always_convertible_layout::mapping { +public: + using extents_type = Extents; + using index_type = typename extents_type::index_type; + using size_type = typename extents_type::size_type; + using rank_type = typename extents_type::rank_type; + using layout_type = always_convertible_layout; + +private: + 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; + } + +public: + constexpr mapping() noexcept = delete; + constexpr mapping(const mapping& other) noexcept : extents_(other.extents()){}; + constexpr mapping(const extents_type& ext) noexcept : extents_(ext){}; + + template + constexpr mapping(const mapping& other) noexcept { + if constexpr (extents_type::rank() == OtherExtents::rank()) { + 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); + } else { + extents_ = extents_type(); + } + } + + constexpr mapping& operator=(const mapping& other) noexcept { + extents_ = other.extents_; + return *this; + }; + + constexpr const extents_type& extents() const noexcept { return extents_; } + + 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); + return size; + } + + template + requires((sizeof...(Indices) == extents_type::rank()) && (std::is_convertible_v && ...) && + (std::is_nothrow_constructible_v && ...)) + constexpr index_type operator()(Indices... idx) const noexcept { + std::array idx_a{static_cast(static_cast(idx))...}; + return [&](std::index_sequence) { + index_type res = 0; + ((res = idx_a[extents_type::rank() - 1 - Pos] + extents_.extent(extents_type::rank() - 1 - Pos) * res), ...); + return res; + }(std::make_index_sequence()); + } + + static constexpr bool is_always_unique() noexcept { return false; } + static constexpr bool is_always_exhaustive() noexcept { return true; } + static constexpr bool is_always_strided() noexcept { return false; } + + static constexpr bool is_unique() noexcept { return true; } + static constexpr bool is_exhaustive() noexcept { return true; } + static constexpr bool is_strided() noexcept { return true; } + + constexpr index_type stride(rank_type r) const noexcept + requires(extents_type::rank() > 0) + { + 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()) + friend constexpr bool operator==(const mapping& lhs, const mapping& 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_{}; +}; +#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 +// UNSUPPORTED: !libcpp-has-debug-mode +// XFAIL: availability-verbose_abort-missing + +// + +// 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 +// UNSUPPORTED: !libcpp-has-debug-mode +// XFAIL: availability-verbose_abort-missing + +// + +// 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,50 @@ +// +// 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 +// UNSUPPORTED: !libcpp-has-debug-mode +// XFAIL: availability-verbose_abort-missing + +// + +// 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; + // make sure we are not failing because of using index_type instead of size_type + { + typename layout_wrapping_integral<4>::template mapping> map( + std::dextents(100, 2), not_extents_constructible_tag()); + std::mdspan, layout_wrapping_integral<4>> mds(data.data(), map); + assert(map.required_span_size() == char(8)); + assert((static_cast(200) == mds.size())); + } + { + typename layout_wrapping_integral<4>::template mapping> map( + std::dextents(100, 3), not_extents_constructible_tag()); + std::mdspan, layout_wrapping_integral<4>> mds(data.data(), map); + // sanity check + assert(map.required_span_size() == char(12)); + // 100 x 3 exceeds 256 + { + 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,110 @@ +//===----------------------------------------------------------------------===// +// +// 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& operator=(const mdspan& rhs) = default; + +#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); + + static_assert(std::is_trivially_assignable_v == + ((!std::is_class_v || + std::is_trivially_assignable_v)&&std::is_trivially_assignable_v && + std::is_trivially_assignable_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) { + // make sure we test a trivially assignable mapping + static_assert(std::is_trivially_assignable_v>, + const typename std::layout_left::template mapping>&>); + mixin_extents(handle, std::layout_left(), acc); + mixin_extents(handle, std::layout_right(), acc); + // make sure we test a not trivially assignable mapping + static_assert(!std::is_trivially_assignable_v< + typename layout_wrapping_integral<4>::template mapping>, + const typename layout_wrapping_integral<4>::template mapping>&>); + mixin_extents(handle, layout_wrapping_integral<4>(), acc); +} + +template +constexpr void mixin_accessor() { + ElementPool elements; + // make sure we test trivially constructible 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**) { + 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,276 @@ +//===----------------------------------------------------------------------===// +// +// 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" + +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 +concept mapping_requirements = requires() { + requires(std::copyable && std::equality_comparable) && std::is_nothrow_move_constructible_v && + std::is_nothrow_move_assignable_v && std::is_nothrow_swappable_v; +}; + +template +constexpr void test_conversion(FromMDS from_mds) { + // check some requirements, to see we didn't screw up our test layouts/accessors + static_assert(mapping_requirements); + static_assert(mapping_requirements); + + constexpr bool constructible = + std::is_constructible_v && + std::is_constructible_v; + constexpr bool convertible = + std::is_convertible_v && + std::is_convertible_v; + constexpr bool passes_mandates = + std::is_constructible_v && + std::is_constructible_v; + + if constexpr (constructible) { + if constexpr (passes_mandates) { + 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 construct_from_mds(const FromH& handle, const FromL& layout, const FromE& exts, const FromA& acc) { + using ToMDS = std::mdspan; + using FromMDS = std::mdspan; + test_conversion(FromMDS(handle, construct_mapping(layout, exts), acc)); +} + +template +constexpr void mixin_extents(const FromH& handle, const FromL& layout, const FromA& acc) { + constexpr size_t D = std::dynamic_extent; + // constructible and convertible + construct_from_mds, ToA>(handle, layout, std::dextents(), acc); + construct_from_mds, ToA>(handle, layout, std::dextents(4), acc); + construct_from_mds, ToA>(handle, layout, std::extents(), acc); + construct_from_mds, ToA>(handle, layout, std::dextents(4, 5), acc); + construct_from_mds, ToA>(handle, layout, std::dextents(4, 5), acc); + construct_from_mds, ToA>(handle, layout, std::extents(4), acc); + construct_from_mds, ToA>(handle, layout, std::extents(4), acc); + construct_from_mds, ToA>(handle, layout, std::extents(4), acc); + construct_from_mds, ToA>(handle, layout, std::extents(4, 6), acc); + + // not convertible + construct_from_mds, ToA>(handle, layout, std::dextents(4), acc); + construct_from_mds, ToA>( + handle, layout, std::extents(4, 6, 7), acc); + + // not constructible + construct_from_mds, ToA>(handle, layout, std::dextents(4, 5), acc); + construct_from_mds, ToA>(handle, layout, std::extents(4, 6), acc); +} + +template +constexpr void mixin_layout(const FromH& handle, const FromA& acc) { + mixin_extents(handle, std::layout_left(), acc); + mixin_extents(handle, std::layout_right(), acc); + // Check layout policy conversion + // different layout policies, but constructible and convertible + static_assert(std::is_constructible_v>, + const std::layout_right::mapping>&>); + static_assert(std::is_convertible_v>&, + std::layout_left::mapping>>); + // different layout policies, not constructible + static_assert(!std::is_constructible_v>, + const std::layout_right::mapping>&>); + // different layout policies, constructible and not convertible + static_assert(std::is_constructible_v>, + const std::layout_right::mapping>&>); + static_assert(!std::is_convertible_v>&, + std::layout_left::mapping>>); + + mixin_extents(handle, std::layout_right(), acc); + mixin_extents, ToA>(handle, layout_wrapping_integral<4>(), acc); + // different layout policies, constructible and not convertible + static_assert(!std::is_constructible_v::mapping>, + const layout_wrapping_integral<8>::mapping>&>); + static_assert(std::is_constructible_v::mapping>, + layout_wrapping_integral<8>::mapping>>); + mixin_extents, ToA>(handle, layout_wrapping_integral<8>(), acc); +} + +// check that we cover all corners with respect to constructibility and convertibility +template +constexpr bool test(FromA from_acc) { + static_assert(std::copyable); + static_assert(std::copyable); + static_assert(std::is_constructible_v == constructible_constref_acc); + static_assert(std::is_constructible_v == constructible_nonconst_acc); + static_assert(std::is_constructible_v == + constructible_constref_handle); + static_assert(std::is_constructible_v == + constructible_nonconst_handle); + static_assert(std::is_convertible_v == convertible_constref_acc); + static_assert(std::is_convertible_v == convertible_nonconst_acc); + static_assert(std::is_convertible_v == + convertible_constref_handle); + static_assert(std::is_convertible_v == + convertible_nonconst_handle); + + ElementPool elements; + mixin_layout(typename FromA::data_handle_type(elements.get_ptr()), from_acc); + return true; +} + +int main(int, char**) { + // using shorthands here: t and o for better visual distinguishability + constexpr bool t = true; + constexpr bool o = false; + + // possibility matrix for constructibility and convertibility https://godbolt.org/z/98KGo8Wbc + // you can't have convertibility without constructibility + // and if you take const T& then you also can take T + // this leaves 7 combinations + // const_ref_ctor, const_ref_conv, nonconst_ctor, nonconst_conv, tested + // o o o o X + // o o t o X + // o o t t X + // t o t o X + // t o t t X + // t t t o X + // t t t t X + + // checked_accessor has various weird data handles and some weird conversion properties + // conv_test_accessor_c/nc is an accessor pair which has configurable conversion properties, but plain ptr as data handle + // accessor constructible + test>(std::default_accessor()); + test>(std::default_accessor()); + test>(std::default_accessor()); + test>( + std::default_accessor()); + test>(checked_accessor(1024)); + test>(checked_accessor(1024)); + test>(checked_accessor(1024)); + test>(checked_accessor(1024)); + test>(checked_accessor(1024)); + test>(checked_accessor(1024)); + test>(checked_accessor(1024)); + test>(conv_test_accessor_nc()); + test>(conv_test_accessor_nc()); + test>(conv_test_accessor_nc()); + test>(conv_test_accessor_nc()); + + // ElementType convertible, but accessor not constructible + test>(std::default_accessor()); + test>(checked_accessor(1024)); + test>(checked_accessor(1024)); + test>(conv_test_accessor_nc()); + test>(conv_test_accessor_nc()); + test>(conv_test_accessor_nc()); + + // Ran into trouble with doing it all in one static_assert: exceeding step limit for consteval + static_assert(test>(std::default_accessor())); + static_assert(test>(std::default_accessor())); + static_assert(test>( + std::default_accessor())); + static_assert(test>( + std::default_accessor())); + static_assert(test>(checked_accessor(1024))); + static_assert(test>(checked_accessor(1024))); + static_assert(test>(checked_accessor(1024))); + static_assert(test>(checked_accessor(1024))); + static_assert(test>(checked_accessor(1024))); + static_assert( + test>(checked_accessor(1024))); + static_assert(test>( + checked_accessor(1024))); + static_assert( + test>(conv_test_accessor_nc())); + static_assert( + test>(conv_test_accessor_nc())); + static_assert( + test>(conv_test_accessor_nc())); + static_assert( + test>(conv_test_accessor_nc())); + static_assert(test>(std::default_accessor())); + static_assert(test>(checked_accessor(1024))); + static_assert(test>(checked_accessor(1024))); + static_assert( + test>(conv_test_accessor_nc())); + static_assert( + test>(conv_test_accessor_nc())); + static_assert( + test>(conv_test_accessor_nc())); + + return 0; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/conversion.verify.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/conversion.verify.cpp @@ -0,0 +1,57 @@ +// +// 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 "CustomTestAccessors.h" +#include "CustomTestLayouts.h" + +void cant_construct_data_handle_type() { + int data; + std::mdspan, std::layout_right, convertible_accessor_but_not_handle> m_nc(&data); + // expected-error-re@*:* {{{{.*}}no matching constructor for initialization of {{.*}} (aka 'not_const_convertible_handle')}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}mdspan: incompatible data_handle_type for mdspan construction}} + [[maybe_unused]] std:: + mdspan, std::layout_right, convertible_accessor_but_not_handle> + m_c(m_nc); +} + +void mapping_constructible_despite_extents_compatibility() { + int data; + std::mdspan, always_convertible_layout> m(&data); + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}mdspan: incompatible extents for mdspan construction}} + [[maybe_unused]] std::mdspan, always_convertible_layout> m2(m); +} 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 constructible 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**) { + 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,111 @@ +//===----------------------------------------------------------------------===// +// +// 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; + + static_assert(hc == std::is_default_constructible_v); + static_assert(mc == std::is_default_constructible_v); + static_assert(ac == std::is_default_constructible_v); + + if constexpr (MDS::rank_dynamic() > 0 && hc && mc && ac) { + 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, std::is_same_v>( + 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**) { + 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,197 @@ +//===----------------------------------------------------------------------===// +// +// 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 "../ConvertibleToIntegral.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; + if !consteval { + move_counted_handle::move_counter() = 0; + } + MDS m(handle, exts); + if !consteval { + if constexpr (std::is_same_v>) { + assert((H::move_counter() == 1)); + } + } + + 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; + static_assert(mec == std::is_constructible_v); + static_assert(ac == std::is_default_constructible_v); + if constexpr (mec && ac) { + // 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 constructible from extents (via its move constructor) + static_assert(std::is_constructible_v::template mapping>, + std::extents>); + static_assert(!std::is_constructible_v::template mapping>, + const std::extents&>); + mixin_extents(handle, layout_wrapping_integral<8>(), acc); + // Sanity check that this layouts mapping is not constructible from extents + static_assert(!std::is_constructible_v::template mapping>, + std::extents>); + static_assert(!std::is_constructible_v::template mapping>, + const 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(); + + // test non-constructibility from wrong array type + constexpr size_t D = std::dynamic_extent; + using mds_t = std::mdspan>; + // sanity check + static_assert(std::is_constructible_v>); + static_assert(std::is_constructible_v>); + // wrong size + static_assert(!std::is_constructible_v>); + static_assert(!std::is_constructible_v>); + // not convertible to index_type + static_assert(std::is_convertible_v); + static_assert(!std::is_convertible_v); + static_assert(!std::is_constructible_v>); + + // index_type is not nothrow constructible + using mds_uchar_t = std::mdspan>; + static_assert(std::is_convertible_v); + static_assert(std::is_convertible_v); + static_assert(!std::is_nothrow_constructible_v); + static_assert(!std::is_constructible_v>); + + // convertible from non-const to index_type but not from const + using mds_int_t = std::mdspan>; + static_assert(std::is_convertible_v); + static_assert(!std::is_convertible_v); + static_assert(std::is_nothrow_constructible_v); + static_assert(!std::is_constructible_v>); + + // can't test a combo where std::is_nothrow_constructible_v is true, + // but std::is_convertible_v is false + + // test non-constructibility from wrong handle_type + static_assert(!std::is_constructible_v>); + + return true; +} + +int main(int, char**) { + 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,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 + +// + +// 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; + + static_assert(mec == std::is_constructible_v); + static_assert(ac == std::is_default_constructible_v); + if constexpr (mec && ac) { + if !consteval { + move_counted_handle::move_counter() = 0; + } + // use formulation of constructor which tests that its not explicit + MDS m = {handle, map.extents()}; + if !consteval { + if constexpr (std::is_same_v>) { + assert((H::move_counter() == 1)); + } + } + 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 + // Sanity check that this layouts mapping is constructible from extents (via its move constructor) + static_assert(std::is_constructible_v::template mapping>, + std::extents>); + static_assert(!std::is_constructible_v::template mapping>, + const std::extents&>); + mixin_extents(handle, layout_wrapping_integral<8>(), acc); + // Sanity check that this layouts mapping is not constructible from extents + static_assert(!std::is_constructible_v::template mapping>, + std::extents>); + static_assert(!std::is_constructible_v::template mapping>, + const 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(); + + // test non-constructibility from wrong extents type + constexpr size_t D = std::dynamic_extent; + using mds_t = std::mdspan>; + // sanity check + static_assert(std::is_constructible_v>); + // wrong size + static_assert(!std::is_constructible_v>); + static_assert(!std::is_constructible_v>); + // wrong type in general: note the extents constructor does NOT convert, since it takes by const& + static_assert(!std::is_constructible_v>); + static_assert(!std::is_constructible_v>); + + // test non-constructibility from wrong handle_type + static_assert(!std::is_constructible_v>); + + return true; +} +int main(int, char**) { + 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,165 @@ +//===----------------------------------------------------------------------===// +// +// 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; + + static_assert(mec == std::is_constructible_v); + static_assert(ac == std::is_default_constructible_v); + + if constexpr (mec && ac) { + if !consteval { + move_counted_handle::move_counter() = 0; + } + MDS m(handle, idxs...); + if !consteval { + if constexpr (std::is_same_v>) { + assert((H::move_counter() == 1)); + } + } + + // 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); + + // Use weird layout, make sure it has the properties we want to test + // Sanity check that this layouts mapping is constructible from extents (via its move constructor) + static_assert(std::is_constructible_v::template mapping>, + std::extents>); + static_assert(!std::is_constructible_v::template mapping>, + const std::extents&>); + mixin_extents(handle, layout_wrapping_integral<8>(), acc); + // Sanity check that this layouts mapping is not constructible from extents + static_assert(!std::is_constructible_v::template mapping>, + std::extents>); + static_assert(!std::is_constructible_v::template mapping>, + const 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(); + + // test non-constructibility from wrong integer types + constexpr size_t D = std::dynamic_extent; + using mds_t = std::mdspan>; + // sanity check + static_assert(std::is_constructible_v); + static_assert(std::is_constructible_v); + // wrong number of arguments + static_assert(!std::is_constructible_v); + static_assert(!std::is_constructible_v); + // not convertible to int + static_assert(!std::is_constructible_v>); + + // test non-constructibility from wrong handle_type + static_assert(!std::is_constructible_v); + + return true; +} + +int main(int, char**) { + 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,129 @@ +//===----------------------------------------------------------------------===// +// +// 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; + + static_assert(ac == std::is_default_constructible_v); + if constexpr (ac) { + if !consteval { + move_counted_handle::move_counter() = 0; + } + // use formulation of constructor which tests that it is not explicit + MDS m = {handle, map}; + if !consteval { + if constexpr (std::is_same_v>) { + assert((H::move_counter() == 1)); + } + } + 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); + 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)); +} + +template +using mapping_t = typename std::layout_right::template mapping; + +constexpr bool test() { + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + + constexpr size_t D = std::dynamic_extent; + using mds_t = std::mdspan>; + + // sanity check + static_assert(std::is_constructible_v>>); + + // test non-constructibility from wrong mapping type + // wrong rank + static_assert(!std::is_constructible_v>>); + static_assert(!std::is_constructible_v>>); + // wrong type in general: note the map constructor does NOT convert, since it takes by const& + static_assert(!std::is_constructible_v>>); + static_assert(!std::is_constructible_v>>); + + // test non-constructibility from wrong handle_type + static_assert(!std::is_constructible_v>>); + + return true; +} +int main(int, char**) { + 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,130 @@ +//===----------------------------------------------------------------------===// +// +// 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; + + if !consteval { + move_counted_handle::move_counter() = 0; + } + // use formulation of constructor which tests that it is not explicit + MDS m = {handle, map, acc}; + if !consteval { + if constexpr (std::is_same_v>) { + assert((H::move_counter() == 1)); + } + } + 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); + 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)); +} + +template +using mapping_t = typename std::layout_right::template mapping; + +constexpr bool test() { + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + + // test non-constructibility from wrong args + constexpr size_t D = std::dynamic_extent; + using mds_t = std::mdspan>; + using acc_t = std::default_accessor; + + // sanity check + static_assert(std::is_constructible_v>, acc_t>); + + // test non-constructibility from wrong accessor + static_assert( + !std:: + is_constructible_v>, std::default_accessor>); + + // test non-constructibility from wrong mapping type + // wrong rank + static_assert(!std::is_constructible_v>, acc_t>); + static_assert(!std::is_constructible_v>, acc_t>); + // wrong type in general: note the map constructor does NOT convert, since it takes by const& + static_assert(!std::is_constructible_v>, acc_t>); + static_assert(!std::is_constructible_v>, acc_t>); + + // test non-constructibility from wrong handle_type + static_assert(!std::is_constructible_v>, acc_t>); + + return true; +} +int main(int, char**) { + 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,197 @@ +//===----------------------------------------------------------------------===// +// +// 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, 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 "../ConvertibleToIntegral.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_span(const H& handle, const M& map, const A&, std::span exts) { + using MDS = std::mdspan; + if !consteval { + move_counted_handle::move_counter() = 0; + } + MDS m(handle, exts); + if !consteval { + if constexpr (std::is_same_v>) { + assert((H::move_counter() == 1)); + } + } + + 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; + static_assert(mec == std::is_constructible_v); + static_assert(ac == std::is_default_constructible_v); + if constexpr (mec && ac) { + // test from all extents + auto exts = array_from_extents(map.extents(), std::make_index_sequence()); + test_mdspan_ctor_span(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_span(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 constructible from extents (via its move constructor) + static_assert(std::is_constructible_v::template mapping>, + std::extents>); + static_assert(!std::is_constructible_v::template mapping>, + const std::extents&>); + mixin_extents(handle, layout_wrapping_integral<8>(), acc); + // Sanity check that this layouts mapping is not constructible from extents + static_assert(!std::is_constructible_v::template mapping>, + std::extents>); + static_assert(!std::is_constructible_v::template mapping>, + const 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(); + + // test non-constructibility from wrong span type + constexpr size_t D = std::dynamic_extent; + using mds_t = std::mdspan>; + // sanity check + static_assert(std::is_constructible_v>); + static_assert(std::is_constructible_v>); + // wrong size + static_assert(!std::is_constructible_v>); + static_assert(!std::is_constructible_v>); + // not convertible to index_type + static_assert(std::is_convertible_v); + static_assert(!std::is_convertible_v); + static_assert(!std::is_constructible_v>); + + // index_type is not nothrow constructible + using mds_uchar_t = std::mdspan>; + static_assert(std::is_convertible_v); + static_assert(std::is_convertible_v); + static_assert(!std::is_nothrow_constructible_v); + static_assert(!std::is_constructible_v>); + + // convertible from non-const to index_type but not from const + using mds_int_t = std::mdspan>; + static_assert(std::is_convertible_v); + static_assert(!std::is_convertible_v); + static_assert(std::is_nothrow_constructible_v); + static_assert(!std::is_constructible_v>); + + // can't test a combo where std::is_nothrow_constructible_v is true, + // but std::is_convertible_v is false + + // test non-constructibility from wrong handle_type + static_assert(!std::is_constructible_v>); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} Index: libcxx/test/std/containers/views/mdspan/mdspan/ctor.move.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/ctor.move.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(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(std::move(m_org)); + static_assert(std::is_trivially_move_constructible_v == + (std::is_trivially_move_constructible_v && std::is_trivially_move_constructible_v && + std::is_trivially_move_constructible_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_move_constructible_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_move_constructible_v< + typename layout_wrapping_integral<4>::template mapping>>); + mixin_extents(handle, layout_wrapping_integral<4>(), acc); +} + +template +constexpr void mixin_accessor() { + ElementPool elements; + // make sure we test trivially constructible accessor and data_handle + static_assert(std::is_trivially_move_constructible_v>); + static_assert(std::is_trivially_move_constructible_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(std::is_trivially_move_constructible_v::data_handle_type> == + 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**) { + 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**) { + 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,259 @@ +//===----------------------------------------------------------------------===// +// +// 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); + + std::array args_arr{static_cast(args)...}; + int* ptr3 = &mds[args_arr]; + assert(ptr3 == ptr2); + int* ptr4 = &mds[std::span(args_arr)]; + assert(ptr4 == 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__ >= 17 + 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))); + + constexpr bool t = true; + constexpr bool o = false; + static_assert(!check_operator_constraints( + std::mdspan(data, construct_mapping(Layout(), std::extents(1, 1))), + unsigned(0), + IntConfig(0))); + static_assert(check_operator_constraints( + std::mdspan(data, construct_mapping(Layout(), std::extents(1, 1))), + unsigned(0), + IntConfig(0))); + static_assert(check_operator_constraints( + std::mdspan(data, construct_mapping(Layout(), std::extents(1, 1))), + unsigned(0), + IntConfig(0))); + static_assert(!check_operator_constraints( + std::mdspan(data, construct_mapping(Layout(), std::extents(1, 1))), + unsigned(0), + IntConfig(0))); + static_assert(check_operator_constraints( + std::mdspan(data, construct_mapping(Layout(), std::extents(1, 1))), + unsigned(0), + IntConfig(0))); + + // layout_wrapped wouldn't quite work here the way we wrote the check + // IntConfig has configurable conversion properties: convert from const&, convert from non-const, no-throw-ctor from const&, no-throw-ctor from non-const + if constexpr (std::is_same_v) { + static_assert(!check_operator_constraints( + std::mdspan(data, construct_mapping(Layout(), std::extents(1))), std::array{IntConfig(0)})); + static_assert(!check_operator_constraints( + std::mdspan(data, construct_mapping(Layout(), std::extents(1))), std::array{IntConfig(0)})); + static_assert(!check_operator_constraints( + std::mdspan(data, construct_mapping(Layout(), std::extents(1))), std::array{IntConfig(0)})); + static_assert(!check_operator_constraints( + std::mdspan(data, construct_mapping(Layout(), std::extents(1))), std::array{IntConfig(0)})); + static_assert(check_operator_constraints( + std::mdspan(data, construct_mapping(Layout(), std::extents(1))), std::array{IntConfig(0)})); + static_assert(check_operator_constraints( + std::mdspan(data, construct_mapping(Layout(), std::extents(1))), std::array{IntConfig(0)})); + + { + std::array idx{IntConfig(0)}; + std::span s(idx); + assert(!check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents(1))), s)); + } + { + std::array idx{IntConfig(0)}; + std::span s(idx); + assert(!check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents(1))), s)); + } + { + std::array idx{IntConfig(0)}; + std::span s(idx); + assert(!check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents(1))), s)); + } + { + std::array idx{IntConfig(0)}; + std::span s(idx); + assert(!check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents(1))), s)); + } + { + std::array idx{IntConfig(0)}; + std::span s(idx); + assert(!check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents(1))), s)); + } + { + std::array idx{IntConfig(0)}; + std::span s(idx); + assert(!check_operator_constraints(std::mdspan(data, construct_mapping(Layout(), std::extents(1))), s)); + } + } +#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/move.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/containers/views/mdspan/mdspan/move.pass.cpp @@ -0,0 +1,100 @@ +//===----------------------------------------------------------------------===// +// +// 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& operator=(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_copy(m_org); + MDS m(std::move(m_copy)); + + 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); + + static_assert(std::is_trivially_move_assignable_v == + (std::is_trivially_move_assignable_v && std::is_trivially_move_assignable_v && + std::is_trivially_move_assignable_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) { + // make sure we test a trivially copyable mapping + static_assert(std::is_trivially_move_assignable_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_move_assignable_v::template mapping>>); + mixin_extents(handle, layout_wrapping_integral<4>(), acc); +} + +template +constexpr void mixin_accessor() { + ElementPool elements; + // make sure we test trivially constructible 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**) { + test(); + static_assert(test()); + return 0; +} 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()); + 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()); + 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())); + assert(MDS::is_always_unique() == M::is_always_unique()); + assert(MDS::is_always_exhaustive() == M::is_always_exhaustive()); + 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**) { + 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,180 @@ +//===----------------------------------------------------------------------===// +// +// 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 "CustomTestAccessors.h" +#include "CustomTestLayouts.h" + +// Calculated expected size of an mdspan +// Note this expectes that only default_accessor is empty +template +constexpr size_t expected_size() { + size_t sizeof_dht = sizeof(typename MDS::data_handle_type); + size_t result = sizeof_dht; + if(MDS::rank_dynamic() > 0) { + size_t alignof_idx = alignof(typename MDS::index_type); + size_t sizeof_idx = sizeof(typename MDS::index_type); + // add alignment if necessary + result += sizeof_dht%alignof_idx == 0?0:alignof_idx - (sizeof_dht%alignof_idx); + // add sizeof stored extents + result += MDS::rank_dynamic() * sizeof_idx; + } + using A = typename MDS::accessor_type; + if(!std::is_same_v>) { + size_t alignof_acc = alignof(A); + size_t sizeof_acc = sizeof(A); + // add alignment if necessary + result += result%alignof_acc == 0?0:alignof_acc - (result%alignof_acc); + // add sizeof stored accessor + result += sizeof_acc; + } + // add alignment of the mdspan itself + result += result%alignof(MDS) == 0?0:alignof(MDS) - (result%alignof(MDS)); + return result; +} + +// check triviality +template +constexpr bool trv_df_ctor = std::is_trivially_default_constructible_v; +template +constexpr bool trv_cp_ctor = std::is_trivially_copy_constructible_v; +template +constexpr bool trv_mv_ctor = std::is_trivially_move_constructible_v; +template +constexpr bool trv_dstruct = std::is_trivially_destructible_v; +template +constexpr bool trv_cp_asgn = std::is_trivially_copy_assignable_v; +template +constexpr bool trv_mv_asgn = std::is_trivially_move_assignable_v; + +template +void check_triviality() { + static_assert(trv_df_ctor == default_ctor); + static_assert(trv_cp_ctor == copy_ctor); + static_assert(trv_mv_ctor == move_ctor); + static_assert(trv_dstruct == destr); + static_assert(trv_cp_asgn == copy_assign); + static_assert(trv_mv_asgn == move_assign); +} + +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); + +// This miserably failed with clang-cl - likely because it doesn't honor/enable +// no-unique-address fully by default +#ifndef _WIN32 + // check the size of mdspan + if constexpr (std::is_same_v || std::is_same_v) { + LIBCPP_STATIC_ASSERT(sizeof(MDS) == expected_size()); + } +#endif + + // check default template parameters: + ASSERT_SAME_TYPE(std::mdspan, std::mdspan>); + ASSERT_SAME_TYPE(std::mdspan, std::mdspan>); + + // check triviality + using DH = typename MDS::data_handle_type; + using MP = typename MDS::mapping_type; + + check_triviality && trv_cp_ctor && trv_cp_ctor, + trv_mv_ctor && trv_mv_ctor && trv_mv_ctor, + trv_dstruct && trv_dstruct && trv_dstruct, + trv_cp_asgn && trv_cp_asgn && trv_cp_asgn, + trv_mv_asgn && trv_mv_asgn && trv_mv_asgn>(); +} + +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>(); + mixin_layout>(); +} + +int main(int, char**) { + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + mixin_accessor(); + + // sanity checks for triviality + check_triviality>, false, true, true, true, true, true>(); + check_triviality>, false, true, true, true, true, true>(); + check_triviality, std::layout_right, checked_accessor>, + false, + true, + false, + true, + true, + true>(); + return 0; +}