On Mon, Sep 07, 2020 at 11:19:47PM -0400, Jason Merrill wrote:
> On 9/6/20 11:34 AM, Marek Polacek wrote:
> > @@ -3944,9 +3935,9 @@ 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 deduce_array_p = (TREE_CODE (type) == ARRAY_TYPE
> > +                          && !TYPE_DOMAIN (type));
> > +  if (*init && (deduce_array_p || (nelts && cxx_dialect >= cxx20)))
> 
> Looks like this won't handle new (char[4]), for which we also get an
> ARRAY_TYPE.

Good catch.  Fixed & paren-init37.C added.

> >       {
> >         /* This means we have 'new T[]()'.  */
> >         if ((*init)->is_empty ())
> > @@ -3955,16 +3946,20 @@ build_new (location_t loc, vec<tree, va_gc> 
> > **placement, tree type,
> >       CONSTRUCTOR_IS_DIRECT_INIT (ctor) = true;
> >       vec_safe_push (*init, ctor);
> >     }
> > +      tree array_type = deduce_array_p ? TREE_TYPE (type) : type;
> 
> I'd call this variable elt_type.

Right, and it should be inside the block below.

> >         tree &elt = (**init)[0];
> >         /* 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").  */
> > +     /* Handle new char[]("foo"): turn it into new char[]{"foo"}.  */
> >       if (vec_safe_length (*init) == 1
> > -         && char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type)))
> > +         && char_type_p (TYPE_MAIN_VARIANT (array_type))
> >           && TREE_CODE (tree_strip_any_location_wrapper (elt))
> >              == STRING_CST)
> > -       /* Leave it alone: the string should not be wrapped in {}.  */;
> > +       {
> > +         elt = build_constructor_single (init_list_type_node, NULL_TREE, 
> > elt);
> > +         CONSTRUCTOR_IS_DIRECT_INIT (elt) = true;
> > +       }
> >       else
> >         {
> >           tree ctor = build_constructor_from_vec (init_list_type_node, 
> > *init);
> 
> With this change, doesn't the string special case produce the same result as
> the general case?

The problem is that reshape_init won't do anything for 
CONSTRUCTOR_IS_PAREN_INIT.
So the reshape_init in build_new_1 wouldn't unwrap the outermost { } around
a STRING_CST.

Perhaps reshape_init should be adjusted to do that unwrapping even when it gets
a CONSTRUCTOR_IS_PAREN_INIT CONSTRUCTOR.  But I'm not sure if it should also do
the reference_related_p unwrapping in reshape_init_r in that case.

> > @@ -3977,9 +3972,15 @@ build_new (location_t loc, vec<tree, va_gc> 
> > **placement, tree type,
> >         }
> >     }
> >         /* 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 (deduce_array_p)
> > +   {
> > +     /* Don't reshape 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);
> 
> The comment is unclear; this call does reshape the CONSTRUCTOR ELT points
> to, it just doesn't change ELT if the reshape call returns something else.

Yea, I've amended the comment.

> Why are we reshaping here, anyway?  Won't that lead to undesired brace
> elision?

We have to reshape before deducing the array, otherwise we could deduce the
wrong number of elements when certain braces were omitted.  E.g. in

  struct S { int x, y; };
  new S[]{1, 2, 3, 4}; // braces elided, is { {1, 2}, {3, 4} }

we want S[2], not S[4].  A way to test it would be

  struct S { int x, y; };
  S *p = new S[]{1, 2, 3, 4};

  void* operator new (unsigned long int size)
  {
      if (size != sizeof (S) * 2)
        __builtin_abort ();
      return __builtin_malloc (size);
  }

  int main () { }

I can add that too, if you want.  (It'd be safer if cp_complete_array_type
always reshaped but that's not trivial, as the original patch mentions.)
()-init-list wouldn't be reshaped because CONSTRUCTOR_IS_PAREN_INIT is set.

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

Thanks,

-- >8 --
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.

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
        * 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.

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/paren-init36.C: New test.
        * g++.dg/cpp2a/paren-init37.C: New test.
        * g++.dg/pr84729.C: Adjust dg-error.
---
 gcc/cp/init.c                                 | 41 +++++++++++--------
 gcc/testsuite/g++.dg/cpp2a/paren-init36.C     | 14 +++++++
 gcc/testsuite/g++.dg/cpp2a/paren-init37.C     | 14 +++++++
 gcc/testsuite/g++.dg/pr84729.C                |  2 +-
 gcc/testsuite/g++.old-deja/g++.ext/arrnew2.C  |  2 +-
 gcc/testsuite/g++.old-deja/g++.robertl/eb58.C |  2 +-
 gcc/testsuite/g++.old-deja/g++.robertl/eb63.C |  2 +-
 7 files changed, 55 insertions(+), 22 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/paren-init36.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/paren-init37.C

diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 3268ae4ad3f..fe4d49df402 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -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,12 +3949,16 @@ 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").  */
+         tree elt_type = array_p ? TREE_TYPE (type) : type;
+         /* Handle new char[]("foo"): turn it into new char[]{"foo"}.  */
          if (vec_safe_length (*init) == 1
-             && char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type)))
+             && char_type_p (TYPE_MAIN_VARIANT (elt_type))
              && TREE_CODE (tree_strip_any_location_wrapper (elt))
                 == STRING_CST)
