Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
----------------------------------

Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
22 changes: 15 additions & 7 deletions clang/lib/Sema/SemaLambda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 better when it is a generic lambda. Are there any behaviour
// changes? `FunctionTemplateDecl` is always passed when handling simple
// function templates.
Comment on lines +1975 to +1978
Copy link
Contributor

Choose a reason for hiding this comment

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

This comment is still here; I'd like to suggest we remove it because it doesn't look very clear: what behavior changes are we talking about/which function that Decl is passed to?

ActOnFinishFunctionBody(LSI.CallOperator, Body, /*IsInstantiation=*/false,
/*RetainFunctionScopeInfo=*/true);

Expand Down Expand Up @@ -2149,24 +2153,28 @@ 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<Decl>(CallOperator);
Comment on lines -2154 to -2157
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you can leave it as-is? That way you can

sema::AnalysisBasedWarnings::Policy *ActivePolicy = IsGenericLambda ? nullptr : &WP;
Sema::PoppedFunctionScopePtr _ =
      PopFunctionScopeInfo(ActivePolicy, TemplateOrNonTemplateCallOperatorDecl);

Though I have no idea why LSI->CallOperator doesn't work when it's a template.

Decl *TemplateOrNonTemplateCallOperatorDecl = CallOperator;
sema::AnalysisBasedWarnings::Policy *ActivePolicy = &WP;
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.
TemplateOrNonTemplateCallOperatorDecl->setLexicalDeclContext(Class);

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 _ =
PopFunctionScopeInfo(ActivePolicy, TemplateOrNonTemplateCallOperatorDecl);

// True if the current capture has a used capture or default before it.
bool CurHasPreviousCapture = CaptureDefault != LCD_None;
Expand Down
38 changes: 38 additions & 0 deletions clang/test/SemaCXX/warn-unused-value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,41 @@ 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() {
0, 0; // expected-warning {{left operand of comma operator has no effect}}
return;
0, 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why don't we have warnings for non-generic lambdas? Is that pre-existing issue?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We have warnings controlled by -Wunreachable-code but it isn't enabled by default.

Copy link
Contributor

@zyn0217 zyn0217 Sep 18, 2025

Choose a reason for hiding this comment

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

But with your patch, we do have after-return warnings surfaced for generic-lambdas.

This makes us inconsistent.

Edit: I see we are already inconsistent with non-templates: https://gcc.godbolt.org/z/PsqYTrMEv

Does -Wunreachable-code change anything? The warnings are still not very great to me.

}
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}}
};
}
}
#endif