diff --git a/libcxx/docs/Cxx2aStatusIssuesStatus.csv b/libcxx/docs/Cxx2aStatusIssuesStatus.csv --- a/libcxx/docs/Cxx2aStatusIssuesStatus.csv +++ b/libcxx/docs/Cxx2aStatusIssuesStatus.csv @@ -297,3 +297,4 @@ "`3396 `__","Clarify point of reference for ``source_location::current()``\ (DE 169)","Prague","","" "`3397 `__","``ranges::basic_istream_view::iterator``\ should not provide ``iterator_category``\ ","Prague","","" "`3398 `__","``tuple_element_t``\ is also wrong for ``const subrange``\ ","Prague","","" +"`3446 `__","``indirectly_readable_traits``\ ambiguity for types with both ``value_type``\ and ``element_type``\ ","November virtual meeting","|Complete|","13.0" diff --git a/libcxx/include/iterator b/libcxx/include/iterator --- a/libcxx/include/iterator +++ b/libcxx/include/iterator @@ -17,7 +17,8 @@ namespace std { -template struct incrementable_traits; // since C++20 +template struct incrementable_traits; // since C++20 +template struct indirectly_readable_traits; // since C++20 template struct iterator_traits @@ -470,6 +471,54 @@ }; // TODO(cjdb): add iter_difference_t once iterator_traits is cleaned up. + +// [readable.traits] +template struct indirectly_readable_traits {}; + +template +requires is_array_v<_Ip> +struct indirectly_readable_traits<_Ip> { + using value_type = remove_cv_t>; +}; + +template +struct indirectly_readable_traits : indirectly_readable_traits<_Ip> {}; + +template struct __cond_value_type {}; + +template +requires is_object_v<_Tp> +struct __cond_value_type<_Tp> { using value_type = remove_cv_t<_Tp>; }; + +template +struct indirectly_readable_traits<_Tp*> : __cond_value_type<_Tp> {}; + +template +concept __has_member_value_type = requires { typename _Tp::value_type; }; + +template<__has_member_value_type _Tp> +struct indirectly_readable_traits<_Tp> + : __cond_value_type {}; + +template +concept __has_member_element_type = requires { typename _Tp::element_type; }; + +template<__has_member_element_type _Tp> +struct indirectly_readable_traits<_Tp> + : __cond_value_type {}; + +template +concept __has_members_value_type_and_element_type = + __has_member_value_type<_Tp> && + __has_member_element_type<_Tp> && + same_as, + remove_cv_t>; + +template<__has_members_value_type_and_element_type _Tp> +struct indirectly_readable_traits<_Tp> + : __cond_value_type {}; + +// TODO(cjdb): add iter_value_t once iterator_traits is cleaned up. #endif // !defined(_LIBCPP_HAS_NO_RANGES) template diff --git a/libcxx/test/std/iterators/iterator.requirements/iterator.assoc.types/readable.traits/indirectly_readable_traits.compile.pass.cpp b/libcxx/test/std/iterators/iterator.requirements/iterator.assoc.types/readable.traits/indirectly_readable_traits.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/iterators/iterator.requirements/iterator.assoc.types/readable.traits/indirectly_readable_traits.compile.pass.cpp @@ -0,0 +1,212 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// template +// struct indirectly_readable_traits; + +#include + +#include +#include +#include +#include +#include + +#pragma GCC diagnostic ignored "-Wunused-local-typedefs" + +// clang-format off +template +concept check_has_value_type = requires { + typename std::indirectly_readable_traits::value_type; +}; + +template +concept check_value_type_matches = + check_has_value_type && + std::same_as::value_type, Expected>; +// clang-format on + +template +constexpr bool check_pointer() { + constexpr bool result = check_value_type_matches; + static_assert(check_value_type_matches == result); + static_assert(check_value_type_matches == result); + static_assert(check_value_type_matches == result); + + static_assert(check_value_type_matches == result); + static_assert(check_value_type_matches == result); + static_assert(check_value_type_matches == result); + static_assert(check_value_type_matches == result); + + return result; +} + +static_assert(!check_pointer()); +static_assert(check_pointer()); +static_assert(check_pointer()); +static_assert(check_pointer()); +static_assert(check_pointer()); + +struct S {}; +static_assert(check_pointer()); + +template +constexpr bool check_array() { + constexpr bool result = check_value_type_matches; + static_assert(check_value_type_matches == result); + static_assert(check_value_type_matches == result); + static_assert(check_value_type_matches == result); + static_assert(check_value_type_matches == result); + static_assert(check_value_type_matches == result); + static_assert(check_value_type_matches == result); + static_assert(check_value_type_matches == result); + return result; +} + +static_assert(check_array()); +static_assert(check_array()); +static_assert(check_array()); +static_assert(check_array()); +static_assert(check_array()); + +template +constexpr bool check_explicit_member() { + constexpr bool result = check_value_type_matches; + return result == check_value_type_matches; +} + +struct has_value_type { + using value_type = int; +}; +static_assert(check_explicit_member()); +static_assert(check_explicit_member::iterator, int>()); + +struct has_element_type { + using element_type = S; +}; +static_assert(check_explicit_member()); + +struct has_same_value_and_element_type { + using value_type = int; + using element_type = int; +}; +static_assert(check_explicit_member()); +static_assert(check_explicit_member, long>()); +static_assert(check_explicit_member, long>()); + +// clang-format off +template +requires std::same_as, std::remove_cv_t > +struct possibly_different_cv_qualifiers { + using value_type = T; + using element_type = U; +}; +// clang-format on + +static_assert( + check_explicit_member, int>()); +static_assert(check_explicit_member< + possibly_different_cv_qualifiers, int>()); +static_assert(check_explicit_member< + possibly_different_cv_qualifiers, int>()); +static_assert( + check_explicit_member< + possibly_different_cv_qualifiers, int>()); +static_assert(check_explicit_member< + possibly_different_cv_qualifiers, int>()); +static_assert(check_explicit_member< + possibly_different_cv_qualifiers, int>()); +static_assert( + check_explicit_member< + possibly_different_cv_qualifiers, int>()); +static_assert(check_explicit_member< + possibly_different_cv_qualifiers, + int>()); +static_assert(check_explicit_member< + possibly_different_cv_qualifiers, int>()); +static_assert( + check_explicit_member< + possibly_different_cv_qualifiers, int>()); +static_assert( + check_explicit_member< + possibly_different_cv_qualifiers, int>()); +static_assert( + check_explicit_member< + possibly_different_cv_qualifiers, + int>()); +static_assert( + check_explicit_member< + possibly_different_cv_qualifiers, int>()); +static_assert(check_explicit_member< + possibly_different_cv_qualifiers, + int>()); +static_assert( + check_explicit_member< + possibly_different_cv_qualifiers, + int>()); +static_assert(check_explicit_member, + int>()); + +struct S2 {}; +namespace std { +template <> +struct indirectly_readable_traits { + using value_type = int; +}; +} // namespace std +static_assert(check_value_type_matches); +static_assert(check_value_type_matches, int>); +static_assert(check_value_type_matches::iterator, int>); +static_assert(check_value_type_matches::const_iterator, int>); +static_assert(check_value_type_matches, int>); +static_assert(check_value_type_matches, char>); +static_assert(check_value_type_matches, int>); + +template +constexpr bool check_ref() { + struct ref_value { + using value_type = T&; + }; + constexpr bool result = check_has_value_type; + + struct ref_element { + using element_type = T&; + }; + static_assert(check_has_value_type == result); + + return result; +} + +static_assert(!check_ref()); +static_assert(!check_ref()); +static_assert(!check_ref >()); + +static_assert(!check_has_value_type); +static_assert(!check_has_value_type); +static_assert(!check_has_value_type); +static_assert(!check_has_value_type); + +struct has_different_value_and_element_type { + using value_type = int; + using element_type = long; +}; +static_assert(!check_has_value_type); + +struct void_value { + using value_type = void; +}; +static_assert(!check_has_value_type); + +struct void_element { + using element_type = void; +}; +static_assert(!check_has_value_type);