[clang] [Clang] fix crash with generic lambdas in function default args (PR #222987)
Oleksandr Tarasiuk via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 14 04:09:55 PDT 2026
https://github.com/a-tarasyuk updated https://github.com/llvm/llvm-project/pull/222987
>From af5b6ee39d4dfaed820c17f4303316e91302aa74 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Fri, 11 Sep 2026 15:45:35 +0300
Subject: [PATCH 1/3] [Clang] fix crash with generic lambdas in function
default args
---
clang/docs/ReleaseNotes.md | 2 +
clang/lib/Parse/ParseDecl.cpp | 6 +++
clang/lib/Sema/SemaDeclCXX.cpp | 9 ++++-
clang/lib/Sema/SemaLambda.cpp | 2 +-
clang/lib/Sema/SemaTemplate.cpp | 5 +--
clang/test/SemaCXX/lambda-unevaluated.cpp | 47 +++++++++++++++++++++++
6 files changed, 65 insertions(+), 6 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index ef694e1d0f5cc..1d799c0ed0ba2 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -675,6 +675,8 @@ features cannot lower the translation-unit ABI level;
class with an invalid non-static data member, such as one qualified with an
address space. (#GH194605)
+- Fixed a crash with generic lambdas in default arguments of functions with `auto` parameters.
+
#### Bug Fixes to AST Handling
- Fixed a non-deterministic ordering of unused local typedefs that made
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 1d3789a10d9de..bf42a04adf965 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -7756,6 +7756,12 @@ void Parser::ParseParameterDeclarationClause(
DelayTemplateIdDestructionRAII DontDestructTemplateIds(
*this, /*DelayTemplateIdDestruction=*/true);
+ TemplateParameterDepthRAII CurTemplateDepthTracker(
+ TemplateParameterDepth);
+ unsigned Depth = Actions.getTemplateDepth(getCurScope());
+ if (Depth > TemplateParameterDepth)
+ CurTemplateDepthTracker.addDepth(Depth - TemplateParameterDepth);
+
// The argument isn't actually potentially evaluated unless it is
// used.
EnterExpressionEvaluationContext Eval(
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index ea628f29d8a00..568bb4c785140 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -165,12 +165,19 @@ bool CheckDefaultArgumentVisitor::VisitPseudoObjectExpr(
}
bool CheckDefaultArgumentVisitor::VisitLambdaExpr(const LambdaExpr *Lambda) {
+ bool Invalid = false;
+ for (NamedDecl *P : Lambda->getExplicitTemplateParameters()) {
+ const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P);
+ if (!NTTP || !NTTP->hasDefaultArgument())
+ continue;
+ Invalid |= Visit(NTTP->getDefaultArgument().getArgument().getAsExpr());
+ }
+
// [expr.prim.lambda.capture]p9
// a lambda-expression appearing in a default argument cannot implicitly or
// explicitly capture any local entity. Such a lambda-expression can still
// have an init-capture if any full-expression in its initializer satisfies
// the constraints of an expression appearing in a default argument.
- bool Invalid = false;
for (const LambdaCapture &LC : Lambda->captures()) {
if (!Lambda->isInitCapture(&LC))
return S.Diag(LC.getLocation(), diag::err_lambda_capture_default_arg);
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index 288f3c4f664cb..ffbe5e72d4c51 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -1143,7 +1143,7 @@ void Sema::ActOnLambdaExpressionAfterIntroducer(LambdaIntroducer &Intro,
// be dependent, because there are template parameters in scope.
CXXRecordDecl::LambdaDependencyKind LambdaDependencyKind =
CXXRecordDecl::LDK_Unknown;
- if (CurScope->getTemplateParamParent() != nullptr) {
+ if (getTemplateDepth(CurScope) > 0) {
LambdaDependencyKind = CXXRecordDecl::LDK_AlwaysDependent;
} else if (Scope *ParentScope = CurScope->getParent()) {
// Given a lambda defined inside a requires expression,
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index b8b0c71894daa..b800a433b3bf7 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -76,11 +76,8 @@ unsigned Sema::getTemplateDepth(Scope *S) const {
if (auto *LSI = dyn_cast<LambdaScopeInfo>(FSI)) {
if (!LSI->TemplateParams.empty()) {
ParamsAtDepth(LSI->AutoTemplateParameterDepth);
- break;
- }
- if (LSI->GLTemplateParameterList) {
+ } else if (LSI->GLTemplateParameterList) {
ParamsAtDepth(LSI->GLTemplateParameterList->getDepth());
- break;
}
}
}
diff --git a/clang/test/SemaCXX/lambda-unevaluated.cpp b/clang/test/SemaCXX/lambda-unevaluated.cpp
index 9c723c6bc3b99..018d597e6831b 100644
--- a/clang/test/SemaCXX/lambda-unevaluated.cpp
+++ b/clang/test/SemaCXX/lambda-unevaluated.cpp
@@ -1,5 +1,6 @@
// RUN: %clang_cc1 -std=c++20 %s -Wno-c++23-extensions -verify
// RUN: %clang_cc1 -std=c++23 %s -verify
+// RUN: %clang_cc1 -std=c++26 %s -verify
template <auto> struct Nothing {};
Nothing<[]() { return 0; }()> nothing;
@@ -283,10 +284,21 @@ static_assert(__is_same_as(int, helper<int>));
} // namespace GH138018
namespace GH172814 {
+auto f() {
+ int x = 0;
+ return [](auto w = [&] { x += w(); }); // expected-error {{lambda expression in default argument cannot capture any entity}} \
+ // expected-error {{expected body of lambda expression}}
+}
+
auto t() {
int x = 0;
return [](auto w = [&] { return x; }) { }; // expected-error {{lambda expression in default argument cannot capture any entity}}
};
+
+auto g() {
+ int x = 0;
+ return []<class T>(T w = [&] { return x; }) {}; // expected-error {{lambda expression in default argument cannot capture any entity}}
+}
}
namespace GH176534 {
@@ -318,3 +330,38 @@ struct S {
void c(int x, int = sizeof([=] { return x; }));
};
}
+
+namespace GH48768 {
+
+auto a(auto x = 1, auto = []<auto = x> {}()); // expected-error {{default argument references parameter 'x'}}
+void b(auto x, auto = []<auto = x> {}); // expected-error {{default argument references parameter 'x'}}
+auto c = [](auto x, int = []<auto = x> { return 0; }()) {}; // expected-error {{default argument references parameter 'x'}}
+
+constexpr int d(auto x, int n = []<auto N = sizeof(x)> { return N; }()) {
+ return n;
+}
+
+constexpr int e(auto x, int n = []<class T = decltype(x)> { return sizeof(T); }()) {
+ return n;
+}
+
+constexpr auto f = [](auto x, int n = []<auto N = sizeof(x)> { return N; }()) {
+ return n;
+};
+
+constexpr auto g = [](auto x, int n = []<class T = decltype(x)> { return sizeof(T); }()) {
+ return n;
+};
+
+constexpr auto h = [](auto x) {
+ return [](auto y, int n = []<auto N = sizeof(y)> { return N; }()) {
+ return n;
+ };
+};
+
+static_assert(d(0) == sizeof(int));
+static_assert(e(0) == sizeof(int));
+static_assert(f(0) == sizeof(int));
+static_assert(g(0) == sizeof(int));
+static_assert(h(0)('a') == 1);
+}
>From 123d67a77650fed1ad6b7ad8dc037e3f6e02e423 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Mon, 14 Sep 2026 14:05:25 +0300
Subject: [PATCH 2/3] update release notes
---
clang/docs/ReleaseNotes.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 1d799c0ed0ba2..f497d38ca1dd2 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -675,7 +675,7 @@ features cannot lower the translation-unit ABI level;
class with an invalid non-static data member, such as one qualified with an
address space. (#GH194605)
-- Fixed a crash with generic lambdas in default arguments of functions with `auto` parameters.
+- Fixed a crash with generic lambdas in default arguments of functions with `auto` parameters. (#GH48768)
#### Bug Fixes to AST Handling
>From 7f9612bfd2d1b1699d547deeecb6bc2422b96837 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Mon, 14 Sep 2026 14:08:51 +0300
Subject: [PATCH 3/3] explain default argument tpl depth
---
clang/lib/Parse/ParseDecl.cpp | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index bf42a04adf965..9834293a0d09b 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -7756,6 +7756,10 @@ void Parser::ParseParameterDeclarationClause(
DelayTemplateIdDestructionRAII DontDestructTemplateIds(
*this, /*DelayTemplateIdDestruction=*/true);
+ // Include the template level introduced by 'auto' parameters:
+ // void f(auto x, int = []<auto N = sizeof(x)>() { return N; }());
+ // The type parameter for x has depth 0, so N starts at depth 1.
+ // Otherwise, instantiation would reduce N's depth to -1.
TemplateParameterDepthRAII CurTemplateDepthTracker(
TemplateParameterDepth);
unsigned Depth = Actions.getTemplateDepth(getCurScope());
More information about the cfe-commits
mailing list