r373159 - For P0784R7: compute whether a variable has constant destruction if it

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Sat Sep 28 22:08:46 PDT 2019


Author: rsmith
Date: Sat Sep 28 22:08:46 2019
New Revision: 373159

URL: http://llvm.org/viewvc/llvm-project?rev=373159&view=rev
Log:
For P0784R7: compute whether a variable has constant destruction if it
has a constexpr destructor.

For constexpr variables, reject if the variable does not have constant
destruction. In all cases, do not emit runtime calls to the destructor
for variables with constant destruction.

Added:
    cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp
    cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp
Modified:
    cfe/trunk/include/clang/AST/Decl.h
    cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/AST/ASTContext.cpp
    cfe/trunk/lib/AST/Decl.cpp
    cfe/trunk/lib/AST/ExprConstant.cpp
    cfe/trunk/lib/AST/Interp/Interp.cpp
    cfe/trunk/lib/AST/TextNodeDumper.cpp
    cfe/trunk/lib/CodeGen/CGCall.cpp
    cfe/trunk/lib/CodeGen/CGClass.cpp
    cfe/trunk/lib/CodeGen/CGDecl.cpp
    cfe/trunk/lib/CodeGen/CGDeclCXX.cpp
    cfe/trunk/lib/CodeGen/CodeGenModule.cpp
    cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
    cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
    cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp
    cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp
    cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp
    cfe/trunk/test/CodeGenCXX/no_destroy.cpp

Modified: cfe/trunk/include/clang/AST/Decl.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=373159&r1=373158&r2=373159&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Sat Sep 28 22:08:46 2019
@@ -808,12 +808,19 @@ struct EvaluatedStmt {
   /// valid if CheckedICE is true.
   bool IsICE : 1;
 
+  /// Whether this variable is known to have constant destruction. That is,
+  /// whether running the destructor on the initial value is a side-effect
+  /// (and doesn't inspect any state that might have changed during program
+  /// execution). This is currently only computed if the destructor is
+  /// non-trivial.
+  bool HasConstantDestruction : 1;
+
   Stmt *Value;
   APValue Evaluated;
 
-  EvaluatedStmt() : WasEvaluated(false), IsEvaluating(false), CheckedICE(false),
-                    CheckingICE(false), IsICE(false) {}
-
+  EvaluatedStmt()
+      : WasEvaluated(false), IsEvaluating(false), CheckedICE(false),
+        CheckingICE(false), IsICE(false), HasConstantDestruction(false) {}
 };
 
 /// Represents a variable declaration or definition.
@@ -1267,6 +1274,14 @@ public:
   /// to untyped APValue if the value could not be evaluated.
   APValue *getEvaluatedValue() const;
 
+  /// Evaluate the destruction of this variable to determine if it constitutes
+  /// constant destruction.
+  ///
+  /// \pre isInitICE()
+  /// \return \c true if this variable has constant destruction, \c false if
+  ///         not.
+  bool evaluateDestruction(SmallVectorImpl<PartialDiagnosticAt> &Notes) const;
+
   /// Determines whether it is already known whether the
   /// initializer is an integral constant expression or not.
   bool isInitKnownICE() const;
@@ -1505,9 +1520,14 @@ public:
   // has no definition within this source file.
   bool isKnownToBeDefined() const;
 
-  /// Do we need to emit an exit-time destructor for this variable?
+  /// Is destruction of this variable entirely suppressed? If so, the variable
+  /// need not have a usable destructor at all.
   bool isNoDestroy(const ASTContext &) const;
 
+  /// Do we need to emit an exit-time destructor for this variable, and if so,
+  /// what kind?
+  QualType::DestructionKind needsDestruction(const ASTContext &Ctx) const;
+
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) { return K >= firstVar && K <= lastVar; }

Modified: cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td?rev=373159&r1=373158&r2=373159&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td Sat Sep 28 22:08:46 2019
@@ -145,8 +145,10 @@ def note_constexpr_access_volatile_obj :
   "a constant expression">;
 def note_constexpr_volatile_here : Note<
   "volatile %select{temporary created|object declared|member declared}0 here">;
-def note_constexpr_ltor_mutable : Note<
-  "read of mutable member %0 is not allowed in a constant expression">;
+def note_constexpr_access_mutable : Note<
+  "%select{read of|read of|assignment to|increment of|decrement of|"
+  "member call on|dynamic_cast of|typeid applied to|destruction of}0 "
+  "mutable member %1 is not allowed in a constant expression">;
 def note_constexpr_ltor_non_const_int : Note<
   "read of non-const variable %0 is not allowed in a constant expression">;
 def note_constexpr_ltor_non_constexpr : Note<

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=373159&r1=373158&r2=373159&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Sat Sep 28 22:08:46 2019
@@ -2384,6 +2384,8 @@ def err_constexpr_var_non_literal : Erro
   "constexpr variable cannot have non-literal type %0">;
 def err_constexpr_var_requires_const_init : Error<
   "constexpr variable %0 must be initialized by a constant expression">;
