https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128475

            Bug ID: 128475
           Summary: `bits/iterator_concepts.h` line 880~922: inconsistent
                    uses of dereference operator. (Maybe a potential LWG
                    Defect)
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: libstdc++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: songb432 at gmail dot com
  Target Milestone: ---

Original code:
```
    struct _IterSwap
    {
    private:
      template<typename _Tp, typename _Up>
        static consteval bool
        _S_noexcept()
        {
          if constexpr (__adl_iswap<_Tp, _Up>)
            return noexcept(iter_swap(std::declval<_Tp>(),
                                      std::declval<_Up>()));
          else if constexpr (indirectly_readable<_Tp>
              && indirectly_readable<_Up>
              && swappable_with<iter_reference_t<_Tp>, iter_reference_t<_Up>>)
            return noexcept(ranges::swap(*std::declval<_Tp>(),
                                         *std::declval<_Up>()));
          else
            return noexcept(*std::declval<_Tp>()
                = __iswap::__iter_exchange_move(std::declval<_Up>(),
                                                std::declval<_Tp>()));
        }

    public:
      template<typename _Tp, typename _Up>
        requires __adl_iswap<_Tp, _Up>
        || (indirectly_readable<remove_reference_t<_Tp>>
            && indirectly_readable<remove_reference_t<_Up>>
            && swappable_with<iter_reference_t<_Tp>, iter_reference_t<_Up>>)
        || (indirectly_movable_storable<_Tp, _Up>
            && indirectly_movable_storable<_Up, _Tp>)
        constexpr void
        operator()(_Tp&& __e1, _Up&& __e2) const
        noexcept(_S_noexcept<_Tp, _Up>())
        {
          if constexpr (__adl_iswap<_Tp, _Up>)
            iter_swap(static_cast<_Tp&&>(__e1), static_cast<_Up&&>(__e2));
          else if constexpr (indirectly_readable<_Tp>
              && indirectly_readable<_Up>
              && swappable_with<iter_reference_t<_Tp>, iter_reference_t<_Up>>)
            ranges::swap(*__e1, *__e2);
          else
            *__e1 = __iswap::__iter_exchange_move(__e2, __e1);
        }
    };
```


Maybe the standard wording is wrong, `iter_swap` should consider lvalue
dereference at every place. Or maybe cppreference is wrong, ``reference type of
E`` is ``decltype(*E)``, not ``iter_reference_t<decltype((E))>``.


Suggested fix (if the standard wording is wrong):
```
      template<typename _Tp, typename _Up>
        static consteval bool
        _S_noexcept()
        {
          if constexpr (__adl_iswap<_Tp, _Up>)
            return noexcept(iter_swap(std::declval<_Tp>(),
                                      std::declval<_Up>()));
          else if constexpr (indirectly_readable<_Tp>
              && indirectly_readable<_Up>
              && swappable_with<iter_reference_t<_Tp>, iter_reference_t<_Up>>)
            return noexcept(ranges::swap(*std::declval<_Tp&>(),
                                         *std::declval<_Up&>()));
          else
            return noexcept(*std::declval<_Tp&>()
                = __iswap::__iter_exchange_move(std::declval<_Up&>(),
                                                std::declval<_Tp&>()));
        }
```


So that each `noexcept` in `_S_noexcept` are consistent with the cases in
`operator()`.



Or, if cppreference is wrong, ``reference types of E1 and E2`` are
``decltype(*E1)`` and ``decltype(*E2)``. Then the correct fix would be:
```
        static consteval bool
        _S_noexcept()
        {
          if constexpr (__adl_iswap<_Tp, _Up>)
            return noexcept(iter_swap(std::declval<_Tp>(),
                                      std::declval<_Up>()));
          else if constexpr (indirectly_readable<_Tp>
              && indirectly_readable<_Up>
              && swappable_with<decltype(*std::declval<_Tp>()),
                                decltype(*std::declval<_Up>())>)
            return noexcept(ranges::swap(*std::declval<_Tp>(),
                                         *std::declval<_Up>()));
          else
            return noexcept(*std::declval<_Tp>()
                = __iswap::__iter_exchange_move(std::declval<_Up>(),
                                                std::declval<_Tp>()));
        }
```

and

```
        constexpr void
        operator()(_Tp&& __e1, _Up&& __e2) const
        noexcept(_S_noexcept<_Tp, _Up>())
        {
          if constexpr (__adl_iswap<_Tp, _Up>)
            iter_swap(static_cast<_Tp&&>(__e1), static_cast<_Up&&>(__e2));
          else if constexpr (indirectly_readable<_Tp>
              && indirectly_readable<_Up>
              && swappable_with<decltype(*std::declval<_Tp>()),
                                decltype(*std::declval<_Up>())>)
            ranges::swap(*static_cast<_Tp&&>(__e1),
                         *static_cast<_Up&&>(__e2));
          else
            *static_cast<_Tp&&>(__e1)
                = __iswap::__iter_exchange_move(static_cast<_Up&&>(__e2),
                                                static_cast<_Tp&&>(__e1));
        }
```

Reply via email to