[clang] [clang] Add lifetimebound attr to std::span/std::string_view constructor (PR #103716)

Haojian Wu via cfe-commits cfe-commits at lists.llvm.org
Wed Aug 28 01:12:15 PDT 2024


https://github.com/hokein updated https://github.com/llvm/llvm-project/pull/103716

>From 24a84b35dba03ca80027efcc87b4628c378991ff Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Wed, 14 Aug 2024 10:18:09 +0200
Subject: [PATCH] [clang] Add lifetimebound attr to std::span/std::string_view
 constructor

With this patch, clang now automatically adds ``[[clang::lifetimebound]]``
to the parameters of ``std::span, std::string_view`` constructors, this
enables Clang to capture more cases where the returned reference outlives the object.
---
 clang/docs/ReleaseNotes.rst               |  5 +++
 clang/include/clang/Sema/Sema.h           |  3 ++
 clang/lib/Sema/SemaAttr.cpp               | 53 +++++++++++++++++++++++
 clang/lib/Sema/SemaDecl.cpp               | 20 +--------
 clang/test/SemaCXX/attr-lifetimebound.cpp | 21 +++++++++
 5 files changed, 83 insertions(+), 19 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index d9fa068c2910f4..4289b3374a810b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -220,6 +220,11 @@ Attribute Changes in Clang
 - ``[[clang::lifetimebound]]`` is now explicitly disallowed on explicit object member functions
   where they were previously silently ignored.
 
+- Clang now automatically adds ``[[clang::lifetimebound]]`` to the parameters of
+  ``std::span, std::string_view`` constructors, this enables Clang to capture
+  more cases where the returned reference outlives the object.
+  (#GH100567)
+
 Improvements to Clang's diagnostics
 -----------------------------------
 
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 1f7e555d1b8717..ed1c279fefe29c 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1806,6 +1806,9 @@ class Sema final : public SemaBase {
   /// Add [[gsl::Owner]] and [[gsl::Pointer]] attributes for std:: types.
   void inferGslOwnerPointerAttribute(CXXRecordDecl *Record);
 
+  /// Add [[clang:::lifetimebound]] attr for std:: functions and methods.
+  void inferLifetimeBoundAttribute(FunctionDecl *FD);
+
   /// Add [[gsl::Pointer]] attributes for std:: types.
   void inferGslPointerAttribute(TypedefNameDecl *TD);
 
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index a1724820472b59..fb594e6f13c0b3 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -216,6 +216,59 @@ void Sema::inferGslOwnerPointerAttribute(CXXRecordDecl *Record) {
   inferGslPointerAttribute(Record, Record);
 }
 
+void Sema::inferLifetimeBoundAttribute(FunctionDecl *FD) {
+  if (FD->getNumParams() == 0)
+    return;
+
+  if (unsigned BuiltinID = FD->getBuiltinID()) {
+    // Add lifetime attribute to std::move, std::fowrard et al.
+    switch (BuiltinID) {
+    case Builtin::BIaddressof:
+    case Builtin::BI__addressof:
+    case Builtin::BI__builtin_addressof:
+    case Builtin::BIas_const:
+    case Builtin::BIforward:
+    case Builtin::BIforward_like:
+    case Builtin::BImove:
+    case Builtin::BImove_if_noexcept:
+      if (ParmVarDecl *P = FD->getParamDecl(0u);
+          !P->hasAttr<LifetimeBoundAttr>())
+        P->addAttr(
+            LifetimeBoundAttr::CreateImplicit(Context, FD->getLocation()));
+      break;
+    default:
+      break;
+    }
+    return;
+  }
+  if (auto *CMD = dyn_cast<CXXMethodDecl>(FD)) {
+    const auto *CRD = CMD->getParent();
+    if (!CRD->isInStdNamespace() || !CRD->getIdentifier())
+      return;
+
+    if (isa<CXXConstructorDecl>(CMD)) {
+      auto *Param = CMD->getParamDecl(0);
+      if (Param->hasAttr<LifetimeBoundAttr>())
+        return;
+      if (CRD->getName() == "basic_string_view" &&
+          Param->getType()->isPointerType()) {
+        // construct from a char array pointed by a pointer.
+        //   basic_string_view(const CharT* s);
+        //   basic_string_view(const CharT* s, size_type count);
+        Param->addAttr(
+            LifetimeBoundAttr::CreateImplicit(Context, FD->getLocation()));
+      } else if (CRD->getName() == "span") {
+        // construct from a reference of array.
+        //   span(std::type_identity_t<element_type> (&arr)[N]);
+        const auto *LRT = Param->getType()->getAs<LValueReferenceType>();
+        if (LRT && LRT->getPointeeType().IgnoreParens()->isArrayType())
+          Param->addAttr(
+              LifetimeBoundAttr::CreateImplicit(Context, FD->getLocation()));
+      }
+    }
+  }
+}
+
 void Sema::inferNullableClassAttribute(CXXRecordDecl *CRD) {
   static const llvm::StringSet<> Nullable{
       "auto_ptr",         "shared_ptr", "unique_ptr",         "exception_ptr",
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index b0ccbbe34b70c3..4efa80778e71b9 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16608,27 +16608,9 @@ void Sema::AddKnownFunctionAttributes(FunctionDecl *FD) {
     default:
       break;
     }
-
-    // Add lifetime attribute to std::move, std::fowrard et al.
-    switch (BuiltinID) {
-    case Builtin::BIaddressof:
-    case Builtin::BI__addressof:
-    case Builtin::BI__builtin_addressof:
-    case Builtin::BIas_const:
-    case Builtin::BIforward:
-    case Builtin::BIforward_like:
-    case Builtin::BImove:
-    case Builtin::BImove_if_noexcept:
-      if (ParmVarDecl *P = FD->getParamDecl(0u);
-          !P->hasAttr<LifetimeBoundAttr>())
-        P->addAttr(
-            LifetimeBoundAttr::CreateImplicit(Context, FD->getLocation()));
-      break;
-    default:
-      break;
-    }
   }
 
+  inferLifetimeBoundAttribute(FD);
   AddKnownFunctionAttributesForReplaceableGlobalAllocationFunction(FD);
 
   // If C++ exceptions are enabled but we are told extern "C" functions cannot
diff --git a/clang/test/SemaCXX/attr-lifetimebound.cpp b/clang/test/SemaCXX/attr-lifetimebound.cpp
index f4ca840cb276f9..7133efd5c2a25a 100644
--- a/clang/test/SemaCXX/attr-lifetimebound.cpp
+++ b/clang/test/SemaCXX/attr-lifetimebound.cpp
@@ -238,6 +238,16 @@ template <class T> T *addressof(T &arg) {
         &const_cast<char &>(reinterpret_cast<const volatile char &>(arg)));
 }
 
+template<typename T>
+struct basic_string_view {
+  basic_string_view(const T *);
+};
+
+template <class T> struct span {
+  template<size_t _ArrayExtent>
+	span(T (&__arr)[_ArrayExtent]) noexcept;
+};
+
 } // namespace foo
 } // namespace std
 
@@ -266,3 +276,14 @@ namespace move_forward_et_al_examples {
   S *AddressOfOk = std::addressof(X);
 } // namespace move_forward_et_al_examples
 
+namespace ctor_cases {
+std::basic_string_view<char> test1() {
+  char abc[10];
+  return abc;  // expected-warning {{address of stack memory associated with local variable}}
+}
+
+std::span<int> test2() {
+  int abc[10];
+  return abc; // expected-warning {{address of stack memory associated with local variable}}
+}
+} // namespace ctor_cases



More information about the cfe-commits mailing list