https://github.com/akashagrwl created 
https://github.com/llvm/llvm-project/pull/210718

`Sema::ActOnParamDefaultArgument` checked a default-argument expression for 
unexpanded parameter packs before checking whether the parameter itself is a 
pack. For a lambda parameter pack given a default argument that is a pack 
expansion referencing an enclosing function's parameter pack (e.g. `[](Types... 
= args...) {}`), the first check runs while still inside the lambda's scope and 
sets `LambdaScopeInfo::ContainsUnexpandedParameterPack` (a mechanism meant for 
legitimate outer-pack references). The subsequent `isParameterPack()` check 
then correctly diagnoses the real error and discards the default argument, but 
the stale flag survives into the built `LambdaExpr`'s dependence bits. A later 
unexpanded-pack check on that `LambdaExpr` finds nothing to report and hits
`assert(!Unexpanded.empty() || LambdaReferencingOuterPacks)`, aborting instead 
of just diagnosing the error.

Fix: check `Param->isParameterPack()` first and return immediately after 
discarding the default argument, before ever calling 
`DiagnoseUnexpandedParameterPack` on an expression that is about to be thrown 
away. [dcl.fct.default]p3 forbids a default argument on a pack parameter 
unconditionally, so this ordering is also semantically correct, not just a 
crash workaround.

Fixes #210714

>From c8616664214149f2a9fdb636499174310430735d Mon Sep 17 00:00:00 2001
From: Akash Agrawal <[email protected]>
Date: Mon, 20 Jul 2026 06:28:23 -0700
Subject: [PATCH] [Clang][Sema] Fix crash on lambda parameter pack with illegal
 default argument

`Sema::ActOnParamDefaultArgument` checked a default-argument expression for
unexpanded parameter packs before checking whether the parameter itself is a
pack. For a lambda parameter pack given a default argument that is a pack
expansion referencing an enclosing function's parameter pack (e.g.
`[](Types... = args...) {}`), the first check runs while still inside the
lambda's scope and sets `LambdaScopeInfo::ContainsUnexpandedParameterPack`
(a mechanism meant for legitimate outer-pack references). The subsequent
`isParameterPack()` check then correctly diagnoses the real error and discards
the default argument, but the stale flag survives into the built `LambdaExpr`'s
dependence bits. A later unexpanded-pack check on that `LambdaExpr` finds
nothing to report and hits
`assert(!Unexpanded.empty() || LambdaReferencingOuterPacks)`, aborting instead
of just diagnosing the error.

Fix: check `Param->isParameterPack()` first and return immediately after
discarding the default argument, before ever calling
`DiagnoseUnexpandedParameterPack` on an expression that is about to be thrown
away. [dcl.fct.default]p3 forbids a default argument on a pack parameter
unconditionally, so this ordering is also semantically correct, not just a
crash workaround.

Fixes #210714
---
 clang/docs/ReleaseNotes.md                         |  5 +++++
 clang/lib/Sema/SemaDeclCXX.cpp                     | 14 ++++++++++----
 .../dcl.decl/dcl.meaning/dcl.fct.default/p3.cpp    | 10 ++++++++++
 3 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 9301745b9628e..9737e9281e579 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -318,6 +318,11 @@ latest release, please see the [Clang Web 
Site](https://clang.llvm.org) or the
 - Fixed a crash when a using-declaration naming an unresolvable member of a
   dependent base was shadowed by an invalid using-declaration. (#GH209427)
 
+- Fixed a crash when a lambda parameter pack was given a default argument that
+  is a pack expansion referencing an enclosing function's parameter pack (e.g.
+  `[](Types... = args...) {}`). Clang now diagnoses the illegal default
+  argument instead of asserting. (#GH210714)
+
 #### Bug Fixes to AST Handling
 
 - Fixed a non-deterministic ordering of unused local typedefs that made
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 37bb69da90b6c..e5a5963db27c2 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -359,13 +359,15 @@ Sema::ActOnParamDefaultArgument(Decl *param, 
SourceLocation EqualLoc,
     return ActOnParamDefaultArgumentError(param, EqualLoc, DefaultArg);
   }
 
-  // Check for unexpanded parameter packs.
-  if (DiagnoseUnexpandedParameterPack(DefaultArg, UPPC_DefaultArgument))
-    return ActOnParamDefaultArgumentError(param, EqualLoc, DefaultArg);
-
   // C++11 [dcl.fct.default]p3
   //   A default argument expression [...] shall not be specified for a
   //   parameter pack.
+  //
+  // Check this before looking for unexpanded parameter packs in DefaultArg:
+  // if DefaultArg references a pack from an enclosing lambda/block, that
+  // check would (incorrectly) mark the lambda as containing an unexpanded
+  // pack that never actually appears in the final AST once we discard
+  // DefaultArg below.
   if (Param->isParameterPack()) {
     Diag(EqualLoc, diag::err_param_default_argument_on_parameter_pack)
         << DefaultArg->getSourceRange();
@@ -374,6 +376,10 @@ Sema::ActOnParamDefaultArgument(Decl *param, 
SourceLocation EqualLoc,
     return;
   }
 
+  // Check for unexpanded parameter packs.
+  if (DiagnoseUnexpandedParameterPack(DefaultArg, UPPC_DefaultArgument))
+    return ActOnParamDefaultArgumentError(param, EqualLoc, DefaultArg);
+
   ExprResult Result = ConvertParamDefaultArgument(Param, DefaultArg, EqualLoc);
   if (Result.isInvalid())
     return ActOnParamDefaultArgumentError(param, EqualLoc, DefaultArg);
diff --git a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p3.cpp 
b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p3.cpp
index 11c17f32728a1..eaee2393ed12c 100644
--- a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p3.cpp
+++ b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct.default/p3.cpp
@@ -17,3 +17,13 @@ struct X0 {
 
 template <typename... Ts>
 void defaultpack(Ts... = 0) {} // expected-error{{parameter pack cannot have a 
default argument}}
+
+// A lambda parameter pack whose default argument is a pack expansion
+// referencing the enclosing function's parameter pack must be diagnosed
+// without crashing.
+template <class... Types> void lambda_pack_default_arg(Types... args) {
+  auto lm = [](Types... = args...) {}; // expected-error{{parameter pack 
cannot have a default argument}} \
+                                       // expected-warning{{'...' in this 
location creates a C-style varargs function}} \
+                                       // expected-note{{preceding '...' 
declares a function parameter pack}} \
+                                       // expected-note{{insert ',' before 
'...' to silence this warning}}
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to