Fix cases where we were not producing an error when a computed goto could
authorRafael Espindola <rafael.espindola@gmail.com>
Sat, 27 Oct 2012 01:17:42 +0000 (01:17 +0000)
committerRafael Espindola <rafael.espindola@gmail.com>
Sat, 27 Oct 2012 01:17:42 +0000 (01:17 +0000)
jump over destructor calls.
Fixes pr13812.

llvm-svn: 166855

clang/lib/Sema/JumpDiagnostics.cpp
clang/test/SemaCXX/scope-check.cpp

index a48779a..b920a43 100644 (file)
@@ -123,7 +123,7 @@ typedef std::pair<unsigned,unsigned> ScopePair;
 /// diagnostic that should be emitted if control goes over it. If not, return 0.
 static ScopePair GetDiagForGotoScopeDecl(ASTContext &Context, const Decl *D) {
   if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
-    unsigned InDiag = 0, OutDiag = 0;
+    unsigned InDiag = 0;
     if (VD->getType()->isVariablyModifiedType())
       InDiag = diag::note_protected_by_vla;
 
@@ -164,43 +164,49 @@ static ScopePair GetDiagForGotoScopeDecl(ASTContext &Context, const Decl *D) {
       //   where it is in scope is ill-formed unless the variable has
       //   POD type and is declared without an initializer.
 
-      if (const Expr *init = VD->getInit()) {
-        // We actually give variables of record type (or array thereof)
-        // an initializer even if that initializer only calls a trivial
-        // ctor.  Detect that case.
-        // FIXME: With generalized initializer lists, this may
-        // classify "X x{};" as having no initializer.
-        unsigned inDiagToUse = diag::note_protected_by_variable_init;
-
-        const CXXRecordDecl *record = 0;
-
-        if (const CXXConstructExpr *cce = dyn_cast<CXXConstructExpr>(init)) {
-          const CXXConstructorDecl *ctor = cce->getConstructor();
-          record = ctor->getParent();
-
-          if (ctor->isTrivial() && ctor->isDefaultConstructor()) {
-            if (!record->hasTrivialDestructor())
-              inDiagToUse = diag::note_protected_by_variable_nontriv_destructor;
-            else if (!record->isPOD())
-              inDiagToUse = diag::note_protected_by_variable_non_pod;
-            else
-              inDiagToUse = 0;
-          }
-        } else if (VD->getType()->isArrayType()) {
-          record = VD->getType()->getBaseElementTypeUnsafe()
-                                ->getAsCXXRecordDecl();
+      const Expr *Init = VD->getInit();
+      if (!Init)
+        return ScopePair(InDiag, 0);
+
+      const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Init);
+      if (EWC)
+        Init = EWC->getSubExpr();
+
+      const MaterializeTemporaryExpr *M = NULL;
+      Init = Init->findMaterializedTemporary(M);
+
+      SmallVector<SubobjectAdjustment, 2> Adjustments;
+      Init = Init->skipRValueSubobjectAdjustments(Adjustments);
+
+      const Type *T = Init->getType().getTypePtr();
+      if (T->isArrayType())
+        T = T->getBaseElementTypeUnsafe();
+
+      const CXXRecordDecl *Record = T->getAsCXXRecordDecl();
+      if (!Record)
+        return ScopePair(diag::note_protected_by_variable_init, 0);
+
+      // If we need to call a non trivial destructor for this variable,
+      // record an out diagnostic.
+      unsigned OutDiag = 0;
+      if (!Record->hasTrivialDestructor() && !Init->isGLValue())
+        OutDiag = diag::note_exits_dtor;
+
+      if (const CXXConstructExpr *cce = dyn_cast<CXXConstructExpr>(Init)) {
+        const CXXConstructorDecl *ctor = cce->getConstructor();
+        if (ctor->isTrivial() && ctor->isDefaultConstructor()) {
+          if (OutDiag)
+            InDiag = diag::note_protected_by_variable_nontriv_destructor;
+          else if (!Record->isPOD())
+            InDiag = diag::note_protected_by_variable_non_pod;
+          return ScopePair(InDiag, OutDiag);
         }
-
-        if (inDiagToUse)
-          InDiag = inDiagToUse;
-
-        // Also object to indirect jumps which leave scopes with dtors.
-        if (record && !record->hasTrivialDestructor())
-          OutDiag = diag::note_exits_dtor;
       }
+
+      return ScopePair(diag::note_protected_by_variable_init, OutDiag);
     }
-    
-    return ScopePair(InDiag, OutDiag);    
+
+    return ScopePair(InDiag, 0);
   }
 
   if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(D)) {
index 86d1bd8..78228c0 100644 (file)
@@ -202,3 +202,61 @@ namespace test10 {
     return 0;
   }
 }
+
+// pr13812
+namespace test11 {
+  struct C {
+    C(int x);
+    ~C();
+  };
+  void f(void **ip) {
+    static void *ips[] = { &&l0 };
+  l0:  // expected-note {{possible target of indirect goto}}
+    C c0 = 42; // expected-note {{jump exits scope of variable with non-trivial destructor}}
+    goto *ip; // expected-error {{indirect goto might cross protected scopes}}
+  }
+}
+
+namespace test12 {
+  struct C {
+    C(int x);
+    ~C();
+  };
+  void f(void **ip) {
+    static void *ips[] = { &&l0 };
+    const C c0 = 17;
+  l0: // expected-note {{possible target of indirect goto}}
+    const C &c1 = 42; // expected-note {{jump exits scope of variable with non-trivial destructor}}
+    const C &c2 = c0;
+    goto *ip; // expected-error {{indirect goto might cross protected scopes}}
+  }
+}
+
+namespace test13 {
+  struct C {
+    C(int x);
+    ~C();
+    int i;
+  };
+  void f(void **ip) {
+    static void *ips[] = { &&l0 };
+  l0: // expected-note {{possible target of indirect goto}}
+    const int &c1 = C(1).i; // expected-note {{jump exits scope of variable with non-trivial destructor}}
+    goto *ip;  // expected-error {{indirect goto might cross protected scopes}}
+  }
+}
+
+namespace test14 {
+  struct C {
+    C(int x);
+    ~C();
+    operator int&() const;
+  };
+  void f(void **ip) {
+    static void *ips[] = { &&l0 };
+  l0:
+    // no warning since the C temporary is destructed before the goto.
+    const int &c1 = C(1);
+    goto *ip;
+  }
+}