Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -5327,6 +5327,9 @@ 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); Index: lib/Sema/SemaLambda.cpp =================================================================== --- lib/Sema/SemaLambda.cpp +++ lib/Sema/SemaLambda.cpp @@ -1438,13 +1438,30 @@ 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 Type *T = (From.isThisCapture() ? getCurrentThisType()->getPointeeType() + : From.getCaptureType()) + ->getBaseElementTypeUnsafe(); + if (const CXXRecordDecl *RD = T->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'"; Index: test/SemaCXX/warn-unused-lambda-capture.cpp =================================================================== --- test/SemaCXX/warn-unused-lambda-capture.cpp +++ test/SemaCXX/warn-unused-lambda-capture.cpp @@ -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,64 @@ 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] {}; } -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 +163,18 @@ 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] {}; } void test_use_template() {