]> granicus.if.org Git - clang/commitdiff
Fold dangling-field warning into general initialization lifetime checks.
authorRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 20 Jul 2018 22:25:55 +0000 (22:25 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 20 Jul 2018 22:25:55 +0000 (22:25 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@337627 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/DiagnosticSemaKinds.td
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaInit.cpp
test/Analysis/cxx-uninitialized-object-ptr-ref.cpp
test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp

index 5ae749fa9331eeef62cd7ccbfa7ff49cac434263..e45f5f3376accec6e308d804d231dab49cf072fa 100644 (file)
@@ -7870,11 +7870,11 @@ def note_ref_var_local_bind : Note<
 // Check for initializing a member variable with the address or a reference to
 // a constructor parameter.
 def warn_bind_ref_member_to_parameter : Warning<
-  "binding reference member %0 to stack allocated parameter %1">,
-  InGroup<DanglingField>;
+  "binding reference member %0 to stack allocated "
+  "%select{variable|parameter}2 %1">, InGroup<DanglingField>;
 def warn_init_ptr_member_to_parameter_addr : Warning<
-  "initializing pointer member %0 with the stack address of parameter %1">,
-  InGroup<DanglingField>;
+  "initializing pointer member %0 with the stack address of "
+  "%select{variable|parameter}2 %1">, InGroup<DanglingField>;
 def note_ref_or_ptr_member_declared_here : Note<
   "%select{reference|pointer}0 member declared here">;
 
index d2a2bb202066fb897a7727d47fd4a84c426a0ecd..031f8dbb38c45c80d8d3fd396c831f2bd2598035 100644 (file)
@@ -3946,53 +3946,6 @@ Sema::BuildMemInitializer(Decl *ConstructorD,
   return BuildBaseInitializer(BaseType, TInfo, Init, ClassDecl, EllipsisLoc);
 }
 
-/// Checks a member initializer expression for cases where reference (or
-/// pointer) members are bound to by-value parameters (or their addresses).
-static void CheckForDanglingReferenceOrPointer(Sema &S, ValueDecl *Member,
-                                               Expr *Init,
-                                               SourceLocation IdLoc) {
-  QualType MemberTy = Member->getType();
-
-  // We only handle pointers and references currently.
-  // FIXME: Would this be relevant for ObjC object pointers? Or block pointers?
-  if (!MemberTy->isReferenceType() && !MemberTy->isPointerType())
-    return;
-
-  const bool IsPointer = MemberTy->isPointerType();
-  if (IsPointer) {
-    if (const UnaryOperator *Op
-          = dyn_cast<UnaryOperator>(Init->IgnoreParenImpCasts())) {
-      // The only case we're worried about with pointers requires taking the
-      // address.
-      if (Op->getOpcode() != UO_AddrOf)
-        return;
-
-      Init = Op->getSubExpr();
-    } else {
-      // We only handle address-of expression initializers for pointers.
-      return;
-    }
-  }
-
-  if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Init->IgnoreParens())) {
-    // We only warn when referring to a non-reference parameter declaration.
-    const ParmVarDecl *Parameter = dyn_cast<ParmVarDecl>(DRE->getDecl());
-    if (!Parameter || Parameter->getType()->isReferenceType())
-      return;
-
-    S.Diag(Init->getExprLoc(),
-           IsPointer ? diag::warn_init_ptr_member_to_parameter_addr
-                     : diag::warn_bind_ref_member_to_parameter)
-      << Member << Parameter << Init->getSourceRange();
-  } else {
-    // Other initializers are fine.
-    return;
-  }
-
-  S.Diag(Member->getLocation(), diag::note_ref_or_ptr_member_declared_here)
-    << (unsigned)IsPointer;
-}
-
 MemInitResult
 Sema::BuildMemberInitializer(ValueDecl *Member, Expr *Init,
                              SourceLocation IdLoc) {
@@ -4047,8 +4000,6 @@ Sema::BuildMemberInitializer(ValueDecl *Member, Expr *Init,
     if (MemberInit.isInvalid())
       return true;
 
-    CheckForDanglingReferenceOrPointer(*this, Member, MemberInit.get(), IdLoc);
-
     // C++11 [class.base.init]p7:
     //   The initialization of each base and member constitutes a
     //   full-expression.
index f94eabd67192632dad682783b217ecbde7f4fe4b..73643a50fcfdfb0cc1154784ced3ef6ef9691922 100644 (file)
@@ -6227,7 +6227,7 @@ using LifetimeResult =
 /// Determine the declaration which an initialized entity ultimately refers to,
 /// for the purpose of lifetime-extending a temporary bound to a reference in
 /// the initialization of \p Entity.
-static LifetimeResult getEntityForTemporaryLifetimeExtension(
+static LifetimeResult getEntityLifetime(
     const InitializedEntity *Entity,
     const InitializedEntity *InitField = nullptr) {
   // C++11 [class.temporary]p5:
@@ -6239,8 +6239,7 @@ static LifetimeResult getEntityForTemporaryLifetimeExtension(
   case InitializedEntity::EK_Member:
     // For subobjects, we look at the complete object.
     if (Entity->getParent())
-      return getEntityForTemporaryLifetimeExtension(Entity->getParent(),
-                                                    Entity);
+      return getEntityLifetime(Entity->getParent(), Entity);
 
     //   except:
     // C++17 [class.base.init]p8:
@@ -6291,14 +6290,12 @@ static LifetimeResult getEntityForTemporaryLifetimeExtension(
 
   case InitializedEntity::EK_ArrayElement:
     // For subobjects, we look at the complete object.
-    return getEntityForTemporaryLifetimeExtension(Entity->getParent(),
-                                                  InitField);
+    return getEntityLifetime(Entity->getParent(), InitField);
 
   case InitializedEntity::EK_Base:
     // For subobjects, we look at the complete object.
     if (Entity->getParent())
-      return getEntityForTemporaryLifetimeExtension(Entity->getParent(),
-                                                    InitField);
+      return getEntityLifetime(Entity->getParent(), InitField);
     return {InitField, LK_MemInitializer};
 
   case InitializedEntity::EK_Delegating:
@@ -6311,46 +6308,61 @@ static LifetimeResult getEntityForTemporaryLifetimeExtension(
   case InitializedEntity::EK_BlockElement:
   case InitializedEntity::EK_LambdaToBlockConversionBlockElement:
   case InitializedEntity::EK_LambdaCapture:
-  case InitializedEntity::EK_Exception:
   case InitializedEntity::EK_VectorElement:
   case InitializedEntity::EK_ComplexElement:
     return {nullptr, LK_FullExpression};
+
+  case InitializedEntity::EK_Exception:
+    // FIXME: Can we diagnose lifetime problems with exceptions?
+    return {nullptr, LK_FullExpression};
   }
   llvm_unreachable("unknown entity kind");
 }
 
 namespace {
-enum ExtensionKind {
+enum ReferenceKind {
   /// Lifetime would be extended by a reference binding to a temporary.
-  EK_ReferenceBinding,
+  RK_ReferenceBinding,
   /// Lifetime would be extended by a std::initializer_list object binding to
   /// its backing array.
-  EK_StdInitializerList,
+  RK_StdInitializerList,
+};
+
+/// A temporary or local variable.
+using Local = llvm::PointerUnion<MaterializeTemporaryExpr*, ValueDecl*>;
+
+/// 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 {
+    DefaultInit,
+    AddressOf,
+  } Kind;
+  Expr *E;
 };
-using IndirectTemporaryPathEntry =
-    llvm::PointerUnion<CXXDefaultInitExpr *, ValueDecl *>;
-using IndirectTemporaryPath = llvm::SmallVectorImpl<IndirectTemporaryPathEntry>;
+
+using IndirectLocalPath = llvm::SmallVectorImpl<IndirectLocalPathEntry>;
 
 struct RevertToOldSizeRAII {
-  IndirectTemporaryPath &Path;
+  IndirectLocalPath &Path;
   unsigned OldSize = Path.size();
-  RevertToOldSizeRAII(IndirectTemporaryPath &Path) : Path(Path) {}
+  RevertToOldSizeRAII(IndirectLocalPath &Path) : Path(Path) {}
   ~RevertToOldSizeRAII() { Path.resize(OldSize); }
 };
 }
 
-template <typename TemporaryVisitor>
-static void visitTemporariesExtendedByInitializer(IndirectTemporaryPath &Path,
-                                                  Expr *Init,
-                                                  TemporaryVisitor Visit);
+template <typename LocalVisitor>
+static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
+                                             Expr *Init, LocalVisitor Visit,
+                                             bool RevisitSubinits);
 
-/// Visit the temporaries whose lifetimes would be extended by binding a
-/// reference to the glvalue expression \c Init.
-template <typename TemporaryVisitor>
-static void
-visitTemporariesExtendedByReferenceBinding(IndirectTemporaryPath &Path,
-                                           Expr *Init, ExtensionKind EK,
-                                           TemporaryVisitor Visit) {
+/// 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) {
   RevertToOldSizeRAII RAII(Path);
 
   // Walk past any constructs which we can lifetime-extend across.
@@ -6382,7 +6394,7 @@ visitTemporariesExtendedByReferenceBinding(IndirectTemporaryPath &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(DIE);
+      Path.push_back({IndirectLocalPathEntry::DefaultInit, DIE});
       Init = DIE->getExpr();
 
       if (auto *EWC = dyn_cast<ExprWithCleanups>(Init))
@@ -6391,25 +6403,36 @@ visitTemporariesExtendedByReferenceBinding(IndirectTemporaryPath &Path,
   } while (Init != Old);
 
   if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Init)) {
-    if (Visit(Path, MTE, EK))
-      visitTemporariesExtendedByInitializer(Path, MTE->GetTemporaryExpr(),
-                                            Visit);
+    if (Visit(Path, Local(MTE), RK))
+      visitLocalsRetainedByInitializer(Path, MTE->GetTemporaryExpr(), Visit,
+                                       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())) {
+    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);
+    }
   }
 }
 
