From: Malcolm Parsons Date: Wed, 1 Mar 2017 10:23:38 +0000 (+0000) Subject: [Sema] Improve side effect checking for unused-lambda-capture warning X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=90dab93aeac98d94cd28cb0df975fe3d6919c1b0;p=clang [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 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@296602 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 3ab7c1b568..31db905de7 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -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); diff --git a/lib/Sema/SemaLambda.cpp b/lib/Sema/SemaLambda.cpp index c17f9fbd07..fc823f4aca 100644 --- a/lib/Sema/SemaLambda.cpp +++ b/lib/Sema/SemaLambda.cpp @@ -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'"; diff --git a/test/SemaCXX/warn-unused-lambda-capture.cpp b/test/SemaCXX/warn-unused-lambda-capture.cpp index f4b1c92637..48f8bfea7e 100644 --- a/test/SemaCXX/warn-unused-lambda-capture.cpp +++ b/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,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() {