https://github.com/dmpolukhin updated 
https://github.com/llvm/llvm-project/pull/140912

>From e3ce9bb18f165649b00db14b2282649315b28883 Mon Sep 17 00:00:00 2001
From: Dmitry Polukhin <dmitry.poluk...@gmail.com>
Date: Wed, 21 May 2025 07:54:02 -0700
Subject: [PATCH 1/3] [clang-tidy][performance-unnecessary-value-param] Avoid
 in coroutines

Summary:
Replacing by-value parameters with passing by-reference is not safe for 
coroutines
because the caller may be executed in parallel with the callee, which increases
the chances of resulting in dangling references and hard-to-find crashes.

Test Plan: check-clang-tools
---
 .../UnnecessaryValueParamCheck.cpp            |  1 +
 .../unnecessary-value-param-coroutine.cpp     | 81 +++++++++++++++++++
 2 files changed, 82 insertions(+)
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-value-param-coroutine.cpp

diff --git 
a/clang-tools-extra/clang-tidy/performance/UnnecessaryValueParamCheck.cpp 
b/clang-tools-extra/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
index a877f9a7ee912..52d4c70a83f65 100644
--- a/clang-tools-extra/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
+++ b/clang-tools-extra/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
@@ -65,6 +65,7 @@ void UnnecessaryValueParamCheck::registerMatchers(MatchFinder 
*Finder) {
           TK_AsIs,
           functionDecl(hasBody(stmt()), isDefinition(), unless(isImplicit()),
                        unless(cxxMethodDecl(anyOf(isOverride(), isFinal()))),
+                       unless(hasBody(coroutineBodyStmt())),
                        has(typeLoc(forEach(ExpensiveValueParamDecl))),
                        decl().bind("functionDecl"))),
       this);
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-value-param-coroutine.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-value-param-coroutine.cpp
new file mode 100644
index 0000000000000..f2fb477143578
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-value-param-coroutine.cpp
@@ -0,0 +1,81 @@
+// RUN: %check_clang_tidy -std=c++20-or-later %s 
performance-unnecessary-value-param %t -- -fix-errors
+
+namespace std {
+
+template <typename R, typename...> struct coroutine_traits {
+  using promise_type = typename R::promise_type;
+};
+
+template <typename Promise = void> struct coroutine_handle;
+
+template <> struct coroutine_handle<void> {
+  static coroutine_handle from_address(void *addr) noexcept {
+    coroutine_handle me;
+    me.ptr = addr;
+    return me;
+  }
+  void operator()() { resume(); }
+  void *address() const noexcept { return ptr; }
+  void resume() const {  }
+  void destroy() const { }
+  bool done() const { return true; }
+  coroutine_handle &operator=(decltype(nullptr)) {
+    ptr = nullptr;
+    return *this;
+  }
+  coroutine_handle(decltype(nullptr)) : ptr(nullptr) {}
+  coroutine_handle() : ptr(nullptr) {}
+  //  void reset() { ptr = nullptr; } // add to P0057?
+  explicit operator bool() const { return ptr; }
+
+protected:
+  void *ptr;
+};
+
+template <typename Promise> struct coroutine_handle : coroutine_handle<> {
+  using coroutine_handle<>::operator=;
+
+  static coroutine_handle from_address(void *addr) noexcept {
+    coroutine_handle me;
+    me.ptr = addr;
+    return me;
+  }
+
+  Promise &promise() const {
+    return *reinterpret_cast<Promise *>(
+        __builtin_coro_promise(ptr, alignof(Promise), false));
+  }
+  static coroutine_handle from_promise(Promise &promise) {
+    coroutine_handle p;
+    p.ptr = __builtin_coro_promise(&promise, alignof(Promise), true);
+    return p;
+  }
+};
+
+struct suspend_always {
+  bool await_ready() noexcept { return false; }
+  void await_suspend(std::coroutine_handle<>) noexcept {}
+  void await_resume() noexcept {}
+};
+
+} // namespace std
+
+struct ReturnObject {
+    struct promise_type {
+        ReturnObject get_return_object() { return {}; }
+        std::suspend_always initial_suspend() { return {}; }
+        std::suspend_always final_suspend() noexcept { return {}; }
+        void unhandled_exception() {}
+        std::suspend_always yield_value(int value) { return {}; }
+    };
+};
+
+struct A {
+  A(const A&);
+};
+
+ReturnObject evaluateModels(const A timeMachineId) {
+// No change for non-coroutine function expected because it is not safe.
+// CHECK-FIXES: ReturnObject evaluateModels(const A timeMachineId) {
+  co_return;
+}

