[llvm-branch-commits] [clang] [Clang] Fix -Wunused-parameter for implicit coroutine uses (backport #217518) (PR #218584)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Aug 24 22:16:25 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-coroutines

Author: Lane0218

<details>
<summary>Changes</summary>

Backport of 01fec9890ad8a4980beb8ec1945320e126ddbdcd from `main`.

This fixes `-Wunused-parameter` handling for implicit coroutine parameter uses during allocation-function lookup and promise initialization.

The backport required a manual resolution in `clang/docs/ReleaseNotes.md`.

Validation:
- `clang/test/SemaCXX/warn-unused-parameters-coroutine.cpp`: 1/1 passed
- `clang/test/SemaCXX/coroutine*.cpp`: 23/23 passed

/cherry-pick-of 217518

---
Full diff: https://github.com/llvm/llvm-project/pull/218584.diff


3 Files Affected:

- (modified) clang/docs/ReleaseNotes.md (+5) 
- (modified) clang/lib/Sema/SemaCoroutine.cpp (+30-3) 
- (modified) clang/test/SemaCXX/warn-unused-parameters-coroutine.cpp (+44) 


``````````diff
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 59576a299c593..695beb91b3929 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -590,6 +590,11 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
 
 ### Improvements to Clang's diagnostics
 
+- Fixed `-Wunused-parameter` to diagnose coroutine parameters that are only
+  considered during allocation function lookup or promise object
+  initialization, while not diagnosing parameters passed to the selected
+  allocation function or promise constructor. (#GH217501)
+
 - Fixed bug in `-Wdocumentation` so that it correctly handles explicit
   function template instantiations (#64087).
 
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index 7f9b1d642cf9d..4b99e4454e57a 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -462,6 +462,12 @@ static ExprResult buildPromiseCall(Sema &S, VarDecl *Promise,
   return buildMemberCall(S, PromiseRef.get(), Loc, Name, Args);
 }
 
+static void markCoroutineParametersReferenced(FunctionDecl &FD) {
+  for (auto *PD : FD.parameters())
+    if (!PD->getType()->isDependentType())
+      PD->setReferenced();
+}
+
 VarDecl *Sema::buildCoroutinePromise(SourceLocation Loc) {
   assert(isa<FunctionDecl>(CurContext) && "not in a function scope");
   auto *FD = cast<FunctionDecl>(CurContext);
@@ -555,6 +561,10 @@ VarDecl *Sema::buildCoroutinePromise(SourceLocation Loc) {
         VD->setInit(MaybeCreateExprWithCleanups(Result.get()));
         VD->setInitStyle(VarDecl::CallInit);
         CheckCompleteVariableDeclaration(VD);
+        // The constructor is selected with the coroutine parameter copies as
+        // arguments. Mark the original parameters as referenced for
+        // -Wunused-parameter.
+        markCoroutineParametersReferenced(*FD);
       }
     } else
       ActOnUninitializedDecl(VD);
@@ -1385,9 +1395,14 @@ static bool collectPlacementArgs(Sema &S, FunctionDecl &FD, SourceLocation Loc,
 
     // Build a reference to the parameter.
     auto PDLoc = PD->getLocation();
+    // Preserve the referenced state for unused parameter diagnostics.
+    bool DeclReferenced = PD->isReferenced();
     ExprResult PDRefExpr =
         S.BuildDeclRefExpr(PD, PD->getOriginalType().getNonReferenceType(),
                            ExprValueKind::VK_LValue, PDLoc);
+
+    PD->setReferenced(DeclReferenced);
+
     if (PDRefExpr.isInvalid())
       return false;
 
@@ -1443,6 +1458,8 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
 
   FunctionDecl *OperatorNew = nullptr;
   SmallVector<Expr *, 1> PlacementArgs;
+  // Track whether PlacementArgs still refer to the coroutine parameters.
+  bool PlacementArgsFromCoroutine = false;
   DeclarationName NewName =
       S.getASTContext().DeclarationNames.getCXXOperatorName(OO_New);
 
@@ -1491,8 +1508,11 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
   // We don't expect to call to global operator new with (size, p0, …, pn).
   // So if we choose to lookup the allocation function in global scope, we
   // shouldn't lookup placement arguments.
-  if (PromiseContainsNew && !collectPlacementArgs(S, FD, Loc, PlacementArgs))
-    return false;
+  if (PromiseContainsNew) {
+    if (!collectPlacementArgs(S, FD, Loc, PlacementArgs))
+      return false;
+    PlacementArgsFromCoroutine = true;
+  }
 
   LookupAllocationFunction();
 
@@ -1558,6 +1578,7 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
     if (!StdNoThrow)
       return false;
     PlacementArgs = {StdNoThrow};
+    PlacementArgsFromCoroutine = false;
     OperatorNew = nullptr;
     LookupAllocationFunction(AllocationFunctionScope::Global);
   }
@@ -1644,8 +1665,14 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
       isAlignedAllocation(IAP.PassAlignment))
     NewArgs.push_back(FrameAlignment);
 
-  if (OperatorNew->getNumParams() > NewArgs.size())
+  // getNumParams() does not include an ellipsis, but a variadic allocation
+  // function still receives the coroutine parameters as placement arguments.
+  if (OperatorNew->isVariadic() ||
+      OperatorNew->getNumParams() > NewArgs.size()) {
     llvm::append_range(NewArgs, PlacementArgs);
+    if (PlacementArgsFromCoroutine)
+      markCoroutineParametersReferenced(FD);
+  }
 
   ExprResult NewExpr =
       S.BuildCallExpr(S.getCurScope(), NewRef.get(), Loc, NewArgs, Loc);
diff --git a/clang/test/SemaCXX/warn-unused-parameters-coroutine.cpp b/clang/test/SemaCXX/warn-unused-parameters-coroutine.cpp
index fee379d869112..ef0c767dbc802 100644
--- a/clang/test/SemaCXX/warn-unused-parameters-coroutine.cpp
+++ b/clang/test/SemaCXX/warn-unused-parameters-coroutine.cpp
@@ -8,8 +8,12 @@ struct awaitable {
   void await_suspend(std::coroutine_handle<>) noexcept;
 };
 
+struct promise_arg {};
+
 struct task : awaitable {
   struct promise_type {
+    promise_type();
+    promise_type(promise_arg);
     task get_return_object() noexcept;
     awaitable initial_suspend() noexcept;
     awaitable final_suspend() noexcept;
@@ -18,10 +22,50 @@ struct task : awaitable {
   };
 };
 
+struct allocation_arg {};
+
+struct task_with_new {
+  struct promise_type {
+    void *operator new(decltype(sizeof(0)));
+    void *operator new(decltype(sizeof(0)), allocation_arg);
+    task_with_new get_return_object();
+    awaitable initial_suspend();
+    awaitable final_suspend() noexcept;
+    void unhandled_exception();
+    void return_void();
+  };
+};
+
+struct task_with_variadic_new {
+  struct promise_type {
+    void *operator new(decltype(sizeof(0)), ...);
+    task_with_variadic_new get_return_object();
+    awaitable initial_suspend();
+    awaitable final_suspend() noexcept;
+    void unhandled_exception();
+    void return_void();
+  };
+};
+
 task foo(int a) { // expected-warning{{unused parameter 'a'}}
   co_return;
 }
 
+task promise_constructor_uses_parameter(promise_arg a) { co_return; }
+
+task_with_new class_specific_new_fallback(
+    int a) { // expected-warning{{unused parameter 'a'}}
+  co_return;
+}
+
+task_with_new placement_allocation_uses_parameter(allocation_arg a) {
+  co_return;
+}
+
+task_with_variadic_new variadic_allocation_uses_parameter(int a) {
+  co_return;
+}
+
 task bar(int a, int b) { // expected-warning{{unused parameter 'b'}}
   a = a + 1;
   co_return;

``````````

</details>


https://github.com/llvm/llvm-project/pull/218584


More information about the llvm-branch-commits mailing list