Fold -Wreturn-stack-address into general initialization lifetime
authorRichard Smith <richard-llvm@metafoo.co.uk>
Mon, 23 Jul 2018 21:21:22 +0000 (21:21 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Mon, 23 Jul 2018 21:21:22 +0000 (21:21 +0000)
checking.

llvm-svn: 337743

17 files changed:
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Sema/SemaChecking.cpp
clang/lib/Sema/SemaInit.cpp
clang/test/Analysis/stack-addr-ps.c
clang/test/Analysis/stack-addr-ps.cpp
clang/test/Analysis/stackaddrleak.c
clang/test/CXX/drs/dr16xx.cpp
clang/test/CXX/drs/dr18xx.cpp
clang/test/CXX/special/class.copy/p11.0x.copy.cpp
clang/test/CXX/special/class.ctor/p5-0x.cpp
clang/test/CXX/temp/temp.param/p5.cpp
clang/test/SemaCXX/constexpr-default-arg.cpp
clang/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp
clang/test/SemaCXX/eval-crashes.cpp
clang/test/SemaCXX/return-stack-addr-2.cpp
clang/test/SemaCXX/return-stack-addr.cpp
clang/test/SemaCXX/rval-references.cpp

index c6f263d..0d000e7 100644 (file)
@@ -7849,8 +7849,8 @@ def warn_null_ret : Warning<
 
 // CHECK: returning address/reference of stack memory
 def warn_ret_stack_addr_ref : Warning<
-  "%select{address of|reference to}0 stack memory associated with local "
-  "variable %1 returned">,
+  "%select{address of|reference to}0 stack memory associated with "
+  "%select{local variable|parameter}2 %1 returned">,
   InGroup<ReturnStackAddress>;
 def warn_ret_local_temp_addr_ref : Warning<
   "returning %select{address of|reference to}0 local temporary object">,
@@ -7860,8 +7860,10 @@ def warn_ret_addr_label : Warning<
   InGroup<ReturnStackAddress>;
 def err_ret_local_block : Error<
   "returning block that lives on the local stack">;
-def note_ref_var_local_bind : Note<
-  "binding reference variable %0 here">;
+def note_local_var_initializer : Note<
+  "%select{via initialization of|binding reference}0 variable %1 here">;
+def note_init_with_default_member_initalizer : Note<
+  "initializing field %0 with default member initializer">;
 
 // Check for initializing a member variable with the address or a reference to
 // a constructor parameter.
@@ -7902,8 +7904,6 @@ def warn_default_member_init_init_list_not_extended : Warning<
   "created by aggregate initialization using default member initializer "
   "is not supported; lifetime of backing array will end at the end of the "
   "full-expression">, InGroup<DanglingInitializerList>;
-def note_in_default_member_initalizer_here : Note<
-  "in default member initializer for field %0 used here">;
 
 // For non-floating point, expressions of the form x == x or x != x
 // should result in a warning, since these always evaluate to a constant.
index 6492346..8d953b6 100644 (file)
@@ -9232,421 +9232,12 @@ void Sema::CheckStrncatArguments(const CallExpr *CE,
     << FixItHint::CreateReplacement(SR, OS.str());
 }
 
-//===--- CHECK: Return Address of Stack Variable --------------------------===//
-
-static const Expr *EvalVal(const Expr *E,
-                           SmallVectorImpl<const DeclRefExpr *> &refVars,
-                           const Decl *ParentDecl);
-static const Expr *EvalAddr(const Expr *E,
-                            SmallVectorImpl<const DeclRefExpr *> &refVars,
-                            const Decl *ParentDecl);
-
-/// CheckReturnStackAddr - Check if a return statement returns the address
-///   of a stack variable.
-static void
-CheckReturnStackAddr(Sema &S, Expr *RetValExp, QualType lhsType,
-                     SourceLocation ReturnLoc) {
-  const Expr *stackE = nullptr;
-  SmallVector<const DeclRefExpr *, 8> refVars;
-
-  // Perform checking for returned stack addresses, local blocks,
-  // label addresses or references to temporaries.
-  if (lhsType->isPointerType() ||
-      (!S.getLangOpts().ObjCAutoRefCount && lhsType->isBlockPointerType())) {
-    stackE = EvalAddr(RetValExp, refVars, /*ParentDecl=*/nullptr);
-  } else if (lhsType->isReferenceType()) {
-    stackE = EvalVal(RetValExp, refVars, /*ParentDecl=*/nullptr);
-  }
-
-  if (!stackE)
-    return; // Nothing suspicious was found.
-
-  // Parameters are initialized in the calling scope, so taking the address
-  // of a parameter reference doesn't need a warning.
-  for (auto *DRE : refVars)
-    if (isa<ParmVarDecl>(DRE->getDecl()))
-      return;
-
-  SourceLocation diagLoc;
-  SourceRange diagRange;
-  if (refVars.empty()) {
-    diagLoc = stackE->getLocStart();
-    diagRange = stackE->getSourceRange();
-  } else {
-    // We followed through a reference variable. 'stackE' contains the
-    // problematic expression but we will warn at the return statement pointing
-    // at the reference variable. We will later display the "trail" of
-    // reference variables using notes.
-    diagLoc = refVars[0]->getLocStart();
-    diagRange = refVars[0]->getSourceRange();
-  }
-
-  if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(stackE)) {
-    // address of local var
-    S.Diag(diagLoc, diag::warn_ret_stack_addr_ref) << lhsType->isReferenceType()
-     << DR->getDecl()->getDeclName() << diagRange;
-  } else if (isa<BlockExpr>(stackE)) { // local block.
-    S.Diag(diagLoc, diag::err_ret_local_block) << diagRange;
-  } else if (isa<AddrLabelExpr>(stackE)) { // address of label.
-    S.Diag(diagLoc, diag::warn_ret_addr_label) << diagRange;
-  } else { // local temporary.
-    // If there is an LValue->RValue conversion, then the value of the
-    // reference type is used, not the reference.
-    if (auto *ICE = dyn_cast<ImplicitCastExpr>(RetValExp)) {
-      if (ICE->getCastKind() == CK_LValueToRValue) {
-        return;
-      }
-    }
-    S.Diag(diagLoc, diag::warn_ret_local_temp_addr_ref)
-     << lhsType->isReferenceType() << diagRange;
-  }
-
-  // Display the "trail" of reference variables that we followed until we
-  // found the problematic expression using notes.
-  for (unsigned i = 0, e = refVars.size(); i != e; ++i) {
-    const VarDecl *VD = cast<VarDecl>(refVars[i]->getDecl());
-    // If this var binds to another reference var, show the range of the next
-    // var, otherwise the var binds to the problematic expression, in which case
-    // show the range of the expression.
-    SourceRange range = (i < e - 1) ? refVars[i + 1]->getSourceRange()
-                                    : stackE->getSourceRange();
-    S.Diag(VD->getLocation(), diag::note_ref_var_local_bind)
-        << VD->getDeclName() << range;
-  }
-}
-
-/// EvalAddr - EvalAddr and EvalVal are mutually recursive functions that
-///  check if the expression in a return statement evaluates to an address
-///  to a location on the stack, a local block, an address of a label, or a
-///  reference to local temporary. The recursion is used to traverse the
-///  AST of the return expression, with recursion backtracking when we
-///  encounter a subexpression that (1) clearly does not lead to one of the
-///  above problematic expressions (2) is something we cannot determine leads to
-///  a problematic expression based on such local checking.
-///
-///  Both EvalAddr and EvalVal follow through reference variables to evaluate
-///  the expression that they point to. Such variables are added to the
-///  'refVars' vector so that we know what the reference variable "trail" was.
-///
-///  EvalAddr processes expressions that are pointers that are used as
-///  references (and not L-values).  EvalVal handles all other values.
-///  At the base case of the recursion is a check for the above problematic
-///  expressions.
-///
-///  This implementation handles:
-///
-///   * pointer-to-pointer casts
-///   * implicit conversions from array references to pointers
-///   * taking the address of fields
-///   * arbitrary interplay between "&" and "*" operators
-///   * pointer arithmetic from an address of a stack variable
-///   * taking the address of an array element where the array is on the stack
-static const Expr *EvalAddr(const Expr *E,
-                            SmallVectorImpl<const DeclRefExpr *> &refVars,
-                            const Decl *ParentDecl) {
-  if (E->isTypeDependent())
-    return nullptr;
-
-  // We should only be called for evaluating pointer expressions.
-  assert((E->getType()->isAnyPointerType() ||
-          E->getType()->isBlockPointerType() ||
-          E->getType()->isObjCQualifiedIdType()) &&
-         "EvalAddr only works on pointers");
-
-  E = E->IgnoreParens();
-
-  // Our "symbolic interpreter" is just a dispatch off the currently
-  // viewed AST node.  We then recursively traverse the AST by calling
-  // EvalAddr and EvalVal appropriately.
-  switch (E->getStmtClass()) {
-  case Stmt::DeclRefExprClass: {
-    const DeclRefExpr *DR = cast<DeclRefExpr>(E);
-
-    // If we leave the immediate function, the lifetime isn't about to end.
-    if (DR->refersToEnclosingVariableOrCapture())
-      return nullptr;
-
-    if (const VarDecl *V = dyn_cast<VarDecl>(DR->getDecl()))
-      // If this is a reference variable, follow through to the expression that
-      // it points to.
-      if (V->hasLocalStorage() &&
-          V->getType()->isReferenceType() && V->hasInit()) {
-        // Add the reference variable to the "trail".
-        refVars.push_back(DR);
-        return EvalAddr(V->getInit(), refVars, ParentDecl);
-      }
-
-    return nullptr;
-  }
-
-  case Stmt::UnaryOperatorClass: {
-    // The only unary operator that make sense to handle here
-    // is AddrOf.  All others don't make sense as pointers.
-    const UnaryOperator *U = cast<UnaryOperator>(E);
-
-    if (U->getOpcode() == UO_AddrOf)
-      return EvalVal(U->getSubExpr(), refVars, ParentDecl);
-    return nullptr;
-  }
-
-  case Stmt::BinaryOperatorClass: {
-    // Handle pointer arithmetic.  All other binary operators are not valid
-    // in this context.
-    const BinaryOperator *B = cast<BinaryOperator>(E);
-    BinaryOperatorKind op = B->getOpcode();
-
-    if (op != BO_Add && op != BO_Sub)
-      return nullptr;
-
-    const Expr *Base = B->getLHS();
-
-    // Determine which argument is the real pointer base.  It could be
-    // the RHS argument instead of the LHS.
-    if (!Base->getType()->isPointerType())
-      Base = B->getRHS();
-
-    assert(Base->getType()->isPointerType());
-    return EvalAddr(Base, refVars, ParentDecl);
-  }
-
-  // For conditional operators we need to see if either the LHS or RHS are
-  // valid DeclRefExpr*s.  If one of them is valid, we return it.
-  case Stmt::ConditionalOperatorClass: {
-    const ConditionalOperator *C = cast<ConditionalOperator>(E);
-
-    // Handle the GNU extension for missing LHS.
-    // FIXME: That isn't a ConditionalOperator, so doesn't get here.
-    if (const Expr *LHSExpr = C->getLHS()) {
-      // In C++, we can have a throw-expression, which has 'void' type.
-      if (!LHSExpr->getType()->isVoidType())
-        if (const Expr *LHS = EvalAddr(LHSExpr, refVars, ParentDecl))
-          return LHS;
-    }
-
-    // In C++, we can have a throw-expression, which has 'void' type.
-    if (C->getRHS()->getType()->isVoidType())
-      return nullptr;
-
-    return EvalAddr(C->getRHS(), refVars, ParentDecl);
-  }
-
-  case Stmt::BlockExprClass:
-    if (cast<BlockExpr>(E)->getBlockDecl()->hasCaptures())
-      return E; // local block.
-    return nullptr;
-
-  case Stmt::AddrLabelExprClass:
-    return E; // address of label.
-
-  case Stmt::ExprWithCleanupsClass:
-    return EvalAddr(cast<ExprWithCleanups>(E)->getSubExpr(), refVars,
-                    ParentDecl);
-
-  // For casts, we need to handle conversions from arrays to
-  // pointer values, and pointer-to-pointer conversions.
-  case Stmt::ImplicitCastExprClass:
-  case Stmt::CStyleCastExprClass:
-  case Stmt::CXXFunctionalCastExprClass:
-  case Stmt::ObjCBridgedCastExprClass:
-  case Stmt::CXXStaticCastExprClass:
-  case Stmt::CXXDynamicCastExprClass:
-  case Stmt::CXXConstCastExprClass:
-  case Stmt::CXXReinterpretCastExprClass: {
-    const Expr* SubExpr = cast<CastExpr>(E)->getSubExpr();
-    switch (cast<CastExpr>(E)->getCastKind()) {
-    case CK_LValueToRValue:
-    case CK_NoOp:
-    case CK_BaseToDerived:
-    case CK_DerivedToBase:
-    case CK_UncheckedDerivedToBase:
-    case CK_Dynamic:
-    case CK_CPointerToObjCPointerCast:
-    case CK_BlockPointerToObjCPointerCast:
-    case CK_AnyPointerToBlockPointerCast:
-      return EvalAddr(SubExpr, refVars, ParentDecl);
-
-    case CK_ArrayToPointerDecay:
-      return EvalVal(SubExpr, refVars, ParentDecl);
-
-    case CK_BitCast:
-      if (SubExpr->getType()->isAnyPointerType() ||
-          SubExpr->getType()->isBlockPointerType() ||
-          SubExpr->getType()->isObjCQualifiedIdType())
-        return EvalAddr(SubExpr, refVars, ParentDecl);
-      else
-        return nullptr;
-
-    default:
-      return nullptr;
-    }
-  }
-
-  case Stmt::MaterializeTemporaryExprClass:
-    if (const Expr *Result =
-            EvalAddr(cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr(),
-                     refVars, ParentDecl))
-      return Result;
-    return E;
-
-  // Everything else: we simply don't reason about them.
-  default:
-    return nullptr;
-  }
-}
-
-///  EvalVal - This function is complements EvalAddr in the mutual recursion.
-///   See the comments for EvalAddr for more details.
-static const Expr *EvalVal(const Expr *E,
-                           SmallVectorImpl<const DeclRefExpr *> &refVars,
-                           const Decl *ParentDecl) {
-  do {
-    // We should only be called for evaluating non-pointer expressions, or
-    // expressions with a pointer type that are not used as references but
-    // instead
-    // are l-values (e.g., DeclRefExpr with a pointer type).
-
-    // Our "symbolic interpreter" is just a dispatch off the currently
-    // viewed AST node.  We then recursively traverse the AST by calling
-    // EvalAddr and EvalVal appropriately.
-
-    E = E->IgnoreParens();
-    switch (E->getStmtClass()) {
-    case Stmt::ImplicitCastExprClass: {
-      const ImplicitCastExpr *IE = cast<ImplicitCastExpr>(E);
-      if (IE->getValueKind() == VK_LValue) {
-        E = IE->getSubExpr();
-        continue;
-      }
-      return nullptr;
-    }
-
-    case Stmt::ExprWithCleanupsClass:
-      return EvalVal(cast<ExprWithCleanups>(E)->getSubExpr(), refVars,
-                     ParentDecl);
-
-    case Stmt::DeclRefExprClass: {
-      // When we hit a DeclRefExpr we are looking at code that refers to a
-      // variable's name. If it's not a reference variable we check if it has
-      // local storage within the function, and if so, return the expression.
-      const DeclRefExpr *DR = cast<DeclRefExpr>(E);
-
-      // If we leave the immediate function, the lifetime isn't about to end.
-      if (DR->refersToEnclosingVariableOrCapture())
-        return nullptr;
-
-      if (const VarDecl *V = dyn_cast<VarDecl>(DR->getDecl())) {
-        // Check if it refers to itself, e.g. "int& i = i;".
-        if (V == ParentDecl)
-          return DR;
-
-        if (V->hasLocalStorage()) {
-          if (!V->getType()->isReferenceType())
-            return DR;
-
-          // Reference variable, follow through to the expression that
-          // it points to.
-          if (V->hasInit()) {
-            // Add the reference variable to the "trail".
-            refVars.push_back(DR);
-            return EvalVal(V->getInit(), refVars, V);
-          }
-        }
-      }
-
-      return nullptr;
-    }
-
-    case Stmt::UnaryOperatorClass: {
-      // The only unary operator that make sense to handle here
-      // is Deref.  All others don't resolve to a "name."  This includes
-      // handling all sorts of rvalues passed to a unary operator.
-      const UnaryOperator *U = cast<UnaryOperator>(E);
-
-      if (U->getOpcode() == UO_Deref)
-        return EvalAddr(U->getSubExpr(), refVars, ParentDecl);
-
-      return nullptr;
-    }
-
-    case Stmt::ArraySubscriptExprClass: {
-      // Array subscripts are potential references to data on the stack.  We
-      // retrieve the DeclRefExpr* for the array variable if it indeed
-      // has local storage.
-      const auto *ASE = cast<ArraySubscriptExpr>(E);
-      if (ASE->isTypeDependent())
-        return nullptr;
-      return EvalAddr(ASE->getBase(), refVars, ParentDecl);
-    }
-
-    case Stmt::OMPArraySectionExprClass: {
-      return EvalAddr(cast<OMPArraySectionExpr>(E)->getBase(), refVars,
-                      ParentDecl);
-    }
-
-    case Stmt::ConditionalOperatorClass: {
-      // For conditional operators we need to see if either the LHS or RHS are
-      // non-NULL Expr's.  If one is non-NULL, we return it.
-      const ConditionalOperator *C = cast<ConditionalOperator>(E);
-
-      // Handle the GNU extension for missing LHS.
-      if (const Expr *LHSExpr = C->getLHS()) {
-        // In C++, we can have a throw-expression, which has 'void' type.
-        if (!LHSExpr->getType()->isVoidType())
-          if (const Expr *LHS = EvalVal(LHSExpr, refVars, ParentDecl))
-            return LHS;
-      }
-
-      // In C++, we can have a throw-expression, which has 'void' type.
-      if (C->getRHS()->getType()->isVoidType())
-        return nullptr;
-
-      return EvalVal(C->getRHS(), refVars, ParentDecl);
-    }
-
-    // Accesses to members are potential references to data on the stack.
-    case Stmt::MemberExprClass: {
-      const MemberExpr *M = cast<MemberExpr>(E);
-
-      // Check for indirect access.  We only want direct field accesses.
-      if (M->isArrow())
-        return nullptr;
-
-      // Check whether the member type is itself a reference, in which case
-      // we're not going to refer to the member, but to what the member refers
-      // to.
-      if (M->getMemberDecl()->getType()->isReferenceType())
-        return nullptr;
-
-      return EvalVal(M->getBase(), refVars, ParentDecl);
-    }
-
-    case Stmt::MaterializeTemporaryExprClass:
-      if (const Expr *Result =
-              EvalVal(cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr(),
-                      refVars, ParentDecl))
-        return Result;
-      return E;
-
-    default:
-      // Check that we don't return or take the address of a reference to a
-      // temporary. This is only useful in C++.
-      if (!E->isTypeDependent() && E->isRValue())
-        return E;
-
-      // Everything else: we simply don't reason about them.
-      return nullptr;
-    }
-  } while (true);
-}
-
 void
 Sema::CheckReturnValExpr(Expr *RetValExp, QualType lhsType,
                          SourceLocation ReturnLoc,
                          bool isObjCMethod,
                          const AttrVec *Attrs,
                          const FunctionDecl *FD) {
-  CheckReturnStackAddr(*this, RetValExp, lhsType, ReturnLoc);
-
   // Check if the return value is null but should not be.
   if (((Attrs && hasSpecificAttr<ReturnsNonNullAttr>(*Attrs)) ||
        (!isObjCMethod && isNonNullType(Context, lhsType))) &&
index e457de4..5c891be 100644 (file)
@@ -15,6 +15,7 @@
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/ExprObjC.h"
+#include "clang/AST/ExprOpenMP.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Sema/Designator.h"
@@ -6348,18 +6349,28 @@ enum ReferenceKind {
   RK_StdInitializerList,
 };
 
-/// A temporary or local variable.
-using Local = llvm::PointerUnion<MaterializeTemporaryExpr*, ValueDecl*>;
+/// A temporary or local variable. This will be one of:
+///  * A MaterializeTemporaryExpr.
+///  * A DeclRefExpr whose declaration is a local.
+///  * An AddrLabelExpr.
+///  * A BlockExpr for a block with captures.
+using Local = Expr*;
 
 /// Expressions we stepped over when looking for the local state. Any steps
 /// that would inhibit lifetime extension or take us out of subexpressions of
 /// the initializer are included.
 struct IndirectLocalPathEntry {
-  enum {
+  enum EntryKind {
     DefaultInit,
     AddressOf,
+    VarInit,
+    LValToRVal,
   } Kind;
   Expr *E;
+  Decl *D = nullptr;
+  IndirectLocalPathEntry() {}
+  IndirectLocalPathEntry(EntryKind K, Expr *E) : Kind(K), E(E) {}
+  IndirectLocalPathEntry(EntryKind K, Expr *E, Decl *D) : Kind(K), E(E), D(D) {}
 };
 
 using IndirectLocalPath = llvm::SmallVectorImpl<IndirectLocalPathEntry>;
@@ -6370,16 +6381,24 @@ struct RevertToOldSizeRAII {
   RevertToOldSizeRAII(IndirectLocalPath &Path) : Path(Path) {}
   ~RevertToOldSizeRAII() { Path.resize(OldSize); }
 };
+
+using LocalVisitor = llvm::function_ref<bool(IndirectLocalPath &Path, Local L,
+                                             ReferenceKind RK)>;
+}
+
+static bool isVarOnPath(IndirectLocalPath &Path, VarDecl *VD) {
+  for (auto E : Path)
+    if (E.Kind == IndirectLocalPathEntry::VarInit && E.D == VD)
+      return true;
+  return false;
 }
 
-template <typename LocalVisitor>
 static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
                                              Expr *Init, LocalVisitor Visit,
                                              bool RevisitSubinits);
 
 /// Visit the locals that would be reachable through a reference bound to the
 /// glvalue expression \c Init.
-template <typename LocalVisitor>
 static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
                                                   Expr *Init, ReferenceKind RK,
                                                   LocalVisitor Visit) {
@@ -6390,6 +6409,9 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
   do {
     Old = Init;
 
+    if (auto *EWC = dyn_cast<ExprWithCleanups>(Init))
+      Init = EWC->getSubExpr();
+
     if (InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) {
       // If this is just redundant braces around an initializer, step over it.
       if (ILE->isTransparent())
@@ -6409,20 +6431,22 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
     // Per the current approach for DR1299, look through array element access
     // on array glvalues when performing lifetime extension.
     if (auto *ASE = dyn_cast<ArraySubscriptExpr>(Init)) {
-      auto *ICE = dyn_cast<ImplicitCastExpr>(ASE->getBase());
-      if (!ICE || ICE->getCastKind() != CK_ArrayToPointerDecay)
-        return;
-      Init = ICE->getSubExpr();
+      Init = ASE->getBase();
+      auto *ICE = dyn_cast<ImplicitCastExpr>(Init);
+      if (ICE && ICE->getCastKind() == CK_ArrayToPointerDecay)
+        Init = ICE->getSubExpr();
+      else
+        // We can't lifetime extend through this but we might still find some
+        // retained temporaries.
+        return visitLocalsRetainedByInitializer(Path, Init, Visit, true);
     }
 
     // Step into CXXDefaultInitExprs so we can diagnose cases where a
     // constructor inherits one as an implicit mem-initializer.
     if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(Init)) {
-      Path.push_back({IndirectLocalPathEntry::DefaultInit, DIE});
+      Path.push_back(
+          {IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()});
       Init = DIE->getExpr();
-
-      if (auto *EWC = dyn_cast<ExprWithCleanups>(Init))
-        Init = EWC->getSubExpr();
     }
   } while (Init != Old);
 
@@ -6432,22 +6456,64 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
                                        true);
   }
 
