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:
Richard Smith 2022-04-15 14:45:30 -07:00
parent 9e7eef9989
commit fc30901096
12 changed files with 117 additions and 35 deletions

View File

@ -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")

View File

@ -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.

View File

@ -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: {

View File

@ -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:

View File

@ -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 =

View File

@ -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:

View File

@ -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.

View File

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

View File

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

View File

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

View File

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

View File

@ -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)) {}"