C++ PATCH for c++/91363 - P0960R3: Parenthesized initialization of aggregates

Message ID 20191022145308.GI2922@redhat.com
State New
Headers show
Series
  • C++ PATCH for c++/91363 - P0960R3: Parenthesized initialization of aggregates
Related show

Commit Message

Marek Polacek Oct. 22, 2019, 2:53 p.m.
This patch implements C++20 P0960R3: Parenthesized initialization of aggregates
(<wg21.link/p0960>; see R0 for more background info).  Essentially, if you have
an aggregate, you can now initialize it by (x, y), similarly to {x, y}.  E.g.

  struct A {
    int x, y;
    // no A(int, int) ctor (see paren-init14.C for = delete; case)
  };
  A a(1, 2);

The difference between ()-init and {}-init is that narrowing conversions are
permitted, designators are not permitted, a temporary object bound to
a reference does not have its lifetime extended, and there is no brace elision.
Further, things like

  int a[](1, 2, 3); // will deduce the array size
  const A& r(1, 2.3, 3); // narrowing is OK
  int (&&rr)[](1, 2, 3);
  int b[3](1, 2); // b[2] will be value-initialized

now work as expected.  Note that

  char f[]("fluff");

has always worked and this patch keeps it that way.  Also note that A a((1, 2))
is not the same as A a{{1,2}}; the inner (1, 2) remains a COMPOUND_EXPR.

The approach I took was to handle (1, 2) similarly to {1, 2} -- conjure up
a CONSTRUCTOR, and introduce LOOKUP_AGGREGATE_PAREN_INIT to distinguish
between the two.  This kind of initialization is only supported in C++20;
I've made no attempt to support it in earlier standards, like we don't
support CTAD pre-C++17, for instance.

I heard rumors that a NB comment said that perhaps this proposal should be
reverted (?).  I hope having an implementation of it will help to gauge the
impact of this change.  But it shouldn't break existing code; it can only
make previously invalid code to compile now.

Bootstrapped/regtested on x86_64-linux, built Boost and cmcstl2.

Ok?

2019-10-22  Marek Polacek  <polacek@redhat.com>

	PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
	* c-cppbuiltin.c (c_cpp_builtins): Predefine
	__cpp_aggregate_paren_init=201902 for -std=c++2a.

	* call.c (build_array_conv): Set rvaluedness_matches_p when
	LOOKUP_AGGREGATE_PAREN_INIT.
	(implicit_conversion): Don't reset LOOKUP_AGGREGATE_PAREN_INIT.
	(convert_like_real) <case ck_aggr>: Maybe use
	LOOKUP_AGGREGATE_PAREN_INIT when digesting the initializer.
	(build_new_method_call_1): Handle parenthesized initialization of
	aggregates by building up a CONSTRUCTOR.
	(perform_direct_initialization_if_possible): Use
	LOOKUP_AGGREGATE_PAREN_INIT when calling build_special_member_call.  If
	it returned a BRACE_ENCLOSED_INITIALIZER_P, call
	finish_compound_literal.
	* cp-tree.h (LOOKUP_AGGREGATE_PAREN_INIT): Define.  Add
	fcl_functional_paren.
	* decl.c (grok_reference_init): Handle aggregate initialization from
	a parenthesized list of values.
	(check_initializer): Handle initialization of an array from a
	a parenthesized list of values.  If build_aggr_init_full_exprs
	returned a BRACE_ENCLOSED_INITIALIZER_P, handle it and set
	LOOKUP_AGGREGATE_PAREN_INIT.
	* init.c (perform_member_init): Handle the case when a member
	initializer initializes an aggregate from a parenthesized list of
	values.
	(expand_default_init): Set LOOKUP_AGGREGATE_PAREN_INIT for the
	build_special_member_call call.  Set TREE_SIDE_EFFECTS if it returns
	a BRACE_ENCLOSED_INITIALIZER_P.
	* semantics.c (finish_compound_literal): Set LOOKUP_AGGREGATE_PAREN_INIT
	when fcl_functional_paren.
	* typeck2.c (store_init_value): Don't extend_ref_init_temps when
	LOOKUP_AGGREGATE_PAREN_INIT.
	(digest_init_r): Allow narrowing when LOOKUP_AGGREGATE_PAREN_INIT.
	Change gcc_unreachable to gcc_assert and don't crash when
	LOOKUP_AGGREGATE_PAREN_INIT is on, return error_mark_node instead.
	(massage_init_elt): Don't lose LOOKUP_AGGREGATE_PAREN_INIT when passing
	flags to digest_init_r.
	(build_functional_cast): Use LOOKUP_AGGREGATE_PAREN_INIT when calling
	build_special_member_call.  If it returned a
	BRACE_ENCLOSED_INITIALIZER_P, call finish_compound_literal.

	* g++.dg/cpp0x/constexpr-99.C: Only expect an error in C++17 and
	lesser.
	* g++.dg/cpp0x/explicit7.C: Likewise.
	* g++.dg/cpp0x/initlist12.C: Adjust dg-error.
	* g++.dg/cpp0x/pr31437.C: Likewise.
	* g++.dg/cpp2a/feat-cxx2a.C: Add __cpp_aggregate_paren_init test.
	* g++.dg/cpp2a/paren-init1.C: New test.
	* g++.dg/cpp2a/paren-init10.C: New test.
	* g++.dg/cpp2a/paren-init11.C: New test.
	* g++.dg/cpp2a/paren-init12.C: New test.
	* g++.dg/cpp2a/paren-init13.C: New test.
	* g++.dg/cpp2a/paren-init14.C: New test.
	* g++.dg/cpp2a/paren-init15.C: New test.
	* g++.dg/cpp2a/paren-init16.C: New test.
	* g++.dg/cpp2a/paren-init2.C: New test.
	* g++.dg/cpp2a/paren-init3.C: New test.
	* g++.dg/cpp2a/paren-init4.C: New test.
	* g++.dg/cpp2a/paren-init5.C: New test.
	* g++.dg/cpp2a/paren-init6.C: New test.
	* g++.dg/cpp2a/paren-init7.C: New test.
	* g++.dg/cpp2a/paren-init8.C: New test.
	* g++.dg/cpp2a/paren-init9.C: New test.
	* g++.dg/ext/desig10.C: Adjust dg-error.
	* g++.dg/template/crash107.C: Likewise.
	* g++.dg/template/crash95.C: Likewise.
	* g++.old-deja/g++.law/ctors11.C: Only expect an error in C++17 and
	lesser.
	* g++.old-deja/g++.law/ctors9.C: Likewise.
	* g++.old-deja/g++.mike/net22.C: Likewise.
	* g++.old-deja/g++.niklas/t128.C: Likewise.

Comments

Jason Merrill Nov. 1, 2019, 8:37 p.m. | #1
On 10/22/19 10:53 AM, Marek Polacek wrote:
> This patch implements C++20 P0960R3: Parenthesized initialization of aggregates

> (<wg21.link/p0960>; see R0 for more background info).  Essentially, if you have

> an aggregate, you can now initialize it by (x, y), similarly to {x, y}.  E.g.

> 

>    struct A {

>      int x, y;

>      // no A(int, int) ctor (see paren-init14.C for = delete; case)

>    };

>    A a(1, 2);

> 

> The difference between ()-init and {}-init is that narrowing conversions are

> permitted, designators are not permitted, a temporary object bound to

> a reference does not have its lifetime extended, and there is no brace elision.

> Further, things like

> 

>    int a[](1, 2, 3); // will deduce the array size

>    const A& r(1, 2.3, 3); // narrowing is OK

>    int (&&rr)[](1, 2, 3);

>    int b[3](1, 2); // b[2] will be value-initialized

> 

> now work as expected.  Note that

> 

>    char f[]("fluff");

> 

> has always worked and this patch keeps it that way.  Also note that A a((1, 2))

> is not the same as A a{{1,2}}; the inner (1, 2) remains a COMPOUND_EXPR.

> 

> The approach I took was to handle (1, 2) similarly to {1, 2} -- conjure up

> a CONSTRUCTOR, and introduce LOOKUP_AGGREGATE_PAREN_INIT to distinguish

> between the two.  This kind of initialization is only supported in C++20;

> I've made no attempt to support it in earlier standards, like we don't

> support CTAD pre-C++17, for instance.


Could we use a flag on the CONSTRUCTOR to distinguish between them 
instead, rather than a LOOKUP flag and a flag in the conversion?

Jason
Marek Polacek Nov. 1, 2019, 8:42 p.m. | #2
On Fri, Nov 01, 2019 at 04:37:15PM -0400, Jason Merrill wrote:
> On 10/22/19 10:53 AM, Marek Polacek wrote:

> > This patch implements C++20 P0960R3: Parenthesized initialization of aggregates

> > (<wg21.link/p0960>; see R0 for more background info).  Essentially, if you have

> > an aggregate, you can now initialize it by (x, y), similarly to {x, y}.  E.g.

> > 

> >    struct A {

> >      int x, y;

> >      // no A(int, int) ctor (see paren-init14.C for = delete; case)

> >    };

> >    A a(1, 2);

> > 

> > The difference between ()-init and {}-init is that narrowing conversions are

> > permitted, designators are not permitted, a temporary object bound to

> > a reference does not have its lifetime extended, and there is no brace elision.

> > Further, things like

> > 

> >    int a[](1, 2, 3); // will deduce the array size

> >    const A& r(1, 2.3, 3); // narrowing is OK

> >    int (&&rr)[](1, 2, 3);

> >    int b[3](1, 2); // b[2] will be value-initialized

> > 

> > now work as expected.  Note that

> > 

> >    char f[]("fluff");

> > 

> > has always worked and this patch keeps it that way.  Also note that A a((1, 2))

> > is not the same as A a{{1,2}}; the inner (1, 2) remains a COMPOUND_EXPR.

> > 

> > The approach I took was to handle (1, 2) similarly to {1, 2} -- conjure up

> > a CONSTRUCTOR, and introduce LOOKUP_AGGREGATE_PAREN_INIT to distinguish

> > between the two.  This kind of initialization is only supported in C++20;

> > I've made no attempt to support it in earlier standards, like we don't

> > support CTAD pre-C++17, for instance.

> 

> Could we use a flag on the CONSTRUCTOR to distinguish between them instead,

> rather than a LOOKUP flag and a flag in the conversion?


I think I tried and it didn't play well with digest_init and narrowing.
If we have { 2.0 } then when recursing on the CONSTRUCTOR element we
don't know it came from a paren-init, so a LOOKUP flag was necessary.

Marek
Jason Merrill Nov. 1, 2019, 9:08 p.m. | #3
On 11/1/19 4:42 PM, Marek Polacek wrote:
> On Fri, Nov 01, 2019 at 04:37:15PM -0400, Jason Merrill wrote:

>> On 10/22/19 10:53 AM, Marek Polacek wrote:

>>> This patch implements C++20 P0960R3: Parenthesized initialization of aggregates

>>> (<wg21.link/p0960>; see R0 for more background info).  Essentially, if you have

>>> an aggregate, you can now initialize it by (x, y), similarly to {x, y}.  E.g.

>>>

>>>     struct A {

>>>       int x, y;

>>>       // no A(int, int) ctor (see paren-init14.C for = delete; case)

>>>     };

>>>     A a(1, 2);

>>>

>>> The difference between ()-init and {}-init is that narrowing conversions are

>>> permitted, designators are not permitted, a temporary object bound to

>>> a reference does not have its lifetime extended, and there is no brace elision.

>>> Further, things like

>>>

>>>     int a[](1, 2, 3); // will deduce the array size

>>>     const A& r(1, 2.3, 3); // narrowing is OK

>>>     int (&&rr)[](1, 2, 3);

>>>     int b[3](1, 2); // b[2] will be value-initialized

>>>

>>> now work as expected.  Note that

>>>

>>>     char f[]("fluff");

>>>

>>> has always worked and this patch keeps it that way.  Also note that A a((1, 2))

>>> is not the same as A a{{1,2}}; the inner (1, 2) remains a COMPOUND_EXPR.

>>>

>>> The approach I took was to handle (1, 2) similarly to {1, 2} -- conjure up

>>> a CONSTRUCTOR, and introduce LOOKUP_AGGREGATE_PAREN_INIT to distinguish

>>> between the two.  This kind of initialization is only supported in C++20;

>>> I've made no attempt to support it in earlier standards, like we don't

>>> support CTAD pre-C++17, for instance.

>>

>> Could we use a flag on the CONSTRUCTOR to distinguish between them instead,

>> rather than a LOOKUP flag and a flag in the conversion?

> 

> I think I tried and it didn't play well with digest_init and narrowing.

> If we have { 2.0 } then when recursing on the CONSTRUCTOR element we

> don't know it came from a paren-init, so a LOOKUP flag was necessary.


Ah, good point.  Perhaps process_init_constructor could add that flag 
based on the CONSTRUCTOR flag rather than use rvaluedness_matches_p? 
That ought to avoid a few of the changes.  And extend_ref_init_temps 
could check it rather than change store_init_value.

It also looks like you're using the LOOKUP flag to mean two different 
things:

1) try to treat parenthesized args as an aggregate initializer 
(build_new_method_call_1)
2) treat this CONSTRUCTOR as coming from parenthesized args 
(store_init_value/digest_init)

Why is the flag needed for #1?  When do we not want to try to treat the 
args as an aggregate initializer?

Jason
Marek Polacek Nov. 19, 2019, 1:44 a.m. | #4
On Fri, Nov 01, 2019 at 05:08:04PM -0400, Jason Merrill wrote:
> On 11/1/19 4:42 PM, Marek Polacek wrote:

> > On Fri, Nov 01, 2019 at 04:37:15PM -0400, Jason Merrill wrote:

> > > On 10/22/19 10:53 AM, Marek Polacek wrote:

> > > > This patch implements C++20 P0960R3: Parenthesized initialization of aggregates

> > > > (<wg21.link/p0960>; see R0 for more background info).  Essentially, if you have

> > > > an aggregate, you can now initialize it by (x, y), similarly to {x, y}.  E.g.

> > > > 

> > > >     struct A {

> > > >       int x, y;

> > > >       // no A(int, int) ctor (see paren-init14.C for = delete; case)

> > > >     };

> > > >     A a(1, 2);

> > > > 

> > > > The difference between ()-init and {}-init is that narrowing conversions are

> > > > permitted, designators are not permitted, a temporary object bound to

> > > > a reference does not have its lifetime extended, and there is no brace elision.

> > > > Further, things like

> > > > 

> > > >     int a[](1, 2, 3); // will deduce the array size

> > > >     const A& r(1, 2.3, 3); // narrowing is OK

> > > >     int (&&rr)[](1, 2, 3);

> > > >     int b[3](1, 2); // b[2] will be value-initialized

> > > > 

> > > > now work as expected.  Note that

> > > > 

> > > >     char f[]("fluff");

> > > > 

> > > > has always worked and this patch keeps it that way.  Also note that A a((1, 2))

> > > > is not the same as A a{{1,2}}; the inner (1, 2) remains a COMPOUND_EXPR.

> > > > 

> > > > The approach I took was to handle (1, 2) similarly to {1, 2} -- conjure up

> > > > a CONSTRUCTOR, and introduce LOOKUP_AGGREGATE_PAREN_INIT to distinguish

> > > > between the two.  This kind of initialization is only supported in C++20;

> > > > I've made no attempt to support it in earlier standards, like we don't

> > > > support CTAD pre-C++17, for instance.

> > > 

> > > Could we use a flag on the CONSTRUCTOR to distinguish between them instead,

> > > rather than a LOOKUP flag and a flag in the conversion?

> > 

> > I think I tried and it didn't play well with digest_init and narrowing.

> > If we have { 2.0 } then when recursing on the CONSTRUCTOR element we

> > don't know it came from a paren-init, so a LOOKUP flag was necessary.

> 

> Ah, good point.  Perhaps process_init_constructor could add that flag based

> on the CONSTRUCTOR flag rather than use rvaluedness_matches_p? That ought to

> avoid a few of the changes.  And extend_ref_init_temps could check it rather

> than change store_init_value.


f you're fine with a new CONSTRUCTOR flag, sure.  Done in this patch.

> It also looks like you're using the LOOKUP flag to mean two different

> things:

> 

> 1) try to treat parenthesized args as an aggregate initializer

> (build_new_method_call_1)

> 2) treat this CONSTRUCTOR as coming from parenthesized args

> (store_init_value/digest_init)


Correct.

> Why is the flag needed for #1?  When do we not want to try to treat the args

> as an aggregate initializer?


There are cases where treating the args as an aggregate initializer causes
spurious overload resolution successes, e.g.

  void swap(int&, int&);

  int& get();

  struct pair {
    void swap(pair&) noexcept(noexcept(swap(get(), get()))) { } // { dg-error "no matching function for call" }
  };

There are no viable candidates for pair::swap (# of args mismatch) but since
pair is an aggregate, build_new_method_call_1 would return a CONSTRUCTOR so
overload resolution would succeed.  Another case had to do with SFINAE and
decltype where we didn't evaluate the arg, but succeeding in the
no-viable-function case caused the compiler to choose the wrong function.

There were a bunch of concepts-*.C failures too but I don't know what was
going on there.

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

2019-11-18  Marek Polacek  <polacek@redhat.com>

	PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
	* c-cppbuiltin.c (c_cpp_builtins): Predefine
	__cpp_aggregate_paren_init=201902 for -std=c++2a.

	* call.c (implicit_conversion): Don't reset LOOKUP_AGGREGATE_PAREN_INIT.
	(convert_like_real) <case ck_aggr>: Maybe use
	LOOKUP_AGGREGATE_PAREN_INIT when digesting the initializer.
	(build_new_method_call_1): Handle parenthesized initialization of
	aggregates by building up a CONSTRUCTOR.
	(perform_direct_initialization_if_possible): Use
	LOOKUP_AGGREGATE_PAREN_INIT when calling build_special_member_call.  If
	it returned a BRACE_ENCLOSED_INITIALIZER_P, call
	finish_compound_literal.
	(extend_ref_init_temps): Do nothing for CONSTRUCTOR_IS_PAREN_INIT.
	* cp-tree.h (LOOKUP_AGGREGATE_PAREN_INIT, CONSTRUCTOR_IS_PAREN_INIT):
	Define.  Add fcl_functional_paren.
	* decl.c (grok_reference_init): Handle aggregate initialization from
	a parenthesized list of values.
	(reshape_init): Set CONSTRUCTOR_IS_PAREN_INIT.
	(check_initializer): Handle initialization of an array from a
	a parenthesized list of values.  If build_aggr_init_full_exprs
	returned a BRACE_ENCLOSED_INITIALIZER_P, handle it and set
	LOOKUP_AGGREGATE_PAREN_INIT.
	* init.c (perform_member_init): Handle the case when a member
	initializer initializes an aggregate from a parenthesized list of
	values.
	(expand_default_init): Set LOOKUP_AGGREGATE_PAREN_INIT for the
	build_special_member_call call.  Set TREE_SIDE_EFFECTS if it returns
	a BRACE_ENCLOSED_INITIALIZER_P.
	* semantics.c (finish_compound_literal): Set LOOKUP_AGGREGATE_PAREN_INIT
	when fcl_functional_paren.
	* typeck2.c (digest_init_r): Allow narrowing when
	LOOKUP_AGGREGATE_PAREN_INIT.
	Change gcc_unreachable to gcc_assert and don't crash when
	LOOKUP_AGGREGATE_PAREN_INIT is on, return error_mark_node instead.
	(massage_init_elt): Don't lose LOOKUP_AGGREGATE_PAREN_INIT when passing
	flags to digest_init_r.
	(build_functional_cast): Use LOOKUP_AGGREGATE_PAREN_INIT when calling
	build_special_member_call.  If it returned a
	BRACE_ENCLOSED_INITIALIZER_P, call finish_compound_literal.

	* g++.dg/cpp0x/constexpr-99.C: Only expect an error in C++17 and
	lesser.
	* g++.dg/cpp0x/explicit7.C: Likewise.
	* g++.dg/cpp0x/initlist12.C: Adjust dg-error.
	* g++.dg/cpp0x/pr31437.C: Likewise.
	* g++.dg/cpp2a/feat-cxx2a.C: Add __cpp_aggregate_paren_init test.
	* g++.dg/cpp2a/paren-init1.C: New test.
	* g++.dg/cpp2a/paren-init10.C: New test.
	* g++.dg/cpp2a/paren-init11.C: New test.
	* g++.dg/cpp2a/paren-init12.C: New test.
	* g++.dg/cpp2a/paren-init13.C: New test.
	* g++.dg/cpp2a/paren-init14.C: New test.
	* g++.dg/cpp2a/paren-init15.C: New test.
	* g++.dg/cpp2a/paren-init16.C: New test.
	* g++.dg/cpp2a/paren-init2.C: New test.
	* g++.dg/cpp2a/paren-init3.C: New test.
	* g++.dg/cpp2a/paren-init4.C: New test.
	* g++.dg/cpp2a/paren-init5.C: New test.
	* g++.dg/cpp2a/paren-init6.C: New test.
	* g++.dg/cpp2a/paren-init7.C: New test.
	* g++.dg/cpp2a/paren-init8.C: New test.
	* g++.dg/cpp2a/paren-init9.C: New test.
	* g++.dg/ext/desig10.C: Adjust dg-error.
	* g++.dg/template/crash107.C: Likewise.
	* g++.dg/template/crash95.C: Likewise.
	* g++.old-deja/g++.law/ctors11.C: Only expect an error in C++17 and
	lesser.
	* g++.old-deja/g++.law/ctors9.C: Likewise.
	* g++.old-deja/g++.mike/net22.C: Likewise.
	* g++.old-deja/g++.niklas/t128.C: Likewise.

diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c
index 76d1e4a380e..e9c269caf24 100644
--- gcc/c-family/c-cppbuiltin.c
+++ gcc/c-family/c-cppbuiltin.c
@@ -996,6 +996,7 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_impl_destroying_delete=201806L");
 	  cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907L");
 	  cpp_define (pfile, "__cpp_impl_three_way_comparison=201907L");
+	  cpp_define (pfile, "__cpp_aggregate_paren_init=201902L");
 	}
       if (flag_concepts)
         {
diff --git gcc/cp/call.c gcc/cp/call.c
index 13639a1c901..d82dae5656f 100644
--- gcc/cp/call.c
+++ gcc/cp/call.c
@@ -1950,7 +1950,8 @@ implicit_conversion (tree to, tree from, tree expr, bool c_cast_p,
      resolution, or after we've chosen one.  */
   flags &= (LOOKUP_ONLYCONVERTING|LOOKUP_NO_CONVERSION|LOOKUP_COPY_PARM
 	    |LOOKUP_NO_TEMP_BIND|LOOKUP_NO_RVAL_BIND|LOOKUP_PREFER_RVALUE
-	    |LOOKUP_NO_NARROWING|LOOKUP_PROTECT|LOOKUP_NO_NON_INTEGRAL);
+	    |LOOKUP_NO_NARROWING|LOOKUP_PROTECT|LOOKUP_NO_NON_INTEGRAL
+	    |LOOKUP_AGGREGATE_PAREN_INIT);
 
   /* FIXME: actually we don't want warnings either, but we can't just
      have 'complain &= ~(tf_warning|tf_error)' because it would cause
@@ -7508,7 +7509,14 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	  return expr;
 	}
       expr = reshape_init (totype, expr, complain);
-      expr = get_target_expr_sfinae (digest_init (totype, expr, complain),
+      /* We want an implicit lookup, but when initializing an aggregate
+	 from a parenthesized list, we must remember not to warn about
+	 narrowing conversions.  */
+      flags = LOOKUP_IMPLICIT;
+      if (CONSTRUCTOR_IS_PAREN_INIT (expr))
+	flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+      expr = get_target_expr_sfinae (digest_init_flags (totype, expr,
+							flags, complain),
 				     complain);
       if (expr != error_mark_node)
 	TARGET_EXPR_LIST_INIT_P (expr) = true;
@@ -10099,6 +10107,28 @@ build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args,
 
   if (!any_viable_p)
     {
+      /* [dcl.init], 17.6.2.2:
+
+	 Otherwise, if no constructor is viable, the destination type is
+	 a (possibly cv-qualified) aggregate class A, and the initializer
+	 is a parenthesized expression-list, the object is initialized as
+	 follows...
+
+	 We achieve this by building up a CONSTRUCTOR, as for list-init,
+	 and setting LOOKUP_AGGREGATE_PAREN_INIT to distinguish between
+	 the two.  */
+      if ((flags & LOOKUP_AGGREGATE_PAREN_INIT)
+	  && cxx_dialect >= cxx2a
+	  && CP_AGGREGATE_TYPE_P (basetype)
+	  && !user_args->is_empty ())
+	{
+	  /* Create a CONSTRUCTOR from ARGS, e.g. {1, 2} from <1, 2>.  */
+	  tree list = build_tree_list_vec (user_args);
+	  tree ctor = build_constructor_from_list (init_list_type_node, list);
+	  CONSTRUCTOR_IS_DIRECT_INIT (ctor) = true;
+	  CONSTRUCTOR_IS_PAREN_INIT (ctor) = true;
+	  return ctor;
+	}
       if (complain & tf_error)
 	complain_about_no_candidates_for_method_call (instance, candidates,
 						      explicit_targs, basetype,
@@ -11775,14 +11805,28 @@ perform_direct_initialization_if_possible (tree type,
      If the destination type is a (possibly cv-qualified) class type:
 
      -- If the initialization is direct-initialization ...,
-     constructors are considered. ... If no constructor applies, or
-     the overload resolution is ambiguous, the initialization is
-     ill-formed.  */
+     constructors are considered.
+
+       -- If overload resolution is successful, the selected constructor
+       is called to initialize the object, with the initializer expression
+       or expression-list as its argument(s).
+
+       -- Otherwise, if no constructor is viable, the destination type is
+       a (possibly cv-qualified) aggregate class A, and the initializer is
+       a parenthesized expression-list, the object is initialized as
+       follows...  */
   if (CLASS_TYPE_P (type))
     {
       releasing_vec args (make_tree_vector_single (expr));
+      int flags = LOOKUP_NORMAL | LOOKUP_AGGREGATE_PAREN_INIT;
       expr = build_special_member_call (NULL_TREE, complete_ctor_identifier,
-					&args, type, LOOKUP_NORMAL, complain);
+					&args, type, flags, complain);
+      if (BRACE_ENCLOSED_INITIALIZER_P (expr))
+	{
+	  gcc_assert (cxx_dialect >= cxx2a);
+	  return finish_compound_literal (type, expr, complain,
+					  fcl_functional_paren);
+	}
       return build_cplus_new (type, expr, complain);
     }
 
@@ -12133,6 +12177,12 @@ extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups)
 	ctor = TARGET_EXPR_INITIAL (ctor);
       if (TREE_CODE (ctor) == CONSTRUCTOR)
 	{
+	  /* [dcl.init] When initializing an aggregate from a parenthesized list
+	     of values... a temporary object bound to a reference does not have
+	     its lifetime extended.  */
+	  if (CONSTRUCTOR_IS_PAREN_INIT (ctor))
+	    return init;
+
 	  if (is_std_init_list (type))
 	    {
 	      /* The temporary array underlying a std::initializer_list
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index d6e9357385a..871b58b69bc 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -4325,6 +4325,11 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define CONSTRUCTOR_IS_DESIGNATED_INIT(NODE) \
   (TREE_LANG_FLAG_6 (CONSTRUCTOR_CHECK (NODE)))
 
+/* True if this CONSTRUCTOR comes from a parenthesized list of values, e.g.
+   A(1, 2, 3).  */
+#define CONSTRUCTOR_IS_PAREN_INIT(NODE) \
+  (CONSTRUCTOR_CHECK(NODE)->base.private_flag)
+
 /* True if NODE represents a conversion for direct-initialization in a
    template.  Set by perform_implicit_conversion_flags.  */
 #define IMPLICIT_CONV_EXPR_DIRECT_INIT(NODE) \
@@ -5583,6 +5588,8 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
    args), then we swap the conversions back in build_new_op_1 (so they
    correspond to the order of the args in the candidate).  */
 #define LOOKUP_REVERSED (LOOKUP_REWRITTEN << 1)
+/* We're initializing an aggregate from a parenthesized list of values.  */
+#define LOOKUP_AGGREGATE_PAREN_INIT (LOOKUP_REVERSED << 1)
 
 #define LOOKUP_NAMESPACES_ONLY(F)  \
   (((F) & LOOKUP_PREFER_NAMESPACES) && !((F) & LOOKUP_PREFER_TYPES))
@@ -7160,8 +7167,9 @@ extern cp_expr finish_unary_op_expr		(location_t, enum tree_code, cp_expr,
 						 tsubst_flags_t);
 /* Whether this call to finish_compound_literal represents a C++11 functional
    cast or a C99 compound literal.  */
-enum fcl_t { fcl_functional, fcl_c99 };
-extern tree finish_compound_literal		(tree, tree, tsubst_flags_t, fcl_t = fcl_functional);
+enum fcl_t { fcl_functional, fcl_functional_paren, fcl_c99 };
+extern tree finish_compound_literal		(tree, tree, tsubst_flags_t,
+						 fcl_t = fcl_functional);
 extern tree finish_fname			(tree);
 extern void finish_translation_unit		(void);
 extern tree finish_template_type_parm		(tree, tree);
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 86e38f4af69..812e6894df8 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -5531,11 +5531,29 @@ grok_reference_init (tree decl, tree type, tree init, int flags)
       return NULL_TREE;
     }
 
+  tree ttype = TREE_TYPE (type);
   if (TREE_CODE (init) == TREE_LIST)
-    init = build_x_compound_expr_from_list (init, ELK_INIT,
-					    tf_warning_or_error);
+    {
+      /* This handles (C++20 only) code like
+
+	   const A& r(1, 2, 3);
+
+	 where we treat the parenthesized list as a CONSTRUCTOR.  */
+      if (TREE_TYPE (init) == NULL_TREE
+	  && CP_AGGREGATE_TYPE_P (ttype)
+	  && !DECL_DECOMPOSITION_P (decl)
+	  && (cxx_dialect >= cxx2a))
+	{
+	  init = build_constructor_from_list (init_list_type_node, init);
+	  CONSTRUCTOR_IS_DIRECT_INIT (init) = true;
+	  CONSTRUCTOR_IS_PAREN_INIT (init) = true;
+	  flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+	}
+      else
+	init = build_x_compound_expr_from_list (init, ELK_INIT,
+						tf_warning_or_error);
+    }
 
-  tree ttype = TREE_TYPE (type);
   if (TREE_CODE (ttype) != ARRAY_TYPE
       && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE)
     /* Note: default conversion is only called in very special cases.  */
@@ -6476,6 +6494,9 @@ reshape_init (tree type, tree init, tsubst_flags_t complain)
   if (CONSTRUCTOR_IS_DESIGNATED_INIT (init)
       && BRACE_ENCLOSED_INITIALIZER_P (new_init))
     CONSTRUCTOR_IS_DESIGNATED_INIT (new_init) = true;
+  if (CONSTRUCTOR_IS_PAREN_INIT (init)
+      && BRACE_ENCLOSED_INITIALIZER_P (new_init))
+    CONSTRUCTOR_IS_PAREN_INIT (new_init) = true;
 
   return new_init;
 }
@@ -6640,6 +6661,42 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
 	      flags |= LOOKUP_NO_NARROWING;
 	    }
 	}
+      /* [dcl.init] "Otherwise, if the destination type is an array, the object
+	 is initialized as follows..."  So handle things like
+
+	  int a[](1, 2, 3);
+
+	 which is permitted in C++20 by P0960.  */
+      else if (TREE_CODE (init) == TREE_LIST
+	       && TREE_TYPE (init) == NULL_TREE
+	       && TREE_CODE (type) == ARRAY_TYPE
+	       && !DECL_DECOMPOSITION_P (decl)
+	       && (cxx_dialect >= cxx2a))
+	{
+	  /* [dcl.init.string] "An array of ordinary character type [...]
+	     can be initialized by an ordinary string literal [...] by an
+	     appropriately-typed string literal enclosed in braces" only
+	     talks about braces, but GCC has always accepted
+
+	       char a[]("foobar");
+
+	     so we continue to do so.  */
+	  tree val = TREE_VALUE (init);
+	  if (TREE_CHAIN (init) == NULL_TREE
+	      && char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type)))
+	      && TREE_CODE (tree_strip_any_location_wrapper (val))
+		 == STRING_CST)
+	    /* If the list has a single element and it's a string literal,
+	       then it's the initializer for the array as a whole.  */
+	    init = val;
+	  else
+	    {
+	      init = build_constructor_from_list (init_list_type_node, init);
+	      CONSTRUCTOR_IS_DIRECT_INIT (init) = true;
+	      CONSTRUCTOR_IS_PAREN_INIT (init) = true;
+	      flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+	    }
+	}
       else if (TREE_CODE (init) == TREE_LIST
 	       && TREE_TYPE (init) != unknown_type_node
 	       && !MAYBE_CLASS_TYPE_P (type))
@@ -6704,6 +6761,14 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
 		}
 	      init_code = NULL_TREE;
 	    }
+	  else if (BRACE_ENCLOSED_INITIALIZER_P (init_code))
+	    {
+	      /* In C++20, the call to build_aggr_init could have created
+		 a CONSTRUCTOR to handle A(1, 2).  */
+	      gcc_assert (cxx_dialect >= cxx2a);
+	      init = init_code;
+	      flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+	    }
 	  else
 	    init = NULL_TREE;
 	}
diff --git gcc/cp/init.c gcc/cp/init.c
index c620b05fe07..03385377ed9 100644
--- gcc/cp/init.c
+++ gcc/cp/init.c
@@ -921,8 +921,33 @@ perform_member_init (tree member, tree init)
 		inform (DECL_SOURCE_LOCATION (member),
 			"%q#D should be initialized", member );
 	    }
-	  finish_expr_stmt (build_aggr_init (decl, init, flags,
-					     tf_warning_or_error));
+	  init = build_aggr_init (decl, init, flags, tf_warning_or_error);
+	  /* In C++20, a member initializer list can be initializing an
+	     aggregate from a parenthesized list of values:
+
+	       struct S {
+		 A aggr;
+		 S() : aggr(1, 2, 3) { }
+	       };
+
+	      Build up an INIT_EXPR like we do for aggr{1, 2, 3}, so that
+	      build_data_member_initialization can grok it.  */
+	  if (cxx_dialect >= cxx2a)
+	    {
+	      tree t = init;
+	      while (TREE_CODE (t) == EXPR_STMT
+		     || TREE_CODE (t) == CONVERT_EXPR)
+		t = TREE_OPERAND (t, 0);
+	      if (BRACE_ENCLOSED_INITIALIZER_P (t))
+		{
+		  t = digest_init_flags (type, t,
+					 (LOOKUP_IMPLICIT
+					  | LOOKUP_AGGREGATE_PAREN_INIT),
+					 tf_warning_or_error);
+		  init = build2 (INIT_EXPR, type, decl, t);
+		}
+	    }
+	  finish_expr_stmt (init);
 	}
     }
   else
@@ -1967,8 +1992,22 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
       tree ctor_name = (true_exp == exp
 			? complete_ctor_identifier : base_ctor_identifier);
 
+      /* Given class A,
+
+	   A a(1, 2);
+
+	 can mean a call to a constructor A::A(int, int), if present.  If not,
+	 but A is an aggregate, we will try aggregate initialization.  */
+      flags |= LOOKUP_AGGREGATE_PAREN_INIT;
       rval = build_special_member_call (exp, ctor_name, &parms, binfo, flags,
 					complain);
+      if (BRACE_ENCLOSED_INITIALIZER_P (rval))
+	{
+	  gcc_assert (cxx_dialect >= cxx2a);
+	  /* So that we do finish_expr_stmt below.  Don't return here, we
+	     need to release PARMS.  */
+	  TREE_SIDE_EFFECTS (rval) = 1;
+	}
     }
 
   if (parms != NULL)
diff --git gcc/cp/semantics.c gcc/cp/semantics.c
index 0ce73af5bc6..fc2e002aa26 100644
--- gcc/cp/semantics.c
+++ gcc/cp/semantics.c
@@ -2956,8 +2956,10 @@ finish_compound_literal (tree type, tree compound_literal,
       if (type == error_mark_node)
 	return error_mark_node;
     }
-  compound_literal = digest_init_flags (type, compound_literal,
-					LOOKUP_NORMAL | LOOKUP_NO_NARROWING,
+  int flags = LOOKUP_NORMAL | LOOKUP_NO_NARROWING;
+  if (fcl_context == fcl_functional_paren)
+    flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+  compound_literal = digest_init_flags (type, compound_literal, flags,
 					complain);
   if (compound_literal == error_mark_node)
     return error_mark_node;
diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
index 9fb36fd1ed3..9e44adc2a44 100644
--- gcc/cp/typeck2.c
+++ gcc/cp/typeck2.c
@@ -1224,7 +1224,9 @@ digest_init_r (tree type, tree init, int nested, int flags,
   if ((code != COMPLEX_TYPE || BRACE_ENCLOSED_INITIALIZER_P (stripped_init))
       && (SCALAR_TYPE_P (type) || code == REFERENCE_TYPE))
     {
-      if (nested)
+      /* Narrowing is OK when initializing an aggregate from
+	 a parenthesized list.  */
+      if (nested && !(flags & LOOKUP_AGGREGATE_PAREN_INIT))
 	flags |= LOOKUP_NO_NARROWING;
       init = convert_for_initialization (0, type, init, flags,
 					 ICR_INIT, NULL_TREE, 0,
@@ -1272,8 +1274,13 @@ digest_init_r (tree type, tree init, int nested, int flags,
 		inform (loc, "remove %<{ }%> around initializer");
 	    }
 	  else if (flag_checking)
-	    /* We should have fixed this in reshape_init.  */
-	    gcc_unreachable ();
+	    /* We should have fixed this in reshape_init.  Except that we
+	       don't reshape parenthesized lists where brace elision is
+	       not permitted.  */
+	    {
+	      gcc_assert (flags & LOOKUP_AGGREGATE_PAREN_INIT);
+	      return error_mark_node;
+	    }
 	}
     }
 
@@ -1384,9 +1391,12 @@ static tree
 massage_init_elt (tree type, tree init, int nested, int flags,
 		  tsubst_flags_t complain)
 {
-  flags &= LOOKUP_ALLOW_FLEXARRAY_INIT;
-  flags |= LOOKUP_IMPLICIT;
-  init = digest_init_r (type, init, nested ? 2 : 1, flags, complain);
+  int new_flags = LOOKUP_IMPLICIT;
+  if (flags & LOOKUP_ALLOW_FLEXARRAY_INIT)
+    new_flags |= LOOKUP_ALLOW_FLEXARRAY_INIT;
+  if (flags & LOOKUP_AGGREGATE_PAREN_INIT)
+    new_flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+  init = digest_init_r (type, init, nested ? 2 : 1, new_flags, complain);
   /* Strip a simple TARGET_EXPR when we know this is an initializer.  */
   if (SIMPLE_TARGET_EXPR_P (init))
     init = TARGET_EXPR_INITIAL (init);
@@ -2343,10 +2353,17 @@ build_functional_cast (tree exp, tree parms, tsubst_flags_t complain)
   releasing_vec parmvec;
   for (; parms != NULL_TREE; parms = TREE_CHAIN (parms))
     vec_safe_push (parmvec, TREE_VALUE (parms));
+  int flags = LOOKUP_NORMAL | LOOKUP_AGGREGATE_PAREN_INIT;
   exp = build_special_member_call (NULL_TREE, complete_ctor_identifier,
-				   &parmvec, type, LOOKUP_NORMAL, complain);
+				   &parmvec, type, flags, complain);
 
-  if (exp == error_mark_node)
+  if (BRACE_ENCLOSED_INITIALIZER_P (exp))
+    {
+      gcc_assert (cxx_dialect >= cxx2a);
+      return finish_compound_literal (type, exp, complain,
+				      fcl_functional_paren);
+    }
+  else if (exp == error_mark_node)
     return error_mark_node;
 
   return build_cplus_new (type, exp, complain);
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-99.C gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
index 8d791dd032d..4d3953d0983 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
@@ -9,5 +9,6 @@ struct A
 struct B
 {
   A a;
-    constexpr B() : a(0) {} // { dg-error "no matching function" }
+  // P0960R3 allows paren-init.
+  constexpr B() : a(0) {} // { dg-error "no matching function" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/explicit7.C gcc/testsuite/g++.dg/cpp0x/explicit7.C
index 574796d117d..67b50542bc3 100644
--- gcc/testsuite/g++.dg/cpp0x/explicit7.C
+++ gcc/testsuite/g++.dg/cpp0x/explicit7.C
@@ -10,7 +10,7 @@ struct A { };
 struct B: A { };
 struct C {
   explicit operator B*();	// { dg-message "explicit" }
-  explicit operator B&();	// { dg-message "explicit" }
+  explicit operator B&();	// { dg-message "explicit" "" { target c++17_down } }
 };
 
 C c;
diff --git gcc/testsuite/g++.dg/cpp0x/initlist12.C gcc/testsuite/g++.dg/cpp0x/initlist12.C
index f72a6dac525..c2a2b86520f 100644
--- gcc/testsuite/g++.dg/cpp0x/initlist12.C
+++ gcc/testsuite/g++.dg/cpp0x/initlist12.C
@@ -6,15 +6,15 @@ struct A
   int i;
 };
 
-A a({1,2});			// { dg-error "no match" }
+A a({1,2});			// { dg-error "no match|cannot convert" }
 
 union U
 {
   int i,j;
 };
 
-U u({1,2});			// { dg-error "no match" }
+U u({1,2});			// { dg-error "no match|cannot convert" }
 
 union V {};
 
-V v({1});			// { dg-error "no match" }
+V v({1});			// { dg-error "no match|too many initializers" }
diff --git gcc/testsuite/g++.dg/cpp0x/pr31437.C gcc/testsuite/g++.dg/cpp0x/pr31437.C
index 7e2ed7a1375..532b533c8d3 100644
--- gcc/testsuite/g++.dg/cpp0x/pr31437.C
+++ gcc/testsuite/g++.dg/cpp0x/pr31437.C
@@ -1,9 +1,9 @@
 // { dg-do compile { target c++11 } }
-template <typename... T> struct A // { dg-message "candidates|A" }
+template <typename... T> struct A // { dg-message "candidates|A" "" { target c++17_down } }
 {
   A(T* p) {  // { dg-error "parameter packs|T" }
    (A<T...>*)(p); 
   }
 };
 
-A<int> a(0); // { dg-error "no matching" }
+A<int> a(0); // { dg-error "no matching|too many initializers" }
diff --git gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
index 95251c2f5c6..736207aa635 100644
--- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
+++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
@@ -460,6 +460,12 @@
 #  error "__cpp_constexpr_dynamic_alloc != 201907"
 #endif
 
+#ifndef __cpp_aggregate_paren_init
+#  error "__cpp_aggregate_paren_init"
+#elif __cpp_aggregate_paren_init != 201902
+#  error "__cpp_aggregate_paren_init != 201902"
+#endif
+
 #ifdef __has_cpp_attribute
 
 #  if ! __has_cpp_attribute(maybe_unused)
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init1.C gcc/testsuite/g++.dg/cpp2a/paren-init1.C
new file mode 100644
index 00000000000..a54b2ccaf6d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init1.C
@@ -0,0 +1,116 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+struct A {
+  int i;
+  int j;
+  int k;
+};
+
+struct B {
+  int i;
+  int j;
+  int k = 42;
+};
+
+struct C {
+  A a;
+};
+
+struct D {
+  A a1;
+  A a2;
+};
+
+struct E {
+  int i;
+};
+
+// F has a base class, but it's not virtual, private, or protected, so this is
+// still an aggregate (since C++17).
+struct F : E {
+  int j;
+  int k;
+};
+
+F f({1}, 2, 3);
+
+// A non-virtual member function doesn't make it a non-aggregate.
+struct G {
+  int i;
+  double j;
+  int foo(int, int);
+};
+
+G g(1, 2.14);
+
+class H {
+public:
+  H (int) { }
+};
+
+class I : public H { };
+
+int i;
+A a1(1, 2);
+A a2(1.0, 2);
+A a3(++i, ++i);
+const A& ra(1, 2);
+
+A ca = A(1, 2);
+A ca2 = A(1.0, 2);
+A ca3 = A(++i, ++i);
+const A& rca = A(1, 2);
+
+B b1(1, 2, 3);
+B b2(1, 2);
+B b3(1);
+
+C c1({5, 6, 7});
+D d1({1, 2, 3}, {5, 6, 7});
+
+struct W {
+  const char *s, *t;
+};
+W w1("fluffer", "nutter");
+
+struct Y {
+  char a[4];
+};
+Y y("yew");
+
+int
+main ()
+{
+  I(10);
+
+  // A::k will be value-initialized.
+  if (a1.i != 1 || a1.j != 2 || a1.k != 0)
+    __builtin_abort ();
+  if (a2.i != 1 || a2.j != 2 || a2.k != 0)
+    __builtin_abort ();
+  if (a3.i != 1 || a3.j != 2 || a3.k != 0)
+    __builtin_abort ();
+  if (ra.i != 1 || ra.j != 2 || ra.k != 0)
+    __builtin_abort ();
+  if (ca.i != 1 || ca.j != 2 || ca.k != 0)
+    __builtin_abort ();
+  if (ca2.i != 1 || ca2.j != 2 || ca2.k != 0)
+    __builtin_abort ();
+  if (ca3.i != 3 || ca3.j != 4 || ca3.k != 0)
+    __builtin_abort ();
+
+  if (b1.i != 1 || b1.j != 2 || b1.k != 3)
+    __builtin_abort ();
+  // The default member initializer will be used for B::k.
+  if (b2.i != 1 || b2.j != 2 || b2.k != 42)
+    __builtin_abort ();
+  if (b3.i != 1 || b3.j != 0 || b3.k != 42)
+    __builtin_abort ();
+
+  if (c1.a.i != 5 || c1.a.j != 6 || c1.a.k != 7)
+    __builtin_abort ();
+
+  if (f.i != 1 || f.j != 2 || f.k != 3)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init10.C gcc/testsuite/g++.dg/cpp2a/paren-init10.C
new file mode 100644
index 00000000000..5c70d9d59ee
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init10.C
@@ -0,0 +1,18 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test from [dcl.init].
+
+struct A {
+  int a;
+  int&& r;
+};
+
+int f();
+int n = 10;
+
+A a1{1, f()};               // OK, lifetime is extended
+A a2(1, f());               // well-formed, but dangling reference
+A a3{1.0, 1};               // { dg-error "narrowing conversion" }
+A a4(1.0, 1);               // well-formed, but dangling reference
+A a5(1.0, static_cast<int&&>(n));    // OK
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init11.C gcc/testsuite/g++.dg/cpp2a/paren-init11.C
new file mode 100644
index 00000000000..82ca2669545
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init11.C
@@ -0,0 +1,88 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test ill-formed code.
+
+// If k is greater than the size of the array, the program is ill-formed.
+int a1[2](1, 2, 3); // { dg-error "too many initializers" }
+int a2[](); // { dg-error "array of functions" }
+int a3[](()); // { dg-error "expected primary-expression" }
+int a4[]("raccoon"); // { dg-error "invalid conversion" }
+
+struct S {
+  int i;
+  int j;
+};
+
+S s1(1, 1, 1); // { dg-error "too many initializers" }
+
+union U2 {
+  int a;
+  float b;
+};
+
+// [dcl.init.aggr]/19:
+// When a union is initialized with an initializer list, there shall not be
+// more than one explicitly initialized element.
+U2 u4 = U2(1, 2); // { dg-error "too many initializers" }
+
+// Test there is no brace elision.
+
+int a[2][2](1, 2, 3, 4); // { dg-error "too many initializers|array must be initialized with a brace-enclosed initializer" }
+
+// private/protected/virtual base class -> not an aggregate.
+struct B { };
+struct D : private B {
+  int i;
+  int j;
+};
+
+D d({}, 1, 2); // { dg-error "no matching function" }
+
+// Private non-static data member -> not an aggregate.
+struct P {
+  int i;
+private:
+  int j;
+};
+
+P p(1, 2); // { dg-error "no matching function" }
+
+// User-declared constructor -> not an aggregate.
+struct U {
+  U() {}
+  int i;
+  int j;  
+};
+
+U u(1, 2); // { dg-error "no matching function" }
+
+// virtual member function -> not an aggregate.
+struct V {
+  int i;
+  int j;
+  virtual int foo(int);
+};
+
+V v(1, 2); // { dg-error "no matching function" }
+
+struct nonaggr {
+  int i;
+  int j;
+private:
+  int x;
+};
+
+struct F {
+  nonaggr n;
+  F() : n(1) { } // { dg-error "no matching function" }
+};
+
+struct G {
+  char a[4];
+};
+
+struct H {
+  G g;
+  H() : g("oaks") { } // { dg-error "initializer-string" }
+};
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init12.C gcc/testsuite/g++.dg/cpp2a/paren-init12.C
new file mode 100644
index 00000000000..d7be6f2b55d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init12.C
@@ -0,0 +1,17 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct A;
+
+struct C {
+  operator A();
+};
+
+struct A {
+  C c;
+};
+
+C c;
+A a(c);  // invokes C’s conversion function to A
+
+// { dg-final { scan-assembler "_ZN1Ccv1AEv" } }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init13.C gcc/testsuite/g++.dg/cpp2a/paren-init13.C
new file mode 100644
index 00000000000..4b9107c70ff
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init13.C
@@ -0,0 +1,16 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct X { int a, b; };
+struct Y { X x; };
+
+void
+f()
+{
+  // This is ok...
+  Y y1{{1, 2}};
+  Y y2({1, 2});
+  // ...but Y y((1,2)) is not the same as Y y{{1,2}}.  (1, 2) is a
+  // COMPOUND_EXPR.
+  Y y3((1, 2)); // { dg-error "could not convert" }
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init14.C gcc/testsuite/g++.dg/cpp2a/paren-init14.C
new file mode 100644
index 00000000000..837f9531260
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init14.C
@@ -0,0 +1,10 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++11 } }
+
+struct A {
+  int x;
+  int y;
+  A(int, int) = delete;
+};
+
+A a(1, 2); // { dg-error "use of deleted function" }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init15.C gcc/testsuite/g++.dg/cpp2a/paren-init15.C
new file mode 100644
index 00000000000..7464b1cc78a
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init15.C
@@ -0,0 +1,35 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct A {
+  int i;
+  int j;
+};
+
+struct B : A
+{
+  B (): A(1.7, 2) { }
+};
+
+void f(A);
+
+void
+g ()
+{
+  f (A(1, 2));
+}
+
+struct S {
+  int a[2];
+};
+
+S h() { return S(1, 2.2); }
+
+struct Z {
+  int i;
+  int j;
+  operator A();
+};
+
+Z z;
+A a = Z(1, 2.3);
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init16.C gcc/testsuite/g++.dg/cpp2a/paren-init16.C
new file mode 100644
index 00000000000..a59cbf5e20e
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init16.C
@@ -0,0 +1,14 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct S { int a[2]; };
+struct A { S s[1]; };
+
+template <typename, int N>
+struct R { static constexpr auto h = A(S{N}); };
+
+template <typename, int N>
+struct R2 { static constexpr auto h = A(S({N, N})); };
+
+A foo = R<int, 10>::h;
+A foo2 = R2<int, 10>::h;
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init2.C gcc/testsuite/g++.dg/cpp2a/paren-init2.C
new file mode 100644
index 00000000000..e9e90d7acb6
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init2.C
@@ -0,0 +1,56 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+struct A {
+  int i = 0;
+  int j = 0;
+};
+
+struct B {
+  A a;
+  constexpr B() : a(1.1, 2) { }
+};
+
+struct C {
+  int i;
+};
+
+struct E {
+  C c;
+  E() : c(1.2) { }
+};
+
+struct F {
+  char a[4];
+};
+
+struct G {
+  F f;
+  G() : f("yew") { }
+};
+
+struct H {
+  int i;
+  int &&r;
+};
+
+int f() { return 42; }
+
+struct I {
+  H h;
+  I() : h(1, f()) { }
+};
+
+I i;  // dangling ref to f():
+      // {.i=1, .r=(int &) &TARGET_EXPR <D.2118, f ()>}
+
+int
+main ()
+{
+  B b;
+  if (b.a.i != 1 || b.a.j != 2)
+    __builtin_abort ();
+  E e;
+  if (e.c.i != 1)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init3.C gcc/testsuite/g++.dg/cpp2a/paren-init3.C
new file mode 100644
index 00000000000..f444005a09f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init3.C
@@ -0,0 +1,11 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+class a {
+  int b{};
+};
+class c {
+  c();
+  a d;
+};
+c::c() {}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init4.C gcc/testsuite/g++.dg/cpp2a/paren-init4.C
new file mode 100644
index 00000000000..f8c7bd10b63
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init4.C
@@ -0,0 +1,142 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+// Test T[]().
+
+int i;
+int a1[](1, 2, 3);
+static_assert(sizeof(a1) == 3 * sizeof (int), "");
+int a2[](1.0, 2, 3);
+static_assert(sizeof(a2) == 3 * sizeof (int), "");
+int a3[3](1, 2, 3);
+int a4[3](1, 2); // a4[2] is value-initialized.
+int a5[](++i, ++i);
+static_assert(sizeof(a5) == 2 * sizeof (int), "");
+int a6[](1);
+static_assert(sizeof(a6) == sizeof (int), "");
+int a7[]({});
+static_assert(sizeof(a7) == sizeof (int), "");
+int a8[]({}, {}, {}, {}, {}, {});
+static_assert(sizeof(a8) == 6 * sizeof (int), "");
+int a9[]((1));
+static_assert(sizeof(a9) == sizeof (int), "");
+int a10[]((1), (2), (3));
+static_assert(sizeof(a10) == 3 * sizeof (int), "");
+int a11[][2]{1};
+static_assert(sizeof(a11) == 2 * sizeof (int), "");
+int a12[][2]({1, 2}, {3, 4});
+static_assert(sizeof(a12) == 4 * sizeof (int), "");
+
+const int (&ra1)[](1, 2);
+const int (&ra2)[](1.0, 2);
+const int (&ra3)[2](1.0, 2);
+int (&&rra1)[](1, 2);
+int (&&rra2)[](1.0, 2);
+int (&&rra3)[2](1.0, 2);
+
+struct S { int i; } s;
+S s1[]({1});
+static_assert(sizeof(s1) == sizeof (S), "");
+S s2[]({1}, {2});
+static_assert(sizeof(s2) == 2 * sizeof (S), "");
+S s3[]({1}, {2}, {3});
+static_assert(sizeof(s3) == 3 * sizeof (S), "");
+S s4[3]({1}, {2});
+static_assert(sizeof(s4) == 3 * sizeof (S), "");
+S s5[]({++i}, {++i});
+static_assert(sizeof(s2) == 2 * sizeof (S), "");
+S s6[](s, s);
+
+struct R { int i; int j; };
+R r1[]({1, 2});
+static_assert(sizeof(r1) == sizeof (R), "");
+R r2[]({1, 2}, {3, 4});
+static_assert(sizeof(r2) == 2 * sizeof (R), "");
+R r3[]({1.0, 2}, {3.2, 4});
+static_assert(sizeof(r3) == 2 * sizeof (R), "");
+
+char c1[]('r');
+char c2[]('r', 'a', 'c', 'c', 'o', 'o', 'n');
+char c3[]("yarrow");
+char c4[4]("oak");
+char c5[10]("cat");
+const char (&c6)[4]("eel");
+
+int g;
+struct X {
+  int i;
+  X() { ++g; }
+  X(int) { };
+};
+
+int
+main ()
+{
+  // Here we'll value-initialize l[1] and l[2], which will zero-initialize it.
+  int l[3](42);
+  if (l[0] != 42 || l[1] != 0 || l[2] != 0)
+    __builtin_abort ();
+
+  // Here we'll value-initialize x[2] and x[3].  Since X is a class type
+  // with a user-provided ctor, we'll default-initialize in both cases.
+  X x[4]({ 1 }, { 2 });
+  if (g != 2)
+    __builtin_abort ();
+
+  if (a1[0] != 1 || a1[1] != 2 || a1[2] != 3)
+    __builtin_abort ();
+  if (a2[0] != 1 || a2[1] != 2 || a2[2] != 3)
+    __builtin_abort ();
+  if (a3[0] != 1 || a3[1] != 2 || a3[2] != 3)
+    __builtin_abort ();
+  if (a4[0] != 1 || a4[1] != 2 || a4[2] != 0)
+    __builtin_abort ();
+  if (a5[0] != 1 || a5[1] != 2)
+    __builtin_abort ();
+  if (a6[0] != 1)
+    __builtin_abort ();
+  if (a7[0] != 0)
+    __builtin_abort ();
+  if (a8[0] != 0 || a8[1] != 0 || a8[2] != 0 || a8[3] != 0
+      || a8[4] != 0 || a8[5] != 0)
+    __builtin_abort ();
+  if (a9[0] != 1)
+    __builtin_abort ();
+  if (a10[0] != 1 || a10[1] != 2 || a10[2] != 3)
+    __builtin_abort ();
+  if (a11[0][0] != 1 || a11[0][1] != 0)
+    __builtin_abort ();
+  if (a12[0][0] != 1 || a12[0][1] != 2 || a12[1][0] != 3 || a12[1][1] != 4)
+    __builtin_abort ();
+
+  if (ra1[0] != 1 || ra1[1] != 2)
+    __builtin_abort ();
+  if (ra2[0] != 1 || ra2[1] != 2)
+    __builtin_abort ();
+  if (ra3[0] != 1 || ra3[1] != 2)
+    __builtin_abort ();
+  if (rra1[0] != 1 || rra1[1] != 2)
+    __builtin_abort ();
+  if (rra2[0] != 1 || rra2[1] != 2)
+    __builtin_abort ();
+  if (rra3[0] != 1 || rra3[1] != 2)
+    __builtin_abort ();
+
+  if (s1[0].i != 1)
+    __builtin_abort ();
+  if (s2[0].i != 1 || s2[1].i != 2)
+    __builtin_abort ();
+  if (s3[0].i != 1 || s3[1].i != 2 || s3[2].i != 3)
+    __builtin_abort ();
+  if (s4[0].i != 1 || s4[1].i != 2 || s4[2].i != 0)
+    __builtin_abort ();
+  if (s5[0].i != 3 || s5[1].i != 4)
+    __builtin_abort ();
+
+  if (r1[0].i != 1 || r1[0].j != 2)
+    __builtin_abort ();
+  if (r2[0].i != 1 || r2[0].j != 2 || r2[1].i != 3 || r2[1].j != 4)
+    __builtin_abort ();
+  if (r3[0].i != 1 || r3[0].j != 2 || r3[1].i != 3 || r3[1].j != 4)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init5.C gcc/testsuite/g++.dg/cpp2a/paren-init5.C
new file mode 100644
index 00000000000..a64cb00ebbc
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init5.C
@@ -0,0 +1,25 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+union U {
+  int a;
+  float b;
+};
+
+// u1 has no active member
+U u1;
+// u2 zero-initializes the first member, so u2.a is the active member and
+// its value is 0.
+U u2 = U();
+// u3 uses non-list aggregate initialization, so u3.a is the active member
+// and its value is 1.
+U u3 = U(1);
+
+int
+main ()
+{
+  if (u2.a != 0)
+    __builtin_abort ();
+  if (u3.a != 1)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init6.C gcc/testsuite/g++.dg/cpp2a/paren-init6.C
new file mode 100644
index 00000000000..b5d97dcb3d8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init6.C
@@ -0,0 +1,14 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test that we don't perform lifetime extension for () init.
+
+struct A {
+  int a;
+  int&& r;
+};
+
+int f();
+A a(1, f());
+
+// { dg-final { scan-assembler-not "_ZGR1a" } }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init7.C gcc/testsuite/g++.dg/cpp2a/paren-init7.C
new file mode 100644
index 00000000000..32af1a7265c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init7.C
@@ -0,0 +1,20 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+int h;
+struct i {
+  i() {}
+  explicit i(i &) {}
+  template <typename j> i(j &) { h++; }
+};
+
+int main() {
+  {
+    i a[6];
+    auto [b, c, d, e, f, g] = a;
+  }
+  i a[6];
+  auto [b, c, d, e, f, g](a);
+  if (h != 6)
+    __builtin_abort();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init8.C gcc/testsuite/g++.dg/cpp2a/paren-init8.C
new file mode 100644
index 00000000000..30e71650bc9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init8.C
@@ -0,0 +1,13 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test that we don't accept designated inits in ( ).
+
+struct S {
+  int i;
+  int j = 42;
+};
+
+S s(.i = 12); // { dg-error "expected" }
+
+int a[]([0] = 42); // { dg-error "" }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init9.C gcc/testsuite/g++.dg/cpp2a/paren-init9.C
new file mode 100644
index 00000000000..c44b206feb8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init9.C
@@ -0,0 +1,10 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct B { };
+struct A : B {
+  int i;
+};
+
+B b;
+A a(b);
diff --git gcc/testsuite/g++.dg/ext/desig10.C gcc/testsuite/g++.dg/ext/desig10.C
index 24057dd0ad6..c8d672b72d9 100644
--- gcc/testsuite/g++.dg/ext/desig10.C
+++ gcc/testsuite/g++.dg/ext/desig10.C
@@ -1,4 +1,4 @@
 // PR c++/84972
 // { dg-additional-options "-w" }
 
-char(a[])({.a = 0});  // { dg-error "designated initializer" }
+char(a[])({.a = 0});  // { dg-error "designated initializer|cannot convert" }
diff --git gcc/testsuite/g++.dg/template/crash107.C gcc/testsuite/g++.dg/template/crash107.C
index 3b0b4e8211f..9d8d394d93d 100644
--- gcc/testsuite/g++.dg/template/crash107.C
+++ gcc/testsuite/g++.dg/template/crash107.C
@@ -3,7 +3,7 @@
 // { dg-options "" }
 // { dg-additional-options "-Wno-return-type" }
 
-template<typename FP_> struct Vec { // { dg-message "note" }
+template<typename FP_> struct Vec { // { dg-message "note" "" { target c++17_down } }
     Vec& operator^=(Vec& rhs)     {
         union {
             struct {FP_ x,y,z;}; // { dg-error "20:anonymous struct" }
@@ -14,6 +14,6 @@ template<typename FP_> struct Vec { // { dg-message "note" }
         return Vec(*this)^=rhs; // { dg-message "required" }
     }
 };
-Vec<double> v(3,4,12); // { dg-error "no matching" }
-Vec<double> V(12,4,3);  // { dg-error "no matching" }
+Vec<double> v(3,4,12); // { dg-error "no matching|too many initializers" }
+Vec<double> V(12,4,3);  // { dg-error "no matching|too many initializers" }
 Vec<double> c = v^V;   // { dg-message "required" }
diff --git gcc/testsuite/g++.dg/template/crash95.C gcc/testsuite/g++.dg/template/crash95.C
index f60e635ae66..47346111328 100644
--- gcc/testsuite/g++.dg/template/crash95.C
+++ gcc/testsuite/g++.dg/template/crash95.C
@@ -8,4 +8,4 @@ template < typename > struct S
   };
 };
 
-S < int > s(0); // { dg-error "no matching" }
+S < int > s(0); // { dg-error "no matching|too many initializers" }
diff --git gcc/testsuite/g++.old-deja/g++.law/ctors11.C gcc/testsuite/g++.old-deja/g++.law/ctors11.C
index 39ee76b0ae7..b29b18a62a3 100644
--- gcc/testsuite/g++.old-deja/g++.law/ctors11.C
+++ gcc/testsuite/g++.old-deja/g++.law/ctors11.C
@@ -10,12 +10,12 @@ public:
   inline A(int x){printf("constructing A with %d\n", x);}
 };
 
-class B:public A{ // { dg-message "note" } non-default constructor
+class B:public A{ // { dg-message "note" "" { target c++17_down } } non-default constructor
 private:
 public:
 };
 
 int main()
 {
-  B(10);// { dg-error "match" } B doesn't have a constructor taking int
+  B(10);// { dg-error "match" "" { target c++17_down } } B doesn't have a constructor taking int
 }
diff --git gcc/testsuite/g++.old-deja/g++.law/ctors9.C gcc/testsuite/g++.old-deja/g++.law/ctors9.C
index 43ba1262c95..5856d5dc883 100644
--- gcc/testsuite/g++.old-deja/g++.law/ctors9.C
+++ gcc/testsuite/g++.old-deja/g++.law/ctors9.C
@@ -20,7 +20,7 @@ Foo::Foo(int aa)
 { }
 
 
-struct var_Foo: public Foo // { dg-message "note" }  base.*// ERROR -  in class.*
+struct var_Foo: public Foo // { dg-message "note" "" { target c++17_down } }  base.*// ERROR -  in class.*
 {
   var_Foo* operator-> () {return this;}
 };
@@ -32,7 +32,7 @@ int blort(Foo& f)
 
 int main()
 {
-  var_Foo b(2);// { dg-error "match" } 
+  var_Foo b(2);// { dg-error "match" "" { target c++17_down } } 
   b->a = 0;
   int x = blort(b);
   return x;
diff --git gcc/testsuite/g++.old-deja/g++.mike/net22.C gcc/testsuite/g++.old-deja/g++.mike/net22.C
index e5e1cb1081d..abbf6191023 100644
--- gcc/testsuite/g++.old-deja/g++.mike/net22.C
+++ gcc/testsuite/g++.old-deja/g++.mike/net22.C
@@ -5,10 +5,11 @@ public:
   Parent( char *s ) {}
 };
 
-class Child : public Parent {		// { dg-message "note" } called
+class Child : public Parent {		// { dg-message "note" "" { target c++17_down } } called
 };
 
 int main() {
-  Child c( "String initializer" );	// { dg-error "match" } bad
+  Child c( "String initializer" );	// { dg-error "match" "" { target c++17_down } } bad
+// { dg-error "forbids converting a string constant" "" { target c++2a } .-1 }
   return 0;
 }
diff --git gcc/testsuite/g++.old-deja/g++.niklas/t128.C gcc/testsuite/g++.old-deja/g++.niklas/t128.C
index 19e3ca1dab0..0b3346c4ef3 100644
--- gcc/testsuite/g++.old-deja/g++.niklas/t128.C
+++ gcc/testsuite/g++.old-deja/g++.niklas/t128.C
@@ -1,5 +1,5 @@
 // { dg-do assemble  }
 // GROUPS niklas uncaught default-construct
 struct A { A (int); };
-struct B : A {}; // { dg-message "note" } without ctor // ERROR - candidates
-void f () { B (0); }// { dg-error "match" } .*
+struct B : A {}; // { dg-message "note" "" { target c++17_down } } without ctor // ERROR - candidates
+void f () { B (0); }// { dg-error "match" "" { target c++17_down } } .*
Jason Merrill Nov. 19, 2019, 10:33 p.m. | #5
On 11/19/19 1:44 AM, Marek Polacek wrote:
>> It also looks like you're using the LOOKUP flag to mean two different

>> things:

>>

>> 1) try to treat parenthesized args as an aggregate initializer

>> (build_new_method_call_1)

>> 2) treat this CONSTRUCTOR as coming from parenthesized args

>> (store_init_value/digest_init)

> 

> Correct.

> 

>> Why is the flag needed for #1?  When do we not want to try to treat the args

>> as an aggregate initializer?

> 

> There are cases where treating the args as an aggregate initializer causes

> spurious overload resolution successes, e.g.

> 

>   void swap(int&, int&);

> 

>   int& get();

> 

>   struct pair {

>     void swap(pair&) noexcept(noexcept(swap(get(), get()))) { } // { dg-error "no matching function for call" }

>   };

> 

> There are no viable candidates for pair::swap (# of args mismatch) but since

> pair is an aggregate, build_new_method_call_1 would return a CONSTRUCTOR so

> overload resolution would succeed.  Another case had to do with SFINAE and

> decltype where we didn't evaluate the arg, but succeeding in the

> no-viable-function case caused the compiler to choose the wrong function.


Hmm, but then the parenthesized list is arguments for swap, not an 
initializer for a single argument of swap.  That would be using it for 
copy-initialization, and we only want to treat parenthesized args as an 
aggregate initializer in direct-initialization.  Can we check for 
direct-init (i.e. !LOOKUP_ONLYCONVERTING) instead?

> -      expr = get_target_expr_sfinae (digest_init (totype, expr, complain),

> +      /* We want an implicit lookup, but when initializing an aggregate

> +	 from a parenthesized list, we must remember not to warn about

> +	 narrowing conversions.  */

> +      flags = LOOKUP_IMPLICIT;

> +      if (CONSTRUCTOR_IS_PAREN_INIT (expr))

> +	flags |= LOOKUP_AGGREGATE_PAREN_INIT;

> +      expr = get_target_expr_sfinae (digest_init_flags (totype, expr,

> +							flags, complain),


I was thinking to set the flag inside digest_init, so callers don't need 
to know about it.

> @@ -6704,6 +6761,14 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)

>  		}

>  	      init_code = NULL_TREE;

>  	    }

> +	  else if (BRACE_ENCLOSED_INITIALIZER_P (init_code))

> +	    {

> +	      /* In C++20, the call to build_aggr_init could have created

> +		 a CONSTRUCTOR to handle A(1, 2).  */

> +	      gcc_assert (cxx_dialect >= cxx2a);

> +	      init = init_code;

> +	      flags |= LOOKUP_AGGREGATE_PAREN_INIT;

> +	    }


I think you want to clear init_code after copying it into init.

> +	  init = build_aggr_init (decl, init, flags, tf_warning_or_error);

> +	  /* In C++20, a member initializer list can be initializing an

> +	     aggregate from a parenthesized list of values:

> +

> +	       struct S {

> +		 A aggr;

> +		 S() : aggr(1, 2, 3) { }

> +	       };

> +

> +	      Build up an INIT_EXPR like we do for aggr{1, 2, 3}, so that

> +	      build_data_member_initialization can grok it.  */

> +	  if (cxx_dialect >= cxx2a)

> +	    {

> +	      tree t = init;

> +	      while (TREE_CODE (t) == EXPR_STMT

> +		     || TREE_CODE (t) == CONVERT_EXPR)

> +		t = TREE_OPERAND (t, 0);

> +	      if (BRACE_ENCLOSED_INITIALIZER_P (t))

> +		{

> +		  t = digest_init_flags (type, t,

> +					 (LOOKUP_IMPLICIT

> +					  | LOOKUP_AGGREGATE_PAREN_INIT),

> +					 tf_warning_or_error);

> +		  init = build2 (INIT_EXPR, type, decl, t);

> +		}

> +	    }


All this should happen within the call to build_aggr_init. 
expand_default_init already builds INIT_EXPR in various cases; it could 
do so for this case, too.

> +  if (BRACE_ENCLOSED_INITIALIZER_P (exp))

> +    {

> +      gcc_assert (cxx_dialect >= cxx2a);

> +      return finish_compound_literal (type, exp, complain,

> +				      fcl_functional_paren);

> +    }


How about handling this in build_cplus_new instead of places that also 
call build_cplus_new?

It might also be better to call digest_init in build_new_method_call_1 
and not involve finish_compound_literal at all, since the latter calls 
reshape_init.

Jason
Marek Polacek Nov. 20, 2019, 8:37 p.m. | #6
On Tue, Nov 19, 2019 at 05:33:09PM -0500, Jason Merrill wrote:
> On 11/19/19 1:44 AM, Marek Polacek wrote:

> > > It also looks like you're using the LOOKUP flag to mean two different

> > > things:

> > > 

> > > 1) try to treat parenthesized args as an aggregate initializer

> > > (build_new_method_call_1)

> > > 2) treat this CONSTRUCTOR as coming from parenthesized args

> > > (store_init_value/digest_init)

> > 

> > Correct.

> > 

> > > Why is the flag needed for #1?  When do we not want to try to treat the args

> > > as an aggregate initializer?

> > 

> > There are cases where treating the args as an aggregate initializer causes

> > spurious overload resolution successes, e.g.

> > 

> >   void swap(int&, int&);

> > 

> >   int& get();

> > 

> >   struct pair {

> >     void swap(pair&) noexcept(noexcept(swap(get(), get()))) { } // { dg-error "no matching function for call" }

> >   };

> > 

> > There are no viable candidates for pair::swap (# of args mismatch) but since

> > pair is an aggregate, build_new_method_call_1 would return a CONSTRUCTOR so

> > overload resolution would succeed.  Another case had to do with SFINAE and

> > decltype where we didn't evaluate the arg, but succeeding in the

> > no-viable-function case caused the compiler to choose the wrong function.

> 

> Hmm, but then the parenthesized list is arguments for swap, not an

> initializer for a single argument of swap.  That would be using it for

> copy-initialization, and we only want to treat parenthesized args as an

> aggregate initializer in direct-initialization.  Can we check for

> direct-init (i.e. !LOOKUP_ONLYCONVERTING) instead?


Unfortunately that doesn't work.  We call build_new_method_call from context
where LOOKUP_ONLYCONVERTING isn't set and so it would still break things.

> > -      expr = get_target_expr_sfinae (digest_init (totype, expr, complain),

> > +      /* We want an implicit lookup, but when initializing an aggregate

> > +	 from a parenthesized list, we must remember not to warn about

> > +	 narrowing conversions.  */

> > +      flags = LOOKUP_IMPLICIT;

> > +      if (CONSTRUCTOR_IS_PAREN_INIT (expr))

> > +	flags |= LOOKUP_AGGREGATE_PAREN_INIT;

> > +      expr = get_target_expr_sfinae (digest_init_flags (totype, expr,

> > +							flags, complain),

> 

> I was thinking to set the flag inside digest_init, so callers don't need to

> know about it.


Got it, done.

> > @@ -6704,6 +6761,14 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)

> >  		}

> >  	      init_code = NULL_TREE;

> >  	    }

> > +	  else if (BRACE_ENCLOSED_INITIALIZER_P (init_code))

> > +	    {

> > +	      /* In C++20, the call to build_aggr_init could have created

> > +		 a CONSTRUCTOR to handle A(1, 2).  */

> > +	      gcc_assert (cxx_dialect >= cxx2a);

> > +	      init = init_code;

> > +	      flags |= LOOKUP_AGGREGATE_PAREN_INIT;

> > +	    }

> 

> I think you want to clear init_code after copying it into init.


In fact, now I dropped this whole hunk.

> > +	  init = build_aggr_init (decl, init, flags, tf_warning_or_error);

> > +	  /* In C++20, a member initializer list can be initializing an

> > +	     aggregate from a parenthesized list of values:

> > +

> > +	       struct S {

> > +		 A aggr;

> > +		 S() : aggr(1, 2, 3) { }

> > +	       };

> > +

> > +	      Build up an INIT_EXPR like we do for aggr{1, 2, 3}, so that

> > +	      build_data_member_initialization can grok it.  */

> > +	  if (cxx_dialect >= cxx2a)

> > +	    {

> > +	      tree t = init;

> > +	      while (TREE_CODE (t) == EXPR_STMT

> > +		     || TREE_CODE (t) == CONVERT_EXPR)

> > +		t = TREE_OPERAND (t, 0);

> > +	      if (BRACE_ENCLOSED_INITIALIZER_P (t))

> > +		{

> > +		  t = digest_init_flags (type, t,

> > +					 (LOOKUP_IMPLICIT

> > +					  | LOOKUP_AGGREGATE_PAREN_INIT),

> > +					 tf_warning_or_error);

> > +		  init = build2 (INIT_EXPR, type, decl, t);

> > +		}

> > +	    }

> 

> All this should happen within the call to build_aggr_init.

> expand_default_init already builds INIT_EXPR in various cases; it could do

> so for this case, too.


True, done.

> > +  if (BRACE_ENCLOSED_INITIALIZER_P (exp))

> > +    {

> > +      gcc_assert (cxx_dialect >= cxx2a);

> > +      return finish_compound_literal (type, exp, complain,

> > +				      fcl_functional_paren);

> > +    }

> 

> How about handling this in build_cplus_new instead of places that also call

> build_cplus_new?


Is it really what we want?  We now have two spots where we need to handle
the case when build_special_member_call returns a BRACE_ENCLOSED_INITIALIZER_P
but build_cplus_new is called in many other spots where we don't expect to see
a CONSTRUCTOR.

> It might also be better to call digest_init in build_new_method_call_1 and

> not involve finish_compound_literal at all, since the latter calls

> reshape_init.


Unfortunately calling digest_init in build_new_method_call_1 causes an ICE,
but now that we have CONSTRUCTOR_IS_PAREN_INIT I was able to avoid all the
finish_compound_literal changes.  And reshape_init now just gives up when it
sees a CONSTRUCTOR_IS_PAREN_INIT.  (And that showed that I was mistakenly
allowing brace elision in paren-init15.C and paren-init16.C; now fixed.)

Bootstrapped/regtested on x86_64-linux.

2019-11-20  Marek Polacek  <polacek@redhat.com>

	PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
	* c-cppbuiltin.c (c_cpp_builtins): Predefine
	__cpp_aggregate_paren_init=201902 for -std=c++2a.

	* call.c (build_new_method_call_1): Handle parenthesized initialization
	of aggregates by building up a CONSTRUCTOR.
	(perform_direct_initialization_if_possible): Use
	LOOKUP_AGGREGATE_PAREN_INIT when calling build_special_member_call.  If
	it returned a BRACE_ENCLOSED_INITIALIZER_P, call
	finish_compound_literal.
	(extend_ref_init_temps): Do nothing for CONSTRUCTOR_IS_PAREN_INIT.
	* cp-tree.h (CONSTRUCTOR_IS_PAREN_INIT, LOOKUP_AGGREGATE_PAREN_INIT):
	Define.
	* decl.c (grok_reference_init): Handle aggregate initialization from
	a parenthesized list of values.
	(reshape_init): Do nothing for CONSTRUCTOR_IS_PAREN_INIT.
	(check_initializer): Handle initialization of an array from a
	parenthesized list of values.  Use NULL_TREE instead of NULL.
	* init.c (perform_member_init): Don't call finish_expr_stmt if
	build_aggr_init returns an INIT_EXPR.
	(expand_default_init): Set LOOKUP_AGGREGATE_PAREN_INIT for the
	build_special_member_call call.  If it returns a
	BRACE_ENCLOSED_INITIALIZER_P, call digest_init and build an INIT_EXPR.
	Set TREE_SIDE_EFFECTS on it.
	* typeck2.c (digest_init_r): Allow narrowing when
	LOOKUP_AGGREGATE_PAREN_INIT.
	Change gcc_unreachable to gcc_assert and don't crash when
	LOOKUP_AGGREGATE_PAREN_INIT is on, return error_mark_node instead.
	(digest_init): Set LOOKUP_AGGREGATE_PAREN_INIT if it receives
	a CONSTRUCTOR with CONSTRUCTOR_IS_PAREN_INIT set.
	(digest_init_flags): Likewise.
	(massage_init_elt): Don't lose LOOKUP_AGGREGATE_PAREN_INIT when passing
	flags to digest_init_r.
	(build_functional_cast): Use LOOKUP_AGGREGATE_PAREN_INIT when calling
	build_special_member_call.  If it returned a
	BRACE_ENCLOSED_INITIALIZER_P, call finish_compound_literal.

	* g++.dg/cpp0x/constexpr-99.C: Only expect an error in C++17 and
	lesser.
	* g++.dg/cpp0x/explicit7.C: Likewise.
	* g++.dg/cpp0x/initlist12.C: Adjust dg-error.
	* g++.dg/cpp0x/pr31437.C: Likewise.
	* g++.dg/cpp2a/feat-cxx2a.C: Add __cpp_aggregate_paren_init test.
	* g++.dg/cpp2a/paren-init1.C: New test.
	* g++.dg/cpp2a/paren-init10.C: New test.
	* g++.dg/cpp2a/paren-init11.C: New test.
	* g++.dg/cpp2a/paren-init12.C: New test.
	* g++.dg/cpp2a/paren-init13.C: New test.
	* g++.dg/cpp2a/paren-init14.C: New test.
	* g++.dg/cpp2a/paren-init15.C: New test.
	* g++.dg/cpp2a/paren-init16.C: New test.
	* g++.dg/cpp2a/paren-init2.C: New test.
	* g++.dg/cpp2a/paren-init3.C: New test.
	* g++.dg/cpp2a/paren-init4.C: New test.
	* g++.dg/cpp2a/paren-init5.C: New test.
	* g++.dg/cpp2a/paren-init6.C: New test.
	* g++.dg/cpp2a/paren-init7.C: New test.
	* g++.dg/cpp2a/paren-init8.C: New test.
	* g++.dg/cpp2a/paren-init9.C: New test.
	* g++.dg/ext/desig10.C: Adjust dg-error.
	* g++.dg/template/crash107.C: Likewise.
	* g++.dg/template/crash95.C: Likewise.
	* g++.old-deja/g++.law/ctors11.C: Only expect an error in C++17 and
	lesser.
	* g++.old-deja/g++.law/ctors9.C: Likewise.
	* g++.old-deja/g++.mike/net22.C: Likewise.
	* g++.old-deja/g++.niklas/t128.C: Likewise.

diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c
index 76d1e4a380e..e9c269caf24 100644
--- gcc/c-family/c-cppbuiltin.c
+++ gcc/c-family/c-cppbuiltin.c
@@ -996,6 +996,7 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_impl_destroying_delete=201806L");
 	  cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907L");
 	  cpp_define (pfile, "__cpp_impl_three_way_comparison=201907L");
+	  cpp_define (pfile, "__cpp_aggregate_paren_init=201902L");
 	}
       if (flag_concepts)
         {
diff --git gcc/cp/call.c gcc/cp/call.c
index cccb90f4f99..db1969dd486 100644
--- gcc/cp/call.c
+++ gcc/cp/call.c
@@ -10112,6 +10112,28 @@ build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args,
 
   if (!any_viable_p)
     {
+      /* [dcl.init], 17.6.2.2:
+
+	 Otherwise, if no constructor is viable, the destination type is
+	 a (possibly cv-qualified) aggregate class A, and the initializer
+	 is a parenthesized expression-list, the object is initialized as
+	 follows...
+
+	 We achieve this by building up a CONSTRUCTOR, as for list-init,
+	 and setting CONSTRUCTOR_IS_PAREN_INIT to distinguish between
+	 the two.  */
+      if ((flags & LOOKUP_AGGREGATE_PAREN_INIT)
+	  && cxx_dialect >= cxx2a
+	  && CP_AGGREGATE_TYPE_P (basetype)
+	  && !user_args->is_empty ())
+	{
+	  /* Create a CONSTRUCTOR from ARGS, e.g. {1, 2} from <1, 2>.  */
+	  tree list = build_tree_list_vec (user_args);
+	  tree ctor = build_constructor_from_list (init_list_type_node, list);
+	  CONSTRUCTOR_IS_DIRECT_INIT (ctor) = true;
+	  CONSTRUCTOR_IS_PAREN_INIT (ctor) = true;
+	  return ctor;
+	}
       if (complain & tf_error)
 	complain_about_no_candidates_for_method_call (instance, candidates,
 						      explicit_targs, basetype,
@@ -11793,14 +11815,27 @@ perform_direct_initialization_if_possible (tree type,
      If the destination type is a (possibly cv-qualified) class type:
 
      -- If the initialization is direct-initialization ...,
-     constructors are considered. ... If no constructor applies, or
-     the overload resolution is ambiguous, the initialization is
-     ill-formed.  */
+     constructors are considered.
+
+       -- If overload resolution is successful, the selected constructor
+       is called to initialize the object, with the initializer expression
+       or expression-list as its argument(s).
+
+       -- Otherwise, if no constructor is viable, the destination type is
+       a (possibly cv-qualified) aggregate class A, and the initializer is
+       a parenthesized expression-list, the object is initialized as
+       follows...  */
   if (CLASS_TYPE_P (type))
     {
       releasing_vec args (make_tree_vector_single (expr));
+      int flags = LOOKUP_NORMAL | LOOKUP_AGGREGATE_PAREN_INIT;
       expr = build_special_member_call (NULL_TREE, complete_ctor_identifier,
-					&args, type, LOOKUP_NORMAL, complain);
+					&args, type, flags, complain);
+      if (BRACE_ENCLOSED_INITIALIZER_P (expr))
+	{
+	  gcc_assert (cxx_dialect >= cxx2a);
+	  return finish_compound_literal (type, expr, complain);
+	}
       return build_cplus_new (type, expr, complain);
     }
 
@@ -12151,6 +12186,12 @@ extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups)
 	ctor = TARGET_EXPR_INITIAL (ctor);
       if (TREE_CODE (ctor) == CONSTRUCTOR)
 	{
+	  /* [dcl.init] When initializing an aggregate from a parenthesized list
+	     of values... a temporary object bound to a reference does not have
+	     its lifetime extended.  */
+	  if (CONSTRUCTOR_IS_PAREN_INIT (ctor))
+	    return init;
+
 	  if (is_std_init_list (type))
 	    {
 	      /* The temporary array underlying a std::initializer_list
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index d6e9357385a..c6d899b9211 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -4325,6 +4325,11 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define CONSTRUCTOR_IS_DESIGNATED_INIT(NODE) \
   (TREE_LANG_FLAG_6 (CONSTRUCTOR_CHECK (NODE)))
 
+/* True if this CONSTRUCTOR comes from a parenthesized list of values, e.g.
+   A(1, 2, 3).  */
+#define CONSTRUCTOR_IS_PAREN_INIT(NODE) \
+  (CONSTRUCTOR_CHECK(NODE)->base.private_flag)
+
 /* True if NODE represents a conversion for direct-initialization in a
    template.  Set by perform_implicit_conversion_flags.  */
 #define IMPLICIT_CONV_EXPR_DIRECT_INIT(NODE) \
@@ -5583,6 +5588,8 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
    args), then we swap the conversions back in build_new_op_1 (so they
    correspond to the order of the args in the candidate).  */
 #define LOOKUP_REVERSED (LOOKUP_REWRITTEN << 1)
+/* We're initializing an aggregate from a parenthesized list of values.  */
+#define LOOKUP_AGGREGATE_PAREN_INIT (LOOKUP_REVERSED << 1)
 
 #define LOOKUP_NAMESPACES_ONLY(F)  \
   (((F) & LOOKUP_PREFER_NAMESPACES) && !((F) & LOOKUP_PREFER_TYPES))
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 86e38f4af69..d26f464d0f4 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -5531,11 +5531,28 @@ grok_reference_init (tree decl, tree type, tree init, int flags)
       return NULL_TREE;
     }
 
+  tree ttype = TREE_TYPE (type);
   if (TREE_CODE (init) == TREE_LIST)
-    init = build_x_compound_expr_from_list (init, ELK_INIT,
-					    tf_warning_or_error);
+    {
+      /* This handles (C++20 only) code like
+
+	   const A& r(1, 2, 3);
+
+	 where we treat the parenthesized list as a CONSTRUCTOR.  */
+      if (TREE_TYPE (init) == NULL_TREE
+	  && CP_AGGREGATE_TYPE_P (ttype)
+	  && !DECL_DECOMPOSITION_P (decl)
+	  && (cxx_dialect >= cxx2a))
+	{
+	  init = build_constructor_from_list (init_list_type_node, init);
+	  CONSTRUCTOR_IS_DIRECT_INIT (init) = true;
+	  CONSTRUCTOR_IS_PAREN_INIT (init) = true;
+	}
+      else
+	init = build_x_compound_expr_from_list (init, ELK_INIT,
+						tf_warning_or_error);
+    }
 
-  tree ttype = TREE_TYPE (type);
   if (TREE_CODE (ttype) != ARRAY_TYPE
       && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE)
     /* Note: default conversion is only called in very special cases.  */
@@ -6437,6 +6454,11 @@ reshape_init (tree type, tree init, tsubst_flags_t complain)
   if (vec_safe_is_empty (v))
     return init;
 
+  /* Brace elision is not performed for a CONSTRUCTOR representing
+     parenthesized aggregate initialization.  */
+  if (CONSTRUCTOR_IS_PAREN_INIT (init))
+    return init;
+
   /* Handle [dcl.init.list] direct-list-initialization from
      single element of enumeration with a fixed underlying type.  */
   if (is_direct_enum_init (type, init))
@@ -6640,6 +6662,41 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
 	      flags |= LOOKUP_NO_NARROWING;
 	    }
 	}
+      /* [dcl.init] "Otherwise, if the destination type is an array, the object
+	 is initialized as follows..."  So handle things like
+
+	  int a[](1, 2, 3);
+
+	 which is permitted in C++20 by P0960.  */
+      else if (TREE_CODE (init) == TREE_LIST
+	       && TREE_TYPE (init) == NULL_TREE
+	       && TREE_CODE (type) == ARRAY_TYPE
+	       && !DECL_DECOMPOSITION_P (decl)
+	       && (cxx_dialect >= cxx2a))
+	{
+	  /* [dcl.init.string] "An array of ordinary character type [...]
+	     can be initialized by an ordinary string literal [...] by an
+	     appropriately-typed string literal enclosed in braces" only
+	     talks about braces, but GCC has always accepted
+
+	       char a[]("foobar");
+
+	     so we continue to do so.  */
+	  tree val = TREE_VALUE (init);
+	  if (TREE_CHAIN (init) == NULL_TREE
+	      && char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type)))
+	      && TREE_CODE (tree_strip_any_location_wrapper (val))
+		 == STRING_CST)
+	    /* If the list has a single element and it's a string literal,
+	       then it's the initializer for the array as a whole.  */
+	    init = val;
+	  else
+	    {
+	      init = build_constructor_from_list (init_list_type_node, init);
+	      CONSTRUCTOR_IS_DIRECT_INIT (init) = true;
+	      CONSTRUCTOR_IS_PAREN_INIT (init) = true;
+	    }
+	}
       else if (TREE_CODE (init) == TREE_LIST
 	       && TREE_TYPE (init) != unknown_type_node
 	       && !MAYBE_CLASS_TYPE_P (type))
@@ -6683,6 +6740,9 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
 	    init_code = TREE_OPERAND (init_code, 0);
 	  if (TREE_CODE (init_code) == INIT_EXPR)
 	    {
+	      /* In C++20, the call to build_aggr_init could have created
+		 an INIT_EXPR with a CONSTRUCTOR as the RHS to handle
+		 A(1, 2).  */
 	      init = TREE_OPERAND (init_code, 1);
 	      init_code = NULL_TREE;
 	      /* Don't call digest_init; it's unnecessary and will complain
@@ -6736,7 +6796,7 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
 			0, "array %qD initialized by parenthesized "
 			"string literal %qE",
 			decl, DECL_INITIAL (decl));
-	  init = NULL;
+	  init = NULL_TREE;
 	}
     }
   else
diff --git gcc/cp/init.c gcc/cp/init.c
index c620b05fe07..5d32a9fb810 100644
--- gcc/cp/init.c
+++ gcc/cp/init.c
@@ -921,8 +921,20 @@ perform_member_init (tree member, tree init)
 		inform (DECL_SOURCE_LOCATION (member),
 			"%q#D should be initialized", member );
 	    }
-	  finish_expr_stmt (build_aggr_init (decl, init, flags,
-					     tf_warning_or_error));
+	  init = build_aggr_init (decl, init, flags, tf_warning_or_error);
+	  /* In C++20, a member initializer list can be initializing an
+	     aggregate from a parenthesized list of values:
+
+	       struct S {
+		 A aggr;
+		 S() : aggr(1, 2, 3) { }
+	       };
+
+	      In such case, build_aggr_init will build up an INIT_EXPR like
+	      we do for aggr{1, 2, 3}, so that build_data_member_initialization
+	      can grok it.  */
+	  if (TREE_CODE (init) != INIT_EXPR)
+	    finish_expr_stmt (init);
 	}
     }
   else
@@ -1967,8 +1979,24 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
       tree ctor_name = (true_exp == exp
 			? complete_ctor_identifier : base_ctor_identifier);
 
+      /* Given class A,
+
+	   A a(1, 2);
+
+	 can mean a call to a constructor A::A(int, int), if present.  If not,
+	 but A is an aggregate, we will try aggregate initialization.  */
+      flags |= LOOKUP_AGGREGATE_PAREN_INIT;
       rval = build_special_member_call (exp, ctor_name, &parms, binfo, flags,
 					complain);
+      if (BRACE_ENCLOSED_INITIALIZER_P (rval))
+	{
+	  gcc_assert (cxx_dialect >= cxx2a);
+	  rval = digest_init (type, rval, tf_warning_or_error);
+	  rval = build2 (INIT_EXPR, type, exp, rval);
+	  /* So that we do finish_expr_stmt below.  Don't return here, we
+	     need to release PARMS.  */
+	  TREE_SIDE_EFFECTS (rval) = 1;
+	}
     }
 
   if (parms != NULL)
diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
index 9fb36fd1ed3..9f7590c70f4 100644
--- gcc/cp/typeck2.c
+++ gcc/cp/typeck2.c
@@ -1224,7 +1224,9 @@ digest_init_r (tree type, tree init, int nested, int flags,
   if ((code != COMPLEX_TYPE || BRACE_ENCLOSED_INITIALIZER_P (stripped_init))
       && (SCALAR_TYPE_P (type) || code == REFERENCE_TYPE))
     {
-      if (nested)
+      /* Narrowing is OK when initializing an aggregate from
+	 a parenthesized list.  */
+      if (nested && !(flags & LOOKUP_AGGREGATE_PAREN_INIT))
 	flags |= LOOKUP_NO_NARROWING;
       init = convert_for_initialization (0, type, init, flags,
 					 ICR_INIT, NULL_TREE, 0,
@@ -1272,8 +1274,13 @@ digest_init_r (tree type, tree init, int nested, int flags,
 		inform (loc, "remove %<{ }%> around initializer");
 	    }
 	  else if (flag_checking)
-	    /* We should have fixed this in reshape_init.  */
-	    gcc_unreachable ();
+	    /* We should have fixed this in reshape_init.  Except that we
+	       don't reshape parenthesized lists where brace elision is
+	       not permitted.  */
+	    {
+	      gcc_assert (flags & LOOKUP_AGGREGATE_PAREN_INIT);
+	      return error_mark_node;
+	    }
 	}
     }
 
@@ -1319,12 +1326,19 @@ digest_init_r (tree type, tree init, int nested, int flags,
 tree
 digest_init (tree type, tree init, tsubst_flags_t complain)
 {
-  return digest_init_r (type, init, 0, LOOKUP_IMPLICIT, complain);
+  int flags = LOOKUP_IMPLICIT;
+  if (BRACE_ENCLOSED_INITIALIZER_P (init)
+      && CONSTRUCTOR_IS_PAREN_INIT (init))
+    flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+  return digest_init_r (type, init, 0, flags, complain);
 }
 
 tree
 digest_init_flags (tree type, tree init, int flags, tsubst_flags_t complain)
 {
+  if (BRACE_ENCLOSED_INITIALIZER_P (init)
+      && CONSTRUCTOR_IS_PAREN_INIT (init))
+    flags |= LOOKUP_AGGREGATE_PAREN_INIT;
   return digest_init_r (type, init, 0, flags, complain);
 }
 
@@ -1384,9 +1398,12 @@ static tree
 massage_init_elt (tree type, tree init, int nested, int flags,
 		  tsubst_flags_t complain)
 {
-  flags &= LOOKUP_ALLOW_FLEXARRAY_INIT;
-  flags |= LOOKUP_IMPLICIT;
-  init = digest_init_r (type, init, nested ? 2 : 1, flags, complain);
+  int new_flags = LOOKUP_IMPLICIT;
+  if (flags & LOOKUP_ALLOW_FLEXARRAY_INIT)
+    new_flags |= LOOKUP_ALLOW_FLEXARRAY_INIT;
+  if (flags & LOOKUP_AGGREGATE_PAREN_INIT)
+    new_flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+  init = digest_init_r (type, init, nested ? 2 : 1, new_flags, complain);
   /* Strip a simple TARGET_EXPR when we know this is an initializer.  */
   if (SIMPLE_TARGET_EXPR_P (init))
     init = TARGET_EXPR_INITIAL (init);
@@ -2343,10 +2360,16 @@ build_functional_cast (tree exp, tree parms, tsubst_flags_t complain)
   releasing_vec parmvec;
   for (; parms != NULL_TREE; parms = TREE_CHAIN (parms))
     vec_safe_push (parmvec, TREE_VALUE (parms));
+  int flags = LOOKUP_NORMAL | LOOKUP_AGGREGATE_PAREN_INIT;
   exp = build_special_member_call (NULL_TREE, complete_ctor_identifier,
-				   &parmvec, type, LOOKUP_NORMAL, complain);
+				   &parmvec, type, flags, complain);
 
-  if (exp == error_mark_node)
+  if (BRACE_ENCLOSED_INITIALIZER_P (exp))
+    {
+      gcc_assert (cxx_dialect >= cxx2a);
+      return finish_compound_literal (type, exp, complain);
+    }
+  else if (exp == error_mark_node)
     return error_mark_node;
 
   return build_cplus_new (type, exp, complain);
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-99.C gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
index 8d791dd032d..4d3953d0983 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
@@ -9,5 +9,6 @@ struct A
 struct B
 {
   A a;
-    constexpr B() : a(0) {} // { dg-error "no matching function" }
+  // P0960R3 allows paren-init.
+  constexpr B() : a(0) {} // { dg-error "no matching function" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/explicit7.C gcc/testsuite/g++.dg/cpp0x/explicit7.C
index 574796d117d..67b50542bc3 100644
--- gcc/testsuite/g++.dg/cpp0x/explicit7.C
+++ gcc/testsuite/g++.dg/cpp0x/explicit7.C
@@ -10,7 +10,7 @@ struct A { };
 struct B: A { };
 struct C {
   explicit operator B*();	// { dg-message "explicit" }
-  explicit operator B&();	// { dg-message "explicit" }
+  explicit operator B&();	// { dg-message "explicit" "" { target c++17_down } }
 };
 
 C c;
diff --git gcc/testsuite/g++.dg/cpp0x/initlist12.C gcc/testsuite/g++.dg/cpp0x/initlist12.C
index f72a6dac525..c2a2b86520f 100644
--- gcc/testsuite/g++.dg/cpp0x/initlist12.C
+++ gcc/testsuite/g++.dg/cpp0x/initlist12.C
@@ -6,15 +6,15 @@ struct A
   int i;
 };
 
-A a({1,2});			// { dg-error "no match" }
+A a({1,2});			// { dg-error "no match|cannot convert" }
 
 union U
 {
   int i,j;
 };
 
-U u({1,2});			// { dg-error "no match" }
+U u({1,2});			// { dg-error "no match|cannot convert" }
 
 union V {};
 
-V v({1});			// { dg-error "no match" }
+V v({1});			// { dg-error "no match|too many initializers" }
diff --git gcc/testsuite/g++.dg/cpp0x/pr31437.C gcc/testsuite/g++.dg/cpp0x/pr31437.C
index 7e2ed7a1375..532b533c8d3 100644
--- gcc/testsuite/g++.dg/cpp0x/pr31437.C
+++ gcc/testsuite/g++.dg/cpp0x/pr31437.C
@@ -1,9 +1,9 @@
 // { dg-do compile { target c++11 } }
-template <typename... T> struct A // { dg-message "candidates|A" }
+template <typename... T> struct A // { dg-message "candidates|A" "" { target c++17_down } }
 {
   A(T* p) {  // { dg-error "parameter packs|T" }
    (A<T...>*)(p); 
   }
 };
 
-A<int> a(0); // { dg-error "no matching" }
+A<int> a(0); // { dg-error "no matching|too many initializers" }
diff --git gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
index 95251c2f5c6..736207aa635 100644
--- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
+++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
@@ -460,6 +460,12 @@
 #  error "__cpp_constexpr_dynamic_alloc != 201907"
 #endif
 
+#ifndef __cpp_aggregate_paren_init
+#  error "__cpp_aggregate_paren_init"
+#elif __cpp_aggregate_paren_init != 201902
+#  error "__cpp_aggregate_paren_init != 201902"
+#endif
+
 #ifdef __has_cpp_attribute
 
 #  if ! __has_cpp_attribute(maybe_unused)
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init1.C gcc/testsuite/g++.dg/cpp2a/paren-init1.C
new file mode 100644
index 00000000000..a54b2ccaf6d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init1.C
@@ -0,0 +1,116 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+struct A {
+  int i;
+  int j;
+  int k;
+};
+
+struct B {
+  int i;
+  int j;
+  int k = 42;
+};
+
+struct C {
+  A a;
+};
+
+struct D {
+  A a1;
+  A a2;
+};
+
+struct E {
+  int i;
+};
+
+// F has a base class, but it's not virtual, private, or protected, so this is
+// still an aggregate (since C++17).
+struct F : E {
+  int j;
+  int k;
+};
+
+F f({1}, 2, 3);
+
+// A non-virtual member function doesn't make it a non-aggregate.
+struct G {
+  int i;
+  double j;
+  int foo(int, int);
+};
+
+G g(1, 2.14);
+
+class H {
+public:
+  H (int) { }
+};
+
+class I : public H { };
+
+int i;
+A a1(1, 2);
+A a2(1.0, 2);
+A a3(++i, ++i);
+const A& ra(1, 2);
+
+A ca = A(1, 2);
+A ca2 = A(1.0, 2);
+A ca3 = A(++i, ++i);
+const A& rca = A(1, 2);
+
+B b1(1, 2, 3);
+B b2(1, 2);
+B b3(1);
+
+C c1({5, 6, 7});
+D d1({1, 2, 3}, {5, 6, 7});
+
+struct W {
+  const char *s, *t;
+};
+W w1("fluffer", "nutter");
+
+struct Y {
+  char a[4];
+};
+Y y("yew");
+
+int
+main ()
+{
+  I(10);
+
+  // A::k will be value-initialized.
+  if (a1.i != 1 || a1.j != 2 || a1.k != 0)
+    __builtin_abort ();
+  if (a2.i != 1 || a2.j != 2 || a2.k != 0)
+    __builtin_abort ();
+  if (a3.i != 1 || a3.j != 2 || a3.k != 0)
+    __builtin_abort ();
+  if (ra.i != 1 || ra.j != 2 || ra.k != 0)
+    __builtin_abort ();
+  if (ca.i != 1 || ca.j != 2 || ca.k != 0)
+    __builtin_abort ();
+  if (ca2.i != 1 || ca2.j != 2 || ca2.k != 0)
+    __builtin_abort ();
+  if (ca3.i != 3 || ca3.j != 4 || ca3.k != 0)
+    __builtin_abort ();
+
+  if (b1.i != 1 || b1.j != 2 || b1.k != 3)
+    __builtin_abort ();
+  // The default member initializer will be used for B::k.
+  if (b2.i != 1 || b2.j != 2 || b2.k != 42)
+    __builtin_abort ();
+  if (b3.i != 1 || b3.j != 0 || b3.k != 42)
+    __builtin_abort ();
+
+  if (c1.a.i != 5 || c1.a.j != 6 || c1.a.k != 7)
+    __builtin_abort ();
+
+  if (f.i != 1 || f.j != 2 || f.k != 3)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init10.C gcc/testsuite/g++.dg/cpp2a/paren-init10.C
new file mode 100644
index 00000000000..5c70d9d59ee
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init10.C
@@ -0,0 +1,18 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test from [dcl.init].
+
+struct A {
+  int a;
+  int&& r;
+};
+
+int f();
+int n = 10;
+
+A a1{1, f()};               // OK, lifetime is extended
+A a2(1, f());               // well-formed, but dangling reference
+A a3{1.0, 1};               // { dg-error "narrowing conversion" }
+A a4(1.0, 1);               // well-formed, but dangling reference
+A a5(1.0, static_cast<int&&>(n));    // OK
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init11.C gcc/testsuite/g++.dg/cpp2a/paren-init11.C
new file mode 100644
index 00000000000..82ca2669545
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init11.C
@@ -0,0 +1,88 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test ill-formed code.
+
+// If k is greater than the size of the array, the program is ill-formed.
+int a1[2](1, 2, 3); // { dg-error "too many initializers" }
+int a2[](); // { dg-error "array of functions" }
+int a3[](()); // { dg-error "expected primary-expression" }
+int a4[]("raccoon"); // { dg-error "invalid conversion" }
+
+struct S {
+  int i;
+  int j;
+};
+
+S s1(1, 1, 1); // { dg-error "too many initializers" }
+
+union U2 {
+  int a;
+  float b;
+};
+
+// [dcl.init.aggr]/19:
+// When a union is initialized with an initializer list, there shall not be
+// more than one explicitly initialized element.
+U2 u4 = U2(1, 2); // { dg-error "too many initializers" }
+
+// Test there is no brace elision.
+
+int a[2][2](1, 2, 3, 4); // { dg-error "too many initializers|array must be initialized with a brace-enclosed initializer" }
+
+// private/protected/virtual base class -> not an aggregate.
+struct B { };
+struct D : private B {
+  int i;
+  int j;
+};
+
+D d({}, 1, 2); // { dg-error "no matching function" }
+
+// Private non-static data member -> not an aggregate.
+struct P {
+  int i;
+private:
+  int j;
+};
+
+P p(1, 2); // { dg-error "no matching function" }
+
+// User-declared constructor -> not an aggregate.
+struct U {
+  U() {}
+  int i;
+  int j;  
+};
+
+U u(1, 2); // { dg-error "no matching function" }
+
+// virtual member function -> not an aggregate.
+struct V {
+  int i;
+  int j;
+  virtual int foo(int);
+};
+
+V v(1, 2); // { dg-error "no matching function" }
+
+struct nonaggr {
+  int i;
+  int j;
+private:
+  int x;
+};
+
+struct F {
+  nonaggr n;
+  F() : n(1) { } // { dg-error "no matching function" }
+};
+
+struct G {
+  char a[4];
+};
+
+struct H {
+  G g;
+  H() : g("oaks") { } // { dg-error "initializer-string" }
+};
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init12.C gcc/testsuite/g++.dg/cpp2a/paren-init12.C
new file mode 100644
index 00000000000..d7be6f2b55d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init12.C
@@ -0,0 +1,17 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct A;
+
+struct C {
+  operator A();
+};
+
+struct A {
+  C c;
+};
+
+C c;
+A a(c);  // invokes C’s conversion function to A
+
+// { dg-final { scan-assembler "_ZN1Ccv1AEv" } }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init13.C gcc/testsuite/g++.dg/cpp2a/paren-init13.C
new file mode 100644
index 00000000000..4b9107c70ff
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init13.C
@@ -0,0 +1,16 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct X { int a, b; };
+struct Y { X x; };
+
+void
+f()
+{
+  // This is ok...
+  Y y1{{1, 2}};
+  Y y2({1, 2});
+  // ...but Y y((1,2)) is not the same as Y y{{1,2}}.  (1, 2) is a
+  // COMPOUND_EXPR.
+  Y y3((1, 2)); // { dg-error "could not convert" }
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init14.C gcc/testsuite/g++.dg/cpp2a/paren-init14.C
new file mode 100644
index 00000000000..837f9531260
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init14.C
@@ -0,0 +1,10 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++11 } }
+
+struct A {
+  int x;
+  int y;
+  A(int, int) = delete;
+};
+
+A a(1, 2); // { dg-error "use of deleted function" }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init15.C gcc/testsuite/g++.dg/cpp2a/paren-init15.C
new file mode 100644
index 00000000000..4311dd4df59
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init15.C
@@ -0,0 +1,35 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct A {
+  int i;
+  int j;
+};
+
+struct B : A
+{
+  B (): A(1.7, 2) { }
+};
+
+void f(A);
+
+void
+g ()
+{
+  f (A(1, 2));
+}
+
+struct S {
+  int a[2];
+};
+
+S h() { return S({1, 2}); }
+
+struct Z {
+  int i;
+  int j;
+  operator A();
+};
+
+Z z;
+A a = Z(1, 2.3);
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init16.C gcc/testsuite/g++.dg/cpp2a/paren-init16.C
new file mode 100644
index 00000000000..c8ed924877e
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init16.C
@@ -0,0 +1,14 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct S { int a[2]; };
+struct A { S s[1]; };
+
+template <typename, int N>
+struct R { static constexpr auto h = A({S{N}}); };
+
+template <typename, int N>
+struct R2 { static constexpr auto h = A({S({N, N})}); };
+
+A foo = R<int, 10>::h;
+A foo2 = R2<int, 10>::h;
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init2.C gcc/testsuite/g++.dg/cpp2a/paren-init2.C
new file mode 100644
index 00000000000..e9e90d7acb6
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init2.C
@@ -0,0 +1,56 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+struct A {
+  int i = 0;
+  int j = 0;
+};
+
+struct B {
+  A a;
+  constexpr B() : a(1.1, 2) { }
+};
+
+struct C {
+  int i;
+};
+
+struct E {
+  C c;
+  E() : c(1.2) { }
+};
+
+struct F {
+  char a[4];
+};
+
+struct G {
+  F f;
+  G() : f("yew") { }
+};
+
+struct H {
+  int i;
+  int &&r;
+};
+
+int f() { return 42; }
+
+struct I {
+  H h;
+  I() : h(1, f()) { }
+};
+
+I i;  // dangling ref to f():
+      // {.i=1, .r=(int &) &TARGET_EXPR <D.2118, f ()>}
+
+int
+main ()
+{
+  B b;
+  if (b.a.i != 1 || b.a.j != 2)
+    __builtin_abort ();
+  E e;
+  if (e.c.i != 1)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init3.C gcc/testsuite/g++.dg/cpp2a/paren-init3.C
new file mode 100644
index 00000000000..f444005a09f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init3.C
@@ -0,0 +1,11 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+class a {
+  int b{};
+};
+class c {
+  c();
+  a d;
+};
+c::c() {}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init4.C gcc/testsuite/g++.dg/cpp2a/paren-init4.C
new file mode 100644
index 00000000000..f8c7bd10b63
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init4.C
@@ -0,0 +1,142 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+// Test T[]().
+
+int i;
+int a1[](1, 2, 3);
+static_assert(sizeof(a1) == 3 * sizeof (int), "");
+int a2[](1.0, 2, 3);
+static_assert(sizeof(a2) == 3 * sizeof (int), "");
+int a3[3](1, 2, 3);
+int a4[3](1, 2); // a4[2] is value-initialized.
+int a5[](++i, ++i);
+static_assert(sizeof(a5) == 2 * sizeof (int), "");
+int a6[](1);
+static_assert(sizeof(a6) == sizeof (int), "");
+int a7[]({});
+static_assert(sizeof(a7) == sizeof (int), "");
+int a8[]({}, {}, {}, {}, {}, {});
+static_assert(sizeof(a8) == 6 * sizeof (int), "");
+int a9[]((1));
+static_assert(sizeof(a9) == sizeof (int), "");
+int a10[]((1), (2), (3));
+static_assert(sizeof(a10) == 3 * sizeof (int), "");
+int a11[][2]{1};
+static_assert(sizeof(a11) == 2 * sizeof (int), "");
+int a12[][2]({1, 2}, {3, 4});
+static_assert(sizeof(a12) == 4 * sizeof (int), "");
+
+const int (&ra1)[](1, 2);
+const int (&ra2)[](1.0, 2);
+const int (&ra3)[2](1.0, 2);
+int (&&rra1)[](1, 2);
+int (&&rra2)[](1.0, 2);
+int (&&rra3)[2](1.0, 2);
+
+struct S { int i; } s;
+S s1[]({1});
+static_assert(sizeof(s1) == sizeof (S), "");
+S s2[]({1}, {2});
+static_assert(sizeof(s2) == 2 * sizeof (S), "");
+S s3[]({1}, {2}, {3});
+static_assert(sizeof(s3) == 3 * sizeof (S), "");
+S s4[3]({1}, {2});
+static_assert(sizeof(s4) == 3 * sizeof (S), "");
+S s5[]({++i}, {++i});
+static_assert(sizeof(s2) == 2 * sizeof (S), "");
+S s6[](s, s);
+
+struct R { int i; int j; };
+R r1[]({1, 2});
+static_assert(sizeof(r1) == sizeof (R), "");
+R r2[]({1, 2}, {3, 4});
+static_assert(sizeof(r2) == 2 * sizeof (R), "");
+R r3[]({1.0, 2}, {3.2, 4});
+static_assert(sizeof(r3) == 2 * sizeof (R), "");
+
+char c1[]('r');
+char c2[]('r', 'a', 'c', 'c', 'o', 'o', 'n');
+char c3[]("yarrow");
+char c4[4]("oak");
+char c5[10]("cat");
+const char (&c6)[4]("eel");
+
+int g;
+struct X {
+  int i;
+  X() { ++g; }
+  X(int) { };
+};
+
+int
+main ()
+{
+  // Here we'll value-initialize l[1] and l[2], which will zero-initialize it.
+  int l[3](42);
+  if (l[0] != 42 || l[1] != 0 || l[2] != 0)
+    __builtin_abort ();
+
+  // Here we'll value-initialize x[2] and x[3].  Since X is a class type
+  // with a user-provided ctor, we'll default-initialize in both cases.
+  X x[4]({ 1 }, { 2 });
+  if (g != 2)
+    __builtin_abort ();
+
+  if (a1[0] != 1 || a1[1] != 2 || a1[2] != 3)
+    __builtin_abort ();
+  if (a2[0] != 1 || a2[1] != 2 || a2[2] != 3)
+    __builtin_abort ();
+  if (a3[0] != 1 || a3[1] != 2 || a3[2] != 3)
+    __builtin_abort ();
+  if (a4[0] != 1 || a4[1] != 2 || a4[2] != 0)
+    __builtin_abort ();
+  if (a5[0] != 1 || a5[1] != 2)
+    __builtin_abort ();
+  if (a6[0] != 1)
+    __builtin_abort ();
+  if (a7[0] != 0)
+    __builtin_abort ();
+  if (a8[0] != 0 || a8[1] != 0 || a8[2] != 0 || a8[3] != 0
+      || a8[4] != 0 || a8[5] != 0)
+    __builtin_abort ();
+  if (a9[0] != 1)
+    __builtin_abort ();
+  if (a10[0] != 1 || a10[1] != 2 || a10[2] != 3)
+    __builtin_abort ();
+  if (a11[0][0] != 1 || a11[0][1] != 0)
+    __builtin_abort ();
+  if (a12[0][0] != 1 || a12[0][1] != 2 || a12[1][0] != 3 || a12[1][1] != 4)
+    __builtin_abort ();
+
+  if (ra1[0] != 1 || ra1[1] != 2)
+    __builtin_abort ();
+  if (ra2[0] != 1 || ra2[1] != 2)
+    __builtin_abort ();
+  if (ra3[0] != 1 || ra3[1] != 2)
+    __builtin_abort ();
+  if (rra1[0] != 1 || rra1[1] != 2)
+    __builtin_abort ();
+  if (rra2[0] != 1 || rra2[1] != 2)
+    __builtin_abort ();
+  if (rra3[0] != 1 || rra3[1] != 2)
+    __builtin_abort ();
+
+  if (s1[0].i != 1)
+    __builtin_abort ();
+  if (s2[0].i != 1 || s2[1].i != 2)
+    __builtin_abort ();
+  if (s3[0].i != 1 || s3[1].i != 2 || s3[2].i != 3)
+    __builtin_abort ();
+  if (s4[0].i != 1 || s4[1].i != 2 || s4[2].i != 0)
+    __builtin_abort ();
+  if (s5[0].i != 3 || s5[1].i != 4)
+    __builtin_abort ();
+
+  if (r1[0].i != 1 || r1[0].j != 2)
+    __builtin_abort ();
+  if (r2[0].i != 1 || r2[0].j != 2 || r2[1].i != 3 || r2[1].j != 4)
+    __builtin_abort ();
+  if (r3[0].i != 1 || r3[0].j != 2 || r3[1].i != 3 || r3[1].j != 4)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init5.C gcc/testsuite/g++.dg/cpp2a/paren-init5.C
new file mode 100644
index 00000000000..a64cb00ebbc
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init5.C
@@ -0,0 +1,25 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+union U {
+  int a;
+  float b;
+};
+
+// u1 has no active member
+U u1;
+// u2 zero-initializes the first member, so u2.a is the active member and
+// its value is 0.
+U u2 = U();
+// u3 uses non-list aggregate initialization, so u3.a is the active member
+// and its value is 1.
+U u3 = U(1);
+
+int
+main ()
+{
+  if (u2.a != 0)
+    __builtin_abort ();
+  if (u3.a != 1)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init6.C gcc/testsuite/g++.dg/cpp2a/paren-init6.C
new file mode 100644
index 00000000000..b5d97dcb3d8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init6.C
@@ -0,0 +1,14 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test that we don't perform lifetime extension for () init.
+
+struct A {
+  int a;
+  int&& r;
+};
+
+int f();
+A a(1, f());
+
+// { dg-final { scan-assembler-not "_ZGR1a" } }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init7.C gcc/testsuite/g++.dg/cpp2a/paren-init7.C
new file mode 100644
index 00000000000..32af1a7265c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init7.C
@@ -0,0 +1,20 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+int h;
+struct i {
+  i() {}
+  explicit i(i &) {}
+  template <typename j> i(j &) { h++; }
+};
+
+int main() {
+  {
+    i a[6];
+    auto [b, c, d, e, f, g] = a;
+  }
+  i a[6];
+  auto [b, c, d, e, f, g](a);
+  if (h != 6)
+    __builtin_abort();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init8.C gcc/testsuite/g++.dg/cpp2a/paren-init8.C
new file mode 100644
index 00000000000..30e71650bc9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init8.C
@@ -0,0 +1,13 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test that we don't accept designated inits in ( ).
+
+struct S {
+  int i;
+  int j = 42;
+};
+
+S s(.i = 12); // { dg-error "expected" }
+
+int a[]([0] = 42); // { dg-error "" }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init9.C gcc/testsuite/g++.dg/cpp2a/paren-init9.C
new file mode 100644
index 00000000000..c44b206feb8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init9.C
@@ -0,0 +1,10 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct B { };
+struct A : B {
+  int i;
+};
+
+B b;
+A a(b);
diff --git gcc/testsuite/g++.dg/ext/desig10.C gcc/testsuite/g++.dg/ext/desig10.C
index 24057dd0ad6..c8d672b72d9 100644
--- gcc/testsuite/g++.dg/ext/desig10.C
+++ gcc/testsuite/g++.dg/ext/desig10.C
@@ -1,4 +1,4 @@
 // PR c++/84972
 // { dg-additional-options "-w" }
 
-char(a[])({.a = 0});  // { dg-error "designated initializer" }
+char(a[])({.a = 0});  // { dg-error "designated initializer|cannot convert" }
diff --git gcc/testsuite/g++.dg/template/crash107.C gcc/testsuite/g++.dg/template/crash107.C
index 3b0b4e8211f..9d8d394d93d 100644
--- gcc/testsuite/g++.dg/template/crash107.C
+++ gcc/testsuite/g++.dg/template/crash107.C
@@ -3,7 +3,7 @@
 // { dg-options "" }
 // { dg-additional-options "-Wno-return-type" }
 
-template<typename FP_> struct Vec { // { dg-message "note" }
+template<typename FP_> struct Vec { // { dg-message "note" "" { target c++17_down } }
     Vec& operator^=(Vec& rhs)     {
         union {
             struct {FP_ x,y,z;}; // { dg-error "20:anonymous struct" }
@@ -14,6 +14,6 @@ template<typename FP_> struct Vec { // { dg-message "note" }
         return Vec(*this)^=rhs; // { dg-message "required" }
     }
 };
-Vec<double> v(3,4,12); // { dg-error "no matching" }
-Vec<double> V(12,4,3);  // { dg-error "no matching" }
+Vec<double> v(3,4,12); // { dg-error "no matching|too many initializers" }
+Vec<double> V(12,4,3);  // { dg-error "no matching|too many initializers" }
 Vec<double> c = v^V;   // { dg-message "required" }
diff --git gcc/testsuite/g++.dg/template/crash95.C gcc/testsuite/g++.dg/template/crash95.C
index f60e635ae66..47346111328 100644
--- gcc/testsuite/g++.dg/template/crash95.C
+++ gcc/testsuite/g++.dg/template/crash95.C
@@ -8,4 +8,4 @@ template < typename > struct S
   };
 };
 
-S < int > s(0); // { dg-error "no matching" }
+S < int > s(0); // { dg-error "no matching|too many initializers" }
diff --git gcc/testsuite/g++.old-deja/g++.law/ctors11.C gcc/testsuite/g++.old-deja/g++.law/ctors11.C
index 39ee76b0ae7..b29b18a62a3 100644
--- gcc/testsuite/g++.old-deja/g++.law/ctors11.C
+++ gcc/testsuite/g++.old-deja/g++.law/ctors11.C
@@ -10,12 +10,12 @@ public:
   inline A(int x){printf("constructing A with %d\n", x);}
 };
 
-class B:public A{ // { dg-message "note" } non-default constructor
+class B:public A{ // { dg-message "note" "" { target c++17_down } } non-default constructor
 private:
 public:
 };
 
 int main()
 {
-  B(10);// { dg-error "match" } B doesn't have a constructor taking int
+  B(10);// { dg-error "match" "" { target c++17_down } } B doesn't have a constructor taking int
 }
diff --git gcc/testsuite/g++.old-deja/g++.law/ctors9.C gcc/testsuite/g++.old-deja/g++.law/ctors9.C
index 43ba1262c95..5856d5dc883 100644
--- gcc/testsuite/g++.old-deja/g++.law/ctors9.C
+++ gcc/testsuite/g++.old-deja/g++.law/ctors9.C
@@ -20,7 +20,7 @@ Foo::Foo(int aa)
 { }
 
 
-struct var_Foo: public Foo // { dg-message "note" }  base.*// ERROR -  in class.*
+struct var_Foo: public Foo // { dg-message "note" "" { target c++17_down } }  base.*// ERROR -  in class.*
 {
   var_Foo* operator-> () {return this;}
 };
@@ -32,7 +32,7 @@ int blort(Foo& f)
 
 int main()
 {
-  var_Foo b(2);// { dg-error "match" } 
+  var_Foo b(2);// { dg-error "match" "" { target c++17_down } } 
   b->a = 0;
   int x = blort(b);
   return x;
diff --git gcc/testsuite/g++.old-deja/g++.mike/net22.C gcc/testsuite/g++.old-deja/g++.mike/net22.C
index e5e1cb1081d..abbf6191023 100644
--- gcc/testsuite/g++.old-deja/g++.mike/net22.C
+++ gcc/testsuite/g++.old-deja/g++.mike/net22.C
@@ -5,10 +5,11 @@ public:
   Parent( char *s ) {}
 };
 
-class Child : public Parent {		// { dg-message "note" } called
+class Child : public Parent {		// { dg-message "note" "" { target c++17_down } } called
 };
 
 int main() {
-  Child c( "String initializer" );	// { dg-error "match" } bad
+  Child c( "String initializer" );	// { dg-error "match" "" { target c++17_down } } bad
+// { dg-error "forbids converting a string constant" "" { target c++2a } .-1 }
   return 0;
 }
diff --git gcc/testsuite/g++.old-deja/g++.niklas/t128.C gcc/testsuite/g++.old-deja/g++.niklas/t128.C
index 19e3ca1dab0..0b3346c4ef3 100644
--- gcc/testsuite/g++.old-deja/g++.niklas/t128.C
+++ gcc/testsuite/g++.old-deja/g++.niklas/t128.C
@@ -1,5 +1,5 @@
 // { dg-do assemble  }
 // GROUPS niklas uncaught default-construct
 struct A { A (int); };
-struct B : A {}; // { dg-message "note" } without ctor // ERROR - candidates
-void f () { B (0); }// { dg-error "match" } .*
+struct B : A {}; // { dg-message "note" "" { target c++17_down } } without ctor // ERROR - candidates
+void f () { B (0); }// { dg-error "match" "" { target c++17_down } } .*
Jason Merrill Nov. 21, 2019, 1:11 a.m. | #7
On 11/20/19 8:37 PM, Marek Polacek wrote:
> On Tue, Nov 19, 2019 at 05:33:09PM -0500, Jason Merrill wrote:

>> On 11/19/19 1:44 AM, Marek Polacek wrote:

>>>> It also looks like you're using the LOOKUP flag to mean two different

>>>> things:

>>>>

>>>> 1) try to treat parenthesized args as an aggregate initializer

>>>> (build_new_method_call_1)

>>>> 2) treat this CONSTRUCTOR as coming from parenthesized args

>>>> (store_init_value/digest_init)

>>>

>>> Correct.

>>>

>>>> Why is the flag needed for #1?  When do we not want to try to treat the args

>>>> as an aggregate initializer?

>>>

>>> There are cases where treating the args as an aggregate initializer causes

>>> spurious overload resolution successes, e.g.

>>>

>>>    void swap(int&, int&);

>>>

>>>    int& get();

>>>

>>>    struct pair {

>>>      void swap(pair&) noexcept(noexcept(swap(get(), get()))) { } // { dg-error "no matching function for call" }

>>>    };

>>>

>>> There are no viable candidates for pair::swap (# of args mismatch) but since

>>> pair is an aggregate, build_new_method_call_1 would return a CONSTRUCTOR so

>>> overload resolution would succeed.  Another case had to do with SFINAE and

>>> decltype where we didn't evaluate the arg, but succeeding in the

>>> no-viable-function case caused the compiler to choose the wrong function.

>>

>> Hmm, but then the parenthesized list is arguments for swap, not an

>> initializer for a single argument of swap.  That would be using it for

>> copy-initialization, and we only want to treat parenthesized args as an

>> aggregate initializer in direct-initialization.  Can we check for

>> direct-init (i.e. !LOOKUP_ONLYCONVERTING) instead?

> 

> Unfortunately that doesn't work.  We call build_new_method_call from context

> where LOOKUP_ONLYCONVERTING isn't set and so it would still break things.


How so?  If we call it for a constructor, surely we can check that flag 
to distinguish between copy- and direct-initialization, or I'd expect 
wrong behavior wrt explicit.

>>> +  if (BRACE_ENCLOSED_INITIALIZER_P (exp))

>>> +    {

>>> +      gcc_assert (cxx_dialect >= cxx2a);

>>> +      return finish_compound_literal (type, exp, complain,

>>> +				      fcl_functional_paren);

>>> +    }

>>

>> How about handling this in build_cplus_new instead of places that also call

>> build_cplus_new?

> 

> Is it really what we want?  We now have two spots where we need to handle

> the case when build_special_member_call returns a BRACE_ENCLOSED_INITIALIZER_P

> but build_cplus_new is called in many other spots where we don't expect to see

> a CONSTRUCTOR.


I think build_cplus_new should be able to handle whatever 
build_special_member_call returns.

> @@ -921,8 +921,20 @@ perform_member_init (tree member, tree init)

>   		inform (DECL_SOURCE_LOCATION (member),

>   			"%q#D should be initialized", member );

>   	    }

> -	  finish_expr_stmt (build_aggr_init (decl, init, flags,

> -					     tf_warning_or_error));

> +	  init = build_aggr_init (decl, init, flags, tf_warning_or_error);

> +	  /* In C++20, a member initializer list can be initializing an

> +	     aggregate from a parenthesized list of values:

> +

> +	       struct S {

> +		 A aggr;

> +		 S() : aggr(1, 2, 3) { }

> +	       };

> +

> +	      In such case, build_aggr_init will build up an INIT_EXPR like

> +	      we do for aggr{1, 2, 3}, so that build_data_member_initialization

> +	      can grok it.  */

> +	  if (TREE_CODE (init) != INIT_EXPR)

> +	    finish_expr_stmt (init);


Why don't we want to finish_expr_stmt an INIT_EXPR?

> @@ -1272,8 +1274,13 @@ digest_init_r (tree type, tree init, int nested, int flags,

>   		inform (loc, "remove %<{ }%> around initializer");

>   	    }

>   	  else if (flag_checking)

> -	    /* We should have fixed this in reshape_init.  */

> -	    gcc_unreachable ();

> +	    /* We should have fixed this in reshape_init.  Except that we

> +	       don't reshape parenthesized lists where brace elision is

> +	       not permitted.  */

> +	    {

> +	      gcc_assert (flags & LOOKUP_AGGREGATE_PAREN_INIT);

> +	      return error_mark_node;


We shouldn't get here for parenthesized lists, either, I thnk; we should 
have called the copy constructor rather than try aggregate initialization.

> @@ -1319,12 +1326,19 @@ digest_init_r (tree type, tree init, int nested, int flags,

>   tree

>   digest_init (tree type, tree init, tsubst_flags_t complain)

>   {

> -  return digest_init_r (type, init, 0, LOOKUP_IMPLICIT, complain);

> +  int flags = LOOKUP_IMPLICIT;

> +  if (BRACE_ENCLOSED_INITIALIZER_P (init)

> +      && CONSTRUCTOR_IS_PAREN_INIT (init))

> +    flags |= LOOKUP_AGGREGATE_PAREN_INIT;

> +  return digest_init_r (type, init, 0, flags, complain);

>   }

>   

>   tree

>   digest_init_flags (tree type, tree init, int flags, tsubst_flags_t complain)

>   {

> +  if (BRACE_ENCLOSED_INITIALIZER_P (init)

> +      && CONSTRUCTOR_IS_PAREN_INIT (init))

> +    flags |= LOOKUP_AGGREGATE_PAREN_INIT;


Maybe do this in digest_init_r?  What happens if we have a braced 
aggregate initializer containing a parenthesized aggregate initializer, e.g.

{ aggr(1,2,3) }

Jason
Marek Polacek Nov. 27, 2019, 6:58 p.m. | #8
On Wed, Nov 20, 2019 at 08:11:27PM -0500, Jason Merrill wrote:
> On 11/20/19 8:37 PM, Marek Polacek wrote:

> > On Tue, Nov 19, 2019 at 05:33:09PM -0500, Jason Merrill wrote:

> > > On 11/19/19 1:44 AM, Marek Polacek wrote:

> > > > > It also looks like you're using the LOOKUP flag to mean two different

> > > > > things:

> > > > > 

> > > > > 1) try to treat parenthesized args as an aggregate initializer

> > > > > (build_new_method_call_1)

> > > > > 2) treat this CONSTRUCTOR as coming from parenthesized args

> > > > > (store_init_value/digest_init)

> > > > 

> > > > Correct.

> > > > 

> > > > > Why is the flag needed for #1?  When do we not want to try to treat the args

> > > > > as an aggregate initializer?

> > > > 

> > > > There are cases where treating the args as an aggregate initializer causes

> > > > spurious overload resolution successes, e.g.

> > > > 

> > > >    void swap(int&, int&);

> > > > 

> > > >    int& get();

> > > > 

> > > >    struct pair {

> > > >      void swap(pair&) noexcept(noexcept(swap(get(), get()))) { } // { dg-error "no matching function for call" }

> > > >    };

> > > > 

> > > > There are no viable candidates for pair::swap (# of args mismatch) but since

> > > > pair is an aggregate, build_new_method_call_1 would return a CONSTRUCTOR so

> > > > overload resolution would succeed.  Another case had to do with SFINAE and

> > > > decltype where we didn't evaluate the arg, but succeeding in the

> > > > no-viable-function case caused the compiler to choose the wrong function.

> > > 

> > > Hmm, but then the parenthesized list is arguments for swap, not an

> > > initializer for a single argument of swap.  That would be using it for

> > > copy-initialization, and we only want to treat parenthesized args as an

> > > aggregate initializer in direct-initialization.  Can we check for

> > > direct-init (i.e. !LOOKUP_ONLYCONVERTING) instead?

> > 

> > Unfortunately that doesn't work.  We call build_new_method_call from context

> > where LOOKUP_ONLYCONVERTING isn't set and so it would still break things.

> 

> How so?  If we call it for a constructor, surely we can check that flag to

> distinguish between copy- and direct-initialization, or I'd expect wrong

> behavior wrt explicit.


[ Sorry for the delayed response, been too busy with other stuff.  Hope to be
more prompt from now on. ]

Indeed, if I check DECL_CONSTRUCTOR_P, it works.  But I was confused by a test
like the newly-added paren-init17.C.  Here we're checking if A can be trivially
initialized by B, so constructible_expr builds up a build_stub_object object
and calls build_special_member_call.  It doesn't find a viable constructor for
A, but A is an aggregate and there are arguments, so it does parenthesized
initialization, and then we think A can be trivially initialized from B.  But
I don't think we should attempt to handle this case.  I also saw a similar
problem with decltype/declval.  Fixed by checking cp_unevaluated_operand...

Another difference is that g++.old-deja/g++.jason/crash3.C now passes in
C++20.  But that should be fine.

> > > > +  if (BRACE_ENCLOSED_INITIALIZER_P (exp))

> > > > +    {

> > > > +      gcc_assert (cxx_dialect >= cxx2a);

> > > > +      return finish_compound_literal (type, exp, complain,

> > > > +				      fcl_functional_paren);

> > > > +    }

> > > 

> > > How about handling this in build_cplus_new instead of places that also call

> > > build_cplus_new?

> > 

> > Is it really what we want?  We now have two spots where we need to handle

> > the case when build_special_member_call returns a BRACE_ENCLOSED_INITIALIZER_P

> > but build_cplus_new is called in many other spots where we don't expect to see

> > a CONSTRUCTOR.

> 

> I think build_cplus_new should be able to handle whatever

> build_special_member_call returns.


Ok, adjusted.

> > @@ -921,8 +921,20 @@ perform_member_init (tree member, tree init)

> >   		inform (DECL_SOURCE_LOCATION (member),

> >   			"%q#D should be initialized", member );

> >   	    }

> > -	  finish_expr_stmt (build_aggr_init (decl, init, flags,

> > -					     tf_warning_or_error));

> > +	  init = build_aggr_init (decl, init, flags, tf_warning_or_error);

> > +	  /* In C++20, a member initializer list can be initializing an

> > +	     aggregate from a parenthesized list of values:

> > +

> > +	       struct S {

> > +		 A aggr;

> > +		 S() : aggr(1, 2, 3) { }

> > +	       };

> > +

> > +	      In such case, build_aggr_init will build up an INIT_EXPR like

> > +	      we do for aggr{1, 2, 3}, so that build_data_member_initialization

> > +	      can grok it.  */

> > +	  if (TREE_CODE (init) != INIT_EXPR)

> > +	    finish_expr_stmt (init);

> 

> Why don't we want to finish_expr_stmt an INIT_EXPR?


Because expand_default_init has already finish_expr_stmt-ed it.  Adding it
twice sounds wrong.

> > @@ -1272,8 +1274,13 @@ digest_init_r (tree type, tree init, int nested, int flags,

> >   		inform (loc, "remove %<{ }%> around initializer");

> >   	    }

> >   	  else if (flag_checking)

> > -	    /* We should have fixed this in reshape_init.  */

> > -	    gcc_unreachable ();

> > +	    /* We should have fixed this in reshape_init.  Except that we

> > +	       don't reshape parenthesized lists where brace elision is

> > +	       not permitted.  */

> > +	    {

> > +	      gcc_assert (flags & LOOKUP_AGGREGATE_PAREN_INIT);

> > +	      return error_mark_node;

> 

> We shouldn't get here for parenthesized lists, either, I thnk; we should

> have called the copy constructor rather than try aggregate initialization.


True.  I'm not sure why I hit it before but I removed this hunk now.

> > @@ -1319,12 +1326,19 @@ digest_init_r (tree type, tree init, int nested, int flags,

> >   tree

> >   digest_init (tree type, tree init, tsubst_flags_t complain)

> >   {

> > -  return digest_init_r (type, init, 0, LOOKUP_IMPLICIT, complain);

> > +  int flags = LOOKUP_IMPLICIT;

> > +  if (BRACE_ENCLOSED_INITIALIZER_P (init)

> > +      && CONSTRUCTOR_IS_PAREN_INIT (init))

> > +    flags |= LOOKUP_AGGREGATE_PAREN_INIT;

> > +  return digest_init_r (type, init, 0, flags, complain);

> >   }

> >   tree

> >   digest_init_flags (tree type, tree init, int flags, tsubst_flags_t complain)

> >   {

> > +  if (BRACE_ENCLOSED_INITIALIZER_P (init)

> > +      && CONSTRUCTOR_IS_PAREN_INIT (init))

> > +    flags |= LOOKUP_AGGREGATE_PAREN_INIT;

> 

> Maybe do this in digest_init_r?  


Done.

> What happens if we have a braced aggregate

> initializer containing a parenthesized aggregate initializer, e.g.

> 

> { aggr(1,2,3) }


Seems to work as expected.  I've added paren-init18.C to specifically test it.

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

2019-11-27  Marek Polacek  <polacek@redhat.com>

	PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
	* c-cppbuiltin.c (c_cpp_builtins): Predefine
	__cpp_aggregate_paren_init=201902 for -std=c++2a.

	* call.c (build_new_method_call_1): Handle parenthesized initialization
	of aggregates by building up a CONSTRUCTOR.
	(extend_ref_init_temps): Do nothing for CONSTRUCTOR_IS_PAREN_INIT.
	* cp-tree.h (CONSTRUCTOR_IS_PAREN_INIT, LOOKUP_AGGREGATE_PAREN_INIT):
	Define.
	* decl.c (grok_reference_init): Handle aggregate initialization from
	a parenthesized list of values.
	(reshape_init): Do nothing for CONSTRUCTOR_IS_PAREN_INIT.
	(check_initializer): Handle initialization of an array from a
	parenthesized list of values.  Use NULL_TREE instead of NULL.
	* init.c (perform_member_init): Don't call finish_expr_stmt if
	build_aggr_init returns an INIT_EXPR.
	(expand_default_init): Set LOOKUP_AGGREGATE_PAREN_INIT for the
	build_special_member_call call.  If it returns a
	BRACE_ENCLOSED_INITIALIZER_P, call digest_init and build an INIT_EXPR.
	Set TREE_SIDE_EFFECTS on it.
	* tree.c (build_cplus_new): Handle BRACE_ENCLOSED_INITIALIZER_P.
	* typeck2.c (digest_init_r): Set LOOKUP_AGGREGATE_PAREN_INIT if it
	receives a CONSTRUCTOR with CONSTRUCTOR_IS_PAREN_INIT set.  Allow
	narrowing when LOOKUP_AGGREGATE_PAREN_INIT.
	(massage_init_elt): Don't lose LOOKUP_AGGREGATE_PAREN_INIT when passing
	flags to digest_init_r.

	* g++.dg/cpp0x/constexpr-99.C: Only expect an error in C++17 and
	lesser.
	* g++.dg/cpp0x/explicit7.C: Likewise.
	* g++.dg/cpp0x/initlist12.C: Adjust dg-error.
	* g++.dg/cpp0x/pr31437.C: Likewise.
	* g++.dg/cpp2a/feat-cxx2a.C: Add __cpp_aggregate_paren_init test.
	* g++.dg/cpp2a/paren-init1.C: New test.
	* g++.dg/cpp2a/paren-init10.C: New test.
	* g++.dg/cpp2a/paren-init11.C: New test.
	* g++.dg/cpp2a/paren-init12.C: New test.
	* g++.dg/cpp2a/paren-init13.C: New test.
	* g++.dg/cpp2a/paren-init14.C: New test.
	* g++.dg/cpp2a/paren-init15.C: New test.
	* g++.dg/cpp2a/paren-init16.C: New test.
	* g++.dg/cpp2a/paren-init17.C: New test.
	* g++.dg/cpp2a/paren-init18.C: New test.
	* g++.dg/cpp2a/paren-init2.C: New test.
	* g++.dg/cpp2a/paren-init3.C: New test.
	* g++.dg/cpp2a/paren-init4.C: New test.
	* g++.dg/cpp2a/paren-init5.C: New test.
	* g++.dg/cpp2a/paren-init6.C: New test.
	* g++.dg/cpp2a/paren-init7.C: New test.
	* g++.dg/cpp2a/paren-init8.C: New test.
	* g++.dg/cpp2a/paren-init9.C: New test.
	* g++.dg/ext/desig10.C: Adjust dg-error.
	* g++.dg/template/crash107.C: Likewise.
	* g++.dg/template/crash95.C: Likewise.
	* g++.old-deja/g++.jason/crash3.C: Only expect an error in C++17 and
	lesser.
	* g++.old-deja/g++.law/ctors11.C: Likewise.
	* g++.old-deja/g++.law/ctors9.C: Likewise.
	* g++.old-deja/g++.mike/net22.C: Likewise.
	* g++.old-deja/g++.niklas/t128.C: Likewise.

diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c
index 50066e4dd8b..2f30b2a3235 100644
--- gcc/c-family/c-cppbuiltin.c
+++ gcc/c-family/c-cppbuiltin.c
@@ -1004,6 +1004,7 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_impl_destroying_delete=201806L");
 	  cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907L");
 	  cpp_define (pfile, "__cpp_impl_three_way_comparison=201907L");
+	  cpp_define (pfile, "__cpp_aggregate_paren_init=201902L");
 	}
       if (flag_concepts)
         {
diff --git gcc/cp/call.c gcc/cp/call.c
index 8bfe3368816..1d14409ec43 100644
--- gcc/cp/call.c
+++ gcc/cp/call.c
@@ -10111,6 +10111,30 @@ build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args,
 
   if (!any_viable_p)
     {
+      /* [dcl.init], 17.6.2.2:
+
+	 Otherwise, if no constructor is viable, the destination type is
+	 a (possibly cv-qualified) aggregate class A, and the initializer
+	 is a parenthesized expression-list, the object is initialized as
+	 follows...
+
+	 We achieve this by building up a CONSTRUCTOR, as for list-init,
+	 and setting CONSTRUCTOR_IS_PAREN_INIT to distinguish between
+	 the two.  */
+      if (DECL_CONSTRUCTOR_P (fn)
+	  && !(flags & LOOKUP_ONLYCONVERTING)
+	  && !cp_unevaluated_operand
+	  && cxx_dialect >= cxx2a
+	  && CP_AGGREGATE_TYPE_P (basetype)
+	  && !user_args->is_empty ())
+	{
+	  /* Create a CONSTRUCTOR from ARGS, e.g. {1, 2} from <1, 2>.  */
+	  tree list = build_tree_list_vec (user_args);
+	  tree ctor = build_constructor_from_list (init_list_type_node, list);
+	  CONSTRUCTOR_IS_DIRECT_INIT (ctor) = true;
+	  CONSTRUCTOR_IS_PAREN_INIT (ctor) = true;
+	  return ctor;
+	}
       if (complain & tf_error)
 	complain_about_no_candidates_for_method_call (instance, candidates,
 						      explicit_targs, basetype,
@@ -11792,9 +11816,16 @@ perform_direct_initialization_if_possible (tree type,
      If the destination type is a (possibly cv-qualified) class type:
 
      -- If the initialization is direct-initialization ...,
-     constructors are considered. ... If no constructor applies, or
-     the overload resolution is ambiguous, the initialization is
-     ill-formed.  */
+     constructors are considered.
+
+       -- If overload resolution is successful, the selected constructor
+       is called to initialize the object, with the initializer expression
+       or expression-list as its argument(s).
+
+       -- Otherwise, if no constructor is viable, the destination type is
+       a (possibly cv-qualified) aggregate class A, and the initializer is
+       a parenthesized expression-list, the object is initialized as
+       follows...  */
   if (CLASS_TYPE_P (type))
     {
       releasing_vec args (make_tree_vector_single (expr));
@@ -12150,6 +12181,12 @@ extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups)
 	ctor = TARGET_EXPR_INITIAL (ctor);
       if (TREE_CODE (ctor) == CONSTRUCTOR)
 	{
+	  /* [dcl.init] When initializing an aggregate from a parenthesized list
+	     of values... a temporary object bound to a reference does not have
+	     its lifetime extended.  */
+	  if (CONSTRUCTOR_IS_PAREN_INIT (ctor))
+	    return init;
+
 	  if (is_std_init_list (type))
 	    {
 	      /* The temporary array underlying a std::initializer_list
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index 4b4bc245d81..4aa871a21c0 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -4325,6 +4325,11 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define CONSTRUCTOR_IS_DESIGNATED_INIT(NODE) \
   (TREE_LANG_FLAG_6 (CONSTRUCTOR_CHECK (NODE)))
 
+/* True if this CONSTRUCTOR comes from a parenthesized list of values, e.g.
+   A(1, 2, 3).  */
+#define CONSTRUCTOR_IS_PAREN_INIT(NODE) \
+  (CONSTRUCTOR_CHECK(NODE)->base.private_flag)
+
 /* True if NODE represents a conversion for direct-initialization in a
    template.  Set by perform_implicit_conversion_flags.  */
 #define IMPLICIT_CONV_EXPR_DIRECT_INIT(NODE) \
@@ -5583,6 +5588,8 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
    args), then we swap the conversions back in build_new_op_1 (so they
    correspond to the order of the args in the candidate).  */
 #define LOOKUP_REVERSED (LOOKUP_REWRITTEN << 1)
+/* We're initializing an aggregate from a parenthesized list of values.  */
+#define LOOKUP_AGGREGATE_PAREN_INIT (LOOKUP_REVERSED << 1)
 
 #define LOOKUP_NAMESPACES_ONLY(F)  \
   (((F) & LOOKUP_PREFER_NAMESPACES) && !((F) & LOOKUP_PREFER_TYPES))
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 26120720f07..33cd474f834 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -5531,11 +5531,28 @@ grok_reference_init (tree decl, tree type, tree init, int flags)
       return NULL_TREE;
     }
 
+  tree ttype = TREE_TYPE (type);
   if (TREE_CODE (init) == TREE_LIST)
-    init = build_x_compound_expr_from_list (init, ELK_INIT,
-					    tf_warning_or_error);
+    {
+      /* This handles (C++20 only) code like
+
+	   const A& r(1, 2, 3);
+
+	 where we treat the parenthesized list as a CONSTRUCTOR.  */
+      if (TREE_TYPE (init) == NULL_TREE
+	  && CP_AGGREGATE_TYPE_P (ttype)
+	  && !DECL_DECOMPOSITION_P (decl)
+	  && (cxx_dialect >= cxx2a))
+	{
+	  init = build_constructor_from_list (init_list_type_node, init);
+	  CONSTRUCTOR_IS_DIRECT_INIT (init) = true;
+	  CONSTRUCTOR_IS_PAREN_INIT (init) = true;
+	}
+      else
+	init = build_x_compound_expr_from_list (init, ELK_INIT,
+						tf_warning_or_error);
+    }
 
-  tree ttype = TREE_TYPE (type);
   if (TREE_CODE (ttype) != ARRAY_TYPE
       && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE)
     /* Note: default conversion is only called in very special cases.  */
@@ -6437,6 +6454,11 @@ reshape_init (tree type, tree init, tsubst_flags_t complain)
   if (vec_safe_is_empty (v))
     return init;
 
+  /* Brace elision is not performed for a CONSTRUCTOR representing
+     parenthesized aggregate initialization.  */
+  if (CONSTRUCTOR_IS_PAREN_INIT (init))
+    return init;
+
   /* Handle [dcl.init.list] direct-list-initialization from
      single element of enumeration with a fixed underlying type.  */
   if (is_direct_enum_init (type, init))
@@ -6640,6 +6662,41 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
 	      flags |= LOOKUP_NO_NARROWING;
 	    }
 	}
+      /* [dcl.init] "Otherwise, if the destination type is an array, the object
+	 is initialized as follows..."  So handle things like
+
+	  int a[](1, 2, 3);
+
+	 which is permitted in C++20 by P0960.  */
+      else if (TREE_CODE (init) == TREE_LIST
+	       && TREE_TYPE (init) == NULL_TREE
+	       && TREE_CODE (type) == ARRAY_TYPE
+	       && !DECL_DECOMPOSITION_P (decl)
+	       && (cxx_dialect >= cxx2a))
+	{
+	  /* [dcl.init.string] "An array of ordinary character type [...]
+	     can be initialized by an ordinary string literal [...] by an
+	     appropriately-typed string literal enclosed in braces" only
+	     talks about braces, but GCC has always accepted
+
+	       char a[]("foobar");
+
+	     so we continue to do so.  */
+	  tree val = TREE_VALUE (init);
+	  if (TREE_CHAIN (init) == NULL_TREE
+	      && char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type)))
+	      && TREE_CODE (tree_strip_any_location_wrapper (val))
+		 == STRING_CST)
+	    /* If the list has a single element and it's a string literal,
+	       then it's the initializer for the array as a whole.  */
+	    init = val;
+	  else
+	    {
+	      init = build_constructor_from_list (init_list_type_node, init);
+	      CONSTRUCTOR_IS_DIRECT_INIT (init) = true;
+	      CONSTRUCTOR_IS_PAREN_INIT (init) = true;
+	    }
+	}
       else if (TREE_CODE (init) == TREE_LIST
 	       && TREE_TYPE (init) != unknown_type_node
 	       && !MAYBE_CLASS_TYPE_P (type))
@@ -6683,6 +6740,9 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
 	    init_code = TREE_OPERAND (init_code, 0);
 	  if (TREE_CODE (init_code) == INIT_EXPR)
 	    {
+	      /* In C++20, the call to build_aggr_init could have created
+		 an INIT_EXPR with a CONSTRUCTOR as the RHS to handle
+		 A(1, 2).  */
 	      init = TREE_OPERAND (init_code, 1);
 	      init_code = NULL_TREE;
 	      /* Don't call digest_init; it's unnecessary and will complain
@@ -6736,7 +6796,7 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
 			0, "array %qD initialized by parenthesized "
 			"string literal %qE",
 			decl, DECL_INITIAL (decl));
-	  init = NULL;
+	  init = NULL_TREE;
 	}
     }
   else
diff --git gcc/cp/init.c gcc/cp/init.c
index aa48f80e58d..16fc47fea4f 100644
--- gcc/cp/init.c
+++ gcc/cp/init.c
@@ -921,8 +921,20 @@ perform_member_init (tree member, tree init)
 		inform (DECL_SOURCE_LOCATION (member),
 			"%q#D should be initialized", member );
 	    }
-	  finish_expr_stmt (build_aggr_init (decl, init, flags,
-					     tf_warning_or_error));
+	  init = build_aggr_init (decl, init, flags, tf_warning_or_error);
+	  /* In C++20, a member initializer list can be initializing an
+	     aggregate from a parenthesized list of values:
+
+	       struct S {
+		 A aggr;
+		 S() : aggr(1, 2, 3) { }
+	       };
+
+	      In such case, build_aggr_init will build up an INIT_EXPR like
+	      we do for aggr{1, 2, 3}, so that build_data_member_initialization
+	      can grok it.  */
+	  if (TREE_CODE (init) != INIT_EXPR)
+	    finish_expr_stmt (init);
 	}
     }
   else
@@ -1967,8 +1979,23 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
       tree ctor_name = (true_exp == exp
 			? complete_ctor_identifier : base_ctor_identifier);
 
+      /* Given class A,
+
+	   A a(1, 2);
+
+	 can mean a call to a constructor A::A(int, int), if present.  If not,
+	 but A is an aggregate, we will try aggregate initialization.  */
       rval = build_special_member_call (exp, ctor_name, &parms, binfo, flags,
 					complain);
+      if (BRACE_ENCLOSED_INITIALIZER_P (rval))
+	{
+	  gcc_assert (cxx_dialect >= cxx2a);
+	  rval = digest_init (type, rval, tf_warning_or_error);
+	  rval = build2 (INIT_EXPR, type, exp, rval);
+	  /* So that we do finish_expr_stmt below.  Don't return here, we
+	     need to release PARMS.  */
+	  TREE_SIDE_EFFECTS (rval) = 1;
+	}
     }
 
   if (parms != NULL)
diff --git gcc/cp/tree.c gcc/cp/tree.c
index d125d60b270..dede4dce36b 100644
--- gcc/cp/tree.c
+++ gcc/cp/tree.c
@@ -669,6 +669,15 @@ build_aggr_init_expr (tree type, tree init)
 tree
 build_cplus_new (tree type, tree init, tsubst_flags_t complain)
 {
+  /* This function should cope with what build_special_member_call
+     can produce.  When performing parenthesized aggregate initialization,
+     it can produce a { }.  */
+  if (BRACE_ENCLOSED_INITIALIZER_P (init))
+    {
+      gcc_assert (cxx_dialect >= cxx2a);
+      return finish_compound_literal (type, init, complain);
+    }
+
   tree rval = build_aggr_init_expr (type, init);
   tree slot;
 
diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
index b8868546444..9c6f25c8608 100644
--- gcc/cp/typeck2.c
+++ gcc/cp/typeck2.c
@@ -1117,6 +1117,10 @@ digest_init_r (tree type, tree init, int nested, int flags,
 
   tree stripped_init = init;
 
+  if (BRACE_ENCLOSED_INITIALIZER_P (init)
+      && CONSTRUCTOR_IS_PAREN_INIT (init))
+    flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+
   /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue
      (g++.old-deja/g++.law/casts2.C).  */
   if (TREE_CODE (init) == NON_LVALUE_EXPR)
@@ -1224,7 +1228,9 @@ digest_init_r (tree type, tree init, int nested, int flags,
   if ((code != COMPLEX_TYPE || BRACE_ENCLOSED_INITIALIZER_P (stripped_init))
       && (SCALAR_TYPE_P (type) || code == REFERENCE_TYPE))
     {
-      if (nested)
+      /* Narrowing is OK when initializing an aggregate from
+	 a parenthesized list.  */
+      if (nested && !(flags & LOOKUP_AGGREGATE_PAREN_INIT))
 	flags |= LOOKUP_NO_NARROWING;
       init = convert_for_initialization (0, type, init, flags,
 					 ICR_INIT, NULL_TREE, 0,
@@ -1384,9 +1390,12 @@ static tree
 massage_init_elt (tree type, tree init, int nested, int flags,
 		  tsubst_flags_t complain)
 {
-  flags &= LOOKUP_ALLOW_FLEXARRAY_INIT;
-  flags |= LOOKUP_IMPLICIT;
-  init = digest_init_r (type, init, nested ? 2 : 1, flags, complain);
+  int new_flags = LOOKUP_IMPLICIT;
+  if (flags & LOOKUP_ALLOW_FLEXARRAY_INIT)
+    new_flags |= LOOKUP_ALLOW_FLEXARRAY_INIT;
+  if (flags & LOOKUP_AGGREGATE_PAREN_INIT)
+    new_flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+  init = digest_init_r (type, init, nested ? 2 : 1, new_flags, complain);
   /* Strip a simple TARGET_EXPR when we know this is an initializer.  */
   if (SIMPLE_TARGET_EXPR_P (init))
     init = TARGET_EXPR_INITIAL (init);
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-99.C gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
index 8d791dd032d..4d3953d0983 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
@@ -9,5 +9,6 @@ struct A
 struct B
 {
   A a;
-    constexpr B() : a(0) {} // { dg-error "no matching function" }
+  // P0960R3 allows paren-init.
+  constexpr B() : a(0) {} // { dg-error "no matching function" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/explicit7.C gcc/testsuite/g++.dg/cpp0x/explicit7.C
index 574796d117d..67b50542bc3 100644
--- gcc/testsuite/g++.dg/cpp0x/explicit7.C
+++ gcc/testsuite/g++.dg/cpp0x/explicit7.C
@@ -10,7 +10,7 @@ struct A { };
 struct B: A { };
 struct C {
   explicit operator B*();	// { dg-message "explicit" }
-  explicit operator B&();	// { dg-message "explicit" }
+  explicit operator B&();	// { dg-message "explicit" "" { target c++17_down } }
 };
 
 C c;
diff --git gcc/testsuite/g++.dg/cpp0x/initlist12.C gcc/testsuite/g++.dg/cpp0x/initlist12.C
index f72a6dac525..c2a2b86520f 100644
--- gcc/testsuite/g++.dg/cpp0x/initlist12.C
+++ gcc/testsuite/g++.dg/cpp0x/initlist12.C
@@ -6,15 +6,15 @@ struct A
   int i;
 };
 
-A a({1,2});			// { dg-error "no match" }
+A a({1,2});			// { dg-error "no match|cannot convert" }
 
 union U
 {
   int i,j;
 };
 
-U u({1,2});			// { dg-error "no match" }
+U u({1,2});			// { dg-error "no match|cannot convert" }
 
 union V {};
 
-V v({1});			// { dg-error "no match" }
+V v({1});			// { dg-error "no match|too many initializers" }
diff --git gcc/testsuite/g++.dg/cpp0x/pr31437.C gcc/testsuite/g++.dg/cpp0x/pr31437.C
index 7e2ed7a1375..532b533c8d3 100644
--- gcc/testsuite/g++.dg/cpp0x/pr31437.C
+++ gcc/testsuite/g++.dg/cpp0x/pr31437.C
@@ -1,9 +1,9 @@
 // { dg-do compile { target c++11 } }
-template <typename... T> struct A // { dg-message "candidates|A" }
+template <typename... T> struct A // { dg-message "candidates|A" "" { target c++17_down } }
 {
   A(T* p) {  // { dg-error "parameter packs|T" }
    (A<T...>*)(p); 
   }
 };
 
-A<int> a(0); // { dg-error "no matching" }
+A<int> a(0); // { dg-error "no matching|too many initializers" }
diff --git gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
index 753a6ecd0a8..bea30447f6b 100644
--- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
+++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
@@ -460,6 +460,12 @@
 #  error "__cpp_constexpr_dynamic_alloc != 201907"
 #endif
 
+#ifndef __cpp_aggregate_paren_init
+#  error "__cpp_aggregate_paren_init"
+#elif __cpp_aggregate_paren_init != 201902
+#  error "__cpp_aggregate_paren_init != 201902"
+#endif
+
 #ifdef __has_cpp_attribute
 
 #  if ! __has_cpp_attribute(maybe_unused)
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init1.C gcc/testsuite/g++.dg/cpp2a/paren-init1.C
new file mode 100644
index 00000000000..a54b2ccaf6d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init1.C
@@ -0,0 +1,116 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+struct A {
+  int i;
+  int j;
+  int k;
+};
+
+struct B {
+  int i;
+  int j;
+  int k = 42;
+};
+
+struct C {
+  A a;
+};
+
+struct D {
+  A a1;
+  A a2;
+};
+
+struct E {
+  int i;
+};
+
+// F has a base class, but it's not virtual, private, or protected, so this is
+// still an aggregate (since C++17).
+struct F : E {
+  int j;
+  int k;
+};
+
+F f({1}, 2, 3);
+
+// A non-virtual member function doesn't make it a non-aggregate.
+struct G {
+  int i;
+  double j;
+  int foo(int, int);
+};
+
+G g(1, 2.14);
+
+class H {
+public:
+  H (int) { }
+};
+
+class I : public H { };
+
+int i;
+A a1(1, 2);
+A a2(1.0, 2);
+A a3(++i, ++i);
+const A& ra(1, 2);
+
+A ca = A(1, 2);
+A ca2 = A(1.0, 2);
+A ca3 = A(++i, ++i);
+const A& rca = A(1, 2);
+
+B b1(1, 2, 3);
+B b2(1, 2);
+B b3(1);
+
+C c1({5, 6, 7});
+D d1({1, 2, 3}, {5, 6, 7});
+
+struct W {
+  const char *s, *t;
+};
+W w1("fluffer", "nutter");
+
+struct Y {
+  char a[4];
+};
+Y y("yew");
+
+int
+main ()
+{
+  I(10);
+
+  // A::k will be value-initialized.
+  if (a1.i != 1 || a1.j != 2 || a1.k != 0)
+    __builtin_abort ();
+  if (a2.i != 1 || a2.j != 2 || a2.k != 0)
+    __builtin_abort ();
+  if (a3.i != 1 || a3.j != 2 || a3.k != 0)
+    __builtin_abort ();
+  if (ra.i != 1 || ra.j != 2 || ra.k != 0)
+    __builtin_abort ();
+  if (ca.i != 1 || ca.j != 2 || ca.k != 0)
+    __builtin_abort ();
+  if (ca2.i != 1 || ca2.j != 2 || ca2.k != 0)
+    __builtin_abort ();
+  if (ca3.i != 3 || ca3.j != 4 || ca3.k != 0)
+    __builtin_abort ();
+
+  if (b1.i != 1 || b1.j != 2 || b1.k != 3)
+    __builtin_abort ();
+  // The default member initializer will be used for B::k.
+  if (b2.i != 1 || b2.j != 2 || b2.k != 42)
+    __builtin_abort ();
+  if (b3.i != 1 || b3.j != 0 || b3.k != 42)
+    __builtin_abort ();
+
+  if (c1.a.i != 5 || c1.a.j != 6 || c1.a.k != 7)
+    __builtin_abort ();
+
+  if (f.i != 1 || f.j != 2 || f.k != 3)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init10.C gcc/testsuite/g++.dg/cpp2a/paren-init10.C
new file mode 100644
index 00000000000..5c70d9d59ee
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init10.C
@@ -0,0 +1,18 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test from [dcl.init].
+
+struct A {
+  int a;
+  int&& r;
+};
+
+int f();
+int n = 10;
+
+A a1{1, f()};               // OK, lifetime is extended
+A a2(1, f());               // well-formed, but dangling reference
+A a3{1.0, 1};               // { dg-error "narrowing conversion" }
+A a4(1.0, 1);               // well-formed, but dangling reference
+A a5(1.0, static_cast<int&&>(n));    // OK
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init11.C gcc/testsuite/g++.dg/cpp2a/paren-init11.C
new file mode 100644
index 00000000000..82ca2669545
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init11.C
@@ -0,0 +1,88 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test ill-formed code.
+
+// If k is greater than the size of the array, the program is ill-formed.
+int a1[2](1, 2, 3); // { dg-error "too many initializers" }
+int a2[](); // { dg-error "array of functions" }
+int a3[](()); // { dg-error "expected primary-expression" }
+int a4[]("raccoon"); // { dg-error "invalid conversion" }
+
+struct S {
+  int i;
+  int j;
+};
+
+S s1(1, 1, 1); // { dg-error "too many initializers" }
+
+union U2 {
+  int a;
+  float b;
+};
+
+// [dcl.init.aggr]/19:
+// When a union is initialized with an initializer list, there shall not be
+// more than one explicitly initialized element.
+U2 u4 = U2(1, 2); // { dg-error "too many initializers" }
+
+// Test there is no brace elision.
+
+int a[2][2](1, 2, 3, 4); // { dg-error "too many initializers|array must be initialized with a brace-enclosed initializer" }
+
+// private/protected/virtual base class -> not an aggregate.
+struct B { };
+struct D : private B {
+  int i;
+  int j;
+};
+
+D d({}, 1, 2); // { dg-error "no matching function" }
+
+// Private non-static data member -> not an aggregate.
+struct P {
+  int i;
+private:
+  int j;
+};
+
+P p(1, 2); // { dg-error "no matching function" }
+
+// User-declared constructor -> not an aggregate.
+struct U {
+  U() {}
+  int i;
+  int j;  
+};
+
+U u(1, 2); // { dg-error "no matching function" }
+
+// virtual member function -> not an aggregate.
+struct V {
+  int i;
+  int j;
+  virtual int foo(int);
+};
+
+V v(1, 2); // { dg-error "no matching function" }
+
+struct nonaggr {
+  int i;
+  int j;
+private:
+  int x;
+};
+
+struct F {
+  nonaggr n;
+  F() : n(1) { } // { dg-error "no matching function" }
+};
+
+struct G {
+  char a[4];
+};
+
+struct H {
+  G g;
+  H() : g("oaks") { } // { dg-error "initializer-string" }
+};
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init12.C gcc/testsuite/g++.dg/cpp2a/paren-init12.C
new file mode 100644
index 00000000000..d7be6f2b55d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init12.C
@@ -0,0 +1,17 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct A;
+
+struct C {
+  operator A();
+};
+
+struct A {
+  C c;
+};
+
+C c;
+A a(c);  // invokes C’s conversion function to A
+
+// { dg-final { scan-assembler "_ZN1Ccv1AEv" } }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init13.C gcc/testsuite/g++.dg/cpp2a/paren-init13.C
new file mode 100644
index 00000000000..4b9107c70ff
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init13.C
@@ -0,0 +1,16 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct X { int a, b; };
+struct Y { X x; };
+
+void
+f()
+{
+  // This is ok...
+  Y y1{{1, 2}};
+  Y y2({1, 2});
+  // ...but Y y((1,2)) is not the same as Y y{{1,2}}.  (1, 2) is a
+  // COMPOUND_EXPR.
+  Y y3((1, 2)); // { dg-error "could not convert" }
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init14.C gcc/testsuite/g++.dg/cpp2a/paren-init14.C
new file mode 100644
index 00000000000..837f9531260
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init14.C
@@ -0,0 +1,10 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++11 } }
+
+struct A {
+  int x;
+  int y;
+  A(int, int) = delete;
+};
+
+A a(1, 2); // { dg-error "use of deleted function" }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init15.C gcc/testsuite/g++.dg/cpp2a/paren-init15.C
new file mode 100644
index 00000000000..4311dd4df59
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init15.C
@@ -0,0 +1,35 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct A {
+  int i;
+  int j;
+};
+
+struct B : A
+{
+  B (): A(1.7, 2) { }
+};
+
+void f(A);
+
+void
+g ()
+{
+  f (A(1, 2));
+}
+
+struct S {
+  int a[2];
+};
+
+S h() { return S({1, 2}); }
+
+struct Z {
+  int i;
+  int j;
+  operator A();
+};
+
+Z z;
+A a = Z(1, 2.3);
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init16.C gcc/testsuite/g++.dg/cpp2a/paren-init16.C
new file mode 100644
index 00000000000..c8ed924877e
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init16.C
@@ -0,0 +1,14 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct S { int a[2]; };
+struct A { S s[1]; };
+
+template <typename, int N>
+struct R { static constexpr auto h = A({S{N}}); };
+
+template <typename, int N>
+struct R2 { static constexpr auto h = A({S({N, N})}); };
+
+A foo = R<int, 10>::h;
+A foo2 = R2<int, 10>::h;
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init17.C gcc/testsuite/g++.dg/cpp2a/paren-init17.C
new file mode 100644
index 00000000000..8e08b5288a5
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init17.C
@@ -0,0 +1,6 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++11 } }
+
+struct A { };
+struct B { };
+static_assert (!__is_trivially_constructible(A, B), "");
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init18.C gcc/testsuite/g++.dg/cpp2a/paren-init18.C
new file mode 100644
index 00000000000..0aea493f214
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init18.C
@@ -0,0 +1,9 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct A { int a, b, c; };
+struct S { A a; };
+constexpr S s{ A(1, 2, 3) };
+static_assert (s.a.a == 1 && s.a.b == 2 && s.a.c == 3);
+constexpr S s2 = { A(1, 2, 3) };
+static_assert (s2.a.a == 1 && s2.a.b == 2 && s2.a.c == 3);
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init2.C gcc/testsuite/g++.dg/cpp2a/paren-init2.C
new file mode 100644
index 00000000000..e9e90d7acb6
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init2.C
@@ -0,0 +1,56 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+struct A {
+  int i = 0;
+  int j = 0;
+};
+
+struct B {
+  A a;
+  constexpr B() : a(1.1, 2) { }
+};
+
+struct C {
+  int i;
+};
+
+struct E {
+  C c;
+  E() : c(1.2) { }
+};
+
+struct F {
+  char a[4];
+};
+
+struct G {
+  F f;
+  G() : f("yew") { }
+};
+
+struct H {
+  int i;
+  int &&r;
+};
+
+int f() { return 42; }
+
+struct I {
+  H h;
+  I() : h(1, f()) { }
+};
+
+I i;  // dangling ref to f():
+      // {.i=1, .r=(int &) &TARGET_EXPR <D.2118, f ()>}
+
+int
+main ()
+{
+  B b;
+  if (b.a.i != 1 || b.a.j != 2)
+    __builtin_abort ();
+  E e;
+  if (e.c.i != 1)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init3.C gcc/testsuite/g++.dg/cpp2a/paren-init3.C
new file mode 100644
index 00000000000..f444005a09f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init3.C
@@ -0,0 +1,11 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+class a {
+  int b{};
+};
+class c {
+  c();
+  a d;
+};
+c::c() {}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init4.C gcc/testsuite/g++.dg/cpp2a/paren-init4.C
new file mode 100644
index 00000000000..f8c7bd10b63
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init4.C
@@ -0,0 +1,142 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+// Test T[]().
+
+int i;
+int a1[](1, 2, 3);
+static_assert(sizeof(a1) == 3 * sizeof (int), "");
+int a2[](1.0, 2, 3);
+static_assert(sizeof(a2) == 3 * sizeof (int), "");
+int a3[3](1, 2, 3);
+int a4[3](1, 2); // a4[2] is value-initialized.
+int a5[](++i, ++i);
+static_assert(sizeof(a5) == 2 * sizeof (int), "");
+int a6[](1);
+static_assert(sizeof(a6) == sizeof (int), "");
+int a7[]({});
+static_assert(sizeof(a7) == sizeof (int), "");
+int a8[]({}, {}, {}, {}, {}, {});
+static_assert(sizeof(a8) == 6 * sizeof (int), "");
+int a9[]((1));
+static_assert(sizeof(a9) == sizeof (int), "");
+int a10[]((1), (2), (3));
+static_assert(sizeof(a10) == 3 * sizeof (int), "");
+int a11[][2]{1};
+static_assert(sizeof(a11) == 2 * sizeof (int), "");
+int a12[][2]({1, 2}, {3, 4});
+static_assert(sizeof(a12) == 4 * sizeof (int), "");
+
+const int (&ra1)[](1, 2);
+const int (&ra2)[](1.0, 2);
+const int (&ra3)[2](1.0, 2);
+int (&&rra1)[](1, 2);
+int (&&rra2)[](1.0, 2);
+int (&&rra3)[2](1.0, 2);
+
+struct S { int i; } s;
+S s1[]({1});
+static_assert(sizeof(s1) == sizeof (S), "");
+S s2[]({1}, {2});
+static_assert(sizeof(s2) == 2 * sizeof (S), "");
+S s3[]({1}, {2}, {3});
+static_assert(sizeof(s3) == 3 * sizeof (S), "");
+S s4[3]({1}, {2});
+static_assert(sizeof(s4) == 3 * sizeof (S), "");
+S s5[]({++i}, {++i});
+static_assert(sizeof(s2) == 2 * sizeof (S), "");
+S s6[](s, s);
+
+struct R { int i; int j; };
+R r1[]({1, 2});
+static_assert(sizeof(r1) == sizeof (R), "");
+R r2[]({1, 2}, {3, 4});
+static_assert(sizeof(r2) == 2 * sizeof (R), "");
+R r3[]({1.0, 2}, {3.2, 4});
+static_assert(sizeof(r3) == 2 * sizeof (R), "");
+
+char c1[]('r');
+char c2[]('r', 'a', 'c', 'c', 'o', 'o', 'n');
+char c3[]("yarrow");
+char c4[4]("oak");
+char c5[10]("cat");
+const char (&c6)[4]("eel");
+
+int g;
+struct X {
+  int i;
+  X() { ++g; }
+  X(int) { };
+};
+
+int
+main ()
+{
+  // Here we'll value-initialize l[1] and l[2], which will zero-initialize it.
+  int l[3](42);
+  if (l[0] != 42 || l[1] != 0 || l[2] != 0)
+    __builtin_abort ();
+
+  // Here we'll value-initialize x[2] and x[3].  Since X is a class type
+  // with a user-provided ctor, we'll default-initialize in both cases.
+  X x[4]({ 1 }, { 2 });
+  if (g != 2)
+    __builtin_abort ();
+
+  if (a1[0] != 1 || a1[1] != 2 || a1[2] != 3)
+    __builtin_abort ();
+  if (a2[0] != 1 || a2[1] != 2 || a2[2] != 3)
+    __builtin_abort ();
+  if (a3[0] != 1 || a3[1] != 2 || a3[2] != 3)
+    __builtin_abort ();
+  if (a4[0] != 1 || a4[1] != 2 || a4[2] != 0)
+    __builtin_abort ();
+  if (a5[0] != 1 || a5[1] != 2)
+    __builtin_abort ();
+  if (a6[0] != 1)
+    __builtin_abort ();
+  if (a7[0] != 0)
+    __builtin_abort ();
+  if (a8[0] != 0 || a8[1] != 0 || a8[2] != 0 || a8[3] != 0
+      || a8[4] != 0 || a8[5] != 0)
+    __builtin_abort ();
+  if (a9[0] != 1)
+    __builtin_abort ();
+  if (a10[0] != 1 || a10[1] != 2 || a10[2] != 3)
+    __builtin_abort ();
+  if (a11[0][0] != 1 || a11[0][1] != 0)
+    __builtin_abort ();
+  if (a12[0][0] != 1 || a12[0][1] != 2 || a12[1][0] != 3 || a12[1][1] != 4)
+    __builtin_abort ();
+
+  if (ra1[0] != 1 || ra1[1] != 2)
+    __builtin_abort ();
+  if (ra2[0] != 1 || ra2[1] != 2)
+    __builtin_abort ();
+  if (ra3[0] != 1 || ra3[1] != 2)
+    __builtin_abort ();
+  if (rra1[0] != 1 || rra1[1] != 2)
+    __builtin_abort ();
+  if (rra2[0] != 1 || rra2[1] != 2)
+    __builtin_abort ();
+  if (rra3[0] != 1 || rra3[1] != 2)
+    __builtin_abort ();
+
+  if (s1[0].i != 1)
+    __builtin_abort ();
+  if (s2[0].i != 1 || s2[1].i != 2)
+    __builtin_abort ();
+  if (s3[0].i != 1 || s3[1].i != 2 || s3[2].i != 3)
+    __builtin_abort ();
+  if (s4[0].i != 1 || s4[1].i != 2 || s4[2].i != 0)
+    __builtin_abort ();
+  if (s5[0].i != 3 || s5[1].i != 4)
+    __builtin_abort ();
+
+  if (r1[0].i != 1 || r1[0].j != 2)
+    __builtin_abort ();
+  if (r2[0].i != 1 || r2[0].j != 2 || r2[1].i != 3 || r2[1].j != 4)
+    __builtin_abort ();
+  if (r3[0].i != 1 || r3[0].j != 2 || r3[1].i != 3 || r3[1].j != 4)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init5.C gcc/testsuite/g++.dg/cpp2a/paren-init5.C
new file mode 100644
index 00000000000..a64cb00ebbc
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init5.C
@@ -0,0 +1,25 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+union U {
+  int a;
+  float b;
+};
+
+// u1 has no active member
+U u1;
+// u2 zero-initializes the first member, so u2.a is the active member and
+// its value is 0.
+U u2 = U();
+// u3 uses non-list aggregate initialization, so u3.a is the active member
+// and its value is 1.
+U u3 = U(1);
+
+int
+main ()
+{
+  if (u2.a != 0)
+    __builtin_abort ();
+  if (u3.a != 1)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init6.C gcc/testsuite/g++.dg/cpp2a/paren-init6.C
new file mode 100644
index 00000000000..b5d97dcb3d8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init6.C
@@ -0,0 +1,14 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test that we don't perform lifetime extension for () init.
+
+struct A {
+  int a;
+  int&& r;
+};
+
+int f();
+A a(1, f());
+
+// { dg-final { scan-assembler-not "_ZGR1a" } }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init7.C gcc/testsuite/g++.dg/cpp2a/paren-init7.C
new file mode 100644
index 00000000000..32af1a7265c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init7.C
@@ -0,0 +1,20 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+int h;
+struct i {
+  i() {}
+  explicit i(i &) {}
+  template <typename j> i(j &) { h++; }
+};
+
+int main() {
+  {
+    i a[6];
+    auto [b, c, d, e, f, g] = a;
+  }
+  i a[6];
+  auto [b, c, d, e, f, g](a);
+  if (h != 6)
+    __builtin_abort();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init8.C gcc/testsuite/g++.dg/cpp2a/paren-init8.C
new file mode 100644
index 00000000000..30e71650bc9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init8.C
@@ -0,0 +1,13 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test that we don't accept designated inits in ( ).
+
+struct S {
+  int i;
+  int j = 42;
+};
+
+S s(.i = 12); // { dg-error "expected" }
+
+int a[]([0] = 42); // { dg-error "" }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init9.C gcc/testsuite/g++.dg/cpp2a/paren-init9.C
new file mode 100644
index 00000000000..c44b206feb8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init9.C
@@ -0,0 +1,10 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct B { };
+struct A : B {
+  int i;
+};
+
+B b;
+A a(b);
diff --git gcc/testsuite/g++.dg/ext/desig10.C gcc/testsuite/g++.dg/ext/desig10.C
index 24057dd0ad6..c8d672b72d9 100644
--- gcc/testsuite/g++.dg/ext/desig10.C
+++ gcc/testsuite/g++.dg/ext/desig10.C
@@ -1,4 +1,4 @@
 // PR c++/84972
 // { dg-additional-options "-w" }
 
-char(a[])({.a = 0});  // { dg-error "designated initializer" }
+char(a[])({.a = 0});  // { dg-error "designated initializer|cannot convert" }
diff --git gcc/testsuite/g++.dg/template/crash107.C gcc/testsuite/g++.dg/template/crash107.C
index 3b0b4e8211f..9d8d394d93d 100644
--- gcc/testsuite/g++.dg/template/crash107.C
+++ gcc/testsuite/g++.dg/template/crash107.C
@@ -3,7 +3,7 @@
 // { dg-options "" }
 // { dg-additional-options "-Wno-return-type" }
 
-template<typename FP_> struct Vec { // { dg-message "note" }
+template<typename FP_> struct Vec { // { dg-message "note" "" { target c++17_down } }
     Vec& operator^=(Vec& rhs)     {
         union {
             struct {FP_ x,y,z;}; // { dg-error "20:anonymous struct" }
@@ -14,6 +14,6 @@ template<typename FP_> struct Vec { // { dg-message "note" }
         return Vec(*this)^=rhs; // { dg-message "required" }
     }
 };
-Vec<double> v(3,4,12); // { dg-error "no matching" }
-Vec<double> V(12,4,3);  // { dg-error "no matching" }
+Vec<double> v(3,4,12); // { dg-error "no matching|too many initializers" }
+Vec<double> V(12,4,3);  // { dg-error "no matching|too many initializers" }
 Vec<double> c = v^V;   // { dg-message "required" }
diff --git gcc/testsuite/g++.dg/template/crash95.C gcc/testsuite/g++.dg/template/crash95.C
index f60e635ae66..47346111328 100644
--- gcc/testsuite/g++.dg/template/crash95.C
+++ gcc/testsuite/g++.dg/template/crash95.C
@@ -8,4 +8,4 @@ template < typename > struct S
   };
 };
 
-S < int > s(0); // { dg-error "no matching" }
+S < int > s(0); // { dg-error "no matching|too many initializers" }
diff --git gcc/testsuite/g++.old-deja/g++.jason/crash3.C gcc/testsuite/g++.old-deja/g++.jason/crash3.C
index e94cc7c9781..3c4a1f84969 100644
--- gcc/testsuite/g++.old-deja/g++.jason/crash3.C
+++ gcc/testsuite/g++.old-deja/g++.jason/crash3.C
@@ -2,12 +2,12 @@
 // Bug: g++ tries to generate initialization semantics for a Node from an int,
 // and fails.
 
-struct Node			// { dg-message "note" }
+struct Node			// { dg-message "note" "" { target c++17_down } }
 {
   Node* child[2];
 };
 
 void bug(int i)
 {
-  Node* q = new Node(i);	// { dg-error "no matching" } 
+  Node* q = new Node(i);	// { dg-error "no matching" "" { target c++17_down }  } 
 }
diff --git gcc/testsuite/g++.old-deja/g++.law/ctors11.C gcc/testsuite/g++.old-deja/g++.law/ctors11.C
index 39ee76b0ae7..b29b18a62a3 100644
--- gcc/testsuite/g++.old-deja/g++.law/ctors11.C
+++ gcc/testsuite/g++.old-deja/g++.law/ctors11.C
@@ -10,12 +10,12 @@ public:
   inline A(int x){printf("constructing A with %d\n", x);}
 };
 
-class B:public A{ // { dg-message "note" } non-default constructor
+class B:public A{ // { dg-message "note" "" { target c++17_down } } non-default constructor
 private:
 public:
 };
 
 int main()
 {
-  B(10);// { dg-error "match" } B doesn't have a constructor taking int
+  B(10);// { dg-error "match" "" { target c++17_down } } B doesn't have a constructor taking int
 }
diff --git gcc/testsuite/g++.old-deja/g++.law/ctors9.C gcc/testsuite/g++.old-deja/g++.law/ctors9.C
index 43ba1262c95..5856d5dc883 100644
--- gcc/testsuite/g++.old-deja/g++.law/ctors9.C
+++ gcc/testsuite/g++.old-deja/g++.law/ctors9.C
@@ -20,7 +20,7 @@ Foo::Foo(int aa)
 { }
 
 
-struct var_Foo: public Foo // { dg-message "note" }  base.*// ERROR -  in class.*
+struct var_Foo: public Foo // { dg-message "note" "" { target c++17_down } }  base.*// ERROR -  in class.*
 {
   var_Foo* operator-> () {return this;}
 };
@@ -32,7 +32,7 @@ int blort(Foo& f)
 
 int main()
 {
-  var_Foo b(2);// { dg-error "match" } 
+  var_Foo b(2);// { dg-error "match" "" { target c++17_down } } 
   b->a = 0;
   int x = blort(b);
   return x;
diff --git gcc/testsuite/g++.old-deja/g++.mike/net22.C gcc/testsuite/g++.old-deja/g++.mike/net22.C
index e5e1cb1081d..abbf6191023 100644
--- gcc/testsuite/g++.old-deja/g++.mike/net22.C
+++ gcc/testsuite/g++.old-deja/g++.mike/net22.C
@@ -5,10 +5,11 @@ public:
   Parent( char *s ) {}
 };
 
-class Child : public Parent {		// { dg-message "note" } called
+class Child : public Parent {		// { dg-message "note" "" { target c++17_down } } called
 };
 
 int main() {
-  Child c( "String initializer" );	// { dg-error "match" } bad
+  Child c( "String initializer" );	// { dg-error "match" "" { target c++17_down } } bad
+// { dg-error "forbids converting a string constant" "" { target c++2a } .-1 }
   return 0;
 }
diff --git gcc/testsuite/g++.old-deja/g++.niklas/t128.C gcc/testsuite/g++.old-deja/g++.niklas/t128.C
index 19e3ca1dab0..0b3346c4ef3 100644
--- gcc/testsuite/g++.old-deja/g++.niklas/t128.C
+++ gcc/testsuite/g++.old-deja/g++.niklas/t128.C
@@ -1,5 +1,5 @@
 // { dg-do assemble  }
 // GROUPS niklas uncaught default-construct
 struct A { A (int); };
-struct B : A {}; // { dg-message "note" } without ctor // ERROR - candidates
-void f () { B (0); }// { dg-error "match" } .*
+struct B : A {}; // { dg-message "note" "" { target c++17_down } } without ctor // ERROR - candidates
+void f () { B (0); }// { dg-error "match" "" { target c++17_down } } .*
Jason Merrill Nov. 27, 2019, 9:54 p.m. | #9
On 11/27/19 1:58 PM, Marek Polacek wrote:
> On Wed, Nov 20, 2019 at 08:11:27PM -0500, Jason Merrill wrote:

>> On 11/20/19 8:37 PM, Marek Polacek wrote:

>>> On Tue, Nov 19, 2019 at 05:33:09PM -0500, Jason Merrill wrote:

>>>> On 11/19/19 1:44 AM, Marek Polacek wrote:

>>>>>> It also looks like you're using the LOOKUP flag to mean two different

>>>>>> things:

>>>>>>

>>>>>> 1) try to treat parenthesized args as an aggregate initializer

>>>>>> (build_new_method_call_1)

>>>>>> 2) treat this CONSTRUCTOR as coming from parenthesized args

>>>>>> (store_init_value/digest_init)

>>>>>

>>>>> Correct.

>>>>>

>>>>>> Why is the flag needed for #1?  When do we not want to try to treat the args

>>>>>> as an aggregate initializer?

>>>>>

>>>>> There are cases where treating the args as an aggregate initializer causes

>>>>> spurious overload resolution successes, e.g.

>>>>>

>>>>>     void swap(int&, int&);

>>>>>

>>>>>     int& get();

>>>>>

>>>>>     struct pair {

>>>>>       void swap(pair&) noexcept(noexcept(swap(get(), get()))) { } // { dg-error "no matching function for call" }

>>>>>     };

>>>>>

>>>>> There are no viable candidates for pair::swap (# of args mismatch) but since

>>>>> pair is an aggregate, build_new_method_call_1 would return a CONSTRUCTOR so

>>>>> overload resolution would succeed.  Another case had to do with SFINAE and

>>>>> decltype where we didn't evaluate the arg, but succeeding in the

>>>>> no-viable-function case caused the compiler to choose the wrong function.

>>>>

>>>> Hmm, but then the parenthesized list is arguments for swap, not an

>>>> initializer for a single argument of swap.  That would be using it for

>>>> copy-initialization, and we only want to treat parenthesized args as an

>>>> aggregate initializer in direct-initialization.  Can we check for

>>>> direct-init (i.e. !LOOKUP_ONLYCONVERTING) instead?

>>>

>>> Unfortunately that doesn't work.  We call build_new_method_call from context

>>> where LOOKUP_ONLYCONVERTING isn't set and so it would still break things.

>>

>> How so?  If we call it for a constructor, surely we can check that flag to

>> distinguish between copy- and direct-initialization, or I'd expect wrong

>> behavior wrt explicit.

> 

> [ Sorry for the delayed response, been too busy with other stuff.  Hope to be

> more prompt from now on. ]

> 

> Indeed, if I check DECL_CONSTRUCTOR_P, it works.  But I was confused by a test

> like the newly-added paren-init17.C.  Here we're checking if A can be trivially

> initialized by B, so constructible_expr builds up a build_stub_object object

> and calls build_special_member_call.  It doesn't find a viable constructor for

> A, but A is an aggregate and there are arguments, so it does parenthesized

> initialization, and then we think A can be trivially initialized from B.  But

> I don't think we should attempt to handle this case.  I also saw a similar

> problem with decltype/declval.  Fixed by checking cp_unevaluated_operand...

> 

> Another difference is that g++.old-deja/g++.jason/crash3.C now passes in

> C++20.  But that should be fine.

> 

>>>>> +  if (BRACE_ENCLOSED_INITIALIZER_P (exp))

>>>>> +    {

>>>>> +      gcc_assert (cxx_dialect >= cxx2a);

>>>>> +      return finish_compound_literal (type, exp, complain,

>>>>> +				      fcl_functional_paren);

>>>>> +    }

>>>>

>>>> How about handling this in build_cplus_new instead of places that also call

>>>> build_cplus_new?

>>>

>>> Is it really what we want?  We now have two spots where we need to handle

>>> the case when build_special_member_call returns a BRACE_ENCLOSED_INITIALIZER_P

>>> but build_cplus_new is called in many other spots where we don't expect to see

>>> a CONSTRUCTOR.

>>

>> I think build_cplus_new should be able to handle whatever

>> build_special_member_call returns.

> 

> Ok, adjusted.

> 

>>> @@ -921,8 +921,20 @@ perform_member_init (tree member, tree init)

>>>    		inform (DECL_SOURCE_LOCATION (member),

>>>    			"%q#D should be initialized", member );

>>>    	    }

>>> -	  finish_expr_stmt (build_aggr_init (decl, init, flags,

>>> -					     tf_warning_or_error));

>>> +	  init = build_aggr_init (decl, init, flags, tf_warning_or_error);

>>> +	  /* In C++20, a member initializer list can be initializing an

>>> +	     aggregate from a parenthesized list of values:

>>> +

>>> +	       struct S {

>>> +		 A aggr;

>>> +		 S() : aggr(1, 2, 3) { }

>>> +	       };

>>> +

>>> +	      In such case, build_aggr_init will build up an INIT_EXPR like

>>> +	      we do for aggr{1, 2, 3}, so that build_data_member_initialization

>>> +	      can grok it.  */

>>> +	  if (TREE_CODE (init) != INIT_EXPR)

>>> +	    finish_expr_stmt (init);

>>

>> Why don't we want to finish_expr_stmt an INIT_EXPR?

> 

> Because expand_default_init has already finish_expr_stmt-ed it.  Adding it

> twice sounds wrong.


But the finish_expr_stmt in expand_default_init is inside the 
STATEMENT_LIST we pushed into in build_aggr_init.  If finish_init_stmts 
returned the INIT_EXPR, we still need to add it to the enclosing 
STATEMENT_LIST.

>>> @@ -1272,8 +1274,13 @@ digest_init_r (tree type, tree init, int nested, int flags,

>>>    		inform (loc, "remove %<{ }%> around initializer");

>>>    	    }

>>>    	  else if (flag_checking)

>>> -	    /* We should have fixed this in reshape_init.  */

>>> -	    gcc_unreachable ();

>>> +	    /* We should have fixed this in reshape_init.  Except that we

>>> +	       don't reshape parenthesized lists where brace elision is

>>> +	       not permitted.  */

>>> +	    {

>>> +	      gcc_assert (flags & LOOKUP_AGGREGATE_PAREN_INIT);

>>> +	      return error_mark_node;

>>

>> We shouldn't get here for parenthesized lists, either, I thnk; we should

>> have called the copy constructor rather than try aggregate initialization.

> 

> True.  I'm not sure why I hit it before but I removed this hunk now.

> 

>>> @@ -1319,12 +1326,19 @@ digest_init_r (tree type, tree init, int nested, int flags,

>>>    tree

>>>    digest_init (tree type, tree init, tsubst_flags_t complain)

>>>    {

>>> -  return digest_init_r (type, init, 0, LOOKUP_IMPLICIT, complain);

>>> +  int flags = LOOKUP_IMPLICIT;

>>> +  if (BRACE_ENCLOSED_INITIALIZER_P (init)

>>> +      && CONSTRUCTOR_IS_PAREN_INIT (init))

>>> +    flags |= LOOKUP_AGGREGATE_PAREN_INIT;

>>> +  return digest_init_r (type, init, 0, flags, complain);

>>>    }

>>>    tree

>>>    digest_init_flags (tree type, tree init, int flags, tsubst_flags_t complain)

>>>    {

>>> +  if (BRACE_ENCLOSED_INITIALIZER_P (init)

>>> +      && CONSTRUCTOR_IS_PAREN_INIT (init))

>>> +    flags |= LOOKUP_AGGREGATE_PAREN_INIT;

>>

>> Maybe do this in digest_init_r?

> 

> Done.

> 

>> What happens if we have a braced aggregate

>> initializer containing a parenthesized aggregate initializer, e.g.

>>

>> { aggr(1,2,3) }

> 

> Seems to work as expected.  I've added paren-init18.C to specifically test it.

> 

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

> 

> 2019-11-27  Marek Polacek  <polacek@redhat.com>

> 

> 	PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> 	* c-cppbuiltin.c (c_cpp_builtins): Predefine

> 	__cpp_aggregate_paren_init=201902 for -std=c++2a.

> 

> 	* call.c (build_new_method_call_1): Handle parenthesized initialization

> 	of aggregates by building up a CONSTRUCTOR.

> 	(extend_ref_init_temps): Do nothing for CONSTRUCTOR_IS_PAREN_INIT.

> 	* cp-tree.h (CONSTRUCTOR_IS_PAREN_INIT, LOOKUP_AGGREGATE_PAREN_INIT):

> 	Define.

> 	* decl.c (grok_reference_init): Handle aggregate initialization from

> 	a parenthesized list of values.

> 	(reshape_init): Do nothing for CONSTRUCTOR_IS_PAREN_INIT.

> 	(check_initializer): Handle initialization of an array from a

> 	parenthesized list of values.  Use NULL_TREE instead of NULL.

> 	* init.c (perform_member_init): Don't call finish_expr_stmt if

> 	build_aggr_init returns an INIT_EXPR.

> 	(expand_default_init): Set LOOKUP_AGGREGATE_PAREN_INIT for the

> 	build_special_member_call call.  If it returns a

> 	BRACE_ENCLOSED_INITIALIZER_P, call digest_init and build an INIT_EXPR.

> 	Set TREE_SIDE_EFFECTS on it.

> 	* tree.c (build_cplus_new): Handle BRACE_ENCLOSED_INITIALIZER_P.

> 	* typeck2.c (digest_init_r): Set LOOKUP_AGGREGATE_PAREN_INIT if it

> 	receives a CONSTRUCTOR with CONSTRUCTOR_IS_PAREN_INIT set.  Allow

> 	narrowing when LOOKUP_AGGREGATE_PAREN_INIT.

> 	(massage_init_elt): Don't lose LOOKUP_AGGREGATE_PAREN_INIT when passing

> 	flags to digest_init_r.

> 

> 	* g++.dg/cpp0x/constexpr-99.C: Only expect an error in C++17 and

> 	lesser.

> 	* g++.dg/cpp0x/explicit7.C: Likewise.

> 	* g++.dg/cpp0x/initlist12.C: Adjust dg-error.

> 	* g++.dg/cpp0x/pr31437.C: Likewise.

> 	* g++.dg/cpp2a/feat-cxx2a.C: Add __cpp_aggregate_paren_init test.

> 	* g++.dg/cpp2a/paren-init1.C: New test.

> 	* g++.dg/cpp2a/paren-init10.C: New test.

> 	* g++.dg/cpp2a/paren-init11.C: New test.

> 	* g++.dg/cpp2a/paren-init12.C: New test.

> 	* g++.dg/cpp2a/paren-init13.C: New test.

> 	* g++.dg/cpp2a/paren-init14.C: New test.

> 	* g++.dg/cpp2a/paren-init15.C: New test.

> 	* g++.dg/cpp2a/paren-init16.C: New test.

> 	* g++.dg/cpp2a/paren-init17.C: New test.

> 	* g++.dg/cpp2a/paren-init18.C: New test.

> 	* g++.dg/cpp2a/paren-init2.C: New test.

> 	* g++.dg/cpp2a/paren-init3.C: New test.

> 	* g++.dg/cpp2a/paren-init4.C: New test.

> 	* g++.dg/cpp2a/paren-init5.C: New test.

> 	* g++.dg/cpp2a/paren-init6.C: New test.

> 	* g++.dg/cpp2a/paren-init7.C: New test.

> 	* g++.dg/cpp2a/paren-init8.C: New test.

> 	* g++.dg/cpp2a/paren-init9.C: New test.

> 	* g++.dg/ext/desig10.C: Adjust dg-error.

> 	* g++.dg/template/crash107.C: Likewise.

> 	* g++.dg/template/crash95.C: Likewise.

> 	* g++.old-deja/g++.jason/crash3.C: Only expect an error in C++17 and

> 	lesser.

> 	* g++.old-deja/g++.law/ctors11.C: Likewise.

> 	* g++.old-deja/g++.law/ctors9.C: Likewise.

> 	* g++.old-deja/g++.mike/net22.C: Likewise.

> 	* g++.old-deja/g++.niklas/t128.C: Likewise.

> 

> diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c

> index 50066e4dd8b..2f30b2a3235 100644

> --- gcc/c-family/c-cppbuiltin.c

> +++ gcc/c-family/c-cppbuiltin.c

> @@ -1004,6 +1004,7 @@ c_cpp_builtins (cpp_reader *pfile)

>   	  cpp_define (pfile, "__cpp_impl_destroying_delete=201806L");

>   	  cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907L");

>   	  cpp_define (pfile, "__cpp_impl_three_way_comparison=201907L");

> +	  cpp_define (pfile, "__cpp_aggregate_paren_init=201902L");

>   	}

>         if (flag_concepts)

>           {

> diff --git gcc/cp/call.c gcc/cp/call.c

> index 8bfe3368816..1d14409ec43 100644

> --- gcc/cp/call.c

> +++ gcc/cp/call.c

> @@ -10111,6 +10111,30 @@ build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args,

>   

>     if (!any_viable_p)

>       {

> +      /* [dcl.init], 17.6.2.2:

> +

> +	 Otherwise, if no constructor is viable, the destination type is

> +	 a (possibly cv-qualified) aggregate class A, and the initializer

> +	 is a parenthesized expression-list, the object is initialized as

> +	 follows...

> +

> +	 We achieve this by building up a CONSTRUCTOR, as for list-init,

> +	 and setting CONSTRUCTOR_IS_PAREN_INIT to distinguish between

> +	 the two.  */

> +      if (DECL_CONSTRUCTOR_P (fn)

> +	  && !(flags & LOOKUP_ONLYCONVERTING)

> +	  && !cp_unevaluated_operand

> +	  && cxx_dialect >= cxx2a

> +	  && CP_AGGREGATE_TYPE_P (basetype)

> +	  && !user_args->is_empty ())

> +	{

> +	  /* Create a CONSTRUCTOR from ARGS, e.g. {1, 2} from <1, 2>.  */

> +	  tree list = build_tree_list_vec (user_args);

> +	  tree ctor = build_constructor_from_list (init_list_type_node, list);

> +	  CONSTRUCTOR_IS_DIRECT_INIT (ctor) = true;

> +	  CONSTRUCTOR_IS_PAREN_INIT (ctor) = true;

> +	  return ctor;

> +	}

>         if (complain & tf_error)

>   	complain_about_no_candidates_for_method_call (instance, candidates,

>   						      explicit_targs, basetype,

> @@ -11792,9 +11816,16 @@ perform_direct_initialization_if_possible (tree type,

>        If the destination type is a (possibly cv-qualified) class type:

>   

>        -- If the initialization is direct-initialization ...,

> -     constructors are considered. ... If no constructor applies, or

> -     the overload resolution is ambiguous, the initialization is

> -     ill-formed.  */

> +     constructors are considered.

> +

> +       -- If overload resolution is successful, the selected constructor

> +       is called to initialize the object, with the initializer expression

> +       or expression-list as its argument(s).

> +

> +       -- Otherwise, if no constructor is viable, the destination type is

> +       a (possibly cv-qualified) aggregate class A, and the initializer is

> +       a parenthesized expression-list, the object is initialized as

> +       follows...  */

>     if (CLASS_TYPE_P (type))

>       {

>         releasing_vec args (make_tree_vector_single (expr));

> @@ -12150,6 +12181,12 @@ extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups)

>   	ctor = TARGET_EXPR_INITIAL (ctor);

>         if (TREE_CODE (ctor) == CONSTRUCTOR)

>   	{

> +	  /* [dcl.init] When initializing an aggregate from a parenthesized list

> +	     of values... a temporary object bound to a reference does not have

> +	     its lifetime extended.  */

> +	  if (CONSTRUCTOR_IS_PAREN_INIT (ctor))

> +	    return init;

> +

>   	  if (is_std_init_list (type))

>   	    {

>   	      /* The temporary array underlying a std::initializer_list

> diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h

> index 4b4bc245d81..4aa871a21c0 100644

> --- gcc/cp/cp-tree.h

> +++ gcc/cp/cp-tree.h

> @@ -4325,6 +4325,11 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)

>   #define CONSTRUCTOR_IS_DESIGNATED_INIT(NODE) \

>     (TREE_LANG_FLAG_6 (CONSTRUCTOR_CHECK (NODE)))

>   

> +/* True if this CONSTRUCTOR comes from a parenthesized list of values, e.g.

> +   A(1, 2, 3).  */

> +#define CONSTRUCTOR_IS_PAREN_INIT(NODE) \

> +  (CONSTRUCTOR_CHECK(NODE)->base.private_flag)

> +

>   /* True if NODE represents a conversion for direct-initialization in a

>      template.  Set by perform_implicit_conversion_flags.  */

>   #define IMPLICIT_CONV_EXPR_DIRECT_INIT(NODE) \

> @@ -5583,6 +5588,8 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };

>      args), then we swap the conversions back in build_new_op_1 (so they

>      correspond to the order of the args in the candidate).  */

>   #define LOOKUP_REVERSED (LOOKUP_REWRITTEN << 1)

> +/* We're initializing an aggregate from a parenthesized list of values.  */

> +#define LOOKUP_AGGREGATE_PAREN_INIT (LOOKUP_REVERSED << 1)

>   

>   #define LOOKUP_NAMESPACES_ONLY(F)  \

>     (((F) & LOOKUP_PREFER_NAMESPACES) && !((F) & LOOKUP_PREFER_TYPES))

> diff --git gcc/cp/decl.c gcc/cp/decl.c

> index 26120720f07..33cd474f834 100644

> --- gcc/cp/decl.c

> +++ gcc/cp/decl.c

> @@ -5531,11 +5531,28 @@ grok_reference_init (tree decl, tree type, tree init, int flags)

>         return NULL_TREE;

>       }

>   

> +  tree ttype = TREE_TYPE (type);

>     if (TREE_CODE (init) == TREE_LIST)

> -    init = build_x_compound_expr_from_list (init, ELK_INIT,

> -					    tf_warning_or_error);

> +    {

> +      /* This handles (C++20 only) code like

> +

> +	   const A& r(1, 2, 3);

> +

> +	 where we treat the parenthesized list as a CONSTRUCTOR.  */

> +      if (TREE_TYPE (init) == NULL_TREE

> +	  && CP_AGGREGATE_TYPE_P (ttype)

> +	  && !DECL_DECOMPOSITION_P (decl)

> +	  && (cxx_dialect >= cxx2a))

> +	{

> +	  init = build_constructor_from_list (init_list_type_node, init);

> +	  CONSTRUCTOR_IS_DIRECT_INIT (init) = true;

> +	  CONSTRUCTOR_IS_PAREN_INIT (init) = true;

> +	}

> +      else

> +	init = build_x_compound_expr_from_list (init, ELK_INIT,

> +						tf_warning_or_error);

> +    }

>   

> -  tree ttype = TREE_TYPE (type);

>     if (TREE_CODE (ttype) != ARRAY_TYPE

>         && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE)

>       /* Note: default conversion is only called in very special cases.  */

> @@ -6437,6 +6454,11 @@ reshape_init (tree type, tree init, tsubst_flags_t complain)

>     if (vec_safe_is_empty (v))

>       return init;

>   

> +  /* Brace elision is not performed for a CONSTRUCTOR representing

> +     parenthesized aggregate initialization.  */

> +  if (CONSTRUCTOR_IS_PAREN_INIT (init))

> +    return init;

> +

>     /* Handle [dcl.init.list] direct-list-initialization from

>        single element of enumeration with a fixed underlying type.  */

>     if (is_direct_enum_init (type, init))

> @@ -6640,6 +6662,41 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)

>   	      flags |= LOOKUP_NO_NARROWING;

>   	    }

>   	}

> +      /* [dcl.init] "Otherwise, if the destination type is an array, the object

> +	 is initialized as follows..."  So handle things like

> +

> +	  int a[](1, 2, 3);

> +

> +	 which is permitted in C++20 by P0960.  */

> +      else if (TREE_CODE (init) == TREE_LIST

> +	       && TREE_TYPE (init) == NULL_TREE

> +	       && TREE_CODE (type) == ARRAY_TYPE

> +	       && !DECL_DECOMPOSITION_P (decl)

> +	       && (cxx_dialect >= cxx2a))

> +	{

> +	  /* [dcl.init.string] "An array of ordinary character type [...]

> +	     can be initialized by an ordinary string literal [...] by an

> +	     appropriately-typed string literal enclosed in braces" only

> +	     talks about braces, but GCC has always accepted

> +

> +	       char a[]("foobar");

> +

> +	     so we continue to do so.  */

> +	  tree val = TREE_VALUE (init);

> +	  if (TREE_CHAIN (init) == NULL_TREE

> +	      && char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type)))

> +	      && TREE_CODE (tree_strip_any_location_wrapper (val))

> +		 == STRING_CST)

> +	    /* If the list has a single element and it's a string literal,

> +	       then it's the initializer for the array as a whole.  */

> +	    init = val;

> +	  else

> +	    {

> +	      init = build_constructor_from_list (init_list_type_node, init);

> +	      CONSTRUCTOR_IS_DIRECT_INIT (init) = true;

> +	      CONSTRUCTOR_IS_PAREN_INIT (init) = true;

> +	    }

> +	}

>         else if (TREE_CODE (init) == TREE_LIST

>   	       && TREE_TYPE (init) != unknown_type_node

>   	       && !MAYBE_CLASS_TYPE_P (type))

> @@ -6683,6 +6740,9 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)

>   	    init_code = TREE_OPERAND (init_code, 0);

>   	  if (TREE_CODE (init_code) == INIT_EXPR)

>   	    {

> +	      /* In C++20, the call to build_aggr_init could have created

> +		 an INIT_EXPR with a CONSTRUCTOR as the RHS to handle

> +		 A(1, 2).  */

>   	      init = TREE_OPERAND (init_code, 1);

>   	      init_code = NULL_TREE;

>   	      /* Don't call digest_init; it's unnecessary and will complain

> @@ -6736,7 +6796,7 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)

>   			0, "array %qD initialized by parenthesized "

>   			"string literal %qE",

>   			decl, DECL_INITIAL (decl));

> -	  init = NULL;

> +	  init = NULL_TREE;

>   	}

>       }

>     else

> diff --git gcc/cp/init.c gcc/cp/init.c

> index aa48f80e58d..16fc47fea4f 100644

> --- gcc/cp/init.c

> +++ gcc/cp/init.c

> @@ -921,8 +921,20 @@ perform_member_init (tree member, tree init)

>   		inform (DECL_SOURCE_LOCATION (member),

>   			"%q#D should be initialized", member );

>   	    }

> -	  finish_expr_stmt (build_aggr_init (decl, init, flags,

> -					     tf_warning_or_error));

> +	  init = build_aggr_init (decl, init, flags, tf_warning_or_error);

> +	  /* In C++20, a member initializer list can be initializing an

> +	     aggregate from a parenthesized list of values:

> +

> +	       struct S {

> +		 A aggr;

> +		 S() : aggr(1, 2, 3) { }

> +	       };

> +

> +	      In such case, build_aggr_init will build up an INIT_EXPR like

> +	      we do for aggr{1, 2, 3}, so that build_data_member_initialization

> +	      can grok it.  */

> +	  if (TREE_CODE (init) != INIT_EXPR)

> +	    finish_expr_stmt (init);

>   	}

>       }

>     else

> @@ -1967,8 +1979,23 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,

>         tree ctor_name = (true_exp == exp

>   			? complete_ctor_identifier : base_ctor_identifier);

>   

> +      /* Given class A,

> +

> +	   A a(1, 2);

> +

> +	 can mean a call to a constructor A::A(int, int), if present.  If not,

> +	 but A is an aggregate, we will try aggregate initialization.  */

>         rval = build_special_member_call (exp, ctor_name, &parms, binfo, flags,

>   					complain);

> +      if (BRACE_ENCLOSED_INITIALIZER_P (rval))

> +	{

> +	  gcc_assert (cxx_dialect >= cxx2a);

> +	  rval = digest_init (type, rval, tf_warning_or_error);

> +	  rval = build2 (INIT_EXPR, type, exp, rval);

> +	  /* So that we do finish_expr_stmt below.  Don't return here, we

> +	     need to release PARMS.  */

> +	  TREE_SIDE_EFFECTS (rval) = 1;

> +	}

>       }

>   

>     if (parms != NULL)

> diff --git gcc/cp/tree.c gcc/cp/tree.c

> index d125d60b270..dede4dce36b 100644

> --- gcc/cp/tree.c

> +++ gcc/cp/tree.c

> @@ -669,6 +669,15 @@ build_aggr_init_expr (tree type, tree init)

>   tree

>   build_cplus_new (tree type, tree init, tsubst_flags_t complain)

>   {

> +  /* This function should cope with what build_special_member_call

> +     can produce.  When performing parenthesized aggregate initialization,

> +     it can produce a { }.  */

> +  if (BRACE_ENCLOSED_INITIALIZER_P (init))

> +    {

> +      gcc_assert (cxx_dialect >= cxx2a);

> +      return finish_compound_literal (type, init, complain);

> +    }

> +

>     tree rval = build_aggr_init_expr (type, init);

>     tree slot;

>   

> diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c

> index b8868546444..9c6f25c8608 100644

> --- gcc/cp/typeck2.c

> +++ gcc/cp/typeck2.c

> @@ -1117,6 +1117,10 @@ digest_init_r (tree type, tree init, int nested, int flags,

>   

>     tree stripped_init = init;

>   

> +  if (BRACE_ENCLOSED_INITIALIZER_P (init)

> +      && CONSTRUCTOR_IS_PAREN_INIT (init))

> +    flags |= LOOKUP_AGGREGATE_PAREN_INIT;

> +

>     /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue

>        (g++.old-deja/g++.law/casts2.C).  */

>     if (TREE_CODE (init) == NON_LVALUE_EXPR)

> @@ -1224,7 +1228,9 @@ digest_init_r (tree type, tree init, int nested, int flags,

>     if ((code != COMPLEX_TYPE || BRACE_ENCLOSED_INITIALIZER_P (stripped_init))

>         && (SCALAR_TYPE_P (type) || code == REFERENCE_TYPE))

>       {

> -      if (nested)

> +      /* Narrowing is OK when initializing an aggregate from

> +	 a parenthesized list.  */

> +      if (nested && !(flags & LOOKUP_AGGREGATE_PAREN_INIT))

>   	flags |= LOOKUP_NO_NARROWING;

>         init = convert_for_initialization (0, type, init, flags,

>   					 ICR_INIT, NULL_TREE, 0,

> @@ -1384,9 +1390,12 @@ static tree

>   massage_init_elt (tree type, tree init, int nested, int flags,

>   		  tsubst_flags_t complain)

>   {

> -  flags &= LOOKUP_ALLOW_FLEXARRAY_INIT;

> -  flags |= LOOKUP_IMPLICIT;

> -  init = digest_init_r (type, init, nested ? 2 : 1, flags, complain);

> +  int new_flags = LOOKUP_IMPLICIT;

> +  if (flags & LOOKUP_ALLOW_FLEXARRAY_INIT)

> +    new_flags |= LOOKUP_ALLOW_FLEXARRAY_INIT;

> +  if (flags & LOOKUP_AGGREGATE_PAREN_INIT)

> +    new_flags |= LOOKUP_AGGREGATE_PAREN_INIT;

> +  init = digest_init_r (type, init, nested ? 2 : 1, new_flags, complain);

>     /* Strip a simple TARGET_EXPR when we know this is an initializer.  */

>     if (SIMPLE_TARGET_EXPR_P (init))

>       init = TARGET_EXPR_INITIAL (init);

> diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-99.C gcc/testsuite/g++.dg/cpp0x/constexpr-99.C

> index 8d791dd032d..4d3953d0983 100644

> --- gcc/testsuite/g++.dg/cpp0x/constexpr-99.C

> +++ gcc/testsuite/g++.dg/cpp0x/constexpr-99.C

> @@ -9,5 +9,6 @@ struct A

>   struct B

>   {

>     A a;

> -    constexpr B() : a(0) {} // { dg-error "no matching function" }

> +  // P0960R3 allows paren-init.

> +  constexpr B() : a(0) {} // { dg-error "no matching function" "" { target c++17_down } }

>   };

> diff --git gcc/testsuite/g++.dg/cpp0x/explicit7.C gcc/testsuite/g++.dg/cpp0x/explicit7.C

> index 574796d117d..67b50542bc3 100644

> --- gcc/testsuite/g++.dg/cpp0x/explicit7.C

> +++ gcc/testsuite/g++.dg/cpp0x/explicit7.C

> @@ -10,7 +10,7 @@ struct A { };

>   struct B: A { };

>   struct C {

>     explicit operator B*();	// { dg-message "explicit" }

> -  explicit operator B&();	// { dg-message "explicit" }

> +  explicit operator B&();	// { dg-message "explicit" "" { target c++17_down } }

>   };

>   

>   C c;

> diff --git gcc/testsuite/g++.dg/cpp0x/initlist12.C gcc/testsuite/g++.dg/cpp0x/initlist12.C

> index f72a6dac525..c2a2b86520f 100644

> --- gcc/testsuite/g++.dg/cpp0x/initlist12.C

> +++ gcc/testsuite/g++.dg/cpp0x/initlist12.C

> @@ -6,15 +6,15 @@ struct A

>     int i;

>   };

>   

> -A a({1,2});			// { dg-error "no match" }

> +A a({1,2});			// { dg-error "no match|cannot convert" }

>   

>   union U

>   {

>     int i,j;

>   };

>   

> -U u({1,2});			// { dg-error "no match" }

> +U u({1,2});			// { dg-error "no match|cannot convert" }

>   

>   union V {};

>   

> -V v({1});			// { dg-error "no match" }

> +V v({1});			// { dg-error "no match|too many initializers" }

> diff --git gcc/testsuite/g++.dg/cpp0x/pr31437.C gcc/testsuite/g++.dg/cpp0x/pr31437.C

> index 7e2ed7a1375..532b533c8d3 100644

> --- gcc/testsuite/g++.dg/cpp0x/pr31437.C

> +++ gcc/testsuite/g++.dg/cpp0x/pr31437.C

> @@ -1,9 +1,9 @@

>   // { dg-do compile { target c++11 } }

> -template <typename... T> struct A // { dg-message "candidates|A" }

> +template <typename... T> struct A // { dg-message "candidates|A" "" { target c++17_down } }

>   {

>     A(T* p) {  // { dg-error "parameter packs|T" }

>      (A<T...>*)(p);

>     }

>   };

>   

> -A<int> a(0); // { dg-error "no matching" }

> +A<int> a(0); // { dg-error "no matching|too many initializers" }

> diff --git gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C

> index 753a6ecd0a8..bea30447f6b 100644

> --- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C

> +++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C

> @@ -460,6 +460,12 @@

>   #  error "__cpp_constexpr_dynamic_alloc != 201907"

>   #endif

>   

> +#ifndef __cpp_aggregate_paren_init

> +#  error "__cpp_aggregate_paren_init"

> +#elif __cpp_aggregate_paren_init != 201902

> +#  error "__cpp_aggregate_paren_init != 201902"

> +#endif

> +

>   #ifdef __has_cpp_attribute

>   

>   #  if ! __has_cpp_attribute(maybe_unused)

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init1.C gcc/testsuite/g++.dg/cpp2a/paren-init1.C

> new file mode 100644

> index 00000000000..a54b2ccaf6d

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init1.C

> @@ -0,0 +1,116 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do run { target c++2a } }

> +

> +struct A {

> +  int i;

> +  int j;

> +  int k;

> +};

> +

> +struct B {

> +  int i;

> +  int j;

> +  int k = 42;

> +};

> +

> +struct C {

> +  A a;

> +};

> +

> +struct D {

> +  A a1;

> +  A a2;

> +};

> +

> +struct E {

> +  int i;

> +};

> +

> +// F has a base class, but it's not virtual, private, or protected, so this is

> +// still an aggregate (since C++17).

> +struct F : E {

> +  int j;

> +  int k;

> +};

> +

> +F f({1}, 2, 3);

> +

> +// A non-virtual member function doesn't make it a non-aggregate.

> +struct G {

> +  int i;

> +  double j;

> +  int foo(int, int);

> +};

> +

> +G g(1, 2.14);

> +

> +class H {

> +public:

> +  H (int) { }

> +};

> +

> +class I : public H { };

> +

> +int i;

> +A a1(1, 2);

> +A a2(1.0, 2);

> +A a3(++i, ++i);

> +const A& ra(1, 2);

> +

> +A ca = A(1, 2);

> +A ca2 = A(1.0, 2);

> +A ca3 = A(++i, ++i);

> +const A& rca = A(1, 2);

> +

> +B b1(1, 2, 3);

> +B b2(1, 2);

> +B b3(1);

> +

> +C c1({5, 6, 7});

> +D d1({1, 2, 3}, {5, 6, 7});

> +

> +struct W {

> +  const char *s, *t;

> +};

> +W w1("fluffer", "nutter");

> +

> +struct Y {

> +  char a[4];

> +};

> +Y y("yew");

> +

> +int

> +main ()

> +{

> +  I(10);

> +

> +  // A::k will be value-initialized.

> +  if (a1.i != 1 || a1.j != 2 || a1.k != 0)

> +    __builtin_abort ();

> +  if (a2.i != 1 || a2.j != 2 || a2.k != 0)

> +    __builtin_abort ();

> +  if (a3.i != 1 || a3.j != 2 || a3.k != 0)

> +    __builtin_abort ();

> +  if (ra.i != 1 || ra.j != 2 || ra.k != 0)

> +    __builtin_abort ();

> +  if (ca.i != 1 || ca.j != 2 || ca.k != 0)

> +    __builtin_abort ();

> +  if (ca2.i != 1 || ca2.j != 2 || ca2.k != 0)

> +    __builtin_abort ();

> +  if (ca3.i != 3 || ca3.j != 4 || ca3.k != 0)

> +    __builtin_abort ();

> +

> +  if (b1.i != 1 || b1.j != 2 || b1.k != 3)

> +    __builtin_abort ();

> +  // The default member initializer will be used for B::k.

> +  if (b2.i != 1 || b2.j != 2 || b2.k != 42)

> +    __builtin_abort ();

> +  if (b3.i != 1 || b3.j != 0 || b3.k != 42)

> +    __builtin_abort ();

> +

> +  if (c1.a.i != 5 || c1.a.j != 6 || c1.a.k != 7)

> +    __builtin_abort ();

> +

> +  if (f.i != 1 || f.j != 2 || f.k != 3)

> +    __builtin_abort ();

> +}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init10.C gcc/testsuite/g++.dg/cpp2a/paren-init10.C

> new file mode 100644

> index 00000000000..5c70d9d59ee

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init10.C

> @@ -0,0 +1,18 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +// Test from [dcl.init].

> +

> +struct A {

> +  int a;

> +  int&& r;

> +};

> +

> +int f();

> +int n = 10;

> +

> +A a1{1, f()};               // OK, lifetime is extended

> +A a2(1, f());               // well-formed, but dangling reference

> +A a3{1.0, 1};               // { dg-error "narrowing conversion" }

> +A a4(1.0, 1);               // well-formed, but dangling reference

> +A a5(1.0, static_cast<int&&>(n));    // OK

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init11.C gcc/testsuite/g++.dg/cpp2a/paren-init11.C

> new file mode 100644

> index 00000000000..82ca2669545

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init11.C

> @@ -0,0 +1,88 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +// Test ill-formed code.

> +

> +// If k is greater than the size of the array, the program is ill-formed.

> +int a1[2](1, 2, 3); // { dg-error "too many initializers" }

> +int a2[](); // { dg-error "array of functions" }

> +int a3[](()); // { dg-error "expected primary-expression" }

> +int a4[]("raccoon"); // { dg-error "invalid conversion" }

> +

> +struct S {

> +  int i;

> +  int j;

> +};

> +

> +S s1(1, 1, 1); // { dg-error "too many initializers" }

> +

> +union U2 {

> +  int a;

> +  float b;

> +};

> +

> +// [dcl.init.aggr]/19:

> +// When a union is initialized with an initializer list, there shall not be

> +// more than one explicitly initialized element.

> +U2 u4 = U2(1, 2); // { dg-error "too many initializers" }

> +

> +// Test there is no brace elision.

> +

> +int a[2][2](1, 2, 3, 4); // { dg-error "too many initializers|array must be initialized with a brace-enclosed initializer" }

> +

> +// private/protected/virtual base class -> not an aggregate.

> +struct B { };

> +struct D : private B {

> +  int i;

> +  int j;

> +};

> +

> +D d({}, 1, 2); // { dg-error "no matching function" }

> +

> +// Private non-static data member -> not an aggregate.

> +struct P {

> +  int i;

> +private:

> +  int j;

> +};

> +

> +P p(1, 2); // { dg-error "no matching function" }

> +

> +// User-declared constructor -> not an aggregate.

> +struct U {

> +  U() {}

> +  int i;

> +  int j;

> +};

> +

> +U u(1, 2); // { dg-error "no matching function" }

> +

> +// virtual member function -> not an aggregate.

> +struct V {

> +  int i;

> +  int j;

> +  virtual int foo(int);

> +};

> +

> +V v(1, 2); // { dg-error "no matching function" }

> +

> +struct nonaggr {

> +  int i;

> +  int j;

> +private:

> +  int x;

> +};

> +

> +struct F {

> +  nonaggr n;

> +  F() : n(1) { } // { dg-error "no matching function" }

> +};

> +

> +struct G {

> +  char a[4];

> +};

> +

> +struct H {

> +  G g;

> +  H() : g("oaks") { } // { dg-error "initializer-string" }

> +};

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init12.C gcc/testsuite/g++.dg/cpp2a/paren-init12.C

> new file mode 100644

> index 00000000000..d7be6f2b55d

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init12.C

> @@ -0,0 +1,17 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +struct A;

> +

> +struct C {

> +  operator A();

> +};

> +

> +struct A {

> +  C c;

> +};

> +

> +C c;

> +A a(c);  // invokes C’s conversion function to A

> +

> +// { dg-final { scan-assembler "_ZN1Ccv1AEv" } }

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init13.C gcc/testsuite/g++.dg/cpp2a/paren-init13.C

> new file mode 100644

> index 00000000000..4b9107c70ff

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init13.C

> @@ -0,0 +1,16 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +struct X { int a, b; };

> +struct Y { X x; };

> +

> +void

> +f()

> +{

> +  // This is ok...

> +  Y y1{{1, 2}};

> +  Y y2({1, 2});

> +  // ...but Y y((1,2)) is not the same as Y y{{1,2}}.  (1, 2) is a

> +  // COMPOUND_EXPR.

> +  Y y3((1, 2)); // { dg-error "could not convert" }

> +}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init14.C gcc/testsuite/g++.dg/cpp2a/paren-init14.C

> new file mode 100644

> index 00000000000..837f9531260

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init14.C

> @@ -0,0 +1,10 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++11 } }

> +

> +struct A {

> +  int x;

> +  int y;

> +  A(int, int) = delete;

> +};

> +

> +A a(1, 2); // { dg-error "use of deleted function" }

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init15.C gcc/testsuite/g++.dg/cpp2a/paren-init15.C

> new file mode 100644

> index 00000000000..4311dd4df59

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init15.C

> @@ -0,0 +1,35 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +struct A {

> +  int i;

> +  int j;

> +};

> +

> +struct B : A

> +{

> +  B (): A(1.7, 2) { }

> +};

> +

> +void f(A);

> +

> +void

> +g ()

> +{

> +  f (A(1, 2));

> +}

> +

> +struct S {

> +  int a[2];

> +};

> +

> +S h() { return S({1, 2}); }

> +

> +struct Z {

> +  int i;

> +  int j;

> +  operator A();

> +};

> +

> +Z z;

> +A a = Z(1, 2.3);

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init16.C gcc/testsuite/g++.dg/cpp2a/paren-init16.C

> new file mode 100644

> index 00000000000..c8ed924877e

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init16.C

> @@ -0,0 +1,14 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +struct S { int a[2]; };

> +struct A { S s[1]; };

> +

> +template <typename, int N>

> +struct R { static constexpr auto h = A({S{N}}); };

> +

> +template <typename, int N>

> +struct R2 { static constexpr auto h = A({S({N, N})}); };

> +

> +A foo = R<int, 10>::h;

> +A foo2 = R2<int, 10>::h;

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init17.C gcc/testsuite/g++.dg/cpp2a/paren-init17.C

> new file mode 100644

> index 00000000000..8e08b5288a5

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init17.C

> @@ -0,0 +1,6 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++11 } }

> +

> +struct A { };

> +struct B { };

> +static_assert (!__is_trivially_constructible(A, B), "");

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init18.C gcc/testsuite/g++.dg/cpp2a/paren-init18.C

> new file mode 100644

> index 00000000000..0aea493f214

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init18.C

> @@ -0,0 +1,9 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +struct A { int a, b, c; };

> +struct S { A a; };

> +constexpr S s{ A(1, 2, 3) };

> +static_assert (s.a.a == 1 && s.a.b == 2 && s.a.c == 3);

> +constexpr S s2 = { A(1, 2, 3) };

> +static_assert (s2.a.a == 1 && s2.a.b == 2 && s2.a.c == 3);

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init2.C gcc/testsuite/g++.dg/cpp2a/paren-init2.C

> new file mode 100644

> index 00000000000..e9e90d7acb6

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init2.C

> @@ -0,0 +1,56 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do run { target c++2a } }

> +

> +struct A {

> +  int i = 0;

> +  int j = 0;

> +};

> +

> +struct B {

> +  A a;

> +  constexpr B() : a(1.1, 2) { }

> +};

> +

> +struct C {

> +  int i;

> +};

> +

> +struct E {

> +  C c;

> +  E() : c(1.2) { }

> +};

> +

> +struct F {

> +  char a[4];

> +};

> +

> +struct G {

> +  F f;

> +  G() : f("yew") { }

> +};

> +

> +struct H {

> +  int i;

> +  int &&r;

> +};

> +

> +int f() { return 42; }

> +

> +struct I {

> +  H h;

> +  I() : h(1, f()) { }

> +};

> +

> +I i;  // dangling ref to f():

> +      // {.i=1, .r=(int &) &TARGET_EXPR <D.2118, f ()>}

> +

> +int

> +main ()

> +{

> +  B b;

> +  if (b.a.i != 1 || b.a.j != 2)

> +    __builtin_abort ();

> +  E e;

> +  if (e.c.i != 1)

> +    __builtin_abort ();

> +}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init3.C gcc/testsuite/g++.dg/cpp2a/paren-init3.C

> new file mode 100644

> index 00000000000..f444005a09f

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init3.C

> @@ -0,0 +1,11 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +class a {

> +  int b{};

> +};

> +class c {

> +  c();

> +  a d;

> +};

> +c::c() {}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init4.C gcc/testsuite/g++.dg/cpp2a/paren-init4.C

> new file mode 100644

> index 00000000000..f8c7bd10b63

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init4.C

> @@ -0,0 +1,142 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do run { target c++2a } }

> +

> +// Test T[]().

> +

> +int i;

> +int a1[](1, 2, 3);

> +static_assert(sizeof(a1) == 3 * sizeof (int), "");

> +int a2[](1.0, 2, 3);

> +static_assert(sizeof(a2) == 3 * sizeof (int), "");

> +int a3[3](1, 2, 3);

> +int a4[3](1, 2); // a4[2] is value-initialized.

> +int a5[](++i, ++i);

> +static_assert(sizeof(a5) == 2 * sizeof (int), "");

> +int a6[](1);

> +static_assert(sizeof(a6) == sizeof (int), "");

> +int a7[]({});

> +static_assert(sizeof(a7) == sizeof (int), "");

> +int a8[]({}, {}, {}, {}, {}, {});

> +static_assert(sizeof(a8) == 6 * sizeof (int), "");

> +int a9[]((1));

> +static_assert(sizeof(a9) == sizeof (int), "");

> +int a10[]((1), (2), (3));

> +static_assert(sizeof(a10) == 3 * sizeof (int), "");

> +int a11[][2]{1};

> +static_assert(sizeof(a11) == 2 * sizeof (int), "");

> +int a12[][2]({1, 2}, {3, 4});

> +static_assert(sizeof(a12) == 4 * sizeof (int), "");

> +

> +const int (&ra1)[](1, 2);

> +const int (&ra2)[](1.0, 2);

> +const int (&ra3)[2](1.0, 2);

> +int (&&rra1)[](1, 2);

> +int (&&rra2)[](1.0, 2);

> +int (&&rra3)[2](1.0, 2);

> +

> +struct S { int i; } s;

> +S s1[]({1});

> +static_assert(sizeof(s1) == sizeof (S), "");

> +S s2[]({1}, {2});

> +static_assert(sizeof(s2) == 2 * sizeof (S), "");

> +S s3[]({1}, {2}, {3});

> +static_assert(sizeof(s3) == 3 * sizeof (S), "");

> +S s4[3]({1}, {2});

> +static_assert(sizeof(s4) == 3 * sizeof (S), "");

> +S s5[]({++i}, {++i});

> +static_assert(sizeof(s2) == 2 * sizeof (S), "");

> +S s6[](s, s);

> +

> +struct R { int i; int j; };

> +R r1[]({1, 2});

> +static_assert(sizeof(r1) == sizeof (R), "");

> +R r2[]({1, 2}, {3, 4});

> +static_assert(sizeof(r2) == 2 * sizeof (R), "");

> +R r3[]({1.0, 2}, {3.2, 4});

> +static_assert(sizeof(r3) == 2 * sizeof (R), "");

> +

> +char c1[]('r');

> +char c2[]('r', 'a', 'c', 'c', 'o', 'o', 'n');

> +char c3[]("yarrow");

> +char c4[4]("oak");

> +char c5[10]("cat");

> +const char (&c6)[4]("eel");

> +

> +int g;

> +struct X {

> +  int i;

> +  X() { ++g; }

> +  X(int) { };

> +};

> +

> +int

> +main ()

> +{

> +  // Here we'll value-initialize l[1] and l[2], which will zero-initialize it.

> +  int l[3](42);

> +  if (l[0] != 42 || l[1] != 0 || l[2] != 0)

> +    __builtin_abort ();

> +

> +  // Here we'll value-initialize x[2] and x[3].  Since X is a class type

> +  // with a user-provided ctor, we'll default-initialize in both cases.

> +  X x[4]({ 1 }, { 2 });

> +  if (g != 2)

> +    __builtin_abort ();

> +

> +  if (a1[0] != 1 || a1[1] != 2 || a1[2] != 3)

> +    __builtin_abort ();

> +  if (a2[0] != 1 || a2[1] != 2 || a2[2] != 3)

> +    __builtin_abort ();

> +  if (a3[0] != 1 || a3[1] != 2 || a3[2] != 3)

> +    __builtin_abort ();

> +  if (a4[0] != 1 || a4[1] != 2 || a4[2] != 0)

> +    __builtin_abort ();

> +  if (a5[0] != 1 || a5[1] != 2)

> +    __builtin_abort ();

> +  if (a6[0] != 1)

> +    __builtin_abort ();

> +  if (a7[0] != 0)

> +    __builtin_abort ();

> +  if (a8[0] != 0 || a8[1] != 0 || a8[2] != 0 || a8[3] != 0

> +      || a8[4] != 0 || a8[5] != 0)

> +    __builtin_abort ();

> +  if (a9[0] != 1)

> +    __builtin_abort ();

> +  if (a10[0] != 1 || a10[1] != 2 || a10[2] != 3)

> +    __builtin_abort ();

> +  if (a11[0][0] != 1 || a11[0][1] != 0)

> +    __builtin_abort ();

> +  if (a12[0][0] != 1 || a12[0][1] != 2 || a12[1][0] != 3 || a12[1][1] != 4)

> +    __builtin_abort ();

> +

> +  if (ra1[0] != 1 || ra1[1] != 2)

> +    __builtin_abort ();

> +  if (ra2[0] != 1 || ra2[1] != 2)

> +    __builtin_abort ();

> +  if (ra3[0] != 1 || ra3[1] != 2)

> +    __builtin_abort ();

> +  if (rra1[0] != 1 || rra1[1] != 2)

> +    __builtin_abort ();

> +  if (rra2[0] != 1 || rra2[1] != 2)

> +    __builtin_abort ();

> +  if (rra3[0] != 1 || rra3[1] != 2)

> +    __builtin_abort ();

> +

> +  if (s1[0].i != 1)

> +    __builtin_abort ();

> +  if (s2[0].i != 1 || s2[1].i != 2)

> +    __builtin_abort ();

> +  if (s3[0].i != 1 || s3[1].i != 2 || s3[2].i != 3)

> +    __builtin_abort ();

> +  if (s4[0].i != 1 || s4[1].i != 2 || s4[2].i != 0)

> +    __builtin_abort ();

> +  if (s5[0].i != 3 || s5[1].i != 4)

> +    __builtin_abort ();

> +

> +  if (r1[0].i != 1 || r1[0].j != 2)

> +    __builtin_abort ();

> +  if (r2[0].i != 1 || r2[0].j != 2 || r2[1].i != 3 || r2[1].j != 4)

> +    __builtin_abort ();

> +  if (r3[0].i != 1 || r3[0].j != 2 || r3[1].i != 3 || r3[1].j != 4)

> +    __builtin_abort ();

> +}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init5.C gcc/testsuite/g++.dg/cpp2a/paren-init5.C

> new file mode 100644

> index 00000000000..a64cb00ebbc

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init5.C

> @@ -0,0 +1,25 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do run { target c++2a } }

> +

> +union U {

> +  int a;

> +  float b;

> +};

> +

> +// u1 has no active member

> +U u1;

> +// u2 zero-initializes the first member, so u2.a is the active member and

> +// its value is 0.

> +U u2 = U();

> +// u3 uses non-list aggregate initialization, so u3.a is the active member

> +// and its value is 1.

> +U u3 = U(1);

> +

> +int

> +main ()

> +{

> +  if (u2.a != 0)

> +    __builtin_abort ();

> +  if (u3.a != 1)

> +    __builtin_abort ();

> +}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init6.C gcc/testsuite/g++.dg/cpp2a/paren-init6.C

> new file mode 100644

> index 00000000000..b5d97dcb3d8

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init6.C

> @@ -0,0 +1,14 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +// Test that we don't perform lifetime extension for () init.

> +

> +struct A {

> +  int a;

> +  int&& r;

> +};

> +

> +int f();

> +A a(1, f());

> +

> +// { dg-final { scan-assembler-not "_ZGR1a" } }

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init7.C gcc/testsuite/g++.dg/cpp2a/paren-init7.C

> new file mode 100644

> index 00000000000..32af1a7265c

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init7.C

> @@ -0,0 +1,20 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do run { target c++2a } }

> +

> +int h;

> +struct i {

> +  i() {}

> +  explicit i(i &) {}

> +  template <typename j> i(j &) { h++; }

> +};

> +

> +int main() {

> +  {

> +    i a[6];

> +    auto [b, c, d, e, f, g] = a;

> +  }

> +  i a[6];

> +  auto [b, c, d, e, f, g](a);

> +  if (h != 6)

> +    __builtin_abort();

> +}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init8.C gcc/testsuite/g++.dg/cpp2a/paren-init8.C

> new file mode 100644

> index 00000000000..30e71650bc9

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init8.C

> @@ -0,0 +1,13 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +// Test that we don't accept designated inits in ( ).

> +

> +struct S {

> +  int i;

> +  int j = 42;

> +};

> +

> +S s(.i = 12); // { dg-error "expected" }

> +

> +int a[]([0] = 42); // { dg-error "" }

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init9.C gcc/testsuite/g++.dg/cpp2a/paren-init9.C

> new file mode 100644

> index 00000000000..c44b206feb8

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init9.C

> @@ -0,0 +1,10 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +struct B { };

> +struct A : B {

> +  int i;

> +};

> +

> +B b;

> +A a(b);

> diff --git gcc/testsuite/g++.dg/ext/desig10.C gcc/testsuite/g++.dg/ext/desig10.C

> index 24057dd0ad6..c8d672b72d9 100644

> --- gcc/testsuite/g++.dg/ext/desig10.C

> +++ gcc/testsuite/g++.dg/ext/desig10.C

> @@ -1,4 +1,4 @@

>   // PR c++/84972

>   // { dg-additional-options "-w" }

>   

> -char(a[])({.a = 0});  // { dg-error "designated initializer" }

> +char(a[])({.a = 0});  // { dg-error "designated initializer|cannot convert" }

> diff --git gcc/testsuite/g++.dg/template/crash107.C gcc/testsuite/g++.dg/template/crash107.C

> index 3b0b4e8211f..9d8d394d93d 100644

> --- gcc/testsuite/g++.dg/template/crash107.C

> +++ gcc/testsuite/g++.dg/template/crash107.C

> @@ -3,7 +3,7 @@

>   // { dg-options "" }

>   // { dg-additional-options "-Wno-return-type" }

>   

> -template<typename FP_> struct Vec { // { dg-message "note" }

> +template<typename FP_> struct Vec { // { dg-message "note" "" { target c++17_down } }

>       Vec& operator^=(Vec& rhs)     {

>           union {

>               struct {FP_ x,y,z;}; // { dg-error "20:anonymous struct" }

> @@ -14,6 +14,6 @@ template<typename FP_> struct Vec { // { dg-message "note" }

>           return Vec(*this)^=rhs; // { dg-message "required" }

>       }

>   };

> -Vec<double> v(3,4,12); // { dg-error "no matching" }

> -Vec<double> V(12,4,3);  // { dg-error "no matching" }

> +Vec<double> v(3,4,12); // { dg-error "no matching|too many initializers" }

> +Vec<double> V(12,4,3);  // { dg-error "no matching|too many initializers" }

>   Vec<double> c = v^V;   // { dg-message "required" }

> diff --git gcc/testsuite/g++.dg/template/crash95.C gcc/testsuite/g++.dg/template/crash95.C

> index f60e635ae66..47346111328 100644

> --- gcc/testsuite/g++.dg/template/crash95.C

> +++ gcc/testsuite/g++.dg/template/crash95.C

> @@ -8,4 +8,4 @@ template < typename > struct S

>     };

>   };

>   

> -S < int > s(0); // { dg-error "no matching" }

> +S < int > s(0); // { dg-error "no matching|too many initializers" }

> diff --git gcc/testsuite/g++.old-deja/g++.jason/crash3.C gcc/testsuite/g++.old-deja/g++.jason/crash3.C

> index e94cc7c9781..3c4a1f84969 100644

> --- gcc/testsuite/g++.old-deja/g++.jason/crash3.C

> +++ gcc/testsuite/g++.old-deja/g++.jason/crash3.C

> @@ -2,12 +2,12 @@

>   // Bug: g++ tries to generate initialization semantics for a Node from an int,

>   // and fails.

>   

> -struct Node			// { dg-message "note" }

> +struct Node			// { dg-message "note" "" { target c++17_down } }

>   {

>     Node* child[2];

>   };

>   

>   void bug(int i)

>   {

> -  Node* q = new Node(i);	// { dg-error "no matching" }

> +  Node* q = new Node(i);	// { dg-error "no matching" "" { target c++17_down }  }

>   }

> diff --git gcc/testsuite/g++.old-deja/g++.law/ctors11.C gcc/testsuite/g++.old-deja/g++.law/ctors11.C

> index 39ee76b0ae7..b29b18a62a3 100644

> --- gcc/testsuite/g++.old-deja/g++.law/ctors11.C

> +++ gcc/testsuite/g++.old-deja/g++.law/ctors11.C

> @@ -10,12 +10,12 @@ public:

>     inline A(int x){printf("constructing A with %d\n", x);}

>   };

>   

> -class B:public A{ // { dg-message "note" } non-default constructor

> +class B:public A{ // { dg-message "note" "" { target c++17_down } } non-default constructor

>   private:

>   public:

>   };

>   

>   int main()

>   {

> -  B(10);// { dg-error "match" } B doesn't have a constructor taking int

> +  B(10);// { dg-error "match" "" { target c++17_down } } B doesn't have a constructor taking int

>   }

> diff --git gcc/testsuite/g++.old-deja/g++.law/ctors9.C gcc/testsuite/g++.old-deja/g++.law/ctors9.C

> index 43ba1262c95..5856d5dc883 100644

> --- gcc/testsuite/g++.old-deja/g++.law/ctors9.C

> +++ gcc/testsuite/g++.old-deja/g++.law/ctors9.C

> @@ -20,7 +20,7 @@ Foo::Foo(int aa)

>   { }

>   

>   

> -struct var_Foo: public Foo // { dg-message "note" }  base.*// ERROR -  in class.*

> +struct var_Foo: public Foo // { dg-message "note" "" { target c++17_down } }  base.*// ERROR -  in class.*

>   {

>     var_Foo* operator-> () {return this;}

>   };

> @@ -32,7 +32,7 @@ int blort(Foo& f)

>   

>   int main()

>   {

> -  var_Foo b(2);// { dg-error "match" }

> +  var_Foo b(2);// { dg-error "match" "" { target c++17_down } }

>     b->a = 0;

>     int x = blort(b);

>     return x;

> diff --git gcc/testsuite/g++.old-deja/g++.mike/net22.C gcc/testsuite/g++.old-deja/g++.mike/net22.C

> index e5e1cb1081d..abbf6191023 100644

> --- gcc/testsuite/g++.old-deja/g++.mike/net22.C

> +++ gcc/testsuite/g++.old-deja/g++.mike/net22.C

> @@ -5,10 +5,11 @@ public:

>     Parent( char *s ) {}

>   };

>   

> -class Child : public Parent {		// { dg-message "note" } called

> +class Child : public Parent {		// { dg-message "note" "" { target c++17_down } } called

>   };

>   

>   int main() {

> -  Child c( "String initializer" );	// { dg-error "match" } bad

> +  Child c( "String initializer" );	// { dg-error "match" "" { target c++17_down } } bad

> +// { dg-error "forbids converting a string constant" "" { target c++2a } .-1 }

>     return 0;

>   }

> diff --git gcc/testsuite/g++.old-deja/g++.niklas/t128.C gcc/testsuite/g++.old-deja/g++.niklas/t128.C

> index 19e3ca1dab0..0b3346c4ef3 100644

> --- gcc/testsuite/g++.old-deja/g++.niklas/t128.C

> +++ gcc/testsuite/g++.old-deja/g++.niklas/t128.C

> @@ -1,5 +1,5 @@

>   // { dg-do assemble  }

>   // GROUPS niklas uncaught default-construct

>   struct A { A (int); };

> -struct B : A {}; // { dg-message "note" } without ctor // ERROR - candidates

> -void f () { B (0); }// { dg-error "match" } .*

> +struct B : A {}; // { dg-message "note" "" { target c++17_down } } without ctor // ERROR - candidates

> +void f () { B (0); }// { dg-error "match" "" { target c++17_down } } .*

>
Marek Polacek Nov. 27, 2019, 10:25 p.m. | #10
On Wed, Nov 27, 2019 at 04:54:55PM -0500, Jason Merrill wrote:
> > > > @@ -921,8 +921,20 @@ perform_member_init (tree member, tree init)

> > > >    		inform (DECL_SOURCE_LOCATION (member),

> > > >    			"%q#D should be initialized", member );

> > > >    	    }

> > > > -	  finish_expr_stmt (build_aggr_init (decl, init, flags,

> > > > -					     tf_warning_or_error));

> > > > +	  init = build_aggr_init (decl, init, flags, tf_warning_or_error);

> > > > +	  /* In C++20, a member initializer list can be initializing an

> > > > +	     aggregate from a parenthesized list of values:

> > > > +

> > > > +	       struct S {

> > > > +		 A aggr;

> > > > +		 S() : aggr(1, 2, 3) { }

> > > > +	       };

> > > > +

> > > > +	      In such case, build_aggr_init will build up an INIT_EXPR like

> > > > +	      we do for aggr{1, 2, 3}, so that build_data_member_initialization

> > > > +	      can grok it.  */

> > > > +	  if (TREE_CODE (init) != INIT_EXPR)

> > > > +	    finish_expr_stmt (init);

> > > 

> > > Why don't we want to finish_expr_stmt an INIT_EXPR?

> > 

> > Because expand_default_init has already finish_expr_stmt-ed it.  Adding it

> > twice sounds wrong.

> 

> But the finish_expr_stmt in expand_default_init is inside the STATEMENT_LIST

> we pushed into in build_aggr_init.  If finish_init_stmts returned the

> INIT_EXPR, we still need to add it to the enclosing STATEMENT_LIST.


I don't understand why none of my tests broke but here's a patch with that if
removed:

dg.exp passed, bootstrap/regtest running.

2019-11-27  Marek Polacek  <polacek@redhat.com>

	PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
	* c-cppbuiltin.c (c_cpp_builtins): Predefine
	__cpp_aggregate_paren_init=201902 for -std=c++2a.

	* call.c (build_new_method_call_1): Handle parenthesized initialization
	of aggregates by building up a CONSTRUCTOR.
	(extend_ref_init_temps): Do nothing for CONSTRUCTOR_IS_PAREN_INIT.
	* cp-tree.h (CONSTRUCTOR_IS_PAREN_INIT, LOOKUP_AGGREGATE_PAREN_INIT):
	Define.
	* decl.c (grok_reference_init): Handle aggregate initialization from
	a parenthesized list of values.
	(reshape_init): Do nothing for CONSTRUCTOR_IS_PAREN_INIT.
	(check_initializer): Handle initialization of an array from a
	parenthesized list of values.  Use NULL_TREE instead of NULL.
	* init.c (expand_default_init): Set LOOKUP_AGGREGATE_PAREN_INIT for the
	build_special_member_call call.  If it returns a
	BRACE_ENCLOSED_INITIALIZER_P, call digest_init and build an INIT_EXPR.
	Set TREE_SIDE_EFFECTS on it.
	* tree.c (build_cplus_new): Handle BRACE_ENCLOSED_INITIALIZER_P.
	* typeck2.c (digest_init_r): Set LOOKUP_AGGREGATE_PAREN_INIT if it
	receives a CONSTRUCTOR with CONSTRUCTOR_IS_PAREN_INIT set.  Allow
	narrowing when LOOKUP_AGGREGATE_PAREN_INIT.
	(massage_init_elt): Don't lose LOOKUP_AGGREGATE_PAREN_INIT when passing
	flags to digest_init_r.

	* g++.dg/cpp0x/constexpr-99.C: Only expect an error in C++17 and
	lesser.
	* g++.dg/cpp0x/explicit7.C: Likewise.
	* g++.dg/cpp0x/initlist12.C: Adjust dg-error.
	* g++.dg/cpp0x/pr31437.C: Likewise.
	* g++.dg/cpp2a/feat-cxx2a.C: Add __cpp_aggregate_paren_init test.
	* g++.dg/cpp2a/paren-init1.C: New test.
	* g++.dg/cpp2a/paren-init10.C: New test.
	* g++.dg/cpp2a/paren-init11.C: New test.
	* g++.dg/cpp2a/paren-init12.C: New test.
	* g++.dg/cpp2a/paren-init13.C: New test.
	* g++.dg/cpp2a/paren-init14.C: New test.
	* g++.dg/cpp2a/paren-init15.C: New test.
	* g++.dg/cpp2a/paren-init16.C: New test.
	* g++.dg/cpp2a/paren-init17.C: New test.
	* g++.dg/cpp2a/paren-init18.C: New test.
	* g++.dg/cpp2a/paren-init2.C: New test.
	* g++.dg/cpp2a/paren-init3.C: New test.
	* g++.dg/cpp2a/paren-init4.C: New test.
	* g++.dg/cpp2a/paren-init5.C: New test.
	* g++.dg/cpp2a/paren-init6.C: New test.
	* g++.dg/cpp2a/paren-init7.C: New test.
	* g++.dg/cpp2a/paren-init8.C: New test.
	* g++.dg/cpp2a/paren-init9.C: New test.
	* g++.dg/ext/desig10.C: Adjust dg-error.
	* g++.dg/template/crash107.C: Likewise.
	* g++.dg/template/crash95.C: Likewise.
	* g++.old-deja/g++.jason/crash3.C: Only expect an error in C++17 and
	lesser.
	* g++.old-deja/g++.law/ctors11.C: Likewise.
	* g++.old-deja/g++.law/ctors9.C: Likewise.
	* g++.old-deja/g++.mike/net22.C: Likewise.
	* g++.old-deja/g++.niklas/t128.C: Likewise.

diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c
index 50066e4dd8b..2f30b2a3235 100644
--- gcc/c-family/c-cppbuiltin.c
+++ gcc/c-family/c-cppbuiltin.c
@@ -1004,6 +1004,7 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_impl_destroying_delete=201806L");
 	  cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907L");
 	  cpp_define (pfile, "__cpp_impl_three_way_comparison=201907L");
+	  cpp_define (pfile, "__cpp_aggregate_paren_init=201902L");
 	}
       if (flag_concepts)
         {
diff --git gcc/cp/call.c gcc/cp/call.c
index 8bfe3368816..1d14409ec43 100644
--- gcc/cp/call.c
+++ gcc/cp/call.c
@@ -10111,6 +10111,30 @@ build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args,
 
   if (!any_viable_p)
     {
+      /* [dcl.init], 17.6.2.2:
+
+	 Otherwise, if no constructor is viable, the destination type is
+	 a (possibly cv-qualified) aggregate class A, and the initializer
+	 is a parenthesized expression-list, the object is initialized as
+	 follows...
+
+	 We achieve this by building up a CONSTRUCTOR, as for list-init,
+	 and setting CONSTRUCTOR_IS_PAREN_INIT to distinguish between
+	 the two.  */
+      if (DECL_CONSTRUCTOR_P (fn)
+	  && !(flags & LOOKUP_ONLYCONVERTING)
+	  && !cp_unevaluated_operand
+	  && cxx_dialect >= cxx2a
+	  && CP_AGGREGATE_TYPE_P (basetype)
+	  && !user_args->is_empty ())
+	{
+	  /* Create a CONSTRUCTOR from ARGS, e.g. {1, 2} from <1, 2>.  */
+	  tree list = build_tree_list_vec (user_args);
+	  tree ctor = build_constructor_from_list (init_list_type_node, list);
+	  CONSTRUCTOR_IS_DIRECT_INIT (ctor) = true;
+	  CONSTRUCTOR_IS_PAREN_INIT (ctor) = true;
+	  return ctor;
+	}
       if (complain & tf_error)
 	complain_about_no_candidates_for_method_call (instance, candidates,
 						      explicit_targs, basetype,
@@ -11792,9 +11816,16 @@ perform_direct_initialization_if_possible (tree type,
      If the destination type is a (possibly cv-qualified) class type:
 
      -- If the initialization is direct-initialization ...,
-     constructors are considered. ... If no constructor applies, or
-     the overload resolution is ambiguous, the initialization is
-     ill-formed.  */
+     constructors are considered.
+
+       -- If overload resolution is successful, the selected constructor
+       is called to initialize the object, with the initializer expression
+       or expression-list as its argument(s).
+
+       -- Otherwise, if no constructor is viable, the destination type is
+       a (possibly cv-qualified) aggregate class A, and the initializer is
+       a parenthesized expression-list, the object is initialized as
+       follows...  */
   if (CLASS_TYPE_P (type))
     {
       releasing_vec args (make_tree_vector_single (expr));
@@ -12150,6 +12181,12 @@ extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups)
 	ctor = TARGET_EXPR_INITIAL (ctor);
       if (TREE_CODE (ctor) == CONSTRUCTOR)
 	{
+	  /* [dcl.init] When initializing an aggregate from a parenthesized list
+	     of values... a temporary object bound to a reference does not have
+	     its lifetime extended.  */
+	  if (CONSTRUCTOR_IS_PAREN_INIT (ctor))
+	    return init;
+
 	  if (is_std_init_list (type))
 	    {
 	      /* The temporary array underlying a std::initializer_list
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index 4b4bc245d81..4aa871a21c0 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -4325,6 +4325,11 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define CONSTRUCTOR_IS_DESIGNATED_INIT(NODE) \
   (TREE_LANG_FLAG_6 (CONSTRUCTOR_CHECK (NODE)))
 
+/* True if this CONSTRUCTOR comes from a parenthesized list of values, e.g.
+   A(1, 2, 3).  */
+#define CONSTRUCTOR_IS_PAREN_INIT(NODE) \
+  (CONSTRUCTOR_CHECK(NODE)->base.private_flag)
+
 /* True if NODE represents a conversion for direct-initialization in a
    template.  Set by perform_implicit_conversion_flags.  */
 #define IMPLICIT_CONV_EXPR_DIRECT_INIT(NODE) \
@@ -5583,6 +5588,8 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
    args), then we swap the conversions back in build_new_op_1 (so they
    correspond to the order of the args in the candidate).  */
 #define LOOKUP_REVERSED (LOOKUP_REWRITTEN << 1)
+/* We're initializing an aggregate from a parenthesized list of values.  */
+#define LOOKUP_AGGREGATE_PAREN_INIT (LOOKUP_REVERSED << 1)
 
 #define LOOKUP_NAMESPACES_ONLY(F)  \
   (((F) & LOOKUP_PREFER_NAMESPACES) && !((F) & LOOKUP_PREFER_TYPES))
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 26120720f07..33cd474f834 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -5531,11 +5531,28 @@ grok_reference_init (tree decl, tree type, tree init, int flags)
       return NULL_TREE;
     }
 
+  tree ttype = TREE_TYPE (type);
   if (TREE_CODE (init) == TREE_LIST)
-    init = build_x_compound_expr_from_list (init, ELK_INIT,
-					    tf_warning_or_error);
+    {
+      /* This handles (C++20 only) code like
+
+	   const A& r(1, 2, 3);
+
+	 where we treat the parenthesized list as a CONSTRUCTOR.  */
+      if (TREE_TYPE (init) == NULL_TREE
+	  && CP_AGGREGATE_TYPE_P (ttype)
+	  && !DECL_DECOMPOSITION_P (decl)
+	  && (cxx_dialect >= cxx2a))
+	{
+	  init = build_constructor_from_list (init_list_type_node, init);
+	  CONSTRUCTOR_IS_DIRECT_INIT (init) = true;
+	  CONSTRUCTOR_IS_PAREN_INIT (init) = true;
+	}
+      else
+	init = build_x_compound_expr_from_list (init, ELK_INIT,
+						tf_warning_or_error);
+    }
 
-  tree ttype = TREE_TYPE (type);
   if (TREE_CODE (ttype) != ARRAY_TYPE
       && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE)
     /* Note: default conversion is only called in very special cases.  */
@@ -6437,6 +6454,11 @@ reshape_init (tree type, tree init, tsubst_flags_t complain)
   if (vec_safe_is_empty (v))
     return init;
 
+  /* Brace elision is not performed for a CONSTRUCTOR representing
+     parenthesized aggregate initialization.  */
+  if (CONSTRUCTOR_IS_PAREN_INIT (init))
+    return init;
+
   /* Handle [dcl.init.list] direct-list-initialization from
      single element of enumeration with a fixed underlying type.  */
   if (is_direct_enum_init (type, init))
@@ -6640,6 +6662,41 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
 	      flags |= LOOKUP_NO_NARROWING;
 	    }
 	}
+      /* [dcl.init] "Otherwise, if the destination type is an array, the object
+	 is initialized as follows..."  So handle things like
+
+	  int a[](1, 2, 3);
+
+	 which is permitted in C++20 by P0960.  */
+      else if (TREE_CODE (init) == TREE_LIST
+	       && TREE_TYPE (init) == NULL_TREE
+	       && TREE_CODE (type) == ARRAY_TYPE
+	       && !DECL_DECOMPOSITION_P (decl)
+	       && (cxx_dialect >= cxx2a))
+	{
+	  /* [dcl.init.string] "An array of ordinary character type [...]
+	     can be initialized by an ordinary string literal [...] by an
+	     appropriately-typed string literal enclosed in braces" only
+	     talks about braces, but GCC has always accepted
+
+	       char a[]("foobar");
+
+	     so we continue to do so.  */
+	  tree val = TREE_VALUE (init);
+	  if (TREE_CHAIN (init) == NULL_TREE
+	      && char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type)))
+	      && TREE_CODE (tree_strip_any_location_wrapper (val))
+		 == STRING_CST)
+	    /* If the list has a single element and it's a string literal,
+	       then it's the initializer for the array as a whole.  */
+	    init = val;
+	  else
+	    {
+	      init = build_constructor_from_list (init_list_type_node, init);
+	      CONSTRUCTOR_IS_DIRECT_INIT (init) = true;
+	      CONSTRUCTOR_IS_PAREN_INIT (init) = true;
+	    }
+	}
       else if (TREE_CODE (init) == TREE_LIST
 	       && TREE_TYPE (init) != unknown_type_node
 	       && !MAYBE_CLASS_TYPE_P (type))
@@ -6683,6 +6740,9 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
 	    init_code = TREE_OPERAND (init_code, 0);
 	  if (TREE_CODE (init_code) == INIT_EXPR)
 	    {
+	      /* In C++20, the call to build_aggr_init could have created
+		 an INIT_EXPR with a CONSTRUCTOR as the RHS to handle
+		 A(1, 2).  */
 	      init = TREE_OPERAND (init_code, 1);
 	      init_code = NULL_TREE;
 	      /* Don't call digest_init; it's unnecessary and will complain
@@ -6736,7 +6796,7 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
 			0, "array %qD initialized by parenthesized "
 			"string literal %qE",
 			decl, DECL_INITIAL (decl));
-	  init = NULL;
+	  init = NULL_TREE;
 	}
     }
   else
diff --git gcc/cp/init.c gcc/cp/init.c
index aa48f80e58d..77bbea20705 100644
--- gcc/cp/init.c
+++ gcc/cp/init.c
@@ -921,6 +921,17 @@ perform_member_init (tree member, tree init)
 		inform (DECL_SOURCE_LOCATION (member),
 			"%q#D should be initialized", member );
 	    }
+	  /* In C++20, a member initializer list can be initializing an
+	     aggregate from a parenthesized list of values:
+
+	       struct S {
+		 A aggr;
+		 S() : aggr(1, 2, 3) { }
+	       };
+
+	      In such case, build_aggr_init will build up an INIT_EXPR like
+	      we do for aggr{1, 2, 3}, so that build_data_member_initialization
+	      can grok it.  */
 	  finish_expr_stmt (build_aggr_init (decl, init, flags,
 					     tf_warning_or_error));
 	}
@@ -1967,8 +1978,23 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
       tree ctor_name = (true_exp == exp
 			? complete_ctor_identifier : base_ctor_identifier);
 
+      /* Given class A,
+
+	   A a(1, 2);
+
+	 can mean a call to a constructor A::A(int, int), if present.  If not,
+	 but A is an aggregate, we will try aggregate initialization.  */
       rval = build_special_member_call (exp, ctor_name, &parms, binfo, flags,
 					complain);
+      if (BRACE_ENCLOSED_INITIALIZER_P (rval))
+	{
+	  gcc_assert (cxx_dialect >= cxx2a);
+	  rval = digest_init (type, rval, tf_warning_or_error);
+	  rval = build2 (INIT_EXPR, type, exp, rval);
+	  /* So that we do finish_expr_stmt below.  Don't return here, we
+	     need to release PARMS.  */
+	  TREE_SIDE_EFFECTS (rval) = 1;
+	}
     }
 
   if (parms != NULL)
diff --git gcc/cp/tree.c gcc/cp/tree.c
index d125d60b270..dede4dce36b 100644
--- gcc/cp/tree.c
+++ gcc/cp/tree.c
@@ -669,6 +669,15 @@ build_aggr_init_expr (tree type, tree init)
 tree
 build_cplus_new (tree type, tree init, tsubst_flags_t complain)
 {
+  /* This function should cope with what build_special_member_call
+     can produce.  When performing parenthesized aggregate initialization,
+     it can produce a { }.  */
+  if (BRACE_ENCLOSED_INITIALIZER_P (init))
+    {
+      gcc_assert (cxx_dialect >= cxx2a);
+      return finish_compound_literal (type, init, complain);
+    }
+
   tree rval = build_aggr_init_expr (type, init);
   tree slot;
 
diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
index b8868546444..9c6f25c8608 100644
--- gcc/cp/typeck2.c
+++ gcc/cp/typeck2.c
@@ -1117,6 +1117,10 @@ digest_init_r (tree type, tree init, int nested, int flags,
 
   tree stripped_init = init;
 
+  if (BRACE_ENCLOSED_INITIALIZER_P (init)
+      && CONSTRUCTOR_IS_PAREN_INIT (init))
+    flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+
   /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue
      (g++.old-deja/g++.law/casts2.C).  */
   if (TREE_CODE (init) == NON_LVALUE_EXPR)
@@ -1224,7 +1228,9 @@ digest_init_r (tree type, tree init, int nested, int flags,
   if ((code != COMPLEX_TYPE || BRACE_ENCLOSED_INITIALIZER_P (stripped_init))
       && (SCALAR_TYPE_P (type) || code == REFERENCE_TYPE))
     {
-      if (nested)
+      /* Narrowing is OK when initializing an aggregate from
+	 a parenthesized list.  */
+      if (nested && !(flags & LOOKUP_AGGREGATE_PAREN_INIT))
 	flags |= LOOKUP_NO_NARROWING;
       init = convert_for_initialization (0, type, init, flags,
 					 ICR_INIT, NULL_TREE, 0,
@@ -1384,9 +1390,12 @@ static tree
 massage_init_elt (tree type, tree init, int nested, int flags,
 		  tsubst_flags_t complain)
 {
-  flags &= LOOKUP_ALLOW_FLEXARRAY_INIT;
-  flags |= LOOKUP_IMPLICIT;
-  init = digest_init_r (type, init, nested ? 2 : 1, flags, complain);
+  int new_flags = LOOKUP_IMPLICIT;
+  if (flags & LOOKUP_ALLOW_FLEXARRAY_INIT)
+    new_flags |= LOOKUP_ALLOW_FLEXARRAY_INIT;
+  if (flags & LOOKUP_AGGREGATE_PAREN_INIT)
+    new_flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+  init = digest_init_r (type, init, nested ? 2 : 1, new_flags, complain);
   /* Strip a simple TARGET_EXPR when we know this is an initializer.  */
   if (SIMPLE_TARGET_EXPR_P (init))
     init = TARGET_EXPR_INITIAL (init);
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-99.C gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
index 8d791dd032d..4d3953d0983 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
@@ -9,5 +9,6 @@ struct A
 struct B
 {
   A a;
-    constexpr B() : a(0) {} // { dg-error "no matching function" }
+  // P0960R3 allows paren-init.
+  constexpr B() : a(0) {} // { dg-error "no matching function" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/explicit7.C gcc/testsuite/g++.dg/cpp0x/explicit7.C
index 574796d117d..67b50542bc3 100644
--- gcc/testsuite/g++.dg/cpp0x/explicit7.C
+++ gcc/testsuite/g++.dg/cpp0x/explicit7.C
@@ -10,7 +10,7 @@ struct A { };
 struct B: A { };
 struct C {
   explicit operator B*();	// { dg-message "explicit" }
-  explicit operator B&();	// { dg-message "explicit" }
+  explicit operator B&();	// { dg-message "explicit" "" { target c++17_down } }
 };
 
 C c;
diff --git gcc/testsuite/g++.dg/cpp0x/initlist12.C gcc/testsuite/g++.dg/cpp0x/initlist12.C
index f72a6dac525..c2a2b86520f 100644
--- gcc/testsuite/g++.dg/cpp0x/initlist12.C
+++ gcc/testsuite/g++.dg/cpp0x/initlist12.C
@@ -6,15 +6,15 @@ struct A
   int i;
 };
 
-A a({1,2});			// { dg-error "no match" }
+A a({1,2});			// { dg-error "no match|cannot convert" }
 
 union U
 {
   int i,j;
 };
 
-U u({1,2});			// { dg-error "no match" }
+U u({1,2});			// { dg-error "no match|cannot convert" }
 
 union V {};
 
-V v({1});			// { dg-error "no match" }
+V v({1});			// { dg-error "no match|too many initializers" }
diff --git gcc/testsuite/g++.dg/cpp0x/pr31437.C gcc/testsuite/g++.dg/cpp0x/pr31437.C
index 7e2ed7a1375..532b533c8d3 100644
--- gcc/testsuite/g++.dg/cpp0x/pr31437.C
+++ gcc/testsuite/g++.dg/cpp0x/pr31437.C
@@ -1,9 +1,9 @@
 // { dg-do compile { target c++11 } }
-template <typename... T> struct A // { dg-message "candidates|A" }
+template <typename... T> struct A // { dg-message "candidates|A" "" { target c++17_down } }
 {
   A(T* p) {  // { dg-error "parameter packs|T" }
    (A<T...>*)(p); 
   }
 };
 
-A<int> a(0); // { dg-error "no matching" }
+A<int> a(0); // { dg-error "no matching|too many initializers" }
diff --git gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
index 753a6ecd0a8..bea30447f6b 100644
--- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
+++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
@@ -460,6 +460,12 @@
 #  error "__cpp_constexpr_dynamic_alloc != 201907"
 #endif
 
+#ifndef __cpp_aggregate_paren_init
+#  error "__cpp_aggregate_paren_init"
+#elif __cpp_aggregate_paren_init != 201902
+#  error "__cpp_aggregate_paren_init != 201902"
+#endif
+
 #ifdef __has_cpp_attribute
 
 #  if ! __has_cpp_attribute(maybe_unused)
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init1.C gcc/testsuite/g++.dg/cpp2a/paren-init1.C
new file mode 100644
index 00000000000..a54b2ccaf6d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init1.C
@@ -0,0 +1,116 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+struct A {
+  int i;
+  int j;
+  int k;
+};
+
+struct B {
+  int i;
+  int j;
+  int k = 42;
+};
+
+struct C {
+  A a;
+};
+
+struct D {
+  A a1;
+  A a2;
+};
+
+struct E {
+  int i;
+};
+
+// F has a base class, but it's not virtual, private, or protected, so this is
+// still an aggregate (since C++17).
+struct F : E {
+  int j;
+  int k;
+};
+
+F f({1}, 2, 3);
+
+// A non-virtual member function doesn't make it a non-aggregate.
+struct G {
+  int i;
+  double j;
+  int foo(int, int);
+};
+
+G g(1, 2.14);
+
+class H {
+public:
+  H (int) { }
+};
+
+class I : public H { };
+
+int i;
+A a1(1, 2);
+A a2(1.0, 2);
+A a3(++i, ++i);
+const A& ra(1, 2);
+
+A ca = A(1, 2);
+A ca2 = A(1.0, 2);
+A ca3 = A(++i, ++i);
+const A& rca = A(1, 2);
+
+B b1(1, 2, 3);
+B b2(1, 2);
+B b3(1);
+
+C c1({5, 6, 7});
+D d1({1, 2, 3}, {5, 6, 7});
+
+struct W {
+  const char *s, *t;
+};
+W w1("fluffer", "nutter");
+
+struct Y {
+  char a[4];
+};
+Y y("yew");
+
+int
+main ()
+{
+  I(10);
+
+  // A::k will be value-initialized.
+  if (a1.i != 1 || a1.j != 2 || a1.k != 0)
+    __builtin_abort ();
+  if (a2.i != 1 || a2.j != 2 || a2.k != 0)
+    __builtin_abort ();
+  if (a3.i != 1 || a3.j != 2 || a3.k != 0)
+    __builtin_abort ();
+  if (ra.i != 1 || ra.j != 2 || ra.k != 0)
+    __builtin_abort ();
+  if (ca.i != 1 || ca.j != 2 || ca.k != 0)
+    __builtin_abort ();
+  if (ca2.i != 1 || ca2.j != 2 || ca2.k != 0)
+    __builtin_abort ();
+  if (ca3.i != 3 || ca3.j != 4 || ca3.k != 0)
+    __builtin_abort ();
+
+  if (b1.i != 1 || b1.j != 2 || b1.k != 3)
+    __builtin_abort ();
+  // The default member initializer will be used for B::k.
+  if (b2.i != 1 || b2.j != 2 || b2.k != 42)
+    __builtin_abort ();
+  if (b3.i != 1 || b3.j != 0 || b3.k != 42)
+    __builtin_abort ();
+
+  if (c1.a.i != 5 || c1.a.j != 6 || c1.a.k != 7)
+    __builtin_abort ();
+
+  if (f.i != 1 || f.j != 2 || f.k != 3)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init10.C gcc/testsuite/g++.dg/cpp2a/paren-init10.C
new file mode 100644
index 00000000000..5c70d9d59ee
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init10.C
@@ -0,0 +1,18 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test from [dcl.init].
+
+struct A {
+  int a;
+  int&& r;
+};
+
+int f();
+int n = 10;
+
+A a1{1, f()};               // OK, lifetime is extended
+A a2(1, f());               // well-formed, but dangling reference
+A a3{1.0, 1};               // { dg-error "narrowing conversion" }
+A a4(1.0, 1);               // well-formed, but dangling reference
+A a5(1.0, static_cast<int&&>(n));    // OK
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init11.C gcc/testsuite/g++.dg/cpp2a/paren-init11.C
new file mode 100644
index 00000000000..82ca2669545
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init11.C
@@ -0,0 +1,88 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test ill-formed code.
+
+// If k is greater than the size of the array, the program is ill-formed.
+int a1[2](1, 2, 3); // { dg-error "too many initializers" }
+int a2[](); // { dg-error "array of functions" }
+int a3[](()); // { dg-error "expected primary-expression" }
+int a4[]("raccoon"); // { dg-error "invalid conversion" }
+
+struct S {
+  int i;
+  int j;
+};
+
+S s1(1, 1, 1); // { dg-error "too many initializers" }
+
+union U2 {
+  int a;
+  float b;
+};
+
+// [dcl.init.aggr]/19:
+// When a union is initialized with an initializer list, there shall not be
+// more than one explicitly initialized element.
+U2 u4 = U2(1, 2); // { dg-error "too many initializers" }
+
+// Test there is no brace elision.
+
+int a[2][2](1, 2, 3, 4); // { dg-error "too many initializers|array must be initialized with a brace-enclosed initializer" }
+
+// private/protected/virtual base class -> not an aggregate.
+struct B { };
+struct D : private B {
+  int i;
+  int j;
+};
+
+D d({}, 1, 2); // { dg-error "no matching function" }
+
+// Private non-static data member -> not an aggregate.
+struct P {
+  int i;
+private:
+  int j;
+};
+
+P p(1, 2); // { dg-error "no matching function" }
+
+// User-declared constructor -> not an aggregate.
+struct U {
+  U() {}
+  int i;
+  int j;  
+};
+
+U u(1, 2); // { dg-error "no matching function" }
+
+// virtual member function -> not an aggregate.
+struct V {
+  int i;
+  int j;
+  virtual int foo(int);
+};
+
+V v(1, 2); // { dg-error "no matching function" }
+
+struct nonaggr {
+  int i;
+  int j;
+private:
+  int x;
+};
+
+struct F {
+  nonaggr n;
+  F() : n(1) { } // { dg-error "no matching function" }
+};
+
+struct G {
+  char a[4];
+};
+
+struct H {
+  G g;
+  H() : g("oaks") { } // { dg-error "initializer-string" }
+};
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init12.C gcc/testsuite/g++.dg/cpp2a/paren-init12.C
new file mode 100644
index 00000000000..d7be6f2b55d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init12.C
@@ -0,0 +1,17 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct A;
+
+struct C {
+  operator A();
+};
+
+struct A {
+  C c;
+};
+
+C c;
+A a(c);  // invokes C’s conversion function to A
+
+// { dg-final { scan-assembler "_ZN1Ccv1AEv" } }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init13.C gcc/testsuite/g++.dg/cpp2a/paren-init13.C
new file mode 100644
index 00000000000..4b9107c70ff
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init13.C
@@ -0,0 +1,16 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct X { int a, b; };
+struct Y { X x; };
+
+void
+f()
+{
+  // This is ok...
+  Y y1{{1, 2}};
+  Y y2({1, 2});
+  // ...but Y y((1,2)) is not the same as Y y{{1,2}}.  (1, 2) is a
+  // COMPOUND_EXPR.
+  Y y3((1, 2)); // { dg-error "could not convert" }
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init14.C gcc/testsuite/g++.dg/cpp2a/paren-init14.C
new file mode 100644
index 00000000000..837f9531260
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init14.C
@@ -0,0 +1,10 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++11 } }
+
+struct A {
+  int x;
+  int y;
+  A(int, int) = delete;
+};
+
+A a(1, 2); // { dg-error "use of deleted function" }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init15.C gcc/testsuite/g++.dg/cpp2a/paren-init15.C
new file mode 100644
index 00000000000..4311dd4df59
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init15.C
@@ -0,0 +1,35 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct A {
+  int i;
+  int j;
+};
+
+struct B : A
+{
+  B (): A(1.7, 2) { }
+};
+
+void f(A);
+
+void
+g ()
+{
+  f (A(1, 2));
+}
+
+struct S {
+  int a[2];
+};
+
+S h() { return S({1, 2}); }
+
+struct Z {
+  int i;
+  int j;
+  operator A();
+};
+
+Z z;
+A a = Z(1, 2.3);
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init16.C gcc/testsuite/g++.dg/cpp2a/paren-init16.C
new file mode 100644
index 00000000000..c8ed924877e
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init16.C
@@ -0,0 +1,14 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct S { int a[2]; };
+struct A { S s[1]; };
+
+template <typename, int N>
+struct R { static constexpr auto h = A({S{N}}); };
+
+template <typename, int N>
+struct R2 { static constexpr auto h = A({S({N, N})}); };
+
+A foo = R<int, 10>::h;
+A foo2 = R2<int, 10>::h;
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init17.C gcc/testsuite/g++.dg/cpp2a/paren-init17.C
new file mode 100644
index 00000000000..8e08b5288a5
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init17.C
@@ -0,0 +1,6 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++11 } }
+
+struct A { };
+struct B { };
+static_assert (!__is_trivially_constructible(A, B), "");
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init18.C gcc/testsuite/g++.dg/cpp2a/paren-init18.C
new file mode 100644
index 00000000000..0aea493f214
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init18.C
@@ -0,0 +1,9 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct A { int a, b, c; };
+struct S { A a; };
+constexpr S s{ A(1, 2, 3) };
+static_assert (s.a.a == 1 && s.a.b == 2 && s.a.c == 3);
+constexpr S s2 = { A(1, 2, 3) };
+static_assert (s2.a.a == 1 && s2.a.b == 2 && s2.a.c == 3);
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init2.C gcc/testsuite/g++.dg/cpp2a/paren-init2.C
new file mode 100644
index 00000000000..e9e90d7acb6
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init2.C
@@ -0,0 +1,56 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+struct A {
+  int i = 0;
+  int j = 0;
+};
+
+struct B {
+  A a;
+  constexpr B() : a(1.1, 2) { }
+};
+
+struct C {
+  int i;
+};
+
+struct E {
+  C c;
+  E() : c(1.2) { }
+};
+
+struct F {
+  char a[4];
+};
+
+struct G {
+  F f;
+  G() : f("yew") { }
+};
+
+struct H {
+  int i;
+  int &&r;
+};
+
+int f() { return 42; }
+
+struct I {
+  H h;
+  I() : h(1, f()) { }
+};
+
+I i;  // dangling ref to f():
+      // {.i=1, .r=(int &) &TARGET_EXPR <D.2118, f ()>}
+
+int
+main ()
+{
+  B b;
+  if (b.a.i != 1 || b.a.j != 2)
+    __builtin_abort ();
+  E e;
+  if (e.c.i != 1)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init3.C gcc/testsuite/g++.dg/cpp2a/paren-init3.C
new file mode 100644
index 00000000000..f444005a09f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init3.C
@@ -0,0 +1,11 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+class a {
+  int b{};
+};
+class c {
+  c();
+  a d;
+};
+c::c() {}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init4.C gcc/testsuite/g++.dg/cpp2a/paren-init4.C
new file mode 100644
index 00000000000..f8c7bd10b63
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init4.C
@@ -0,0 +1,142 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+// Test T[]().
+
+int i;
+int a1[](1, 2, 3);
+static_assert(sizeof(a1) == 3 * sizeof (int), "");
+int a2[](1.0, 2, 3);
+static_assert(sizeof(a2) == 3 * sizeof (int), "");
+int a3[3](1, 2, 3);
+int a4[3](1, 2); // a4[2] is value-initialized.
+int a5[](++i, ++i);
+static_assert(sizeof(a5) == 2 * sizeof (int), "");
+int a6[](1);
+static_assert(sizeof(a6) == sizeof (int), "");
+int a7[]({});
+static_assert(sizeof(a7) == sizeof (int), "");
+int a8[]({}, {}, {}, {}, {}, {});
+static_assert(sizeof(a8) == 6 * sizeof (int), "");
+int a9[]((1));
+static_assert(sizeof(a9) == sizeof (int), "");
+int a10[]((1), (2), (3));
+static_assert(sizeof(a10) == 3 * sizeof (int), "");
+int a11[][2]{1};
+static_assert(sizeof(a11) == 2 * sizeof (int), "");
+int a12[][2]({1, 2}, {3, 4});
+static_assert(sizeof(a12) == 4 * sizeof (int), "");
+
+const int (&ra1)[](1, 2);
+const int (&ra2)[](1.0, 2);
+const int (&ra3)[2](1.0, 2);
+int (&&rra1)[](1, 2);
+int (&&rra2)[](1.0, 2);
+int (&&rra3)[2](1.0, 2);
+
+struct S { int i; } s;
+S s1[]({1});
+static_assert(sizeof(s1) == sizeof (S), "");
+S s2[]({1}, {2});
+static_assert(sizeof(s2) == 2 * sizeof (S), "");
+S s3[]({1}, {2}, {3});
+static_assert(sizeof(s3) == 3 * sizeof (S), "");
+S s4[3]({1}, {2});
+static_assert(sizeof(s4) == 3 * sizeof (S), "");
+S s5[]({++i}, {++i});
+static_assert(sizeof(s2) == 2 * sizeof (S), "");
+S s6[](s, s);
+
+struct R { int i; int j; };
+R r1[]({1, 2});
+static_assert(sizeof(r1) == sizeof (R), "");
+R r2[]({1, 2}, {3, 4});
+static_assert(sizeof(r2) == 2 * sizeof (R), "");
+R r3[]({1.0, 2}, {3.2, 4});
+static_assert(sizeof(r3) == 2 * sizeof (R), "");
+
+char c1[]('r');
+char c2[]('r', 'a', 'c', 'c', 'o', 'o', 'n');
+char c3[]("yarrow");
+char c4[4]("oak");
+char c5[10]("cat");
+const char (&c6)[4]("eel");
+
+int g;
+struct X {
+  int i;
+  X() { ++g; }
+  X(int) { };
+};
+
+int
+main ()
+{
+  // Here we'll value-initialize l[1] and l[2], which will zero-initialize it.
+  int l[3](42);
+  if (l[0] != 42 || l[1] != 0 || l[2] != 0)
+    __builtin_abort ();
+
+  // Here we'll value-initialize x[2] and x[3].  Since X is a class type
+  // with a user-provided ctor, we'll default-initialize in both cases.
+  X x[4]({ 1 }, { 2 });
+  if (g != 2)
+    __builtin_abort ();
+
+  if (a1[0] != 1 || a1[1] != 2 || a1[2] != 3)
+    __builtin_abort ();
+  if (a2[0] != 1 || a2[1] != 2 || a2[2] != 3)
+    __builtin_abort ();
+  if (a3[0] != 1 || a3[1] != 2 || a3[2] != 3)
+    __builtin_abort ();
+  if (a4[0] != 1 || a4[1] != 2 || a4[2] != 0)
+    __builtin_abort ();
+  if (a5[0] != 1 || a5[1] != 2)
+    __builtin_abort ();
+  if (a6[0] != 1)
+    __builtin_abort ();
+  if (a7[0] != 0)
+    __builtin_abort ();
+  if (a8[0] != 0 || a8[1] != 0 || a8[2] != 0 || a8[3] != 0
+      || a8[4] != 0 || a8[5] != 0)
+    __builtin_abort ();
+  if (a9[0] != 1)
+    __builtin_abort ();
+  if (a10[0] != 1 || a10[1] != 2 || a10[2] != 3)
+    __builtin_abort ();
+  if (a11[0][0] != 1 || a11[0][1] != 0)
+    __builtin_abort ();
+  if (a12[0][0] != 1 || a12[0][1] != 2 || a12[1][0] != 3 || a12[1][1] != 4)
+    __builtin_abort ();
+
+  if (ra1[0] != 1 || ra1[1] != 2)
+    __builtin_abort ();
+  if (ra2[0] != 1 || ra2[1] != 2)
+    __builtin_abort ();
+  if (ra3[0] != 1 || ra3[1] != 2)
+    __builtin_abort ();
+  if (rra1[0] != 1 || rra1[1] != 2)
+    __builtin_abort ();
+  if (rra2[0] != 1 || rra2[1] != 2)
+    __builtin_abort ();
+  if (rra3[0] != 1 || rra3[1] != 2)
+    __builtin_abort ();
+
+  if (s1[0].i != 1)
+    __builtin_abort ();
+  if (s2[0].i != 1 || s2[1].i != 2)
+    __builtin_abort ();
+  if (s3[0].i != 1 || s3[1].i != 2 || s3[2].i != 3)
+    __builtin_abort ();
+  if (s4[0].i != 1 || s4[1].i != 2 || s4[2].i != 0)
+    __builtin_abort ();
+  if (s5[0].i != 3 || s5[1].i != 4)
+    __builtin_abort ();
+
+  if (r1[0].i != 1 || r1[0].j != 2)
+    __builtin_abort ();
+  if (r2[0].i != 1 || r2[0].j != 2 || r2[1].i != 3 || r2[1].j != 4)
+    __builtin_abort ();
+  if (r3[0].i != 1 || r3[0].j != 2 || r3[1].i != 3 || r3[1].j != 4)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init5.C gcc/testsuite/g++.dg/cpp2a/paren-init5.C
new file mode 100644
index 00000000000..a64cb00ebbc
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init5.C
@@ -0,0 +1,25 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+union U {
+  int a;
+  float b;
+};
+
+// u1 has no active member
+U u1;
+// u2 zero-initializes the first member, so u2.a is the active member and
+// its value is 0.
+U u2 = U();
+// u3 uses non-list aggregate initialization, so u3.a is the active member
+// and its value is 1.
+U u3 = U(1);
+
+int
+main ()
+{
+  if (u2.a != 0)
+    __builtin_abort ();
+  if (u3.a != 1)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init6.C gcc/testsuite/g++.dg/cpp2a/paren-init6.C
new file mode 100644
index 00000000000..b5d97dcb3d8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init6.C
@@ -0,0 +1,14 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test that we don't perform lifetime extension for () init.
+
+struct A {
+  int a;
+  int&& r;
+};
+
+int f();
+A a(1, f());
+
+// { dg-final { scan-assembler-not "_ZGR1a" } }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init7.C gcc/testsuite/g++.dg/cpp2a/paren-init7.C
new file mode 100644
index 00000000000..32af1a7265c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init7.C
@@ -0,0 +1,20 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+int h;
+struct i {
+  i() {}
+  explicit i(i &) {}
+  template <typename j> i(j &) { h++; }
+};
+
+int main() {
+  {
+    i a[6];
+    auto [b, c, d, e, f, g] = a;
+  }
+  i a[6];
+  auto [b, c, d, e, f, g](a);
+  if (h != 6)
+    __builtin_abort();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init8.C gcc/testsuite/g++.dg/cpp2a/paren-init8.C
new file mode 100644
index 00000000000..30e71650bc9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init8.C
@@ -0,0 +1,13 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test that we don't accept designated inits in ( ).
+
+struct S {
+  int i;
+  int j = 42;
+};
+
+S s(.i = 12); // { dg-error "expected" }
+
+int a[]([0] = 42); // { dg-error "" }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init9.C gcc/testsuite/g++.dg/cpp2a/paren-init9.C
new file mode 100644
index 00000000000..c44b206feb8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init9.C
@@ -0,0 +1,10 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct B { };
+struct A : B {
+  int i;
+};
+
+B b;
+A a(b);
diff --git gcc/testsuite/g++.dg/ext/desig10.C gcc/testsuite/g++.dg/ext/desig10.C
index 24057dd0ad6..c8d672b72d9 100644
--- gcc/testsuite/g++.dg/ext/desig10.C
+++ gcc/testsuite/g++.dg/ext/desig10.C
@@ -1,4 +1,4 @@
 // PR c++/84972
 // { dg-additional-options "-w" }
 
-char(a[])({.a = 0});  // { dg-error "designated initializer" }
+char(a[])({.a = 0});  // { dg-error "designated initializer|cannot convert" }
diff --git gcc/testsuite/g++.dg/template/crash107.C gcc/testsuite/g++.dg/template/crash107.C
index 3b0b4e8211f..9d8d394d93d 100644
--- gcc/testsuite/g++.dg/template/crash107.C
+++ gcc/testsuite/g++.dg/template/crash107.C
@@ -3,7 +3,7 @@
 // { dg-options "" }
 // { dg-additional-options "-Wno-return-type" }
 
-template<typename FP_> struct Vec { // { dg-message "note" }
+template<typename FP_> struct Vec { // { dg-message "note" "" { target c++17_down } }
     Vec& operator^=(Vec& rhs)     {
         union {
             struct {FP_ x,y,z;}; // { dg-error "20:anonymous struct" }
@@ -14,6 +14,6 @@ template<typename FP_> struct Vec { // { dg-message "note" }
         return Vec(*this)^=rhs; // { dg-message "required" }
     }
 };
-Vec<double> v(3,4,12); // { dg-error "no matching" }
-Vec<double> V(12,4,3);  // { dg-error "no matching" }
+Vec<double> v(3,4,12); // { dg-error "no matching|too many initializers" }
+Vec<double> V(12,4,3);  // { dg-error "no matching|too many initializers" }
 Vec<double> c = v^V;   // { dg-message "required" }
diff --git gcc/testsuite/g++.dg/template/crash95.C gcc/testsuite/g++.dg/template/crash95.C
index f60e635ae66..47346111328 100644
--- gcc/testsuite/g++.dg/template/crash95.C
+++ gcc/testsuite/g++.dg/template/crash95.C
@@ -8,4 +8,4 @@ template < typename > struct S
   };
 };
 
-S < int > s(0); // { dg-error "no matching" }
+S < int > s(0); // { dg-error "no matching|too many initializers" }
diff --git gcc/testsuite/g++.old-deja/g++.jason/crash3.C gcc/testsuite/g++.old-deja/g++.jason/crash3.C
index e94cc7c9781..3c4a1f84969 100644
--- gcc/testsuite/g++.old-deja/g++.jason/crash3.C
+++ gcc/testsuite/g++.old-deja/g++.jason/crash3.C
@@ -2,12 +2,12 @@
 // Bug: g++ tries to generate initialization semantics for a Node from an int,
 // and fails.
 
-struct Node			// { dg-message "note" }
+struct Node			// { dg-message "note" "" { target c++17_down } }
 {
   Node* child[2];
 };
 
 void bug(int i)
 {
-  Node* q = new Node(i);	// { dg-error "no matching" } 
+  Node* q = new Node(i);	// { dg-error "no matching" "" { target c++17_down }  } 
 }
diff --git gcc/testsuite/g++.old-deja/g++.law/ctors11.C gcc/testsuite/g++.old-deja/g++.law/ctors11.C
index 39ee76b0ae7..b29b18a62a3 100644
--- gcc/testsuite/g++.old-deja/g++.law/ctors11.C
+++ gcc/testsuite/g++.old-deja/g++.law/ctors11.C
@@ -10,12 +10,12 @@ public:
   inline A(int x){printf("constructing A with %d\n", x);}
 };
 
-class B:public A{ // { dg-message "note" } non-default constructor
+class B:public A{ // { dg-message "note" "" { target c++17_down } } non-default constructor
 private:
 public:
 };
 
 int main()
 {
-  B(10);// { dg-error "match" } B doesn't have a constructor taking int
+  B(10);// { dg-error "match" "" { target c++17_down } } B doesn't have a constructor taking int
 }
diff --git gcc/testsuite/g++.old-deja/g++.law/ctors9.C gcc/testsuite/g++.old-deja/g++.law/ctors9.C
index 43ba1262c95..5856d5dc883 100644
--- gcc/testsuite/g++.old-deja/g++.law/ctors9.C
+++ gcc/testsuite/g++.old-deja/g++.law/ctors9.C
@@ -20,7 +20,7 @@ Foo::Foo(int aa)
 { }
 
 
-struct var_Foo: public Foo // { dg-message "note" }  base.*// ERROR -  in class.*
+struct var_Foo: public Foo // { dg-message "note" "" { target c++17_down } }  base.*// ERROR -  in class.*
 {
   var_Foo* operator-> () {return this;}
 };
@@ -32,7 +32,7 @@ int blort(Foo& f)
 
 int main()
 {
-  var_Foo b(2);// { dg-error "match" } 
+  var_Foo b(2);// { dg-error "match" "" { target c++17_down } } 
   b->a = 0;
   int x = blort(b);
   return x;
diff --git gcc/testsuite/g++.old-deja/g++.mike/net22.C gcc/testsuite/g++.old-deja/g++.mike/net22.C
index e5e1cb1081d..abbf6191023 100644
--- gcc/testsuite/g++.old-deja/g++.mike/net22.C
+++ gcc/testsuite/g++.old-deja/g++.mike/net22.C
@@ -5,10 +5,11 @@ public:
   Parent( char *s ) {}
 };
 
-class Child : public Parent {		// { dg-message "note" } called
+class Child : public Parent {		// { dg-message "note" "" { target c++17_down } } called
 };
 
 int main() {
-  Child c( "String initializer" );	// { dg-error "match" } bad
+  Child c( "String initializer" );	// { dg-error "match" "" { target c++17_down } } bad
+// { dg-error "forbids converting a string constant" "" { target c++2a } .-1 }
   return 0;
 }
diff --git gcc/testsuite/g++.old-deja/g++.niklas/t128.C gcc/testsuite/g++.old-deja/g++.niklas/t128.C
index 19e3ca1dab0..0b3346c4ef3 100644
--- gcc/testsuite/g++.old-deja/g++.niklas/t128.C
+++ gcc/testsuite/g++.old-deja/g++.niklas/t128.C
@@ -1,5 +1,5 @@
 // { dg-do assemble  }
 // GROUPS niklas uncaught default-construct
 struct A { A (int); };
-struct B : A {}; // { dg-message "note" } without ctor // ERROR - candidates
-void f () { B (0); }// { dg-error "match" } .*
+struct B : A {}; // { dg-message "note" "" { target c++17_down } } without ctor // ERROR - candidates
+void f () { B (0); }// { dg-error "match" "" { target c++17_down } } .*
Jason Merrill Nov. 28, 2019, 6:01 a.m. | #11
On 11/27/19 5:25 PM, Marek Polacek wrote:
> On Wed, Nov 27, 2019 at 04:54:55PM -0500, Jason Merrill wrote:

>>>>> @@ -921,8 +921,20 @@ perform_member_init (tree member, tree init)

>>>>>     		inform (DECL_SOURCE_LOCATION (member),

>>>>>     			"%q#D should be initialized", member );

>>>>>     	    }

>>>>> -	  finish_expr_stmt (build_aggr_init (decl, init, flags,

>>>>> -					     tf_warning_or_error));

>>>>> +	  init = build_aggr_init (decl, init, flags, tf_warning_or_error);

>>>>> +	  /* In C++20, a member initializer list can be initializing an

>>>>> +	     aggregate from a parenthesized list of values:

>>>>> +

>>>>> +	       struct S {

>>>>> +		 A aggr;

>>>>> +		 S() : aggr(1, 2, 3) { }

>>>>> +	       };

>>>>> +

>>>>> +	      In such case, build_aggr_init will build up an INIT_EXPR like

>>>>> +	      we do for aggr{1, 2, 3}, so that build_data_member_initialization

>>>>> +	      can grok it.  */

>>>>> +	  if (TREE_CODE (init) != INIT_EXPR)

>>>>> +	    finish_expr_stmt (init);

>>>>

>>>> Why don't we want to finish_expr_stmt an INIT_EXPR?

>>>

>>> Because expand_default_init has already finish_expr_stmt-ed it.  Adding it

>>> twice sounds wrong.

>>

>> But the finish_expr_stmt in expand_default_init is inside the STATEMENT_LIST

>> we pushed into in build_aggr_init.  If finish_init_stmts returned the

>> INIT_EXPR, we still need to add it to the enclosing STATEMENT_LIST.

> 

> I don't understand why none of my tests broke


This seems to be because build_aggr_init returns an EXPR_STMT, so the 
test didn't affect anything.

> but here's a patch with that if removed:

> 

> dg.exp passed, bootstrap/regtest running.

> 

> 2019-11-27  Marek Polacek  <polacek@redhat.com>

> 

> 	PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> 	* c-cppbuiltin.c (c_cpp_builtins): Predefine

> 	__cpp_aggregate_paren_init=201902 for -std=c++2a.

> 

> 	* call.c (build_new_method_call_1): Handle parenthesized initialization

> 	of aggregates by building up a CONSTRUCTOR.

> 	(extend_ref_init_temps): Do nothing for CONSTRUCTOR_IS_PAREN_INIT.

> 	* cp-tree.h (CONSTRUCTOR_IS_PAREN_INIT, LOOKUP_AGGREGATE_PAREN_INIT):

> 	Define.

> 	* decl.c (grok_reference_init): Handle aggregate initialization from

> 	a parenthesized list of values.

> 	(reshape_init): Do nothing for CONSTRUCTOR_IS_PAREN_INIT.

> 	(check_initializer): Handle initialization of an array from a

> 	parenthesized list of values.  Use NULL_TREE instead of NULL.

> 	* init.c (expand_default_init): Set LOOKUP_AGGREGATE_PAREN_INIT for the

> 	build_special_member_call call.  If it returns a

> 	BRACE_ENCLOSED_INITIALIZER_P, call digest_init and build an INIT_EXPR.

> 	Set TREE_SIDE_EFFECTS on it.

> 	* tree.c (build_cplus_new): Handle BRACE_ENCLOSED_INITIALIZER_P.

> 	* typeck2.c (digest_init_r): Set LOOKUP_AGGREGATE_PAREN_INIT if it

> 	receives a CONSTRUCTOR with CONSTRUCTOR_IS_PAREN_INIT set.  Allow

> 	narrowing when LOOKUP_AGGREGATE_PAREN_INIT.

> 	(massage_init_elt): Don't lose LOOKUP_AGGREGATE_PAREN_INIT when passing

> 	flags to digest_init_r.

> 

> 	* g++.dg/cpp0x/constexpr-99.C: Only expect an error in C++17 and

> 	lesser.

> 	* g++.dg/cpp0x/explicit7.C: Likewise.

> 	* g++.dg/cpp0x/initlist12.C: Adjust dg-error.

> 	* g++.dg/cpp0x/pr31437.C: Likewise.

> 	* g++.dg/cpp2a/feat-cxx2a.C: Add __cpp_aggregate_paren_init test.

> 	* g++.dg/cpp2a/paren-init1.C: New test.

> 	* g++.dg/cpp2a/paren-init10.C: New test.

> 	* g++.dg/cpp2a/paren-init11.C: New test.

> 	* g++.dg/cpp2a/paren-init12.C: New test.

> 	* g++.dg/cpp2a/paren-init13.C: New test.

> 	* g++.dg/cpp2a/paren-init14.C: New test.

> 	* g++.dg/cpp2a/paren-init15.C: New test.

> 	* g++.dg/cpp2a/paren-init16.C: New test.

> 	* g++.dg/cpp2a/paren-init17.C: New test.

> 	* g++.dg/cpp2a/paren-init18.C: New test.

> 	* g++.dg/cpp2a/paren-init2.C: New test.

> 	* g++.dg/cpp2a/paren-init3.C: New test.

> 	* g++.dg/cpp2a/paren-init4.C: New test.

> 	* g++.dg/cpp2a/paren-init5.C: New test.

> 	* g++.dg/cpp2a/paren-init6.C: New test.

> 	* g++.dg/cpp2a/paren-init7.C: New test.

> 	* g++.dg/cpp2a/paren-init8.C: New test.

> 	* g++.dg/cpp2a/paren-init9.C: New test.

> 	* g++.dg/ext/desig10.C: Adjust dg-error.

> 	* g++.dg/template/crash107.C: Likewise.

> 	* g++.dg/template/crash95.C: Likewise.

> 	* g++.old-deja/g++.jason/crash3.C: Only expect an error in C++17 and

> 	lesser.

> 	* g++.old-deja/g++.law/ctors11.C: Likewise.

> 	* g++.old-deja/g++.law/ctors9.C: Likewise.

> 	* g++.old-deja/g++.mike/net22.C: Likewise.

> 	* g++.old-deja/g++.niklas/t128.C: Likewise.

> 

> diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c

> index 50066e4dd8b..2f30b2a3235 100644

> --- gcc/c-family/c-cppbuiltin.c

> +++ gcc/c-family/c-cppbuiltin.c

> @@ -1004,6 +1004,7 @@ c_cpp_builtins (cpp_reader *pfile)

>   	  cpp_define (pfile, "__cpp_impl_destroying_delete=201806L");

>   	  cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907L");

>   	  cpp_define (pfile, "__cpp_impl_three_way_comparison=201907L");

> +	  cpp_define (pfile, "__cpp_aggregate_paren_init=201902L");

>   	}

>         if (flag_concepts)

>           {

> diff --git gcc/cp/call.c gcc/cp/call.c

> index 8bfe3368816..1d14409ec43 100644

> --- gcc/cp/call.c

> +++ gcc/cp/call.c

> @@ -10111,6 +10111,30 @@ build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args,

>   

>     if (!any_viable_p)

>       {

> +      /* [dcl.init], 17.6.2.2:

> +

> +	 Otherwise, if no constructor is viable, the destination type is

> +	 a (possibly cv-qualified) aggregate class A, and the initializer

> +	 is a parenthesized expression-list, the object is initialized as

> +	 follows...

> +

> +	 We achieve this by building up a CONSTRUCTOR, as for list-init,

> +	 and setting CONSTRUCTOR_IS_PAREN_INIT to distinguish between

> +	 the two.  */

> +      if (DECL_CONSTRUCTOR_P (fn)

> +	  && !(flags & LOOKUP_ONLYCONVERTING)

> +	  && !cp_unevaluated_operand

> +	  && cxx_dialect >= cxx2a

> +	  && CP_AGGREGATE_TYPE_P (basetype)

> +	  && !user_args->is_empty ())

> +	{

> +	  /* Create a CONSTRUCTOR from ARGS, e.g. {1, 2} from <1, 2>.  */

> +	  tree list = build_tree_list_vec (user_args);

> +	  tree ctor = build_constructor_from_list (init_list_type_node, list);

> +	  CONSTRUCTOR_IS_DIRECT_INIT (ctor) = true;

> +	  CONSTRUCTOR_IS_PAREN_INIT (ctor) = true;

> +	  return ctor;

> +	}


This still bothers me for the case where instance is not a dummy; 
returning the CONSTRUCTOR forgets all about instance and we have to rely 
on the caller reintroducing it.

I think for that case we want to build an INIT_EXPR here, like the T{} 
handling just above.

>         if (complain & tf_error)

>   	complain_about_no_candidates_for_method_call (instance, candidates,

>   						      explicit_targs, basetype,

> @@ -11792,9 +11816,16 @@ perform_direct_initialization_if_possible (tree type,

>        If the destination type is a (possibly cv-qualified) class type:

>   

>        -- If the initialization is direct-initialization ...,

> -     constructors are considered. ... If no constructor applies, or

> -     the overload resolution is ambiguous, the initialization is

> -     ill-formed.  */

> +     constructors are considered.

> +

> +       -- If overload resolution is successful, the selected constructor

> +       is called to initialize the object, with the initializer expression

> +       or expression-list as its argument(s).

> +

> +       -- Otherwise, if no constructor is viable, the destination type is

> +       a (possibly cv-qualified) aggregate class A, and the initializer is

> +       a parenthesized expression-list, the object is initialized as

> +       follows...  */

>     if (CLASS_TYPE_P (type))

>       {

>         releasing_vec args (make_tree_vector_single (expr));

> @@ -12150,6 +12181,12 @@ extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups)

>   	ctor = TARGET_EXPR_INITIAL (ctor);

>         if (TREE_CODE (ctor) == CONSTRUCTOR)

>   	{

> +	  /* [dcl.init] When initializing an aggregate from a parenthesized list

> +	     of values... a temporary object bound to a reference does not have

> +	     its lifetime extended.  */

> +	  if (CONSTRUCTOR_IS_PAREN_INIT (ctor))

> +	    return init;

> +

>   	  if (is_std_init_list (type))

>   	    {

>   	      /* The temporary array underlying a std::initializer_list

> diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h

> index 4b4bc245d81..4aa871a21c0 100644

> --- gcc/cp/cp-tree.h

> +++ gcc/cp/cp-tree.h

> @@ -4325,6 +4325,11 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)

>   #define CONSTRUCTOR_IS_DESIGNATED_INIT(NODE) \

>     (TREE_LANG_FLAG_6 (CONSTRUCTOR_CHECK (NODE)))

>   

> +/* True if this CONSTRUCTOR comes from a parenthesized list of values, e.g.

> +   A(1, 2, 3).  */

> +#define CONSTRUCTOR_IS_PAREN_INIT(NODE) \

> +  (CONSTRUCTOR_CHECK(NODE)->base.private_flag)

> +

>   /* True if NODE represents a conversion for direct-initialization in a

>      template.  Set by perform_implicit_conversion_flags.  */

>   #define IMPLICIT_CONV_EXPR_DIRECT_INIT(NODE) \

> @@ -5583,6 +5588,8 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };

>      args), then we swap the conversions back in build_new_op_1 (so they

>      correspond to the order of the args in the candidate).  */

>   #define LOOKUP_REVERSED (LOOKUP_REWRITTEN << 1)

> +/* We're initializing an aggregate from a parenthesized list of values.  */

> +#define LOOKUP_AGGREGATE_PAREN_INIT (LOOKUP_REVERSED << 1)

>   

>   #define LOOKUP_NAMESPACES_ONLY(F)  \

>     (((F) & LOOKUP_PREFER_NAMESPACES) && !((F) & LOOKUP_PREFER_TYPES))

> diff --git gcc/cp/decl.c gcc/cp/decl.c

> index 26120720f07..33cd474f834 100644

> --- gcc/cp/decl.c

> +++ gcc/cp/decl.c

> @@ -5531,11 +5531,28 @@ grok_reference_init (tree decl, tree type, tree init, int flags)

>         return NULL_TREE;

>       }

>   

> +  tree ttype = TREE_TYPE (type);

>     if (TREE_CODE (init) == TREE_LIST)

> -    init = build_x_compound_expr_from_list (init, ELK_INIT,

> -					    tf_warning_or_error);

> +    {

> +      /* This handles (C++20 only) code like

> +

> +	   const A& r(1, 2, 3);

> +

> +	 where we treat the parenthesized list as a CONSTRUCTOR.  */

> +      if (TREE_TYPE (init) == NULL_TREE

> +	  && CP_AGGREGATE_TYPE_P (ttype)

> +	  && !DECL_DECOMPOSITION_P (decl)

> +	  && (cxx_dialect >= cxx2a))

> +	{

> +	  init = build_constructor_from_list (init_list_type_node, init);

> +	  CONSTRUCTOR_IS_DIRECT_INIT (init) = true;

> +	  CONSTRUCTOR_IS_PAREN_INIT (init) = true;

> +	}

> +      else

> +	init = build_x_compound_expr_from_list (init, ELK_INIT,

> +						tf_warning_or_error);

> +    }

>   

> -  tree ttype = TREE_TYPE (type);

>     if (TREE_CODE (ttype) != ARRAY_TYPE

>         && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE)

>       /* Note: default conversion is only called in very special cases.  */

> @@ -6437,6 +6454,11 @@ reshape_init (tree type, tree init, tsubst_flags_t complain)

>     if (vec_safe_is_empty (v))

>       return init;

>   

> +  /* Brace elision is not performed for a CONSTRUCTOR representing

> +     parenthesized aggregate initialization.  */

> +  if (CONSTRUCTOR_IS_PAREN_INIT (init))

> +    return init;

> +

>     /* Handle [dcl.init.list] direct-list-initialization from

>        single element of enumeration with a fixed underlying type.  */

>     if (is_direct_enum_init (type, init))

> @@ -6640,6 +6662,41 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)

>   	      flags |= LOOKUP_NO_NARROWING;

>   	    }

>   	}

> +      /* [dcl.init] "Otherwise, if the destination type is an array, the object

> +	 is initialized as follows..."  So handle things like

> +

> +	  int a[](1, 2, 3);

> +

> +	 which is permitted in C++20 by P0960.  */

> +      else if (TREE_CODE (init) == TREE_LIST

> +	       && TREE_TYPE (init) == NULL_TREE

> +	       && TREE_CODE (type) == ARRAY_TYPE

> +	       && !DECL_DECOMPOSITION_P (decl)

> +	       && (cxx_dialect >= cxx2a))

> +	{

> +	  /* [dcl.init.string] "An array of ordinary character type [...]

> +	     can be initialized by an ordinary string literal [...] by an

> +	     appropriately-typed string literal enclosed in braces" only

> +	     talks about braces, but GCC has always accepted

> +

> +	       char a[]("foobar");

> +

> +	     so we continue to do so.  */

> +	  tree val = TREE_VALUE (init);

> +	  if (TREE_CHAIN (init) == NULL_TREE

> +	      && char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type)))

> +	      && TREE_CODE (tree_strip_any_location_wrapper (val))

> +		 == STRING_CST)

> +	    /* If the list has a single element and it's a string literal,

> +	       then it's the initializer for the array as a whole.  */

> +	    init = val;

> +	  else

> +	    {

> +	      init = build_constructor_from_list (init_list_type_node, init);

> +	      CONSTRUCTOR_IS_DIRECT_INIT (init) = true;

> +	      CONSTRUCTOR_IS_PAREN_INIT (init) = true;

> +	    }

> +	}

>         else if (TREE_CODE (init) == TREE_LIST

>   	       && TREE_TYPE (init) != unknown_type_node

>   	       && !MAYBE_CLASS_TYPE_P (type))

> @@ -6683,6 +6740,9 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)

>   	    init_code = TREE_OPERAND (init_code, 0);

>   	  if (TREE_CODE (init_code) == INIT_EXPR)

>   	    {

> +	      /* In C++20, the call to build_aggr_init could have created

> +		 an INIT_EXPR with a CONSTRUCTOR as the RHS to handle

> +		 A(1, 2).  */

>   	      init = TREE_OPERAND (init_code, 1);

>   	      init_code = NULL_TREE;

>   	      /* Don't call digest_init; it's unnecessary and will complain

> @@ -6736,7 +6796,7 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)

>   			0, "array %qD initialized by parenthesized "

>   			"string literal %qE",

>   			decl, DECL_INITIAL (decl));

> -	  init = NULL;

> +	  init = NULL_TREE;

>   	}

>       }

>     else

> diff --git gcc/cp/init.c gcc/cp/init.c

> index aa48f80e58d..77bbea20705 100644

> --- gcc/cp/init.c

> +++ gcc/cp/init.c

> @@ -921,6 +921,17 @@ perform_member_init (tree member, tree init)

>   		inform (DECL_SOURCE_LOCATION (member),

>   			"%q#D should be initialized", member );

>   	    }

> +	  /* In C++20, a member initializer list can be initializing an

> +	     aggregate from a parenthesized list of values:

> +

> +	       struct S {

> +		 A aggr;

> +		 S() : aggr(1, 2, 3) { }

> +	       };

> +

> +	      In such case, build_aggr_init will build up an INIT_EXPR like

> +	      we do for aggr{1, 2, 3}, so that build_data_member_initialization

> +	      can grok it.  */


I think we can drop this comment, too.

>   	  finish_expr_stmt (build_aggr_init (decl, init, flags,

>   					     tf_warning_or_error));

>   	}

> @@ -1967,8 +1978,23 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,

>         tree ctor_name = (true_exp == exp

>   			? complete_ctor_identifier : base_ctor_identifier);

>   

> +      /* Given class A,

> +

> +	   A a(1, 2);

> +

> +	 can mean a call to a constructor A::A(int, int), if present.  If not,

> +	 but A is an aggregate, we will try aggregate initialization.  */

>         rval = build_special_member_call (exp, ctor_name, &parms, binfo, flags,

>   					complain);

> +      if (BRACE_ENCLOSED_INITIALIZER_P (rval))

> +	{

> +	  gcc_assert (cxx_dialect >= cxx2a);

> +	  rval = digest_init (type, rval, tf_warning_or_error);

> +	  rval = build2 (INIT_EXPR, type, exp, rval);

> +	  /* So that we do finish_expr_stmt below.  Don't return here, we

> +	     need to release PARMS.  */

> +	  TREE_SIDE_EFFECTS (rval) = 1;

> +	}

>       }

>   

>     if (parms != NULL)

> diff --git gcc/cp/tree.c gcc/cp/tree.c

> index d125d60b270..dede4dce36b 100644

> --- gcc/cp/tree.c

> +++ gcc/cp/tree.c

> @@ -669,6 +669,15 @@ build_aggr_init_expr (tree type, tree init)

>   tree

>   build_cplus_new (tree type, tree init, tsubst_flags_t complain)

>   {

> +  /* This function should cope with what build_special_member_call

> +     can produce.  When performing parenthesized aggregate initialization,

> +     it can produce a { }.  */

> +  if (BRACE_ENCLOSED_INITIALIZER_P (init))

> +    {

> +      gcc_assert (cxx_dialect >= cxx2a);

> +      return finish_compound_literal (type, init, complain);

> +    }

> +

>     tree rval = build_aggr_init_expr (type, init);

>     tree slot;

>   

> diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c

> index b8868546444..9c6f25c8608 100644

> --- gcc/cp/typeck2.c

> +++ gcc/cp/typeck2.c

> @@ -1117,6 +1117,10 @@ digest_init_r (tree type, tree init, int nested, int flags,

>   

>     tree stripped_init = init;

>   

> +  if (BRACE_ENCLOSED_INITIALIZER_P (init)

> +      && CONSTRUCTOR_IS_PAREN_INIT (init))

> +    flags |= LOOKUP_AGGREGATE_PAREN_INIT;

> +

>     /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue

>        (g++.old-deja/g++.law/casts2.C).  */

>     if (TREE_CODE (init) == NON_LVALUE_EXPR)

> @@ -1224,7 +1228,9 @@ digest_init_r (tree type, tree init, int nested, int flags,

>     if ((code != COMPLEX_TYPE || BRACE_ENCLOSED_INITIALIZER_P (stripped_init))

>         && (SCALAR_TYPE_P (type) || code == REFERENCE_TYPE))

>       {

> -      if (nested)

> +      /* Narrowing is OK when initializing an aggregate from

> +	 a parenthesized list.  */

> +      if (nested && !(flags & LOOKUP_AGGREGATE_PAREN_INIT))

>   	flags |= LOOKUP_NO_NARROWING;

>         init = convert_for_initialization (0, type, init, flags,

>   					 ICR_INIT, NULL_TREE, 0,

> @@ -1384,9 +1390,12 @@ static tree

>   massage_init_elt (tree type, tree init, int nested, int flags,

>   		  tsubst_flags_t complain)

>   {

> -  flags &= LOOKUP_ALLOW_FLEXARRAY_INIT;

> -  flags |= LOOKUP_IMPLICIT;

> -  init = digest_init_r (type, init, nested ? 2 : 1, flags, complain);

> +  int new_flags = LOOKUP_IMPLICIT;

> +  if (flags & LOOKUP_ALLOW_FLEXARRAY_INIT)

> +    new_flags |= LOOKUP_ALLOW_FLEXARRAY_INIT;

> +  if (flags & LOOKUP_AGGREGATE_PAREN_INIT)

> +    new_flags |= LOOKUP_AGGREGATE_PAREN_INIT;

> +  init = digest_init_r (type, init, nested ? 2 : 1, new_flags, complain);

>     /* Strip a simple TARGET_EXPR when we know this is an initializer.  */

>     if (SIMPLE_TARGET_EXPR_P (init))

>       init = TARGET_EXPR_INITIAL (init);

> diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-99.C gcc/testsuite/g++.dg/cpp0x/constexpr-99.C

> index 8d791dd032d..4d3953d0983 100644

> --- gcc/testsuite/g++.dg/cpp0x/constexpr-99.C

> +++ gcc/testsuite/g++.dg/cpp0x/constexpr-99.C

> @@ -9,5 +9,6 @@ struct A

>   struct B

>   {

>     A a;

> -    constexpr B() : a(0) {} // { dg-error "no matching function" }

> +  // P0960R3 allows paren-init.

> +  constexpr B() : a(0) {} // { dg-error "no matching function" "" { target c++17_down } }

>   };

> diff --git gcc/testsuite/g++.dg/cpp0x/explicit7.C gcc/testsuite/g++.dg/cpp0x/explicit7.C

> index 574796d117d..67b50542bc3 100644

> --- gcc/testsuite/g++.dg/cpp0x/explicit7.C

> +++ gcc/testsuite/g++.dg/cpp0x/explicit7.C

> @@ -10,7 +10,7 @@ struct A { };

>   struct B: A { };

>   struct C {

>     explicit operator B*();	// { dg-message "explicit" }

> -  explicit operator B&();	// { dg-message "explicit" }

> +  explicit operator B&();	// { dg-message "explicit" "" { target c++17_down } }

>   };

>   

>   C c;

> diff --git gcc/testsuite/g++.dg/cpp0x/initlist12.C gcc/testsuite/g++.dg/cpp0x/initlist12.C

> index f72a6dac525..c2a2b86520f 100644

> --- gcc/testsuite/g++.dg/cpp0x/initlist12.C

> +++ gcc/testsuite/g++.dg/cpp0x/initlist12.C

> @@ -6,15 +6,15 @@ struct A

>     int i;

>   };

>   

> -A a({1,2});			// { dg-error "no match" }

> +A a({1,2});			// { dg-error "no match|cannot convert" }

>   

>   union U

>   {

>     int i,j;

>   };

>   

> -U u({1,2});			// { dg-error "no match" }

> +U u({1,2});			// { dg-error "no match|cannot convert" }

>   

>   union V {};

>   

> -V v({1});			// { dg-error "no match" }

> +V v({1});			// { dg-error "no match|too many initializers" }

> diff --git gcc/testsuite/g++.dg/cpp0x/pr31437.C gcc/testsuite/g++.dg/cpp0x/pr31437.C

> index 7e2ed7a1375..532b533c8d3 100644

> --- gcc/testsuite/g++.dg/cpp0x/pr31437.C

> +++ gcc/testsuite/g++.dg/cpp0x/pr31437.C

> @@ -1,9 +1,9 @@

>   // { dg-do compile { target c++11 } }

> -template <typename... T> struct A // { dg-message "candidates|A" }

> +template <typename... T> struct A // { dg-message "candidates|A" "" { target c++17_down } }

>   {

>     A(T* p) {  // { dg-error "parameter packs|T" }

>      (A<T...>*)(p);

>     }

>   };

>   

> -A<int> a(0); // { dg-error "no matching" }

> +A<int> a(0); // { dg-error "no matching|too many initializers" }

> diff --git gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C

> index 753a6ecd0a8..bea30447f6b 100644

> --- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C

> +++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C

> @@ -460,6 +460,12 @@

>   #  error "__cpp_constexpr_dynamic_alloc != 201907"

>   #endif

>   

> +#ifndef __cpp_aggregate_paren_init

> +#  error "__cpp_aggregate_paren_init"

> +#elif __cpp_aggregate_paren_init != 201902

> +#  error "__cpp_aggregate_paren_init != 201902"

> +#endif

> +

>   #ifdef __has_cpp_attribute

>   

>   #  if ! __has_cpp_attribute(maybe_unused)

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init1.C gcc/testsuite/g++.dg/cpp2a/paren-init1.C

> new file mode 100644

> index 00000000000..a54b2ccaf6d

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init1.C

> @@ -0,0 +1,116 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do run { target c++2a } }

> +

> +struct A {

> +  int i;

> +  int j;

> +  int k;

> +};

> +

> +struct B {

> +  int i;

> +  int j;

> +  int k = 42;

> +};

> +

> +struct C {

> +  A a;

> +};

> +

> +struct D {

> +  A a1;

> +  A a2;

> +};

> +

> +struct E {

> +  int i;

> +};

> +

> +// F has a base class, but it's not virtual, private, or protected, so this is

> +// still an aggregate (since C++17).

> +struct F : E {

> +  int j;

> +  int k;

> +};

> +

> +F f({1}, 2, 3);

> +

> +// A non-virtual member function doesn't make it a non-aggregate.

> +struct G {

> +  int i;

> +  double j;

> +  int foo(int, int);

> +};

> +

> +G g(1, 2.14);

> +

> +class H {

> +public:

> +  H (int) { }

> +};

> +

> +class I : public H { };

> +

> +int i;

> +A a1(1, 2);

> +A a2(1.0, 2);

> +A a3(++i, ++i);

> +const A& ra(1, 2);

> +

> +A ca = A(1, 2);

> +A ca2 = A(1.0, 2);

> +A ca3 = A(++i, ++i);

> +const A& rca = A(1, 2);

> +

> +B b1(1, 2, 3);

> +B b2(1, 2);

> +B b3(1);

> +

> +C c1({5, 6, 7});

> +D d1({1, 2, 3}, {5, 6, 7});

> +

> +struct W {

> +  const char *s, *t;

> +};

> +W w1("fluffer", "nutter");

> +

> +struct Y {

> +  char a[4];

> +};

> +Y y("yew");

> +

> +int

> +main ()

> +{

> +  I(10);

> +

> +  // A::k will be value-initialized.

> +  if (a1.i != 1 || a1.j != 2 || a1.k != 0)

> +    __builtin_abort ();

> +  if (a2.i != 1 || a2.j != 2 || a2.k != 0)

> +    __builtin_abort ();

> +  if (a3.i != 1 || a3.j != 2 || a3.k != 0)

> +    __builtin_abort ();

> +  if (ra.i != 1 || ra.j != 2 || ra.k != 0)

> +    __builtin_abort ();

> +  if (ca.i != 1 || ca.j != 2 || ca.k != 0)

> +    __builtin_abort ();

> +  if (ca2.i != 1 || ca2.j != 2 || ca2.k != 0)

> +    __builtin_abort ();

> +  if (ca3.i != 3 || ca3.j != 4 || ca3.k != 0)

> +    __builtin_abort ();

> +

> +  if (b1.i != 1 || b1.j != 2 || b1.k != 3)

> +    __builtin_abort ();

> +  // The default member initializer will be used for B::k.

> +  if (b2.i != 1 || b2.j != 2 || b2.k != 42)

> +    __builtin_abort ();

> +  if (b3.i != 1 || b3.j != 0 || b3.k != 42)

> +    __builtin_abort ();

> +

> +  if (c1.a.i != 5 || c1.a.j != 6 || c1.a.k != 7)

> +    __builtin_abort ();

> +

> +  if (f.i != 1 || f.j != 2 || f.k != 3)

> +    __builtin_abort ();

> +}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init10.C gcc/testsuite/g++.dg/cpp2a/paren-init10.C

> new file mode 100644

> index 00000000000..5c70d9d59ee

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init10.C

> @@ -0,0 +1,18 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +// Test from [dcl.init].

> +

> +struct A {

> +  int a;

> +  int&& r;

> +};

> +

> +int f();

> +int n = 10;

> +

> +A a1{1, f()};               // OK, lifetime is extended

> +A a2(1, f());               // well-formed, but dangling reference

> +A a3{1.0, 1};               // { dg-error "narrowing conversion" }

> +A a4(1.0, 1);               // well-formed, but dangling reference

> +A a5(1.0, static_cast<int&&>(n));    // OK

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init11.C gcc/testsuite/g++.dg/cpp2a/paren-init11.C

> new file mode 100644

> index 00000000000..82ca2669545

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init11.C

> @@ -0,0 +1,88 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +// Test ill-formed code.

> +

> +// If k is greater than the size of the array, the program is ill-formed.

> +int a1[2](1, 2, 3); // { dg-error "too many initializers" }

> +int a2[](); // { dg-error "array of functions" }

> +int a3[](()); // { dg-error "expected primary-expression" }

> +int a4[]("raccoon"); // { dg-error "invalid conversion" }

> +

> +struct S {

> +  int i;

> +  int j;

> +};

> +

> +S s1(1, 1, 1); // { dg-error "too many initializers" }

> +

> +union U2 {

> +  int a;

> +  float b;

> +};

> +

> +// [dcl.init.aggr]/19:

> +// When a union is initialized with an initializer list, there shall not be

> +// more than one explicitly initialized element.

> +U2 u4 = U2(1, 2); // { dg-error "too many initializers" }

> +

> +// Test there is no brace elision.

> +

> +int a[2][2](1, 2, 3, 4); // { dg-error "too many initializers|array must be initialized with a brace-enclosed initializer" }

> +

> +// private/protected/virtual base class -> not an aggregate.

> +struct B { };

> +struct D : private B {

> +  int i;

> +  int j;

> +};

> +

> +D d({}, 1, 2); // { dg-error "no matching function" }

> +

> +// Private non-static data member -> not an aggregate.

> +struct P {

> +  int i;

> +private:

> +  int j;

> +};

> +

> +P p(1, 2); // { dg-error "no matching function" }

> +

> +// User-declared constructor -> not an aggregate.

> +struct U {

> +  U() {}

> +  int i;

> +  int j;

> +};

> +

> +U u(1, 2); // { dg-error "no matching function" }

> +

> +// virtual member function -> not an aggregate.

> +struct V {

> +  int i;

> +  int j;

> +  virtual int foo(int);

> +};

> +

> +V v(1, 2); // { dg-error "no matching function" }

> +

> +struct nonaggr {

> +  int i;

> +  int j;

> +private:

> +  int x;

> +};

> +

> +struct F {

> +  nonaggr n;

> +  F() : n(1) { } // { dg-error "no matching function" }

> +};

> +

> +struct G {

> +  char a[4];

> +};

> +

> +struct H {

> +  G g;

> +  H() : g("oaks") { } // { dg-error "initializer-string" }

> +};

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init12.C gcc/testsuite/g++.dg/cpp2a/paren-init12.C

> new file mode 100644

> index 00000000000..d7be6f2b55d

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init12.C

> @@ -0,0 +1,17 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +struct A;

> +

> +struct C {

> +  operator A();

> +};

> +

> +struct A {

> +  C c;

> +};

> +

> +C c;

> +A a(c);  // invokes C’s conversion function to A

> +

> +// { dg-final { scan-assembler "_ZN1Ccv1AEv" } }

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init13.C gcc/testsuite/g++.dg/cpp2a/paren-init13.C

> new file mode 100644

> index 00000000000..4b9107c70ff

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init13.C

> @@ -0,0 +1,16 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +struct X { int a, b; };

> +struct Y { X x; };

> +

> +void

> +f()

> +{

> +  // This is ok...

> +  Y y1{{1, 2}};

> +  Y y2({1, 2});

> +  // ...but Y y((1,2)) is not the same as Y y{{1,2}}.  (1, 2) is a

> +  // COMPOUND_EXPR.

> +  Y y3((1, 2)); // { dg-error "could not convert" }

> +}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init14.C gcc/testsuite/g++.dg/cpp2a/paren-init14.C

> new file mode 100644

> index 00000000000..837f9531260

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init14.C

> @@ -0,0 +1,10 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++11 } }

> +

> +struct A {

> +  int x;

> +  int y;

> +  A(int, int) = delete;

> +};

> +

> +A a(1, 2); // { dg-error "use of deleted function" }

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init15.C gcc/testsuite/g++.dg/cpp2a/paren-init15.C

> new file mode 100644

> index 00000000000..4311dd4df59

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init15.C

> @@ -0,0 +1,35 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +struct A {

> +  int i;

> +  int j;

> +};

> +

> +struct B : A

> +{

> +  B (): A(1.7, 2) { }

> +};

> +

> +void f(A);

> +

> +void

> +g ()

> +{

> +  f (A(1, 2));

> +}

> +

> +struct S {

> +  int a[2];

> +};

> +

> +S h() { return S({1, 2}); }

> +

> +struct Z {

> +  int i;

> +  int j;

> +  operator A();

> +};

> +

> +Z z;

> +A a = Z(1, 2.3);

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init16.C gcc/testsuite/g++.dg/cpp2a/paren-init16.C

> new file mode 100644

> index 00000000000..c8ed924877e

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init16.C

> @@ -0,0 +1,14 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +struct S { int a[2]; };

> +struct A { S s[1]; };

> +

> +template <typename, int N>

> +struct R { static constexpr auto h = A({S{N}}); };

> +

> +template <typename, int N>

> +struct R2 { static constexpr auto h = A({S({N, N})}); };

> +

> +A foo = R<int, 10>::h;

> +A foo2 = R2<int, 10>::h;

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init17.C gcc/testsuite/g++.dg/cpp2a/paren-init17.C

> new file mode 100644

> index 00000000000..8e08b5288a5

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init17.C

> @@ -0,0 +1,6 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++11 } }

> +

> +struct A { };

> +struct B { };

> +static_assert (!__is_trivially_constructible(A, B), "");

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init18.C gcc/testsuite/g++.dg/cpp2a/paren-init18.C

> new file mode 100644

> index 00000000000..0aea493f214

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init18.C

> @@ -0,0 +1,9 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +struct A { int a, b, c; };

> +struct S { A a; };

> +constexpr S s{ A(1, 2, 3) };

> +static_assert (s.a.a == 1 && s.a.b == 2 && s.a.c == 3);

> +constexpr S s2 = { A(1, 2, 3) };

> +static_assert (s2.a.a == 1 && s2.a.b == 2 && s2.a.c == 3);

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init2.C gcc/testsuite/g++.dg/cpp2a/paren-init2.C

> new file mode 100644

> index 00000000000..e9e90d7acb6

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init2.C

> @@ -0,0 +1,56 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do run { target c++2a } }

> +

> +struct A {

> +  int i = 0;

> +  int j = 0;

> +};

> +

> +struct B {

> +  A a;

> +  constexpr B() : a(1.1, 2) { }

> +};

> +

> +struct C {

> +  int i;

> +};

> +

> +struct E {

> +  C c;

> +  E() : c(1.2) { }

> +};

> +

> +struct F {

> +  char a[4];

> +};

> +

> +struct G {

> +  F f;

> +  G() : f("yew") { }

> +};

> +

> +struct H {

> +  int i;

> +  int &&r;

> +};

> +

> +int f() { return 42; }

> +

> +struct I {

> +  H h;

> +  I() : h(1, f()) { }

> +};

> +

> +I i;  // dangling ref to f():

> +      // {.i=1, .r=(int &) &TARGET_EXPR <D.2118, f ()>}

> +

> +int

> +main ()

> +{

> +  B b;

> +  if (b.a.i != 1 || b.a.j != 2)

> +    __builtin_abort ();

> +  E e;

> +  if (e.c.i != 1)

> +    __builtin_abort ();

> +}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init3.C gcc/testsuite/g++.dg/cpp2a/paren-init3.C

> new file mode 100644

> index 00000000000..f444005a09f

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init3.C

> @@ -0,0 +1,11 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +class a {

> +  int b{};

> +};

> +class c {

> +  c();

> +  a d;

> +};

> +c::c() {}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init4.C gcc/testsuite/g++.dg/cpp2a/paren-init4.C

> new file mode 100644

> index 00000000000..f8c7bd10b63

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init4.C

> @@ -0,0 +1,142 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do run { target c++2a } }

> +

> +// Test T[]().

> +

> +int i;

> +int a1[](1, 2, 3);

> +static_assert(sizeof(a1) == 3 * sizeof (int), "");

> +int a2[](1.0, 2, 3);

> +static_assert(sizeof(a2) == 3 * sizeof (int), "");

> +int a3[3](1, 2, 3);

> +int a4[3](1, 2); // a4[2] is value-initialized.

> +int a5[](++i, ++i);

> +static_assert(sizeof(a5) == 2 * sizeof (int), "");

> +int a6[](1);

> +static_assert(sizeof(a6) == sizeof (int), "");

> +int a7[]({});

> +static_assert(sizeof(a7) == sizeof (int), "");

> +int a8[]({}, {}, {}, {}, {}, {});

> +static_assert(sizeof(a8) == 6 * sizeof (int), "");

> +int a9[]((1));

> +static_assert(sizeof(a9) == sizeof (int), "");

> +int a10[]((1), (2), (3));

> +static_assert(sizeof(a10) == 3 * sizeof (int), "");

> +int a11[][2]{1};

> +static_assert(sizeof(a11) == 2 * sizeof (int), "");

> +int a12[][2]({1, 2}, {3, 4});

> +static_assert(sizeof(a12) == 4 * sizeof (int), "");

> +

> +const int (&ra1)[](1, 2);

> +const int (&ra2)[](1.0, 2);

> +const int (&ra3)[2](1.0, 2);

> +int (&&rra1)[](1, 2);

> +int (&&rra2)[](1.0, 2);

> +int (&&rra3)[2](1.0, 2);

> +

> +struct S { int i; } s;

> +S s1[]({1});

> +static_assert(sizeof(s1) == sizeof (S), "");

> +S s2[]({1}, {2});

> +static_assert(sizeof(s2) == 2 * sizeof (S), "");

> +S s3[]({1}, {2}, {3});

> +static_assert(sizeof(s3) == 3 * sizeof (S), "");

> +S s4[3]({1}, {2});

> +static_assert(sizeof(s4) == 3 * sizeof (S), "");

> +S s5[]({++i}, {++i});

> +static_assert(sizeof(s2) == 2 * sizeof (S), "");

> +S s6[](s, s);

> +

> +struct R { int i; int j; };

> +R r1[]({1, 2});

> +static_assert(sizeof(r1) == sizeof (R), "");

> +R r2[]({1, 2}, {3, 4});

> +static_assert(sizeof(r2) == 2 * sizeof (R), "");

> +R r3[]({1.0, 2}, {3.2, 4});

> +static_assert(sizeof(r3) == 2 * sizeof (R), "");

> +

> +char c1[]('r');

> +char c2[]('r', 'a', 'c', 'c', 'o', 'o', 'n');

> +char c3[]("yarrow");

> +char c4[4]("oak");

> +char c5[10]("cat");

> +const char (&c6)[4]("eel");

> +

> +int g;

> +struct X {

> +  int i;

> +  X() { ++g; }

> +  X(int) { };

> +};

> +

> +int

> +main ()

> +{

> +  // Here we'll value-initialize l[1] and l[2], which will zero-initialize it.

> +  int l[3](42);

> +  if (l[0] != 42 || l[1] != 0 || l[2] != 0)

> +    __builtin_abort ();

> +

> +  // Here we'll value-initialize x[2] and x[3].  Since X is a class type

> +  // with a user-provided ctor, we'll default-initialize in both cases.

> +  X x[4]({ 1 }, { 2 });

> +  if (g != 2)

> +    __builtin_abort ();

> +

> +  if (a1[0] != 1 || a1[1] != 2 || a1[2] != 3)

> +    __builtin_abort ();

> +  if (a2[0] != 1 || a2[1] != 2 || a2[2] != 3)

> +    __builtin_abort ();

> +  if (a3[0] != 1 || a3[1] != 2 || a3[2] != 3)

> +    __builtin_abort ();

> +  if (a4[0] != 1 || a4[1] != 2 || a4[2] != 0)

> +    __builtin_abort ();

> +  if (a5[0] != 1 || a5[1] != 2)

> +    __builtin_abort ();

> +  if (a6[0] != 1)

> +    __builtin_abort ();

> +  if (a7[0] != 0)

> +    __builtin_abort ();

> +  if (a8[0] != 0 || a8[1] != 0 || a8[2] != 0 || a8[3] != 0

> +      || a8[4] != 0 || a8[5] != 0)

> +    __builtin_abort ();

> +  if (a9[0] != 1)

> +    __builtin_abort ();

> +  if (a10[0] != 1 || a10[1] != 2 || a10[2] != 3)

> +    __builtin_abort ();

> +  if (a11[0][0] != 1 || a11[0][1] != 0)

> +    __builtin_abort ();

> +  if (a12[0][0] != 1 || a12[0][1] != 2 || a12[1][0] != 3 || a12[1][1] != 4)

> +    __builtin_abort ();

> +

> +  if (ra1[0] != 1 || ra1[1] != 2)

> +    __builtin_abort ();

> +  if (ra2[0] != 1 || ra2[1] != 2)

> +    __builtin_abort ();

> +  if (ra3[0] != 1 || ra3[1] != 2)

> +    __builtin_abort ();

> +  if (rra1[0] != 1 || rra1[1] != 2)

> +    __builtin_abort ();

> +  if (rra2[0] != 1 || rra2[1] != 2)

> +    __builtin_abort ();

> +  if (rra3[0] != 1 || rra3[1] != 2)

> +    __builtin_abort ();

> +

> +  if (s1[0].i != 1)

> +    __builtin_abort ();

> +  if (s2[0].i != 1 || s2[1].i != 2)

> +    __builtin_abort ();

> +  if (s3[0].i != 1 || s3[1].i != 2 || s3[2].i != 3)

> +    __builtin_abort ();

> +  if (s4[0].i != 1 || s4[1].i != 2 || s4[2].i != 0)

> +    __builtin_abort ();

> +  if (s5[0].i != 3 || s5[1].i != 4)

> +    __builtin_abort ();

> +

> +  if (r1[0].i != 1 || r1[0].j != 2)

> +    __builtin_abort ();

> +  if (r2[0].i != 1 || r2[0].j != 2 || r2[1].i != 3 || r2[1].j != 4)

> +    __builtin_abort ();

> +  if (r3[0].i != 1 || r3[0].j != 2 || r3[1].i != 3 || r3[1].j != 4)

> +    __builtin_abort ();

> +}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init5.C gcc/testsuite/g++.dg/cpp2a/paren-init5.C

> new file mode 100644

> index 00000000000..a64cb00ebbc

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init5.C

> @@ -0,0 +1,25 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do run { target c++2a } }

> +

> +union U {

> +  int a;

> +  float b;

> +};

> +

> +// u1 has no active member

> +U u1;

> +// u2 zero-initializes the first member, so u2.a is the active member and

> +// its value is 0.

> +U u2 = U();

> +// u3 uses non-list aggregate initialization, so u3.a is the active member

> +// and its value is 1.

> +U u3 = U(1);

> +

> +int

> +main ()

> +{

> +  if (u2.a != 0)

> +    __builtin_abort ();

> +  if (u3.a != 1)

> +    __builtin_abort ();

> +}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init6.C gcc/testsuite/g++.dg/cpp2a/paren-init6.C

> new file mode 100644

> index 00000000000..b5d97dcb3d8

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init6.C

> @@ -0,0 +1,14 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +// Test that we don't perform lifetime extension for () init.

> +

> +struct A {

> +  int a;

> +  int&& r;

> +};

> +

> +int f();

> +A a(1, f());

> +

> +// { dg-final { scan-assembler-not "_ZGR1a" } }

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init7.C gcc/testsuite/g++.dg/cpp2a/paren-init7.C

> new file mode 100644

> index 00000000000..32af1a7265c

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init7.C

> @@ -0,0 +1,20 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do run { target c++2a } }

> +

> +int h;

> +struct i {

> +  i() {}

> +  explicit i(i &) {}

> +  template <typename j> i(j &) { h++; }

> +};

> +

> +int main() {

> +  {

> +    i a[6];

> +    auto [b, c, d, e, f, g] = a;

> +  }

> +  i a[6];

> +  auto [b, c, d, e, f, g](a);

> +  if (h != 6)

> +    __builtin_abort();

> +}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init8.C gcc/testsuite/g++.dg/cpp2a/paren-init8.C

> new file mode 100644

> index 00000000000..30e71650bc9

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init8.C

> @@ -0,0 +1,13 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +// Test that we don't accept designated inits in ( ).

> +

> +struct S {

> +  int i;

> +  int j = 42;

> +};

> +

> +S s(.i = 12); // { dg-error "expected" }

> +

> +int a[]([0] = 42); // { dg-error "" }

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init9.C gcc/testsuite/g++.dg/cpp2a/paren-init9.C

> new file mode 100644

> index 00000000000..c44b206feb8

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init9.C

> @@ -0,0 +1,10 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +struct B { };

> +struct A : B {

> +  int i;

> +};

> +

> +B b;

> +A a(b);

> diff --git gcc/testsuite/g++.dg/ext/desig10.C gcc/testsuite/g++.dg/ext/desig10.C

> index 24057dd0ad6..c8d672b72d9 100644

> --- gcc/testsuite/g++.dg/ext/desig10.C

> +++ gcc/testsuite/g++.dg/ext/desig10.C

> @@ -1,4 +1,4 @@

>   // PR c++/84972

>   // { dg-additional-options "-w" }

>   

> -char(a[])({.a = 0});  // { dg-error "designated initializer" }

> +char(a[])({.a = 0});  // { dg-error "designated initializer|cannot convert" }

> diff --git gcc/testsuite/g++.dg/template/crash107.C gcc/testsuite/g++.dg/template/crash107.C

> index 3b0b4e8211f..9d8d394d93d 100644

> --- gcc/testsuite/g++.dg/template/crash107.C

> +++ gcc/testsuite/g++.dg/template/crash107.C

> @@ -3,7 +3,7 @@

>   // { dg-options "" }

>   // { dg-additional-options "-Wno-return-type" }

>   

> -template<typename FP_> struct Vec { // { dg-message "note" }

> +template<typename FP_> struct Vec { // { dg-message "note" "" { target c++17_down } }

>       Vec& operator^=(Vec& rhs)     {

>           union {

>               struct {FP_ x,y,z;}; // { dg-error "20:anonymous struct" }

> @@ -14,6 +14,6 @@ template<typename FP_> struct Vec { // { dg-message "note" }

>           return Vec(*this)^=rhs; // { dg-message "required" }

>       }

>   };

> -Vec<double> v(3,4,12); // { dg-error "no matching" }

> -Vec<double> V(12,4,3);  // { dg-error "no matching" }

> +Vec<double> v(3,4,12); // { dg-error "no matching|too many initializers" }

> +Vec<double> V(12,4,3);  // { dg-error "no matching|too many initializers" }

>   Vec<double> c = v^V;   // { dg-message "required" }

> diff --git gcc/testsuite/g++.dg/template/crash95.C gcc/testsuite/g++.dg/template/crash95.C

> index f60e635ae66..47346111328 100644

> --- gcc/testsuite/g++.dg/template/crash95.C

> +++ gcc/testsuite/g++.dg/template/crash95.C

> @@ -8,4 +8,4 @@ template < typename > struct S

>     };

>   };

>   

> -S < int > s(0); // { dg-error "no matching" }

> +S < int > s(0); // { dg-error "no matching|too many initializers" }

> diff --git gcc/testsuite/g++.old-deja/g++.jason/crash3.C gcc/testsuite/g++.old-deja/g++.jason/crash3.C

> index e94cc7c9781..3c4a1f84969 100644

> --- gcc/testsuite/g++.old-deja/g++.jason/crash3.C

> +++ gcc/testsuite/g++.old-deja/g++.jason/crash3.C

> @@ -2,12 +2,12 @@

>   // Bug: g++ tries to generate initialization semantics for a Node from an int,

>   // and fails.

>   

> -struct Node			// { dg-message "note" }

> +struct Node			// { dg-message "note" "" { target c++17_down } }

>   {

>     Node* child[2];

>   };

>   

>   void bug(int i)

>   {

> -  Node* q = new Node(i);	// { dg-error "no matching" }

> +  Node* q = new Node(i);	// { dg-error "no matching" "" { target c++17_down }  }

>   }

> diff --git gcc/testsuite/g++.old-deja/g++.law/ctors11.C gcc/testsuite/g++.old-deja/g++.law/ctors11.C

> index 39ee76b0ae7..b29b18a62a3 100644

> --- gcc/testsuite/g++.old-deja/g++.law/ctors11.C

> +++ gcc/testsuite/g++.old-deja/g++.law/ctors11.C

> @@ -10,12 +10,12 @@ public:

>     inline A(int x){printf("constructing A with %d\n", x);}

>   };

>   

> -class B:public A{ // { dg-message "note" } non-default constructor

> +class B:public A{ // { dg-message "note" "" { target c++17_down } } non-default constructor

>   private:

>   public:

>   };

>   

>   int main()

>   {

> -  B(10);// { dg-error "match" } B doesn't have a constructor taking int

> +  B(10);// { dg-error "match" "" { target c++17_down } } B doesn't have a constructor taking int

>   }

> diff --git gcc/testsuite/g++.old-deja/g++.law/ctors9.C gcc/testsuite/g++.old-deja/g++.law/ctors9.C

> index 43ba1262c95..5856d5dc883 100644

> --- gcc/testsuite/g++.old-deja/g++.law/ctors9.C

> +++ gcc/testsuite/g++.old-deja/g++.law/ctors9.C

> @@ -20,7 +20,7 @@ Foo::Foo(int aa)

>   { }

>   

>   

> -struct var_Foo: public Foo // { dg-message "note" }  base.*// ERROR -  in class.*

> +struct var_Foo: public Foo // { dg-message "note" "" { target c++17_down } }  base.*// ERROR -  in class.*

>   {

>     var_Foo* operator-> () {return this;}

>   };

> @@ -32,7 +32,7 @@ int blort(Foo& f)

>   

>   int main()

>   {

> -  var_Foo b(2);// { dg-error "match" }

> +  var_Foo b(2);// { dg-error "match" "" { target c++17_down } }

>     b->a = 0;

>     int x = blort(b);

>     return x;

> diff --git gcc/testsuite/g++.old-deja/g++.mike/net22.C gcc/testsuite/g++.old-deja/g++.mike/net22.C

> index e5e1cb1081d..abbf6191023 100644

> --- gcc/testsuite/g++.old-deja/g++.mike/net22.C

> +++ gcc/testsuite/g++.old-deja/g++.mike/net22.C

> @@ -5,10 +5,11 @@ public:

>     Parent( char *s ) {}

>   };

>   

> -class Child : public Parent {		// { dg-message "note" } called

> +class Child : public Parent {		// { dg-message "note" "" { target c++17_down } } called

>   };

>   

>   int main() {

> -  Child c( "String initializer" );	// { dg-error "match" } bad

> +  Child c( "String initializer" );	// { dg-error "match" "" { target c++17_down } } bad

> +// { dg-error "forbids converting a string constant" "" { target c++2a } .-1 }

>     return 0;

>   }

> diff --git gcc/testsuite/g++.old-deja/g++.niklas/t128.C gcc/testsuite/g++.old-deja/g++.niklas/t128.C

> index 19e3ca1dab0..0b3346c4ef3 100644

> --- gcc/testsuite/g++.old-deja/g++.niklas/t128.C

> +++ gcc/testsuite/g++.old-deja/g++.niklas/t128.C

> @@ -1,5 +1,5 @@

>   // { dg-do assemble  }

>   // GROUPS niklas uncaught default-construct

>   struct A { A (int); };

> -struct B : A {}; // { dg-message "note" } without ctor // ERROR - candidates

> -void f () { B (0); }// { dg-error "match" } .*

> +struct B : A {}; // { dg-message "note" "" { target c++17_down } } without ctor // ERROR - candidates

> +void f () { B (0); }// { dg-error "match" "" { target c++17_down } } .*

>
Marek Polacek Dec. 3, 2019, 12:31 a.m. | #12
On Thu, Nov 28, 2019 at 01:01:44AM -0500, Jason Merrill wrote:
> On 11/27/19 5:25 PM, Marek Polacek wrote:

> > On Wed, Nov 27, 2019 at 04:54:55PM -0500, Jason Merrill wrote:

> > > > > > @@ -921,8 +921,20 @@ perform_member_init (tree member, tree init)

> > > > > >     		inform (DECL_SOURCE_LOCATION (member),

> > > > > >     			"%q#D should be initialized", member );

> > > > > >     	    }

> > > > > > -	  finish_expr_stmt (build_aggr_init (decl, init, flags,

> > > > > > -					     tf_warning_or_error));

> > > > > > +	  init = build_aggr_init (decl, init, flags, tf_warning_or_error);

> > > > > > +	  /* In C++20, a member initializer list can be initializing an

> > > > > > +	     aggregate from a parenthesized list of values:

> > > > > > +

> > > > > > +	       struct S {

> > > > > > +		 A aggr;

> > > > > > +		 S() : aggr(1, 2, 3) { }

> > > > > > +	       };

> > > > > > +

> > > > > > +	      In such case, build_aggr_init will build up an INIT_EXPR like

> > > > > > +	      we do for aggr{1, 2, 3}, so that build_data_member_initialization

> > > > > > +	      can grok it.  */

> > > > > > +	  if (TREE_CODE (init) != INIT_EXPR)

> > > > > > +	    finish_expr_stmt (init);

> > > > > 

> > > > > Why don't we want to finish_expr_stmt an INIT_EXPR?

> > > > 

> > > > Because expand_default_init has already finish_expr_stmt-ed it.  Adding it

> > > > twice sounds wrong.

> > > 

> > > But the finish_expr_stmt in expand_default_init is inside the STATEMENT_LIST

> > > we pushed into in build_aggr_init.  If finish_init_stmts returned the

> > > INIT_EXPR, we still need to add it to the enclosing STATEMENT_LIST.

> > 

> > I don't understand why none of my tests broke

> 

> This seems to be because build_aggr_init returns an EXPR_STMT, so the test

> didn't affect anything.


Ah, got it.  Sorry about that.

> > --- gcc/cp/call.c

> > +++ gcc/cp/call.c

> > @@ -10111,6 +10111,30 @@ build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args,

> >     if (!any_viable_p)

> >       {

> > +      /* [dcl.init], 17.6.2.2:

> > +

> > +	 Otherwise, if no constructor is viable, the destination type is

> > +	 a (possibly cv-qualified) aggregate class A, and the initializer

> > +	 is a parenthesized expression-list, the object is initialized as

> > +	 follows...

> > +

> > +	 We achieve this by building up a CONSTRUCTOR, as for list-init,

> > +	 and setting CONSTRUCTOR_IS_PAREN_INIT to distinguish between

> > +	 the two.  */

> > +      if (DECL_CONSTRUCTOR_P (fn)

> > +	  && !(flags & LOOKUP_ONLYCONVERTING)

> > +	  && !cp_unevaluated_operand

> > +	  && cxx_dialect >= cxx2a

> > +	  && CP_AGGREGATE_TYPE_P (basetype)

> > +	  && !user_args->is_empty ())

> > +	{

> > +	  /* Create a CONSTRUCTOR from ARGS, e.g. {1, 2} from <1, 2>.  */

> > +	  tree list = build_tree_list_vec (user_args);

> > +	  tree ctor = build_constructor_from_list (init_list_type_node, list);

> > +	  CONSTRUCTOR_IS_DIRECT_INIT (ctor) = true;

> > +	  CONSTRUCTOR_IS_PAREN_INIT (ctor) = true;

> > +	  return ctor;

> > +	}

> 

> This still bothers me for the case where instance is not a dummy; returning

> the CONSTRUCTOR forgets all about instance and we have to rely on the caller

> reintroducing it.

> 

> I think for that case we want to build an INIT_EXPR here, like the T{}

> handling just above.


OK, adjusted as suggested.

> > --- gcc/cp/init.c

> > +++ gcc/cp/init.c

> > @@ -921,6 +921,17 @@ perform_member_init (tree member, tree init)

> >   		inform (DECL_SOURCE_LOCATION (member),

> >   			"%q#D should be initialized", member );

> >   	    }

> > +	  /* In C++20, a member initializer list can be initializing an

> > +	     aggregate from a parenthesized list of values:

> > +

> > +	       struct S {

> > +		 A aggr;

> > +		 S() : aggr(1, 2, 3) { }

> > +	       };

> > +

> > +	      In such case, build_aggr_init will build up an INIT_EXPR like

> > +	      we do for aggr{1, 2, 3}, so that build_data_member_initialization

> > +	      can grok it.  */

> 

> I think we can drop this comment, too.


Duly dropped.

I've also come across a new test, so paren-init19.C added.

Bootstrapped/regtested on x86_64-linux, built Boost and cmcstl2.

2019-12-02  Marek Polacek  <polacek@redhat.com>

	PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
	* c-cppbuiltin.c (c_cpp_builtins): Predefine
	__cpp_aggregate_paren_init=201902 for -std=c++2a.

	* call.c (build_new_method_call_1): Handle parenthesized initialization
	of aggregates by building up a CONSTRUCTOR.
	(extend_ref_init_temps): Do nothing for CONSTRUCTOR_IS_PAREN_INIT.
	* cp-tree.h (CONSTRUCTOR_IS_PAREN_INIT, LOOKUP_AGGREGATE_PAREN_INIT):
	Define.
	* decl.c (grok_reference_init): Handle aggregate initialization from
	a parenthesized list of values.
	(reshape_init): Do nothing for CONSTRUCTOR_IS_PAREN_INIT.
	(check_initializer): Handle initialization of an array from a
	parenthesized list of values.  Use NULL_TREE instead of NULL.
	* init.c (expand_default_init): Set LOOKUP_AGGREGATE_PAREN_INIT for the
	build_special_member_call call.  If it returns a
	BRACE_ENCLOSED_INITIALIZER_P, call digest_init and build an INIT_EXPR.
	Set TREE_SIDE_EFFECTS on it.
	* tree.c (build_cplus_new): Handle BRACE_ENCLOSED_INITIALIZER_P.
	* typeck2.c (digest_init_r): Set LOOKUP_AGGREGATE_PAREN_INIT if it
	receives a CONSTRUCTOR with CONSTRUCTOR_IS_PAREN_INIT set.  Allow
	narrowing when LOOKUP_AGGREGATE_PAREN_INIT.
	(massage_init_elt): Don't lose LOOKUP_AGGREGATE_PAREN_INIT when passing
	flags to digest_init_r.

	* g++.dg/cpp0x/constexpr-99.C: Only expect an error in C++17 and
	lesser.
	* g++.dg/cpp0x/explicit7.C: Likewise.
	* g++.dg/cpp0x/initlist12.C: Adjust dg-error.
	* g++.dg/cpp0x/pr31437.C: Likewise.
	* g++.dg/cpp2a/feat-cxx2a.C: Add __cpp_aggregate_paren_init test.
	* g++.dg/cpp2a/paren-init1.C: New test.
	* g++.dg/cpp2a/paren-init10.C: New test.
	* g++.dg/cpp2a/paren-init11.C: New test.
	* g++.dg/cpp2a/paren-init12.C: New test.
	* g++.dg/cpp2a/paren-init13.C: New test.
	* g++.dg/cpp2a/paren-init14.C: New test.
	* g++.dg/cpp2a/paren-init15.C: New test.
	* g++.dg/cpp2a/paren-init16.C: New test.
	* g++.dg/cpp2a/paren-init17.C: New test.
	* g++.dg/cpp2a/paren-init18.C: New test.
	* g++.dg/cpp2a/paren-init19.C: New test.
	* g++.dg/cpp2a/paren-init2.C: New test.
	* g++.dg/cpp2a/paren-init3.C: New test.
	* g++.dg/cpp2a/paren-init4.C: New test.
	* g++.dg/cpp2a/paren-init5.C: New test.
	* g++.dg/cpp2a/paren-init6.C: New test.
	* g++.dg/cpp2a/paren-init7.C: New test.
	* g++.dg/cpp2a/paren-init8.C: New test.
	* g++.dg/cpp2a/paren-init9.C: New test.
	* g++.dg/ext/desig10.C: Adjust dg-error.
	* g++.dg/template/crash107.C: Likewise.
	* g++.dg/template/crash95.C: Likewise.
	* g++.old-deja/g++.jason/crash3.C: Likewise.
	* g++.old-deja/g++.law/ctors11.C: Likewise.
	* g++.old-deja/g++.law/ctors9.C: Likewise.
	* g++.old-deja/g++.mike/net22.C: Likewise.
	* g++.old-deja/g++.niklas/t128.C: Likewise.

diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c
index 6491545bc3b..c7f4659456a 100644
--- gcc/c-family/c-cppbuiltin.c
+++ gcc/c-family/c-cppbuiltin.c
@@ -1006,6 +1006,7 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_impl_destroying_delete=201806L");
 	  cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907L");
 	  cpp_define (pfile, "__cpp_impl_three_way_comparison=201907L");
+	  cpp_define (pfile, "__cpp_aggregate_paren_init=201902L");
 	}
       if (flag_concepts)
         {
diff --git gcc/cp/call.c gcc/cp/call.c
index acc7e1322d0..7454ffb7715 100644
--- gcc/cp/call.c
+++ gcc/cp/call.c
@@ -10111,6 +10111,38 @@ build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args,
 
   if (!any_viable_p)
     {
+      /* [dcl.init], 17.6.2.2:
+
+	 Otherwise, if no constructor is viable, the destination type is
+	 a (possibly cv-qualified) aggregate class A, and the initializer
+	 is a parenthesized expression-list, the object is initialized as
+	 follows...
+
+	 We achieve this by building up a CONSTRUCTOR, as for list-init,
+	 and setting CONSTRUCTOR_IS_PAREN_INIT to distinguish between
+	 the two.  */
+      if (DECL_CONSTRUCTOR_P (fn)
+	  && !(flags & LOOKUP_ONLYCONVERTING)
+	  && !cp_unevaluated_operand
+	  && cxx_dialect >= cxx2a
+	  && CP_AGGREGATE_TYPE_P (basetype)
+	  && !user_args->is_empty ())
+	{
+	  /* Create a CONSTRUCTOR from ARGS, e.g. {1, 2} from <1, 2>.  */
+	  tree list = build_tree_list_vec (user_args);
+	  tree ctor = build_constructor_from_list (init_list_type_node, list);
+	  CONSTRUCTOR_IS_DIRECT_INIT (ctor) = true;
+	  CONSTRUCTOR_IS_PAREN_INIT (ctor) = true;
+	  if (is_dummy_object (instance))
+	    return ctor;
+	  else
+	    {
+	      ctor = digest_init (basetype, ctor, complain);
+	      ctor = build2 (INIT_EXPR, TREE_TYPE (instance), instance, ctor);
+	      TREE_SIDE_EFFECTS (ctor) = true;
+	      return ctor;
+	    }
+	}
       if (complain & tf_error)
 	complain_about_no_candidates_for_method_call (instance, candidates,
 						      explicit_targs, basetype,
@@ -11792,9 +11824,16 @@ perform_direct_initialization_if_possible (tree type,
      If the destination type is a (possibly cv-qualified) class type:
 
      -- If the initialization is direct-initialization ...,
-     constructors are considered. ... If no constructor applies, or
-     the overload resolution is ambiguous, the initialization is
-     ill-formed.  */
+     constructors are considered.
+
+       -- If overload resolution is successful, the selected constructor
+       is called to initialize the object, with the initializer expression
+       or expression-list as its argument(s).
+
+       -- Otherwise, if no constructor is viable, the destination type is
+       a (possibly cv-qualified) aggregate class A, and the initializer is
+       a parenthesized expression-list, the object is initialized as
+       follows...  */
   if (CLASS_TYPE_P (type))
     {
       releasing_vec args (make_tree_vector_single (expr));
@@ -12150,6 +12189,12 @@ extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups)
 	ctor = TARGET_EXPR_INITIAL (ctor);
       if (TREE_CODE (ctor) == CONSTRUCTOR)
 	{
+	  /* [dcl.init] When initializing an aggregate from a parenthesized list
+	     of values... a temporary object bound to a reference does not have
+	     its lifetime extended.  */
+	  if (CONSTRUCTOR_IS_PAREN_INIT (ctor))
+	    return init;
+
 	  if (is_std_init_list (type))
 	    {
 	      /* The temporary array underlying a std::initializer_list
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index 7e810b8ee7b..4af18b0ae62 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -4325,6 +4325,11 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define CONSTRUCTOR_IS_DESIGNATED_INIT(NODE) \
   (TREE_LANG_FLAG_6 (CONSTRUCTOR_CHECK (NODE)))
 
+/* True if this CONSTRUCTOR comes from a parenthesized list of values, e.g.
+   A(1, 2, 3).  */
+#define CONSTRUCTOR_IS_PAREN_INIT(NODE) \
+  (CONSTRUCTOR_CHECK(NODE)->base.private_flag)
+
 /* True if NODE represents a conversion for direct-initialization in a
    template.  Set by perform_implicit_conversion_flags.  */
 #define IMPLICIT_CONV_EXPR_DIRECT_INIT(NODE) \
@@ -5583,6 +5588,8 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
    args), then we swap the conversions back in build_new_op_1 (so they
    correspond to the order of the args in the candidate).  */
 #define LOOKUP_REVERSED (LOOKUP_REWRITTEN << 1)
+/* We're initializing an aggregate from a parenthesized list of values.  */
+#define LOOKUP_AGGREGATE_PAREN_INIT (LOOKUP_REVERSED << 1)
 
 #define LOOKUP_NAMESPACES_ONLY(F)  \
   (((F) & LOOKUP_PREFER_NAMESPACES) && !((F) & LOOKUP_PREFER_TYPES))
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 81d73433547..54f09507516 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -5531,11 +5531,28 @@ grok_reference_init (tree decl, tree type, tree init, int flags)
       return NULL_TREE;
     }
 
+  tree ttype = TREE_TYPE (type);
   if (TREE_CODE (init) == TREE_LIST)
-    init = build_x_compound_expr_from_list (init, ELK_INIT,
-					    tf_warning_or_error);
+    {
+      /* This handles (C++20 only) code like
+
+	   const A& r(1, 2, 3);
+
+	 where we treat the parenthesized list as a CONSTRUCTOR.  */
+      if (TREE_TYPE (init) == NULL_TREE
+	  && CP_AGGREGATE_TYPE_P (ttype)
+	  && !DECL_DECOMPOSITION_P (decl)
+	  && (cxx_dialect >= cxx2a))
+	{
+	  init = build_constructor_from_list (init_list_type_node, init);
+	  CONSTRUCTOR_IS_DIRECT_INIT (init) = true;
+	  CONSTRUCTOR_IS_PAREN_INIT (init) = true;
+	}
+      else
+	init = build_x_compound_expr_from_list (init, ELK_INIT,
+						tf_warning_or_error);
+    }
 
-  tree ttype = TREE_TYPE (type);
   if (TREE_CODE (ttype) != ARRAY_TYPE
       && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE)
     /* Note: default conversion is only called in very special cases.  */
@@ -6437,6 +6454,11 @@ reshape_init (tree type, tree init, tsubst_flags_t complain)
   if (vec_safe_is_empty (v))
     return init;
 
+  /* Brace elision is not performed for a CONSTRUCTOR representing
+     parenthesized aggregate initialization.  */
+  if (CONSTRUCTOR_IS_PAREN_INIT (init))
+    return init;
+
   /* Handle [dcl.init.list] direct-list-initialization from
      single element of enumeration with a fixed underlying type.  */
   if (is_direct_enum_init (type, init))
@@ -6640,6 +6662,41 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
 	      flags |= LOOKUP_NO_NARROWING;
 	    }
 	}
+      /* [dcl.init] "Otherwise, if the destination type is an array, the object
+	 is initialized as follows..."  So handle things like
+
+	  int a[](1, 2, 3);
+
+	 which is permitted in C++20 by P0960.  */
+      else if (TREE_CODE (init) == TREE_LIST
+	       && TREE_TYPE (init) == NULL_TREE
+	       && TREE_CODE (type) == ARRAY_TYPE
+	       && !DECL_DECOMPOSITION_P (decl)
+	       && (cxx_dialect >= cxx2a))
+	{
+	  /* [dcl.init.string] "An array of ordinary character type [...]
+	     can be initialized by an ordinary string literal [...] by an
+	     appropriately-typed string literal enclosed in braces" only
+	     talks about braces, but GCC has always accepted
+
+	       char a[]("foobar");
+
+	     so we continue to do so.  */
+	  tree val = TREE_VALUE (init);
+	  if (TREE_CHAIN (init) == NULL_TREE
+	      && char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type)))
+	      && TREE_CODE (tree_strip_any_location_wrapper (val))
+		 == STRING_CST)
+	    /* If the list has a single element and it's a string literal,
+	       then it's the initializer for the array as a whole.  */
+	    init = val;
+	  else
+	    {
+	      init = build_constructor_from_list (init_list_type_node, init);
+	      CONSTRUCTOR_IS_DIRECT_INIT (init) = true;
+	      CONSTRUCTOR_IS_PAREN_INIT (init) = true;
+	    }
+	}
       else if (TREE_CODE (init) == TREE_LIST
 	       && TREE_TYPE (init) != unknown_type_node
 	       && !MAYBE_CLASS_TYPE_P (type))
@@ -6683,6 +6740,9 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
 	    init_code = TREE_OPERAND (init_code, 0);
 	  if (TREE_CODE (init_code) == INIT_EXPR)
 	    {
+	      /* In C++20, the call to build_aggr_init could have created
+		 an INIT_EXPR with a CONSTRUCTOR as the RHS to handle
+		 A(1, 2).  */
 	      init = TREE_OPERAND (init_code, 1);
 	      init_code = NULL_TREE;
 	      /* Don't call digest_init; it's unnecessary and will complain
@@ -6736,7 +6796,7 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
 			0, "array %qD initialized by parenthesized "
 			"string literal %qE",
 			decl, DECL_INITIAL (decl));
-	  init = NULL;
+	  init = NULL_TREE;
 	}
     }
   else
diff --git gcc/cp/init.c gcc/cp/init.c
index aa48f80e58d..77bbea20705 100644
--- gcc/cp/init.c
+++ gcc/cp/init.c
@@ -1967,8 +1978,23 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
       tree ctor_name = (true_exp == exp
 			? complete_ctor_identifier : base_ctor_identifier);
 
+      /* Given class A,
+
+	   A a(1, 2);
+
+	 can mean a call to a constructor A::A(int, int), if present.  If not,
+	 but A is an aggregate, we will try aggregate initialization.  */
       rval = build_special_member_call (exp, ctor_name, &parms, binfo, flags,
 					complain);
+      if (BRACE_ENCLOSED_INITIALIZER_P (rval))
+	{
+	  gcc_assert (cxx_dialect >= cxx2a);
+	  rval = digest_init (type, rval, tf_warning_or_error);
+	  rval = build2 (INIT_EXPR, type, exp, rval);
+	  /* So that we do finish_expr_stmt below.  Don't return here, we
+	     need to release PARMS.  */
+	  TREE_SIDE_EFFECTS (rval) = 1;
+	}
     }
 
   if (parms != NULL)
diff --git gcc/cp/tree.c gcc/cp/tree.c
index 8817860b4d3..8b625e8b18e 100644
--- gcc/cp/tree.c
+++ gcc/cp/tree.c
@@ -669,6 +669,15 @@ build_aggr_init_expr (tree type, tree init)
 tree
 build_cplus_new (tree type, tree init, tsubst_flags_t complain)
 {
+  /* This function should cope with what build_special_member_call
+     can produce.  When performing parenthesized aggregate initialization,
+     it can produce a { }.  */
+  if (BRACE_ENCLOSED_INITIALIZER_P (init))
+    {
+      gcc_assert (cxx_dialect >= cxx2a);
+      return finish_compound_literal (type, init, complain);
+    }
+
   tree rval = build_aggr_init_expr (type, init);
   tree slot;
 
diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
index b8868546444..9c6f25c8608 100644
--- gcc/cp/typeck2.c
+++ gcc/cp/typeck2.c
@@ -1117,6 +1117,10 @@ digest_init_r (tree type, tree init, int nested, int flags,
 
   tree stripped_init = init;
 
+  if (BRACE_ENCLOSED_INITIALIZER_P (init)
+      && CONSTRUCTOR_IS_PAREN_INIT (init))
+    flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+
   /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue
      (g++.old-deja/g++.law/casts2.C).  */
   if (TREE_CODE (init) == NON_LVALUE_EXPR)
@@ -1224,7 +1228,9 @@ digest_init_r (tree type, tree init, int nested, int flags,
   if ((code != COMPLEX_TYPE || BRACE_ENCLOSED_INITIALIZER_P (stripped_init))
       && (SCALAR_TYPE_P (type) || code == REFERENCE_TYPE))
     {
-      if (nested)
+      /* Narrowing is OK when initializing an aggregate from
+	 a parenthesized list.  */
+      if (nested && !(flags & LOOKUP_AGGREGATE_PAREN_INIT))
 	flags |= LOOKUP_NO_NARROWING;
       init = convert_for_initialization (0, type, init, flags,
 					 ICR_INIT, NULL_TREE, 0,
@@ -1384,9 +1390,12 @@ static tree
 massage_init_elt (tree type, tree init, int nested, int flags,
 		  tsubst_flags_t complain)
 {
-  flags &= LOOKUP_ALLOW_FLEXARRAY_INIT;
-  flags |= LOOKUP_IMPLICIT;
-  init = digest_init_r (type, init, nested ? 2 : 1, flags, complain);
+  int new_flags = LOOKUP_IMPLICIT;
+  if (flags & LOOKUP_ALLOW_FLEXARRAY_INIT)
+    new_flags |= LOOKUP_ALLOW_FLEXARRAY_INIT;
+  if (flags & LOOKUP_AGGREGATE_PAREN_INIT)
+    new_flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+  init = digest_init_r (type, init, nested ? 2 : 1, new_flags, complain);
   /* Strip a simple TARGET_EXPR when we know this is an initializer.  */
   if (SIMPLE_TARGET_EXPR_P (init))
     init = TARGET_EXPR_INITIAL (init);
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-99.C gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
index 8d791dd032d..4d3953d0983 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
@@ -9,5 +9,6 @@ struct A
 struct B
 {
   A a;
-    constexpr B() : a(0) {} // { dg-error "no matching function" }
+  // P0960R3 allows paren-init.
+  constexpr B() : a(0) {} // { dg-error "no matching function" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/explicit7.C gcc/testsuite/g++.dg/cpp0x/explicit7.C
index 574796d117d..67b50542bc3 100644
--- gcc/testsuite/g++.dg/cpp0x/explicit7.C
+++ gcc/testsuite/g++.dg/cpp0x/explicit7.C
@@ -10,7 +10,7 @@ struct A { };
 struct B: A { };
 struct C {
   explicit operator B*();	// { dg-message "explicit" }
-  explicit operator B&();	// { dg-message "explicit" }
+  explicit operator B&();	// { dg-message "explicit" "" { target c++17_down } }
 };
 
 C c;
diff --git gcc/testsuite/g++.dg/cpp0x/initlist12.C gcc/testsuite/g++.dg/cpp0x/initlist12.C
index f72a6dac525..c2a2b86520f 100644
--- gcc/testsuite/g++.dg/cpp0x/initlist12.C
+++ gcc/testsuite/g++.dg/cpp0x/initlist12.C
@@ -6,15 +6,15 @@ struct A
   int i;
 };
 
-A a({1,2});			// { dg-error "no match" }
+A a({1,2});			// { dg-error "no match|cannot convert" }
 
 union U
 {
   int i,j;
 };
 
-U u({1,2});			// { dg-error "no match" }
+U u({1,2});			// { dg-error "no match|cannot convert" }
 
 union V {};
 
-V v({1});			// { dg-error "no match" }
+V v({1});			// { dg-error "no match|too many initializers" }
diff --git gcc/testsuite/g++.dg/cpp0x/pr31437.C gcc/testsuite/g++.dg/cpp0x/pr31437.C
index 7e2ed7a1375..532b533c8d3 100644
--- gcc/testsuite/g++.dg/cpp0x/pr31437.C
+++ gcc/testsuite/g++.dg/cpp0x/pr31437.C
@@ -1,9 +1,9 @@
 // { dg-do compile { target c++11 } }
-template <typename... T> struct A // { dg-message "candidates|A" }
+template <typename... T> struct A // { dg-message "candidates|A" "" { target c++17_down } }
 {
   A(T* p) {  // { dg-error "parameter packs|T" }
    (A<T...>*)(p); 
   }
 };
 
-A<int> a(0); // { dg-error "no matching" }
+A<int> a(0); // { dg-error "no matching|too many initializers" }
diff --git gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
index 9b6e2f59d2c..389b25e16ea 100644
--- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
+++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
@@ -460,6 +460,12 @@
 #  error "__cpp_constexpr_dynamic_alloc != 201907"
 #endif
 
+#ifndef __cpp_aggregate_paren_init
+#  error "__cpp_aggregate_paren_init"
+#elif __cpp_aggregate_paren_init != 201902
+#  error "__cpp_aggregate_paren_init != 201902"
+#endif
+
 #ifdef __has_cpp_attribute
 
 #  if ! __has_cpp_attribute(maybe_unused)
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init1.C gcc/testsuite/g++.dg/cpp2a/paren-init1.C
new file mode 100644
index 00000000000..a54b2ccaf6d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init1.C
@@ -0,0 +1,116 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+struct A {
+  int i;
+  int j;
+  int k;
+};
+
+struct B {
+  int i;
+  int j;
+  int k = 42;
+};
+
+struct C {
+  A a;
+};
+
+struct D {
+  A a1;
+  A a2;
+};
+
+struct E {
+  int i;
+};
+
+// F has a base class, but it's not virtual, private, or protected, so this is
+// still an aggregate (since C++17).
+struct F : E {
+  int j;
+  int k;
+};
+
+F f({1}, 2, 3);
+
+// A non-virtual member function doesn't make it a non-aggregate.
+struct G {
+  int i;
+  double j;
+  int foo(int, int);
+};
+
+G g(1, 2.14);
+
+class H {
+public:
+  H (int) { }
+};
+
+class I : public H { };
+
+int i;
+A a1(1, 2);
+A a2(1.0, 2);
+A a3(++i, ++i);
+const A& ra(1, 2);
+
+A ca = A(1, 2);
+A ca2 = A(1.0, 2);
+A ca3 = A(++i, ++i);
+const A& rca = A(1, 2);
+
+B b1(1, 2, 3);
+B b2(1, 2);
+B b3(1);
+
+C c1({5, 6, 7});
+D d1({1, 2, 3}, {5, 6, 7});
+
+struct W {
+  const char *s, *t;
+};
+W w1("fluffer", "nutter");
+
+struct Y {
+  char a[4];
+};
+Y y("yew");
+
+int
+main ()
+{
+  I(10);
+
+  // A::k will be value-initialized.
+  if (a1.i != 1 || a1.j != 2 || a1.k != 0)
+    __builtin_abort ();
+  if (a2.i != 1 || a2.j != 2 || a2.k != 0)
+    __builtin_abort ();
+  if (a3.i != 1 || a3.j != 2 || a3.k != 0)
+    __builtin_abort ();
+  if (ra.i != 1 || ra.j != 2 || ra.k != 0)
+    __builtin_abort ();
+  if (ca.i != 1 || ca.j != 2 || ca.k != 0)
+    __builtin_abort ();
+  if (ca2.i != 1 || ca2.j != 2 || ca2.k != 0)
+    __builtin_abort ();
+  if (ca3.i != 3 || ca3.j != 4 || ca3.k != 0)
+    __builtin_abort ();
+
+  if (b1.i != 1 || b1.j != 2 || b1.k != 3)
+    __builtin_abort ();
+  // The default member initializer will be used for B::k.
+  if (b2.i != 1 || b2.j != 2 || b2.k != 42)
+    __builtin_abort ();
+  if (b3.i != 1 || b3.j != 0 || b3.k != 42)
+    __builtin_abort ();
+
+  if (c1.a.i != 5 || c1.a.j != 6 || c1.a.k != 7)
+    __builtin_abort ();
+
+  if (f.i != 1 || f.j != 2 || f.k != 3)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init10.C gcc/testsuite/g++.dg/cpp2a/paren-init10.C
new file mode 100644
index 00000000000..5c70d9d59ee
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init10.C
@@ -0,0 +1,18 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test from [dcl.init].
+
+struct A {
+  int a;
+  int&& r;
+};
+
+int f();
+int n = 10;
+
+A a1{1, f()};               // OK, lifetime is extended
+A a2(1, f());               // well-formed, but dangling reference
+A a3{1.0, 1};               // { dg-error "narrowing conversion" }
+A a4(1.0, 1);               // well-formed, but dangling reference
+A a5(1.0, static_cast<int&&>(n));    // OK
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init11.C gcc/testsuite/g++.dg/cpp2a/paren-init11.C
new file mode 100644
index 00000000000..82ca2669545
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init11.C
@@ -0,0 +1,88 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test ill-formed code.
+
+// If k is greater than the size of the array, the program is ill-formed.
+int a1[2](1, 2, 3); // { dg-error "too many initializers" }
+int a2[](); // { dg-error "array of functions" }
+int a3[](()); // { dg-error "expected primary-expression" }
+int a4[]("raccoon"); // { dg-error "invalid conversion" }
+
+struct S {
+  int i;
+  int j;
+};
+
+S s1(1, 1, 1); // { dg-error "too many initializers" }
+
+union U2 {
+  int a;
+  float b;
+};
+
+// [dcl.init.aggr]/19:
+// When a union is initialized with an initializer list, there shall not be
+// more than one explicitly initialized element.
+U2 u4 = U2(1, 2); // { dg-error "too many initializers" }
+
+// Test there is no brace elision.
+
+int a[2][2](1, 2, 3, 4); // { dg-error "too many initializers|array must be initialized with a brace-enclosed initializer" }
+
+// private/protected/virtual base class -> not an aggregate.
+struct B { };
+struct D : private B {
+  int i;
+  int j;
+};
+
+D d({}, 1, 2); // { dg-error "no matching function" }
+
+// Private non-static data member -> not an aggregate.
+struct P {
+  int i;
+private:
+  int j;
+};
+
+P p(1, 2); // { dg-error "no matching function" }
+
+// User-declared constructor -> not an aggregate.
+struct U {
+  U() {}
+  int i;
+  int j;  
+};
+
+U u(1, 2); // { dg-error "no matching function" }
+
+// virtual member function -> not an aggregate.
+struct V {
+  int i;
+  int j;
+  virtual int foo(int);
+};
+
+V v(1, 2); // { dg-error "no matching function" }
+
+struct nonaggr {
+  int i;
+  int j;
+private:
+  int x;
+};
+
+struct F {
+  nonaggr n;
+  F() : n(1) { } // { dg-error "no matching function" }
+};
+
+struct G {
+  char a[4];
+};
+
+struct H {
+  G g;
+  H() : g("oaks") { } // { dg-error "initializer-string" }
+};
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init12.C gcc/testsuite/g++.dg/cpp2a/paren-init12.C
new file mode 100644
index 00000000000..d7be6f2b55d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init12.C
@@ -0,0 +1,17 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct A;
+
+struct C {
+  operator A();
+};
+
+struct A {
+  C c;
+};
+
+C c;
+A a(c);  // invokes C’s conversion function to A
+
+// { dg-final { scan-assembler "_ZN1Ccv1AEv" } }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init13.C gcc/testsuite/g++.dg/cpp2a/paren-init13.C
new file mode 100644
index 00000000000..4b9107c70ff
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init13.C
@@ -0,0 +1,16 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct X { int a, b; };
+struct Y { X x; };
+
+void
+f()
+{
+  // This is ok...
+  Y y1{{1, 2}};
+  Y y2({1, 2});
+  // ...but Y y((1,2)) is not the same as Y y{{1,2}}.  (1, 2) is a
+  // COMPOUND_EXPR.
+  Y y3((1, 2)); // { dg-error "could not convert" }
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init14.C gcc/testsuite/g++.dg/cpp2a/paren-init14.C
new file mode 100644
index 00000000000..837f9531260
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init14.C
@@ -0,0 +1,10 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++11 } }
+
+struct A {
+  int x;
+  int y;
+  A(int, int) = delete;
+};
+
+A a(1, 2); // { dg-error "use of deleted function" }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init15.C gcc/testsuite/g++.dg/cpp2a/paren-init15.C
new file mode 100644
index 00000000000..4311dd4df59
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init15.C
@@ -0,0 +1,35 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct A {
+  int i;
+  int j;
+};
+
+struct B : A
+{
+  B (): A(1.7, 2) { }
+};
+
+void f(A);
+
+void
+g ()
+{
+  f (A(1, 2));
+}
+
+struct S {
+  int a[2];
+};
+
+S h() { return S({1, 2}); }
+
+struct Z {
+  int i;
+  int j;
+  operator A();
+};
+
+Z z;
+A a = Z(1, 2.3);
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init16.C gcc/testsuite/g++.dg/cpp2a/paren-init16.C
new file mode 100644
index 00000000000..c8ed924877e
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init16.C
@@ -0,0 +1,14 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct S { int a[2]; };
+struct A { S s[1]; };
+
+template <typename, int N>
+struct R { static constexpr auto h = A({S{N}}); };
+
+template <typename, int N>
+struct R2 { static constexpr auto h = A({S({N, N})}); };
+
+A foo = R<int, 10>::h;
+A foo2 = R2<int, 10>::h;
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init17.C gcc/testsuite/g++.dg/cpp2a/paren-init17.C
new file mode 100644
index 00000000000..8e08b5288a5
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init17.C
@@ -0,0 +1,6 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++11 } }
+
+struct A { };
+struct B { };
+static_assert (!__is_trivially_constructible(A, B), "");
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init18.C gcc/testsuite/g++.dg/cpp2a/paren-init18.C
new file mode 100644
index 00000000000..0aea493f214
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init18.C
@@ -0,0 +1,9 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct A { int a, b, c; };
+struct S { A a; };
+constexpr S s{ A(1, 2, 3) };
+static_assert (s.a.a == 1 && s.a.b == 2 && s.a.c == 3);
+constexpr S s2 = { A(1, 2, 3) };
+static_assert (s2.a.a == 1 && s2.a.b == 2 && s2.a.c == 3);
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init19.C gcc/testsuite/g++.dg/cpp2a/paren-init19.C
new file mode 100644
index 00000000000..73643fa3aa2
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init19.C
@@ -0,0 +1,12 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct A {
+  int i[2];
+};
+
+A a({1});
+A a2({1, 2});
+A a3(1); // { dg-error "array must be initialized with a brace-enclosed initializer" }
+A a4 = A(1); // { dg-error "array must be initialized with a brace-enclosed initializer" }
+A a5 = A({1});
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init2.C gcc/testsuite/g++.dg/cpp2a/paren-init2.C
new file mode 100644
index 00000000000..e9e90d7acb6
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init2.C
@@ -0,0 +1,56 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+struct A {
+  int i = 0;
+  int j = 0;
+};
+
+struct B {
+  A a;
+  constexpr B() : a(1.1, 2) { }
+};
+
+struct C {
+  int i;
+};
+
+struct E {
+  C c;
+  E() : c(1.2) { }
+};
+
+struct F {
+  char a[4];
+};
+
+struct G {
+  F f;
+  G() : f("yew") { }
+};
+
+struct H {
+  int i;
+  int &&r;
+};
+
+int f() { return 42; }
+
+struct I {
+  H h;
+  I() : h(1, f()) { }
+};
+
+I i;  // dangling ref to f():
+      // {.i=1, .r=(int &) &TARGET_EXPR <D.2118, f ()>}
+
+int
+main ()
+{
+  B b;
+  if (b.a.i != 1 || b.a.j != 2)
+    __builtin_abort ();
+  E e;
+  if (e.c.i != 1)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init3.C gcc/testsuite/g++.dg/cpp2a/paren-init3.C
new file mode 100644
index 00000000000..f444005a09f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init3.C
@@ -0,0 +1,11 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+class a {
+  int b{};
+};
+class c {
+  c();
+  a d;
+};
+c::c() {}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init4.C gcc/testsuite/g++.dg/cpp2a/paren-init4.C
new file mode 100644
index 00000000000..f8c7bd10b63
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init4.C
@@ -0,0 +1,142 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+// Test T[]().
+
+int i;
+int a1[](1, 2, 3);
+static_assert(sizeof(a1) == 3 * sizeof (int), "");
+int a2[](1.0, 2, 3);
+static_assert(sizeof(a2) == 3 * sizeof (int), "");
+int a3[3](1, 2, 3);
+int a4[3](1, 2); // a4[2] is value-initialized.
+int a5[](++i, ++i);
+static_assert(sizeof(a5) == 2 * sizeof (int), "");
+int a6[](1);
+static_assert(sizeof(a6) == sizeof (int), "");
+int a7[]({});
+static_assert(sizeof(a7) == sizeof (int), "");
+int a8[]({}, {}, {}, {}, {}, {});
+static_assert(sizeof(a8) == 6 * sizeof (int), "");
+int a9[]((1));
+static_assert(sizeof(a9) == sizeof (int), "");
+int a10[]((1), (2), (3));
+static_assert(sizeof(a10) == 3 * sizeof (int), "");
+int a11[][2]{1};
+static_assert(sizeof(a11) == 2 * sizeof (int), "");
+int a12[][2]({1, 2}, {3, 4});
+static_assert(sizeof(a12) == 4 * sizeof (int), "");
+
+const int (&ra1)[](1, 2);
+const int (&ra2)[](1.0, 2);
+const int (&ra3)[2](1.0, 2);
+int (&&rra1)[](1, 2);
+int (&&rra2)[](1.0, 2);
+int (&&rra3)[2](1.0, 2);
+
+struct S { int i; } s;
+S s1[]({1});
+static_assert(sizeof(s1) == sizeof (S), "");
+S s2[]({1}, {2});
+static_assert(sizeof(s2) == 2 * sizeof (S), "");
+S s3[]({1}, {2}, {3});
+static_assert(sizeof(s3) == 3 * sizeof (S), "");
+S s4[3]({1}, {2});
+static_assert(sizeof(s4) == 3 * sizeof (S), "");
+S s5[]({++i}, {++i});
+static_assert(sizeof(s2) == 2 * sizeof (S), "");
+S s6[](s, s);
+
+struct R { int i; int j; };
+R r1[]({1, 2});
+static_assert(sizeof(r1) == sizeof (R), "");
+R r2[]({1, 2}, {3, 4});
+static_assert(sizeof(r2) == 2 * sizeof (R), "");
+R r3[]({1.0, 2}, {3.2, 4});
+static_assert(sizeof(r3) == 2 * sizeof (R), "");
+
+char c1[]('r');
+char c2[]('r', 'a', 'c', 'c', 'o', 'o', 'n');
+char c3[]("yarrow");
+char c4[4]("oak");
+char c5[10]("cat");
+const char (&c6)[4]("eel");
+
+int g;
+struct X {
+  int i;
+  X() { ++g; }
+  X(int) { };
+};
+
+int
+main ()
+{
+  // Here we'll value-initialize l[1] and l[2], which will zero-initialize it.
+  int l[3](42);
+  if (l[0] != 42 || l[1] != 0 || l[2] != 0)
+    __builtin_abort ();
+
+  // Here we'll value-initialize x[2] and x[3].  Since X is a class type
+  // with a user-provided ctor, we'll default-initialize in both cases.
+  X x[4]({ 1 }, { 2 });
+  if (g != 2)
+    __builtin_abort ();
+
+  if (a1[0] != 1 || a1[1] != 2 || a1[2] != 3)
+    __builtin_abort ();
+  if (a2[0] != 1 || a2[1] != 2 || a2[2] != 3)
+    __builtin_abort ();
+  if (a3[0] != 1 || a3[1] != 2 || a3[2] != 3)
+    __builtin_abort ();
+  if (a4[0] != 1 || a4[1] != 2 || a4[2] != 0)
+    __builtin_abort ();
+  if (a5[0] != 1 || a5[1] != 2)
+    __builtin_abort ();
+  if (a6[0] != 1)
+    __builtin_abort ();
+  if (a7[0] != 0)
+    __builtin_abort ();
+  if (a8[0] != 0 || a8[1] != 0 || a8[2] != 0 || a8[3] != 0
+      || a8[4] != 0 || a8[5] != 0)
+    __builtin_abort ();
+  if (a9[0] != 1)
+    __builtin_abort ();
+  if (a10[0] != 1 || a10[1] != 2 || a10[2] != 3)
+    __builtin_abort ();
+  if (a11[0][0] != 1 || a11[0][1] != 0)
+    __builtin_abort ();
+  if (a12[0][0] != 1 || a12[0][1] != 2 || a12[1][0] != 3 || a12[1][1] != 4)
+    __builtin_abort ();
+
+  if (ra1[0] != 1 || ra1[1] != 2)
+    __builtin_abort ();
+  if (ra2[0] != 1 || ra2[1] != 2)
+    __builtin_abort ();
+  if (ra3[0] != 1 || ra3[1] != 2)
+    __builtin_abort ();
+  if (rra1[0] != 1 || rra1[1] != 2)
+    __builtin_abort ();
+  if (rra2[0] != 1 || rra2[1] != 2)
+    __builtin_abort ();
+  if (rra3[0] != 1 || rra3[1] != 2)
+    __builtin_abort ();
+
+  if (s1[0].i != 1)
+    __builtin_abort ();
+  if (s2[0].i != 1 || s2[1].i != 2)
+    __builtin_abort ();
+  if (s3[0].i != 1 || s3[1].i != 2 || s3[2].i != 3)
+    __builtin_abort ();
+  if (s4[0].i != 1 || s4[1].i != 2 || s4[2].i != 0)
+    __builtin_abort ();
+  if (s5[0].i != 3 || s5[1].i != 4)
+    __builtin_abort ();
+
+  if (r1[0].i != 1 || r1[0].j != 2)
+    __builtin_abort ();
+  if (r2[0].i != 1 || r2[0].j != 2 || r2[1].i != 3 || r2[1].j != 4)
+    __builtin_abort ();
+  if (r3[0].i != 1 || r3[0].j != 2 || r3[1].i != 3 || r3[1].j != 4)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init5.C gcc/testsuite/g++.dg/cpp2a/paren-init5.C
new file mode 100644
index 00000000000..a64cb00ebbc
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init5.C
@@ -0,0 +1,25 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+union U {
+  int a;
+  float b;
+};
+
+// u1 has no active member
+U u1;
+// u2 zero-initializes the first member, so u2.a is the active member and
+// its value is 0.
+U u2 = U();
+// u3 uses non-list aggregate initialization, so u3.a is the active member
+// and its value is 1.
+U u3 = U(1);
+
+int
+main ()
+{
+  if (u2.a != 0)
+    __builtin_abort ();
+  if (u3.a != 1)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init6.C gcc/testsuite/g++.dg/cpp2a/paren-init6.C
new file mode 100644
index 00000000000..b5d97dcb3d8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init6.C
@@ -0,0 +1,14 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test that we don't perform lifetime extension for () init.
+
+struct A {
+  int a;
+  int&& r;
+};
+
+int f();
+A a(1, f());
+
+// { dg-final { scan-assembler-not "_ZGR1a" } }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init7.C gcc/testsuite/g++.dg/cpp2a/paren-init7.C
new file mode 100644
index 00000000000..32af1a7265c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init7.C
@@ -0,0 +1,20 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+int h;
+struct i {
+  i() {}
+  explicit i(i &) {}
+  template <typename j> i(j &) { h++; }
+};
+
+int main() {
+  {
+    i a[6];
+    auto [b, c, d, e, f, g] = a;
+  }
+  i a[6];
+  auto [b, c, d, e, f, g](a);
+  if (h != 6)
+    __builtin_abort();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init8.C gcc/testsuite/g++.dg/cpp2a/paren-init8.C
new file mode 100644
index 00000000000..30e71650bc9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init8.C
@@ -0,0 +1,13 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test that we don't accept designated inits in ( ).
+
+struct S {
+  int i;
+  int j = 42;
+};
+
+S s(.i = 12); // { dg-error "expected" }
+
+int a[]([0] = 42); // { dg-error "" }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init9.C gcc/testsuite/g++.dg/cpp2a/paren-init9.C
new file mode 100644
index 00000000000..c44b206feb8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init9.C
@@ -0,0 +1,10 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct B { };
+struct A : B {
+  int i;
+};
+
+B b;
+A a(b);
diff --git gcc/testsuite/g++.dg/ext/desig10.C gcc/testsuite/g++.dg/ext/desig10.C
index 24057dd0ad6..c8d672b72d9 100644
--- gcc/testsuite/g++.dg/ext/desig10.C
+++ gcc/testsuite/g++.dg/ext/desig10.C
@@ -1,4 +1,4 @@
 // PR c++/84972
 // { dg-additional-options "-w" }
 
-char(a[])({.a = 0});  // { dg-error "designated initializer" }
+char(a[])({.a = 0});  // { dg-error "designated initializer|cannot convert" }
diff --git gcc/testsuite/g++.dg/template/crash107.C gcc/testsuite/g++.dg/template/crash107.C
index 3b0b4e8211f..9d8d394d93d 100644
--- gcc/testsuite/g++.dg/template/crash107.C
+++ gcc/testsuite/g++.dg/template/crash107.C
@@ -3,7 +3,7 @@
 // { dg-options "" }
 // { dg-additional-options "-Wno-return-type" }
 
-template<typename FP_> struct Vec { // { dg-message "note" }
+template<typename FP_> struct Vec { // { dg-message "note" "" { target c++17_down } }
     Vec& operator^=(Vec& rhs)     {
         union {
             struct {FP_ x,y,z;}; // { dg-error "20:anonymous struct" }
@@ -14,6 +14,6 @@ template<typename FP_> struct Vec { // { dg-message "note" }
         return Vec(*this)^=rhs; // { dg-message "required" }
     }
 };
-Vec<double> v(3,4,12); // { dg-error "no matching" }
-Vec<double> V(12,4,3);  // { dg-error "no matching" }
+Vec<double> v(3,4,12); // { dg-error "no matching|too many initializers" }
+Vec<double> V(12,4,3);  // { dg-error "no matching|too many initializers" }
 Vec<double> c = v^V;   // { dg-message "required" }
diff --git gcc/testsuite/g++.dg/template/crash95.C gcc/testsuite/g++.dg/template/crash95.C
index f60e635ae66..47346111328 100644
--- gcc/testsuite/g++.dg/template/crash95.C
+++ gcc/testsuite/g++.dg/template/crash95.C
@@ -8,4 +8,4 @@ template < typename > struct S
   };
 };
 
-S < int > s(0); // { dg-error "no matching" }
+S < int > s(0); // { dg-error "no matching|too many initializers" }
diff --git gcc/testsuite/g++.old-deja/g++.jason/crash3.C gcc/testsuite/g++.old-deja/g++.jason/crash3.C
index e94cc7c9781..c4950531328 100644
--- gcc/testsuite/g++.old-deja/g++.jason/crash3.C
+++ gcc/testsuite/g++.old-deja/g++.jason/crash3.C
@@ -2,12 +2,13 @@
 // Bug: g++ tries to generate initialization semantics for a Node from an int,
 // and fails.
 
-struct Node			// { dg-message "note" }
+struct Node			// { dg-message "note" "" { target c++17_down } }
 {
   Node* child[2];
 };
 
 void bug(int i)
 {
-  Node* q = new Node(i);	// { dg-error "no matching" } 
+  Node* q = new Node(i);	// { dg-error "no matching" "" { target c++17_down }  } 
+// { dg-error "array must be initialized" "" { target c++2a } .-1 }
 }
diff --git gcc/testsuite/g++.old-deja/g++.law/ctors11.C gcc/testsuite/g++.old-deja/g++.law/ctors11.C
index 39ee76b0ae7..b29b18a62a3 100644
--- gcc/testsuite/g++.old-deja/g++.law/ctors11.C
+++ gcc/testsuite/g++.old-deja/g++.law/ctors11.C
@@ -10,12 +10,12 @@ public:
   inline A(int x){printf("constructing A with %d\n", x);}
 };
 
-class B:public A{ // { dg-message "note" } non-default constructor
+class B:public A{ // { dg-message "note" "" { target c++17_down } } non-default constructor
 private:
 public:
 };
 
 int main()
 {
-  B(10);// { dg-error "match" } B doesn't have a constructor taking int
+  B(10);// { dg-error "match" "" { target c++17_down } } B doesn't have a constructor taking int
 }
diff --git gcc/testsuite/g++.old-deja/g++.law/ctors9.C gcc/testsuite/g++.old-deja/g++.law/ctors9.C
index 43ba1262c95..5856d5dc883 100644
--- gcc/testsuite/g++.old-deja/g++.law/ctors9.C
+++ gcc/testsuite/g++.old-deja/g++.law/ctors9.C
@@ -20,7 +20,7 @@ Foo::Foo(int aa)
 { }
 
 
-struct var_Foo: public Foo // { dg-message "note" }  base.*// ERROR -  in class.*
+struct var_Foo: public Foo // { dg-message "note" "" { target c++17_down } }  base.*// ERROR -  in class.*
 {
   var_Foo* operator-> () {return this;}
 };
@@ -32,7 +32,7 @@ int blort(Foo& f)
 
 int main()
 {
-  var_Foo b(2);// { dg-error "match" } 
+  var_Foo b(2);// { dg-error "match" "" { target c++17_down } } 
   b->a = 0;
   int x = blort(b);
   return x;
diff --git gcc/testsuite/g++.old-deja/g++.mike/net22.C gcc/testsuite/g++.old-deja/g++.mike/net22.C
index e5e1cb1081d..abbf6191023 100644
--- gcc/testsuite/g++.old-deja/g++.mike/net22.C
+++ gcc/testsuite/g++.old-deja/g++.mike/net22.C
@@ -5,10 +5,11 @@ public:
   Parent( char *s ) {}
 };
 
-class Child : public Parent {		// { dg-message "note" } called
+class Child : public Parent {		// { dg-message "note" "" { target c++17_down } } called
 };
 
 int main() {
-  Child c( "String initializer" );	// { dg-error "match" } bad
+  Child c( "String initializer" );	// { dg-error "match" "" { target c++17_down } } bad
+// { dg-error "forbids converting a string constant" "" { target c++2a } .-1 }
   return 0;
 }
diff --git gcc/testsuite/g++.old-deja/g++.niklas/t128.C gcc/testsuite/g++.old-deja/g++.niklas/t128.C
index 19e3ca1dab0..0b3346c4ef3 100644
--- gcc/testsuite/g++.old-deja/g++.niklas/t128.C
+++ gcc/testsuite/g++.old-deja/g++.niklas/t128.C
@@ -1,5 +1,5 @@
 // { dg-do assemble  }
 // GROUPS niklas uncaught default-construct
 struct A { A (int); };
-struct B : A {}; // { dg-message "note" } without ctor // ERROR - candidates
-void f () { B (0); }// { dg-error "match" } .*
+struct B : A {}; // { dg-message "note" "" { target c++17_down } } without ctor // ERROR - candidates
+void f () { B (0); }// { dg-error "match" "" { target c++17_down } } .*
Jason Merrill Dec. 3, 2019, 7:01 a.m. | #13
On 12/2/19 7:31 PM, Marek Polacek wrote:
> @@ -1967,8 +1978,23 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,

>         tree ctor_name = (true_exp == exp

>   			? complete_ctor_identifier : base_ctor_identifier);

>   

> +      /* Given class A,

> +

> +	   A a(1, 2);

> +

> +	 can mean a call to a constructor A::A(int, int), if present.  If not,

> +	 but A is an aggregate, we will try aggregate initialization.  */

>         rval = build_special_member_call (exp, ctor_name, &parms, binfo, flags,

>   					complain);

> +      if (BRACE_ENCLOSED_INITIALIZER_P (rval))

> +	{

> +	  gcc_assert (cxx_dialect >= cxx2a);

> +	  rval = digest_init (type, rval, tf_warning_or_error);

> +	  rval = build2 (INIT_EXPR, type, exp, rval);

> +	  /* So that we do finish_expr_stmt below.  Don't return here, we

> +	     need to release PARMS.  */

> +	  TREE_SIDE_EFFECTS (rval) = 1;

> +	}


Can we still get a CONSTRUCTOR here after the change to 
build_new_method_call_1?

Jason
Marek Polacek Dec. 3, 2019, 2:38 p.m. | #14
On Tue, Dec 03, 2019 at 02:01:24AM -0500, Jason Merrill wrote:
> On 12/2/19 7:31 PM, Marek Polacek wrote:

> > @@ -1967,8 +1978,23 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,

> >         tree ctor_name = (true_exp == exp

> >   			? complete_ctor_identifier : base_ctor_identifier);

> > +      /* Given class A,

> > +

> > +	   A a(1, 2);

> > +

> > +	 can mean a call to a constructor A::A(int, int), if present.  If not,

> > +	 but A is an aggregate, we will try aggregate initialization.  */

> >         rval = build_special_member_call (exp, ctor_name, &parms, binfo, flags,

> >   					complain);

> > +      if (BRACE_ENCLOSED_INITIALIZER_P (rval))

> > +	{

> > +	  gcc_assert (cxx_dialect >= cxx2a);

> > +	  rval = digest_init (type, rval, tf_warning_or_error);

> > +	  rval = build2 (INIT_EXPR, type, exp, rval);

> > +	  /* So that we do finish_expr_stmt below.  Don't return here, we

> > +	     need to release PARMS.  */

> > +	  TREE_SIDE_EFFECTS (rval) = 1;

> > +	}

> 

> Can we still get a CONSTRUCTOR here after the change to

> build_new_method_call_1?


Oop, not anymore.  Dropped the whole cp/init.c hunk.

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

2019-12-03  Marek Polacek  <polacek@redhat.com>

	PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
	* c-cppbuiltin.c (c_cpp_builtins): Predefine
	__cpp_aggregate_paren_init=201902 for -std=c++2a.

	* call.c (build_new_method_call_1): Handle parenthesized initialization
	of aggregates by building up a CONSTRUCTOR.
	(extend_ref_init_temps): Do nothing for CONSTRUCTOR_IS_PAREN_INIT.
	* cp-tree.h (CONSTRUCTOR_IS_PAREN_INIT, LOOKUP_AGGREGATE_PAREN_INIT):
	Define.
	* decl.c (grok_reference_init): Handle aggregate initialization from
	a parenthesized list of values.
	(reshape_init): Do nothing for CONSTRUCTOR_IS_PAREN_INIT.
	(check_initializer): Handle initialization of an array from a
	parenthesized list of values.  Use NULL_TREE instead of NULL.
	* tree.c (build_cplus_new): Handle BRACE_ENCLOSED_INITIALIZER_P.
	* typeck2.c (digest_init_r): Set LOOKUP_AGGREGATE_PAREN_INIT if it
	receives a CONSTRUCTOR with CONSTRUCTOR_IS_PAREN_INIT set.  Allow
	narrowing when LOOKUP_AGGREGATE_PAREN_INIT.
	(massage_init_elt): Don't lose LOOKUP_AGGREGATE_PAREN_INIT when passing
	flags to digest_init_r.

	* g++.dg/cpp0x/constexpr-99.C: Only expect an error in C++17 and
	lesser.
	* g++.dg/cpp0x/explicit7.C: Likewise.
	* g++.dg/cpp0x/initlist12.C: Adjust dg-error.
	* g++.dg/cpp0x/pr31437.C: Likewise.
	* g++.dg/cpp2a/feat-cxx2a.C: Add __cpp_aggregate_paren_init test.
	* g++.dg/cpp2a/paren-init1.C: New test.
	* g++.dg/cpp2a/paren-init10.C: New test.
	* g++.dg/cpp2a/paren-init11.C: New test.
	* g++.dg/cpp2a/paren-init12.C: New test.
	* g++.dg/cpp2a/paren-init13.C: New test.
	* g++.dg/cpp2a/paren-init14.C: New test.
	* g++.dg/cpp2a/paren-init15.C: New test.
	* g++.dg/cpp2a/paren-init16.C: New test.
	* g++.dg/cpp2a/paren-init17.C: New test.
	* g++.dg/cpp2a/paren-init18.C: New test.
	* g++.dg/cpp2a/paren-init19.C: New test.
	* g++.dg/cpp2a/paren-init2.C: New test.
	* g++.dg/cpp2a/paren-init3.C: New test.
	* g++.dg/cpp2a/paren-init4.C: New test.
	* g++.dg/cpp2a/paren-init5.C: New test.
	* g++.dg/cpp2a/paren-init6.C: New test.
	* g++.dg/cpp2a/paren-init7.C: New test.
	* g++.dg/cpp2a/paren-init8.C: New test.
	* g++.dg/cpp2a/paren-init9.C: New test.
	* g++.dg/ext/desig10.C: Adjust dg-error.
	* g++.dg/template/crash107.C: Likewise.
	* g++.dg/template/crash95.C: Likewise.
	* g++.old-deja/g++.jason/crash3.C: Likewise.
	* g++.old-deja/g++.law/ctors11.C: Likewise.
	* g++.old-deja/g++.law/ctors9.C: Likewise.
	* g++.old-deja/g++.mike/net22.C: Likewise.
	* g++.old-deja/g++.niklas/t128.C: Likewise.

diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c
index 6491545bc3b..c7f4659456a 100644
--- gcc/c-family/c-cppbuiltin.c
+++ gcc/c-family/c-cppbuiltin.c
@@ -1006,6 +1006,7 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_impl_destroying_delete=201806L");
 	  cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907L");
 	  cpp_define (pfile, "__cpp_impl_three_way_comparison=201907L");
+	  cpp_define (pfile, "__cpp_aggregate_paren_init=201902L");
 	}
       if (flag_concepts)
         {
diff --git gcc/cp/call.c gcc/cp/call.c
index 062cff4c735..ea0e8b7e7d7 100644
--- gcc/cp/call.c
+++ gcc/cp/call.c
@@ -10124,6 +10124,38 @@ build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args,
 
   if (!any_viable_p)
     {
+      /* [dcl.init], 17.6.2.2:
+
+	 Otherwise, if no constructor is viable, the destination type is
+	 a (possibly cv-qualified) aggregate class A, and the initializer
+	 is a parenthesized expression-list, the object is initialized as
+	 follows...
+
+	 We achieve this by building up a CONSTRUCTOR, as for list-init,
+	 and setting CONSTRUCTOR_IS_PAREN_INIT to distinguish between
+	 the two.  */
+      if (DECL_CONSTRUCTOR_P (fn)
+	  && !(flags & LOOKUP_ONLYCONVERTING)
+	  && !cp_unevaluated_operand
+	  && cxx_dialect >= cxx2a
+	  && CP_AGGREGATE_TYPE_P (basetype)
+	  && !user_args->is_empty ())
+	{
+	  /* Create a CONSTRUCTOR from ARGS, e.g. {1, 2} from <1, 2>.  */
+	  tree list = build_tree_list_vec (user_args);
+	  tree ctor = build_constructor_from_list (init_list_type_node, list);
+	  CONSTRUCTOR_IS_DIRECT_INIT (ctor) = true;
+	  CONSTRUCTOR_IS_PAREN_INIT (ctor) = true;
+	  if (is_dummy_object (instance))
+	    return ctor;
+	  else
+	    {
+	      ctor = digest_init (basetype, ctor, complain);
+	      ctor = build2 (INIT_EXPR, TREE_TYPE (instance), instance, ctor);
+	      TREE_SIDE_EFFECTS (ctor) = true;
+	      return ctor;
+	    }
+	}
       if (complain & tf_error)
 	complain_about_no_candidates_for_method_call (instance, candidates,
 						      explicit_targs, basetype,
@@ -11789,9 +11821,16 @@ perform_direct_initialization_if_possible (tree type,
      If the destination type is a (possibly cv-qualified) class type:
 
      -- If the initialization is direct-initialization ...,
-     constructors are considered. ... If no constructor applies, or
-     the overload resolution is ambiguous, the initialization is
-     ill-formed.  */
+     constructors are considered.
+
+       -- If overload resolution is successful, the selected constructor
+       is called to initialize the object, with the initializer expression
+       or expression-list as its argument(s).
+
+       -- Otherwise, if no constructor is viable, the destination type is
+       a (possibly cv-qualified) aggregate class A, and the initializer is
+       a parenthesized expression-list, the object is initialized as
+       follows...  */
   if (CLASS_TYPE_P (type))
     {
       releasing_vec args (make_tree_vector_single (expr));
@@ -12147,6 +12186,12 @@ extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups)
 	ctor = TARGET_EXPR_INITIAL (ctor);
       if (TREE_CODE (ctor) == CONSTRUCTOR)
 	{
+	  /* [dcl.init] When initializing an aggregate from a parenthesized list
+	     of values... a temporary object bound to a reference does not have
+	     its lifetime extended.  */
+	  if (CONSTRUCTOR_IS_PAREN_INIT (ctor))
+	    return init;
+
 	  if (is_std_init_list (type))
 	    {
 	      /* The temporary array underlying a std::initializer_list
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index 7e810b8ee7b..4af18b0ae62 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -4325,6 +4325,11 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define CONSTRUCTOR_IS_DESIGNATED_INIT(NODE) \
   (TREE_LANG_FLAG_6 (CONSTRUCTOR_CHECK (NODE)))
 
+/* True if this CONSTRUCTOR comes from a parenthesized list of values, e.g.
+   A(1, 2, 3).  */
+#define CONSTRUCTOR_IS_PAREN_INIT(NODE) \
+  (CONSTRUCTOR_CHECK(NODE)->base.private_flag)
+
 /* True if NODE represents a conversion for direct-initialization in a
    template.  Set by perform_implicit_conversion_flags.  */
 #define IMPLICIT_CONV_EXPR_DIRECT_INIT(NODE) \
@@ -5583,6 +5588,8 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
    args), then we swap the conversions back in build_new_op_1 (so they
    correspond to the order of the args in the candidate).  */
 #define LOOKUP_REVERSED (LOOKUP_REWRITTEN << 1)
+/* We're initializing an aggregate from a parenthesized list of values.  */
+#define LOOKUP_AGGREGATE_PAREN_INIT (LOOKUP_REVERSED << 1)
 
 #define LOOKUP_NAMESPACES_ONLY(F)  \
   (((F) & LOOKUP_PREFER_NAMESPACES) && !((F) & LOOKUP_PREFER_TYPES))
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 81d73433547..54f09507516 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -5531,11 +5531,28 @@ grok_reference_init (tree decl, tree type, tree init, int flags)
       return NULL_TREE;
     }
 
+  tree ttype = TREE_TYPE (type);
   if (TREE_CODE (init) == TREE_LIST)
-    init = build_x_compound_expr_from_list (init, ELK_INIT,
-					    tf_warning_or_error);
+    {
+      /* This handles (C++20 only) code like
+
+	   const A& r(1, 2, 3);
+
+	 where we treat the parenthesized list as a CONSTRUCTOR.  */
+      if (TREE_TYPE (init) == NULL_TREE
+	  && CP_AGGREGATE_TYPE_P (ttype)
+	  && !DECL_DECOMPOSITION_P (decl)
+	  && (cxx_dialect >= cxx2a))
+	{
+	  init = build_constructor_from_list (init_list_type_node, init);
+	  CONSTRUCTOR_IS_DIRECT_INIT (init) = true;
+	  CONSTRUCTOR_IS_PAREN_INIT (init) = true;
+	}
+      else
+	init = build_x_compound_expr_from_list (init, ELK_INIT,
+						tf_warning_or_error);
+    }
 
-  tree ttype = TREE_TYPE (type);
   if (TREE_CODE (ttype) != ARRAY_TYPE
       && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE)
     /* Note: default conversion is only called in very special cases.  */
@@ -6437,6 +6454,11 @@ reshape_init (tree type, tree init, tsubst_flags_t complain)
   if (vec_safe_is_empty (v))
     return init;
 
+  /* Brace elision is not performed for a CONSTRUCTOR representing
+     parenthesized aggregate initialization.  */
+  if (CONSTRUCTOR_IS_PAREN_INIT (init))
+    return init;
+
   /* Handle [dcl.init.list] direct-list-initialization from
      single element of enumeration with a fixed underlying type.  */
   if (is_direct_enum_init (type, init))
@@ -6640,6 +6662,41 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
 	      flags |= LOOKUP_NO_NARROWING;
 	    }
 	}
+      /* [dcl.init] "Otherwise, if the destination type is an array, the object
+	 is initialized as follows..."  So handle things like
+
+	  int a[](1, 2, 3);
+
+	 which is permitted in C++20 by P0960.  */
+      else if (TREE_CODE (init) == TREE_LIST
+	       && TREE_TYPE (init) == NULL_TREE
+	       && TREE_CODE (type) == ARRAY_TYPE
+	       && !DECL_DECOMPOSITION_P (decl)
+	       && (cxx_dialect >= cxx2a))
+	{
+	  /* [dcl.init.string] "An array of ordinary character type [...]
+	     can be initialized by an ordinary string literal [...] by an
+	     appropriately-typed string literal enclosed in braces" only
+	     talks about braces, but GCC has always accepted
+
+	       char a[]("foobar");
+
+	     so we continue to do so.  */
+	  tree val = TREE_VALUE (init);
+	  if (TREE_CHAIN (init) == NULL_TREE
+	      && char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type)))
+	      && TREE_CODE (tree_strip_any_location_wrapper (val))
+		 == STRING_CST)
+	    /* If the list has a single element and it's a string literal,
+	       then it's the initializer for the array as a whole.  */
+	    init = val;
+	  else
+	    {
+	      init = build_constructor_from_list (init_list_type_node, init);
+	      CONSTRUCTOR_IS_DIRECT_INIT (init) = true;
+	      CONSTRUCTOR_IS_PAREN_INIT (init) = true;
+	    }
+	}
       else if (TREE_CODE (init) == TREE_LIST
 	       && TREE_TYPE (init) != unknown_type_node
 	       && !MAYBE_CLASS_TYPE_P (type))
@@ -6683,6 +6740,9 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
 	    init_code = TREE_OPERAND (init_code, 0);
 	  if (TREE_CODE (init_code) == INIT_EXPR)
 	    {
+	      /* In C++20, the call to build_aggr_init could have created
+		 an INIT_EXPR with a CONSTRUCTOR as the RHS to handle
+		 A(1, 2).  */
 	      init = TREE_OPERAND (init_code, 1);
 	      init_code = NULL_TREE;
 	      /* Don't call digest_init; it's unnecessary and will complain
@@ -6736,7 +6796,7 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
 			0, "array %qD initialized by parenthesized "
 			"string literal %qE",
 			decl, DECL_INITIAL (decl));
-	  init = NULL;
+	  init = NULL_TREE;
 	}
     }
   else
diff --git gcc/cp/tree.c gcc/cp/tree.c
index 8817860b4d3..8b625e8b18e 100644
--- gcc/cp/tree.c
+++ gcc/cp/tree.c
@@ -669,6 +669,15 @@ build_aggr_init_expr (tree type, tree init)
 tree
 build_cplus_new (tree type, tree init, tsubst_flags_t complain)
 {
+  /* This function should cope with what build_special_member_call
+     can produce.  When performing parenthesized aggregate initialization,
+     it can produce a { }.  */
+  if (BRACE_ENCLOSED_INITIALIZER_P (init))
+    {
+      gcc_assert (cxx_dialect >= cxx2a);
+      return finish_compound_literal (type, init, complain);
+    }
+
   tree rval = build_aggr_init_expr (type, init);
   tree slot;
 
diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
index ae00de2468d..7fda6266a35 100644
--- gcc/cp/typeck2.c
+++ gcc/cp/typeck2.c
@@ -1117,6 +1117,10 @@ digest_init_r (tree type, tree init, int nested, int flags,
 
   tree stripped_init = init;
 
+  if (BRACE_ENCLOSED_INITIALIZER_P (init)
+      && CONSTRUCTOR_IS_PAREN_INIT (init))
+    flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+
   /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue
      (g++.old-deja/g++.law/casts2.C).  */
   if (TREE_CODE (init) == NON_LVALUE_EXPR)
@@ -1224,7 +1228,9 @@ digest_init_r (tree type, tree init, int nested, int flags,
   if ((code != COMPLEX_TYPE || BRACE_ENCLOSED_INITIALIZER_P (stripped_init))
       && (SCALAR_TYPE_P (type) || code == REFERENCE_TYPE))
     {
-      if (nested)
+      /* Narrowing is OK when initializing an aggregate from
+	 a parenthesized list.  */
+      if (nested && !(flags & LOOKUP_AGGREGATE_PAREN_INIT))
 	flags |= LOOKUP_NO_NARROWING;
       init = convert_for_initialization (0, type, init, flags,
 					 ICR_INIT, NULL_TREE, 0,
@@ -1386,9 +1392,12 @@ static tree
 massage_init_elt (tree type, tree init, int nested, int flags,
 		  tsubst_flags_t complain)
 {
-  flags &= LOOKUP_ALLOW_FLEXARRAY_INIT;
-  flags |= LOOKUP_IMPLICIT;
-  init = digest_init_r (type, init, nested ? 2 : 1, flags, complain);
+  int new_flags = LOOKUP_IMPLICIT;
+  if (flags & LOOKUP_ALLOW_FLEXARRAY_INIT)
+    new_flags |= LOOKUP_ALLOW_FLEXARRAY_INIT;
+  if (flags & LOOKUP_AGGREGATE_PAREN_INIT)
+    new_flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+  init = digest_init_r (type, init, nested ? 2 : 1, new_flags, complain);
   /* Strip a simple TARGET_EXPR when we know this is an initializer.  */
   if (SIMPLE_TARGET_EXPR_P (init))
     init = TARGET_EXPR_INITIAL (init);
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-99.C gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
index 8d791dd032d..4d3953d0983 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
@@ -9,5 +9,6 @@ struct A
 struct B
 {
   A a;
-    constexpr B() : a(0) {} // { dg-error "no matching function" }
+  // P0960R3 allows paren-init.
+  constexpr B() : a(0) {} // { dg-error "no matching function" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/explicit7.C gcc/testsuite/g++.dg/cpp0x/explicit7.C
index 574796d117d..67b50542bc3 100644
--- gcc/testsuite/g++.dg/cpp0x/explicit7.C
+++ gcc/testsuite/g++.dg/cpp0x/explicit7.C
@@ -10,7 +10,7 @@ struct A { };
 struct B: A { };
 struct C {
   explicit operator B*();	// { dg-message "explicit" }
-  explicit operator B&();	// { dg-message "explicit" }
+  explicit operator B&();	// { dg-message "explicit" "" { target c++17_down } }
 };
 
 C c;
diff --git gcc/testsuite/g++.dg/cpp0x/initlist12.C gcc/testsuite/g++.dg/cpp0x/initlist12.C
index f72a6dac525..c2a2b86520f 100644
--- gcc/testsuite/g++.dg/cpp0x/initlist12.C
+++ gcc/testsuite/g++.dg/cpp0x/initlist12.C
@@ -6,15 +6,15 @@ struct A
   int i;
 };
 
-A a({1,2});			// { dg-error "no match" }
+A a({1,2});			// { dg-error "no match|cannot convert" }
 
 union U
 {
   int i,j;
 };
 
-U u({1,2});			// { dg-error "no match" }
+U u({1,2});			// { dg-error "no match|cannot convert" }
 
 union V {};
 
-V v({1});			// { dg-error "no match" }
+V v({1});			// { dg-error "no match|too many initializers" }
diff --git gcc/testsuite/g++.dg/cpp0x/pr31437.C gcc/testsuite/g++.dg/cpp0x/pr31437.C
index 7e2ed7a1375..532b533c8d3 100644
--- gcc/testsuite/g++.dg/cpp0x/pr31437.C
+++ gcc/testsuite/g++.dg/cpp0x/pr31437.C
@@ -1,9 +1,9 @@
 // { dg-do compile { target c++11 } }
-template <typename... T> struct A // { dg-message "candidates|A" }
+template <typename... T> struct A // { dg-message "candidates|A" "" { target c++17_down } }
 {
   A(T* p) {  // { dg-error "parameter packs|T" }
    (A<T...>*)(p); 
   }
 };
 
-A<int> a(0); // { dg-error "no matching" }
+A<int> a(0); // { dg-error "no matching|too many initializers" }
diff --git gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
index 9b6e2f59d2c..389b25e16ea 100644
--- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
+++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
@@ -460,6 +460,12 @@
 #  error "__cpp_constexpr_dynamic_alloc != 201907"
 #endif
 
+#ifndef __cpp_aggregate_paren_init
+#  error "__cpp_aggregate_paren_init"
+#elif __cpp_aggregate_paren_init != 201902
+#  error "__cpp_aggregate_paren_init != 201902"
+#endif
+
 #ifdef __has_cpp_attribute
 
 #  if ! __has_cpp_attribute(maybe_unused)
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init1.C gcc/testsuite/g++.dg/cpp2a/paren-init1.C
new file mode 100644
index 00000000000..a54b2ccaf6d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init1.C
@@ -0,0 +1,116 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+struct A {
+  int i;
+  int j;
+  int k;
+};
+
+struct B {
+  int i;
+  int j;
+  int k = 42;
+};
+
+struct C {
+  A a;
+};
+
+struct D {
+  A a1;
+  A a2;
+};
+
+struct E {
+  int i;
+};
+
+// F has a base class, but it's not virtual, private, or protected, so this is
+// still an aggregate (since C++17).
+struct F : E {
+  int j;
+  int k;
+};
+
+F f({1}, 2, 3);
+
+// A non-virtual member function doesn't make it a non-aggregate.
+struct G {
+  int i;
+  double j;
+  int foo(int, int);
+};
+
+G g(1, 2.14);
+
+class H {
+public:
+  H (int) { }
+};
+
+class I : public H { };
+
+int i;
+A a1(1, 2);
+A a2(1.0, 2);
+A a3(++i, ++i);
+const A& ra(1, 2);
+
+A ca = A(1, 2);
+A ca2 = A(1.0, 2);
+A ca3 = A(++i, ++i);
+const A& rca = A(1, 2);
+
+B b1(1, 2, 3);
+B b2(1, 2);
+B b3(1);
+
+C c1({5, 6, 7});
+D d1({1, 2, 3}, {5, 6, 7});
+
+struct W {
+  const char *s, *t;
+};
+W w1("fluffer", "nutter");
+
+struct Y {
+  char a[4];
+};
+Y y("yew");
+
+int
+main ()
+{
+  I(10);
+
+  // A::k will be value-initialized.
+  if (a1.i != 1 || a1.j != 2 || a1.k != 0)
+    __builtin_abort ();
+  if (a2.i != 1 || a2.j != 2 || a2.k != 0)
+    __builtin_abort ();
+  if (a3.i != 1 || a3.j != 2 || a3.k != 0)
+    __builtin_abort ();
+  if (ra.i != 1 || ra.j != 2 || ra.k != 0)
+    __builtin_abort ();
+  if (ca.i != 1 || ca.j != 2 || ca.k != 0)
+    __builtin_abort ();
+  if (ca2.i != 1 || ca2.j != 2 || ca2.k != 0)
+    __builtin_abort ();
+  if (ca3.i != 3 || ca3.j != 4 || ca3.k != 0)
+    __builtin_abort ();
+
+  if (b1.i != 1 || b1.j != 2 || b1.k != 3)
+    __builtin_abort ();
+  // The default member initializer will be used for B::k.
+  if (b2.i != 1 || b2.j != 2 || b2.k != 42)
+    __builtin_abort ();
+  if (b3.i != 1 || b3.j != 0 || b3.k != 42)
+    __builtin_abort ();
+
+  if (c1.a.i != 5 || c1.a.j != 6 || c1.a.k != 7)
+    __builtin_abort ();
+
+  if (f.i != 1 || f.j != 2 || f.k != 3)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init10.C gcc/testsuite/g++.dg/cpp2a/paren-init10.C
new file mode 100644
index 00000000000..5c70d9d59ee
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init10.C
@@ -0,0 +1,18 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test from [dcl.init].
+
+struct A {
+  int a;
+  int&& r;
+};
+
+int f();
+int n = 10;
+
+A a1{1, f()};               // OK, lifetime is extended
+A a2(1, f());               // well-formed, but dangling reference
+A a3{1.0, 1};               // { dg-error "narrowing conversion" }
+A a4(1.0, 1);               // well-formed, but dangling reference
+A a5(1.0, static_cast<int&&>(n));    // OK
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init11.C gcc/testsuite/g++.dg/cpp2a/paren-init11.C
new file mode 100644
index 00000000000..82ca2669545
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init11.C
@@ -0,0 +1,88 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test ill-formed code.
+
+// If k is greater than the size of the array, the program is ill-formed.
+int a1[2](1, 2, 3); // { dg-error "too many initializers" }
+int a2[](); // { dg-error "array of functions" }
+int a3[](()); // { dg-error "expected primary-expression" }
+int a4[]("raccoon"); // { dg-error "invalid conversion" }
+
+struct S {
+  int i;
+  int j;
+};
+
+S s1(1, 1, 1); // { dg-error "too many initializers" }
+
+union U2 {
+  int a;
+  float b;
+};
+
+// [dcl.init.aggr]/19:
+// When a union is initialized with an initializer list, there shall not be
+// more than one explicitly initialized element.
+U2 u4 = U2(1, 2); // { dg-error "too many initializers" }
+
+// Test there is no brace elision.
+
+int a[2][2](1, 2, 3, 4); // { dg-error "too many initializers|array must be initialized with a brace-enclosed initializer" }
+
+// private/protected/virtual base class -> not an aggregate.
+struct B { };
+struct D : private B {
+  int i;
+  int j;
+};
+
+D d({}, 1, 2); // { dg-error "no matching function" }
+
+// Private non-static data member -> not an aggregate.
+struct P {
+  int i;
+private:
+  int j;
+};
+
+P p(1, 2); // { dg-error "no matching function" }
+
+// User-declared constructor -> not an aggregate.
+struct U {
+  U() {}
+  int i;
+  int j;  
+};
+
+U u(1, 2); // { dg-error "no matching function" }
+
+// virtual member function -> not an aggregate.
+struct V {
+  int i;
+  int j;
+  virtual int foo(int);
+};
+
+V v(1, 2); // { dg-error "no matching function" }
+
+struct nonaggr {
+  int i;
+  int j;
+private:
+  int x;
+};
+
+struct F {
+  nonaggr n;
+  F() : n(1) { } // { dg-error "no matching function" }
+};
+
+struct G {
+  char a[4];
+};
+
+struct H {
+  G g;
+  H() : g("oaks") { } // { dg-error "initializer-string" }
+};
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init12.C gcc/testsuite/g++.dg/cpp2a/paren-init12.C
new file mode 100644
index 00000000000..d7be6f2b55d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init12.C
@@ -0,0 +1,17 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct A;
+
+struct C {
+  operator A();
+};
+
+struct A {
+  C c;
+};
+
+C c;
+A a(c);  // invokes C’s conversion function to A
+
+// { dg-final { scan-assembler "_ZN1Ccv1AEv" } }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init13.C gcc/testsuite/g++.dg/cpp2a/paren-init13.C
new file mode 100644
index 00000000000..4b9107c70ff
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init13.C
@@ -0,0 +1,16 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct X { int a, b; };
+struct Y { X x; };
+
+void
+f()
+{
+  // This is ok...
+  Y y1{{1, 2}};
+  Y y2({1, 2});
+  // ...but Y y((1,2)) is not the same as Y y{{1,2}}.  (1, 2) is a
+  // COMPOUND_EXPR.
+  Y y3((1, 2)); // { dg-error "could not convert" }
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init14.C gcc/testsuite/g++.dg/cpp2a/paren-init14.C
new file mode 100644
index 00000000000..837f9531260
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init14.C
@@ -0,0 +1,10 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++11 } }
+
+struct A {
+  int x;
+  int y;
+  A(int, int) = delete;
+};
+
+A a(1, 2); // { dg-error "use of deleted function" }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init15.C gcc/testsuite/g++.dg/cpp2a/paren-init15.C
new file mode 100644
index 00000000000..4311dd4df59
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init15.C
@@ -0,0 +1,35 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct A {
+  int i;
+  int j;
+};
+
+struct B : A
+{
+  B (): A(1.7, 2) { }
+};
+
+void f(A);
+
+void
+g ()
+{
+  f (A(1, 2));
+}
+
+struct S {
+  int a[2];
+};
+
+S h() { return S({1, 2}); }
+
+struct Z {
+  int i;
+  int j;
+  operator A();
+};
+
+Z z;
+A a = Z(1, 2.3);
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init16.C gcc/testsuite/g++.dg/cpp2a/paren-init16.C
new file mode 100644
index 00000000000..c8ed924877e
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init16.C
@@ -0,0 +1,14 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct S { int a[2]; };
+struct A { S s[1]; };
+
+template <typename, int N>
+struct R { static constexpr auto h = A({S{N}}); };
+
+template <typename, int N>
+struct R2 { static constexpr auto h = A({S({N, N})}); };
+
+A foo = R<int, 10>::h;
+A foo2 = R2<int, 10>::h;
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init17.C gcc/testsuite/g++.dg/cpp2a/paren-init17.C
new file mode 100644
index 00000000000..8e08b5288a5
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init17.C
@@ -0,0 +1,6 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++11 } }
+
+struct A { };
+struct B { };
+static_assert (!__is_trivially_constructible(A, B), "");
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init18.C gcc/testsuite/g++.dg/cpp2a/paren-init18.C
new file mode 100644
index 00000000000..0aea493f214
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init18.C
@@ -0,0 +1,9 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct A { int a, b, c; };
+struct S { A a; };
+constexpr S s{ A(1, 2, 3) };
+static_assert (s.a.a == 1 && s.a.b == 2 && s.a.c == 3);
+constexpr S s2 = { A(1, 2, 3) };
+static_assert (s2.a.a == 1 && s2.a.b == 2 && s2.a.c == 3);
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init19.C gcc/testsuite/g++.dg/cpp2a/paren-init19.C
new file mode 100644
index 00000000000..73643fa3aa2
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init19.C
@@ -0,0 +1,12 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct A {
+  int i[2];
+};
+
+A a({1});
+A a2({1, 2});
+A a3(1); // { dg-error "array must be initialized with a brace-enclosed initializer" }
+A a4 = A(1); // { dg-error "array must be initialized with a brace-enclosed initializer" }
+A a5 = A({1});
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init2.C gcc/testsuite/g++.dg/cpp2a/paren-init2.C
new file mode 100644
index 00000000000..e9e90d7acb6
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init2.C
@@ -0,0 +1,56 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+struct A {
+  int i = 0;
+  int j = 0;
+};
+
+struct B {
+  A a;
+  constexpr B() : a(1.1, 2) { }
+};
+
+struct C {
+  int i;
+};
+
+struct E {
+  C c;
+  E() : c(1.2) { }
+};
+
+struct F {
+  char a[4];
+};
+
+struct G {
+  F f;
+  G() : f("yew") { }
+};
+
+struct H {
+  int i;
+  int &&r;
+};
+
+int f() { return 42; }
+
+struct I {
+  H h;
+  I() : h(1, f()) { }
+};
+
+I i;  // dangling ref to f():
+      // {.i=1, .r=(int &) &TARGET_EXPR <D.2118, f ()>}
+
+int
+main ()
+{
+  B b;
+  if (b.a.i != 1 || b.a.j != 2)
+    __builtin_abort ();
+  E e;
+  if (e.c.i != 1)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init3.C gcc/testsuite/g++.dg/cpp2a/paren-init3.C
new file mode 100644
index 00000000000..f444005a09f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init3.C
@@ -0,0 +1,11 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+class a {
+  int b{};
+};
+class c {
+  c();
+  a d;
+};
+c::c() {}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init4.C gcc/testsuite/g++.dg/cpp2a/paren-init4.C
new file mode 100644
index 00000000000..f8c7bd10b63
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init4.C
@@ -0,0 +1,142 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+// Test T[]().
+
+int i;
+int a1[](1, 2, 3);
+static_assert(sizeof(a1) == 3 * sizeof (int), "");
+int a2[](1.0, 2, 3);
+static_assert(sizeof(a2) == 3 * sizeof (int), "");
+int a3[3](1, 2, 3);
+int a4[3](1, 2); // a4[2] is value-initialized.
+int a5[](++i, ++i);
+static_assert(sizeof(a5) == 2 * sizeof (int), "");
+int a6[](1);
+static_assert(sizeof(a6) == sizeof (int), "");
+int a7[]({});
+static_assert(sizeof(a7) == sizeof (int), "");
+int a8[]({}, {}, {}, {}, {}, {});
+static_assert(sizeof(a8) == 6 * sizeof (int), "");
+int a9[]((1));
+static_assert(sizeof(a9) == sizeof (int), "");
+int a10[]((1), (2), (3));
+static_assert(sizeof(a10) == 3 * sizeof (int), "");
+int a11[][2]{1};
+static_assert(sizeof(a11) == 2 * sizeof (int), "");
+int a12[][2]({1, 2}, {3, 4});
+static_assert(sizeof(a12) == 4 * sizeof (int), "");
+
+const int (&ra1)[](1, 2);
+const int (&ra2)[](1.0, 2);
+const int (&ra3)[2](1.0, 2);
+int (&&rra1)[](1, 2);
+int (&&rra2)[](1.0, 2);
+int (&&rra3)[2](1.0, 2);
+
+struct S { int i; } s;
+S s1[]({1});
+static_assert(sizeof(s1) == sizeof (S), "");
+S s2[]({1}, {2});
+static_assert(sizeof(s2) == 2 * sizeof (S), "");
+S s3[]({1}, {2}, {3});
+static_assert(sizeof(s3) == 3 * sizeof (S), "");
+S s4[3]({1}, {2});
+static_assert(sizeof(s4) == 3 * sizeof (S), "");
+S s5[]({++i}, {++i});
+static_assert(sizeof(s2) == 2 * sizeof (S), "");
+S s6[](s, s);
+
+struct R { int i; int j; };
+R r1[]({1, 2});
+static_assert(sizeof(r1) == sizeof (R), "");
+R r2[]({1, 2}, {3, 4});
+static_assert(sizeof(r2) == 2 * sizeof (R), "");
+R r3[]({1.0, 2}, {3.2, 4});
+static_assert(sizeof(r3) == 2 * sizeof (R), "");
+
+char c1[]('r');
+char c2[]('r', 'a', 'c', 'c', 'o', 'o', 'n');
+char c3[]("yarrow");
+char c4[4]("oak");
+char c5[10]("cat");
+const char (&c6)[4]("eel");
+
+int g;
+struct X {
+  int i;
+  X() { ++g; }
+  X(int) { };
+};
+
+int
+main ()
+{
+  // Here we'll value-initialize l[1] and l[2], which will zero-initialize it.
+  int l[3](42);
+  if (l[0] != 42 || l[1] != 0 || l[2] != 0)
+    __builtin_abort ();
+
+  // Here we'll value-initialize x[2] and x[3].  Since X is a class type
+  // with a user-provided ctor, we'll default-initialize in both cases.
+  X x[4]({ 1 }, { 2 });
+  if (g != 2)
+    __builtin_abort ();
+
+  if (a1[0] != 1 || a1[1] != 2 || a1[2] != 3)
+    __builtin_abort ();
+  if (a2[0] != 1 || a2[1] != 2 || a2[2] != 3)
+    __builtin_abort ();
+  if (a3[0] != 1 || a3[1] != 2 || a3[2] != 3)
+    __builtin_abort ();
+  if (a4[0] != 1 || a4[1] != 2 || a4[2] != 0)
+    __builtin_abort ();
+  if (a5[0] != 1 || a5[1] != 2)
+    __builtin_abort ();
+  if (a6[0] != 1)
+    __builtin_abort ();
+  if (a7[0] != 0)
+    __builtin_abort ();
+  if (a8[0] != 0 || a8[1] != 0 || a8[2] != 0 || a8[3] != 0
+      || a8[4] != 0 || a8[5] != 0)
+    __builtin_abort ();
+  if (a9[0] != 1)
+    __builtin_abort ();
+  if (a10[0] != 1 || a10[1] != 2 || a10[2] != 3)
+    __builtin_abort ();
+  if (a11[0][0] != 1 || a11[0][1] != 0)
+    __builtin_abort ();
+  if (a12[0][0] != 1 || a12[0][1] != 2 || a12[1][0] != 3 || a12[1][1] != 4)
+    __builtin_abort ();
+
+  if (ra1[0] != 1 || ra1[1] != 2)
+    __builtin_abort ();
+  if (ra2[0] != 1 || ra2[1] != 2)
+    __builtin_abort ();
+  if (ra3[0] != 1 || ra3[1] != 2)
+    __builtin_abort ();
+  if (rra1[0] != 1 || rra1[1] != 2)
+    __builtin_abort ();
+  if (rra2[0] != 1 || rra2[1] != 2)
+    __builtin_abort ();
+  if (rra3[0] != 1 || rra3[1] != 2)
+    __builtin_abort ();
+
+  if (s1[0].i != 1)
+    __builtin_abort ();
+  if (s2[0].i != 1 || s2[1].i != 2)
+    __builtin_abort ();
+  if (s3[0].i != 1 || s3[1].i != 2 || s3[2].i != 3)
+    __builtin_abort ();
+  if (s4[0].i != 1 || s4[1].i != 2 || s4[2].i != 0)
+    __builtin_abort ();
+  if (s5[0].i != 3 || s5[1].i != 4)
+    __builtin_abort ();
+
+  if (r1[0].i != 1 || r1[0].j != 2)
+    __builtin_abort ();
+  if (r2[0].i != 1 || r2[0].j != 2 || r2[1].i != 3 || r2[1].j != 4)
+    __builtin_abort ();
+  if (r3[0].i != 1 || r3[0].j != 2 || r3[1].i != 3 || r3[1].j != 4)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init5.C gcc/testsuite/g++.dg/cpp2a/paren-init5.C
new file mode 100644
index 00000000000..a64cb00ebbc
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init5.C
@@ -0,0 +1,25 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+union U {
+  int a;
+  float b;
+};
+
+// u1 has no active member
+U u1;
+// u2 zero-initializes the first member, so u2.a is the active member and
+// its value is 0.
+U u2 = U();
+// u3 uses non-list aggregate initialization, so u3.a is the active member
+// and its value is 1.
+U u3 = U(1);
+
+int
+main ()
+{
+  if (u2.a != 0)
+    __builtin_abort ();
+  if (u3.a != 1)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init6.C gcc/testsuite/g++.dg/cpp2a/paren-init6.C
new file mode 100644
index 00000000000..b5d97dcb3d8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init6.C
@@ -0,0 +1,14 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test that we don't perform lifetime extension for () init.
+
+struct A {
+  int a;
+  int&& r;
+};
+
+int f();
+A a(1, f());
+
+// { dg-final { scan-assembler-not "_ZGR1a" } }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init7.C gcc/testsuite/g++.dg/cpp2a/paren-init7.C
new file mode 100644
index 00000000000..32af1a7265c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init7.C
@@ -0,0 +1,20 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+int h;
+struct i {
+  i() {}
+  explicit i(i &) {}
+  template <typename j> i(j &) { h++; }
+};
+
+int main() {
+  {
+    i a[6];
+    auto [b, c, d, e, f, g] = a;
+  }
+  i a[6];
+  auto [b, c, d, e, f, g](a);
+  if (h != 6)
+    __builtin_abort();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init8.C gcc/testsuite/g++.dg/cpp2a/paren-init8.C
new file mode 100644
index 00000000000..30e71650bc9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init8.C
@@ -0,0 +1,13 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test that we don't accept designated inits in ( ).
+
+struct S {
+  int i;
+  int j = 42;
+};
+
+S s(.i = 12); // { dg-error "expected" }
+
+int a[]([0] = 42); // { dg-error "" }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init9.C gcc/testsuite/g++.dg/cpp2a/paren-init9.C
new file mode 100644
index 00000000000..c44b206feb8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init9.C
@@ -0,0 +1,10 @@
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct B { };
+struct A : B {
+  int i;
+};
+
+B b;
+A a(b);
diff --git gcc/testsuite/g++.dg/ext/desig10.C gcc/testsuite/g++.dg/ext/desig10.C
index 24057dd0ad6..c8d672b72d9 100644
--- gcc/testsuite/g++.dg/ext/desig10.C
+++ gcc/testsuite/g++.dg/ext/desig10.C
@@ -1,4 +1,4 @@
 // PR c++/84972
 // { dg-additional-options "-w" }
 
-char(a[])({.a = 0});  // { dg-error "designated initializer" }
+char(a[])({.a = 0});  // { dg-error "designated initializer|cannot convert" }
diff --git gcc/testsuite/g++.dg/template/crash107.C gcc/testsuite/g++.dg/template/crash107.C
index 3b0b4e8211f..9d8d394d93d 100644
--- gcc/testsuite/g++.dg/template/crash107.C
+++ gcc/testsuite/g++.dg/template/crash107.C
@@ -3,7 +3,7 @@
 // { dg-options "" }
 // { dg-additional-options "-Wno-return-type" }
 
-template<typename FP_> struct Vec { // { dg-message "note" }
+template<typename FP_> struct Vec { // { dg-message "note" "" { target c++17_down } }
     Vec& operator^=(Vec& rhs)     {
         union {
             struct {FP_ x,y,z;}; // { dg-error "20:anonymous struct" }
@@ -14,6 +14,6 @@ template<typename FP_> struct Vec { // { dg-message "note" }
         return Vec(*this)^=rhs; // { dg-message "required" }
     }
 };
-Vec<double> v(3,4,12); // { dg-error "no matching" }
-Vec<double> V(12,4,3);  // { dg-error "no matching" }
+Vec<double> v(3,4,12); // { dg-error "no matching|too many initializers" }
+Vec<double> V(12,4,3);  // { dg-error "no matching|too many initializers" }
 Vec<double> c = v^V;   // { dg-message "required" }
diff --git gcc/testsuite/g++.dg/template/crash95.C gcc/testsuite/g++.dg/template/crash95.C
index f60e635ae66..47346111328 100644
--- gcc/testsuite/g++.dg/template/crash95.C
+++ gcc/testsuite/g++.dg/template/crash95.C
@@ -8,4 +8,4 @@ template < typename > struct S
   };
 };
 
-S < int > s(0); // { dg-error "no matching" }
+S < int > s(0); // { dg-error "no matching|too many initializers" }
diff --git gcc/testsuite/g++.old-deja/g++.jason/crash3.C gcc/testsuite/g++.old-deja/g++.jason/crash3.C
index e94cc7c9781..c4950531328 100644
--- gcc/testsuite/g++.old-deja/g++.jason/crash3.C
+++ gcc/testsuite/g++.old-deja/g++.jason/crash3.C
@@ -2,12 +2,13 @@
 // Bug: g++ tries to generate initialization semantics for a Node from an int,
 // and fails.
 
-struct Node			// { dg-message "note" }
+struct Node			// { dg-message "note" "" { target c++17_down } }
 {
   Node* child[2];
 };
 
 void bug(int i)
 {
-  Node* q = new Node(i);	// { dg-error "no matching" } 
+  Node* q = new Node(i);	// { dg-error "no matching" "" { target c++17_down }  } 
+// { dg-error "array must be initialized" "" { target c++2a } .-1 }
 }
diff --git gcc/testsuite/g++.old-deja/g++.law/ctors11.C gcc/testsuite/g++.old-deja/g++.law/ctors11.C
index 39ee76b0ae7..b29b18a62a3 100644
--- gcc/testsuite/g++.old-deja/g++.law/ctors11.C
+++ gcc/testsuite/g++.old-deja/g++.law/ctors11.C
@@ -10,12 +10,12 @@ public:
   inline A(int x){printf("constructing A with %d\n", x);}
 };
 
-class B:public A{ // { dg-message "note" } non-default constructor
+class B:public A{ // { dg-message "note" "" { target c++17_down } } non-default constructor
 private:
 public:
 };
 
 int main()
 {
-  B(10);// { dg-error "match" } B doesn't have a constructor taking int
+  B(10);// { dg-error "match" "" { target c++17_down } } B doesn't have a constructor taking int
 }
diff --git gcc/testsuite/g++.old-deja/g++.law/ctors9.C gcc/testsuite/g++.old-deja/g++.law/ctors9.C
index 43ba1262c95..5856d5dc883 100644
--- gcc/testsuite/g++.old-deja/g++.law/ctors9.C
+++ gcc/testsuite/g++.old-deja/g++.law/ctors9.C
@@ -20,7 +20,7 @@ Foo::Foo(int aa)
 { }
 
 
-struct var_Foo: public Foo // { dg-message "note" }  base.*// ERROR -  in class.*
+struct var_Foo: public Foo // { dg-message "note" "" { target c++17_down } }  base.*// ERROR -  in class.*
 {
   var_Foo* operator-> () {return this;}
 };
@@ -32,7 +32,7 @@ int blort(Foo& f)
 
 int main()
 {
-  var_Foo b(2);// { dg-error "match" } 
+  var_Foo b(2);// { dg-error "match" "" { target c++17_down } } 
   b->a = 0;
   int x = blort(b);
   return x;
diff --git gcc/testsuite/g++.old-deja/g++.mike/net22.C gcc/testsuite/g++.old-deja/g++.mike/net22.C
index e5e1cb1081d..abbf6191023 100644
--- gcc/testsuite/g++.old-deja/g++.mike/net22.C
+++ gcc/testsuite/g++.old-deja/g++.mike/net22.C
@@ -5,10 +5,11 @@ public:
   Parent( char *s ) {}
 };
 
-class Child : public Parent {		// { dg-message "note" } called
+class Child : public Parent {		// { dg-message "note" "" { target c++17_down } } called
 };
 
 int main() {
-  Child c( "String initializer" );	// { dg-error "match" } bad
+  Child c( "String initializer" );	// { dg-error "match" "" { target c++17_down } } bad
+// { dg-error "forbids converting a string constant" "" { target c++2a } .-1 }
   return 0;
 }
diff --git gcc/testsuite/g++.old-deja/g++.niklas/t128.C gcc/testsuite/g++.old-deja/g++.niklas/t128.C
index 19e3ca1dab0..0b3346c4ef3 100644
--- gcc/testsuite/g++.old-deja/g++.niklas/t128.C
+++ gcc/testsuite/g++.old-deja/g++.niklas/t128.C
@@ -1,5 +1,5 @@
 // { dg-do assemble  }
 // GROUPS niklas uncaught default-construct
 struct A { A (int); };
-struct B : A {}; // { dg-message "note" } without ctor // ERROR - candidates
-void f () { B (0); }// { dg-error "match" } .*
+struct B : A {}; // { dg-message "note" "" { target c++17_down } } without ctor // ERROR - candidates
+void f () { B (0); }// { dg-error "match" "" { target c++17_down } } .*
Jason Merrill Dec. 3, 2019, 3:55 p.m. | #15
On 12/3/19 9:38 AM, Marek Polacek wrote:
> On Tue, Dec 03, 2019 at 02:01:24AM -0500, Jason Merrill wrote:

>> On 12/2/19 7:31 PM, Marek Polacek wrote:

>>> @@ -1967,8 +1978,23 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,

>>>          tree ctor_name = (true_exp == exp

>>>    			? complete_ctor_identifier : base_ctor_identifier);

>>> +      /* Given class A,

>>> +

>>> +	   A a(1, 2);

>>> +

>>> +	 can mean a call to a constructor A::A(int, int), if present.  If not,

>>> +	 but A is an aggregate, we will try aggregate initialization.  */

>>>          rval = build_special_member_call (exp, ctor_name, &parms, binfo, flags,

>>>    					complain);

>>> +      if (BRACE_ENCLOSED_INITIALIZER_P (rval))

>>> +	{

>>> +	  gcc_assert (cxx_dialect >= cxx2a);

>>> +	  rval = digest_init (type, rval, tf_warning_or_error);

>>> +	  rval = build2 (INIT_EXPR, type, exp, rval);

>>> +	  /* So that we do finish_expr_stmt below.  Don't return here, we

>>> +	     need to release PARMS.  */

>>> +	  TREE_SIDE_EFFECTS (rval) = 1;

>>> +	}

>>

>> Can we still get a CONSTRUCTOR here after the change to

>> build_new_method_call_1?

> 

> Oop, not anymore.  Dropped the whole cp/init.c hunk.

> 

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


OK, thanks.

> 2019-12-03  Marek Polacek  <polacek@redhat.com>

> 

> 	PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> 	* c-cppbuiltin.c (c_cpp_builtins): Predefine

> 	__cpp_aggregate_paren_init=201902 for -std=c++2a.

> 

> 	* call.c (build_new_method_call_1): Handle parenthesized initialization

> 	of aggregates by building up a CONSTRUCTOR.

> 	(extend_ref_init_temps): Do nothing for CONSTRUCTOR_IS_PAREN_INIT.

> 	* cp-tree.h (CONSTRUCTOR_IS_PAREN_INIT, LOOKUP_AGGREGATE_PAREN_INIT):

> 	Define.

> 	* decl.c (grok_reference_init): Handle aggregate initialization from

> 	a parenthesized list of values.

> 	(reshape_init): Do nothing for CONSTRUCTOR_IS_PAREN_INIT.

> 	(check_initializer): Handle initialization of an array from a

> 	parenthesized list of values.  Use NULL_TREE instead of NULL.

> 	* tree.c (build_cplus_new): Handle BRACE_ENCLOSED_INITIALIZER_P.

> 	* typeck2.c (digest_init_r): Set LOOKUP_AGGREGATE_PAREN_INIT if it

> 	receives a CONSTRUCTOR with CONSTRUCTOR_IS_PAREN_INIT set.  Allow

> 	narrowing when LOOKUP_AGGREGATE_PAREN_INIT.

> 	(massage_init_elt): Don't lose LOOKUP_AGGREGATE_PAREN_INIT when passing

> 	flags to digest_init_r.

> 

> 	* g++.dg/cpp0x/constexpr-99.C: Only expect an error in C++17 and

> 	lesser.

> 	* g++.dg/cpp0x/explicit7.C: Likewise.

> 	* g++.dg/cpp0x/initlist12.C: Adjust dg-error.

> 	* g++.dg/cpp0x/pr31437.C: Likewise.

> 	* g++.dg/cpp2a/feat-cxx2a.C: Add __cpp_aggregate_paren_init test.

> 	* g++.dg/cpp2a/paren-init1.C: New test.

> 	* g++.dg/cpp2a/paren-init10.C: New test.

> 	* g++.dg/cpp2a/paren-init11.C: New test.

> 	* g++.dg/cpp2a/paren-init12.C: New test.

> 	* g++.dg/cpp2a/paren-init13.C: New test.

> 	* g++.dg/cpp2a/paren-init14.C: New test.

> 	* g++.dg/cpp2a/paren-init15.C: New test.

> 	* g++.dg/cpp2a/paren-init16.C: New test.

> 	* g++.dg/cpp2a/paren-init17.C: New test.

> 	* g++.dg/cpp2a/paren-init18.C: New test.

> 	* g++.dg/cpp2a/paren-init19.C: New test.

> 	* g++.dg/cpp2a/paren-init2.C: New test.

> 	* g++.dg/cpp2a/paren-init3.C: New test.

> 	* g++.dg/cpp2a/paren-init4.C: New test.

> 	* g++.dg/cpp2a/paren-init5.C: New test.

> 	* g++.dg/cpp2a/paren-init6.C: New test.

> 	* g++.dg/cpp2a/paren-init7.C: New test.

> 	* g++.dg/cpp2a/paren-init8.C: New test.

> 	* g++.dg/cpp2a/paren-init9.C: New test.

> 	* g++.dg/ext/desig10.C: Adjust dg-error.

> 	* g++.dg/template/crash107.C: Likewise.

> 	* g++.dg/template/crash95.C: Likewise.

> 	* g++.old-deja/g++.jason/crash3.C: Likewise.

> 	* g++.old-deja/g++.law/ctors11.C: Likewise.

> 	* g++.old-deja/g++.law/ctors9.C: Likewise.

> 	* g++.old-deja/g++.mike/net22.C: Likewise.

> 	* g++.old-deja/g++.niklas/t128.C: Likewise.

> 

> diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c

> index 6491545bc3b..c7f4659456a 100644

> --- gcc/c-family/c-cppbuiltin.c

> +++ gcc/c-family/c-cppbuiltin.c

> @@ -1006,6 +1006,7 @@ c_cpp_builtins (cpp_reader *pfile)

>   	  cpp_define (pfile, "__cpp_impl_destroying_delete=201806L");

>   	  cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907L");

>   	  cpp_define (pfile, "__cpp_impl_three_way_comparison=201907L");

> +	  cpp_define (pfile, "__cpp_aggregate_paren_init=201902L");

>   	}

>         if (flag_concepts)

>           {

> diff --git gcc/cp/call.c gcc/cp/call.c

> index 062cff4c735..ea0e8b7e7d7 100644

> --- gcc/cp/call.c

> +++ gcc/cp/call.c

> @@ -10124,6 +10124,38 @@ build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args,

>   

>     if (!any_viable_p)

>       {

> +      /* [dcl.init], 17.6.2.2:

> +

> +	 Otherwise, if no constructor is viable, the destination type is

> +	 a (possibly cv-qualified) aggregate class A, and the initializer

> +	 is a parenthesized expression-list, the object is initialized as

> +	 follows...

> +

> +	 We achieve this by building up a CONSTRUCTOR, as for list-init,

> +	 and setting CONSTRUCTOR_IS_PAREN_INIT to distinguish between

> +	 the two.  */

> +      if (DECL_CONSTRUCTOR_P (fn)

> +	  && !(flags & LOOKUP_ONLYCONVERTING)

> +	  && !cp_unevaluated_operand

> +	  && cxx_dialect >= cxx2a

> +	  && CP_AGGREGATE_TYPE_P (basetype)

> +	  && !user_args->is_empty ())

> +	{

> +	  /* Create a CONSTRUCTOR from ARGS, e.g. {1, 2} from <1, 2>.  */

> +	  tree list = build_tree_list_vec (user_args);

> +	  tree ctor = build_constructor_from_list (init_list_type_node, list);

> +	  CONSTRUCTOR_IS_DIRECT_INIT (ctor) = true;

> +	  CONSTRUCTOR_IS_PAREN_INIT (ctor) = true;

> +	  if (is_dummy_object (instance))

> +	    return ctor;

> +	  else

> +	    {

> +	      ctor = digest_init (basetype, ctor, complain);

> +	      ctor = build2 (INIT_EXPR, TREE_TYPE (instance), instance, ctor);

> +	      TREE_SIDE_EFFECTS (ctor) = true;

> +	      return ctor;

> +	    }

> +	}

>         if (complain & tf_error)

>   	complain_about_no_candidates_for_method_call (instance, candidates,

>   						      explicit_targs, basetype,

> @@ -11789,9 +11821,16 @@ perform_direct_initialization_if_possible (tree type,

>        If the destination type is a (possibly cv-qualified) class type:

>   

>        -- If the initialization is direct-initialization ...,

> -     constructors are considered. ... If no constructor applies, or

> -     the overload resolution is ambiguous, the initialization is

> -     ill-formed.  */

> +     constructors are considered.

> +

> +       -- If overload resolution is successful, the selected constructor

> +       is called to initialize the object, with the initializer expression

> +       or expression-list as its argument(s).

> +

> +       -- Otherwise, if no constructor is viable, the destination type is

> +       a (possibly cv-qualified) aggregate class A, and the initializer is

> +       a parenthesized expression-list, the object is initialized as

> +       follows...  */

>     if (CLASS_TYPE_P (type))

>       {

>         releasing_vec args (make_tree_vector_single (expr));

> @@ -12147,6 +12186,12 @@ extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups)

>   	ctor = TARGET_EXPR_INITIAL (ctor);

>         if (TREE_CODE (ctor) == CONSTRUCTOR)

>   	{

> +	  /* [dcl.init] When initializing an aggregate from a parenthesized list

> +	     of values... a temporary object bound to a reference does not have

> +	     its lifetime extended.  */

> +	  if (CONSTRUCTOR_IS_PAREN_INIT (ctor))

> +	    return init;

> +

>   	  if (is_std_init_list (type))

>   	    {

>   	      /* The temporary array underlying a std::initializer_list

> diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h

> index 7e810b8ee7b..4af18b0ae62 100644

> --- gcc/cp/cp-tree.h

> +++ gcc/cp/cp-tree.h

> @@ -4325,6 +4325,11 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)

>   #define CONSTRUCTOR_IS_DESIGNATED_INIT(NODE) \

>     (TREE_LANG_FLAG_6 (CONSTRUCTOR_CHECK (NODE)))

>   

> +/* True if this CONSTRUCTOR comes from a parenthesized list of values, e.g.

> +   A(1, 2, 3).  */

> +#define CONSTRUCTOR_IS_PAREN_INIT(NODE) \

> +  (CONSTRUCTOR_CHECK(NODE)->base.private_flag)

> +

>   /* True if NODE represents a conversion for direct-initialization in a

>      template.  Set by perform_implicit_conversion_flags.  */

>   #define IMPLICIT_CONV_EXPR_DIRECT_INIT(NODE) \

> @@ -5583,6 +5588,8 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };

>      args), then we swap the conversions back in build_new_op_1 (so they

>      correspond to the order of the args in the candidate).  */

>   #define LOOKUP_REVERSED (LOOKUP_REWRITTEN << 1)

> +/* We're initializing an aggregate from a parenthesized list of values.  */

> +#define LOOKUP_AGGREGATE_PAREN_INIT (LOOKUP_REVERSED << 1)

>   

>   #define LOOKUP_NAMESPACES_ONLY(F)  \

>     (((F) & LOOKUP_PREFER_NAMESPACES) && !((F) & LOOKUP_PREFER_TYPES))

> diff --git gcc/cp/decl.c gcc/cp/decl.c

> index 81d73433547..54f09507516 100644

> --- gcc/cp/decl.c

> +++ gcc/cp/decl.c

> @@ -5531,11 +5531,28 @@ grok_reference_init (tree decl, tree type, tree init, int flags)

>         return NULL_TREE;

>       }

>   

> +  tree ttype = TREE_TYPE (type);

>     if (TREE_CODE (init) == TREE_LIST)

> -    init = build_x_compound_expr_from_list (init, ELK_INIT,

> -					    tf_warning_or_error);

> +    {

> +      /* This handles (C++20 only) code like

> +

> +	   const A& r(1, 2, 3);

> +

> +	 where we treat the parenthesized list as a CONSTRUCTOR.  */

> +      if (TREE_TYPE (init) == NULL_TREE

> +	  && CP_AGGREGATE_TYPE_P (ttype)

> +	  && !DECL_DECOMPOSITION_P (decl)

> +	  && (cxx_dialect >= cxx2a))

> +	{

> +	  init = build_constructor_from_list (init_list_type_node, init);

> +	  CONSTRUCTOR_IS_DIRECT_INIT (init) = true;

> +	  CONSTRUCTOR_IS_PAREN_INIT (init) = true;

> +	}

> +      else

> +	init = build_x_compound_expr_from_list (init, ELK_INIT,

> +						tf_warning_or_error);

> +    }

>   

> -  tree ttype = TREE_TYPE (type);

>     if (TREE_CODE (ttype) != ARRAY_TYPE

>         && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE)

>       /* Note: default conversion is only called in very special cases.  */

> @@ -6437,6 +6454,11 @@ reshape_init (tree type, tree init, tsubst_flags_t complain)

>     if (vec_safe_is_empty (v))

>       return init;

>   

> +  /* Brace elision is not performed for a CONSTRUCTOR representing

> +     parenthesized aggregate initialization.  */

> +  if (CONSTRUCTOR_IS_PAREN_INIT (init))

> +    return init;

> +

>     /* Handle [dcl.init.list] direct-list-initialization from

>        single element of enumeration with a fixed underlying type.  */

>     if (is_direct_enum_init (type, init))

> @@ -6640,6 +6662,41 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)

>   	      flags |= LOOKUP_NO_NARROWING;

>   	    }

>   	}

> +      /* [dcl.init] "Otherwise, if the destination type is an array, the object

> +	 is initialized as follows..."  So handle things like

> +

> +	  int a[](1, 2, 3);

> +

> +	 which is permitted in C++20 by P0960.  */

> +      else if (TREE_CODE (init) == TREE_LIST

> +	       && TREE_TYPE (init) == NULL_TREE

> +	       && TREE_CODE (type) == ARRAY_TYPE

> +	       && !DECL_DECOMPOSITION_P (decl)

> +	       && (cxx_dialect >= cxx2a))

> +	{

> +	  /* [dcl.init.string] "An array of ordinary character type [...]

> +	     can be initialized by an ordinary string literal [...] by an

> +	     appropriately-typed string literal enclosed in braces" only

> +	     talks about braces, but GCC has always accepted

> +

> +	       char a[]("foobar");

> +

> +	     so we continue to do so.  */

> +	  tree val = TREE_VALUE (init);

> +	  if (TREE_CHAIN (init) == NULL_TREE

> +	      && char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type)))

> +	      && TREE_CODE (tree_strip_any_location_wrapper (val))

> +		 == STRING_CST)

> +	    /* If the list has a single element and it's a string literal,

> +	       then it's the initializer for the array as a whole.  */

> +	    init = val;

> +	  else

> +	    {

> +	      init = build_constructor_from_list (init_list_type_node, init);

> +	      CONSTRUCTOR_IS_DIRECT_INIT (init) = true;

> +	      CONSTRUCTOR_IS_PAREN_INIT (init) = true;

> +	    }

> +	}

>         else if (TREE_CODE (init) == TREE_LIST

>   	       && TREE_TYPE (init) != unknown_type_node

>   	       && !MAYBE_CLASS_TYPE_P (type))

> @@ -6683,6 +6740,9 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)

>   	    init_code = TREE_OPERAND (init_code, 0);

>   	  if (TREE_CODE (init_code) == INIT_EXPR)

>   	    {

> +	      /* In C++20, the call to build_aggr_init could have created

> +		 an INIT_EXPR with a CONSTRUCTOR as the RHS to handle

> +		 A(1, 2).  */

>   	      init = TREE_OPERAND (init_code, 1);

>   	      init_code = NULL_TREE;

>   	      /* Don't call digest_init; it's unnecessary and will complain

> @@ -6736,7 +6796,7 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)

>   			0, "array %qD initialized by parenthesized "

>   			"string literal %qE",

>   			decl, DECL_INITIAL (decl));

> -	  init = NULL;

> +	  init = NULL_TREE;

>   	}

>       }

>     else

> diff --git gcc/cp/tree.c gcc/cp/tree.c

> index 8817860b4d3..8b625e8b18e 100644

> --- gcc/cp/tree.c

> +++ gcc/cp/tree.c

> @@ -669,6 +669,15 @@ build_aggr_init_expr (tree type, tree init)

>   tree

>   build_cplus_new (tree type, tree init, tsubst_flags_t complain)

>   {

> +  /* This function should cope with what build_special_member_call

> +     can produce.  When performing parenthesized aggregate initialization,

> +     it can produce a { }.  */

> +  if (BRACE_ENCLOSED_INITIALIZER_P (init))

> +    {

> +      gcc_assert (cxx_dialect >= cxx2a);

> +      return finish_compound_literal (type, init, complain);

> +    }

> +

>     tree rval = build_aggr_init_expr (type, init);

>     tree slot;

>   

> diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c

> index ae00de2468d..7fda6266a35 100644

> --- gcc/cp/typeck2.c

> +++ gcc/cp/typeck2.c

> @@ -1117,6 +1117,10 @@ digest_init_r (tree type, tree init, int nested, int flags,

>   

>     tree stripped_init = init;

>   

> +  if (BRACE_ENCLOSED_INITIALIZER_P (init)

> +      && CONSTRUCTOR_IS_PAREN_INIT (init))

> +    flags |= LOOKUP_AGGREGATE_PAREN_INIT;

> +

>     /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue

>        (g++.old-deja/g++.law/casts2.C).  */

>     if (TREE_CODE (init) == NON_LVALUE_EXPR)

> @@ -1224,7 +1228,9 @@ digest_init_r (tree type, tree init, int nested, int flags,

>     if ((code != COMPLEX_TYPE || BRACE_ENCLOSED_INITIALIZER_P (stripped_init))

>         && (SCALAR_TYPE_P (type) || code == REFERENCE_TYPE))

>       {

> -      if (nested)

> +      /* Narrowing is OK when initializing an aggregate from

> +	 a parenthesized list.  */

> +      if (nested && !(flags & LOOKUP_AGGREGATE_PAREN_INIT))

>   	flags |= LOOKUP_NO_NARROWING;

>         init = convert_for_initialization (0, type, init, flags,

>   					 ICR_INIT, NULL_TREE, 0,

> @@ -1386,9 +1392,12 @@ static tree

>   massage_init_elt (tree type, tree init, int nested, int flags,

>   		  tsubst_flags_t complain)

>   {

> -  flags &= LOOKUP_ALLOW_FLEXARRAY_INIT;

> -  flags |= LOOKUP_IMPLICIT;

> -  init = digest_init_r (type, init, nested ? 2 : 1, flags, complain);

> +  int new_flags = LOOKUP_IMPLICIT;

> +  if (flags & LOOKUP_ALLOW_FLEXARRAY_INIT)

> +    new_flags |= LOOKUP_ALLOW_FLEXARRAY_INIT;

> +  if (flags & LOOKUP_AGGREGATE_PAREN_INIT)

> +    new_flags |= LOOKUP_AGGREGATE_PAREN_INIT;

> +  init = digest_init_r (type, init, nested ? 2 : 1, new_flags, complain);

>     /* Strip a simple TARGET_EXPR when we know this is an initializer.  */

>     if (SIMPLE_TARGET_EXPR_P (init))

>       init = TARGET_EXPR_INITIAL (init);

> diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-99.C gcc/testsuite/g++.dg/cpp0x/constexpr-99.C

> index 8d791dd032d..4d3953d0983 100644

> --- gcc/testsuite/g++.dg/cpp0x/constexpr-99.C

> +++ gcc/testsuite/g++.dg/cpp0x/constexpr-99.C

> @@ -9,5 +9,6 @@ struct A

>   struct B

>   {

>     A a;

> -    constexpr B() : a(0) {} // { dg-error "no matching function" }

> +  // P0960R3 allows paren-init.

> +  constexpr B() : a(0) {} // { dg-error "no matching function" "" { target c++17_down } }

>   };

> diff --git gcc/testsuite/g++.dg/cpp0x/explicit7.C gcc/testsuite/g++.dg/cpp0x/explicit7.C

> index 574796d117d..67b50542bc3 100644

> --- gcc/testsuite/g++.dg/cpp0x/explicit7.C

> +++ gcc/testsuite/g++.dg/cpp0x/explicit7.C

> @@ -10,7 +10,7 @@ struct A { };

>   struct B: A { };

>   struct C {

>     explicit operator B*();	// { dg-message "explicit" }

> -  explicit operator B&();	// { dg-message "explicit" }

> +  explicit operator B&();	// { dg-message "explicit" "" { target c++17_down } }

>   };

>   

>   C c;

> diff --git gcc/testsuite/g++.dg/cpp0x/initlist12.C gcc/testsuite/g++.dg/cpp0x/initlist12.C

> index f72a6dac525..c2a2b86520f 100644

> --- gcc/testsuite/g++.dg/cpp0x/initlist12.C

> +++ gcc/testsuite/g++.dg/cpp0x/initlist12.C

> @@ -6,15 +6,15 @@ struct A

>     int i;

>   };

>   

> -A a({1,2});			// { dg-error "no match" }

> +A a({1,2});			// { dg-error "no match|cannot convert" }

>   

>   union U

>   {

>     int i,j;

>   };

>   

> -U u({1,2});			// { dg-error "no match" }

> +U u({1,2});			// { dg-error "no match|cannot convert" }

>   

>   union V {};

>   

> -V v({1});			// { dg-error "no match" }

> +V v({1});			// { dg-error "no match|too many initializers" }

> diff --git gcc/testsuite/g++.dg/cpp0x/pr31437.C gcc/testsuite/g++.dg/cpp0x/pr31437.C

> index 7e2ed7a1375..532b533c8d3 100644

> --- gcc/testsuite/g++.dg/cpp0x/pr31437.C

> +++ gcc/testsuite/g++.dg/cpp0x/pr31437.C

> @@ -1,9 +1,9 @@

>   // { dg-do compile { target c++11 } }

> -template <typename... T> struct A // { dg-message "candidates|A" }

> +template <typename... T> struct A // { dg-message "candidates|A" "" { target c++17_down } }

>   {

>     A(T* p) {  // { dg-error "parameter packs|T" }

>      (A<T...>*)(p);

>     }

>   };

>   

> -A<int> a(0); // { dg-error "no matching" }

> +A<int> a(0); // { dg-error "no matching|too many initializers" }

> diff --git gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C

> index 9b6e2f59d2c..389b25e16ea 100644

> --- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C

> +++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C

> @@ -460,6 +460,12 @@

>   #  error "__cpp_constexpr_dynamic_alloc != 201907"

>   #endif

>   

> +#ifndef __cpp_aggregate_paren_init

> +#  error "__cpp_aggregate_paren_init"

> +#elif __cpp_aggregate_paren_init != 201902

> +#  error "__cpp_aggregate_paren_init != 201902"

> +#endif

> +

>   #ifdef __has_cpp_attribute

>   

>   #  if ! __has_cpp_attribute(maybe_unused)

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init1.C gcc/testsuite/g++.dg/cpp2a/paren-init1.C

> new file mode 100644

> index 00000000000..a54b2ccaf6d

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init1.C

> @@ -0,0 +1,116 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do run { target c++2a } }

> +

> +struct A {

> +  int i;

> +  int j;

> +  int k;

> +};

> +

> +struct B {

> +  int i;

> +  int j;

> +  int k = 42;

> +};

> +

> +struct C {

> +  A a;

> +};

> +

> +struct D {

> +  A a1;

> +  A a2;

> +};

> +

> +struct E {

> +  int i;

> +};

> +

> +// F has a base class, but it's not virtual, private, or protected, so this is

> +// still an aggregate (since C++17).

> +struct F : E {

> +  int j;

> +  int k;

> +};

> +

> +F f({1}, 2, 3);

> +

> +// A non-virtual member function doesn't make it a non-aggregate.

> +struct G {

> +  int i;

> +  double j;

> +  int foo(int, int);

> +};

> +

> +G g(1, 2.14);

> +

> +class H {

> +public:

> +  H (int) { }

> +};

> +

> +class I : public H { };

> +

> +int i;

> +A a1(1, 2);

> +A a2(1.0, 2);

> +A a3(++i, ++i);

> +const A& ra(1, 2);

> +

> +A ca = A(1, 2);

> +A ca2 = A(1.0, 2);

> +A ca3 = A(++i, ++i);

> +const A& rca = A(1, 2);

> +

> +B b1(1, 2, 3);

> +B b2(1, 2);

> +B b3(1);

> +

> +C c1({5, 6, 7});

> +D d1({1, 2, 3}, {5, 6, 7});

> +

> +struct W {

> +  const char *s, *t;

> +};

> +W w1("fluffer", "nutter");

> +

> +struct Y {

> +  char a[4];

> +};

> +Y y("yew");

> +

> +int

> +main ()

> +{

> +  I(10);

> +

> +  // A::k will be value-initialized.

> +  if (a1.i != 1 || a1.j != 2 || a1.k != 0)

> +    __builtin_abort ();

> +  if (a2.i != 1 || a2.j != 2 || a2.k != 0)

> +    __builtin_abort ();

> +  if (a3.i != 1 || a3.j != 2 || a3.k != 0)

> +    __builtin_abort ();

> +  if (ra.i != 1 || ra.j != 2 || ra.k != 0)

> +    __builtin_abort ();

> +  if (ca.i != 1 || ca.j != 2 || ca.k != 0)

> +    __builtin_abort ();

> +  if (ca2.i != 1 || ca2.j != 2 || ca2.k != 0)

> +    __builtin_abort ();

> +  if (ca3.i != 3 || ca3.j != 4 || ca3.k != 0)

> +    __builtin_abort ();

> +

> +  if (b1.i != 1 || b1.j != 2 || b1.k != 3)

> +    __builtin_abort ();

> +  // The default member initializer will be used for B::k.

> +  if (b2.i != 1 || b2.j != 2 || b2.k != 42)

> +    __builtin_abort ();

> +  if (b3.i != 1 || b3.j != 0 || b3.k != 42)

> +    __builtin_abort ();

> +

> +  if (c1.a.i != 5 || c1.a.j != 6 || c1.a.k != 7)

> +    __builtin_abort ();

> +

> +  if (f.i != 1 || f.j != 2 || f.k != 3)

> +    __builtin_abort ();

> +}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init10.C gcc/testsuite/g++.dg/cpp2a/paren-init10.C

> new file mode 100644

> index 00000000000..5c70d9d59ee

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init10.C

> @@ -0,0 +1,18 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +// Test from [dcl.init].

> +

> +struct A {

> +  int a;

> +  int&& r;

> +};

> +

> +int f();

> +int n = 10;

> +

> +A a1{1, f()};               // OK, lifetime is extended

> +A a2(1, f());               // well-formed, but dangling reference

> +A a3{1.0, 1};               // { dg-error "narrowing conversion" }

> +A a4(1.0, 1);               // well-formed, but dangling reference

> +A a5(1.0, static_cast<int&&>(n));    // OK

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init11.C gcc/testsuite/g++.dg/cpp2a/paren-init11.C

> new file mode 100644

> index 00000000000..82ca2669545

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init11.C

> @@ -0,0 +1,88 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +// Test ill-formed code.

> +

> +// If k is greater than the size of the array, the program is ill-formed.

> +int a1[2](1, 2, 3); // { dg-error "too many initializers" }

> +int a2[](); // { dg-error "array of functions" }

> +int a3[](()); // { dg-error "expected primary-expression" }

> +int a4[]("raccoon"); // { dg-error "invalid conversion" }

> +

> +struct S {

> +  int i;

> +  int j;

> +};

> +

> +S s1(1, 1, 1); // { dg-error "too many initializers" }

> +

> +union U2 {

> +  int a;

> +  float b;

> +};

> +

> +// [dcl.init.aggr]/19:

> +// When a union is initialized with an initializer list, there shall not be

> +// more than one explicitly initialized element.

> +U2 u4 = U2(1, 2); // { dg-error "too many initializers" }

> +

> +// Test there is no brace elision.

> +

> +int a[2][2](1, 2, 3, 4); // { dg-error "too many initializers|array must be initialized with a brace-enclosed initializer" }

> +

> +// private/protected/virtual base class -> not an aggregate.

> +struct B { };

> +struct D : private B {

> +  int i;

> +  int j;

> +};

> +

> +D d({}, 1, 2); // { dg-error "no matching function" }

> +

> +// Private non-static data member -> not an aggregate.

> +struct P {

> +  int i;

> +private:

> +  int j;

> +};

> +

> +P p(1, 2); // { dg-error "no matching function" }

> +

> +// User-declared constructor -> not an aggregate.

> +struct U {

> +  U() {}

> +  int i;

> +  int j;

> +};

> +

> +U u(1, 2); // { dg-error "no matching function" }

> +

> +// virtual member function -> not an aggregate.

> +struct V {

> +  int i;

> +  int j;

> +  virtual int foo(int);

> +};

> +

> +V v(1, 2); // { dg-error "no matching function" }

> +

> +struct nonaggr {

> +  int i;

> +  int j;

> +private:

> +  int x;

> +};

> +

> +struct F {

> +  nonaggr n;

> +  F() : n(1) { } // { dg-error "no matching function" }

> +};

> +

> +struct G {

> +  char a[4];

> +};

> +

> +struct H {

> +  G g;

> +  H() : g("oaks") { } // { dg-error "initializer-string" }

> +};

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init12.C gcc/testsuite/g++.dg/cpp2a/paren-init12.C

> new file mode 100644

> index 00000000000..d7be6f2b55d

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init12.C

> @@ -0,0 +1,17 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +struct A;

> +

> +struct C {

> +  operator A();

> +};

> +

> +struct A {

> +  C c;

> +};

> +

> +C c;

> +A a(c);  // invokes C’s conversion function to A

> +

> +// { dg-final { scan-assembler "_ZN1Ccv1AEv" } }

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init13.C gcc/testsuite/g++.dg/cpp2a/paren-init13.C

> new file mode 100644

> index 00000000000..4b9107c70ff

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init13.C

> @@ -0,0 +1,16 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +struct X { int a, b; };

> +struct Y { X x; };

> +

> +void

> +f()

> +{

> +  // This is ok...

> +  Y y1{{1, 2}};

> +  Y y2({1, 2});

> +  // ...but Y y((1,2)) is not the same as Y y{{1,2}}.  (1, 2) is a

> +  // COMPOUND_EXPR.

> +  Y y3((1, 2)); // { dg-error "could not convert" }

> +}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init14.C gcc/testsuite/g++.dg/cpp2a/paren-init14.C

> new file mode 100644

> index 00000000000..837f9531260

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init14.C

> @@ -0,0 +1,10 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++11 } }

> +

> +struct A {

> +  int x;

> +  int y;

> +  A(int, int) = delete;

> +};

> +

> +A a(1, 2); // { dg-error "use of deleted function" }

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init15.C gcc/testsuite/g++.dg/cpp2a/paren-init15.C

> new file mode 100644

> index 00000000000..4311dd4df59

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init15.C

> @@ -0,0 +1,35 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +struct A {

> +  int i;

> +  int j;

> +};

> +

> +struct B : A

> +{

> +  B (): A(1.7, 2) { }

> +};

> +

> +void f(A);

> +

> +void

> +g ()

> +{

> +  f (A(1, 2));

> +}

> +

> +struct S {

> +  int a[2];

> +};

> +

> +S h() { return S({1, 2}); }

> +

> +struct Z {

> +  int i;

> +  int j;

> +  operator A();

> +};

> +

> +Z z;

> +A a = Z(1, 2.3);

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init16.C gcc/testsuite/g++.dg/cpp2a/paren-init16.C

> new file mode 100644

> index 00000000000..c8ed924877e

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init16.C

> @@ -0,0 +1,14 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +struct S { int a[2]; };

> +struct A { S s[1]; };

> +

> +template <typename, int N>

> +struct R { static constexpr auto h = A({S{N}}); };

> +

> +template <typename, int N>

> +struct R2 { static constexpr auto h = A({S({N, N})}); };

> +

> +A foo = R<int, 10>::h;

> +A foo2 = R2<int, 10>::h;

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init17.C gcc/testsuite/g++.dg/cpp2a/paren-init17.C

> new file mode 100644

> index 00000000000..8e08b5288a5

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init17.C

> @@ -0,0 +1,6 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++11 } }

> +

> +struct A { };

> +struct B { };

> +static_assert (!__is_trivially_constructible(A, B), "");

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init18.C gcc/testsuite/g++.dg/cpp2a/paren-init18.C

> new file mode 100644

> index 00000000000..0aea493f214

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init18.C

> @@ -0,0 +1,9 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +struct A { int a, b, c; };

> +struct S { A a; };

> +constexpr S s{ A(1, 2, 3) };

> +static_assert (s.a.a == 1 && s.a.b == 2 && s.a.c == 3);

> +constexpr S s2 = { A(1, 2, 3) };

> +static_assert (s2.a.a == 1 && s2.a.b == 2 && s2.a.c == 3);

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init19.C gcc/testsuite/g++.dg/cpp2a/paren-init19.C

> new file mode 100644

> index 00000000000..73643fa3aa2

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init19.C

> @@ -0,0 +1,12 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +struct A {

> +  int i[2];

> +};

> +

> +A a({1});

> +A a2({1, 2});

> +A a3(1); // { dg-error "array must be initialized with a brace-enclosed initializer" }

> +A a4 = A(1); // { dg-error "array must be initialized with a brace-enclosed initializer" }

> +A a5 = A({1});

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init2.C gcc/testsuite/g++.dg/cpp2a/paren-init2.C

> new file mode 100644

> index 00000000000..e9e90d7acb6

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init2.C

> @@ -0,0 +1,56 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do run { target c++2a } }

> +

> +struct A {

> +  int i = 0;

> +  int j = 0;

> +};

> +

> +struct B {

> +  A a;

> +  constexpr B() : a(1.1, 2) { }

> +};

> +

> +struct C {

> +  int i;

> +};

> +

> +struct E {

> +  C c;

> +  E() : c(1.2) { }

> +};

> +

> +struct F {

> +  char a[4];

> +};

> +

> +struct G {

> +  F f;

> +  G() : f("yew") { }

> +};

> +

> +struct H {

> +  int i;

> +  int &&r;

> +};

> +

> +int f() { return 42; }

> +

> +struct I {

> +  H h;

> +  I() : h(1, f()) { }

> +};

> +

> +I i;  // dangling ref to f():

> +      // {.i=1, .r=(int &) &TARGET_EXPR <D.2118, f ()>}

> +

> +int

> +main ()

> +{

> +  B b;

> +  if (b.a.i != 1 || b.a.j != 2)

> +    __builtin_abort ();

> +  E e;

> +  if (e.c.i != 1)

> +    __builtin_abort ();

> +}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init3.C gcc/testsuite/g++.dg/cpp2a/paren-init3.C

> new file mode 100644

> index 00000000000..f444005a09f

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init3.C

> @@ -0,0 +1,11 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +class a {

> +  int b{};

> +};

> +class c {

> +  c();

> +  a d;

> +};

> +c::c() {}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init4.C gcc/testsuite/g++.dg/cpp2a/paren-init4.C

> new file mode 100644

> index 00000000000..f8c7bd10b63

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init4.C

> @@ -0,0 +1,142 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do run { target c++2a } }

> +

> +// Test T[]().

> +

> +int i;

> +int a1[](1, 2, 3);

> +static_assert(sizeof(a1) == 3 * sizeof (int), "");

> +int a2[](1.0, 2, 3);

> +static_assert(sizeof(a2) == 3 * sizeof (int), "");

> +int a3[3](1, 2, 3);

> +int a4[3](1, 2); // a4[2] is value-initialized.

> +int a5[](++i, ++i);

> +static_assert(sizeof(a5) == 2 * sizeof (int), "");

> +int a6[](1);

> +static_assert(sizeof(a6) == sizeof (int), "");

> +int a7[]({});

> +static_assert(sizeof(a7) == sizeof (int), "");

> +int a8[]({}, {}, {}, {}, {}, {});

> +static_assert(sizeof(a8) == 6 * sizeof (int), "");

> +int a9[]((1));

> +static_assert(sizeof(a9) == sizeof (int), "");

> +int a10[]((1), (2), (3));

> +static_assert(sizeof(a10) == 3 * sizeof (int), "");

> +int a11[][2]{1};

> +static_assert(sizeof(a11) == 2 * sizeof (int), "");

> +int a12[][2]({1, 2}, {3, 4});

> +static_assert(sizeof(a12) == 4 * sizeof (int), "");

> +

> +const int (&ra1)[](1, 2);

> +const int (&ra2)[](1.0, 2);

> +const int (&ra3)[2](1.0, 2);

> +int (&&rra1)[](1, 2);

> +int (&&rra2)[](1.0, 2);

> +int (&&rra3)[2](1.0, 2);

> +

> +struct S { int i; } s;

> +S s1[]({1});

> +static_assert(sizeof(s1) == sizeof (S), "");

> +S s2[]({1}, {2});

> +static_assert(sizeof(s2) == 2 * sizeof (S), "");

> +S s3[]({1}, {2}, {3});

> +static_assert(sizeof(s3) == 3 * sizeof (S), "");

> +S s4[3]({1}, {2});

> +static_assert(sizeof(s4) == 3 * sizeof (S), "");

> +S s5[]({++i}, {++i});

> +static_assert(sizeof(s2) == 2 * sizeof (S), "");

> +S s6[](s, s);

> +

> +struct R { int i; int j; };

> +R r1[]({1, 2});

> +static_assert(sizeof(r1) == sizeof (R), "");

> +R r2[]({1, 2}, {3, 4});

> +static_assert(sizeof(r2) == 2 * sizeof (R), "");

> +R r3[]({1.0, 2}, {3.2, 4});

> +static_assert(sizeof(r3) == 2 * sizeof (R), "");

> +

> +char c1[]('r');

> +char c2[]('r', 'a', 'c', 'c', 'o', 'o', 'n');

> +char c3[]("yarrow");

> +char c4[4]("oak");

> +char c5[10]("cat");

> +const char (&c6)[4]("eel");

> +

> +int g;

> +struct X {

> +  int i;

> +  X() { ++g; }

> +  X(int) { };

> +};

> +

> +int

> +main ()

> +{

> +  // Here we'll value-initialize l[1] and l[2], which will zero-initialize it.

> +  int l[3](42);

> +  if (l[0] != 42 || l[1] != 0 || l[2] != 0)

> +    __builtin_abort ();

> +

> +  // Here we'll value-initialize x[2] and x[3].  Since X is a class type

> +  // with a user-provided ctor, we'll default-initialize in both cases.

> +  X x[4]({ 1 }, { 2 });

> +  if (g != 2)

> +    __builtin_abort ();

> +

> +  if (a1[0] != 1 || a1[1] != 2 || a1[2] != 3)

> +    __builtin_abort ();

> +  if (a2[0] != 1 || a2[1] != 2 || a2[2] != 3)

> +    __builtin_abort ();

> +  if (a3[0] != 1 || a3[1] != 2 || a3[2] != 3)

> +    __builtin_abort ();

> +  if (a4[0] != 1 || a4[1] != 2 || a4[2] != 0)

> +    __builtin_abort ();

> +  if (a5[0] != 1 || a5[1] != 2)

> +    __builtin_abort ();

> +  if (a6[0] != 1)

> +    __builtin_abort ();

> +  if (a7[0] != 0)

> +    __builtin_abort ();

> +  if (a8[0] != 0 || a8[1] != 0 || a8[2] != 0 || a8[3] != 0

> +      || a8[4] != 0 || a8[5] != 0)

> +    __builtin_abort ();

> +  if (a9[0] != 1)

> +    __builtin_abort ();

> +  if (a10[0] != 1 || a10[1] != 2 || a10[2] != 3)

> +    __builtin_abort ();

> +  if (a11[0][0] != 1 || a11[0][1] != 0)

> +    __builtin_abort ();

> +  if (a12[0][0] != 1 || a12[0][1] != 2 || a12[1][0] != 3 || a12[1][1] != 4)

> +    __builtin_abort ();

> +

> +  if (ra1[0] != 1 || ra1[1] != 2)

> +    __builtin_abort ();

> +  if (ra2[0] != 1 || ra2[1] != 2)

> +    __builtin_abort ();

> +  if (ra3[0] != 1 || ra3[1] != 2)

> +    __builtin_abort ();

> +  if (rra1[0] != 1 || rra1[1] != 2)

> +    __builtin_abort ();

> +  if (rra2[0] != 1 || rra2[1] != 2)

> +    __builtin_abort ();

> +  if (rra3[0] != 1 || rra3[1] != 2)

> +    __builtin_abort ();

> +

> +  if (s1[0].i != 1)

> +    __builtin_abort ();

> +  if (s2[0].i != 1 || s2[1].i != 2)

> +    __builtin_abort ();

> +  if (s3[0].i != 1 || s3[1].i != 2 || s3[2].i != 3)

> +    __builtin_abort ();

> +  if (s4[0].i != 1 || s4[1].i != 2 || s4[2].i != 0)

> +    __builtin_abort ();

> +  if (s5[0].i != 3 || s5[1].i != 4)

> +    __builtin_abort ();

> +

> +  if (r1[0].i != 1 || r1[0].j != 2)

> +    __builtin_abort ();

> +  if (r2[0].i != 1 || r2[0].j != 2 || r2[1].i != 3 || r2[1].j != 4)

> +    __builtin_abort ();

> +  if (r3[0].i != 1 || r3[0].j != 2 || r3[1].i != 3 || r3[1].j != 4)

> +    __builtin_abort ();

> +}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init5.C gcc/testsuite/g++.dg/cpp2a/paren-init5.C

> new file mode 100644

> index 00000000000..a64cb00ebbc

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init5.C

> @@ -0,0 +1,25 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do run { target c++2a } }

> +

> +union U {

> +  int a;

> +  float b;

> +};

> +

> +// u1 has no active member

> +U u1;

> +// u2 zero-initializes the first member, so u2.a is the active member and

> +// its value is 0.

> +U u2 = U();

> +// u3 uses non-list aggregate initialization, so u3.a is the active member

> +// and its value is 1.

> +U u3 = U(1);

> +

> +int

> +main ()

> +{

> +  if (u2.a != 0)

> +    __builtin_abort ();

> +  if (u3.a != 1)

> +    __builtin_abort ();

> +}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init6.C gcc/testsuite/g++.dg/cpp2a/paren-init6.C

> new file mode 100644

> index 00000000000..b5d97dcb3d8

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init6.C

> @@ -0,0 +1,14 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +// Test that we don't perform lifetime extension for () init.

> +

> +struct A {

> +  int a;

> +  int&& r;

> +};

> +

> +int f();

> +A a(1, f());

> +

> +// { dg-final { scan-assembler-not "_ZGR1a" } }

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init7.C gcc/testsuite/g++.dg/cpp2a/paren-init7.C

> new file mode 100644

> index 00000000000..32af1a7265c

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init7.C

> @@ -0,0 +1,20 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do run { target c++2a } }

> +

> +int h;

> +struct i {

> +  i() {}

> +  explicit i(i &) {}

> +  template <typename j> i(j &) { h++; }

> +};

> +

> +int main() {

> +  {

> +    i a[6];

> +    auto [b, c, d, e, f, g] = a;

> +  }

> +  i a[6];

> +  auto [b, c, d, e, f, g](a);

> +  if (h != 6)

> +    __builtin_abort();

> +}

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init8.C gcc/testsuite/g++.dg/cpp2a/paren-init8.C

> new file mode 100644

> index 00000000000..30e71650bc9

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init8.C

> @@ -0,0 +1,13 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +// Test that we don't accept designated inits in ( ).

> +

> +struct S {

> +  int i;

> +  int j = 42;

> +};

> +

> +S s(.i = 12); // { dg-error "expected" }

> +

> +int a[]([0] = 42); // { dg-error "" }

> diff --git gcc/testsuite/g++.dg/cpp2a/paren-init9.C gcc/testsuite/g++.dg/cpp2a/paren-init9.C

> new file mode 100644

> index 00000000000..c44b206feb8

> --- /dev/null

> +++ gcc/testsuite/g++.dg/cpp2a/paren-init9.C

> @@ -0,0 +1,10 @@

> +// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.

> +// { dg-do compile { target c++2a } }

> +

> +struct B { };

> +struct A : B {

> +  int i;

> +};

> +

> +B b;

> +A a(b);

> diff --git gcc/testsuite/g++.dg/ext/desig10.C gcc/testsuite/g++.dg/ext/desig10.C

> index 24057dd0ad6..c8d672b72d9 100644

> --- gcc/testsuite/g++.dg/ext/desig10.C

> +++ gcc/testsuite/g++.dg/ext/desig10.C

> @@ -1,4 +1,4 @@

>   // PR c++/84972

>   // { dg-additional-options "-w" }

>   

> -char(a[])({.a = 0});  // { dg-error "designated initializer" }

> +char(a[])({.a = 0});  // { dg-error "designated initializer|cannot convert" }

> diff --git gcc/testsuite/g++.dg/template/crash107.C gcc/testsuite/g++.dg/template/crash107.C

> index 3b0b4e8211f..9d8d394d93d 100644

> --- gcc/testsuite/g++.dg/template/crash107.C

> +++ gcc/testsuite/g++.dg/template/crash107.C

> @@ -3,7 +3,7 @@

>   // { dg-options "" }

>   // { dg-additional-options "-Wno-return-type" }

>   

> -template<typename FP_> struct Vec { // { dg-message "note" }

> +template<typename FP_> struct Vec { // { dg-message "note" "" { target c++17_down } }

>       Vec& operator^=(Vec& rhs)     {

>           union {

>               struct {FP_ x,y,z;}; // { dg-error "20:anonymous struct" }

> @@ -14,6 +14,6 @@ template<typename FP_> struct Vec { // { dg-message "note" }

>           return Vec(*this)^=rhs; // { dg-message "required" }

>       }

>   };

> -Vec<double> v(3,4,12); // { dg-error "no matching" }

> -Vec<double> V(12,4,3);  // { dg-error "no matching" }

> +Vec<double> v(3,4,12); // { dg-error "no matching|too many initializers" }

> +Vec<double> V(12,4,3);  // { dg-error "no matching|too many initializers" }

>   Vec<double> c = v^V;   // { dg-message "required" }

> diff --git gcc/testsuite/g++.dg/template/crash95.C gcc/testsuite/g++.dg/template/crash95.C

> index f60e635ae66..47346111328 100644

> --- gcc/testsuite/g++.dg/template/crash95.C

> +++ gcc/testsuite/g++.dg/template/crash95.C

> @@ -8,4 +8,4 @@ template < typename > struct S

>     };

>   };

>   

> -S < int > s(0); // { dg-error "no matching" }

> +S < int > s(0); // { dg-error "no matching|too many initializers" }

> diff --git gcc/testsuite/g++.old-deja/g++.jason/crash3.C gcc/testsuite/g++.old-deja/g++.jason/crash3.C

> index e94cc7c9781..c4950531328 100644

> --- gcc/testsuite/g++.old-deja/g++.jason/crash3.C

> +++ gcc/testsuite/g++.old-deja/g++.jason/crash3.C

> @@ -2,12 +2,13 @@

>   // Bug: g++ tries to generate initialization semantics for a Node from an int,

>   // and fails.

>   

> -struct Node			// { dg-message "note" }

> +struct Node			// { dg-message "note" "" { target c++17_down } }

>   {

>     Node* child[2];

>   };

>   

>   void bug(int i)

>   {

> -  Node* q = new Node(i);	// { dg-error "no matching" }

> +  Node* q = new Node(i);	// { dg-error "no matching" "" { target c++17_down }  }

> +// { dg-error "array must be initialized" "" { target c++2a } .-1 }

>   }

> diff --git gcc/testsuite/g++.old-deja/g++.law/ctors11.C gcc/testsuite/g++.old-deja/g++.law/ctors11.C

> index 39ee76b0ae7..b29b18a62a3 100644

> --- gcc/testsuite/g++.old-deja/g++.law/ctors11.C

> +++ gcc/testsuite/g++.old-deja/g++.law/ctors11.C

> @@ -10,12 +10,12 @@ public:

>     inline A(int x){printf("constructing A with %d\n", x);}

>   };

>   

> -class B:public A{ // { dg-message "note" } non-default constructor

> +class B:public A{ // { dg-message "note" "" { target c++17_down } } non-default constructor

>   private:

>   public:

>   };

>   

>   int main()

>   {

> -  B(10);// { dg-error "match" } B doesn't have a constructor taking int

> +  B(10);// { dg-error "match" "" { target c++17_down } } B doesn't have a constructor taking int

>   }

> diff --git gcc/testsuite/g++.old-deja/g++.law/ctors9.C gcc/testsuite/g++.old-deja/g++.law/ctors9.C

> index 43ba1262c95..5856d5dc883 100644

> --- gcc/testsuite/g++.old-deja/g++.law/ctors9.C

> +++ gcc/testsuite/g++.old-deja/g++.law/ctors9.C

> @@ -20,7 +20,7 @@ Foo::Foo(int aa)

>   { }

>   

>   

> -struct var_Foo: public Foo // { dg-message "note" }  base.*// ERROR -  in class.*

> +struct var_Foo: public Foo // { dg-message "note" "" { target c++17_down } }  base.*// ERROR -  in class.*

>   {

>     var_Foo* operator-> () {return this;}

>   };

> @@ -32,7 +32,7 @@ int blort(Foo& f)

>   

>   int main()

>   {

> -  var_Foo b(2);// { dg-error "match" }

> +  var_Foo b(2);// { dg-error "match" "" { target c++17_down } }

>     b->a = 0;

>     int x = blort(b);

>     return x;

> diff --git gcc/testsuite/g++.old-deja/g++.mike/net22.C gcc/testsuite/g++.old-deja/g++.mike/net22.C

> index e5e1cb1081d..abbf6191023 100644

> --- gcc/testsuite/g++.old-deja/g++.mike/net22.C

> +++ gcc/testsuite/g++.old-deja/g++.mike/net22.C

> @@ -5,10 +5,11 @@ public:

>     Parent( char *s ) {}

>   };

>   

> -class Child : public Parent {		// { dg-message "note" } called

> +class Child : public Parent {		// { dg-message "note" "" { target c++17_down } } called

>   };

>   

>   int main() {

> -  Child c( "String initializer" );	// { dg-error "match" } bad

> +  Child c( "String initializer" );	// { dg-error "match" "" { target c++17_down } } bad

> +// { dg-error "forbids converting a string constant" "" { target c++2a } .-1 }

>     return 0;

>   }

> diff --git gcc/testsuite/g++.old-deja/g++.niklas/t128.C gcc/testsuite/g++.old-deja/g++.niklas/t128.C

> index 19e3ca1dab0..0b3346c4ef3 100644

> --- gcc/testsuite/g++.old-deja/g++.niklas/t128.C

> +++ gcc/testsuite/g++.old-deja/g++.niklas/t128.C

> @@ -1,5 +1,5 @@

>   // { dg-do assemble  }

>   // GROUPS niklas uncaught default-construct

>   struct A { A (int); };

> -struct B : A {}; // { dg-message "note" } without ctor // ERROR - candidates

> -void f () { B (0); }// { dg-error "match" } .*

> +struct B : A {}; // { dg-message "note" "" { target c++17_down } } without ctor // ERROR - candidates

> +void f () { B (0); }// { dg-error "match" "" { target c++17_down } } .*

>

Patch

diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c
index 8a66c911aec..91b425c7d78 100644
--- gcc/c-family/c-cppbuiltin.c
+++ gcc/c-family/c-cppbuiltin.c
@@ -990,6 +990,7 @@  c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806");
 	  cpp_define (pfile, "__cpp_impl_destroying_delete=201806");
 	  cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907");
+	  cpp_define (pfile, "__cpp_aggregate_paren_init=201902");
 	}
       if (flag_concepts)
         {
diff --git gcc/cp/call.c gcc/cp/call.c
index 55d2abaaddd..8237afe3175 100644
--- gcc/cp/call.c
+++ gcc/cp/call.c
@@ -105,7 +105,7 @@  struct conversion {
      being bound to an rvalue expression.  If KIND is ck_rvalue or ck_base,
      true when we are treating an lvalue as an rvalue (12.8p33).  If
      ck_identity, we will be binding a reference directly or decaying to
-     a pointer.  */
+     a pointer.  If KIND is ck_aggr, narrowing conversions are permitted.  */
   BOOL_BITFIELD rvaluedness_matches_p: 1;
   BOOL_BITFIELD check_narrowing: 1;
   /* Whether check_narrowing should only check TREE_CONSTANTs; used
@@ -1033,6 +1033,7 @@  build_array_conv (tree type, tree ctor, int flags, tsubst_flags_t complain)
   tree val;
   bool bad = false;
   bool user = false;
+  const bool paren_init = (flags & LOOKUP_AGGREGATE_PAREN_INIT);
   enum conversion_rank rank = cr_exact;
 
   /* We might need to propagate the size from the element to the array.  */
@@ -1069,6 +1070,7 @@  build_array_conv (tree type, tree ctor, int flags, tsubst_flags_t complain)
   c->rank = rank;
   c->user_conv_p = user;
   c->bad_p = bad;
+  c->rvaluedness_matches_p = paren_init;
   c->u.next = build_identity_conv (TREE_TYPE (ctor), ctor);
   return c;
 }
@@ -1953,7 +1955,8 @@  implicit_conversion (tree to, tree from, tree expr, bool c_cast_p,
      resolution, or after we've chosen one.  */
   flags &= (LOOKUP_ONLYCONVERTING|LOOKUP_NO_CONVERSION|LOOKUP_COPY_PARM
 	    |LOOKUP_NO_TEMP_BIND|LOOKUP_NO_RVAL_BIND|LOOKUP_PREFER_RVALUE
-	    |LOOKUP_NO_NARROWING|LOOKUP_PROTECT|LOOKUP_NO_NON_INTEGRAL);
+	    |LOOKUP_NO_NARROWING|LOOKUP_PROTECT|LOOKUP_NO_NON_INTEGRAL
+	    |LOOKUP_AGGREGATE_PAREN_INIT);
 
   /* FIXME: actually we don't want warnings either, but we can't just
      have 'complain &= ~(tf_warning|tf_error)' because it would cause
@@ -7296,7 +7299,14 @@  convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	  return expr;
 	}
       expr = reshape_init (totype, expr, complain);
-      expr = get_target_expr_sfinae (digest_init (totype, expr, complain),
+      /* We want an implicit lookup, but when initializing an aggregate
+	 from a parenthesized list, we must remember not to warn about
+	 narrowing conversions.  */
+      flags = LOOKUP_IMPLICIT;
+      if (convs->rvaluedness_matches_p)
+	flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+      expr = get_target_expr_sfinae (digest_init_flags (totype, expr,
+							flags, complain),
 				     complain);
       if (expr != error_mark_node)
 	TARGET_EXPR_LIST_INIT_P (expr) = true;
@@ -9818,6 +9828,27 @@  build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args,
 
   if (!any_viable_p)
     {
+      /* [dcl.init], 17.6.2.2:
+
+	 Otherwise, if no constructor is viable, the destination type is
+	 a (possibly cv-qualified) aggregate class A, and the initializer
+	 is a parenthesized expression-list, the object is initialized as
+	 follows...
+
+	 We achieve this by building up a CONSTRUCTOR, as for list-init,
+	 and setting LOOKUP_AGGREGATE_PAREN_INIT to distinguish between
+	 the two.  */
+      if ((flags & LOOKUP_AGGREGATE_PAREN_INIT)
+	  && cxx_dialect >= cxx2a
+	  && CP_AGGREGATE_TYPE_P (basetype)
+	  && !user_args->is_empty ())
+	{
+	  /* Create a CONSTRUCTOR from ARGS, e.g. {1, 2} from <1, 2>.  */
+	  tree list = build_tree_list_vec (user_args);
+	  tree ctor = build_constructor_from_list (init_list_type_node, list);
+	  CONSTRUCTOR_IS_DIRECT_INIT (ctor) = true;
+	  return ctor;
+	}
       if (complain & tf_error)
 	complain_about_no_candidates_for_method_call (instance, candidates,
 						      explicit_targs, basetype,
@@ -11447,14 +11478,28 @@  perform_direct_initialization_if_possible (tree type,
      If the destination type is a (possibly cv-qualified) class type:
 
      -- If the initialization is direct-initialization ...,
-     constructors are considered. ... If no constructor applies, or
-     the overload resolution is ambiguous, the initialization is
-     ill-formed.  */
+     constructors are considered.
+
+       -- If overload resolution is successful, the selected constructor
+       is called to initialize the object, with the initializer expression
+       or expression-list as its argument(s).
+
+       -- Otherwise, if no constructor is viable, the destination type is
+       a (possibly cv-qualified) aggregate class A, and the initializer is
+       a parenthesized expression-list, the object is initialized as
+       follows...  */
   if (CLASS_TYPE_P (type))
     {
       releasing_vec args (make_tree_vector_single (expr));
+      int flags = LOOKUP_NORMAL | LOOKUP_AGGREGATE_PAREN_INIT;
       expr = build_special_member_call (NULL_TREE, complete_ctor_identifier,
-					&args, type, LOOKUP_NORMAL, complain);
+					&args, type, flags, complain);
+      if (BRACE_ENCLOSED_INITIALIZER_P (expr))
+	{
+	  gcc_assert (cxx_dialect >= cxx2a);
+	  return finish_compound_literal (type, expr, complain,
+					  fcl_functional_paren);
+	}
       return build_cplus_new (type, expr, complain);
     }
 
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index e9d54466289..220520cbd9d 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -5550,6 +5550,8 @@  enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
 #define LOOKUP_ALLOW_FLEXARRAY_INIT (LOOKUP_DELEGATING_CONS << 1)
 /* Require constant initialization of a non-constant variable.  */
 #define LOOKUP_CONSTINIT (LOOKUP_ALLOW_FLEXARRAY_INIT << 1)
+/* We're initializing an aggregate from a parenthesized list of values.  */
+#define LOOKUP_AGGREGATE_PAREN_INIT (LOOKUP_CONSTINIT << 1)
 
 #define LOOKUP_NAMESPACES_ONLY(F)  \
   (((F) & LOOKUP_PREFER_NAMESPACES) && !((F) & LOOKUP_PREFER_TYPES))
@@ -7111,8 +7113,9 @@  extern cp_expr finish_unary_op_expr		(location_t, enum tree_code, cp_expr,
 						 tsubst_flags_t);
 /* Whether this call to finish_compound_literal represents a C++11 functional
    cast or a C99 compound literal.  */
-enum fcl_t { fcl_functional, fcl_c99 };
-extern tree finish_compound_literal		(tree, tree, tsubst_flags_t, fcl_t = fcl_functional);
+enum fcl_t { fcl_functional, fcl_functional_paren, fcl_c99 };
+extern tree finish_compound_literal		(tree, tree, tsubst_flags_t,
+						 fcl_t = fcl_functional);
 extern tree finish_fname			(tree);
 extern void finish_translation_unit		(void);
 extern tree finish_template_type_parm		(tree, tree);
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 52f1f020dc3..df017bbde28 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -5502,11 +5502,27 @@  grok_reference_init (tree decl, tree type, tree init, int flags)
       return NULL_TREE;
     }
 
+  tree ttype = TREE_TYPE (type);
   if (TREE_CODE (init) == TREE_LIST)
-    init = build_x_compound_expr_from_list (init, ELK_INIT,
-					    tf_warning_or_error);
+    {
+      /* This handles (C++20 only) code like
+
+	   const A& r(1, 2, 3);
+
+	 where we treat the parenthesized list as a CONSTRUCTOR.  */
+      if (TREE_TYPE (init) == NULL_TREE
+	  && CP_AGGREGATE_TYPE_P (ttype)
+	  && !DECL_DECOMPOSITION_P (decl)
+	  && (cxx_dialect >= cxx2a))
+	{
+	  init = build_constructor_from_list (init_list_type_node, init);
+	  flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+	}
+      else
+	init = build_x_compound_expr_from_list (init, ELK_INIT,
+						tf_warning_or_error);
+    }
 
-  tree ttype = TREE_TYPE (type);
   if (TREE_CODE (ttype) != ARRAY_TYPE
       && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE)
     /* Note: default conversion is only called in very special cases.  */
@@ -6612,6 +6628,41 @@  check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
 	      flags |= LOOKUP_NO_NARROWING;
 	    }
 	}
+      /* [dcl.init] "Otherwise, if the destination type is an array, the object
+	 is initialized as follows..."  So handle things like
+
+	  int a[](1, 2, 3);
+
+	 which is permitted in C++20 by P0960.  */
+      else if (TREE_CODE (init) == TREE_LIST
+	       && TREE_TYPE (init) == NULL_TREE
+	       && TREE_CODE (type) == ARRAY_TYPE
+	       && !DECL_DECOMPOSITION_P (decl)
+	       && (cxx_dialect >= cxx2a))
+	{
+	  /* [dcl.init.string] "An array of ordinary character type [...]
+	     can be initialized by an ordinary string literal [...] by an
+	     appropriately-typed string literal enclosed in braces" only
+	     talks about braces, but GCC has always accepted
+
+	       char a[]("foobar");
+
+	     so we continue to do so.  */
+	  tree val = TREE_VALUE (init);
+	  if (TREE_CHAIN (init) == NULL_TREE
+	      && char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type)))
+	      && TREE_CODE (tree_strip_any_location_wrapper (val))
+		 == STRING_CST)
+	    /* If the list has a single element and it's a string literal,
+	       then it's the initializer for the array as a whole.  */
+	    init = val;
+	  else
+	    {
+	      init = build_constructor_from_list (init_list_type_node, init);
+	      CONSTRUCTOR_IS_DIRECT_INIT (init) = true;
+	      flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+	    }
+	}
       else if (TREE_CODE (init) == TREE_LIST
 	       && TREE_TYPE (init) != unknown_type_node
 	       && !MAYBE_CLASS_TYPE_P (type))
@@ -6676,6 +6727,14 @@  check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
 		}
 	      init_code = NULL_TREE;
 	    }
+	  else if (BRACE_ENCLOSED_INITIALIZER_P (init_code))
+	    {
+	      /* In C++20, the call to build_aggr_init could have created
+		 a CONSTRUCTOR to handle A(1, 2).  */
+	      gcc_assert (cxx_dialect >= cxx2a);
+	      init = init_code;
+	      flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+	    }
 	  else
 	    init = NULL_TREE;
 	}
diff --git gcc/cp/init.c gcc/cp/init.c
index 1c51e26febe..a101d81e61f 100644
--- gcc/cp/init.c
+++ gcc/cp/init.c
@@ -923,8 +923,33 @@  perform_member_init (tree member, tree init)
 		inform (DECL_SOURCE_LOCATION (member),
 			"%q#D should be initialized", member );
 	    }
-	  finish_expr_stmt (build_aggr_init (decl, init, flags,
-					     tf_warning_or_error));
+	  init = build_aggr_init (decl, init, flags, tf_warning_or_error);
+	  /* In C++20, a member initializer list can be initializing an
+	     aggregate from a parenthesized list of values:
+
+	       struct S {
+		 A aggr;
+		 S() : aggr(1, 2, 3) { }
+	       };
+
+	      Build up an INIT_EXPR like we do for aggr{1, 2, 3}, so that
+	      build_data_member_initialization can grok it.  */
+	  if (cxx_dialect >= cxx2a)
+	    {
+	      tree t = init;
+	      while (TREE_CODE (t) == EXPR_STMT
+		     || TREE_CODE (t) == CONVERT_EXPR)
+		t = TREE_OPERAND (t, 0);
+	      if (BRACE_ENCLOSED_INITIALIZER_P (t))
+		{
+		  t = digest_init_flags (type, t,
+					 (LOOKUP_IMPLICIT
+					  | LOOKUP_AGGREGATE_PAREN_INIT),
+					 tf_warning_or_error);
+		  init = build2 (INIT_EXPR, type, decl, t);
+		}
+	    }
+	  finish_expr_stmt (init);
 	}
     }
   else
@@ -1969,8 +1994,22 @@  expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
       tree ctor_name = (true_exp == exp
 			? complete_ctor_identifier : base_ctor_identifier);
 
+      /* Given class A,
+
+	   A a(1, 2);
+
+	 can mean a call to a constructor A::A(int, int), if present.  If not,
+	 but A is an aggregate, we will try aggregate initialization.  */
+      flags |= LOOKUP_AGGREGATE_PAREN_INIT;
       rval = build_special_member_call (exp, ctor_name, &parms, binfo, flags,
 					complain);
+      if (BRACE_ENCLOSED_INITIALIZER_P (rval))
+	{
+	  gcc_assert (cxx_dialect >= cxx2a);
+	  /* So that we do finish_expr_stmt below.  Don't return here, we
+	     need to release PARMS.  */
+	  TREE_SIDE_EFFECTS (rval) = 1;
+	}
     }
 
   if (parms != NULL)
diff --git gcc/cp/semantics.c gcc/cp/semantics.c
index 59def3170ab..64341d0006a 100644
--- gcc/cp/semantics.c
+++ gcc/cp/semantics.c
@@ -2956,8 +2956,10 @@  finish_compound_literal (tree type, tree compound_literal,
       if (type == error_mark_node)
 	return error_mark_node;
     }
-  compound_literal = digest_init_flags (type, compound_literal,
-					LOOKUP_NORMAL | LOOKUP_NO_NARROWING,
+  int flags = LOOKUP_NORMAL | LOOKUP_NO_NARROWING;
+  if (fcl_context == fcl_functional_paren)
+    flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+  compound_literal = digest_init_flags (type, compound_literal, flags,
 					complain);
   if (compound_literal == error_mark_node)
     return error_mark_node;
diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
index 2402c38fdf3..c4560e99759 100644
--- gcc/cp/typeck2.c
+++ gcc/cp/typeck2.c
@@ -845,7 +845,11 @@  store_init_value (tree decl, tree init, vec<tree, va_gc>** cleanups, int flags)
   value = braced_lists_to_strings (type, value);
 
   current_ref_temp_count = 0;
-  value = extend_ref_init_temps (decl, value, cleanups);
+  /* [dcl.init] When initializing an aggregate from a parenthesized list
+     of values... a temporary object bound to a reference does not have
+     its lifetime extended.  */
+  if (!(flags & LOOKUP_AGGREGATE_PAREN_INIT))
+    value = extend_ref_init_temps (decl, value, cleanups);
 
   /* In C++11 constant expression is a semantic, not syntactic, property.
      In C++98, make sure that what we thought was a constant expression at
@@ -1218,7 +1222,9 @@  digest_init_r (tree type, tree init, int nested, int flags,
   if ((code != COMPLEX_TYPE || BRACE_ENCLOSED_INITIALIZER_P (stripped_init))
       && (SCALAR_TYPE_P (type) || code == REFERENCE_TYPE))
     {
-      if (nested)
+      /* Narrowing is OK when initializing an aggregate from
+	 a parenthesized list.  */
+      if (nested && !(flags & LOOKUP_AGGREGATE_PAREN_INIT))
 	flags |= LOOKUP_NO_NARROWING;
       init = convert_for_initialization (0, type, init, flags,
 					 ICR_INIT, NULL_TREE, 0,
@@ -1266,8 +1272,13 @@  digest_init_r (tree type, tree init, int nested, int flags,
 		inform (loc, "remove %<{ }%> around initializer");
 	    }
 	  else if (flag_checking)
-	    /* We should have fixed this in reshape_init.  */
-	    gcc_unreachable ();
+	    /* We should have fixed this in reshape_init.  Except that we
+	       don't reshape parenthesized lists where brace elision is
+	       not permitted.  */
+	    {
+	      gcc_assert (flags & LOOKUP_AGGREGATE_PAREN_INIT);
+	      return error_mark_node;
+	    }
 	}
     }
 
@@ -1378,9 +1389,12 @@  static tree
 massage_init_elt (tree type, tree init, int nested, int flags,
 		  tsubst_flags_t complain)
 {
-  flags &= LOOKUP_ALLOW_FLEXARRAY_INIT;
-  flags |= LOOKUP_IMPLICIT;
-  init = digest_init_r (type, init, nested ? 2 : 1, flags, complain);
+  int new_flags = LOOKUP_IMPLICIT;
+  if (flags & LOOKUP_ALLOW_FLEXARRAY_INIT)
+    new_flags |= LOOKUP_ALLOW_FLEXARRAY_INIT;
+  if (flags & LOOKUP_AGGREGATE_PAREN_INIT)
+    new_flags |= LOOKUP_AGGREGATE_PAREN_INIT;
+  init = digest_init_r (type, init, nested ? 2 : 1, new_flags, complain);
   /* Strip a simple TARGET_EXPR when we know this is an initializer.  */
   if (SIMPLE_TARGET_EXPR_P (init))
     init = TARGET_EXPR_INITIAL (init);
@@ -2337,10 +2351,17 @@  build_functional_cast (tree exp, tree parms, tsubst_flags_t complain)
   releasing_vec parmvec;
   for (; parms != NULL_TREE; parms = TREE_CHAIN (parms))
     vec_safe_push (parmvec, TREE_VALUE (parms));
+  int flags = LOOKUP_NORMAL | LOOKUP_AGGREGATE_PAREN_INIT;
   exp = build_special_member_call (NULL_TREE, complete_ctor_identifier,
-				   &parmvec, type, LOOKUP_NORMAL, complain);
+				   &parmvec, type, flags, complain);
 
-  if (exp == error_mark_node)
+  if (BRACE_ENCLOSED_INITIALIZER_P (exp))
+    {
+      gcc_assert (cxx_dialect >= cxx2a);
+      return finish_compound_literal (type, exp, complain,
+				      fcl_functional_paren);
+    }
+  else if (exp == error_mark_node)
     return error_mark_node;
 
   return build_cplus_new (type, exp, complain);
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-99.C gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
index 8d791dd032d..4d3953d0983 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-99.C
@@ -9,5 +9,6 @@  struct A
 struct B
 {
   A a;
-    constexpr B() : a(0) {} // { dg-error "no matching function" }
+  // P0960R3 allows paren-init.
+  constexpr B() : a(0) {} // { dg-error "no matching function" "" { target c++17_down } }
 };
diff --git gcc/testsuite/g++.dg/cpp0x/explicit7.C gcc/testsuite/g++.dg/cpp0x/explicit7.C
index 574796d117d..67b50542bc3 100644
--- gcc/testsuite/g++.dg/cpp0x/explicit7.C
+++ gcc/testsuite/g++.dg/cpp0x/explicit7.C
@@ -10,7 +10,7 @@  struct A { };
 struct B: A { };
 struct C {
   explicit operator B*();	// { dg-message "explicit" }
-  explicit operator B&();	// { dg-message "explicit" }
+  explicit operator B&();	// { dg-message "explicit" "" { target c++17_down } }
 };
 
 C c;
diff --git gcc/testsuite/g++.dg/cpp0x/initlist12.C gcc/testsuite/g++.dg/cpp0x/initlist12.C
index f72a6dac525..c2a2b86520f 100644
--- gcc/testsuite/g++.dg/cpp0x/initlist12.C
+++ gcc/testsuite/g++.dg/cpp0x/initlist12.C
@@ -6,15 +6,15 @@  struct A
   int i;
 };
 
-A a({1,2});			// { dg-error "no match" }
+A a({1,2});			// { dg-error "no match|cannot convert" }
 
 union U
 {
   int i,j;
 };
 
-U u({1,2});			// { dg-error "no match" }
+U u({1,2});			// { dg-error "no match|cannot convert" }
 
 union V {};
 
-V v({1});			// { dg-error "no match" }
+V v({1});			// { dg-error "no match|too many initializers" }
diff --git gcc/testsuite/g++.dg/cpp0x/pr31437.C gcc/testsuite/g++.dg/cpp0x/pr31437.C
index 7e2ed7a1375..532b533c8d3 100644
--- gcc/testsuite/g++.dg/cpp0x/pr31437.C
+++ gcc/testsuite/g++.dg/cpp0x/pr31437.C
@@ -1,9 +1,9 @@ 
 // { dg-do compile { target c++11 } }
-template <typename... T> struct A // { dg-message "candidates|A" }
+template <typename... T> struct A // { dg-message "candidates|A" "" { target c++17_down } }
 {
   A(T* p) {  // { dg-error "parameter packs|T" }
    (A<T...>*)(p); 
   }
 };
 
-A<int> a(0); // { dg-error "no matching" }
+A<int> a(0); // { dg-error "no matching|too many initializers" }
diff --git gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
index 95251c2f5c6..736207aa635 100644
--- gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
+++ gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
@@ -460,6 +460,12 @@ 
 #  error "__cpp_constexpr_dynamic_alloc != 201907"
 #endif
 
+#ifndef __cpp_aggregate_paren_init
+#  error "__cpp_aggregate_paren_init"
+#elif __cpp_aggregate_paren_init != 201902
+#  error "__cpp_aggregate_paren_init != 201902"
+#endif
+
 #ifdef __has_cpp_attribute
 
 #  if ! __has_cpp_attribute(maybe_unused)
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init1.C gcc/testsuite/g++.dg/cpp2a/paren-init1.C
new file mode 100644
index 00000000000..a54b2ccaf6d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init1.C
@@ -0,0 +1,116 @@ 
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+struct A {
+  int i;
+  int j;
+  int k;
+};
+
+struct B {
+  int i;
+  int j;
+  int k = 42;
+};
+
+struct C {
+  A a;
+};
+
+struct D {
+  A a1;
+  A a2;
+};
+
+struct E {
+  int i;
+};
+
+// F has a base class, but it's not virtual, private, or protected, so this is
+// still an aggregate (since C++17).
+struct F : E {
+  int j;
+  int k;
+};
+
+F f({1}, 2, 3);
+
+// A non-virtual member function doesn't make it a non-aggregate.
+struct G {
+  int i;
+  double j;
+  int foo(int, int);
+};
+
+G g(1, 2.14);
+
+class H {
+public:
+  H (int) { }
+};
+
+class I : public H { };
+
+int i;
+A a1(1, 2);
+A a2(1.0, 2);
+A a3(++i, ++i);
+const A& ra(1, 2);
+
+A ca = A(1, 2);
+A ca2 = A(1.0, 2);
+A ca3 = A(++i, ++i);
+const A& rca = A(1, 2);
+
+B b1(1, 2, 3);
+B b2(1, 2);
+B b3(1);
+
+C c1({5, 6, 7});
+D d1({1, 2, 3}, {5, 6, 7});
+
+struct W {
+  const char *s, *t;
+};
+W w1("fluffer", "nutter");
+
+struct Y {
+  char a[4];
+};
+Y y("yew");
+
+int
+main ()
+{
+  I(10);
+
+  // A::k will be value-initialized.
+  if (a1.i != 1 || a1.j != 2 || a1.k != 0)
+    __builtin_abort ();
+  if (a2.i != 1 || a2.j != 2 || a2.k != 0)
+    __builtin_abort ();
+  if (a3.i != 1 || a3.j != 2 || a3.k != 0)
+    __builtin_abort ();
+  if (ra.i != 1 || ra.j != 2 || ra.k != 0)
+    __builtin_abort ();
+  if (ca.i != 1 || ca.j != 2 || ca.k != 0)
+    __builtin_abort ();
+  if (ca2.i != 1 || ca2.j != 2 || ca2.k != 0)
+    __builtin_abort ();
+  if (ca3.i != 3 || ca3.j != 4 || ca3.k != 0)
+    __builtin_abort ();
+
+  if (b1.i != 1 || b1.j != 2 || b1.k != 3)
+    __builtin_abort ();
+  // The default member initializer will be used for B::k.
+  if (b2.i != 1 || b2.j != 2 || b2.k != 42)
+    __builtin_abort ();
+  if (b3.i != 1 || b3.j != 0 || b3.k != 42)
+    __builtin_abort ();
+
+  if (c1.a.i != 5 || c1.a.j != 6 || c1.a.k != 7)
+    __builtin_abort ();
+
+  if (f.i != 1 || f.j != 2 || f.k != 3)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init10.C gcc/testsuite/g++.dg/cpp2a/paren-init10.C
new file mode 100644
index 00000000000..5c70d9d59ee
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init10.C
@@ -0,0 +1,18 @@ 
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test from [dcl.init].
+
+struct A {
+  int a;
+  int&& r;
+};
+
+int f();
+int n = 10;
+
+A a1{1, f()};               // OK, lifetime is extended
+A a2(1, f());               // well-formed, but dangling reference
+A a3{1.0, 1};               // { dg-error "narrowing conversion" }
+A a4(1.0, 1);               // well-formed, but dangling reference
+A a5(1.0, static_cast<int&&>(n));    // OK
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init11.C gcc/testsuite/g++.dg/cpp2a/paren-init11.C
new file mode 100644
index 00000000000..82ca2669545
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init11.C
@@ -0,0 +1,88 @@ 
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test ill-formed code.
+
+// If k is greater than the size of the array, the program is ill-formed.
+int a1[2](1, 2, 3); // { dg-error "too many initializers" }
+int a2[](); // { dg-error "array of functions" }
+int a3[](()); // { dg-error "expected primary-expression" }
+int a4[]("raccoon"); // { dg-error "invalid conversion" }
+
+struct S {
+  int i;
+  int j;
+};
+
+S s1(1, 1, 1); // { dg-error "too many initializers" }
+
+union U2 {
+  int a;
+  float b;
+};
+
+// [dcl.init.aggr]/19:
+// When a union is initialized with an initializer list, there shall not be
+// more than one explicitly initialized element.
+U2 u4 = U2(1, 2); // { dg-error "too many initializers" }
+
+// Test there is no brace elision.
+
+int a[2][2](1, 2, 3, 4); // { dg-error "too many initializers|array must be initialized with a brace-enclosed initializer" }
+
+// private/protected/virtual base class -> not an aggregate.
+struct B { };
+struct D : private B {
+  int i;
+  int j;
+};
+
+D d({}, 1, 2); // { dg-error "no matching function" }
+
+// Private non-static data member -> not an aggregate.
+struct P {
+  int i;
+private:
+  int j;
+};
+
+P p(1, 2); // { dg-error "no matching function" }
+
+// User-declared constructor -> not an aggregate.
+struct U {
+  U() {}
+  int i;
+  int j;  
+};
+
+U u(1, 2); // { dg-error "no matching function" }
+
+// virtual member function -> not an aggregate.
+struct V {
+  int i;
+  int j;
+  virtual int foo(int);
+};
+
+V v(1, 2); // { dg-error "no matching function" }
+
+struct nonaggr {
+  int i;
+  int j;
+private:
+  int x;
+};
+
+struct F {
+  nonaggr n;
+  F() : n(1) { } // { dg-error "no matching function" }
+};
+
+struct G {
+  char a[4];
+};
+
+struct H {
+  G g;
+  H() : g("oaks") { } // { dg-error "initializer-string" }
+};
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init12.C gcc/testsuite/g++.dg/cpp2a/paren-init12.C
new file mode 100644
index 00000000000..d7be6f2b55d
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init12.C
@@ -0,0 +1,17 @@ 
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct A;
+
+struct C {
+  operator A();
+};
+
+struct A {
+  C c;
+};
+
+C c;
+A a(c);  // invokes C’s conversion function to A
+
+// { dg-final { scan-assembler "_ZN1Ccv1AEv" } }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init13.C gcc/testsuite/g++.dg/cpp2a/paren-init13.C
new file mode 100644
index 00000000000..4b9107c70ff
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init13.C
@@ -0,0 +1,16 @@ 
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct X { int a, b; };
+struct Y { X x; };
+
+void
+f()
+{
+  // This is ok...
+  Y y1{{1, 2}};
+  Y y2({1, 2});
+  // ...but Y y((1,2)) is not the same as Y y{{1,2}}.  (1, 2) is a
+  // COMPOUND_EXPR.
+  Y y3((1, 2)); // { dg-error "could not convert" }
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init14.C gcc/testsuite/g++.dg/cpp2a/paren-init14.C
new file mode 100644
index 00000000000..837f9531260
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init14.C
@@ -0,0 +1,10 @@ 
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++11 } }
+
+struct A {
+  int x;
+  int y;
+  A(int, int) = delete;
+};
+
+A a(1, 2); // { dg-error "use of deleted function" }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init15.C gcc/testsuite/g++.dg/cpp2a/paren-init15.C
new file mode 100644
index 00000000000..7464b1cc78a
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init15.C
@@ -0,0 +1,35 @@ 
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct A {
+  int i;
+  int j;
+};
+
+struct B : A
+{
+  B (): A(1.7, 2) { }
+};
+
+void f(A);
+
+void
+g ()
+{
+  f (A(1, 2));
+}
+
+struct S {
+  int a[2];
+};
+
+S h() { return S(1, 2.2); }
+
+struct Z {
+  int i;
+  int j;
+  operator A();
+};
+
+Z z;
+A a = Z(1, 2.3);
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init16.C gcc/testsuite/g++.dg/cpp2a/paren-init16.C
new file mode 100644
index 00000000000..a59cbf5e20e
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init16.C
@@ -0,0 +1,14 @@ 
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct S { int a[2]; };
+struct A { S s[1]; };
+
+template <typename, int N>
+struct R { static constexpr auto h = A(S{N}); };
+
+template <typename, int N>
+struct R2 { static constexpr auto h = A(S({N, N})); };
+
+A foo = R<int, 10>::h;
+A foo2 = R2<int, 10>::h;
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init2.C gcc/testsuite/g++.dg/cpp2a/paren-init2.C
new file mode 100644
index 00000000000..e9e90d7acb6
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init2.C
@@ -0,0 +1,56 @@ 
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+struct A {
+  int i = 0;
+  int j = 0;
+};
+
+struct B {
+  A a;
+  constexpr B() : a(1.1, 2) { }
+};
+
+struct C {
+  int i;
+};
+
+struct E {
+  C c;
+  E() : c(1.2) { }
+};
+
+struct F {
+  char a[4];
+};
+
+struct G {
+  F f;
+  G() : f("yew") { }
+};
+
+struct H {
+  int i;
+  int &&r;
+};
+
+int f() { return 42; }
+
+struct I {
+  H h;
+  I() : h(1, f()) { }
+};
+
+I i;  // dangling ref to f():
+      // {.i=1, .r=(int &) &TARGET_EXPR <D.2118, f ()>}
+
+int
+main ()
+{
+  B b;
+  if (b.a.i != 1 || b.a.j != 2)
+    __builtin_abort ();
+  E e;
+  if (e.c.i != 1)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init3.C gcc/testsuite/g++.dg/cpp2a/paren-init3.C
new file mode 100644
index 00000000000..f444005a09f
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init3.C
@@ -0,0 +1,11 @@ 
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+class a {
+  int b{};
+};
+class c {
+  c();
+  a d;
+};
+c::c() {}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init4.C gcc/testsuite/g++.dg/cpp2a/paren-init4.C
new file mode 100644
index 00000000000..f8c7bd10b63
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init4.C
@@ -0,0 +1,142 @@ 
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+// Test T[]().
+
+int i;
+int a1[](1, 2, 3);
+static_assert(sizeof(a1) == 3 * sizeof (int), "");
+int a2[](1.0, 2, 3);
+static_assert(sizeof(a2) == 3 * sizeof (int), "");
+int a3[3](1, 2, 3);
+int a4[3](1, 2); // a4[2] is value-initialized.
+int a5[](++i, ++i);
+static_assert(sizeof(a5) == 2 * sizeof (int), "");
+int a6[](1);
+static_assert(sizeof(a6) == sizeof (int), "");
+int a7[]({});
+static_assert(sizeof(a7) == sizeof (int), "");
+int a8[]({}, {}, {}, {}, {}, {});
+static_assert(sizeof(a8) == 6 * sizeof (int), "");
+int a9[]((1));
+static_assert(sizeof(a9) == sizeof (int), "");
+int a10[]((1), (2), (3));
+static_assert(sizeof(a10) == 3 * sizeof (int), "");
+int a11[][2]{1};
+static_assert(sizeof(a11) == 2 * sizeof (int), "");
+int a12[][2]({1, 2}, {3, 4});
+static_assert(sizeof(a12) == 4 * sizeof (int), "");
+
+const int (&ra1)[](1, 2);
+const int (&ra2)[](1.0, 2);
+const int (&ra3)[2](1.0, 2);
+int (&&rra1)[](1, 2);
+int (&&rra2)[](1.0, 2);
+int (&&rra3)[2](1.0, 2);
+
+struct S { int i; } s;
+S s1[]({1});
+static_assert(sizeof(s1) == sizeof (S), "");
+S s2[]({1}, {2});
+static_assert(sizeof(s2) == 2 * sizeof (S), "");
+S s3[]({1}, {2}, {3});
+static_assert(sizeof(s3) == 3 * sizeof (S), "");
+S s4[3]({1}, {2});
+static_assert(sizeof(s4) == 3 * sizeof (S), "");
+S s5[]({++i}, {++i});
+static_assert(sizeof(s2) == 2 * sizeof (S), "");
+S s6[](s, s);
+
+struct R { int i; int j; };
+R r1[]({1, 2});
+static_assert(sizeof(r1) == sizeof (R), "");
+R r2[]({1, 2}, {3, 4});
+static_assert(sizeof(r2) == 2 * sizeof (R), "");
+R r3[]({1.0, 2}, {3.2, 4});
+static_assert(sizeof(r3) == 2 * sizeof (R), "");
+
+char c1[]('r');
+char c2[]('r', 'a', 'c', 'c', 'o', 'o', 'n');
+char c3[]("yarrow");
+char c4[4]("oak");
+char c5[10]("cat");
+const char (&c6)[4]("eel");
+
+int g;
+struct X {
+  int i;
+  X() { ++g; }
+  X(int) { };
+};
+
+int
+main ()
+{
+  // Here we'll value-initialize l[1] and l[2], which will zero-initialize it.
+  int l[3](42);
+  if (l[0] != 42 || l[1] != 0 || l[2] != 0)
+    __builtin_abort ();
+
+  // Here we'll value-initialize x[2] and x[3].  Since X is a class type
+  // with a user-provided ctor, we'll default-initialize in both cases.
+  X x[4]({ 1 }, { 2 });
+  if (g != 2)
+    __builtin_abort ();
+
+  if (a1[0] != 1 || a1[1] != 2 || a1[2] != 3)
+    __builtin_abort ();
+  if (a2[0] != 1 || a2[1] != 2 || a2[2] != 3)
+    __builtin_abort ();
+  if (a3[0] != 1 || a3[1] != 2 || a3[2] != 3)
+    __builtin_abort ();
+  if (a4[0] != 1 || a4[1] != 2 || a4[2] != 0)
+    __builtin_abort ();
+  if (a5[0] != 1 || a5[1] != 2)
+    __builtin_abort ();
+  if (a6[0] != 1)
+    __builtin_abort ();
+  if (a7[0] != 0)
+    __builtin_abort ();
+  if (a8[0] != 0 || a8[1] != 0 || a8[2] != 0 || a8[3] != 0
+      || a8[4] != 0 || a8[5] != 0)
+    __builtin_abort ();
+  if (a9[0] != 1)
+    __builtin_abort ();
+  if (a10[0] != 1 || a10[1] != 2 || a10[2] != 3)
+    __builtin_abort ();
+  if (a11[0][0] != 1 || a11[0][1] != 0)
+    __builtin_abort ();
+  if (a12[0][0] != 1 || a12[0][1] != 2 || a12[1][0] != 3 || a12[1][1] != 4)
+    __builtin_abort ();
+
+  if (ra1[0] != 1 || ra1[1] != 2)
+    __builtin_abort ();
+  if (ra2[0] != 1 || ra2[1] != 2)
+    __builtin_abort ();
+  if (ra3[0] != 1 || ra3[1] != 2)
+    __builtin_abort ();
+  if (rra1[0] != 1 || rra1[1] != 2)
+    __builtin_abort ();
+  if (rra2[0] != 1 || rra2[1] != 2)
+    __builtin_abort ();
+  if (rra3[0] != 1 || rra3[1] != 2)
+    __builtin_abort ();
+
+  if (s1[0].i != 1)
+    __builtin_abort ();
+  if (s2[0].i != 1 || s2[1].i != 2)
+    __builtin_abort ();
+  if (s3[0].i != 1 || s3[1].i != 2 || s3[2].i != 3)
+    __builtin_abort ();
+  if (s4[0].i != 1 || s4[1].i != 2 || s4[2].i != 0)
+    __builtin_abort ();
+  if (s5[0].i != 3 || s5[1].i != 4)
+    __builtin_abort ();
+
+  if (r1[0].i != 1 || r1[0].j != 2)
+    __builtin_abort ();
+  if (r2[0].i != 1 || r2[0].j != 2 || r2[1].i != 3 || r2[1].j != 4)
+    __builtin_abort ();
+  if (r3[0].i != 1 || r3[0].j != 2 || r3[1].i != 3 || r3[1].j != 4)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init5.C gcc/testsuite/g++.dg/cpp2a/paren-init5.C
new file mode 100644
index 00000000000..a64cb00ebbc
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init5.C
@@ -0,0 +1,25 @@ 
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+union U {
+  int a;
+  float b;
+};
+
+// u1 has no active member
+U u1;
+// u2 zero-initializes the first member, so u2.a is the active member and
+// its value is 0.
+U u2 = U();
+// u3 uses non-list aggregate initialization, so u3.a is the active member
+// and its value is 1.
+U u3 = U(1);
+
+int
+main ()
+{
+  if (u2.a != 0)
+    __builtin_abort ();
+  if (u3.a != 1)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init6.C gcc/testsuite/g++.dg/cpp2a/paren-init6.C
new file mode 100644
index 00000000000..b5d97dcb3d8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init6.C
@@ -0,0 +1,14 @@ 
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test that we don't perform lifetime extension for () init.
+
+struct A {
+  int a;
+  int&& r;
+};
+
+int f();
+A a(1, f());
+
+// { dg-final { scan-assembler-not "_ZGR1a" } }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init7.C gcc/testsuite/g++.dg/cpp2a/paren-init7.C
new file mode 100644
index 00000000000..32af1a7265c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init7.C
@@ -0,0 +1,20 @@ 
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do run { target c++2a } }
+
+int h;
+struct i {
+  i() {}
+  explicit i(i &) {}
+  template <typename j> i(j &) { h++; }
+};
+
+int main() {
+  {
+    i a[6];
+    auto [b, c, d, e, f, g] = a;
+  }
+  i a[6];
+  auto [b, c, d, e, f, g](a);
+  if (h != 6)
+    __builtin_abort();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init8.C gcc/testsuite/g++.dg/cpp2a/paren-init8.C
new file mode 100644
index 00000000000..30e71650bc9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init8.C
@@ -0,0 +1,13 @@ 
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+// Test that we don't accept designated inits in ( ).
+
+struct S {
+  int i;
+  int j = 42;
+};
+
+S s(.i = 12); // { dg-error "expected" }
+
+int a[]([0] = 42); // { dg-error "" }
diff --git gcc/testsuite/g++.dg/cpp2a/paren-init9.C gcc/testsuite/g++.dg/cpp2a/paren-init9.C
new file mode 100644
index 00000000000..c44b206feb8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/paren-init9.C
@@ -0,0 +1,10 @@ 
+// PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
+// { dg-do compile { target c++2a } }
+
+struct B { };
+struct A : B {
+  int i;
+};
+
+B b;
+A a(b);
diff --git gcc/testsuite/g++.dg/ext/desig10.C gcc/testsuite/g++.dg/ext/desig10.C
index 24057dd0ad6..c8d672b72d9 100644
--- gcc/testsuite/g++.dg/ext/desig10.C
+++ gcc/testsuite/g++.dg/ext/desig10.C
@@ -1,4 +1,4 @@ 
 // PR c++/84972
 // { dg-additional-options "-w" }
 
-char(a[])({.a = 0});  // { dg-error "designated initializer" }
+char(a[])({.a = 0});  // { dg-error "designated initializer|cannot convert" }
diff --git gcc/testsuite/g++.dg/template/crash107.C gcc/testsuite/g++.dg/template/crash107.C
index 3b0b4e8211f..9d8d394d93d 100644
--- gcc/testsuite/g++.dg/template/crash107.C
+++ gcc/testsuite/g++.dg/template/crash107.C
@@ -3,7 +3,7 @@ 
 // { dg-options "" }
 // { dg-additional-options "-Wno-return-type" }
 
-template<typename FP_> struct Vec { // { dg-message "note" }
+template<typename FP_> struct Vec { // { dg-message "note" "" { target c++17_down } }
     Vec& operator^=(Vec& rhs)     {
         union {
             struct {FP_ x,y,z;}; // { dg-error "20:anonymous struct" }
@@ -14,6 +14,6 @@  template<typename FP_> struct Vec { // { dg-message "note" }
         return Vec(*this)^=rhs; // { dg-message "required" }
     }
 };
-Vec<double> v(3,4,12); // { dg-error "no matching" }
-Vec<double> V(12,4,3);  // { dg-error "no matching" }
+Vec<double> v(3,4,12); // { dg-error "no matching|too many initializers" }
+Vec<double> V(12,4,3);  // { dg-error "no matching|too many initializers" }
 Vec<double> c = v^V;   // { dg-message "required" }
diff --git gcc/testsuite/g++.dg/template/crash95.C gcc/testsuite/g++.dg/template/crash95.C
index f60e635ae66..47346111328 100644
--- gcc/testsuite/g++.dg/template/crash95.C
+++ gcc/testsuite/g++.dg/template/crash95.C
@@ -8,4 +8,4 @@  template < typename > struct S
   };
 };
 
-S < int > s(0); // { dg-error "no matching" }
+S < int > s(0); // { dg-error "no matching|too many initializers" }
diff --git gcc/testsuite/g++.old-deja/g++.law/ctors11.C gcc/testsuite/g++.old-deja/g++.law/ctors11.C
index 39ee76b0ae7..b29b18a62a3 100644
--- gcc/testsuite/g++.old-deja/g++.law/ctors11.C
+++ gcc/testsuite/g++.old-deja/g++.law/ctors11.C
@@ -10,12 +10,12 @@  public:
   inline A(int x){printf("constructing A with %d\n", x);}
 };
 
-class B:public A{ // { dg-message "note" } non-default constructor
+class B:public A{ // { dg-message "note" "" { target c++17_down } } non-default constructor
 private:
 public:
 };
 
 int main()
 {
-  B(10);// { dg-error "match" } B doesn't have a constructor taking int
+  B(10);// { dg-error "match" "" { target c++17_down } } B doesn't have a constructor taking int
 }
diff --git gcc/testsuite/g++.old-deja/g++.law/ctors9.C gcc/testsuite/g++.old-deja/g++.law/ctors9.C
index 43ba1262c95..5856d5dc883 100644
--- gcc/testsuite/g++.old-deja/g++.law/ctors9.C
+++ gcc/testsuite/g++.old-deja/g++.law/ctors9.C
@@ -20,7 +20,7 @@  Foo::Foo(int aa)
 { }
 
 
-struct var_Foo: public Foo // { dg-message "note" }  base.*// ERROR -  in class.*
+struct var_Foo: public Foo // { dg-message "note" "" { target c++17_down } }  base.*// ERROR -  in class.*
 {
   var_Foo* operator-> () {return this;}
 };
@@ -32,7 +32,7 @@  int blort(Foo& f)
 
 int main()
 {
-  var_Foo b(2);// { dg-error "match" } 
+  var_Foo b(2);// { dg-error "match" "" { target c++17_down } } 
   b->a = 0;
   int x = blort(b);
   return x;
diff --git gcc/testsuite/g++.old-deja/g++.mike/net22.C gcc/testsuite/g++.old-deja/g++.mike/net22.C
index e5e1cb1081d..abbf6191023 100644
--- gcc/testsuite/g++.old-deja/g++.mike/net22.C
+++ gcc/testsuite/g++.old-deja/g++.mike/net22.C
@@ -5,10 +5,11 @@  public:
   Parent( char *s ) {}
 };
 
-class Child : public Parent {		// { dg-message "note" } called
+class Child : public Parent {		// { dg-message "note" "" { target c++17_down } } called
 };
 
 int main() {
-  Child c( "String initializer" );	// { dg-error "match" } bad
+  Child c( "String initializer" );	// { dg-error "match" "" { target c++17_down } } bad
+// { dg-error "forbids converting a string constant" "" { target c++2a } .-1 }
   return 0;
 }
diff --git gcc/testsuite/g++.old-deja/g++.niklas/t128.C gcc/testsuite/g++.old-deja/g++.niklas/t128.C
index 19e3ca1dab0..0b3346c4ef3 100644
--- gcc/testsuite/g++.old-deja/g++.niklas/t128.C
+++ gcc/testsuite/g++.old-deja/g++.niklas/t128.C
@@ -1,5 +1,5 @@ 
 // { dg-do assemble  }
 // GROUPS niklas uncaught default-construct
 struct A { A (int); };
-struct B : A {}; // { dg-message "note" } without ctor // ERROR - candidates
-void f () { B (0); }// { dg-error "match" } .*
+struct B : A {}; // { dg-message "note" "" { target c++17_down } } without ctor // ERROR - candidates
+void f () { B (0); }// { dg-error "match" "" { target c++17_down } } .*