26 changes: 26 additions & 0 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2130,6 +2130,32 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,

TheCall->setType(Context.VoidPtrTy);
break;
case Builtin::BIaddressof:
case Builtin::BI__addressof:
case Builtin::BIforward:
case Builtin::BImove:
case Builtin::BImove_if_noexcept:
case Builtin::BIas_const: {
// These are all expected to be of the form
// T &/&&/* f(U &/&&)
// where T and U only differ in qualification.
if (checkArgCount(*this, TheCall, 1))
return ExprError();
QualType Param = FDecl->getParamDecl(0)->getType();
QualType Result = FDecl->getReturnType();
bool ReturnsPointer = BuiltinID == Builtin::BIaddressof ||
BuiltinID == Builtin::BI__addressof;
if (!(Param->isReferenceType() &&
(ReturnsPointer ? Result->isPointerType()
: Result->isReferenceType()) &&
Context.hasSameUnqualifiedType(Param->getPointeeType(),
Result->getPointeeType()))) {
Diag(TheCall->getBeginLoc(), diag::err_builtin_move_forward_unsupported)
<< FDecl;
return ExprError();
}
break;
}
// OpenCL v2.0, s6.13.16 - Pipe functions
case Builtin::BIread_pipe:
case Builtin::BIwrite_pipe:
Expand Down
64 changes: 46 additions & 18 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9276,6 +9276,32 @@ static Scope *getTagInjectionScope(Scope *S, const LangOptions &LangOpts) {
return S;
}

/// Determine whether a declaration matches a known function in namespace std.
static bool isStdBuiltin(ASTContext &Ctx, FunctionDecl *FD,
unsigned BuiltinID) {
switch (BuiltinID) {
case Builtin::BI__GetExceptionInfo:
// No type checking whatsoever.
return Ctx.getTargetInfo().getCXXABI().isMicrosoft();

case Builtin::BIaddressof:
case Builtin::BI__addressof:
case Builtin::BIforward:
case Builtin::BImove:
case Builtin::BImove_if_noexcept:
case Builtin::BIas_const: {
// Ensure that we don't treat the algorithm
// OutputIt std::move(InputIt, InputIt, OutputIt)
// as the builtin std::move.
const auto *FPT = FD->getType()->castAs<FunctionProtoType>();
return FPT->getNumParams() == 1 && !FPT->isVariadic();
}

default:
return false;
}
}

NamedDecl*
Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
TypeSourceInfo *TInfo, LookupResult &Previous,
Expand Down Expand Up @@ -10128,28 +10154,30 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,

