diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -1645,8 +1645,17 @@ // (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. +// Furthermore, we can't do this for user-defined iterators because their +// `to_address` may be non-constexpr and/or have observable side effects. +// Furthermore, some algorithms (e.g. std::copy, but not std::sort) need +// to convert the "unwrapped" pointer back into an iterator, for which there +// is no standard API: we rely on implicit conversion to do the right thing. +// For user-defined iterators this isn't a conforming assumption. +// So the only type we actually unwrap is our own __wrap_iter. -template ::value> +template class __wrap_iter; + +template struct __unwrap_iter_impl { static _LIBCPP_CONSTEXPR _Iter __apply(_Iter __i) _NOEXCEPT { @@ -1657,10 +1666,10 @@ #if _LIBCPP_DEBUG_LEVEL < 2 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); +struct __unwrap_iter_impl<__wrap_iter<_Iter> > { + static _LIBCPP_CONSTEXPR _Iter + __apply(__wrap_iter<_Iter> __i) _NOEXCEPT { + return __i.base(); } }; @@ -1716,11 +1725,11 @@ copy(_InputIterator __first, _InputIterator __last, _OutputIterator __result) { if (__libcpp_is_constant_evaluated()) { - return _VSTD::__copy_constexpr( - _VSTD::__unwrap_iter(__first), _VSTD::__unwrap_iter(__last), _VSTD::__unwrap_iter(__result)); + return _VSTD::__copy_constexpr(__first, __last, __result); } else { - return _VSTD::__copy( - _VSTD::__unwrap_iter(__first), _VSTD::__unwrap_iter(__last), _VSTD::__unwrap_iter(__result)); + return _VSTD::__copy(_VSTD::__unwrap_iter(__first), + _VSTD::__unwrap_iter(__last), + _VSTD::__unwrap_iter(__result)); } } @@ -1770,9 +1779,7 @@ _BidirectionalIterator2 __result) { if (__libcpp_is_constant_evaluated()) { - return _VSTD::__copy_backward_constexpr(_VSTD::__unwrap_iter(__first), - _VSTD::__unwrap_iter(__last), - _VSTD::__unwrap_iter(__result)); + return _VSTD::__copy_backward_constexpr(__first, __last, __result); } else { return _VSTD::__copy_backward(_VSTD::__unwrap_iter(__first), _VSTD::__unwrap_iter(__last), @@ -1843,8 +1850,6 @@ // move -// __move_constexpr exists so that __move doesn't call itself when delegating to the constexpr -// version of __move. template inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 _OutputIterator @@ -1873,8 +1878,6 @@ >::type __move(_Tp* __first, _Tp* __last, _Up* __result) { - if (__libcpp_is_constant_evaluated()) - return _VSTD::__move_constexpr(__first, __last, __result); const size_t __n = static_cast(__last - __first); if (__n > 0) _VSTD::memmove(__result, __first, __n * sizeof(_Up)); @@ -1886,13 +1889,17 @@ _OutputIterator move(_InputIterator __first, _InputIterator __last, _OutputIterator __result) { - return _VSTD::__move(_VSTD::__unwrap_iter(__first), _VSTD::__unwrap_iter(__last), _VSTD::__unwrap_iter(__result)); + if (__libcpp_is_constant_evaluated()) { + return _VSTD::__move_constexpr(__first, __last, __result); + } else { + return _VSTD::__move(_VSTD::__unwrap_iter(__first), + _VSTD::__unwrap_iter(__last), + _VSTD::__unwrap_iter(__result)); + } } // move_backward -// __move_backward_constexpr exists so that __move_backward doesn't call itself when delegating to -// the constexpr version of __move_backward. template inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14 _OutputIterator @@ -1921,8 +1928,6 @@ >::type __move_backward(_Tp* __first, _Tp* __last, _Up* __result) { - if (__libcpp_is_constant_evaluated()) - return _VSTD::__move_backward_constexpr(__first, __last, __result); const size_t __n = static_cast(__last - __first); if (__n > 0) { @@ -1938,7 +1943,13 @@ move_backward(_BidirectionalIterator1 __first, _BidirectionalIterator1 __last, _BidirectionalIterator2 __result) { - return _VSTD::__move_backward(_VSTD::__unwrap_iter(__first), _VSTD::__unwrap_iter(__last), _VSTD::__unwrap_iter(__result)); + if (__libcpp_is_constant_evaluated()) { + return _VSTD::__move_backward_constexpr(__first, __last, __result); + } else { + return _VSTD::__move_backward(_VSTD::__unwrap_iter(__first), + _VSTD::__unwrap_iter(__last), + _VSTD::__unwrap_iter(__result)); + } } // iter_swap diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/contiguous_nonconstexpr.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/contiguous_nonconstexpr.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/contiguous_nonconstexpr.pass.cpp @@ -0,0 +1,60 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++11, c++14, c++17 + +// + +#include +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" + +struct NCCI : public contiguous_iterator { + using contiguous_iterator::contiguous_iterator; +}; + +template<> +struct std::pointer_traits { + static int *to_address(NCCI it) { + return it.base(); + } +}; + +constexpr bool test() +{ + int ia[4] = {1,2,3,4}; + int ib[4] = {0,0,0,0}; + + std::copy(NCCI(ia), NCCI(ia+4), ib); + std::copy(ia, ia+4, NCCI(ib)); + + std::copy_backward(NCCI(ia), NCCI(ia+4), ib+4); + std::copy_backward(ia, ia+4, NCCI(ib+4)); + + std::move(NCCI(ia), NCCI(ia+4), ib); + std::move(ia, ia+4, NCCI(ib)); + + std::move_backward(NCCI(ia), NCCI(ia+4), ib+4); + std::move_backward(ia, ia+4, NCCI(ib+4)); + + return true; +} + + +int main(int, char**) +{ + static_assert(test()); + + return 0; +}