span should be ill-formed where possibleSection: 23.7.2.2.4 [span.sub] Status: C++20 Submitter: Tomasz Kamiński Opened: 2018-04-13 Last modified: 2021-02-25
Priority: 3
View all other issues in [span.sub].
View all issues with C++20 status.
Discussion:
Currently all out-of-bound/inputs errors in the functions taking an subview of span lead to undefined behavior,
even in the situation when they could be detected at compile time. This is inconsistent with the behavior of the span
constructors, which make similar constructs ill-formed.
subspan function, the following invocation:
span<T, N> s; // N > 0 s.subspan<O>(); // with O > 0
is ill-formed when O > N + 1, as the return of the function is span<T, K> with K < -1.
However in case when O == N + 1, runtime sized span is returned (span<T, -1>) instead and
the behavior of the function is undefined.
N == dynamic_extent) and fixed sized (N > 0) object s of
type span<T, N>, the following constructs should be ill-formed, instead of having undefined behavior:
s.first<C>() with C < 0
s.last<C>() with C < 0
s.subspan<O, E> with O < 0 or E < 0 and E != dynamic_extent.
This would follow span specification, that make instantiation of span<T, N> ill-formed for
N < 0 and N != dynamic_extent.
s of type
span<T, N> (with N > 0):
s.first<C>() with C > N
s.last<C>() with C > N
s.subspan<O, dynamic_extent>() with O > N
s.subspan<O, C>() with O + C > N
This will match the span constructor that made construction of fixed size span<T, N> from fixed
size span of different size ill-formed.
[2018-04-24 Priority set to 3 after discussion on the reflector.]
[2018-11 San Diego Thursday night issue processing]
Tomasz to provide updated wording.
Previous resolution: [SUPERSEDED]This wording is relative to N4741.
Edit 23.7.2.2.4 [span.sub] as indicated:
template<ptrdiff_t Count> constexpr span<element_type, Count> first() const;-?- Remarks: If
-1- Requires:Count < 0 || (Extent != dynamic_extent && Count > Extent), the program is ill-formed.. -2- Effects: Equivalent to:0 <= Count &&Count <= size()return {data(), Count};template<ptrdiff_t Count> constexpr span<element_type, Count> last() const;-?- Remarks: If
-3- Requires:Count < 0 || (Extent != dynamic_extent && Count > Extent), the program is ill-formed.. -4- Effects: Equivalent to:0 <= Count &&Count <= size()return {data() + (size() - Count), Count};template<ptrdiff_t Offset, ptrdiff_t Count = dynamic_extent> constexpr span<element_type, see below> subspan() const;-?- Remarks: The program is ill-formed if:
-5- Requires:
Offset < 0 || (Count < 0 && Count != dynamic_extent), or
Extend != dynamic_extent && (Offset > Extent || (Count != dynamic_extent && Offset + Count > Extent)).. -6- Effects: Equivalent to:(0 <= Offset &&Offset <= size())&& (Count == dynamic_extent ||Count >= 0 &&Offset + Count <= size())return span<ElementType, see below>( data() + Offset, Count != dynamic_extent ? Count : size() - Offset);-7- Remarks: The second template argument of the returnedspantype is:Count != dynamic_extent ? Count : (Extent != dynamic_extent ? Extent - Offset : dynamic_extent)
[2018-11-09; Tomasz provides updated wording]
I have decided to replace all Requires: elements in the section 23.7.2.2.4 [span.sub] to preserve consistency.
Previous resolution: [SUPERSEDED]This wording is relative to N4778.
Edit 23.7.2.2.4 [span.sub] as indicated:
template<ptrdiff_t Count> constexpr span<element_type, Count> first() const;-?- Mandates:
-1-Count >= 0 && (Extent == dynamic_extent || Count <= Extent).RequiresExpects:. -2- Effects: Equivalent to:0 <= Count &&Count <= size()return {data(), Count};template<ptrdiff_t Count> constexpr span<element_type, Count> last() const;-?- Mandates:
-3-Count >= 0 && (Extent == dynamic_extent || Count <= Extent).RequiresExpects:. -4- Effects: Equivalent to:0 <= Count &&Count <= size()return {data() + (size() - Count), Count};template<ptrdiff_t Offset, ptrdiff_t Count = dynamic_extent> constexpr span<element_type, see below> subspan() const;-?- Mandates:
-5-Offset >= 0 && (Count >= 0 || Count == dynamic_extent) && (Extent == dynamic_extent || (Offset <= Extent && (Count == dynamic_extent || Offset + Count <= Extent))).RequiresExpects:. -6- Effects: Equivalent to:(0 <= Offset &&Offset <= size())&& (Count == dynamic_extent ||Count >= 0 &&Offset + Count <= size())return span<ElementType, see below>( data() + Offset, Count != dynamic_extent ? Count : size() - Offset);-7- Remarks: The second template argument of the returnedspantype is:Count != dynamic_extent ? Count : (Extent != dynamic_extent ? Extent - Offset : dynamic_extent)constexpr span<element_type, dynamic_extent> first(index_type count) const;-8-
-9- Effects: Equivalent to:RequiresExpects:0 <= count && count <= size().return {data(), count};constexpr span<element_type, dynamic_extent> last(index_type count) const;-10-
-11- Effects: Equivalent to:RequiresExpects:0 <= count && count <= size().return {data() + (size() - count), count};constexpr span<element_type, dynamic_extent> subspan( index_type offset, index_type count = dynamic_extent) const;-12-
-13- Effects: Equivalent to:RequiresExpects:(0 <= offset && offset <= size()) && (count == dynamic_extent || count >= 0 && offset + count <= size())return {data() + offset, count == dynamic_extent ? size() - offset : count};
[2019-06-23; Tomasz comments and provides updated wording]
The current proposed resolution no longer applies to the newest revision of the standard
(N4820), due changes introduced in
P1227 (making size() and template parameters
of span unsigned).
[2019 Cologne Wednesday night]
Status to Ready
Proposed resolution:
This wording is relative to N4820.
[Drafting note: This wording relies on observation, that the condition in formExtent == dynamic_extent || Count <= Extent, can be simplified intoCount <= Extent, becausedynamic_extentis equal tonumeric_limits<size_t>::max(), thussize() <= Extentis always true, andExtent == dynamic_extentimplies thatCount <= Extent. Furthermore we check thatCount != dynamic_extent || Count <= Extent - Offset, as theOffset + Count <= Extentmay overflow (defined for unsigned integers) and produce false positive result. This change is also applied to Expects clause. ]
Edit 23.7.2.2.4 [span.sub] as indicated:
template<size_t Count> constexpr span<element_type, Count> first() const;-?- Mandates:
-1- Expects:Count <= Extentistrue.Count <= size()istrue. -2- Effects: Equivalent to:return {data(), Count};template<size_t Count> constexpr span<element_type, Count> last() const;-?- Mandates:
-3- Expects:Count <= Extentistrue.Count <= size()istrue. -4- Effects: Equivalent to:return {data() + (size() - Count), Count};template<size_t Offset, size_t Count = dynamic_extent> constexpr span<element_type, see below> subspan() const;[…]-?- Mandates:
-5- Expects:Offset <= Extent && (Count == dynamic_extent || Count <= Extent - Offset)istrue.Offset <= size() && (Count == dynamic_extent ||isOffset + Count <= size()Count <= size() - Offset)true. -6- Effects: Equivalent to:return span<ElementType, see below>(data() + Offset, Count != dynamic_extent ? Count : size() - Offset);-7- Remarks: The second template argument of the returnedspantype is:Count != dynamic_extent ? Count : (Extent != dynamic_extent ? Extent - Offset : dynamic_extent)constexpr span<element_type, dynamic_extent> subspan( index_type offset, index_type count = dynamic_extent) const;-12- Expects:
-13- Effects: Equivalent to:offset <= size() && (count == dynamic_extent ||isoffset + count <= size()count <= size() - offset)true.return {data() + offset, count == dynamic_extent ? size() - offset : count};