+def err_constexpr_var_requires_const_destruction : Error<
+  "constexpr variable %0 must have constant destruction">;
 def err_constexpr_redecl_mismatch : Error<
   "%select{non-constexpr|constexpr|consteval}1 declaration of %0"
   " follows %select{non-constexpr|constexpr|consteval}2 declaration">;

Modified: cfe/trunk/lib/AST/ASTContext.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=373159&r1=373158&r2=373159&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTContext.cpp (original)
+++ cfe/trunk/lib/AST/ASTContext.cpp Sat Sep 28 22:08:46 2019
@@ -10064,7 +10064,7 @@ bool ASTContext::DeclMustBeEmitted(const
     return false;
 
   // Variables that have destruction with side-effects are required.
-  if (VD->getType().isDestructedType())
+  if (VD->needsDestruction(*this))
     return true;
 
   // Variables that have initialization with side-effects are required.

Modified: cfe/trunk/lib/AST/Decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=373159&r1=373158&r2=373159&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Decl.cpp (original)
+++ cfe/trunk/lib/AST/Decl.cpp Sat Sep 28 22:08:46 2019
@@ -2592,6 +2592,18 @@ bool VarDecl::isNoDestroy(const ASTConte
                                  !hasAttr<AlwaysDestroyAttr>()));
 }
 
