<div dir="ltr"><div dir="ltr">Hi Richard,<div><br></div><div>I posted a patch that partially reverts your commit: <a href="https://reviews.llvm.org/D72411">https://reviews.llvm.org/D72411</a>. Unfortunately I can't revert it fully.</div><div><br></div><div>Thanks,</div><div>Alex</div></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, 11 Nov 2019 at 15:30, Alex L <<a href="mailto:arphaman@gmail.com">arphaman@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr">Here's a reduced test case.<div><br></div><div>```</div><div>// OPTIONS: -x objective-c -std=gnu99 -fobjc-arc -emit-llvm</div><div>@interface Foo<br></div><div>@end<br></div><div><br></div><div>Foo *foo() {</div><div>  static Foo *f = ((void*)0;</div><div>  return f;</div><div>}</div><div>// CHECK-NOT: cxa_guard</div><div>```</div><div><br></div><div>AFAIK clang should not emit C++ guard variables in non-C++ code. Is that correct?</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, 11 Nov 2019 at 14:16, Alex L <<a href="mailto:arphaman@gmail.com" target="_blank">arphaman@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr">Hi Richard,<div><br></div><div>I have a question about this commit. It looks like it started emitting `cxa_guard_acquire/release` calls in Objective-C code, for globals with constant initializers, causing link failures for things that don't link with libc++abi. Was that intentional? I'm still working on a reduced test-case that I can post.</div><div><br></div><div>Thanks,</div><div>Alex</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sat, 28 Sep 2019 at 22:06, Richard Smith via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">Author: rsmith<br>
Date: Sat Sep 28 22:08:46 2019<br>
New Revision: 373159<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=373159&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=373159&view=rev</a><br>
Log:<br>
For P0784R7: compute whether a variable has constant destruction if it<br>
has a constexpr destructor.<br>
<br>
For constexpr variables, reject if the variable does not have constant<br>
destruction. In all cases, do not emit runtime calls to the destructor<br>
for variables with constant destruction.<br>
<br>
Added:<br>
    cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp<br>
    cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp<br>
Modified:<br>
    cfe/trunk/include/clang/AST/Decl.h<br>
    cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td<br>
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td<br>
    cfe/trunk/lib/AST/ASTContext.cpp<br>
    cfe/trunk/lib/AST/Decl.cpp<br>
    cfe/trunk/lib/AST/ExprConstant.cpp<br>
    cfe/trunk/lib/AST/Interp/Interp.cpp<br>
    cfe/trunk/lib/AST/TextNodeDumper.cpp<br>
    cfe/trunk/lib/CodeGen/CGCall.cpp<br>
    cfe/trunk/lib/CodeGen/CGClass.cpp<br>
    cfe/trunk/lib/CodeGen/CGDecl.cpp<br>
    cfe/trunk/lib/CodeGen/CGDeclCXX.cpp<br>
    cfe/trunk/lib/CodeGen/CodeGenModule.cpp<br>
    cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp<br>
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp<br>
    cfe/trunk/lib/Serialization/ASTReaderDecl.cpp<br>
    cfe/trunk/lib/Serialization/ASTWriterDecl.cpp<br>
    cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp<br>
    cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp<br>
    cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp<br>
    cfe/trunk/test/CodeGenCXX/no_destroy.cpp<br>
<br>
Modified: cfe/trunk/include/clang/AST/Decl.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/AST/Decl.h (original)<br>
+++ cfe/trunk/include/clang/AST/Decl.h Sat Sep 28 22:08:46 2019<br>
@@ -808,12 +808,19 @@ struct EvaluatedStmt {<br>
   /// valid if CheckedICE is true.<br>
   bool IsICE : 1;<br>
<br>
+  /// Whether this variable is known to have constant destruction. That is,<br>
+  /// whether running the destructor on the initial value is a side-effect<br>
+  /// (and doesn't inspect any state that might have changed during program<br>
+  /// execution). This is currently only computed if the destructor is<br>
+  /// non-trivial.<br>
+  bool HasConstantDestruction : 1;<br>
+<br>
   Stmt *Value;<br>
   APValue Evaluated;<br>
<br>
-  EvaluatedStmt() : WasEvaluated(false), IsEvaluating(false), CheckedICE(false),<br>
-                    CheckingICE(false), IsICE(false) {}<br>
-<br>
+  EvaluatedStmt()<br>
+      : WasEvaluated(false), IsEvaluating(false), CheckedICE(false),<br>
+        CheckingICE(false), IsICE(false), HasConstantDestruction(false) {}<br>
 };<br>
<br>
 /// Represents a variable declaration or definition.<br>
@@ -1267,6 +1274,14 @@ public:<br>
   /// to untyped APValue if the value could not be evaluated.<br>
   APValue *getEvaluatedValue() const;<br>
<br>
+  /// Evaluate the destruction of this variable to determine if it constitutes<br>
+  /// constant destruction.<br>
+  ///<br>
+  /// \pre isInitICE()<br>
+  /// \return \c true if this variable has constant destruction, \c false if<br>
+  ///         not.<br>
+  bool evaluateDestruction(SmallVectorImpl<PartialDiagnosticAt> &Notes) const;<br>
+<br>
   /// Determines whether it is already known whether the<br>
   /// initializer is an integral constant expression or not.<br>
   bool isInitKnownICE() const;<br>
@@ -1505,9 +1520,14 @@ public:<br>
   // has no definition within this source file.<br>
   bool isKnownToBeDefined() const;<br>
