diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index ce7599ad34bea..903a50a4d4d3a 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_disable_lifetimebound]]``. Improvements to Clang's diagnostics ----------------------------------- diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index db17211747b17..fda62aaae22c7 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 CoroDisableLifetimeBound : InheritableAttr { + let Spellings = [Clang<"coro_disable_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 98a7ecc7fd7df..cd3dcf2ccf441 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_disable_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 coro(const int& a) { co_return a + 1; } - Task [[clang::coro_wrapper]] coro_wrapper(const int& a, const int& b) { + [[clang::coro_wrapper]] Task coro_wrapper(const int& a, const int& b) { return a > b ? coro(a) : coro(b); } Task temporary_reference() { @@ -7718,6 +7721,21 @@ 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 all calls to a particular function by annotating the function +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 coro(int a) { co_return a + 1; } + [[clang::coro_wrapper, clang::coro_disable_lifetimebound]] Task coro_wrapper(const int& a) { + return coro(a + 1); + } + void use() { + 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 .. _`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 61d244f3bb979..60c0e3e74204e 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() && - RD->hasAttr(); + RD->hasAttr() && + !Callee->hasAttr(); } for (unsigned I = 0, N = std::min(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 7b0cda0bca078..2f80c96e1d527 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -57,6 +57,7 @@ // CHECK-NEXT: ConsumableAutoCast (SubjectMatchRule_record) // CHECK-NEXT: ConsumableSetOnRead (SubjectMatchRule_record) // CHECK-NEXT: Convergent (SubjectMatchRule_function) +// CHECK-NEXT: CoroDisableLifetimeBound (SubjectMatchRule_function) // CHECK-NEXT: CoroLifetimeBound (SubjectMatchRule_record) // CHECK-NEXT: CoroOnlyDestroyWhenComplete (SubjectMatchRule_record) // CHECK-NEXT: CoroReturnType (SubjectMatchRule_record) diff --git a/clang/test/SemaCXX/coro-lifetimebound.cpp b/clang/test/SemaCXX/coro-lifetimebound.cpp index b4dc029a13984..3fc7ca70a14a1 100644 --- a/clang/test/SemaCXX/coro-lifetimebound.cpp +++ b/clang/test/SemaCXX/coro-lifetimebound.cpp @@ -115,3 +115,18 @@ CoNoCRT bar(int a) { co_return 1; } } // namespace not_a_crt + +// ============================================================================= +// Not lifetime bound coroutine wrappers: [[clang::coro_disable_lifetimebound]]. +// ============================================================================= +namespace disable_lifetimebound { +Co foo(int x) { co_return x; } + +[[clang::coro_wrapper, clang::coro_disable_lifetimebound]] +Co foo_wrapper(const int& x) { return foo(x); } + +[[clang::coro_wrapper]] Co caller() { + // The call to foo_wrapper is wrapper is safe. + return foo_wrapper(1); +} +} // namespace disable_lifetimebound