From patchwork Thu Dec 4 21:28:42 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luc Grosheintz X-Patchwork-Id: 125934 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 818474C31852 for ; Thu, 4 Dec 2025 21:28:18 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 818474C31852 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=DET8VpMO X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-wr1-f50.google.com (mail-wr1-f50.google.com [209.85.221.50]) by sourceware.org (Postfix) with ESMTPS id D37004B1A364 for ; Thu, 4 Dec 2025 21:26:43 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D37004B1A364 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org D37004B1A364 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=209.85.221.50 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1764883604; cv=none; b=EkmeMrGAndHvJbChWws0RLHPlUdg8sjsSJ+L5iujOIQrcby4rDfSaEstW0ObMCMDbghx+RbS3MvDEBUYyTrSWo0YSHlq1wcBRDmKhlREJtSy4VWmE58RV+WYx0nKC9viwyBvT2dwgANTiPgb/OO/aVJh9ETr7N+ioSM/bi2Yhl4= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1764883604; c=relaxed/simple; bh=pF+vCuMegm1ffdKvJmuhYtqUuucVJBfoypluSaSxDBs=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=nIxqaCsvsVE2OCXeGEBbe0jEerehVKAOC2HWLOKFHNbICnnsS0VFJIs8IGRoJhyF9WIhmDRsTQQY5onBOdh9E/xVA0yrmF97tmj2iPn4grUTky/jHmgKuDTMMF6WJCvFoSF+VWa3OWUbjjSUypyqP/Amg4VD0wUwwlRHEuHA39k= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D37004B1A364 Received: by mail-wr1-f50.google.com with SMTP id ffacd0b85a97d-42e2e239ec0so943569f8f.0 for ; Thu, 04 Dec 2025 13:26:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1764883603; x=1765488403; darn=gcc.gnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Bf4GjIDsab3qPQgyVBAA2K4GysByuw4oWvWnSnHab1o=; b=DET8VpMON/OoXEAZfz9l/QyoLwcFuIF+StnlLI3BxqcxEqvY1jw+Wr2byq6iT59E2v CAu0VFQPA3G4zds3VECws9cemwATTsVxksS7XRsX298iw3dtey+q6ljncvYlfqOQEXAL dEKU5shtSjYDBDA/kFILCVcK/pxdbzT7wfRivy9OztX21mC+5lThG2Fi5WlWu2ruG9Py 0rH/LGdF6GFWjxnsaRuMc3h+ZLjfTGs/fGsA6sHrtnh45w7G3GJfggR643ep2SL/QqB4 g//VEpqxpFnk/90tWmLMAYQkiF4Zop+J0ru67VNZk7LFs2QaTwNzS39ga5h88jrs7LUm Vs3w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764883603; x=1765488403; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=Bf4GjIDsab3qPQgyVBAA2K4GysByuw4oWvWnSnHab1o=; b=NqhlfatTAEE4cK+kGSDt0Xd9Va7k0vG5/nLR0o5LHhmbLXAcArfX0xkj1oUbTb2cO/ Dlz/2obas0FJksgejUdTTGWVC3eg7CT4BktANbrn98PYrhdcncuRM7zdY6ePkwOuOHpU aGuxjdu/1QbWSl27S7iDx56AwcXj4MFJ4wUfGzcwSlW+G1yMMdR2OPXy9TVfTri/THHQ S7cKA7lBt4BK1hqebZh/m2P9JnSHHcmFU6+F1b8ogcOz0sCaiYQ2idlPhmy8Z3g96D2k +He/VqNO+3HZZejoE3sUQdwEVq3AuccJYp+Wqs/uVuoLGLT4Vy/Y7ni8IShRNmahbjgM trag== X-Gm-Message-State: AOJu0YyYv1mpkUEeMz2yk2y8RJTuC0kRsi0ZPHw3NgcdlCbNQbC+T1Ik yLWvJo/j6C52apJmV4kIh6HtjI3Dw5lLLeaMy5NOivJagZluHGNOR68uEeoXqA== X-Gm-Gg: ASbGnct3EDiqZUwyrm+E32Lh9iCnE3g7iZyiDyRJOzy4E6C+AeWulc7JVdtshgxE4Rz rX17lW5zEc+QrI4nuvK70xP7Pfm6JPzDF12wP2Igjh9QHaX9ue20X8i0yNVTt0m7+j+VsJ5k3Vn nZxzBlGpWm1GYMFRYXQ8IndVurp1Tz40vB2rtNvtjo+jafF0z+StNsoh9GchPQg1ifZ1sQot+4c sufDVptpvFyqpz1en2gBZaX56/bUt3PMtP7bPQvoTPUlNRTBrqfAocruxa4ZzFsh274Qn0nNgTC j7HESPjffefLyG2+/vbRCGxd0ctfGPDXYO6b09+Z6UJz1cPUs5wMJ3Q9DVRrbFpcZ4SpVWLS1k/ yDrDbnx9t6eXwIJnlCESZEH5boGJU0JIbheEyL5O9Xuvoa4yNJ4hGhigaBeQfAsVjxH4dUiChat lI60isxPzzW+XEvIYneB9OZNyHp+0wYiqNx3GR7P4FTBHnFr+cxjP5gqjls4kU X-Google-Smtp-Source: AGHT+IETsQUV8/tY0WG3Jbui5Ypd5qgqf1D981J2PESZPeFeFD8ZPefyyKYgq0cNFj0+2nVvfjL4Rg== X-Received: by 2002:a05:6000:2483:b0:42b:3131:5436 with SMTP id ffacd0b85a97d-42f7317286fmr8126191f8f.16.1764883602325; Thu, 04 Dec 2025 13:26:42 -0800 (PST) Received: from piglet.localdomain (212-51-146-223.fiber7.init7.net. [212.51.146.223]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-42f7d331e29sm5354432f8f.32.2025.12.04.13.26.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 04 Dec 2025 13:26:41 -0800 (PST) From: Luc Grosheintz To: libstdc++@gcc.gnu.org Cc: gcc-patches@gcc.gnu.org, Luc Grosheintz Subject: [PATCH v4 1/5] libstdc++: Implement submdspan and submdspan_mapping for layout_left. [PR110352] Date: Thu, 4 Dec 2025 22:28:42 +0100 Message-ID: <20251204212846.248887-2-luc.grosheintz@gmail.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20251204212846.248887-1-luc.grosheintz@gmail.com> References: <20251204212846.248887-1-luc.grosheintz@gmail.com> MIME-Version: 1.0 X-Spam-Status: No, score=-11.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_NONE, SPF_PASS, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org Implements `submdspan` and `submdspan_mapping` for layout_left as described in P3663 (Future proofing mdspan). When computing the offset of the submdspan, one must check that the lower bound of the slice range isn't out-of-range. There's a few cases when the lower bound is never out-of-range: - full_extent and exts.extent(k) != 0, - collapsing slice types. If those conditions are known to hold, no checks are generated. Similarly, if all slices are full_extent, there's no need to call mapping(0,...,0) for standardized mappings. The implementation prepares to use the symmetry between layout_left and layout_right and introduces concepts like a "layout side", i.e. left, right or unknown/strided. The tests use an iterator to replace nested for-loops. Which also makes it easier to write the core test logic in a rank-independent manner. PR libstdc++/110352 libstdc++-v3/ChangeLog: * include/std/mdspan (layout_left::mapping::submdspan_mapping): New friend function. (submdspan): New function. * src/c++23/std.cc.in: Add submdspan. * testsuite/23_containers/mdspan/submdspan/submdspan.cc: New test. * testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc: New test. * testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc: New test. Signed-off-by: Luc Grosheintz --- libstdc++-v3/include/std/mdspan | 557 ++++++++++++++++-- libstdc++-v3/src/c++23/std.cc.in | 2 +- .../mdspan/submdspan/submdspan.cc | 369 ++++++++++++ .../mdspan/submdspan/submdspan_mapping.cc | 136 +++++ .../mdspan/submdspan/submdspan_neg.cc | 161 +++++ 5 files changed, 1163 insertions(+), 62 deletions(-) create mode 100644 libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc create mode 100644 libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc create mode 100644 libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan index bfec288aded..54739c0008d 100644 --- a/libstdc++-v3/include/std/mdspan +++ b/libstdc++-v3/include/std/mdspan @@ -357,40 +357,50 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION static_assert(__is_standard_integer<_ExtentType>::value || __detail::__integral_constant_like<_ExtentType>); static_assert(__is_standard_integer<_StrideType>::value || __detail::__integral_constant_like<_StrideType>); using offset_type = _OffsetType; using extent_type = _ExtentType; using stride_type = _StrideType; [[no_unique_address]] offset_type offset{}; [[no_unique_address]] extent_type extent{}; [[no_unique_address]] stride_type stride{}; }; template struct submdspan_mapping_result { [[no_unique_address]] _Mapping mapping = _Mapping(); size_t offset{}; }; + + template + constexpr bool __is_submdspan_mapping_result = false; + + template + constexpr bool __is_submdspan_mapping_result> = true; + + template + concept __submdspan_mapping_result = __is_submdspan_mapping_result<_Mapping>; + #endif // __glibcxx_submdspan template class extents { static_assert(__is_standard_integer<_IndexType>::value, "IndexType must be a signed or unsigned integer type"); static_assert( (__mdspan::__valid_static_extent<_Extents, _IndexType> && ...), "Extents must either be dynamic or representable as IndexType"); using _Storage = __mdspan::_ExtentsStorage< _IndexType, array{_Extents...}>; [[no_unique_address]] _Storage _M_exts; public: using index_type = _IndexType; using size_type = make_unsigned_t; using rank_type = size_t; @@ -572,40 +582,50 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template constexpr typename _Extents::index_type __fwd_prod(const _Extents& __exts, size_t __r) noexcept { constexpr size_t __rank = _Extents::rank(); constexpr auto& __sta_exts = __static_extents<_Extents>(); if constexpr (__rank == 1) return 1; else if constexpr (__rank == 2) return __r == 0 ? 1 : __exts.extent(0); else if constexpr (__all_dynamic(std::span(__sta_exts).first(__rank-1))) return __extents_prod(__exts, 1, 0, __r); else { size_t __sta_prod = __fwd_partial_prods<__sta_exts>[__r]; return __extents_prod(__exts, __sta_prod, 0, __r); } } + template + consteval _IndexType + __fwd_prod(span __values) + { + _IndexType __ret = 1; + for(auto __value : __values) + __ret *= __value; + return __ret; + } + // Preconditions: _r < _Extents::rank() template constexpr typename _Extents::index_type __rev_prod(const _Extents& __exts, size_t __r) noexcept { constexpr size_t __rank = _Extents::rank(); constexpr auto& __sta_exts = __static_extents<_Extents>(); if constexpr (__rank == 1) return 1; else if constexpr (__rank == 2) return __r == 0 ? __exts.extent(1) : 1; else if constexpr (__all_dynamic(std::span(__sta_exts).last(__rank-1))) return __extents_prod(__exts, 1, __r + 1, __rank); else { size_t __sta_prod = __rev_partial_prods<__sta_exts>[__r]; return __extents_prod(__exts, __sta_prod, __r + 1, __rank); } } @@ -849,64 +869,503 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return std::cmp_less_equal(__min, __value) && std::cmp_less_equal(__value, __max); } template constexpr bool __is_constant_wrapper = false; template<_CwFixedValue _Xv, typename _Tp> constexpr bool __is_constant_wrapper> = true; template constexpr auto __extract_extent(const _Extents& __exts) { using _IndexType = typename _Extents::index_type; return extents<_IndexType, _Extents::static_extent(_Index)>{ __exts.extent(_Index)}; } + template + concept __acceptable_slice_type = same_as<_Slice, full_extent_t> + || same_as<_Slice, _IndexType> || __is_constant_wrapper<_Slice> + || __is_strided_slice<_Slice>; + template consteval auto __subrank() { return (static_cast(!convertible_to<_Slices, _IndexType>) + ... + 0); } template consteval auto __inv_map_rank() { constexpr auto __rank = sizeof...(_Slices); constexpr auto __sub_rank = __subrank<_IndexType, _Slices...>(); auto __map = std::array{}; auto __is_int_like = std::array{ convertible_to<_Slices, _IndexType>...}; size_t __i = 0; for (size_t __k = 0; __k < __rank; ++__k) if (!__is_int_like[__k]) __map[__i++] = __k; return __map; } + + template + constexpr auto + __slice_begin(_Slice __slice) + { + if constexpr (same_as<_Slice, full_extent_t>) + return 0; + else if constexpr (__is_strided_slice<_Slice>) + return __slice.offset; + else + return __slice; // collapsing slice + } + + template + constexpr size_t + __suboffset(const _Mapping& __mapping, const _Slices&... __slices) + { + using _IndexType = typename _Mapping::index_type; + auto __any_past_the_end = [&](index_sequence<_Is...>) + { + auto __is_past_the_end = [](const auto& __slice, const auto& __ext) + { + using _Slice = remove_cvref_t; + if constexpr (is_convertible_v<_Slice, _IndexType>) + return false; + else if constexpr (same_as<_Slice, full_extent_t> + && __ext.static_extent(0) != 0 + && __ext.static_extent(0) != dynamic_extent) + return false; + else + return __mdspan::__slice_begin(__slice) == __ext.extent(0); + }; + + const auto& __exts = __mapping.extents(); + return ((__is_past_the_end(__slices...[_Is], + __mdspan::__extract_extent<_Is>(__exts))) || ...); + }; + + if constexpr ((same_as<_Slices, full_extent_t> && ...)) + return __mdspan::__offset(__mapping); + + if (__any_past_the_end(std::make_index_sequence())) + return __mapping.required_span_size(); + return __mapping(__mdspan::__slice_begin(__slices)...); + } + + template + consteval size_t + __static_slice_extent() + { + if constexpr (same_as<_Slice, full_extent_t>) + return _Extent; + else if constexpr (same_as<_Slice, constant_wrapper<_IndexType(0)>>) + return 0; + else if constexpr (__is_constant_wrapper + && __is_constant_wrapper) + return 1 + ((typename _Slice::extent_type{}) - 1) + / (typename _Slice::stride_type{}); + else + return dynamic_extent; + } + + template + constexpr typename _Extents::index_type + __dynamic_slice_extent(const _Extents& __exts, _Slice __slice) + { + if constexpr (__is_strided_slice<_Slice>) + return __slice.extent == 0 ? 0 : 1 + (__slice.extent - 1) / __slice.stride; + else + return __exts.extent(_K); + } + + template + requires (sizeof...(_Slices) == sizeof...(_Extents)) + constexpr auto + __subextents(const extents<_IndexType, _Extents...>& __exts, + _Slices... __slices) + { + constexpr auto __inv_map = __mdspan::__inv_map_rank<_IndexType, _Slices...>(); + auto __impl = [&](std::index_sequence<_Indices...>) + { + using _SubExts = extents<_IndexType, + __mdspan::__static_slice_extent<_IndexType, + _Extents...[__inv_map[_Indices]], + _Slices...[__inv_map[_Indices]]>()...>; + if constexpr (_SubExts::rank_dynamic() == 0) + return _SubExts{}; + else + { + using _StaticSubExtents = __mdspan::_StaticExtents< + __mdspan::__static_extents<_SubExts>()>; + auto __create = [&](std::index_sequence<_Is...>) + { + constexpr auto __slice_idx = [__inv_map](size_t __i) consteval + { + return __inv_map[_StaticSubExtents::_S_dynamic_index_inv(__i)]; + }; + + return _SubExts{__mdspan::__dynamic_slice_extent<__slice_idx(_Is)>( + __exts, __slices...[__slice_idx(_Is)])...}; + }; + constexpr auto __dyn_subrank = _SubExts::rank_dynamic(); + return __create(std::make_index_sequence<__dyn_subrank>()); + } + }; + + return __impl(std::make_index_sequence<__inv_map.size()>()); + } + + enum class _LayoutSide + { + __left, + __right, + __unknown + }; + + template + consteval _LayoutSide + __mapping_side() + { + if constexpr (__is_left_padded_mapping<_Mapping> + || __mapping_of) + return _LayoutSide::__left; + if constexpr (__is_right_padded_mapping<_Mapping> + || __mapping_of) + return _LayoutSide::__right; + else + return _LayoutSide::__unknown; + } + + template<_LayoutSide _Side, size_t _Rank> + struct _StridesTrait + { + static constexpr const _LayoutSide _S_side = _Side; + + static constexpr size_t + _S_idx(size_t __k) noexcept + { + if constexpr (_Side == _LayoutSide::__left) + return __k; + else + return _Rank - 1 - __k; + } + + // Unifies the formulas for computing strides for padded and unpadded + // layouts. + template + static constexpr typename _Mapping::index_type + _S_padded_extent(const _Mapping& __mapping, size_t __k) + { + if (__k == 0) + return __mapping.stride(_S_idx(1)); + else + return __mapping.extents().extent(_S_idx(__k)); + } + + template + static consteval auto + _S_inv_map() + { + static_assert(_Side != _LayoutSide::__unknown); + auto __impl = [&](std::index_sequence<_Is...>) + { + return __mdspan::__inv_map_rank<_IndexType, _Slices...[_S_idx(_Is)]...>(); + }; + return __impl(std::make_index_sequence<_Rank>()); + } + }; + + template + constexpr auto + __substrides_generic(const _Mapping& __mapping, const _Slices&... __slices) + { + using _IndexType = typename _Mapping::index_type; + if constexpr (_SubExts::rank() == 0) + return array<_IndexType, _SubExts::rank()>{}; + else + { + auto __stride = [&__mapping](size_t __k, auto __slice) -> _IndexType + { + if constexpr (__is_strided_slice) + if (__slice.stride < __slice.extent) + return __mapping.stride(__k) * __slice.stride; + return __mapping.stride(__k); + }; + + auto __impl = [&](std::index_sequence<_Is...>) + { + constexpr auto __inv_map + = __mdspan::__inv_map_rank<_IndexType, _Slices...>(); + return array<_IndexType, _SubExts::rank()>{ + __stride(__inv_map[_Is], __slices...[__inv_map[_Is]])...}; + }; + return __impl(std::make_index_sequence<_SubExts::rank()>()); + } + }; + + template + constexpr auto + __substrides_standardized(const _Mapping& __mapping, + const _Slices&... __slices) + { + using _IndexType = typename _Mapping::index_type; + using _Trait = _StridesTrait<__mapping_side<_Mapping>(), + _Mapping::extents_type::rank()>; + using _SubTrait = _StridesTrait<__mapping_side<_Mapping>(), _SubExts::rank()>; + + constexpr size_t __sub_rank = _SubExts::rank(); + + std::array<_IndexType, __sub_rank> __ret; + if constexpr (__sub_rank > 0) + { + constexpr auto __inv_map + = _Trait::template _S_inv_map<_IndexType, _Slices...>(); + auto __loop = [&](std::index_sequence<_Ks...>) + { + size_t __i0 = 0; + size_t __stride = 1; + auto __body = [&](size_t __k, auto __slice) + { + for (size_t __i = __i0; __i < __inv_map[__k]; ++__i) + __stride *= _Trait::_S_padded_extent(__mapping, __i); + + size_t __krev = _SubTrait::_S_idx(__k); + if constexpr (__is_strided_slice) + __ret[__krev] = __stride * __slice.stride; + else + __ret[__krev] = __stride; + + __i0 = __inv_map[__k]; + }; + + ((__body(_Ks, __slices...[_Trait::_S_idx(__inv_map[_Ks])])),...); + }; + __loop(std::make_index_sequence<__sub_rank>()); + } + return __ret; + } + + + template + constexpr auto + __substrides(const _Mapping& __mapping, const _Slices&... __slices) + { + if constexpr (__mdspan::__mapping_side<_Mapping>() == _LayoutSide::__unknown) + return __mdspan::__substrides_generic<_SubExts>(__mapping, __slices...); + else + return __mdspan::__substrides_standardized<_SubExts>(__mapping, __slices...); + } + + template + concept __is_unit_stride_slice = (__mdspan::__is_strided_slice<_Slice> + && __mdspan::__is_constant_wrapper + && _Slice::stride_type::value == 1) + || std::same_as<_Slice, full_extent_t>; + + // These are (forced) exclusive categories: + // - full & collapsing: obvious, + // - unit_strided_slice: strided_slice{a, b, cw<1>}, but not `full`, + // - strided_slice: strided_slice{a, b, c} with c != cw<1>. + enum class _SliceKind + { + __strided_slice, + __unit_strided_slice, + __full, + __collapsing + }; + + template + consteval _SliceKind + __make_slice_kind() + { + if constexpr (std::same_as<_Slice, full_extent_t>) + return _SliceKind::__full; + else if constexpr (__mdspan::__is_strided_slice<_Slice>) + { + if constexpr (__mdspan::__is_unit_stride_slice<_Slice>) + return _SliceKind::__unit_strided_slice; + else + return _SliceKind::__strided_slice; + } + else + return _SliceKind::__collapsing; + } + + template + consteval array<_SliceKind, sizeof...(_Slices)> + __make_slice_kind_array() + { + return array<_SliceKind, sizeof...(_Slices)>{ + __mdspan::__make_slice_kind<_Slices>()...}; + } + + // _BlockSize - 1 + // [full, ..., full, unit_slice , *] + template + consteval bool + __is_block(span __slice_kinds) + { + if (_BlockSize == 0) + return false; + + if (_BlockSize > __slice_kinds.size()) + return false; + + for (size_t __i = 0; __i < _BlockSize - 1; ++__i) + if (__slice_kinds[__i] != _SliceKind::__full) + return false; + + auto __last = __slice_kinds[_BlockSize - 1]; + return __last == _SliceKind::__full + || __last == _SliceKind::__unit_strided_slice; + } + + // __u __u + _SubRank-2 + // [unit_slice, i, ..., k, full, ..., full, unit_slice, *] + template + static consteval size_t + __padded_block_begin_generic(span __slice_kinds) + { + if (__slice_kinds[0] != _SliceKind::__full + && __slice_kinds[0] != _SliceKind::__unit_strided_slice) + return dynamic_extent; + else if (__slice_kinds.size() == 1) + return dynamic_extent; + else + { + static_assert(_SubRank != dynamic_extent, + "The implementation can't handle submdspans with rank == size_t(-1)"); + + size_t __u = 1; + while(__u < __slice_kinds.size() + && __slice_kinds[__u] == _SliceKind::__collapsing) + ++__u; + + if (__mdspan::__is_block<_SubRank-1>(__slice_kinds.subspan(__u))) + return __u; + return dynamic_extent; + } + } + + template<_LayoutSide _Side, size_t _SubRank, size_t _Nm> + static consteval size_t + __padded_block_begin(span __slice_kinds) + { + if constexpr (_Side == _LayoutSide::__left) + return __mdspan::__padded_block_begin_generic<_SubRank>(__slice_kinds); + else + { + std::array<_SliceKind, _Nm> __rev_slice_kinds; + for(size_t __i = 0; __i < _Nm; ++__i) + __rev_slice_kinds[__i] = __slice_kinds[_Nm - 1 - __i]; + + auto __u = __mdspan::__padded_block_begin_generic<_SubRank>( + std::span(__rev_slice_kinds)); + return __u == dynamic_extent ? dynamic_extent : _Nm - 1 - __u; + } + } + + template<_LayoutSide _Side> + struct _SubMdspanMapping; + + template<> + struct _SubMdspanMapping<_LayoutSide::__left> + { + using _Layout = layout_left; + template using _PaddedLayout = layout_left_padded<_Pad>; + + template + static consteval size_t + _S_pad() + { + using _Extents = typename _Mapping::extents_type; + constexpr auto __sta_exts = __mdspan::__static_extents<_Extents>(0, _Us); + if constexpr (!__mdspan::__all_static(__sta_exts)) + return dynamic_extent; + else + return __mdspan::__fwd_prod(__sta_exts); + } + + template + static consteval bool + _S_is_unpadded_submdspan(span __slice_kinds) + { return __mdspan::__is_block<_SubRank>(__slice_kinds); } + }; + + template + constexpr auto + __submdspan_mapping_impl(const _Mapping& __mapping) + { return submdspan_mapping_result{__mapping, 0}; } + + template + requires (sizeof...(_Slices) > 0) + constexpr auto + __submdspan_mapping_impl(const _Mapping& __mapping, _Slices... __slices) + { + constexpr auto __side = __mdspan::__mapping_side<_Mapping>(); + using _Trait = _SubMdspanMapping<__side>; + + constexpr auto __slice_kinds = __mdspan::__make_slice_kind_array<_Slices...>(); + auto __offset = __mdspan::__suboffset(__mapping, __slices...); + auto __sub_exts = __mdspan::__subextents(__mapping.extents(), __slices...); + using _SubExts = decltype(__sub_exts); + constexpr auto __sub_rank = _SubExts::rank(); + if constexpr (__sub_rank == 0) + return submdspan_mapping_result{ + typename _Trait::_Layout::mapping(__sub_exts), __offset}; + else if constexpr ( + _Trait::template _S_is_unpadded_submdspan<__sub_rank>( + std::span(__slice_kinds))) + return submdspan_mapping_result{ + typename _Trait::_Layout::mapping(__sub_exts), __offset}; + else if constexpr ( + constexpr auto __u = __padded_block_begin<__side, __sub_rank>( + span(__slice_kinds)); + __u != dynamic_extent) + { + constexpr auto __pad = _Trait::template _S_pad<_Mapping, __u>(); + using _Layout = typename _Trait::template _PaddedLayout<__pad>; + return submdspan_mapping_result{ + typename _Layout::mapping(__sub_exts, __mapping.stride(__u)), + __offset}; + } + else + { + auto __sub_strides + = __mdspan::__substrides<_SubExts>(__mapping, __slices...); + return submdspan_mapping_result{ + layout_stride::mapping(__sub_exts, __sub_strides), __offset}; + } + } + + // Enables ADL-only calls from submdspan. + void submdspan_mapping() = delete; #endif // __glibcxx_submdspan } template class layout_left::mapping { public: using extents_type = _Extents; using index_type = typename extents_type::index_type; using size_type = typename extents_type::size_type; using rank_type = typename extents_type::rank_type; using layout_type = layout_left; static_assert(__mdspan::__representable_size, "The size of extents_type must be representable as index_type"); constexpr mapping() noexcept = default; constexpr @@ -1014,40 +1473,48 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template requires (extents_type::rank() == _OExtents::rank()) friend constexpr bool operator==(const mapping& __self, const mapping<_OExtents>& __other) noexcept { return __self.extents() == __other.extents(); } private: template constexpr explicit mapping(const _OExtents& __oexts, __mdspan::__internal_ctor) noexcept : _M_extents(__oexts) { static_assert(__mdspan::__representable_size<_OExtents, index_type>, "The size of OtherExtents must be representable as index_type"); __glibcxx_assert(__mdspan::__is_representable_extents(_M_extents)); } +#if __glibcxx_submdspan + template<__mdspan::__acceptable_slice_type... _Slices> + requires (extents_type::rank() == sizeof...(_Slices)) + friend constexpr auto + submdspan_mapping(const mapping& __mapping, _Slices... __slices) + { return __mdspan::__submdspan_mapping_impl(__mapping, __slices...); } +#endif // __glibcxx_submdspan + [[no_unique_address]] extents_type _M_extents{}; }; namespace __mdspan { template constexpr typename _Extents::index_type __linear_index_right(const _Extents& __exts, _Indices... __indices) noexcept { using _IndexType = typename _Extents::index_type; array<_IndexType, sizeof...(__indices)> __ind_arr{__indices...}; _IndexType __res = 0; if constexpr (sizeof...(__indices) > 0) { _IndexType __mult = 1; auto __update = [&, __pos = __exts.rank()](_IndexType) mutable { --__pos; _GLIBCXX_DEBUG_ASSERT(cmp_less(__ind_arr[__pos], @@ -2673,118 +3140,86 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION else if constexpr (__is_constant_wrapper<_Slice> && _Extent != dynamic_extent) static_assert(std::cmp_less(_Slice::value, _Extent)); else if constexpr (convertible_to<_Slice, _IndexType>) __glibcxx_assert(__slice < __ext.extent(0)); } template constexpr void __check_valid_slices(const _Extents& __exts, const _Slices&... __slices) { constexpr auto __rank = _Extents::rank(); auto __impl = [&](index_sequence<_Is...>) { ((__mdspan::__check_valid_slice(__extract_extent<_Is>(__exts), __slices...[_Is])),...); }; __impl(make_index_sequence<__rank>()); } - template - consteval size_t - __static_slice_extent() + template + concept __sliceable_mapping = requires(_Mapping __m) { - if constexpr (same_as<_Slice, full_extent_t>) - return _Extent; - else if constexpr (same_as<_Slice, constant_wrapper<_IndexType(0)>>) - return 0; - else if constexpr (__is_constant_wrapper - && __is_constant_wrapper) - return 1 + ((typename _Slice::extent_type{}) - 1) - / (typename _Slice::stride_type{}); - else - return dynamic_extent; - } - - template - constexpr typename _Extents::index_type - __dynamic_slice_extent(const _Extents& __exts, _Slice __slice) - { - if constexpr (__is_strided_slice<_Slice>) - return __slice.extent == 0 ? 0 : 1 + (__slice.extent - 1) / __slice.stride; - else - return __exts.extent(_K); - } - - template - requires (sizeof...(_Slices) == sizeof...(_Extents)) - constexpr auto - __subextents(const extents<_IndexType, _Extents...>& __exts, - _Slices... __slices) - { - constexpr auto __inv_map = __mdspan::__inv_map_rank<_IndexType, _Slices...>(); - auto __impl = [&](index_sequence<_Indices...>) { - using _SubExtents = extents<_IndexType, - (__mdspan::__static_slice_extent<_IndexType, - _Extents...[__inv_map[_Indices]], - _Slices...[__inv_map[_Indices]]>())...>; - if constexpr (_SubExtents::rank_dynamic() == 0) - return _SubExtents{}; - else - { - using _StaticSubExtents = __mdspan::_StaticExtents< - __mdspan::__static_extents<_SubExtents>()>; - auto __create = [&](index_sequence<_Is...>) - { - constexpr auto __slice_idx = [__inv_map](size_t __i) consteval - { - return __inv_map[_StaticSubExtents::_S_dynamic_index_inv(__i)]; - }; - - return _SubExtents{ - (__mdspan::__dynamic_slice_extent<__slice_idx(_Is)>( - __exts, __slices...[__slice_idx(_Is)]))...}; - }; - constexpr auto __dyn_subrank = _SubExtents::rank_dynamic(); - return __create(make_index_sequence<__dyn_subrank>()); - } - }; - - return __impl(make_index_sequence<__inv_map.size()>()); - } + [__m](index_sequence<_Is...>) + { + return submdspan_mapping(__m, ((void) _Is, full_extent)...); + }(make_index_sequence<_Mapping::extents_type::rank()>()) + } -> __submdspan_mapping_result; + }; } template requires (sizeof...(_RawSlices) == sizeof...(_Extents)) constexpr auto submdspan_extents(const extents<_IndexType, _Extents...>& __exts, _RawSlices... __raw_slices) { auto __impl = [&__exts](auto... __slices) { __mdspan::__check_valid_slices(__exts, __slices...); return __mdspan::__subextents(__exts, __slices...); }; return __impl(__mdspan::__slice_cast<_IndexType>(__raw_slices)...); } template requires (sizeof...(_Extents) == sizeof...(_RawSlices)) constexpr auto submdspan_canonicalize_slices(const extents<_IndexType, _Extents...>& __exts, _RawSlices... __raw_slices) { auto __impl = [&__exts](auto... __slices) { __mdspan::__check_valid_slices(__exts, __slices...); return std::make_tuple(__slices...); }; return __impl(__mdspan::__slice_cast<_IndexType>(__raw_slices)...); } + + template + requires (sizeof...(_RawSlices) == _Extents::rank() + && __mdspan::__sliceable_mapping>) + constexpr auto + submdspan( + const mdspan<_ElementType, _Extents, _Layout, _Accessor>& __md, + _RawSlices... __raw_slices) + { + using _IndexType = typename _Extents::index_type; + auto __impl = [&__md](auto... __slices) + { + using __mdspan::submdspan_mapping; + __mdspan::__check_valid_slices(__md.extents(), __slices...); + auto __result = submdspan_mapping(__md.mapping(), __slices...); + return std::mdspan(__md.accessor().offset(__md.data_handle(), __result.offset), + __result.mapping, typename _Accessor::offset_policy(__md.accessor())); + }; + return __impl(__mdspan::__slice_cast<_IndexType>(__raw_slices)...); + } #endif // __glibcxx_submdspan _GLIBCXX_END_NAMESPACE_VERSION } #endif #endif diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/std.cc.in index c2a9293b05a..2dac6a6d887 100644 --- a/libstdc++-v3/src/c++23/std.cc.in +++ b/libstdc++-v3/src/c++23/std.cc.in @@ -1868,42 +1868,42 @@ export namespace std #endif using std::layout_left; using std::layout_right; using std::layout_stride; using std::default_accessor; #if __glibcxx_aligned_accessor using std::aligned_accessor; #endif using std::mdspan; #if __glibcxx_padded_layouts using std::layout_left_padded; using std::layout_right_padded; #endif #if __glibcxx_submdspan using std::strided_slice; using std::full_extent_t; using std::full_extent; using std::submdspan_mapping_result; using std::submdspan_canonicalize_slices; using std::submdspan_extents; + using std::submdspan; #endif - // FIXME mdsubspan } #endif // 20.2 export namespace std { using std::align; using std::allocator; using std::allocator_arg; using std::allocator_arg_t; using std::allocator_traits; using std::assume_aligned; #if __glibcxx_is_sufficiently_aligned using std::is_sufficiently_aligned; #endif using std::make_obj_using_allocator; using std::pointer_traits; using std::to_address; using std::uninitialized_construct_using_allocator; using std::uses_allocator; diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc new file mode 100644 index 00000000000..53e91407a9c --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc @@ -0,0 +1,369 @@ +// { dg-do run { target c++26 } } +#include + +#include // TODO remove +#include +#include +#include "../layout_traits.h" +#include + +constexpr size_t dyn = std::dynamic_extent; +constexpr auto all = std::full_extent; + +template + constexpr bool is_strided_slice = false; + +template + constexpr bool is_strided_slice> = true; + +template + constexpr void + fill(const MDSpan& md) + { + using IndexType = typename MDSpan::index_type; + auto exts = md.extents(); + if constexpr (exts.rank() == 3) + for(IndexType i = 0; i < exts.extent(0); ++i) + for(IndexType j = 0; j < exts.extent(1); ++j) + for(IndexType k = 0; k < exts.extent(2); ++k) + md[i, j, k] = 100 * i + 10 * j + k; + } + +template + class multi_index_generator + { + class EndIt + { }; + + class BeginIt + { + public: + constexpr + BeginIt(const std::array& shape) + : M_shape(shape) + { } + + constexpr BeginIt& + operator++() + { + if constexpr (Rank > 0) + { + ++M_indices[Rank-1]; + for(size_t i = Rank; i > 1; --i) + if (M_indices[i-1] == M_shape[i-1]) + { + M_indices[i-1] = 0; + ++M_indices[i-2]; + } + } + return *this; + } + + constexpr auto + operator*() + { return M_indices; } + + constexpr bool + operator==(EndIt) + { + if constexpr (Rank > 0) + return M_indices[0] == M_shape[0]; + else + return true; + } + + private: + std::array M_indices{}; + std::array M_shape; + }; + + public: + constexpr + multi_index_generator(std::array shape) + : M_shape(shape) + { } + + constexpr BeginIt + begin() const + { return BeginIt(M_shape); } + + constexpr EndIt + end() const + { return EndIt{}; } + + private: + std::array M_shape; + }; + +constexpr bool +test_multi_index() +{ + auto shape = std::array{3, 5, 7, 1}; + + std::vector> expected; + for (int i = 0; i < shape[0]; ++i) + for (int j = 0; j < shape[1]; ++j) + for (int k = 0; k + consteval auto + inv_collapsed_index_map() + { + constexpr size_t rank = sizeof...(Slices); + auto is_collapsing = std::array{std::same_as...}; + constexpr auto collapsed_rank = ((!std::same_as) + ... + 0); + + std::array ret; + if constexpr (collapsed_rank > 0) + for(size_t k = 0, i = 0; i < rank; ++i) + if (!is_collapsing[i]) + ret[k++] = i; + return ret; + } + +static_assert(inv_collapsed_index_map() + == std::array{}); + +static_assert(inv_collapsed_index_map() + == std::array{1}); + +template + constexpr std::vector + make_selection(IndexType extent, const Slice& slice) + { + if constexpr (std::convertible_to) + return {static_cast(slice)}; + else if constexpr (std::same_as) + { + auto ret = std::vector(static_cast(extent)); + std::ranges::iota(ret, 0); + return ret; + } + else if constexpr (is_strided_slice) + { + auto ret = std::vector{}; + size_t n = static_cast(slice.extent); + for(size_t i = 0; i < n; i += slice.stride) + ret.push_back(slice.offset + i); + return ret; + } + else + { + auto [begin, end] = slice; + auto ret = std::vector(static_cast(end - begin)); + std::ranges::iota(ret, begin); + return ret; + } + } + +template + constexpr bool + check_selection(std::index_sequence, auto md, Slices... slices) + { + auto exts = md.extents(); + auto outer_shape = std::array{exts.extent(0), exts.extent(1), exts.extent(2)}; + + constexpr auto full_index = inv_collapsed_index_map(); + auto make_slice = [](size_t i, auto slice) + { + if constexpr (std::same_as) + return i; + else + return slice; + }; + + auto loop_body = [&](std::index_sequence, auto ijk, + auto... slices) + { + auto submd = submdspan(md, slices...[I]...); + auto selection = std::tuple{make_selection(exts.extent(I), slices...[I])...}; + auto inner_shape = std::array{ + std::get(selection).size()... + }; + + for (auto ij : multi_index_generator(inner_shape)) + { + ((ijk[full_index[J]] = get(selection)[ij[J]]),...); + VERIFY(submd[ij] == md[ijk]); + } + }; + + for (auto ijk : multi_index_generator(outer_shape)) + loop_body(std::make_index_sequence(), ijk, + make_slice(ijk[I], slices...[I])...); + return true; + } + +template + constexpr bool + check_selection(std::mdspan md, Slices... slices) + { + auto indices = std::make_index_sequence(); + return check_selection(indices, md, slices...); + } + +template + constexpr bool + check_selection(std::extentsexts, Slices... slices) + { + auto run = [&](auto m) + { + auto storage = std::vector(m.required_span_size()); + auto md = std::mdspan(storage.data(), m); + fill(md); + return check_selection(md, slices...); + }; + + if constexpr (std::same_as) + { + auto m = typename Layout::mapping(exts, std::array{15, 2, 50}); + return run(m); + } + else + { + auto m = typename Layout::mapping(exts); + return run(m); + } + } + +template + constexpr bool + test_scalar_selection(auto exts) + { + check_selection(exts, collapse{}, collapse{}, collapse{}); + return true; + } + +template + constexpr bool + test_full_lines(auto exts) + { + check_selection(exts, all, collapse{}, collapse{}); + check_selection(exts, collapse{}, all, collapse{}); + check_selection(exts, collapse{}, collapse{}, all); + return true; + } + +template + constexpr bool + test_full_blocks(auto exts) + { + check_selection(exts, all, all, collapse{}); + check_selection(exts, all, collapse{}, all); + check_selection(exts, collapse{}, all, all); + return true; + } + +template + constexpr bool + test_cubes(auto exts) + { + auto s0 = std::pair{0, 2}; + auto s1 = std::pair{1, 4}; + auto s2 = std::pair{3, 7}; + + check_selection(exts, all, all, all); + check_selection(exts, all, all, s2); + check_selection(exts, s0, all, all); + check_selection(exts, s0, all, s2); + check_selection(exts, s0, s1, s2); + return true; + } + +template + constexpr bool + test_strided_line_selection(auto exts) + { + auto check = [&](auto s) + { + check_selection(exts, collapse{}, s, collapse{}); + }; + + check(std::strided_slice(0, 2, 2)); + check(std::strided_slice(0, 3, 2)); + check(std::strided_slice(1, 3, 2)); + check(std::strided_slice(1, std::cw<3>, std::cw<2>)); + return true; + } + +template + constexpr bool + test_strided_box_selection(auto exts) + { + auto s0 = std::strided_slice(0, 3, 2); + auto s1 = std::strided_slice(1, 4, 2); + auto s2 = std::strided_slice(0, 7, 3); + + check_selection(exts, s0, s1, s2); + return true; + } + +template + constexpr bool + test_all_cheap() + { + constexpr auto dyn_exts = std::extents(3, 5, 7); + constexpr auto sta_exts = std::extents{}; + + test_scalar_selection(dyn_exts); + test_scalar_selection(sta_exts); + static_assert(test_scalar_selection(dyn_exts)); + static_assert(test_scalar_selection(sta_exts)); + + test_full_lines(dyn_exts); + test_full_lines(sta_exts); + static_assert(test_full_lines(dyn_exts)); + static_assert(test_full_lines(sta_exts)); + + test_strided_box_selection(dyn_exts); + test_strided_box_selection(sta_exts); + static_assert(test_strided_box_selection(dyn_exts)); + static_assert(test_strided_box_selection(sta_exts)); + return true; + } + +template + constexpr bool + test_all_expensive() + { + auto run = [](auto exts) + { + test_full_blocks(exts); + test_cubes(exts); + }; + + run(std::extents(3, 5, 7)); + run(std::extents{}); + return true; + } + +template + constexpr bool + test_all() + { + test_all_cheap(); + test_all_expensive(); + return true; + } + +int +main() +{ + test_all(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc new file mode 100644 index 00000000000..a37d3cd588f --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc @@ -0,0 +1,136 @@ +// { dg-do run { target c++26 } } +#include + +#include // TODO remove +#include "../layout_traits.h" +#include + +constexpr size_t dyn = std::dynamic_extent; + +template + constexpr auto + call_submdspan_mapping(const Mapping& m, std::tuple slices) + { + auto impl = [&](std::index_sequence) + { return submdspan_mapping(m, get(slices)...); }; + return impl(std::make_index_sequence()); + } + +template + constexpr bool + test_layout_unpadded_return_types() + { + constexpr auto padding_side = DeducePaddingSide::from_typename(); + using Traits = LayoutTraits; + + { + auto m0 = typename Layout::mapping(std::extents()); + auto result = submdspan_mapping(m0); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(std::same_as); + } + + auto exts = Traits::make_extents(std::dims<5, int>(3, 5, 7, 11, 13)); + auto m = typename Layout::mapping(exts); + auto all = std::full_extent; + auto s251 = std::strided_slice{2, 5, std::cw<1>}; + + { + auto slices = std::tuple{0, 0, 0, 0, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(std::same_as); + } + + { + auto slices = std::tuple{all, all, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(std::same_as); + } + + { + auto s0 = std::strided_slice{1, 1, std::cw<1>}; + auto slices = std::tuple{s0, all, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(is_same_padded); + } + + { + auto s0 = std::strided_slice{1, 2, std::cw<1>}; + auto slices = std::tuple{s0, all, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(is_same_padded); + } + + { + auto s0 = std::strided_slice{1, 2, std::cw<1>}; + auto slices = std::tuple{s0, 0, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(is_same_padded); + } + + { + auto s0 = std::strided_slice{1, 2, 1}; + auto slices = std::tuple{s0, all, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = decltype(result.mapping)::layout_type; + static_assert(std::same_as); + } + + { + auto slices = std::tuple{1, all, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = decltype(result.mapping)::layout_type; + static_assert(std::same_as); + } + + { + auto s3 = std::strided_slice{2, std::cw<7>, std::cw<2>}; + auto slices = std::tuple{all, all, all, s3, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = decltype(result.mapping)::layout_type; + static_assert(std::same_as); + } + return true; + } + +template + constexpr bool + test_layout_unpadded_padding_value() + { + using Traits = LayoutTraits()>; + auto s0 = std::strided_slice{size_t(1), size_t(2), std::cw}; + auto s3 = std::strided_slice{size_t(2), size_t(5), std::cw}; + auto all = std::full_extent; + + auto check = [&](auto exts, size_t expected) + { + auto m = typename Layout::mapping(Traits::make_extents(exts)); + auto slices = std::tuple{s0, size_t(0), all, s3, size_t(0)}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + auto padding_value = decltype(result.mapping)::padding_value; + VERIFY(padding_value == expected); + }; + + check(std::extents(std::cw<3>, std::cw<5>, std::cw<7>, 11, 13), 3*5); + check(std::extents(std::cw<3>, std::cw<5>, 7, 11, 13), 3*5); + check(std::extents(std::cw<3>, 5, 7, 11, 13), dyn); + check(std::extents(3, 5, 7, 11, 13), dyn); + return true; + } + +int +main() +{ + test_layout_unpadded_return_types(); + static_assert(test_layout_unpadded_return_types()); + + test_layout_unpadded_padding_value(); + static_assert(test_layout_unpadded_padding_value()); + return 0; +} + diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc new file mode 100644 index 00000000000..cdc8a2b7e23 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc @@ -0,0 +1,161 @@ +// { dg-do compile { target c++26 } } +#include + +#include + +template + constexpr bool + check_slice_range(Slices... slices) + { + auto m = typename Layout::mapping>{}; + auto storage = std::vector(m.required_span_size()); + auto md = std::mdspan(storage.data(), m); + + auto submd = submdspan(md, slices...); // { dg-error "expansion of" } + (void) submd; + return true; + } + +template + constexpr bool + test_int_under() + { + check_slice_range(1, -1, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_int_under()); // { dg-error "expansion of" } + +template + constexpr bool + test_int_over() + { + check_slice_range(1, 5, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_int_over()); // { dg-error "expansion of" } + +template + constexpr bool + test_tuple_under() + { + check_slice_range(1, std::tuple{-1, 2}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_tuple_under()); // { dg-error "expansion of" } + +template + constexpr bool + test_tuple_reversed() + { + check_slice_range(1, std::tuple{3, 2}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_tuple_reversed()); // { dg-error "expansion of" } + +template + constexpr bool + test_tuple_over() + { + check_slice_range(1, std::tuple{0, 6}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_tuple_over()); // { dg-error "expansion of" } + +template + constexpr bool + test_strided_slice_zero() + { + check_slice_range(1, std::strided_slice{1, 1, 0}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_strided_slice_zero()); // { dg-error "expansion of" } + +template + constexpr bool + test_strided_slice_offset_under() + { + check_slice_range(1, std::strided_slice{-1, 1, 1}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_strided_slice_offset_under()); // { dg-error "expansion of" } + +template + constexpr bool + test_strided_slice_offset_over() + { + check_slice_range(1, std::strided_slice{6, 0, 1}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_strided_slice_offset_over()); // { dg-error "expansion of" } + +template + constexpr bool + test_strided_slice_extent_over() + { + check_slice_range(1, std::strided_slice{1, 5, 1}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_strided_slice_extent_over()); // { dg-error "expansion of" } + +namespace adl +{ + struct NoFull + { + template + class mapping + { + public: + using extents_type = Extents; + using index_type = typename extents_type::index_type; + + private: + friend constexpr auto + submdspan_mapping(mapping, int) + { return std::submdspan_mapping_result{mapping{}, 0}; } + }; + }; + + struct WrongReturnValue + { + template + class mapping + { + public: + using extents_type = Extents; + using index_type = typename extents_type::index_type; + + private: + friend constexpr int + submdspan_mapping(mapping, std::full_extent_t) + { return 42; } + }; + }; +} + +constexpr bool +test_invalid_mapping1() +{ + using Extents = std::extents; + auto map = adl::NoFull::mapping{}; + std::mdspan m(nullptr, map); + std::submdspan(m, 1); // { dg-error "no matching function" } + return true; +} +static_assert(test_invalid_mapping1()); + +constexpr bool +test_invalid_mapping2() +{ + using Extents = std::extents; + auto map = adl::WrongReturnValue::mapping{}; + std::mdspan m(nullptr, map); + std::submdspan(m, std::full_extent); // { dg-error "no matching function" } + return true; +} +static_assert(test_invalid_mapping2()); + +// { dg-prune-output "static assertion failed" } +// { dg-prune-output "__glibcxx_assert_fail" } +// { dg-prune-output "non-constant condition" } +// { dg-prune-output "no matching function" } +// { dg-prune-output "does not satisfy placeholder constraints" }