[clang] [coroutines] Introduce [[clang::coro_not_lifetimebound]] (PR #76818)
Utkarsh Saxena via cfe-commits
cfe-commits at lists.llvm.org
Wed Jan 3 06:08:44 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/2] [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/2] 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
More information about the cfe-commits
mailing list