r315662 - Support for destroying operator delete, per C++2a proposal P0722.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 12 18:55:36 PDT 2017


Author: rsmith
Date: Thu Oct 12 18:55:36 2017
New Revision: 315662

URL: http://llvm.org/viewvc/llvm-project?rev=315662&view=rev
Log:
Support for destroying operator delete, per C++2a proposal P0722.

This feature is not (yet) approved by the C++ committee, so this is liable to
be reverted or significantly modified based on committee feedback.

No functionality change intended for existing code (a new type must be defined
in namespace std to take advantage of this feature).

Added:
    cfe/trunk/test/CodeGenCXX/cxx2a-destroying-delete.cpp
    cfe/trunk/test/SemaCXX/cxx2a-destroying-delete.cpp
Modified:
    cfe/trunk/include/clang/AST/ASTMutationListener.h
    cfe/trunk/include/clang/AST/Decl.h
    cfe/trunk/include/clang/AST/DeclCXX.h
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Serialization/ASTWriter.h
    cfe/trunk/lib/AST/Decl.cpp
    cfe/trunk/lib/AST/DeclCXX.cpp
    cfe/trunk/lib/AST/ExprCXX.cpp
    cfe/trunk/lib/CodeGen/CGClass.cpp
    cfe/trunk/lib/CodeGen/CGExprCXX.cpp
    cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp
    cfe/trunk/lib/Frontend/MultiplexConsumer.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaExprCXX.cpp
    cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
    cfe/trunk/lib/Serialization/ASTWriter.cpp
    cfe/trunk/lib/Serialization/ASTWriterDecl.cpp

Modified: cfe/trunk/include/clang/AST/ASTMutationListener.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ASTMutationListener.h?rev=315662&r1=315661&r2=315662&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/ASTMutationListener.h (original)
+++ cfe/trunk/include/clang/AST/ASTMutationListener.h Thu Oct 12 18:55:36 2017
@@ -22,6 +22,7 @@ namespace clang {
   class CXXRecordDecl;
   class Decl;
   class DeclContext;
+  class Expr;
   class FieldDecl;
   class FunctionDecl;
   class FunctionTemplateDecl;
@@ -80,7 +81,8 @@ public:
 
   /// \brief A virtual destructor's operator delete has been resolved.
   virtual void ResolvedOperatorDelete(const CXXDestructorDecl *DD,
-                                      const FunctionDecl *Delete) {}
+                                      const FunctionDecl *Delete,
+                                      Expr *ThisArg) {}
 
   /// \brief An implicit member got a definition.
   virtual void CompletedImplicitDefinition(const FunctionDecl *D) {}

Modified: cfe/trunk/include/clang/AST/Decl.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=315662&r1=315661&r2=315662&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Thu Oct 12 18:55:36 2017
@@ -2036,6 +2036,9 @@ public:
   /// true through IsAligned.
   bool isReplaceableGlobalAllocationFunction(bool *IsAligned = nullptr) const;
 
+  /// \brief Determine whether this is a destroying operator delete.
+  bool isDestroyingOperatorDelete() const;
+
   /// Compute the language linkage.
   LanguageLinkage getLanguageLinkage() const;
 

Modified: cfe/trunk/include/clang/AST/DeclCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=315662&r1=315661&r2=315662&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclCXX.h (original)
+++ cfe/trunk/include/clang/AST/DeclCXX.h Thu Oct 12 18:55:36 2017
@@ -2566,7 +2566,10 @@ public:
 class CXXDestructorDecl : public CXXMethodDecl {
   void anchor() override;
 
+  // FIXME: Don't allocate storage for these except in the first declaration
+  // of a virtual destructor.
   FunctionDecl *OperatorDelete;
+  Expr *OperatorDeleteThisArg;
 
   CXXDestructorDecl(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
                     const DeclarationNameInfo &NameInfo,
@@ -2574,7 +2577,7 @@ class CXXDestructorDecl : public CXXMeth
                     bool isInline, bool isImplicitlyDeclared)
     : CXXMethodDecl(CXXDestructor, C, RD, StartLoc, NameInfo, T, TInfo,
                     SC_None, isInline, /*isConstexpr=*/false, SourceLocation()),
-      OperatorDelete(nullptr) {
+      OperatorDelete(nullptr), OperatorDeleteThisArg(nullptr) {
     setImplicit(isImplicitlyDeclared);
   }
 
@@ -2587,10 +2590,13 @@ public:
                                    bool isImplicitlyDeclared);
   static CXXDestructorDecl *CreateDeserialized(ASTContext & C, unsigned ID);
 
-  void setOperatorDelete(FunctionDecl *OD);
+  void setOperatorDelete(FunctionDecl *OD, Expr *ThisArg);
   const FunctionDecl *getOperatorDelete() const {
     return getCanonicalDecl()->OperatorDelete;
   }
+  Expr *getOperatorDeleteThisArg() const {
+    return getCanonicalDecl()->OperatorDeleteThisArg;
+  }
 
   CXXDestructorDecl *getCanonicalDecl() override {
     return cast<CXXDestructorDecl>(FunctionDecl::getCanonicalDecl());

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=315662&r1=315661&r2=315662&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu Oct 12 18:55:36 2017
@@ -7612,6 +7612,11 @@ def err_operator_delete_dependent_param_
   "%0 cannot take a dependent type as first parameter; use %1 instead">;
 def err_operator_delete_param_type : Error<
   "first parameter of %0 must have type %1">;
+def err_destroying_operator_delete_not_usual : Error<
+  "destroying operator delete can have only an optional size and optional "
+  "alignment parameter">;
+def note_implicit_delete_this_in_destructor_here : Note<
+  "while checking implicit 'delete this' for virtual destructor">;
 
 // C++ literal operators
 def err_literal_operator_outside_namespace : Error<

Modified: cfe/trunk/include/clang/Serialization/ASTWriter.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Serialization/ASTWriter.h?rev=315662&r1=315661&r2=315662&view=diff
==============================================================================
--- cfe/trunk/include/clang/Serialization/ASTWriter.h (original)
+++ cfe/trunk/include/clang/Serialization/ASTWriter.h Thu Oct 12 18:55:36 2017
@@ -698,7 +698,8 @@ private:
   void ResolvedExceptionSpec(const FunctionDecl *FD) override;
   void DeducedReturnType(const FunctionDecl *FD, QualType ReturnType) override;
   void ResolvedOperatorDelete(const CXXDestructorDecl *DD,
-                              const FunctionDecl *Delete) override;
+                              const FunctionDecl *Delete,
+                              Expr *ThisArg) override;
   void CompletedImplicitDefinition(const FunctionDecl *D) override;
   void StaticDataMemberInstantiated(const VarDecl *D) override;
   void DefaultArgumentInstantiated(const ParmVarDecl *D) override;

Modified: cfe/trunk/lib/AST/Decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=315662&r1=315661&r2=315662&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Decl.cpp (original)
+++ cfe/trunk/lib/AST/Decl.cpp Thu Oct 12 18:55:36 2017
@@ -2732,6 +2732,20 @@ bool FunctionDecl::isReplaceableGlobalAl
   return Params == FPT->getNumParams();
 }
 
