This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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


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.

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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]