Skip to content

Commit e43c93d

Browse files
committed
Don't treat 'T &forward(T&&)' as builtin.
This allows the standard library to diagnose it properly. Suppress warning in libc++ testsuite for unused result of call to std::forward.
1 parent b8a929c commit e43c93d

File tree

3 files changed

+27
-1
lines changed

3 files changed

+27
-1
lines changed

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,20 @@ static bool isRelevantAttr(Sema &S, const Decl *D, const Attr *A) {
621621
return true;
622622
}
623623

624+
if (const auto *BA = dyn_cast<BuiltinAttr>(A)) {
625+
// Do not treat 'std::forward' as a builtin if it takes an rvalue reference
626+
// type and returns an lvalue reference type. The library implementation
627+
// will produce an error in this case; don't get in its way.
628+
if (BA->getID() == Builtin::BIforward) {
629+
const FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
630+
if (FD && FD->getNumParams() >= 1 &&
631+
FD->getParamDecl(0)->getType()->isRValueReferenceType() &&
632+
FD->getReturnType()->isLValueReferenceType()) {
633+
return false;
634+
}
635+
}
636+
}
637+
624638
return true;
625639
}
626640

clang/test/SemaCXX/builtin-std-move.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,18 @@ namespace std {
3030
template<typename T> struct remove_reference<T&> { using type = T; };
3131
template<typename T> struct remove_reference<T&&> { using type = T; };
3232

33+
template<typename T> struct is_lvalue_reference { static constexpr bool value = false; };
34+
template<typename T> struct is_lvalue_reference<T&> { static constexpr bool value = true; };
35+
3336
template<typename T> CONSTEXPR T &&forward(typename remove_reference<T>::type &x) {
3437
static_assert(T::moveable, "instantiated forward"); // expected-error {{no member named 'moveable' in 'B'}}
3538
// expected-error@-1 {{no member named 'moveable' in 'C'}}
3639
return static_cast<T&&>(x);
3740
}
41+
template<typename T> CONSTEXPR T &&forward(typename remove_reference<T>::type &&x) {
42+
static_assert(!is_lvalue_reference<T>::value, "should not forward rval as lval"); // expected-error {{static_assert failed}}
43+
return static_cast<T&&>(x);
44+
}
3845

3946
template<typename T> CONSTEXPR const T &as_const(T &x) {
4047
static_assert(T::moveable, "instantiated as_const"); // expected-error {{no member named 'moveable' in 'B'}}
@@ -76,6 +83,11 @@ static_assert(f({}), "should be constexpr");
7683
// expected-note@#call {{}}
7784
#endif
7885

86+
A &forward_rval_as_lval() {
87+
std::forward<A&&>(A()); // expected-warning {{const attribute}}
88+
return std::forward<A&>(A()); // expected-note {{instantiation of}}
89+
}
90+
7991
struct B {};
8092
B &&(*pMove)(B&) = std::move; // #1 expected-note {{instantiation of}}
8193
B &&(*pMoveIfNoexcept)(B&) = &std::move_if_noexcept; // #2 expected-note {{instantiation of}}

libcxx/test/std/utilities/utility/forward/forward.fail.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const A csource() {return A();}
2222
int main(int, char**)
2323
{
2424
{
25-
std::forward<A&>(source()); // expected-note {{requested here}}
25+
(void)std::forward<A&>(source()); // expected-note {{requested here}}
2626
// expected-error-re@*:* 1 {{static_assert failed{{.*}} "cannot forward an rvalue as an lvalue"}}
2727
}
2828
{

0 commit comments

Comments
 (0)