This is an archive of the discontinued LLVM Phabricator instance.

[libc++] Remove operator-> from iterator archetypes that don't need it
ClosedPublic

Authored by ldionne on Jan 27 2022, 11:57 AM.

Details

Summary

operator-> is not a requirement for most iterators, so remove it. To
account for this change, the common_iterator.operator-> test needs to
be refactored quite a bit -- improve test coverage while we're at it.

Diff Detail

Event Timeline

ldionne requested review of this revision.Jan 27 2022, 11:57 AM
ldionne created this revision.
Herald added a project: Restricted Project. · View Herald TranscriptJan 27 2022, 11:57 AM
Herald added a reviewer: Restricted Project. · View Herald Transcript
Quuxplusone requested changes to this revision.Jan 27 2022, 12:57 PM
Quuxplusone added a subscriber: Quuxplusone.
Quuxplusone added inline comments.
libcxx/test/support/test_iterators.h
255–266

(A) please don't reopen namespace std, but also (B) please don't specialize pointer_traits.
Is your goal just to make contiguous_iterator a contiguous iterator while (for some reason) removing its operator->? You can do that by giving contiguous_iterator a member using element_type = typename pointer_traits<It>::element_type;.
https://stackoverflow.com/questions/65712091/in-c20-how-do-i-write-a-contiguous-iterator

This revision now requires changes to proceed.Jan 27 2022, 12:57 PM
ldionne added inline comments.Jan 28 2022, 11:40 AM
libcxx/test/support/test_iterators.h
255–266

(A) please don't reopen namespace std, but also (B) please don't specialize pointer_traits.

Can you please explain why that's bad? That seems gratuitous -- pointer_traits is explicitly made to be customized, no? Arguably, if I could remove all that complexity and only have element_type, that would indeed better, but I don't see why specializing pointer_traits would be bad in itself. Edit: Nope, that doesn't work, I have to keep operator->().

Is your goal just to make contiguous_iterator a contiguous iterator while (for some reason) removing its operator->?

Yes, that's my goal. The reason is to make congituous_iterator more minimal, just like the other changes we did to e.g. cpp17_input_iterator to remove default-constructibility. Because it makes it more useful for testing.

ldionne updated this revision to Diff 404131.Jan 28 2022, 12:21 PM

Upload the patch correctly.

I don't agree with this direction, but I think the only hill I'll particularly die on is "don't reopen namespace std."

libcxx/test/std/iterators/predef.iterators/iterators.common/arrow.pass.cpp
28–38

Consider

{
  Common common(iter);
  std::same_as<IteratorWithArrow> auto r1 = static_cast<Common&>(common).operator->();
  std::same_as<IteratorWithArrow> auto r2 = static_cast<Common&&>(common).operator->();
  std::same_as<IteratorWithArrow> auto r3 = static_cast<const Common&>(common).operator->();
  std::same_as<IteratorWithArrow> auto r4 = static_cast<const Common&&>(common).operator->();
  assert(base(r1) == buffer);
}

just to hit the move cases as well. (If IteratorWithArrow were move-only, would there be no operator->? ....waitaminute. You have an infinite loop in your operator->, don't you? IteratorWithArrow::operator->() can't return another IteratorWithArrow! It should be returning int*.)

(And I think I just remembered that common_iterator doesn't work with move-only iterator types, right? because it's meant as an adaptor to produce a C++17 iterator, which must be copyable by definition?)

libcxx/test/std/iterators/predef.iterators/iterators.common/iterator_traits.compile.pass.cpp
94

The code on the left strikes me as weird. But whatever, I'm getting deja vu to a conversation with @CaseyCarter where he said nobody cares about the value of ::pointer. :)

libcxx/test/support/test_iterators.h
255–266

(A) please don't reopen namespace std

https://quuxplusone.github.io/blog/2021/10/27/dont-reopen-namespace-std/

(B) please don't specialize pointer_traits