-  // If we find the name of a local non-reference parameter, we could have a
-  // lifetime problem.
-  if (auto *DRE = dyn_cast<DeclRefExpr>(Init->IgnoreParens())) {
+  switch (Init->getStmtClass()) {
+  case Stmt::DeclRefExprClass: {
+    // If we find the name of a local non-reference parameter, we could have a
+    // lifetime problem.
+    auto *DRE = cast<DeclRefExpr>(Init);
     auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
     if (VD && VD->hasLocalStorage() &&
         !DRE->refersToEnclosingVariableOrCapture()) {
-      // FIXME: Recurse to the initializer of a local reference.
-      if (!VD->getType()->isReferenceType())
-        Visit(Path, Local(VD), RK);
+      if (!VD->getType()->isReferenceType()) {
+        Visit(Path, Local(DRE), RK);
+      } else if (isa<ParmVarDecl>(DRE->getDecl())) {
+        // The lifetime of a reference parameter is unknown; assume it's OK
+        // for now.
+        break;
+      } else if (VD->getInit() && !isVarOnPath(Path, VD)) {
+        Path.push_back({IndirectLocalPathEntry::VarInit, DRE, VD});
+        visitLocalsRetainedByReferenceBinding(Path, VD->getInit(),
+                                              RK_ReferenceBinding, Visit);
+      }
     }
+    break;
+  }
+
+  case Stmt::UnaryOperatorClass: {
+    // The only unary operator that make sense to handle here
+    // is Deref.  All others don't resolve to a "name."  This includes
+    // handling all sorts of rvalues passed to a unary operator.
+    const UnaryOperator *U = cast<UnaryOperator>(Init);
+    if (U->getOpcode() == UO_Deref)
+      visitLocalsRetainedByInitializer(Path, U->getSubExpr(), Visit, true);
+    break;
+  }
+
+  case Stmt::OMPArraySectionExprClass: {
+    visitLocalsRetainedByInitializer(
+        Path, cast<OMPArraySectionExpr>(Init)->getBase(), Visit, true);
+    break;
+  }
+
+  case Stmt::ConditionalOperatorClass:
+  case Stmt::BinaryConditionalOperatorClass: {
+    auto *C = cast<AbstractConditionalOperator>(Init);
+    if (!C->getTrueExpr()->getType()->isVoidType())
+      visitLocalsRetainedByReferenceBinding(Path, C->getTrueExpr(), RK, Visit);
+    if (!C->getFalseExpr()->getType()->isVoidType())
+      visitLocalsRetainedByReferenceBinding(Path, C->getFalseExpr(), RK, Visit);
+    break;
+  }
+
+  // FIXME: Visit the left-hand side of an -> or ->*.
+
+  default:
+    break;
   }
 }
 
 /// Visit the locals that would be reachable through an object initialized by
 /// the prvalue expression \c Init.
