r197103 - PR18217: Rewrite JumpDiagnostics' handling of temporaries, to correctly handle

Richard Smith richard-llvm at metafoo.co.uk
Wed Dec 11 17:27:02 PST 2013


Author: rsmith
Date: Wed Dec 11 19:27:02 2013
New Revision: 197103

URL: http://llvm.org/viewvc/llvm-project?rev=197103&view=rev
Log:
PR18217: Rewrite JumpDiagnostics' handling of temporaries, to correctly handle
declarations that might lifetime-extend multiple temporaries. In passing, fix a
crasher (PR18217) if an initializer was dependent and exactly the wrong shape,
and remove a bogus function (Expr::findMaterializedTemporary) now its last use
is gone.

Modified:
    cfe/trunk/include/clang/AST/Expr.h
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/AST/Expr.cpp
    cfe/trunk/lib/Sema/JumpDiagnostics.cpp
    cfe/trunk/test/SemaCXX/scope-check.cpp

Modified: cfe/trunk/include/clang/AST/Expr.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Expr.h?rev=197103&r1=197102&r2=197103&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Expr.h (original)
+++ cfe/trunk/include/clang/AST/Expr.h Wed Dec 11 19:27:02 2013
@@ -764,11 +764,6 @@ public:
       SmallVectorImpl<const Expr *> &CommaLHS,
       SmallVectorImpl<SubobjectAdjustment> &Adjustments) const;
 
-  /// Skip irrelevant expressions to find what should be materialize for
-  /// binding with a reference.
-  const Expr *
-  findMaterializedTemporary(const MaterializeTemporaryExpr *&MTE) const;
-
   static bool classof(const Stmt *T) {
     return T->getStmtClass() >= firstExprConstant &&
            T->getStmtClass() <= lastExprConstant;

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=197103&r1=197102&r2=197103&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed Dec 11 19:27:02 2013
@@ -3941,6 +3941,9 @@ def note_exits_cleanup : Note<
   "jump exits scope of variable with __attribute__((cleanup))">;
 def note_exits_dtor : Note<
   "jump exits scope of variable with non-trivial destructor">;
+def note_exits_temporary_dtor : Note<
+  "jump exits scope of lifetime-extended temporary with non-trivial "
+  "destructor">;
 def note_exits___block : Note<
   "jump exits scope of __block variable">;
 def note_exits_objc_try : Note<

Modified: cfe/trunk/lib/AST/Expr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Expr.cpp?rev=197103&r1=197102&r2=197103&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Expr.cpp (original)
+++ cfe/trunk/lib/AST/Expr.cpp Wed Dec 11 19:27:02 2013
@@ -105,37 +105,6 @@ const Expr *Expr::skipRValueSubobjectAdj
   return E;
 }
 
-const Expr *
-Expr::findMaterializedTemporary(const MaterializeTemporaryExpr *&MTE) const {
-  const Expr *E = this;
-
-  // This might be a default initializer for a reference member. Walk over the
-  // wrapper node for that.
-  if (const CXXDefaultInitExpr *DAE = dyn_cast<CXXDefaultInitExpr>(E))
-    E = DAE->getExpr();
-
-  // Look through single-element init lists that claim to be lvalues. They're
-  // just syntactic wrappers in this case.
-  if (const InitListExpr *ILE = dyn_cast<InitListExpr>(E)) {
-    if (ILE->getNumInits() == 1 && ILE->isGLValue()) {
-      E = ILE->getInit(0);
-      if (const CXXDefaultInitExpr *DAE = dyn_cast<CXXDefaultInitExpr>(E))
-        E = DAE->getExpr();
-    }
-  }
-
-  // Look through expressions for materialized temporaries (for now).
-  if (const MaterializeTemporaryExpr *M
-      = dyn_cast<MaterializeTemporaryExpr>(E)) {
-    MTE = M;
-    E = M->GetTemporaryExpr();
-  }
-
-  if (const CXXDefaultArgExpr *DAE = dyn_cast<CXXDefaultArgExpr>(E))
-    E = DAE->getExpr();
-  return E;
-}
-
 /// isKnownToHaveBooleanValue - Return true if this is an integer expression
 /// that is known to return 0 or 1.  This happens for _Bool/bool expressions
 /// but also int expressions which are produced by things like comparisons in

