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  <pola...@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 } } .*


Reply via email to