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