https://github.com/WillemKauf updated https://github.com/llvm/llvm-project/pull/182916
>From 267e91b89c918eb9680a70d2ea5740a069780c67 Mon Sep 17 00:00:00 2001 From: Willem Kaufmann <[email protected]> Date: Thu, 19 Mar 2026 21:14:08 -0400 Subject: [PATCH] [clang-tidy] Add `AllowExplicitObjectParameters` option to `avoid-capturing-lambda-coroutines` Add an off-by-default `AllowExplicitObjectParameters` option to the existing `cppcoreguidelines-avoid-capturing-lambda-coroutines` check. When enabled, lambda coroutines that use C++23 "deducing this" (explicit object parameter) are not flagged, since captures are moved into the coroutine frame ([1], [2], [3]). In C++23 mode, the check also provides fix-it hints to add `this auto` as the first parameter for lambdas that don't use it. The option is off by default to match the current C++ Core Guidelines, which do not yet recognize explicit object parameters as a solution ([4]). Once the guidelines adopt the proposal, the default can be flipped. [1]: https://github.com/scylladb/seastar/blob/master/doc/lambda-coroutine-fiasco.md#solution-c23-and-up [2]: https://www.scs.stanford.edu/~dm/blog/vexing-capture.html [3]: https://lists.isocpp.org/std-proposals/2020/05/1391.php [4]: https://github.com/isocpp/CppCoreGuidelines/pull/2289#issuecomment-3756500251 --- .../AvoidCapturingLambdaCoroutinesCheck.cpp | 103 +++++- .../AvoidCapturingLambdaCoroutinesCheck.h | 8 +- clang-tools-extra/docs/ReleaseNotes.rst | 7 + .../avoid-capturing-lambda-coroutines.rst | 32 ++ ...tines-allow-explicit-object-parameters.cpp | 346 ++++++++++++++++++ 5 files changed, 489 insertions(+), 7 deletions(-) create mode 100644 clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-capturing-lambda-coroutines-allow-explicit-object-parameters.cpp diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCapturingLambdaCoroutinesCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCapturingLambdaCoroutinesCheck.cpp index 618554663ab91..1f8c13d950842 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCapturingLambdaCoroutinesCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCapturingLambdaCoroutinesCheck.cpp @@ -9,6 +9,7 @@ #include "AvoidCapturingLambdaCoroutinesCheck.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" using namespace clang::ast_matchers; @@ -21,12 +22,27 @@ AST_MATCHER(LambdaExpr, hasCoroutineBody) { } AST_MATCHER(LambdaExpr, hasCaptures) { return Node.capture_size() != 0U; } + +AST_MATCHER(LambdaExpr, hasDeducingThis) { + return Node.getCallOperator()->isExplicitObjectMemberFunction(); +} } // namespace +AvoidCapturingLambdaCoroutinesCheck::AvoidCapturingLambdaCoroutinesCheck( + StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + AllowExplicitObjectParameters( + Options.get("AllowExplicitObjectParameters", false)) {} + void AvoidCapturingLambdaCoroutinesCheck::registerMatchers( MatchFinder *Finder) { - Finder->addMatcher( - lambdaExpr(hasCaptures(), hasCoroutineBody()).bind("lambda"), this); + auto Matcher = lambdaExpr(hasCaptures(), hasCoroutineBody()); + + if (AllowExplicitObjectParameters) + Matcher = lambdaExpr(hasCaptures(), hasCoroutineBody(), + unless(hasDeducingThis())); + + Finder->addMatcher(Matcher.bind("lambda"), this); } bool AvoidCapturingLambdaCoroutinesCheck::isLanguageVersionSupported( @@ -34,12 +50,89 @@ bool AvoidCapturingLambdaCoroutinesCheck::isLanguageVersionSupported( return LangOpts.CPlusPlus20; } +void AvoidCapturingLambdaCoroutinesCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "AllowExplicitObjectParameters", + AllowExplicitObjectParameters); +} + void AvoidCapturingLambdaCoroutinesCheck::check( const MatchFinder::MatchResult &Result) { const auto *MatchedLambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda"); - diag(MatchedLambda->getExprLoc(), - "coroutine lambda may cause use-after-free, avoid captures or ensure " - "lambda closure object has guaranteed lifetime"); + + if (AllowExplicitObjectParameters && getLangOpts().CPlusPlus23) { + const CXXMethodDecl *Call = MatchedLambda->getCallOperator(); + const bool HasExplicitParams = MatchedLambda->hasExplicitParameters(); + + auto DiagBuilder = + diag(MatchedLambda->getExprLoc(), + "coroutine lambda with captures may cause use-after-free; use " + "'this auto' as the first parameter to move captures into the " + "coroutine frame"); + + if (HasExplicitParams) { + const bool HasParams = !Call->param_empty(); + if (HasParams) { + const ParmVarDecl *FirstParam = Call->parameters().front(); + DiagBuilder << FixItHint::CreateInsertion(FirstParam->getBeginLoc(), + "this auto, "); + } else { + DiagBuilder << FixItHint::CreateInsertion( + Call->getFunctionTypeLoc().getRParenLoc(), "this auto"); + } + } else { + // No explicit parameter list — insert `(this auto) ` where the + // parameter list would go in the grammar: + // [captures] <tparams> t-requires front-attr (params) + // Start after the template parameter list (including its requires + // clause) or the capture list, then skip past any attributes that + // appear before the implicit parameter list position. + const auto &SM = *Result.SourceManager; + const auto &LO = getLangOpts(); + SourceLocation InsertLoc; + + if (const auto *TPL = MatchedLambda->getTemplateParameterList()) { + if (const Expr *RC = TPL->getRequiresClause()) + InsertLoc = Lexer::getLocForEndOfToken(RC->getEndLoc(), 0, SM, LO); + else + InsertLoc = + Lexer::getLocForEndOfToken(TPL->getRAngleLoc(), 0, SM, LO); + } else { + InsertLoc = Lexer::getLocForEndOfToken( + MatchedLambda->getIntroducerRange().getEnd(), 0, SM, LO); + } + + // Skip past any front-attributes. getRange() covers only the + // attribute name/arguments, not the enclosing brackets. + // Advance past the closing brackets based on the syntax: + // `[[attr]]` — 2 tokens (`]]`) + // `__attribute__((attr))` — 2 tokens (`))`) + // `__declspec(attr)` — 1 token (`)`) + // keyword / other — 0 tokens + if (Call->hasAttrs()) { + const auto *LastAttr = Call->getAttrs().back(); + SourceLocation AttrEnd = Lexer::getLocForEndOfToken( + LastAttr->getRange().getEnd(), 0, SM, LO); + unsigned ClosingTokens = 0; + if (LastAttr->isStandardAttributeSyntax() || + LastAttr->getSyntax() == AttributeCommonInfo::AS_GNU) + ClosingTokens = 2; + else if (LastAttr->getSyntax() == AttributeCommonInfo::AS_Declspec || + LastAttr->getSyntax() == AttributeCommonInfo::AS_Microsoft) + ClosingTokens = 1; + for (unsigned I = 0; I < ClosingTokens; ++I) + AttrEnd = Lexer::getLocForEndOfToken(AttrEnd, 0, SM, LO); + if (AttrEnd.isValid()) + InsertLoc = AttrEnd; + } + + DiagBuilder << FixItHint::CreateInsertion(InsertLoc, " (this auto)"); + } + } else { + diag(MatchedLambda->getExprLoc(), + "coroutine lambda may cause use-after-free, avoid captures or ensure " + "lambda closure object has guaranteed lifetime"); + } } } // namespace clang::tidy::cppcoreguidelines diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCapturingLambdaCoroutinesCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCapturingLambdaCoroutinesCheck.h index de59ff189c595..72e5ff3168719 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCapturingLambdaCoroutinesCheck.h +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCapturingLambdaCoroutinesCheck.h @@ -21,11 +21,15 @@ namespace clang::tidy::cppcoreguidelines { /// https://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/avoid-capturing-lambda-coroutines.html class AvoidCapturingLambdaCoroutinesCheck : public ClangTidyCheck { public: - AvoidCapturingLambdaCoroutinesCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} + AvoidCapturingLambdaCoroutinesCheck(StringRef Name, + ClangTidyContext *Context); void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; bool isLanguageVersionSupported(const LangOptions &LangOpts) const override; + +private: + const bool AllowExplicitObjectParameters; }; } // namespace clang::tidy::cppcoreguidelines diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 64c8fbbe2f07a..681e159c60599 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -191,6 +191,13 @@ Changes in existing checks the invalidating function in the warning message when a custom invalidation function is used (via the `InvalidationFunctions` option). +- Improved :doc:`cppcoreguidelines-avoid-capturing-lambda-coroutines + <clang-tidy/checks/cppcoreguidelines/avoid-capturing-lambda-coroutines>` + check by adding the `AllowExplicitObjectParameters` option. When enabled, + lambda coroutines using C++23 deducing ``this`` (explicit object parameter) + are not flagged, and fix-it hints are provided to add ``this auto`` to the + argument list of those that don't use it. + - Improved :doc:`cppcoreguidelines-init-variables <clang-tidy/checks/cppcoreguidelines/init-variables>` check by ensuring that member pointers are correctly flagged as uninitialized. diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-capturing-lambda-coroutines.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-capturing-lambda-coroutines.rst index 58bfc35c557dc..ecf4519b11219 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-capturing-lambda-coroutines.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-capturing-lambda-coroutines.rst @@ -52,3 +52,35 @@ captures or ensuring the lambda closure object has a guaranteed lifetime. Following these guidelines can help ensure the safe and reliable use of coroutine lambdas in C++ code. + +Options +------- + +.. option:: AllowExplicitObjectParameters + + When set to `true`, lambda coroutines that use C++23 "deducing this" + (explicit object parameter, e.g. ``this auto``) are not flagged by this + check, because the captures are moved into the coroutine frame, decoupling + their lifetime from the lambda object. Additionally, when compiling in C++23 + mode or later, the check will provide fix-it hints to add ``this auto`` as + the first parameter to lambda coroutines that do not already use it. + + Default is `false`. + + The example from above can be made safe and will pass this check with the + following change: + +.. code-block:: c++ + + int value = get_value(); + std::shared_ptr<Foo> sharedFoo = get_foo(); + { + // Pass "this auto" as the first argument to the lambda + const auto lambda = [value, sharedFoo](this auto) -> std::future<void> + { + co_await something(); + }; + lambda(); + } // the lambda closure object has now gone out of scope, but captures are + // no longer coupled to its lifetime + diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-capturing-lambda-coroutines-allow-explicit-object-parameters.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-capturing-lambda-coroutines-allow-explicit-object-parameters.cpp new file mode 100644 index 0000000000000..ca59407ab8ca3 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-capturing-lambda-coroutines-allow-explicit-object-parameters.cpp @@ -0,0 +1,346 @@ +// RUN: %check_clang_tidy -std=c++23 %s cppcoreguidelines-avoid-capturing-lambda-coroutines %t \ +// RUN: -- -config='{CheckOptions: {cppcoreguidelines-avoid-capturing-lambda-coroutines.AllowExplicitObjectParameters: true}}' \ +// RUN: -- -isystem %S/Inputs/system + +#include <coroutines.h> + +// --- Cases that SHOULD trigger the warning and provide fix-its --- + +void test_capture_no_parens() { + int x = 42; + [&x] -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free; use 'this auto' as the first parameter to move captures into the coroutine frame [cppcoreguidelines-avoid-capturing-lambda-coroutines] + // CHECK-FIXES: {{^}} [&x] (this auto) -> task {{{$}} + co_return; + }; +} + +void test_capture_empty_parens() { + int x = 42; + [&x]() -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&x](this auto) -> task {{{$}} + co_return; + }; +} + +void test_capture_with_params() { + int x = 42; + [&x](int a) -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&x](this auto, int a) -> task {{{$}} + co_return; + }; +} + +void test_capture_with_multiple_params() { + int x = 42; + [&x](int a, int b) -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&x](this auto, int a, int b) -> task {{{$}} + co_return; + }; +} + +void test_capture_by_value() { + int x = 42; + [x]() -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [x](this auto) -> task {{{$}} + co_return; + }; +} + +void test_default_capture_ref() { + int x = 42; + [&]() -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&](this auto) -> task {{{$}} + (void)x; + co_return; + }; +} + +void test_default_capture_copy() { + int x = 42; + [=]() -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [=](this auto) -> task {{{$}} + (void)x; + co_return; + }; +} + +void test_init_capture() { + int x = 42; + [y = x]() -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [y = x](this auto) -> task {{{$}} + co_return; + }; +} + +void test_init_capture_brace() { + int x = 42; + [y{x}]() -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [y{x}](this auto) -> task {{{$}} + co_return; + }; +} + +struct S { + void test_this_capture() { + [this]() -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [this](this auto) -> task {{{$}} + co_return; + }; + } + + void test_star_this_capture() { + [*this]() -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [*this](this auto) -> task {{{$}} + co_return; + }; + } +}; + +void test_mutable() { + int x = 42; + [x]() mutable -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [x](this auto) mutable -> task {{{$}} + co_return; + }; +} + +void test_noexcept() { + int x = 42; + [&x]() noexcept -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&x](this auto) noexcept -> task {{{$}} + co_return; + }; +} + +void test_template_params() { + int x = 42; + [&x]<typename T>(T a) -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&x]<typename T>(this auto, T a) -> task {{{$}} + co_return; + }; +} + +void test_template_params_empty_parens() { + int x = 42; + [&x]<typename T>() -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&x]<typename T>(this auto) -> task {{{$}} + co_return; + }; +} + +void test_template_params_no_parens() { + int x = 42; + [&x]<typename T> -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&x]<typename T> (this auto) -> task {{{$}} + co_return; + }; +} + +void test_auto_param() { + int x = 42; + [&x](auto a) -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&x](this auto, auto a) -> task {{{$}} + co_return; + }; +} + +void test_front_attr_with_parens() { + int x = 42; + [&x] [[nodiscard]] () -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&x] {{\[\[}}nodiscard]] (this auto) -> task {{{$}} + co_return; + }; +} + +void test_front_attr_no_parens() { + int x = 42; + [&x] [[nodiscard]] -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&x] {{\[\[}}nodiscard]] (this auto) -> task {{{$}} + co_return; + }; +} + +void test_multiple_front_attrs_no_parens() { + int x = 42; + [&x] [[nodiscard]] [[deprecated("use something else")]] -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&x] {{\[\[}}nodiscard]] {{\[\[}}deprecated("use something else")]] (this auto) -> task {{{$}} + co_return; + }; +} + +void test_template_with_front_attr() { + int x = 42; + [&x]<typename T> [[nodiscard]] (T a) -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&x]<typename T> {{\[\[}}nodiscard]] (this auto, T a) -> task {{{$}} + co_return; + }; +} + +void test_gnu_attr_no_parens() { + int x = 42; + [&x] __attribute__((noinline)) -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&x] __attribute__((noinline)) (this auto) -> task {{{$}} + co_return; + }; +} + +void test_template_with_multiple_front_attrs_no_parens() { + int x = 42; + [&x]<typename T> [[nodiscard]] [[deprecated("bad")]] -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&x]<typename T> {{\[\[}}nodiscard]] {{\[\[}}deprecated("bad")]] (this auto) -> task {{{$}} + co_return; + }; +} + +void test_mixed_captures_default_ref() { + int x = 42, y = 0; + [&, x]() -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&, x](this auto) -> task {{{$}} + (void)y; + co_return; + }; +} + +void test_mixed_captures_default_copy() { + int x = 42, y = 0; + [=, &x]() -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [=, &x](this auto) -> task {{{$}} + (void)y; + co_return; + }; +} + +void test_variadic_template_params() { + int x = 42; + [&x]<typename... Ts>(Ts... args) -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&x]<typename... Ts>(this auto, Ts... args) -> task {{{$}} + co_return; + }; +} + +void test_param_pack() { + int x = 42; + [&x](auto&&... args) -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&x](this auto, auto&&... args) -> task {{{$}} + co_return; + }; +} + +template<typename T> +concept Integral = requires(T t) { t + 1; }; + +void test_template_requires_clause() { + int x = 42; + [&x]<typename T> requires Integral<T> (T a) -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&x]<typename T> requires Integral<T> (this auto, T a) -> task {{{$}} + co_return; + }; +} + +void test_template_requires_no_parens() { + int x = 42; + [&x]<typename T> requires Integral<T> -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&x]<typename T> requires Integral<T> (this auto) -> task {{{$}} + co_return; + }; +} + +void test_trailing_requires() { + int x = 42; + [&x](auto a) -> task requires Integral<decltype(a)> { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&x](this auto, auto a) -> task requires Integral<decltype(a)> {{{$}} + co_return; + }; +} + +void test_mutable_noexcept() { + int x = 42; + [x]() mutable noexcept -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [x](this auto) mutable noexcept -> task {{{$}} + co_return; + }; +} + +void test_noexcept_expr() { + int x = 42; + [&x]() noexcept(true) -> task { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: coroutine lambda with captures may cause use-after-free + // CHECK-FIXES: {{^}} [&x](this auto) noexcept(true) -> task {{{$}} + co_return; + }; +} + +// --- Cases that should NOT trigger the warning --- + +void test_no_captures_no_coroutine() { + []() { return; }; +} + +void test_no_captures_coroutine() { + []() -> task { co_return; }; +} + +void test_deducing_this_coroutine() { + int x = 42; + [&x](this auto) -> task { co_return; }; +} + +void test_deducing_this_with_params() { + int x = 42; + [&x](this auto, int a) -> task { co_return; }; +} + +void test_deducing_this_with_template() { + int x = 42; + [&x]<typename T>(this auto, T a) -> task { co_return; }; +} + +void test_captures_not_coroutine() { + int x = 42; + [&x]() { (void)x; }; +} + +void test_no_captures_template_coroutine() { + []<typename T>(T a) -> task { co_return; }; +} + +void test_deducing_this_ref_qualified() { + int x = 42; + [&x](this auto&&) -> task { co_return; }; +} + +void test_deducing_this_with_requires() { + int x = 42; + [&x]<typename T>(this auto, T a) -> task requires Integral<T> { co_return; }; +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
