Index: include/memory =================================================================== --- include/memory +++ include/memory @@ -1612,7 +1612,8 @@ __construct_forward(allocator_type& __a, _Ptr __begin1, _Ptr __end1, _Ptr& __begin2) { for (; __begin1 != __end1; ++__begin1, (void) ++__begin2) - construct(__a, _VSTD::__to_raw_pointer(__begin2), _VSTD::move_if_noexcept(*__begin1)); + construct(__a, _VSTD::__to_raw_pointer(__begin2), + _VSTD::__extended_move_if_noexcept(*__begin1)); } template @@ -1676,8 +1677,9 @@ { while (__end1 != __begin1) { - construct(__a, _VSTD::__to_raw_pointer(__end2-1), _VSTD::move_if_noexcept(*--__end1)); - --__end2; + construct(__a, _VSTD::__to_raw_pointer(__end2 - 1), + _VSTD::__extended_move_if_noexcept(*--__end1)); + --__end2; } } Index: include/utility =================================================================== --- include/utility +++ include/utility @@ -273,20 +273,46 @@ } template -inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 +struct __noexcept_traits { +private: #ifndef _LIBCPP_CXX03_LANG -typename conditional -< - !is_nothrow_move_constructible<_Tp>::value && is_copy_constructible<_Tp>::value, - const _Tp&, - _Tp&& ->::type -#else // _LIBCPP_CXX03_LANG -const _Tp& + enum { + __exceptions_disabled = +#ifdef _LIBCPP_NO_EXCEPTIONS + true, +#else + false, #endif -move_if_noexcept(_Tp& __x) _NOEXCEPT -{ - return _VSTD::move(__x); + __move_if_noexcept = is_nothrow_move_constructible<_Tp>::value || + !is_copy_constructible<_Tp>::value, + }; + + template + using _SelectReturnType = + typename conditional<_Pred, _Tp&&, const _Tp&>::type; + +public: + using __move_if_noexcept_t = _SelectReturnType<__move_if_noexcept>; + using __extended_move_if_noexcept_t = + _SelectReturnType<__move_if_noexcept || __exceptions_disabled>; +#else + typedef const _Tp& __move_if_noexcept_t; + typedef const _Tp& __extended_move_if_noexcept_t; +#endif +}; + +template +inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 +typename __noexcept_traits<_Tp>::__move_if_noexcept_t +move_if_noexcept(_Tp& __x) _NOEXCEPT { + return _VSTD::move(__x); +} + +template +inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 +typename __noexcept_traits<_Tp>::__extended_move_if_noexcept_t +__extended_move_if_noexcept(_Tp& __x) _NOEXCEPT { + return _VSTD::move(__x); } #if _LIBCPP_STD_VER > 14 Index: test/libcxx/containers/sequences/vector/exception_safety_exceptions_disabled.sh.cpp =================================================================== --- /dev/null +++ test/libcxx/containers/sequences/vector/exception_safety_exceptions_disabled.sh.cpp @@ -0,0 +1,57 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// RUN: %build -fno-exceptions +// RUN: %run + +// UNSUPPORTED: c++98, c++03 + +// + +// Test that vector always moves elements when exceptions are disabled. +// vector is allowed to move or copy elements while resizing, so long as +// it still provides the strong exception safety guarantee. + +#include +#include + +#include "test_macros.h" + +#ifndef TEST_HAS_NO_EXCEPTIONS +#error exceptions should be disabled. +#endif + +bool allow_moves = false; + +class A { +public: + A() {} + A(A&&) { assert(allow_moves); } + explicit A(int) {} + A(A const&) { assert(false); } +}; + +int main(int, char**) { + std::vector v; + + // Create a vector containing some number of elements that will + // have to be moved when it is resized. + v.reserve(10); + size_t old_cap = v.capacity(); + for (int i = 0; i < v.capacity(); ++i) { + v.emplace_back(42); + } + assert(v.capacity() == old_cap); + assert(v.size() == v.capacity()); + + // The next emplace back should resize. + allow_moves = true; + v.emplace_back(42); + + return 0; +} Index: test/libcxx/utilities/utility/forward/extended_move_if_noexcept.sh.cpp =================================================================== --- /dev/null +++ test/libcxx/utilities/utility/forward/extended_move_if_noexcept.sh.cpp @@ -0,0 +1,112 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// RUN: %build -fexceptions -DHAVE_EXCEPTIONS +// RUN: %run +// RUN: %build -fno-exceptions -DNO_EXCEPTIONS +// RUN: %run + +// It's not clear if this test will always be able to override builds +// where exceptions are disabled. +// +// UNSUPPORTED: libcpp-no-exceptions + +// + +// template +// auto __extended_move_if_noexcept(_Tp&) -> see below + +// __extended_move_if_noexcept is similar to `move_if_noexcept` except +// that it unconditionally moves when exceptions are disabled. + +// __extended_move_if_noexcept is used by containers that need to provide +// the strong exception safety guarantee for certain modifying operations. +// +// When exceptions are disabled strong exception safety guarantee is trivially +// satisfied and the container is allowed to optimize its implementation by +// unconditionally moving. + +#include + +#include "test_macros.h" + +#if !defined(HAVE_EXCEPTIONS) && !defined(NO_EXCEPTIONS) +#error Incorrect test setup +#endif + + +class A +{ + A(const A&); + A& operator=(const A&); +public: + + A() {} +#if TEST_STD_VER >= 11 + A(A&&) {} +#endif +}; + +struct legacy +{ + legacy() {} + legacy(const legacy&); +}; + +template +void test_standard_behavior(Input& in) { + ASSERT_SAME_TYPE(decltype(std::__extended_move_if_noexcept(in)), decltype(std::move_if_noexcept(in))); + ASSERT_SAME_TYPE(decltype(std::__extended_move_if_noexcept(in)), Expect); + ASSERT_NOEXCEPT(std::__extended_move_if_noexcept(in)); +} + +template +void test_extended_behavior(Input& in) { + ASSERT_SAME_TYPE(decltype(std::__extended_move_if_noexcept(in)), Expect); + static_assert(!std::is_same::value, ""); + ASSERT_NOEXCEPT(std::__extended_move_if_noexcept(in)); +} + +int main(int, char**) +{ + int i = 0; + const int ci = 0; + + legacy l; + A a; + const A ca; + +#if TEST_STD_VER >= 11 + test_standard_behavior(i); + test_standard_behavior(ci); + test_standard_behavior(a); + test_standard_behavior(ca); +#ifdef NO_EXCEPTIONS + test_extended_behavior(l); +#else + test_standard_behavior(l); +#endif +#else // C++ < 11 + // In C++03 libc++ #define's decltype to be __decltype on clang and + // __typeof__ for other compilers. __typeof__ does not deduce the reference + // qualifiers and will cause this test to fail. + test_standard_behavior(i); + test_standard_behavior(ci); + test_standard_behavior(a); + test_standard_behavior(ca); + test_standard_behavior(l); +#endif + +#if TEST_STD_VER > 11 + constexpr int i1 = 23; + constexpr int i2 = std::__extended_move_if_noexcept(i1); + static_assert(i2 == 23, "" ); +#endif + + return 0; +}