<br>
-  /// Do we need to emit an exit-time destructor for this variable?<br>
+  /// Is destruction of this variable entirely suppressed? If so, the variable<br>
+  /// need not have a usable destructor at all.<br>
   bool isNoDestroy(const ASTContext &) const;<br>
<br>
+  /// Do we need to emit an exit-time destructor for this variable, and if so,<br>
+  /// what kind?<br>
+  QualType::DestructionKind needsDestruction(const ASTContext &Ctx) const;<br>
+<br>
   // Implement isa/cast/dyncast/etc.<br>
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }<br>
   static bool classofKind(Kind K) { return K >= firstVar && K <= lastVar; }<br>
<br>
Modified: cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td (original)<br>
+++ cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td Sat Sep 28 22:08:46 2019<br>
@@ -145,8 +145,10 @@ def note_constexpr_access_volatile_obj :<br>
   "a constant expression">;<br>
 def note_constexpr_volatile_here : Note<<br>
   "volatile %select{temporary created|object declared|member declared}0 here">;<br>
-def note_constexpr_ltor_mutable : Note<<br>
-  "read of mutable member %0 is not allowed in a constant expression">;<br>
+def note_constexpr_access_mutable : Note<<br>
+  "%select{read of|read of|assignment to|increment of|decrement of|"<br>
+  "member call on|dynamic_cast of|typeid applied to|destruction of}0 "<br>
+  "mutable member %1 is not allowed in a constant expression">;<br>
 def note_constexpr_ltor_non_const_int : Note<<br>
   "read of non-const variable %0 is not allowed in a constant expression">;<br>
 def note_constexpr_ltor_non_constexpr : Note<<br>
<br>
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)<br>
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Sat Sep 28 22:08:46 2019<br>
@@ -2384,6 +2384,8 @@ def err_constexpr_var_non_literal : Erro<br>
   "constexpr variable cannot have non-literal type %0">;<br>
 def err_constexpr_var_requires_const_init : Error<<br>
   "constexpr variable %0 must be initialized by a constant expression">;<br>
+def err_constexpr_var_requires_const_destruction : Error<<br>
+  "constexpr variable %0 must have constant destruction">;<br>
 def err_constexpr_redecl_mismatch : Error<<br>
   "%select{non-constexpr|constexpr|consteval}1 declaration of %0"<br>
   " follows %select{non-constexpr|constexpr|consteval}2 declaration">;<br>
<br>
Modified: cfe/trunk/lib/AST/ASTContext.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/AST/ASTContext.cpp (original)<br>
+++ cfe/trunk/lib/AST/ASTContext.cpp Sat Sep 28 22:08:46 2019<br>
@@ -10064,7 +10064,7 @@ bool ASTContext::DeclMustBeEmitted(const<br>
     return false;<br>
<br>
   // Variables that have destruction with side-effects are required.<br>
-  if (VD->getType().isDestructedType())<br>
+  if (VD->needsDestruction(*this))<br>
     return true;<br>
<br>
   // Variables that have initialization with side-effects are required.<br>
<br>
Modified: cfe/trunk/lib/AST/Decl.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/AST/Decl.cpp (original)<br>
+++ cfe/trunk/lib/AST/Decl.cpp Sat Sep 28 22:08:46 2019<br>
@@ -2592,6 +2592,18 @@ bool VarDecl::isNoDestroy(const ASTConte<br>
                                  !hasAttr<AlwaysDestroyAttr>()));<br>
 }<br>
<br>
+QualType::DestructionKind<br>
+VarDecl::needsDestruction(const ASTContext &Ctx) const {<br>
+  if (EvaluatedStmt *Eval = Init.dyn_cast<EvaluatedStmt *>())<br>
+    if (Eval->HasConstantDestruction)<br>
+      return QualType::DK_none;<br>
+<br>
+  if (isNoDestroy(Ctx))<br>
+    return QualType::DK_none;<br>
+<br>
+  return getType().isDestructedType();<br>
+}<br>
+<br>
 MemberSpecializationInfo *VarDecl::getMemberSpecializationInfo() const {<br>
   if (isStaticDataMember())<br>
     // FIXME: Remove ?<br>
<br>
Modified: cfe/trunk/lib/AST/ExprConstant.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)<br>
+++ cfe/trunk/lib/AST/ExprConstant.cpp Sat Sep 28 22:08:46 2019<br>
@@ -744,6 +744,15 @@ namespace {<br>
     /// evaluated, if any.<br>
     APValue::LValueBase EvaluatingDecl;<br>
<br>
+    enum class EvaluatingDeclKind {<br>
+      None,<br>
+      /// We're evaluating the construction of EvaluatingDecl.<br>
+      Ctor,<br>
+      /// We're evaluating the destruction of EvaluatingDecl.<br>
+      Dtor,<br>
+    };<br>
+    EvaluatingDeclKind IsEvaluatingDecl = EvaluatingDeclKind::None;<br>
+<br>
     /// EvaluatingDeclValue - This is the value being constructed for the<br>
     /// declaration whose initializer is being evaluated, if any.<br>
     APValue *EvaluatingDeclValue;<br>
@@ -902,8 +911,10 @@ namespace {<br>
       discardCleanups();<br>
     }<br>
<br>
-    void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) {<br>
+    void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value,<br>
+                           EvaluatingDeclKind EDK = EvaluatingDeclKind::Ctor) {<br>
       EvaluatingDecl = Base;<br>
+      IsEvaluatingDecl = EDK;<br>
       EvaluatingDeclValue = &Value;<br>
     }<br>
<br>
@@ -2913,8 +2924,8 @@ static bool isReadByLvalueToRvalueConver<br>
<br>
 /// Diagnose an attempt to read from any unreadable field within the specified<br>
 /// type, which might be a class type.<br>