-template <typename LocalVisitor>
 static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
                                              Expr *Init, LocalVisitor Visit,
                                              bool RevisitSubinits) {
@@ -6456,13 +6522,13 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
   // Step into CXXDefaultInitExprs so we can diagnose cases where a
   // constructor inherits one as an implicit mem-initializer.
   if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(Init)) {
-    Path.push_back({IndirectLocalPathEntry::DefaultInit, DIE});
+    Path.push_back({IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()});
     Init = DIE->getExpr();
-
-    if (auto *EWC = dyn_cast<ExprWithCleanups>(Init))
-      Init = EWC->getSubExpr();
   }
 
+  if (auto *EWC = dyn_cast<ExprWithCleanups>(Init))
+    Init = EWC->getSubExpr();
+
   // Dig out the expression which constructs the extended temporary.
   Init = const_cast<Expr *>(Init->skipRValueSubobjectAdjustments());
 
@@ -6528,15 +6594,119 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
     return;
   }
 
-  // If the initializer is the address of a local, we could have a lifetime
-  // problem.
-  if (auto *Op = dyn_cast<UnaryOperator>(Init->IgnoreParenImpCasts())) {
-    if (Op->getOpcode() == UO_AddrOf) {
-      Path.push_back({IndirectLocalPathEntry::AddressOf, Op});
-      Init = Op->getSubExpr();
-      return visitLocalsRetainedByReferenceBinding(Path, Init,
+  // Step over value-preserving rvalue casts.
+  while (auto *CE = dyn_cast<CastExpr>(Init)) {
+    switch (CE->getCastKind()) {
+    case CK_LValueToRValue:
+      // If we can match the lvalue to a const object, we can look at its
+      // initializer.
+      Path.push_back({IndirectLocalPathEntry::LValToRVal, CE});
+      return visitLocalsRetainedByReferenceBinding(
+          Path, Init, RK_ReferenceBinding,
+          [&](IndirectLocalPath &Path, Local L, ReferenceKind RK) -> bool {
+        if (auto *DRE = dyn_cast<DeclRefExpr>(L)) {
+          auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
+          if (VD && VD->getType().isConstQualified() && VD->getInit()) {
+            Path.push_back({IndirectLocalPathEntry::VarInit, DRE, VD});
+            visitLocalsRetainedByInitializer(Path, VD->getInit(), Visit, true);
+          }
+        } else if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L)) {
+          if (MTE->getType().isConstQualified())
+            visitLocalsRetainedByInitializer(Path, MTE->GetTemporaryExpr(),
+                                             Visit, true);
+        }
+        return false;
+      });
+
+      // We assume that objects can be retained by pointers cast to integers,
+      // but not if the integer is cast to floating-point type or to _Complex.
+      // We assume that casts to 'bool' do not preserve enough information to
+      // retain a local object.
+    case CK_NoOp:
+    case CK_BitCast:
+    case CK_BaseToDerived:
+    case CK_DerivedToBase:
+    case CK_UncheckedDerivedToBase:
+    case CK_Dynamic:
+    case CK_ToUnion:
+    case CK_IntegralToPointer:
+    case CK_PointerToIntegral:
+    case CK_VectorSplat:
+    case CK_IntegralCast:
+    case CK_CPointerToObjCPointerCast:
+    case CK_BlockPointerToObjCPointerCast:
+    case CK_AnyPointerToBlockPointerCast:
+    case CK_AddressSpaceConversion:
+      break;
+
+    case CK_ArrayToPointerDecay:
+      // Model array-to-pointer decay as taking the address of the array
+      // lvalue.
+      Path.push_back({IndirectLocalPathEntry::AddressOf, CE});
+      return visitLocalsRetainedByReferenceBinding(Path, CE->getSubExpr(),
                                                    RK_ReferenceBinding, Visit);
+
+    default:
+      return;
+    }
+
+    Init = CE->getSubExpr();
+  }
+
+  Init = Init->IgnoreParens();
+  switch (Init->getStmtClass()) {
+  case Stmt::UnaryOperatorClass: {
+    auto *UO = cast<UnaryOperator>(Init);
+    // If the initializer is the address of a local, we could have a lifetime
+    // problem.
+    if (UO->getOpcode() == UO_AddrOf) {
+      Path.push_back({IndirectLocalPathEntry::AddressOf, UO});
+      visitLocalsRetainedByReferenceBinding(Path, UO->getSubExpr(),
+                                            RK_ReferenceBinding, Visit);
+    }
+    break;
+  }
+
+  case Stmt::BinaryOperatorClass: {
+    // Handle pointer arithmetic.
+    auto *BO = cast<BinaryOperator>(Init);
+    BinaryOperatorKind BOK = BO->getOpcode();
+    if (!BO->getType()->isPointerType() || (BOK != BO_Add && BOK != BO_Sub))
+      break;
+
+    if (BO->getLHS()->getType()->isPointerType())
+      visitLocalsRetainedByInitializer(Path, BO->getLHS(), Visit, true);
+    else if (BO->getRHS()->getType()->isPointerType())
+      visitLocalsRetainedByInitializer(Path, BO->getRHS(), Visit, true);
+    break;
+  }
+
+  case Stmt::ConditionalOperatorClass:
+  case Stmt::BinaryConditionalOperatorClass: {
+    auto *C = cast<AbstractConditionalOperator>(Init);
+    // In C++, we can have a throw-expression operand, which has 'void' type
+    // and isn't interesting from a lifetime perspective.
+    if (!C->getTrueExpr()->getType()->isVoidType())
+      visitLocalsRetainedByInitializer(Path, C->getTrueExpr(), Visit, true);
+    if (!C->getFalseExpr()->getType()->isVoidType())
+      visitLocalsRetainedByInitializer(Path, C->getFalseExpr(), Visit, true);
+    break;
+  }
+
+  case Stmt::BlockExprClass:
+    if (cast<BlockExpr>(Init)->getBlockDecl()->hasCaptures()) {
+      // This is a local block, whose lifetime is that of the function.
+      Visit(Path, Local(cast<BlockExpr>(Init)), RK_ReferenceBinding);
     }
+    break;
+
+  case Stmt::AddrLabelExprClass:
+    // We want to warn if the address of a label would escape the function.
+    Visit(Path, Local(cast<AddrLabelExpr>(Init)), RK_ReferenceBinding);
+    break;
+
+  default:
+    break;
   }
 }
 
