Adapt now working cfg tests.
Handle more cases for lifetime extension.
Fix bug in materialize-temporary handling.

http://reviews.llvm.org/D4706

Files:
  lib/Analysis/CFG.cpp
  test/Analysis/auto-obj-dtors-cfg-output.cpp
  test/Analysis/cfg.cpp
  test/Analysis/temp-obj-dtors-cfg-output.cpp
Index: lib/Analysis/CFG.cpp
===================================================================
--- lib/Analysis/CFG.cpp
+++ lib/Analysis/CFG.cpp
@@ -435,6 +435,9 @@
     return Visit(S, AddStmtChoice::AlwaysAdd);
   }
   CFGBlock *addInitializer(CXXCtorInitializer *I);
+  void addAutomaticObjDtorsForLifetimeExtendedTemporaries(
+      Stmt *E, bool LifetimeExtended = false);
+  void addAutomaticObjDtorsForVarDecl(VarDecl *VD, Stmt* S);
   void addAutomaticObjDtors(LocalScope::const_iterator B,
                             LocalScope::const_iterator E, Stmt *S);
   void addImplicitDtorsForDestructor(const CXXDestructorDecl *DD);
@@ -445,6 +448,9 @@
   void addLocalScopeForStmt(Stmt *S);
   LocalScope* addLocalScopeForDeclStmt(DeclStmt *DS,
                                        LocalScope* Scope = nullptr);
+  bool needsLocalScopeForLifetimeExtendedTemporary(const Stmt* E);
+  bool needsLocalScopeForVarDecl(const VarDecl* VD);
+  bool needsLocalScopeForType(QualType QT);
   LocalScope* addLocalScopeForVarDecl(VarDecl *VD, LocalScope* Scope = nullptr);
 
   void addLocalScopeAndDtors(Stmt *S);
@@ -1029,52 +1035,87 @@
   return Block;
 }
 