// If this is the first declaration of a library builtin function, add
// attributes as appropriate.
if (!D.isRedeclaration() &&
NewFD->getDeclContext()->getRedeclContext()->isFileContext()) {
if (!D.isRedeclaration()) {
if (IdentifierInfo *II = Previous.getLookupName().getAsIdentifierInfo()) {
if (unsigned BuiltinID = II->getBuiltinID()) {
if (NewFD->getLanguageLinkage() == CLanguageLinkage) {
// Validate the type matches unless this builtin is specified as
// matching regardless of its declared type.
if (Context.BuiltinInfo.allowTypeMismatch(BuiltinID)) {
NewFD->addAttr(BuiltinAttr::CreateImplicit(Context, BuiltinID));
} else {
ASTContext::GetBuiltinTypeError Error;
LookupNecessaryTypesForBuiltin(S, BuiltinID);
QualType BuiltinType = Context.GetBuiltinType(BuiltinID, Error);

if (!Error && !BuiltinType.isNull() &&
Context.hasSameFunctionTypeIgnoringExceptionSpec(
NewFD->getType(), BuiltinType))
bool InStdNamespace = Context.BuiltinInfo.isInStdNamespace(BuiltinID);
if (!InStdNamespace &&
NewFD->getDeclContext()->getRedeclContext()->isFileContext()) {
if (NewFD->getLanguageLinkage() == CLanguageLinkage) {
// Validate the type matches unless this builtin is specified as
// matching regardless of its declared type.
if (Context.BuiltinInfo.allowTypeMismatch(BuiltinID)) {
NewFD->addAttr(BuiltinAttr::CreateImplicit(Context, BuiltinID));
} else {
ASTContext::GetBuiltinTypeError Error;
LookupNecessaryTypesForBuiltin(S, BuiltinID);
QualType BuiltinType = Context.GetBuiltinType(BuiltinID, Error);

if (!Error && !BuiltinType.isNull() &&
Context.hasSameFunctionTypeIgnoringExceptionSpec(
NewFD->getType(), BuiltinType))
NewFD->addAttr(BuiltinAttr::CreateImplicit(Context, BuiltinID));
}
}
} else if (BuiltinID == Builtin::BI__GetExceptionInfo &&
Context.getTargetInfo().getCXXABI().isMicrosoft()) {
// FIXME: We should consider this a builtin only in the std namespace.
} else if (InStdNamespace && NewFD->isInStdNamespace() &&
isStdBuiltin(Context, NewFD, BuiltinID)) {
NewFD->addAttr(BuiltinAttr::CreateImplicit(Context, BuiltinID));
}
}
Expand Down
35 changes: 33 additions & 2 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3396,7 +3396,7 @@ ExprResult Sema::BuildDeclarationNameExpr(

case Decl::Function: {
if (unsigned BID = cast<FunctionDecl>(VD)->getBuiltinID()) {
if (!Context.BuiltinInfo.isPredefinedLibFunction(BID)) {
if (!Context.BuiltinInfo.isDirectlyAddressable(BID)) {
type = Context.BuiltinFnTy;
valueKind = VK_PRValue;
break;
Expand Down Expand Up @@ -20480,14 +20480,45 @@ ExprResult Sema::CheckPlaceholderExpr(Expr *E) {
auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts());
if (DRE) {
auto *FD = cast<FunctionDecl>(DRE->getDecl());
if (FD->getBuiltinID() == Builtin::BI__noop) {
unsigned BuiltinID = FD->getBuiltinID();
if (BuiltinID == Builtin::BI__noop) {
E = ImpCastExprToType(E, Context.getPointerType(FD->getType()),
CK_BuiltinFnToFnPtr)
.get();
return CallExpr::Create(Context, E, /*Args=*/{}, Context.IntTy,
VK_PRValue, SourceLocation(),
FPOptionsOverride());
}

if (Context.BuiltinInfo.isInStdNamespace(BuiltinID)) {
// Any use of these other than a direct call is ill-formed as of C++20,
// because they are not addressable functions. In earlier language
// modes, warn and force an instantiation of the real body.
Diag(E->getBeginLoc(),
getLangOpts().CPlusPlus20
? diag::err_use_of_unaddressable_function
: diag::warn_cxx20_compat_use_of_unaddressable_function);
if (FD->isImplicitlyInstantiable()) {
// Require a definition here because a normal attempt at
// instantiation for a builtin will be ignored, and we won't try
// again later. We assume that the definition of the template
// precedes this use.
InstantiateFunctionDefinition(E->getBeginLoc(), FD,
/*Recursive=*/false,
/*DefinitionRequired=*/true,
/*AtEndOfTU=*/false);
}
// Produce a properly-typed reference to the function.
CXXScopeSpec SS;
SS.Adopt(DRE->getQualifierLoc());
TemplateArgumentListInfo TemplateArgs;
DRE->copyTemplateArgumentsInto(TemplateArgs);
return BuildDeclRefExpr(
FD, FD->getType(), VK_LValue, DRE->getNameInfo(),
DRE->hasQualifier() ? &SS : nullptr, DRE->getFoundDecl(),
DRE->getTemplateKeywordLoc(),
DRE->hasExplicitTemplateArgs() ? &TemplateArgs : nullptr);
}
}

Diag(E->getBeginLoc(), diag::err_builtin_fn_use);
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4237,6 +4237,14 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
return ExprError();

From = FixOverloadedFunctionReference(From, Found, Fn);

// We might get back another placeholder expression if we resolved to a
// builtin.
ExprResult Checked = CheckPlaceholderExpr(From);
if (Checked.isInvalid())
return ExprError();

From = Checked.get();
FromType = From->getType();
}

Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Sema/SemaInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8215,6 +8215,10 @@ ExprResult InitializationSequence::Perform(Sema &S,
CurInit = S.FixOverloadedFunctionReference(CurInit,
Step->Function.FoundDecl,
Step->Function.Function);
// We might get back another placeholder expression if we resolved to a
// builtin.
if (!CurInit.isInvalid())
CurInit = S.CheckPlaceholderExpr(CurInit.get());
break;

case SK_CastDerivedToBasePRValue:
Expand Down
32 changes: 17 additions & 15 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1747,13 +1747,6 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
"Non-address-of operator for overloaded function expression");
FromType = S.Context.getPointerType(FromType);
}

// Check that we've computed the proper type after overload resolution.
// FIXME: FixOverloadedFunctionReference has side-effects; we shouldn't
// be calling it from within an NDEBUG block.
assert(S.Context.hasSameType(
FromType,
S.FixOverloadedFunctionReference(From, AccessPair, Fn)->getType()));
} else {
return false;
}
Expand Down Expand Up @@ -15188,10 +15181,9 @@ Expr *Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found,
if (SubExpr == UnOp->getSubExpr())
return UnOp;

return UnaryOperator::Create(
Context, SubExpr, UO_AddrOf, Context.getPointerType(SubExpr->getType()),
VK_PRValue, OK_Ordinary, UnOp->getOperatorLoc(), false,
CurFPFeatureOverrides());
// FIXME: This can't currently fail, but in principle it could.
return CreateBuiltinUnaryOp(UnOp->getOperatorLoc(), UO_AddrOf, SubExpr)
.get();
}

if (UnresolvedLookupExpr *ULE = dyn_cast<UnresolvedLookupExpr>(E)) {
Expand All @@ -15202,10 +15194,20 @@ Expr *Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found,
TemplateArgs = &TemplateArgsBuffer;
}

DeclRefExpr *DRE =
BuildDeclRefExpr(Fn, Fn->getType(), VK_LValue, ULE->getNameInfo(),
ULE->getQualifierLoc(), Found.getDecl(),
ULE->getTemplateKeywordLoc(), TemplateArgs);
QualType Type = Fn->getType();
ExprValueKind ValueKind = getLangOpts().CPlusPlus ? VK_LValue : VK_PRValue;

// FIXME: Duplicated from BuildDeclarationNameExpr.
if (unsigned BID = Fn->getBuiltinID()) {
if (!Context.BuiltinInfo.isDirectlyAddressable(BID)) {
Type = Context.BuiltinFnTy;
ValueKind = VK_PRValue;
}
}

DeclRefExpr *DRE = BuildDeclRefExpr(
Fn, Type, ValueKind, ULE->getNameInfo(), ULE->getQualifierLoc(),
Found.getDecl(), ULE->getTemplateKeywordLoc(), TemplateArgs);
DRE->setHadMultipleCandidates(ULE->getNumDecls() > 1);
return DRE;
}
Expand Down
31 changes: 31 additions & 0 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,31 @@ static bool isRelevantAttr(Sema &S, const Decl *D, const Attr *A) {
return true;
}

if (const auto *BA = dyn_cast<BuiltinAttr>(A)) {
const FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
switch (BA->getID()) {
case Builtin::BIforward:
// Do not treat 'std::forward' as a builtin if it takes an rvalue reference
// type and returns an lvalue reference type. The library implementation
// will produce an error in this case; don't get in its way.
if (FD && FD->getNumParams() >= 1 &&
FD->getParamDecl(0)->getType()->isRValueReferenceType() &&
FD->getReturnType()->isLValueReferenceType()) {
return false;
}
LLVM_FALLTHROUGH;
case Builtin::BImove:
case Builtin::BImove_if_noexcept:
// HACK: Super-old versions of libc++ (3.1 and earlier) provide
// std::forward and std::move overloads that sometimes return by value
// instead of by reference when building in C++98 mode. Don't treat such
// cases as builtins.
if (FD && !FD->getReturnType()->isReferenceType())
return false;
break;
}
}

return true;
}

Expand Down Expand Up @@ -4771,6 +4796,12 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
if (TSK == TSK_ExplicitSpecialization)
return;

// Never implicitly instantiate a builtin; we don't actually need a function
// body.
if (Function->getBuiltinID() && TSK == TSK_ImplicitInstantiation &&
!DefinitionRequired)
return;

// Don't instantiate a definition if we already have one.
const FunctionDecl *ExistingDefn = nullptr;
if (Function->isDefined(ExistingDefn,
Expand Down
2 changes: 1 addition & 1 deletion clang/test/Analysis/inner-pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ void func_addressof() {
const char *c;
std::string s;
c = s.c_str();
addressof(s);
(void)addressof(s);
consume(c); // no-warning
}

Expand Down
4 changes: 2 additions & 2 deletions clang/test/Analysis/use-after-move.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ void reinitializationTest(int i) {
A a;
if (i == 1) { // peaceful-note 2 {{'i' is not equal to 1}}
// peaceful-note@-1 2 {{Taking false branch}}
std::move(a);
(void)std::move(a);
}
if (i == 2) { // peaceful-note 2 {{'i' is not equal to 2}}
// peaceful-note@-1 2 {{Taking false branch}}
Expand Down Expand Up @@ -494,7 +494,7 @@ void templateArgIsNotUseTest() {
// Moves of global variables are not reported.
A global_a;
void globalVariablesTest() {
std::move(global_a);
(void)std::move(global_a);
global_a.foo(); // no-warning
}

Expand Down
66 changes: 66 additions & 0 deletions clang/test/CodeGenCXX/builtin-std-move.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -o - -std=c++17 %s | FileCheck %s --implicit-check-not=@_ZSt4move

namespace std {
template<typename T> constexpr T &&move(T &val) { return static_cast<T&&>(val); }
template<typename T> constexpr T &&move_if_noexcept(T &val);
template<typename T> constexpr T &&forward(T &val);
template<typename T> constexpr const T &as_const(T &val);

// Not the builtin.
template<typename T, typename U> T move(U source, U source_end, T dest);
}

class T {};
extern "C" void take(T &&);
extern "C" void take_lval(const T &);

T a;

// Check emission of a constant-evaluated call.
// CHECK-DAG: @move_a = constant ptr @a
T &&move_a = std::move(a);
// CHECK-DAG: @move_if_noexcept_a = constant ptr @a
T &&move_if_noexcept_a = std::move_if_noexcept(a);
// CHECK-DAG: @forward_a = constant ptr @a
T &forward_a = std::forward<T&>(a);

// Check emission of a non-constant call.
// CHECK-LABEL: define {{.*}} void @test
extern "C" void test(T &t) {
// CHECK: store ptr %{{.*}}, ptr %[[T_REF:[^,]*]]
// CHECK: %0 = load ptr, ptr %[[T_REF]]
// CHECK: call void @take(ptr {{.*}} %0)
take(std::move(t));
// CHECK: %1 = load ptr, ptr %[[T_REF]]
// CHECK: call void @take(ptr {{.*}} %1)
take(std::move_if_noexcept(t));
// CHECK: %2 = load ptr, ptr %[[T_REF]]
// CHECK: call void @take(ptr {{.*}} %2)
take(std::forward<T&&>(t));
// CHECK: %3 = load ptr, ptr %[[T_REF]]
// CHECK: call void @take_lval(ptr {{.*}} %3)
take_lval(std::as_const<T&&>(t));

// CHECK: call {{.*}} @_ZSt4moveI1TS0_ET_T0_S2_S1_
std::move(t, t, t);
}

// CHECK: declare {{.*}} @_ZSt4moveI1TS0_ET_T0_S2_S1_

// Check that we instantiate and emit if the address is taken.
// CHECK-LABEL: define {{.*}} @use_address
extern "C" void *use_address() {
// CHECK: ret {{.*}} @_ZSt4moveIiEOT_RS0_
return (void*)&std::move<int>;
}

// CHECK: define {{.*}} ptr @_ZSt4moveIiEOT_RS0_(ptr

extern "C" void take_const_int_rref(const int &&);
// CHECK-LABEL: define {{.*}} @move_const_int(
extern "C" void move_const_int() {
// CHECK: store i32 5, ptr %[[N_ADDR:[^,]*]]
const int n = 5;
// CHECK: call {{.*}} @take_const_int_rref(ptr {{.*}} %[[N_ADDR]])
take_const_int_rref(std::move(n));
}
18 changes: 18 additions & 0 deletions clang/test/CodeGenCXX/builtins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,24 @@ S *addressof(bool b, S &s, S &t) {
return __builtin_addressof(b ? s : t);
}

namespace std { template<typename T> T *addressof(T &); }

// CHECK: define {{.*}} @_Z13std_addressofbR1SS0_(
S *std_addressof(bool b, S &s, S &t) {
// CHECK: %[[LVALUE:.*]] = phi
// CHECK: ret {{.*}}* %[[LVALUE]]
return std::addressof(b ? s : t);
}

namespace std { template<typename T> T *__addressof(T &); }

// CHECK: define {{.*}} @_Z15std___addressofbR1SS0_(
S *std___addressof(bool b, S &s, S &t) {
// CHECK: %[[LVALUE:.*]] = phi
// CHECK: ret {{.*}}* %[[LVALUE]]
return std::__addressof(b ? s : t);
}

extern "C" int __builtin_abs(int); // #1
long __builtin_abs(long); // #2
extern "C" int __builtin_abs(int); // #3
Expand Down
6 changes: 0 additions & 6 deletions clang/test/CodeGenCXX/microsoft-abi-throw.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// RUN: %clang_cc1 -no-opaque-pointers -emit-llvm -o - -triple=i386-pc-win32 -std=c++11 %s -fcxx-exceptions -fms-extensions | FileCheck %s
// RUN: %clang_cc1 -no-opaque-pointers -emit-llvm -o - -triple=i386-pc-win32 -std=c++11 %s -fcxx-exceptions -fms-extensions -DSTD | FileCheck %s

// CHECK-DAG: @"??_R0?AUY@@@8" = linkonce_odr global %rtti.TypeDescriptor7 { i8** @"??_7type_info@@6B@", i8* null, [8 x i8] c".?AUY@@\00" }, comdat
// CHECK-DAG: @"_CT??_R0?AUY@@@8??0Y@@QAE@ABU0@@Z8" = linkonce_odr unnamed_addr constant %eh.CatchableType { i32 4, i8* bitcast (%rtti.TypeDescriptor7* @"??_R0?AUY@@@8" to i8*), i32 0, i32 -1, i32 0, i32 8, i8* bitcast (%struct.Y* (%struct.Y*, %struct.Y*, i32)* @"??0Y@@QAE@ABU0@@Z" to i8*) }, section ".xdata", comdat
Expand Down Expand Up @@ -134,15 +133,10 @@ void h() {
throw nullptr;
}

#ifdef STD
namespace std {
template <typename T>
void *__GetExceptionInfo(T);
}
#else
template <typename T>
void *__GetExceptionInfo(T);
#endif
using namespace std;

void *GetExceptionInfo_test0() {
Expand Down
20 changes: 20 additions & 0 deletions clang/test/SemaCXX/builtin-std-move-nobuiltin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// RUN: %clang_cc1 -std=c++20 -verify %s -DBUILTIN=builtin
// RUN: %clang_cc1 -std=c++20 -verify %s -DBUILTIN=nobuiltin -fno-builtin
// RUN: %clang_cc1 -std=c++20 -verify %s -DBUILTIN=nobuiltin -fno-builtin-std-move -fno-builtin-std-move_if_noexcept -fno-builtin-std-forward
// RUN: %clang_cc1 -std=c++20 -verify %s -DBUILTIN=nobuiltin -ffreestanding
// expected-no-diagnostics

int nobuiltin;

namespace std {
template<typename T> constexpr T &&move(T &x) { return (T&&)nobuiltin; }
template<typename T> constexpr T &&move_if_noexcept(T &x) { return (T&&)nobuiltin; }
template<typename T> constexpr T &&forward(T &x) { return (T&&)nobuiltin; }
}

template<typename T> constexpr T *addr(T &&r) { return &r; }

int builtin;
static_assert(addr(std::move(builtin)) == &BUILTIN);
static_assert(addr(std::move_if_noexcept(builtin)) == &BUILTIN);
static_assert(addr(std::forward(builtin)) == &BUILTIN);
138 changes: 138 additions & 0 deletions clang/test/SemaCXX/builtin-std-move.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// RUN: %clang_cc1 -std=c++17 -verify %s
// RUN: %clang_cc1 -std=c++17 -verify %s -DNO_CONSTEXPR
// RUN: %clang_cc1 -std=c++20 -verify %s

namespace std {
#ifndef NO_CONSTEXPR
#define CONSTEXPR constexpr
#else
#define CONSTEXPR
#endif

template<typename T> CONSTEXPR T &&move(T &x) {
static_assert(T::moveable, "instantiated move"); // expected-error {{no member named 'moveable' in 'B'}}
// expected-error@-1 {{no member named 'moveable' in 'C'}}
return static_cast<T&&>(x);
}

// Unrelated move functions are not the builtin.
template<typename T> CONSTEXPR int move(T, T) { return 5; }

template<typename T, bool Rref> struct ref { using type = T&; };
template<typename T> struct ref<T, true> { using type = T&&; };

template<typename T> CONSTEXPR auto move_if_noexcept(T &x) -> typename ref<T, noexcept(T(static_cast<T&&>(x)))>::type {
static_assert(T::moveable, "instantiated move_if_noexcept"); // expected-error {{no member named 'moveable' in 'B'}}
return static_cast<typename ref<T, noexcept(T(static_cast<T&&>(x)))>::type>(x);
}

template<typename T> struct remove_reference { using type = T; };
template<typename T> struct remove_reference<T&> { using type = T; };
template<typename T> struct remove_reference<T&&> { using type = T; };

template<typename T> struct is_lvalue_reference { static constexpr bool value = false; };
template<typename T> struct is_lvalue_reference<T&> { static constexpr bool value = true; };

template<typename T> CONSTEXPR T &&forward(typename remove_reference<T>::type &x) {
static_assert(T::moveable, "instantiated forward"); // expected-error {{no member named 'moveable' in 'B'}}
// expected-error@-1 {{no member named 'moveable' in 'C'}}
return static_cast<T&&>(x);
}
template<typename T> CONSTEXPR T &&forward(typename remove_reference<T>::type &&x) {
static_assert(!is_lvalue_reference<T>::value, "should not forward rval as lval"); // expected-error {{static_assert failed}}
return static_cast<T&&>(x);
}

template<typename T> CONSTEXPR const T &as_const(T &x) {
static_assert(T::moveable, "instantiated as_const"); // expected-error {{no member named 'moveable' in 'B'}}
return x;
}

template<typename T> CONSTEXPR T *addressof(T &x) {
static_assert(T::moveable, "instantiated addressof"); // expected-error {{no member named 'moveable' in 'B'}}
return __builtin_addressof(x);
}

template<typename T> CONSTEXPR T *__addressof(T &x) {
static_assert(T::moveable, "instantiated __addressof"); // expected-error {{no member named 'moveable' in 'B'}}
return __builtin_addressof(x);
}
}

// Note: this doesn't have a 'moveable' member. Instantiation of the above
// functions will fail if it's attempted.
struct A {};
constexpr bool f(A a) { // #f
A &&move = std::move(a); // #call
A &&move_if_noexcept = std::move_if_noexcept(a);
A &&forward1 = std::forward<A>(a);
A &forward2 = std::forward<A&>(a);
const A &as_const = std::as_const(a);
A *addressof = std::addressof(a);
A *addressof2 = std::__addressof(a);
return &move == &a && &move_if_noexcept == &a &&
&forward1 == &a && &forward2 == &a &&
&as_const == &a && addressof == &a &&
addressof2 == &a && std::move(a, a) == 5;
}

#ifndef NO_CONSTEXPR
static_assert(f({}), "should be constexpr");
#else
// expected-error@#f {{never produces a constant expression}}
// expected-note@#call {{}}
#endif

A &forward_rval_as_lval() {
std::forward<A&&>(A()); // expected-warning {{const attribute}}
return std::forward<A&>(A()); // expected-note {{instantiation of}}
}

struct B {};
B &&(*pMove)(B&) = std::move; // #1 expected-note {{instantiation of}}
B &&(*pMoveIfNoexcept)(B&) = &std::move_if_noexcept; // #2 expected-note {{instantiation of}}
B &&(*pForward)(B&) = &std::forward<B>; // #3 expected-note {{instantiation of}}
const B &(*pAsConst)(B&) = &std::as_const; // #4 expected-note {{instantiation of}}
B *(*pAddressof)(B&) = &std::addressof; // #5 expected-note {{instantiation of}}
B *(*pUnderUnderAddressof)(B&) = &std::__addressof; // #6 expected-note {{instantiation of}}
int (*pUnrelatedMove)(B, B) = std::move;

struct C {};
C &&(&rMove)(C&) = std::move; // #7 expected-note {{instantiation of}}
C &&(&rForward)(C&) = std::forward<C>; // #8 expected-note {{instantiation of}}
int (&rUnrelatedMove)(B, B) = std::move;

#if __cplusplus <= 201703L
// expected-warning@#1 {{non-addressable}}
// expected-warning@#2 {{non-addressable}}
// expected-warning@#3 {{non-addressable}}
// expected-warning@#4 {{non-addressable}}
// expected-warning@#5 {{non-addressable}}
// expected-warning@#6 {{non-addressable}}
// expected-warning@#7 {{non-addressable}}
// expected-warning@#8 {{non-addressable}}
#else
// expected-error@#1 {{non-addressable}}
// expected-error@#2 {{non-addressable}}
// expected-error@#3 {{non-addressable}}
// expected-error@#4 {{non-addressable}}
// expected-error@#5 {{non-addressable}}
// expected-error@#6 {{non-addressable}}
// expected-error@#7 {{non-addressable}}
// expected-error@#8 {{non-addressable}}
#endif

void attribute_const() {
int n;
std::move(n); // expected-warning {{ignoring return value}}
std::move_if_noexcept(n); // expected-warning {{ignoring return value}}
std::forward<int>(n); // expected-warning {{ignoring return value}}
std::addressof(n); // expected-warning {{ignoring return value}}
std::__addressof(n); // expected-warning {{ignoring return value}}
std::as_const(n); // expected-warning {{ignoring return value}}
}

namespace std {
template<typename T> int &move(T);
}
int bad_signature = std::move(0); // expected-error {{unsupported signature for 'std::move<int>'}}
32 changes: 32 additions & 0 deletions clang/test/SemaCXX/libcxx_move_hack.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// RUN: %clang_cc1 -fsyntax-only %s -std=c++98 -verify
// expected-no-diagnostics

// This is a test for a hack in Clang that works around an issue with libc++
// 3.1's std::move and std::forward implementation. When emulating these
// functions in C++98 mode, libc++ 3.1 has a "fake rvalue reference" type, and
// std::move will return by value when given an instance of that type.

namespace std {
struct rv {};

template<bool B, typename T> struct enable_if;
template<typename T> struct enable_if<true, T> { typedef T type; };

template<typename T> typename enable_if<__is_convertible(T, rv), T>::type move(T &);
template<typename T> typename enable_if<!__is_convertible(T, rv), T&>::type move(T &);

template<typename U, typename T> typename enable_if<__is_convertible(T, rv), U>::type forward(T &);
template<typename U, typename T> typename enable_if<!__is_convertible(T, rv), U&>::type forward(T &);
}

struct A {};
void f(A a, std::rv rv) {
a = std::move(a);
rv = std::move(rv);

a = std::forward<A>(a);
rv = std::forward<std::rv>(rv);

a = std::forward<A&>(a);
rv = std::forward<std::rv&>(rv);
}
12 changes: 6 additions & 6 deletions clang/test/SemaCXX/unqualified-std-call-fixits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@

namespace std {

void move(auto &&a) {}
int &&move(auto &&a) { return a; }

void forward(auto &a) {}
int &&forward(auto &a) { return a; }

} // namespace std

using namespace std;

void f() {
int i = 0;
move(i); // expected-warning {{unqualified call to std::move}}
// CHECK: {{^}} std::
forward(i); // expected-warning {{unqualified call to std::forward}}
// CHECK: {{^}} std::
(void)move(i); // expected-warning {{unqualified call to std::move}}
// CHECK: {{^}} (void)std::move
(void)forward(i); // expected-warning {{unqualified call to std::forward}}
// CHECK: {{^}} (void)std::forward
}
6 changes: 3 additions & 3 deletions clang/test/SemaCXX/unqualified-std-call.cpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
// RUN: %clang_cc1 -fsyntax-only -verify -Wall -std=c++11 %s
// RUN: %clang_cc1 -fsyntax-only -verify -Wall -std=c++11 %s -Wno-unused-value

namespace std {

template <typename T>
void dummy(T &&) {}
template <typename T>
void move(T &&) {}
T &&move(T &&x) { return x; }
template <typename T, typename U>
void move(T &&, U &&) {}

inline namespace __1 {
template <typename T>
void forward(T &) {}
T &forward(T &x) { return x; }
} // namespace __1

struct foo {};
Expand Down
6 changes: 3 additions & 3 deletions clang/test/SemaCXX/warn-consumed-analysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -953,12 +953,12 @@ void test6() {
namespace std {
void move();
template<class T>
void move(T&&);
T &&move(T&);

namespace __1 {
void move();
template<class T>
void move(T&&);
T &&move(T&);
}
}

Expand All @@ -971,7 +971,7 @@ namespace PR18260 {
void test() {
x.move();
std::move();
std::move(x);
std::move(x); // expected-warning {{ignoring return value}}
std::__1::move();
std::__1::move(x);
}
Expand Down
2 changes: 1 addition & 1 deletion clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1444,7 +1444,7 @@ TEST(ExprMutationAnalyzerTest, UnevaluatedContext) {
TEST(ExprMutationAnalyzerTest, ReproduceFailureMinimal) {
const std::string Reproducer =
"namespace std {"
"template <class T> T forward(T & A) { return static_cast<T&&>(A); }"
"template <class T> T &forward(T &A) { return static_cast<T&&>(A); }"
"template <class T> struct __bind {"
" T f;"
" template <class V> __bind(T v, V &&) : f(forward(v)) {}"
Expand Down
2 changes: 1 addition & 1 deletion libcxx/test/std/utilities/utility/forward/forward.fail.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const A csource() {return A();}
int main(int, char**)
{
{
std::forward<A&>(source()); // expected-note {{requested here}}
(void)std::forward<A&>(source()); // expected-note {{requested here}}
// expected-error-re@*:* 1 {{static_assert failed{{.*}} "cannot forward an rvalue as an lvalue"}}
}
{
Expand Down