[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