Parcourir la source

For P0784R7: add support for constexpr destructors, and call them as
appropriate during constant evaluation.

Note that the evaluator is sometimes invoked on incomplete expressions.
In such cases, if an object is constructed but we never reach the point
where it would be destroyed (and it has non-trivial destruction), we
treat the expression as having an unmodeled side-effect.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@372538 91177308-0d34-0410-b5e6-96231b3b80d8

Richard Smith il y a 6 ans
Parent
commit
92b0353a5f

+ 22 - 7
include/clang/AST/DeclCXX.h

@@ -445,6 +445,9 @@ class CXXRecordDecl : public RecordDecl {
     /// or an implicitly declared constexpr default constructor.
     /// or an implicitly declared constexpr default constructor.
     unsigned HasConstexprDefaultConstructor : 1;
     unsigned HasConstexprDefaultConstructor : 1;
 
 
+    /// True if a defaulted destructor for this class would be constexpr.
+    unsigned DefaultedDestructorIsConstexpr : 1;
+
     /// True when this class contains at least one non-static data
     /// True when this class contains at least one non-static data
     /// member or base class of non-literal or volatile type.
     /// member or base class of non-literal or volatile type.
     unsigned HasNonLiteralTypeFieldsOrBases : 1;
     unsigned HasNonLiteralTypeFieldsOrBases : 1;
@@ -1441,6 +1444,16 @@ public:
             !(data().HasTrivialSpecialMembers & SMF_MoveAssignment));
             !(data().HasTrivialSpecialMembers & SMF_MoveAssignment));
   }
   }
 
 
+  /// Determine whether a defaulted default constructor for this class
+  /// would be constexpr.
+  bool defaultedDestructorIsConstexpr() const {
+    return data().DefaultedDestructorIsConstexpr &&
+           getASTContext().getLangOpts().CPlusPlus2a;
+  }
+
+  /// Determine whether this class has a constexpr destructor.
+  bool hasConstexprDestructor() const;
+
   /// Determine whether this class has a trivial destructor
   /// Determine whether this class has a trivial destructor
   /// (C++ [class.dtor]p3)
   /// (C++ [class.dtor]p3)
   bool hasTrivialDestructor() const {
   bool hasTrivialDestructor() const {
@@ -1532,8 +1545,10 @@ public:
   ///
   ///
   /// Only in C++17 and beyond, are lambdas literal types.
   /// Only in C++17 and beyond, are lambdas literal types.
   bool isLiteral() const {
   bool isLiteral() const {
-    return hasTrivialDestructor() &&
-           (!isLambda() || getASTContext().getLangOpts().CPlusPlus17) &&
+    ASTContext &Ctx = getASTContext();
+    return (Ctx.getLangOpts().CPlusPlus2a ? hasConstexprDestructor()
+                                          : hasTrivialDestructor()) &&
+           (!isLambda() || Ctx.getLangOpts().CPlusPlus17) &&
            !hasNonLiteralTypeFieldsOrBases() &&
            !hasNonLiteralTypeFieldsOrBases() &&
            (isAggregate() || isLambda() ||
            (isAggregate() || isLambda() ||
             hasConstexprNonCopyMoveConstructor() ||
             hasConstexprNonCopyMoveConstructor() ||
@@ -2802,9 +2817,9 @@ class CXXDestructorDecl : public CXXMethodDecl {
   CXXDestructorDecl(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
   CXXDestructorDecl(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
                     const DeclarationNameInfo &NameInfo, QualType T,
                     const DeclarationNameInfo &NameInfo, QualType T,
                     TypeSourceInfo *TInfo, bool isInline,
                     TypeSourceInfo *TInfo, bool isInline,
-                    bool isImplicitlyDeclared)
+                    bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind)
       : CXXMethodDecl(CXXDestructor, C, RD, StartLoc, NameInfo, T, TInfo,
       : CXXMethodDecl(CXXDestructor, C, RD, StartLoc, NameInfo, T, TInfo,
-                      SC_None, isInline, CSK_unspecified, SourceLocation()) {
+                      SC_None, isInline, ConstexprKind, SourceLocation()) {
     setImplicit(isImplicitlyDeclared);
     setImplicit(isImplicitlyDeclared);
   }
   }
 
 
@@ -2814,9 +2829,9 @@ public:
   static CXXDestructorDecl *Create(ASTContext &C, CXXRecordDecl *RD,
   static CXXDestructorDecl *Create(ASTContext &C, CXXRecordDecl *RD,
                                    SourceLocation StartLoc,
                                    SourceLocation StartLoc,
                                    const DeclarationNameInfo &NameInfo,
                                    const DeclarationNameInfo &NameInfo,
-                                   QualType T, TypeSourceInfo* TInfo,
-                                   bool isInline,
-                                   bool isImplicitlyDeclared);
+                                   QualType T, TypeSourceInfo *TInfo,
+                                   bool isInline, bool isImplicitlyDeclared,
+                                   ConstexprSpecKind ConstexprKind);
   static CXXDestructorDecl *CreateDeserialized(ASTContext & C, unsigned ID);
   static CXXDestructorDecl *CreateDeserialized(ASTContext & C, unsigned ID);
 
 
   void setOperatorDelete(FunctionDecl *OD, Expr *ThisArg);
   void setOperatorDelete(FunctionDecl *OD, Expr *ThisArg);

+ 2 - 0
include/clang/Basic/DiagnosticASTKinds.td

@@ -193,6 +193,8 @@ def note_constexpr_baa_insufficient_alignment : Note<
 def note_constexpr_baa_value_insufficient_alignment : Note<
 def note_constexpr_baa_value_insufficient_alignment : Note<
   "value of the aligned pointer (%0) is not a multiple of the asserted %1 "
   "value of the aligned pointer (%0) is not a multiple of the asserted %1 "
   "%plural{1:byte|:bytes}1">;
   "%plural{1:byte|:bytes}1">;
+def note_constexpr_unsupported_destruction : Note<
+  "non-trivial destruction of type %0 in a constant expression is not supported">;
 def note_constexpr_unsupported_unsized_array : Note<
 def note_constexpr_unsupported_unsized_array : Note<
   "array-to-pointer decay of array member without known bound is not supported">;
   "array-to-pointer decay of array member without known bound is not supported">;
 def note_constexpr_unsized_array_indexed : Note<
 def note_constexpr_unsized_array_indexed : Note<

+ 9 - 1
include/clang/Basic/DiagnosticSemaKinds.td

@@ -2366,7 +2366,13 @@ def err_constexpr_tag : Error<
   "%select{class|struct|interface|union|enum}0 "
   "%select{class|struct|interface|union|enum}0 "
   "cannot be marked %sub{select_constexpr_spec_kind}1">;
   "cannot be marked %sub{select_constexpr_spec_kind}1">;
 def err_constexpr_dtor : Error<
 def err_constexpr_dtor : Error<
-  "destructor cannot be marked %sub{select_constexpr_spec_kind}0">;
+  "destructor cannot be declared %sub{select_constexpr_spec_kind}0">;
+def err_constexpr_dtor_subobject : Error<
+  "destructor cannot be declared %sub{select_constexpr_spec_kind}0 because "
+  "%select{data member %2|base class %3}1 does not have a "
+  "constexpr destructor">;
+def note_constexpr_dtor_subobject : Note<
+  "%select{data member %1|base class %2}0 declared here">;
 def err_constexpr_wrong_decl_kind : Error<
 def err_constexpr_wrong_decl_kind : Error<
   "%sub{select_constexpr_spec_kind}0 can only be used "
   "%sub{select_constexpr_spec_kind}0 can only be used "
   "in %select{|variable and function|function|variable}0 declarations">;
   "in %select{|variable and function|function|variable}0 declarations">;
@@ -2507,6 +2513,8 @@ def note_non_literal_user_provided_dtor : Note<
   "%0 is not literal because it has a user-provided destructor">;
   "%0 is not literal because it has a user-provided destructor">;
 def note_non_literal_nontrivial_dtor : Note<
 def note_non_literal_nontrivial_dtor : Note<
   "%0 is not literal because it has a non-trivial destructor">;
   "%0 is not literal because it has a non-trivial destructor">;
+def note_non_literal_non_constexpr_dtor : Note<
+  "%0 is not literal because its destructor is not constexpr">;
 def note_non_literal_lambda : Note<
 def note_non_literal_lambda : Note<
   "lambda closure types are non-literal types before C++17">;
   "lambda closure types are non-literal types before C++17">;
 def warn_private_extern : Warning<
 def warn_private_extern : Warning<

+ 1 - 1
lib/AST/ASTImporter.cpp

@@ -3244,7 +3244,7 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
     if (GetImportedOrCreateDecl<CXXDestructorDecl>(
     if (GetImportedOrCreateDecl<CXXDestructorDecl>(
         ToFunction, D, Importer.getToContext(), cast<CXXRecordDecl>(DC),
         ToFunction, D, Importer.getToContext(), cast<CXXRecordDecl>(DC),
         ToInnerLocStart, NameInfo, T, TInfo, D->isInlineSpecified(),
         ToInnerLocStart, NameInfo, T, TInfo, D->isInlineSpecified(),
-        D->isImplicit()))
+        D->isImplicit(), D->getConstexprKind()))
       return ToFunction;
       return ToFunction;
 
 
     CXXDestructorDecl *ToDtor = cast<CXXDestructorDecl>(ToFunction);
     CXXDestructorDecl *ToDtor = cast<CXXDestructorDecl>(ToFunction);

+ 27 - 12
lib/AST/DeclCXX.cpp

@@ -95,6 +95,7 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
       HasDefaultedDefaultConstructor(false),
       HasDefaultedDefaultConstructor(false),
       DefaultedDefaultConstructorIsConstexpr(true),
       DefaultedDefaultConstructorIsConstexpr(true),
       HasConstexprDefaultConstructor(false),
       HasConstexprDefaultConstructor(false),
+      DefaultedDestructorIsConstexpr(true),
       HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false),
       HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false),
       UserProvidedDefaultConstructor(false), DeclaredSpecialMembers(0),
       UserProvidedDefaultConstructor(false), DeclaredSpecialMembers(0),
       ImplicitCopyConstructorCanHaveConstParamForVBase(true),
       ImplicitCopyConstructorCanHaveConstParamForVBase(true),
@@ -325,10 +326,12 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
       data().IsStandardLayout = false;
       data().IsStandardLayout = false;
       data().IsCXX11StandardLayout = false;
       data().IsCXX11StandardLayout = false;
 
 
-      // C++11 [dcl.constexpr]p4:
-      //   In the definition of a constexpr constructor [...]
-      //    -- the class shall not have any virtual base classes
+      // C++20 [dcl.constexpr]p3:
+      //   In the definition of a constexpr function [...]
+      //    -- if the function is a constructor or destructor,
+      //       its class shall not have any virtual base classes
       data().DefaultedDefaultConstructorIsConstexpr = false;
       data().DefaultedDefaultConstructorIsConstexpr = false;
+      data().DefaultedDestructorIsConstexpr = false;
 
 
       // C++1z [class.copy]p8:
       // C++1z [class.copy]p8:
       //   The implicitly-declared copy constructor for a class X will have
       //   The implicitly-declared copy constructor for a class X will have
@@ -520,6 +523,19 @@ void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) {
     data().NeedOverloadResolutionForMoveConstructor = true;
     data().NeedOverloadResolutionForMoveConstructor = true;
     data().NeedOverloadResolutionForDestructor = true;
     data().NeedOverloadResolutionForDestructor = true;
   }
   }
+
+  // C++2a [dcl.constexpr]p4:
+  //   The definition of a constexpr destructor [shall] satisfy the
+  //   following requirement:
+  //   -- for every subobject of class type or (possibly multi-dimensional)
+  //      array thereof, that class type shall have a constexpr destructor
+  if (!Subobj->hasConstexprDestructor())
+    data().DefaultedDestructorIsConstexpr = false;
+}
+
+bool CXXRecordDecl::hasConstexprDestructor() const {
+  auto *Dtor = getDestructor();
+  return Dtor ? Dtor->isConstexpr() : defaultedDestructorIsConstexpr();
 }
 }
 
 
 bool CXXRecordDecl::hasAnyDependentBases() const {
 bool CXXRecordDecl::hasAnyDependentBases() const {
@@ -2571,20 +2587,19 @@ CXXDestructorDecl *
 CXXDestructorDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
 CXXDestructorDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
   return new (C, ID)
   return new (C, ID)
       CXXDestructorDecl(C, nullptr, SourceLocation(), DeclarationNameInfo(),
       CXXDestructorDecl(C, nullptr, SourceLocation(), DeclarationNameInfo(),
-                        QualType(), nullptr, false, false);
+                        QualType(), nullptr, false, false, CSK_unspecified);
 }
 }
 
 
-CXXDestructorDecl *
-CXXDestructorDecl::Create(ASTContext &C, CXXRecordDecl *RD,
-                          SourceLocation StartLoc,
-                          const DeclarationNameInfo &NameInfo,
-                          QualType T, TypeSourceInfo *TInfo,
-                          bool isInline, bool isImplicitlyDeclared) {
+CXXDestructorDecl *CXXDestructorDecl::Create(
+    ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
+    const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
+    bool isInline, bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind) {
   assert(NameInfo.getName().getNameKind()
   assert(NameInfo.getName().getNameKind()
          == DeclarationName::CXXDestructorName &&
          == DeclarationName::CXXDestructorName &&
          "Name must refer to a destructor");
          "Name must refer to a destructor");
-  return new (C, RD) CXXDestructorDecl(C, RD, StartLoc, NameInfo, T, TInfo,
-                                       isInline, isImplicitlyDeclared);
+  return new (C, RD)
+      CXXDestructorDecl(C, RD, StartLoc, NameInfo, T, TInfo, isInline,
+                        isImplicitlyDeclared, ConstexprKind);
 }
 }
 
 
 void CXXDestructorDecl::setOperatorDelete(FunctionDecl *OD, Expr *ThisArg) {
 void CXXDestructorDecl::setOperatorDelete(FunctionDecl *OD, Expr *ThisArg) {

+ 448 - 101
lib/AST/ExprConstant.cpp

@@ -134,6 +134,14 @@ namespace {
     return E.getAsBaseOrMember().getInt();
     return E.getAsBaseOrMember().getInt();
   }
   }
 
 
+  /// Given an expression, determine the type used to store the result of
+  /// evaluating that expression.
+  static QualType getStorageType(ASTContext &Ctx, Expr *E) {
+    if (E->isRValue())
+      return E->getType();
+    return Ctx.getLValueReferenceType(E->getType());
+  }
+
   /// Given a CallExpr, try to get the alloc_size attribute. May return null.
   /// Given a CallExpr, try to get the alloc_size attribute. May return null.
   static const AllocSizeAttr *getAllocSizeAttr(const CallExpr *CE) {
   static const AllocSizeAttr *getAllocSizeAttr(const CallExpr *CE) {
     const FunctionDecl *Callee = CE->getDirectCallee();
     const FunctionDecl *Callee = CE->getDirectCallee();
@@ -572,7 +580,13 @@ namespace {
       return 0;
       return 0;
     }
     }
 
 
-    APValue &createTemporary(const void *Key, bool IsLifetimeExtended);
+    /// Allocate storage for an object of type T in this stack frame.
+    /// Populates LV with a handle to the created object. Key identifies
+    /// the temporary within the stack frame, and must not be reused without
+    /// bumping the temporary version number.
+    template<typename KeyT>
+    APValue &createTemporary(const KeyT *Key, QualType T,
+                             bool IsLifetimeExtended, LValue &LV);
 
 
     void describe(llvm::raw_ostream &OS) override;
     void describe(llvm::raw_ostream &OS) override;
 
 
@@ -596,18 +610,33 @@ namespace {
     CallStackFrame &Frame;
     CallStackFrame &Frame;
     const LValue *OldThis;
     const LValue *OldThis;
   };
   };
+}
+
+static bool HandleDestructorCall(EvalInfo &Info, APValue::LValueBase LVBase,
+                                 APValue &Value, QualType T);
 
 
+namespace {
   /// A cleanup, and a flag indicating whether it is lifetime-extended.
   /// A cleanup, and a flag indicating whether it is lifetime-extended.
   class Cleanup {
   class Cleanup {
     llvm::PointerIntPair<APValue*, 1, bool> Value;
     llvm::PointerIntPair<APValue*, 1, bool> Value;
+    APValue::LValueBase Base;
+    QualType T;
 
 
   public:
   public:
-    Cleanup(APValue *Val, bool IsLifetimeExtended)
-        : Value(Val, IsLifetimeExtended) {}
+    Cleanup(APValue *Val, APValue::LValueBase Base, QualType T,
+            bool IsLifetimeExtended)
+        : Value(Val, IsLifetimeExtended), Base(Base), T(T) {}
 
 
     bool isLifetimeExtended() const { return Value.getInt(); }
     bool isLifetimeExtended() const { return Value.getInt(); }
-    void endLifetime() {
+    bool endLifetime(EvalInfo &Info, bool RunDestructors) {
+      if (RunDestructors && T.isDestructedType())
+        return HandleDestructorCall(Info, Base, *Value.getPointer(), T);
       *Value.getPointer() = APValue();
       *Value.getPointer() = APValue();
+      return true;
+    }
+
+    bool hasSideEffect() {
+      return T.isDestructedType();
     }
     }
   };
   };
 
 
@@ -623,7 +652,13 @@ namespace {
       return llvm::hash_combine(Obj.Base, Obj.Path);
       return llvm::hash_combine(Obj.Base, Obj.Path);
     }
     }
   };
   };
