c++: Further tweaks for new-expression and paren-init [PR77841]
authorMarek Polacek <polacek@redhat.com>
Sun, 6 Sep 2020 00:50:32 +0000 (20:50 -0400)
committerMarek Polacek <polacek@redhat.com>
Wed, 9 Sep 2020 21:46:04 +0000 (17:46 -0400)
This patch corrects our handling of array new-expression with ()-init:

  new int[4](1, 2, 3, 4);

should work even with the explicit array bound, and

  new char[3]("so_sad");

should cause an error, but we weren't giving any.

Fixed by handling array new-expressions with ()-init in the same spot
where we deduce the array bound in array new-expression.  I'm now
always passing STRING_CSTs to build_new_1 wrapped in { } which allowed
me to remove the special handling of STRING_CSTs in build_new_1.  And
since the DIRECT_LIST_INIT_P block in build_new_1 calls digest_init, we
report errors about too short arrays. reshape_init now does the {"foo"}
-> "foo" transformation even for CONSTRUCTOR_IS_PAREN_INIT, so no need
to do it in build_new.

I took a stab at cp_complete_array_type's "FIXME: this code is duplicated
from reshape_init", but calling reshape_init there, I ran into issues
with has_designator_problem: when we reshape an already reshaped
CONSTRUCTOR again, d.cur.index has been filled, so we think that we
have a user-provided designator (though there was no designator in the
source code), and report an error.

gcc/cp/ChangeLog:

PR c++/77841
* decl.c (reshape_init): If we're initializing a char array from
a string-literal that is enclosed in braces, unwrap it.
* init.c (build_new_1): Don't handle string-initializers here.
(build_new): Handle new-expression with paren-init when the
array bound is known.  Always pass string constants to build_new_1
enclosed in braces.  Don't handle string-initializers in any
special way.

gcc/testsuite/ChangeLog:

PR c++/77841
* g++.old-deja/g++.ext/arrnew2.C: Expect the error only in C++17
and less.
* g++.old-deja/g++.robertl/eb58.C: Adjust dg-error.
* g++.old-deja/g++.robertl/eb63.C: Expect the error only in C++17
and less.
* g++.dg/cpp2a/new-array5.C: New test.
* g++.dg/cpp2a/paren-init36.C: New test.
* g++.dg/cpp2a/paren-init37.C: New test.
* g++.dg/pr84729.C: Adjust dg-error.

gcc/cp/decl.c
gcc/cp/init.c
gcc/testsuite/g++.dg/cpp2a/new-array5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/paren-init36.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/paren-init37.C [new file with mode: 0644]
gcc/testsuite/g++.dg/pr84729.C
gcc/testsuite/g++.old-deja/g++.ext/arrnew2.C
gcc/testsuite/g++.old-deja/g++.robertl/eb58.C
gcc/testsuite/g++.old-deja/g++.robertl/eb63.C

index ca1c8a4a0e6dc7c143d6c2755de797836cc6e4db..ce97d19884d240cf49b6f7334afe2eab14ab2b1a 100644 (file)
@@ -6599,7 +6599,17 @@ reshape_init (tree type, tree init, tsubst_flags_t complain)
   /* Brace elision is not performed for a CONSTRUCTOR representing
      parenthesized aggregate initialization.  */
   if (CONSTRUCTOR_IS_PAREN_INIT (init))
-    return init;
+    {
+      tree elt = (*v)[0].value;
+      /* If we're initializing a char array from a string-literal that is
+        enclosed in braces, unwrap it here.  */
+      if (TREE_CODE (type) == ARRAY_TYPE
+         && vec_safe_length (v) == 1
+         && char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type)))
+         && TREE_CODE (tree_strip_any_location_wrapper (elt)) == STRING_CST)
+       return elt;
+      return init;
+    }
 
   /* Handle [dcl.init.list] direct-list-initialization from
      single element of enumeration with a fixed underlying type.  */
