[clang] [Clang] fix crash with generic lambdas in function default args (PR #222987)
Oleksandr Tarasiuk via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 23 12:50:13 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/4] [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 ef694e1d0f5cc7..1d799c0ed0ba27 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 1d3789a10d9deb..bf42a04adf9652 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 ea628f29d8a005..568bb4c7851401 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 288f3c4f664cb4..ffbe5e72d4c516 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 b8b0c71894daac..b800a433b3bf74 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 9c723c6bc3b998..018d597e6831ba 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/4] 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 1d799c0ed0ba27..f497d38ca1dd2a 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/4] 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 bf42a04adf9652..9834293a0d09b6 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());
>From 7f107d45fc18f27d68bf49b93de0caf1abfb93c0 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 23 Sep 2026 22:47:38 +0300
Subject: [PATCH 4/4] fix implicit tpl scope handling for auto parameters
---
clang/include/clang/Parse/Parser.h | 21 ++++----
clang/include/clang/Sema/Scope.h | 2 +
clang/include/clang/Sema/Sema.h | 19 +------
clang/lib/Parse/ParseCXXInlineMethods.cpp | 17 +-----
clang/lib/Parse/ParseDecl.cpp | 25 ++++-----
clang/lib/Parse/ParseDeclCXX.cpp | 7 +--
clang/lib/Parse/ParseExprCXX.cpp | 22 ++++----
clang/lib/Parse/ParseTemplate.cpp | 19 +++++++
clang/lib/Parse/Parser.cpp | 11 +---
clang/lib/Sema/Scope.cpp | 14 +++++
clang/lib/Sema/Sema.cpp | 11 ----
clang/lib/Sema/SemaDecl.cpp | 10 +---
clang/lib/Sema/SemaDeclCXX.cpp | 23 ++++++---
clang/lib/Sema/SemaLambda.cpp | 29 +----------
clang/lib/Sema/SemaTemplate.cpp | 28 +---------
clang/lib/Sema/SemaType.cpp | 3 ++
clang/test/SemaCXX/lambda-unevaluated.cpp | 63 ++++++++++++++++-------
17 files changed, 139 insertions(+), 185 deletions(-)
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 6913c42884a367..fbbbf2dfaec32a 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1339,10 +1339,6 @@ class Parser : public CodeCompletionHandler {
typedef SmallVector<LateParsedDeclaration *, 2>
LateParsedDeclarationsContainer;
- /// Utility to re-enter a possibly-templated scope while parsing its
- /// late-parsed components.
- struct ReenterTemplateScopeRAII;
-
/// Utility to re-enter a class scope while parsing its late-parsed
/// components.
struct ReenterClassScopeRAII;
@@ -7941,13 +7937,18 @@ class Parser : public CodeCompletionHandler {
Depth += D;
AddedLevels += D;
}
- void setAddedDepth(unsigned D) {
- Depth = Depth - AddedLevels + D;
- AddedLevels = D;
- }
-
unsigned getDepth() const { return Depth; }
- unsigned getOriginalDepth() const { return Depth - AddedLevels; }
+ };
+
+ /// Utility to re-enter a possibly-templated scope while parsing its
+ /// late-parsed components.
+ struct ReenterTemplateScopeRAII {
+ MultiParseScope Scopes;
+ TemplateParameterDepthRAII CurTemplateDepthTracker;
+
+ ReenterTemplateScopeRAII(Parser &P, Decl *MaybeTemplated,
+ bool Enter = true);
+ ReenterTemplateScopeRAII(Parser &P, const Declarator &D);
};
/// Gathers and cleans up TemplateIdAnnotations when parsing of a
diff --git a/clang/include/clang/Sema/Scope.h b/clang/include/clang/Sema/Scope.h
index 58ca2c0738f3c7..ff153a198bc9f4 100644
--- a/clang/include/clang/Sema/Scope.h
+++ b/clang/include/clang/Sema/Scope.h
@@ -270,6 +270,8 @@ class Scope {
void setFlags(unsigned F) { setFlags(getParent(), F); }
+ void EnterTemplateParameterScope();
+
/// Get the label that precedes this scope.
LabelDecl *getPrecedingLabel() const { return PrecedingLabel; }
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index fc8da0ed560054..493b8014a4c2e0 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1118,9 +1118,6 @@ class Sema final : public SemaBase {
sema::LambdaScopeInfo *
getCurLambda(bool IgnoreNonLambdaCapturingScope = false);
- /// Retrieve the current generic lambda info, if any.
- sema::LambdaScopeInfo *getCurGenericLambda();
-
/// Retrieve the current captured region, if any.
sema::CapturedRegionScopeInfo *getCurCapturedRegion();
@@ -3524,10 +3521,6 @@ class Sema final : public SemaBase {
public:
IdentifierResolver IdResolver;
- /// The index of the first InventedParameterInfo that refers to the current
- /// context.
- unsigned InventedParameterInfosStart = 0;
-
/// A RAII object to temporarily push a declaration context.
class ContextRAII {
private:
@@ -3536,22 +3529,19 @@ class Sema final : public SemaBase {
ProcessingContextState SavedContextState;
QualType SavedCXXThisTypeOverride;
unsigned SavedFunctionScopesStart;
- unsigned SavedInventedParameterInfosStart;
public:
ContextRAII(Sema &S, DeclContext *ContextToPush, bool NewThisContext = true)
: S(S), SavedContext(S.CurContext),
SavedContextState(S.DelayedDiagnostics.pushUndelayed()),
SavedCXXThisTypeOverride(S.CXXThisTypeOverride),
- SavedFunctionScopesStart(S.FunctionScopesStart),
- SavedInventedParameterInfosStart(S.InventedParameterInfosStart) {
+ SavedFunctionScopesStart(S.FunctionScopesStart) {
assert(ContextToPush && "pushing null context");
S.CurContext = ContextToPush;
if (NewThisContext)
S.CXXThisTypeOverride = QualType();
// Any saved FunctionScopes do not refer to this context.
S.FunctionScopesStart = S.FunctionScopes.size();
- S.InventedParameterInfosStart = S.InventedParameterInfos.size();
}
void pop() {
@@ -3561,7 +3551,6 @@ class Sema final : public SemaBase {
S.DelayedDiagnostics.popUndelayed(SavedContextState);
S.CXXThisTypeOverride = SavedCXXThisTypeOverride;
S.FunctionScopesStart = SavedFunctionScopesStart;
- S.InventedParameterInfosStart = SavedInventedParameterInfosStart;
SavedContext = nullptr;
}
@@ -11445,12 +11434,6 @@ class Sema final : public SemaBase {
FpPragmaStack.CurrentValue = FPO.getChangesFrom(FPOptions(LangOpts));
}
- ArrayRef<InventedTemplateParameterInfo> getInventedParameterInfos() const {
- return llvm::ArrayRef(InventedParameterInfos.begin() +
- InventedParameterInfosStart,
- InventedParameterInfos.end());
- }
-
ArrayRef<sema::FunctionScopeInfo *> getFunctionScopes() const {
return llvm::ArrayRef(FunctionScopes.begin() + FunctionScopesStart,
FunctionScopes.end());
diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index e472628dd36bc8..43e73249baa340 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -318,27 +318,14 @@ void Parser::LateParsedPragma::ParseLexedPragmas() {
Self->ParseLexedPragma(*this);
}
-struct Parser::ReenterTemplateScopeRAII {
- Parser &P;
- MultiParseScope Scopes;
- TemplateParameterDepthRAII CurTemplateDepthTracker;
-
- ReenterTemplateScopeRAII(Parser &P, Decl *MaybeTemplated, bool Enter = true)
- : P(P), Scopes(P), CurTemplateDepthTracker(P.TemplateParameterDepth) {
- if (Enter) {
- CurTemplateDepthTracker.addDepth(
- P.ReenterTemplateScopes(Scopes, MaybeTemplated));
- }
- }
-};
-
struct Parser::ReenterClassScopeRAII : ReenterTemplateScopeRAII {
+ Parser &P;
ParsingClass &Class;
ReenterClassScopeRAII(Parser &P, ParsingClass &Class)
: ReenterTemplateScopeRAII(P, Class.TagOrTemplate,
/*Enter=*/!Class.TopLevelClass),
- Class(Class) {
+ P(P), Class(Class) {
// If this is the top-level class, we're still within its scope.
if (Class.TopLevelClass)
return;
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 9834293a0d09b6..c1511a5f0cb93f 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2187,11 +2187,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
;
if (Tok.is(tok::kw_requires)) {
- TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
- // With abbreviated function templates - we need to explicitly add depth to
- // account for the implicit template parameter list induced by the template.
- if (!TemplateInfo.TemplateParams && D.getInventedTemplateParameterList())
- ++CurTemplateDepthTracker;
+ ReenterTemplateScopeRAII InTemplateScope(*this, D);
ParseTrailingRequiresClauseWithScope(D);
}
@@ -6943,6 +6939,10 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
while (true) {
if (Tok.is(tok::l_paren)) {
bool IsFunctionDeclaration = D.isFunctionDeclaratorAFunctionDeclaration();
+ ParseScope ImplicitTemplateScope(this, Scope::NoScope,
+ getLangOpts().CPlusPlus &&
+ IsFunctionDeclaration);
+
// Enter function-declaration scope, limiting any declarators to the
// function prototype scope, including parameter declarators.
ParseScope PrototypeScope(
@@ -7280,6 +7280,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
BalancedDelimiterTracker &Tracker,
bool IsAmbiguous,
bool RequiresArg) {
+ llvm::SaveAndRestore<unsigned> SavedTemplateDepth(TemplateParameterDepth);
assert(getCurScope()->isFunctionPrototypeScope() &&
"Should call from a Function scope");
// lparen is already consumed!
@@ -7723,8 +7724,12 @@ void Parser::ParseParameterDeclarationClause(
// Inform the actions module about the parameter declarator, so it gets
// added to the current scope.
+ Scope *TemplateScope = getCurScope()->getTemplateParamParent();
Decl *Param =
Actions.ActOnParamDeclarator(getCurScope(), ParmDeclarator, ThisLoc);
+
+ if (getCurScope()->getTemplateParamParent() != TemplateScope)
+ ++TemplateParameterDepth;
// Parse the default argument, if any. We parse the default
// arguments in all dialects; the semantic analysis in
// ActOnParamDefaultArgument will reject the default argument in
@@ -7756,16 +7761,6 @@ 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());
- 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/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index dbc1b85acd1433..2f26d2299a68fd 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -2600,12 +2600,7 @@ bool Parser::ParseCXXMemberDeclaratorBeforeInitializer(
if (BitfieldSize.isInvalid())
SkipUntil(tok::comma, StopAtSemi | StopBeforeMatch);
} else if (Tok.is(tok::kw_requires)) {
- TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
- // With abbreviated function templates - we need to explicitly add depth to
- // account for the implicit template parameter list induced by the template.
- if (DeclaratorInfo.getTemplateParameterLists().empty() &&
- DeclaratorInfo.getInventedTemplateParameterList())
- ++CurTemplateDepthTracker;
+ ReenterTemplateScopeRAII InTemplateScope(*this, DeclaratorInfo);
ParseTrailingRequiresClauseWithScope(DeclaratorInfo);
} else {
ParseOptionalCXX11VirtSpecifierSeq(
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 860c069e18fcaf..ce6633bbcfdb9d 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -1215,7 +1215,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
// Parse lambda-declarator[opt].
DeclSpec DS(AttrFactory);
Declarator D(DS, ParsedAttributesView::none(), DeclaratorContext::LambdaExpr);
- TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
+ llvm::SaveAndRestore<unsigned> SavedTemplateDepth(TemplateParameterDepth);
ParseScope LambdaScope(this, Scope::LambdaScope | Scope::DeclScope |
Scope::FunctionDeclarationScope |
@@ -1255,8 +1255,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
SmallVector<NamedDecl*, 4> TemplateParams;
SourceLocation LAngleLoc, RAngleLoc;
- if (ParseTemplateParameters(TemplateParamScope,
- CurTemplateDepthTracker.getDepth(),
+ if (ParseTemplateParameters(TemplateParamScope, TemplateParameterDepth,
TemplateParams, LAngleLoc, RAngleLoc)) {
Actions.ActOnLambdaError(LambdaBeginLoc, getCurScope());
return ExprError();
@@ -1275,7 +1274,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
// This way, abbreviated generic lambdas could have different template
// depths, avoiding substitution into the wrong template parameters during
// constraint satisfaction check.
- ++CurTemplateDepthTracker;
+ ++TemplateParameterDepth;
ExprResult RequiresClause;
if (TryConsumeToken(tok::kw_requires)) {
RequiresClause =
@@ -1311,6 +1310,10 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
bool HasSpecifiers = false;
SourceLocation MutableLoc;
+ ParseScope ImplicitTemplateScope(
+ this, Scope::NoScope,
+ Actions.getCurLambda()->NumExplicitTemplateParams == 0);
+
ParseScope Prototype(this, Scope::FunctionPrototypeScope |
Scope::FunctionDeclarationScope |
Scope::DeclScope);
@@ -1325,17 +1328,9 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
LParenLoc = T.getOpenLocation();
if (Tok.isNot(tok::r_paren)) {
- Actions.RecordParsingTemplateParameterDepth(
- CurTemplateDepthTracker.getOriginalDepth());
+ Actions.RecordParsingTemplateParameterDepth(SavedTemplateDepth.get());
ParseParameterDeclarationClause(D, Attributes, ParamInfo, EllipsisLoc);
- // For a generic lambda, each 'auto' within the parameter declaration
- // clause creates a template type parameter, so increment the depth.
- // If we've parsed any explicit template parameters, then the depth will
- // have already been incremented. So we make sure that at most a single
- // depth level is added.
- if (Actions.getCurGenericLambda())
- CurTemplateDepthTracker.setAddedDepth(1);
}
T.consumeClose();
@@ -1480,6 +1475,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
StmtResult Stmt(ParseCompoundStatementBody());
BodyScope.Exit();
+ ImplicitTemplateScope.Exit();
TemplateParamScope.Exit();
LambdaScope.Exit();
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index 1e5aa55338309e..a60e47e70d24cf 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -22,6 +22,25 @@
#include "clang/Sema/Scope.h"
using namespace clang;
+Parser::ReenterTemplateScopeRAII::ReenterTemplateScopeRAII(Parser &P,
+ Decl *MaybeTemplated,
+ bool Enter)
+ : Scopes(P), CurTemplateDepthTracker(P.TemplateParameterDepth) {
+ if (Enter)
+ CurTemplateDepthTracker.addDepth(
+ P.ReenterTemplateScopes(Scopes, MaybeTemplated));
+}
+
+Parser::ReenterTemplateScopeRAII::ReenterTemplateScopeRAII(Parser &P,
+ const Declarator &D)
+ : Scopes(P), CurTemplateDepthTracker(P.TemplateParameterDepth) {
+ const auto *Params = D.getInventedTemplateParameterList();
+ if (Params && Params->getParam(0)->isImplicit()) {
+ Scopes.Enter(Scope::TemplateParamScope);
+ ++CurTemplateDepthTracker;
+ }
+}
+
unsigned Parser::ReenterTemplateScopes(MultiParseScope &S, Decl *D) {
return Actions.ActOnReenterTemplateScope(D, [&] {
S.Enter(Scope::TemplateParamScope);
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 8ca118cd89ec46..dd95b1ca0b6711 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -1185,7 +1185,7 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
// Poison SEH identifiers so they are flagged as illegal in function bodies.
PoisonSEHIdentifiersRAIIObject PoisonSEHIdentifiers(*this, true);
const DeclaratorChunk::FunctionTypeInfo &FTI = D.getFunctionTypeInfo();
- TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth);
+ ReenterTemplateScopeRAII InTemplateScope(*this, D);
// If this is C89 and the declspecs were completely missing, fudge in an
// implicit int. We do this here because this is the only place where
@@ -1371,15 +1371,6 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
return Res;
}
- // With abbreviated function templates - we need to explicitly add depth to
- // account for the implicit template parameter list induced by the template.
- if (const auto *Template = dyn_cast_if_present<FunctionTemplateDecl>(Res);
- Template && Template->isAbbreviated() &&
- Template->getTemplateParameters()->getParam(0)->isImplicit())
- // First template parameter is implicit - meaning no explicit template
- // parameter list was specified.
- CurTemplateDepthTracker.addDepth(1);
-
// Late attributes are parsed in the same scope as the function body.
if (LateParsedAttrs)
ParseLexedAttributeList(*LateParsedAttrs, Res, /*EnterScope=*/false,
diff --git a/clang/lib/Sema/Scope.cpp b/clang/lib/Sema/Scope.cpp
index fc79b1a056ed9e..ca5886f88e9a96 100644
--- a/clang/lib/Sema/Scope.cpp
+++ b/clang/lib/Sema/Scope.cpp
@@ -103,6 +103,20 @@ void Scope::Init(Scope *parent, unsigned flags) {
NRVO = std::nullopt;
}
+void Scope::EnterTemplateParameterScope() {
+ assert(isFunctionPrototypeScope() && isFunctionDeclarationScope());
+
+ Scope *Parent = getParent();
+ assert(Parent && !Parent->Entity && Parent->DeclsInScope.empty() &&
+ Parent->UsingDirectives.empty());
+ assert((Parent->getFlags() &
+ ~(OpenMPSimdDirectiveScope | OpenMPOrderClauseScope)) == NoScope &&
+ "expected a reserved, inactive template scope");
+
+ Parent->setFlags(TemplateParamScope);
+ TemplateParamParent = Parent;
+}
+
bool Scope::containedInPrototypeScope() const {
const Scope *S = this;
while (S) {
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 21f71d7f8b40e2..10ee85c33441de 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -2739,17 +2739,6 @@ LambdaScopeInfo *Sema::getCurLambda(bool IgnoreNonLambdaCapturingScope) {
return CurLSI;
}
-// We have a generic lambda if we parsed auto parameters, or we have
-// an associated template parameter list.
-LambdaScopeInfo *Sema::getCurGenericLambda() {
- if (LambdaScopeInfo *LSI = getCurLambda()) {
- return (LSI->TemplateParams.size() ||
- LSI->GLTemplateParameterList) ? LSI : nullptr;
- }
- return nullptr;
-}
-
-
void Sema::ActOnComment(SourceRange Comment) {
if (!LangOpts.RetainCommentsFromSystemHeaders &&
SourceMgr.isInSystemHeader(Comment.getBegin()))
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 5de5821fe263e5..f23fe78d537b7a 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16399,14 +16399,8 @@ LambdaScopeInfo *Sema::RebuildLambdaScopeInfo(CXXMethodDecl *CallOperator) {
LSI->AfterParameterList = CurContext == CallOperator;
LSI->BeforeCompoundStatement = false;
- // GLTemplateParameterList is necessary for getCurGenericLambda() which is
- // used at the point of dealing with potential captures.
- //
- // We don't use LambdaClass->isGenericLambda() because this value doesn't
- // flip for instantiated generic lambdas, where no FunctionTemplateDecls are
- // associated. (Technically, we could recover that list from their
- // instantiation patterns, but for now, the GLTemplateParameterList seems
- // unnecessary in these cases.)
+ // A generic lambda's call operator specialization has no template parameter
+ // list, even though the closure is still marked as generic.
if (FunctionTemplateDecl *FTD = CallOperator->getDescribedFunctionTemplate())
LSI->GLTemplateParameterList = FTD->getTemplateParameters();
const LambdaCaptureDefault LCD = LambdaClass->getLambdaCaptureDefault();
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 568bb4c7851401..58818167b69e69 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -81,6 +81,7 @@ class CheckDefaultArgumentVisitor
bool VisitExpr(const Expr *Node);
bool VisitDeclRefExpr(const DeclRefExpr *DRE);
bool VisitCXXThisExpr(const CXXThisExpr *ThisE);
+ bool VisitTemplateParameters(ArrayRef<NamedDecl *> Parameters);
bool VisitLambdaExpr(const LambdaExpr *Lambda);
bool VisitPseudoObjectExpr(const PseudoObjectExpr *POE);
bool VisitCoawaitExpr(const CoawaitExpr *E);
@@ -164,14 +165,24 @@ bool CheckDefaultArgumentVisitor::VisitPseudoObjectExpr(
return Invalid;
}
-bool CheckDefaultArgumentVisitor::VisitLambdaExpr(const LambdaExpr *Lambda) {
+bool CheckDefaultArgumentVisitor::VisitTemplateParameters(
+ ArrayRef<NamedDecl *> Parameters) {
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());
+ for (NamedDecl *P : Parameters) {
+ if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P)) {
+ if (NTTP->hasDefaultArgument())
+ Invalid |= Visit(NTTP->getDefaultArgument().getArgument().getAsExpr());
+ } else if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(P)) {
+ Invalid |=
+ VisitTemplateParameters(TTP->getTemplateParameters()->asArray());
+ }
}
+ return Invalid;
+}
+
+bool CheckDefaultArgumentVisitor::VisitLambdaExpr(const LambdaExpr *Lambda) {
+ bool Invalid =
+ VisitTemplateParameters(Lambda->getExplicitTemplateParameters());
// [expr.prim.lambda.capture]p9
// a lambda-expression appearing in a default argument cannot implicitly or
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index ffbe5e72d4c516..dfa46ed19a7751 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -1064,6 +1064,7 @@ void Sema::AddTemplateParametersToLambdaCallOperator(
TemplateParams, CallOperator);
TemplateMethod->setAccess(AS_public);
CallOperator->setDescribedFunctionTemplate(TemplateMethod);
+ Class->setLambdaIsGeneric(true);
}
void Sema::CompleteLambdaCallOperator(
@@ -1097,7 +1098,6 @@ void Sema::CompleteLambdaCallOperator(
} else {
LSI->Lambda->addDecl(Method);
}
- LSI->Lambda->setLambdaIsGeneric(TemplateParams);
LSI->Lambda->setLambdaTypeInfo(MethodTyInfo);
Method->setLexicalDeclContext(DC);
@@ -1143,32 +1143,8 @@ void Sema::ActOnLambdaExpressionAfterIntroducer(LambdaIntroducer &Intro,
// be dependent, because there are template parameters in scope.
CXXRecordDecl::LambdaDependencyKind LambdaDependencyKind =
CXXRecordDecl::LDK_Unknown;
- if (getTemplateDepth(CurScope) > 0) {
+ if (CurrentScope->getTemplateParamParent())
LambdaDependencyKind = CXXRecordDecl::LDK_AlwaysDependent;
- } else if (Scope *ParentScope = CurScope->getParent()) {
- // Given a lambda defined inside a requires expression,
- //
- // struct S {
- // S(auto var) requires requires { [&] -> decltype(var) { }; }
- // {}
- // };
- //
- // The parameter var is not injected into the function Decl at the point of
- // parsing lambda. In such scenarios, perceiving it as dependent could
- // result in the constraint being evaluated, which matches what GCC does.
- Scope *LookupScope = ParentScope;
- while (LookupScope->getEntity() &&
- LookupScope->getEntity()->isRequiresExprBody())
- LookupScope = LookupScope->getParent();
-
- if (LookupScope != ParentScope &&
- LookupScope->isFunctionDeclarationScope() &&
- llvm::any_of(LookupScope->decls(), [](Decl *D) {
- return isa<ParmVarDecl>(D) &&
- cast<ParmVarDecl>(D)->getType()->isTemplateTypeParmType();
- }))
- LambdaDependencyKind = CXXRecordDecl::LDK_AlwaysDependent;
- }
CXXRecordDecl *Class = createLambdaClosureType(
Intro.Range, /*Info=*/nullptr, LambdaDependencyKind, Intro.Default);
@@ -1448,7 +1424,6 @@ void Sema::ActOnLambdaClosureParameters(
if (TemplateParams) {
AddTemplateParametersToLambdaCallOperator(LSI->CallOperator, LSI->Lambda,
TemplateParams);
- LSI->Lambda->setLambdaIsGeneric(true);
LSI->ContainsUnexpandedParameterPack |=
TemplateParams->containsUnexpandedParameterPack();
}
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index b800a433b3bf74..238b0073cdca1a 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -63,34 +63,8 @@ unsigned Sema::getTemplateDepth(Scope *S) const {
// Each template parameter scope represents one level of template parameter
// depth.
for (Scope *TempParamScope = S->getTemplateParamParent(); TempParamScope;
- TempParamScope = TempParamScope->getParent()->getTemplateParamParent()) {
+ TempParamScope = TempParamScope->getParent()->getTemplateParamParent())
++Depth;
- }
-
- // Note that there are template parameters with the given depth.
- auto ParamsAtDepth = [&](unsigned D) { Depth = std::max(Depth, D + 1); };
-
- // Look for parameters of an enclosing generic lambda. We don't create a
- // template parameter scope for these.
- for (FunctionScopeInfo *FSI : getFunctionScopes()) {
- if (auto *LSI = dyn_cast<LambdaScopeInfo>(FSI)) {
- if (!LSI->TemplateParams.empty()) {
- ParamsAtDepth(LSI->AutoTemplateParameterDepth);
- } else if (LSI->GLTemplateParameterList) {
- ParamsAtDepth(LSI->GLTemplateParameterList->getDepth());
- }
- }
- }
-
- // Look for parameters of an enclosing terse function template. We don't
- // create a template parameter scope for these either.
- for (const InventedTemplateParameterInfo &Info :
- getInventedParameterInfos()) {
- if (!Info.TemplateParams.empty()) {
- ParamsAtDepth(Info.AutoTemplateParameterDepth);
- break;
- }
- }
return Depth;
}
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 9de4f12aabf684..bf56cbc88a0735 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -3099,6 +3099,9 @@ InventTemplateParameter(TypeProcessingState &state, QualType T,
InventedTemplateParam->setImplicit();
Info.TemplateParams.push_back(InventedTemplateParam);
+ if (AutoParameterPosition == 0)
+ S.getCurScope()->EnterTemplateParameterScope();
+
// Attach type constraints to the new parameter.
if (Auto->isConstrained()) {
if (TrailingTSI) {
diff --git a/clang/test/SemaCXX/lambda-unevaluated.cpp b/clang/test/SemaCXX/lambda-unevaluated.cpp
index 018d597e6831ba..ac0b09f787894c 100644
--- a/clang/test/SemaCXX/lambda-unevaluated.cpp
+++ b/clang/test/SemaCXX/lambda-unevaluated.cpp
@@ -284,18 +284,18 @@ static_assert(__is_same_as(int, helper<int>));
} // namespace GH138018
namespace GH172814 {
-auto f() {
+auto a() {
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() {
+auto b() {
int x = 0;
return [](auto w = [&] { return x; }) { }; // expected-error {{lambda expression in default argument cannot capture any entity}}
};
-auto g() {
+auto c() {
int x = 0;
return []<class T>(T w = [&] { return x; }) {}; // expected-error {{lambda expression in default argument cannot capture any entity}}
}
@@ -332,36 +332,61 @@ struct S {
}
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'}}
+void d(auto x, auto = []<template<auto = x> class> {}); // 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(int x, auto y, auto z, int n = [](auto x) { return sizeof(x); }(123)) {
+ return x + y + z + n;
}
+static_assert(e(1, 2, char(3)) == 6 + sizeof(int));
-constexpr int e(auto x, int n = []<class T = decltype(x)> { return sizeof(T); }()) {
+constexpr int f(auto *x, int n = []<class T = decltype(*x), auto N = sizeof(T)> { return N; }()) noexcept([]<class T = decltype(*x)> { return sizeof(T) == 1; }()) {
return n;
}
+static_assert(f(static_cast<int *>(nullptr)) == sizeof(int));
+static_assert(f(static_cast<char *>(nullptr)) == 1);
+static_assert(noexcept(f(static_cast<char *>(nullptr), 0)));
+static_assert(!noexcept(f(static_cast<int *>(nullptr), 0)));
-constexpr auto f = [](auto x, int n = []<auto N = sizeof(x)> { return N; }()) {
+constexpr int g(auto, int n = []<class T, unsigned N>(const T (&)[N]) { return sizeof(T) + N; }("abc")) {
return n;
-};
+}
+static_assert(g(0) == 5);
-constexpr auto g = [](auto x, int n = []<class T = decltype(x)> { return sizeof(T); }()) {
+template <class T>
+constexpr int h(T x, auto y, int n = []<auto N = sizeof(x) + sizeof(y)> { return N; }()) {
return n;
+}
+static_assert(h('a', 0) == 1 + sizeof(int));
+
+auto i = [](auto x) {
+ return [](auto y, int n = []<class T = decltype(y), auto N = 2 * sizeof(x) + sizeof(T)> { return N; }()) { return n; };
};
+static_assert(i(0)('a') == 2 * sizeof(int) + 1);
+static_assert(i('a')(0) == 2 + sizeof(int));
+
+template <auto> struct A {};
+template <class> constexpr auto j() noexcept {
+ return [](auto x, int n = [](auto) noexcept { return 0; }(123)) noexcept([]<class T = decltype(x)> { return sizeof(T) == 1; }()) -> A<[]<auto N = sizeof(x)> { return N; }()> { return {}; };
+}
+static_assert(__is_same(decltype(j<void>()('a')), A<sizeof(char)>));
+static_assert(__is_same(decltype(j<void>()(0)), A<sizeof(int)>));
+static_assert(noexcept(j<void>()('a')) && !noexcept(j<void>()(0)));
-constexpr auto h = [](auto x) {
- return [](auto y, int n = []<auto N = sizeof(y)> { return N; }()) {
- return n;
- };
+template <class T> struct B { // expected-note {{B defined here}}
+ static constexpr int a(auto x);
+ void b(auto) requires ([](auto) { return true; }(T{}));
};
+template <class T> constexpr int B<T>::a(auto x) {
+ return [](auto y) { return sizeof(y) + sizeof(T); }(x);
+}
+static_assert(B<char>::a(0) == 1 + sizeof(int));
+
+template <class T>
+void B<T>::b(auto) requires ([](auto) { return true; }(T{})) {} // expected-error {{out-of-line definition of 'b' does not match any declaration}}
-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);
+template <template <class T> requires requires(T t) { t; } class> struct C {};
+static_assert([](auto x) { return x; }(1) == 1);
}
More information about the cfe-commits
mailing list