-/// \brief Retrieve the type of the temporary object whose lifetime was 
-/// extended by a local reference with the given initializer.
-static QualType getReferenceInitTemporaryType(ASTContext &Context,
-                                              const Expr *Init) {
-  while (true) {
-    // Skip parentheses.
-    Init = Init->IgnoreParens();
-    
-    // Skip through cleanups.
-    if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Init)) {
-      Init = EWC->getSubExpr();
-      continue;
-    }
-    
-    // Skip through the temporary-materialization expression.
-    if (const MaterializeTemporaryExpr *MTE
-          = dyn_cast<MaterializeTemporaryExpr>(Init)) {
-      Init = MTE->GetTemporaryExpr();
-      continue;
+void CFGBuilder::addAutomaticObjDtorsForLifetimeExtendedTemporaries(
+    Stmt *E, bool LifetimeExtended) {
+  switch (E->getStmtClass()) {
+    // FIXME: What do we want to do for conditional operators and binary
+    // conditional operators?
+  case Stmt::MaterializeTemporaryExprClass: {
+    const MaterializeTemporaryExpr * MTE = cast<MaterializeTemporaryExpr>(E);
+    if (MTE->getStorageDuration() != SD_FullExpression) {
+      SmallVector<const Expr *, 2> CommaLHSs;
+      SmallVector<SubobjectAdjustment, 2> Adjustments;
+      assert(MTE->GetTemporaryExpr());
+      const Expr *ExtendedE =
+          MTE->GetTemporaryExpr()->skipRValueSubobjectAdjustments(CommaLHSs,
+                                                                  Adjustments);
+      addAutomaticObjDtorsForLifetimeExtendedTemporaries(
+          const_cast<Expr*>(ExtendedE), /*LifetimeExtended=*/true);
     }
-    
-    // Skip derived-to-base and no-op casts.
-    if (const CastExpr *CE = dyn_cast<CastExpr>(Init)) {
-      if ((CE->getCastKind() == CK_DerivedToBase ||
-           CE->getCastKind() == CK_UncheckedDerivedToBase ||
-           CE->getCastKind() == CK_NoOp) &&
-          Init->getType()->isRecordType()) {
-        Init = CE->getSubExpr();
-        continue;
+  } break;
+  case Stmt::CXXBindTemporaryExprClass:
+    // If the temporary is not lifetime extended, we'll handle it in
+    // VisitForTemporaryDtors instead.
+    if (LifetimeExtended) {
+      const CXXDestructorDecl *Dtor =
+          cast<CXXBindTemporaryExpr>(E)->getTemporary()->getDestructor();
+      if (Dtor->isNoReturn()) {
+        Block = createNoReturnBlock();
+      } else {
+        autoCreateBlock();
       }
+
+      appendTemporaryDtor(Block, cast<CXXBindTemporaryExpr>(E));
     }
-    
-    // Skip member accesses into rvalues.
-    if (const MemberExpr *ME = dyn_cast<MemberExpr>(Init)) {
-      if (!ME->isArrow() && ME->getBase()->isRValue()) {
-        Init = ME->getBase();
-        continue;
-      }
+    break;
+  case Stmt::ConditionalOperatorClass:
+  case Stmt::BinaryConditionalOperatorClass:
+    // FIXME: Implement. If both the true and false branch yield lifetime
+    // extended temporaries, we need to add a branch that triggers on which
+    // temporary constructor was executed.
+    break;
+  default:
+    LifetimeExtended = false;
+    // Fallthrough.
+  case Stmt::ParenExprClass:
+  case Stmt::CXXFunctionalCastExprClass:
+  case Stmt::ImplicitCastExprClass:
+  case Stmt::ExprWithCleanupsClass:
+  case Stmt::CXXDefaultArgExprClass:
+  case Stmt::CXXDefaultInitExprClass:
+    for (Stmt *Child : E->children()) {
+      if (Child)
+        addAutomaticObjDtorsForLifetimeExtendedTemporaries(Child,
+                                                           LifetimeExtended);
     }
-    
+    break;
+  case Stmt::BlockExprClass:
+  case Stmt::LambdaExprClass:
     break;
   }
+}
+
+void CFGBuilder::addAutomaticObjDtorsForVarDecl(VarDecl *VD,
+                                                Stmt *S) {
+  if (!VD->getType()->isReferenceType() &&
+      needsLocalScopeForType(VD->getType())) {
+    QualType QT = VD->getType();
+    QT = Context->getBaseElementType(QT);
+
+    const CXXDestructorDecl *Dtor = QT->getAsCXXRecordDecl()->getDestructor();
+    if (Dtor->isNoReturn())
+      Block = createNoReturnBlock();
+    else
+      autoCreateBlock();
 
-  return Init->getType();
+    appendAutomaticObjDtor(Block, VD, S);
+  }
+  if (const Expr *Init = VD->getInit()) {
+    addAutomaticObjDtorsForLifetimeExtendedTemporaries(
+        const_cast<Expr *>(Init));
+  }
 }
-  
+
 /// addAutomaticObjDtors - Add to current block automatic objects destructors
 /// for objects in range of local scope positions. Use S as trigger statement
 /// for destructors.
@@ -1098,22 +1139,7 @@
   for (SmallVectorImpl<VarDecl*>::reverse_iterator I = Decls.rbegin(),
                                                    E = Decls.rend();
        I != E; ++I) {
-    // If this destructor is marked as a no-return destructor, we need to
-    // create a new block for the destructor which does not have as a successor
-    // anything built thus far: control won't flow out of this block.
-    QualType Ty = (*I)->getType();
-    if (Ty->isReferenceType()) {
-      Ty = getReferenceInitTemporaryType(*Context, (*I)->getInit());
-    }
-    Ty = Context->getBaseElementType(Ty);
-
-    const CXXDestructorDecl *Dtor = Ty->getAsCXXRecordDecl()->getDestructor();
-    if (Dtor->isNoReturn())
-      Block = createNoReturnBlock();
-    else
-      autoCreateBlock();
-
-    appendAutomaticObjDtor(Block, *I, S);
+    addAutomaticObjDtorsForVarDecl(*I, S);
   }
 }
 
@@ -1211,6 +1237,58 @@
   return Scope;
 }
 
+bool CFGBuilder::needsLocalScopeForType(QualType QT) {
+  // Check for constant size array. Set type to array element type.
+  while (const ConstantArrayType *AT = Context->getAsConstantArrayType(QT)) {
+    if (AT->getSize() == 0)
+      return false;
+    QT = AT->getElementType();
+  }
+
+  // Check if type is a C++ class with non-trivial destructor.
+  if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl())
+    return !CD->hasTrivialDestructor();
+  return false;
+}
+
+bool CFGBuilder::needsLocalScopeForLifetimeExtendedTemporary(const Stmt* E) {
+  // FIXME: Handle cases where the scope would be introduced by a nested
+  // lambda or block.
+  switch (E->getStmtClass()) {
+  case Stmt::MaterializeTemporaryExprClass: {
+    const MaterializeTemporaryExpr * MTE = cast<MaterializeTemporaryExpr>(E);
+    if (MTE->getStorageDuration() != SD_FullExpression) {
+      SmallVector<const Expr *, 2> CommaLHSs;
+      SmallVector<SubobjectAdjustment, 2> Adjustments;
+      assert(MTE->GetTemporaryExpr());
+      const Expr *ExtendedE =
+          MTE->GetTemporaryExpr()->skipRValueSubobjectAdjustments(CommaLHSs,
+                                                                  Adjustments);
+      return needsLocalScopeForType(ExtendedE->getType()) ||
+             needsLocalScopeForLifetimeExtendedTemporary(ExtendedE);
+    }
+  } break;
+  default:
+    for (const Stmt *Child : E->children()) {
+      if (Child && needsLocalScopeForLifetimeExtendedTemporary(Child))
+        return true;
+    }
+    break;
+  }
+  return false;
+}
+
+bool CFGBuilder::needsLocalScopeForVarDecl(const VarDecl* VD) {
+  if (!VD->getType()->isReferenceType() &&
+      needsLocalScopeForType(VD->getType())) {
+    return true;
+  }
+  if (const Expr *Init = VD->getInit()) {
+    return needsLocalScopeForLifetimeExtendedTemporary(Init);
+  }
+  return false;
+}
+
 /// addLocalScopeForVarDecl - Add LocalScope for variable declaration. It will
 /// create add scope for automatic objects and temporary objects bound to
 /// const reference. Will reuse Scope if not NULL.
@@ -1228,43 +1306,11 @@
   default: return Scope;
   }
 
