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