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