Title
Some converting constructors of `layout_left_padded::mapping` and `layout_right_padded::mapping` are missing a representability precondition for the padded stride
Status
new
Section
[mdspan.layout.leftpad.cons][mdspan.layout.rightpad.cons]
Submitter
Xi Chen

Created on 2026-03-18.00:00:00 last changed 1 week ago

Messages

Date: 2026-03-21.13:39:04

Proposed resolution:

This wording is relative to N5032.

  1. Modify [mdspan.layout.leftpad.cons] as indicated:

    template<class OtherExtents>
      constexpr explicit(see below)
        mapping(const layout_stride::mapping<OtherExtents>& other);
    

    -10- Constraints: […]

    -11- Preconditions:

    • (11.1) — […]

    • (11.2) — […]

    • (11.3) — […]

    • (11.4) — `other.required_span_size()` is representable as a value of type index_type.

    • (11.5) — If rank_ is greater than `1` and static-padding-stride equals `dynamic_extent`, `other.stride(1)` is representable as a value of type index_type.

    -12- Effects: […]

    -13- Remarks: […]

    template<class LayoutLeftPaddedMapping>
      constexpr explicit(see below)
        mapping(const LayoutLeftPaddedMapping& other);
    

    -14- Constraints: […]

    -15- Mandates: […]

    -16- Preconditions:

    • (16.1) — […]

    • (16.2) — `other.required_span_size()` is representable as a value of type index_type.

    • (16.3) — If rank_ is greater than `1` and static-padding-stride equals `dynamic_extent`, `other.stride(1)` is representable as a value of type index_type.

    -17- Effects: […]

    -18- Remarks: […]

  2. Modify [mdspan.layout.rightpad.cons] as indicated:

    template<class OtherExtents>
      constexpr explicit(see below)
        mapping(const layout_stride::mapping<OtherExtents>& other);
    

    -10- Constraints: […]

    -11- Preconditions:

    • (11.1) — […]

    • (11.2) — […]

    • (11.3) — […]

    • (11.4) — `other.required_span_size()` is representable as a value of type index_type.

    • (11.5) — If rank_ is greater than `1` and static-padding-stride equals `dynamic_extent`, other.stride(rank_ - 2) is representable as a value of type index_type.

    -12- Effects: […]

    -13- Remarks: […]

    template<class LayoutRightPaddedMapping>
      constexpr explicit(see below)
        mapping(const LayoutRightPaddedMapping& other);
    

    -14- Constraints: […]

    -15- Mandates: […]

    -16- Preconditions:

    • (16.1) — […]

    • (16.2) — `other.required_span_size()` is representable as a value of type index_type.

    • (16.3) — If rank_ is greater than `1` and static-padding-stride equals `dynamic_extent`, other.stride(rank_ - 2) is representable as a value of type index_type.

    -17- Effects: […]

    -18- Remarks: […]

Date: 2026-03-18.00:00:00

It appears that these constructors are missing a precondition requiring the incoming padded stride to be representable as a value of the destination `index_type`.

The existing preconditions check that `other.required_span_size()` is representable as a value of type `index_type`, but that is not sufficient when the source mapping is empty. In that case, `other.required_span_size()` can be `0` even though the source padded stride is too large for the destination `index_type`. The padded stride is then silently truncated, and that truncation is observable through `stride()`.

Example:

#include <mdspan>
#include <cstdint>
#include <cassert>

void test() {
  using Src = std::layout_left_padded<std::dynamic_extent>::mapping<std::extents<uint32_t, 1, 0>>;
  using Dst = std::layout_left_padded<std::dynamic_extent>::mapping<std::extents<uint8_t, 1, 0>>;

  // src.stride(1) == 256.
  Src src(std::extents<uint32_t, 1, 0>(), 256);

  // other.required_span_size() is 0, so the existing preconditions are met.
  // The stored padded stride is then converted to uint8_t and truncated to 0.
  Dst dst(src);

  assert(dst.stride(1) == 0);
}
History
Date User Action Args
2026-03-21 13:39:04adminsetmessages: + msg16032
2026-03-18 00:00:00admincreate