This is an archive of the discontinued LLVM Phabricator instance.

[libc++] NFCI: Restore code duplication in wrap_iter, with test.
ClosedPublic

Authored by Quuxplusone on Jul 13 2021, 8:07 AM.

Details

Summary
It turns out that D105040 broke `std::rel_ops`; we actually do need
both a one-template-parameter and a two-template-parameter version of
all the comparison operators, because if we have only the heterogeneous
two-parameter version, then `x > x` is ambiguous:

    template<class T, class U> int f(S<T>, S<U>) { return 1; }
    template<class T> int f(T, T) { return 2; }  // rel_ops
    S<int> s; f(s,s);  // ambiguous between #1 and #2

Adding the one-template-parameter version fixes the ambiguity:

    template<class T, class U> int f(S<T>, S<U>) { return 1; }
    template<class T> int f(T, T) { return 2; }  // rel_ops
    template<class T> int f(S<T>, S<T>) { return 3; }
    S<int> s; f(s,s);  // #3 beats both #1 and #2

Diff Detail

Event Timeline

bgraur requested review of this revision.Jul 13 2021, 8:07 AM
bgraur created this revision.
Herald added a project: Restricted Project. · View Herald Transcript
Herald added a reviewer: Restricted Project. · View Herald Transcript

The reverted commit introduced an error when std::rel_ops are used.
Here's a small reproducer:

#include <utility> // for std::rel_ops.
#include <vector>

using namespace std::rel_ops;

struct T;
void fn() {
  std::vector<T>::const_iterator a;
  for (std::vector<T>::const_iterator i; i >= a;)
    ;
}

The compilation result:

repro.cc:9:44: error: use of overloaded operator '>=' is ambiguous (with operand types 'std::vector<T>::const_iterator' (aka '__wrap_iter<const T *>') and 'std::vector<T>::const_iterator' (aka '__wrap_iter<const T *>'))
  for (std::vector<T>::const_iterator i; i >= a;)
                                         ~ ^  ~
/google/src/cloud/bgraur/cherrypick/google3/third_party/crosstool/v18/llvm_unstable/toolchain/bin/../include/c++/v1/__utility/rel_ops.h:56:1: note: candidate function [with _Tp = std::__wrap_iter<const T *>]
operator>=(const _Tp& __x, const _Tp& __y)
^
/google/src/cloud/bgraur/cherrypick/google3/third_party/crosstool/v18/llvm_unstable/toolchain/bin/../include/c++/v1/__iterator/wrap_iter.h:201:6: note: candidate function [with _Iter1 = const T *, _Iter2 = const T *]
bool operator>=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT
     ^
1 error generated.
ldionne commandeered this revision.Jul 13 2021, 12:45 PM
ldionne edited reviewers, added: bgraur; removed: ldionne.

Thanks a lot for the bug report.

I don't think the Standard requires this to work:

#include <utility> // for std::rel_ops.
#include <vector>

using namespace std::rel_ops;

struct T;
void fn() {
  std::vector<T>::const_iterator a;
  for (std::vector<T>::const_iterator i; i >= a;)
    ;
}

We're allowed to implement vector::iterator however we want as long as it's a LegacyRandomAccessIterator, which includes being able to compare it as x < y, etc. But there is no mention of those comparison operators being required to be injected only when they are used (i.e. implemented as hidden friends).

I'll still try to fix this and add a test (specific to libc++) since this used to work, but I would suggest you try to move away from using std::rel_ops, since it's pretty broken anyway.

ldionne updated this revision to Diff 358419.Jul 13 2021, 1:45 PM

Add test to trigger the std::rel_ops failure.

Arthur kindly suggested to me that he could handle finding the solution, and I will take on that offer so I can keep reviewing ranges stuff. Feel free to commandeer. Thanks!

Quuxplusone commandeered this revision.Jul 13 2021, 1:51 PM
Quuxplusone edited reviewers, added: ldionne; removed: Quuxplusone.
Quuxplusone retitled this revision from Revert "[libc++] NFCI: Remove code duplication and obsolete declarations in wrap_iter" to [libc++] NFCI: Restore code duplication in wrap_iter, with test..
Quuxplusone edited the summary of this revision. (Show Details)

Restore the homogeneous comparison operators. Expand @ldionne's test case a little further. This is ready to land, IMHO, assuming CI is green.

ldionne accepted this revision.Jul 14 2021, 9:38 AM

LGTM once CI passes. Thanks!

This revision is now accepted and ready to land.Jul 14 2021, 9:38 AM

Disable the new test when <filesystem> is missing.
Refactor a bit.
I was planning to add tests for reverse_iterator comparisons as well, but it turns out that nobody supports that (and neither do we).

jgorbe added a subscriber: jgorbe.Jul 14 2021, 1:15 PM

Rebase on main to re-trigger CI.

alexfh added a subscriber: alexfh.Jul 14 2021, 3:15 PM
This revision was landed with ongoing or failed builds.Jul 14 2021, 5:11 PM
This revision was automatically updated to reflect the committed changes.

@ldionne: Thanks for fixing that!