diff --git a/libcxx/include/iterator b/libcxx/include/iterator --- a/libcxx/include/iterator +++ b/libcxx/include/iterator @@ -59,6 +59,11 @@ using iter_rvalue_reference_t = decltype(ranges::iter_move(declval<T&>())); +// [iterator.concepts], iterator concepts +// [iterator.concept.readable], concept indirectly_Âreadable +template<class In> + concept indirectly_readable = see below; // since C++20 + template<class Category, class T, class Distance = ptrdiff_t, class Pointer = T*, class Reference = T&> struct iterator @@ -2508,6 +2513,23 @@ requires requires(_Tp& __t) { { ranges::iter_move(__t) } -> __can_reference; } using iter_rvalue_reference_t = decltype(ranges::iter_move(declval<_Tp&>())); +// [iterator.concept.readable] +template<class _In> +concept __indirectly_readable_impl = + requires(const _In __in) { + typename iter_value_t<_In>; + typename iter_reference_t<_In>; + typename iter_rvalue_reference_t<_In>; + { *__in } -> same_as<iter_reference_t<_In>>; + { ranges::iter_move(__in) } -> same_as<iter_rvalue_reference_t<_In>>; + } && + common_reference_with<iter_reference_t<_In>&&, iter_value_t<_In>&> && + common_reference_with<iter_reference_t<_In>&&, iter_rvalue_reference_t<_In>&&> && + common_reference_with<iter_rvalue_reference_t<_In>&&, const iter_value_t<_In>&>; + +template<class _In> +concept indirectly_readable = __indirectly_readable_impl<remove_cvref_t<_In>>; + #undef _LIBCPP_NOEXCEPT_RETURN #endif // !defined(_LIBCPP_HAS_NO_RANGES) diff --git a/libcxx/test/std/iterators/iterator.requirements/iterator.concepts/iterator.concept.readable/indirectly_readable.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/iterator.concepts/iterator.concept.readable/indirectly_readable.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/iterators/iterator.requirements/iterator.concepts/iterator.concept.readable/indirectly_readable.compile.pass.cpp @@ -0,0 +1,319 @@ +//===----------------------------------------------------------------------===// +// +// 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<class In> +// concept indirectly_readable; + +#include <iterator> + +#include <array> +#include <concepts> +#include <deque> +#ifndef _LIBCPP_HAS_NO_FILESYSTEM_LIBRARY +#include <filesystem> +#endif +#include <forward_list> +#include <istream> +#include <list> +#include <map> +#include <memory> +#include <set> +#include <string> +#include <string_view> +#include <optional> +#include <ostream> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +#include "../read_write.h" + +template <class In> +[[nodiscard]] constexpr bool check_indirectly_readable() { + constexpr bool result = std::indirectly_readable<In>; + static_assert(std::indirectly_readable<In const> == result); + static_assert(std::indirectly_readable<In volatile> == result); + static_assert(std::indirectly_readable<In const volatile> == result); + static_assert(std::indirectly_readable<In const&> == result); + static_assert(std::indirectly_readable<In volatile&> == result); + static_assert(std::indirectly_readable<In const volatile&> == result); + static_assert(std::indirectly_readable<In const&&> == result); + static_assert(std::indirectly_readable<In volatile&&> == result); + static_assert(std::indirectly_readable<In const volatile&&> == result); + return result; +} + +static_assert(check_indirectly_readable<traditional_indirection>()); + +struct alternative_indirection { + using element_type = long; + element_type& operator*() const; +}; +static_assert(check_indirectly_readable<alternative_indirection>()); + +static_assert(check_indirectly_readable<proxy_indirection>()); +static_assert(check_indirectly_readable<read_only_indirection>()); + +struct indirection_mismatch { + using value_type = int; + float& operator*() const; +}; +static_assert(!std::same_as<std::iter_value_t<indirection_mismatch>, + std::iter_reference_t<indirection_mismatch> > && + check_indirectly_readable<indirection_mismatch>()); +static_assert(!check_indirectly_readable<missing_dereference>()); + +// `iter_rvalue_reference_t` can't be missing unless the dereference operator is also missing. + +struct iter_move_mismatch { + using value_type = int; + value_type& operator*() const; + + friend float& iter_move(iter_move_mismatch&); +}; +static_assert(!check_indirectly_readable<iter_move_mismatch>()); + +struct indirection_and_iter_move_mismatch { + using value_type = int; + float& operator*() const; + + friend unsigned long long& iter_move(indirection_and_iter_move_mismatch&); +}; +static_assert(!check_indirectly_readable<indirection_and_iter_move_mismatch>()); + +struct missing_iter_value_t { + int operator*() const; +}; +static_assert(!check_indirectly_readable<missing_iter_value_t>()); + +struct lvalue_ref_has_no_common_type_with_rvalue_ref {}; + +struct iter_ref1 {}; +namespace std { +template <> +struct common_reference<iter_ref1&, iter_ref1&&> {}; + +template <> +struct common_reference<iter_ref1&&, iter_ref1&> {}; +} // namespace std +static_assert(!std::common_reference_with<iter_ref1&, iter_ref1&&>); + +struct bad_iter_reference_t { + using value_type = int; + iter_ref1& operator*() const; +}; +static_assert(!check_indirectly_readable<bad_iter_reference_t>()); + +struct iter_ref2 {}; +struct iter_rvalue_ref {}; + +struct + no_common_reference_between_rvalue_ref_to_iter_ref_and_rvalue_ref_to_iter_rvalue_ref { + using value_type = iter_ref2; + iter_ref2& operator*() const; + friend iter_rvalue_ref&& iter_move( + no_common_reference_between_rvalue_ref_to_iter_ref_and_rvalue_ref_to_iter_rvalue_ref); +}; +static_assert( + !check_indirectly_readable< + no_common_reference_between_rvalue_ref_to_iter_ref_and_rvalue_ref_to_iter_rvalue_ref>()); + +struct iter_ref3 { + operator iter_rvalue_ref() const; +}; +namespace std { +template <template <class> class XQual, template <class> class YQual> +struct basic_common_reference<iter_ref3, iter_rvalue_ref, XQual, YQual> { + using type = iter_rvalue_ref; +}; +template <template <class> class XQual, template <class> class YQual> +struct basic_common_reference<iter_rvalue_ref, iter_ref3, XQual, YQual> { + using type = iter_rvalue_ref; +}; +} // namespace std +static_assert(std::common_reference_with<iter_ref3&&, iter_rvalue_ref&&>); + +struct different_reference_types_but_share_common_reference { + using value_type = iter_ref3; + iter_ref3& operator*() const; + friend iter_rvalue_ref&& + iter_move(different_reference_types_but_share_common_reference); +}; +static_assert(check_indirectly_readable< + different_reference_types_but_share_common_reference>()); + +struct iter_ref4 { + operator iter_rvalue_ref() const; +}; +namespace std { +template <template <class> class XQual, template <class> class YQual> +struct basic_common_reference<iter_ref4, iter_rvalue_ref, XQual, YQual> { + using type = iter_rvalue_ref; +}; +template <template <class> class XQual, template <class> class YQual> +struct basic_common_reference<iter_rvalue_ref, iter_ref4, XQual, YQual> { + using type = iter_rvalue_ref; +}; + +template <> +struct common_reference<iter_ref4 const&, iter_rvalue_ref&&> {}; +template <> +struct common_reference<iter_rvalue_ref&&, iter_ref4 const&> {}; +} // namespace std +static_assert(std::common_reference_with<iter_ref4&&, iter_rvalue_ref&&>); +static_assert(!std::common_reference_with<iter_ref4 const&, iter_rvalue_ref&&>); + +struct different_reference_types_without_common_reference_to_const { + using value_type = iter_ref4; + iter_ref4& operator*() const; + friend iter_rvalue_ref&& + iter_move(different_reference_types_without_common_reference_to_const); +}; +static_assert(!check_indirectly_readable< + different_reference_types_without_common_reference_to_const>()); + +namespace standard_types { +static_assert(check_indirectly_readable<int*>()); +static_assert(check_indirectly_readable<int const*>()); +static_assert(check_indirectly_readable<int volatile*>()); +static_assert(check_indirectly_readable<int const volatile*>()); + +// <array> +static_assert(check_indirectly_readable<std::array<int, 10>::iterator>()); +static_assert(check_indirectly_readable<std::array<int, 10>::const_iterator>()); +static_assert( + check_indirectly_readable<std::array<int, 10>::reverse_iterator>()); +static_assert( + check_indirectly_readable<std::array<int, 10>::const_reverse_iterator>()); + +// <deque> +static_assert(check_indirectly_readable<std::deque<int>::iterator>()); +static_assert(check_indirectly_readable<std::deque<int>::const_iterator>()); +static_assert(check_indirectly_readable<std::deque<int>::reverse_iterator>()); +static_assert( + check_indirectly_readable<std::deque<int>::const_reverse_iterator>()); + +// <filesystem> +#ifndef _LIBCPP_HAS_NO_FILESYSTEM_LIBRARY +static_assert(check_indirectly_readable<std::filesystem::directory_iterator>()); +static_assert( + check_indirectly_readable<std::filesystem::recursive_directory_iterator>()); +#endif + +// <forward_list> +static_assert(check_indirectly_readable<std::forward_list<int>::iterator>()); +static_assert( + check_indirectly_readable<std::forward_list<int>::const_iterator>()); + +// <iterator> +static_assert(!check_indirectly_readable< + std::back_insert_iterator<std::vector<int> > >()); +static_assert(!check_indirectly_readable< + std::front_insert_iterator<std::deque<int> > >()); +static_assert( + !check_indirectly_readable<std::insert_iterator<std::deque<int> > >()); +static_assert(check_indirectly_readable<std::istream_iterator<int, char> >()); +static_assert(check_indirectly_readable<std::istreambuf_iterator<char> >()); +static_assert(check_indirectly_readable< + std::move_iterator<std::vector<int>::iterator> >()); +static_assert(!check_indirectly_readable<std::ostream_iterator<int, char> >()); +static_assert( + !check_indirectly_readable<std::ostreambuf_iterator<int, char> >()); + +// <list> +static_assert(check_indirectly_readable<std::list<int>::iterator>()); +static_assert(check_indirectly_readable<std::list<int>::const_iterator>()); +static_assert(check_indirectly_readable<std::list<int>::reverse_iterator>()); +static_assert( + check_indirectly_readable<std::list<int>::const_reverse_iterator>()); + +// <map> +static_assert(check_indirectly_readable<std::map<int, int>::iterator>()); +static_assert(check_indirectly_readable<std::map<int, int>::const_iterator>()); +static_assert( + check_indirectly_readable<std::map<int, int>::reverse_iterator>()); +static_assert( + check_indirectly_readable<std::map<int, int>::const_reverse_iterator>()); + +static_assert(check_indirectly_readable<std::multimap<int, int>::iterator>()); +static_assert( + check_indirectly_readable<std::multimap<int, int>::const_iterator>()); +static_assert( + check_indirectly_readable<std::multimap<int, int>::reverse_iterator>()); +static_assert(check_indirectly_readable< + std::multimap<int, int>::const_reverse_iterator>()); + +// <memory> +static_assert(check_indirectly_readable<std::shared_ptr<int> >()); +static_assert(!check_indirectly_readable<std::shared_ptr<void> >()); +static_assert(check_indirectly_readable<std::unique_ptr<int> >()); +static_assert(!check_indirectly_readable<std::unique_ptr<void> >()); + +// <optional> +static_assert(!check_indirectly_readable<std::optional<int> >()); + +// <set> +static_assert(check_indirectly_readable<std::set<int>::iterator>()); +static_assert(check_indirectly_readable<std::set<int>::const_iterator>()); +static_assert(check_indirectly_readable<std::set<int>::reverse_iterator>()); +static_assert( + check_indirectly_readable<std::set<int>::const_reverse_iterator>()); + +static_assert(check_indirectly_readable<std::multiset<int>::iterator>()); +static_assert(check_indirectly_readable<std::multiset<int>::const_iterator>()); +static_assert( + check_indirectly_readable<std::multiset<int>::reverse_iterator>()); +static_assert( + check_indirectly_readable<std::multiset<int>::const_reverse_iterator>()); + +// <string> +static_assert(check_indirectly_readable<std::string::iterator>()); +static_assert(check_indirectly_readable<std::string::const_iterator>()); +static_assert(check_indirectly_readable<std::string::reverse_iterator>()); +static_assert(check_indirectly_readable<std::string::const_reverse_iterator>()); + +// <string_view> +static_assert(check_indirectly_readable<std::string_view::iterator>()); +static_assert(check_indirectly_readable<std::string_view::const_iterator>()); +static_assert(check_indirectly_readable<std::string_view::reverse_iterator>()); +static_assert( + check_indirectly_readable<std::string_view::const_reverse_iterator>()); + +// <unordered_map> +static_assert( + check_indirectly_readable<std::unordered_map<int, int>::iterator>()); +static_assert( + check_indirectly_readable<std::unordered_map<int, int>::const_iterator>()); + +static_assert( + check_indirectly_readable<std::unordered_multimap<int, int>::iterator>()); +static_assert(check_indirectly_readable< + std::unordered_multimap<int, int>::const_iterator>()); + +// <unordered_set> +static_assert(check_indirectly_readable<std::unordered_set<int>::iterator>()); +static_assert( + check_indirectly_readable<std::unordered_set<int>::const_iterator>()); + +static_assert( + check_indirectly_readable<std::unordered_multiset<int, int>::iterator>()); +static_assert(check_indirectly_readable< + std::unordered_multiset<int, int>::const_iterator>()); + +// <vector> +static_assert(check_indirectly_readable<std::vector<int>::iterator>()); +static_assert(check_indirectly_readable<std::vector<int>::const_iterator>()); +static_assert(check_indirectly_readable<std::vector<int>::reverse_iterator>()); +static_assert( + check_indirectly_readable<std::vector<int>::const_reverse_iterator>()); +static_assert(!check_indirectly_readable<std::vector<int> >()); +} // namespace standard_types diff --git a/libcxx/test/std/iterators/iterator.requirements/iterator.concepts/read_write.h b/libcxx/test/std/iterators/iterator.requirements/iterator.concepts/read_write.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/iterators/iterator.requirements/iterator.concepts/read_write.h @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +#ifndef LIBCPP_TEST_STD_ITERATORS_ITERATOR_REQUIREMENTS_ITERATOR_CONCEPTS_READ_WRITE_H +#define LIBCPP_TEST_STD_ITERATORS_ITERATOR_REQUIREMENTS_ITERATOR_CONCEPTS_READ_WRITE_H + +struct traditional_indirection { + using value_type = int; + value_type& operator*() const; +}; + +struct proxy_indirection { + using value_type = int; + value_type operator*() const; +}; + +struct read_only_indirection { + using value_type = int const; + value_type& operator*() const; +}; + +// doubles as missing_iter_reference_t +struct missing_dereference { + using value_type = int; +}; + +#endif // LIBCPP_TEST_STD_ITERATORS_ITERATOR_REQUIREMENTS_ITERATOR_CONCEPTS_READ_WRITE_H