[clang] [Clang][Sema] Make lambda in non-dependent context generate same analysis-based warnings as function[ template] (PR #159364)
Yanzuo Liu via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 17 06:57:31 PDT 2025
https://github.com/zwuis created https://github.com/llvm/llvm-project/pull/159364
This patch fixes following two issues:
- When handling warnings which should be emitted if reachable, expressions in global lambdas aren't treated as if in functions, so warnings are emitted immediately without being filtered by reachability analysis.
```cpp
auto L = [] {
return;
// previous: warning: left operand of comma operator has no effect
// expected: no warning, same as in functions
0, 0;
};
```
- Reachability analysis doesn't emit warnings if it's a dependent context. Non-null warning policy is passed to `Sema::PopFunctionScopeInfo` and warnings disappear if it's a generic lambda. Global generic lambdas don't suffer from this issue because of the issue above.
Before #154458, `Sema::PopFunctionScopeInfo` for lambda is called by `Sema::ActOnFinishFunctionBody`. This patch modifies arguments of `Sema::PopFunctionScopeInfo` only because I didn't find behaviour changes of modifying arguments of `Sema::ActOnFinishFunctionBody` in regression tests.
```cpp
void f() {
auto L = [](auto) {
// previous: no warning
// expected: warning emitted same as in function templates
0, 0;
};
}
```
>From 06b010d27dcfbd3a03453d6aab04f854ed734891 Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zwuis at outlook.com>
Date: Wed, 17 Sep 2025 21:54:01 +0800
Subject: [PATCH] Make lambda in non-dependent context generate same
analysis-based warnings as function[ template]
---
clang/docs/ReleaseNotes.rst | 3 ++
clang/include/clang/Sema/Sema.h | 2 +-
clang/lib/Sema/Sema.cpp | 4 +--
clang/lib/Sema/SemaExpr.cpp | 2 +-
clang/lib/Sema/SemaLambda.cpp | 16 +++++++++--
clang/test/SemaCXX/warn-unused-value.cpp | 36 ++++++++++++++++++++++++
6 files changed, 56 insertions(+), 7 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 518ed9e0f4b3e..0b68512d0c872 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -312,6 +312,9 @@ Improvements to Clang's diagnostics
properly being rejected when used at compile-time. It was not implemented
and caused assertion failures before (#GH158471).
+- Some reachability-analysis-based warnings in lambda expression which is in
+ non-templated context are emitted same as in function[ template].
+
Improvements to Clang's time-trace
----------------------------------
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index d017d1f829015..197b1eedc906d 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1166,7 +1166,7 @@ class Sema final : public SemaBase {
/// getCurFunctionOrMethodDecl - Return the Decl for the current ObjC method
/// or C function we're in, otherwise return null. If we're currently
/// in a 'block', this returns the containing context.
- NamedDecl *getCurFunctionOrMethodDecl() const;
+ NamedDecl *getCurFunctionOrMethodDecl(bool AllowLambda = false) const;
/// Warn if we're implicitly casting from a _Nullable pointer type to a
/// _Nonnull one.
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 39fa25f66f3b7..913a11bedc55d 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1656,8 +1656,8 @@ ObjCMethodDecl *Sema::getCurMethodDecl() {
return dyn_cast<ObjCMethodDecl>(DC);
}
-NamedDecl *Sema::getCurFunctionOrMethodDecl() const {
- DeclContext *DC = getFunctionLevelDeclContext();
+NamedDecl *Sema::getCurFunctionOrMethodDecl(bool AllowLambda) const {
+ DeclContext *DC = getFunctionLevelDeclContext(AllowLambda);
if (isa<ObjCMethodDecl>(DC) || isa<FunctionDecl>(DC))
return cast<NamedDecl>(DC);
return nullptr;
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 73b16ae09e922..b6fdcfb646cb0 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -20542,7 +20542,7 @@ void Sema::MarkDeclarationsReferencedInExpr(Expr *E,
/// namespace { auto *p = new double[3][false ? (1, 2) : 3]; }
bool Sema::DiagIfReachable(SourceLocation Loc, ArrayRef<const Stmt *> Stmts,
const PartialDiagnostic &PD) {
- if (!Stmts.empty() && getCurFunctionOrMethodDecl()) {
+ if (!Stmts.empty() && getCurFunctionOrMethodDecl(/*AllowLambda=*/true)) {
if (!FunctionScopes.empty())
FunctionScopes.back()->PossiblyUnreachableDiags.push_back(
sema::PossiblyUnreachableDiag(PD, Loc, Stmts));
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index fbc2e7eb30676..2a6d0b5b4a9de 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -1972,6 +1972,10 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body) {
if (LSI.CallOperator->hasAttr<SYCLKernelEntryPointAttr>())
SYCL().CheckSYCLEntryPointFunctionDecl(LSI.CallOperator);
+ // TODO: Find out if passing LSI.CallOperator->getDescribedFunctionTemplate()
+ // is necessary when it is a generic lambda. Are there any behaviour
+ // changes? `FunctionTemplateDecl` is always passed when handling simple
+ // function templates.
ActOnFinishFunctionBody(LSI.CallOperator, Body, /*IsInstantiation=*/false,
/*RetainFunctionScopeInfo=*/true);
@@ -2162,11 +2166,17 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc,
PopExpressionEvaluationContext();
- sema::AnalysisBasedWarnings::Policy WP =
- AnalysisWarnings.getPolicyInEffectAt(EndLoc);
// We cannot release LSI until we finish computing captures, which
// requires the scope to be popped.
- Sema::PoppedFunctionScopePtr _ = PopFunctionScopeInfo(&WP, LSI->CallOperator);
+ Sema::PoppedFunctionScopePtr _ = [&] {
+ if (LSI->CallOperator->getDescribedFunctionTemplate())
+ return PopFunctionScopeInfo(/*WP=*/nullptr,
+ TemplateOrNonTemplateCallOperatorDecl);
+
+ sema::AnalysisBasedWarnings::Policy WP =
+ AnalysisWarnings.getPolicyInEffectAt(EndLoc);
+ return PopFunctionScopeInfo(&WP, TemplateOrNonTemplateCallOperatorDecl);
+ }();
// True if the current capture has a used capture or default before it.
bool CurHasPreviousCapture = CaptureDefault != LCD_None;
diff --git a/clang/test/SemaCXX/warn-unused-value.cpp b/clang/test/SemaCXX/warn-unused-value.cpp
index 2a07a0324f3f0..7a2cbab275e0c 100644
--- a/clang/test/SemaCXX/warn-unused-value.cpp
+++ b/clang/test/SemaCXX/warn-unused-value.cpp
@@ -178,3 +178,39 @@ auto b() {
}
} // namespace test6
#endif
+
+// ensure lambda in non-dependent context generate same diagnostics as function[ template]
+namespace lambda_in_non_dependent_context {
+void f1() {
+ 0, 0; // expected-warning {{left operand of comma operator has no effect}}
+ return;
+ 0, 0;
+}
+template <typename T> void f2(T) {
+ 0, 0; // expected-warning {{left operand of comma operator has no effect}}
+ return;
+ 0, 0; // expected-warning {{left operand of comma operator has no effect}}
+}
+auto L1 = [] {
+ 0, 0; // expected-warning {{left operand of comma operator has no effect}}
+ return;
+ 0, 0;
+};
+auto L2 = [](auto) {
+ 0, 0; // expected-warning {{left operand of comma operator has no effect}}
+ return;
+ 0, 0; // expected-warning {{left operand of comma operator has no effect}}
+};
+void f() {
+ auto L1 = [] {
+ 0, 0; // expected-warning {{left operand of comma operator has no effect}}
+ return;
+ 0, 0;
+ };
+ auto L2 = [](auto) {
+ 0, 0; // expected-warning {{left operand of comma operator has no effect}}
+ return;
+ 0, 0; // expected-warning {{left operand of comma operator has no effect}}
+ };
+}
+}
More information about the cfe-commits
mailing list