+bool FunctionDecl::isDestroyingOperatorDelete() const {
+  // C++ P0722:
+  //   Within a class C, a single object deallocation function with signature
+  //     (T, std::destroying_delete_t, <more params>)
+  //   is a destroying operator delete.
+  if (!isa<CXXMethodDecl>(this) || getOverloadedOperator() != OO_Delete ||
+      getNumParams() < 2)
+    return false;
+
+  auto *RD = getParamDecl(1)->getType()->getAsCXXRecordDecl();
+  return RD && RD->isInStdNamespace() && RD->getIdentifier() &&
+         RD->getIdentifier()->isStr("destroying_delete_t");
+}
+
 LanguageLinkage FunctionDecl::getLanguageLinkage() const {
   return getDeclLanguageLinkage(*this);
 }

Modified: cfe/trunk/lib/AST/DeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclCXX.cpp?rev=315662&r1=315661&r2=315662&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclCXX.cpp (original)
+++ cfe/trunk/lib/AST/DeclCXX.cpp Thu Oct 12 18:55:36 2017
@@ -1788,6 +1788,14 @@ bool CXXMethodDecl::isUsualDeallocationF
     return true;
   unsigned UsualParams = 1;
 
+  // C++ P0722:
+  //   A destroying operator delete is a usual deallocation function if
+  //   removing the std::destroying_delete_t parameter and changing the
+  //   first parameter type from T* to void* results in the signature of
+  //   a usual deallocation function.
+  if (isDestroyingOperatorDelete())
+    ++UsualParams;
+
   // C++ <=14 [basic.stc.dynamic.deallocation]p2:
   //   [...] If class T does not declare such an operator delete but does 
   //   declare a member deallocation function named operator delete with 
@@ -2187,12 +2195,13 @@ CXXDestructorDecl::Create(ASTContext &C,
                                        isInline, isImplicitlyDeclared);
 }
 
-void CXXDestructorDecl::setOperatorDelete(FunctionDecl *OD) {
+void CXXDestructorDecl::setOperatorDelete(FunctionDecl *OD, Expr *ThisArg) {
   auto *First = cast<CXXDestructorDecl>(getFirstDecl());
   if (OD && !First->OperatorDelete) {
     First->OperatorDelete = OD;
+    First->OperatorDeleteThisArg = ThisArg;
     if (auto *L = getASTMutationListener())
-      L->ResolvedOperatorDelete(First, OD);
+      L->ResolvedOperatorDelete(First, OD, ThisArg);
   }
 }
 

Modified: cfe/trunk/lib/AST/ExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprCXX.cpp?rev=315662&r1=315661&r2=315662&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprCXX.cpp (original)
+++ cfe/trunk/lib/AST/ExprCXX.cpp Thu Oct 12 18:55:36 2017
@@ -160,6 +160,22 @@ bool CXXNewExpr::shouldNullCheckAllocati
 // CXXDeleteExpr
 QualType CXXDeleteExpr::getDestroyedType() const {
   const Expr *Arg = getArgument();
+
+  // For a destroying operator delete, we may have implicitly converted the
+  // pointer type to the type of the parameter of the 'operator delete'
+  // function.
+  while (auto *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
+    if (ICE->getCastKind() == CK_DerivedToBase ||
+        ICE->getCastKind() == CK_UncheckedDerivedToBase ||
+        ICE->getCastKind() == CK_NoOp) {
+      assert((ICE->getCastKind() == CK_NoOp ||
+              getOperatorDelete()->isDestroyingOperatorDelete()) &&
+             "only a destroying operator delete can have a converted arg");
+      Arg = ICE->getSubExpr();
+    } else
+      break;
+  }
+
   // The type-to-delete may not be a pointer if it's a dependent type.
   const QualType ArgType = Arg->getType();
 

Modified: cfe/trunk/lib/CodeGen/CGClass.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGClass.cpp?rev=315662&r1=315661&r2=315662&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGClass.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGClass.cpp Thu Oct 12 18:55:36 2017
@@ -1413,10 +1413,11 @@ void CodeGenFunction::EmitDestructorBody
   // possible to delegate the destructor body to the complete
   // destructor.  Do so.
   if (DtorType == Dtor_Deleting) {
+    RunCleanupsScope DtorEpilogue(*this);
     EnterDtorCleanups(Dtor, Dtor_Deleting);
-    EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false,
-                          /*Delegating=*/false, LoadCXXThisAddress());
-    PopCleanupBlock();
+    if (HaveInsertPoint())
+      EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false,
+                            /*Delegating=*/false, LoadCXXThisAddress());
     return;
   }
 
