[Sema] Improve side effect checking for unused-lambda-capture warning

Summary:
Don't warn about unused lambda captures that involve copying a
value of a type that cannot be trivially copied and destroyed.

Fixes PR31977

Reviewers: rsmith, aaron.ballman

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D30327

llvm-svn: 296602
This commit is contained in:
Malcolm Parsons 2017-03-01 10:23:38 +00:00
parent 9b802e4650
commit ded2306208
3 changed files with 104 additions and 5 deletions

View File

@ -5340,6 +5340,9 @@ public:
ExprResult ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body,
Scope *CurScope);
/// \brief Does copying/destroying the captured variable have side effects?
bool CaptureHasSideEffects(const sema::LambdaScopeInfo::Capture &From);
/// \brief Diagnose if an explicit lambda capture is unused.
void DiagnoseUnusedLambdaCapture(const sema::LambdaScopeInfo::Capture &From);

View File

@ -1438,13 +1438,35 @@ mapImplicitCaptureStyle(CapturingScopeInfo::ImplicitCaptureStyle ICS) {
llvm_unreachable("Unknown implicit capture style");
}
void Sema::DiagnoseUnusedLambdaCapture(const LambdaScopeInfo::Capture &From) {
bool Sema::CaptureHasSideEffects(const LambdaScopeInfo::Capture &From) {
if (!From.isVLATypeCapture()) {
Expr *Init = From.getInitExpr();
if (Init && Init->HasSideEffects(Context))
return;
return true;
}
if (!From.isCopyCapture())
return false;
const QualType T = From.isThisCapture()
? getCurrentThisType()->getPointeeType()
: From.getCaptureType();
if (T.isVolatileQualified())
return true;
const Type *BaseT = T->getBaseElementTypeUnsafe();
if (const CXXRecordDecl *RD = BaseT->getAsCXXRecordDecl())
return !RD->isCompleteDefinition() || !RD->hasTrivialCopyConstructor() ||
!RD->hasTrivialDestructor();
return false;
}
void Sema::DiagnoseUnusedLambdaCapture(const LambdaScopeInfo::Capture &From) {
if (CaptureHasSideEffects(From))
return;
auto diag = Diag(From.getLocation(), diag::warn_unused_lambda_capture);
if (From.isThisCapture())
diag << "'this'";

View File

@ -1,10 +1,16 @@
// RUN: %clang_cc1 -fsyntax-only -Wunused-lambda-capture -Wused-but-marked-unused -Wno-uninitialized -verify -std=c++14 %s
// RUN: %clang_cc1 -fsyntax-only -Wunused-lambda-capture -Wused-but-marked-unused -Wno-uninitialized -verify -std=c++1z %s
class NonTrivialConstructor {
public:
NonTrivialConstructor() {}
};
class NonTrivialCopyConstructor {
public:
NonTrivialCopyConstructor() = default;
NonTrivialCopyConstructor(const NonTrivialCopyConstructor &) {}
};
class NonTrivialDestructor {
public:
~NonTrivialDestructor() {}
@ -57,14 +63,67 @@ void test() {
auto explicit_by_value_used = [i] { return i + 1; };
auto explicit_by_value_unused = [i] {}; // expected-warning{{lambda capture 'i' is not used}}
};
Trivial trivial;
auto explicit_by_value_trivial = [trivial] {}; // expected-warning{{lambda capture 'trivial' is not used}}
NonTrivialConstructor cons;
auto explicit_by_value_non_trivial_constructor = [cons] {}; // expected-warning{{lambda capture 'cons' is not used}}
NonTrivialCopyConstructor copy_cons;
auto explicit_by_value_non_trivial_copy_constructor = [copy_cons] {};
NonTrivialDestructor dest;
auto explicit_by_value_non_trivial_destructor = [dest] {};
volatile int v;
auto explicit_by_value_volatile = [v] {};
}
class Foo
{
class TrivialThis : Trivial {
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}}
auto explicit_star_this_used = [*this] { return i; };
auto explicit_star_this_used_void = [*this] { (void)this; };
auto explicit_star_this_unused = [*this] {}; // expected-warning{{lambda capture 'this' is not used}}
}
int i;
};
class NonTrivialConstructorThis : NonTrivialConstructor {
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}}
auto explicit_star_this_used = [*this] { return i; };
auto explicit_star_this_used_void = [*this] { (void)this; };
auto explicit_star_this_unused = [*this] {}; // expected-warning{{lambda capture 'this' is not used}}
}
int i;
};
class NonTrivialCopyConstructorThis : NonTrivialCopyConstructor {
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}}
auto explicit_star_this_used = [*this] { return i; };
auto explicit_star_this_used_void = [*this] { (void)this; };
auto explicit_star_this_unused = [*this] {};
}
int i;
};
class NonTrivialDestructorThis : NonTrivialDestructor {
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}}
auto explicit_star_this_used = [*this] { return i; };
auto explicit_star_this_used_void = [*this] { (void)this; };
auto explicit_star_this_unused = [*this] {};
}
int i;
};
@ -107,6 +166,21 @@ void test_templated() {
auto explicit_by_value_used = [i] { return i + 1; };
auto explicit_by_value_unused = [i] {}; // expected-warning{{lambda capture 'i' is not used}}
};
Trivial trivial;
auto explicit_by_value_trivial = [trivial] {}; // expected-warning{{lambda capture 'trivial' is not used}}
NonTrivialConstructor cons;
auto explicit_by_value_non_trivial_constructor = [cons] {}; // expected-warning{{lambda capture 'cons' is not used}}
NonTrivialCopyConstructor copy_cons;
auto explicit_by_value_non_trivial_copy_constructor = [copy_cons] {};
NonTrivialDestructor dest;
auto explicit_by_value_non_trivial_destructor = [dest] {};
volatile int v;
auto explicit_by_value_volatile = [v] {};
}
void test_use_template() {