Skip to content

Conversation

zyn0217
Copy link
Contributor

@zyn0217 zyn0217 commented Aug 18, 2025

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 #145776

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.
@zyn0217 zyn0217 requested review from cor3ntin and erichkeane August 18, 2025 12:21
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Aug 18, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 18, 2025

@llvm/pr-subscribers-clang

Author: Younan Zhang (zyn0217)

Changes

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 #145776


Full diff: https://github.com/llvm/llvm-project/pull/154106.diff

6 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+1)
  • (modified) clang/include/clang/Sema/Sema.h (+7-6)
  • (modified) clang/lib/Sema/SemaDecl.cpp (+4-7)
  • (modified) clang/lib/Sema/SemaLambda.cpp (+12-2)
  • (modified) clang/lib/Sema/TreeTransform.h (+4-7)
  • (modified) clang/test/SemaCXX/cxx2b-consteval-propagate.cpp (+16)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 9231f2cae9bfa..ca6fc663c9525 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -158,6 +158,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.
 
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 5211373367677..160b0739b5aa2 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -4179,8 +4179,9 @@ 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);
+  Decl *ActOnFinishFunctionBody(Decl *Decl, Stmt *Body,
+                                bool IsInstantiation = false,
+                                bool RetainFunctionScopeInfo = false);
   Decl *ActOnSkippedFunctionBody(Decl *Decl);
   void ActOnFinishInlineFunctionDef(FunctionDecl *D);
 
@@ -6873,23 +6874,23 @@ class Sema final : public SemaBase {
     assert(!ExprEvalContexts.empty() &&
            "Must be in an expression evaluation context");
     return ExprEvalContexts.back();
-  };
+  }
 
   ExpressionEvaluationContextRecord &currentEvaluationContext() {
     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 ==
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index b5eb825eb52cc..4c8974b2e6c9a 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16149,10 +16149,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 {
@@ -16223,8 +16219,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;
 
@@ -16681,7 +16677,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 bc3c4b0addeba..eb6bc83cf861a 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -1968,12 +1968,13 @@ 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);
 }
@@ -2134,6 +2135,11 @@ ConstructFixItRangeForUnusedCapture(Sema &S, SourceRange CaptureRange,
 
 ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
                                  LambdaScopeInfo *LSI) {
+  // Copy the LSI before PopFunctionScopeInfo removes it.
+  // FIXME: This is dumb. Store the lambda information somewhere that outlives
+  // the call operator.
+  LambdaScopeInfo LSICopy = *LSI;
+  LSI = &LSICopy;
   // Collect information from the lambda scope.
   SmallVector<LambdaCapture, 4> Captures;
   SmallVector<Expr *, 4> CaptureInits;
@@ -2170,6 +2176,10 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
 
     PopExpressionEvaluationContext();
 
+    sema::AnalysisBasedWarnings::Policy WP =
+        AnalysisWarnings.getPolicyInEffectAt(EndLoc);
+    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 5ff5fbf52ae25..c3893959c85bf 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -15791,12 +15791,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
@@ -15832,7 +15829,7 @@ 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);
   // Clean up the type cache created previously. Then, we re-create a type for
   // such Decl with the new DependencyKind.
@@ -15840,7 +15837,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
   getSema().Context.getTypeDeclType(Class);
 
   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;
+    }();
+  }();
+}
+
+}

@zyn0217 zyn0217 requested a review from erichkeane August 19, 2025 03:55
@zyn0217 zyn0217 merged commit 2b32ad1 into llvm:main Aug 19, 2025
10 checks passed
AnalysisWarnings.getPolicyInEffectAt(EndLoc);
// We cannot release LSI until we finish computing captures, which
// requires the scope to be popped.
PoppedFunctionScopePtr _ = PopFunctionScopeInfo(&WP, LSI->CallOperator);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is causing a use-after-free that CI is hitting:
#154342
#154347

because LSI is used outside of this block:

DiagnoseShadowingLambdaDecls(LSI);

maybeAddDeclWithEffects(LSI->CallOperator);

Perhaps we should revert the changes until that's fixed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I reverted in #154382 :(

zyn0217 added a commit that referenced this pull request Aug 19, 2025
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Aug 19, 2025
@gulfemsavrun
Copy link
Contributor

We started seeing a segmentation fault when compiling FuzzerIO.cpp, and I bisected it to this commit.

