forked from OSchip/llvm-project
Extend support for std::move etc to also cover std::as_const and
std::addressof, plus the libstdc++-specific std::__addressof. This brings us to parity with the corresponding GCC behavior. Remove STDBUILTIN macro that ended up not being used.
This commit is contained in:
parent
9e7eef9989
commit
fc30901096
|
@ -103,8 +103,7 @@
|
|||
// V:N: -> requires vectors of at least N bits to be legal
|
||||
// C<N,M_0,...,M_k> -> callback behavior: argument N is called with argument
|
||||
// M_0, ..., M_k as payload
|
||||
// z -> this is a C++ standard library function in (possibly-versioned)
|
||||
// namespace std; implied by STDBUILTIN
|
||||
// z -> this is a function in (possibly-versioned) namespace std
|
||||
// FIXME: gcc has nonnull
|
||||
|
||||
#if defined(BUILTIN) && !defined(LIBBUILTIN)
|
||||
|
@ -115,10 +114,6 @@
|
|||
# define LANGBUILTIN(ID, TYPE, ATTRS, BUILTIN_LANG) BUILTIN(ID, TYPE, ATTRS)
|
||||
#endif
|
||||
|
||||
#if defined(BUILTIN) && !defined(STDBUILTIN)
|
||||
# define STDBUILTIN(ID, TYPE, ATTRS, HEADER) LIBBUILTIN(ID, TYPE, "zf" ATTRS, HEADER, CXX_LANG)
|
||||
#endif
|
||||
|
||||
// Standard libc/libm functions:
|
||||
BUILTIN(__builtin_atan2 , "ddd" , "Fne")
|
||||
BUILTIN(__builtin_atan2f, "fff" , "Fne")
|
||||
|
@ -1551,10 +1546,14 @@ LIBBUILTIN(_Block_object_assign, "vv*vC*iC", "f", "Blocks.h", ALL_LANGUAGES)
|
|||
LIBBUILTIN(_Block_object_dispose, "vvC*iC", "f", "Blocks.h", ALL_LANGUAGES)
|
||||
// FIXME: Also declare NSConcreteGlobalBlock and NSConcreteStackBlock.
|
||||
|
||||
// C++11
|
||||
STDBUILTIN(move, "v&v&", "ncTh", "utility")
|
||||
STDBUILTIN(move_if_noexcept, "v&v&", "ncTh", "utility")
|
||||
STDBUILTIN(forward, "v&v&", "ncTh", "utility")
|
||||
// C++ standard library builtins in namespace 'std'.
|
||||
LIBBUILTIN(addressof, "v*v&", "zfncTh", "memory", CXX_LANG)
|
||||
// Synonym for addressof used internally by libstdc++.
|
||||
LANGBUILTIN(__addressof, "v*v&", "zfncT", CXX_LANG)
|
||||
LIBBUILTIN(as_const, "v&v&", "zfncTh", "utility", CXX_LANG)
|
||||
LIBBUILTIN(forward, "v&v&", "zfncTh", "utility", CXX_LANG)
|
||||
LIBBUILTIN(move, "v&v&", "zfncTh", "utility", CXX_LANG)
|
||||
LIBBUILTIN(move_if_noexcept, "v&v&", "zfncTh", "utility", CXX_LANG)
|
||||
|
||||
// Annotation function
|
||||
BUILTIN(__builtin_annotation, "v.", "tn")
|
||||
|
|
|
@ -6587,7 +6587,7 @@ def warn_self_move : Warning<
|
|||
InGroup<SelfMove>, DefaultIgnore;
|
||||
|
||||
def err_builtin_move_forward_unsupported : Error<
|
||||
"unsupported signature for '%select{std::move|std::forward}0'">;
|
||||
"unsupported signature for %q0">;
|
||||
def err_use_of_unaddressable_function : Error<
|
||||
"taking address of non-addressable standard library function">;
|
||||
// FIXME: This should also be in -Wc++23-compat once we have it.
|
||||
|
|
|
@ -8295,9 +8295,10 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
|
|||
|
||||
bool LValueExprEvaluator::VisitCallExpr(const CallExpr *E) {
|
||||
switch (unsigned BuiltinOp = E->getBuiltinCallee()) {
|
||||
case Builtin::BIas_const:
|
||||
case Builtin::BIforward:
|
||||
case Builtin::BImove:
|
||||
case Builtin::BImove_if_noexcept:
|
||||
case Builtin::BIforward:
|
||||
if (cast<FunctionDecl>(E->getCalleeDecl())->isConstexpr())
|
||||
return Visit(E->getArg(0));
|
||||
break;
|
||||
|
@ -9084,6 +9085,8 @@ static bool isOneByteCharacterType(QualType T) {
|
|||
bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
|
||||
unsigned BuiltinOp) {
|
||||
switch (BuiltinOp) {
|
||||
case Builtin::BIaddressof:
|
||||
case Builtin::BI__addressof:
|
||||
case Builtin::BI__builtin_addressof:
|
||||
return evaluateLValue(E->getArg(0), Result);
|
||||
case Builtin::BI__builtin_assume_aligned: {
|
||||
|
|
|
@ -713,9 +713,10 @@ Stmt *BodyFarm::getBody(const FunctionDecl *D) {
|
|||
|
||||
if (unsigned BuiltinID = D->getBuiltinID()) {
|
||||
switch (BuiltinID) {
|
||||
case Builtin::BIas_const:
|
||||
case Builtin::BIforward:
|
||||
case Builtin::BImove:
|
||||
case Builtin::BImove_if_noexcept:
|
||||
case Builtin::BIforward:
|
||||
FF = create_std_move_forward;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -4566,6 +4566,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
|
|||
|
||||
return RValue::get(Carry);
|
||||
}
|
||||
case Builtin::BIaddressof:
|
||||
case Builtin::BI__addressof:
|
||||
case Builtin::BI__builtin_addressof:
|
||||
return RValue::get(EmitLValue(E->getArg(0)).getPointer(*this));
|
||||
case Builtin::BI__builtin_function_start:
|
||||
|
@ -4729,6 +4731,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
|
|||
case Builtin::BImove:
|
||||
case Builtin::BImove_if_noexcept:
|
||||
case Builtin::BIforward:
|
||||
case Builtin::BIas_const:
|
||||
return RValue::get(EmitLValue(E->getArg(0)).getPointer(*this));
|
||||
case Builtin::BI__GetExceptionInfo: {
|
||||
if (llvm::GlobalVariable *GV =
|
||||
|
|
|
@ -2130,18 +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::BIforward:
|
||||
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();
|
||||
if (!Context.hasSameUnqualifiedType(TheCall->getType(),
|
||||
TheCall->getArg(0)->getType())) {
|
||||
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)
|
||||
<< (BuiltinID == Builtin::BIforward);
|
||||
<< FDecl;
|
||||
return ExprError();
|
||||
}
|
||||
break;
|
||||
}
|
||||
// OpenCL v2.0, s6.13.16 - Pipe functions
|
||||
case Builtin::BIread_pipe:
|
||||
case Builtin::BIwrite_pipe:
|
||||
|
|
|
@ -9277,9 +9277,12 @@ static bool isStdBuiltin(ASTContext &Ctx, FunctionDecl *FD,
|
|||
// 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::BIforward: {
|
||||
case Builtin::BIas_const: {
|
||||
// Ensure that we don't treat the algorithm
|
||||
// OutputIt std::move(InputIt, InputIt, OutputIt)
|
||||
// as the builtin std::move.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
// RUN: %clang_cc1 -no-opaque-pointers -triple=x86_64-linux-gnu -emit-llvm -o - -std=c++17 %s | FileCheck %s --implicit-check-not=@_ZSt4move
|
||||
// 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);
|
||||
|
@ -11,30 +12,34 @@ namespace std {
|
|||
|
||||
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 %[[T:.*]]* @a
|
||||
// CHECK-DAG: @move_a = constant ptr @a
|
||||
T &&move_a = std::move(a);
|
||||
// CHECK-DAG: @move_if_noexcept_a = constant %[[T]]* @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 %[[T]]* @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 %[[T]]* %{{.*}}, %[[T]]** %[[T_REF:[^,]*]]
|
||||
// CHECK: %0 = load %[[T]]*, %[[T]]** %[[T_REF]]
|
||||
// CHECK: call void @take(%[[T]]* {{.*}} %0)
|
||||
// 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 %[[T]]*, %[[T]]** %[[T_REF]]
|
||||
// CHECK: call void @take(%[[T]]* {{.*}} %1)
|
||||
// CHECK: %1 = load ptr, ptr %[[T_REF]]
|
||||
// CHECK: call void @take(ptr {{.*}} %1)
|
||||
take(std::move_if_noexcept(t));
|
||||
// CHECK: %2 = load %[[T]]*, %[[T]]** %[[T_REF]]
|
||||
// CHECK: call void @take(%[[T]]* {{.*}} %2)
|
||||
// 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);
|
||||
|
@ -49,4 +54,4 @@ extern "C" void *use_address() {
|
|||
return (void*)&std::move<int>;
|
||||
}
|
||||
|
||||
// CHECK: define {{.*}} i32* @_ZSt4moveIiEOT_RS0_(i32*
|
||||
// CHECK: define {{.*}} ptr @_ZSt4moveIiEOT_RS0_(ptr
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -35,6 +35,21 @@ namespace std {
|
|||
// expected-error@-1 {{no member named 'moveable' in 'C'}}
|
||||
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
|
||||
|
@ -45,9 +60,13 @@ constexpr bool f(A a) { // #f
|
|||
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 &&
|
||||
std::move(a, a) == 5;
|
||||
&as_const == &a && addressof == &a &&
|
||||
addressof2 == &a && std::move(a, a) == 5;
|
||||
}
|
||||
|
||||
#ifndef NO_CONSTEXPR
|
||||
|
@ -61,11 +80,14 @@ 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; // #4 expected-note {{instantiation of}}
|
||||
C &&(&rForward)(C&) = std::forward<C>; // #5 expected-note {{instantiation of}}
|
||||
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
|
||||
|
@ -74,12 +96,18 @@ int (&rUnrelatedMove)(B, B) = std::move;
|
|||
// 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() {
|
||||
|
@ -87,4 +115,12 @@ void attribute_const() {
|
|||
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>'}}
|
||||
|
|
|
@ -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)) {}"
|
||||
|
|
Loading…
Reference in New Issue