diff --git a/libcxx/include/__memory/pointer_traits.h b/libcxx/include/__memory/pointer_traits.h --- a/libcxx/include/__memory/pointer_traits.h +++ b/libcxx/include/__memory/pointer_traits.h @@ -162,6 +162,70 @@ #endif }; +// to_address + +template struct __to_address_helper; + +template <> struct __to_address_helper { + template + using __return_type = decltype(pointer_traits<_Pointer>::to_address(_VSTD::declval())); + + template + _LIBCPP_CONSTEXPR + static __return_type<_Pointer> + __do_it(const _Pointer &__p) _NOEXCEPT { return pointer_traits<_Pointer>::to_address(__p); } +}; + +template +using __choose_to_address = __to_address_helper<_IsValidExpansion<__to_address_helper<_Dummy>::template __return_type, _Pointer>::value>; + +template +inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR +_Tp* +__to_address(_Tp* __p) _NOEXCEPT +{ + static_assert(!is_function<_Tp>::value, "_Tp is a function type"); + return __p; +} + +template +inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR +typename __choose_to_address<_Pointer>::template __return_type<_Pointer> +__to_address(const _Pointer& __p) _NOEXCEPT +{ + return __choose_to_address<_Pointer>::__do_it(__p); +} + +template <> struct __to_address_helper { + template + using __return_type = typename pointer_traits<_Pointer>::element_type*; + + template + _LIBCPP_CONSTEXPR + static __return_type<_Pointer> + __do_it(const _Pointer &__p) _NOEXCEPT { return _VSTD::__to_address(__p.operator->()); } +}; + + +#if _LIBCPP_STD_VER > 17 +template +inline _LIBCPP_INLINE_VISIBILITY constexpr +_Tp* +to_address(_Tp* __p) _NOEXCEPT +{ + static_assert(!is_function_v<_Tp>, "_Tp is a function type"); + return __p; +} + +template +inline _LIBCPP_INLINE_VISIBILITY constexpr +auto +to_address(const _Pointer& __p) _NOEXCEPT +{ + return _VSTD::__to_address(__p); +} +#endif + _LIBCPP_END_NAMESPACE_STD _LIBCPP_POP_MACROS diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -1639,68 +1639,42 @@ __value_, __equal_to<__v, _Tp>()); } -// copy -template -inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR -_Iter -__unwrap_iter(_Iter __i) -{ - return __i; -} - -template -inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR -typename enable_if -< - is_trivially_copy_assignable<_Tp>::value, - _Tp* ->::type -__unwrap_iter(move_iterator<_Tp*> __i) -{ - return __i.base(); -} +// __unwrap_iter + +// The job of __unwrap_iter is to lower iterators-that-are-tantamount-to-pointers +// (such as vector::iterator) into pointers, to reduce the number of template +// instantiations and to enable pointer-based optimizations e.g. in std::copy. +// In debug mode, we don't do this. + +template ::value> +struct __unwrap_iter_impl { + static _LIBCPP_CONSTEXPR _Iter + __apply(_Iter __i) _NOEXCEPT { + return __i; + } +}; #if _LIBCPP_DEBUG_LEVEL < 2 -template -inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR -typename enable_if -< - is_trivially_copy_assignable<_Tp>::value, - _Tp* ->::type -__unwrap_iter(__wrap_iter<_Tp*> __i) -{ - return __i.base(); -} - -template -inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR -typename enable_if -< - is_trivially_copy_assignable<_Tp>::value, - const _Tp* ->::type -__unwrap_iter(__wrap_iter __i) -{ - return __i.base(); -} +template +struct __unwrap_iter_impl<_Iter, true> { + static _LIBCPP_CONSTEXPR decltype(_VSTD::__to_address(declval<_Iter>())) + __apply(_Iter __i) _NOEXCEPT { + return _VSTD::__to_address(__i); + } +}; -#else +#endif // _LIBCPP_DEBUG_LEVEL < 2 -template +template > inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR -typename enable_if -< - is_trivially_copy_assignable<_Tp>::value, - __wrap_iter<_Tp*> ->::type -__unwrap_iter(__wrap_iter<_Tp*> __i) +decltype(_Impl::__apply(_VSTD::declval<_Iter>())) +__unwrap_iter(_Iter __i) _NOEXCEPT { - return __i; + return _Impl::__apply(__i); } -#endif // _LIBCPP_DEBUG_LEVEL < 2 +// copy template inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 @@ -1894,7 +1868,7 @@ typename enable_if < is_same::type, _Up>::value && - is_trivially_copy_assignable<_Up>::value, + is_trivially_move_assignable<_Up>::value, _Up* >::type __move(_Tp* __first, _Tp* __last, _Up* __result) @@ -1942,7 +1916,7 @@ typename enable_if < is_same::type, _Up>::value && - is_trivially_copy_assignable<_Up>::value, + is_trivially_move_assignable<_Up>::value, _Up* >::type __move_backward(_Tp* __first, _Tp* __last, _Up* __result) diff --git a/libcxx/include/filesystem b/libcxx/include/filesystem --- a/libcxx/include/filesystem +++ b/libcxx/include/filesystem @@ -1495,7 +1495,7 @@ "'char' or 'char8_t'"); #if defined(_LIBCPP_WIN32API) using _Traits = __is_pathable<_Source>; - return u8path(__unwrap_iter(_Traits::__range_begin(__s)), __unwrap_iter(_Traits::__range_end(__s))); + return u8path(_VSTD::__unwrap_iter(_Traits::__range_begin(__s)), _VSTD::__unwrap_iter(_Traits::__range_end(__s))); #else return path(__s); #endif diff --git a/libcxx/include/iterator b/libcxx/include/iterator --- a/libcxx/include/iterator +++ b/libcxx/include/iterator @@ -421,6 +421,7 @@ #include #include #include <__memory/base.h> +#include <__memory/pointer_traits.h> #include #include <__debug> @@ -439,9 +440,7 @@ struct _LIBCPP_TEMPLATE_VIS bidirectional_iterator_tag : public forward_iterator_tag {}; struct _LIBCPP_TEMPLATE_VIS random_access_iterator_tag : public bidirectional_iterator_tag {}; #if _LIBCPP_STD_VER > 17 -// TODO(EricWF) contiguous_iterator_tag is provided as an extension prior to -// C++20 to allow optimizations for users providing wrapped iterator types. -struct _LIBCPP_TEMPLATE_VIS contiguous_iterator_tag: public random_access_iterator_tag { }; +struct _LIBCPP_TEMPLATE_VIS contiguous_iterator_tag : public random_access_iterator_tag {}; #endif template @@ -517,6 +516,17 @@ static const bool value = sizeof(__test<_Tp>(nullptr)) == 1; }; +template +struct __has_iterator_concept +{ +private: + struct __two {char __lx; char __lxx;}; + template static __two __test(...); + template static char __test(typename _Up::iterator_concept* = nullptr); +public: + static const bool value = sizeof(__test<_Tp>(nullptr)) == 1; +}; + template struct __iterator_traits_impl {}; template @@ -568,11 +578,19 @@ template >::value> struct __has_iterator_category_convertible_to - : public integral_constant::iterator_category, _Up>::value> + : _BoolConstant::iterator_category, _Up>::value> +{}; + +template +struct __has_iterator_category_convertible_to<_Tp, _Up, false> : false_type {}; + +template ::value> +struct __has_iterator_concept_convertible_to + : _BoolConstant::value> {}; template -struct __has_iterator_category_convertible_to<_Tp, _Up, false> : public false_type {}; +struct __has_iterator_concept_convertible_to<_Tp, _Up, false> : false_type {}; template struct __is_cpp17_input_iterator : public __has_iterator_category_convertible_to<_Tp, input_iterator_tag> {}; @@ -586,14 +604,26 @@ template struct __is_cpp17_random_access_iterator : public __has_iterator_category_convertible_to<_Tp, random_access_iterator_tag> {}; +// __is_cpp17_contiguous_iterator determines if an iterator is contiguous, +// either because it advertises itself as such (in C++20) or because it +// is a pointer type or a known trivial wrapper around a pointer type, +// such as __wrap_iter. +// #if _LIBCPP_STD_VER > 17 template -struct __is_cpp17_contiguous_iterator : public __has_iterator_category_convertible_to<_Tp, contiguous_iterator_tag> {}; +struct __is_cpp17_contiguous_iterator : _Or< + __has_iterator_category_convertible_to<_Tp, contiguous_iterator_tag>, + __has_iterator_concept_convertible_to<_Tp, contiguous_iterator_tag> +> {}; #else template -struct __is_cpp17_contiguous_iterator : public false_type {}; +struct __is_cpp17_contiguous_iterator : false_type {}; #endif +// Any native pointer which is an iterator is also a contiguous iterator. +template +struct __is_cpp17_contiguous_iterator<_Up*> : true_type {}; + template struct __is_exactly_cpp17_input_iterator @@ -759,6 +789,14 @@ typedef typename iterator_traits<_Iter>::difference_type difference_type; typedef typename iterator_traits<_Iter>::reference reference; typedef typename iterator_traits<_Iter>::pointer pointer; + typedef _If<__is_cpp17_random_access_iterator<_Iter>::value, + random_access_iterator_tag, + typename iterator_traits<_Iter>::iterator_category> iterator_category; +#if _LIBCPP_STD_VER > 17 + typedef _If<__is_cpp17_random_access_iterator<_Iter>::value, + random_access_iterator_tag, + bidirectional_iterator_tag> iterator_concept; +#endif _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 reverse_iterator() : __t(), current() {} @@ -1206,10 +1244,16 @@ _Iter __i; public: typedef _Iter iterator_type; - typedef typename iterator_traits::iterator_category iterator_category; typedef typename iterator_traits::value_type value_type; typedef typename iterator_traits::difference_type difference_type; typedef iterator_type pointer; + typedef _If<__is_cpp17_random_access_iterator<_Iter>::value, + random_access_iterator_tag, + typename iterator_traits<_Iter>::iterator_category> iterator_category; +#if _LIBCPP_STD_VER > 17 + typedef input_iterator_tag iterator_concept; +#endif + #ifndef _LIBCPP_CXX03_LANG typedef typename iterator_traits::reference __reference; typedef typename conditional< @@ -1393,40 +1437,21 @@ template _Op _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 move(_Ip, _Ip, _Op); template _B2 _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 move_backward(_B1, _B1, _B2); -#if _LIBCPP_DEBUG_LEVEL < 2 - -template -_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR -typename enable_if -< - is_trivially_copy_assignable<_Tp>::value, - _Tp* ->::type -__unwrap_iter(__wrap_iter<_Tp*>); - -#else - -template -inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR -typename enable_if -< - is_trivially_copy_assignable<_Tp>::value, - __wrap_iter<_Tp*> ->::type -__unwrap_iter(__wrap_iter<_Tp*> __i); - -#endif - template class __wrap_iter { public: typedef _Iter iterator_type; - typedef typename iterator_traits::iterator_category iterator_category; typedef typename iterator_traits::value_type value_type; typedef typename iterator_traits::difference_type difference_type; typedef typename iterator_traits::pointer pointer; typedef typename iterator_traits::reference reference; + typedef typename iterator_traits::iterator_category iterator_category; +#if _LIBCPP_STD_VER > 17 + typedef _If<__is_cpp17_contiguous_iterator<_Iter>::value, + contiguous_iterator_tag, iterator_category> iterator_concept; +#endif + private: iterator_type __i; public: @@ -1603,27 +1628,19 @@ template friend _LIBCPP_CONSTEXPR_AFTER_CXX17 _B2 copy_backward(_B1, _B1, _B2); template friend _LIBCPP_CONSTEXPR_AFTER_CXX17 _Op move(_Ip, _Ip, _Op); template friend _LIBCPP_CONSTEXPR_AFTER_CXX17 _B2 move_backward(_B1, _B1, _B2); +}; -#if _LIBCPP_DEBUG_LEVEL < 2 - template - _LIBCPP_CONSTEXPR friend - typename enable_if - < - is_trivially_copy_assignable<_Tp>::value, - _Tp* - >::type - __unwrap_iter(__wrap_iter<_Tp*>); -#else - template - inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR friend - typename enable_if - < - is_trivially_copy_assignable<_Tp>::value, - __wrap_iter<_Tp*> - >::type - __unwrap_iter(__wrap_iter<_Tp*> __i); +#if _LIBCPP_STD_VER <= 17 +template +struct __is_cpp17_contiguous_iterator<__wrap_iter<_It> > : __is_cpp17_contiguous_iterator<_It> {}; #endif -}; + +template +_LIBCPP_CONSTEXPR +_EnableIf<__is_cpp17_contiguous_iterator<_Iter>::value, decltype(_VSTD::__to_address(declval<_Iter>()))> +__to_address(__wrap_iter<_Iter> __w) _NOEXCEPT { + return _VSTD::__to_address(__w.base()); +} template inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_IF_NODEBUG diff --git a/libcxx/include/memory b/libcxx/include/memory --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -722,68 +722,6 @@ #endif } -template struct __to_address_helper; - -template <> struct __to_address_helper { - template - using __return_type = decltype(pointer_traits<_Pointer>::to_address(_VSTD::declval())); - - template - _LIBCPP_CONSTEXPR - static __return_type<_Pointer> - __do_it(const _Pointer &__p) _NOEXCEPT { return pointer_traits<_Pointer>::to_address(__p); } -}; - -template -using __choose_to_address = __to_address_helper<_IsValidExpansion<__to_address_helper<_Dummy>::template __return_type, _Pointer>::value>; - - -template -inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR -_Tp* -__to_address(_Tp* __p) _NOEXCEPT -{ - static_assert(!is_function<_Tp>::value, "_Tp is a function type"); - return __p; -} - -template -inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR -typename __choose_to_address<_Pointer>::template __return_type<_Pointer> -__to_address(const _Pointer& __p) _NOEXCEPT { - return __choose_to_address<_Pointer>::__do_it(__p); -} - -template <> struct __to_address_helper { - template - using __return_type = typename pointer_traits<_Pointer>::element_type*; - - template - _LIBCPP_CONSTEXPR - static __return_type<_Pointer> - __do_it(const _Pointer &__p) _NOEXCEPT { return _VSTD::__to_address(__p.operator->()); } -}; - - -#if _LIBCPP_STD_VER > 17 -template -inline _LIBCPP_INLINE_VISIBILITY constexpr -_Tp* -to_address(_Tp* __p) _NOEXCEPT -{ - static_assert(!is_function_v<_Tp>, "_Tp is a function type"); - return __p; -} - -template -inline _LIBCPP_INLINE_VISIBILITY constexpr -auto -to_address(const _Pointer& __p) _NOEXCEPT -{ - return _VSTD::__to_address(__p); -} -#endif - template class allocator; #if _LIBCPP_STD_VER <= 17 || defined(_LIBCPP_ENABLE_CXX20_REMOVED_ALLOCATOR_MEMBERS) diff --git a/libcxx/test/libcxx/iterators/contiguous_iterators.pass.cpp b/libcxx/test/libcxx/iterators/contiguous_iterators.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/iterators/contiguous_iterators.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 +// +//===----------------------------------------------------------------------===// +// + +// + +// __is_cpp17_contiguous_iterator<_Tp> + +// __is_cpp17_contiguous_iterator determines if an iterator is contiguous, +// either because it advertises itself as such (in C++20) or because it +// is a pointer type or a known trivial wrapper around a pointer type, +// such as __wrap_iter. +// + +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" + +#if TEST_STD_VER >= 17 +#include +#endif + +#if TEST_STD_VER >= 20 +#include +#endif + +#if TEST_STD_VER >= 11 +#define DELETE_FUNCTION = delete +#else +#define DELETE_FUNCTION +#endif + +class T; // incomplete + +class my_input_iterator +{ + struct tag : std::input_iterator_tag {}; + typedef my_input_iterator Self; + int *state_; +public: + typedef tag iterator_category; + typedef int value_type; + typedef int difference_type; + typedef int* pointer; + typedef int& reference; + + my_input_iterator(); + reference operator*() const; + pointer operator->() const; + + Self& operator++(); + Self operator++(int); + friend bool operator==(const Self&, const Self&); + friend bool operator!=(const Self&, const Self&); +}; + +class my_random_access_iterator +{ + struct tag : std::random_access_iterator_tag {}; + typedef my_random_access_iterator Self; + int *state_; +public: + typedef tag iterator_category; + typedef int value_type; + typedef int difference_type; + typedef int* pointer; + typedef int& reference; + + my_random_access_iterator(); + reference operator*() const; + pointer operator->() const; + reference operator[](difference_type) const; + + Self& operator++(); + Self operator++(int); + Self& operator--(); + Self operator--(int); + friend Self& operator+=(Self&, difference_type); + friend Self& operator-=(Self&, difference_type); + friend Self operator+(Self, difference_type); + friend Self operator+(difference_type, Self); + friend Self operator-(Self, difference_type); + friend difference_type operator-(Self, Self); + friend bool operator==(const Self&, const Self&); + friend bool operator!=(const Self&, const Self&); + friend bool operator<(const Self&, const Self&); + friend bool operator>(const Self&, const Self&); + friend bool operator<=(const Self&, const Self&); + friend bool operator>=(const Self&, const Self&); +}; + +#if TEST_STD_VER >= 20 +class my_contiguous_iterator +{ + struct tag : std::contiguous_iterator_tag {}; + typedef my_contiguous_iterator Self; + int *state_; +public: + typedef tag iterator_category; + typedef int value_type; + typedef int difference_type; + typedef int* pointer; + typedef int& reference; + typedef int element_type; // enable to_address via pointer_traits + + my_contiguous_iterator(); + reference operator*() const; + pointer operator->() const; + reference operator[](difference_type) const; + + Self& operator++(); + Self operator++(int); + Self& operator--(); + Self operator--(int); + friend Self& operator+=(Self&, difference_type); + friend Self& operator-=(Self&, difference_type); + friend Self operator+(Self, difference_type); + friend Self operator+(difference_type, Self); + friend Self operator-(Self, difference_type); + friend difference_type operator-(Self, Self); + friend bool operator==(const Self&, const Self&); + friend bool operator!=(const Self&, const Self&); + friend bool operator<(const Self&, const Self&); + friend bool operator>(const Self&, const Self&); + friend bool operator<=(const Self&, const Self&); + friend bool operator>=(const Self&, const Self&); +}; +#endif + +int main(int, char**) +{ +// basic tests + static_assert(( std::__is_cpp17_contiguous_iterator::value), ""); + static_assert(( std::__is_cpp17_contiguous_iterator::value), ""); + static_assert(( std::__is_cpp17_contiguous_iterator::value), ""); + static_assert(( std::__is_cpp17_contiguous_iterator::value), ""); + static_assert(( std::__is_cpp17_contiguous_iterator::value), ""); + + static_assert((!std::__is_cpp17_contiguous_iterator::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator::value), ""); +#if TEST_STD_VER >= 20 + static_assert(( std::__is_cpp17_contiguous_iterator::value), ""); +#endif + + // move_iterator changes value category, which makes it pretty sketchy to use in optimized codepaths + static_assert((!std::__is_cpp17_contiguous_iterator >::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator >::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator >::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator >::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator >::value), ""); +#if TEST_STD_VER >= 20 + static_assert((!std::__is_cpp17_contiguous_iterator >::value), ""); +#endif + + static_assert((!std::__is_cpp17_contiguous_iterator >::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator >::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator >::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator >::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator >::value), ""); +#if TEST_STD_VER >= 20 + static_assert((!std::__is_cpp17_contiguous_iterator >::value), ""); +#endif + + static_assert(( std::__is_cpp17_contiguous_iterator >::value), ""); + static_assert(( std::__is_cpp17_contiguous_iterator >::value), ""); + static_assert(( std::__is_cpp17_contiguous_iterator >::value), ""); + + static_assert(( std::__is_cpp17_contiguous_iterator >::value), ""); + static_assert(( std::__is_cpp17_contiguous_iterator > >::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator > >::value), ""); + + static_assert((!std::__is_cpp17_contiguous_iterator >::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator > >::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator > >::value), ""); + +#if TEST_STD_VER >= 20 + static_assert(( std::__is_cpp17_contiguous_iterator >::value), ""); + static_assert(( std::__is_cpp17_contiguous_iterator > >::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator > >::value), ""); +#endif + +// iterators in the libc++ test suite + static_assert((!std::__is_cpp17_contiguous_iterator >::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator >::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator >::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator >::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator >::value), ""); +#if TEST_STD_VER >= 20 + static_assert(( std::__is_cpp17_contiguous_iterator >::value), ""); +#endif + static_assert((!std::__is_cpp17_contiguous_iterator >::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator >::value), ""); + +// +// iterators from libc++'s containers +// + +// vector + static_assert(( std::__is_cpp17_contiguous_iterator::iterator> ::value), ""); + static_assert(( std::__is_cpp17_contiguous_iterator::const_iterator> ::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator::reverse_iterator> ::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator::const_reverse_iterator> ::value), ""); + static_assert(( std::__is_cpp17_contiguous_iterator::iterator> >::value), ""); + +// string + static_assert(( std::__is_cpp17_contiguous_iterator ::value), ""); + static_assert(( std::__is_cpp17_contiguous_iterator ::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator ::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator::value), ""); + static_assert(( std::__is_cpp17_contiguous_iterator ::value), ""); + static_assert(( std::__is_cpp17_contiguous_iterator ::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator ::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator::value), ""); + +// deque is random-access but not contiguous + static_assert((!std::__is_cpp17_contiguous_iterator::iterator> ::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator::const_iterator> ::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator::reverse_iterator> ::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator::const_reverse_iterator> ::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator::iterator> >::value), ""); + +// vector is random-access but not contiguous + static_assert((!std::__is_cpp17_contiguous_iterator::iterator> ::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator::const_iterator> ::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator::reverse_iterator> ::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator::const_reverse_iterator> ::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator::iterator> >::value), ""); + +#if TEST_STD_VER >= 11 + static_assert(( std::__is_cpp17_contiguous_iterator::iterator> ::value), ""); + static_assert(( std::__is_cpp17_contiguous_iterator::const_iterator>::value), ""); +#endif + +#if TEST_STD_VER >= 17 + static_assert(( std::__is_cpp17_contiguous_iterator ::value), ""); + static_assert(( std::__is_cpp17_contiguous_iterator::value), ""); +#endif + +#if TEST_STD_VER >= 20 + static_assert(( std::__is_cpp17_contiguous_iterator::iterator> ::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator::reverse_iterator>::value), ""); + static_assert(( std::__is_cpp17_contiguous_iterator::iterator> ::value), ""); + static_assert((!std::__is_cpp17_contiguous_iterator::reverse_iterator>::value), ""); +#endif + + return 0; +} diff --git a/libcxx/test/libcxx/iterators/trivial_iterators.pass.cpp b/libcxx/test/libcxx/iterators/trivial_iterators.pass.cpp --- a/libcxx/test/libcxx/iterators/trivial_iterators.pass.cpp +++ b/libcxx/test/libcxx/iterators/trivial_iterators.pass.cpp @@ -130,36 +130,42 @@ static_assert(( std::__is_cpp17_forward_iterator ::value), "" ); static_assert(( std::__is_cpp17_bidirectional_iterator::value), "" ); static_assert(( std::__is_cpp17_random_access_iterator::value), "" ); + static_assert(( std::__is_cpp17_contiguous_iterator ::value), "" ); static_assert((!std::__is_exactly_cpp17_input_iterator::value), "" ); static_assert(( std::__is_cpp17_input_iterator >::value), "" ); static_assert((!std::__is_cpp17_forward_iterator >::value), "" ); static_assert((!std::__is_cpp17_bidirectional_iterator >::value), "" ); static_assert((!std::__is_cpp17_random_access_iterator >::value), "" ); + static_assert((!std::__is_cpp17_contiguous_iterator >::value), "" ); static_assert(( std::__is_exactly_cpp17_input_iterator >::value), "" ); static_assert(( std::__is_cpp17_input_iterator >::value), "" ); static_assert(( std::__is_cpp17_forward_iterator >::value), "" ); static_assert((!std::__is_cpp17_bidirectional_iterator >::value), "" ); static_assert((!std::__is_cpp17_random_access_iterator >::value), "" ); + static_assert((!std::__is_cpp17_contiguous_iterator >::value), "" ); static_assert((!std::__is_exactly_cpp17_input_iterator >::value), "" ); static_assert(( std::__is_cpp17_input_iterator >::value), "" ); static_assert(( std::__is_cpp17_forward_iterator >::value), "" ); static_assert(( std::__is_cpp17_bidirectional_iterator >::value), "" ); static_assert((!std::__is_cpp17_random_access_iterator >::value), "" ); + static_assert((!std::__is_cpp17_contiguous_iterator >::value), "" ); static_assert((!std::__is_exactly_cpp17_input_iterator >::value), "" ); static_assert(( std::__is_cpp17_input_iterator >::value), "" ); static_assert(( std::__is_cpp17_forward_iterator >::value), "" ); static_assert(( std::__is_cpp17_bidirectional_iterator >::value), "" ); static_assert(( std::__is_cpp17_random_access_iterator >::value), "" ); + static_assert((!std::__is_cpp17_contiguous_iterator >::value), "" ); static_assert((!std::__is_exactly_cpp17_input_iterator >::value), "" ); static_assert(( std::__is_cpp17_input_iterator >::value), "" ); static_assert((!std::__is_cpp17_forward_iterator >::value), "" ); static_assert((!std::__is_cpp17_bidirectional_iterator >::value), "" ); static_assert((!std::__is_cpp17_random_access_iterator >::value), "" ); + static_assert((!std::__is_cpp17_contiguous_iterator >::value), "" ); static_assert(( std::__is_exactly_cpp17_input_iterator >::value), "" ); // diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/contiguous_trivial_optimization.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/contiguous_trivial_optimization.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/contiguous_trivial_optimization.pass.cpp @@ -0,0 +1,109 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Older compilers don't support std::is_constant_evaluated +// UNSUPPORTED: clang-4, clang-5, clang-6, clang-7, clang-8 +// UNSUPPORTED: apple-clang-9, apple-clang-10 +// UNSUPPORTED: c++03 + +// + +// We optimize std::copy(_backward) and std::move(_backward) into memmove +// when the iterator is trivial and contiguous and the type in question +// is also trivially (copyable, movable). This test verifies that the +// optimization never eliminates an actually non-trivial copy or move. + +#include +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" + +struct TMBNTC { + int *p; + constexpr TMBNTC(int& copies) : p(&copies) {} + constexpr TMBNTC(const TMBNTC&) = default; + TEST_CONSTEXPR_CXX14 TMBNTC& operator=(TMBNTC&&) = default; + TEST_CONSTEXPR_CXX14 TMBNTC& operator=(const TMBNTC&) { ++*p; return *this; } +}; + +TEST_CONSTEXPR_CXX20 bool +test_trivial_moveassign_but_no_trivial_copyassign() +{ + int copies = 0; + TMBNTC ia[] = { copies, copies, copies, copies }; + TMBNTC ib[] = { copies, copies, copies, copies }; + std::copy(ia, ia+4, ib); + assert(copies == 4); + copies = 0; + std::copy_backward(ia, ia+4, ib+4); + assert(copies == 4); + + copies = 0; + std::copy(std::make_move_iterator(ia), std::make_move_iterator(ia+4), ib); + assert(copies == 0); + std::copy_backward(std::make_move_iterator(ia), std::make_move_iterator(ia+4), ib+4); + assert(copies == 0); + + std::move(ia, ia+4, ib); + assert(copies == 0); + std::move_backward(ia, ia+4, ib+4); + assert(copies == 0); + + return true; +} + +struct TCBNTM { + int *p; + constexpr TCBNTM(int& moves) : p(&moves) {} + constexpr TCBNTM(const TCBNTM&) = default; + TEST_CONSTEXPR_CXX14 TCBNTM& operator=(TCBNTM&&) { ++*p; return *this; } + TEST_CONSTEXPR_CXX14 TCBNTM& operator=(const TCBNTM&) = default; +}; + +TEST_CONSTEXPR_CXX20 bool +test_trivial_copyassign_but_no_trivial_moveassign() +{ + int moves = 0; + TCBNTM ia[] = { moves, moves, moves, moves }; + TCBNTM ib[] = { moves, moves, moves, moves }; + std::move(ia, ia+4, ib); + assert(moves == 4); + moves = 0; + std::move_backward(ia, ia+4, ib+4); + assert(moves == 4); + + moves = 0; + std::copy(std::make_move_iterator(ia), std::make_move_iterator(ia+4), ib); + assert(moves == 4); + moves = 0; + std::copy_backward(std::make_move_iterator(ia), std::make_move_iterator(ia+4), ib+4); + assert(moves == 4); + + moves = 0; + std::copy(ia, ia+4, ib); + assert(moves == 0); + std::copy_backward(ia, ia+4, ib+4); + assert(moves == 0); + + return true; +} + +int main(int, char**) +{ + test_trivial_moveassign_but_no_trivial_copyassign(); + test_trivial_copyassign_but_no_trivial_moveassign(); + +#if TEST_STD_VER > 17 + static_assert(test_trivial_moveassign_but_no_trivial_copyassign()); + static_assert(test_trivial_copyassign_but_no_trivial_moveassign()); +#endif + + return 0; +} diff --git a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iterator/types.pass.cpp b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iterator/types.pass.cpp --- a/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iterator/types.pass.cpp +++ b/libcxx/test/std/iterators/predef.iterators/move.iterators/move.iterator/types.pass.cpp @@ -54,7 +54,15 @@ #else static_assert((std::is_same::value), ""); #endif +#if TEST_STD_VER > 17 + if constexpr (std::is_same_v) { + static_assert((std::is_same::value), ""); + } else { + static_assert((std::is_same::value), ""); + } +#else static_assert((std::is_same::value), ""); +#endif } int main(int, char**) @@ -94,5 +102,14 @@ } #endif +#if TEST_STD_VER > 17 + test>(); + static_assert(std::is_same_v>::iterator_concept, std::input_iterator_tag>); + static_assert(std::is_same_v>::iterator_concept, std::input_iterator_tag>); + static_assert(std::is_same_v>::iterator_concept, std::input_iterator_tag>); + static_assert(std::is_same_v>::iterator_concept, std::input_iterator_tag>); + static_assert(std::is_same_v::iterator_concept, std::input_iterator_tag>); +#endif + return 0; } diff --git a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iterator/types.pass.cpp b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iterator/types.pass.cpp --- a/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iterator/types.pass.cpp +++ b/libcxx/test/std/iterators/predef.iterators/reverse.iterators/reverse.iterator/types.pass.cpp @@ -49,7 +49,15 @@ static_assert((std::is_same::value), ""); static_assert((std::is_same::value), ""); static_assert((std::is_same::pointer>::value), ""); +#if TEST_STD_VER > 17 + if constexpr (std::is_same_v) { + static_assert((std::is_same::value), ""); + } else { + static_assert((std::is_same::value), ""); + } +#else static_assert((std::is_same::value), ""); +#endif } int main(int, char**) @@ -58,5 +66,13 @@ test >(); test(); +#if TEST_STD_VER > 17 + test>(); + static_assert(std::is_same_v>::iterator_concept, std::bidirectional_iterator_tag>); + static_assert(std::is_same_v>::iterator_concept, std::random_access_iterator_tag>); + static_assert(std::is_same_v>::iterator_concept, std::random_access_iterator_tag>); + static_assert(std::is_same_v::iterator_concept, std::random_access_iterator_tag>); +#endif + return 0; } diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h --- a/libcxx/test/support/test_iterators.h +++ b/libcxx/test/support/test_iterators.h @@ -311,6 +311,85 @@ return x.base() - y.base(); } +#if TEST_STD_VER >= 20 +template +class contiguous_iterator +{ + It it_; + + template friend class contiguous_iterator; +public: + typedef std::contiguous_iterator_tag iterator_category; + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::difference_type difference_type; + typedef It pointer; + typedef typename std::iterator_traits::reference reference; + typedef typename std::iterator_traits::value_type element_type; + + TEST_CONSTEXPR_CXX14 It base() const {return it_;} + + TEST_CONSTEXPR_CXX14 contiguous_iterator() : it_() {} + explicit TEST_CONSTEXPR_CXX14 contiguous_iterator(It it) : it_(it) {} + template + TEST_CONSTEXPR_CXX14 contiguous_iterator(const contiguous_iterator& u) :it_(u.it_) {} + + TEST_CONSTEXPR_CXX14 reference operator*() const {return *it_;} + TEST_CONSTEXPR_CXX14 pointer operator->() const {return it_;} + + TEST_CONSTEXPR_CXX14 contiguous_iterator& operator++() {++it_; return *this;} + TEST_CONSTEXPR_CXX14 contiguous_iterator operator++(int) + {contiguous_iterator tmp(*this); ++(*this); return tmp;} + + TEST_CONSTEXPR_CXX14 contiguous_iterator& operator--() {--it_; return *this;} + TEST_CONSTEXPR_CXX14 contiguous_iterator operator--(int) + {contiguous_iterator tmp(*this); --(*this); return tmp;} + + TEST_CONSTEXPR_CXX14 contiguous_iterator& operator+=(difference_type n) {it_ += n; return *this;} + TEST_CONSTEXPR_CXX14 contiguous_iterator operator+(difference_type n) const + {contiguous_iterator tmp(*this); tmp += n; return tmp;} + friend TEST_CONSTEXPR_CXX14 contiguous_iterator operator+(difference_type n, contiguous_iterator x) + {x += n; return x;} + TEST_CONSTEXPR_CXX14 contiguous_iterator& operator-=(difference_type n) {return *this += -n;} + TEST_CONSTEXPR_CXX14 contiguous_iterator operator-(difference_type n) const + {contiguous_iterator tmp(*this); tmp -= n; return tmp;} + + TEST_CONSTEXPR_CXX14 reference operator[](difference_type n) const {return it_[n];} + + template + void operator,(T const &) DELETE_FUNCTION; + + friend TEST_CONSTEXPR_CXX14 + difference_type operator-(const contiguous_iterator& x, const contiguous_iterator& y) { + return x.base() - y.base(); + } + + friend TEST_CONSTEXPR_CXX14 + difference_type operator<(const contiguous_iterator& x, const contiguous_iterator& y) { + return x.base() < y.base(); + } + friend TEST_CONSTEXPR_CXX14 + difference_type operator>(const contiguous_iterator& x, const contiguous_iterator& y) { + return x.base() > y.base(); + } + friend TEST_CONSTEXPR_CXX14 + difference_type operator<=(const contiguous_iterator& x, const contiguous_iterator& y) { + return x.base() <= y.base(); + } + friend TEST_CONSTEXPR_CXX14 + difference_type operator>=(const contiguous_iterator& x, const contiguous_iterator& y) { + return x.base() >= y.base(); + } + friend TEST_CONSTEXPR_CXX14 + difference_type operator==(const contiguous_iterator& x, const contiguous_iterator& y) { + return x.base() == y.base(); + } + friend TEST_CONSTEXPR_CXX14 + difference_type operator!=(const contiguous_iterator& x, const contiguous_iterator& y) { + return x.base() != y.base(); + } +}; +#endif + template inline TEST_CONSTEXPR_CXX14 Iter base(output_iterator i) { return i.base(); } @@ -326,6 +405,11 @@ template inline TEST_CONSTEXPR_CXX14 Iter base(random_access_iterator i) { return i.base(); } +#if TEST_STD_VER >= 20 +template +inline TEST_CONSTEXPR_CXX14 Iter base(contiguous_iterator i) { return i.base(); } +#endif + template // everything else inline TEST_CONSTEXPR_CXX14 Iter base(Iter i) { return i; }