Bug 121061 - extents/mdspan constructor is ILL-FORMED for IntegerLike with mutable conversions
Summary: extents/mdspan constructor is ILL-FORMED for IntegerLike with mutable convers...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 16.0
: P3 normal
Target Milestone: 16.0
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2025-07-14 06:40 UTC by Tomasz Kamiński
Modified: 2025-07-28 14:53 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2025-07-14 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Tomasz Kamiński 2025-07-14 06:40:11 UTC
For the following code:
```
template<bool Const>
class IntLike
{
public:
  explicit
  IntLike(int i)
  : _M_i(i)
  { }

  IntLike() = delete;
  IntLike(const IntLike&) = delete;
  IntLike(IntLike&&) = delete;

  const IntLike&
  operator=(const IntLike&) = delete;

  const IntLike&
  operator=(IntLike&&) = delete;

  constexpr
  operator int() const noexcept
    requires (Const)
  { return _M_i; }

  constexpr
  operator int() noexcept
    requires (!Const)
  { return _M_i; }

private:
  int _M_i;
};
```
The static assertion fails incorrectly: 
static_assert(!std::is_constructible_v<std::extents<int, std::dynamic_extent>, std::array<IntLike<false>, 1>>);.
It should be eliminated by https://eel.is/c++draft/mdspan.extents#cons-9.1.

We emit the ill-formed error from corresponding cosntructor:
std::array<IntLike<false>, 1> a1{IntLike<false>(1)};
std::extents<int, std::dynamic_extent> e1(a1);

See: https://godbolt.org/z/6GnqnascY
Comment 1 Tomasz Kamiński 2025-07-14 06:44:11 UTC
This is due incorrect constrain on corresponding constructor:
   template<__mdspan::__valid_index_type<index_type> _OIndexType, size_t _Nm>
        requires (_Nm == rank() || _Nm == rank_dynamic())
        constexpr explicit(_Nm != rank_dynamic())
        extents(span<_OIndexType, _Nm> __exts) noexcept
        : _M_exts(span<const _OIndexType, _Nm>(__exts))
        { }
We check `__mdspan::__valid_index_type<_OtherIndex, index_type>`, while we we should check `__mdspan::__valid_index_type<const _OtherIndex&, index_type>`, i.e.:
  template<typename _OIndexType, size_t _Nm>
        requires 
         __mdspan::__valid_index_type<const _OIndexType&, index_type>
         (_Nm == rank() || _Nm == rank_dynamic())
        constexpr explicit(_Nm != rank_dynamic())
        extents(span<_OIndexType, _Nm> __exts) noexcept
        : _M_exts(span<const _OIndexType, _Nm>(__exts))
        { }
Similary for the array constructor, and the constructors of mdspan.
Comment 2 Tomasz Kamiński 2025-07-14 06:52:39 UTC
Related to above, the following is ill-formed:
```
class IntLike
{
public:
  explicit
  IntLike(int i)
  : _M_i(i)
  { }


  constexpr
  operator int() && noexcept
  { return _M_i; }


private:
  int _M_i;
};


IntLike il(1);
static_assert(std::is_constructible_v<std::extents<int, std::dynamic_extent>, IntLike>);
std::extents<int, std::dynamic_extent> e1(std::move(il));
```
See: https://godbolt.org/z/hnd6zbde5

This is because, we do not move the index in constructor:
      template<__mdspan::__valid_index_type<index_type>... _OIndexTypes>
        requires (sizeof...(_OIndexTypes) == rank()
                  || sizeof...(_OIndexTypes) == rank_dynamic())
        constexpr explicit extents(_OIndexTypes... __exts) noexcept
        : _M_exts(span<const _IndexType, sizeof...(_OIndexTypes)>(
            initializer_list{_S_storage::_S_int_cast(__exts)...}))
        { }
`__exts` should be moved here, and _S_int_cast needs to be changed to be forwarding.

This affects also operators() and mdspan/extents constructors.
Comment 3 GCC Commits 2025-07-17 14:04:07 UTC
The master branch has been updated by Tomasz Kaminski <tkaminsk@gcc.gnu.org>:

https://gcc.gnu.org/g:df7beaccef31f19ee73f034eb98e0e47be008d8e