-  enum class ConstructionPhase { None, Bases, AfterBases };
+  enum class ConstructionPhase {
+    None,
+    Bases,
+    AfterBases,
+    Destroying,
+    DestroyingBases
+  };
 }
 }
 
 
 namespace llvm {
 namespace llvm {
@@ -728,9 +763,29 @@ namespace {
       }
       }
     };
     };
 
 
+    struct EvaluatingDestructorRAII {
+      EvalInfo &EI;
+      ObjectUnderConstruction Object;
+      EvaluatingDestructorRAII(EvalInfo &EI, ObjectUnderConstruction Object)
+          : EI(EI), Object(Object) {
+        bool DidInsert = EI.ObjectsUnderConstruction
+                             .insert({Object, ConstructionPhase::Destroying})
+                             .second;
+        (void)DidInsert;
+        assert(DidInsert && "destroyed object multiple times");
+      }
+      void startedDestroyingBases() {
+        EI.ObjectsUnderConstruction[Object] =
+            ConstructionPhase::DestroyingBases;
+      }
+      ~EvaluatingDestructorRAII() {
+        EI.ObjectsUnderConstruction.erase(Object);
+      }
+    };
+
     ConstructionPhase
     ConstructionPhase
-    isEvaluatingConstructor(APValue::LValueBase Base,
-                            ArrayRef<APValue::LValuePathEntry> Path) {
+    isEvaluatingCtorDtor(APValue::LValueBase Base,
+                         ArrayRef<APValue::LValuePathEntry> Path) {
       return ObjectsUnderConstruction.lookup({Base, Path});
       return ObjectsUnderConstruction.lookup({Base, Path});
     }
     }
 
 
@@ -811,6 +866,10 @@ namespace {
           HasFoldFailureDiagnostic(false), InConstantContext(false),
           HasFoldFailureDiagnostic(false), InConstantContext(false),
           EvalMode(Mode) {}
           EvalMode(Mode) {}
 
 
+    ~EvalInfo() {
+      discardCleanups();
+    }
+
     void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) {
     void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) {
       EvaluatingDecl = Base;
       EvaluatingDecl = Base;
       EvaluatingDeclValue = &Value;
       EvaluatingDeclValue = &Value;
@@ -858,6 +917,25 @@ namespace {
       return true;
       return true;
     }
     }
 
 
+    void performLifetimeExtension() {
+      // Disable the cleanups for lifetime-extended temporaries.
+      CleanupStack.erase(
+          std::remove_if(CleanupStack.begin(), CleanupStack.end(),
+                         [](Cleanup &C) { return C.isLifetimeExtended(); }),
+          CleanupStack.end());
+     }
+
+    /// Throw away any remaining cleanups at the end of evaluation. If any
+    /// cleanups would have had a side-effect, note that as an unmodeled
+    /// side-effect and return false. Otherwise, return true.
+    bool discardCleanups() {
+      for (Cleanup &C : CleanupStack)
+        if (C.hasSideEffect())
+          if (!noteSideEffect())
+            return false;
+      return true;
+    }
+
   private:
   private:
     interp::Frame *getCurrentFrame() override { return CurrentCall; }
     interp::Frame *getCurrentFrame() override { return CurrentCall; }
     const interp::Frame *getBottomFrame() const override { return &BottomFrame; }
     const interp::Frame *getBottomFrame() const override { return &BottomFrame; }
@@ -1101,29 +1179,37 @@ namespace {
       // temporaries created in different iterations of a loop.
       // temporaries created in different iterations of a loop.
       Info.CurrentCall->pushTempVersion();
       Info.CurrentCall->pushTempVersion();
     }
     }
+    bool destroy(bool RunDestructors = true) {
+      bool OK = cleanup(Info, RunDestructors, OldStackSize);
+      OldStackSize = -1U;
+      return OK;
+    }
     ~ScopeRAII() {
     ~ScopeRAII() {
+      if (OldStackSize != -1U)
+        destroy(false);
       // Body moved to a static method to encourage the compiler to inline away
       // Body moved to a static method to encourage the compiler to inline away
       // instances of this class.
       // instances of this class.
-      cleanup(Info, OldStackSize);
       Info.CurrentCall->popTempVersion();
       Info.CurrentCall->popTempVersion();
     }
     }
   private:
   private:
-    static void cleanup(EvalInfo &Info, unsigned OldStackSize) {
-      unsigned NewEnd = OldStackSize;
-      for (unsigned I = OldStackSize, N = Info.CleanupStack.size();
-           I != N; ++I) {
-        if (IsFullExpression && Info.CleanupStack[I].isLifetimeExtended()) {
-          // Full-expression cleanup of a lifetime-extended temporary: nothing
-          // to do, just move this cleanup to the right place in the stack.
-          std::swap(Info.CleanupStack[I], Info.CleanupStack[NewEnd]);
-          ++NewEnd;
-        } else {
-          // End the lifetime of the object.
-          Info.CleanupStack[I].endLifetime();
+    static bool cleanup(EvalInfo &Info, bool RunDestructors, unsigned OldStackSize) {
+      // Run all cleanups for a block scope, and non-lifetime-extended cleanups
+      // for a full-expression scope.
+      for (unsigned I = Info.CleanupStack.size(); I > OldStackSize; --I) {
+        if (!(IsFullExpression && Info.CleanupStack[I-1].isLifetimeExtended())) {
+          if (!Info.CleanupStack[I-1].endLifetime(Info, RunDestructors))
+            return false;
         }
         }
       }
       }
-      Info.CleanupStack.erase(Info.CleanupStack.begin() + NewEnd,
-                              Info.CleanupStack.end());
+
+      // Compact lifetime-extended cleanups.
+      auto NewEnd = Info.CleanupStack.begin() + OldStackSize;
+      if (IsFullExpression)
+        NewEnd =
+            std::remove_if(NewEnd, Info.CleanupStack.end(),
+                           [](Cleanup &C) { return !C.isLifetimeExtended(); });
+      Info.CleanupStack.erase(NewEnd, Info.CleanupStack.end());
+      return true;
     }
     }
   };
   };
   typedef ScopeRAII<false> BlockScopeRAII;
   typedef ScopeRAII<false> BlockScopeRAII;
