[clang] fc30901 - Extend support for std::move etc to also cover std::as_const and

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Fri Apr 15 16:31:58 PDT 2022


Author: Richard Smith
Date: 2022-04-15T16:31:39-07:00
New Revision: fc3090109643af8d2da9822d0f99c84742b9c877

URL: https://github.com/llvm/llvm-project/commit/fc3090109643af8d2da9822d0f99c84742b9c877
DIFF: https://github.com/llvm/llvm-project/commit/fc3090109643af8d2da9822d0f99c84742b9c877.diff

LOG: 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.

Added: 
    

Modified: 
    clang/include/clang/Basic/Builtins.def
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/lib/AST/ExprConstant.cpp
    clang/lib/Analysis/BodyFarm.cpp
    clang/lib/CodeGen/CGBuiltin.cpp
    clang/lib/Sema/SemaChecking.cpp
    clang/lib/Sema/SemaDecl.cpp
    clang/test/Analysis/inner-pointer.cpp
    clang/test/CodeGenCXX/builtin-std-move.cpp
    clang/test/CodeGenCXX/builtins.cpp
    clang/test/SemaCXX/builtin-std-move.cpp
    clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp

Removed: 
    


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

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

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

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 
diff er 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<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

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

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

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


        


More information about the cfe-commits mailing list