diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def index c853229f13970..c22957e14de7d 100644 --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -103,8 +103,7 @@ // V:N: -> requires vectors of at least N bits to be legal // C -> 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") diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index cebd24302f1d1..f71f576f9ff47 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -6587,7 +6587,7 @@ def warn_self_move : Warning< InGroup, 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. diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index ce007edca0fc6..410fc8285bb65 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -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(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: { diff --git a/clang/lib/Analysis/BodyFarm.cpp b/clang/lib/Analysis/BodyFarm.cpp index e8c2d4421c618..95bed548c567a 100644 --- a/clang/lib/Analysis/BodyFarm.cpp +++ b/clang/lib/Analysis/BodyFarm.cpp @@ -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: diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index dc4b104201632..640879f92493e 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -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 = diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 870f1331069f2..cd069d5664571 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -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: diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index cacd08a62a320..937478416f6d7 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -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. diff --git a/clang/test/Analysis/inner-pointer.cpp b/clang/test/Analysis/inner-pointer.cpp index 920a1fe8dcc9d..5db17a952f90b 100644 --- a/clang/test/Analysis/inner-pointer.cpp +++ b/clang/test/Analysis/inner-pointer.cpp @@ -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 } diff --git a/clang/test/CodeGenCXX/builtin-std-move.cpp b/clang/test/CodeGenCXX/builtin-std-move.cpp index 51498002cd987..9df4beea72bec 100644 --- a/clang/test/CodeGenCXX/builtin-std-move.cpp +++ b/clang/test/CodeGenCXX/builtin-std-move.cpp @@ -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 constexpr T &&move(T &val) { return static_cast(val); } template constexpr T &&move_if_noexcept(T &val); template constexpr T &&forward(T &val); + template constexpr const T &as_const(T &val); // Not the builtin. template 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(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)); + // CHECK: %3 = load ptr, ptr %[[T_REF]] + // CHECK: call void @take_lval(ptr {{.*}} %3) + take_lval(std::as_const(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; } -// CHECK: define {{.*}} i32* @_ZSt4moveIiEOT_RS0_(i32* +// CHECK: define {{.*}} ptr @_ZSt4moveIiEOT_RS0_(ptr diff --git a/clang/test/CodeGenCXX/builtins.cpp b/clang/test/CodeGenCXX/builtins.cpp index c1c8b51cbae6d..56a29eac2490c 100644 --- a/clang/test/CodeGenCXX/builtins.cpp +++ b/clang/test/CodeGenCXX/builtins.cpp @@ -30,6 +30,24 @@ S *addressof(bool b, S &s, S &t) { return __builtin_addressof(b ? s : t); } +namespace std { template 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 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 diff --git a/clang/test/SemaCXX/builtin-std-move.cpp b/clang/test/SemaCXX/builtin-std-move.cpp index 6a263239ae6b0..a53e57c86c14c 100644 --- a/clang/test/SemaCXX/builtin-std-move.cpp +++ b/clang/test/SemaCXX/builtin-std-move.cpp @@ -35,6 +35,21 @@ namespace std { // expected-error@-1 {{no member named 'moveable' in 'C'}} return static_cast(x); } + + template 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 CONSTEXPR T *addressof(T &x) { + static_assert(T::moveable, "instantiated addressof"); // expected-error {{no member named 'moveable' in 'B'}} + return __builtin_addressof(x); + } + + template 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 &forward2 = std::forward(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; // #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; // #5 expected-note {{instantiation of}} +C &&(&rMove)(C&) = std::move; // #7 expected-note {{instantiation of}} +C &&(&rForward)(C&) = std::forward; // #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(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 int move(T); } +int bad_signature = std::move(0); // expected-error {{unsupported signature for 'std::move'}} diff --git a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp index a4410d330d393..e6360d88a893d 100644 --- a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp +++ b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp @@ -1444,7 +1444,7 @@ TEST(ExprMutationAnalyzerTest, UnevaluatedContext) { TEST(ExprMutationAnalyzerTest, ReproduceFailureMinimal) { const std::string Reproducer = "namespace std {" - "template T forward(T & A) { return static_cast(A); }" + "template T &forward(T &A) { return static_cast(A); }" "template struct __bind {" " T f;" " template __bind(T v, V &&) : f(forward(v)) {}"