commit r16-2325-gdf7beaccef31f19ee73f034eb98e0e47be008d8e
Author: Luc Grosheintz <luc.grosheintz@gmail.com>
Date:   Wed Jul 16 15:45:43 2025 +0200

    libstdc++: Refactor mdspan tests [PR121061]
    
    PR121061 shows that the test coverage for custom integer types is
    insufficient. Custom IndexTypes are passed to mdspan related objects in
    one of two ways:
    
      * as a template parameter pack,
      * or as an array/span.
    
    These two cases have different requirements on the (constness of) custom
    IndexTypes. Therefore, the tests are restructured as follows:
    
      * allow testing with different custom integers,
      * separate code that tests the two cases described above,
      * use int_like.h for all tests with custom integers.
    
    The affected tests are for:
    
      * creating extents, layout_stride::mapping and mdspan from
      custom integers,
    
      * mapping::operator() and mdspan::operator[].
    
            PR libstdc++/121061
    
    libstdc++-v3/ChangeLog:
    
            * testsuite/23_containers/mdspan/extents/custom_integer.cc:
            Enable checking with different custom integers. Improve
            checking non-existence of overloads for incompatible custom
            integers.
            * testsuite/23_containers/mdspan/layouts/mapping.cc: ditto. Also
            improve reuse of int_like.h.
            * testsuite/23_containers/mdspan/layouts/stride.cc: ditto.
            * testsuite/23_containers/mdspan/mdspan.cc: ditto.
            * testsuite/23_containers/mdspan/extents/int_like.h: Rename (old
            name).
            * testsuite/23_containers/mdspan/int_like.h: Rename (new name).
            (ThrowingInt): Add.
            (NotIntLike): Add.
    
    Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
    Reviewed-by: Tomasz KamiÅski <tkaminsk@redhat.com>
    Signed-off-by: Luc Grosheintz <luc.grosheintz@gmail.com>
Comment 4 GCC Commits 2025-07-17 14:10:00 UTC
The master branch has been updated by Tomasz Kaminski <tkaminsk@gcc.gnu.org>:

https://gcc.gnu.org/g:1eee8430794f790b6d364603685e70d83d8d42f5

commit r16-2327-g1eee8430794f790b6d364603685e70d83d8d42f5
Author: Luc Grosheintz <luc.grosheintz@gmail.com>
Date:   Wed Jul 16 15:45:44 2025 +0200

    libstdc++: Fix constraint for custom integer types in mdspan [PR121061]
    
    PR121061 consists of two bugs for mdspan related code. This commit fixes
    the first one. Namely, when passing custom IndexType as an array or
    span, the conversion to int must be const. Prior to this commit the
    constraint incorrectly also allowed non-const conversion. This commit
    updates all related constraints to check
    
      __valid_index_type<const OtherIndexType&, index_type>
    
    in those cases. Also adds a MutatingInt to int_like.h which only
    supports non-const conversion to int and updates the tests.
    
            PR libstdc++/121061
    
    libstdc++-v3/ChangeLog:
    
            * include/std/mdspan (extents::extents): Fix constraint to
            prevent non-const conversion to index_type.
            (layout_stride::mapping::mapping): Ditto.
            (mdspan::mdspan): Ditto.
            (mdspan::operator[]): Ditto.
            * testsuite/23_containers/mdspan/extents/custom_integer.cc: Add
            test for MutatingInt.
            * testsuite/23_containers/mdspan/int_like.h (MutatingInt): Add.
            * testsuite/23_containers/mdspan/layouts/mapping.cc: Add test for
            MutatingInt.
            * testsuite/23_containers/mdspan/layouts/stride.cc: Ditto.
            * testsuite/23_containers/mdspan/mdspan.cc: Ditto.
    
    Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
    Reviewed-by: Tomasz KamiÅski <tkaminsk@redhat.com>
    Signed-off-by: Luc Grosheintz <luc.grosheintz@gmail.com>
Comment 5 GCC Commits 2025-07-17 14:13:43 UTC
The master branch has been updated by Tomasz Kaminski <tkaminsk@gcc.gnu.org>:

https://gcc.gnu.org/g:29d53f6213e0a1569aa8ca9db613b48df642986c

commit r16-2328-g29d53f6213e0a1569aa8ca9db613b48df642986c
Author: Luc Grosheintz <luc.grosheintz@gmail.com>
Date:   Wed Jul 16 15:45:45 2025 +0200

    libstdc++: Fix forwarding of custom IndexType in mdspan [PR121061]
    
    The second bug report in PR121061 is that the conversion of custom
    OtherIndexType to IndexType is incorrectly not done via r-value
    references.
    
    This commit fixes the forwarding issue, adds a custom IndexType called
    RValueInt, which only allows conversion to int via r-value reference.
    
            PR libstdc++/121061
    
    libstdc++-v3/ChangeLog:
    
            * include/std/mdspan (extents::extents): Perform conversion to
            index_type of an r-value reference.
            (layout_left::mapping::operator()): Ditto.
            (layout_right::mapping::operator()): Ditto.
            (layout_stride::mapping::operator()): Ditto.
            * testsuite/23_containers/mdspan/extents/custom_integer.cc: Add
            tests for RValueInt and MutatingInt.
            * testsuite/23_containers/mdspan/int_like.h (RValueInt): Add.
            * testsuite/23_containers/mdspan/layouts/mapping.cc: Test with
            RValueInt.
            * testsuite/23_containers/mdspan/mdspan.cc: Ditto.
    
    Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
    Reviewed-by: Tomasz KamiÅski <tkaminsk@redhat.com>
    Signed-off-by: Luc Grosheintz <luc.grosheintz@gmail.com>
Comment 6 Tomasz Kamiński 2025-07-28 14:53:04 UTC
Fixed in v16.