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 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 returnedspan
type 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 returnedspan
type 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_extent
is equal tonumeric_limits<size_t>::max()
, thussize() <= Extent
is always true, andExtent == dynamic_extent
implies thatCount <= Extent
. Furthermore we check thatCount != dynamic_extent || Count <= Extent - Offset
, as theOffset + Count <= Extent
may 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 <= Extent
istrue
.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 <= Extent
istrue
.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 returnedspan
type 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};