Hi! For array new and -fexceptions, we only try to build cleanup if TYPE_HAS_NONTRIVIAL_DESTRUCTOR and so don't complain if the array element has trivial but deleted destructor.
The following patch changes it to build the dtor whenever type_build_dtor_call but only registers it as cleanup if TYPE_HAS_NONTRIVIAL_DESTRUCTOR. build_vec_delete_1 has a special case for these type_build_dtor_call && !TYPE_HAS_NONTRIVIAL_DESTRUCTOR cases where it does less work. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? Though, I wonder if we also shouldn't test whether the ctor isn't noexcept, then we wouldn't have to change the new4.C test. Though, clang++ rejects that as well even when it has noexcept ctor. 2025-12-09 Jakub Jelinek <[email protected]> PR c++/123030 * init.cc (build_vec_init): Call build_vec_delete_1 for -fexceptions even if just type_build_dtor_call, not only when TYPE_HAS_NONTRIVIAL_DESTRUCTOR. But register cleanups only for TYPE_HAS_NONTRIVIAL_DESTRUCTOR. * g++.dg/cpp0x/deleted18.C: New test. * g++.dg/cpp0x/new4.C: Expect an error. --- gcc/cp/init.cc.jj 2025-10-08 17:46:23.000000000 +0200 +++ gcc/cp/init.cc 2025-12-08 14:51:52.602538854 +0100 @@ -4785,11 +4785,12 @@ build_vec_init (tree base, tree maxindex /* Protect the entire array initialization so that we can destroy the partially constructed array if an exception is thrown. But don't do this if we're assigning. */ - if (flag_exceptions && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type) + if (flag_exceptions /* And don't clean up from clobbers, the actual initialization will follow as a separate build_vec_init. */ && !(init && TREE_CLOBBER_P (init)) - && from_array != 2) + && from_array != 2 + && type_build_dtor_call (type)) { tree e; tree m = cp_build_binary_op (input_location, @@ -4812,24 +4813,28 @@ build_vec_init (tree base, tree maxindex /*in_cleanup*/true); if (e == error_mark_node) errors = true; - TARGET_EXPR_CLEANUP (iterator_targ) = e; - CLEANUP_EH_ONLY (iterator_targ) = true; + if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type)) + { + TARGET_EXPR_CLEANUP (iterator_targ) = e; + CLEANUP_EH_ONLY (iterator_targ) = true; - /* Since we push this cleanup before doing any initialization, cleanups - for any temporaries in the initialization are naturally within our - cleanup region, so we don't want wrap_temporary_cleanups to do - anything for arrays. But if the array is a subobject, we need to - tell split_nonconstant_init or cp_genericize_target_expr how to turn - off this cleanup in favor of the cleanup for the complete object. + /* Since we push this cleanup before doing any initialization, + cleanups for any temporaries in the initialization are naturally + within our cleanup region, so we don't want + wrap_temporary_cleanups to do anything for arrays. But if the + array is a subobject, we need to tell split_nonconstant_init or + cp_genericize_target_expr how to turn off this cleanup in favor + of the cleanup for the complete object. - ??? For an array temporary such as an initializer_list backing array, - it would avoid redundancy to leave this cleanup active, clear - CLEANUP_EH_ONLY, and not build another cleanup for the temporary - itself. But that breaks when gimplify_target_expr adds a clobber - cleanup that runs before the build_vec_init cleanup. */ - if (cleanup_flags) - vec_safe_push (*cleanup_flags, - build_tree_list (rval, build_zero_cst (ptype))); + ??? For an array temporary such as an initializer_list backing + array, it would avoid redundancy to leave this cleanup active, + clear CLEANUP_EH_ONLY, and not build another cleanup for the + temporary itself. But that breaks when gimplify_target_expr adds + a clobber cleanup that runs before the build_vec_init cleanup. */ + if (cleanup_flags) + vec_safe_push (*cleanup_flags, + build_tree_list (rval, build_zero_cst (ptype))); + } } /* Should we try to create a constant initializer? */ --- gcc/testsuite/g++.dg/cpp0x/deleted18.C.jj 2025-12-08 14:57:56.924273735 +0100 +++ gcc/testsuite/g++.dg/cpp0x/deleted18.C 2025-12-08 14:56:56.628310620 +0100 @@ -0,0 +1,20 @@ +// PR c++/123030 +// { dg-do compile { target c++11 } } + +int n; +struct Y { + Y () { if (++n == 2) throw 42; } + ~Y () = delete; // { dg-message "declared here" } +}; + +int +main () +{ + try + { + new Y[2]; // { dg-error "use of deleted function 'Y::~Y\\\(\\\)'" } + } + catch (...) + { + } +} --- gcc/testsuite/g++.dg/cpp0x/new4.C.jj 2020-12-09 09:03:38.277054575 +0100 +++ gcc/testsuite/g++.dg/cpp0x/new4.C 2025-12-09 10:34:06.498584909 +0100 @@ -3,7 +3,7 @@ struct A { ~A () = delete; }; A *pa{new A{}}; -A *pa2{new A[2]{}}; +A *pa2{new A[2]{}}; // { dg-error "use of deleted function 'A::~A\\\(\\\)'" } class B { ~B () = default; }; B *pb{new B{}}; Jakub