-  // Check for const references bound to temporary. Set type to pointee.
-  QualType QT = VD->getType();
-  if (QT.getTypePtr()->isReferenceType()) {
-    // Attempt to determine whether this declaration lifetime-extends a
-    // temporary.
-    //
-    // FIXME: This is incorrect. Non-reference declarations can lifetime-extend
-    // temporaries, and a single declaration can extend multiple temporaries.
-    // We should look at the storage duration on each nested
-    // MaterializeTemporaryExpr instead.
-    const Expr *Init = VD->getInit();
-    if (!Init)
-      return Scope;
-    if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Init))
-      Init = EWC->getSubExpr();
-    if (!isa<MaterializeTemporaryExpr>(Init))
-      return Scope;
-
-    // Lifetime-extending a temporary.
-    QT = getReferenceInitTemporaryType(*Context, Init);
-  }
-
-  // Check for constant size array. Set type to array element type.
-  while (const ConstantArrayType *AT = Context->getAsConstantArrayType(QT)) {
-    if (AT->getSize() == 0)
-      return Scope;
-    QT = AT->getElementType();
-  }
-
-  // Check if type is a C++ class with non-trivial destructor.
-  if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl())
-    if (!CD->hasTrivialDestructor()) {
-      // Add the variable to scope
-      Scope = createOrReuseLocalScope(Scope);
-      Scope->addVar(VD);
-      ScopePos = Scope->begin();
-    }
+  if (!needsLocalScopeForVarDecl(VD))
+    return Scope;
+  Scope = createOrReuseLocalScope(Scope);
+  Scope->addVar(VD);
+  ScopePos = Scope->begin();
   return Scope;
 }
 
Index: test/Analysis/auto-obj-dtors-cfg-output.cpp
===================================================================
--- test/Analysis/auto-obj-dtors-cfg-output.cpp
+++ test/Analysis/auto-obj-dtors-cfg-output.cpp
@@ -42,7 +42,7 @@
 // CHECK-NEXT:   8: [B1.7] (ImplicitCastExpr, NoOp, const class A)
 // CHECK-NEXT:   9: [B1.8]
 // CHECK:       10: const A &c = A();
-// CHECK:       11: [B1.10].~A() (Implicit destructor)
+// CHECK:       11: ~A()
 // CHECK:       12: [B1.2].~A() (Implicit destructor)
 // CHECK-NEXT:   Preds (1): B2
 // CHECK-NEXT:   Succs (1): B0
Index: test/Analysis/cfg.cpp
===================================================================
--- test/Analysis/cfg.cpp
+++ test/Analysis/cfg.cpp
@@ -1,6 +1,8 @@
 // RUN: %clang_cc1 -analyze -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -analyzer-config cfg-temporary-dtors=true -std=c++11 %s > %t 2>&1
 // RUN: FileCheck --input-file=%t %s
 
+#include "Inputs/system-header-simulator-cxx.h"
+
 // CHECK-LABEL: void checkWrap(int i)
 // CHECK: ENTRY
 // CHECK-NEXT: Succs (1): B1
@@ -376,10 +378,9 @@
   MyClass* obj = new (buffer) MyClass[5];
 }
 
-
 // CHECK-LABEL: void test_lifetime_extended_temporaries()
 // CHECK: [B1]
