diff --git a/libcxx/include/__iterator/concepts.h b/libcxx/include/__iterator/concepts.h --- a/libcxx/include/__iterator/concepts.h +++ b/libcxx/include/__iterator/concepts.h @@ -112,6 +112,16 @@ incrementable<_Ip> && sentinel_for<_Ip, _Ip>; +// [iterator.concept.bidir] +template +concept bidirectional_iterator = + forward_iterator<_Ip> && + derived_from<_ITER_CONCEPT<_Ip>, bidirectional_iterator_tag> && + requires(_Ip __i) { + { --__i } -> same_as<_Ip&>; + { __i-- } -> same_as<_Ip>; + }; + // clang-format on #endif // !defined(_LIBCPP_HAS_NO_RANGES) diff --git a/libcxx/include/__ranges/concepts.h b/libcxx/include/__ranges/concepts.h --- a/libcxx/include/__ranges/concepts.h +++ b/libcxx/include/__ranges/concepts.h @@ -58,6 +58,9 @@ template concept forward_range = input_range<_Tp>&& forward_iterator >; +template +concept bidirectional_range = + forward_range<_Tp>&& bidirectional_iterator >; } // namespace ranges #endif // !defined(_LIBCPP_HAS_NO_RANGES) diff --git a/libcxx/include/iterator b/libcxx/include/iterator --- a/libcxx/include/iterator +++ b/libcxx/include/iterator @@ -76,6 +76,10 @@ template concept forward_iterator = see below; // since C++20 +// [iterator.concept.bidir], concept bidirectional_iterator +template + concept bidirectional_­iterator = see below; // since C++20 + template struct iterator diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -50,6 +50,9 @@ template concept forward_range = see below; + + template + concept bidirectional_­range = see below; } */ diff --git a/libcxx/test/std/iterators/iterator.requirements/iterator.concepts/iterator.concept.bidir/bidirectional_iterator.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/iterator.concepts/iterator.concept.bidir/bidirectional_iterator.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/iterators/iterator.requirements/iterator.concepts/iterator.concept.bidir/bidirectional_iterator.compile.pass.cpp @@ -0,0 +1,258 @@ +//===----------------------------------------------------------------------===// +// +// 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: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: gcc-10 + +// template +// concept bidirectional_iterator; + +#include + +#include +#include +#include +#ifndef _LIBCPP_HAS_NO_FILESYSTEM_LIBRARY +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_iterators.h" + +namespace standard_types { +static_assert(std::bidirectional_iterator); +static_assert(std::bidirectional_iterator); +static_assert(std::bidirectional_iterator); +static_assert(std::bidirectional_iterator); + +// +static_assert(std::bidirectional_iterator::iterator>); +static_assert(std::bidirectional_iterator::const_iterator>); +static_assert( + std::bidirectional_iterator::reverse_iterator>); +static_assert( + std::bidirectional_iterator::const_reverse_iterator>); + +// +static_assert(std::bidirectional_iterator::iterator>); +static_assert(std::bidirectional_iterator::const_iterator>); +static_assert(std::bidirectional_iterator::reverse_iterator>); +static_assert( + std::bidirectional_iterator::const_reverse_iterator>); + +// +static_assert(!std::bidirectional_iterator::iterator>); +static_assert( + !std::bidirectional_iterator::const_iterator>); + +// +static_assert(std::bidirectional_iterator::iterator>); +static_assert(std::bidirectional_iterator::const_iterator>); +static_assert(std::bidirectional_iterator::reverse_iterator>); +static_assert( + std::bidirectional_iterator::const_reverse_iterator>); + +// +static_assert(std::bidirectional_iterator::iterator>); +static_assert(std::bidirectional_iterator::const_iterator>); +static_assert( + std::bidirectional_iterator::reverse_iterator>); +static_assert( + std::bidirectional_iterator::const_reverse_iterator>); + +static_assert(std::bidirectional_iterator::iterator>); +static_assert( + std::bidirectional_iterator::const_iterator>); +static_assert( + std::bidirectional_iterator::reverse_iterator>); +static_assert(std::bidirectional_iterator< + std::multimap::const_reverse_iterator>); + +// +static_assert(std::bidirectional_iterator::iterator>); +static_assert(std::bidirectional_iterator::const_iterator>); +static_assert(std::bidirectional_iterator::reverse_iterator>); +static_assert( + std::bidirectional_iterator::const_reverse_iterator>); + +static_assert(std::bidirectional_iterator::iterator>); +static_assert(std::bidirectional_iterator::const_iterator>); +static_assert( + std::bidirectional_iterator::reverse_iterator>); +static_assert( + std::bidirectional_iterator::const_reverse_iterator>); + +// +static_assert(std::bidirectional_iterator); +static_assert(std::bidirectional_iterator); +static_assert(std::bidirectional_iterator); +static_assert(std::bidirectional_iterator); + +// +static_assert(std::bidirectional_iterator); +static_assert(std::bidirectional_iterator); +static_assert(std::bidirectional_iterator); +static_assert( + std::bidirectional_iterator); + +// +static_assert( + !std::bidirectional_iterator::iterator>); +static_assert( + !std::bidirectional_iterator::const_iterator>); + +static_assert( + !std::bidirectional_iterator::iterator>); +static_assert(!std::bidirectional_iterator< + std::unordered_multimap::const_iterator>); + +// +static_assert(!std::bidirectional_iterator::iterator>); +static_assert( + !std::bidirectional_iterator::const_iterator>); + +static_assert( + !std::bidirectional_iterator::iterator>); +static_assert(!std::bidirectional_iterator< + std::unordered_multiset::const_iterator>); + +// +static_assert(std::bidirectional_iterator::iterator>); +static_assert(std::bidirectional_iterator::const_iterator>); +static_assert(std::bidirectional_iterator::reverse_iterator>); +static_assert( + std::bidirectional_iterator::const_reverse_iterator>); +} // namespace standard_types + +struct not_forward_iterator { + using value_type = int; + using difference_type = std::ptrdiff_t; + using iterator_concept = std::bidirectional_iterator_tag; + + value_type operator*() const; + + not_forward_iterator& operator++(); + not_forward_iterator operator++(int); + + not_forward_iterator& operator--(); + not_forward_iterator& operator--(int); +}; +static_assert(std::input_iterator && + !std::forward_iterator && + !std::bidirectional_iterator); + +struct wrong_iterator_category { + using value_type = int; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + value_type& operator*() const; + + wrong_iterator_category& operator++(); + wrong_iterator_category operator++(int); + + wrong_iterator_category& operator--(); + wrong_iterator_category operator--(int); + + bool operator==(wrong_iterator_category const&) const = default; +}; +static_assert(!std::bidirectional_iterator); + +struct wrong_iterator_concept { + using value_type = int; + using difference_type = std::ptrdiff_t; + using iterator_concept = std::forward_iterator_tag; + + value_type& operator*() const; + + wrong_iterator_concept& operator++(); + wrong_iterator_concept operator++(int); + + wrong_iterator_concept& operator--(); + wrong_iterator_concept operator--(int); + + bool operator==(wrong_iterator_concept const&) const = default; +}; +static_assert(!std::bidirectional_iterator); + +struct no_predecrement { + using value_type = int; + using difference_type = std::ptrdiff_t; + using iterator_concept = std::bidirectional_iterator_tag; + + value_type& operator*() const; + + no_predecrement& operator++(); + no_predecrement operator++(int); + + no_predecrement operator--(int); + + bool operator==(no_predecrement const&) const = default; +}; +static_assert(!std::bidirectional_iterator); + +struct bad_predecrement { + using value_type = int; + using difference_type = std::ptrdiff_t; + using iterator_concept = std::bidirectional_iterator_tag; + + value_type& operator*() const; + + bad_predecrement& operator++(); + bad_predecrement operator++(int); + + bad_predecrement operator--(); + bad_predecrement operator--(int); + + bool operator==(bad_predecrement const&) const = default; +}; +static_assert(!std::bidirectional_iterator); + +struct no_postdecrement { + using value_type = int; + using difference_type = std::ptrdiff_t; + using iterator_concept = std::bidirectional_iterator_tag; + + value_type& operator*() const; + + no_postdecrement& operator++(); + no_postdecrement operator++(int); + + no_postdecrement& operator--(); + + bool operator==(no_postdecrement const&) const = default; +}; +static_assert(!std::bidirectional_iterator); + +struct bad_postdecrement { + using value_type = int; + using difference_type = std::ptrdiff_t; + using iterator_concept = std::bidirectional_iterator_tag; + + value_type& operator*() const; + + bad_postdecrement& operator++(); + bad_postdecrement operator++(int); + + bad_postdecrement& operator--(); + bad_postdecrement& operator--(int); + + bool operator==(bad_postdecrement const&) const = default; +}; +static_assert(!std::bidirectional_iterator); diff --git a/libcxx/test/std/iterators/iterator.requirements/iterator.concepts/iterator.concept.bidir/subsumption.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/iterator.concepts/iterator.concept.bidir/subsumption.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/iterators/iterator.requirements/iterator.concepts/iterator.concept.bidir/subsumption.compile.pass.cpp @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// 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: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: gcc-10 + +// template +// concept bidirectional_iterator; + +#include + +#include + +// clang-format off +template +requires std::derived_from, std::bidirectional_iterator_tag> +[[nodiscard]] constexpr bool check_subsumption() { + return false; +} + +template +[[nodiscard]] constexpr bool check_subsumption() { + return true; +} +// clang-format on + +static_assert(check_subsumption()); diff --git a/libcxx/test/std/ranges/range.refinements/bidirectional_range.compile.pass.cpp b/libcxx/test/std/ranges/range.refinements/bidirectional_range.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.refinements/bidirectional_range.compile.pass.cpp @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// +// 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: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: gcc-10 + +// template +// concept bidirectional_range; + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_range.h" + +namespace stdr = std::ranges; + +static_assert(stdr::bidirectional_range >); +static_assert(stdr::bidirectional_range >); +static_assert(!stdr::bidirectional_range >); +static_assert(stdr::bidirectional_range >); +static_assert(stdr::bidirectional_range >); +static_assert(stdr::bidirectional_range >); +static_assert(stdr::bidirectional_range >); +static_assert(stdr::bidirectional_range >); +static_assert(stdr::bidirectional_range); +static_assert(stdr::bidirectional_range); +static_assert(!stdr::bidirectional_range >); +static_assert(!stdr::bidirectional_range >); +static_assert(!stdr::bidirectional_range >); +static_assert(!stdr::bidirectional_range >); +static_assert(stdr::bidirectional_range >); + +static_assert( + stdr::bidirectional_range > >); +static_assert( + stdr::bidirectional_range > >); +static_assert(stdr::bidirectional_range > >); +static_assert( + stdr::bidirectional_range > >); +static_assert( + stdr::bidirectional_range > >); +static_assert( + stdr::bidirectional_range > >); +static_assert(stdr::bidirectional_range > >); +static_assert(stdr::bidirectional_range >); +static_assert( + stdr::bidirectional_range >); +static_assert(stdr::bidirectional_range< + bidirectional_range > >); +static_assert(stdr::bidirectional_range< + bidirectional_range > >); +static_assert(stdr::bidirectional_range< + bidirectional_range > >); +static_assert( + stdr::bidirectional_range > >); +static_assert( + stdr::bidirectional_range > >); diff --git a/libcxx/test/std/ranges/range.refinements/subsumption.compile.pass.cpp b/libcxx/test/std/ranges/range.refinements/subsumption.compile.pass.cpp --- a/libcxx/test/std/ranges/range.refinements/subsumption.compile.pass.cpp +++ b/libcxx/test/std/ranges/range.refinements/subsumption.compile.pass.cpp @@ -53,3 +53,19 @@ // clang-format on static_assert(check_forward_range_subsumption()); + +// clang-format off +template +requires std::bidirectional_iterator > +[[nodiscard]] constexpr bool check_bidirectional_range_subsumption() { + return false; +} + +template +requires true +[[nodiscard]] constexpr bool check_bidirectional_range_subsumption() { + return true; +} +// clang-format on + +static_assert(check_bidirectional_range_subsumption()); diff --git a/libcxx/test/support/test_range.h b/libcxx/test/support/test_range.h --- a/libcxx/test/support/test_range.h +++ b/libcxx/test/support/test_range.h @@ -43,4 +43,12 @@ sentinel end() const; }; +template +struct bidirectional_range { + bidirectional_iterator > begin(); + bidirectional_iterator > begin() const; + sentinel end(); + sentinel end() const; +}; + #endif // LIBCXX_TEST_SUPPORT_TEST_RANGE_H