summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/Basic/DiagnosticGroups.td6
-rw-r--r--clang/include/clang/Basic/DiagnosticSemaKinds.td3
-rw-r--r--clang/include/clang/Sema/ScopeInfo.h17
-rw-r--r--clang/include/clang/Sema/Sema.h3
-rw-r--r--clang/lib/Sema/SemaExpr.cpp4
-rw-r--r--clang/lib/Sema/SemaExprCXX.cpp1
-rw-r--r--clang/lib/Sema/SemaLambda.cpp23
-rw-r--r--clang/test/CXX/expr/expr.prim/expr.prim.lambda/p12.cpp2
-rw-r--r--clang/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp2
-rw-r--r--clang/test/CXX/expr/expr.prim/expr.prim.lambda/p16.cpp2
-rw-r--r--clang/test/CXX/expr/expr.prim/expr.prim.lambda/p18.cpp2
-rw-r--r--clang/test/CXX/expr/expr.prim/expr.prim.lambda/p19.cpp2
-rw-r--r--clang/test/SemaCXX/uninitialized.cpp2
-rw-r--r--clang/test/SemaCXX/warn-unused-lambda-capture.cpp110
14 files changed, 166 insertions, 13 deletions
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 4173d03de9f..5d314498ddc 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -480,6 +480,7 @@ def UnusedFunction : DiagGroup<"unused-function", [UnneededInternalDecl]>;
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 @@ def Unused : DiagGroup<"unused",
[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.
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 23d61c0848b..a6552cb233c 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -316,6 +316,9 @@ def warn_unneeded_member_function : Warning<
InGroup<UnneededMemberFunction>, DefaultIgnore;
def warn_unused_private_field: Warning<"private field %0 is not used">,
InGroup<UnusedPrivateField>, 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<UnusedLambdaCapture>, DefaultIgnore;
def warn_parameter_size: Warning<
"%0 is a large (%1 bytes) pass-by-value argument; "
diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h
index 4b54807ab66..29ae08470fc 100644
--- a/clang/include/clang/Sema/ScopeInfo.h
+++ b/clang/include/clang/Sema/ScopeInfo.h
@@ -452,6 +452,14 @@ public:
/// 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 @@ public:
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 @@ public:
: 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 @@ public:
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();
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index c180a8ea3ee..6ac02be697b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -5323,6 +5323,9 @@ public:
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,
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index d62e8fd68b6..7b404f40a76 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -13916,8 +13916,10 @@ bool Sema::tryCaptureVariable(
// 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
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index b2fb33f5343..66bdf1a2da8 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1106,6 +1106,7 @@ bool Sema::CheckCXXThisCapture(SourceLocation Loc, const bool Explicit,
dyn_cast<CapturingScopeInfo>(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<LambdaScopeInfo>(CSI);
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index a0d574915eb..da2768f2131 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -1384,7 +1384,7 @@ static void addBlockPointerConversion(Sema &S,
}
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 @@ mapImplicitCaptureStyle(CapturingScopeInfo::ImplicitCaptureStyle ICS) {
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 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
// 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(
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p12.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p12.cpp
index 4a2a4f3d735..e7fce11abc5 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p12.cpp
+++ b/clang/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;
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp
index 8bb707e0dbc..b55beb7d4ed 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp
+++ b/clang/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;
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p16.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p16.cpp
index 94f8111015a..905192ff830 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p16.cpp
+++ b/clang/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 {
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p18.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p18.cpp
index 93c2805497f..72cf93be519 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p18.cpp
+++ b/clang/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<typename T, typename U>
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p19.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p19.cpp
index 1dbcbf49803..a8b40249f0f 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/p19.cpp
+++ b/clang/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&&);
diff --git a/clang/test/SemaCXX/uninitialized.cpp b/clang/test/SemaCXX/uninitialized.cpp
index 5769a0c028c..51a2d687205 100644
--- a/clang/test/SemaCXX/uninitialized.cpp
+++ b/clang/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 {
diff --git a/clang/test/SemaCXX/warn-unused-lambda-capture.cpp b/clang/test/SemaCXX/warn-unused-lambda-capture.cpp
new file mode 100644
index 00000000000..18c5d6483d2
--- /dev/null
+++ b/clang/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 <typename T>
+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<int>(); // expected-note{{in instantiation of function template specialization 'test_templated<int>' requested here}}
+}
OpenPOWER on IntegriCloud