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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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); } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