@@ -1512,6 +1513,13 @@ void CodeGenFunction::emitImplicitAssign
 }
 
 namespace {
+  llvm::Value *LoadThisForDtorDelete(CodeGenFunction &CGF,
+                                     const CXXDestructorDecl *DD) {
+    if (Expr *ThisArg = DD->getOperatorDeleteThisArg())
+      return CGF.EmitScalarExpr(DD->getOperatorDeleteThisArg());
+    return CGF.LoadCXXThis();
+  }
+
   /// Call the operator delete associated with the current destructor.
   struct CallDtorDelete final : EHScopeStack::Cleanup {
     CallDtorDelete() {}
@@ -1519,11 +1527,38 @@ namespace {
     void Emit(CodeGenFunction &CGF, Flags flags) override {
       const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CGF.CurCodeDecl);
       const CXXRecordDecl *ClassDecl = Dtor->getParent();
-      CGF.EmitDeleteCall(Dtor->getOperatorDelete(), CGF.LoadCXXThis(),
+      CGF.EmitDeleteCall(Dtor->getOperatorDelete(),
+                         LoadThisForDtorDelete(CGF, Dtor),
                          CGF.getContext().getTagDeclType(ClassDecl));
     }
   };
 
+  void EmitConditionalDtorDeleteCall(CodeGenFunction &CGF,
+                                     llvm::Value *ShouldDeleteCondition,
+                                     bool ReturnAfterDelete) {
+    llvm::BasicBlock *callDeleteBB = CGF.createBasicBlock("dtor.call_delete");
+    llvm::BasicBlock *continueBB = CGF.createBasicBlock("dtor.continue");
+    llvm::Value *ShouldCallDelete
+      = CGF.Builder.CreateIsNull(ShouldDeleteCondition);
+    CGF.Builder.CreateCondBr(ShouldCallDelete, continueBB, callDeleteBB);
+
+    CGF.EmitBlock(callDeleteBB);
+    const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CGF.CurCodeDecl);
+    const CXXRecordDecl *ClassDecl = Dtor->getParent();
+    CGF.EmitDeleteCall(Dtor->getOperatorDelete(),
+                       LoadThisForDtorDelete(CGF, Dtor),
+                       CGF.getContext().getTagDeclType(ClassDecl));
+    assert(Dtor->getOperatorDelete()->isDestroyingOperatorDelete() ==
+               ReturnAfterDelete &&
+           "unexpected value for ReturnAfterDelete");
+    if (ReturnAfterDelete)
+      CGF.EmitBranchThroughCleanup(CGF.ReturnBlock);
+    else
+      CGF.Builder.CreateBr(continueBB);
+
+    CGF.EmitBlock(continueBB);
+  }
+
   struct CallDtorDeleteConditional final : EHScopeStack::Cleanup {
     llvm::Value *ShouldDeleteCondition;
 
@@ -1534,20 +1569,8 @@ namespace {
     }
 
     void Emit(CodeGenFunction &CGF, Flags flags) override {
-      llvm::BasicBlock *callDeleteBB = CGF.createBasicBlock("dtor.call_delete");
-      llvm::BasicBlock *continueBB = CGF.createBasicBlock("dtor.continue");
-      llvm::Value *ShouldCallDelete
-        = CGF.Builder.CreateIsNull(ShouldDeleteCondition);
-      CGF.Builder.CreateCondBr(ShouldCallDelete, continueBB, callDeleteBB);
-
-      CGF.EmitBlock(callDeleteBB);
-      const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CGF.CurCodeDecl);
-      const CXXRecordDecl *ClassDecl = Dtor->getParent();
-      CGF.EmitDeleteCall(Dtor->getOperatorDelete(), CGF.LoadCXXThis(),
-                         CGF.getContext().getTagDeclType(ClassDecl));
-      CGF.Builder.CreateBr(continueBB);
-
-      CGF.EmitBlock(continueBB);
+      EmitConditionalDtorDeleteCall(CGF, ShouldDeleteCondition,
+                                    /*ReturnAfterDelete*/false);
     }
   };
 
