libstdc++: potential bug in specification of layouts.

Tomasz Kaminski tkaminsk@redhat.com
Mon Jun 23 07:43:36 GMT 2025


On Fri, Jun 20, 2025 at 7:04 PM Luc Grosheintz <luc.grosheintz@gmail.com>
wrote:

> 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 have suggested an alternative of providing a non-member operator ==:
I think that this could be fixed by changing the friend operator== in
layout_stride,
to generic overload in std:
template<typename StridedMapping1, typename StridedMapping2>
constexpr bool operator==(const StridedMapping1& lhs, const
StridedMapping2& rhs);
Constrains:
  ** **layout-mapping-alike*
<https://en.cppreference.com/w/cpp/named_req/LayoutMapping#layout-mapping-alike>
 <StridedMapping1> && *layout-mapping-alike*
<https://en.cppreference.com/w/cpp/named_req/LayoutMapping#layout-mapping-alike>
 <StridedMapping12>
  * StridedMapping1::extents_type::rank() ==
StridedMapping2::extents_type::rank()
  * StridedMapping1::is_always_strided() && StridedMapping2::
is_always_strided()
This would allow any strided mapping to be compared without conversions.
With the solution you are proposing, I am a bit concerned that some
comparison may become
ambiguous, for example layout_left/stride pair, both operator== in
layout_left and layout_stride
are candidates. I think the tie-breaker that favors non-reversed candidates
saves us here.
But, if we get a user-defined layout with conversion to both, I think this
will be truly ambiguous,
while the free-function I am suggesting, would be best-candidate.


>
>
>
> 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?
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20250623/aa3d5278/attachment-0001.htm>


More information about the Libstdc++ mailing list