Skip to content

Commit

Permalink
[libc++] NFCI: Restore code duplication in wrap_iter, with test.
Browse files Browse the repository at this point in the history
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

We have the same problem with `reverse_iterator` as with `__wrap_iter`.
But so do libstdc++ and Microsoft, so we're not going to worry about it.

Differential Revision: https://reviews.llvm.org/D105894
  • Loading branch information
Quuxplusone authored and memfrob committed Oct 4, 2022
1 parent f352994 commit 0ddc58a
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 0 deletions.
46 changes: 46 additions & 0 deletions libcxx/include/__iterator/wrap_iter.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,31 @@ class __wrap_iter
template <class _Tp, size_t> friend class _LIBCPP_TEMPLATE_VIS span;
};

template <class _Iter1>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_IF_NODEBUG
bool operator==(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT
{
return __x.base() == __y.base();
}

template <class _Iter1, class _Iter2>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_IF_NODEBUG
bool operator==(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT
{
return __x.base() == __y.base();
}

template <class _Iter1>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_IF_NODEBUG
bool operator<(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT
{
#if _LIBCPP_DEBUG_LEVEL == 2
_LIBCPP_ASSERT(__get_const_db()->__less_than_comparable(&__x, &__y),
"Attempted to compare incomparable iterators");
#endif
return __x.base() < __y.base();
}

template <class _Iter1, class _Iter2>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_IF_NODEBUG
bool operator<(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT
Expand All @@ -182,27 +200,55 @@ bool operator<(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _
return __x.base() < __y.base();
}

template <class _Iter1>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_IF_NODEBUG
bool operator!=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT
{
return !(__x == __y);
}

template <class _Iter1, class _Iter2>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_IF_NODEBUG
bool operator!=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT
{
return !(__x == __y);
}

template <class _Iter1>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_IF_NODEBUG
bool operator>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT
{
return __y < __x;
}

template <class _Iter1, class _Iter2>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_IF_NODEBUG
bool operator>(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT
{
return __y < __x;
}

template <class _Iter1>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_IF_NODEBUG
bool operator>=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT
{
return !(__x < __y);
}

template <class _Iter1, class _Iter2>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_IF_NODEBUG
bool operator>=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT
{
return !(__x < __y);
}

template <class _Iter1>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_IF_NODEBUG
bool operator<=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter1>& __y) _NOEXCEPT
{
return !(__y < __x);
}

template <class _Iter1, class _Iter2>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_IF_NODEBUG
bool operator<=(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT
Expand Down
140 changes: 140 additions & 0 deletions libcxx/test/std/containers/iterator.rel_ops.compile.pass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: libcpp-has-no-filesystem-library

// Make sure the various containers' iterators are not broken by the use of `std::rel_ops`.

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

#include <array>
#include <deque>
#include <forward_list>
#include <list>
#include <map>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include "test_macros.h"

#if TEST_STD_VER >= 11
#include "filesystem_include.h"
#endif

#if TEST_STD_VER >= 17
#include <string_view>
#endif

#if TEST_STD_VER >= 20
#include <span>
#endif

using namespace std::rel_ops;

template<class It, class ConstIt>
void test_eq(It it, ConstIt cit) {
(void)(it == it);
(void)(it != it);
(void)(it == cit);
(void)(it != cit);
(void)(cit == it);
(void)(cit != it);
(void)(cit == cit);
(void)(cit != cit);
}

template<class It, class ConstIt>
void test_lt(It it, ConstIt cit) {
(void)(it < it);
(void)(it <= it);
(void)(it > it);
(void)(it >= it);
(void)(it < cit);
(void)(it <= cit);
(void)(it > cit);
(void)(it >= cit);
(void)(cit < it);
(void)(cit <= it);
(void)(cit > it);
(void)(cit >= it);
(void)(cit < cit);
(void)(cit <= cit);
(void)(cit > cit);
(void)(cit >= cit);

// Test subtraction too, even though std::rel_ops shouldn't affect it.

(void)(it - it);
(void)(it - cit);
(void)(cit - it);
(void)(cit - cit);
}

template<class Container>
void test_forward() {
// There is no need to distinguish "forward" from "bidirectional."
// libc++ already can't handle `c.rbegin() >= c.rbegin()` in the
// presence of std::rel_ops, and neither can Microsoft nor libstdc++.

Container c;
typename Container::iterator it = c.begin();
typename Container::const_iterator cit = c.begin();
test_eq(it, cit);
}

template<class Container>
void test_random_access() {
Container c;
typename Container::iterator it = c.begin();
typename Container::const_iterator cit = c.begin();
test_eq(it, cit);
test_lt(it, cit);
}

template void test_random_access<std::array<int, 10> >();
template void test_random_access<std::deque<int> >();
template void test_forward<std::forward_list<int> >();
template void test_forward<std::list<int> >();
template void test_forward<std::map<int, int> >();
template void test_forward<std::multimap<int, int> >();
template void test_forward<std::multiset<int> >();
template void test_forward<std::set<int> >();
template void test_random_access<std::string>();
template void test_forward<std::unordered_map<int, int> >();
template void test_forward<std::unordered_multimap<int, int> >();
template void test_forward<std::unordered_multiset<int> >();
template void test_forward<std::unordered_set<int> >();
template void test_random_access<std::vector<int> >();

#if TEST_STD_VER >= 11
void test_directory_iterators() {
fs::directory_iterator it;
test_eq(it, it);

fs::recursive_directory_iterator rdit;
test_eq(rdit, rdit);
}

template void test_forward<fs::path>();
#endif

#if TEST_STD_VER >= 17
template void test_random_access<std::string_view>();
#endif

#if TEST_STD_VER >= 20
void test_span() {
std::span<int> c;
std::span<int>::iterator it = c.begin(); // span has no const_iterator
test_eq(it, it);
test_lt(it, it);
}
#endif

0 comments on commit 0ddc58a

Please sign in to comment.