index 3268ae4ad3f164e6e2f32c0cfb39f83348a847ff..e84e985492dfaddcc6a28665080af75f1f746733 100644 (file)
@@ -3596,15 +3596,6 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
                  vecinit = digest_init (arraytype, vecinit, complain);
                }
            }
-         /* This handles code like new char[]{"foo"}.  */
-         else if (len == 1
-                  && char_type_p (TYPE_MAIN_VARIANT (type))
-                  && TREE_CODE (tree_strip_any_location_wrapper ((**init)[0]))
-                     == STRING_CST)
-           {
-             vecinit = (**init)[0];
-             STRIP_ANY_LOCATION_WRAPPER (vecinit);
-           }
          else if (*init)
             {
               if (complain & tf_error)
@@ -3944,9 +3935,8 @@ build_new (location_t loc, vec<tree, va_gc> **placement, tree type,
     }
 
   /* P1009: Array size deduction in new-expressions.  */
-  if (TREE_CODE (type) == ARRAY_TYPE
-      && !TYPE_DOMAIN (type)
-      && *init)
+  const bool array_p = TREE_CODE (type) == ARRAY_TYPE;
+  if (*init && (array_p || (nelts && cxx_dialect >= cxx20)))
     {
       /* This means we have 'new T[]()'.  */
       if ((*init)->is_empty ())
@@ -3959,27 +3949,29 @@ build_new (location_t loc, vec<tree, va_gc> **placement, tree type,
       /* The C++20 'new T[](e_0, ..., e_k)' case allowed by P0960.  */
       if (!DIRECT_LIST_INIT_P (elt) && cxx_dialect >= cxx20)
        {
-         /* Handle new char[]("foo").  */
-         if (vec_safe_length (*init) == 1
-             && char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type)))
-             && TREE_CODE (tree_strip_any_location_wrapper (elt))
-                == STRING_CST)
-           /* Leave it alone: the string should not be wrapped in {}.  */;
-         else
-           {
-             tree ctor = build_constructor_from_vec (init_list_type_node, *init);
-             CONSTRUCTOR_IS_DIRECT_INIT (ctor) = true;
-             CONSTRUCTOR_IS_PAREN_INIT (ctor) = true;
-             elt = ctor;
-             /* We've squashed all the vector elements into the first one;
-                truncate the rest.  */
-             (*init)->truncate (1);
-           }
+         tree ctor = build_constructor_from_vec (init_list_type_node, *init);
+         CONSTRUCTOR_IS_DIRECT_INIT (ctor) = true;
+         CONSTRUCTOR_IS_PAREN_INIT (ctor) = true;
+         elt = ctor;
+         /* We've squashed all the vector elements into the first one;
+            truncate the rest.  */
+         (*init)->truncate (1);
        }
       /* Otherwise we should have 'new T[]{e_0, ..., e_k}'.  */
-      if (BRACE_ENCLOSED_INITIALIZER_P (elt))
-       elt = reshape_init (type, elt, complain);
-      cp_complete_array_type (&type, elt, /*do_default*/false);
+      if (array_p && !TYPE_DOMAIN (type))
+       {
+         /* We need to reshape before deducing the bounds to handle code like
+
+              struct S { int x, y; };
+              new S[]{1, 2, 3, 4};
+
+            which should deduce S[2].  But don't change ELT itself: we want to
+            pass a list-initializer to build_new_1, even for STRING_CSTs.  */
+         tree e = elt;
+         if (BRACE_ENCLOSED_INITIALIZER_P (e))
+           e = reshape_init (type, e, complain);
+         cp_complete_array_type (&type, e, /*do_default*/false);
+       }
     }
 
   /* The type allocated must be complete.  If the new-type-id was
diff --git a/gcc/testsuite/g++.dg/cpp2a/new-array5.C b/gcc/testsuite/g++.dg/cpp2a/new-array5.C
new file mode 100644 (file)
index 0000000..2379079
--- /dev/null
@@ -0,0 +1,15 @@
+// PR c++/77841
+// { dg-do compile { target c++11 } }
+
+auto p1 = new int[][1]();
+auto p2 = new int[1][1]();
+#if __cpp_aggregate_paren_init
+auto p3 = new int[][4]({1, 2}, {3, 4});
+auto p4 = new int[2][4]({1, 2}, {3, 4});
+auto p5 = new int[2][1]({1, 2}, {3}); // { dg-error "too many initializers" "" { target c++20 } }
+#endif
+
+auto b1 = new int[][1]{};
+auto b2 = new int[1][1]{};
+auto b3 = new int[][4]{{1, 2}, {3, 4}};
+auto b4 = new int[2][4]{{1, 2}, {3, 4}};
diff --git a/gcc/testsuite/g++.dg/cpp2a/paren-init36.C b/gcc/testsuite/g++.dg/cpp2a/paren-init36.C
new file mode 100644 (file)
index 0000000..9962495
--- /dev/null
@@ -0,0 +1,14 @@
+// PR c++/77841
+// { dg-do compile { target c++20 } }
+
+int *p0 = new int[1]();
+int *p1 = new int[1](1);
+int *p2 = new int[4](1, 2, 3, 4);
+int *p3 = new int[2](1, 2, 3, 4); // { dg-error "too many initializers" }
+
+char *c1 = new char[]("foo");
+char *c2 = new char[4]("foo");
+char *c3 = new char[]{"foo"};
+char *c4 = new char[4]{"foo"};
+char *c5 = new char[3]("so_sad"); // { dg-error "too long" }
+char *c6 = new char[3]{"so_sad"}; // { dg-error "too long" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/paren-init37.C b/gcc/testsuite/g++.dg/cpp2a/paren-init37.C
new file mode 100644 (file)
index 0000000..551a982
--- /dev/null
@@ -0,0 +1,14 @@
+// PR c++/77841
+// { dg-do compile { target c++20 } }
+
+int *p0 = new (int[1])();
+int *p1 = new (int[1])(1);
+int *p2 = new (int[4])(1, 2, 3, 4);
+int *p3 = new (int[2])(1, 2, 3, 4); // { dg-error "too many initializers" }
+
+char *c1 = new (char[])("foo");
+char *c2 = new (char[4])("foo");
+char *c3 = new (char[]){"foo"};
+char *c4 = new (char[4]){"foo"};
+char *c5 = new (char[3])("so_sad"); // { dg-error "too long" }
+char *c6 = new (char[3]){"so_sad"}; // { dg-error "too long" }
index e5d689e0460c813e9b5bc0f8eec0e7442e10d79f..530dbff23e14e1e2358531e356ff6dac7d5ef058 100644 (file)
@@ -3,5 +3,5 @@
 
 typedef int b[2];
 void a() {
-  new b(a); // { dg-error "parenthesized initializer in array new" }
+  new b(a); // { dg-error "parenthesized initializer in array new|invalid conversion" }
 }
index aff6b9c7c63b4e1e19e57d000635e34ec1c70a98..fceb95e9ee57695a548a66cf130960120a312ba1 100644 (file)
@@ -1,7 +1,7 @@
 // { dg-do compile }
 // { dg-options "-w -fpermissive" }
 
-int *foo = new int[1](42); // { dg-error "parenthesized" }
+int *foo = new int[1](42); // { dg-error "parenthesized" "" { target c++17_down } }
 int main ()
 {
   return foo[0] != 42;
index d702296bdc7810ef3b7e76e53364b83640783540..1e51e14c51db62d947c1c2ac0e03c7fb947add85 100644 (file)
@@ -11,5 +11,5 @@ private:
 
 main()
 {
-  A *list = new A[10](4); // { dg-error "parenthesized" }
+  A *list = new A[10](4); // { dg-error "parenthesized|could not convert" }
 }
index 653351b8dfad9297e5489d29fd90617caa080fa7..50cf30f28fccfabcc9b32950f9e227cb1c56b88e 100644 (file)
@@ -13,5 +13,5 @@ public:
 main() {
         A* a;
 
-        a = new A[2](1,false); // { dg-error "parenthesized" }
+        a = new A[2](1,false); // { dg-error "parenthesized" "" { target c++17_down } }
 }