Hi!

The following patch implements the C++26 paper which makes delete
and delete[] on incomplete class types invalid, previously it has
been UB unless the class had trivial destructor and no custom
deallocator.

The patch uses permerror_opt, so -Wno-delete-incomplete makes it
still compile without warnings like before, and -fpermissive makes
it warn but not error; in SFINAE contexts it is considered an error
in C++26 and later.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2024-07-02  Jakub Jelinek  <ja...@redhat.com>
            Jason Merrill  <ja...@redhat.com>

        PR c++/115747
gcc/cp/
        * init.cc: Implement C++26 P3144R2 - Deleting a Pointer to an
        Incomplete Type Should be Ill-formed.
        (build_vec_delete_1): Emit permerror_at and return error_mark_node
        for delete [] on incomplete type.
        (build_delete): Similarly for delete.
gcc/testsuite/
        * g++.dg/init/delete1.C: Adjust expected diagnostics for C++26.
        * g++.dg/warn/Wdelete-incomplete-1.C: Likewise.
        * g++.dg/warn/incomplete1.C: Likewise.
        * g++.dg/ipa/pr85607.C: Likewise.
        * g++.dg/cpp26/delete1.C: New test.
        * g++.dg/cpp26/delete2.C: New test.
        * g++.dg/cpp26/delete3.C: New test.

--- gcc/cp/init.cc.jj   2024-06-04 13:19:03.706604998 +0200
+++ gcc/cp/init.cc      2024-07-02 19:16:57.596412502 +0200
@@ -4114,7 +4114,24 @@ build_vec_delete_1 (location_t loc, tree
 
   if (!COMPLETE_TYPE_P (type))
     {
-      if (complain & tf_warning)
+      if (cxx_dialect > cxx23)
+       {
+         if (complain & tf_error)
+           {
+             int saved_errorcount = errorcount;
+             if (permerror_opt (loc, OPT_Wdelete_incomplete,
+                                "operator %<delete []%> used on "
+                                "incomplete type"))
+               {
+                 cxx_incomplete_type_inform (type);
+                 if (errorcount != saved_errorcount)
+                   return error_mark_node;
+               }
+           }
+         else
+           return error_mark_node;
+       }
+      else if (complain & tf_warning)
        {
          auto_diagnostic_group d;
          if (warning_at (loc, OPT_Wdelete_incomplete,
@@ -5178,7 +5195,24 @@ build_delete (location_t loc, tree otype
 
          if (!COMPLETE_TYPE_P (type))
            {
-             if (complain & tf_warning)
+             if (cxx_dialect > cxx23)
+               {
+                 if (complain & tf_error)
+                   {
+                     int saved_errorcount = errorcount;
+                     if (permerror_opt (loc, OPT_Wdelete_incomplete,
+                                        "operator %<delete%> used on "
+                                        "incomplete type"))
+                       {
+                         cxx_incomplete_type_inform (type);
+                         if (errorcount != saved_errorcount)
+                           return error_mark_node;
+                       }
+                   }
+                 else
+                   return error_mark_node;
+               }
+             else if (complain & tf_warning)
                {
                  auto_diagnostic_group d;
                  if (warning_at (loc, OPT_Wdelete_incomplete,
--- gcc/testsuite/g++.dg/init/delete1.C.jj      2020-01-12 11:54:37.185401761 
+0100
+++ gcc/testsuite/g++.dg/init/delete1.C 2024-07-02 17:38:58.742393502 +0200
@@ -3,7 +3,8 @@
 class C; // { dg-message "7:forward" }
 
 void foo(void *p) {
-  delete [] ((C*)p) ; // { dg-warning "3:possible problem detected in 
invocation of operator .delete \\\[\\\]." }
-  // { dg-message "3:neither the destructor nor the class-specific" "note" { 
target *-*-* } .-1 }
-  // { dg-warning "invalid use of incomplete type" "" { target *-*-* } .-2 }
+  delete [] ((C*)p) ; // { dg-warning "3:possible problem detected in 
invocation of operator .delete \\\[\\\]." "" { target c++23_down } }
+  // { dg-message "3:neither the destructor nor the class-specific" "note" { 
target c++23_down } .-1 }
+  // { dg-warning "invalid use of incomplete type" "" { target c++23_down } 
.-2 }
+  // { dg-error "operator 'delete \\\[\\\]' used on incomplete type" "" { 
target c++26 } .-3 }
 }
--- gcc/testsuite/g++.dg/warn/Wdelete-incomplete-1.C.jj 2020-01-12 
11:54:37.282400298 +0100
+++ gcc/testsuite/g++.dg/warn/Wdelete-incomplete-1.C    2024-07-02 
17:37:39.326397364 +0200
@@ -2,7 +2,8 @@
 
 class Foo;         // { dg-message "7:forward declaration" }
 int main() {
-   Foo* p;         // { dg-warning "9:.p. has incomplete type" }
-   delete [] p;    // { dg-warning "4:possible problem detected in invocation 
of operator .delete \\\[\\\]." }
-   // { dg-message "4:neither the destructor nor the class-specific" "note" { 
target *-*-* } .-1 }
+   Foo* p;         // { dg-warning "9:.p. has incomplete type" "" { target 
c++23_down } }
+   delete [] p;    // { dg-warning "4:possible problem detected in invocation 
of operator .delete \\\[\\\]." "" { target c++23_down } }
+   // { dg-message "4:neither the destructor nor the class-specific" "note" { 
target c++23_down } .-1 }
+   // { dg-error "operator 'delete \\\[\\\]' used on incomplete type" "" { 
target c++26 } .-2 }
 }
--- gcc/testsuite/g++.dg/warn/incomplete1.C.jj  2020-01-12 11:54:37.290400177 
+0100
+++ gcc/testsuite/g++.dg/warn/incomplete1.C     2024-07-02 17:38:27.988782244 
+0200
@@ -11,12 +11,13 @@
 
 class A;       // { dg-message "7:forward declaration of 'class A'" }
 
-A *a;          // { dg-warning "4:'a' has incomplete type" }
+A *a;          // { dg-warning "4:'a' has incomplete type" "" { target 
c++23_down } }
 
 int
 main (int argc, char **argv)
 {
-  delete a;    // { dg-warning "3:possible problem detected in invocation of 
.operator delete." "warn" }
-  // { dg-message "3:neither the destructor nor the class-specific" "note" { 
target *-*-* } .-1 }
+  delete a;    // { dg-warning "3:possible problem detected in invocation of 
.operator delete." "warn" { target c++23_down } }
+  // { dg-message "3:neither the destructor nor the class-specific" "note" { 
target c++23_down } .-1 }
+  // { dg-error "operator 'delete' used on incomplete type" "" { target c++26 
} .-2 }
   return 0;
 }
--- gcc/testsuite/g++.dg/ipa/pr85607.C.jj       2020-01-12 11:54:37.193401641 
+0100
+++ gcc/testsuite/g++.dg/ipa/pr85607.C  2024-07-02 17:39:20.601117195 +0200
@@ -3,12 +3,13 @@
 
 class A;       // { dg-message "7:forward declaration of 'class A'" }
 
-A *a;          // { dg-warning "4:'a' has incomplete type" }
+A *a;          // { dg-warning "4:'a' has incomplete type" "" { target 
c++23_down } }
 
 int
 main (int argc, char **argv)
 {
-  delete a;    // { dg-warning "3:possible problem detected in invocation of 
.operator delete." "warn" }
-  // { dg-message "3:neither the destructor nor the class-specific" "note" { 
target *-*-* } .-1 }
+  delete a;    // { dg-warning "3:possible problem detected in invocation of 
.operator delete." "warn" { target c++23_down } }
+  // { dg-message "3:neither the destructor nor the class-specific" "note" { 
target c++23_down } .-1 }
+  // { dg-error "operator 'delete' used on incomplete type" "" { target c++26 
} .-2 }
   return 0;
 }
--- gcc/testsuite/g++.dg/cpp26/delete1.C.jj     2024-07-02 12:08:55.547027171 
+0200
+++ gcc/testsuite/g++.dg/cpp26/delete1.C        2024-07-02 17:41:14.089682637 
+0200
@@ -0,0 +1,36 @@
+// C++26 P3144R2 - Deleting a Pointer to an Incomplete Type Should be 
Ill-formed
+// { dg-do compile { target c++26 } }
+
+struct S;      // { dg-message "forward declaration of 'struct S'" }
+struct T;      // { dg-message "forward declaration of 'struct T'" }
+struct U;      // { dg-message "forward declaration of 'struct U'" }
+
+void
+foo (S *p, T *q, U *r, S *s, T *t, U *u)
+{
+  delete p;    // { dg-error "operator 'delete' used on incomplete type" }
+  delete q;    // { dg-error "operator 'delete' used on incomplete type" }
+  delete r;    // { dg-error "operator 'delete' used on incomplete type" }
+  delete[] s;  // { dg-error "operator 'delete \\\[\\\]' used on incomplete 
type" }
+  delete[] t;  // { dg-error "operator 'delete \\\[\\\]' used on incomplete 
type" }
+  delete[] u;  // { dg-error "operator 'delete \\\[\\\]' used on incomplete 
type" }
+}
+
+struct S
+{
+  int s;
+};
+
+struct T
+{
+  int t;
+  ~T () {}
+};
+
+struct U
+{
+  int u;
+  void operator delete (void *) noexcept;
+  void operator delete[] (void *) noexcept;
+};
+
--- gcc/testsuite/g++.dg/cpp26/delete2.C.jj     2024-07-02 17:39:49.549751269 
+0200
+++ gcc/testsuite/g++.dg/cpp26/delete2.C        2024-07-02 17:41:19.943608640 
+0200
@@ -0,0 +1,36 @@
+// C++26 P3144R2 - Deleting a Pointer to an Incomplete Type Should be 
Ill-formed
+// { dg-do compile { target c++26 } }
+// { dg-options "-Wno-delete-incomplete" }
+
+struct S;
+struct T;
+struct U;
+
+void
+foo (S *p, T *q, U *r, S *s, T *t, U *u)
+{
+  delete p;    // { dg-bogus "operator 'delete' used on incomplete type" }
+  delete q;    // { dg-bogus "operator 'delete' used on incomplete type" }
+  delete r;    // { dg-bogus "operator 'delete' used on incomplete type" }
+  delete[] s;  // { dg-bogus "operator 'delete \\\[\\\]' used on incomplete 
type" }
+  delete[] t;  // { dg-bogus "operator 'delete \\\[\\\]' used on incomplete 
type" }
+  delete[] u;  // { dg-bogus "operator 'delete \\\[\\\]' used on incomplete 
type" }
+}
+
+struct S
+{
+  int s;
+};
+
+struct T
+{
+  int t;
+  ~T () {}
+};
+
+struct U
+{
+  int u;
+  void operator delete (void *) noexcept;
+  void operator delete[] (void *) noexcept;
+};
--- gcc/testsuite/g++.dg/cpp26/delete3.C.jj     2024-07-02 17:41:29.026493828 
+0200
+++ gcc/testsuite/g++.dg/cpp26/delete3.C        2024-07-02 17:52:13.829368589 
+0200
@@ -0,0 +1,36 @@
+// C++26 P3144R2 - Deleting a Pointer to an Incomplete Type Should be 
Ill-formed
+// { dg-do compile { target c++26 } }
+// { dg-options "-fpermissive" }
+
+struct S;      // { dg-message "forward declaration of 'struct S'" }
+struct T;      // { dg-message "forward declaration of 'struct T'" }
+struct U;      // { dg-message "forward declaration of 'struct U'" }
+
+void
+foo (S *p, T *q, U *r, S *s, T *t, U *u)
+{
+  delete p;    // { dg-warning "operator 'delete' used on incomplete type" }
+  delete q;    // { dg-warning "operator 'delete' used on incomplete type" }
+  delete r;    // { dg-warning "operator 'delete' used on incomplete type" }
+  delete[] s;  // { dg-warning "operator 'delete \\\[\\\]' used on incomplete 
type" }
+  delete[] t;  // { dg-warning "operator 'delete \\\[\\\]' used on incomplete 
type" }
+  delete[] u;  // { dg-warning "operator 'delete \\\[\\\]' used on incomplete 
type" }
+}
+
+struct S
+{
+  int s;
+};
+
+struct T
+{
+  int t;
+  ~T () {}
+};
+
+struct U
+{
+  int u;
+  void operator delete (void *) noexcept;
+  void operator delete[] (void *) noexcept;
+};

        Jakub

Reply via email to