https://github.com/usx95 created https://github.com/llvm/llvm-project/pull/76818

Lifetime-bound analysis of reference parameters of coroutines and coroutine 
wrappers is helpful in surfacing memory bugs associated with using temporaries 
and stack variables in call expressions in plain return statements.

This is the default semantics of `[[clang::coro_lifetimebound]]`. But it should 
be okay to relax the requirements for a function when the reference arguments 
are not lifetime bound. For example:

A coroutine wrapper accepts a reference parameter but does not pass it to the 
underlying coroutine call.
```cpp
[[clang::coro_wrapper]] Task<int> wrapper(const Request& req) {
  return req.shouldCallA() ? coroA() : coroB();
}
```
Or passes it the coroutine by value
```cpp
Task<int> coro(std::string s) { co_return s.size(); }
[[clang::coro_wrapper]] wrapper(const std::string& s) { return coro(s); }
```

This patch allows functions to be annotated with 
`[[clang::coro_not_lifetimebound]]` to disable lifetime bound analysis for all 
calls to this function.

>From cc7ff8b7ecb93165172dbb481c7d5fcb64289a96 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <u...@google.com>
Date: Wed, 3 Jan 2024 14:44:58 +0100
Subject: [PATCH] [coroutines] Introduce [[clang::coro_not_lifetimebound]]

---
 clang/docs/ReleaseNotes.rst                   |  1 +
 clang/include/clang/Basic/Attr.td             |  8 ++++++
 clang/include/clang/Basic/AttrDocs.td         | 25 ++++++++++++++++---
 clang/lib/Sema/SemaInit.cpp                   |  3 ++-
 ...a-attribute-supported-attributes-list.test |  1 +
 clang/test/SemaCXX/coro-lifetimebound.cpp     | 15 +++++++++++
 6 files changed, 48 insertions(+), 5 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index ee211c16a48ac8..88a0bf7d005dab 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -357,6 +357,7 @@ Attribute Changes in Clang
 - Clang now introduced ``[[clang::coro_lifetimebound]]`` attribute.
   All parameters of a function are considered to be lifetime bound if the 
function
   returns a type annotated with ``[[clang::coro_lifetimebound]]`` and 
``[[clang::coro_return_type]]``.
+  This analysis can be disabled for a function by annotating the function with 
``[[clang::coro_not_lifetimebound]]``.
 
 Improvements to Clang's diagnostics
 -----------------------------------
diff --git a/clang/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index db17211747b17d..3dd3cb305dc4ff 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1121,6 +1121,14 @@ def CoroLifetimeBound : InheritableAttr {
   let SimpleHandler = 1;
 }
 
+def CoroNotLifetimeBound : InheritableAttr {
+  let Spellings = [Clang<"coro_not_lifetimebound">];
+  let Subjects = SubjectList<[Function]>;
+  let LangOpts = [CPlusPlus];
+  let Documentation = [CoroLifetimeBoundDoc];
+  let SimpleHandler = 1;
+}
+
 // OSObject-based attributes.
 def OSConsumed : InheritableParamAttr {
   let Spellings = [Clang<"os_consumed">];
diff --git a/clang/include/clang/Basic/AttrDocs.td 
b/clang/include/clang/Basic/AttrDocs.td
index 98a7ecc7fd7df3..d6c4d12564e6b0 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -7671,9 +7671,12 @@ The ``[[clang::coro_lifetimebound]]`` is a class 
attribute which can be applied
 to a coroutine return type (`CRT`_) (i.e.
 it should also be annotated with ``[[clang::coro_return_type]]``).
 
-All parameters of a function are considered to be lifetime bound. See 
`documentation`_
-of ``[[clang::lifetimebound]]`` for more details.
-if the function returns a coroutine return type (CRT) annotated with 
``[[clang::coro_lifetimebound]]``.
+All parameters of a function are considered to be lifetime bound if the 
function returns a
+coroutine return type (CRT) annotated with ``[[clang::coro_lifetimebound]]``.
+This lifetime bound analysis can be disabled for a coroutine wrapper or a 
coroutine by annotating the function
+with ``[[clang::coro_not_lifetimebound]]`` function attribute .
+See `documentation`_ of ``[[clang::lifetimebound]]`` for details about 
lifetime bound analysis.
+
 
 Reference parameters of a coroutine are susceptible to capturing references to 
temporaries or local variables.
 
@@ -7703,7 +7706,7 @@ Both coroutines and coroutine wrappers are part of this 
analysis.
   };
 
   Task<int> coro(const int& a) { co_return a + 1; }
