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

Reply via email to