+QualType::DestructionKind
+VarDecl::needsDestruction(const ASTContext &Ctx) const {
+  if (EvaluatedStmt *Eval = Init.dyn_cast<EvaluatedStmt *>())
+    if (Eval->HasConstantDestruction)
+      return QualType::DK_none;
+
+  if (isNoDestroy(Ctx))
+    return QualType::DK_none;
+
+  return getType().isDestructedType();
+}
+
 MemberSpecializationInfo *VarDecl::getMemberSpecializationInfo() const {
   if (isStaticDataMember())
     // FIXME: Remove ?

Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=373159&r1=373158&r2=373159&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Sat Sep 28 22:08:46 2019
@@ -744,6 +744,15 @@ namespace {
     /// evaluated, if any.
     APValue::LValueBase EvaluatingDecl;
 
+    enum class EvaluatingDeclKind {
+      None,
+      /// We're evaluating the construction of EvaluatingDecl.
+      Ctor,
+      /// We're evaluating the destruction of EvaluatingDecl.
+      Dtor,
+    };
+    EvaluatingDeclKind IsEvaluatingDecl = EvaluatingDeclKind::None;
+
     /// EvaluatingDeclValue - This is the value being constructed for the
     /// declaration whose initializer is being evaluated, if any.
     APValue *EvaluatingDeclValue;
@@ -902,8 +911,10 @@ namespace {
       discardCleanups();
     }
 
-    void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) {
+    void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value,
+                           EvaluatingDeclKind EDK = EvaluatingDeclKind::Ctor) {
       EvaluatingDecl = Base;
+      IsEvaluatingDecl = EDK;
       EvaluatingDeclValue = &Value;
     }
 
@@ -2913,8 +2924,8 @@ static bool isReadByLvalueToRvalueConver
 
 /// Diagnose an attempt to read from any unreadable field within the specified
 /// type, which might be a class type.
-static bool diagnoseUnreadableFields(EvalInfo &Info, const Expr *E,
-                                     QualType T) {
+static bool diagnoseMutableFields(EvalInfo &Info, const Expr *E, AccessKinds AK,
+                                  QualType T) {
   CXXRecordDecl *RD = T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
   if (!RD)
     return false;
@@ -2929,17 +2940,17 @@ static bool diagnoseUnreadableFields(Eva
     // FIXME: Add core issue number for the union case.
     if (Field->isMutable() &&
         (RD->isUnion() || isReadByLvalueToRvalueConversion(Field->getType()))) {
-      Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1) << Field;
+      Info.FFDiag(E, diag::note_constexpr_access_mutable, 1) << AK << Field;
       Info.Note(Field->getLocation(), diag::note_declared_at);
       return true;
     }
 
-    if (diagnoseUnreadableFields(Info, E, Field->getType()))
+    if (diagnoseMutableFields(Info, E, AK, Field->getType()))
       return true;
   }
 
   for (auto &BaseSpec : RD->bases())
-    if (diagnoseUnreadableFields(Info, E, BaseSpec.getType()))
+    if (diagnoseMutableFields(Info, E, AK, BaseSpec.getType()))
       return true;
 
   // All mutable fields were empty, and thus not actually read.
@@ -2947,7 +2958,8 @@ static bool diagnoseUnreadableFields(Eva
 }
 
 static bool lifetimeStartedInEvaluation(EvalInfo &Info,
-                                        APValue::LValueBase Base) {
+                                        APValue::LValueBase Base,
+                                        bool MutableSubobject = false) {
   // A temporary we created.
   if (Base.getCallIndex())
     return true;
@@ -2956,19 +2968,42 @@ static bool lifetimeStartedInEvaluation(
   if (!Evaluating)
     return false;
 
-  // The variable whose initializer we're evaluating.
-  if (auto *BaseD = Base.dyn_cast<const ValueDecl*>())
-    if (declaresSameEntity(Evaluating, BaseD))
-      return true;
+  auto *BaseD = Base.dyn_cast<const ValueDecl*>();
 
-  // A temporary lifetime-extended by the variable whose initializer we're
-  // evaluating.
-  if (auto *BaseE = Base.dyn_cast<const Expr *>())
-    if (auto *BaseMTE = dyn_cast<MaterializeTemporaryExpr>(BaseE))
-      if (declaresSameEntity(BaseMTE->getExtendingDecl(), Evaluating))
-        return true;
+  switch (Info.IsEvaluatingDecl) {
+  case EvalInfo::EvaluatingDeclKind::None:
+    return false;
 
-  return false;
+  case EvalInfo::EvaluatingDeclKind::Ctor:
+    // The variable whose initializer we're evaluating.
+    if (BaseD)
+      return declaresSameEntity(Evaluating, BaseD);
+
+    // A temporary lifetime-extended by the variable whose initializer we're
+    // evaluating.
+    if (auto *BaseE = Base.dyn_cast<const Expr *>())
+      if (auto *BaseMTE = dyn_cast<MaterializeTemporaryExpr>(BaseE))
+        return declaresSameEntity(BaseMTE->getExtendingDecl(), Evaluating);
+    return false;
+
+  case EvalInfo::EvaluatingDeclKind::Dtor:
+    // C++2a [expr.const]p6:
+    //   [during constant destruction] the lifetime of a and its non-mutable
+    //   subobjects (but not its mutable subobjects) [are] considered to start
+    //   within e.
+    //
+    // FIXME: We can meaningfully extend this to cover non-const objects, but
+    // we will need special handling: we should be able to access only
+    // subobjects of such objects that are themselves declared const.
+    if (!BaseD ||
+        !(BaseD->getType().isConstQualified() ||
+          BaseD->getType()->isReferenceType()) ||
+        MutableSubobject)
+      return false;
+    return declaresSameEntity(Evaluating, BaseD);
+  }
+
+  llvm_unreachable("unknown evaluating decl kind");
 }
 
 namespace {
@@ -2986,13 +3021,13 @@ struct CompleteObject {
   CompleteObject(APValue::LValueBase Base, APValue *Value, QualType Type)
       : Base(Base), Value(Value), Type(Type) {}
 
-  bool mayReadMutableMembers(EvalInfo &Info) const {
+  bool mayAccessMutableMembers(EvalInfo &Info, AccessKinds AK) const {
     // In C++14 onwards, it is permitted to read a mutable member whose
     // lifetime began within the evaluation.
     // FIXME: Should we also allow this in C++11?
     if (!Info.getLangOpts().CPlusPlus14)
       return false;
-    return lifetimeStartedInEvaluation(Info, Base);
+    return lifetimeStartedInEvaluation(Info, Base, /*MutableSubobject*/true);
   }
 
   explicit operator bool() const { return !Type.isNull(); }
@@ -3097,9 +3132,9 @@ findSubobject(EvalInfo &Info, const Expr
       // things we need to check: if there are any mutable subobjects, we
       // cannot perform this read. (This only happens when performing a trivial
       // copy or assignment.)
-      if (ObjType->isRecordType() && isRead(handler.AccessKind) &&
-          !Obj.mayReadMutableMembers(Info) &&
-          diagnoseUnreadableFields(Info, E, ObjType))
+      if (ObjType->isRecordType() &&
+          !Obj.mayAccessMutableMembers(Info, handler.AccessKind) &&
+          diagnoseMutableFields(Info, E, handler.AccessKind, ObjType))
         return handler.failed();
     }
 
@@ -3167,10 +3202,10 @@ findSubobject(EvalInfo &Info, const Expr
                                    : O->getComplexFloatReal(), ObjType);
       }
     } else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) {
-      if (Field->isMutable() && isRead(handler.AccessKind) &&
-          !Obj.mayReadMutableMembers(Info)) {
-        Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1)
-          << Field;
+      if (Field->isMutable() &&
+          !Obj.mayAccessMutableMembers(Info, handler.AccessKind)) {
+        Info.FFDiag(E, diag::note_constexpr_access_mutable, 1)
+          << handler.AccessKind << Field;
         Info.Note(Field->getLocation(), diag::note_declared_at);
         return handler.failed();
       }
@@ -3427,8 +3462,7 @@ static CompleteObject findCompleteObject
     // the variable we're reading must be const.
     if (!Frame) {
       if (Info.getLangOpts().CPlusPlus14 &&
-          declaresSameEntity(
-              VD, Info.EvaluatingDecl.dyn_cast<const ValueDecl *>())) {
+          lifetimeStartedInEvaluation(Info, LVal.Base)) {
         // OK, we can read and modify an object if we're in the process of
         // evaluating its initializer, because its lifetime began in this
         // evaluation.
@@ -3518,11 +3552,14 @@ static CompleteObject findCompleteObject
         //   int x = ++r;
         //   constexpr int k = r;
         // Therefore we use the C++14 rules in C++11 too.
-        const ValueDecl *VD = Info.EvaluatingDecl.dyn_cast<const ValueDecl*>();
-        const ValueDecl *ED = MTE->getExtendingDecl();
+        //
+        // Note that temporaries whose lifetimes began while evaluating a
+        // variable's constructor are not usable while evaluating the
+        // corresponding destructor, not even if they're of const-qualified
+        // types.
         if (!(BaseType.isConstQualified() &&
               BaseType->isIntegralOrEnumerationType()) &&
-            !(VD && VD->getCanonicalDecl() == ED->getCanonicalDecl())) {
+            !lifetimeStartedInEvaluation(Info, LVal.Base)) {
           if (!IsAccess)
             return CompleteObject(LVal.getLValueBase(), nullptr, BaseType);
           Info.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK;
@@ -13282,6 +13319,41 @@ bool Expr::EvaluateAsInitializer(APValue
          CheckMemoryLeaks(Info);
 }
 
+bool VarDecl::evaluateDestruction(
+    SmallVectorImpl<PartialDiagnosticAt> &Notes) const {
+  assert(getEvaluatedValue() && !getEvaluatedValue()->isAbsent() &&
+         "cannot evaluate destruction of non-constant-initialized variable");
+
+  Expr::EvalStatus EStatus;
+  EStatus.Diag = &Notes;
+
+  // Make a copy of the value for the destructor to mutate.
+  APValue DestroyedValue = *getEvaluatedValue();
+
+  EvalInfo Info(getASTContext(), EStatus, EvalInfo::EM_ConstantExpression);
+  Info.setEvaluatingDecl(this, DestroyedValue,
+                         EvalInfo::EvaluatingDeclKind::Dtor);
+  Info.InConstantContext = true;
+
+  SourceLocation DeclLoc = getLocation();
+  QualType DeclTy = getType();
+
+  LValue LVal;
+  LVal.set(this);
+
+  // FIXME: Consider storing whether this variable has constant destruction in
+  // the EvaluatedStmt so that CodeGen can query it.
+  if (!HandleDestruction(Info, DeclLoc, LVal.Base, DestroyedValue, DeclTy) ||
+      EStatus.HasSideEffects)
+    return false;
+
+  if (!Info.discardCleanups())
+    llvm_unreachable("Unhandled cleanup; missing full expression marker?");
+
+  ensureEvaluatedStmt()->HasConstantDestruction = true;
+  return true;
+}
+
 /// isEvaluatable - Call EvaluateAsRValue to see if this expression can be
 /// constant folded, but discard the result.
 bool Expr::isEvaluatable(const ASTContext &Ctx, SideEffectsKind SEK) const {

Modified: cfe/trunk/lib/AST/Interp/Interp.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Interp/Interp.cpp?rev=373159&r1=373158&r2=373159&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Interp/Interp.cpp (original)
+++ cfe/trunk/lib/AST/Interp/Interp.cpp Sat Sep 28 22:08:46 2019
@@ -275,7 +275,7 @@ bool CheckMutable(InterpState &S, CodePt
 
   const SourceInfo &Loc = S.Current->getSource(OpPC);
   const FieldDecl *Field = Ptr.getField();
-  S.FFDiag(Loc, diag::note_constexpr_ltor_mutable, 1) << Field;
+  S.FFDiag(Loc, diag::note_constexpr_access_mutable, 1) << AK_Read << Field;
   S.Note(Field->getLocation(), diag::note_declared_at);
   return false;
 }

Modified: cfe/trunk/lib/AST/TextNodeDumper.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/TextNodeDumper.cpp?rev=373159&r1=373158&r2=373159&view=diff
==============================================================================
--- cfe/trunk/lib/AST/TextNodeDumper.cpp (original)
+++ cfe/trunk/lib/AST/TextNodeDumper.cpp Sat Sep 28 22:08:46 2019
@@ -1384,6 +1384,8 @@ void TextNodeDumper::VisitVarDecl(const
       break;
     }
   }
+  if (D->needsDestruction(D->getASTContext()))
+    OS << " destroyed";
   if (D->isParameterPack())
     OS << " pack";
 }

Modified: cfe/trunk/lib/CodeGen/CGCall.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCall.cpp?rev=373159&r1=373158&r2=373159&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGCall.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGCall.cpp Sat Sep 28 22:08:46 2019
@@ -3093,7 +3093,7 @@ void CodeGenFunction::EmitDelegateCallAr
   // Deactivate the cleanup for the callee-destructed param that was pushed.
   if (hasAggregateEvaluationKind(type) && !CurFuncIsThunk &&
       type->getAs<RecordType>()->getDecl()->isParamDestroyedInCallee() &&
-      type.isDestructedType()) {
+      param->needsDestruction(getContext())) {
     EHScopeStack::stable_iterator cleanup =
         CalleeDestructedParamCleanups.lookup(cast<ParmVarDecl>(param));
     assert(cleanup.isValid() &&

Modified: cfe/trunk/lib/CodeGen/CGClass.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGClass.cpp?rev=373159&r1=373158&r2=373159&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGClass.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGClass.cpp Sat Sep 28 22:08:46 2019
@@ -2083,7 +2083,7 @@ static bool canEmitDelegateCallArgs(Code
   if (CGF.getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee()) {
     // If the parameters are callee-cleanup, it's not safe to forward.
     for (auto *P : Ctor->parameters())
-      if (P->getType().isDestructedType())
+      if (P->needsDestruction(CGF.getContext()))
         return false;
 
     // Likewise if they're inalloca.

Modified: cfe/trunk/lib/CodeGen/CGDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDecl.cpp?rev=373159&r1=373158&r2=373159&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGDecl.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGDecl.cpp Sat Sep 28 22:08:46 2019
@@ -305,14 +305,6 @@ llvm::Constant *CodeGenModule::getOrCrea
   return Addr;
 }
 
-/// hasNontrivialDestruction - Determine whether a type's destruction is
-/// non-trivial. If so, and the variable uses static initialization, we must
-/// register its destructor to run on exit.
-static bool hasNontrivialDestruction(QualType T) {
-  CXXRecordDecl *RD = T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
-  return RD && !RD->hasTrivialDestructor();
-}
-
 /// AddInitializerToStaticVarDecl - Add the initializer for 'D' to the
 /// global variable that has already been created for it.  If the initializer
 /// has a different type than GV does, this may free GV and return a different
@@ -372,7 +364,7 @@ CodeGenFunction::AddInitializerToStaticV
 
   emitter.finalize(GV);
 
-  if (hasNontrivialDestruction(D.getType()) && HaveInsertPoint()) {
+  if (D.needsDestruction(getContext()) && HaveInsertPoint()) {
     // We have a constant initializer, but a nontrivial destructor. We still
     // need to perform a guarded "initialization" in order to register the
     // destructor.
@@ -1994,7 +1986,7 @@ void CodeGenFunction::EmitAutoVarCleanup
   const VarDecl &D = *emission.Variable;
 
   // Check the type for a cleanup.
-  if (QualType::DestructionKind dtorKind = D.getType().isDestructedType())
+  if (QualType::DestructionKind dtorKind = D.needsDestruction(getContext()))
     emitAutoVarTypeCleanup(emission, dtorKind);
 
   // In GC mode, honor objc_precise_lifetime.
@@ -2404,7 +2396,8 @@ void CodeGenFunction::EmitParmDecl(const
     // cleanup.
     if (hasAggregateEvaluationKind(Ty) && !CurFuncIsThunk &&
         Ty->getAs<RecordType>()->getDecl()->isParamDestroyedInCallee()) {
-      if (QualType::DestructionKind DtorKind = Ty.isDestructedType()) {
+      if (QualType::DestructionKind DtorKind =
+              D.needsDestruction(getContext())) {
         assert((DtorKind == QualType::DK_cxx_destructor ||
                 DtorKind == QualType::DK_nontrivial_c_struct) &&
                "unexpected destructor type");

Modified: cfe/trunk/lib/CodeGen/CGDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDeclCXX.cpp?rev=373159&r1=373158&r2=373159&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGDeclCXX.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGDeclCXX.cpp Sat Sep 28 22:08:46 2019
@@ -73,16 +73,10 @@ static void EmitDeclDestroy(CodeGenFunct
   // that isn't balanced out by a destructor call as intended by the
   // attribute. This also checks for -fno-c++-static-destructors and
   // bails even if the attribute is not present.
-  if (D.isNoDestroy(CGF.getContext()))
-    return;
-
-  CodeGenModule &CGM = CGF.CGM;
+  QualType::DestructionKind DtorKind = D.needsDestruction(CGF.getContext());
 
   // FIXME:  __attribute__((cleanup)) ?
 
-  QualType Type = D.getType();
-  QualType::DestructionKind DtorKind = Type.isDestructedType();
-
   switch (DtorKind) {
   case QualType::DK_none:
     return;
@@ -101,6 +95,9 @@ static void EmitDeclDestroy(CodeGenFunct
   llvm::FunctionCallee Func;
   llvm::Constant *Argument;
 
+  CodeGenModule &CGM = CGF.CGM;
+  QualType Type = D.getType();
+
   // Special-case non-array C++ destructors, if they have the right signature.
   // Under some ABIs, destructors return this instead of void, and cannot be
   // passed directly to __cxa_atexit if the target does not allow this

Modified: cfe/trunk/lib/CodeGen/CodeGenModule.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenModule.cpp?rev=373159&r1=373158&r2=373159&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenModule.cpp (original)
+++ cfe/trunk/lib/CodeGen/CodeGenModule.cpp Sat Sep 28 22:08:46 2019
@@ -3809,9 +3809,9 @@ void CodeGenModule::EmitGlobalVarDefinit
     return;
 
   llvm::Constant *Init = nullptr;
-  CXXRecordDecl *RD = ASTTy->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
   bool NeedsGlobalCtor = false;
-  bool NeedsGlobalDtor = RD && !RD->hasTrivialDestructor();
+  bool NeedsGlobalDtor =
+      D->needsDestruction(getContext()) == QualType::DK_cxx_destructor;
 
   const VarDecl *InitDecl;
   const Expr *InitExpr = D->getAnyInitializer(InitDecl);

Modified: cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp?rev=373159&r1=373158&r2=373159&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp (original)
+++ cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp Sat Sep 28 22:08:46 2019
@@ -350,7 +350,7 @@ public:
     // If we have the only definition, we don't need a thread wrapper if we
     // will emit the value as a constant.
     if (isUniqueGVALinkage(getContext().GetGVALinkageForVariable(VD)))
-      return !VD->getType().isDestructedType() && InitDecl->evaluateValue();
+      return !VD->needsDestruction(getContext()) && InitDecl->evaluateValue();
 
     // Otherwise, we need a thread wrapper unless we know that every
     // translation unit will emit the value as a constant. We rely on

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=373159&r1=373158&r2=373159&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Sat Sep 28 22:08:46 2019
@@ -13398,6 +13398,19 @@ void Sema::FinalizeVarWithDestructor(Var
   }
 
   if (Destructor->isTrivial()) return;
+
+  // If the destructor is constexpr, check whether the variable has constant
+  // destruction now.
+  if (Destructor->isConstexpr() && VD->evaluateValue()) {
+    SmallVector<PartialDiagnosticAt, 8> Notes;
+    if (!VD->evaluateDestruction(Notes) && VD->isConstexpr()) {
+      Diag(VD->getLocation(),
+           diag::err_constexpr_var_requires_const_destruction) << VD;
+      for (unsigned I = 0, N = Notes.size(); I != N; ++I)
+        Diag(Notes[I].first, Notes[I].second);
+    }
+  }
+
   if (!VD->hasGlobalStorage()) return;
 
   // Emit warning for non-trivial dtor in global scope (a real global,

Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=373159&r1=373158&r2=373159&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Sat Sep 28 22:08:46 2019
@@ -1390,10 +1390,11 @@ ASTDeclReader::RedeclarableResult ASTDec
 
   if (uint64_t Val = Record.readInt()) {
     VD->setInit(Record.readExpr());
-    if (Val > 1) { // IsInitKnownICE = 1, IsInitNotICE = 2, IsInitICE = 3
+    if (Val > 1) {
       EvaluatedStmt *Eval = VD->ensureEvaluatedStmt();
       Eval->CheckedICE = true;
-      Eval->IsICE = Val == 3;
+      Eval->IsICE = (Val & 1) != 0;
+      Eval->HasConstantDestruction = (Val & 4) != 0;
     }
   }
 

Modified: cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterDecl.cpp?rev=373159&r1=373158&r2=373159&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTWriterDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTWriterDecl.cpp Sat Sep 28 22:08:46 2019
@@ -968,7 +968,14 @@ void ASTDeclWriter::VisitVarDecl(VarDecl
   Record.push_back(D->getLinkageInternal());
 
   if (D->getInit()) {
-    Record.push_back(!D->isInitKnownICE() ? 1 : (D->isInitICE() ? 3 : 2));
+    if (!D->isInitKnownICE())
+      Record.push_back(1);
+    else {
+      Record.push_back(
+          2 |
+          (D->isInitICE() ? 1 : 0) |
+          (D->ensureEvaluatedStmt()->HasConstantDestruction ? 4 : 0));
+    }
     Record.AddStmt(D->getInit());
   } else {
     Record.push_back(0);
@@ -2140,7 +2147,7 @@ void ASTWriter::WriteDeclAbbrevs() {
   Abv->Add(BitCodeAbbrevOp(0));                         // ImplicitParamKind
   Abv->Add(BitCodeAbbrevOp(0));                         // EscapingByref
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Linkage
-  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // IsInitICE (local)
+  Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // IsInitICE (local)
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // VarKind (local enum)
   // Type Source Info
   Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array));

Modified: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp
URL: 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
==============================================================================
--- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp (original)
+++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp Sat Sep 28 22:08:46 2019
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2a %s
 
 // A constexpr specifier used in an object declaration declares the object as
 // const.
@@ -35,3 +36,19 @@ struct pixel {
 };
 constexpr pixel ur = { 1294, 1024 }; // ok
 constexpr pixel origin;              // expected-error {{default initialization of an object of const type 'const pixel' without a user-provided default constructor}}
+
+#if __cplusplus > 201702L
+// A constexpr variable shall have constant destruction.
+struct A {
+  bool ok;
+  constexpr A(bool ok) : ok(ok) {}
+  constexpr ~A() noexcept(false) {
+    void oops(); // expected-note 2{{declared here}}
+    if (!ok) oops(); // expected-note 2{{non-constexpr function}}
+  }
+};
+
+constexpr A const_dtor(true);
+constexpr A non_const_dtor(false); // expected-error {{must have constant destruction}} expected-note {{in call}}
+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()'}}
+#endif

Added: cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp?rev=373159&view=auto
==============================================================================
--- cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp (added)
+++ cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp Sat Sep 28 22:08:46 2019
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -std=c++2a -verify %s
+
+constexpr int non_class = 42;
+constexpr int arr_non_class[5] = {1, 2, 3};
+
+struct A {
+  int member = 1;
+  constexpr ~A() { member = member + 1; }
+};
+constexpr A class_ = {};
+constexpr A arr_class[5] = {{}, {}};
+
+struct Mutable {
+  mutable int member = 1; // expected-note {{declared here}}
+  constexpr ~Mutable() { member = member + 1; } // expected-note {{read of mutable member}}
+};
+constexpr Mutable mut_member; // expected-error {{must have constant destruction}} expected-note {{in call}}
+
+struct MutableStore {
+  mutable int member = 1; // expected-note {{declared here}}
+  constexpr ~MutableStore() { member = 2; } // expected-note {{assignment to mutable member}}
+};
+constexpr MutableStore mut_store; // expected-error {{must have constant destruction}} expected-note {{in call}}
+
+// Note: the constant destruction rules disallow this example even though hcm.n is a const object.
+struct MutableConst {
+  struct HasConstMember {
+    const int n = 4;
+  };
+  mutable HasConstMember hcm; // expected-note {{here}}
+  constexpr ~MutableConst() {
+    int q = hcm.n; // expected-note {{read of mutable}}
+  }
+};
+constexpr MutableConst mc; // expected-error {{must have constant destruction}} expected-note {{in call}}
+
+struct Temporary {
+  int &&temp;
+  constexpr ~Temporary() {
+    int n = temp; // expected-note {{outside the expression that created the temporary}}
+  }
+};
+constexpr Temporary t = {3}; // expected-error {{must have constant destruction}} expected-note {{created here}} expected-note {{in call}}

Modified: cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp?rev=373159&r1=373158&r2=373159&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp Sat Sep 28 22:08:46 2019
@@ -14,6 +14,7 @@
 
 class a {
 public:
+  a();
   ~a();
 };
 class logger_base {

Modified: cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp?rev=373159&r1=373158&r2=373159&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp Sat Sep 28 22:08:46 2019
@@ -1,5 +1,58 @@
-// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin -emit-llvm -o - %s -std=c++2a | FileCheck %s
-// expected-no-diagnostics
+// 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
+
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-pch -o %t.pch %s -std=c++2a
+// 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
 
 // CHECK: @a = global i32 123,
 int a = (delete new int, 123);
+
+struct B {
+  constexpr B() {}
+  constexpr ~B() { n *= 5; }
+  int n = 123;
+};
+// CHECK: @b = global {{.*}} i32 123
+extern constexpr B b = B();
+
+// CHECK: @_ZL1c = internal global {{.*}} i32 123
+const B c;
+int use_c() { return c.n; }
+
+struct D {
+  int n;
+  constexpr ~D() {}
+};
+D d;
+// CHECK: @d = global {{.*}} zeroinitializer
+
+D d_arr[3];
+// CHECK: @d_arr = global {{.*}} zeroinitializer
+
+thread_local D d_tl;
+// CHECK: @d_tl = thread_local global {{.*}} zeroinitializer
+
+// CHECK-NOT: @llvm.global_ctors
+
+// CHECK-LABEL: define {{.*}} @_Z1fv(
+void f() {
+  // CHECK-NOT: call
+  // CHECK: call {{.*}}memcpy
+  // CHECK-NOT: call
+  // CHECK: call {{.*}}memset
+  // CHECK-NOT: call
+  // CHECK: }
+  constexpr B b;
+  D d = D();
+}
+
+// CHECK-LABEL: define {{.*}} @_Z1gv(
+void g() {
+  // CHECK-NOT: call
+  // CHECK-NOT: cxa_guard
+  // CHECK-NOT: _ZGV
+  // CHECK: }
+  static constexpr B b1;
+  static const B b2;
+  static D d;
+  thread_local D d_tl;
+}

Modified: cfe/trunk/test/CodeGenCXX/no_destroy.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/no_destroy.cpp?rev=373159&r1=373158&r2=373159&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/no_destroy.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/no_destroy.cpp Sat Sep 28 22:08:46 2019
@@ -5,10 +5,8 @@ struct NonTrivial {
   ~NonTrivial();
 };
 
-// CHECK-LABEL: define internal void @__cxx_global_var_init
 // CHECK-NOT: __cxa_atexit{{.*}}_ZN10NonTrivialD1Ev
 [[clang::no_destroy]] NonTrivial nt1;
-// CHECK-LABEL: define internal void @__cxx_global_var_init
 // CHECK-NOT: _tlv_atexit{{.*}}_ZN10NonTrivialD1Ev
 [[clang::no_destroy]] thread_local NonTrivial nt2;
 
@@ -16,11 +14,9 @@ struct NonTrivial2 {
   ~NonTrivial2();
 };
 
-// CHECK-LABEL: define internal void @__cxx_global_var_init
-// CHECK: __cxa_atexit{{.*}}_ZN11NonTrivial2D1Ev
+// CHECK: __cxa_atexit{{.*}}_ZN11NonTrivial2D1Ev{{.*}}nt21
 NonTrivial2 nt21;
-// CHECK-LABEL: define internal void @__cxx_global_var_init
-// CHECK: _tlv_atexit{{.*}}_ZN11NonTrivial2D1Ev
+// CHECK: _tlv_atexit{{.*}}_ZN11NonTrivial2D1Ev{{.*}}nt22
 thread_local NonTrivial2 nt22;
 
 // CHECK-LABEL: define void @_Z1fv

Added: cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp?rev=373159&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp (added)
+++ cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp Sat Sep 28 22:08:46 2019
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s -std=c++2a | FileCheck %s
+
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-pch -o %t.pch %s -std=c++2a
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -include-pch %t.pch -x c++ /dev/null -emit-llvm -o - -std=c++2a | FileCheck %s
+
+struct B {
+  constexpr B() {}
+  constexpr ~B() { n *= 5; }
+  int n = 123;
+};
+
+// We emit a dynamic destructor here because b.n might have been modified
+// before b is destroyed.
+//
+// CHECK: @b = global {{.*}} i32 123
+B b = B();
+
+// CHECK: define {{.*}}cxx_global_var_init
+// CHECK: call {{.*}} @__cxa_atexit({{.*}} @_ZN1BD1Ev {{.*}} @b




More information about the cfe-commits mailing list