-/// Visit the temporaries whose lifetimes would be extended by
-/// lifetime-extending the object initialized by the prvalue expression \c
-/// Init.
-template <typename TemporaryVisitor>
-static void visitTemporariesExtendedByInitializer(IndirectTemporaryPath &Path,
-                                                  Expr *Init,
-                                                  TemporaryVisitor Visit) {
+/// 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) {
   RevertToOldSizeRAII RAII(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(DIE);
+    Path.push_back({IndirectLocalPathEntry::DefaultInit, DIE});
     Init = DIE->getExpr();
 
     if (auto *EWC = dyn_cast<ExprWithCleanups>(Init))
@@ -6426,17 +6449,24 @@ static void visitTemporariesExtendedByInitializer(IndirectTemporaryPath &Path,
   //   initializing an initializer_list object from the array extends the
   //   lifetime of the array exactly like binding a reference to a temporary.
   if (auto *ILE = dyn_cast<CXXStdInitializerListExpr>(Init))
-    return visitTemporariesExtendedByReferenceBinding(
-        Path, ILE->getSubExpr(), EK_StdInitializerList, Visit);
+    return visitLocalsRetainedByReferenceBinding(Path, ILE->getSubExpr(),
+                                                 RK_StdInitializerList, Visit);
 
   if (InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) {
+    // We already visited the elements of this initializer list while
+    // performing the initialization. Don't visit them again unless we've
+    // changed the lifetime of the initialized entity.
+    if (!RevisitSubinits)
+      return;
+
     if (ILE->isTransparent())
-      return visitTemporariesExtendedByInitializer(Path, ILE->getInit(0),
-                                                   Visit);
+      return visitLocalsRetainedByInitializer(Path, ILE->getInit(0), Visit,
+                                              RevisitSubinits);
 
     if (ILE->getType()->isArrayType()) {
       for (unsigned I = 0, N = ILE->getNumInits(); I != N; ++I)
-        visitTemporariesExtendedByInitializer(Path, ILE->getInit(I), Visit);
+        visitLocalsRetainedByInitializer(Path, ILE->getInit(I), Visit,
+                                         RevisitSubinits);
       return;
     }
 
@@ -6448,8 +6478,8 @@ static void visitTemporariesExtendedByInitializer(IndirectTemporaryPath &Path,
       // bound to temporaries, those temporaries are also lifetime-extended.
       if (RD->isUnion() && ILE->getInitializedFieldInUnion() &&
           ILE->getInitializedFieldInUnion()->getType()->isReferenceType())
-        visitTemporariesExtendedByReferenceBinding(Path, ILE->getInit(0),
-                                                   EK_ReferenceBinding, Visit);
+        visitLocalsRetainedByReferenceBinding(Path, ILE->getInit(0),
+                                              RK_ReferenceBinding, Visit);
       else {
         unsigned Index = 0;
         for (const auto *I : RD->fields()) {
@@ -6459,25 +6489,38 @@ static void visitTemporariesExtendedByInitializer(IndirectTemporaryPath &Path,
             continue;
           Expr *SubInit = ILE->getInit(Index);
           if (I->getType()->isReferenceType())
-            visitTemporariesExtendedByReferenceBinding(
-                Path, SubInit, EK_ReferenceBinding, Visit);
+            visitLocalsRetainedByReferenceBinding(Path, SubInit,
+                                                  RK_ReferenceBinding, Visit);
           else
             // This might be either aggregate-initialization of a member or
             // initialization of a std::initializer_list object. Regardless,
             // we should recursively lifetime-extend that initializer.
-            visitTemporariesExtendedByInitializer(Path, SubInit, Visit);
+            visitLocalsRetainedByInitializer(Path, SubInit, Visit,
+                                             RevisitSubinits);
           ++Index;
         }
       }
     }
+    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,
+                                                   RK_ReferenceBinding, Visit);
+    }
   }
 }
 
 /// Determine whether this is an indirect path to a temporary that we are
 /// supposed to lifetime-extend along (but don't).
-static bool shouldLifetimeExtendThroughPath(const IndirectTemporaryPath &Path) {
+static bool shouldLifetimeExtendThroughPath(const IndirectLocalPath &Path) {
   for (auto Elem : Path) {
-    if (!Elem.is<CXXDefaultInitExpr*>())
+    if (Elem.Kind != IndirectLocalPathEntry::DefaultInit)
       return false;
   }
   return true;
@@ -6485,7 +6528,7 @@ static bool shouldLifetimeExtendThroughPath(const IndirectTemporaryPath &Path) {
 
 void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
                                     Expr *Init) {
-  LifetimeResult LR = getEntityForTemporaryLifetimeExtension(&Entity);
+  LifetimeResult LR = getEntityLifetime(&Entity);
   LifetimeKind LK = LR.getInt();
   const InitializedEntity *ExtendingEntity = LR.getPointer();
 
@@ -6494,9 +6537,54 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
   if (LK == LK_FullExpression)
     return;
 
-  auto TemporaryVisitor = [&](IndirectTemporaryPath &Path,
-                              MaterializeTemporaryExpr *MTE,
-                              ExtensionKind EK) -> bool {
+  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:
+        // FIXME: Move -Wreturn-stack-address checks here.
+        return false;
+      }
+      return false;
+    }
+
+    auto *MTE = L.get<MaterializeTemporaryExpr*>();
     switch (LK) {
     case LK_FullExpression:
       llvm_unreachable("already handled this");
@@ -6520,11 +6608,11 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
         // would be to clone the initializer expression on each use that would
         // lifetime extend its temporaries.
         Diag(MTE->getExprLoc(),
-             EK == EK_ReferenceBinding
+             RK == RK_ReferenceBinding
                  ? diag::warn_default_member_init_temporary_not_extended
                  : diag::warn_default_member_init_init_list_not_extended);
       } else {
-        llvm_unreachable("unexpected indirect temporary path");
+        // FIXME: Warn on this.
       }
       break;
 
@@ -6532,18 +6620,20 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
       // 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->getDecl()) {
+      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
-            << EK;
+            << 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().is<CXXDefaultInitExpr*>()) {
+        if (Path.empty() ||
+            Path.back().Kind != IndirectLocalPathEntry::DefaultInit) {
           Diag(ExtendingDecl->getLocation(),
                diag::note_lifetime_extending_member_declared_here)
-              << EK << IsSubobjectMember;
+              << RK << IsSubobjectMember;
         }
       } else {
         // We have a mem-initializer but no particular field within it; this
@@ -6557,7 +6647,7 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
       break;
 
     case LK_New:
-      if (EK == EK_ReferenceBinding) {
+      if (RK == RK_ReferenceBinding) {
         Diag(MTE->getExprLoc(), diag::warn_new_dangling_reference);
       } else {
         Diag(MTE->getExprLoc(), diag::warn_new_dangling_initializer_list)
@@ -6573,9 +6663,14 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
     // FIXME: Model these as CodeSynthesisContexts to fix the note emission
     // order.
     for (auto Elem : llvm::reverse(Path)) {
-      if (auto *DIE = Elem.dyn_cast<CXXDefaultInitExpr*>()) {
-        Diag(DIE->getExprLoc(), diag::note_in_default_member_initalizer_here)
-            << DIE->getField();
+      switch (Elem.Kind) {
+      case IndirectLocalPathEntry::DefaultInit:
+        Diag(Elem.E->getExprLoc(), diag::note_in_default_member_initalizer_here)
+            << cast<CXXDefaultInitExpr>(Elem.E)->getField();
+        break;
+
+      case IndirectLocalPathEntry::AddressOf:
+        break;
       }
     }
 
@@ -6584,12 +6679,12 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
     return false;
   };
 
-  llvm::SmallVector<IndirectTemporaryPathEntry, 8> Path;
+  llvm::SmallVector<IndirectLocalPathEntry, 8> Path;
   if (Init->isGLValue())
-    visitTemporariesExtendedByReferenceBinding(Path, Init, EK_ReferenceBinding,
-                                               TemporaryVisitor);
+    visitLocalsRetainedByReferenceBinding(Path, Init, RK_ReferenceBinding,
+                                          TemporaryVisitor);
   else
-    visitTemporariesExtendedByInitializer(Path, Init, TemporaryVisitor);
+    visitLocalsRetainedByInitializer(Path, Init, TemporaryVisitor, false);
 }
 
 static void DiagnoseNarrowingInInitList(Sema &S,
@@ -6853,6 +6948,7 @@ InitializationSequence::Perform(Sema &S,
 
   // Diagnose cases where we initialize a pointer to an array temporary, and the
   // pointer obviously outlives the temporary.
+  // FIXME: Fold this into checkInitializerLifetime.
   if (Args.size() == 1 && Args[0]->getType()->isArrayType() &&
       Entity.getType()->isPointerType() &&
       InitializedEntityOutlivesFullExpression(Entity)) {
@@ -7015,11 +7111,6 @@ InitializationSequence::Perform(Sema &S,
         }
       }
 
-      // Even though we didn't materialize a temporary, the binding may still
-      // extend the lifetime of a temporary. This happens if we bind a
-      // reference to the result of a cast to reference type.
-      S.checkInitializerLifetime(Entity, CurInit.get());
-
       CheckForNullPointerDereference(S, CurInit.get());
       break;
 
@@ -7036,10 +7127,6 @@ InitializationSequence::Perform(Sema &S,
           Step->Type, CurInit.get(), Entity.getType()->isLValueReferenceType());
       CurInit = MTE;
 
-      // Maybe lifetime-extend the temporary's subobjects to match the
-      // entity's lifetime.
-      S.checkInitializerLifetime(Entity, CurInit.get());
-
       // If we're extending this temporary to automatic storage duration -- we
       // need to register its cleanup during the full-expression's cleanups.
       if (MTE->getStorageDuration() == SD_Automatic &&
@@ -7490,10 +7577,6 @@ InitializationSequence::Perform(Sema &S,
       // Wrap it in a construction of a std::initializer_list<T>.
       CurInit = new (S.Context) CXXStdInitializerListExpr(Step->Type, MTE);
 
-      // Maybe lifetime-extend the array temporary's subobjects to match the
-      // entity's lifetime.
-      S.checkInitializerLifetime(Entity, CurInit.get());
-
       // Bind the result, in case the library has given initializer_list a
       // non-trivial destructor.
       if (shouldBindAsTemporary(Entity))
@@ -7612,6 +7695,11 @@ InitializationSequence::Perform(Sema &S,
     }
   }
 
+  // Check whether the initializer has a shorter lifetime than the initialized
+  // entity, and if not, either lifetime-extend or warn as appropriate.
+  if (auto *Init = CurInit.get())
+    S.checkInitializerLifetime(Entity, Init);
+
   // Diagnose non-fatal problems with the completed initialization.
   if (Entity.getKind() == InitializedEntity::EK_Member &&
       cast<FieldDecl>(Entity.getDecl())->isBitField())
index db025420d7ec89c6d1234d7bb827154304deb75e..1507098c5e75718f5262b9338d34a606b8b9a32a 100644 (file)
@@ -235,10 +235,10 @@ void fVoidPointerTest2() {
 }
 
 class VoidPointerRRefTest1 {
-  void *&&vptrrref;
+  void *&&vptrrref; // expected-note {{here}}
 
 public:
-  VoidPointerRRefTest1(void *vptr, char) : vptrrref(static_cast<void *&&>(vptr)) {
+  VoidPointerRRefTest1(void *vptr, char) : vptrrref(static_cast<void *&&>(vptr)) { // expected-warning {{binding reference member 'vptrrref' to stack allocated parameter 'vptr'}}
     // All good!
   }
 };
@@ -249,10 +249,10 @@ void fVoidPointerRRefTest1() {
 }
 
 class VoidPointerRRefTest2 {
-  void **&&vpptrrref;
+  void **&&vpptrrref; // expected-note {{here}}
 
 public:
-  VoidPointerRRefTest2(void **vptr, char) : vpptrrref(static_cast<void **&&>(vptr)) {
+  VoidPointerRRefTest2(void **vptr, char) : vpptrrref(static_cast<void **&&>(vptr)) { // expected-warning {{binding reference member 'vpptrrref' to stack allocated parameter 'vptr'}}
     // All good!
   }
 };
@@ -263,10 +263,10 @@ void fVoidPointerRRefTest2() {
 }
 
 class VoidPointerLRefTest {
-  void *&vptrrref;
+  void *&vptrrref; // expected-note {{here}}
 
 public:
-  VoidPointerLRefTest(void *vptr, char) : vptrrref(static_cast<void *&>(vptr)) {
+  VoidPointerLRefTest(void *vptr, char) : vptrrref(static_cast<void *&>(vptr)) { // expected-warning {{binding reference member 'vptrrref' to stack allocated parameter 'vptr'}}
     // All good!
   }
 };
index 0184a1d6e73d319a16f1c5d0cb392d9a9507f441..e3c7e26afa817bac88fe195bff8060149f55ade6 100644 (file)
@@ -72,18 +72,18 @@ std::initializer_list<int> thread_local x = {1, 2, 3, 4};
 
 // X86: @_ZN15partly_constant1kE = global i32 0, align 4
 // X86: @_ZN15partly_constant2ilE = global {{.*}} null, align 8
-// X86: @[[PARTLY_CONSTANT_OUTER:_ZGRN15partly_constant2ilE.*]] = internal global {{.*}} zeroinitializer, align 8
-// X86: @[[PARTLY_CONSTANT_INNER:_ZGRN15partly_constant2ilE.*]] = internal global [3 x {{.*}}] zeroinitializer, align 8
-// X86: @[[PARTLY_CONSTANT_FIRST:_ZGRN15partly_constant2ilE.*]] = internal constant [3 x i32] [i32 1, i32 2, i32 3], align 4
-// X86: @[[PARTLY_CONSTANT_SECOND:_ZGRN15partly_constant2ilE.*]] = internal global [2 x i32] zeroinitializer, align 4
-// X86: @[[PARTLY_CONSTANT_THIRD:_ZGRN15partly_constant2ilE.*]] = internal constant [4 x i32] [i32 5, i32 6, i32 7, i32 8], align 4
+// X86: @[[PARTLY_CONSTANT_OUTER:_ZGRN15partly_constant2ilE_]] = internal global {{.*}} zeroinitializer, align 8
+// X86: @[[PARTLY_CONSTANT_INNER:_ZGRN15partly_constant2ilE0_]] = internal global [3 x {{.*}}] zeroinitializer, align 8
+// X86: @[[PARTLY_CONSTANT_FIRST:_ZGRN15partly_constant2ilE1_]] = internal constant [3 x i32] [i32 1, i32 2, i32 3], align 4
+// X86: @[[PARTLY_CONSTANT_SECOND:_ZGRN15partly_constant2ilE2_]] = internal global [2 x i32] zeroinitializer, align 4
+// X86: @[[PARTLY_CONSTANT_THIRD:_ZGRN15partly_constant2ilE3_]] = internal constant [4 x i32] [i32 5, i32 6, i32 7, i32 8], align 4
 // AMDGCN: @_ZN15partly_constant1kE = addrspace(1) global i32 0, align 4
 // AMDGCN: @_ZN15partly_constant2ilE = addrspace(4) global {{.*}} null, align 8
-// AMDGCN: @[[PARTLY_CONSTANT_OUTER:_ZGRN15partly_constant2ilE.*]] = internal addrspace(4) global {{.*}} zeroinitializer, align 8
-// AMDGCN: @[[PARTLY_CONSTANT_INNER:_ZGRN15partly_constant2ilE.*]] = internal addrspace(4) global [3 x {{.*}}] zeroinitializer, align 8
-// AMDGCN: @[[PARTLY_CONSTANT_FIRST:_ZGRN15partly_constant2ilE.*]] = internal addrspace(4) constant [3 x i32] [i32 1, i32 2, i32 3], align 4
-// AMDGCN: @[[PARTLY_CONSTANT_SECOND:_ZGRN15partly_constant2ilE.*]] = internal addrspace(4) global [2 x i32] zeroinitializer, align 4
-// AMDGCN: @[[PARTLY_CONSTANT_THIRD:_ZGRN15partly_constant2ilE.*]] = internal addrspace(4) constant [4 x i32] [i32 5, i32 6, i32 7, i32 8], align 4
+// AMDGCN: @[[PARTLY_CONSTANT_OUTER:_ZGRN15partly_constant2ilE_]] = internal addrspace(4) global {{.*}} zeroinitializer, align 8
+// AMDGCN: @[[PARTLY_CONSTANT_INNER:_ZGRN15partly_constant2ilE0_]] = internal addrspace(4) global [3 x {{.*}}] zeroinitializer, align 8
+// AMDGCN: @[[PARTLY_CONSTANT_FIRST:_ZGRN15partly_constant2ilE1_]] = internal addrspace(4) constant [3 x i32] [i32 1, i32 2, i32 3], align 4
+// AMDGCN: @[[PARTLY_CONSTANT_SECOND:_ZGRN15partly_constant2ilE2_]] = internal addrspace(4) global [2 x i32] zeroinitializer, align 4
+// AMDGCN: @[[PARTLY_CONSTANT_THIRD:_ZGRN15partly_constant2ilE3_]] = internal addrspace(4) constant [4 x i32] [i32 5, i32 6, i32 7, i32 8], align 4
 
 // X86: @[[REFTMP1:.*]] = private constant [2 x i32] [i32 42, i32 43], align 4
 // X86: @[[REFTMP2:.*]] = private constant [3 x %{{.*}}] [%{{.*}} { i32 1 }, %{{.*}} { i32 2 }, %{{.*}} { i32 3 }], align 4
@@ -375,7 +375,7 @@ namespace partly_constant {
   // CHECK-NOT: @[[PARTLY_CONSTANT_THIRD]],
   // CHECK: store i32* getelementptr inbounds ({{.*}}, {{.*}}* {{.*}}@[[PARTLY_CONSTANT_THIRD]]{{.*}}, i64 0, i64 0),
   // CHECK:       i32** getelementptr inbounds ({{.*}}, {{.*}}* {{.*}}@[[PARTLY_CONSTANT_INNER]]{{.*}}, i64 0, i64 2, i32 0)
-  // CHECK: store i64 4, i64* getelementptr inbounds ({{.*}}, {{.*}}* {{.*}}@_ZGRN15partly_constant2ilE4_{{.*}}, i64 0, i64 2, i32 1)
+  // CHECK: store i64 4, i64* getelementptr inbounds ({{.*}}, {{.*}}* {{.*}}@[[PARTLY_CONSTANT_INNER]]{{.*}}, i64 0, i64 2, i32 1)
   // CHECK-NOT: @[[PARTLY_CONSTANT_THIRD]],
   //
   // Outer init list.