libstdc++: potential bug in specification of layouts.

Luc Grosheintz luc.grosheintz@gmail.com
Fri Jun 20 17:04:49 GMT 2025


On 5/13/25 12:53, Jonathan Wakely wrote:
> On Tue, 13 May 2025 at 10:54, Luc Grosheintz wrote:
>> 1. operator==
>>
>> The observation is that for rank0 and rank1, two mappings, one of type
>> layout_left<Extents> and the other layout_right<OExtents>, the
>> operator== is ambiguous if `Extents == OExtents`; but not if the extents
>> are convertible (because the convertability is only in one direction).
>>
>> Godbolt: https://godbolt.org/z/35TbMnc1e
>>
>> Resolution: It's not clear to me what to propose. Likely, something that
>> fixes the ambiguity issue to allow comparing all rank 0 and rank 1
>> instances of layout_left and layout_right. Help would be most welcome:
>> either by collaboratively working on the proposed change; or if someone
>> wants to take over completely that would also work.
> 
> N.B. it's not necessary to have a resolution when reporting an issue.
> Generally issues will get resolved sooner if there's a proposed
> resolution (even if it's a bad one, because that might nerdsnipe
> somebody into writing a better one!
> https://meta.wikimedia.org/wiki/Cunningham%27s_Law applies here).

In that spirit, I'll make a start.

First, recall the current spec for layout_left and layout_right:

```
template<class OtherExtents>
   friend constexpr bool
   operator==(const mapping& x, const mapping<OtherExtents>& y)
   noexcept;

Constraints: extents_type::rank() == OtherExtents::rank() is
true. Effects: Equivalent to: return x.extents() == y.extents();
```
https://eel.is/c++draft/mdspan.layout#lib:operator==,layout_left::mapping


I'd like to propose that both layout_left and layout_right
replace their description of operator== with the text provided
for layout_stride:

```
template<class OtherMapping>
   friend constexpr bool
   operator==(const mapping& x, const OtherMapping& y) noexcept;

Constraints:
— layout-mapping-alike <OtherMapping> is satisfied.
— rank_ == OtherMapping::extents_type::rank() is true.
— OtherMapping::is_always_strided() is true.

Preconditions: OtherMapping meets the layout mapping requirements
(23.7.3.4.3).

Returns: true if x.extents() == y.extents() is true, OFFSET (y)
== 0 is true, and each of x.stride(r) == y.stride(r) is true for
r in the range [0, x.extents().rank()). Otherwise, false.
```
https://eel.is/c++draft/mdspan.layout#lib:operator==,layout_stride::mapping


Clarifications:

   * Any implementation should still be free to skip checking the
     strides, when appropriate, e.g. if `x` and `y` are two
     mappings of the same layout.

   * Similarly OFFSET(y) == 0 can be skipped if `y` is one of the
     standardized mappings (because OFFSET(y) is always 0 for any
     of the mappings described in the standard. (We already
     implement __offset with this optimization.)

   * Therefore, implementations should still be able to make use
     of the same shortcuts described in the current standard.

The advantages:

   * When comparing layout_left and layout_right, a copy must be
     created under the current spec. This proposal only needs a
     const reference to the other object.

   * The conditions of when `m1 == m2` is valid become simpler. As
     long as both `m1` and `m2` have the same rank and look like
     mappings, they can be compared. Convertibility doesn't enter
     the picture.

   * The semantics of operator== can be described as: Two mapping
     objects `m1` and `m2` are equal if and only if they can't be
     distinguished by calling any of the methods (with the same
     arguments) described by the layout mapping policy.

     This is the same notion of equal as before. The difference is
     that now it also applies to pairs of mappings that couldn't
     be compared previously, e.g.

       layout_left::mapping<extents<int, 3, 5>>
       layout_right::mapping<extents<int, 3, 5>>

     can't be compared with the current standard. With this
     proposal they can be compared and are considered not equal.

   * Comparing a user-defined layout with layout_stride is
     possible, but comparing to layout_{left,right} isn't
     possible. This proposal removes this difference.

The impact:

   * Many more pairs of `m1 == m2` are valid. In some sense, this
     proposal only makes more programs valid and doesn't change
     the meaning of `m1 == m2`. However, code that uses concepts
     to check if two objects are comparable will change behaviour.

I've implemented the change locally and the mappings, as best I
can tell, behave exactly as described above.

Questions:
   1. Is making a change of this size at all possible?
   2. What's wrong with this idea?



More information about the Libstdc++ mailing list