]> git.ipfire.org Git - thirdparty/gcc.git/commit
libstdc++: Avoid using std::__to_address with iterators
authorJonathan Wakely <jwakely@redhat.com>
Fri, 18 Oct 2024 11:11:10 +0000 (12:11 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Tue, 22 Oct 2024 16:08:32 +0000 (17:08 +0100)
commit85e5b80ee2de80024b736e864e50df136d801402
tree0ef0cc9afcfe6e690a5ad044fada5dcc89b3faf1
parentbf11ecbb02b517dff0034f02adacf9269a11a095
libstdc++: Avoid using std::__to_address with iterators

In r12-3935-g82626be2d633a9 I added the partial specialization
std::pointer_traits<__normal_iterator<It, Cont>> so that __to_address
would work with __normal_iterator objects. Soon after that, François
replaced it in r12-6004-g807ad4bc854cae with an overload of __to_address
that served the same purpose, but was less complicated and less wrong.

I now think that both commits were mistakes, and that instead of adding
hacks to make __normal_iterator work with __to_address, we should not be
using __to_address with iterators at all before C++20.

The pre-C++20 std::__to_address function should only be used with
pointer-like types, specifically allocator_traits<A>::pointer types.
Those pointer-like types are guaranteed to be contiguous iterators, so
that getting a raw memory address from them is OK.

For arbitrary iterators, even random access iterators, we don't know
that it's safe to lower the iterator to a pointer e.g. for std::deque
iterators it's not, because (it + n) == (std::to_address(it) + n) only
holds within the same block of the deque's storage.

For C++20, std::to_address does work correctly for contiguous iterators,
including __normal_iterator, and __to_address just calls std::to_address
so also works. But we have to be sure we have an iterator that satisfies
the std::contiguous_iterator concept for it to be safe, and we can't
check that before C++20.

So for pre-C++20 code the correct way to handle iterators that might be
pointers or might be __normal_iterator is to call __niter_base, and if
necessary use is_pointer to check whether __niter_base returned a real
pointer.

We currently have some uses of std::__to_address with iterators where
we've checked that they're either pointers, or __normal_iterator
wrappers around pointers, or satisfy std::contiguous_iterator. But this
seems a little fragile, and it would be better to just use
std::__niter_base for the pointers and __normal_iterator cases, and use
C++20 std::to_address when the C++20 std::contiguous_iterator concept is
satisfied. This patch does that.

libstdc++-v3/ChangeLog:

* include/bits/basic_string.h (basic_string::assign): Replace
use of __to_address with __niter_base or std::to_address as
appropriate.
* include/bits/ptr_traits.h (__to_address): Add comment.
* include/bits/shared_ptr_base.h (__shared_ptr): Qualify calls
to __to_address.
* include/bits/stl_algo.h (find): Replace use of __to_address
with __niter_base or std::to_address as appropriate. Only use
either of them when the range is not empty.
* include/bits/stl_iterator.h (__to_address): Remove overload
for __normal_iterator.
* include/debug/safe_iterator.h (__to_address): Remove overload
for _Safe_iterator.
* include/std/ranges (views::counted): Replace use of
__to_address with std::to_address.
* testsuite/24_iterators/normal_iterator/to_address.cc: Removed.
libstdc++-v3/include/bits/basic_string.h
libstdc++-v3/include/bits/ptr_traits.h
libstdc++-v3/include/bits/shared_ptr_base.h
libstdc++-v3/include/bits/stl_algo.h
libstdc++-v3/include/bits/stl_iterator.h
libstdc++-v3/include/debug/safe_iterator.h
libstdc++-v3/include/std/ranges
libstdc++-v3/testsuite/24_iterators/normal_iterator/to_address.cc [deleted file]