[clang] [coroutines] Introduce [[clang::coro_not_lifetimebound]] (PR #76818)

Utkarsh Saxena via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 3 06:32:02 PST 2024


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

>From cc7ff8b7ecb93165172dbb481c7d5fcb64289a96 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Wed, 3 Jan 2024 14:44:58 +0100
Subject: [PATCH 1/3] [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

>From 926829018ade5a67012b54656f713e48e307dc99 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Wed, 3 Jan 2024 15:08:33 +0100
Subject: [PATCH 2/3] fix docs

---
 clang/include/clang/Basic/AttrDocs.td | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index d6c4d12564e6b0..8ef6f55cba4c98 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -7721,9 +7721,10 @@ 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.
+This analysis can be disabled for all calls to a particular function by annotating the function
+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++
 
@@ -7732,7 +7733,7 @@ which accept reference parameters but do not pass them to the underlying corouti
     return coro(a + 1);
   }
   void use() {
-    auto task = coro_wrapper(1); // use of temporary is fine as coro_wrapper is not lifetime bound.
+    auto task = coro_wrapper(1); // use of temporary is fine as the argument is not lifetime bound.
   }
 
 .. _`documentation`: https://clang.llvm.org/docs/AttributeReference.html#lifetimebound

>From 0dfb4900c6508f55c58ee70636e3fa027e8858bb Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Wed, 3 Jan 2024 15:31:47 +0100
Subject: [PATCH 3/3] rename to [[clang::coro_disable_lifetimebound]]

---
 clang/docs/ReleaseNotes.rst                               | 2 +-
 clang/include/clang/Basic/Attr.td                         | 4 ++--
 clang/include/clang/Basic/AttrDocs.td                     | 6 +++---
 clang/lib/Sema/SemaInit.cpp                               | 2 +-
 .../Misc/pragma-attribute-supported-attributes-list.test  | 2 +-
 clang/test/SemaCXX/coro-lifetimebound.cpp                 | 8 ++++----
 6 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 88a0bf7d005dab..d35910218601e4 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -357,7 +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]]``.
+  This analysis can be disabled for a function by annotating the function with ``[[clang::coro_disable_lifetimebound]]``.
 
 Improvements to Clang's diagnostics
 -----------------------------------
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 3dd3cb305dc4ff..fda62aaae22c78 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1121,8 +1121,8 @@ def CoroLifetimeBound : InheritableAttr {
   let SimpleHandler = 1;
 }
 
-def CoroNotLifetimeBound : InheritableAttr {
-  let Spellings = [Clang<"coro_not_lifetimebound">];
+def CoroDisableLifetimeBound : InheritableAttr {
+  let Spellings = [Clang<"coro_disable_lifetimebound">];
   let Subjects = SubjectList<[Function]>;
   let LangOpts = [CPlusPlus];
   let Documentation = [CoroLifetimeBoundDoc];
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 8ef6f55cba4c98..cd3dcf2ccf4411 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -7674,7 +7674,7 @@ it should also be annotated with ``[[clang::coro_return_type]]``).
 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 .
+with ``[[clang::coro_disable_lifetimebound]]`` function attribute .
 See `documentation`_ of ``[[clang::lifetimebound]]`` for details about lifetime bound analysis.
 
 
@@ -7722,14 +7722,14 @@ Both coroutines and coroutine wrappers are part of this analysis.
   }
 
 This analysis can be disabled for all calls to a particular function by annotating the function
-with function attribute ``[[clang::coro_not_lifetimebound]]``.
+with function attribute ``[[clang::coro_disable_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) {
+  [[clang::coro_wrapper, clang::coro_disable_lifetimebound]] Task<int> coro_wrapper(const int& a) {
     return coro(a + 1);
   }
   void use() {
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 130aa5d04b82f2..60c0e3e74204ec 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -7582,7 +7582,7 @@ static void visitLifetimeBoundArguments(IndirectLocalPath &Path, Expr *Call,
   if (const auto *RD = Callee->getReturnType()->getAsRecordDecl()) {
     CheckCoroCall = RD->hasAttr<CoroLifetimeBoundAttr>() &&
                     RD->hasAttr<CoroReturnTypeAttr>() &&
-                    !Callee->hasAttr<CoroNotLifetimeBoundAttr>();
+                    !Callee->hasAttr<CoroDisableLifetimeBoundAttr>();
   }
   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 88dd60ea584609..b3f6cdf21c5515 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -58,7 +58,7 @@
 // CHECK-NEXT: ConsumableSetOnRead (SubjectMatchRule_record)
 // CHECK-NEXT: Convergent (SubjectMatchRule_function)
 // CHECK-NEXT: CoroLifetimeBound (SubjectMatchRule_record)
-// CHECK-NEXT: CoroNotLifetimeBound (SubjectMatchRule_function)
+// CHECK-NEXT: CoroDisableLifetimeBoundAttr (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 dce909b3e41a9b..e8ab48eda70446 100644
--- a/clang/test/SemaCXX/coro-lifetimebound.cpp
+++ b/clang/test/SemaCXX/coro-lifetimebound.cpp
@@ -117,16 +117,16 @@ CoNoCRT<int> bar(int a) {
 } // namespace not_a_crt
 
 // =============================================================================
-// Not lifetime bound coroutine wrappers: [[clang::coro_not_lifetimebound]].
+// Not lifetime bound coroutine wrappers: [[clang::coro_disable_lifetimebound]].
 // =============================================================================
-namespace not_lifetimebound {
+namespace disable_lifetimebound {
 Co<int> foo(int x) {  co_return x; }
 
-[[clang::coro_wrapper, clang::coro_not_lifetimebound]] 
+[[clang::coro_wrapper, clang::coro_disable_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
+} // namespace disable_lifetimebound
\ No newline at end of file



More information about the cfe-commits mailing list