-struct LifetimeExtend { LifetimeExtend(int); ~LifetimeExtend(); };
+struct LifetimeExtend { LifetimeExtend(int); ~LifetimeExtend(); int i; };
 struct Aggregate { const LifetimeExtend a; const LifetimeExtend b; };
 struct AggregateRef { const LifetimeExtend &a; const LifetimeExtend &b; };
 void test_lifetime_extended_temporaries() {
@@ -420,15 +421,36 @@
   }
   // CHECK: LifetimeExtend(5)
   // CHECK-NEXT: : 5
-  // FIXME: We want to emit the destructors of the lifetime
-  // extended variables here.
+  // CHECK-NEXT: ~LifetimeExtend()
+  // CHECK-NEXT: ~LifetimeExtend()
   // CHECK-NOT: ~LifetimeExtend()
   {
     AggregateRef a{LifetimeExtend(5), LifetimeExtend(5)};
     5;
   }
-  // FIXME: Add tests for lifetime extension via subobject
-  // references (LifetimeExtend().some_member).
+  // CHECK: LifetimeExtend(6)
+  // CHECK-NEXT: : 6
+  // CHECK-NEXT: ~LifetimeExtend()
+  // CHECK-NOT: ~LifetimeExtend()
+  {
+    const int &i = LifetimeExtend(6).i;
+    6;
+  }
+}
+
+
+// CHECK-LABEL: void test_lifetime_extended_temporaries_for_range()
+void test_lifetime_extended_temporaries_for_range() {
+  // CHECK: [B1]
+  // CHECK-NEXT: ~const LifetimeExtend [1]()
+  // CHECK-NEXT: Preds (1): B2
+  // CHECK: auto &&__range = { LifetimeExtend(7) };
+  // CHECK-NEXT: ~LifetimeExtend() (Temporary object destructor)
+  {
+    for (LifetimeExtend e : {LifetimeExtend(7)}) {
+      1;
+    }
+  }
 }
 
 
Index: test/Analysis/temp-obj-dtors-cfg-output.cpp
===================================================================
--- test/Analysis/temp-obj-dtors-cfg-output.cpp
+++ test/Analysis/temp-obj-dtors-cfg-output.cpp
@@ -698,7 +698,6 @@
 // CHECK:   [B1]
 // CHECK:     1: ~B() (Temporary object destructor)
 // CHECK:     2: int b;
-// CHECK:     3: [B10.4].~A() (Implicit destructor)
 // CHECK:     Preds (2): B2 B3
 // CHECK:     Succs (1): B0
 // CHECK:   [B2]
@@ -874,7 +873,6 @@
 // CHECK:   [B1]
 // CHECK:     1: ~A() (Temporary object destructor)
 // CHECK:     2: int b;
-// CHECK:     3: [B9.4].~A() (Implicit destructor)
 // CHECK:     Preds (2): B2 B3
 // CHECK:     Succs (1): B0
 // CHECK:   [B2]
@@ -994,7 +992,7 @@
 // CHECK:    12: [B1.7]([B1.11])
 // CHECK:    13: ~A() (Temporary object destructor)
 // CHECK:    14: int b;
-// CHECK:    15: [B1.5].~A() (Implicit destructor)
+// CHECK:    15: ~A() (Temporary object destructor)
 // CHECK:     Preds (1): B2
 // CHECK:     Succs (1): B0
 // CHECK:   [B0 (EXIT)]
@@ -1038,7 +1036,7 @@
 // CHECK:    16: [B1.9]([B1.15])
 // CHECK:    17: ~A() (Temporary object destructor)
 // CHECK:    18: int b;
-// CHECK:    19: [B1.7].~A() (Implicit destructor)
+// CHECK:    19: ~A() (Temporary object destructor)
 // CHECK:     Preds (1): B2
 // CHECK:     Succs (1): B0
 // CHECK:   [B0 (EXIT)]
@@ -1089,6 +1087,7 @@
 // CHECK:     Succs (1): B2
 // CHECK:   [B1]
 // CHECK:     1: int b;
+// CHECK:     Preds (1): B2(Unreachable)
 // CHECK:     Succs (1): B0
 // CHECK:   [B2 (NORETURN)]
 // CHECK:     1: int a;
@@ -1105,6 +1104,7 @@
 // CHECK:     Succs (1): B2
 // CHECK:   [B1]
 // CHECK:     1: int b;
+// CHECK:     Preds (1): B2(Unreachable)
 // CHECK:     Succs (1): B0
 // CHECK:   [B2 (NORETURN)]
 // CHECK:     1: int a;
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to