>From 470bf837a1a958c75f4661c0c13dc7e48513d12d Mon Sep 17 00:00:00 2001
From: Dmitry Polukhin <dmitry.poluk...@gmail.com>
Date: Wed, 21 May 2025 09:19:56 -0700
Subject: [PATCH 2/3] Add documentation and simplify test

---
 clang-tools-extra/docs/ReleaseNotes.rst       |  4 +-
 .../performance/unnecessary-value-param.rst   |  4 ++
 .../unnecessary-value-param-coroutine.cpp     | 63 ++++++-------------
 3 files changed, 25 insertions(+), 46 deletions(-)

diff --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index 761c1d3a80359..9522b5ac8d6ca 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -183,7 +183,7 @@ Changes in existing checks
   explicit casting of built-in types within member list initialization.
 
 - Improved :doc:`modernize-use-ranges
-  <clang-tidy/checks/modernize/use-ranges>` check by updating suppress 
+  <clang-tidy/checks/modernize/use-ranges>` check by updating suppress
   warnings logic for ``nullptr`` in ``std::find``.
 
 - Improved :doc:`modernize-use-starts-ends-with
@@ -203,6 +203,8 @@ Changes in existing checks
   <clang-tidy/checks/performance/unnecessary-value-param>` check performance by
   tolerating fix-it breaking compilation when functions is used as pointers
   to avoid matching usage of functions within the current compilation unit.
+  Also suppressed this check for coroutine functions because it may not be safe
+  and suggested fixes may result in hard-to-find bugs and crashes.
 
 Removed checks
 ^^^^^^^^^^^^^^
diff --git 
a/clang-tools-extra/docs/clang-tidy/checks/performance/unnecessary-value-param.rst
 
b/clang-tools-extra/docs/clang-tidy/checks/performance/unnecessary-value-param.rst
index dc86530b95f13..665e0a6360723 100644
--- 
a/clang-tools-extra/docs/clang-tidy/checks/performance/unnecessary-value-param.rst
+++ 
b/clang-tools-extra/docs/clang-tidy/checks/performance/unnecessary-value-param.rst
@@ -18,6 +18,10 @@ the following heuristic is employed:
    on it, or it is used as const reference or value argument in constructors or
    function calls.
 
+Note: This check does not suggest passing parameters by reference in coroutines
+because, after a coroutine suspend point, references could be dangling and no
+longer valid, so suggested changes may result in hard-to-find bugs and crashes.
+
 Example:
 
 .. code-block:: c++
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-value-param-coroutine.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-value-param-coroutine.cpp
index f2fb477143578..b6e9bd0a60d44 100644
--- 
a/clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-value-param-coroutine.cpp
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/performance/unnecessary-value-param-coroutine.cpp
@@ -2,59 +2,32 @@
 
 namespace std {
 
-template <typename R, typename...> struct coroutine_traits {
-  using promise_type = typename R::promise_type;
+template <class Ret, typename... T> struct coroutine_traits {
+  using promise_type = typename Ret::promise_type;
 };
 
-template <typename Promise = void> struct coroutine_handle;
-
-template <> struct coroutine_handle<void> {
-  static coroutine_handle from_address(void *addr) noexcept {
-    coroutine_handle me;
-    me.ptr = addr;
-    return me;
-  }
-  void operator()() { resume(); }
-  void *address() const noexcept { return ptr; }
-  void resume() const {  }
-  void destroy() const { }
-  bool done() const { return true; }
-  coroutine_handle &operator=(decltype(nullptr)) {
-    ptr = nullptr;
-    return *this;
-  }
-  coroutine_handle(decltype(nullptr)) : ptr(nullptr) {}
-  coroutine_handle() : ptr(nullptr) {}
-  //  void reset() { ptr = nullptr; } // add to P0057?
-  explicit operator bool() const { return ptr; }
-
-protected:
-  void *ptr;
+template <class Promise = void> struct coroutine_handle {
+  static coroutine_handle from_address(void *) noexcept;
+  static coroutine_handle from_promise(Promise &promise);
+  constexpr void *address() const noexcept;
 };
 
-template <typename Promise> struct coroutine_handle : coroutine_handle<> {
-  using coroutine_handle<>::operator=;
-
-  static coroutine_handle from_address(void *addr) noexcept {
-    coroutine_handle me;
-    me.ptr = addr;
-    return me;
-  }
-
-  Promise &promise() const {
-    return *reinterpret_cast<Promise *>(
-        __builtin_coro_promise(ptr, alignof(Promise), false));
-  }
-  static coroutine_handle from_promise(Promise &promise) {
-    coroutine_handle p;
-    p.ptr = __builtin_coro_promise(&promise, alignof(Promise), true);
-    return p;
-  }
+template <> struct coroutine_handle<void> {
+  template <class PromiseType>
+  coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+  static coroutine_handle from_address(void *);
+  constexpr void *address() const noexcept;
 };
 
 struct suspend_always {
   bool await_ready() noexcept { return false; }
-  void await_suspend(std::coroutine_handle<>) noexcept {}
+  void await_suspend(coroutine_handle<>) noexcept {}
+  void await_resume() noexcept {}
+};
+
+struct suspend_never {
+  bool await_ready() noexcept { return true; }
+  void await_suspend(coroutine_handle<>) noexcept {}
   void await_resume() noexcept {}
 };
 

>From e8635e02964aa8a38c11b9741dc76f89c2093159 Mon Sep 17 00:00:00 2001
From: Dmitry Polukhin <dmitry.poluk...@gmail.com>
Date: Wed, 21 May 2025 09:54:34 -0700
Subject: [PATCH 3/3] Move note to more appropiate place

---
 .../checks/performance/unnecessary-value-param.rst     | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git 
a/clang-tools-extra/docs/clang-tidy/checks/performance/unnecessary-value-param.rst
 
b/clang-tools-extra/docs/clang-tidy/checks/performance/unnecessary-value-param.rst
index 665e0a6360723..fce3bec00504e 100644
--- 
a/clang-tools-extra/docs/clang-tidy/checks/performance/unnecessary-value-param.rst
+++ 
b/clang-tools-extra/docs/clang-tidy/checks/performance/unnecessary-value-param.rst
@@ -18,10 +18,6 @@ the following heuristic is employed:
    on it, or it is used as const reference or value argument in constructors or
    function calls.
 
-Note: This check does not suggest passing parameters by reference in coroutines
-because, after a coroutine suspend point, references could be dangling and no
-longer valid, so suggested changes may result in hard-to-find bugs and crashes.
-
 Example:
 
 .. code-block:: c++
@@ -60,7 +56,11 @@ Will become:
 
 Because the fix-it needs to change the signature of the function, it may break
 builds if the function is used in multiple translation units or some codes
-depends on funcion signatures.
+depends on function signatures.
+
+Note: This check does not suggest passing parameters by reference in coroutines
+because, after a coroutine suspend point, references could be dangling and no
+longer valid, so suggested changes may result in hard-to-find bugs and crashes.
 
 Options
 -------

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to