[clang] [clang] Fixed predefined expressions after lambda parameters (PR #211811)

via cfe-commits cfe-commits at lists.llvm.org
Mon Jul 27 07:18:45 PDT 2026


https://github.com/StefanPaulet updated https://github.com/llvm/llvm-project/pull/211811

>From 9bb556e0041bad23057343ff227aac711871463e Mon Sep 17 00:00:00 2001
From: StefanPaulet <tudor.stefan.paulet at gmail.com>
Date: Mon, 20 Jul 2026 17:38:02 +0300
Subject: [PATCH 1/6] Fixed predefined expressions after lambda parameters

---
 clang/include/clang/Sema/ScopeInfo.h |  2 ++
 clang/lib/Sema/SemaExpr.cpp          | 12 +++++--
 clang/lib/Sema/SemaLambda.cpp        |  2 ++
 clang/lib/Sema/TreeTransform.h       |  2 ++
 clang/test/SemaCXX/GH122657.cpp      | 49 ++++++++++++++++++++++++++++
 5 files changed, 64 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/SemaCXX/GH122657.cpp

diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h
index 7e4d3f2e0d1cb..a7a98e56b25ec 100644
--- a/clang/include/clang/Sema/ScopeInfo.h
+++ b/clang/include/clang/Sema/ScopeInfo.h
@@ -874,6 +874,8 @@ class LambdaScopeInfo final :
   /// is known.
   bool AfterParameterList = true;
 
+  bool BeforeCompoundStatement = false;
+
   ParmVarDecl *ExplicitObjectParameter = nullptr;
 
   /// Source range covering the lambda introducer [...].
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 592cb1d588370..0a9b02af8a8dd 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -2114,7 +2114,13 @@ static PredefinedIdentKind getPredefinedExprKind(tok::TokenKind Kind) {
 /// getPredefinedExprDecl - Returns Decl of a given DeclContext that can be used
 /// to determine the value of a PredefinedExpr. This can be either a
 /// block, lambda, captured statement, function, otherwise a nullptr.
-static Decl *getPredefinedExprDecl(DeclContext *DC) {
+static Decl *getPredefinedExprDecl(Sema &S, DeclContext *DC) {
+  if (isLambdaCallOperator(DC)) {
+    LambdaScopeInfo *LSI = S.getCurLambda();
+    if (LSI->BeforeCompoundStatement) {
+      DC = DC->getParent();
+    }
+  }
   while (DC && !isa<BlockDecl, CapturedDecl, FunctionDecl, ObjCMethodDecl>(DC))
     DC = DC->getParent();
   return cast_or_null<Decl>(DC);
@@ -2200,7 +2206,7 @@ Sema::ExpandFunctionLocalPredefinedMacros(ArrayRef<Token> Toks) {
   // Note: Although function local macros are defined only inside functions,
   // we ensure a valid `CurrentDecl` even outside of a function. This allows
   // expansion of macros into empty string literals without additional checks.
-  Decl *CurrentDecl = getPredefinedExprDecl(CurContext);
+  Decl *CurrentDecl = getPredefinedExprDecl(*this, CurContext);
   if (!CurrentDecl)
     CurrentDecl = Context.getTranslationUnitDecl();
 
@@ -3627,7 +3633,7 @@ static void ConvertUTF8ToWideString(unsigned CharByteWidth, StringRef Source,
 
 ExprResult Sema::BuildPredefinedExpr(SourceLocation Loc,
                                      PredefinedIdentKind IK) {
-  Decl *currentDecl = getPredefinedExprDecl(CurContext);
+  Decl *currentDecl = getPredefinedExprDecl(*this, CurContext);
   if (!currentDecl) {
     Diag(Loc, diag::ext_predef_outside_function);
     currentDecl = Context.getTranslationUnitDecl();
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index 8572e3a742a6c..0fb4e1fa92d99 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -1436,6 +1436,7 @@ void Sema::ActOnLambdaClosureParameters(
         TemplateParams->containsUnexpandedParameterPack();
   }
   LSI->AfterParameterList = true;
+  LSI->BeforeCompoundStatement = true;
 }
 
 void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
@@ -1444,6 +1445,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
 
   LambdaScopeInfo *LSI = getCurrentLambdaScopeUnsafe(*this);
   LSI->CallOperator->setConstexprKind(DS.getConstexprSpecifier());
+  LSI->BeforeCompoundStatement = false;
 
   SmallVector<ParmVarDecl *, 8> Params;
   bool ExplicitResultType;
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 3c8fcbe582b43..df2c0429ef7e0 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16077,6 +16077,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
         TPL->containsUnexpandedParameterPack();
   }
 
+  LSI->BeforeCompoundStatement = true;
   TypeLocBuilder NewCallOpTLBuilder;
   TypeLoc OldCallOpTypeLoc =
       E->getCallOperator()->getTypeSourceInfo()->getTypeLoc();
@@ -16101,6 +16102,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
       !TRC.ArgPackSubstIndex)
     TRC.ArgPackSubstIndex = SemaRef.ArgPackSubstIndex;
 
+  LSI->BeforeCompoundStatement = false;
   getSema().CompleteLambdaCallOperator(
       NewCallOperator, E->getCallOperator()->getLocation(),
       E->getCallOperator()->getInnerLocStart(), TRC, NewCallOpTSI,
diff --git a/clang/test/SemaCXX/GH122657.cpp b/clang/test/SemaCXX/GH122657.cpp
new file mode 100644
index 0000000000000..1faca88bf3a77
--- /dev/null
+++ b/clang/test/SemaCXX/GH122657.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 -x c++ -std=c++11 -fblocks -fsyntax-only -verify %s
+// expected-no-diagnostics
+
+template <unsigned long long n>
+struct Sized {
+  char data[n];
+};
+
+template <typename T>
+int baz() {
+  static constexpr auto funcSize = sizeof(__func__);
+  static constexpr auto functionSize = sizeof(__FUNCTION__);
+  static constexpr auto prettySize = sizeof(__PRETTY_FUNCTION__);
+
+  auto lfunc = []() noexcept(sizeof(__func__) == funcSize) -> Sized<sizeof(__func__)> { return {}; };
+  auto lfunction = []() noexcept(sizeof(__FUNCTION__) == functionSize) -> Sized<sizeof(__FUNCTION__)> { return {}; };
+  auto lpretty = []() noexcept(sizeof(__PRETTY_FUNCTION__) == prettySize) -> Sized<sizeof(__PRETTY_FUNCTION__)> { return {}; };
+
+  static_assert(sizeof(lfunc()) == 5, "baz");
+  static_assert(noexcept(lfunc()) == true, "noexcept");
+
+  static_assert(sizeof(lfunction()) == 5, "baz");
+  static_assert(noexcept(lfunction()) == true, "noexcept");
+
+  static_assert(sizeof(lpretty()) == 33, "int baz() [T = int]_block_invoke");
+  static_assert(noexcept(lpretty()) == true, "noexcept");
+
+  return 0;
+}
+
+int main() {
+  static constexpr auto funcSize = sizeof(__func__);
+  static constexpr auto functionSize = sizeof(__FUNCTION__);
+  static constexpr auto prettySize = sizeof(__PRETTY_FUNCTION__);
+
+  auto lfunc = []() noexcept(sizeof(__func__) == funcSize) -> Sized<sizeof(__func__)> { return {}; };
+  auto lfunction = []() noexcept(sizeof(__FUNCTION__) == functionSize) -> Sized<sizeof(__FUNCTION__)> { return {}; };
+  auto lpretty = []() noexcept(sizeof(__PRETTY_FUNCTION__) == prettySize) -> Sized<sizeof(__PRETTY_FUNCTION__)> { return {}; };
+
+  static_assert(sizeof(lfunc()) == 5, "main");
+  static_assert(noexcept(lfunc()) == true, "noexcept");
+
+  static_assert(sizeof(lfunction()) == 5, "main");
+  static_assert(noexcept(lfunction()) == true, "noexcept");
+
+  static_assert(sizeof(lpretty()) == 11, "int main()");
+  static_assert(noexcept(lpretty()) == true, "noexcept");
+}
+

>From aa5f7c86cecfef9e4ea967426e978b26fbcc7eb2 Mon Sep 17 00:00:00 2001
From: StefanPaulet <tudor.stefan.paulet at gmail.com>
Date: Fri, 24 Jul 2026 17:45:42 +0300
Subject: [PATCH 2/6] Added release note

---
 clang/docs/ReleaseNotes.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 83a2b10d96046..8840286927a68 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -338,6 +338,7 @@ features cannot lower the translation-unit ABI level;
 
 - Fixed a constraint comparison bug in partial ordering. (#GH182671)
 - Fixed a rejected-valid case that used an explicit object parameter in an out-of-line definition of a nested class member. (#GH136472)
+- Fixed a bug where `__func__`, `__PRETTY_FUNCTION__` and `__FUNCTION__` were not resolving to the proper function when inside a lambda return type (#GH211811)
 
 #### Bug Fixes to Compiler Builtins
 

>From 69f3bc776864f88c938f0fbce7da9d2837a7f36f Mon Sep 17 00:00:00 2001
From: StefanPaulet <tudor.stefan.paulet at gmail.com>
Date: Sat, 25 Jul 2026 17:00:19 +0300
Subject: [PATCH 3/6] Addressed review comment

---
 clang/include/clang/Sema/ScopeInfo.h | 2 +-
 clang/lib/Sema/SemaLambda.cpp        | 1 -
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h
index 8fccad70eb9dd..8dc40a5bc58bd 100644
--- a/clang/include/clang/Sema/ScopeInfo.h
+++ b/clang/include/clang/Sema/ScopeInfo.h
@@ -879,7 +879,7 @@ class LambdaScopeInfo final :
   /// is known.
   bool AfterParameterList = true;
 
-  bool BeforeCompoundStatement = false;
+  bool BeforeCompoundStatement = true;
 
   ParmVarDecl *ExplicitObjectParameter = nullptr;
 
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index a580fbbd33cf1..bbe93f6ab8a40 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -1436,7 +1436,6 @@ void Sema::ActOnLambdaClosureParameters(
         TemplateParams->containsUnexpandedParameterPack();
   }
   LSI->AfterParameterList = true;
-  LSI->BeforeCompoundStatement = true;
 }
 
 void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,

>From c93c24dcbc721017e0e4d9c9068182de642fe80f Mon Sep 17 00:00:00 2001
From: StefanPaulet <tudor.stefan.paulet at gmail.com>
Date: Sat, 25 Jul 2026 18:18:10 +0300
Subject: [PATCH 4/6] Addressed review comment

---
 clang/lib/Sema/SemaExpr.cpp | 26 ++++++++++++++++++++------
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index e936fdf1f8fc0..1066a337f2012 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -2117,14 +2117,28 @@ static PredefinedIdentKind getPredefinedExprKind(tok::TokenKind Kind) {
 /// to determine the value of a PredefinedExpr. This can be either a
 /// block, lambda, captured statement, function, otherwise a nullptr.
 static Decl *getPredefinedExprDecl(Sema &S, DeclContext *DC) {
-  if (isLambdaCallOperator(DC)) {
-    LambdaScopeInfo *LSI = S.getCurLambda();
-    if (LSI->BeforeCompoundStatement) {
-      DC = DC->getParent();
+  auto LSI = S.FunctionScopes.rbegin();
+
+  auto tryAdjustLambdaContext = [&S, &LSI](DeclContext *&DC) {
+    if (isLambdaCallOperator(DC)) {
+      auto E = S.FunctionScopes.rend();
+      while (LSI != E && isa<CapturingScopeInfo>(*LSI) &&
+             !isa<LambdaScopeInfo>(*LSI))
+        ++LSI;
+      assert(LSI != E && "Should be in a lambda scope info");
+      if (dyn_cast<LambdaScopeInfo>(*LSI)->BeforeCompoundStatement)
+        DC = DC->getParent();
+      ++LSI;
     }
-  }
-  while (DC && !isa<BlockDecl, CapturedDecl, FunctionDecl, ObjCMethodDecl>(DC))
+  };
+
+  tryAdjustLambdaContext(DC);
+  while (DC &&
+         !isa<BlockDecl, CapturedDecl, FunctionDecl, ObjCMethodDecl>(DC)) {
     DC = DC->getParent();
+    tryAdjustLambdaContext(DC);
+  }
+
   return cast_or_null<Decl>(DC);
 }
 

>From 19a38795768a94998d16045561d0c0389f714219 Mon Sep 17 00:00:00 2001
From: StefanPaulet <tudor.stefan.paulet at gmail.com>
Date: Sun, 26 Jul 2026 16:06:18 +0300
Subject: [PATCH 5/6] Fixed error on template lambdas found from libcxx tests

---
 clang/lib/Sema/SemaDecl.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 7de5542e72559..0d0229940d783 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16260,6 +16260,7 @@ LambdaScopeInfo *Sema::RebuildLambdaScopeInfo(CXXMethodDecl *CallOperator) {
   // substituting into it. In this case the flag needs to be true such that
   // tryCaptureVariable can correctly handle potential captures thereof.
   LSI->AfterParameterList = CurContext == CallOperator;
+  LSI->BeforeCompoundStatement = false;
 
   // GLTemplateParameterList is necessary for getCurGenericLambda() which is
   // used at the point of dealing with potential captures.

>From 0759f95594f12f9ea4efe8edf666c06862a21ec9 Mon Sep 17 00:00:00 2001
From: StefanPaulet <tudor.stefan.paulet at gmail.com>
Date: Mon, 27 Jul 2026 17:18:18 +0300
Subject: [PATCH 6/6] Moved test to source_location.cpp. Removed unnecessary
 assignment.

---
 clang/lib/Sema/TreeTransform.h         |  1 -
 clang/test/SemaCXX/GH122657.cpp        | 49 ------------------------
 clang/test/SemaCXX/source_location.cpp | 53 ++++++++++++++++++++++++++
 3 files changed, 53 insertions(+), 50 deletions(-)
 delete mode 100644 clang/test/SemaCXX/GH122657.cpp

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 3e75753c4c098..0725664a4e050 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16312,7 +16312,6 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
         TPL->containsUnexpandedParameterPack();
   }
 
-  LSI->BeforeCompoundStatement = true;
   TypeLocBuilder NewCallOpTLBuilder;
   TypeLoc OldCallOpTypeLoc =
       E->getCallOperator()->getTypeSourceInfo()->getTypeLoc();
diff --git a/clang/test/SemaCXX/GH122657.cpp b/clang/test/SemaCXX/GH122657.cpp
deleted file mode 100644
index 1faca88bf3a77..0000000000000
--- a/clang/test/SemaCXX/GH122657.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-// RUN: %clang_cc1 -x c++ -std=c++11 -fblocks -fsyntax-only -verify %s
-// expected-no-diagnostics
-
-template <unsigned long long n>
-struct Sized {
-  char data[n];
-};
-
-template <typename T>
-int baz() {
-  static constexpr auto funcSize = sizeof(__func__);
-  static constexpr auto functionSize = sizeof(__FUNCTION__);
-  static constexpr auto prettySize = sizeof(__PRETTY_FUNCTION__);
-
-  auto lfunc = []() noexcept(sizeof(__func__) == funcSize) -> Sized<sizeof(__func__)> { return {}; };
-  auto lfunction = []() noexcept(sizeof(__FUNCTION__) == functionSize) -> Sized<sizeof(__FUNCTION__)> { return {}; };
-  auto lpretty = []() noexcept(sizeof(__PRETTY_FUNCTION__) == prettySize) -> Sized<sizeof(__PRETTY_FUNCTION__)> { return {}; };
-
-  static_assert(sizeof(lfunc()) == 5, "baz");
-  static_assert(noexcept(lfunc()) == true, "noexcept");
-
-  static_assert(sizeof(lfunction()) == 5, "baz");
-  static_assert(noexcept(lfunction()) == true, "noexcept");
-
-  static_assert(sizeof(lpretty()) == 33, "int baz() [T = int]_block_invoke");
-  static_assert(noexcept(lpretty()) == true, "noexcept");
-
-  return 0;
-}
-
-int main() {
-  static constexpr auto funcSize = sizeof(__func__);
-  static constexpr auto functionSize = sizeof(__FUNCTION__);
-  static constexpr auto prettySize = sizeof(__PRETTY_FUNCTION__);
-
-  auto lfunc = []() noexcept(sizeof(__func__) == funcSize) -> Sized<sizeof(__func__)> { return {}; };
-  auto lfunction = []() noexcept(sizeof(__FUNCTION__) == functionSize) -> Sized<sizeof(__FUNCTION__)> { return {}; };
-  auto lpretty = []() noexcept(sizeof(__PRETTY_FUNCTION__) == prettySize) -> Sized<sizeof(__PRETTY_FUNCTION__)> { return {}; };
-
-  static_assert(sizeof(lfunc()) == 5, "main");
-  static_assert(noexcept(lfunc()) == true, "noexcept");
-
-  static_assert(sizeof(lfunction()) == 5, "main");
-  static_assert(noexcept(lfunction()) == true, "noexcept");
-
-  static_assert(sizeof(lpretty()) == 11, "int main()");
-  static_assert(noexcept(lpretty()) == true, "noexcept");
-}
-
diff --git a/clang/test/SemaCXX/source_location.cpp b/clang/test/SemaCXX/source_location.cpp
index eaa6cb04c5d1c..390fd88b323c4 100644
--- a/clang/test/SemaCXX/source_location.cpp
+++ b/clang/test/SemaCXX/source_location.cpp
@@ -1091,3 +1091,56 @@ namespace GH178324 {
   template <class> void c() { decltype(a(current()))::e; }
 } // namespace GH178324
 #endif
+
+namespace GH122657 {
+template <unsigned long long n>
+struct Sized {
+  char data[n];
+};
+
+template <typename T>
+int baz() {
+  static constexpr auto funcSize = sizeof(__func__);
+  static constexpr auto functionSize = sizeof(__FUNCTION__);
+  static constexpr auto prettySize = sizeof(__PRETTY_FUNCTION__);
+
+  auto lfunc = []() noexcept(sizeof(__func__) == funcSize) -> Sized<sizeof(__func__)> { return {}; };
+  auto lfunction = []() noexcept(sizeof(__FUNCTION__) == functionSize) -> Sized<sizeof(__FUNCTION__)> { return {}; };
+  auto lpretty = []() noexcept(sizeof(__PRETTY_FUNCTION__) == prettySize) -> Sized<sizeof(__PRETTY_FUNCTION__)> { return {}; };
+
+  static_assert(sizeof(lfunc()) == 5, "baz");
+  static_assert(noexcept(lfunc()) == true, "noexcept");
+
+  static_assert(sizeof(lfunction()) == 5, "baz");
+  static_assert(noexcept(lfunction()) == true, "noexcept");
+
+  static_assert(sizeof(lpretty()) == 43, "int GH122657::baz() [T = int]_block_invoke");
+  static_assert(noexcept(lpretty()) == true, "noexcept");
+
+  return 0;
+}
+
+int main() {
+  static constexpr auto funcSize = sizeof(__func__);
+  static constexpr auto functionSize = sizeof(__FUNCTION__);
+  static constexpr auto prettySize = sizeof(__PRETTY_FUNCTION__);
+
+  auto lfunc = []() noexcept(sizeof(__func__) == funcSize) -> Sized<sizeof(__func__)> { return {}; };
+  auto lfunction = []() noexcept(sizeof(__FUNCTION__) == functionSize) -> Sized<sizeof(__FUNCTION__)> { return {}; };
+  auto lpretty = []() noexcept(sizeof(__PRETTY_FUNCTION__) == prettySize) -> Sized<sizeof(__PRETTY_FUNCTION__)> { return {}; };
+
+  static_assert(sizeof(lfunc()) == 5, "main");
+  static_assert(noexcept(lfunc()) == true, "noexcept");
+
+#ifdef MS
+  static_assert(sizeof(lfunction()) == 15, "main");
+#else
+  static_assert(sizeof(lfunction()) == 5, "main");
+#endif
+  static_assert(noexcept(lfunction()) == true, "noexcept");
+
+  static_assert(sizeof(lpretty()) == 21, "int GH122657::main()");
+  static_assert(noexcept(lpretty()) == true, "noexcept");
+  return 0;
+}
+} // namespace GH122657
\ No newline at end of file



More information about the cfe-commits mailing list