[clang] [coroutines] Introduce [[clang::coro_lifetimebound]] (PR #72851)
Utkarsh Saxena via cfe-commits
cfe-commits at lists.llvm.org
Tue Nov 21 02:22:16 PST 2023
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/72851
>From 164bf1e94ec05e50be05d085ce2a4381711df11b Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Mon, 20 Nov 2023 12:17:30 +0100
Subject: [PATCH 1/8] Introduce [[clang::coro_lifetimebound]]
(cherry picked from commit 28e9fda4b78e1e60287048891cc92bafdef3ac4c)
---
clang/include/clang/Basic/Attr.td | 8 ++
clang/include/clang/Basic/AttrDocs.td | 8 +-
clang/lib/Sema/SemaInit.cpp | 7 +-
...a-attribute-supported-attributes-list.test | 3 +-
clang/test/SemaCXX/coro-lifetimebound.cpp | 93 +++++++++++++++++++
5 files changed, 116 insertions(+), 3 deletions(-)
create mode 100644 clang/test/SemaCXX/coro-lifetimebound.cpp
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index c2fbdfc66c540d6..03ed6accf700c4e 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1110,6 +1110,14 @@ def CoroWrapper : InheritableAttr {
let SimpleHandler = 1;
}
+def CoroLifetimeBound : InheritableAttr {
+ let Spellings = [Clang<"coro_lifetimebound">];
+ let Subjects = SubjectList<[CXXRecord]>;
+ 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 b5ceb47b6b8ad55..8b81926a0477db2 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -7483,7 +7483,6 @@ generation of the other destruction cases, optimizing the above `foo.destroy` to
}];
}
-
def CoroReturnTypeAndWrapperDoc : Documentation {
let Category = DocCatDecl;
let Content = [{
@@ -7581,3 +7580,10 @@ alignment boundary. Its value must be a power of 2, between 1 and 4096
}];
}
+
+def CoroLifetimeBoundDoc : Documentation {
+ let Category = DocCatDecl;
+ let Content = [{
+asdam
+}];
+}
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 80b51b09bf5445f..631b6a266412ccb 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -7580,10 +7580,15 @@ static void visitLifetimeBoundArguments(IndirectLocalPath &Path, Expr *Call,
if (ObjectArg && implicitObjectParamIsLifetimeBound(Callee))
VisitLifetimeBoundArg(Callee, ObjectArg);
+ bool checkCoroCall = false;
+ if (const auto *RD = Callee->getReturnType()->getAsRecordDecl()) {
+ checkCoroCall |= RD->hasAttr<CoroLifetimeBoundAttr>() &&
+ RD->hasAttr<CoroReturnTypeAttr>();
+ }
for (unsigned I = 0,
N = std::min<unsigned>(Callee->getNumParams(), Args.size());
I != N; ++I) {
- if (Callee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())
+ if (checkCoroCall || Callee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())
VisitLifetimeBoundArg(Callee->getParamDecl(I), Args[I]);
}
}
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index a57bc011c059483..dd91f4f88ad685b 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -56,9 +56,10 @@
// CHECK-NEXT: ConsumableAutoCast (SubjectMatchRule_record)
// CHECK-NEXT: ConsumableSetOnRead (SubjectMatchRule_record)
// CHECK-NEXT: Convergent (SubjectMatchRule_function)
+// CHECK-NEXT: CoroLifetimeBound (SubjectMatchRule_record)
// CHECK-NEXT: CoroOnlyDestroyWhenComplete (SubjectMatchRule_record)
// CHECK-NEXT: CoroReturnType (SubjectMatchRule_record)
-// CHECK-NEXT: CoroWrapper
+// CHECK-NEXT: CoroWrapper (SubjectMatchRule_function)
// CHECK-NEXT: CountedBy (SubjectMatchRule_field)
// CHECK-NEXT: DLLExport (SubjectMatchRule_function, SubjectMatchRule_variable, SubjectMatchRule_record, SubjectMatchRule_objc_interface)
// CHECK-NEXT: DLLImport (SubjectMatchRule_function, SubjectMatchRule_variable, SubjectMatchRule_record, SubjectMatchRule_objc_interface)
diff --git a/clang/test/SemaCXX/coro-lifetimebound.cpp b/clang/test/SemaCXX/coro-lifetimebound.cpp
new file mode 100644
index 000000000000000..3f719866eae0ee4
--- /dev/null
+++ b/clang/test/SemaCXX/coro-lifetimebound.cpp
@@ -0,0 +1,93 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -std=c++20 -fsyntax-only -verify -Wall -Wextra -Wno-error=unreachable-code -Wno-unused
+
+#include "Inputs/std-coroutine.h"
+
+using std::suspend_always;
+using std::suspend_never;
+
+template <typename T> struct [[clang::coro_lifetimebound, clang::coro_return_type]] Gen {
+ struct promise_type {
+ Gen<T> get_return_object() {
+ return {};
+ }
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void unhandled_exception();
+ void return_value(const T &t);
+
+ template <typename U>
+ auto await_transform(const Gen<U> &) {
+ struct awaitable {
+ bool await_ready() noexcept { return false; }
+ void await_suspend(std::coroutine_handle<>) noexcept {}
+ U await_resume() noexcept { return {}; }
+ };
+ return awaitable{};
+ }
+ };
+};
+
+template <typename T> using Co = Gen<T>;
+
+Gen<int> foo_coro(const int& b);
+
+Gen<int> foo_coro(const int& b) {
+ if (b > 0)
+ co_return 1;
+ co_return 2;
+}
+
+int getInt() { return 0; }
+
+Co<int> bar_coro(const int &b, int c) {
+ int x = co_await foo_coro(b);
+ int y = co_await foo_coro(1);
+ int z = co_await foo_coro(getInt());
+ auto unsafe1 = foo_coro(1); // expected-warning {{temporary whose address is used as value of local variable}}
+ auto unsafe2 = foo_coro(getInt()); // expected-warning {{temporary whose address is used as value of local variable}}
+ auto safe1 = foo_coro(b);
+ auto safe2 = foo_coro(c);
+ co_return co_await foo_coro(co_await foo_coro(1));
+}
+
+[[clang::coro_wrapper]] Gen<int> plain_return_co(int b) {
+ return foo_coro(b); // expected-warning {{address of stack memory associated with parameter}}
+}
+
+[[clang::coro_wrapper]] Gen<int> safe_forwarding(const int& b) {
+ return foo_coro(b);
+}
+
+[[clang::coro_wrapper]] Gen<int> unsafe_wrapper(int b) {
+ return safe_forwarding(b); // expected-warning {{address of stack memory associated with parameter}}
+}
+
+[[clang::coro_wrapper]] Co<int> complex_plain_return(int b) {
+ return b > 0
+ ? foo_coro(1) // expected-warning {{returning address of local temporary object}}
+ : bar_coro(0, 1); // expected-warning {{returning address of local temporary object}}
+}
+
+#define CORO_WRAPPER \
+ _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Wc++23-extensions\"") \
+ [[clang::coro_wrapper]] \
+ _Pragma("clang diagnostic pop")
+
+void lambdas() {
+ auto unsafe_lambda = [] CORO_WRAPPER (int b) {
+ return foo_coro(b); // expected-warning {{address of stack memory associated with parameter}}
+ };
+ auto safe_lambda = [](int b) -> Co<int> {
+ int x = co_await foo_coro(1);
+ co_return x + co_await foo_coro(b);
+ };
+}
+// =============================================================================
+// Safe usage when parameters are value
+// =============================================================================
+namespace by_value {
+Gen<int> value_coro(int b) { co_return co_await foo_coro(b); }
+[[clang::coro_wrapper]] Gen<int> wrapper1(int b) { return value_coro(b); }
+[[clang::coro_wrapper]] Gen<int> wrapper2(const int& b) { return value_coro(b); }
+}
>From 51fe7bc2312969ea9c3f7e9b1fada025bf7fe681 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Mon, 20 Nov 2023 16:14:25 +0100
Subject: [PATCH 2/8] add docs
(cherry picked from commit cfc5fa4e59e551a391ecc9be8db6d4b98d37e45a)
---
clang/include/clang/Basic/AttrDocs.td | 49 ++++++++++++++++++++++-
clang/test/SemaCXX/coro-lifetimebound.cpp | 35 +++++++++-------
2 files changed, 69 insertions(+), 15 deletions(-)
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 8b81926a0477db2..071dacd0db51e3b 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -7584,6 +7584,53 @@ alignment boundary. Its value must be a power of 2, between 1 and 4096
def CoroLifetimeBoundDoc : Documentation {
let Category = DocCatDecl;
let Content = [{
-asdam
+The ``[[clang::coro_lifetimebound]]`` is a class attribute which can be applied
+to a `coroutine return type (CRT) <https://clang.llvm.org/docs/AttributeReference.html#coro-return-type>` _ (i.e.
+it should also be annotated with ``[[clang::coro_return_type]]``).
+
+All arguments to a function are considered to be lifetime bound if the function
+returns a coroutine return type (CRT) annotated with ``[[clang::coro_lifetimebound]]``.
+
+Reference parameters of a coroutine are susceptible to capturing references to temporaries or local variables.
+
+For example,
+
+.. code-block:: c++
+ task<int> coro(const int& a) { co_return a + 1; }
+ task<int> dangling_refs(int a) {
+ // `coro` captures reference to a temporary. `foo` would now contain a dangling reference to `a`.
+ auto foo = coro(1);
+ // `coro` captures reference to local variable `a` which is destroyed after the return.
+ return coro(a);
+ }
+
+`Lifetime bound <https://clang.llvm.org/docs/AttributeReference.html#lifetimebound> _` static analysis
+can be used to detect such instances when coroutines capture references which may die earlier than the
+coroutine frame itself. In the above example, if the CRT `task` is annotated with
+``[[clang::coro_lifetimebound]]``, then lifetime bound analysis would detect capturing reference to
+temporaries or return address of a local variable.
+
+Both coroutines and coroutine wrappers are part of this analysis.
+
+.. code-block:: c++
+ template <typename T> struct [[clang::coro_return_type, clang::coro_lifetimebound]] Task {
+ using promise_type = some_promise_type;
+ };
+
+ Task<int> coro(const int& a) { co_return a + 1; }
+ Task<int> [[clang::coro_wrapper]] coro_wrapper(const int& a, const int& b) {
+ return a > b ? coro(a) : coro(b);
+ }
+ Task<int> temporary_reference() {
+ auto foo = coro(1); // warning: capturing reference to a temporary which would die after the expression.
+
+ int a = 1;
+ auto bar = coro_wrapper(a, 0); // warning: `b` captures reference to a temporary.
+
+ co_return co_await coro(1); // fine.
+ }
+ Task<int> stack_reference(int a) {
+ return coro(a); // returning address of stack variable `a`.
+ }
}];
}
diff --git a/clang/test/SemaCXX/coro-lifetimebound.cpp b/clang/test/SemaCXX/coro-lifetimebound.cpp
index 3f719866eae0ee4..a12315d17096199 100644
--- a/clang/test/SemaCXX/coro-lifetimebound.cpp
+++ b/clang/test/SemaCXX/coro-lifetimebound.cpp
@@ -5,9 +5,9 @@
using std::suspend_always;
using std::suspend_never;
-template <typename T> struct [[clang::coro_lifetimebound, clang::coro_return_type]] Gen {
+template <typename T> struct [[clang::coro_lifetimebound, clang::coro_return_type]] Co {
struct promise_type {
- Gen<T> get_return_object() {
+ Co<T> get_return_object() {
return {};
}
suspend_always initial_suspend();
@@ -16,7 +16,7 @@ template <typename T> struct [[clang::coro_lifetimebound, clang::coro_return_typ
void return_value(const T &t);
template <typename U>
- auto await_transform(const Gen<U> &) {
+ auto await_transform(const Co<U> &) {
struct awaitable {
bool await_ready() noexcept { return false; }
void await_suspend(std::coroutine_handle<>) noexcept {}
@@ -27,11 +27,7 @@ template <typename T> struct [[clang::coro_lifetimebound, clang::coro_return_typ
};
};
-template <typename T> using Co = Gen<T>;
-
-Gen<int> foo_coro(const int& b);
-
-Gen<int> foo_coro(const int& b) {
+Co<int> foo_coro(const int& b) {
if (b > 0)
co_return 1;
co_return 2;
@@ -50,15 +46,15 @@ Co<int> bar_coro(const int &b, int c) {
co_return co_await foo_coro(co_await foo_coro(1));
}
-[[clang::coro_wrapper]] Gen<int> plain_return_co(int b) {
+[[clang::coro_wrapper]] Co<int> plain_return_co(int b) {
return foo_coro(b); // expected-warning {{address of stack memory associated with parameter}}
}
-[[clang::coro_wrapper]] Gen<int> safe_forwarding(const int& b) {
+[[clang::coro_wrapper]] Co<int> safe_forwarding(const int& b) {
return foo_coro(b);
}
-[[clang::coro_wrapper]] Gen<int> unsafe_wrapper(int b) {
+[[clang::coro_wrapper]] Co<int> unsafe_wrapper(int b) {
return safe_forwarding(b); // expected-warning {{address of stack memory associated with parameter}}
}
@@ -78,6 +74,17 @@ void lambdas() {
auto unsafe_lambda = [] CORO_WRAPPER (int b) {
return foo_coro(b); // expected-warning {{address of stack memory associated with parameter}}
};
+ auto coro_lambda = [] (const int&) -> Co<int> {
+ co_return 0;
+ };
+ auto unsafe_coro_lambda = [&] (const int& b) -> Co<int> {
+ int x = co_await coro_lambda(b);
+ auto safe = coro_lambda(b);
+ auto unsafe1 = coro_lambda(1); // expected-warning {{temporary whose address is used as value of local variable}}
+ auto unsafe2 = coro_lambda(getInt()); // expected-warning {{temporary whose address is used as value of local variable}}
+ auto unsafe3 = coro_lambda(co_await coro_lambda(b)); // expected-warning {{temporary whose address is used as value of local variable}}
+ co_return co_await safe;
+ };
auto safe_lambda = [](int b) -> Co<int> {
int x = co_await foo_coro(1);
co_return x + co_await foo_coro(b);
@@ -87,7 +94,7 @@ void lambdas() {
// Safe usage when parameters are value
// =============================================================================
namespace by_value {
-Gen<int> value_coro(int b) { co_return co_await foo_coro(b); }
-[[clang::coro_wrapper]] Gen<int> wrapper1(int b) { return value_coro(b); }
-[[clang::coro_wrapper]] Gen<int> wrapper2(const int& b) { return value_coro(b); }
+Co<int> value_coro(int b) { co_return co_await foo_coro(b); }
+[[clang::coro_wrapper]] Co<int> wrapper1(int b) { return value_coro(b); }
+[[clang::coro_wrapper]] Co<int> wrapper2(const int& b) { return value_coro(b); }
}
>From eee67c0b359080bf6bfd2904d6944ada06b46438 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Mon, 20 Nov 2023 16:23:23 +0100
Subject: [PATCH 3/8] add Release notes
(cherry picked from commit 3f5f1a370300ff41483efd6b68c4dd9d85ed025a)
---
clang/docs/ReleaseNotes.rst | 6 ++++++
clang/include/clang/Basic/AttrDocs.td | 4 ++--
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 93ec15a7f095961..edc0d32794d7547 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -317,6 +317,7 @@ Attribute Changes in Clang
should be a coroutine. A non-coroutine function marked with ``[[clang::coro_wrapper]]``
is still allowed to return the such a type. This is helpful for analyzers to recognize coroutines from the function signatures.
+<<<<<<< HEAD
- Clang now supports ``[[clang::code_align(N)]]`` as an attribute which can be
applied to a loop and specifies the byte alignment for a loop. This attribute
accepts a positive integer constant initialization expression indicating the
@@ -333,6 +334,11 @@ Attribute Changes in Clang
void func() {
[[clang::code_align(A)]] for(;;) { }
}
+=======
+- Clang now introduced ``[[clang::coro_lifetimebound]]`` attribute.
+ All arguments to a function are considered to be lifetime bound if the function
+ returns a type annotated with ``[[clang::coro_lifetimebound]]`` and ``[[clang::coro_return_type]]``.
+>>>>>>> 3f5f1a370300 (add Release notes)
Improvements to Clang's diagnostics
-----------------------------------
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 071dacd0db51e3b..945819cde63ec21 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -7588,8 +7588,8 @@ The ``[[clang::coro_lifetimebound]]`` is a class attribute which can be applied
to a `coroutine return type (CRT) <https://clang.llvm.org/docs/AttributeReference.html#coro-return-type>` _ (i.e.
it should also be annotated with ``[[clang::coro_return_type]]``).
-All arguments to a function are considered to be lifetime bound if the function
-returns a coroutine return type (CRT) annotated with ``[[clang::coro_lifetimebound]]``.
+All arguments to a function are considered to be `lifetime bound <https://clang.llvm.org/docs/AttributeReference.html#lifetimebound> _`
+if the function returns a coroutine return type (CRT) annotated with ``[[clang::coro_lifetimebound]]``.
Reference parameters of a coroutine are susceptible to capturing references to temporaries or local variables.
>From dc53ef5fa4e0d1088395be462f760edcc7642dc4 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Mon, 20 Nov 2023 17:41:03 +0100
Subject: [PATCH 4/8] fix codeblock formatting
(cherry picked from commit 54c62257caaa3c5070b977bad9bc96bdd6c8c69e)
---
clang/include/clang/Basic/AttrDocs.td | 2 ++
1 file changed, 2 insertions(+)
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 945819cde63ec21..e8914838d6e89d0 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -7596,6 +7596,7 @@ Reference parameters of a coroutine are susceptible to capturing references to t
For example,
.. code-block:: c++
+
task<int> coro(const int& a) { co_return a + 1; }
task<int> dangling_refs(int a) {
// `coro` captures reference to a temporary. `foo` would now contain a dangling reference to `a`.
@@ -7613,6 +7614,7 @@ temporaries or return address of a local variable.
Both coroutines and coroutine wrappers are part of this analysis.
.. code-block:: c++
+
template <typename T> struct [[clang::coro_return_type, clang::coro_lifetimebound]] Task {
using promise_type = some_promise_type;
};
>From de7e09406a758ddf8cd6d3c31ad155cf9e759157 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Tue, 21 Nov 2023 03:39:40 +0100
Subject: [PATCH 5/8] address comments and add negative test
(cherry picked from commit 932ddd98e6b46b21f1d85834d2586c6de8e024aa)
---
clang/lib/Sema/SemaInit.cpp | 4 ++--
clang/test/SemaCXX/coro-lifetimebound.cpp | 20 ++++++++++++++++++++
2 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 631b6a266412ccb..d8d7d2ee4f48c77 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -7582,8 +7582,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>();
+ checkCoroCall = RD->hasAttr<CoroLifetimeBoundAttr>() &&
+ RD->hasAttr<CoroReturnTypeAttr>();
}
for (unsigned I = 0,
N = std::min<unsigned>(Callee->getNumParams(), Args.size());
diff --git a/clang/test/SemaCXX/coro-lifetimebound.cpp b/clang/test/SemaCXX/coro-lifetimebound.cpp
index a12315d17096199..92229b615866fd7 100644
--- a/clang/test/SemaCXX/coro-lifetimebound.cpp
+++ b/clang/test/SemaCXX/coro-lifetimebound.cpp
@@ -98,3 +98,23 @@ Co<int> value_coro(int b) { co_return co_await foo_coro(b); }
[[clang::coro_wrapper]] Co<int> wrapper1(int b) { return value_coro(b); }
[[clang::coro_wrapper]] Co<int> wrapper2(const int& b) { return value_coro(b); }
}
+
+// =============================================================================
+// Lifetime bound but not a Coroutine Return Type: No analysis.
+// =============================================================================
+namespace not_a_crt {
+template <typename T> struct [[clang::coro_lifetimebound]] Co {
+ struct promise_type {
+ Co<T> get_return_object() {
+ return {};
+ }
+ suspend_always initial_suspend();
+ suspend_always final_suspend() noexcept;
+ void unhandled_exception();
+ void return_value(const T &t);
+ };
+};
+
+Co<int> foo_coro(const int& a) { co_return a; }
+Co<int> bar(int a) { return foo_coro(a); }
+} // namespace not_a_crt
>From 1f5f39dbbcdf1a7fb6e8687cc2fd45f08a37c367 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Tue, 21 Nov 2023 04:48:23 +0100
Subject: [PATCH 6/8] fix release notes
---
clang/docs/ReleaseNotes.rst | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index edc0d32794d7547..afdf4e8fdb78ec0 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -317,7 +317,6 @@ Attribute Changes in Clang
should be a coroutine. A non-coroutine function marked with ``[[clang::coro_wrapper]]``
is still allowed to return the such a type. This is helpful for analyzers to recognize coroutines from the function signatures.
-<<<<<<< HEAD
- Clang now supports ``[[clang::code_align(N)]]`` as an attribute which can be
applied to a loop and specifies the byte alignment for a loop. This attribute
accepts a positive integer constant initialization expression indicating the
@@ -334,11 +333,10 @@ Attribute Changes in Clang
void func() {
[[clang::code_align(A)]] for(;;) { }
}
-=======
+
- Clang now introduced ``[[clang::coro_lifetimebound]]`` attribute.
All arguments to a function are considered to be lifetime bound if the function
returns a type annotated with ``[[clang::coro_lifetimebound]]`` and ``[[clang::coro_return_type]]``.
->>>>>>> 3f5f1a370300 (add Release notes)
Improvements to Clang's diagnostics
-----------------------------------
>From b6e440bc8ecfaf7614db629086e7b5c43abf0657 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Tue, 21 Nov 2023 11:13:02 +0100
Subject: [PATCH 7/8] address comments
---
clang/include/clang/Basic/AttrDocs.td | 4 ++--
clang/lib/Sema/SemaInit.cpp | 6 +++---
clang/test/SemaCXX/coro-lifetimebound.cpp | 9 ++++++---
3 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index e8914838d6e89d0..f85b3a5fbf0bd40 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -7631,8 +7631,8 @@ Both coroutines and coroutine wrappers are part of this analysis.
co_return co_await coro(1); // fine.
}
- Task<int> stack_reference(int a) {
- return coro(a); // returning address of stack variable `a`.
+ [[clang::coro_wrapper]] Task<int> stack_reference(int a) {
+ return coro(a); // warning: returning address of stack variable `a`.
}
}];
}
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index d8d7d2ee4f48c77..c0c321f0f200d21 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -7580,15 +7580,15 @@ static void visitLifetimeBoundArguments(IndirectLocalPath &Path, Expr *Call,
if (ObjectArg && implicitObjectParamIsLifetimeBound(Callee))
VisitLifetimeBoundArg(Callee, ObjectArg);
- bool checkCoroCall = false;
+ bool CheckCoroCall = false;
if (const auto *RD = Callee->getReturnType()->getAsRecordDecl()) {
- checkCoroCall = RD->hasAttr<CoroLifetimeBoundAttr>() &&
+ CheckCoroCall = RD->hasAttr<CoroLifetimeBoundAttr>() &&
RD->hasAttr<CoroReturnTypeAttr>();
}
for (unsigned I = 0,
N = std::min<unsigned>(Callee->getNumParams(), Args.size());
I != N; ++I) {
- if (checkCoroCall || Callee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())
+ if (CheckCoroCall || Callee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())
VisitLifetimeBoundArg(Callee->getParamDecl(I), Args[I]);
}
}
diff --git a/clang/test/SemaCXX/coro-lifetimebound.cpp b/clang/test/SemaCXX/coro-lifetimebound.cpp
index 92229b615866fd7..cfa2bde8fe07119 100644
--- a/clang/test/SemaCXX/coro-lifetimebound.cpp
+++ b/clang/test/SemaCXX/coro-lifetimebound.cpp
@@ -103,7 +103,7 @@ Co<int> value_coro(int b) { co_return co_await foo_coro(b); }
// Lifetime bound but not a Coroutine Return Type: No analysis.
// =============================================================================
namespace not_a_crt {
-template <typename T> struct [[clang::coro_lifetimebound]] Co {
+template <typename T> struct [[clang::coro_lifetimebound]] CoNoCRT {
struct promise_type {
Co<T> get_return_object() {
return {};
@@ -115,6 +115,9 @@ template <typename T> struct [[clang::coro_lifetimebound]] Co {
};
};
-Co<int> foo_coro(const int& a) { co_return a; }
-Co<int> bar(int a) { return foo_coro(a); }
+CoNoCRT<int> foo_coro(const int& a) { co_return a; }
+CoNoCRT<int> bar(int a) {
+ auto x = foo_coro(a);
+ co_return co_await x;
+}
} // namespace not_a_crt
>From 5dd43ae069167dcf2c1c3a207d6a72271afe3620 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Tue, 21 Nov 2023 11:21:58 +0100
Subject: [PATCH 8/8] fix CoNoCRT
---
clang/test/SemaCXX/coro-lifetimebound.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/test/SemaCXX/coro-lifetimebound.cpp b/clang/test/SemaCXX/coro-lifetimebound.cpp
index cfa2bde8fe07119..d3e2d673ebb3c0d 100644
--- a/clang/test/SemaCXX/coro-lifetimebound.cpp
+++ b/clang/test/SemaCXX/coro-lifetimebound.cpp
@@ -105,7 +105,7 @@ Co<int> value_coro(int b) { co_return co_await foo_coro(b); }
namespace not_a_crt {
template <typename T> struct [[clang::coro_lifetimebound]] CoNoCRT {
struct promise_type {
- Co<T> get_return_object() {
+ CoNoCRT<T> get_return_object() {
return {};
}
suspend_always initial_suspend();
@@ -118,6 +118,6 @@ template <typename T> struct [[clang::coro_lifetimebound]] CoNoCRT {
CoNoCRT<int> foo_coro(const int& a) { co_return a; }
CoNoCRT<int> bar(int a) {
auto x = foo_coro(a);
- co_return co_await x;
+ co_return 1;
}
} // namespace not_a_crt
More information about the cfe-commits
mailing list