Author: Younan Zhang Date: 2025-08-19T21:59:23+08:00 New Revision: 2b32ad1316059ac5fbb739684b92facef979dea1
URL: https://github.com/llvm/llvm-project/commit/2b32ad1316059ac5fbb739684b92facef979dea1 DIFF: https://github.com/llvm/llvm-project/commit/2b32ad1316059ac5fbb739684b92facef979dea1.diff LOG: [Clang] Only remove lambda scope after computing evaluation context (#154106) The immediate evaluation context needs the lambda scope info to propagate some flags, however that LSI was removed in ActOnFinishFunctionBody which happened before rebuilding a lambda expression. This also converts the wrapper function to default arguments as a drive-by fix. Fixes https://github.com/llvm/llvm-project/issues/145776 Added: Modified: clang/docs/ReleaseNotes.rst clang/include/clang/Sema/Sema.h clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaLambda.cpp clang/lib/Sema/TreeTransform.h clang/test/SemaCXX/cxx2b-consteval-propagate.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index b86a9c437ffb1..a8dc5352fcbb4 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -225,6 +225,7 @@ Bug Fixes to C++ Support - Diagnose binding a reference to ``*nullptr`` during constant evaluation. (#GH48665) - Suppress ``-Wdeprecated-declarations`` in implicitly generated functions. (#GH147293) - Fix a crash when deleting a pointer to an incomplete array (#GH150359). +- Fixed a mismatched lambda scope bug when propagating up ``consteval`` within nested lambdas. (#GH145776) - Fix an assertion failure when expression in assumption attribute (``[[assume(expr)]]``) creates temporary objects. - Fix the dynamic_cast to final class optimization to correctly handle diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index da9070842694e..098fe277c2b15 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4176,8 +4176,15 @@ class Sema final : public SemaBase { /// return statement in the scope of a variable has the same NRVO candidate, /// that candidate is an NRVO variable. void computeNRVO(Stmt *Body, sema::FunctionScopeInfo *Scope); - Decl *ActOnFinishFunctionBody(Decl *Decl, Stmt *Body); - Decl *ActOnFinishFunctionBody(Decl *Decl, Stmt *Body, bool IsInstantiation); + + /// Performs semantic analysis at the end of a function body. + /// + /// \param RetainFunctionScopeInfo If \c true, the client is responsible for + /// releasing the associated \p FunctionScopeInfo. This is useful when + /// building e.g. LambdaExprs. + Decl *ActOnFinishFunctionBody(Decl *Decl, Stmt *Body, + bool IsInstantiation = false, + bool RetainFunctionScopeInfo = false); Decl *ActOnSkippedFunctionBody(Decl *Decl); void ActOnFinishInlineFunctionDef(FunctionDecl *D); @@ -6874,23 +6881,23 @@ class Sema final : public SemaBase { assert(!ExprEvalContexts.empty() && "Must be in an expression evaluation context"); return ExprEvalContexts.back(); - }; + } ExpressionEvaluationContextRecord ¤tEvaluationContext() { assert(!ExprEvalContexts.empty() && "Must be in an expression evaluation context"); return ExprEvalContexts.back(); - }; + } ExpressionEvaluationContextRecord &parentEvaluationContext() { assert(ExprEvalContexts.size() >= 2 && "Must be in an expression evaluation context"); return ExprEvalContexts[ExprEvalContexts.size() - 2]; - }; + } const ExpressionEvaluationContextRecord &parentEvaluationContext() const { return const_cast<Sema *>(this)->parentEvaluationContext(); - }; + } bool isAttrContext() const { return ExprEvalContexts.back().ExprContext == @@ -9140,8 +9147,7 @@ class Sema final : public SemaBase { /// Complete a lambda-expression having processed and attached the /// lambda body. - ExprResult BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc, - sema::LambdaScopeInfo *LSI); + ExprResult BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc); /// Get the return type to use for a lambda's conversion function(s) to /// function pointer type, given the type of the call operator. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 98485cf9e72be..5e62c0cf01510 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -16228,10 +16228,6 @@ Decl *Sema::ActOnSkippedFunctionBody(Decl *Decl) { return Decl; } -Decl *Sema::ActOnFinishFunctionBody(Decl *D, Stmt *BodyArg) { - return ActOnFinishFunctionBody(D, BodyArg, /*IsInstantiation=*/false); -} - /// RAII object that pops an ExpressionEvaluationContext when exiting a function /// body. class ExitFunctionBodyRAII { @@ -16302,8 +16298,8 @@ void Sema::CheckCoroutineWrapper(FunctionDecl *FD) { Diag(FD->getLocation(), diag::err_coroutine_return_type) << RD; } -Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, - bool IsInstantiation) { +Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, bool IsInstantiation, + bool RetainFunctionScopeInfo) { FunctionScopeInfo *FSI = getCurFunction(); FunctionDecl *FD = dcl ? dcl->getAsFunction() : nullptr; @@ -16760,7 +16756,8 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, if (!IsInstantiation) PopDeclContext(); - PopFunctionScopeInfo(ActivePolicy, dcl); + if (!RetainFunctionScopeInfo) + PopFunctionScopeInfo(ActivePolicy, dcl); // If any errors have occurred, clear out any temporaries that may have // been leftover. This ensures that these temporaries won't be picked up for // deletion in some later function. diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index 0d891fc08c207..f09f1ad44ccec 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -1968,14 +1968,15 @@ ExprResult Sema::BuildCaptureInit(const Capture &Cap, } ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body) { - LambdaScopeInfo LSI = *cast<LambdaScopeInfo>(FunctionScopes.back()); + LambdaScopeInfo &LSI = *cast<LambdaScopeInfo>(FunctionScopes.back()); if (LSI.CallOperator->hasAttr<SYCLKernelEntryPointAttr>()) SYCL().CheckSYCLEntryPointFunctionDecl(LSI.CallOperator); - ActOnFinishFunctionBody(LSI.CallOperator, Body); + ActOnFinishFunctionBody(LSI.CallOperator, Body, /*IsInstantiation=*/false, + /*RetainFunctionScopeInfo=*/true); - return BuildLambdaExpr(StartLoc, Body->getEndLoc(), &LSI); + return BuildLambdaExpr(StartLoc, Body->getEndLoc()); } static LambdaCaptureDefault @@ -2132,8 +2133,9 @@ ConstructFixItRangeForUnusedCapture(Sema &S, SourceRange CaptureRange, return SourceRange(FixItStart, FixItEnd); } -ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc, - LambdaScopeInfo *LSI) { +ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, + SourceLocation EndLoc) { + LambdaScopeInfo *LSI = cast<LambdaScopeInfo>(FunctionScopes.back()); // Collect information from the lambda scope. SmallVector<LambdaCapture, 4> Captures; SmallVector<Expr *, 4> CaptureInits; @@ -2170,6 +2172,12 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc, PopExpressionEvaluationContext(); + sema::AnalysisBasedWarnings::Policy WP = + AnalysisWarnings.getPolicyInEffectAt(EndLoc); + // We cannot release LSI until we finish computing captures, which + // requires the scope to be popped. + PoppedFunctionScopePtr _ = PopFunctionScopeInfo(&WP, LSI->CallOperator); + // True if the current capture has a used capture or default before it. bool CurHasPreviousCapture = CaptureDefault != LCD_None; SourceLocation PrevCaptureLoc = CurHasPreviousCapture ? diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 055d3cd1a8609..3d4fc89d19bb9 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -4051,7 +4051,7 @@ class TreeTransform { PVD->getUninstantiatedDefaultArg() ->containsUnexpandedParameterPack(); } - return getSema().BuildLambdaExpr(StartLoc, EndLoc, LSI); + return getSema().BuildLambdaExpr(StartLoc, EndLoc); } /// Build an empty C++1z fold-expression with the given operator. @@ -15694,12 +15694,9 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { return ExprError(); } - // Copy the LSI before ActOnFinishFunctionBody removes it. - // FIXME: This is dumb. Store the lambda information somewhere that outlives - // the call operator. - auto LSICopy = *LSI; getSema().ActOnFinishFunctionBody(NewCallOperator, Body.get(), - /*IsInstantiation*/ true); + /*IsInstantiation=*/true, + /*RetainFunctionScopeInfo=*/true); SavedContext.pop(); // Recompute the dependency of the lambda so that we can defer the lambda call @@ -15735,11 +15732,11 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { // *after* the substitution in case we can't decide the dependency // so early, e.g. because we want to see if any of the *substituted* // parameters are dependent. - DependencyKind = getDerived().ComputeLambdaDependency(&LSICopy); + DependencyKind = getDerived().ComputeLambdaDependency(LSI); Class->setLambdaDependencyKind(DependencyKind); return getDerived().RebuildLambdaExpr(E->getBeginLoc(), - Body.get()->getEndLoc(), &LSICopy); + Body.get()->getEndLoc(), LSI); } template<typename Derived> diff --git a/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp b/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp index c4cfd9398920a..6cf0e0251ab62 100644 --- a/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp +++ b/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp @@ -610,3 +610,19 @@ namespace GH135281 { void (*ff)() = f2<B>; // expected-note {{instantiation of function template specialization}} } #endif + +namespace GH145776 { + +void runtime_only() {} +consteval void comptime_only() {} + +void fn() { + []() { + runtime_only(); + []() { + &comptime_only; + }(); + }(); +} + +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits