r253630 - [analyzer] DeadStoresChecker: Treat locals captured by reference in C++ lambdas as escaped.

Devin Coughlin via cfe-commits cfe-commits at lists.llvm.org
Thu Nov 19 17:53:45 PST 2015


Author: dcoughlin
Date: Thu Nov 19 19:53:44 2015
New Revision: 253630

URL: http://llvm.org/viewvc/llvm-project?rev=253630&view=rev
Log:
[analyzer] DeadStoresChecker: Treat locals captured by reference in C++ lambdas as escaped.

The analyzer currently reports dead store false positives when a local variable
is captured by reference in a C++ lambda.

For example:

  int local = 0; auto lambda = [&local]() {
    local++;
  };
  local = 7; // False Positive: Value stored to 'local' is never read
  lambda();

In this case, the assignment setting `local` to 7 is not a dead store because
the called lambda will later read that assigned value.

This commit silences this source of false positives by treating locals captured
by reference in C++ lambdas as escaped, similarly to how the DeadStoresChecker
deals with locals whose address is taken.

rdar://problem/22165179

Modified:
    cfe/trunk/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
    cfe/trunk/test/Analysis/lambdas.cpp

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp?rev=253630&r1=253629&r2=253630&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp Thu Nov 19 19:53:44 2015
@@ -401,6 +401,11 @@ public:
     // Check for '&'. Any VarDecl whose address has been taken we treat as
     // escaped.
     // FIXME: What about references?
+    if (auto *LE = dyn_cast<LambdaExpr>(S)) {
+      findLambdaReferenceCaptures(LE);
+      return;
+    }
+
     const UnaryOperator *U = dyn_cast<UnaryOperator>(S);
     if (!U)
       return;
@@ -412,6 +417,28 @@ public:
       if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
         Escaped.insert(VD);
   }
+
+  // Treat local variables captured by reference in C++ lambdas as escaped.
+  void findLambdaReferenceCaptures(const LambdaExpr *LE)  {
+    const CXXRecordDecl *LambdaClass = LE->getLambdaClass();
+    llvm::DenseMap<const VarDecl *, FieldDecl *> CaptureFields;
+    FieldDecl *ThisCaptureField;
+    LambdaClass->getCaptureFields(CaptureFields, ThisCaptureField);
+
+    for (const LambdaCapture &C : LE->captures()) {
+      if (!C.capturesVariable())
+        continue;
+
+      VarDecl *VD = C.getCapturedVar();
+      const FieldDecl *FD = CaptureFields[VD];
+      if (!FD)
+        continue;
+
+      // If the capture field is a reference type, it is capture-by-reference.
+      if (FD->getType()->isReferenceType())
+        Escaped.insert(VD);
+    }
+  }
 };
 } // end anonymous namespace
 

Modified: cfe/trunk/test/Analysis/lambdas.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/lambdas.cpp?rev=253630&r1=253629&r2=253630&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/lambdas.cpp (original)
+++ cfe/trunk/test/Analysis/lambdas.cpp Thu Nov 19 19:53:44 2015
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-config inline-lambdas=true -verify %s 
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -analyze -analyzer-checker=core,deadcode,debug.ExprInspection -analyzer-config inline-lambdas=true -verify %s
 // RUN: %clang_cc1 -std=c++11 -fsyntax-only -analyze -analyzer-checker=core,debug.DumpCFG -analyzer-config inline-lambdas=true %s > %t 2>&1
 // RUN: FileCheck --input-file=%t %s
 
@@ -281,6 +281,49 @@ void captureStructReference(const Struct
   }();
 }
 
+// Lambda capture counts as use for dead-store checking.
+
+int returnsValue();
+
+void captureByCopyCausesUse() {
+  int local1 = returnsValue(); // no-warning
+  int local2 = returnsValue(); // no-warning
+  int local3 = returnsValue(); // expected-warning{{Value stored to 'local3' during its initialization is never read}}
+
+  (void)[local1, local2]() { }; // Explicit capture by copy counts as use.
+
+  int local4 = returnsValue(); // no-warning
+  int local5 = returnsValue(); // expected-warning{{Value stored to 'local5' during its initialization is never read}}
+
+  (void)[=]() {
+    (void)local4; // Implicit capture by copy counts as use
+  };
+}
+
+void captureByReference() {
+  int local1 = returnsValue(); // no-warning
+
+  auto lambda1 = [&local1]() { // Explicit capture by reference
+    local1++;
+  };
+
+  // Don't treat as a dead store because local1 was was captured by reference.
+  local1 = 7; // no-warning
+
+  lambda1();
+
+  int local2 = returnsValue(); // no-warning
+
+  auto lambda2 = [&]() {
+    local2++; // Implicit capture by reference
+  };
+
+  // Don't treat as a dead store because local2 was was captured by reference.
+  local2 = 7; // no-warning
+
+  lambda2();
+}
+
 
 // CHECK: [B2 (ENTRY)]
 // CHECK:   Succs (1): B1




More information about the cfe-commits mailing list