@@ -6563,58 +6733,20 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
 
   auto TemporaryVisitor = [&](IndirectLocalPath &Path, Local L,
                               ReferenceKind RK) -> bool {
-    // If we found a path to a local variable or similar, check whether the
-    // initialized object will outlive it.
-    if (auto *VD = L.dyn_cast<ValueDecl*>()) {
-      switch (LK) {
-      case LK_FullExpression:
-        llvm_unreachable("already handled this");
-
-      case LK_Extended:
-        break;
-
-      case LK_MemInitializer: {
-        // Paths via a default initializer can only occur during error recovery
-        // (there's no other way that a default initializer can refer to a
-        // local). Don't produce a bogus warning on those cases.
-        if (std::any_of(Path.begin(), Path.end(), [](IndirectLocalPathEntry E) {
-              return E.Kind == IndirectLocalPathEntry::DefaultInit;
-            }))
-          break;
-
-        if (auto *Member =
-                ExtendingEntity ? ExtendingEntity->getDecl() : nullptr) {
-          bool AddressTaken =
-              !Path.empty() &&
-              Path.back().Kind == IndirectLocalPathEntry::AddressOf;
-          Diag(Init->getExprLoc(),
-               AddressTaken ? diag::warn_init_ptr_member_to_parameter_addr
-                            : diag::warn_bind_ref_member_to_parameter)
-              << Member << VD << isa<ParmVarDecl>(VD) << Init->getSourceRange();
-          Diag(Member->getLocation(),
-               diag::note_ref_or_ptr_member_declared_here)
-              << (unsigned)AddressTaken;
-        }
-        break;
-      }
-
-      case LK_New:
-        break;
-
-      case LK_Return:
-      case LK_StmtExprResult:
-        // FIXME: Move -Wreturn-stack-address checks here.
-        return false;
-      }
-      return false;
-    }
+    Expr *First = Path.empty() ? L : Path.front().E;
+    SourceLocation DiagLoc = First->getLocStart();
+    SourceRange DiagRange = First->getSourceRange();
 
-    auto *MTE = L.get<MaterializeTemporaryExpr*>();
     switch (LK) {
     case LK_FullExpression:
       llvm_unreachable("already handled this");
 
-    case LK_Extended:
+    case LK_Extended: {
+      auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L);
+      if (!MTE)
+        // FIXME: Warn on this.
+        return false;
+
       // Lifetime-extend the temporary.
       if (Path.empty()) {
         // Update the storage duration of the materialized temporary.
@@ -6632,70 +6764,138 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
         // FIXME: Properly handle this situation. Perhaps the easiest approach
         // would be to clone the initializer expression on each use that would
         // lifetime extend its temporaries.
-        Diag(MTE->getExprLoc(),
+        Diag(DiagLoc,
              RK == RK_ReferenceBinding
                  ? diag::warn_default_member_init_temporary_not_extended
-                 : diag::warn_default_member_init_init_list_not_extended);
+                 : diag::warn_default_member_init_init_list_not_extended)
+            << DiagRange;
       } else {
         // FIXME: Warn on this.
+        return false;
       }
       break;
+    }
 
-    case LK_MemInitializer:
-      // Under C++ DR1696, if a mem-initializer (or a default member
-      // initializer used by the absence of one) would lifetime-extend a
-      // temporary, the program is ill-formed.
-      if (auto *ExtendingDecl =
-              ExtendingEntity ? ExtendingEntity->getDecl() : nullptr) {
-        bool IsSubobjectMember = ExtendingEntity != &Entity;
-        Diag(MTE->getExprLoc(), diag::err_bind_ref_member_to_temporary)
-            << ExtendingDecl << Init->getSourceRange() << IsSubobjectMember
-            << RK;
-        // Don't bother adding a note pointing to the field if we're inside its
-        // default member initializer; our primary diagnostic points to the
-        // same place in that case.
-        if (Path.empty() ||
-            Path.back().Kind != IndirectLocalPathEntry::DefaultInit) {
-          Diag(ExtendingDecl->getLocation(),
-               diag::note_lifetime_extending_member_declared_here)
-              << RK << IsSubobjectMember;
+    case LK_MemInitializer: {
+      if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L)) {
+        // Under C++ DR1696, if a mem-initializer (or a default member
+        // initializer used by the absence of one) would lifetime-extend a
+        // temporary, the program is ill-formed.
+        if (auto *ExtendingDecl =
+                ExtendingEntity ? ExtendingEntity->getDecl() : nullptr) {
+          bool IsSubobjectMember = ExtendingEntity != &Entity;
+          Diag(DiagLoc, diag::err_bind_ref_member_to_temporary)
+              << ExtendingDecl << IsSubobjectMember << RK << DiagRange;
+          // Don't bother adding a note pointing to the field if we're inside
+          // its default member initializer; our primary diagnostic points to
+          // the same place in that case.
+          if (Path.empty() ||
+              Path.back().Kind != IndirectLocalPathEntry::DefaultInit) {
+            Diag(ExtendingDecl->getLocation(),
+                 diag::note_lifetime_extending_member_declared_here)
+                << RK << IsSubobjectMember;
+          }
+        } else {
+          // We have a mem-initializer but no particular field within it; this
+          // is either a base class or a delegating initializer directly
+          // initializing the base-class from something that doesn't live long
+          // enough.
+          //
+          // FIXME: Warn on this.
+          return false;
         }
       } else {
-        // We have a mem-initializer but no particular field within it; this
-        // is either a base class or a delegating initializer directly
-        // initializing the base-class from something that doesn't live long
-        // enough. Either way, that can't happen.
-        // FIXME: Move CheckForDanglingReferenceOrPointer checks here.
-        llvm_unreachable(
-            "temporary initializer for base class / delegating ctor");
+        // Paths via a default initializer can only occur during error recovery
+        // (there's no other way that a default initializer can refer to a
+        // local). Don't produce a bogus warning on those cases.
+        if (std::any_of(Path.begin(), Path.end(), [](IndirectLocalPathEntry E) {
+              return E.Kind == IndirectLocalPathEntry::DefaultInit;
+            }))
+          return false;
+
+        auto *DRE = dyn_cast<DeclRefExpr>(L);
+        auto *VD = DRE ? dyn_cast<VarDecl>(DRE->getDecl()) : nullptr;
+        if (!VD) {
+          // A member was initialized to a local block.
+          // FIXME: Warn on this.
+          return false;
+        }
+
+        if (auto *Member =
+                ExtendingEntity ? ExtendingEntity->getDecl() : nullptr) {
+          bool IsPointer = Member->getType()->isAnyPointerType();
+          Diag(DiagLoc, IsPointer ? diag::warn_init_ptr_member_to_parameter_addr
+                                  : diag::warn_bind_ref_member_to_parameter)
+              << Member << VD << isa<ParmVarDecl>(VD) << DiagRange;
+          Diag(Member->getLocation(),
+               diag::note_ref_or_ptr_member_declared_here)
+              << (unsigned)IsPointer;
+        }
       }
       break;
+    }
 
     case LK_New:
-      if (RK == RK_ReferenceBinding) {
-        Diag(MTE->getExprLoc(), diag::warn_new_dangling_reference);
+      if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L)) {
+        Diag(DiagLoc, RK == RK_ReferenceBinding
+                          ? diag::warn_new_dangling_reference
+                          : diag::warn_new_dangling_initializer_list)
+            << (ExtendingEntity != &Entity) << DiagRange;
       } else {
-        Diag(MTE->getExprLoc(), diag::warn_new_dangling_initializer_list)
-            << (ExtendingEntity != &Entity);
+        // We can't determine if the allocation outlives the local declaration.
+        return false;
       }
       break;
 
     case LK_Return:
     case LK_StmtExprResult:
-      // FIXME: Move -Wreturn-stack-address checks here.
-      return false;
+      if (auto *DRE = dyn_cast<DeclRefExpr>(L)) {
+        // We can't determine if the local variable outlives the statement
+        // expression.
+        if (LK == LK_StmtExprResult)
+          return false;
+        Diag(DiagLoc, diag::warn_ret_stack_addr_ref)
+            << Entity.getType()->isReferenceType() << DRE->getDecl()
+            << isa<ParmVarDecl>(DRE->getDecl()) << DiagRange;
+      } else if (isa<BlockExpr>(L)) {
+        Diag(DiagLoc, diag::err_ret_local_block) << DiagRange;
+      } else if (isa<AddrLabelExpr>(L)) {
+        Diag(DiagLoc, diag::warn_ret_addr_label) << DiagRange;
+      } else {
+        Diag(DiagLoc, diag::warn_ret_local_temp_addr_ref)
+         << Entity.getType()->isReferenceType() << DiagRange;
+      }
+      break;
     }
 
-    // FIXME: Model these as CodeSynthesisContexts to fix the note emission
-    // order.
-    for (auto Elem : llvm::reverse(Path)) {
+    for (unsigned I = 0; I != Path.size(); ++I) {
+      auto Elem = Path[I];
+
+      // Highlight the range of the next step within this path element.
+      SourceRange Range;
+      if (I < Path.size() - 1)
+        Range = Path[I + 1].E->getSourceRange();
+      else
+        Range = L->getSourceRange();
+
       switch (Elem.Kind) {
-      case IndirectLocalPathEntry::DefaultInit:
-        Diag(Elem.E->getExprLoc(), diag::note_in_default_member_initalizer_here)
-            << cast<CXXDefaultInitExpr>(Elem.E)->getField();
+      case IndirectLocalPathEntry::AddressOf:
+      case IndirectLocalPathEntry::LValToRVal:
+        // These exist primarily to mark the path as not permitting lifetime
+        // extension.
         break;
 
-      case IndirectLocalPathEntry::AddressOf:
+      case IndirectLocalPathEntry::DefaultInit: {
+        auto *FD = cast<FieldDecl>(Elem.D);
+        Diag(FD->getLocation(), diag::note_init_with_default_member_initalizer)
+            << FD << Range;
+        break;
+      }
+
+      case IndirectLocalPathEntry::VarInit:
+        const VarDecl *VD = cast<VarDecl>(Elem.D);
+        Diag(VD->getLocation(), diag::note_local_var_initializer)
+            << VD->getType()->isReferenceType() << VD->getDeclName() << Range;
         break;
       }
     }
index 4026cee..721051f 100644 (file)
@@ -6,7 +6,7 @@ int* f1() {
 }
 
 int* f2(int y) {
-  return &y;  // expected-warning{{Address of stack memory associated with local variable 'y' returned}} expected-warning{{address of stack memory associated with local variable 'y' returned}}
+  return &y;  // expected-warning{{Address of stack memory associated with local variable 'y' returned}} expected-warning{{address of stack memory associated with parameter 'y' returned}}
 }
 
 int* f3(int x, int *y) {
index 79afd18..e1f0683 100644 (file)
@@ -91,8 +91,8 @@ struct TS {
 
 // rdar://11345441
 int* f5() {
-  int& i = i; // expected-warning {{Assigned value is garbage or undefined}} expected-note {{binding reference variable 'i' here}} expected-warning{{reference 'i' is not yet bound to a value when used within its own initialization}}
-  return &i; // expected-warning {{address of stack memory associated with local variable 'i' returned}}
+  int& i = i; // expected-warning {{Assigned value is garbage or undefined}} expected-warning{{reference 'i' is not yet bound to a value when used within its own initialization}}
+  return &i;
 }
 
 void *radar13226577() {
index a037d12..8a1519e 100644 (file)
@@ -37,7 +37,7 @@ void test_multi_return() {
 
 intptr_t returnAsNonLoc() {
   int x;
-  return (intptr_t)&x; // expected-warning{{Address of stack memory associated with local variable 'x' returned to caller}}
+  return (intptr_t)&x; // expected-warning{{Address of stack memory associated with local variable 'x' returned to caller}} expected-warning{{address of stack memory associated with local variable 'x' returned}}
 }
 
 bool returnAsBool() {
index 54648cf..d9d404c 100644 (file)
@@ -317,23 +317,25 @@ namespace dr1696 { // dr1696: 7
   //   D1 d1 = {A()};
   // ... which lifetime-extends the A temporary.
   struct D1 {
-    const A &a = A();
 #if __cplusplus < 201402L
     // expected-error@-2 {{binds to a temporary}}
-    // expected-note@-4 {{here}}
-#else
-    // expected-warning-re@-5 {{sorry, lifetime extension {{.*}} not supported}}
 #endif
+    const A &a = A(); // expected-note {{default member init}}
   };
-  D1 d1 = {}; // expected-note {{here}}
+  D1 d1 = {};
+#if __cplusplus < 201402L
+    // expected-note@-2 {{first required here}}
+#else
+    // expected-warning-re@-4 {{sorry, lifetime extension {{.*}} not supported}}
+#endif
 
   struct D2 {
-    const A &a = A(); // expected-error {{binds to a temporary}}
-    D2() {} // expected-note {{used here}}
+    const A &a = A(); // expected-note {{default member init}}
+    D2() {} // expected-error {{binds to a temporary}}
   };
 
-  struct D3 { // expected-note {{used here}}
-    const A &a = A(); // expected-error {{binds to a temporary}}
+  struct D3 { // expected-error {{binds to a temporary}}
+    const A &a = A(); // expected-note {{default member init}}
   };
   D3 d3; // expected-note {{first required here}}
 
@@ -352,14 +354,14 @@ namespace dr1696 { // dr1696: 7
     std::initializer_list<int> il = {1, 2, 3};
   };
 
-  struct haslist4 { // expected-note {{in default member initializer}}
-    std::initializer_list<int> il = {1, 2, 3}; // expected-error {{backing array for 'std::initializer_list' member 'il' is a temporary object}}
+  struct haslist4 { // expected-error {{backing array for 'std::initializer_list' member 'il' is a temporary object}}
+    std::initializer_list<int> il = {1, 2, 3}; // expected-note {{default member initializer}}
   };
   haslist4 hl4; // expected-note {{in implicit default constructor}}
 
   struct haslist5 {
-    std::initializer_list<int> il = {1, 2, 3}; // expected-error {{backing array for 'std::initializer_list' member 'il' is a temporary object}}
-    haslist5() {} // expected-note {{in default member initializer}}
+    std::initializer_list<int> il = {1, 2, 3}; // expected-note {{default member initializer}}
+    haslist5() {} // expected-error {{backing array for 'std::initializer_list' member 'il' is a temporary object}}
   };
 #endif
 }
index f6a4676..16f967b 100644 (file)
@@ -33,10 +33,10 @@ namespace dr1813 { // dr1813: 7
 namespace dr1815 { // dr1815: no
 #if __cplusplus >= 201402L
   // FIXME: needs codegen test
-  struct A { int &&r = 0; }; // FIXME expected-warning {{not supported}}
-  A a = {}; // expected-note {{here}}
+  struct A { int &&r = 0; }; // expected-note {{default member init}}
+  A a = {}; // FIXME expected-warning {{not supported}}
 
-  struct B { int &&r = 0; }; // expected-error {{binds to a temporary}} expected-note {{here}}
+  struct B { int &&r = 0; }; // expected-error {{binds to a temporary}} expected-note {{default member init}}
   B b; // expected-note {{here}}
 #endif
 }
index a4d0cdc..e7256bc 100644 (file)
@@ -128,11 +128,10 @@ struct RValue {
 RValue RVa;
 RValue RVb(RVa); // expected-error{{call to implicitly-deleted copy constructor}}
 
-// FIXME: The note on the class-name is attached to the location of the
+// FIXME: The error on the class-name is attached to the location of the
 // constructor. This is not especially clear.
-struct RValueTmp { // expected-note {{used here}}
-  int && ri = 1; // expected-note{{copy constructor of 'RValueTmp' is implicitly deleted because field 'ri' is of rvalue reference type 'int &&'}}
-  // expected-error@-1 {{reference member 'ri' binds to a temporary}}
+struct RValueTmp { // expected-error {{reference member 'ri' binds to a temporary}}
+  int && ri = 1; // expected-note{{copy constructor of 'RValueTmp' is implicitly deleted because field 'ri' is of rvalue reference type 'int &&'}} // expected-note {{default member init}}
 };
 RValueTmp RVTa; // expected-note {{implicit default constructor for 'RValueTmp' first required here}}
 RValueTmp RVTb(RVTa); // expected-error{{call to implicitly-deleted copy constructor}}
index 5558313..061a3d1 100644 (file)
@@ -47,7 +47,7 @@ class NotDeleted2c { int &&a = static_cast<int&&>(n); };
 NotDeleted2c nd2c;
 // Note: this one does not have a deleted default constructor even though the
 // implicit default constructor is ill-formed!
-class NotDeleted2d { int &&a = 0; }; // expected-error {{reference member 'a' binds to a temporary object}} expected-note {{here}}
+class NotDeleted2d { int &&a = 0; }; // expected-error {{reference member 'a' binds to a temporary object}} expected-note {{default member init}}
 NotDeleted2d nd2d; // expected-note {{first required here}}
 
 // - any non-variant non-static data member of const qualified type (or array
index de902a5..4cb2d43 100644 (file)
@@ -1,13 +1,13 @@
 // RUN: %clang_cc1 -verify %s -std=c++14
 
-template<const int I> struct S { // expected-note {{in default member initializer}}
+template<const int I> struct S { // expected-error {{reference member 'r' binds to a temporary object}}
   decltype(I) n;
-  int &&r = I; // expected-error {{reference member 'r' binds to a temporary object}}
+  int &&r = I; // expected-note {{default member initializer}}
 };
 S<5> s; // expected-note {{implicit default constructor}}
 
-template<typename T, T v> struct U { // expected-note {{in default member initializer}}
+template<typename T, T v> struct U { // expected-error {{reference member 'r' binds to a temporary object}}
   decltype(v) n;
-  int &&r = v; // expected-error {{reference member 'r' binds to a temporary object}}
+  int &&r = v; // expected-note {{default member initializer}}
 };
 U<const int, 6> u; // expected-note {{implicit default constructor}}
index 165c31a..0cef4aa 100644 (file)
@@ -31,8 +31,8 @@ void test_default_arg2() {
 }
 
 // Check that multiple CXXDefaultInitExprs don't cause an assertion failure.
-struct A { int &&r = 0; }; // expected-warning 2{{not supported}}
+struct A { int &&r = 0; }; // expected-note 2{{default member initializer}}
 struct B { A x, y; };
-B b = {}; // expected-note 2{{in default member initializer for field 'r' used here}}
+B b = {}; // expected-warning 2{{not supported}}
 
 }
index ece014d..f371891 100644 (file)
@@ -155,11 +155,11 @@ void dangle() {
 struct haslist1 {
   std::initializer_list<int> il // expected-note {{declared here}}
     = {1, 2, 3}; // ok, unused
-  std::initializer_list<int> jl{1, 2, 3}; // expected-error {{backing array for 'std::initializer_list' member 'jl' is a temporary object}}
+  std::initializer_list<int> jl{1, 2, 3}; // expected-note {{default member init}}
   haslist1();
 };
 
-haslist1::haslist1() // expected-note {{used here}}
+haslist1::haslist1() // expected-error {{backing array for 'std::initializer_list' member 'jl' is a temporary object}}
 : il{1, 2, 3} // expected-error {{backing array for 'std::initializer_list' member 'il' is a temporary object}}
 {}
 
index 33bde75..60c2dee 100644 (file)
@@ -27,9 +27,9 @@ namespace pr33140_0b {
 namespace pr33140_2 {
   // FIXME: The declaration of 'b' below should lifetime-extend two int
   // temporaries.
-  struct A { int &&r = 0; }; // expected-warning 2{{not supported}}
+  struct A { int &&r = 0; }; // expected-note 2{{initializing field 'r' with default member initializer}}
   struct B { A x, y; };
-  B b = {}; // expected-note 2{{used here}}
+  B b = {}; // expected-warning 2{{not supported}}
 }
 
 namespace pr33140_3 {
index 47b4595..e848189 100644 (file)
@@ -65,7 +65,7 @@ const int *int6() {
 
 const int *int7(int x) {
   const int &x2 = x;  // expected-note{{binding reference variable 'x2' here}}
-  return &x2;  //  expected-warning{{address of stack memory associated with local variable 'x' returned}}
+  return &x2;  //  expected-warning{{address of stack memory associated with parameter 'x' returned}}
 }
 
 const int *int8(const int &x = 5) {
index 7670798..a5f84ad 100644 (file)
@@ -63,7 +63,8 @@ int& ret_local_field_ref() {
 int* ret_conditional(bool cond) {
   int x = 1;
   int y = 2;
-  return cond ? &x : &y; // expected-warning {{address of stack memory}}
+  return cond ? &x // expected-warning {{address of stack memory associated with local variable 'x' returned}}
+              : &y; // expected-warning {{address of stack memory associated with local variable 'y' returned}}
 }
 
 int* ret_conditional_rhs(int *x, bool cond) {
index 94b09ce..838bf48 100644 (file)
@@ -65,10 +65,9 @@ void f() {
 }
 
 int&& should_warn(int i) {
-  // FIXME: The stack address return test doesn't reason about casts.
-  return static_cast<int&&>(i); // xpected-warning {{returning reference to temporary}}
+  return static_cast<int&&>(i); // expected-warning {{reference to stack memory associated with parameter 'i' returned}}
 }
-int&& should_not_warn(int&& i) { // But GCC 4.4 does
+int&& should_not_warn(int&& i) {
   return static_cast<int&&>(i);
 }