@@ -1183,15 +1269,6 @@ CallStackFrame::~CallStackFrame() {
   Info.CurrentCall = Caller;
   Info.CurrentCall = Caller;
 }
 }
 
 
-APValue &CallStackFrame::createTemporary(const void *Key,
-                                         bool IsLifetimeExtended) {
-  unsigned Version = Info.CurrentCall->getTempVersion();
-  APValue &Result = Temporaries[MapKeyTy(Key, Version)];
-  assert(Result.isAbsent() && "temporary created multiple times");
-  Info.CleanupStack.push_back(Cleanup(&Result, IsLifetimeExtended));
-  return Result;
-}
-
 static bool isRead(AccessKinds AK) {
 static bool isRead(AccessKinds AK) {
   return AK == AK_Read || AK == AK_ReadObjectRepresentation;
   return AK == AK_Read || AK == AK_ReadObjectRepresentation;
 }
 }
@@ -1544,15 +1621,6 @@ static bool EvaluateFixedPoint(const Expr *E, APFixedPoint &Result,
 // Misc utilities
 // Misc utilities
 //===----------------------------------------------------------------------===//
 //===----------------------------------------------------------------------===//
 
 
-/// A helper function to create a temporary and set an LValue.
-template <class KeyTy>
-static APValue &createTemporary(const KeyTy *Key, bool IsLifetimeExtended,
-                                LValue &LV, CallStackFrame &Frame) {
-  LV.set({Key, Frame.Info.CurrentCall->Index,
-          Frame.Info.CurrentCall->getTempVersion()});
-  return Frame.createTemporary(Key, IsLifetimeExtended);
-}
-
 /// Negate an APSInt in place, converting it to a signed form if necessary, and
 /// Negate an APSInt in place, converting it to a signed form if necessary, and
 /// preserving its value (by extending by up to one bit as needed).
 /// preserving its value (by extending by up to one bit as needed).
 static void negateAsSigned(APSInt &Int) {
 static void negateAsSigned(APSInt &Int) {
@@ -1563,6 +1631,18 @@ static void negateAsSigned(APSInt &Int) {
   Int = -Int;
   Int = -Int;
 }
 }
 
 
+template<typename KeyT>
+APValue &CallStackFrame::createTemporary(const KeyT *Key, QualType T,
+                                         bool IsLifetimeExtended, LValue &LV) {
+  unsigned Version = Info.CurrentCall->getTempVersion();
+  APValue::LValueBase Base(Key, Index, Version);
+  LV.set(Base);
+  APValue &Result = Temporaries[MapKeyTy(Key, Version)];
+  assert(Result.isAbsent() && "temporary created multiple times");
+  Info.CleanupStack.push_back(Cleanup(&Result, Base, T, IsLifetimeExtended));
+  return Result;
+}
+
 /// Produce a string describing the given constexpr call.
 /// Produce a string describing the given constexpr call.
 void CallStackFrame::describe(raw_ostream &Out) {
 void CallStackFrame::describe(raw_ostream &Out) {
   unsigned ArgIndex = 0;
   unsigned ArgIndex = 0;
@@ -2858,12 +2938,12 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
       return handler.failed();
       return handler.failed();
     }
     }
 
 
-    // C++ [class.ctor]p5:
+    // C++ [class.ctor]p5, C++ [class.dtor]p5:
     //    const and volatile semantics are not applied on an object under
     //    const and volatile semantics are not applied on an object under
-    //    construction.
+    //    {con,de}struction.
     if ((ObjType.isConstQualified() || ObjType.isVolatileQualified()) &&
     if ((ObjType.isConstQualified() || ObjType.isVolatileQualified()) &&
         ObjType->isRecordType() &&
         ObjType->isRecordType() &&
-        Info.isEvaluatingConstructor(
+        Info.isEvaluatingCtorDtor(
             Obj.Base, llvm::makeArrayRef(Sub.Entries.begin(),
             Obj.Base, llvm::makeArrayRef(Sub.Entries.begin(),
                                          Sub.Entries.begin() + I)) !=
                                          Sub.Entries.begin() + I)) !=
                           ConstructionPhase::None) {
                           ConstructionPhase::None) {
@@ -3938,7 +4018,8 @@ static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) {
     return true;
     return true;
 
 
   LValue Result;
   LValue Result;
-  APValue &Val = createTemporary(VD, true, Result, *Info.CurrentCall);
+  APValue &Val =
+      Info.CurrentCall->createTemporary(VD, VD->getType(), true, Result);
 
 
   const Expr *InitE = VD->getInit();
   const Expr *InitE = VD->getInit();
   if (!InitE) {
   if (!InitE) {
@@ -3980,7 +4061,9 @@ static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl,
   FullExpressionRAII Scope(Info);
   FullExpressionRAII Scope(Info);
   if (CondDecl && !EvaluateDecl(Info, CondDecl))
   if (CondDecl && !EvaluateDecl(Info, CondDecl))
     return false;
     return false;
-  return EvaluateAsBooleanCondition(Cond, Result, Info);
+  if (!EvaluateAsBooleanCondition(Cond, Result, Info))
+    return false;
+  return Scope.destroy();
 }
 }
 
 
 namespace {
 namespace {
@@ -4016,7 +4099,12 @@ static EvalStmtResult EvaluateLoopBody(StmtResult &Result, EvalInfo &Info,
                                        const Stmt *Body,
                                        const Stmt *Body,
                                        const SwitchCase *Case = nullptr) {
                                        const SwitchCase *Case = nullptr) {
   BlockScopeRAII Scope(Info);
   BlockScopeRAII Scope(Info);
-  switch (EvalStmtResult ESR = EvaluateStmt(Result, Info, Body, Case)) {
+
+  EvalStmtResult ESR = EvaluateStmt(Result, Info, Body, Case);
+  if (ESR != ESR_Failed && ESR != ESR_CaseNotFound && !Scope.destroy())
+    ESR = ESR_Failed;
+
+  switch (ESR) {
   case ESR_Break:
   case ESR_Break:
     return ESR_Succeeded;
     return ESR_Succeeded;
   case ESR_Succeeded:
   case ESR_Succeeded:
@@ -4038,17 +4126,23 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
   // Evaluate the switch condition.
   // Evaluate the switch condition.
   APSInt Value;
   APSInt Value;
   {
   {
-    FullExpressionRAII Scope(Info);
     if (const Stmt *Init = SS->getInit()) {
     if (const Stmt *Init = SS->getInit()) {
       EvalStmtResult ESR = EvaluateStmt(Result, Info, Init);
       EvalStmtResult ESR = EvaluateStmt(Result, Info, Init);
-      if (ESR != ESR_Succeeded)
+      if (ESR != ESR_Succeeded) {
+        if (ESR != ESR_Failed && !Scope.destroy())
+          ESR = ESR_Failed;
         return ESR;
         return ESR;
+      }
     }
     }
+
+    FullExpressionRAII CondScope(Info);
     if (SS->getConditionVariable() &&
     if (SS->getConditionVariable() &&
         !EvaluateDecl(Info, SS->getConditionVariable()))
         !EvaluateDecl(Info, SS->getConditionVariable()))
       return ESR_Failed;
       return ESR_Failed;
     if (!EvaluateInteger(SS->getCond(), Value, Info))
     if (!EvaluateInteger(SS->getCond(), Value, Info))
       return ESR_Failed;
       return ESR_Failed;
+    if (!CondScope.destroy())
+      return ESR_Failed;
   }
   }
 
 
   // Find the switch case corresponding to the value of the condition.
   // Find the switch case corresponding to the value of the condition.
@@ -4072,10 +4166,14 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
   }
   }
 
 
   if (!Found)
   if (!Found)
-    return ESR_Succeeded;
+    return Scope.destroy() ? ESR_Failed : ESR_Succeeded;
 
 
   // Search the switch body for the switch case and evaluate it from there.
   // Search the switch body for the switch case and evaluate it from there.
-  switch (EvalStmtResult ESR = EvaluateStmt(Result, Info, SS->getBody(), Found)) {
+  EvalStmtResult ESR = EvaluateStmt(Result, Info, SS->getBody(), Found);
+  if (ESR != ESR_Failed && ESR != ESR_CaseNotFound && !Scope.destroy())
+    return ESR_Failed;
+
+  switch (ESR) {
   case ESR_Break:
   case ESR_Break:
     return ESR_Succeeded;
     return ESR_Succeeded;
   case ESR_Succeeded:
   case ESR_Succeeded:
@@ -4143,9 +4241,19 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
       // (The same is true for 'for' statements.)
       // (The same is true for 'for' statements.)
 
 
       EvalStmtResult ESR = EvaluateStmt(Result, Info, IS->getThen(), Case);
       EvalStmtResult ESR = EvaluateStmt(Result, Info, IS->getThen(), Case);
-      if (ESR != ESR_CaseNotFound || !IS->getElse())
+      if (ESR == ESR_Failed)
+        return ESR;
+      if (ESR != ESR_CaseNotFound)
+        return Scope.destroy() ? ESR : ESR_Failed;
+      if (!IS->getElse())
+        return ESR_CaseNotFound;
+
+      ESR = EvaluateStmt(Result, Info, IS->getElse(), Case);
+      if (ESR == ESR_Failed)
         return ESR;
         return ESR;
-      return EvaluateStmt(Result, Info, IS->getElse(), Case);
+      if (ESR != ESR_CaseNotFound)
+        return Scope.destroy() ? ESR : ESR_Failed;
+      return ESR_CaseNotFound;
     }
     }
 
 
     case Stmt::WhileStmtClass: {
     case Stmt::WhileStmtClass: {
@@ -4176,7 +4284,7 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
         return ESR;
         return ESR;
       if (FS->getInc()) {
       if (FS->getInc()) {
         FullExpressionRAII IncScope(Info);
         FullExpressionRAII IncScope(Info);
-        if (!EvaluateIgnoredValue(Info, FS->getInc()))
+        if (!EvaluateIgnoredValue(Info, FS->getInc()) || !IncScope.destroy())
           return ESR_Failed;
           return ESR_Failed;
       }
       }
       break;
       break;
@@ -4209,8 +4317,10 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
     if (const Expr *E = dyn_cast<Expr>(S)) {
     if (const Expr *E = dyn_cast<Expr>(S)) {
       // Don't bother evaluating beyond an expression-statement which couldn't
       // Don't bother evaluating beyond an expression-statement which couldn't
       // be evaluated.
       // be evaluated.
+      // FIXME: Do we need the FullExpressionRAII object here?
+      // VisitExprWithCleanups should create one when necessary.
       FullExpressionRAII Scope(Info);
       FullExpressionRAII Scope(Info);
-      if (!EvaluateIgnoredValue(Info, E))
+      if (!EvaluateIgnoredValue(Info, E) || !Scope.destroy())
         return ESR_Failed;
         return ESR_Failed;
       return ESR_Succeeded;
       return ESR_Succeeded;
     }
     }
@@ -4228,6 +4338,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
       FullExpressionRAII Scope(Info);
       FullExpressionRAII Scope(Info);
       if (!EvaluateDecl(Info, D) && !Info.noteFailure())
       if (!EvaluateDecl(Info, D) && !Info.noteFailure())
         return ESR_Failed;
         return ESR_Failed;
+      if (!Scope.destroy())
+        return ESR_Failed;
     }
     }
     return ESR_Succeeded;
     return ESR_Succeeded;
   }
   }
@@ -4240,7 +4352,7 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
               ? EvaluateInPlace(Result.Value, Info, *Result.Slot, RetExpr)
               ? EvaluateInPlace(Result.Value, Info, *Result.Slot, RetExpr)
               : Evaluate(Result.Value, Info, RetExpr)))
               : Evaluate(Result.Value, Info, RetExpr)))
       return ESR_Failed;
       return ESR_Failed;
-    return ESR_Returned;
+    return Scope.destroy() ? ESR_Returned : ESR_Failed;
   }
   }
 
 
   case Stmt::CompoundStmtClass: {
   case Stmt::CompoundStmtClass: {
@@ -4251,10 +4363,15 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
       EvalStmtResult ESR = EvaluateStmt(Result, Info, BI, Case);
       EvalStmtResult ESR = EvaluateStmt(Result, Info, BI, Case);
       if (ESR == ESR_Succeeded)
       if (ESR == ESR_Succeeded)
         Case = nullptr;
         Case = nullptr;
-      else if (ESR != ESR_CaseNotFound)
+      else if (ESR != ESR_CaseNotFound) {
+        if (ESR != ESR_Failed && !Scope.destroy())
+          return ESR_Failed;
         return ESR;
         return ESR;
+      }
     }
     }