[2228/2257](22) Building CXX object compiler-rt/lib/fuzzer/CMakeFiles/RTfuzzer.x86_64.dir/FuzzerIO.cpp.obj
FAILED: compiler-rt/lib/fuzzer/CMakeFiles/RTfuzzer.x86_64.dir/FuzzerIO.cpp.obj 
/b/s/w/ir/x/w/llvm_build/./bin/clang-cl --target=x86_64-pc-windows-msvc  /nologo -TP -DUNICODE -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS -D_GLIBCXX_ASSERTIONS -D_SCL_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/b/s/w/ir/x/w/llvm-llvm-project/compiler-rt/lib/fuzzer/../../include -Xclang -ivfsoverlay -Xclang /b/s/w/ir/cache/windows_sdk/llvm-vfsoverlay.yaml /winsysroot /b/s/w/ir/cache/windows_sdk /Zc:inline /Zc:__cplusplus /Oi /bigobj /permissive- -Werror=unguarded-availability-new -wd4141 -wd4146 -wd4244 -wd4267 -wd4291 -wd4351 -wd4456 -wd4457 -wd4458 -wd4459 -wd4503 -wd4624 -wd4722 -wd4100 -wd4127 -wd4512 -wd4505 -wd4610 -wd4510 -wd4702 -wd4245 -wd4706 -wd4310 -wd4701 -wd4703 -wd4389 -wd4611 -wd4805 -wd4204 -wd4577 -wd4091 -wd4592 -wd4319 -wd4709 -wd5105 -wd4324 -wd4251 -wd4275 -w14062 -we4238 /Gw /W4 -Wno-unused-parameter /O2 /Ob1  -std:c++17 -MT -Zi -fno-builtin -fno-sanitize=safe-stack -fno-lto /Oy- /GS- /Zc:threadSafeInit- -Wthread-safety -Wthread-safety-reference -Wthread-safety-beta /Z7 -Wno-gnu -Wno-variadic-macros -Wno-c99-extensions /wd4146 /wd4291 /wd4391 /wd4722 /wd4800 -ftrivial-auto-var-init=pattern -D_HAS_EXCEPTIONS=0 /showIncludes /Focompiler-rt/lib/fuzzer/CMakeFiles/RTfuzzer.x86_64.dir/FuzzerIO.cpp.obj /Fdcompiler-rt/lib/fuzzer/CMakeFiles/RTfuzzer.x86_64.dir/ -c -- /b/s/w/ir/x/w/llvm-llvm-project/compiler-rt/lib/fuzzer/FuzzerIO.cpp
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.	Program arguments: /b/s/w/ir/x/w/llvm_build/./bin/clang-cl --target=x86_64-pc-windows-msvc /nologo -TP -DUNICODE -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS -D_GLIBCXX_ASSERTIONS -D_SCL_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/b/s/w/ir/x/w/llvm-llvm-project/compiler-rt/lib/fuzzer/../../include -Xclang -ivfsoverlay -Xclang /b/s/w/ir/cache/windows_sdk/llvm-vfsoverlay.yaml /winsysroot /b/s/w/ir/cache/windows_sdk /Zc:inline /Zc:__cplusplus /Oi /bigobj /permissive- -Werror=unguarded-availability-new -wd4141 -wd4146 -wd4244 -wd4267 -wd4291 -wd4351 -wd4456 -wd4457 -wd4458 -wd4459 -wd4503 -wd4624 -wd4722 -wd4100 -wd4127 -wd4512 -wd4505 -wd4610 -wd4510 -wd4702 -wd4245 -wd4706 -wd4310 -wd4701 -wd4703 -wd4389 -wd4611 -wd4805 -wd4204 -wd4577 -wd4091 -wd4592 -wd4319 -wd4709 -wd5105 -wd4324 -wd4251 -wd4275 -w14062 -we4238 /Gw /W4 -Wno-unused-parameter /O2 /Ob1 -std:c++17 -MT -Zi -fno-builtin -fno-sanitize=safe-stack -fno-lto /Oy- /GS- /Zc:threadSafeInit- -Wthread-safety -Wthread-safety-reference -Wthread-safety-beta /Z7 -Wno-gnu -Wno-variadic-macros -Wno-c99-extensions /wd4146 /wd4291 /wd4391 /wd4722 /wd4800 -ftrivial-auto-var-init=pattern -D_HAS_EXCEPTIONS=0 /showIncludes /Focompiler-rt/lib/fuzzer/CMakeFiles/RTfuzzer.x86_64.dir/FuzzerIO.cpp.obj /Fdcompiler-rt/lib/fuzzer/CMakeFiles/RTfuzzer.x86_64.dir/ -c -- /b/s/w/ir/x/w/llvm-llvm-project/compiler-rt/lib/fuzzer/FuzzerIO.cpp
1.	/b/s/w/ir/x/w/llvm-llvm-project/compiler-rt/lib/fuzzer/FuzzerCommand.h:88:6: current parser token ';'
2.	/b/s/w/ir/x/w/llvm-llvm-project/compiler-rt/lib/fuzzer/FuzzerCommand.h:24:1: parsing namespace 'fuzzer'
3.	/b/s/w/ir/x/w/llvm-llvm-project/compiler-rt/lib/fuzzer/FuzzerCommand.h:26:1: parsing struct/union/class body 'fuzzer::Command'
4.	/b/s/w/ir/x/w/llvm-llvm-project/compiler-rt/lib/fuzzer/FuzzerCommand.h:84:47: parsing function body 'fuzzer::Command::hasFlag'
5.	/b/s/w/ir/x/w/llvm-llvm-project/compiler-rt/lib/fuzzer/FuzzerCommand.h:84:47: in compound statement ('{}')
6.	/b/s/w/ir/x/w/llvm-llvm-project/compiler-rt/lib/fuzzer/FuzzerCommand.h:86:20: lambda expression parsing
#0 0x0000557e02d4db78 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/b/s/w/ir/x/w/llvm_build/./bin/clang-cl+0x93efb78)
clang-cl: error: clang frontend command failed with exit code 139 (use -v to see invocation)
Fuchsia clang version 22.0.0git (https://llvm.googlesource.com/llvm-project 2c11a83691b7089d7a79e9f122dc521e6ea7e51e)
Target: x86_64-pc-windows-msvc
Thread model: posix
InstalledDir: /b/s/w/ir/x/w/llvm_build/bin
Build config: +assertions
clang-cl: note: diagnostic msg: 
********************

PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:
Preprocessed source(s) and associated run script(s) are located at:
clang-cl: note: diagnostic msg: /b/s/w/ir/x/w/llvm_build/clang-crashreports/FuzzerIO-3de123.cpp
clang-cl: note: diagnostic msg: /b/s/w/ir/x/w/llvm_build/clang-crashreports/FuzzerIO-3de123.sh
clang-cl: note: diagnostic msg: 

********************

https://logs.chromium.org/logs/fuchsia/buildbucket/cr-buildbucket/8706099643439759905/+/u/clang/build/stdout

I realized that a revert has landed, but I wanted to mention this issue before relanding.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Clang] Immediate-escalating expression attributed to wrong lambda
5 participants