Skip to content

Commit

Permalink
[libc++] Extension: Make move and forward constexpr in C++11.
Browse files Browse the repository at this point in the history
Summary:
`std::move` and `std::forward` were not marked constexpr in C++11.  This can be very damaging because it makes otherwise constant expressions non-constant. For example:

```
#include <utility>
template <class T>
struct Foo {
  constexpr Foo(T&& tx) :  t(std::move(tx)) {}
  T t;
};
[[clang::require_constant_initialization]] Foo<int> f(42); // Foo should be constant initialized but C++11 move is not constexpr. As a result `f` is an unsafe global.
```

This patch applies `constexpr` to `move` and `forward` as an extension in C++11. Normally the library is not allowed to add `constexpr` because it may be observable to the user. In particular adding constexpr may cause valid code to stop compiling. However these problems only happen in more complex situations, like making `__invoke(...)` constexpr. `forward` and `move` are simply enough that applying `constexpr` is safe. 

Note that libstdc++ has offered this extension since at least 4.8.1.

Most of the changes in this patch are simply test cleanups or additions. The main changes in the tests are:

* Fold all `forward_N.fail.cpp` tests into a single `forward.fail.cpp` test using -verify.
* Delete most `move_only_N.fail.cpp` tests because they weren't actually testing anything.
* Fold `move_copy.pass.cpp` and `move_only.pass.cpp` into a single `move.pass.cpp` test.
* Add return type and noexcept tests for `forward` and `move`.




Reviewers: rsmith, mclow.lists, EricWF

Subscribers: K-ballo, loladiro

Differential Revision: https://reviews.llvm.org/D24637

llvm-svn: 282439
  • Loading branch information
EricWF committed Sep 26, 2016
1 parent fedd367 commit c24e6dd
Show file tree
Hide file tree
Showing 17 changed files with 305 additions and 481 deletions.
8 changes: 4 additions & 4 deletions libcxx/include/type_traits
Original file line number Diff line number Diff line change
Expand Up @@ -2155,7 +2155,7 @@ template <class _Tp> _LIBCPP_CONSTEXPR bool is_destructible_v
#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES

template <class _Tp>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
typename remove_reference<_Tp>::type&&
move(_Tp&& __t) _NOEXCEPT
{
Expand All @@ -2164,20 +2164,20 @@ move(_Tp&& __t) _NOEXCEPT
}

template <class _Tp>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
_Tp&&
forward(typename remove_reference<_Tp>::type& __t) _NOEXCEPT
{
return static_cast<_Tp&&>(__t);
}

template <class _Tp>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
_Tp&&
forward(typename remove_reference<_Tp>::type&& __t) _NOEXCEPT
{
static_assert(!is_lvalue_reference<_Tp>::value,
"Can not forward an rvalue as an lvalue.");
"can not forward an rvalue as an lvalue");
return static_cast<_Tp&&>(__t);
}

Expand Down
53 changes: 53 additions & 0 deletions libcxx/test/std/utilities/utility/forward/forward.fail.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// test forward

#include <utility>

#include "test_macros.h"

struct A
{
};

A source() {return A();}
const A csource() {return A();}

int main()
{
#if TEST_STD_VER >= 11
{
std::forward<A&>(source()); // expected-note {{requested here}}
// expected-error@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'}}
}
{
std::forward<A&>(csource()); // expected-error {{no matching function for call to 'forward'}}
}
{
const A ca = A();
std::forward<A>(ca); // expected-error {{no matching function for call to 'forward'}}
}
{
std::forward<A>(csource()); // expected-error {{no matching function for call to 'forward'}}
}
{
A a;
std::forward(a); // expected-error {{no matching function for call to 'forward'}}
}
}
114 changes: 61 additions & 53 deletions libcxx/test/std/utilities/utility/forward/forward.pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,40 @@
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++98, c++03

// test forward

#include <utility>
#include <type_traits>
#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&);

#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES

four test(A&&);
eight test(const A&&);

#endif // _LIBCPP_HAS_NO_RVALUE_REFERENCES
A source() noexcept {return A();}
const A csource() noexcept {return A();}


constexpr bool test_constexpr_forward() {
#if TEST_STD_VER > 11
int x = 42;
const int cx = 101;
return std::forward<int&>(x) == 42
&& std::forward<int>(x) == 42
&& std::forward<const int&>(x) == 42
&& std::forward<const int>(x) == 42
&& std::forward<int&&>(x) == 42
&& std::forward<const int&&>(x) == 42
&& std::forward<const int&>(cx) == 101
&& std::forward<const int>(cx) == 101;
#else
return true;
#endif
}

int main()
{
Expand All @@ -42,42 +50,42 @@ int main()
((void)a); // Prevent unused warning
((void)ca); // Prevent unused warning

#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
static_assert(sizeof(test(std::forward<A&>(a))) == 1, "");
static_assert(sizeof(test(std::forward<A>(a))) == 4, "");
static_assert(sizeof(test(std::forward<A>(source()))) == 4, "");

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))) == 8, "");
static_assert(sizeof(test(std::forward<const A>(source()))) == 8, "");

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))) == 8, "");
static_assert(sizeof(test(std::forward<const A>(csource()))) == 8, "");

#else // _LIBCPP_HAS_NO_RVALUE_REFERENCES

static_assert(sizeof(test(std::forward<A&>(a))) == 1, "");
static_assert(sizeof(test(std::forward<A>(a))) == 1, "");
// 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 // _LIBCPP_HAS_NO_RVALUE_REFERENCES

#if _LIBCPP_STD_VER > 11
constexpr int i1 = std::move(23);
static_assert(i1 == 23, "" );
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())), "");

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())), "");

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())), "");

#if TEST_STD_VER > 11
{
constexpr int i2 = std::forward<int>(42);
static_assert(std::forward<int>(42) == 42, "");
static_assert(std::forward<const int&>(i2) == 42, "");
static_assert(test_constexpr_forward(), "");
}
#endif
#if TEST_STD_VER == 11 && defined(_LIBCPP_VERSION)
// Test that std::forward is constexpr in C++11. This is an extension
// provided by both libc++ and libstdc++.
{
constexpr int i2 = std::forward<int>(42);
static_assert(i2 == 42, "" );
static_assert(std::forward<int>(42) == 42, "" );
static_assert(std::forward<const int&>(i2) == 42, "");
}
#endif
}
24 changes: 0 additions & 24 deletions libcxx/test/std/utilities/utility/forward/forward1.fail.cpp

This file was deleted.

25 changes: 0 additions & 25 deletions libcxx/test/std/utilities/utility/forward/forward2.fail.cpp

This file was deleted.

24 changes: 0 additions & 24 deletions libcxx/test/std/utilities/utility/forward/forward3.fail.cpp

This file was deleted.

25 changes: 0 additions & 25 deletions libcxx/test/std/utilities/utility/forward/forward4.fail.cpp

This file was deleted.

25 changes: 0 additions & 25 deletions libcxx/test/std/utilities/utility/forward/forward5.fail.cpp

This file was deleted.

22 changes: 0 additions & 22 deletions libcxx/test/std/utilities/utility/forward/forward6.fail.cpp

This file was deleted.

Loading

0 comments on commit c24e6dd

Please sign in to comment.