-    return Case ? ESR_CaseNotFound : ESR_Succeeded;
+    if (Case)
+      return ESR_CaseNotFound;
+    return Scope.destroy() ? ESR_Succeeded : ESR_Failed;
   }
   }
 
 
   case Stmt::IfStmtClass: {
   case Stmt::IfStmtClass: {
@@ -4264,8 +4381,11 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
     BlockScopeRAII Scope(Info);
     BlockScopeRAII Scope(Info);
     if (const Stmt *Init = IS->getInit()) {
     if (const Stmt *Init = IS->getInit()) {
       EvalStmtResult ESR = EvaluateStmt(Result, Info, Init);
       EvalStmtResult ESR = EvaluateStmt(Result, Info, Init);
-      if (ESR != ESR_Succeeded)
+      if (ESR != ESR_Succeeded) {
+        if (ESR != ESR_Failed && !Scope.destroy())
+          return ESR_Failed;
         return ESR;
         return ESR;
+      }
     }
     }
     bool Cond;
     bool Cond;
     if (!EvaluateCond(Info, IS->getConditionVariable(), IS->getCond(), Cond))
     if (!EvaluateCond(Info, IS->getConditionVariable(), IS->getCond(), Cond))
@@ -4273,10 +4393,13 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
 
 
     if (const Stmt *SubStmt = Cond ? IS->getThen() : IS->getElse()) {
     if (const Stmt *SubStmt = Cond ? IS->getThen() : IS->getElse()) {
       EvalStmtResult ESR = EvaluateStmt(Result, Info, SubStmt);
       EvalStmtResult ESR = EvaluateStmt(Result, Info, SubStmt);
-      if (ESR != ESR_Succeeded)
+      if (ESR != ESR_Succeeded) {
+        if (ESR != ESR_Failed && !Scope.destroy())
+          return ESR_Failed;
         return ESR;
         return ESR;
+      }
     }
     }
-    return ESR_Succeeded;
+    return Scope.destroy() ? ESR_Succeeded : ESR_Failed;
   }
   }
 
 
   case Stmt::WhileStmtClass: {
   case Stmt::WhileStmtClass: {
@@ -4291,8 +4414,13 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
         break;
         break;
 
 
       EvalStmtResult ESR = EvaluateLoopBody(Result, Info, WS->getBody());
       EvalStmtResult ESR = EvaluateLoopBody(Result, Info, WS->getBody());
-      if (ESR != ESR_Continue)
+      if (ESR != ESR_Continue) {
+        if (ESR != ESR_Failed && !Scope.destroy())
+          return ESR_Failed;
         return ESR;
         return ESR;
+      }
+      if (!Scope.destroy())
+        return ESR_Failed;
     }
     }
     return ESR_Succeeded;
     return ESR_Succeeded;
   }
   }
@@ -4307,7 +4435,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
       Case = nullptr;
       Case = nullptr;
 
 
       FullExpressionRAII CondScope(Info);
       FullExpressionRAII CondScope(Info);
-      if (!EvaluateAsBooleanCondition(DS->getCond(), Continue, Info))
+      if (!EvaluateAsBooleanCondition(DS->getCond(), Continue, Info) ||
+          !CondScope.destroy())
         return ESR_Failed;
         return ESR_Failed;
     } while (Continue);
     } while (Continue);
     return ESR_Succeeded;
     return ESR_Succeeded;
@@ -4315,14 +4444,17 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
 
 
   case Stmt::ForStmtClass: {
   case Stmt::ForStmtClass: {
     const ForStmt *FS = cast<ForStmt>(S);
     const ForStmt *FS = cast<ForStmt>(S);
-    BlockScopeRAII Scope(Info);
+    BlockScopeRAII ForScope(Info);
     if (FS->getInit()) {
     if (FS->getInit()) {
       EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getInit());
       EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getInit());
-      if (ESR != ESR_Succeeded)
+      if (ESR != ESR_Succeeded) {
+        if (ESR != ESR_Failed && !ForScope.destroy())
+          return ESR_Failed;
         return ESR;
         return ESR;
+      }
     }
     }
     while (true) {
     while (true) {
-      BlockScopeRAII Scope(Info);
+      BlockScopeRAII IterScope(Info);
       bool Continue = true;
       bool Continue = true;
       if (FS->getCond() && !EvaluateCond(Info, FS->getConditionVariable(),
       if (FS->getCond() && !EvaluateCond(Info, FS->getConditionVariable(),
                                          FS->getCond(), Continue))
                                          FS->getCond(), Continue))
@@ -4331,16 +4463,22 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
         break;
         break;
 
 
       EvalStmtResult ESR = EvaluateLoopBody(Result, Info, FS->getBody());
       EvalStmtResult ESR = EvaluateLoopBody(Result, Info, FS->getBody());
-      if (ESR != ESR_Continue)
+      if (ESR != ESR_Continue) {
+        if (ESR != ESR_Failed && (!IterScope.destroy() || !ForScope.destroy()))
+          return ESR_Failed;
         return ESR;
         return ESR;
+      }
 
 
       if (FS->getInc()) {
       if (FS->getInc()) {
         FullExpressionRAII IncScope(Info);
         FullExpressionRAII IncScope(Info);
-        if (!EvaluateIgnoredValue(Info, FS->getInc()))
+        if (!EvaluateIgnoredValue(Info, FS->getInc()) || !IncScope.destroy())
           return ESR_Failed;
           return ESR_Failed;
       }
       }
+
+      if (!IterScope.destroy())
+        return ESR_Failed;
     }
     }
-    return ESR_Succeeded;
+    return ForScope.destroy() ? ESR_Succeeded : ESR_Failed;
   }
   }
 
 
   case Stmt::CXXForRangeStmtClass: {
   case Stmt::CXXForRangeStmtClass: {
@@ -4350,22 +4488,34 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
     // Evaluate the init-statement if present.
     // Evaluate the init-statement if present.
     if (FS->getInit()) {
     if (FS->getInit()) {
       EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getInit());
       EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getInit());
-      if (ESR != ESR_Succeeded)
+      if (ESR != ESR_Succeeded) {
+        if (ESR != ESR_Failed && !Scope.destroy())
+          return ESR_Failed;
         return ESR;
         return ESR;
+      }
     }
     }
 
 
     // Initialize the __range variable.
     // Initialize the __range variable.
     EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getRangeStmt());
     EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getRangeStmt());
-    if (ESR != ESR_Succeeded)
+    if (ESR != ESR_Succeeded) {
+      if (ESR != ESR_Failed && !Scope.destroy())
+        return ESR_Failed;
       return ESR;
       return ESR;
+    }
 
 
     // Create the __begin and __end iterators.
     // Create the __begin and __end iterators.
     ESR = EvaluateStmt(Result, Info, FS->getBeginStmt());
     ESR = EvaluateStmt(Result, Info, FS->getBeginStmt());
-    if (ESR != ESR_Succeeded)
+    if (ESR != ESR_Succeeded) {
+      if (ESR != ESR_Failed && !Scope.destroy())
+        return ESR_Failed;
       return ESR;
       return ESR;
+    }
     ESR = EvaluateStmt(Result, Info, FS->getEndStmt());
     ESR = EvaluateStmt(Result, Info, FS->getEndStmt());
-    if (ESR != ESR_Succeeded)
+    if (ESR != ESR_Succeeded) {
+      if (ESR != ESR_Failed && !Scope.destroy())
+        return ESR_Failed;
       return ESR;
       return ESR;
+    }
 
 
     while (true) {
     while (true) {
       // Condition: __begin != __end.
       // Condition: __begin != __end.
@@ -4381,20 +4531,29 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
       // User's variable declaration, initialized by *__begin.
       // User's variable declaration, initialized by *__begin.
       BlockScopeRAII InnerScope(Info);
       BlockScopeRAII InnerScope(Info);
       ESR = EvaluateStmt(Result, Info, FS->getLoopVarStmt());
       ESR = EvaluateStmt(Result, Info, FS->getLoopVarStmt());
-      if (ESR != ESR_Succeeded)
+      if (ESR != ESR_Succeeded) {
+        if (ESR != ESR_Failed && (!InnerScope.destroy() || !Scope.destroy()))
+          return ESR_Failed;
         return ESR;
         return ESR;
+      }
 
 
       // Loop body.
       // Loop body.
       ESR = EvaluateLoopBody(Result, Info, FS->getBody());
       ESR = EvaluateLoopBody(Result, Info, FS->getBody());
-      if (ESR != ESR_Continue)
+      if (ESR != ESR_Continue) {
+        if (ESR != ESR_Failed && (!InnerScope.destroy() || !Scope.destroy()))
+          return ESR_Failed;
         return ESR;
         return ESR;
+      }
 
 
       // Increment: ++__begin
       // Increment: ++__begin
       if (!EvaluateIgnoredValue(Info, FS->getInc()))
       if (!EvaluateIgnoredValue(Info, FS->getInc()))
         return ESR_Failed;
         return ESR_Failed;
+
+      if (!InnerScope.destroy())
+        return ESR_Failed;
     }
     }
 
 
-    return ESR_Succeeded;
+    return Scope.destroy() ? ESR_Succeeded : ESR_Failed;
   }
   }
 
 
   case Stmt::SwitchStmtClass:
   case Stmt::SwitchStmtClass:
@@ -4619,16 +4778,19 @@ static Optional<DynamicType> ComputeDynamicType(EvalInfo &Info, const Expr *E,
   ArrayRef<APValue::LValuePathEntry> Path = This.Designator.Entries;
   ArrayRef<APValue::LValuePathEntry> Path = This.Designator.Entries;
   for (unsigned PathLength = This.Designator.MostDerivedPathLength;
   for (unsigned PathLength = This.Designator.MostDerivedPathLength;
        PathLength <= Path.size(); ++PathLength) {
        PathLength <= Path.size(); ++PathLength) {
-    switch (Info.isEvaluatingConstructor(This.getLValueBase(),
-                                         Path.slice(0, PathLength))) {
+    switch (Info.isEvaluatingCtorDtor(This.getLValueBase(),
+                                      Path.slice(0, PathLength))) {
     case ConstructionPhase::Bases:
     case ConstructionPhase::Bases:
-      // We're constructing a base class. This is not the dynamic type.
+    case ConstructionPhase::DestroyingBases:
+      // We're constructing or destroying a base class. This is not the dynamic
+      // type.
       break;
       break;
 
 
     case ConstructionPhase::None:
     case ConstructionPhase::None:
     case ConstructionPhase::AfterBases:
     case ConstructionPhase::AfterBases:
-      // We've finished constructing the base classes, so this is the dynamic
-      // type.
+    case ConstructionPhase::Destroying:
+      // We've finished constructing the base classes and not yet started
+      // destroying them again, so this is the dynamic type.
       return DynamicType{getBaseClassType(This.Designator, PathLength),
       return DynamicType{getBaseClassType(This.Designator, PathLength),
                          PathLength};
                          PathLength};
     }
     }
@@ -5117,7 +5279,8 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
     CXXConstructorDecl::init_const_iterator I = Definition->init_begin();
     CXXConstructorDecl::init_const_iterator I = Definition->init_begin();
     {
     {
       FullExpressionRAII InitScope(Info);
       FullExpressionRAII InitScope(Info);
-      if (!EvaluateInPlace(Result, Info, This, (*I)->getInit()))
+      if (!EvaluateInPlace(Result, Info, This, (*I)->getInit()) ||
+          !InitScope.destroy())
         return false;
         return false;
     }
     }
     return EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed;
     return EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed;
@@ -5280,7 +5443,8 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
   }
   }
 
 
   return Success &&
   return Success &&
-         EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed;
+         EvaluateStmt(Ret, Info, Definition->getBody()) != ESR_Failed &&
+         LifetimeExtendedScope.destroy();
 }
 }
 
 
 static bool HandleConstructorCall(const Expr *E, const LValue &This,
 static bool HandleConstructorCall(const Expr *E, const LValue &This,
@@ -5295,6 +5459,158 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
                                Info, Result);
                                Info, Result);
 }
 }
 
 
