<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/141499>141499</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            libc++: Several related issues in std::layout_* with empty extents.
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            libc++
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          1uc
      </td>
    </tr>
</table>

<pre>
    Summary: The layouts in <mdspan> suffer from several issues when used with empty extents.

Context: There's a mandate that requires that required_span_size is representable [[0]]; and if the mandate doesn't apply the same condition is asserted as a precondition in every ctor, e.g. [[1]]. For non-empty extents this effectively prevents overflow when computing `stride` or `required_span_size`. However, if one of the extents is `0` either statically or dynamically, the mandate doesn't protect from overflow.

[0]: https://eel.is/c++draft/views.multidim#mdspan.layout.right.overview-4
[1]: https://eel.is/c++draft/views.multidim#mdspan.layout.right.cons-1

Godbolt: https://godbolt.org/z/oqaMnYT74

Example 1:
```
template<typename Int>
constexpr bool
late_static_zero_overflow()
{
    constexpr auto n = std::numeric_limits<Int>::max();
    // Error: extents size not prepresentable as index_type.
    constexpr auto m = std::layout_right::mapping(std::extents<Int, n, n, 0>{});
    return true;
}
static_assert(late_static_zero_overflow<uint16_t>());
```
I believe that this is valid code as it doesn't violate any mandate or precondition, yet it's rejected. (This example doesn't exhibit any overflow.)

Example 2:
```
template<typename Int>
constexpr bool
late_dynamic_zero_overflow()
{
    constexpr auto n = std::numeric_limits<Int>::max();
    constexpr auto m = std::layout_right::mapping(std::extents<Int, n, n, dyn>{0});
    // Error: causes overflow
    static_assert(m.required_span_size() == 0);
    return true;
}
static_assert(late_dynamic_zero_overflow<uint16_t>());
```
This causes an overflow because as an intermediate value it computes `n * n`, due to integer promotion rules, this is computed as `int(n) * int(n)` which (assuming `int` is a 32-bit integer) causes a signed overflow, i.e. UB. (Later it would multiply by `0`, but that happens too late.)

Example 3:
```
template<typename Int>
constexpr bool
stride_overflow()
{
 constexpr auto n = std::numeric_limits<Int>::max();
    constexpr auto m = std::layout_right::mapping(std::extents<Int, 0, n, n, n>{});
 static_assert(m.required_span_size() == 0);

    // Error: causes overflow
    static_assert(m.stride(1) != 0);
    return true;
}
static_assert(stride_overflow<uint16_t>());
```
This computes `n * n` as `int(n) * int(n)` which causes a signed overflow which is UB. The relevant section in the standard seem to be:
https://eel.is/c++draft/mdspan.layout#reqmts-18
https://eel.is/c++draft/views.multidim#mdspan.layout.left.obs-7
https://eel.is/c++draft/views.multidim#mdspan.extents.expo-6

Note that the layout requirements define the strides at any number such that `m(i...) = (s[i]*i + ... + 0)` . For and empty `std::extents`, this is satisfied trivially, i.e. any value of `s[i]` satisfied the condition. The definition of `layout_left::mapping::stride` requires that it's a forward product. Note that the exposition helper itself returns `size_t` not `index_type` suggesting that the forward product is computed in `size_t` (which is indeed one solution to avoid the UB, naturally the overflow still occurs).
 
Example 4:
```
template<typename Int>
constexpr bool
layout_stride_default_ctor()
{
    constexpr auto n = std::numeric_limits<Int>::max();
    // Error: not initialized by a constant, due to overflow while
    // computing _M_strides.
    constexpr auto m = std::layout_stride::mapping<std::extents<Int, 0, n, n, n>>{};
 return true;
}
static_assert(layout_stride_default_ctor<uint16_t>());
```
Here an overflow occurs while computing the strides as part of the default ctor.

I've only looked at C++23 code and not checked `std::layout_padded_{left,right}`.
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJzMWEtv4zgS_jX0pRBBpvw8-GA77Z0GdvYyPYc9GZRYtrhLkWqScuL-9Ysi6dhOp7HpyexggSCGHqzn99VDwnt1NIgrNt2w6eNIDKG1bjUemlFt5Xn129B1wp1ZtYYvLYIWZzsED8oAq7ad9L0wrPoEfjgc0MHB2Q48ntAJDcr7AT08tWhg8CjhSYUWsOvDGfA5oAm-YOWaleutNQGfQ1bikPG5BwGdMFIEhNCKAA6_Dsqhv7uSezJg79U3BOXBYe_Qowmi1gjRo03Jpo_0V21AGAnqAKHFF9HSojeMzwOIvtfn-MyLDqGxRqqgrCG5wnt0ASUIMqt3ePPUALl7hiZYx_gWsDgWWfU4qS5gZx0Yax7ufIfQKg94OGAT1An1mQSf4hN7QnfQ9inFrrFdPwRljsBmpQ9OSWSzEqyj6-8DwWZlAb_YJzKLDFIHsAbBJscvypWn0yUJQhVadOCDCKoRWp9JtDwb0aVLEvJ2zHpnAzYhpf1idM7pJfTVGtoQes-qNeM7xneIulCe8V3D-IbxjXTiEBjfnRQ--aIbdFBSdYxXCV1Fglzh1LENBSmhFx8mScX4z1fRWOMfxsmJv1lZWx2-13BMDwrrjozvvjG-s1_Fr-afX-aTdPLTs-h6jTCmI-Wazcr8V64Ddr0WAVm1DeceDcHtswms-sTKNWkP-Nw7qK3VrFzTm_uUm_03dHZ_iTPjC8aXJHu-YeUaAOB6WAzBAnH0EXyQZEO1NkOHTjV7rToVPKu2WWl82InnLLC6SEuewifnrKMIXKAT2WYspf-Ob4LKgsTnPXlVvG1Sd29SCvw-Bv5iR98rc2R88fJS1psN5lswL_9Ksn--YfPHO8sdhsEZCG7AdJNeKNc5ionNjC9-HNpqOygTxrN9DFCKy0XBbSo_Q41a4SnXqMho5eEktJLQWJmCEm4oc1KW1IIw5xdCWXdXU8ixMwZQIdZBh__CJqAsgPHFl1gzMrauUvG5VbUKUeqVhwkdVyzyPwWLuTL8xWD83-JInk1CUvkaSq9J0IjB47VC57deI6sr3qjL0SOym0wv_zBg347_exEbAZSdEObaaWqMN2OHo64W0HUoFcHzJPSAhOLUiDB2DgOMr8GQUIrfgBBsPHZEQrPtbGyPbtDoUwNJ1MgyYitls1JRJhYmBoav4XpJjempVU1LqBfeD11ugPTKrIxdGSr-QKjPaknIxTOIU4285okaYYEF_L6JPPq7COjIpyc7aAmxJVD_r8-Xtkgn6iEkYrei79F4CNYCJeF7blUf5VZq7D8k1P8_m8p7Spk3SvNHaPJBPua5iS_GCWvjP0zC14n6Oea9SaH3k-FH-M6PlY8Ap0ndocaTMAE8zZdpUo3TbaCu4yR4xI5IW2OC7vtGqLuJifHK4dcu-Ifx4t0S_ssQpvEQClv7h_mHJV6WDHzu7cMsYegfNrw068s6c9kmujjeSDwogzlWlGoPInVWM3Q1DcpD0yYRbFZ2jC9UURQZs1RbPJtuFM2lfK2A8Q0URRF_y5zKtBDQNpIWgjjWv6JVKkCXqulFUP6gUEJw6qQuU3msaGRZKtH2EEVd1M_K23PtzU6TEBIdTVtMOpl5Txm4p328uG4e96tYHlMEHKx7ImD1zsqhCQXcx5qy4JO6FnUfy69Hfci8iwygArCP9Z3my0iJy0AZ3RmOR_RxFXoR-0rrXY-hLfVWKOOLF56QZCKQQfBWD9GuYEGcrErR-n0Ty5gIg4tLEd17oZsPSmuwTTM4z_gyTrs33WDy8UkrpiLXGokHMeiwTzvmXzz2UyYiToRW31BSixRJpUiFPzf_21Kk8V7YdYXd_5qd8j-1IWTw3aNy-3PN6KUfJVffPXP9MBPvrfy_oMO7YSvhJgXqJjZ3JcdDL1y4rO1Zb_zKkBfsz4zPTwjW6DNoa_9NM1WAbSqNvMrrh5ExgU2LDb1xW2qyZ72QEuWezTeR-Hyb-v6cCkgxkqtKLqulGOFqPJ8s-LxcTmejdlWP69l0zqUoOV-UJcq6HNeTCa8Xc47TWo7Uipd8Wk75bDyZLMezQiyny3o-P5TT2WReLxs2KbETShdanzpapEfxg9FqPBlPlsuRFjVqHz9Lca5VnWs-45xNH0duRace6uHo2aTUygd_lRNU0Li6OVOt4bf8UcohkVBePk4p8x3UqPW-8aVqNDi9evURQIV2qIvGdozvSHv-eeidpZ2N8V1Sw_gue3Va8f8EAAD__2QIQcw">