-           /* Leave it alone: the string should not be wrapped in {}.  */;
+           {
+             elt = build_constructor_single (init_list_type_node, NULL_TREE, 
elt);
+             CONSTRUCTOR_IS_DIRECT_INIT (elt) = true;
+           }
          else
            {
              tree ctor = build_constructor_from_vec (init_list_type_node, 
*init);
@@ -3977,9 +3971,20 @@ build_new (location_t loc, vec<tree, va_gc> **placement, 
tree type,
            }
        }
       /* 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/paren-init36.C 
b/gcc/testsuite/g++.dg/cpp2a/paren-init36.C
new file mode 100644
index 00000000000..996249515bf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/paren-init36.C
@@ -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
index 00000000000..551a9822224
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/paren-init37.C
@@ -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/pr84729.C b/gcc/testsuite/g++.dg/pr84729.C
index e5d689e0460..530dbff23e1 100644
--- a/gcc/testsuite/g++.dg/pr84729.C
+++ b/gcc/testsuite/g++.dg/pr84729.C
@@ -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" }
 }
diff --git a/gcc/testsuite/g++.old-deja/g++.ext/arrnew2.C 
b/gcc/testsuite/g++.old-deja/g++.ext/arrnew2.C
index aff6b9c7c63..fceb95e9ee5 100644
--- a/gcc/testsuite/g++.old-deja/g++.ext/arrnew2.C
+++ b/gcc/testsuite/g++.old-deja/g++.ext/arrnew2.C
@@ -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;
diff --git a/gcc/testsuite/g++.old-deja/g++.robertl/eb58.C 
b/gcc/testsuite/g++.old-deja/g++.robertl/eb58.C
index d702296bdc7..1e51e14c51d 100644
--- a/gcc/testsuite/g++.old-deja/g++.robertl/eb58.C
+++ b/gcc/testsuite/g++.old-deja/g++.robertl/eb58.C
@@ -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" }
 }
diff --git a/gcc/testsuite/g++.old-deja/g++.robertl/eb63.C 
b/gcc/testsuite/g++.old-deja/g++.robertl/eb63.C
index 653351b8dfa..50cf30f28fc 100644
--- a/gcc/testsuite/g++.old-deja/g++.robertl/eb63.C
+++ b/gcc/testsuite/g++.old-deja/g++.robertl/eb63.C
@@ -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 } }
 }

base-commit: 87603e565615db055f7f60db0c9888f71d233826
-- 
2.26.2

Reply via email to