Index: cfe/trunk/include/clang/Basic/DiagnosticGroups.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticGroups.td +++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td @@ -480,6 +480,7 @@ def UnusedMemberFunction : DiagGroup<"unused-member-function", [UnneededMemberFunction]>; def UnusedLabel : DiagGroup<"unused-label">; +def UnusedLambdaCapture : DiagGroup<"unused-lambda-capture">; def UnusedParameter : DiagGroup<"unused-parameter">; def UnusedResult : DiagGroup<"unused-result">; def PotentiallyEvaluatedExpression : DiagGroup<"potentially-evaluated-expression">; @@ -617,8 +618,9 @@ [UnusedArgument, UnusedFunction, UnusedLabel, // UnusedParameter, (matches GCC's behavior) // UnusedMemberFunction, (clean-up llvm before enabling) - UnusedPrivateField, UnusedLocalTypedef, - UnusedValue, UnusedVariable, UnusedPropertyIvar]>, + UnusedPrivateField, UnusedLambdaCapture, + UnusedLocalTypedef, UnusedValue, UnusedVariable, + UnusedPropertyIvar]>, DiagCategory<"Unused Entity Issue">; // Format settings. Index: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td @@ -316,6 +316,9 @@ InGroup, DefaultIgnore; def warn_unused_private_field: Warning<"private field %0 is not used">, InGroup, DefaultIgnore; +def warn_unused_lambda_capture: Warning<"lambda capture %0 is not " + "%select{used|required to be captured for use in an unevaluated context}1">, + InGroup, DefaultIgnore; def warn_parameter_size: Warning< "%0 is a large (%1 bytes) pass-by-value argument; " Index: cfe/trunk/include/clang/Sema/ScopeInfo.h =================================================================== --- cfe/trunk/include/clang/Sema/ScopeInfo.h +++ cfe/trunk/include/clang/Sema/ScopeInfo.h @@ -452,6 +452,14 @@ /// non-static data member that would hold the capture. QualType CaptureType; + /// \brief Whether an explicit capture has been odr-used in the body of the + /// lambda. + bool ODRUsed; + + /// \brief Whether an explicit capture has been non-odr-used in the body of + /// the lambda. + bool NonODRUsed; + public: Capture(VarDecl *Var, bool Block, bool ByRef, bool IsNested, SourceLocation Loc, SourceLocation EllipsisLoc, @@ -460,7 +468,8 @@ InitExprAndCaptureKind( Cpy, !Var ? Cap_VLA : Block ? Cap_Block : ByRef ? Cap_ByRef : Cap_ByCopy), - Loc(Loc), EllipsisLoc(EllipsisLoc), CaptureType(CaptureType) {} + Loc(Loc), EllipsisLoc(EllipsisLoc), CaptureType(CaptureType), + ODRUsed(false), NonODRUsed(false) {} enum IsThisCapture { ThisCapture }; Capture(IsThisCapture, bool IsNested, SourceLocation Loc, @@ -468,7 +477,8 @@ : VarAndNestedAndThis( nullptr, (IsThisCaptured | (IsNested ? IsNestedCapture : 0))), InitExprAndCaptureKind(Cpy, ByCopy ? Cap_ByCopy : Cap_ByRef), - Loc(Loc), EllipsisLoc(), CaptureType(CaptureType) {} + Loc(Loc), EllipsisLoc(), CaptureType(CaptureType), ODRUsed(false), + NonODRUsed(false) {} bool isThisCapture() const { return VarAndNestedAndThis.getInt() & IsThisCaptured; @@ -491,6 +501,9 @@ bool isNested() const { return VarAndNestedAndThis.getInt() & IsNestedCapture; } + bool isODRUsed() const { return ODRUsed; } + bool isNonODRUsed() const { return NonODRUsed; } + void markUsed(bool IsODRUse) { (IsODRUse ? ODRUsed : NonODRUsed) = true; } VarDecl *getVariable() const { return VarAndNestedAndThis.getPointer(); Index: cfe/trunk/include/clang/Sema/Sema.h =================================================================== --- cfe/trunk/include/clang/Sema/Sema.h +++ cfe/trunk/include/clang/Sema/Sema.h @@ -5323,6 +5323,9 @@ ExprResult ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body, Scope *CurScope); + /// \brief Diagnose if an explicit lambda capture is unused. + void DiagnoseUnusedLambdaCapture(const sema::LambdaScopeInfo::Capture &From); + /// \brief Complete a lambda-expression having processed and attached the /// lambda body. ExprResult BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc, Index: cfe/trunk/lib/Sema/SemaExpr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaExpr.cpp +++ cfe/trunk/lib/Sema/SemaExpr.cpp @@ -13916,8 +13916,10 @@ // Check whether we've already captured it. if (isVariableAlreadyCapturedInScopeInfo(CSI, Var, Nested, CaptureType, - DeclRefType)) + DeclRefType)) { + CSI->getCapture(Var).markUsed(BuildAndDiagnose); break; + } // If we are instantiating a generic lambda call operator body, // we do not want to capture new variables. What was captured // during either a lambdas transformation or initial parsing Index: cfe/trunk/lib/Sema/SemaExprCXX.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaExprCXX.cpp +++ cfe/trunk/lib/Sema/SemaExprCXX.cpp @@ -1106,6 +1106,7 @@ dyn_cast(FunctionScopes[idx])) { if (CSI->CXXThisCaptureIndex != 0) { // 'this' is already being captured; there isn't anything more to do. + CSI->Captures[CSI->CXXThisCaptureIndex - 1].markUsed(BuildAndDiagnose); break; } LambdaScopeInfo *LSI = dyn_cast(CSI); Index: cfe/trunk/lib/Sema/SemaLambda.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaLambda.cpp +++ cfe/trunk/lib/Sema/SemaLambda.cpp @@ -1384,7 +1384,7 @@ } static ExprResult performLambdaVarCaptureInitialization( - Sema &S, LambdaScopeInfo::Capture &Capture, FieldDecl *Field) { + Sema &S, const LambdaScopeInfo::Capture &Capture, FieldDecl *Field) { assert(Capture.isVariableCapture() && "not a variable capture"); auto *Var = Capture.getVariable(); @@ -1438,6 +1438,21 @@ llvm_unreachable("Unknown implicit capture style"); } +void Sema::DiagnoseUnusedLambdaCapture(const LambdaScopeInfo::Capture &From) { + if (!From.isVLATypeCapture()) { + Expr *Init = From.getInitExpr(); + if (Init && Init->HasSideEffects(Context)) + return; + } + + auto diag = Diag(From.getLocation(), diag::warn_unused_lambda_capture); + if (From.isThisCapture()) + diag << "'this'"; + else + diag << From.getVariable(); + diag << From.isNonODRUsed(); +} + ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc, LambdaScopeInfo *LSI) { // Collect information from the lambda scope. @@ -1476,10 +1491,14 @@ // Translate captures. auto CurField = Class->field_begin(); for (unsigned I = 0, N = LSI->Captures.size(); I != N; ++I, ++CurField) { - LambdaScopeInfo::Capture From = LSI->Captures[I]; + const LambdaScopeInfo::Capture &From = LSI->Captures[I]; assert(!From.isBlockCapture() && "Cannot capture __block variables"); bool IsImplicit = I >= LSI->NumExplicitCaptures; + // Warn about unused explicit captures. + if (!CurContext->isDependentContext() && !IsImplicit && !From.isODRUsed()) + DiagnoseUnusedLambdaCapture(From); + // Handle 'this' capture. if (From.isThisCapture()) { Captures.push_back( Index: cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p12.cpp =================================================================== --- cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p12.cpp +++ cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p12.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++11 %s -Wunused -verify +// RUN: %clang_cc1 -std=c++11 %s -Wunused -Wno-unused-lambda-capture -verify void odr_used() { int i = 17; Index: cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp =================================================================== --- cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp +++ cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++11 %s -Wunused -verify +// RUN: %clang_cc1 -std=c++11 %s -Wunused -Wno-unused-lambda-capture -verify void f2() { int i = 1; Index: cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p16.cpp =================================================================== --- cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p16.cpp +++ cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p16.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++11 %s -Wunused -verify +// RUN: %clang_cc1 -std=c++11 %s -Wunused -Wno-unused-lambda-capture -verify struct X { Index: cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p18.cpp =================================================================== --- cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p18.cpp +++ cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p18.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++11 %s -Wunused -verify +// RUN: %clang_cc1 -std=c++11 %s -Wunused -Wno-unused-lambda-capture -verify // expected-no-diagnostics template Index: cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p19.cpp =================================================================== --- cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p19.cpp +++ cfe/trunk/test/CXX/expr/expr.prim/expr.prim.lambda/p19.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++11 %s -Wunused -verify +// RUN: %clang_cc1 -std=c++11 %s -Wunused -Wno-unused-lambda-capture -verify struct MoveOnly { MoveOnly(MoveOnly&&); Index: cfe/trunk/test/SemaCXX/uninitialized.cpp =================================================================== --- cfe/trunk/test/SemaCXX/uninitialized.cpp +++ cfe/trunk/test/SemaCXX/uninitialized.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -Wall -Wuninitialized -Wno-unused-value -std=c++11 -verify %s +// RUN: %clang_cc1 -fsyntax-only -Wall -Wuninitialized -Wno-unused-value -Wno-unused-lambda-capture -std=c++11 -verify %s // definitions for std::move namespace std { Index: cfe/trunk/test/SemaCXX/warn-unused-lambda-capture.cpp =================================================================== --- cfe/trunk/test/SemaCXX/warn-unused-lambda-capture.cpp +++ cfe/trunk/test/SemaCXX/warn-unused-lambda-capture.cpp @@ -0,0 +1,110 @@ +// RUN: %clang_cc1 -fsyntax-only -Wunused-lambda-capture -Wused-but-marked-unused -Wno-uninitialized -verify -std=c++14 %s + +class NonTrivialConstructor { +public: + NonTrivialConstructor() {} +}; + +class NonTrivialDestructor { +public: + ~NonTrivialDestructor() {} +}; + +class Trivial { +public: + Trivial() = default; + Trivial(int a) {} +}; + +int side_effect() { + return 42; +} + +void test() { + int i = 0; + + auto captures_nothing = [] {}; + + auto captures_nothing_by_value = [=] {}; + auto captures_nothing_by_reference = [&] {}; + + auto implicit_by_value = [=]() mutable { i++; }; + auto implicit_by_reference = [&] { i++; }; + + auto explicit_by_value_used = [i] { return i + 1; }; + auto explicit_by_value_used_void = [i] { (void)i; }; + auto explicit_by_value_unused = [i] {}; // expected-warning{{lambda capture 'i' is not used}} + auto explicit_by_value_unused_sizeof = [i] { return sizeof(i); }; // expected-warning{{lambda capture 'i' is not required to be captured for use in an unevaluated context}} + auto explicit_by_value_unused_decltype = [i] { decltype(i) j = 0; }; // expected-warning{{lambda capture 'i' is not required to be captured for use in an unevaluated context}} + + auto explicit_by_reference_used = [&i] { i++; }; + auto explicit_by_reference_unused = [&i] {}; // expected-warning{{lambda capture 'i' is not used}} + + auto explicit_initialized_reference_used = [&j = i] { return j + 1; }; + auto explicit_initialized_reference_unused = [&j = i]{}; // expected-warning{{lambda capture 'j' is not used}} + + auto explicit_initialized_value_used = [j = 1] { return j + 1; }; + auto explicit_initialized_value_unused = [j = 1] {}; // expected-warning{{lambda capture 'j' is not used}} + auto explicit_initialized_value_non_trivial_constructor = [j = NonTrivialConstructor()]{}; + auto explicit_initialized_value_non_trivial_destructor = [j = NonTrivialDestructor()]{}; + auto explicit_initialized_value_trivial_init = [j = Trivial()]{}; // expected-warning{{lambda capture 'j' is not used}} + auto explicit_initialized_value_non_trivial_init = [j = Trivial(42)]{}; + auto explicit_initialized_value_with_side_effect = [j = side_effect()]{}; + + auto nested = [&i] { + auto explicit_by_value_used = [i] { return i + 1; }; + auto explicit_by_value_unused = [i] {}; // expected-warning{{lambda capture 'i' is not used}} + }; +} + +class Foo +{ + void test() { + auto explicit_this_used = [this] { return i; }; + auto explicit_this_used_void = [this] { (void)this; }; + auto explicit_this_unused = [this] {}; // expected-warning{{lambda capture 'this' is not used}} + } + int i; +}; + +template +void test_templated() { + int i = 0; + + auto captures_nothing = [] {}; + + auto captures_nothing_by_value = [=] {}; + auto captures_nothing_by_reference = [&] {}; + + auto implicit_by_value = [=]() mutable { i++; }; + auto implicit_by_reference = [&] { i++; }; + + auto explicit_by_value_used = [i] { return i + 1; }; + auto explicit_by_value_used_void = [i] { (void)i; }; + auto explicit_by_value_unused = [i] {}; // expected-warning{{lambda capture 'i' is not used}} + auto explicit_by_value_unused_sizeof = [i] { return sizeof(i); }; // expected-warning{{lambda capture 'i' is not required to be captured for use in an unevaluated context}} + auto explicit_by_value_unused_decltype = [i] { decltype(i) j = 0; }; // expected-warning{{lambda capture 'i' is not used}} + + auto explicit_by_reference_used = [&i] { i++; }; + auto explicit_by_reference_unused = [&i] {}; // expected-warning{{lambda capture 'i' is not used}} + + auto explicit_initialized_reference_used = [&j = i] { return j + 1; }; + auto explicit_initialized_reference_unused = [&j = i]{}; // expected-warning{{lambda capture 'j' is not used}} + + auto explicit_initialized_value_used = [j = 1] { return j + 1; }; + auto explicit_initialized_value_unused = [j = 1] {}; // expected-warning{{lambda capture 'j' is not used}} + auto explicit_initialized_value_non_trivial_constructor = [j = NonTrivialConstructor()]{}; + auto explicit_initialized_value_non_trivial_destructor = [j = NonTrivialDestructor()]{}; + auto explicit_initialized_value_trivial_init = [j = Trivial()]{}; // expected-warning{{lambda capture 'j' is not used}} + auto explicit_initialized_value_non_trivial_init = [j = Trivial(42)]{}; + auto explicit_initialized_value_with_side_effect = [j = side_effect()]{}; + + auto nested = [&i] { + auto explicit_by_value_used = [i] { return i + 1; }; + auto explicit_by_value_unused = [i] {}; // expected-warning{{lambda capture 'i' is not used}} + }; +} + +void test_use_template() { + test_templated(); // expected-note{{in instantiation of function template specialization 'test_templated' requested here}} +}