-static bool diagnoseUnreadableFields(EvalInfo &Info, const Expr *E,<br>
-                                     QualType T) {<br>
+static bool diagnoseMutableFields(EvalInfo &Info, const Expr *E, AccessKinds AK,<br>
+                                  QualType T) {<br>
   CXXRecordDecl *RD = T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();<br>
   if (!RD)<br>
     return false;<br>
@@ -2929,17 +2940,17 @@ static bool diagnoseUnreadableFields(Eva<br>
     // FIXME: Add core issue number for the union case.<br>
     if (Field->isMutable() &&<br>
         (RD->isUnion() || isReadByLvalueToRvalueConversion(Field->getType()))) {<br>
-      Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1) << Field;<br>
+      Info.FFDiag(E, diag::note_constexpr_access_mutable, 1) << AK << Field;<br>
       Info.Note(Field->getLocation(), diag::note_declared_at);<br>
       return true;<br>
     }<br>
<br>
-    if (diagnoseUnreadableFields(Info, E, Field->getType()))<br>
+    if (diagnoseMutableFields(Info, E, AK, Field->getType()))<br>
       return true;<br>
   }<br>
<br>
   for (auto &BaseSpec : RD->bases())<br>
-    if (diagnoseUnreadableFields(Info, E, BaseSpec.getType()))<br>
+    if (diagnoseMutableFields(Info, E, AK, BaseSpec.getType()))<br>
       return true;<br>
<br>
   // All mutable fields were empty, and thus not actually read.<br>
@@ -2947,7 +2958,8 @@ static bool diagnoseUnreadableFields(Eva<br>
 }<br>
<br>
 static bool lifetimeStartedInEvaluation(EvalInfo &Info,<br>
-                                        APValue::LValueBase Base) {<br>
+                                        APValue::LValueBase Base,<br>
+                                        bool MutableSubobject = false) {<br>
   // A temporary we created.<br>
   if (Base.getCallIndex())<br>
     return true;<br>
@@ -2956,19 +2968,42 @@ static bool lifetimeStartedInEvaluation(<br>
   if (!Evaluating)<br>
     return false;<br>
<br>
-  // The variable whose initializer we're evaluating.<br>
-  if (auto *BaseD = Base.dyn_cast<const ValueDecl*>())<br>
-    if (declaresSameEntity(Evaluating, BaseD))<br>
-      return true;<br>
+  auto *BaseD = Base.dyn_cast<const ValueDecl*>();<br>
<br>
-  // A temporary lifetime-extended by the variable whose initializer we're<br>
-  // evaluating.<br>
-  if (auto *BaseE = Base.dyn_cast<const Expr *>())<br>
-    if (auto *BaseMTE = dyn_cast<MaterializeTemporaryExpr>(BaseE))<br>
-      if (declaresSameEntity(BaseMTE->getExtendingDecl(), Evaluating))<br>
-        return true;<br>
+  switch (Info.IsEvaluatingDecl) {<br>
+  case EvalInfo::EvaluatingDeclKind::None:<br>
+    return false;<br>
<br>
-  return false;<br>
+  case EvalInfo::EvaluatingDeclKind::Ctor:<br>
+    // The variable whose initializer we're evaluating.<br>
+    if (BaseD)<br>
+      return declaresSameEntity(Evaluating, BaseD);<br>
+<br>
+    // A temporary lifetime-extended by the variable whose initializer we're<br>
+    // evaluating.<br>
+    if (auto *BaseE = Base.dyn_cast<const Expr *>())<br>
+      if (auto *BaseMTE = dyn_cast<MaterializeTemporaryExpr>(BaseE))<br>
+        return declaresSameEntity(BaseMTE->getExtendingDecl(), Evaluating);<br>
+    return false;<br>
+<br>
+  case EvalInfo::EvaluatingDeclKind::Dtor:<br>
+    // C++2a [expr.const]p6:<br>
+    //   [during constant destruction] the lifetime of a and its non-mutable<br>
+    //   subobjects (but not its mutable subobjects) [are] considered to start<br>
+    //   within e.<br>
+    //<br>
+    // FIXME: We can meaningfully extend this to cover non-const objects, but<br>
+    // we will need special handling: we should be able to access only<br>
+    // subobjects of such objects that are themselves declared const.<br>
+    if (!BaseD ||<br>
+        !(BaseD->getType().isConstQualified() ||<br>
+          BaseD->getType()->isReferenceType()) ||<br>
+        MutableSubobject)<br>
+      return false;<br>
+    return declaresSameEntity(Evaluating, BaseD);<br>
+  }<br>
+<br>
+  llvm_unreachable("unknown evaluating decl kind");<br>
 }<br>
<br>
 namespace {<br>
@@ -2986,13 +3021,13 @@ struct CompleteObject {<br>
   CompleteObject(APValue::LValueBase Base, APValue *Value, QualType Type)<br>
       : Base(Base), Value(Value), Type(Type) {}<br>
<br>
-  bool mayReadMutableMembers(EvalInfo &Info) const {<br>
+  bool mayAccessMutableMembers(EvalInfo &Info, AccessKinds AK) const {<br>
     // In C++14 onwards, it is permitted to read a mutable member whose<br>
     // lifetime began within the evaluation.<br>
     // FIXME: Should we also allow this in C++11?<br>
     if (!Info.getLangOpts().CPlusPlus14)<br>
       return false;<br>
-    return lifetimeStartedInEvaluation(Info, Base);<br>
+    return lifetimeStartedInEvaluation(Info, Base, /*MutableSubobject*/true);<br>
   }<br>
<br>
   explicit operator bool() const { return !Type.isNull(); }<br>
@@ -3097,9 +3132,9 @@ findSubobject(EvalInfo &Info, const Expr<br>
       // things we need to check: if there are any mutable subobjects, we<br>
       // cannot perform this read. (This only happens when performing a trivial<br>
       // copy or assignment.)<br>
-      if (ObjType->isRecordType() && isRead(handler.AccessKind) &&<br>
-          !Obj.mayReadMutableMembers(Info) &&<br>
-          diagnoseUnreadableFields(Info, E, ObjType))<br>
+      if (ObjType->isRecordType() &&<br>
+          !Obj.mayAccessMutableMembers(Info, handler.AccessKind) &&<br>
+          diagnoseMutableFields(Info, E, handler.AccessKind, ObjType))<br>
         return handler.failed();<br>
     }<br>
<br>
@@ -3167,10 +3202,10 @@ findSubobject(EvalInfo &Info, const Expr<br>
                                    : O->getComplexFloatReal(), ObjType);<br>
       }<br>
     } else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) {<br>
-      if (Field->isMutable() && isRead(handler.AccessKind) &&<br>
-          !Obj.mayReadMutableMembers(Info)) {<br>
-        Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1)<br>
-          << Field;<br>
+      if (Field->isMutable() &&<br>
+          !Obj.mayAccessMutableMembers(Info, handler.AccessKind)) {<br>
+        Info.FFDiag(E, diag::note_constexpr_access_mutable, 1)<br>
+          << handler.AccessKind << Field;<br>
         Info.Note(Field->getLocation(), diag::note_declared_at);<br>
         return handler.failed();<br>
       }<br>
@@ -3427,8 +3462,7 @@ static CompleteObject findCompleteObject<br>
     // the variable we're reading must be const.<br>
     if (!Frame) {<br>
       if (Info.getLangOpts().CPlusPlus14 &&<br>
-          declaresSameEntity(<br>
-              VD, Info.EvaluatingDecl.dyn_cast<const ValueDecl *>())) {<br>
+          lifetimeStartedInEvaluation(Info, LVal.Base)) {<br>
         // OK, we can read and modify an object if we're in the process of<br>
         // evaluating its initializer, because its lifetime began in this<br>
         // evaluation.<br>
@@ -3518,11 +3552,14 @@ static CompleteObject findCompleteObject<br>
         //   int x = ++r;<br>
         //   constexpr int k = r;<br>
         // Therefore we use the C++14 rules in C++11 too.<br>
-        const ValueDecl *VD = Info.EvaluatingDecl.dyn_cast<const ValueDecl*>();<br>
-        const ValueDecl *ED = MTE->getExtendingDecl();<br>
+        //<br>
+        // Note that temporaries whose lifetimes began while evaluating a<br>
+        // variable's constructor are not usable while evaluating the<br>
+        // corresponding destructor, not even if they're of const-qualified<br>
+        // types.<br>
         if (!(BaseType.isConstQualified() &&<br>
               BaseType->isIntegralOrEnumerationType()) &&<br>
-            !(VD && VD->getCanonicalDecl() == ED->getCanonicalDecl())) {<br>
+            !lifetimeStartedInEvaluation(Info, LVal.Base)) {<br>
           if (!IsAccess)<br>
             return CompleteObject(LVal.getLValueBase(), nullptr, BaseType);<br>
           Info.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK;<br>
@@ -13282,6 +13319,41 @@ bool Expr::EvaluateAsInitializer(APValue<br>
          CheckMemoryLeaks(Info);<br>
 }<br>
<br>
+bool VarDecl::evaluateDestruction(<br>
+    SmallVectorImpl<PartialDiagnosticAt> &Notes) const {<br>
+  assert(getEvaluatedValue() && !getEvaluatedValue()->isAbsent() &&<br>
+         "cannot evaluate destruction of non-constant-initialized variable");<br>
+<br>
+  Expr::EvalStatus EStatus;<br>
+  EStatus.Diag = &Notes;<br>
+<br>
+  // Make a copy of the value for the destructor to mutate.<br>
+  APValue DestroyedValue = *getEvaluatedValue();<br>
+<br>
+  EvalInfo Info(getASTContext(), EStatus, EvalInfo::EM_ConstantExpression);<br>
+  Info.setEvaluatingDecl(this, DestroyedValue,<br>
+                         EvalInfo::EvaluatingDeclKind::Dtor);<br>
+  Info.InConstantContext = true;<br>
+<br>
+  SourceLocation DeclLoc = getLocation();<br>
+  QualType DeclTy = getType();<br>
+<br>
+  LValue LVal;<br>
+  LVal.set(this);<br>
+<br>
+  // FIXME: Consider storing whether this variable has constant destruction in<br>
+  // the EvaluatedStmt so that CodeGen can query it.<br>
+  if (!HandleDestruction(Info, DeclLoc, LVal.Base, DestroyedValue, DeclTy) ||<br>
+      EStatus.HasSideEffects)<br>
+    return false;<br>
+<br>
+  if (!Info.discardCleanups())<br>
+    llvm_unreachable("Unhandled cleanup; missing full expression marker?");<br>
+<br>
+  ensureEvaluatedStmt()->HasConstantDestruction = true;<br>
+  return true;<br>
+}<br>
+<br>
 /// isEvaluatable - Call EvaluateAsRValue to see if this expression can be<br>
 /// constant folded, but discard the result.<br>
 bool Expr::isEvaluatable(const ASTContext &Ctx, SideEffectsKind SEK) const {<br>
<br>
Modified: cfe/trunk/lib/AST/Interp/Interp.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Interp/Interp.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Interp/Interp.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/AST/Interp/Interp.cpp (original)<br>
+++ cfe/trunk/lib/AST/Interp/Interp.cpp Sat Sep 28 22:08:46 2019<br>
@@ -275,7 +275,7 @@ bool CheckMutable(InterpState &S, CodePt<br>
<br>
   const SourceInfo &Loc = S.Current->getSource(OpPC);<br>
   const FieldDecl *Field = Ptr.getField();<br>
-  S.FFDiag(Loc, diag::note_constexpr_ltor_mutable, 1) << Field;<br>
+  S.FFDiag(Loc, diag::note_constexpr_access_mutable, 1) << AK_Read << Field;<br>
   S.Note(Field->getLocation(), diag::note_declared_at);<br>
   return false;<br>
 }<br>
<br>
Modified: cfe/trunk/lib/AST/TextNodeDumper.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/TextNodeDumper.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/TextNodeDumper.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/AST/TextNodeDumper.cpp (original)<br>
+++ cfe/trunk/lib/AST/TextNodeDumper.cpp Sat Sep 28 22:08:46 2019<br>
@@ -1384,6 +1384,8 @@ void TextNodeDumper::VisitVarDecl(const<br>
       break;<br>
     }<br>
   }<br>
+  if (D->needsDestruction(D->getASTContext()))<br>
+    OS << " destroyed";<br>
   if (D->isParameterPack())<br>
     OS << " pack";<br>
 }<br>
<br>
Modified: cfe/trunk/lib/CodeGen/CGCall.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCall.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCall.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/CGCall.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/CGCall.cpp Sat Sep 28 22:08:46 2019<br>
@@ -3093,7 +3093,7 @@ void CodeGenFunction::EmitDelegateCallAr<br>
   // Deactivate the cleanup for the callee-destructed param that was pushed.<br>
   if (hasAggregateEvaluationKind(type) && !CurFuncIsThunk &&<br>
       type->getAs<RecordType>()->getDecl()->isParamDestroyedInCallee() &&<br>
-      type.isDestructedType()) {<br>
+      param->needsDestruction(getContext())) {<br>
     EHScopeStack::stable_iterator cleanup =<br>
         CalleeDestructedParamCleanups.lookup(cast<ParmVarDecl>(param));<br>
     assert(cleanup.isValid() &&<br>
<br>
Modified: cfe/trunk/lib/CodeGen/CGClass.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGClass.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGClass.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/CGClass.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/CGClass.cpp Sat Sep 28 22:08:46 2019<br>
@@ -2083,7 +2083,7 @@ static bool canEmitDelegateCallArgs(Code<br>
   if (CGF.getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee()) {<br>
     // If the parameters are callee-cleanup, it's not safe to forward.<br>
     for (auto *P : Ctor->parameters())<br>
-      if (P->getType().isDestructedType())<br>
+      if (P->needsDestruction(CGF.getContext()))<br>
         return false;<br>
<br>
     // Likewise if they're inalloca.<br>
<br>
Modified: cfe/trunk/lib/CodeGen/CGDecl.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDecl.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDecl.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/CGDecl.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/CGDecl.cpp Sat Sep 28 22:08:46 2019<br>
@@ -305,14 +305,6 @@ llvm::Constant *CodeGenModule::getOrCrea<br>
   return Addr;<br>
 }<br>
<br>
-/// hasNontrivialDestruction - Determine whether a type's destruction is<br>
-/// non-trivial. If so, and the variable uses static initialization, we must<br>
-/// register its destructor to run on exit.<br>
-static bool hasNontrivialDestruction(QualType T) {<br>
-  CXXRecordDecl *RD = T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();<br>
-  return RD && !RD->hasTrivialDestructor();<br>
-}<br>
-<br>
 /// AddInitializerToStaticVarDecl - Add the initializer for 'D' to the<br>
 /// global variable that has already been created for it.  If the initializer<br>
 /// has a different type than GV does, this may free GV and return a different<br>
@@ -372,7 +364,7 @@ CodeGenFunction::AddInitializerToStaticV<br>
<br>
   emitter.finalize(GV);<br>
<br>
-  if (hasNontrivialDestruction(D.getType()) && HaveInsertPoint()) {<br>
+  if (D.needsDestruction(getContext()) && HaveInsertPoint()) {<br>
     // We have a constant initializer, but a nontrivial destructor. We still<br>
     // need to perform a guarded "initialization" in order to register the<br>
     // destructor.<br>
@@ -1994,7 +1986,7 @@ void CodeGenFunction::EmitAutoVarCleanup<br>
   const VarDecl &D = *emission.Variable;<br>
<br>
   // Check the type for a cleanup.<br>
-  if (QualType::DestructionKind dtorKind = D.getType().isDestructedType())<br>
+  if (QualType::DestructionKind dtorKind = D.needsDestruction(getContext()))<br>
     emitAutoVarTypeCleanup(emission, dtorKind);<br>
<br>
   // In GC mode, honor objc_precise_lifetime.<br>
@@ -2404,7 +2396,8 @@ void CodeGenFunction::EmitParmDecl(const<br>
     // cleanup.<br>
     if (hasAggregateEvaluationKind(Ty) && !CurFuncIsThunk &&<br>
         Ty->getAs<RecordType>()->getDecl()->isParamDestroyedInCallee()) {<br>
-      if (QualType::DestructionKind DtorKind = Ty.isDestructedType()) {<br>
+      if (QualType::DestructionKind DtorKind =<br>
+              D.needsDestruction(getContext())) {<br>
         assert((DtorKind == QualType::DK_cxx_destructor ||<br>
                 DtorKind == QualType::DK_nontrivial_c_struct) &&<br>
                "unexpected destructor type");<br>
<br>
Modified: cfe/trunk/lib/CodeGen/CGDeclCXX.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDeclCXX.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDeclCXX.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/CGDeclCXX.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/CGDeclCXX.cpp Sat Sep 28 22:08:46 2019<br>
@@ -73,16 +73,10 @@ static void EmitDeclDestroy(CodeGenFunct<br>
   // that isn't balanced out by a destructor call as intended by the<br>
   // attribute. This also checks for -fno-c++-static-destructors and<br>
   // bails even if the attribute is not present.<br>
-  if (D.isNoDestroy(CGF.getContext()))<br>
-    return;<br>
-<br>
-  CodeGenModule &CGM = CGF.CGM;<br>
+  QualType::DestructionKind DtorKind = D.needsDestruction(CGF.getContext());<br>
<br>
   // FIXME:  __attribute__((cleanup)) ?<br>
<br>
-  QualType Type = D.getType();<br>
-  QualType::DestructionKind DtorKind = Type.isDestructedType();<br>
-<br>
   switch (DtorKind) {<br>
   case QualType::DK_none:<br>
     return;<br>
@@ -101,6 +95,9 @@ static void EmitDeclDestroy(CodeGenFunct<br>
   llvm::FunctionCallee Func;<br>
   llvm::Constant *Argument;<br>
<br>
+  CodeGenModule &CGM = CGF.CGM;<br>
+  QualType Type = D.getType();<br>
+<br>
   // Special-case non-array C++ destructors, if they have the right signature.<br>
   // Under some ABIs, destructors return this instead of void, and cannot be<br>
   // passed directly to __cxa_atexit if the target does not allow this<br>
<br>
Modified: cfe/trunk/lib/CodeGen/CodeGenModule.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenModule.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenModule.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/CodeGenModule.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/CodeGenModule.cpp Sat Sep 28 22:08:46 2019<br>
@@ -3809,9 +3809,9 @@ void CodeGenModule::EmitGlobalVarDefinit<br>
     return;<br>
<br>
   llvm::Constant *Init = nullptr;<br>
-  CXXRecordDecl *RD = ASTTy->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();<br>
   bool NeedsGlobalCtor = false;<br>
-  bool NeedsGlobalDtor = RD && !RD->hasTrivialDestructor();<br>
+  bool NeedsGlobalDtor =<br>
+      D->needsDestruction(getContext()) == QualType::DK_cxx_destructor;<br>
<br>
   const VarDecl *InitDecl;<br>
   const Expr *InitExpr = D->getAnyInitializer(InitDecl);<br>
<br>
Modified: cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp Sat Sep 28 22:08:46 2019<br>
@@ -350,7 +350,7 @@ public:<br>
     // If we have the only definition, we don't need a thread wrapper if we<br>
     // will emit the value as a constant.<br>
     if (isUniqueGVALinkage(getContext().GetGVALinkageForVariable(VD)))<br>
-      return !VD->getType().isDestructedType() && InitDecl->evaluateValue();<br>
+      return !VD->needsDestruction(getContext()) && InitDecl->evaluateValue();<br>
<br>
     // Otherwise, we need a thread wrapper unless we know that every<br>
     // translation unit will emit the value as a constant. We rely on<br>
<br>
Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)<br>
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Sat Sep 28 22:08:46 2019<br>
@@ -13398,6 +13398,19 @@ void Sema::FinalizeVarWithDestructor(Var<br>
   }<br>
<br>
   if (Destructor->isTrivial()) return;<br>
+<br>
+  // If the destructor is constexpr, check whether the variable has constant<br>
+  // destruction now.<br>
+  if (Destructor->isConstexpr() && VD->evaluateValue()) {<br>
+    SmallVector<PartialDiagnosticAt, 8> Notes;<br>
+    if (!VD->evaluateDestruction(Notes) && VD->isConstexpr()) {<br>
+      Diag(VD->getLocation(),<br>
+           diag::err_constexpr_var_requires_const_destruction) << VD;<br>
+      for (unsigned I = 0, N = Notes.size(); I != N; ++I)<br>
+        Diag(Notes[I].first, Notes[I].second);<br>
+    }<br>
+  }<br>
+<br>
   if (!VD->hasGlobalStorage()) return;<br>
<br>
   // Emit warning for non-trivial dtor in global scope (a real global,<br>
<br>
Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)<br>
+++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Sat Sep 28 22:08:46 2019<br>
@@ -1390,10 +1390,11 @@ ASTDeclReader::RedeclarableResult ASTDec<br>
<br>
   if (uint64_t Val = Record.readInt()) {<br>
     VD->setInit(Record.readExpr());<br>
-    if (Val > 1) { // IsInitKnownICE = 1, IsInitNotICE = 2, IsInitICE = 3<br>
+    if (Val > 1) {<br>
       EvaluatedStmt *Eval = VD->ensureEvaluatedStmt();<br>
       Eval->CheckedICE = true;<br>
-      Eval->IsICE = Val == 3;<br>
+      Eval->IsICE = (Val & 1) != 0;<br>
+      Eval->HasConstantDestruction = (Val & 4) != 0;<br>
     }<br>
   }<br>
<br>
<br>
Modified: cfe/trunk/lib/Serialization/ASTWriterDecl.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterDecl.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterDecl.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Serialization/ASTWriterDecl.cpp (original)<br>
+++ cfe/trunk/lib/Serialization/ASTWriterDecl.cpp Sat Sep 28 22:08:46 2019<br>
@@ -968,7 +968,14 @@ void ASTDeclWriter::VisitVarDecl(VarDecl<br>
   Record.push_back(D->getLinkageInternal());<br>
<br>
   if (D->getInit()) {<br>
-    Record.push_back(!D->isInitKnownICE() ? 1 : (D->isInitICE() ? 3 : 2));<br>
+    if (!D->isInitKnownICE())<br>
+      Record.push_back(1);<br>
+    else {<br>
+      Record.push_back(<br>
+          2 |<br>
+          (D->isInitICE() ? 1 : 0) |<br>
+          (D->ensureEvaluatedStmt()->HasConstantDestruction ? 4 : 0));<br>
+    }<br>
     Record.AddStmt(D->getInit());<br>
   } else {<br>
     Record.push_back(0);<br>
@@ -2140,7 +2147,7 @@ void ASTWriter::WriteDeclAbbrevs() {<br>
   Abv->Add(BitCodeAbbrevOp(0));                         // ImplicitParamKind<br>
   Abv->Add(BitCodeAbbrevOp(0));                         // EscapingByref<br>
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Linkage<br>
-  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // IsInitICE (local)<br>
+  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // IsInitICE (local)<br>
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // VarKind (local enum)<br>
   // Type Source Info<br>
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array));<br>
<br>
Modified: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp (original)<br>
+++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp Sat Sep 28 22:08:46 2019<br>
@@ -1,4 +1,5 @@<br>
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s<br>
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2a %s<br>
<br>
 // A constexpr specifier used in an object declaration declares the object as<br>
 // const.<br>
@@ -35,3 +36,19 @@ struct pixel {<br>
 };<br>
 constexpr pixel ur = { 1294, 1024 }; // ok<br>
 constexpr pixel origin;              // expected-error {{default initialization of an object of const type 'const pixel' without a user-provided default constructor}}<br>
+<br>
+#if __cplusplus > 201702L<br>
+// A constexpr variable shall have constant destruction.<br>
+struct A {<br>
+  bool ok;<br>
+  constexpr A(bool ok) : ok(ok) {}<br>
+  constexpr ~A() noexcept(false) {<br>
+    void oops(); // expected-note 2{{declared here}}<br>
+    if (!ok) oops(); // expected-note 2{{non-constexpr function}}<br>
+  }<br>
+};<br>
+<br>
+constexpr A const_dtor(true);<br>
+constexpr A non_const_dtor(false); // expected-error {{must have constant destruction}} expected-note {{in call}}<br>
+constexpr A arr_dtor[5] = {true, true, true, false, true}; // expected-error {{must have constant destruction}} expected-note {{in call to '&arr_dtor[3]->~A()'}}<br>
+#endif<br>
<br>
Added: cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp?rev=373159&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp?rev=373159&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp (added)<br>
+++ cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp Sat Sep 28 22:08:46 2019<br>
@@ -0,0 +1,43 @@<br>
+// RUN: %clang_cc1 -std=c++2a -verify %s<br>
+<br>
+constexpr int non_class = 42;<br>
+constexpr int arr_non_class[5] = {1, 2, 3};<br>
+<br>
+struct A {<br>
+  int member = 1;<br>
+  constexpr ~A() { member = member + 1; }<br>
+};<br>
+constexpr A class_ = {};<br>
+constexpr A arr_class[5] = {{}, {}};<br>
+<br>
+struct Mutable {<br>
+  mutable int member = 1; // expected-note {{declared here}}<br>
+  constexpr ~Mutable() { member = member + 1; } // expected-note {{read of mutable member}}<br>
+};<br>
+constexpr Mutable mut_member; // expected-error {{must have constant destruction}} expected-note {{in call}}<br>
+<br>
+struct MutableStore {<br>
+  mutable int member = 1; // expected-note {{declared here}}<br>
+  constexpr ~MutableStore() { member = 2; } // expected-note {{assignment to mutable member}}<br>
+};<br>
+constexpr MutableStore mut_store; // expected-error {{must have constant destruction}} expected-note {{in call}}<br>
+<br>
+// Note: the constant destruction rules disallow this example even though hcm.n is a const object.<br>
+struct MutableConst {<br>
+  struct HasConstMember {<br>
+    const int n = 4;<br>
+  };<br>
+  mutable HasConstMember hcm; // expected-note {{here}}<br>
+  constexpr ~MutableConst() {<br>
+    int q = hcm.n; // expected-note {{read of mutable}}<br>
+  }<br>
+};<br>
+constexpr MutableConst mc; // expected-error {{must have constant destruction}} expected-note {{in call}}<br>
+<br>
+struct Temporary {<br>
+  int &&temp;<br>
+  constexpr ~Temporary() {<br>
+    int n = temp; // expected-note {{outside the expression that created the temporary}}<br>
+  }<br>
+};<br>
+constexpr Temporary t = {3}; // expected-error {{must have constant destruction}} expected-note {{created here}} expected-note {{in call}}<br>
<br>
Modified: cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp (original)<br>
+++ cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp Sat Sep 28 22:08:46 2019<br>
@@ -14,6 +14,7 @@<br>
<br>
 class a {<br>
 public:<br>
+  a();<br>
   ~a();<br>
 };<br>
 class logger_base {<br>
<br>
Modified: cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp (original)<br>
+++ cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp Sat Sep 28 22:08:46 2019<br>
@@ -1,5 +1,58 @@<br>
-// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin -emit-llvm -o - %s -std=c++2a | FileCheck %s<br>
-// expected-no-diagnostics<br>
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s -std=c++2a | FileCheck %s --implicit-check-not=cxx_global_var_init --implicit-check-not=cxa_atexit<br>
+<br>
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-pch -o %t.pch %s -std=c++2a<br>
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -include-pch %t.pch -x c++ /dev/null -emit-llvm -o - -std=c++2a | FileCheck %s --implicit-check-not=cxx_global_var_init --implicit-check-not=cxa_atexit<br>
<br>
 // CHECK: @a = global i32 123,<br>
 int a = (delete new int, 123);<br>
+<br>
+struct B {<br>
+  constexpr B() {}<br>
+  constexpr ~B() { n *= 5; }<br>
+  int n = 123;<br>
+};<br>
+// CHECK: @b = global {{.*}} i32 123<br>
+extern constexpr B b = B();<br>
+<br>
+// CHECK: @_ZL1c = internal global {{.*}} i32 123<br>
+const B c;<br>
+int use_c() { return c.n; }<br>
+<br>
+struct D {<br>
+  int n;<br>
+  constexpr ~D() {}<br>
+};<br>
+D d;<br>
+// CHECK: @d = global {{.*}} zeroinitializer<br>
+<br>
+D d_arr[3];<br>
+// CHECK: @d_arr = global {{.*}} zeroinitializer<br>
+<br>
+thread_local D d_tl;<br>
+// CHECK: @d_tl = thread_local global {{.*}} zeroinitializer<br>
+<br>
+// CHECK-NOT: @llvm.global_ctors<br>
+<br>
+// CHECK-LABEL: define {{.*}} @_Z1fv(<br>
+void f() {<br>
+  // CHECK-NOT: call<br>
+  // CHECK: call {{.*}}memcpy<br>
+  // CHECK-NOT: call<br>
+  // CHECK: call {{.*}}memset<br>
+  // CHECK-NOT: call<br>
+  // CHECK: }<br>
+  constexpr B b;<br>
+  D d = D();<br>
+}<br>
+<br>
+// CHECK-LABEL: define {{.*}} @_Z1gv(<br>
+void g() {<br>
+  // CHECK-NOT: call<br>
+  // CHECK-NOT: cxa_guard<br>
+  // CHECK-NOT: _ZGV<br>
+  // CHECK: }<br>
+  static constexpr B b1;<br>
+  static const B b2;<br>
+  static D d;<br>
+  thread_local D d_tl;<br>
+}<br>
<br>
Modified: cfe/trunk/test/CodeGenCXX/no_destroy.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/no_destroy.cpp?rev=373159&r1=373158&r2=373159&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/no_destroy.cpp?rev=373159&r1=373158&r2=373159&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGenCXX/no_destroy.cpp (original)<br>
+++ cfe/trunk/test/CodeGenCXX/no_destroy.cpp Sat Sep 28 22:08:46 2019<br>
@@ -5,10 +5,8 @@ struct NonTrivial {<br>
   ~NonTrivial();<br>
 };<br>
<br>
-// CHECK-LABEL: define internal void @__cxx_global_var_init<br>
 // CHECK-NOT: __cxa_atexit{{.*}}_ZN10NonTrivialD1Ev<br>
 [[clang::no_destroy]] NonTrivial nt1;<br>
-// CHECK-LABEL: define internal void @__cxx_global_var_init<br>
 // CHECK-NOT: _tlv_atexit{{.*}}_ZN10NonTrivialD1Ev<br>
 [[clang::no_destroy]] thread_local NonTrivial nt2;<br>
<br>
@@ -16,11 +14,9 @@ struct NonTrivial2 {<br>
   ~NonTrivial2();<br>
 };<br>
<br>
-// CHECK-LABEL: define internal void @__cxx_global_var_init<br>
-// CHECK: __cxa_atexit{{.*}}_ZN11NonTrivial2D1Ev<br>
+// CHECK: __cxa_atexit{{.*}}_ZN11NonTrivial2D1Ev{{.*}}nt21<br>
 NonTrivial2 nt21;<br>
-// CHECK-LABEL: define internal void @__cxx_global_var_init<br>
-// CHECK: _tlv_atexit{{.*}}_ZN11NonTrivial2D1Ev<br>
+// CHECK: _tlv_atexit{{.*}}_ZN11NonTrivial2D1Ev{{.*}}nt22<br>
 thread_local NonTrivial2 nt22;<br>
<br>
 // CHECK-LABEL: define void @_Z1fv<br>
<br>
Added: cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp?rev=373159&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp?rev=373159&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp (added)<br>
+++ cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp Sat Sep 28 22:08:46 2019<br>
@@ -0,0 +1,19 @@<br>
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s -std=c++2a | FileCheck %s<br>
+<br>
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-pch -o %t.pch %s -std=c++2a<br>
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -include-pch %t.pch -x c++ /dev/null -emit-llvm -o - -std=c++2a | FileCheck %s<br>
+<br>
+struct B {<br>
+  constexpr B() {}<br>
+  constexpr ~B() { n *= 5; }<br>
+  int n = 123;<br>
+};<br>
+<br>
+// We emit a dynamic destructor here because b.n might have been modified<br>
+// before b is destroyed.<br>
+//<br>
+// CHECK: @b = global {{.*}} i32 123<br>
+B b = B();<br>
+<br>
+// CHECK: define {{.*}}cxx_global_var_init<br>
+// CHECK: call {{.*}} @__cxa_atexit({{.*}} @_ZN1BD1Ev {{.*}} @b<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
</blockquote></div>
</blockquote></div>
</blockquote></div>