@@ -1706,6 +1729,9 @@ namespace {
 /// \brief Emit all code that comes at the end of class's
 /// destructor. This is to call destructors on members and base classes
 /// in reverse order of their construction.
+///
+/// For a deleting destructor, this also handles the case where a destroying
+/// operator delete completely overrides the definition.
 void CodeGenFunction::EnterDtorCleanups(const CXXDestructorDecl *DD,
                                         CXXDtorType DtorType) {
   assert((!DD->isTrivial() || DD->hasAttr<DLLExportAttr>()) &&
@@ -1718,11 +1744,23 @@ void CodeGenFunction::EnterDtorCleanups(
            "operator delete missing - EnterDtorCleanups");
     if (CXXStructorImplicitParamValue) {
       // If there is an implicit param to the deleting dtor, it's a boolean
-      // telling whether we should call delete at the end of the dtor.
-      EHStack.pushCleanup<CallDtorDeleteConditional>(
-          NormalAndEHCleanup, CXXStructorImplicitParamValue);
+      // telling whether this is a deleting destructor.
+      if (DD->getOperatorDelete()->isDestroyingOperatorDelete())
+        EmitConditionalDtorDeleteCall(*this, CXXStructorImplicitParamValue,
+                                      /*ReturnAfterDelete*/true);
+      else
+        EHStack.pushCleanup<CallDtorDeleteConditional>(
+            NormalAndEHCleanup, CXXStructorImplicitParamValue);
     } else {
-      EHStack.pushCleanup<CallDtorDelete>(NormalAndEHCleanup);
+      if (DD->getOperatorDelete()->isDestroyingOperatorDelete()) {
+        const CXXRecordDecl *ClassDecl = DD->getParent();
+        EmitDeleteCall(DD->getOperatorDelete(),
+                       LoadThisForDtorDelete(*this, DD),
+                       getContext().getTagDeclType(ClassDecl));
+        EmitBranchThroughCleanup(ReturnBlock);
+      } else {
+        EHStack.pushCleanup<CallDtorDelete>(NormalAndEHCleanup);
+      }
     }
     return;
   }

Modified: cfe/trunk/lib/CodeGen/CGExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExprCXX.cpp?rev=315662&r1=315661&r2=315662&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGExprCXX.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGExprCXX.cpp Thu Oct 12 18:55:36 2017
@@ -1311,29 +1311,44 @@ RValue CodeGenFunction::EmitBuiltinNewDe
   llvm_unreachable("predeclared global operator new/delete is missing");
 }
 
-static std::pair<bool, bool>
-shouldPassSizeAndAlignToUsualDelete(const FunctionProtoType *FPT) {
+namespace {
+/// The parameters to pass to a usual operator delete.
+struct UsualDeleteParams {
+  bool DestroyingDelete = false;
+  bool Size = false;
+  bool Alignment = false;
+};
+}
+
+static UsualDeleteParams getUsualDeleteParams(const FunctionDecl *FD) {
+  UsualDeleteParams Params;
+
+  const FunctionProtoType *FPT = FD->getType()->castAs<FunctionProtoType>();
   auto AI = FPT->param_type_begin(), AE = FPT->param_type_end();
 
   // The first argument is always a void*.
   ++AI;
 
-  // Figure out what other parameters we should be implicitly passing.
-  bool PassSize = false;
-  bool PassAlignment = false;
+  // The next parameter may be a std::destroying_delete_t.
+  if (FD->isDestroyingOperatorDelete()) {
+    Params.DestroyingDelete = true;
+    assert(AI != AE);
+    ++AI;
+  }
 
+  // Figure out what other parameters we should be implicitly passing.
   if (AI != AE && (*AI)->isIntegerType()) {
-    PassSize = true;
+    Params.Size = true;
     ++AI;
   }
 
   if (AI != AE && (*AI)->isAlignValT()) {
-    PassAlignment = true;
+    Params.Alignment = true;
     ++AI;
   }
 
   assert(AI == AE && "unexpected usual deallocation function parameter");
-  return {PassSize, PassAlignment};
+  return Params;
 }
 
 namespace {
@@ -1386,25 +1401,27 @@ namespace {
           OperatorDelete->getType()->getAs<FunctionProtoType>();
       CallArgList DeleteArgs;
 
-      // The first argument is always a void*.
+      // The first argument is always a void* (or C* for a destroying operator
+      // delete for class type C).
       DeleteArgs.add(Traits::get(CGF, Ptr), FPT->getParamType(0));
 
       // Figure out what other parameters we should be implicitly passing.
-      bool PassSize = false;
-      bool PassAlignment = false;
+      UsualDeleteParams Params;
       if (NumPlacementArgs) {
         // A placement deallocation function is implicitly passed an alignment
         // if the placement allocation function was, but is never passed a size.
-        PassAlignment = PassAlignmentToPlacementDelete;
+        Params.Alignment = PassAlignmentToPlacementDelete;
       } else {
         // For a non-placement new-expression, 'operator delete' can take a
         // size and/or an alignment if it has the right parameters.
-        std::tie(PassSize, PassAlignment) =
-            shouldPassSizeAndAlignToUsualDelete(FPT);
+        Params = getUsualDeleteParams(OperatorDelete);
       }
 
+      assert(!Params.DestroyingDelete &&
+             "should not call destroying delete in a new-expression");
+
       // The second argument can be a std::size_t (for non-placement delete).
-      if (PassSize)
+      if (Params.Size)
         DeleteArgs.add(Traits::get(CGF, AllocSize),
                        CGF.getContext().getSizeType());
 
@@ -1412,7 +1429,7 @@ namespace {
       // is an enum whose underlying type is std::size_t.
       // FIXME: Use the right type as the parameter type. Note that in a call
       // to operator delete(size_t, ...), we may not have it available.
-      if (PassAlignment)
+      if (Params.Alignment)
         DeleteArgs.add(RValue::get(llvm::ConstantInt::get(
                            CGF.SizeTy, AllocAlign.getQuantity())),
                        CGF.getContext().getSizeType());
@@ -1715,9 +1732,7 @@ void CodeGenFunction::EmitDeleteCall(con
 
   CallArgList DeleteArgs;
 
-  std::pair<bool, bool> PassSizeAndAlign =
-      shouldPassSizeAndAlignToUsualDelete(DeleteFTy);
-
+  auto Params = getUsualDeleteParams(DeleteFD);
   auto ParamTypeIt = DeleteFTy->param_type_begin();
 
   // Pass the pointer itself.
@@ -1725,8 +1740,16 @@ void CodeGenFunction::EmitDeleteCall(con
   llvm::Value *DeletePtr = Builder.CreateBitCast(Ptr, ConvertType(ArgTy));
   DeleteArgs.add(RValue::get(DeletePtr), ArgTy);
 
+  // Pass the std::destroying_delete tag if present.
+  if (Params.DestroyingDelete) {
+    QualType DDTag = *ParamTypeIt++;
+    // Just pass an 'undef'. We expect the tag type to be an empty struct.
+    auto *V = llvm::UndefValue::get(getTypes().ConvertType(DDTag));
+    DeleteArgs.add(RValue::get(V), DDTag);
+  }
+
   // Pass the size if the delete function has a size_t parameter.
-  if (PassSizeAndAlign.first) {
+  if (Params.Size) {
     QualType SizeType = *ParamTypeIt++;
     CharUnits DeleteTypeSize = getContext().getTypeSizeInChars(DeleteTy);
     llvm::Value *Size = llvm::ConstantInt::get(ConvertType(SizeType),
@@ -1745,7 +1768,7 @@ void CodeGenFunction::EmitDeleteCall(con
   }
 
   // Pass the alignment if the delete function has an align_val_t parameter.
-  if (PassSizeAndAlign.second) {
+  if (Params.Alignment) {
     QualType AlignValType = *ParamTypeIt++;
     CharUnits DeleteTypeAlign = getContext().toCharUnitsFromBits(
         getContext().getTypeAlignIfKnown(DeleteTy));
@@ -1787,6 +1810,21 @@ CodeGenFunction::pushCallObjectDeleteCle
                                         OperatorDelete, ElementType);
 }
 
+/// Emit the code for deleting a single object with a destroying operator
+/// delete. If the element type has a non-virtual destructor, Ptr has already
+/// been converted to the type of the parameter of 'operator delete'. Otherwise
+/// Ptr points to an object of the static type.
+static void EmitDestroyingObjectDelete(CodeGenFunction &CGF,
+                                       const CXXDeleteExpr *DE, Address Ptr,
+                                       QualType ElementType) {
+  auto *Dtor = ElementType->getAsCXXRecordDecl()->getDestructor();
+  if (Dtor && Dtor->isVirtual())
+    CGF.CGM.getCXXABI().emitVirtualObjectDelete(CGF, DE, Ptr, ElementType,
+                                                Dtor);
+  else
+    CGF.EmitDeleteCall(DE->getOperatorDelete(), Ptr.getPointer(), ElementType);
+}
+
 /// Emit the code for deleting a single object.
 static void EmitObjectDelete(CodeGenFunction &CGF,
                              const CXXDeleteExpr *DE,
@@ -1801,6 +1839,9 @@ static void EmitObjectDelete(CodeGenFunc
                     DE->getExprLoc(), Ptr.getPointer(),
                     ElementType);
 
+  const FunctionDecl *OperatorDelete = DE->getOperatorDelete();
+  assert(!OperatorDelete->isDestroyingOperatorDelete());
+
   // Find the destructor for the type, if applicable.  If the
   // destructor is virtual, we'll just emit the vcall and return.
   const CXXDestructorDecl *Dtor = nullptr;
@@ -1820,7 +1861,6 @@ static void EmitObjectDelete(CodeGenFunc
   // Make sure that we call delete even if the dtor throws.
   // This doesn't have to a conditional cleanup because we're going
   // to pop it off in a second.
-  const FunctionDecl *OperatorDelete = DE->getOperatorDelete();
   CGF.EHStack.pushCleanup<CallObjectDelete>(NormalAndEHCleanup,
                                             Ptr.getPointer(),
                                             OperatorDelete, ElementType);
@@ -1932,10 +1972,19 @@ void CodeGenFunction::EmitCXXDeleteExpr(
   Builder.CreateCondBr(IsNull, DeleteEnd, DeleteNotNull);
   EmitBlock(DeleteNotNull);
 
+  QualType DeleteTy = E->getDestroyedType();
+
+  // A destroying operator delete overrides the entire operation of the
+  // delete expression.
+  if (E->getOperatorDelete()->isDestroyingOperatorDelete()) {
+    EmitDestroyingObjectDelete(*this, E, Ptr, DeleteTy);
+    EmitBlock(DeleteEnd);
+    return;
+  }
+
   // We might be deleting a pointer to array.  If so, GEP down to the
   // first non-array element.
   // (this assumes that A(*)[3][7] is converted to [3 x [7 x %A]]*)
-  QualType DeleteTy = Arg->getType()->getAs<PointerType>()->getPointeeType();
   if (DeleteTy->isConstantArrayType()) {
     llvm::Value *Zero = Builder.getInt32(0);
     SmallVector<llvm::Value*,8> GEP;

Modified: cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp?rev=315662&r1=315661&r2=315662&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp (original)
+++ cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp Thu Oct 12 18:55:36 2017
@@ -3664,6 +3664,18 @@ void ItaniumCXXABI::emitCXXStructor(cons
       !CGM.TryEmitBaseDestructorAsAlias(DD))
     return;
 
+  // FIXME: The deleting destructor is equivalent to the selected operator
+  // delete if:
+  //  * either the delete is a destroying operator delete or the destructor
+  //    would be trivial if it weren't virtual,
+  //  * the conversion from the 'this' parameter to the first parameter of the
+  //    destructor is equivalent to a bitcast,
+  //  * the destructor does not have an implicit "this" return, and
+  //  * the operator delete has the same calling convention and IR function type
+  //    as the destructor.
+  // In such cases we should try to emit the deleting dtor as an alias to the
+  // selected 'operator delete'.
+
   llvm::Function *Fn = CGM.codegenCXXStructor(MD, Type);
 
   if (CGType == StructorCodegen::COMDAT) {

Modified: cfe/trunk/lib/Frontend/MultiplexConsumer.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/MultiplexConsumer.cpp?rev=315662&r1=315661&r2=315662&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/MultiplexConsumer.cpp (original)
+++ cfe/trunk/lib/Frontend/MultiplexConsumer.cpp Thu Oct 12 18:55:36 2017
@@ -116,7 +116,8 @@ public:
   void ResolvedExceptionSpec(const FunctionDecl *FD) override;
   void DeducedReturnType(const FunctionDecl *FD, QualType ReturnType) override;
   void ResolvedOperatorDelete(const CXXDestructorDecl *DD,
-                              const FunctionDecl *Delete) override;
+                              const FunctionDecl *Delete,
+                              Expr *ThisArg) override;
   void CompletedImplicitDefinition(const FunctionDecl *D) override;
   void StaticDataMemberInstantiated(const VarDecl *D) override;
   void DefaultArgumentInstantiated(const ParmVarDecl *D) override;
@@ -183,9 +184,9 @@ void MultiplexASTMutationListener::Deduc
     Listeners[i]->DeducedReturnType(FD, ReturnType);
 }
 void MultiplexASTMutationListener::ResolvedOperatorDelete(
-    const CXXDestructorDecl *DD, const FunctionDecl *Delete) {
+    const CXXDestructorDecl *DD, const FunctionDecl *Delete, Expr *ThisArg) {
   for (auto *L : Listeners)
-    L->ResolvedOperatorDelete(DD, Delete);
+    L->ResolvedOperatorDelete(DD, Delete, ThisArg);
 }
 void MultiplexASTMutationListener::CompletedImplicitDefinition(
                                                         const FunctionDecl *D) {

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=315662&r1=315661&r2=315662&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Thu Oct 12 18:55:36 2017
@@ -7896,8 +7896,34 @@ bool Sema::CheckDestructor(CXXDestructor
     // If we have a virtual destructor, look up the deallocation function
     if (FunctionDecl *OperatorDelete =
             FindDeallocationFunctionForDestructor(Loc, RD)) {
+      Expr *ThisArg = nullptr;
+
+      // If the notional 'delete this' expression requires a non-trivial
+      // conversion from 'this' to the type of a destroying operator delete's
+      // first parameter, perform that conversion now.
+      if (OperatorDelete->isDestroyingOperatorDelete()) {
+        QualType ParamType = OperatorDelete->getParamDecl(0)->getType();
+        if (!declaresSameEntity(ParamType->getAsCXXRecordDecl(), RD)) {
+          // C++ [class.dtor]p13:
+          //   ... as if for the expression 'delete this' appearing in a
+          //   non-virtual destructor of the destructor's class.
+          ContextRAII SwitchContext(*this, Destructor);
+          ExprResult This =
+              ActOnCXXThis(OperatorDelete->getParamDecl(0)->getLocation());
+          assert(!This.isInvalid() && "couldn't form 'this' expr in dtor?");
+          This = PerformImplicitConversion(This.get(), ParamType, AA_Passing);
+          if (This.isInvalid()) {
+            // FIXME: Register this as a context note so that it comes out
+            // in the right order.
+            Diag(Loc, diag::note_implicit_delete_this_in_destructor_here);
+            return true;
+          }
+          ThisArg = This.get();
+        }
+      }
+
       MarkFunctionReferenced(Loc, OperatorDelete);
-      Destructor->setOperatorDelete(OperatorDelete);
+      Destructor->setOperatorDelete(OperatorDelete, ThisArg);
     }
   }
 
@@ -12664,15 +12690,35 @@ CheckOperatorDeleteDeclaration(Sema &Sem
   if (CheckOperatorNewDeleteDeclarationScope(SemaRef, FnDecl))
     return true;
 
+  auto *MD = dyn_cast<CXXMethodDecl>(FnDecl);
+
+  // C++ P0722:
+  //   Within a class C, the first parameter of a destroying operator delete
+  //   shall be of type C *. The first parameter of any other deallocation
+  //   function shall be of type void *.
+  CanQualType ExpectedFirstParamType =
+      MD && MD->isDestroyingOperatorDelete()
+          ? SemaRef.Context.getCanonicalType(SemaRef.Context.getPointerType(
+                SemaRef.Context.getRecordType(MD->getParent())))
+          : SemaRef.Context.VoidPtrTy;
+
   // C++ [basic.stc.dynamic.deallocation]p2:
-  //   Each deallocation function shall return void and its first parameter
-  //   shall be void*.
-  if (CheckOperatorNewDeleteTypes(SemaRef, FnDecl, SemaRef.Context.VoidTy,
-                                  SemaRef.Context.VoidPtrTy,
-                                 diag::err_operator_delete_dependent_param_type,
-                                 diag::err_operator_delete_param_type))
+  //   Each deallocation function shall return void
+  if (CheckOperatorNewDeleteTypes(
+          SemaRef, FnDecl, SemaRef.Context.VoidTy, ExpectedFirstParamType,
+          diag::err_operator_delete_dependent_param_type,
+          diag::err_operator_delete_param_type))
     return true;
 
+  // C++ P0722:
+  //   A destroying operator delete shall be a usual deallocation function.
+  if (MD && !MD->getParent()->isDependentContext() &&
+      MD->isDestroyingOperatorDelete() && !MD->isUsualDeallocationFunction()) {
+    SemaRef.Diag(MD->getLocation(),
+                 diag::err_destroying_operator_delete_not_usual);
+    return true;
+  }
+
   return false;
 }
 

Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=315662&r1=315661&r2=315662&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Thu Oct 12 18:55:36 2017
@@ -1407,14 +1407,20 @@ namespace {
     UsualDeallocFnInfo() : Found(), FD(nullptr) {}
     UsualDeallocFnInfo(Sema &S, DeclAccessPair Found)
         : Found(Found), FD(dyn_cast<FunctionDecl>(Found->getUnderlyingDecl())),
-          HasSizeT(false), HasAlignValT(false), CUDAPref(Sema::CFP_Native) {
+          Destroying(false), HasSizeT(false), HasAlignValT(false),
+          CUDAPref(Sema::CFP_Native) {
       // A function template declaration is never a usual deallocation function.
       if (!FD)
         return;
-      if (FD->getNumParams() == 3)
+      unsigned NumBaseParams = 1;
+      if (FD->isDestroyingOperatorDelete()) {
+        Destroying = true;
+        ++NumBaseParams;
+      }
+      if (FD->getNumParams() == NumBaseParams + 2)
         HasAlignValT = HasSizeT = true;
-      else if (FD->getNumParams() == 2) {
-        HasSizeT = FD->getParamDecl(1)->getType()->isIntegerType();
+      else if (FD->getNumParams() == NumBaseParams + 1) {
+        HasSizeT = FD->getParamDecl(NumBaseParams)->getType()->isIntegerType();
         HasAlignValT = !HasSizeT;
       }
 
@@ -1428,6 +1434,12 @@ namespace {
 
     bool isBetterThan(const UsualDeallocFnInfo &Other, bool WantSize,
                       bool WantAlign) const {
+      // C++ P0722:
+      //   A destroying operator delete is preferred over a non-destroying
+      //   operator delete.
+      if (Destroying != Other.Destroying)
+        return Destroying;
+
       // C++17 [expr.delete]p10:
       //   If the type has new-extended alignment, a function with a parameter
       //   of type std::align_val_t is preferred; otherwise a function without
@@ -1444,7 +1456,7 @@ namespace {
 
     DeclAccessPair Found;
     FunctionDecl *FD;
-    bool HasSizeT, HasAlignValT;
+    bool Destroying, HasSizeT, HasAlignValT;
     Sema::CUDAFunctionPreference CUDAPref;
   };
 }
@@ -3261,16 +3273,30 @@ Sema::ActOnCXXDelete(SourceLocation Star
 
     MarkFunctionReferenced(StartLoc, OperatorDelete);
 
-    // Check access and ambiguity of operator delete and destructor.
+    // Check access and ambiguity of destructor if we're going to call it.
+    // Note that this is required even for a virtual delete.
+    bool IsVirtualDelete = false;
     if (PointeeRD) {
       if (CXXDestructorDecl *Dtor = LookupDestructor(PointeeRD)) {
-          CheckDestructorAccess(Ex.get()->getExprLoc(), Dtor,
-                      PDiag(diag::err_access_dtor) << PointeeElem);
+        CheckDestructorAccess(Ex.get()->getExprLoc(), Dtor,
+                              PDiag(diag::err_access_dtor) << PointeeElem);
+        IsVirtualDelete = Dtor->isVirtual();
       }
     }
 
     diagnoseUnavailableAlignedAllocation(*OperatorDelete, StartLoc, true,
                                          *this);
+
+    // Convert the operand to the type of the first parameter of operator
+    // delete. This is only necessary if we selected a destroying operator
+    // delete that we are going to call (non-virtually); converting to void*
+    // is trivial and left to AST consumers to handle.
+    QualType ParamType = OperatorDelete->getParamDecl(0)->getType();
+    if (!IsVirtualDelete && !ParamType->getPointeeType()->isVoidType()) {
+      Ex = PerformImplicitConversion(Ex.get(), ParamType, AA_Passing);
+      if (Ex.isInvalid())
+        return ExprError();
+    }
   }
 
   CXXDeleteExpr *Result = new (Context) CXXDeleteExpr(

Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=315662&r1=315661&r2=315662&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Thu Oct 12 18:55:36 2017
@@ -1901,9 +1901,12 @@ void ASTDeclReader::VisitCXXDestructorDe
 
   if (auto *OperatorDelete = ReadDeclAs<FunctionDecl>()) {
     auto *Canon = cast<CXXDestructorDecl>(D->getCanonicalDecl());
+    auto *ThisArg = Record.readExpr();
     // FIXME: Check consistency if we have an old and new operator delete.
-    if (!Canon->OperatorDelete)
+    if (!Canon->OperatorDelete) {
       Canon->OperatorDelete = OperatorDelete;
+      Canon->OperatorDeleteThisArg = ThisArg;
+    }
   }
 }
 
@@ -4113,9 +4116,12 @@ void ASTDeclReader::UpdateDecl(Decl *D,
       // record.
       auto *Del = ReadDeclAs<FunctionDecl>();
       auto *First = cast<CXXDestructorDecl>(D->getCanonicalDecl());
+      auto *ThisArg = Record.readExpr();
       // FIXME: Check consistency if we have an old and new operator delete.
-      if (!First->OperatorDelete)
+      if (!First->OperatorDelete) {
         First->OperatorDelete = Del;
+        First->OperatorDeleteThisArg = ThisArg;
+      }
       break;
     }
 

Modified: cfe/trunk/lib/Serialization/ASTWriter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriter.cpp?rev=315662&r1=315661&r2=315662&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTWriter.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTWriter.cpp Thu Oct 12 18:55:36 2017
@@ -5151,6 +5151,7 @@ void ASTWriter::WriteDeclUpdatesBlocks(R
 
       case UPD_CXX_RESOLVED_DTOR_DELETE:
         Record.AddDeclRef(Update.getDecl());
+        Record.AddStmt(cast<CXXDestructorDecl>(D)->getOperatorDeleteThisArg());
         break;
 
       case UPD_CXX_RESOLVED_EXCEPTION_SPEC:
@@ -6178,7 +6179,8 @@ void ASTWriter::DeducedReturnType(const
 }
 
 void ASTWriter::ResolvedOperatorDelete(const CXXDestructorDecl *DD,
-                                       const FunctionDecl *Delete) {
+                                       const FunctionDecl *Delete,
+                                       Expr *ThisArg) {
   if (Chain && Chain->isProcessingUpdateRecords()) return;
   assert(!WritingAST && "Already writing the AST!");
   assert(Delete && "Not given an operator delete");

Modified: cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterDecl.cpp?rev=315662&r1=315661&r2=315662&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTWriterDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTWriterDecl.cpp Thu Oct 12 18:55:36 2017
@@ -1309,6 +1309,8 @@ void ASTDeclWriter::VisitCXXDestructorDe
   VisitCXXMethodDecl(D);
 
   Record.AddDeclRef(D->getOperatorDelete());
+  if (D->getOperatorDelete())
+    Record.AddStmt(D->getOperatorDeleteThisArg());
 
   Code = serialization::DECL_CXX_DESTRUCTOR;
 }

Added: cfe/trunk/test/CodeGenCXX/cxx2a-destroying-delete.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/cxx2a-destroying-delete.cpp?rev=315662&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenCXX/cxx2a-destroying-delete.cpp (added)
+++ cfe/trunk/test/CodeGenCXX/cxx2a-destroying-delete.cpp Thu Oct 12 18:55:36 2017
@@ -0,0 +1,161 @@
+// RUN: %clang_cc1 -std=c++2a -emit-llvm %s -triple x86_64-linux-gnu -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ITANIUM
+// RUN: %clang_cc1 -std=c++2a -emit-llvm %s -triple x86_64-windows -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-MSABI
+
+namespace std {
+  using size_t = decltype(sizeof(0));
+  enum class align_val_t : size_t;
+  struct destroying_delete_t {};
+}
+
+struct A {
+  void *data;
+  ~A();
+  void operator delete(A*, std::destroying_delete_t);
+};
+void delete_A(A *a) { delete a; }
+// CHECK-LABEL: define {{.*}}delete_A
+// CHECK: %[[a:.*]] = load
+// CHECK: icmp eq %{{.*}} %[[a]], null
+// CHECK: br i1
+//
+// Ensure that we call the destroying delete and not the destructor.
+// CHECK-NOT: call
+// CHECK-ITANIUM: call void @_ZN1AdlEPS_St19destroying_delete_t(%{{.*}}* %[[a]])
+// CHECK-MSABI: call void @"\01??3A@@SAXPEAU0 at Udestroying_delete_t@std@@@Z"(%{{.*}}* %[[a]], i8
+// CHECK-NOT: call
+// CHECK: }
+
+struct B {
+  virtual ~B();
+  void operator delete(B*, std::destroying_delete_t);
+};
+void delete_B(B *b) { delete b; }
+// CHECK-LABEL: define {{.*}}delete_B
+// CHECK: %[[b:.*]] = load
+// CHECK: icmp eq %{{.*}} %[[b]], null
+// CHECK: br i1
+//
+// Ensure that we call the virtual destructor and not the operator delete.
+// CHECK-NOT: call
+// CHECK: %[[VTABLE:.*]] = load
+// CHECK: %[[DTOR:.*]] = load
+// CHECK: call {{void|i8\*}} %[[DTOR]](%{{.*}}* %[[b]]
+// CHECK-MSABI-SAME: , i32 1)
+// CHECK-NOT: call
+// CHECK: }
+
+struct Padding {
+  virtual void f();
+};
+
+struct C : Padding, A {};
+void delete_C(C *c) { delete c; }
+// Check that we perform a derived-to-base conversion on the parameter to 'operator delete'.
+// CHECK-LABEL: define {{.*}}delete_C
+// CHECK: %[[c:.*]] = load
+// CHECK: icmp eq %{{.*}} %[[c]], null
+// CHECK: br i1
+//
+// CHECK: %[[base:.*]] = getelementptr {{.*}}, i64 8
+// CHECK: %[[castbase:.*]] = bitcast {{.*}} %[[base]]
+//
+// CHECK: %[[a:.*]] = phi {{.*}} %[[castbase]]
+// CHECK: icmp eq %{{.*}} %[[a]], null
+// CHECK: br i1
+//
+// CHECK-NOT: call
+// CHECK-ITANIUM: call void @_ZN1AdlEPS_St19destroying_delete_t(%{{.*}}* %[[a]])
+// CHECK-MSABI: call void @"\01??3A@@SAXPEAU0 at Udestroying_delete_t@std@@@Z"(%{{.*}}* %[[a]], i8
+// CHECK-NOT: call
+// CHECK: }
+
+struct VDel { virtual ~VDel(); };
+struct D : Padding, VDel, B {};
+void delete_D(D *d) { delete d; }
+// CHECK-LABEL: define {{.*}}delete_D
+// CHECK: %[[d:.*]] = load
+// CHECK: icmp eq %{{.*}} %[[d]], null
+// CHECK: br i1
+//
+// CHECK-NOT: call
+// CHECK: %[[VTABLE:.*]] = load
+// CHECK: %[[DTOR:.*]] = load
+//
+// For MS, we don't add a new vtable slot to the primary vtable for the virtual
+// destructor. Instead we cast to the VDel base class.
+// CHECK-MSABI: bitcast {{.*}} %[[d]]
+// CHECK-MSABI-NEXT: getelementptr {{.*}}, i64 8
+// CHECK-MSABI-NEXT: %[[d:.*]] = bitcast i8*
+//
+// CHECK: call {{void|i8\*}} %[[DTOR]](%{{.*}}* %[[d]]
+// CHECK-MSABI-SAME: , i32 1)
+// CHECK-NOT: call
+// CHECK: }
+
+struct E { void *data; };
+struct F { void operator delete(F *, std::destroying_delete_t, std::size_t, std::align_val_t); void *data; };
+struct alignas(16) G : E, F { void *data; };
+
+void delete_G(G *g) { delete g; }
+// CHECK-LABEL: define {{.*}}delete_G
+// CHECK-NOT: call
+// CHECK-ITANIUM: call void @_ZN1FdlEPS_St19destroying_delete_tmSt11align_val_t(%{{.*}}* %[[a]], i64 32, i64 16)
+// CHECK-MSABI: call void @"\01??3F@@SAXPEAU0 at Udestroying_delete_t@std@@_KW4align_val_t at 2@@Z"(%{{.*}}* %[[a]], i8 {{[^,]*}}, i64 32, i64 16)
+// CHECK-NOT: call
+// CHECK: }
+
+void call_in_dtor();
+
+struct H : G { virtual ~H(); } h;
+H::~H() { call_in_dtor(); }
+// CHECK-ITANIUM-LABEL: define void @_ZN1HD0Ev(
+// CHECK-ITANIUM-NOT: call
+// CHECK-ITANIUM: getelementptr {{.*}}, i64 24
+// CHECK-ITANIUM-NOT: call
+// CHECK-ITANIUM: call void @_ZN1FdlEPS_St19destroying_delete_tmSt11align_val_t({{.*}}, i64 48, i64 16)
+// CHECK-ITANIUM-NOT: call
+// CHECK-ITANIUM: }
+
+// CHECK-MSABI: define {{.*}} @"\01??_GH@@UEAAPEAXI at Z"(
+// CHECK-MSABI-NOT: call{{ }}
+// CHECK-MSABI: load i32
+// CHECK-MSABI: icmp eq i32 {{.*}}, 0
+// CHECK-MSABI: br i1
+//
+// CHECK-MSABI-NOT: call{{ }}
+// CHECK-MSABI: getelementptr {{.*}}, i64 24
+// CHECK-MSABI-NOT: call{{ }}
+// CHECK-MSABI: call void @"\01??3F@@SAXPEAU0 at Udestroying_delete_t@std@@_KW4align_val_t at 2@@Z"({{.*}}, i64 48, i64 16)
+// CHECK-MSABI: br label %[[RETURN:.*]]
+//
+// CHECK-MSABI: call void @"\01??_DH@@QEAAXXZ"(
+// CHECK-MSABI: br label %[[RETURN]]
+//
+// CHECK-MSABI: }
+
+struct I : H { virtual ~I(); alignas(32) char buffer[32]; } i;
+I::~I() { call_in_dtor(); }
+// CHECK-ITANIUM-LABEL: define void @_ZN1ID0Ev(
+// CHECK-ITANIUM-NOT: call
+// CHECK-ITANIUM: getelementptr {{.*}}, i64 24
+// CHECK-ITANIUM-NOT: call
+// CHECK-ITANIUM: call void @_ZN1FdlEPS_St19destroying_delete_tmSt11align_val_t({{.*}}, i64 96, i64 32)
+// CHECK-ITANIUM-NOT: call
+// CHECK-ITANIUM: }
+
+// CHECK-MSABI: define {{.*}} @"\01??_GI@@UEAAPEAXI at Z"(
+// CHECK-MSABI-NOT: call{{ }}
+// CHECK-MSABI: load i32
+// CHECK-MSABI: icmp eq i32 {{.*}}, 0
+// CHECK-MSABI: br i1
+//
+// CHECK-MSABI-NOT: call{{ }}
+// CHECK-MSABI: getelementptr {{.*}}, i64 24
+// CHECK-MSABI-NOT: call{{ }}
+// CHECK-MSABI: call void @"\01??3F@@SAXPEAU0 at Udestroying_delete_t@std@@_KW4align_val_t at 2@@Z"({{.*}}, i64 96, i64 32)
+// CHECK-MSABI: br label %[[RETURN:.*]]
+//
+// CHECK-MSABI: call void @"\01??_DI@@QEAAXXZ"(
+// CHECK-MSABI: br label %[[RETURN]]
+//
+// CHECK-MSABI: }

Added: cfe/trunk/test/SemaCXX/cxx2a-destroying-delete.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx2a-destroying-delete.cpp?rev=315662&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx2a-destroying-delete.cpp (added)
+++ cfe/trunk/test/SemaCXX/cxx2a-destroying-delete.cpp Thu Oct 12 18:55:36 2017
@@ -0,0 +1,103 @@
+// RUN: %clang_cc1 -std=c++2a -verify %s
+
+namespace std {
+  using size_t = decltype(sizeof(0));
+  enum class align_val_t : size_t;
+
+  struct destroying_delete_t {
+    struct __construct { explicit __construct() = default; };
+    explicit destroying_delete_t(__construct) {}
+  };
+
+  inline constexpr destroying_delete_t destroying_delete(destroying_delete_t::__construct());
+}
+
+void operator delete(void*, std::destroying_delete_t); // ok, just a placement delete
+
+struct A;
+void operator delete(A*, std::destroying_delete_t); // expected-error {{first parameter of 'operator delete' must have type 'void *'}}
+
+struct A {
+  void operator delete(A*, std::destroying_delete_t);
+  void operator delete(A*, std::destroying_delete_t, std::size_t);
+  void operator delete(A*, std::destroying_delete_t, std::align_val_t);
+  void operator delete(A*, std::destroying_delete_t, std::size_t, std::align_val_t);
+  void operator delete(A*, std::destroying_delete_t, int); // expected-error {{destroying operator delete can have only an optional size and optional alignment parameter}}
+  // FIXME: It's probably a language defect that we permit usual operator delete to be variadic.
+  void operator delete(A*, std::destroying_delete_t, std::size_t, ...);
+
+  void operator delete(struct X*, std::destroying_delete_t, std::size_t, ...); // expected-error {{first parameter of 'operator delete' must have type 'A *'}}
+
+  void operator delete(void*, std::size_t);
+};
+
+void delete_A(A *a) { delete a; }
+
+namespace convert_param {
+  struct A {
+    void operator delete(
+        A*,
+        std::destroying_delete_t);
+  };
+  struct B : private A { using A::operator delete; }; // expected-note 2{{declared private here}}
+  struct C : B {};
+  void delete_C(C *c) { delete c; } // expected-error {{cannot cast 'convert_param::C' to its private base class 'convert_param::A'}}
+
+  // expected-error at -7 {{cannot cast 'convert_param::D' to its private base class 'convert_param::A'}}
+  struct D : B { virtual ~D() {} }; // expected-note {{while checking implicit 'delete this' for virtual destructor}}
+}
+
+namespace delete_selection {
+  struct B {
+    void operator delete(void*) = delete;
+    void operator delete(B *, std::destroying_delete_t) = delete; // expected-note {{deleted}}
+  };
+  void delete_B(B *b) { delete b; } // expected-error {{deleted}}
+
+  struct C {
+    C();
+    void *operator new(std::size_t);
+    void operator delete(void*) = delete;
+    void operator delete(C *, std::destroying_delete_t) = delete;
+  };
+  // FIXME: This should be ill-formed, but we incorrectly decide that overload
+  // resolution failed (because it selected a deleted function) and thus no
+  // 'operator delete' should be called.
+  C *new_C() { return new C; }
+
+  struct D {
+    void operator delete(D *, std::destroying_delete_t) = delete; // expected-note {{deleted}}
+    void operator delete(D *, std::destroying_delete_t, std::align_val_t) = delete;
+  };
+  void delete_D(D *d) { delete d; } // expected-error {{deleted}}
+
+  struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2) E {
+    void operator delete(E *, std::destroying_delete_t) = delete;
+    void operator delete(E *, std::destroying_delete_t, std::align_val_t) = delete; // expected-note {{deleted}}
+  };
+  void delete_E(E *e) { delete e; } // expected-error {{deleted}}
+
+  struct F {
+    void operator delete(F *, std::destroying_delete_t) = delete; // expected-note {{deleted}}
+    void operator delete(F *, std::destroying_delete_t, std::size_t) = delete;
+  };
+  void delete_F(F *f) { delete f; } // expected-error {{deleted}}
+
+  struct G {
+    void operator delete(G *, std::destroying_delete_t, std::align_val_t) = delete;
+    void operator delete(G *, std::destroying_delete_t, std::size_t) = delete; // expected-note {{deleted}}
+  };
+  void delete_G(G *g) { delete g; } // expected-error {{deleted}}
+
+  struct H {
+    void operator delete(H *, std::destroying_delete_t, std::align_val_t) = delete; // expected-note {{deleted}}
+    void operator delete(H *, std::destroying_delete_t, std::size_t, std::align_val_t) = delete;
+  };
+  void delete_H(H *h) { delete h; } // expected-error {{deleted}}
+
+  struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 2) I {
+    void operator delete(I *, std::destroying_delete_t, std::size_t) = delete;
+    void operator delete(I *, std::destroying_delete_t, std::size_t, std::align_val_t) = delete; // expected-note {{deleted}}
+  };
+  void delete_I(I *i) { delete i; } // expected-error {{deleted}}
+}




More information about the cfe-commits mailing list