From 4f957124f0ed82da51a12e5ae570fe196f1bcba2 Mon Sep 17 00:00:00 2001 From: Eisenwave Date: Sat, 15 Nov 2025 16:32:41 +0100 Subject: [PATCH] P3663R3 Future-proof submdspan_mapping Fixes NB PL-009, US 66-117 (C++26 CD). --- source/containers.tex | 750 +++++++++++++++++++++++++++--------------- source/support.tex | 2 +- 2 files changed, 479 insertions(+), 273 deletions(-) diff --git a/source/containers.tex b/source/containers.tex index 148d3de34e..1cba12bc53 100644 --- a/source/containers.tex +++ b/source/containers.tex @@ -21065,6 +21065,11 @@ struct full_extent_t { explicit full_extent_t() = default; }; inline constexpr full_extent_t full_extent{}; + %FIXME: this is in adifferent subclause but the paper has no title for it + template + constexpr auto submdspan_canonicalize_slices(const extents& src, + Slices... slices); + template constexpr auto submdspan_extents(const extents&, SliceSpecifiers...); @@ -25257,38 +25262,163 @@ The subset viewed by the created \tcode{mdspan} is determined by the \tcode{SliceSpecifier} arguments. +%FIXME: WEIRD: the paper inconsistently uses math font and code font for the "S" +% that we are defining. +% To my understanding, we should aim to use code fonts for types, +% so this math font $S$ should be replaced. \pnum -For each function defined in \ref{mdspan.sub} that -takes a parameter pack named \tcode{slices} as an argument: - +Given a signed or unsigned integer type \tcode{IndexType}, +a type $S$ is a +%FIXME: the way I marked up this definition is almost certainly not the way it should be done +\defnx{\tcode{submdspan} slide type for \tcode{IndexType}}{\tcode{submdspan}!slice type} +if at least one of the following holds: \begin{itemize} -\item let \tcode{index_type} be +\item + \tcode{is_convertible_v<$S$, full_extent_t>} is \tcode{true}; +\item + \tcode{is_convertible_v<$S$, IndexType>} is \tcode{true}; +\item + $S$ is a specialization of \tcode{strided_slice} and + \tcode{is_convertible_v<$X$, IndexType>} is \tcode{true} for $X$ denoting + \tcode{$S$::offset_type}, + %FIXME: Oxford comma + \tcode{$S$::extent_type} and + \tcode{$S$::stride_type}; or +\item + all of the following hold: \begin{itemize} \item - \tcode{M::index_type} - if the function is a member of a class \tcode{M}, + the declaration \tcode{auto [...ls] = std::move(s);} is well-formed + for some object \tcode{s} of type $S$, \item - otherwise, - \tcode{remove_reference_t::index_type} - if the function has a parameter named \tcode{src}, + \tcode{sizeof...(ls)} is equal to 2, and \item - otherwise, - the same type as the function's template argument \tcode{IndexType}; + \tcode{(is_convertible_v, IndexType> && ...)} is \tcode{true}. \end{itemize} -\item let \tcode{rank} be the number of elements in \tcode{slices}; -\item let $s_k$ be the $k^\text{th}$ element of \tcode{slices}; -\item let $S_k$ be the type of $s_k$; and -\item let \tcode{\placeholder{map-rank}} be an \tcode{array} such that -for each $k$ in the range \range{0}{rank}, -\tcode{\placeholder{map-rank}[$k$]} equals: +\end{itemize} + +\pnum +Given a signed or unsigned integer type \tcode{IndexType}, +a type $S$ is a +\defnx{canonical \tcode{submdspan} index type for \tcode{IndexType}}{\tcode{submdspan}!canonical index type} +if $S$ is either \tcode{IndexType} or \tcode{constant_wrapper} +for some value \tcode{v} of type \tcode{IndexType}, +such that \tcode{v} is greater than or equal to zero. + +\pnum +Given a signed or unsigned integer type \tcode{IndexType}, +a type $S$ is a +\defnx{canonical \tcode{submdspan} slice type for \tcode{IndexType}}{\tcode{submdspan}!canonical slice type} +if exactly one of the following is \tcode{true}: +\begin{itemize} +\item + $S$ is \tcode{full_extent_t}; +\item + $S$ is a canonical \tcode{submdspan} index type for \tcode{IndexType}; or +\item + $S$ is a specialization of \tcode{strided_slice} + where all of the following hold: \begin{itemize} \item - \tcode{dynamic_extent} - if $S_k$ models \tcode{\libconcept{convertible_to}}, + \tcode{S::offset_type}, \tcode{S::extent_type}, and \tcode{S::stride_type} + are all canonical \tcode{submdspan} index types for \tcode{IndexType}; and \item - otherwise, - the number of types $S_j$ with $j < k$ that - do not model \tcode{\libconcept{convertible_to}}. + if \tcode{$S$::stride_type} and \tcode{$S$::extent_type} + are both specializations of \tcode{constant_wrapper}, + then \tcode{$S$::stride_type::value} is greater than zero. + \end{itemize} +\end{itemize} + +\pnum +A type $S$ is a \defnadj{collapsing}{slice type} if +it is neither \tcode{full_extent_t} nor +a specialization of \tcode{strided_slice}. +\begin{note} +Each collapsing slice type in \tcode{submdspan_mapping}'s parameter pack +of slice specifier types +reduces the rank of the result of \tcode{submdspan_mapping} by one. +\end{note} + +\pnum +A type \tcode{S} is a \defnadj{unit-stride}{slice type} if +\begin{itemize} +\item + \tcode{S} is a specialization of \tcode{strided_slice} + where \tcode{S::stride_type} is a specialization of \tcode{constant_wrapper} and + \tcode{S::stride_type::value} is equal to 1, or +\item + \tcode{S} denotes \tcode{full_extent_t}. +\end{itemize} + +\pnum +Given an object \tcode{e} of type \tcode{E} that is a specialization of \tcode{extents}, and +an object \tcode{s} of type \tcode{S} +that is a canonical \tcode{submdspan} slice type for \tcode{E::index_type}, +the \defnx{\tcode{submdspan} slice range of \tcode{s} for the $k^\text{th}$ extent of \tcode{E}}{\tcode{submdspan}!slice range} +of \tcode{e} is: +\begin{itemize} +\item + \range{$0$}{e.extent($k$)}, + if \tcode{S} is \tcode{full_extent_t}; +\item + \range{E::index_type(s.offset)}{E::index_type(s.offset + s.extent)}, + if \tcode{S} is a specialization of \tcode{strided_slice}; otherwise +\item + \range{\tcode{E::index_type(s)}}{$\tcode{E::index_type(s)} + 1$} +\end{itemize} + +\pnum +Given a type \tcode{E} that is a specialization of \tcode{extents}, +a type \tcode{S} is a +%FIXME: the trailing comma here makes no grammatical sense +\defnx{valid \tcode{submdspan} slice type for the $k^\text{th}$ extent of \tcode{E}}{\tcode{submdspan}!valid slice type}, +if \tcode{S} is a canonical slice type for \tcode{E::index_type}, and +for +%FIXME: this is weird ... what is "x"? +%Maybe say "for a value x"? +$x$ equal to \tcode{E::static_extent($k$)}, +either $x$ is equal to \tcode{dynamic_extent}; or +\begin{itemize} +\item + if \tcode{S} is a specialization of \tcode{strided_slice}: + \begin{itemize} + \item + if \tcode{S::offset_type} is a specialization of \tcode{constant_wrapper}, + then \tcode{S::offset_type::value} is less than or equal to $x$; + %FIXME: I'm pretty sure there needs to be some combining "or" whatever for these bullets + \item + if \tcode{S::offset_type} is a specialization of \tcode{constant_wrapper}, + then \tcode{S::extent_type::value} is less than or equal to $x$; + \item + if both \tcode{S::offset_type} and \tcode{S::extent_type} + are specializations of \tcode{constant_wrapper}, + then \tcode{S::offset_type::value + S::extent_type::value} + is less than or equal to $x$; + \end{itemize} +\item + if $S$ is a specialization of \tcode{constant_wrapper}, + then \tcode{S::value} is less than \tcode{x}. +\end{itemize} + +\pnum +Given an object \tcode{e} of type \tcode{E} +that is a specialization of \tcode{extents} and +an object \tcode{s} of type \tcode{S}, +\tcode{s} is a +\defnx{valid \tcode{submdspan} slice for the $k^\text{th}$ extent of \tcode{e}}{\tcode{submdspan}!valid slice} +if +\begin{itemize} +\item + \tcode{S} is a valid \tcode{submdspan} slice type for the $k^\text{th}$ extent of \tcode{E}; +\item + the $k^\text{th}$ interval of \tcode{e} + contains the \tcode{submdspan} slice range of \tcode{s} + for the $k^\text{th}$ extent of \tcode{e}; and +\item + if \tcode{S} is a specialization of \tcode{strided_slice}, then: + \begin{itemize} + \item \tcode{s.extent} is greater than or equal to zero, and + \item either \tcode{s.extent} equals zero or \tcode{s.stride} is greater than zero. \end{itemize} \end{itemize} @@ -25359,125 +25489,143 @@ \rSec4[mdspan.sub.helpers]{Exposition-only helpers} -\indexlibraryglobal{\exposid{de-ice}}% -\indexlibraryglobal{\exposid{first_}}% +\pnum +\indexlibraryglobal{\exposid{MAP_RANK}}% +For a pack \tcode{p} and an integer $i$, +let \tcode{\exposid{MAP_RANK}(p, $i$)} be the number of elements \tcode{p...[$j$]} +for $0 \le j < i$ whose types are not collapsing slice types. + \begin{itemdecl} template - constexpr T @\exposid{de-ice}@(T val) { return val; } -template<@\exposconcept{integral-constant-like}@ T> - constexpr auto @\exposid{de-ice}@(T) { return T::value; } - -template - constexpr IndexType @\exposid{first_}@(SliceSpecifiers... slices); + concept @\defexposconcept{is-strided-slice}@ = @\seebelow;@ \end{itemdecl} \begin{itemdescr} \pnum -\mandates -\tcode{IndexType} is a signed or unsigned integer type. - -%FIXME: What is $k$ in this itemdescr? Did we mean the \tcode{k} passed to first_? If so, what do we do for de-ice? -\pnum -Let $\phi{}_k$ denote the following value: -\begin{itemize} -\item -$s_k$ if $S_k$ models \tcode{\libconcept{convertible_to}}; -\item -otherwise, -\tcode{get<0>($s_k$)} -if $S_k$ models \tcode{\exposconcept{index-pair-like}}; -\item -otherwise, -\tcode{\exposid{de-ice}($s_k$.offset)} -if $S_k$ is a specialization of \tcode{strided_slice}; -\item -otherwise, -\tcode{0}. -\end{itemize} - -\pnum -\expects -$\phi{}_k$ is representable as a value of type \tcode{IndexType}. - -\pnum -\returns -\tcode{extents::\exposid{index-cast}($\phi{}_k$)}. +The concept \tcode{\exposconcept{is_strided-slice}} +is satisfied and modeled if and only +if \tcode{T} is a specialization of \tcode{strided_slice}. \end{itemdescr} -\indexlibraryglobal{\exposid{last_}}% +\indexlibraryglobal{\exposid{canonical-index}}% \begin{itemdecl} -template - constexpr auto @\exposid{last_}@(const Extents& src, SliceSpecifiers... slices); +template + constexpr auto \exposid{canonical-index}(S s); \end{itemdecl} \begin{itemdescr} \pnum \mandates -\tcode{Extents} is a specialization of \tcode{extents}. +If \tcode{S} models \exposconcept{integral-constant-like}, +then \tcode{extents::\exposid{index-cast}(S::value)} +is representable as a value of type \tcode{IndexType}. \pnum -Let \tcode{index_type} be \tcode{typename Extents::index_type}. +\expects +\tcode{extents::\exposid{index-cast}(std::move(s))} +is representable as a value of type \tcode{IndexType}. -%FIXME: What is $k$ in this itemdescr? Don't we want \tcode{k} and not $k$? \pnum -Let $\lambda{}_k$ denote the following value: +\effects +Equivalent to: \begin{itemize} \item -\tcode{\exposid{de-ice}($s_k$) + 1} -if $S_k$ models \tcode{\libconcept{convertible_to}}; otherwise -\item -\tcode{get<1>($s_k$)} -if $S_k$ models \tcode{\exposconcept{index-pair-like}}; otherwise +\tcode{return cw;} +if \tcode{S} models \exposconcept{integral-constant-like}; \item -\tcode{\exposid{de-ice}($s_k$.offset)} \tcode{+} -\tcode{\exposid{de-ice}($s_k$.extent)} -if $S_k$ is a specialization of \tcode{strided_slice}; otherwise -\item -%FIXME: Note that the paper uses \tcode{k} here. -\tcode{src.extent(k)}. +\tcode{return IndexType(std::move(s));} otherwise. \end{itemize} +\end{itemdescr} +\indexlibraryglobal{\exposid{canonical-slice}} +\begin{itemdecl} +template + constexpr auto \exposid{canonical-slice}(S s); +\end{itemdecl} + +\begin{itemdescr} \pnum -\expects -$\lambda{}_k$ is representable as a value of type \tcode{index_type}. +\mandates +\tcode{S} is a \tcode{submdspan} slice type for \tcode{IndexType}. \pnum -\returns -\tcode{Extents::\exposid{index-cast}($\lambda{}_k$)}. +\effects +Equivalent to: +\begin{codeblock} +if constexpr (is_convertible_v) { + return static_cast(std::move(s)); +} +else if constexpr (is_convertible_v) { + return @\exposid{canonical-index}@(std::move(s)); +} +else if constexpr (@\exposconcept{is-strided-slice}@) { + auto c_extent = @\exposid{canonical-index}@(std::move(s.extent)); + auto c_offset = @\exposid{canonical-index}@(std::move(s.offset)); + if constexpr (is_same_v>) { + return strided_slice{ + .offset = c_offset, + .extent = c_extent, + .stride = cw + }; + } + else { + return strided_slice{ + .offset = c_offset, + .extent = c_extent, + .stride = @\exposid{canonical-index}@(std::move(s.stride)) + }; + } +} +else { + auto [s_first, s_last] = std::move(s); + auto c_first = @\exposid{canonical-index}@(std::move(s_first)); + auto c_last = @\exposid{canonical-index}@(std::move(s_last)); + return strided_slice{ + .offset = c_first, + .extent = @\exposid{canonical-index}@(c_last - c_first), + .stride = cw + }; +} +\end{codeblock} \end{itemdescr} -\indexlibraryglobal{\exposid{src-indices}}% +%FIXME: what is this section supposed to be called? +\rSec4[mdspan.sub.canonical]{\tcode{submdspan_extents} something something} + +\indexlibraryglobal{submdspan_canonicalize_slices}% \begin{itemdecl} -template - constexpr array - @\exposid{src-indices}@(const array& indices, SliceSpecifiers... slices); +template + constexpr auto submdspan_canonicalize_slices(const extents& src, + SliceSpecifiers... slices); \end{itemdecl} \begin{itemdescr} \pnum -\mandates -\tcode{IndexType} is a signed or unsigned integer type. +\constraints +\tcode{sizeof...(SliceSpecifiers)} equals \tcode{sizeof...(Extents)}. \pnum -\returns -An \tcode{array src_idx} such that -for each $k$ in the range \range{0}{sizeof...(SliceSpecifiers)}, -\tcode{src_idx[$k$]} equals - +\mandates +For each rank index $k$ of \tcode{src}: \begin{itemize} \item -%FIXME: We're redefining what $k$ is here. -%FIXME: Also, calling first_ on each $k$ below gives us a set of indeces - how do we assign a set to the src_idx[$k$] above? -\tcode{\exposid{first_}(slices...)} for each $k$ -where \tcode{\placeholder{map-rank}[$k$]} equals -\tcode{dynamic_extent}, - + \tcode{SliceSpecifiers...[k]} + is a \tcode{submdspan} slice type for \tcode{IndexType}, and \item -%FIXME: How do we fail the previous \item to get here? There's no "if" test to fail. -otherwise, -\tcode{\exposid{first_}(slices...)} \tcode{+} -\tcode{indices[\placeholder{map-rank}[$k$]]}. + \tcode{decltype(canonical-slice(slices...[k]))} + is a valid submdspan slice type for the $k^\text{th}$ extent of + \tcode{extents}. \end{itemize} + +\pnum +\expects +For each rank index $k$ of \tcode{src}, +\tcode{\exposid{canonical-slice}(slices...[k])} +is a valid \tcode{submdspan} slice for the $k^\text{th}$ extent of \tcode{src}. + +\pnum +\returns +\tcode{make_tuple(\exposid{canonical-slice}(slices)...)}. \end{itemdescr} \rSec4[mdspan.sub.extents]{\tcode{submdspan_extents} function} @@ -25486,82 +25634,76 @@ \begin{itemdecl} template constexpr auto submdspan_extents(const extents& src, - SliceSpecifiers... slices); + SliceSpecifiers... raw_slices); \end{itemdecl} \begin{itemdescr} +\pnum +Let \tcode{slices} be the pack intoduced by the following declaration: +\begin{codeblock} +auto [...slices] = submdspan_canonicalize_slices(src, raw_slices...); +\end{codeblock} + \pnum \constraints -\tcode{sizeof...(slices)} equals \tcode{sizeof...(Extents)}. +%FIXME: The paper cites "equals Extents::rank()" as the pre-existing wording. +%Investigate whether the change is still okay (which only affects the left side). +\tcode{sizeof...(SliceSpecifiers)} equals \tcode{sizeof...(Extents)}. \pnum \mandates -For each rank index $k$ of \tcode{src.extents()}, -exactly one of the following is true: +For each rank index $k$ of \tcode{src}: \begin{itemize} -\item $S_k$ models \tcode{\libconcept{convertible_to}}, -\item $S_k$ models \tcode{\exposconcept{index-pair-like}}, -\item \tcode{is_convertible_v<$S_k$, full_extent_t>} is \tcode{true}, or -\item $S_k$ is a specialization of \tcode{strided_slice}. +\item + \tcode{SliceSpecifiers...[k]} + is a \tcode{submdspan} slice type for \tcode{IndexType}, and +\item + \tcode{decltype(slices...[k])} is a valid \tcode{submdspan} slice type + for the $k^\text{th}$ extent of \tcode{extents}. \end{itemize} \pnum \expects -For each rank index $k$ of \tcode{src.extents()}, -all of the following are \tcode{true}: -\begin{itemize} -\item -if $S_k$ is a specialization of \tcode{strided_slice} - \begin{itemize} - \item $\tcode{$s_k$.extent} = 0$, or - \item $\tcode{$s_k$.stride} > 0$ - \end{itemize} -\item -$0 \le \tcode{\exposid{first_}(slices...)}$ -$\le \tcode{\exposid{last_}<$k$>(src, slices...)}$ -$\le \tcode{src.extent($k$)}$ -\end{itemize} +For each rank index $k$ of \tcode{src}, +\tcode{slices...[k]} is a valid \tcode{submdspan} slice +for the $k^\text{th}$ extent of \tcode{src}. \pnum Let \tcode{SubExtents} be a specialization of \tcode{extents} such that: \begin{itemize} \item -%FIXME: I think we want the count here, "number" is ambiguous. -\tcode{SubExtents::rank()} equals the number of $k$ such that -$S_k$ does not model \tcode{\libconcept{convertible_to}}; and +\tcode{SubExtents::rank()} equals +\tcode{\exposid{MAP_RANK}(slices, Extents::rank())}; and \item for each rank index $k$ of \tcode{Extents} such that -\tcode{\placeholder{map-rank}[$k$] != dynamic_extent} is \tcode{true}, -\tcode{SubExt\-ents::static_extent(\placeholder{map-rank}[$k$])} equals: +the type of \tcode{slices...[$k$]} is not a collapsing slice type, +\tcode{SubExt\-ents::static_extent(\exposid{MAP_RANK}(slices, $k$))} +equals the following, where $\Sigma_k$ +denotes the type of \tcode{slices...[$k$]}: \begin{itemize} \item \tcode{Extents::static_extent($k$)} - if \tcode{is_convertible_v<$S_k$, full_extent_t>} is \tcode{true}; + if $\Sigma_k$ denotes the \tcode{full_extent_t}; otherwise - \item - \tcode{\exposid{de-ice}(tuple_element_t<1, $S_k$>()) -} - \tcode{\exposid{de-ice}(tuple_element_t<0, $S_k$>())} - if $S_k$ models \tcode{\exposconcept{index-pair-like}}, and - both \tcode{tuple_element_t<0, $S_k$>} and - \tcode{tuple_elem\-ent_t<1, $S_k$>} - model \exposconcept{integral-constant-like}; otherwise - \item \tcode{0}, - if $S_k$ is a specialization of \tcode{strided_slice}, whose - \tcode{extent_type} models \exposconceptx{integral-cons\-tant-like}{integral-constant-like}, - for which \tcode{extent_type()} equals zero; otherwise + if $\Sigma_k$ is a specialization of \tcode{strided_slice} and + \tcode{$\Sigma_k$::extent_type} denotes \tcode{constant_wrapper}; + otherwise \item - \tcode{1 + (\exposid{de-ice}($S_k$::extent_type()) - 1) /} - \tcode{\exposid{de-ice}($S_k$::stride_type())}, - if $S_k$ is a specialization of \tcode{strided_slice} whose + %FIXME: IMPORTANT: There is a closing parenthesis in the inserted wording, + %but no opening parenthesis. + %Did we mean to do: (1 + ...) / ... + % or: (1 + ... / ...) + \tcode{1 + ($\Sigma_k$::extent_type::value - 1) / $\Sigma_k$::stride_type::value}, + if $\Sigma_k$ is a specialization of \tcode{strided_slice} whose \tcode{extent_type} and \tcode{stride_type} - model \exposconceptx{integral-cons\-tant-like}{integral-constant-like}; + denote specializations of \tcode{constant_wrapper}; \item otherwise, @@ -25572,62 +25714,166 @@ \pnum \returns A value \tcode{ext} of type \tcode{SubExtents} such that -for each $k$ -for which \tcode{\placeholder{map-rank}[$k$] != dynamic_extent} is \tcode{true}, -\tcode{ext.extent(\placeholder{map-rank}[$k$])} equals: - +for each rank index $k$ of \tcode{extents}, +where the type of \tcode{slices...[$k$]} is not a collapsing slice type, +\tcode{ext.extent(\exposid{MAP_RANK}(slices, $k$))} +equals the following, +where $\sigma_k$ denotes \tcode{slices...[$k$]}: \begin{itemize} \item -\tcode{$s_k$.extent == 0 ? 0 : 1 + (\exposid{de-ice}($s_k$.extent) - 1) / \exposid{de-ice}($s_k$.stride)} -if $S_k$ is a specialization of \tcode{strided_slice}, - + \tcode{$\sigma_k$.extent == 0 ? 0 : 1 + ($\sigma_k$.extent - 1) / $\sigma_k$.stride} + if the type of $\sigma_k$ is a specialization of \tcode{strided_slice}, \item -otherwise, -\tcode{\exposid{last_}<$k$>(src, slices...) - \exposid{first_}(slices...)}. + otherwise, + $U - L$, where \range{$L$}{$U$} is the \tcode{submdspan} slice range + of $\sigma_k$ for the $k^\text{th}$ extent of \tcode{src}. \end{itemize} \end{itemdescr} \rSec4[mdspan.sub.map]{Specializations of \tcode{submdspan_mapping}} -\rSec5[mdspan.sub.map.common]{Common} +\rSec5[mdspan.sub.map.sliceable]{Sliceable layout mapping requirements} \pnum -The following elements apply to all functions in \ref{mdspan.sub.map}. +Let: +\begin{itemize} +\item + \tcode{M} denote a layout mapping class; +\item + \tcode{IT} denote \tcode{M::extent_type::index_type}; +\item + \tcode{m} denote a value of type (possibly const) \tcode{M}; +\item + \tcode{M_rank} be equal to \tcode{M::extent_type::rank()}; +\item + \tcode{valid_slices} denote a pack of (possibly const) objects + for which \tcode{sizeof...(valid_slices) == M_rank} is \tcode{true} and, + for each rank index $i$ of \tcode{m.extents()}, + \tcode{valid_slices...[i]} is a valid \tcode{submdspan} slice + for the $i^\text{th}$ extent of \tcode{m.extents()}; +\item + \tcode{invalid_slices} denote a pack of objects + for which \tcode{sizeof...(invalid_slices) == M_rank} is \tcode{true} and + there exists an integer $k$ such that the cv-unqualified type + of \tcode{invalid_slices...[$k$]} is none of the following: + \begin{itemize} + \item \tcode{IT}, + \item \tcode{full_extent_t}, + \item a specialization of \tcode{constant_wrapper}, or + \item a specialization of \tcode{strided_slice}. + \end{itemize} +\end{itemize} \pnum -\constraints -\tcode{sizeof...(slices)} equals \tcode{extents_type::rank()}. +For the purpose of this section, +the meaning of \tcode{submdspan_mapping} is established +as if by performing argument-dependent lookup only\iref{basic.lookup.argdep}. \pnum -\mandates -For each rank index $k$ of \tcode{extents()}, -exactly one of the following is true: +A type \tcode{M} meets the \defn{sliceable layout mapping requirements} if \begin{itemize} \item -$S_k$ models \tcode{\libconcept{convertible_to}}, +\tcode{M} meets the layout mapping requirements\iref{mdspan.layout.policy.reqmts}, +\item +the expression \tcode{submdspan_mapping(m, invalid_slices...)} is ill-formed, and \item -$S_k$ models \tcode{\exposconcept{index-pair-like}}, +%FIXME: is ISO going to like a bullet ending in a colon? +the following expression is well-formed and has the specified semantics: +\end{itemize} + +\begin{itemdecl} +submdspan_mapping(m, valid_slices...) +\end{itemdecl} + +\begin{itemdescr} +\pnum +\result +A type SMR that is a specialization of type submdspan_mapping_result for some type SM such that +\begin{itemize} +\item \tcode{SM} meets the layout mapping requirements\iref{mdspan.layout.policy.reqmts}, +\item \tcode{SM::extents_type} is a specialization of \tcode{extents}, +\item \tcode{SM::extents_type::rank()} equals +\tcode{\exposid{MAP_RANK}(valid_slices, M_rank)}, and +\item \tcode{SM::extents_type::index_type} denotes \tcode{IT}. +\end{itemize} + +\pnum +\returns +An object \tcode{smr} of type \tcode{SMR} such that +\begin{itemize} \item -\tcode{is_convertible_v<$S_k$, full_extent_t>} is \tcode{true}, or + \tcode{smr.mapping.extents() == submdspan_extents(m.extents(), valid_slices...)} + is \tcode{true}; and \item -$S_k$ is a specialization of \tcode{strided_slice}. + for each integer pack \tcode{i} + which is a multidimensional index in \tcode{smr.mapping.extents()}, + \tcode{smr.mapping(i...) + smr.offset == m(j)} is \tcode{true}, + where \tcode{j} is an integer pack such that + \begin{itemize} + \item + \tcode{sizeof...(j)} is equal to \tcode{M_rank}; and + \item + for each rank index $\rho$ of \tcode{m.extents()}, + \tcode{j...[$\rho$]} is equal to the sum of + \begin{itemize} + \item + the lower bound of the submdspan slice range of \tcode{valid_slices...[$\rho$]} + for extent $\rho$ of \tcode{m.extents()}, and + \item + zero if the type of \tcode{valid_slices...[$\rho$]} is a collapsing slice type, + %FIXME: the comma before "otherwise" seems wrong + \tcode{i...[MAP_RANK(valid_slices,$\rho$)]}, otherwise. + \end{itemize} + \end{itemize} \end{itemize} +\end{itemdescr} + +\begin{itemdecl} +template + concept @\defexposconcept{sliceable-mapping}@ = @\seebelow@; +\end{itemdecl} +\begin{itemdescr} \pnum -\expects -For each rank index $k$ of \tcode{extents()}, -all of the following are \tcode{true}: +Let \tcode{lm} be an object of type \tcode{LayoutMapping} +and let \tcode{fe} denote a pack of objects of type \tcode{full_extent_t} +for which \tcode{sizeof...(fe) == LayoutMapping::extents_type::rank()} is \tcode{true}. +A type \tcode{LayoutMapping} satisfies \exposconcept{sliceable-mapping} if \begin{itemize} \item -if $S_k$ is a specialization of \tcode{strided_slice}, -\tcode{$s_k$.extent} is equal to zero or -\tcode{$s_k$.stride} is greater than zero; and + the expression \tcode{submdspan_mapping(m, fe...)} is well-formed + when treated as an unevaluated operand, and \item -$0 \leq \tcode{\exposid{first_}(slices...)} \\ -\hphantom{0 } \leq \tcode{\exposid{last_}<$k$>(extents(), slices...)} \\ -\hphantom{0 } \leq \tcode{extents().extent($k$)}$ + the type of that expression is a specialization of + \tcode{submdspan_mapping_result}. \end{itemize} +\pnum +A type \tcode{LayoutMapping} models \exposconcept{sliceable-mapping} +if \tcode{LayoutMapping} meets the sliceable layout mapping requirements. +\end{itemdescr} + +\rSec5[mdspan.sub.map.common]{Common} + +\pnum +The following elements apply to all functions in \ref{mdspan.sub.map}. + +\pnum +\constraints +\tcode{sizeof...(SpliceSpecifiers)} equals \tcode{extents_type::rank()}. + +\pnum +\mandates +For each rank index $k$ of \tcode{extents()}, +\tcode{SliceSpecifiers...[$k$]} is a valid \tcode{submdspan} slice type +for the $k^\text{th}$ extent of \tcode{Extents}. + +\pnum +\expects +For each rank index $k$ of \tcode{extents()}, +\tcode{slices...[$k$]} is a valid slice +for the $k^\text{th}$ extent of \tcode{extents()}. + \pnum Let \tcode{sub_ext} be the result of \tcode{submdspan_extents(extents(), slices...)} and @@ -25637,42 +25883,36 @@ Let \tcode{sub_strides} be an \tcode{array} such that for each rank index $k$ of \tcode{extents()} -for which \tcode{\exposid{map-rank}[$k$]} is not \tcode{dynamic_extent}, -\tcode{sub_strides[\exposid{map-rank}[$k$]]} equals: +for which the type of \tcode{slices...[$k$]} is not a collapsing slice type, +\tcode{sub_strides[\exposid{MAP_RANK}(slices,$k$)]} equals: \begin{itemize} \item -\tcode{stride(k) * \exposid{de-ice}($s_k$.stride)} -if $S_k$ is a specialization of \tcode{strided_slice} and -\tcode{$s_k$.stride < $s_k$.\linebreak extent} is \tcode{true}; +\tcode{stride(k) * s.stride} +%FIXME: this doesn't make any sense. +%How can an lvalue like "s" be the specialization of a type? +if \tcode{s} is a specialization of \tcode{strided_slice} and +\tcode{s.stride < s.extent} is \tcode{true} +%FIXME: comma? +where \tcode{s} is \tcode{slices...[$k$]}; \item otherwise, \tcode{stride($k$)}. \end{itemize} \pnum -Let \tcode{P} be a parameter pack -such that \tcode{is_same_v, index_sequence>} -is \tcode{true}. +Let \tcode{ls} be a pack of values of \tcode{index_type}, +where the $\rho^\text{th}$ element equals the lower bound +of the \tcode{submdspan} slice range of \tcode{slices...[$\rho$]} +for extent $\rho$ of \tcode{extents()}. \pnum -If \tcode{\exposid{first_}(slices...)} +If \tcode{ls...[$k$]} equals \tcode{extents().extent($k$)} for any rank index $k$ of \tcode{extents()}, then let \tcode{offset} be a value of type \tcode{size_t} equal to -\tcode{(*this).required_span_size()}. +\tcode{operator().required_span_size()}. Otherwise, let \tcode{offset} be a value of type \tcode{size_t} equal to -\tcode{(*this)(\exposid{first_}(slices...)...)}. - -\pnum -Given a layout mapping type \tcode{M}, a type \tcode{S} is a -\defnadjx{unit-stride}{slice for \tcode{M}}{slice} if -\begin{itemize} -\item \tcode{S} is a specialization of \tcode{strided_slice} -where \tcode{S::stride_type} models \exposconcept{integral-constant-like} -and \tcode{S::stride_type::value} equals \tcode{1}, -\item \tcode{S} models \tcode{\exposconcept{index-pair-like}}, or -\item \tcode{is_convertible_v} is \tcode{true}. -\end{itemize} +\tcode{operator()(ls...)}. \rSec5[mdspan.sub.map.left]{\tcode{layout_left} specialization of \tcode{submdspan_mapping}} @@ -25701,15 +25941,18 @@ if \begin{itemize} \item + %FIXME: drive-by fix this broken parenthesis, + %even though the authors didn't say pretty please like for the same mistake + %in [mdspan.sub.map.rightpad] :( for each $k$ in the range \range{0}{SubExtents::rank() - 1)},\newline - \tcode{is_convertible_v<$S_k$, full_ext\-ent_t>} is \tcode{true}; and + \tcode{SpliceSpecifiers...[$k$]} denotes \tcode{full_extent_t}; and \item for $k$ equal to \tcode{SubExtents::rank() - 1}, - $S_k$ is a unit-stride slice for \tcode{mapping}; + \tcode{SpliceSpecifiers...[$k$]} is a unit-stride slice type; \end{itemize} \begin{note} If the above conditions are true, -all $S_k$ with $k$ larger than \tcode{SubExtents::rank() - 1} +all \tcode{SpliceSpecifiers...[$k$]} with $k$ larger than \tcode{SubExtents::rank() - 1} are convertible to \tcode{index_type}. \end{note} \item @@ -25720,17 +25963,17 @@ \end{codeblock} if for a value $u$ for which $u+1$ is the smallest value $p$ larger than zero -for which $S_p$ is a unit-stride slice for \tcode{mapping}, +for which \tcode{SliceSpecifiers...[p]} is a unit-stride slice type, the following conditions are met: \begin{itemize} \item -$S_0$ is a unit-stride slice for \tcode{mapping}; and +\tcode{SliceSpecifiers...[0]} is a unit-stride slice type; and \item for each $k$ in the range \range{$u$ + 1}{$u$ + SubExtents::rank() - 1},\newline -\tcode{is_convertible_v<$S_k$, full_extent_t>} is \tcode{true}; and +\tcode{SliceSpecifiers...[$k$]} denotes \tcode{full_extent_t}; and \item for $k$ equal to \tcode{$u$ + SubExtents::rank() - 1}, -$S_k$ is a unit-stride slice for \tcode{mapping}; +\tcode{SliceSpecifiers...[$k$]} is a unit-stride slice type; \end{itemize} and where \tcode{S_static} is: \begin{itemize} @@ -25778,14 +26021,14 @@ \begin{itemize} \item for each $k$ in the range \range{\exposid{rank_} - SubExtents::rank() + 1}{\exposid{rank_}},\newline - \tcode{is_convertible_v<$S_k$, full_extent_t>} is \tcode{true}; and + \tcode{SliceSpecifiers...[$k$]} denotes \tcode{full_extent_t}; and \item for $k$ equal to \exposid{rank_} - \tcode{SubExtents::rank()}, - $S_k$ is a unit-stride slice for \tcode{mapping}; + \tcode{SliceSpecifiers...[$k$]} is a unit-stride slice type; \end{itemize} \begin{note} If the above conditions are true, -all $S_k$ with $k < \tcode{\exposid{rank_} - SubExtents::rank()}$ +all \tcode{SliceSpecifiers...[$k$]} with $k < \tcode{\exposid{rank_} - SubExtents::rank()}$ are convertible to \tcode{index_type}. \end{note} \item @@ -25796,19 +26039,19 @@ \end{codeblock} if for a value $u$ for which $\exposid{rank_} - u - 2$ is the largest value $p$ smaller than \tcode{\exposid{rank_} - 1} -for which $S_p$ is a unit-stride slice for \tcode{mapping}, +for which \tcode{SliceSpecifiers...[p]} is a unit-stride slice type, the following conditions are met: \begin{itemize} \item for $k$ equal to \tcode{\exposid{rank_} - 1}, -$S_k$ is a unit-stride slice for \tcode{mapping}; and +\tcode{SliceSpecifiers...[$k$]} is a unit-stride slice type; and \item for each $k$ in the range \range{\exposid{rank_} - SubExtents::rank() - $u$ + 1}{\exposid{rank_} - $u$ - 1},\newline -\tcode{is_con\-vertible_v<$S_k$, full_extent_t>} is \tcode{true}; and +\tcode{SliceSpecifiers...[p]} denotes \tcode{full_extent_t}; and \item for $k$ equal to \tcode{\exposid{rank_} - SubExtents::rank() - $u$},\newline -$S_k$ is a unit-stride slice for \tcode{mapping}; +\tcode{SliceSpecifiers...[$k$]} is a unit-stride slice type; \end{itemize} and where \tcode{S_static} is: \begin{itemize} @@ -25884,7 +26127,7 @@ \item \tcode{SubExtents::rank() == 1} is \tcode{true} and \item -$S_0$ is a unit-stride slice for \tcode{mapping}; +\tcode{SliceSpecifiers...[0]} is a unit-stride slice type; \end{itemize} \item otherwise, @@ -25894,17 +26137,17 @@ \end{codeblock} if for a value $u$ for which \tcode{$u$ + 1} is the smallest value $p$ larger than zero -for which $S_p$ is a unit-stride slice for \tcode{mapping}, +for which \tcode{SliceSpecifiers...[$p$]} is a unit-stride slice type, the following conditions are met: \begin{itemize} \item -$S_0$ is a unit-stride slice for \tcode{mapping}; and +\tcode{SliceSpecifiers...[0]} is a unit-stride slice type; and \item for each $k$ in the range \range{$u$ + 1}{$u$ + SubExtents::rank() - 1}, -\tcode{is_convertible_v<$S_k$, full_extent_t>} is \tcode{true}; and +\tcode{SliceSpecifiers...[$k$]} denotes \tcode{full_extent_t}; and \item for $k$ equal to \tcode{$u$ + SubExtents::rank() - 1}, -$S_k$ is a unit-stride slice for \tcode{mapping}; +\tcode{SliceSpecifiers...[$k$]} is a unit-stride slice type; \end{itemize} where \tcode{S_static} is: \begin{itemize} @@ -25956,7 +26199,7 @@ \tcode{SubExtents::rank() == 1} is \tcode{true} and \item for $k$ equal to \tcode{\exposid{rank_} - 1}, -$S_k$ is a unit-stride slice for \tcode{mapping}; +\tcode{SliceSpecifiers...[$k$]} is a unit-stride slice type; \end{itemize} \item otherwise, @@ -25967,19 +26210,20 @@ if for a value $u$ for which \tcode{\exposid{rank_} - $u$ - 2} is the largest value p smaller than \tcode{\exposid{rank_} - 1} -for which $S_p$ is a unit-stride slice for \tcode{mapping}, +for which \tcode{SliceSpecifiers...[$p$]} is a unit-stride slice type, the following conditions are met: \begin{itemize} \item for $k$ equal to \tcode{\exposid{rank_} - 1}, -$S_k$ is a unit-stride slice for \tcode{mapping}; and +\tcode{SliceSpecifiers...[$k$]} is a unit-stride slice type; and \item for each $k$ in the range +%FIXME: drive-by fix the broken parenthesis \range{\exposid{rank_} - SubExtents::rank() - $u$ + 1}{\exposid{rank_} - $u$ - 1)}, -\tcode{is_convertible_v<$S_k$, full_extent_t>} is \tcode{true}; and +\tcode{SliceSpecifiers...[$k$]} denotes \tcode{full_extent_t}; and \item for $k$ equal to \tcode{\exposid{rank_} - SubExtents::rank() - $u$},\newline -$S_k$ is a unit-stride slice for \tcode{mapping}; +\tcode{SliceSpecifiers...[$k$]} is a unit-stride slice type; \end{itemize} and where \tcode{S_static} is: \begin{itemize} @@ -26016,6 +26260,12 @@ \pnum Let \tcode{index_type} be \tcode{typename Extents::index_type}. +\pnum +Let \tcode{slices} be the pack introduced by the following declaration: +\begin{codeblock} +auto [...slices] = submdspan_canonicalize_slices(src, raw_slices...); +\end{codeblock} + \pnum Let \tcode{sub_map_offset} be the result of \tcode{submdspan_mapping(src.mapping(), slices...)}. @@ -26032,70 +26282,26 @@ \item \tcode{sizeof...(slices)} equals \tcode{Extents::rank()}, and \item -the expression \tcode{submdspan_mapping(src.mapping(), slices...)} -is well-formed when treated as an unevaluated operand. +\tcode{LayoutPolicy::mapping} models \exposconcept{sliceable-mapping}. \end{itemize} \pnum \mandates +For each rank index $k$ of \tcode{src}: \begin{itemize} \item -\tcode{decltype(submdspan_mapping(src.mapping(), slices...))} -is a specialization of \tcode{submd-\linebreak{}span_mapping_result}. - -\item -\tcode{is_same_v,} -\tcode{decltype(\linebreak{}submdspan_extents(src.mapping(), slices...))>} -is \tcode{true}. - + \tcode{SliceSpecifiers...[$k$]} is a \tcode{submdspan} slice type + for \tcode{index_type}, and \item -For each rank index $k$ of \tcode{src.extents()}, -exactly one of the following is true: - \begin{itemize} - \item $S_k$ models \tcode{\libconcept{convertible_to}}, - \item $S_k$ models \tcode{\exposconcept{index-pair-like}}, - \item \tcode{is_convertible_v<$S_k$, full_extent_t>} is \tcode{true}, or - \item $S_k$ is a specialization of \tcode{strided_slice}. - \end{itemize} + \tcode{decltype(slices...[$k$])} is a valid \tcode{submdspan} slice type + for the $k^\text{th}$ extent of \tcode{Extents}. \end{itemize} \pnum \expects -\begin{itemize} -\item For each rank index $k$ of \tcode{src.extents()}, -all of the following are \tcode{true}: - \begin{itemize} - \item - if $S_k$ is a specialization of \tcode{strided_slice} - \begin{itemize} - \item $\tcode{$s_k$.extent} = 0$, or - \item $\tcode{$s_k$.stride} > 0$ - \end{itemize} - \item - $0 \le \tcode{\exposid{first_}(slices...)}$ - $\le \tcode{\exposid{last_}<$k$>(src.extents(), slices...)}$ - $\le \tcode{\linebreak{}src.extent($k$)}$ - \end{itemize} - -\item -\tcode{sub_map_offset.mapping.extents() == submdspan_extents(src.mapping(), slices...)}\linebreak -is \tcode{true}; and - -\item -for each integer pack \tcode{I} which is a multidimensional index -in \tcode{sub_map_offset.mapping.extents()}, -\begin{codeblock} -sub_map_offset.mapping(I...) + sub_map_offset.offset == - src.mapping()(@\exposid{src-indices}@(array{I...}, slices...)) -\end{codeblock} -is \tcode{true}. -\end{itemize} - -\begin{note} -These conditions ensure that the mapping returned by \tcode{submdspan_mapping} -matches the algorithmically expected index-mapping given the slice specifiers. -\end{note} +\tcode{slices...[$k$]} is a valid \tcode{submdspan} slice +for the $k^\text{th}$ extent of \tcode{src.extents()}. \pnum \effects diff --git a/source/support.tex b/source/support.tex index c6929d58ff..da1f0f715c 100644 --- a/source/support.tex +++ b/source/support.tex @@ -847,7 +847,7 @@ #define @\defnlibxname{cpp_lib_string_subview}@ 202506L // also in \libheader{string}, \libheader{string_view} #define @\defnlibxname{cpp_lib_string_udls}@ 201304L // also in \libheader{string} #define @\defnlibxname{cpp_lib_string_view}@ 202403L // also in \libheader{string}, \libheader{string_view} -#define @\defnlibxname{cpp_lib_submdspan}@ 202411L // freestanding, also in \libheader{mdspan} +#define @\defnlibxname{cpp_lib_submdspan}@ 202511L // freestanding, also in \libheader{mdspan} #define @\defnlibxname{cpp_lib_syncbuf}@ 201803L // also in \libheader{syncstream} #define @\defnlibxname{cpp_lib_task}@ 202506L // also in \libheader{execution} #define @\defnlibxname{cpp_lib_text_encoding}@ 202306L // also in \libheader{text_encoding}