Author: Utkarsh Saxena Date: 2024-01-05T10:07:04+01:00 New Revision: 190a75b5f12d3872a5a26d6079d62adae40f147d
URL: https://github.com/llvm/llvm-project/commit/190a75b5f12d3872a5a26d6079d62adae40f147d DIFF: https://github.com/llvm/llvm-project/commit/190a75b5f12d3872a5a26d6079d62adae40f147d.diff LOG: [coroutines] Introduce [[clang::coro_disable_lifetimebound]] (#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_disable_lifetime_bound]]` to disable lifetime bound analysis for all calls to this function. --- One missing piece here is a note suggesting using this annotation in cases of lifetime warnings. This would require some more tweaks in the lifetimebound analysis to recognize violations involving coroutines only and produce this note only in those cases. Added: Modified: clang/docs/ReleaseNotes.rst clang/include/clang/Basic/Attr.td clang/include/clang/Basic/AttrDocs.td clang/lib/Sema/SemaInit.cpp clang/test/Misc/pragma-attribute-supported-attributes-list.test clang/test/SemaCXX/coro-lifetimebound.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index ce7599ad34beaf..903a50a4d4d3a4 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 db17211747b17d..fda62aaae22c78 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 98a7ecc7fd7df3..cd3dcf2ccf4411 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<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,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<int> coro(int a) { co_return a + 1; } + [[clang::coro_wrapper, clang::coro_disable_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 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 61d244f3bb9798..60c0e3e74204ec 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<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 7b0cda0bca078d..2f80c96e1d5271 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 b4dc029a139848..3fc7ca70a14a12 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_disable_lifetimebound]]. +// ============================================================================= +namespace disable_lifetimebound { +Co<int> foo(int x) { co_return x; } + +[[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 disable_lifetimebound _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits