https://github.com/hvdijk created 
https://github.com/llvm/llvm-project/pull/149406

P3144R2 made it ill-formed to delete a pointer to an incomplete class type, and 
asserted that incomplete class types were the only incomplete types that were 
possible to pass to the delete operator. This assertion was wrong, and PR 99278 
already updated the check to exclude incomplete enum types, but that is still 
wrong: pointers to arrays of unknown length are another instance of incomplete 
types that are possible to delete and were not included in PR3144R2's ban. 
Additionally, the diagnostic still claimed that the problem was with deleting 
pointers to incomplete types, rather than to incomplete class types.

This PR ensures that when we implement the check for incomplete class type, we 
only check for incomplete class type, and adjusts the diagnostics to say 
'incomplete struct' or 'incomplete union' to be accurate without being overly 
verbose.

>From 94e652bc0bd6a04ef61a9f9b703257d464c7b714 Mon Sep 17 00:00:00 2001
From: Harald van Dijk <harald.vand...@codeplay.com>
Date: Thu, 17 Jul 2025 21:47:30 +0100
Subject: [PATCH] More fixes for P3144R2 implementation

P3144R2 made it ill-formed to delete a pointer to an incomplete class
type, and asserted that incomplete class types were the only incomplete
types that were possible to pass to the delete operator. This assertion
was wrong, and PR 99278 already updated the check to exclude incomplete
enum types, but that is still wrong: pointers to arrays of unknown
length are another instance of incomplete types that are possible to
delete and were not included in PR3144R2's ban. Additionally, the
diagnostic still claimed that the problem was with deleting pointers to
incomplete types, rather than to incomplete class types.

This PR ensures that when we implement the check for incomplete class
type, we only check for incomplete class type, and adjusts the
diagnostics to say 'incomplete struct' or 'incomplete union' to be
accurate without being overly verbose.
---
 .../clang/Basic/DiagnosticSemaKinds.td        |  6 ++++--
 clang/lib/Sema/SemaExprCXX.cpp                | 21 +++++++++----------
 clang/test/Analysis/dtor.cpp                  |  2 +-
 clang/test/CXX/drs/cwg5xx.cpp                 |  6 +++---
 .../CXX/expr/expr.unary/expr.delete/p5.cpp    |  9 ++++++--
 clang/test/OpenMP/deferred-diags.cpp          |  2 +-
 clang/test/SemaCXX/new-delete.cpp             | 14 ++++++++-----
 7 files changed, 35 insertions(+), 25 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b2ea65ae111be..d86f10da17c61 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -8392,17 +8392,19 @@ def ext_default_init_const : ExtWarn<
   "is a Microsoft extension">,
   InGroup<MicrosoftConstInit>;
 def err_delete_operand : Error<"cannot delete expression of type %0">;
+def err_delete_void_ptr_operand : Error<
+  "cannot delete expression with pointer-to-'void' type %0">;
 def ext_delete_void_ptr_operand : ExtWarn<
   "cannot delete expression with pointer-to-'void' type %0">,
   InGroup<DeleteIncomplete>;
 def err_ambiguous_delete_operand : Error<
   "ambiguous conversion of delete expression of type %0 to a pointer">;
 def warn_delete_incomplete : Warning<
-  "deleting pointer to incomplete type %0 is incompatible with C++2c"
+  "deleting pointer to incomplete %select{struct|union}0 %1 is incompatible 
with C++2c"
   " and may cause undefined behavior">,
   InGroup<DeleteIncomplete>;
 def err_delete_incomplete : Error<
-  "cannot delete pointer to incomplete type %0">;
+  "cannot delete pointer to incomplete %select{struct|union}0 %1">;
 def err_delete_incomplete_class_type : Error<
   "deleting incomplete class type %0; no conversions to pointer type">;
 def err_delete_explicit_conversion : Error<
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index f851c9e1d5015..a741acc8a0a89 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -4033,25 +4033,24 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool 
UseGlobal,
       // effectively bans deletion of "void*". However, most compilers support
       // this, so we treat it as a warning unless we're in a SFINAE context.
       // But we still prohibit this since C++26.
-      Diag(StartLoc, LangOpts.CPlusPlus26 ? diag::err_delete_incomplete
+      Diag(StartLoc, LangOpts.CPlusPlus26 ? diag::err_delete_void_ptr_operand
                                           : diag::ext_delete_void_ptr_operand)
-          << (LangOpts.CPlusPlus26 ? Pointee : Type)
-          << Ex.get()->getSourceRange();
+          << Type << Ex.get()->getSourceRange();
     } else if (Pointee->isFunctionType() || Pointee->isVoidType() ||
                Pointee->isSizelessType()) {
       return ExprError(Diag(StartLoc, diag::err_delete_operand)
-        << Type << Ex.get()->getSourceRange());
+                       << Type << Ex.get()->getSourceRange());
     } else if (!Pointee->isDependentType()) {
       // FIXME: This can result in errors if the definition was imported from a
       // module but is hidden.
-      if (Pointee->isEnumeralType() ||
-          !RequireCompleteType(StartLoc, Pointee,
-                               LangOpts.CPlusPlus26
-                                   ? diag::err_delete_incomplete
-                                   : diag::warn_delete_incomplete,
-                               Ex.get())) {
-        if (const RecordType *RT = PointeeElem->getAs<RecordType>())
+      if (const RecordType *RT = PointeeElem->getAs<RecordType>()) {
+        if (!RequireCompleteType(StartLoc, Pointee,
+                                 LangOpts.CPlusPlus26
+                                     ? diag::err_delete_incomplete
+                                     : diag::warn_delete_incomplete,
+                                 RT->isUnionType(), Ex.get())) {
           PointeeRD = cast<CXXRecordDecl>(RT->getDecl());
+        }
       }
     }
 
diff --git a/clang/test/Analysis/dtor.cpp b/clang/test/Analysis/dtor.cpp
index c17c886d97fb4..66f555cd65240 100644
--- a/clang/test/Analysis/dtor.cpp
+++ b/clang/test/Analysis/dtor.cpp
@@ -499,7 +499,7 @@ namespace PseudoDtor {
 
 namespace Incomplete {
   class Foo; // expected-note{{forward declaration}}
-  void f(Foo *foo) { delete foo; } // expected-warning{{deleting pointer to 
incomplete type}}
+  void f(Foo *foo) { delete foo; } // expected-warning{{deleting pointer to 
incomplete struct 'Foo'}}
 }
 
 namespace TypeTraitExpr {
diff --git a/clang/test/CXX/drs/cwg5xx.cpp b/clang/test/CXX/drs/cwg5xx.cpp
index 1d505adecfb27..269397a0dc221 100644
--- a/clang/test/CXX/drs/cwg5xx.cpp
+++ b/clang/test/CXX/drs/cwg5xx.cpp
@@ -907,7 +907,7 @@ namespace cwg573 { // cwg573: no
   // cxx98-error@-1 {{cast between pointer-to-function and pointer-to-object 
is an extension}}
   void f() { delete a; }
   // cxx98-23-error@-1 {{cannot delete expression with pointer-to-'void' type 
'void *'}}
-  // since-cxx26-error@-2 {{cannot delete pointer to incomplete type 'void'}}
+  // since-cxx26-error@-2 {{cannot delete expression with pointer-to-'void' 
type 'void *'}}
   int n = d - a;
   // expected-error@-1 {{arithmetic on pointers to void}}
   // FIXME: This is ill-formed.
@@ -1298,12 +1298,12 @@ namespace cwg599 { // cwg599: partial
   void f(void *p, void (*q)(), S s, T t, U u, V v) {
     delete p;
     // cxx98-23-error@-1 {{cannot delete expression with pointer-to-'void' 
type 'void *'}}
-    // since-cxx26-error@-2 {{cannot delete pointer to incomplete type 'void'}}
+    // since-cxx26-error@-2 {{cannot delete expression with pointer-to-'void' 
type 'void *'}}
     delete q;
     // expected-error@-1 {{cannot delete expression of type 'void (*)()'}}
     delete s;
     // cxx98-23-error@-1 {{cannot delete expression with pointer-to-'void' 
type 'void *'}}
-    // since-cxx26-error@-2 {{cannot delete pointer to incomplete type 'void'}}
+    // since-cxx26-error@-2 {{cannot delete expression with pointer-to-'void' 
type 'void *'}}
     delete t;
     // expected-error@-1 {{cannot delete expression of type 'T'}}
     // FIXME: This is valid, but is rejected due to a non-conforming GNU
diff --git a/clang/test/CXX/expr/expr.unary/expr.delete/p5.cpp 
b/clang/test/CXX/expr/expr.unary/expr.delete/p5.cpp
index ecb29189af60b..4e68fbab5d670 100644
--- a/clang/test/CXX/expr/expr.unary/expr.delete/p5.cpp
+++ b/clang/test/CXX/expr/expr.unary/expr.delete/p5.cpp
@@ -6,12 +6,12 @@
 
 // The trivial case.
 class T0; // expected-note {{forward declaration}}
-void f0(T0 *a) { delete a; } // expected-warning {{deleting pointer to 
incomplete type}}
+void f0(T0 *a) { delete a; } // expected-warning {{deleting pointer to 
incomplete struct 'T0'}}
 class T0 { ~T0(); };
 
 // The trivial case, inside a template instantiation.
 template<typename T>
-struct T1_A { T *x; ~T1_A() { delete x; } }; // expected-warning {{deleting 
pointer to incomplete type}}
+struct T1_A { T *x; ~T1_A() { delete x; } }; // expected-warning {{deleting 
pointer to incomplete struct 'T1_B'}}
 class T1_B; // expected-note {{forward declaration}}
 void f0() { T1_A<T1_B> x; } // expected-note {{in instantiation of member 
function}}
 
@@ -44,3 +44,8 @@ class T3_A {
 private: 
   ~T3_A(); // expected-note{{declared private here}}
 };
+
+// The trivial case but with a union.
+union T4; // expected-note {{forward declaration}}
+void f4(T4 *a) { delete a; } // expected-warning {{deleting pointer to 
incomplete union 'T4'}}
+union T4 { ~T4(); };
diff --git a/clang/test/OpenMP/deferred-diags.cpp 
b/clang/test/OpenMP/deferred-diags.cpp
index e31b99b8c88e4..a978465322e9d 100644
--- a/clang/test/OpenMP/deferred-diags.cpp
+++ b/clang/test/OpenMP/deferred-diags.cpp
@@ -41,7 +41,7 @@ namespace TestDeleteIncompleteClassDefinition {
 struct a;
 struct b {
   b() {
-    delete c; // expected-warning {{deleting pointer to incomplete type 'a' is 
incompatible with C++2c and may cause undefined behavior}}
+    delete c; // expected-warning {{deleting pointer to incomplete struct 'a' 
is incompatible with C++2c and may cause undefined behavior}}
   }
   a *c;
 };
diff --git a/clang/test/SemaCXX/new-delete.cpp 
b/clang/test/SemaCXX/new-delete.cpp
index f918501554f80..735908c5dc0bb 100644
--- a/clang/test/SemaCXX/new-delete.cpp
+++ b/clang/test/SemaCXX/new-delete.cpp
@@ -225,10 +225,10 @@ void bad_deletes()
   delete [0] (int*)0; // expected-error {{expected variable name or 'this' in 
lambda capture list}}
   delete (void*)0;
   // cxx98-23-warning@-1 {{cannot delete expression with pointer-to-'void' 
type 'void *'}}
-  // since-cxx26-error@-2 {{cannot delete pointer to incomplete type 'void'}}
+  // since-cxx26-error@-2 {{cannot delete expression with pointer-to-'void' 
type 'void *'}}
   delete (T*)0;
-  // cxx98-23-warning@-1 {{deleting pointer to incomplete type}}
-  // since-cxx26-error@-2 {{cannot delete pointer to incomplete type 'T'}}
+  // cxx98-23-warning@-1 {{deleting pointer to incomplete struct 'T'}}
+  // since-cxx26-error@-2 {{cannot delete pointer to incomplete struct 'T'}}
   ::S::delete (int*)0; // expected-error {{expected unqualified-id}}
 }
 
@@ -570,8 +570,8 @@ namespace DeleteIncompleteClassPointerError {
   struct A; // expected-note {{forward declaration}}
   void f(A *x) { 1+delete x; }
   // expected-error@-1 {{invalid operands to binary expression}}
-  // cxx98-23-warning@-2 {{deleting pointer to incomplete type}}
-  // since-cxx26-error@-3 {{cannot delete pointer to incomplete type 'A'}}
+  // cxx98-23-warning@-2 {{deleting pointer to incomplete struct 'A'}}
+  // since-cxx26-error@-3 {{cannot delete pointer to incomplete struct 'A'}}
 }
 
 namespace PR10504 {
@@ -595,6 +595,10 @@ struct GH99278_2 {
   } f;
 };
 GH99278_2<void> e;
+void GH99278_3(int(*p)[]) {
+  delete p;
+  // expected-warning@-1 {{'delete' applied to a pointer-to-array type 'int 
(*)[]' treated as 'delete[]'}}
+};
 #endif
 
 struct PlacementArg {};

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to