[clang] [Clang] Don't skip capture of parameters in default arguments (PR #235529)

via cfe-commits cfe-commits at lists.llvm.org
Sun Oct 25 14:59:34 PDT 2026


https://github.com/Praneeth740 updated https://github.com/llvm/llvm-project/pull/235529

>From 1d95da5f3a00e04f9d87ec79d9827e9212424b15 Mon Sep 17 00:00:00 2001
From: Praneeth <cs24btech11013 at iith.ac.in>
Date: Mon, 26 Oct 2026 02:50:30 +0530
Subject: [PATCH] [clang] Don't skip capture of parameters in default arguments

The early return in tryCaptureVariable was meant for unevaluated
operands but never checked for them, so lambdas in default arguments
skipped capture analysis. Restrict it to unevaluated contexts.

Fixes #233811
---
 clang/lib/Sema/SemaExpr.cpp                    | 12 ++++++++++--
 .../SemaCXX/lambda-as-default-parameter.cpp    |  7 ++++++-
 .../lambda-default-arg-param-capture.cpp       | 18 ++++++++++++++++++
 3 files changed, 34 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/SemaCXX/lambda-default-arg-param-capture.cpp

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 43b0c86132172b..e0f87936d451cd 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -20238,8 +20238,16 @@ bool Sema::tryCaptureVariable(
   // Exception: Function parameters are not tied to the function's DeclContext
   // until we enter the function definition. Capturing them anyway would result
   // in an out-of-bounds error while traversing DC and its parents.
-  if (isa<ParmVarDecl>(Var) && !VarDC->isFunctionOrMethod())
-    return true;
+
+  bool InDefaultArg = llvm::any_of(
+    ExprEvalContexts, [](const ExpressionEvaluationContextRecord &C) {
+      return isa_and_nonnull<ParmVarDecl>(C.ManglingContextDecl);
+    });
+  if (isa<ParmVarDecl>(Var) && !VarDC->isFunctionOrMethod() && !InDefaultArg &&
+    llvm::any_of(ExprEvalContexts,
+                 [](const ExpressionEvaluationContextRecord &C) {
+                   return C.isUnevaluated();
+                 })) return true;
 
   const auto *VD = dyn_cast<VarDecl>(Var);
   if (VD) {
diff --git a/clang/test/SemaCXX/lambda-as-default-parameter.cpp b/clang/test/SemaCXX/lambda-as-default-parameter.cpp
index 1f07a7f5644b7c..1c8ceaa806c60f 100644
--- a/clang/test/SemaCXX/lambda-as-default-parameter.cpp
+++ b/clang/test/SemaCXX/lambda-as-default-parameter.cpp
@@ -3,4 +3,9 @@
 struct a; // expected-note {{forward declaration of 'a'}} \
              expected-note {{forward declaration of 'a'}}
 void b(a c = [] { return c; }); // expected-error {{initialization of incomplete type 'a'}} \
-                                   expected-error {{variable has incomplete type 'a'}}
+                                   expected-error {{variable has incomplete type 'a'}} \
+                                   expected-error {{variable 'c' cannot be implicitly captured in a lambda with no capture-default specified}} \
+                                   expected-note {{'c' declared here}} \
+                                   expected-note {{lambda expression begins here}} \
+                                   expected-note {{capture 'c' by reference}} \
+                                   expected-note {{default capture by reference}}
diff --git a/clang/test/SemaCXX/lambda-default-arg-param-capture.cpp b/clang/test/SemaCXX/lambda-default-arg-param-capture.cpp
new file mode 100644
index 00000000000000..914c0f3754b5e6
--- /dev/null
+++ b/clang/test/SemaCXX/lambda-default-arg-param-capture.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+
+int a(int g, int h = []{ return g; }()); // expected-error {{variable 'g' cannot be implicitly captured in a lambda with no capture-default specified}} expected-note {{'g' declared here}} expected-note {{lambda expression begins here}} expected-note {{capture 'g' by value}} expected-note {{capture 'g' by reference}} expected-note {{default capture by value}} expected-note {{default capture by reference}}
+
+int e(int g, int h = [] { int g = 1; return g; }());
+
+// Unevaluated operand in a trailing return type: still accepted.
+auto u(int g) -> decltype([] { return g; }());
+
+// A default argument is potentially evaluated even when the lambda sits in an
+// unevaluated operand; GCC rejects these too.
+int s1(int x, int h = sizeof([] { return x; }())); // expected-error {{variable 'x' cannot be implicitly captured in a lambda with no capture-default specified}} \
+                                                      expected-note {{'x' declared here}} \
+                                                      expected-note {{lambda expression begins here}} \
+                                                      expected-note {{capture 'x' by value}} \
+                                                      expected-note {{capture 'x' by reference}} \
+                                                      expected-note {{default capture by value}} \
+                                                      expected-note {{default capture by reference}}



More information about the cfe-commits mailing list