+static bool HandleDestructorCallImpl(EvalInfo &Info, SourceLocation CallLoc,
+                                     const LValue &This, APValue &Value,
+                                     QualType T) {
+  // Invent an expression for location purposes.
+  // FIXME: We shouldn't need to do this.
+  OpaqueValueExpr LocE(CallLoc, Info.Ctx.IntTy, VK_RValue);
+
+  // For arrays, destroy elements right-to-left.
+  if (const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(T)) {
+    uint64_t Size = CAT->getSize().getZExtValue();
+    QualType ElemT = CAT->getElementType();
+
+    LValue ElemLV = This;
+    ElemLV.addArray(Info, &LocE, CAT);
+    if (!HandleLValueArrayAdjustment(Info, &LocE, ElemLV, ElemT, Size))
+      return false;
+
+    for (; Size != 0; --Size) {
+      APValue &Elem = Value.getArrayInitializedElt(Size - 1);
+      if (!HandleDestructorCallImpl(Info, CallLoc, ElemLV, Elem, ElemT) ||
+          !HandleLValueArrayAdjustment(Info, &LocE, ElemLV, ElemT, -1))
+        return false;
+    }
+
+    // End the lifetime of this array now.
+    Value = APValue();
+    return true;
+  }
+
+  const CXXRecordDecl *RD = T->getAsCXXRecordDecl();
+  if (!RD) {
+    if (T.isDestructedType()) {
+      Info.FFDiag(CallLoc, diag::note_constexpr_unsupported_destruction) << T;
+      return false;
+    }
+
+    Value = APValue();
+    return true;
+  }
+
+  if (RD->getNumVBases()) {
+    Info.FFDiag(CallLoc, diag::note_constexpr_virtual_base) << RD;
+    return false;
+  }
+
+  const CXXDestructorDecl *DD = RD->getDestructor();
+  if (!DD) {
+    // FIXME: Can we get here for a type with an irrelevant destructor?
+    Info.FFDiag(CallLoc);
+    return false;
+  }
+
+  const FunctionDecl *Definition = nullptr;
+  const Stmt *Body = DD->getBody(Definition);
+
+  if ((DD && DD->isTrivial()) ||
+      (RD->isAnonymousStructOrUnion() && RD->isUnion())) {
+    // A trivial destructor just ends the lifetime of the object. Check for
+    // this case before checking for a body, because we might not bother
+    // building a body for a trivial destructor. Note that it doesn't matter
+    // whether the destructor is constexpr in this case; all trivial
+    // destructors are constexpr.
+    //
+    // If an anonymous union would be destroyed, some enclosing destructor must
+    // have been explicitly defined, and the anonymous union destruction should
+    // have no effect.
+    Value = APValue();
+    return true;
+  }
+
+  if (!Info.CheckCallLimit(CallLoc))
+    return false;
+
+  if (!CheckConstexprFunction(Info, CallLoc, DD, Definition, Body))
+    return false;
+
+  CallStackFrame Frame(Info, CallLoc, Definition, &This, nullptr);
+
+  // We're now in the period of destruction of this object.
+  unsigned BasesLeft = RD->getNumBases();
+  EvalInfo::EvaluatingDestructorRAII EvalObj(
+      Info,
+      ObjectUnderConstruction{This.getLValueBase(), This.Designator.Entries});
+
+  // FIXME: Creating an APValue just to hold a nonexistent return value is
+  // wasteful.
+  APValue RetVal;
+  StmtResult Ret = {RetVal, nullptr};
+  if (EvaluateStmt(Ret, Info, Definition->getBody()) == ESR_Failed)
+    return false;
+
+  // A union destructor does not implicitly destroy its members.
+  if (RD->isUnion())
+    return true;
+
+  const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD);
+
+  // We don't have a good way to iterate fields in reverse, so collect all the
+  // fields first and then walk them backwards.
+  SmallVector<FieldDecl*, 16> Fields(RD->field_begin(), RD->field_end());
+  for (const FieldDecl *FD : llvm::reverse(Fields)) {
+    if (FD->isUnnamedBitfield())
+      continue;
+
+    LValue Subobject = This;
+    if (!HandleLValueMember(Info, &LocE, Subobject, FD, &Layout))
+      return false;
+
+    APValue *SubobjectValue = &Value.getStructField(FD->getFieldIndex());
+    if (!HandleDestructorCallImpl(Info, CallLoc, Subobject, *SubobjectValue,
+                                  FD->getType()))
+      return false;
+  }
+
+  if (BasesLeft != 0)
+    EvalObj.startedDestroyingBases();
+
+  // Destroy base classes in reverse order.
+  for (const CXXBaseSpecifier &Base : llvm::reverse(RD->bases())) {
+    --BasesLeft;
+
+    QualType BaseType = Base.getType();
+    LValue Subobject = This;
+    if (!HandleLValueDirectBase(Info, &LocE, Subobject, RD,
+                                BaseType->getAsCXXRecordDecl(), &Layout))
+      return false;
+
+    APValue *SubobjectValue = &Value.getStructBase(BasesLeft);
+    if (!HandleDestructorCallImpl(Info, CallLoc, Subobject, *SubobjectValue,
+                                  BaseType))
+      return false;
+  }
+  assert(BasesLeft == 0 && "NumBases was wrong?");
+
+  // The period of destruction ends now. The object is gone.
+  Value = APValue();
+  return true;
+}
+
+static bool HandleDestructorCall(EvalInfo &Info, APValue::LValueBase LVBase,
+                                 APValue &Value, QualType T) {
+  SourceLocation Loc;
+  if (const ValueDecl *VD = LVBase.dyn_cast<const ValueDecl*>())
+    Loc = VD->getLocation();
+  else if (const Expr *E = LVBase.dyn_cast<const Expr*>())
+    Loc = E->getExprLoc();
+
+  LValue LV;
+  LV.set({LVBase});
+  return HandleDestructorCallImpl(Info, Loc, LV, Value, T);
+}
+
 //===----------------------------------------------------------------------===//
 //===----------------------------------------------------------------------===//
 // Generic Evaluation
 // Generic Evaluation
 //===----------------------------------------------------------------------===//
 //===----------------------------------------------------------------------===//
@@ -5905,10 +6221,10 @@ public:
     return StmtVisitorTy::Visit(E->getExpr());
     return StmtVisitorTy::Visit(E->getExpr());
   }
   }
 
 
-  // We cannot create any objects for which cleanups are required, so there is
-  // nothing to do here; all cleanups must come from unevaluated subexpressions.
-  bool VisitExprWithCleanups(const ExprWithCleanups *E)
-    { return StmtVisitorTy::Visit(E->getSubExpr()); }
+  bool VisitExprWithCleanups(const ExprWithCleanups *E) {
+    FullExpressionRAII Scope(Info);
+    return StmtVisitorTy::Visit(E->getSubExpr()) && Scope.destroy();
+  }
 
 
   bool VisitCXXReinterpretCastExpr(const CXXReinterpretCastExpr *E) {
   bool VisitCXXReinterpretCastExpr(const CXXReinterpretCastExpr *E) {
     CCEDiag(E, diag::note_constexpr_invalid_cast) << 0;
     CCEDiag(E, diag::note_constexpr_invalid_cast) << 0;
@@ -5948,7 +6264,10 @@ public:
   bool VisitBinaryConditionalOperator(const BinaryConditionalOperator *E) {
   bool VisitBinaryConditionalOperator(const BinaryConditionalOperator *E) {
     // Evaluate and cache the common expression. We treat it as a temporary,
     // Evaluate and cache the common expression. We treat it as a temporary,
     // even though it's not quite the same thing.
     // even though it's not quite the same thing.
-    if (!Evaluate(Info.CurrentCall->createTemporary(E->getOpaqueValue(), false),
+    LValue CommonLV;
+    if (!Evaluate(Info.CurrentCall->createTemporary(
+                      E->getOpaqueValue(), getStorageType(Info.Ctx, E->getOpaqueValue()),
+                      false, CommonLV),
                   Info, E->getCommon()))
                   Info, E->getCommon()))
       return false;
       return false;
 
 
@@ -6255,11 +6574,11 @@ public:
     if (Info.checkingForUndefinedBehavior())
     if (Info.checkingForUndefinedBehavior())
       return Error(E);
       return Error(E);
 
 
-    BlockScopeRAII Scope(Info);
     const CompoundStmt *CS = E->getSubStmt();
     const CompoundStmt *CS = E->getSubStmt();
     if (CS->body_empty())
     if (CS->body_empty())
       return true;
       return true;
 
 
+    BlockScopeRAII Scope(Info);
     for (CompoundStmt::const_body_iterator BI = CS->body_begin(),
     for (CompoundStmt::const_body_iterator BI = CS->body_begin(),
                                            BE = CS->body_end();
                                            BE = CS->body_end();
          /**/; ++BI) {
          /**/; ++BI) {
@@ -6270,7 +6589,7 @@ public:
                       diag::note_constexpr_stmt_expr_unsupported);
                       diag::note_constexpr_stmt_expr_unsupported);
           return false;
           return false;
         }
         }
-        return this->Visit(FinalExpr);
+        return this->Visit(FinalExpr) && Scope.destroy();
       }
       }
 
 
       APValue ReturnValue;
       APValue ReturnValue;
@@ -6619,8 +6938,8 @@ bool LValueExprEvaluator::VisitMaterializeTemporaryExpr(
     *Value = APValue();
     *Value = APValue();
     Result.set(E);
     Result.set(E);
   } else {
   } else {
-    Value = &createTemporary(E, E->getStorageDuration() == SD_Automatic, Result,
-                             *Info.CurrentCall);
+    Value = &Info.CurrentCall->createTemporary(
+        E, E->getType(), E->getStorageDuration() == SD_Automatic, Result);
   }
   }
 
 
   QualType Type = Inner->getType();
   QualType Type = Inner->getType();
@@ -7152,8 +7471,8 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) {
       if (!evaluateLValue(SubExpr, Result))
       if (!evaluateLValue(SubExpr, Result))
         return false;
         return false;
     } else {
     } else {
-      APValue &Value = createTemporary(SubExpr, false, Result,
-                                       *Info.CurrentCall);
+      APValue &Value = Info.CurrentCall->createTemporary(
+          SubExpr, SubExpr->getType(), false, Result);
       if (!EvaluateInPlace(Value, Info, Result, SubExpr))
       if (!EvaluateInPlace(Value, Info, Result, SubExpr))
         return false;
         return false;
     }
     }
