Bug 100639 - reverse_iterator<I>::reference erroneously uses iterator_traits<I>::reference instead of iter_reference_t<I>
Summary: reverse_iterator<I>::reference erroneously uses iterator_traits<I>::reference...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 10.0
: P3 normal
Target Milestone: 10.4
Assignee: Patrick Palka
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-05-17 16:50 UTC by Barry Revzin
Modified: 2021-06-10 18:50 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2021-05-17 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Barry Revzin 2021-05-17 16:50:08 UTC
Short example (from https://stackoverflow.com/q/67573305/2069064):

#include <ranges>

template <typename T> using iota = std::ranges::iota_view<T, T>;
template <typename T> using iota_iter = std::ranges::iterator_t<iota<T>>;

static_assert(std::same_as<
    std::reverse_iterator<iota_iter<int64_t>>::reference,
    int64_t>);

This assertion fails when compiling with -std=c++20 (because the reference type is 'void') but passes with -std=gnu++20. The direct reason is that the difference_type of the iota_view iterator is __int128, which is considered a signed_integral with gnu++20 but not c++20.

But the reason this matters is because std::reverse_iterator<I>::reference is defined as std::iterator_traits<I>::reference (which checks that I satsifies cpp17-input-iterator which has the signed_integral constraint) instead of being defined as std::iter_reference_t<I> (which has no such check). With the latter implementation, the assertion above would pass on either version. 

The result is that reversing an iota_view<int64_t, int64_t> isn't a range on -std=c++20.
Comment 1 Patrick Palka 2021-05-18 19:42:59 UTC
The assertion also fails when __int128 isn't available (which can be simulated by compiling with -U__SIZEOF_INT128__), because in that case iota_view's difference_type is the integer-like class type __max_diff_type, and integer-like class types aren't integral even in GNU mode.
Comment 2 GCC Commits 2021-05-20 18:11:03 UTC
The master branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

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

commit r12-946-gd5cbe0f0d4b7bc11f80b2236521f90ec94e95767
Author: Patrick Palka <ppalka@redhat.com>
Date:   Thu May 20 14:08:17 2021 -0400

    libstdc++: Implement missing P0896R4 changes to reverse_iterator [PR100639]
    
    This implements the P0896R4 changes to reverse_iterator's member types
    value_type, difference_type and reference in C++20 mode, which fixes
    taking the reverse_iterator of an iterator with a non-integral
    difference_type (such as iota_view<long long>).
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/100639
            * include/bits/stl_iterator.h (reverse_iterator::difference_type):
            In C++20 mode, define in terms of iter_difference_t as per P0896R4.
            (reverse_iterator::reference): Likewise, but with iter_reference_t.
            (reverse_iterator::value_type): Likewise, but with iter_value_t.
            * testsuite/std/ranges/adaptors/reverse.cc (test08): New test.
            * testsuite/24_iterators/reverse_iterator/100639.cc: New test.
Comment 3 GCC Commits 2021-06-10 18:43:08 UTC
The releases/gcc-11 branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

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

commit r11-8540-ga50cc70f1b5ce3ae11528095fa3a1feadb206357
Author: Patrick Palka <ppalka@redhat.com>
Date:   Thu May 20 14:08:17 2021 -0400

    libstdc++: Implement missing P0896R4 changes to reverse_iterator [PR100639]
    
    This implements the P0896R4 changes to reverse_iterator's member types
    value_type, difference_type and reference in C++20 mode, which fixes
    taking the reverse_iterator of an iterator with a non-integral
    difference_type (such as iota_view<long long>).
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/100639
            * include/bits/stl_iterator.h (reverse_iterator::difference_type):
            In C++20 mode, define in terms of iter_difference_t as per P0896R4.
            (reverse_iterator::reference): Likewise, but with iter_reference_t.
            (reverse_iterator::value_type): Likewise, but with iter_value_t.
            * testsuite/std/ranges/adaptors/reverse.cc (test08): New test.
            * testsuite/24_iterators/reverse_iterator/100639.cc: New test.
    
    (cherry picked from commit d5cbe0f0d4b7bc11f80b2236521f90ec94e95767)
Comment 4 GCC Commits 2021-06-10 18:49:53 UTC
The releases/gcc-10 branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

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

commit r10-9902-gc8bd39bc10eac3028dbf509b27457172360e6e8c
Author: Patrick Palka <ppalka@redhat.com>
Date:   Thu May 20 14:08:17 2021 -0400

    libstdc++: Implement missing P0896R4 changes to reverse_iterator [PR100639]
    
    This implements the P0896R4 changes to reverse_iterator's member types
    value_type, difference_type and reference in C++20 mode, which fixes
    taking the reverse_iterator of an iterator with a non-integral
    difference_type (such as iota_view<long long>).
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/100639
            * include/bits/stl_iterator.h (reverse_iterator::difference_type):
            In C++20 mode, define in terms of iter_difference_t as per P0896R4.
            (reverse_iterator::reference): Likewise, but with iter_reference_t.
            (reverse_iterator::value_type): Likewise, but with iter_value_t.
            * testsuite/std/ranges/adaptors/reverse.cc (test08): New test.
            * testsuite/24_iterators/reverse_iterator/100639.cc: New test.
    
    (cherry picked from commit d5cbe0f0d4b7bc11f80b2236521f90ec94e95767)
Comment 5 Patrick Palka 2021-06-10 18:50:48 UTC
Fixed for 10.4/11.2/12