Because user iterators won't specialize pointer_traits. I suppose there is a tension here between "Keep test code simple; eliminate metaprogramming; make test code realistic" and "Make test code that stress-tests the corner cases." For example, we =delete the comma operator on all of these iterators (and should delete operator& too, probably) — that's more complicated than necessary, and isn't realistic, but it does stress-test the corner cases. So maybe "let's specialize pointer_traits for all these iterators to make sure all our algorithms respect the specialized pointer_traits" is a good suggestion on par with "Let's =delete the comma operator for all these iterators." Personally I don't think it meets the bar; it's a lot of complexity for what it gives us IMO, especially compared to the simplicity of
https://stackoverflow.com/questions/65712091/in-c20-how-do-i-write-a-contiguous-iterator

ldionne marked 4 inline comments as done.Mar 10 2022, 9:02 AM

I don't agree with this direction, but I think the only hill I'll particularly die on is "don't reopen namespace std."

Hopefully we can agree that this is an improvement if I keep operator-> as a way to define contiguous_iterator?

libcxx/test/std/iterators/predef.iterators/iterators.common/arrow.pass.cpp
28–38

Fixed by removing IteratorWithArrow altogether.

And I think I just remembered that common_iterator doesn't work with move-only iterator types, right?

Yes, that's right.

libcxx/test/support/test_iterators.h
255–266

Okay, I sat on this for a while, but I think I agree. In the next iteration I'll remove operator-> only from the archetypes that truly don't need it for anything, like cpp17_input_iterator & friends. I'll keep it on contiguous_iterator.

Herald added a project: Restricted Project. · View Herald TranscriptMar 10 2022, 9:02 AM
ldionne updated this revision to Diff 414398.Mar 10 2022, 9:03 AM
ldionne marked 2 inline comments as done.
ldionne retitled this revision from [libc++] Remove operator-> from the iterator archetypes to [libc++] Remove operator-> from iterator archetypes that don't need it.
ldionne edited the summary of this revision. (Show Details)

Rebase and address review comments.

LGTM % the check lambda stuff (which I won't insist on, anyway). Thanks!

libcxx/test/std/iterators/predef.iterators/iterators.common/arrow.pass.cpp
24–41

Nit: Why not good old-fashioned

template<class It>
void test_case_1() {
    // Case 1: http://eel.is/c++draft/iterators.common#common.iter.access-5.1
    ~~~
}

template<class It>
void test_case_2() {

etc? The whole check.operator()<T>(); thing works, but it doesn't look nice.

libcxx/test/std/iterators/predef.iterators/iterators.common/iterator_traits.compile.pass.cpp
94

For my information, did we ever figure out why the old code thought it'd be const Iter&?
("No" is an acceptable answer. ;))

ldionne marked 2 inline comments as done.Mar 10 2022, 12:52 PM
ldionne added inline comments.
libcxx/test/std/iterators/predef.iterators/iterators.common/arrow.pass.cpp
24–41

I actually applied this suggestion, and it resulted in the test case definition and the types it is being called on being further apart, which seemed to decrease legibility. So I'd be in favour of keeping the current approach (even though I agree check.operator()<T>() doesn't look pretty).

libcxx/test/std/iterators/predef.iterators/iterators.common/iterator_traits.compile.pass.cpp
94

Frankly, I did not investigate because int* is what I would have expected all along. However, thinking about it right now:

http://eel.is/c++draft/iterators.common#common.iter.access-5.1 says that we return return get<I>(v_­);, when the iterator has operator->, and that is Iter const& here. When we remove operator-> from the underlying iterator, we instead return addressof(*get<I>(v_)), and that is int*, as we're seeing in the diff after.

ldionne accepted this revision as: Restricted Project.Mar 10 2022, 12:52 PM
ldionne marked 2 inline comments as done.
This revision is now accepted and ready to land.Mar 10 2022, 12:52 PM