Modified: cfe/trunk/lib/Sema/JumpDiagnostics.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/JumpDiagnostics.cpp?rev=197103&r1=197102&r2=197103&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/JumpDiagnostics.cpp (original)
+++ cfe/trunk/lib/Sema/JumpDiagnostics.cpp Wed Dec 11 19:27:02 2013
@@ -121,9 +121,11 @@ typedef std::pair<unsigned,unsigned> Sco
 
 /// GetDiagForGotoScopeDecl - If this decl induces a new goto scope, return a
 /// diagnostic that should be emitted if control goes over it. If not, return 0.
-static ScopePair GetDiagForGotoScopeDecl(ASTContext &Context, const Decl *D) {
+static ScopePair GetDiagForGotoScopeDecl(Sema &S, const Decl *D) {
   if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
     unsigned InDiag = 0;
+    unsigned OutDiag = 0;
+
     if (VD->getType()->isVariablyModifiedType())
       InDiag = diag::note_protected_by_vla;
 
@@ -135,21 +137,24 @@ static ScopePair GetDiagForGotoScopeDecl
       return ScopePair(diag::note_protected_by_cleanup,
                        diag::note_exits_cleanup);
 
-    if (Context.getLangOpts().ObjCAutoRefCount && VD->hasLocalStorage()) {
-      switch (VD->getType().getObjCLifetime()) {
-      case Qualifiers::OCL_None:
-      case Qualifiers::OCL_ExplicitNone:
-      case Qualifiers::OCL_Autoreleasing:
-        break;
-
-      case Qualifiers::OCL_Strong:
-      case Qualifiers::OCL_Weak:
+    if (VD->hasLocalStorage()) {
+      switch (VD->getType().isDestructedType()) {
+      case QualType::DK_objc_strong_lifetime:
+      case QualType::DK_objc_weak_lifetime:
         return ScopePair(diag::note_protected_by_objc_ownership,
                          diag::note_exits_objc_ownership);
+
+      case QualType::DK_cxx_destructor:
+        OutDiag = diag::note_exits_dtor;
+        break;
+
+      case QualType::DK_none:
+        break;
       }
     }
 
-    if (Context.getLangOpts().CPlusPlus && VD->hasLocalStorage()) {
+    const Expr *Init = VD->getInit();
+    if (S.Context.getLangOpts().CPlusPlus && VD->hasLocalStorage() && Init) {
       // C++11 [stmt.dcl]p3:
       //   A program that jumps from a point where a variable with automatic
       //   storage duration is not in scope to a point where it is in scope
@@ -164,68 +169,34 @@ static ScopePair GetDiagForGotoScopeDecl
       //   where it is in scope is ill-formed unless the variable has
       //   POD type and is declared without an initializer.
 
-      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<const Expr *, 2> CommaLHSs;
-      SmallVector<SubobjectAdjustment, 2> Adjustments;
-      Init = Init->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments);
-
-      QualType QT = Init->getType();
-      if (QT.isNull())
-        return ScopePair(diag::note_protected_by_variable_init, 0);
-
-      const Type *T = QT.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 (!Init->isGLValue() && !Record->hasTrivialDestructor())
-        OutDiag = diag::note_exits_dtor;
+      InDiag = diag::note_protected_by_variable_init;
 
-      if (const CXXConstructExpr *cce = dyn_cast<CXXConstructExpr>(Init)) {
-        const CXXConstructorDecl *ctor = cce->getConstructor();
-        // For a variable declared without an initializer, we will have
-        // call-style initialization and the initializer will be the
-        // CXXConstructExpr with no intervening nodes.
-        if (ctor->isTrivial() && ctor->isDefaultConstructor() &&
-            VD->getInit() == Init && VD->getInitStyle() == VarDecl::CallInit) {
+      // For a variable of (array of) class type declared without an
+      // initializer, we will have call-style initialization and the initializer
+      // will be the CXXConstructExpr with no intervening nodes.
+      if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(Init)) {
+        const CXXConstructorDecl *Ctor = CCE->getConstructor();
+        if (Ctor->isTrivial() && Ctor->isDefaultConstructor() &&
+            VD->getInitStyle() == VarDecl::CallInit) {
           if (OutDiag)
             InDiag = diag::note_protected_by_variable_nontriv_destructor;
-          else if (!Record->isPOD())
+          else if (!Ctor->getParent()->isPOD())
             InDiag = diag::note_protected_by_variable_non_pod;
-          return ScopePair(InDiag, OutDiag);
+          else
+            InDiag = 0;
         }
       }
-
-      return ScopePair(diag::note_protected_by_variable_init, OutDiag);
     }
 
-    return ScopePair(InDiag, 0);
-  }
-
-  if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(D)) {
-    if (TD->getUnderlyingType()->isVariablyModifiedType())
-      return ScopePair(diag::note_protected_by_vla_typedef, 0);
+    return ScopePair(InDiag, OutDiag);
   }
 
-  if (const TypeAliasDecl *TD = dyn_cast<TypeAliasDecl>(D)) {
+  if (const TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(D)) {
     if (TD->getUnderlyingType()->isVariablyModifiedType())
-      return ScopePair(diag::note_protected_by_vla_type_alias, 0);
+      return ScopePair(isa<TypedefDecl>(TD)
+                           ? diag::note_protected_by_vla_typedef
+                           : diag::note_protected_by_vla_type_alias,
+                       0);
   }
 
   return ScopePair(0U, 0U);
@@ -234,7 +205,7 @@ static ScopePair GetDiagForGotoScopeDecl
 /// \brief Build scope information for a declaration that is part of a DeclStmt.
 void JumpScopeChecker::BuildScopeInformation(Decl *D, unsigned &ParentScope) {
   // If this decl causes a new scope, push and switch to it.
-  std::pair<unsigned,unsigned> Diags = GetDiagForGotoScopeDecl(S.Context, D);
+  std::pair<unsigned,unsigned> Diags = GetDiagForGotoScopeDecl(S, D);
   if (Diags.first || Diags.second) {
     Scopes.push_back(GotoScope(ParentScope, Diags.first, Diags.second,
                                D->getLocation()));
@@ -481,7 +452,26 @@ void JumpScopeChecker::BuildScopeInforma
         }
       }
     }
-    
+
+    // Disallow jumps out of scopes containing temporaries lifetime-extended to
+    // automatic storage duration.
+    if (MaterializeTemporaryExpr *MTE =
+            dyn_cast<MaterializeTemporaryExpr>(SubStmt)) {
+      if (MTE->getStorageDuration() == SD_Automatic) {
+        SmallVector<const Expr *, 4> CommaLHS;
+        SmallVector<SubobjectAdjustment, 4> Adjustments;
+        const Expr *ExtendedObject =
+            MTE->GetTemporaryExpr()->skipRValueSubobjectAdjustments(
+                CommaLHS, Adjustments);
+        if (ExtendedObject->getType().isDestructedType()) {
+          Scopes.push_back(GotoScope(ParentScope, 0,
+                                     diag::note_exits_temporary_dtor,
+                                     ExtendedObject->getExprLoc()));
+          ParentScope = Scopes.size()-1;
+        }
+      }
+    }
+
     // Recursively walk the AST.
     BuildScopeInformation(SubStmt, ParentScope);
   }

Modified: cfe/trunk/test/SemaCXX/scope-check.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/scope-check.cpp?rev=197103&r1=197102&r2=197103&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/scope-check.cpp (original)
+++ cfe/trunk/test/SemaCXX/scope-check.cpp Wed Dec 11 19:27:02 2013
@@ -226,7 +226,7 @@ namespace test12 {
     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 &c1 = 42; // expected-note {{jump exits scope of lifetime-extended temporary with non-trivial destructor}}
     const C &c2 = c0;
     goto *ip; // expected-error {{indirect goto might cross protected scopes}}
   }
@@ -241,7 +241,7 @@ namespace test13 {
   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}}
+    const int &c1 = C(1).i; // expected-note {{jump exits scope of lifetime-extended temporary with non-trivial destructor}}
     goto *ip;  // expected-error {{indirect goto might cross protected scopes}}
   }
 }
