From 000f25a37e76317aa78b0f6dd9283ba41af94591 Mon Sep 17 00:00:00 2001 From: Eric Fiselier Date: Fri, 21 Jun 2019 15:20:55 +0000 Subject: [PATCH] Make move and forward work in C++03. These functions are key to allowing the use of rvalues and variadics in C++03 mode. Everything works the same as in C++11, except for one tangentially related case: struct T { T(T &&) = default; }; In C++11, T has a deleted copy constructor. But in C++03 Clang gives it both a move and a copy constructor. This seems reasonable enough given the extensions it's using. The other changes in this patch were the minimal set required to keep the tests passing after the move/forward change. Most notably the removal of the `__rv` hack that was present in an attempt to make unique_ptr move only without language support. llvm-svn: 364063 --- libcxx/include/algorithm | 4 -- libcxx/include/exception | 8 --- libcxx/include/forward_list | 6 -- libcxx/include/memory | 42 ++++--------- libcxx/include/type_traits | 43 -------------- .../func.require/bullet_4_5_6.pass.cpp | 4 ++ .../utility/forward/forward.fail.cpp | 6 -- .../utility/forward/forward.pass.cpp | 28 ++++----- .../utility/forward/forward_03.pass.cpp | 59 ------------------- .../utilities/utility/forward/move.fail.cpp | 13 ++-- .../utilities/utility/forward/move.pass.cpp | 19 +++--- .../utility/forward/move_if_noexcept.pass.cpp | 6 +- 12 files changed, 43 insertions(+), 195 deletions(-) delete mode 100644 libcxx/test/std/utilities/utility/forward/forward_03.pass.cpp diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm index 326e566fbf78..d17666a29557 100644 --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -3181,11 +3181,7 @@ _SampleIterator sample(_PopulationIterator __first, template void shuffle(_RandomAccessIterator __first, _RandomAccessIterator __last, -#ifndef _LIBCPP_CXX03_LANG _UniformRandomNumberGenerator&& __g) -#else - _UniformRandomNumberGenerator& __g) -#endif { typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type; typedef uniform_int_distribution _Dp; diff --git a/libcxx/include/exception b/libcxx/include/exception index 05ff6015075f..c7dcac2b2b33 100644 --- a/libcxx/include/exception +++ b/libcxx/include/exception @@ -260,11 +260,7 @@ struct __throw_with_nested; template struct __throw_with_nested<_Tp, _Up, true> { _LIBCPP_NORETURN static inline _LIBCPP_INLINE_VISIBILITY void -#ifndef _LIBCPP_CXX03_LANG __do_throw(_Tp&& __t) -#else - __do_throw (_Tp& __t) -#endif // _LIBCPP_CXX03_LANG { throw __nested<_Up>(_VSTD::forward<_Tp>(__t)); } @@ -287,11 +283,7 @@ struct __throw_with_nested<_Tp, _Up, false> { template _LIBCPP_NORETURN void -#ifndef _LIBCPP_CXX03_LANG throw_with_nested(_Tp&& __t) -#else -throw_with_nested (_Tp& __t) -#endif // _LIBCPP_CXX03_LANG { #ifndef _LIBCPP_NO_EXCEPTIONS typedef typename decay<_Tp>::type _Up; diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list index 54021d0a03e8..6316de980d20 100644 --- a/libcxx/include/forward_list +++ b/libcxx/include/forward_list @@ -808,7 +808,6 @@ public: _LIBCPP_INLINE_VISIBILITY void clear() _NOEXCEPT {base::clear();} -#ifndef _LIBCPP_CXX03_LANG _LIBCPP_INLINE_VISIBILITY void splice_after(const_iterator __p, forward_list&& __x); _LIBCPP_INLINE_VISIBILITY @@ -816,7 +815,6 @@ public: _LIBCPP_INLINE_VISIBILITY void splice_after(const_iterator __p, forward_list&& __x, const_iterator __f, const_iterator __l); -#endif // _LIBCPP_CXX03_LANG void splice_after(const_iterator __p, forward_list& __x); void splice_after(const_iterator __p, forward_list& __x, const_iterator __i); void splice_after(const_iterator __p, forward_list& __x, @@ -1468,8 +1466,6 @@ forward_list<_Tp, _Alloc>::splice_after(const_iterator __p, } } -#ifndef _LIBCPP_CXX03_LANG - template inline _LIBCPP_INLINE_VISIBILITY void @@ -1499,8 +1495,6 @@ forward_list<_Tp, _Alloc>::splice_after(const_iterator __p, splice_after(__p, __x, __f, __l); } -#endif // _LIBCPP_CXX03_LANG - template void forward_list<_Tp, _Alloc>::remove(const value_type& __v) diff --git a/libcxx/include/memory b/libcxx/include/memory index b8a2706570f0..cabf3d7cd743 100644 --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -2561,14 +2561,9 @@ public: } _LIBCPP_INLINE_VISIBILITY - operator __rv() { - return __rv(*this); - } - - _LIBCPP_INLINE_VISIBILITY - unique_ptr(__rv __u) - : __ptr_(__u->release(), - _VSTD::forward(__u->get_deleter())) {} + unique_ptr(unique_ptr&& __u) + : __ptr_(__u.release(), + _VSTD::forward(__u.get_deleter())) {} template _LIBCPP_INLINE_VISIBILITY @@ -2586,7 +2581,7 @@ public: _LIBCPP_INLINE_VISIBILITY unique_ptr(pointer __p, deleter_type __d) - : __ptr_(_VSTD::move(__p), _VSTD::move(__d)) {} + : __ptr_(__p, _VSTD::forward(__d)) {} #endif // _LIBCPP_CXX03_LANG #if _LIBCPP_STD_VER <= 14 || defined(_LIBCPP_ENABLE_CXX17_REMOVED_AUTO_PTR) @@ -2863,19 +2858,14 @@ public: : __ptr_(pointer(), _VSTD::forward(__d)) {} _LIBCPP_INLINE_VISIBILITY - operator __rv() { - return __rv(*this); - } + unique_ptr(unique_ptr&& __u) + : __ptr_(__u.release(), + _VSTD::forward(__u.get_deleter())) {} _LIBCPP_INLINE_VISIBILITY - unique_ptr(__rv __u) - : __ptr_(__u->release(), - _VSTD::forward(__u->get_deleter())) {} - - _LIBCPP_INLINE_VISIBILITY - unique_ptr& operator=(__rv __u) { - reset(__u->release()); - __ptr_.second() = _VSTD::forward(__u->get_deleter()); + unique_ptr& operator=(unique_ptr&& __u) { + reset(__u.release()); + __ptr_.second() = _VSTD::forward(__u.get_deleter()); return *this; } @@ -3091,18 +3081,6 @@ operator>=(nullptr_t, const unique_ptr<_T1, _D1>& __x) return !(nullptr < __x); } -#ifdef _LIBCPP_HAS_NO_RVALUE_REFERENCES - -template -inline _LIBCPP_INLINE_VISIBILITY -unique_ptr<_Tp, _Dp> -move(unique_ptr<_Tp, _Dp>& __t) -{ - return unique_ptr<_Tp, _Dp>(__rv >(__t)); -} - -#endif - #if _LIBCPP_STD_VER > 11 template diff --git a/libcxx/include/type_traits b/libcxx/include/type_traits index 7b2f342911f2..67c91776a74e 100644 --- a/libcxx/include/type_traits +++ b/libcxx/include/type_traits @@ -2277,8 +2277,6 @@ _LIBCPP_INLINE_VAR _LIBCPP_CONSTEXPR bool is_destructible_v // move -#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES - template inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR typename remove_reference<_Tp>::type&& @@ -2306,47 +2304,6 @@ forward(typename remove_reference<_Tp>::type&& __t) _NOEXCEPT return static_cast<_Tp&&>(__t); } -#else // _LIBCPP_HAS_NO_RVALUE_REFERENCES - -template -inline _LIBCPP_INLINE_VISIBILITY -_Tp& -move(_Tp& __t) -{ - return __t; -} - -template -inline _LIBCPP_INLINE_VISIBILITY -const _Tp& -move(const _Tp& __t) -{ - return __t; -} - -template -inline _LIBCPP_INLINE_VISIBILITY -_Tp& -forward(typename remove_reference<_Tp>::type& __t) _NOEXCEPT -{ - return __t; -} - - -template -class __rv -{ - typedef typename remove_reference<_Tp>::type _Trr; - _Trr& t_; -public: - _LIBCPP_INLINE_VISIBILITY - _Trr* operator->() {return &t_;} - _LIBCPP_INLINE_VISIBILITY - explicit __rv(_Trr& __t) : t_(__t) {} -}; - -#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES - #ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES template diff --git a/libcxx/test/libcxx/utilities/function.objects/func.require/bullet_4_5_6.pass.cpp b/libcxx/test/libcxx/utilities/function.objects/func.require/bullet_4_5_6.pass.cpp index ff7549d29c9a..dc6ae95285da 100644 --- a/libcxx/test/libcxx/utilities/function.objects/func.require/bullet_4_5_6.pass.cpp +++ b/libcxx/test/libcxx/utilities/function.objects/func.require/bullet_4_5_6.pass.cpp @@ -6,6 +6,10 @@ // //===----------------------------------------------------------------------===// +// FIXME(EricWF): Make this test pass in C++03 with Clang once the transition +// has gotten far enough that __invoke works. +// XFAIL: c++98 || c++03 + // // INVOKE (f, t1, t2, ..., tN) diff --git a/libcxx/test/std/utilities/utility/forward/forward.fail.cpp b/libcxx/test/std/utilities/utility/forward/forward.fail.cpp index 4efc1b6aad15..7dcdf25c5386 100644 --- a/libcxx/test/std/utilities/utility/forward/forward.fail.cpp +++ b/libcxx/test/std/utilities/utility/forward/forward.fail.cpp @@ -21,16 +21,10 @@ const A csource() {return A();} int main(int, char**) { -#if TEST_STD_VER >= 11 { std::forward(source()); // expected-note {{requested here}} // expected-error-re@type_traits:* 1 {{static_assert failed{{.*}} "can not forward an rvalue as an lvalue"}} } -#else - { - std::forward(source()); // expected-error {{no matching function for call to 'forward'}} - } -#endif { const A ca = A(); std::forward(ca); // expected-error {{no matching function for call to 'forward'}} diff --git a/libcxx/test/std/utilities/utility/forward/forward.pass.cpp b/libcxx/test/std/utilities/utility/forward/forward.pass.cpp index b2f7c960392d..ed8a6705c3f6 100644 --- a/libcxx/test/std/utilities/utility/forward/forward.pass.cpp +++ b/libcxx/test/std/utilities/utility/forward/forward.pass.cpp @@ -20,12 +20,12 @@ struct A { }; -A source() noexcept {return A();} -const A csource() noexcept {return A();} +A source() TEST_NOEXCEPT {return A();} +const A csource() TEST_NOEXCEPT {return A();} -constexpr bool test_constexpr_forward() { #if TEST_STD_VER > 11 +constexpr bool test_constexpr_forward() { int x = 42; const int cx = 101; return std::forward(x) == 42 @@ -36,10 +36,8 @@ constexpr bool test_constexpr_forward() { && std::forward(x) == 42 && std::forward(cx) == 101 && std::forward(cx) == 101; -#else - return true; -#endif } +#endif int main(int, char**) { @@ -52,23 +50,23 @@ int main(int, char**) static_assert(std::is_same(a)), A&>::value, ""); static_assert(std::is_same(a)), A&&>::value, ""); static_assert(std::is_same(source())), A&&>::value, ""); - static_assert(noexcept(std::forward(a)), ""); - static_assert(noexcept(std::forward(a)), ""); - static_assert(noexcept(std::forward(source())), ""); + ASSERT_NOEXCEPT(std::forward(a)); + ASSERT_NOEXCEPT(std::forward(a)); + ASSERT_NOEXCEPT(std::forward(source())); static_assert(std::is_same(a)), const A&>::value, ""); static_assert(std::is_same(a)), const A&&>::value, ""); static_assert(std::is_same(source())), const A&&>::value, ""); - static_assert(noexcept(std::forward(a)), ""); - static_assert(noexcept(std::forward(a)), ""); - static_assert(noexcept(std::forward(source())), ""); + ASSERT_NOEXCEPT(std::forward(a)); + ASSERT_NOEXCEPT(std::forward(a)); + ASSERT_NOEXCEPT(std::forward(source())); static_assert(std::is_same(ca)), const A&>::value, ""); static_assert(std::is_same(ca)), const A&&>::value, ""); static_assert(std::is_same(csource())), const A&&>::value, ""); - static_assert(noexcept(std::forward(ca)), ""); - static_assert(noexcept(std::forward(ca)), ""); - static_assert(noexcept(std::forward(csource())), ""); + ASSERT_NOEXCEPT(std::forward(ca)); + ASSERT_NOEXCEPT(std::forward(ca)); + ASSERT_NOEXCEPT(std::forward(csource())); #if TEST_STD_VER > 11 { diff --git a/libcxx/test/std/utilities/utility/forward/forward_03.pass.cpp b/libcxx/test/std/utilities/utility/forward/forward_03.pass.cpp deleted file mode 100644 index 522382dc7ed9..000000000000 --- a/libcxx/test/std/utilities/utility/forward/forward_03.pass.cpp +++ /dev/null @@ -1,59 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -// test forward - -#include -#include - -#include "test_macros.h" - -struct A -{ -}; - -A source() {return A();} -const A csource() {return A();} - -typedef char one; -struct two {one _[2];}; -struct four {one _[4];}; -struct eight {one _[8];}; - -one test(A&); -two test(const A&); - -int main(int, char**) -{ - A a; - const A ca = A(); - - ((void)a); // Prevent unused warning - ((void)ca); // Prevent unused warning - -#if TEST_STD_VER < 11 - static_assert(sizeof(test(std::forward(a))) == 1, ""); - static_assert(sizeof(test(std::forward(a))) == 1, ""); - - // Libc++'s C++03 implementation of 'forward' cannot accept true non-const - // rvalues. - // static_assert(sizeof(test(std::forward(source()))) == 2, ""); - - static_assert(sizeof(test(std::forward(a))) == 2, ""); - static_assert(sizeof(test(std::forward(source()))) == 2, ""); - static_assert(sizeof(test(std::forward(a))) == 2, ""); - static_assert(sizeof(test(std::forward(source()))) == 2, ""); - - static_assert(sizeof(test(std::forward(ca))) == 2, ""); - static_assert(sizeof(test(std::forward(csource()))) == 2, ""); - static_assert(sizeof(test(std::forward(ca))) == 2, ""); - static_assert(sizeof(test(std::forward(csource()))) == 2, ""); -#endif - - return 0; -} diff --git a/libcxx/test/std/utilities/utility/forward/move.fail.cpp b/libcxx/test/std/utilities/utility/forward/move.fail.cpp index 86f3cc3e5d0d..2e6c3892479d 100644 --- a/libcxx/test/std/utilities/utility/forward/move.fail.cpp +++ b/libcxx/test/std/utilities/utility/forward/move.fail.cpp @@ -6,7 +6,11 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++98, c++03 +// This test should pass in C++03 with Clang extensions because Clang does +// not implicitly delete the copy constructor when move constructors are +// defaulted using extensions. + +// XFAIL: c++98, c++03 // test move @@ -26,10 +30,9 @@ void test(move_only) {} int main(int, char**) { - move_only a; - const move_only ca = move_only(); - - test(std::move(ca)); // c + const move_only ca = move_only(); + // expected-error@+1 {{call to implicitly-deleted copy constructor of 'move_only'}} + test(std::move(ca)); return 0; } diff --git a/libcxx/test/std/utilities/utility/forward/move.pass.cpp b/libcxx/test/std/utilities/utility/forward/move.pass.cpp index 3db61cda27d1..64d4911a3395 100644 --- a/libcxx/test/std/utilities/utility/forward/move.pass.cpp +++ b/libcxx/test/std/utilities/utility/forward/move.pass.cpp @@ -8,8 +8,6 @@ // test move -// UNSUPPORTED: c++98, c++03 - #include #include #include @@ -36,7 +34,7 @@ int x = 42; const int& cx = x; template -QualInt get() noexcept { return static_cast(x); } +QualInt get() TEST_NOEXCEPT { return static_cast(x); } int copy_ctor = 0; @@ -49,30 +47,27 @@ struct A { A& operator=(const A&) = delete; }; -constexpr bool test_constexpr_move() { #if TEST_STD_VER > 11 +constexpr bool test_constexpr_move() { int y = 42; const int cy = y; return std::move(y) == 42 && std::move(cy) == 42 && std::move(static_cast(y)) == 42 && std::move(static_cast(y)) == 42; -#else - return true; -#endif } - +#endif int main(int, char**) { { // Test return type and noexcept. static_assert(std::is_same::value, ""); - static_assert(noexcept(std::move(x)), ""); + ASSERT_NOEXCEPT(std::move(x)); static_assert(std::is_same::value, ""); - static_assert(noexcept(std::move(cx)), ""); + ASSERT_NOEXCEPT(std::move(cx)); static_assert(std::is_same::value, ""); - static_assert(noexcept(std::move(42)), ""); + ASSERT_NOEXCEPT(std::move(42)); static_assert(std::is_same())), const int&&>::value, ""); - static_assert(noexcept(std::move(get())), ""); + ASSERT_NOEXCEPT(std::move(get())); } { // test copy and move semantics A a; diff --git a/libcxx/test/std/utilities/utility/forward/move_if_noexcept.pass.cpp b/libcxx/test/std/utilities/utility/forward/move_if_noexcept.pass.cpp index 11ea3c58f097..3d7e762024cb 100644 --- a/libcxx/test/std/utilities/utility/forward/move_if_noexcept.pass.cpp +++ b/libcxx/test/std/utilities/utility/forward/move_if_noexcept.pass.cpp @@ -28,9 +28,7 @@ class A public: A() {} -#if TEST_STD_VER >= 11 A(A&&) {} -#endif }; struct legacy @@ -55,9 +53,7 @@ int main(int, char**) static_assert((std::is_same::value), ""); static_assert((std::is_same::value), ""); #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. + // In C++03 we don't have noexcept so we can never move :-( static_assert((std::is_same::value), ""); static_assert((std::is_same::value), ""); static_assert((std::is_same::value), "");