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<unique_ptr>` hack that was present
in an attempt to make unique_ptr move only without language support.

llvm-svn: 364063
This commit is contained in:
Eric Fiselier 2019-06-21 15:20:55 +00:00
parent ddb9093684
commit 000f25a37e
12 changed files with 43 additions and 195 deletions

View File

@ -3181,11 +3181,7 @@ _SampleIterator sample(_PopulationIterator __first,
template<class _RandomAccessIterator, class _UniformRandomNumberGenerator>
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<ptrdiff_t> _Dp;

View File

@ -260,11 +260,7 @@ struct __throw_with_nested;
template <class _Tp, class _Up>
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 <class _Tp>
_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;

View File

@ -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 <class _Tp, class _Alloc>
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 <class _Tp, class _Alloc>
void
forward_list<_Tp, _Alloc>::remove(const value_type& __v)

View File

@ -2561,14 +2561,9 @@ public:
}
_LIBCPP_INLINE_VISIBILITY
operator __rv<unique_ptr>() {
return __rv<unique_ptr>(*this);
}
_LIBCPP_INLINE_VISIBILITY
unique_ptr(__rv<unique_ptr> __u)
: __ptr_(__u->release(),
_VSTD::forward<deleter_type>(__u->get_deleter())) {}
unique_ptr(unique_ptr&& __u)
: __ptr_(__u.release(),
_VSTD::forward<deleter_type>(__u.get_deleter())) {}
template <class _Up, class _Ep>
_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<deleter_type>(__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<deleter_type>(__d)) {}
_LIBCPP_INLINE_VISIBILITY
operator __rv<unique_ptr>() {
return __rv<unique_ptr>(*this);
}
unique_ptr(unique_ptr&& __u)
: __ptr_(__u.release(),
_VSTD::forward<deleter_type>(__u.get_deleter())) {}
_LIBCPP_INLINE_VISIBILITY
unique_ptr(__rv<unique_ptr> __u)
: __ptr_(__u->release(),
_VSTD::forward<deleter_type>(__u->get_deleter())) {}
_LIBCPP_INLINE_VISIBILITY
unique_ptr& operator=(__rv<unique_ptr> __u) {
reset(__u->release());
__ptr_.second() = _VSTD::forward<deleter_type>(__u->get_deleter());
unique_ptr& operator=(unique_ptr&& __u) {
reset(__u.release());
__ptr_.second() = _VSTD::forward<deleter_type>(__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 <class _Tp, class _Dp>
inline _LIBCPP_INLINE_VISIBILITY
unique_ptr<_Tp, _Dp>
move(unique_ptr<_Tp, _Dp>& __t)
{
return unique_ptr<_Tp, _Dp>(__rv<unique_ptr<_Tp, _Dp> >(__t));
}
#endif
#if _LIBCPP_STD_VER > 11
template<class _Tp>

View File

@ -2277,8 +2277,6 @@ _LIBCPP_INLINE_VAR _LIBCPP_CONSTEXPR bool is_destructible_v
// move
#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
template <class _Tp>
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 <class _Tp>
inline _LIBCPP_INLINE_VISIBILITY
_Tp&
move(_Tp& __t)
{
return __t;
}
template <class _Tp>
inline _LIBCPP_INLINE_VISIBILITY
const _Tp&
move(const _Tp& __t)
{
return __t;
}
template <class _Tp>
inline _LIBCPP_INLINE_VISIBILITY
_Tp&
forward(typename remove_reference<_Tp>::type& __t) _NOEXCEPT
{
return __t;
}
template <class _Tp>
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 <class _Tp>

View File

@ -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
// <functional>
// INVOKE (f, t1, t2, ..., tN)

View File

@ -21,16 +21,10 @@ const A csource() {return A();}
int main(int, char**)
{
#if TEST_STD_VER >= 11
{
std::forward<A&>(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<A&>(source()); // expected-error {{no matching function for call to 'forward'}}
}
#endif
{
const A ca = A();
std::forward<A&>(ca); // expected-error {{no matching function for call to 'forward'}}

View File

@ -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<int&>(x) == 42
@ -36,10 +36,8 @@ constexpr bool test_constexpr_forward() {
&& std::forward<const int&&>(x) == 42
&& std::forward<const int&>(cx) == 101
&& std::forward<const int>(cx) == 101;
#else
return true;
#endif
}
#endif
int main(int, char**)
{
@ -52,23 +50,23 @@ int main(int, char**)
static_assert(std::is_same<decltype(std::forward<A&>(a)), A&>::value, "");
static_assert(std::is_same<decltype(std::forward<A>(a)), A&&>::value, "");
static_assert(std::is_same<decltype(std::forward<A>(source())), A&&>::value, "");
static_assert(noexcept(std::forward<A&>(a)), "");
static_assert(noexcept(std::forward<A>(a)), "");
static_assert(noexcept(std::forward<A>(source())), "");
ASSERT_NOEXCEPT(std::forward<A&>(a));
ASSERT_NOEXCEPT(std::forward<A>(a));
ASSERT_NOEXCEPT(std::forward<A>(source()));
static_assert(std::is_same<decltype(std::forward<const A&>(a)), const A&>::value, "");
static_assert(std::is_same<decltype(std::forward<const A>(a)), const A&&>::value, "");
static_assert(std::is_same<decltype(std::forward<const A>(source())), const A&&>::value, "");
static_assert(noexcept(std::forward<const A&>(a)), "");
static_assert(noexcept(std::forward<const A>(a)), "");
static_assert(noexcept(std::forward<const A>(source())), "");
ASSERT_NOEXCEPT(std::forward<const A&>(a));
ASSERT_NOEXCEPT(std::forward<const A>(a));
ASSERT_NOEXCEPT(std::forward<const A>(source()));
static_assert(std::is_same<decltype(std::forward<const A&>(ca)), const A&>::value, "");
static_assert(std::is_same<decltype(std::forward<const A>(ca)), const A&&>::value, "");
static_assert(std::is_same<decltype(std::forward<const A>(csource())), const A&&>::value, "");
static_assert(noexcept(std::forward<const A&>(ca)), "");
static_assert(noexcept(std::forward<const A>(ca)), "");
static_assert(noexcept(std::forward<const A>(csource())), "");
ASSERT_NOEXCEPT(std::forward<const A&>(ca));
ASSERT_NOEXCEPT(std::forward<const A>(ca));
ASSERT_NOEXCEPT(std::forward<const A>(csource()));
#if TEST_STD_VER > 11
{

View File

@ -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 <utility>
#include <cassert>
#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&>(a))) == 1, "");
static_assert(sizeof(test(std::forward<A>(a))) == 1, "");
// Libc++'s C++03 implementation of 'forward' cannot accept true non-const
// rvalues.
// static_assert(sizeof(test(std::forward<A>(source()))) == 2, "");
static_assert(sizeof(test(std::forward<const A&>(a))) == 2, "");
static_assert(sizeof(test(std::forward<const A&>(source()))) == 2, "");
static_assert(sizeof(test(std::forward<const A>(a))) == 2, "");
static_assert(sizeof(test(std::forward<const A>(source()))) == 2, "");
static_assert(sizeof(test(std::forward<const A&>(ca))) == 2, "");
static_assert(sizeof(test(std::forward<const A&>(csource()))) == 2, "");
static_assert(sizeof(test(std::forward<const A>(ca))) == 2, "");
static_assert(sizeof(test(std::forward<const A>(csource()))) == 2, "");
#endif
return 0;
}

View File

@ -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;
}

View File

@ -8,8 +8,6 @@
// test move
// UNSUPPORTED: c++98, c++03
#include <utility>
#include <type_traits>
#include <cassert>
@ -36,7 +34,7 @@ int x = 42;
const int& cx = x;
template <class QualInt>
QualInt get() noexcept { return static_cast<QualInt>(x); }
QualInt get() TEST_NOEXCEPT { return static_cast<QualInt>(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<int&&>(y)) == 42
&& std::move(static_cast<int const&&>(y)) == 42;
#else
return true;
#endif
}
#endif
int main(int, char**)
{
{ // Test return type and noexcept.
static_assert(std::is_same<decltype(std::move(x)), int&&>::value, "");
static_assert(noexcept(std::move(x)), "");
ASSERT_NOEXCEPT(std::move(x));
static_assert(std::is_same<decltype(std::move(cx)), const int&&>::value, "");
static_assert(noexcept(std::move(cx)), "");
ASSERT_NOEXCEPT(std::move(cx));
static_assert(std::is_same<decltype(std::move(42)), int&&>::value, "");
static_assert(noexcept(std::move(42)), "");
ASSERT_NOEXCEPT(std::move(42));
static_assert(std::is_same<decltype(std::move(get<const int&&>())), const int&&>::value, "");
static_assert(noexcept(std::move(get<int const&&>())), "");
ASSERT_NOEXCEPT(std::move(get<int const&&>()));
}
{ // test copy and move semantics
A a;

View File

@ -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<decltype(std::move_if_noexcept(ca)), const A&&>::value), "");
static_assert((std::is_same<decltype(std::move_if_noexcept(l)), const legacy&>::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<decltype(std::move_if_noexcept(i)), const int&>::value), "");
static_assert((std::is_same<decltype(std::move_if_noexcept(ci)), const int&>::value), "");
static_assert((std::is_same<decltype(std::move_if_noexcept(a)), const A&>::value), "");