Title
Errors in taking subview of span should be ill-formed where possible
Status
c++20
Section
[span.sub]
Submitter
Tomasz Kamiński

Created on 2018-04-13.00:00:00 last changed 45 months ago

Messages

Date: 2019-07-22.17:12:46

Proposed resolution:

This wording is relative to N4820.

[Drafting note: This wording relies on observation, that the condition in form Extent == dynamic_extent || Count <= Extent, can be simplified into Count <= Extent, because dynamic_extent is equal to numeric_limits<size_t>::max(), thus size() <= Extent is always true, and Extent == dynamic_extent implies that Count <= Extent.

Furthermore we check that Count != dynamic_extent || Count <= Extent - Offset, as the Offset + Count <= Extent may overflow (defined for unsigned integers) and produce false positive result. This change is also applied to Expects clause. ]

  1. Edit [span.sub] as indicated:

    template<size_t Count> constexpr span<element_type, Count> first() const;
    

    -?- Mandates: Count <= Extent is true.

    -1- Expects: Count <= size() is true.

    -2- Effects: Equivalent to: return {data(), Count};

    template<size_t Count> constexpr span<element_type, Count> last() const;
    

    -?- Mandates: Count <= Extent is true.

    -3- Expects: Count <= size() is true.

    -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: Offset <= Extent && (Count == dynamic_extent || Count <= Extent - Offset) is true.

    -5- Expects: Offset <= size() && (Count == dynamic_extent || Offset + Count <= size()Count <= size() - Offset) is 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 returned span 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: offset <= size() && (count == dynamic_extent || offset + count <= size()count <= size() - offset) is true.

    -13- Effects: Equivalent to: return {data() + offset, count == dynamic_extent ? size() - offset : count};

Date: 2019-07-22.17:12:46

[ 2019 Cologne Wednesday night ]

Status to Ready

Date: 2019-06-15.00:00:00

[ 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).

Date: 2018-11-15.00:00:00

[ 2018-11-09; Tomasz provides updated wording ]

I have decided to replace all Requires: elements in the section [span.sub] to preserve consistency.

Previous resolution: [SUPERSEDED]

This wording is relative to N4778.

  1. Edit [span.sub] as indicated:

    template<ptrdiff_t Count> constexpr span<element_type, Count> first() const;
    

    -?- Mandates: Count >= 0 && (Extent == dynamic_extent || Count <= Extent).

    -1- RequiresExpects: 0 <= Count && Count <= size().

    -2- Effects: Equivalent to: return {data(), Count};

    template<ptrdiff_t Count> constexpr span<element_type, Count> last() const;
    

    -?- Mandates: Count >= 0 && (Extent == dynamic_extent || Count <= Extent).

    -3- RequiresExpects: 0 <= Count && Count <= size().

    -4- Effects: Equivalent to: return {data() + (size() - Count), Count};

    template<ptrdiff_t Offset, ptrdiff_t Count = dynamic_extent>
      constexpr span<element_type, see below> subspan() const;
    

    -?- Mandates: Offset >= 0 && (Count >= 0 || Count == dynamic_extent) && (Extent == dynamic_extent || (Offset <= Extent && (Count == dynamic_extent || Offset + Count <= Extent))).

    -5- RequiresExpects: (0 <= Offset && Offset <= size()) && (Count == dynamic_extent || Count >= 0 && Offset + Count <= size()).

    -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 returned span 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- RequiresExpects: 0 <= count && count <= size().

    -9- Effects: Equivalent to: return {data(), count};

    constexpr span<element_type, dynamic_extent> last(index_type count) const;
    

    -10- RequiresExpects: 0 <= count && count <= size().

    -11- Effects: Equivalent to: return {data() + (size() - count), count};

    constexpr span<element_type, dynamic_extent> subspan(
      index_type offset, index_type count = dynamic_extent) const;
    

    -12- RequiresExpects: (0 <= offset && offset <= size()) && (count == dynamic_extent || count >= 0 && offset + count <= size())

    -13- Effects: Equivalent to: return {data() + offset, count == dynamic_extent ? size() - offset : count};

Date: 2018-11-14.18:19:04

[ 2018-11 San Diego Thursday night issue processing ]

Tomasz to provide updated wording.

Previous resolution: [SUPERSEDED]

This wording is relative to N4741.

  1. Edit [span.sub] as indicated:

    template<ptrdiff_t Count> constexpr span<element_type, Count> first() const;
    

    -?- Remarks: If Count < 0 || (Extent != dynamic_extent && Count > Extent), the program is ill-formed.

    -1- Requires: 0 <= Count && Count <= size().

    -2- Effects: Equivalent to: return {data(), Count};

    template<ptrdiff_t Count> constexpr span<element_type, Count> last() const;
    

    -?- Remarks: If Count < 0 || (Extent != dynamic_extent && Count > Extent), the program is ill-formed.

    -3- Requires: 0 <= Count && Count <= size().

    -4- Effects: Equivalent to: 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:

    • Offset < 0 || (Count < 0 && Count != dynamic_extent), or

    • Extend != dynamic_extent && (Offset > Extent || (Count != dynamic_extent && Offset + Count > Extent)).

    -5- Requires: (0 <= Offset && Offset <= size()) && (Count == dynamic_extent || Count >= 0 && Offset + Count <= size()).

    -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 returned span type is:

    Count != dynamic_extent ? Count
                            : (Extent != dynamic_extent ? Extent - Offset
                                                        : dynamic_extent)
    
Date: 2018-04-24.00:00:00

[ 2018-04-24 Priority set to 3 after discussion on the reflector. ]

Date: 2018-04-23.17:43:29

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.

Furthermore, with the current specification of the 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.

Firstly, for either run time sized (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:

  1. s.first<C>() with C < 0

  2. s.last<C>() with C < 0

  3. 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.

In addition the following constructs should be made ill-formed for fixed size span s of type span<T, N> (with N > 0):

  1. s.first<C>() with C > N

  2. s.last<C>() with C > N

  3. s.subspan<O, dynamic_extent>() with O > N

  4. 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.

History
Date User Action Args
2021-02-25 10:48:01adminsetstatus: wp -> c++20
2019-11-19 14:48:30adminsetstatus: voting -> wp
2019-10-07 02:48:00adminsetstatus: ready -> voting
2019-07-22 17:12:46adminsetmessages: + msg10489
2019-07-22 17:12:46adminsetstatus: open -> ready
2019-06-23 17:55:19adminsetmessages: + msg10464
2018-11-14 18:19:04adminsetmessages: + msg10223
2018-11-12 05:21:03adminsetmessages: + msg10219
2018-11-12 05:21:03adminsetstatus: new -> open
2018-05-05 12:47:02adminsetmessages: + msg9836
2018-04-22 15:46:08adminsetmessages: + msg9825
2018-04-13 00:00:00admincreate