From 06b010d27dcfbd3a03453d6aab04f854ed734891 Mon Sep 17 00:00:00 2001 From: Yanzuo Liu Date: Wed, 17 Sep 2025 21:54:01 +0800 Subject: [PATCH 1/2] 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(DC); } -NamedDecl *Sema::getCurFunctionOrMethodDecl() const { - DeclContext *DC = getFunctionLevelDeclContext(); +NamedDecl *Sema::getCurFunctionOrMethodDecl(bool AllowLambda) const { + DeclContext *DC = getFunctionLevelDeclContext(AllowLambda); if (isa(DC) || isa(DC)) return cast(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 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()) 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 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}} + }; +} +} From a1a6e2809a1569c1313ecb2fac0023bcf6e175ee Mon Sep 17 00:00:00 2001 From: Yanzuo Liu Date: Thu, 18 Sep 2025 09:22:54 +0800 Subject: [PATCH 2/2] Fix comment, fix test, and avoid unnecessary IILE --- clang/lib/Sema/SemaLambda.cpp | 26 +++++++++++------------- clang/test/SemaCXX/warn-unused-value.cpp | 2 ++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index 2a6d0b5b4a9de..56af63d7eb35f 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -1973,7 +1973,7 @@ ExprResult Sema::ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body) { SYCL().CheckSYCLEntryPointFunctionDecl(LSI.CallOperator); // TODO: Find out if passing LSI.CallOperator->getDescribedFunctionTemplate() - // is necessary when it is a generic lambda. Are there any behaviour + // is better 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, @@ -2153,12 +2153,17 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, CleanupInfo LambdaCleanup = LSI->Cleanup; bool ContainsUnexpandedParameterPack = LSI->ContainsUnexpandedParameterPack; bool IsGenericLambda = Class->isGenericLambda(); + sema::AnalysisBasedWarnings::Policy WP = + AnalysisWarnings.getPolicyInEffectAt(EndLoc); CallOperator->setLexicalDeclContext(Class); - Decl *TemplateOrNonTemplateCallOperatorDecl = - CallOperator->getDescribedFunctionTemplate() - ? CallOperator->getDescribedFunctionTemplate() - : cast(CallOperator); + Decl *TemplateOrNonTemplateCallOperatorDecl = CallOperator; + sema::AnalysisBasedWarnings::Policy *ActivePolicy = ℘ + if (IsGenericLambda) { + TemplateOrNonTemplateCallOperatorDecl = + CallOperator->getDescribedFunctionTemplate(); + ActivePolicy = nullptr; + } // FIXME: Is this really the best choice? Keeping the lexical decl context // set as CurContext seems more faithful to the source. @@ -2168,15 +2173,8 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, // We cannot release LSI until we finish computing captures, which // requires the scope to be popped. - Sema::PoppedFunctionScopePtr _ = [&] { - if (LSI->CallOperator->getDescribedFunctionTemplate()) - return PopFunctionScopeInfo(/*WP=*/nullptr, - TemplateOrNonTemplateCallOperatorDecl); - - sema::AnalysisBasedWarnings::Policy WP = - AnalysisWarnings.getPolicyInEffectAt(EndLoc); - return PopFunctionScopeInfo(&WP, TemplateOrNonTemplateCallOperatorDecl); - }(); + Sema::PoppedFunctionScopePtr _ = + PopFunctionScopeInfo(ActivePolicy, 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 7a2cbab275e0c..5f77c52c8ddc2 100644 --- a/clang/test/SemaCXX/warn-unused-value.cpp +++ b/clang/test/SemaCXX/warn-unused-value.cpp @@ -179,6 +179,7 @@ auto b() { } // namespace test6 #endif +#if __cplusplus >= 201402L // ensure lambda in non-dependent context generate same diagnostics as function[ template] namespace lambda_in_non_dependent_context { void f1() { @@ -214,3 +215,4 @@ void f() { }; } } +#endif