https://gcc.gnu.org/g:beb7a418aaef2ec8a812712110b007c091a73491
commit r15-1794-gbeb7a418aaef2ec8a812712110b007c091a73491 Author: Jakub Jelinek <ja...@redhat.com> Date: Tue Jul 2 22:08:45 2024 +0200 c++: Implement C++26 P3144R2 - Deleting a Pointer to an Incomplete Type Should be Ill-formed [PR115747] 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. 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. Diff: --- gcc/cp/init.cc | 38 ++++++++++++++++++++++-- gcc/testsuite/g++.dg/cpp26/delete1.C | 36 ++++++++++++++++++++++ gcc/testsuite/g++.dg/cpp26/delete2.C | 36 ++++++++++++++++++++++ gcc/testsuite/g++.dg/cpp26/delete3.C | 36 ++++++++++++++++++++++ gcc/testsuite/g++.dg/init/delete1.C | 7 +++-- gcc/testsuite/g++.dg/ipa/pr85607.C | 7 +++-- gcc/testsuite/g++.dg/warn/Wdelete-incomplete-1.C | 7 +++-- gcc/testsuite/g++.dg/warn/incomplete1.C | 7 +++-- 8 files changed, 160 insertions(+), 14 deletions(-) diff --git a/gcc/cp/init.cc b/gcc/cp/init.cc index 4a7ed7f5302..826a31c4a84 100644 --- a/gcc/cp/init.cc +++ b/gcc/cp/init.cc @@ -4114,7 +4114,24 @@ build_vec_delete_1 (location_t loc, tree base, tree maxindex, tree type, 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, tree addr, 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, diff --git a/gcc/testsuite/g++.dg/cpp26/delete1.C b/gcc/testsuite/g++.dg/cpp26/delete1.C new file mode 100644 index 00000000000..ca7766af1b3 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/delete1.C @@ -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; +}; + diff --git a/gcc/testsuite/g++.dg/cpp26/delete2.C b/gcc/testsuite/g++.dg/cpp26/delete2.C new file mode 100644 index 00000000000..c5a6acf0c44 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/delete2.C @@ -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; +}; diff --git a/gcc/testsuite/g++.dg/cpp26/delete3.C b/gcc/testsuite/g++.dg/cpp26/delete3.C new file mode 100644 index 00000000000..d6702b7d678 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp26/delete3.C @@ -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; +}; diff --git a/gcc/testsuite/g++.dg/init/delete1.C b/gcc/testsuite/g++.dg/init/delete1.C index 617c7ba8a10..f7c6257cfe4 100644 --- a/gcc/testsuite/g++.dg/init/delete1.C +++ b/gcc/testsuite/g++.dg/init/delete1.C @@ -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 } } diff --git a/gcc/testsuite/g++.dg/ipa/pr85607.C b/gcc/testsuite/g++.dg/ipa/pr85607.C index 9f619096152..51000c05c30 100644 --- a/gcc/testsuite/g++.dg/ipa/pr85607.C +++ b/gcc/testsuite/g++.dg/ipa/pr85607.C @@ -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; } diff --git a/gcc/testsuite/g++.dg/warn/Wdelete-incomplete-1.C b/gcc/testsuite/g++.dg/warn/Wdelete-incomplete-1.C index d0c40e23db9..8be3446c06a 100644 --- a/gcc/testsuite/g++.dg/warn/Wdelete-incomplete-1.C +++ b/gcc/testsuite/g++.dg/warn/Wdelete-incomplete-1.C @@ -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 } } diff --git a/gcc/testsuite/g++.dg/warn/incomplete1.C b/gcc/testsuite/g++.dg/warn/incomplete1.C index aa44c7ba3fb..f9e2edd224b 100644 --- a/gcc/testsuite/g++.dg/warn/incomplete1.C +++ b/gcc/testsuite/g++.dg/warn/incomplete1.C @@ -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; }