commit 7233b2592c7743352e2dd0268ec9651546f79e41
Author: Rafael Ávila de Espíndola <rafael.espindola@gmail.com>
Date:   Tue Oct 2 19:28:34 2012 -0400

    fix it

diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h
index 443d8be..ef198a5 100644
--- a/include/clang/AST/Expr.h
+++ b/include/clang/AST/Expr.h
@@ -42,6 +42,7 @@ namespace clang {
   class BlockDecl;
   class CXXBaseSpecifier;
   class CXXOperatorCallExpr;
+  class MaterializeTemporaryExpr;
   class CXXMemberCallExpr;
   class ObjCPropertyRefExpr;
   class OpaqueValueExpr;
@@ -695,6 +696,11 @@ public:
   /// behavior if the object isn't dynamically of the derived type.
   const CXXRecordDecl *getBestDynamicClassType() const;
 
+  const Expr *
+  Foobar(SmallVector<const Expr *, 2> &Adjustments) const;
+  const Expr *
+  ZedBar(const MaterializeTemporaryExpr *&MTE) const;
+
   static bool classof(const Stmt *T) {
     return T->getStmtClass() >= firstExprConstant &&
            T->getStmtClass() <= lastExprConstant;
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index e1198b8..4182653 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -48,6 +48,68 @@ const CXXRecordDecl *Expr::getBestDynamicClassType() const {
   return cast<CXXRecordDecl>(D);
 }
 
+const Expr *
+Expr::Foobar(SmallVector<const Expr *, 2> &Adjustments) const {
+  const Expr *E = this;
+  while (true) {
+    E = E->IgnoreParens();
+
+    if (const CastExpr *CE = dyn_cast<CastExpr>(E)) {
+      if ((CE->getCastKind() == CK_DerivedToBase ||
+           CE->getCastKind() == CK_UncheckedDerivedToBase) &&
+          E->getType()->isRecordType()) {
+        E = CE->getSubExpr();
+        Adjustments.push_back(CE);
+        continue;
+      }
+
+      if (CE->getCastKind() == CK_NoOp) {
+        E = CE->getSubExpr();
+        continue;
+      }
+    } else if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
+      if (!ME->isArrow() && ME->getBase()->isRValue()) {
+        assert(ME->getBase()->getType()->isRecordType());
+        if (isa<FieldDecl>(ME->getMemberDecl())) {
+          E = ME->getBase();
+          Adjustments.push_back(ME);
+          continue;
+        }
+      }
+    } else if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
+      if (BO->isPtrMemOp()) {
+        assert(BO->getLHS()->isRValue());
+        E = BO->getLHS();
+        Adjustments.push_back(BO);
+      }
+    }
+
+    return E;
+  }
+}
+
+const Expr *
+Expr::ZedBar(const MaterializeTemporaryExpr *&MTE) const {
+  // Look through single-element init lists that claim to be lvalues. They're
+  // just syntactic wrappers in this case.
+  const Expr *E = this;
+  if (const InitListExpr *ILE = dyn_cast<InitListExpr>(E)) {
+    if (ILE->getNumInits() == 1 && ILE->isGLValue())
+      E = ILE->getInit(0);
+  }
+
+  // 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
diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp
index 9043fe7..fddb7ef 100644
--- a/lib/CodeGen/CGExpr.cpp
+++ b/lib/CodeGen/CGExpr.cpp
@@ -156,66 +156,6 @@ void CodeGenFunction::EmitAnyExprToMem(const Expr *E,
   }
 }
 
-static const Expr *
-Foobar(const Expr *E, SmallVector<const Expr *, 2> &Adjustments) {
-  while (true) {
-    E = E->IgnoreParens();
-
-    if (const CastExpr *CE = dyn_cast<CastExpr>(E)) {
-      if ((CE->getCastKind() == CK_DerivedToBase ||
-           CE->getCastKind() == CK_UncheckedDerivedToBase) &&
-          E->getType()->isRecordType()) {
-        E = CE->getSubExpr();
-        Adjustments.push_back(CE);
-        continue;
-      }
-
-      if (CE->getCastKind() == CK_NoOp) {
-        E = CE->getSubExpr();
-        continue;
-      }
-    } else if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
-      if (!ME->isArrow() && ME->getBase()->isRValue()) {
-        assert(ME->getBase()->getType()->isRecordType());
-        if (isa<FieldDecl>(ME->getMemberDecl())) {
-          E = ME->getBase();
-          Adjustments.push_back(ME);
-          continue;
-        }
-      }
-    } else if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
-      if (BO->isPtrMemOp()) {
-        assert(BO->getLHS()->isRValue());
-        E = BO->getLHS();
-        Adjustments.push_back(BO);
-      }
-    }
-
-    return E;
-  }
-}
-
-static const Expr *
-ZedBar(const Expr *E, const MaterializeTemporaryExpr *&MTE) {
-  // 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);
-  }
-
-  // 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;
-}
-
 static llvm::Value *
 CreateReferenceTemporary(CodeGenFunction &CGF, QualType Type,
                          const NamedDecl *InitializedDecl) {
@@ -250,7 +190,7 @@ EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E,
                             const NamedDecl *InitializedDecl) {
 
   const MaterializeTemporaryExpr *M = NULL;
-  E = ZedBar(E, M);
+  E = E->ZedBar(M);
   // Objective-C++ ARC:
   //   If we are binding a reference to a temporary that has ownership, we
   //   need to perform retain/release operations on the temporary.
@@ -340,7 +280,7 @@ EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E,
     }
 
     SmallVector<const Expr *, 2> Adjustments;
-    E = Foobar(E, Adjustments);
+    E = E->Foobar(Adjustments);
     if (const OpaqueValueExpr *opaque = dyn_cast<OpaqueValueExpr>(E))
       if (opaque->getType()->isRecordType())
         return CGF.EmitOpaqueValueLValue(opaque).getAddress();
diff --git a/lib/Sema/JumpDiagnostics.cpp b/lib/Sema/JumpDiagnostics.cpp
index a48779a..12c03e9 100644
--- a/lib/Sema/JumpDiagnostics.cpp
+++ b/lib/Sema/JumpDiagnostics.cpp
@@ -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,52 @@ 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);
+
+      for (;;) {
+        const MaterializeTemporaryExpr *M = NULL;
+        Init = Init->ZedBar(M);
 
-        if (inDiagToUse)
-          InDiag = inDiagToUse;
+        const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Init);
+        if (!EWC)
+          break;
+        Init = EWC->getSubExpr();
+      }
 
-        // Also object to indirect jumps which leave scopes with dtors.
-        if (record && !record->hasTrivialDestructor())
-          OutDiag = diag::note_exits_dtor;
+      SmallVector<const Expr *, 2> Adjustments;
+      Init = Init->Foobar(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);
+        }
       }
+
+      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)) {
diff --git a/test/SemaCXX/scope-check.cpp b/test/SemaCXX/scope-check.cpp
index 86d1bd8..78228c0 100644
--- a/test/SemaCXX/scope-check.cpp
+++ b/test/SemaCXX/scope-check.cpp
@@ -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;
+  }
+}