@@ -295,6 +295,120 @@ x:  return s.get();
 }
 #endif
 
+namespace test18 {
+  struct A { ~A(); };
+  struct B { const int &r; const A &a; };
+  int f() {
+    void *p = &&x;
+    const A a = A();
+  x:
+    B b = { 0, a }; // ok
+    goto *p;
+  }
+  int g() {
+    void *p = &&x;
+  x: // expected-note {{possible target of indirect goto}}
+    B b = { 0, A() }; // expected-note {{jump exits scope of lifetime-extended temporary with non-trivial destructor}}
+    goto *p; // expected-error {{indirect goto might cross protected scopes}}
+  }
+}
+
+#if __cplusplus >= 201103L
+namespace std {
+  typedef decltype(sizeof(int)) size_t;
+  template<typename T> struct initializer_list {
+    const T *begin;
+    size_t size;
+    initializer_list(const T *, size_t);
+  };
+}
+namespace test19 {
+  struct A { ~A(); };
+
+  int f() {
+    void *p = &&x;
+    A a;
+  x: // expected-note {{possible target of indirect goto}}
+    std::initializer_list<A> il = { a }; // expected-note {{jump exits scope of lifetime-extended temporary with non-trivial destructor}}
+    goto *p; // expected-error {{indirect goto might cross protected scopes}}
+  }
+}
+
+namespace test20 {
+  struct A { ~A(); };
+  struct B {
+    const A &a;
+  };
+
+  int f() {
+    void *p = &&x;
+    A a;
+  x:
+    std::initializer_list<B> il = {
+      a,
+      a
+    };
+    goto *p;
+  }
+  int g() {
+    void *p = &&x;
+    A a;
+  x: // expected-note {{possible target of indirect goto}}
+    std::initializer_list<B> il = {
+      a,
+      { A() } // expected-note {{jump exits scope of lifetime-extended temporary with non-trivial destructor}}
+    };
+    goto *p; // expected-error {{indirect goto might cross protected scopes}}
+  }
+}
+#endif
+
+namespace test21 {
+  template<typename T> void f() {
+  goto x; // expected-error {{protected scope}}
+    T t; // expected-note {{bypasses}}
+ x: return;
+  }
+
+  template void f<int>();
+  struct X { ~X(); };
+  template void f<X>(); // expected-note {{instantiation of}}
+}
+
+namespace PR18217 {
+  typedef int *X;
+
+  template <typename T>
+  class MyCl {
+    T mem;
+  };
+
+  class Source {
+    MyCl<X> m;
+  public:
+    int getKind() const;
+  };
+
+  bool b;
+  template<typename TT>
+  static void foo(const Source &SF, MyCl<TT *> Source::*m) {
+    switch (SF.getKind()) {
+      case 1: return;
+      case 2: break;
+      case 3:
+      case 4: return;
+    };
+    if (b) {
+      auto &y = const_cast<MyCl<TT *> &>(SF.*m); // expected-warning 0-1{{extension}}
+    }
+  }
+
+  int Source::getKind() const {
+    foo(*this, &Source::m);
+    return 0;
+  }
+}
+
 // This test must be last, because the error prohibits further jump diagnostics.
 namespace testInvalid {
 Invalid inv; // expected-error {{unknown type name}}





More information about the cfe-commits mailing list