c++: Further tweaks for new-expression and paren-init [PR77841]

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.
This commit is contained in:
Marek Polacek 2020-09-05 20:50:32 -04:00
parent acbe30bbc8
commit 81de459ec7
9 changed files with 81 additions and 36 deletions

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

View 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

View File

@ -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}};

View File

@ -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" }

View File

@ -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" }

View 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" }
}

View 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;

View 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" }
}

View 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 } }
}