-  Task<int> [[clang::coro_wrapper]] coro_wrapper(const int& a, const int& b) {
+  [[clang::coro_wrapper]] Task<int> coro_wrapper(const int& a, const int& b) {
     return a > b ? coro(a) : coro(b);
   }
   Task<int> temporary_reference() {
@@ -7718,6 +7721,20 @@ Both coroutines and coroutine wrappers are part of this 
analysis.
     return coro(a); // warning: returning address of stack variable `a`.
   }
 
+This analysis can be disabled for a particular function by annotating it with 
function attribute
+``[[clang::coro_not_lifetimebound]]``. For example, this could be useful for 
coroutine wrappers
+which accept reference parameters but do not pass them to the underlying 
coroutine or pass them by value.
+
+.. code-block:: c++
+
+  Task<int> coro(int a) { co_return a + 1; }
+  [[clang::coro_wrapper, clang::coro_not_lifetimebound]] Task<int> 
coro_wrapper(const int& a) {
+    return coro(a + 1);
+  }
+  void use() {
+    auto task = coro_wrapper(1); // use of temporary is fine as coro_wrapper 
is not lifetime bound.
+  }
+
 .. _`documentation`: 
https://clang.llvm.org/docs/AttributeReference.html#lifetimebound
 .. _`CRT`: https://clang.llvm.org/docs/AttributeReference.html#coro-return-type
 }];
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 61d244f3bb9798..130aa5d04b82f2 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -7581,7 +7581,8 @@ static void visitLifetimeBoundArguments(IndirectLocalPath 
&Path, Expr *Call,
   bool CheckCoroCall = false;
   if (const auto *RD = Callee->getReturnType()->getAsRecordDecl()) {
     CheckCoroCall = RD->hasAttr<CoroLifetimeBoundAttr>() &&
-                    RD->hasAttr<CoroReturnTypeAttr>();
+                    RD->hasAttr<CoroReturnTypeAttr>() &&
+                    !Callee->hasAttr<CoroNotLifetimeBoundAttr>();
   }
   for (unsigned I = 0,
                 N = std::min<unsigned>(Callee->getNumParams(), Args.size());
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test 
b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 7b0cda0bca078d..88dd60ea584609 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -58,6 +58,7 @@
 // CHECK-NEXT: ConsumableSetOnRead (SubjectMatchRule_record)
 // CHECK-NEXT: Convergent (SubjectMatchRule_function)
 // CHECK-NEXT: CoroLifetimeBound (SubjectMatchRule_record)
+// CHECK-NEXT: CoroNotLifetimeBound (SubjectMatchRule_function)
 // CHECK-NEXT: CoroOnlyDestroyWhenComplete (SubjectMatchRule_record)
 // CHECK-NEXT: CoroReturnType (SubjectMatchRule_record)
 // CHECK-NEXT: CoroWrapper (SubjectMatchRule_function)
diff --git a/clang/test/SemaCXX/coro-lifetimebound.cpp 
b/clang/test/SemaCXX/coro-lifetimebound.cpp
index b4dc029a139848..dce909b3e41a9b 100644
--- a/clang/test/SemaCXX/coro-lifetimebound.cpp
+++ b/clang/test/SemaCXX/coro-lifetimebound.cpp
@@ -115,3 +115,18 @@ CoNoCRT<int> bar(int a) {
   co_return 1;
 }
 } // namespace not_a_crt
+
+// 
=============================================================================
+// Not lifetime bound coroutine wrappers: [[clang::coro_not_lifetimebound]].
+// 
=============================================================================
+namespace not_lifetimebound {
+Co<int> foo(int x) {  co_return x; }
+
+[[clang::coro_wrapper, clang::coro_not_lifetimebound]] 
+Co<int> foo_wrapper(const int& x) { return foo(x); }
+
+[[clang::coro_wrapper]] Co<int> caller() {
+  // The call to foo_wrapper is wrapper is safe.
+  return foo_wrapper(1);
+}
+} // namespace not_lifetimebound
\ No newline at end of file

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to