@@ -7705,6 +8024,12 @@ namespace {
     bool VisitCXXConstructExpr(const CXXConstructExpr *E, QualType T);
     bool VisitCXXConstructExpr(const CXXConstructExpr *E, QualType T);
     bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E);
     bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E);
 
 
+    // Temporaries are registered when created, so we don't care about
+    // CXXBindTemporaryExpr.
+    bool VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *E) {
+      return Visit(E->getSubExpr());
+    }
+
     bool VisitBinCmp(const BinaryOperator *E);
     bool VisitBinCmp(const BinaryOperator *E);
   };
   };
 }
 }
@@ -8104,7 +8429,8 @@ public:
 
 
   /// Visit an expression which constructs the value of this temporary.
   /// Visit an expression which constructs the value of this temporary.
   bool VisitConstructExpr(const Expr *E) {
   bool VisitConstructExpr(const Expr *E) {
-    APValue &Value = createTemporary(E, false, Result, *Info.CurrentCall);
+    APValue &Value =
+        Info.CurrentCall->createTemporary(E, E->getType(), false, Result);
     return EvaluateInPlace(Value, Info, Result, E);
     return EvaluateInPlace(Value, Info, Result, E);
   }
   }
 
 
@@ -8464,8 +8790,12 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
 }
 }
 
 
 bool ArrayExprEvaluator::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E) {
 bool ArrayExprEvaluator::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *E) {
+  LValue CommonLV;
   if (E->getCommonExpr() &&
   if (E->getCommonExpr() &&
-      !Evaluate(Info.CurrentCall->createTemporary(E->getCommonExpr(), false),
+      !Evaluate(Info.CurrentCall->createTemporary(
+                    E->getCommonExpr(),
+                    getStorageType(Info.Ctx, E->getCommonExpr()), false,
+                    CommonLV),
                 Info, E->getCommonExpr()->getSourceExpr()))
                 Info, E->getCommonExpr()->getSourceExpr()))
     return false;
     return false;
 
 
@@ -12124,13 +12454,14 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) {
     return true;
     return true;
   } else if (T->isArrayType()) {
   } else if (T->isArrayType()) {
     LValue LV;
     LValue LV;
-    APValue &Value = createTemporary(E, false, LV, *Info.CurrentCall);
+    APValue &Value =
+        Info.CurrentCall->createTemporary(E, T, false, LV);
     if (!EvaluateArray(E, LV, Value, Info))
     if (!EvaluateArray(E, LV, Value, Info))
       return false;
       return false;
     Result = Value;
     Result = Value;
   } else if (T->isRecordType()) {
   } else if (T->isRecordType()) {
     LValue LV;
     LValue LV;
-    APValue &Value = createTemporary(E, false, LV, *Info.CurrentCall);
+    APValue &Value = Info.CurrentCall->createTemporary(E, T, false, LV);
     if (!EvaluateRecord(E, LV, Value, Info))
     if (!EvaluateRecord(E, LV, Value, Info))
       return false;
       return false;
     Result = Value;
     Result = Value;
@@ -12144,7 +12475,7 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) {
     QualType Unqual = T.getAtomicUnqualifiedType();
     QualType Unqual = T.getAtomicUnqualifiedType();
     if (Unqual->isArrayType() || Unqual->isRecordType()) {
     if (Unqual->isArrayType() || Unqual->isRecordType()) {
       LValue LV;
       LValue LV;
-      APValue &Value = createTemporary(E, false, LV, *Info.CurrentCall);
+      APValue &Value = Info.CurrentCall->createTemporary(E, Unqual, false, LV);
       if (!EvaluateAtomic(E, &LV, Value, Info))
       if (!EvaluateAtomic(E, &LV, Value, Info))
         return false;
         return false;
     } else {
     } else {
@@ -12372,7 +12703,8 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx,
   EvalInfo Info(Ctx, Result, EvalInfo::EM_ConstantFold);
   EvalInfo Info(Ctx, Result, EvalInfo::EM_ConstantFold);
   Info.InConstantContext = InConstantContext;
   Info.InConstantContext = InConstantContext;
   LValue LV;
   LValue LV;
-  if (!EvaluateLValue(this, LV, Info) || Result.HasSideEffects ||
+  if (!EvaluateLValue(this, LV, Info) || !Info.discardCleanups() ||
+      Result.HasSideEffects ||
       !CheckLValueConstantExpression(Info, getExprLoc(),
       !CheckLValueConstantExpression(Info, getExprLoc(),
                                      Ctx.getLValueReferenceType(getType()), LV,
                                      Ctx.getLValueReferenceType(getType()), LV,
                                      Expr::EvaluateForCodeGen))
                                      Expr::EvaluateForCodeGen))
@@ -12394,6 +12726,9 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, ConstExprUsage Usage,
   if (!::Evaluate(Result.Val, Info, this))
   if (!::Evaluate(Result.Val, Info, this))
     return false;
     return false;
 
 
+  if (!Info.discardCleanups())
+    llvm_unreachable("Unhandled cleanup; missing full expression marker?");
+
   return CheckConstantExpression(Info, getExprLoc(), getType(), Result.Val,
   return CheckConstantExpression(Info, getExprLoc(), getType(), Result.Val,
                                  Usage);
                                  Usage);
 }
 }
@@ -12457,6 +12792,13 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
       EStatus.HasSideEffects)
       EStatus.HasSideEffects)
     return false;
     return false;
 
 
+  // At this point, any lifetime-extended temporaries are completely
+  // initialized.
+  Info.performLifetimeExtension();
+
+  if (!Info.discardCleanups())
+    llvm_unreachable("Unhandled cleanup; missing full expression marker?");
+
   return CheckConstantExpression(Info, DeclLoc, DeclTy, Value);
   return CheckConstantExpression(Info, DeclLoc, DeclTy, Value);
 }
 }
 
 
@@ -13063,7 +13405,11 @@ bool Expr::isCXX11ConstantExpr(const ASTContext &Ctx, APValue *Result,
   EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpression);
   EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpression);
 
 
   APValue Scratch;
   APValue Scratch;
