]> granicus.if.org Git - clang/commitdiff
[c++20] Check for a class-specific operator delete when deleting an
authorRichard Smith <richard-llvm@metafoo.co.uk>
Mon, 7 Oct 2019 03:14:28 +0000 (03:14 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Mon, 7 Oct 2019 03:14:28 +0000 (03:14 +0000)
object of class type with a virtual destructor.

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

lib/AST/ExprConstant.cpp
test/SemaCXX/constant-expression-cxx2a.cpp

index 8c56a3cc5504d7ad8ff86a768bc44ef09fea9243..c32f516aec7e8ddd0f4385ec194567db28f5251a 100644 (file)
@@ -6019,6 +6019,13 @@ static bool hasVirtualDestructor(QualType T) {
   return false;
 }
 
+static const FunctionDecl *getVirtualOperatorDelete(QualType T) {
+  if (CXXRecordDecl *RD = T->getAsCXXRecordDecl())
+    if (CXXDestructorDecl *DD = RD->getDestructor())
+      return DD->isVirtual() ? DD->getOperatorDelete() : nullptr;
+  return nullptr;
+}
+
 /// Check that the given object is a suitable pointer to a heap allocation that
 /// still exists and is of the right kind for the purpose of a deletion.
 ///
@@ -13208,6 +13215,18 @@ bool VoidExprEvaluator::VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
     return false;
   }
 
+  // For a class type with a virtual destructor, the selected operator delete
+  // is the one looked up when building the destructor.
+  if (!E->isArrayForm() && !E->isGlobalDelete()) {
+    const FunctionDecl *VirtualDelete = getVirtualOperatorDelete(AllocType);
+    if (VirtualDelete &&
+        !VirtualDelete->isReplaceableGlobalAllocationFunction()) {
+      Info.FFDiag(E, diag::note_constexpr_new_non_replaceable)
+          << isa<CXXMethodDecl>(VirtualDelete) << VirtualDelete;
+      return false;
+    }
+  }
+
   if (!HandleDestruction(Info, E->getExprLoc(), Pointer.getLValueBase(),
                          (*Alloc)->Value, AllocType))
     return false;
index cb8f16a8b0f57afc4e3bf1de73f59e153dbdfa37..7c1718cebe44ad4a71ceaaec2a6a7e5a4ea643b5 100644 (file)
@@ -1190,6 +1190,25 @@ namespace dtor_call {
   static_assert(virt_dtor(3, "YX"));
   static_assert(virt_dtor(4, "X"));
 
+  constexpr bool virt_delete(bool global) {
+    struct A {
+      virtual constexpr ~A() {}
+    };
+    struct B : A {
+      void operator delete(void *);
+      constexpr ~B() {}
+    };
+
+    A *p = new B;
+    if (global)
+      ::delete p;
+    else
+      delete p; // expected-note {{call to class-specific 'operator delete'}}
+    return true;
+  }
+  static_assert(virt_delete(true));
+  static_assert(virt_delete(false)); // expected-error {{}} expected-note {{in call}}
+
   constexpr void use_after_virt_destroy() {
     char buff[4] = {};
     VU vu;