On 12/9/25 5:50 PM, Jakub Jelinek wrote:
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))
Maybe "else if (TREE_SIDE_EFFECTS (e))"?
OK with that change.
+ {
+ 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