-  bool IsConstExpr = ::EvaluateAsRValue(Info, this, Result ? *Result : Scratch);
+  bool IsConstExpr =
+      ::EvaluateAsRValue(Info, this, Result ? *Result : Scratch) &&
+      // FIXME: We don't produce a diagnostic for this, but the callers that
+      // call us on arbitrary full-expressions should generally not care.
+      Info.discardCleanups();
 
 
   if (!Diags.empty()) {
   if (!Diags.empty()) {
     IsConstExpr = false;
     IsConstExpr = false;
@@ -13115,7 +13461,8 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx,
   // Build fake call to Callee.
   // Build fake call to Callee.
   CallStackFrame Frame(Info, Callee->getLocation(), Callee, ThisPtr,
   CallStackFrame Frame(Info, Callee->getLocation(), Callee, ThisPtr,
                        ArgValues.data());
                        ArgValues.data());
-  return Evaluate(Value, Info, this) && !Info.EvalStatus.HasSideEffects;
+  return Evaluate(Value, Info, this) && Info.discardCleanups() &&
+         !Info.EvalStatus.HasSideEffects;
 }
 }
 
 
 bool Expr::isPotentialConstantExpr(const FunctionDecl *FD,
 bool Expr::isPotentialConstantExpr(const FunctionDecl *FD,

+ 6 - 6
lib/Sema/SemaDecl.cpp

@@ -8172,10 +8172,10 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
     if (DC->isRecord()) {
     if (DC->isRecord()) {
       R = SemaRef.CheckDestructorDeclarator(D, R, SC);
       R = SemaRef.CheckDestructorDeclarator(D, R, SC);
       CXXRecordDecl *Record = cast<CXXRecordDecl>(DC);
       CXXRecordDecl *Record = cast<CXXRecordDecl>(DC);
-      CXXDestructorDecl *NewDD =
-          CXXDestructorDecl::Create(SemaRef.Context, Record, D.getBeginLoc(),
-                                    NameInfo, R, TInfo, isInline,
-                                    /*isImplicitlyDeclared=*/false);
+      CXXDestructorDecl *NewDD = CXXDestructorDecl::Create(
+          SemaRef.Context, Record, D.getBeginLoc(), NameInfo, R, TInfo,
+          isInline,
+          /*isImplicitlyDeclared=*/false, ConstexprKind);
 
 
       // If the destructor needs an implicit exception specification, set it
       // If the destructor needs an implicit exception specification, set it
       // now. FIXME: It'd be nice to be able to create the right type to start
       // now. FIXME: It'd be nice to be able to create the right type to start
@@ -8784,7 +8784,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
       // C++11 [dcl.constexpr]p3: functions declared constexpr are required to
       // C++11 [dcl.constexpr]p3: functions declared constexpr are required to
       // be either constructors or to return a literal type. Therefore,
       // be either constructors or to return a literal type. Therefore,
       // destructors cannot be declared constexpr.
       // destructors cannot be declared constexpr.
-      if (isa<CXXDestructorDecl>(NewFD)) {
+      if (isa<CXXDestructorDecl>(NewFD) && !getLangOpts().CPlusPlus2a) {
         Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_constexpr_dtor)
         Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_constexpr_dtor)
             << ConstexprKind;
             << ConstexprKind;
       }
       }
@@ -10277,7 +10277,7 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
   CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(NewFD);
   CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(NewFD);
   if (!getLangOpts().CPlusPlus14 && MD && MD->isConstexpr() &&
   if (!getLangOpts().CPlusPlus14 && MD && MD->isConstexpr() &&
       !MD->isStatic() && !isa<CXXConstructorDecl>(MD) &&
       !MD->isStatic() && !isa<CXXConstructorDecl>(MD) &&
-      !MD->getMethodQualifiers().hasConst()) {
+      !isa<CXXDestructorDecl>(MD) && !MD->getMethodQualifiers().hasConst()) {
     CXXMethodDecl *OldMD = nullptr;
     CXXMethodDecl *OldMD = nullptr;
     if (OldDecl)
     if (OldDecl)
       OldMD = dyn_cast_or_null<CXXMethodDecl>(OldDecl->getAsFunction());
       OldMD = dyn_cast_or_null<CXXMethodDecl>(OldDecl->getAsFunction());

+ 61 - 9
lib/Sema/SemaDeclCXX.cpp

@@ -1590,6 +1590,36 @@ static bool CheckLiteralType(Sema &SemaRef, Sema::CheckConstexprKind Kind,
   llvm_unreachable("unknown CheckConstexprKind");
   llvm_unreachable("unknown CheckConstexprKind");
 }
 }
 
 
+/// Determine whether a destructor cannot be constexpr due to
+static bool CheckConstexprDestructorSubobjects(Sema &SemaRef,
+                                               const CXXDestructorDecl *DD,
+                                               Sema::CheckConstexprKind Kind) {
+  auto Check = [&](SourceLocation Loc, QualType T, const FieldDecl *FD) {
+    const CXXRecordDecl *RD =
+        T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
+    if (!RD || RD->hasConstexprDestructor())
+      return true;
+
+    if (Kind == Sema::CheckConstexprKind::Diagnose) {
+      SemaRef.Diag(DD->getLocation(), diag::err_constexpr_dtor_subobject)
+          << DD->getConstexprKind() << !FD
+          << (FD ? FD->getDeclName() : DeclarationName()) << T;
+      SemaRef.Diag(Loc, diag::note_constexpr_dtor_subobject)
+          << !FD << (FD ? FD->getDeclName() : DeclarationName()) << T;
+    }
+    return false;
+  };
+
+  const CXXRecordDecl *RD = DD->getParent();
+  for (const CXXBaseSpecifier &B : RD->bases())
+    if (!Check(B.getBaseTypeLoc(), B.getType(), nullptr))
+      return false;
+  for (const FieldDecl *FD : RD->fields())
+    if (!Check(FD->getLocation(), FD->getType(), FD))
+      return false;
+  return true;
+}
+
 // CheckConstexprParameterTypes - Check whether a function's parameter types
 // CheckConstexprParameterTypes - Check whether a function's parameter types
 // are all literal types. If so, return true. If not, produce a suitable
 // are all literal types. If so, return true. If not, produce a suitable
 // diagnostic and return false.
 // diagnostic and return false.
@@ -1645,8 +1675,8 @@ bool Sema::CheckConstexprFunctionDefinition(const FunctionDecl *NewFD,
     //  constraints:
     //  constraints:
     //  - the class shall not have any virtual base classes;
     //  - the class shall not have any virtual base classes;
     //
     //
-    // FIXME: This only applies to constructors, not arbitrary member
-    // functions.
+    // FIXME: This only applies to constructors and destructors, not arbitrary
+    // member functions.
     const CXXRecordDecl *RD = MD->getParent();
     const CXXRecordDecl *RD = MD->getParent();
     if (RD->getNumVBases()) {
     if (RD->getNumVBases()) {
       if (Kind == CheckConstexprKind::CheckValid)
       if (Kind == CheckConstexprKind::CheckValid)
@@ -1699,6 +1729,18 @@ bool Sema::CheckConstexprFunctionDefinition(const FunctionDecl *NewFD,
       return false;
       return false;
   }
   }
 
 
+  if (auto *Dtor = dyn_cast<CXXDestructorDecl>(NewFD)) {
+    // A destructor can be constexpr only if the defaulted destructor could be;
+    // we don't need to check the members and bases if we already know they all
+    // have constexpr destructors.
+    if (!Dtor->getParent()->defaultedDestructorIsConstexpr()) {
+      if (Kind == CheckConstexprKind::CheckValid)
+        return false;
+      if (!CheckConstexprDestructorSubobjects(*this, Dtor, Kind))
+        return false;
+    }
+  }
+
   // - each of its parameter types shall be a literal type;
   // - each of its parameter types shall be a literal type;
   if (!CheckConstexprParameterTypes(*this, NewFD, Kind))
   if (!CheckConstexprParameterTypes(*this, NewFD, Kind))
     return false;
     return false;
@@ -6535,6 +6577,8 @@ specialMemberIsConstexpr(Sema &S, CXXRecordDecl *ClassDecl,
 
 
   if (CSM == Sema::CXXDefaultConstructor)
   if (CSM == Sema::CXXDefaultConstructor)
     return ClassDecl->hasConstexprDefaultConstructor();
     return ClassDecl->hasConstexprDefaultConstructor();
+  if (CSM == Sema::CXXDestructor)
+    return ClassDecl->hasConstexprDestructor();
 
 
   Sema::SpecialMemberOverloadResult SMOR =
   Sema::SpecialMemberOverloadResult SMOR =
       lookupCallFromSpecialMember(S, ClassDecl, CSM, Quals, ConstRHS);
       lookupCallFromSpecialMember(S, ClassDecl, CSM, Quals, ConstRHS);
@@ -6583,6 +6627,8 @@ static bool defaultedSpecialMemberIsConstexpr(
     break;
     break;
 
 
   case Sema::CXXDestructor:
   case Sema::CXXDestructor:
+    return ClassDecl->defaultedDestructorIsConstexpr();
+
   case Sema::CXXInvalid:
   case Sema::CXXInvalid:
     return false;
     return false;
   }
   }
@@ -6835,13 +6881,14 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
   // Do not apply this rule to members of class templates, since core issue 1358
   // Do not apply this rule to members of class templates, since core issue 1358
   // makes such functions always instantiate to constexpr functions. For
   // makes such functions always instantiate to constexpr functions. For
   // functions which cannot be constexpr (for non-constructors in C++11 and for
   // functions which cannot be constexpr (for non-constructors in C++11 and for
-  // destructors in C++1y), this is checked elsewhere.
+  // destructors in C++14 and C++17), this is checked elsewhere.
   //
   //
   // FIXME: This should not apply if the member is deleted.
   // FIXME: This should not apply if the member is deleted.
   bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, RD, CSM,
   bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, RD, CSM,
                                                      HasConstParam);
                                                      HasConstParam);
-  if ((getLangOpts().CPlusPlus14 ? !isa<CXXDestructorDecl>(MD)
-                                 : isa<CXXConstructorDecl>(MD)) &&
+  if ((getLangOpts().CPlusPlus2a ||
+       (getLangOpts().CPlusPlus14 ? !isa<CXXDestructorDecl>(MD)
+                                  : isa<CXXConstructorDecl>(MD))) &&
       MD->isConstexpr() && !Constexpr &&
       MD->isConstexpr() && !Constexpr &&
       MD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) {
       MD->getTemplatedKind() == FunctionDecl::TK_NonTemplate) {
     Diag(MD->getBeginLoc(), MD->isConsteval()
     Diag(MD->getBeginLoc(), MD->isConsteval()
@@ -11464,6 +11511,10 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) {
   if (DSM.isAlreadyBeingDeclared())
   if (DSM.isAlreadyBeingDeclared())
     return nullptr;
     return nullptr;
 
 
+  bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, ClassDecl,
+                                                     CXXDestructor,
+                                                     false);
+
   // Create the actual destructor declaration.
   // Create the actual destructor declaration.
   CanQualType ClassType
   CanQualType ClassType
     = Context.getCanonicalType(Context.getTypeDeclType(ClassDecl));
     = Context.getCanonicalType(Context.getTypeDeclType(ClassDecl));
@@ -11471,10 +11522,11 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) {
   DeclarationName Name
   DeclarationName Name
     = Context.DeclarationNames.getCXXDestructorName(ClassType);
     = Context.DeclarationNames.getCXXDestructorName(ClassType);
   DeclarationNameInfo NameInfo(Name, ClassLoc);
   DeclarationNameInfo NameInfo(Name, ClassLoc);
-  CXXDestructorDecl *Destructor
-      = CXXDestructorDecl::Create(Context, ClassDecl, ClassLoc, NameInfo,
-                                  QualType(), nullptr, /*isInline=*/true,
-                                  /*isImplicitlyDeclared=*/true);
+  CXXDestructorDecl *Destructor =
+      CXXDestructorDecl::Create(Context, ClassDecl, ClassLoc, NameInfo,
+                                QualType(), nullptr, /*isInline=*/true,
+                                /*isImplicitlyDeclared=*/true,
+                                Constexpr ? CSK_constexpr : CSK_unspecified);
   Destructor->setAccess(AS_public);
   Destructor->setAccess(AS_public);
   Destructor->setDefaulted();
   Destructor->setDefaulted();
 
 

+ 3 - 4
lib/Sema/SemaTemplateInstantiateDecl.cpp

@@ -2147,10 +2147,9 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
         Constructor->getConstexprKind());
         Constructor->getConstexprKind());
     Method->setRangeEnd(Constructor->getEndLoc());
     Method->setRangeEnd(Constructor->getEndLoc());
   } else if (CXXDestructorDecl *Destructor = dyn_cast<CXXDestructorDecl>(D)) {
   } else if (CXXDestructorDecl *Destructor = dyn_cast<CXXDestructorDecl>(D)) {
-    Method = CXXDestructorDecl::Create(SemaRef.Context, Record,
-                                       StartLoc, NameInfo, T, TInfo,
-                                       Destructor->isInlineSpecified(),
-                                       false);
+    Method = CXXDestructorDecl::Create(
+        SemaRef.Context, Record, StartLoc, NameInfo, T, TInfo,
+        Destructor->isInlineSpecified(), false, Destructor->getConstexprKind());
     Method->setRangeEnd(Destructor->getEndLoc());
     Method->setRangeEnd(Destructor->getEndLoc());
   } else if (CXXConversionDecl *Conversion = dyn_cast<CXXConversionDecl>(D)) {
   } else if (CXXConversionDecl *Conversion = dyn_cast<CXXConversionDecl>(D)) {
     Method = CXXConversionDecl::Create(
     Method = CXXConversionDecl::Create(

+ 17 - 9
lib/Sema/SemaType.cpp

@@ -8208,20 +8208,28 @@ bool Sema::RequireLiteralType(SourceLocation Loc, QualType T,
         return true;
         return true;
       }
       }
     }
     }
-  } else if (!RD->hasTrivialDestructor()) {
-    // All fields and bases are of literal types, so have trivial destructors.
-    // If this class's destructor is non-trivial it must be user-declared.
+  } else if (getLangOpts().CPlusPlus2a ? !RD->hasConstexprDestructor()
+                                       : !RD->hasTrivialDestructor()) {
+    // All fields and bases are of literal types, so have trivial or constexpr
+    // destructors. If this class's destructor is non-trivial / non-constexpr,
+    // it must be user-declared.
     CXXDestructorDecl *Dtor = RD->getDestructor();
     CXXDestructorDecl *Dtor = RD->getDestructor();
     assert(Dtor && "class has literal fields and bases but no dtor?");
     assert(Dtor && "class has literal fields and bases but no dtor?");
     if (!Dtor)
     if (!Dtor)
       return true;
       return true;
 
 
-    Diag(Dtor->getLocation(), Dtor->isUserProvided() ?
-         diag::note_non_literal_user_provided_dtor :
-         diag::note_non_literal_nontrivial_dtor) << RD;
-    if (!Dtor->isUserProvided())
-      SpecialMemberIsTrivial(Dtor, CXXDestructor, TAH_IgnoreTrivialABI,
-                             /*Diagnose*/true);
+    if (getLangOpts().CPlusPlus2a) {
+      Diag(Dtor->getLocation(), diag::note_non_literal_non_constexpr_dtor)
+          << RD;
+    } else {
+      Diag(Dtor->getLocation(), Dtor->isUserProvided()
+                                    ? diag::note_non_literal_user_provided_dtor
+                                    : diag::note_non_literal_nontrivial_dtor)
+          << RD;
+      if (!Dtor->isUserProvided())
+        SpecialMemberIsTrivial(Dtor, CXXDestructor, TAH_IgnoreTrivialABI,
+                               /*Diagnose*/ true);
+    }
   }
   }
 
 
   return true;
   return true;

+ 6 - 2
test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp

@@ -1,6 +1,7 @@
 // RUN: %clang_cc1 -triple %itanium_abi_triple -fsyntax-only -verify -std=c++11 %s
 // RUN: %clang_cc1 -triple %itanium_abi_triple -fsyntax-only -verify -std=c++11 %s
 // RUN: %clang_cc1 -triple %itanium_abi_triple -fsyntax-only -verify -std=c++14 %s
 // RUN: %clang_cc1 -triple %itanium_abi_triple -fsyntax-only -verify -std=c++14 %s
-// RUN: %clang_cc1 -triple %itanium_abi_triple -fsyntax-only -verify -std=c++1z %s
+// RUN: %clang_cc1 -triple %itanium_abi_triple -fsyntax-only -verify -std=c++17 %s
+// RUN: %clang_cc1 -triple %itanium_abi_triple -fsyntax-only -verify -std=c++2a %s
 
 
 // MSVC always adopted the C++17 rule that implies that constexpr variables are
 // MSVC always adopted the C++17 rule that implies that constexpr variables are
 // implicitly inline, so do the test again.
 // implicitly inline, so do the test again.
@@ -75,7 +76,10 @@ template<typename T> T f6(T); // expected-note {{here}}
 template<typename T> constexpr T f6(T); // expected-error {{constexpr declaration of 'f6' follows non-constexpr declaration}}
 template<typename T> constexpr T f6(T); // expected-error {{constexpr declaration of 'f6' follows non-constexpr declaration}}
 // destructor
 // destructor
 struct ConstexprDtor {
 struct ConstexprDtor {
-  constexpr ~ConstexprDtor() = default; // expected-error {{destructor cannot be marked constexpr}}
+  constexpr ~ConstexprDtor() = default;
+#if __cplusplus <= 201703L
+  // expected-error@-2 {{destructor cannot be declared constexpr}}
+#endif
 };
 };
 
 
 // template stuff
 // template stuff

+ 4 - 1
test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp

@@ -57,7 +57,10 @@ struct T : SS, NonLiteral {
 #ifndef CXX1Y
 #ifndef CXX1Y
   // expected-error@-2 {{constexpr function's return type 'void' is not a literal type}}
   // expected-error@-2 {{constexpr function's return type 'void' is not a literal type}}
 #endif
 #endif
-  constexpr ~T(); // expected-error {{destructor cannot be marked constexpr}}
+  constexpr ~T();
+#ifndef CXX2A
+  // expected-error@-2 {{destructor cannot be declared constexpr}}
+#endif
   typedef NonLiteral F() const;
   typedef NonLiteral F() const;
   constexpr F NonLiteralReturn2; // ok until definition
   constexpr F NonLiteralReturn2; // ok until definition
 
 

+ 1 - 1
test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p2.cpp

@@ -8,7 +8,7 @@ struct S1 {
   constexpr S1(S1&&) = default;
   constexpr S1(S1&&) = default;
   constexpr S1 &operator=(const S1&) const = default; // expected-error {{explicitly-defaulted copy assignment operator may not have}}
   constexpr S1 &operator=(const S1&) const = default; // expected-error {{explicitly-defaulted copy assignment operator may not have}}
   constexpr S1 &operator=(S1&&) const = default; // expected-error {{explicitly-defaulted move assignment operator may not have}}
   constexpr S1 &operator=(S1&&) const = default; // expected-error {{explicitly-defaulted move assignment operator may not have}}
-  constexpr ~S1() = default; // expected-error {{destructor cannot be marked constexpr}}
+  constexpr ~S1() = default; // expected-error {{destructor cannot be declared constexpr}}
   int n;
   int n;
 };
 };
 struct NoCopyMove {
 struct NoCopyMove {

+ 4 - 1
test/CXX/drs/dr2xx.cpp

@@ -751,9 +751,12 @@ namespace dr263 { // dr263: yes
 #if __cplusplus < 201103L
 #if __cplusplus < 201103L
     friend X::X() throw();
     friend X::X() throw();
     friend X::~X() throw();
     friend X::~X() throw();
-#else
+#elif __cplusplus <= 201703L
     friend constexpr X::X() noexcept;
     friend constexpr X::X() noexcept;
     friend X::~X();
     friend X::~X();
+#else
+    friend constexpr X::X() noexcept;
+    friend constexpr X::~X();
 #endif
 #endif
     Y::Y(); // expected-error {{extra qualification}}
     Y::Y(); // expected-error {{extra qualification}}
     Y::~Y(); // expected-error {{extra qualification}}
     Y::~Y(); // expected-error {{extra qualification}}

+ 4 - 3
test/SemaCXX/attr-require-constant-initialization.cpp

@@ -185,7 +185,7 @@ struct TT2 {
   ATTR static constexpr LitType lit = {};
   ATTR static constexpr LitType lit = {};
   ATTR static const NonLit non_lit;
   ATTR static const NonLit non_lit;
   ATTR static const NonLit non_lit_list_init;
   ATTR static const NonLit non_lit_list_init;
-  ATTR static const NonLit non_lit_copy_init; // expected-note {{required by 'require_constant_initialization' attribute here}}
+  ATTR static const NonLit non_lit_copy_init;
 #endif
 #endif
 };
 };
 PODType TT2::pod_noinit; // expected-note 0+ {{declared here}}
 PODType TT2::pod_noinit; // expected-note 0+ {{declared here}}
@@ -203,8 +203,9 @@ PODType TT2::pod_copy_init(TT2::pod_noinit); // expected-error {{variable does n
 #if __cplusplus >= 201402L
 #if __cplusplus >= 201402L
 const NonLit TT2::non_lit(42);
 const NonLit TT2::non_lit(42);
 const NonLit TT2::non_lit_list_init = {42};
 const NonLit TT2::non_lit_list_init = {42};
-const NonLit TT2::non_lit_copy_init = 42; // expected-error {{variable does not have a constant initializer}}
-// expected-note@-1 {{subexpression not valid in a constant expression}}
+// FIXME: This is invalid, but we incorrectly elide the copy. It's OK if we
+// start diagnosing this.
+const NonLit TT2::non_lit_copy_init = 42;
 #endif
 #endif
 
 
 #if __cplusplus >= 201103L
 #if __cplusplus >= 201103L

+ 119 - 0
test/SemaCXX/constant-expression-cxx2a.cpp

@@ -6,6 +6,10 @@ namespace std {
   struct type_info;
   struct type_info;
 };
 };
 
 
+// Helper to print out values for debugging.
+constexpr void not_defined();
+template<typename T> constexpr void print(T) { not_defined(); }
+
 namespace ThreeWayComparison {
 namespace ThreeWayComparison {
   struct A {
   struct A {
     int n;
     int n;
@@ -219,15 +223,19 @@ static_assert(for_range_init());
 namespace Virtual {
 namespace Virtual {
   struct NonZeroOffset { int padding = 123; };
   struct NonZeroOffset { int padding = 123; };
 
 
+  constexpr void assert(bool b) { if (!b) throw 0; }
+
   // Ensure that we pick the right final overrider during construction.
   // Ensure that we pick the right final overrider during construction.
   struct A {
   struct A {
     virtual constexpr char f() const { return 'A'; }
     virtual constexpr char f() const { return 'A'; }
     char a = f();
     char a = f();
+    constexpr ~A() { assert(f() == 'A'); }
   };
   };
   struct NoOverrideA : A {};
   struct NoOverrideA : A {};
   struct B : NonZeroOffset, NoOverrideA {
   struct B : NonZeroOffset, NoOverrideA {
     virtual constexpr char f() const { return 'B'; }
     virtual constexpr char f() const { return 'B'; }
     char b = f();
     char b = f();
+    constexpr ~B() { assert(f() == 'B'); }
   };
   };
   struct NoOverrideB : B {};
   struct NoOverrideB : B {};
   struct C : NonZeroOffset, A {
   struct C : NonZeroOffset, A {
@@ -236,12 +244,15 @@ namespace Virtual {
     char c = ((A*)this)->f();
     char c = ((A*)this)->f();
     char ba = pba->f();
     char ba = pba->f();
     constexpr C(A *pba) : pba(pba) {}
     constexpr C(A *pba) : pba(pba) {}
+    constexpr ~C() { assert(f() == 'C'); }
   };
   };
   struct D : NonZeroOffset, NoOverrideB, C { // expected-warning {{inaccessible}}
   struct D : NonZeroOffset, NoOverrideB, C { // expected-warning {{inaccessible}}
     virtual constexpr char f() const { return 'D'; }
     virtual constexpr char f() const { return 'D'; }
     char d = f();
     char d = f();
     constexpr D() : C((B*)this) {}
     constexpr D() : C((B*)this) {}
+    constexpr ~D() { assert(f() == 'D'); }
   };
   };
+  constexpr int n = (D(), 0);
   constexpr D d;
   constexpr D d;
   static_assert(((B&)d).a == 'A');
   static_assert(((B&)d).a == 'A');
   static_assert(((C&)d).a == 'A');
   static_assert(((C&)d).a == 'A');
@@ -634,3 +645,111 @@ namespace Uninit {
   }
   }
   static_assert(switch_into_init_stmt());
   static_assert(switch_into_init_stmt());
 }
 }
+
+namespace dtor {
+  void lifetime_extension() {
+    struct X { constexpr ~X() {} };
+    X &&a = X();
+  }
+
+  template<typename T> constexpr T &&ref(T &&t) { return (T&&)t; }
+
+  struct Buf {
+    char buf[64];
+    int n = 0;
+    constexpr void operator+=(char c) { buf[n++] = c; }
+    constexpr bool operator==(const char *str) const {
+      return str[n] == 0 && __builtin_memcmp(str, buf, n) == 0;
+    }
+    constexpr bool operator!=(const char *str) const { return !operator==(str); }
+  };
+
+  struct A {
+    constexpr A(Buf &buf, char c) : buf(buf), c(c) { buf += c; }
+    constexpr ~A() { buf += c; }
+    constexpr operator bool() const { return true; }
+    Buf &buf;
+    char c;
+  };
+
+  constexpr bool dtor_calls_dtor() {
+    union U {
+      constexpr U(Buf &buf) : u(buf, 'u') { buf += 'U'; }
+      constexpr ~U() { u.buf += 'U'; }
+      A u, v;
+    };
+
+    struct B : A {
+      A c, &&d, e;
+      union {
+        A f;
+      };
+      U u;
+      constexpr B(Buf &buf)
+          : A(buf, 'a'), c(buf, 'c'), d(ref(A(buf, 'd'))), e(A(buf, 'e')), f(buf, 'f'), u(buf) {
+        buf += 'b';
+      }
+      constexpr ~B() {
+        buf += 'b';
+      }
+    };
+
+    Buf buf;
+    {
+      B b(buf);
+      if (buf != "acddefuUb")
+        return false;
+    }
+    if (buf != "acddefuUbbUeca")
+      return false;
+    return true;
+  }
+  static_assert(dtor_calls_dtor());
+
+  constexpr void abnormal_termination(Buf &buf) {
+    struct Indestructible {
+      constexpr ~Indestructible(); // not defined
+    };
+
+    A a(buf, 'a');
+    A(buf, 'b');
+    int n = 0;
+    for (A &&c = A(buf, 'c'); A d = A(buf, 'd'); A(buf, 'e')) {
+      switch (A f(buf, 'f'); A g = A(buf, 'g')) { // expected-warning {{boolean}}
+      case false: {
+        A x(buf, 'x');
+      }
+
+      case true: {
+        A h(buf, 'h');
+        switch (n++) {
+        case 0:
+          break;
+        case 1:
+          continue;
+        case 2:
+          return;
+        }
+        break;
+      }
+
+      default:
+        Indestructible indest;
+      }
+
+      A j = (A(buf, 'i'), A(buf, 'j'));
+    }
+  }
+
+  constexpr bool check_abnormal_termination() {
+    Buf buf = {};
+    abnormal_termination(buf);
+    return buf ==
+      "abbc"
+        "dfgh" /*break*/ "hgfijijeed"
+        "dfgh" /*continue*/ "hgfeed"
+        "dfgh" /*return*/ "hgfd"
+      "ca";
+  }
+  static_assert(check_abnormal_termination());
+}

+ 7 - 1
test/SemaCXX/cxx2a-consteval.cpp

@@ -27,7 +27,7 @@ struct A {
   }
   }
   consteval A(int i);
   consteval A(int i);
   consteval A() = default;
   consteval A() = default;
-  consteval ~A() = default; // expected-error {{destructor cannot be marked consteval}}
+  consteval ~A() = default;
 };
 };
 
 
 consteval struct B {}; // expected-error {{struct cannot be marked consteval}}
 consteval struct B {}; // expected-error {{struct cannot be marked consteval}}
@@ -45,11 +45,17 @@ consteval int f1() {} // expected-error {{no return statement in consteval funct
 
 
 struct C {
 struct C {
   C() {}
   C() {}
+  ~C() {}
 };
 };
 
 
 struct D {
 struct D {
   C c;
   C c;
   consteval D() = default; // expected-error {{cannot be consteval}}
   consteval D() = default; // expected-error {{cannot be consteval}}
+  consteval ~D() = default; // expected-error {{cannot be consteval}}
+};
+
+struct E : C { // expected-note {{here}}
+  consteval ~E() {} // expected-error {{cannot be declared consteval because base class 'basic_sema::C' does not have a constexpr destructor}}
 };
 };
 }
 }