[PATCH] c++: aggregate CTAD and brace elision [PR101344]

Jason Merrill jason@redhat.com
Mon Aug 16 20:05:26 GMT 2021


On 8/16/21 3:06 PM, Patrick Palka wrote:
> During aggregate CTAD, collect_ctor_idx_types always recurses into a
> sub-CONSTRUCTOR, regardless of whether the corresponding pair of braces
> was elided in the original initializer.  This causes us to reject some
> completely-braced forms of aggregate CTAD as in the first testcase
> below, because collect_ctor_idx_types effectively assumes that the given
> initializer is always minimally-braced (hence the aggregate deduction
> candidate is given a function type that's incompatible with the written
> initializer).
> 
> This patch fixes this by making reshape_init flag CONSTRUCTORs that
> were built to undo brace elision in the original CONSTRUCTOR, so that
> collect_ctor_idx_types can determine whether to recurse into a
> sub-CONSTRUCTOR by simply inspecting this flag.
> 
> This happens to also fix PR101820, which is about aggregate CTAD using
> designated initializers, for a similar reason as above.
> 
> A tricky case is the "intermediately-braced" initialization of 'e3'
> in the first testcase below.  It seems to me we're correct to continue
> to reject this according to [over.match.class.deduct]/1 because here
> the initializer element {1, 2, 3, 4} corresponds to the subobject E::t,
> hence the type T_1 of the first funciton parameter of the aggregate
> deduction candidate is T(&&)[2][2] which the argument {1, 2, 3, 4} isn't
> compatible with (as opposed to say T(&&)[4]).
> 
> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
> trunk/11?

OK.

> 	PR c++/101344
> 	PR c++/101820
> 
> gcc/cp/ChangeLog:
> 
> 	* cp-tree.h (CONSTRUCTOR_BRACES_ELIDED_P): Define.
> 	* decl.c (reshape_init_r): Set it.
> 	* pt.c (collect_ctor_idx_types): Recurse into a sub-CONSTRUCTOR
> 	iff CONSTRUCTOR_BRACES_ELIDED_P.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp2a/class-deduction-aggr11.C: New test.
> 	* g++.dg/cpp2a/class-deduction-aggr12.C: New test.
> ---
>   gcc/cp/cp-tree.h                              |  6 ++++
>   gcc/cp/decl.c                                 | 18 +++++++++---
>   gcc/cp/pt.c                                   |  7 +----
>   .../g++.dg/cpp2a/class-deduction-aggr11.C     | 29 +++++++++++++++++++
>   .../g++.dg/cpp2a/class-deduction-aggr12.C     | 15 ++++++++++
>   5 files changed, 65 insertions(+), 10 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr11.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr12.C
> 
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index bd3f12a393e..8cbf6cc30b0 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -4502,6 +4502,12 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
>   #define CONSTRUCTOR_IS_PAREN_INIT(NODE) \
>     (CONSTRUCTOR_CHECK(NODE)->base.private_flag)
>   
> +/* True if reshape_init built this CONSTRUCTOR to undo the brace elision
> +   of another CONSTRUCTOR.  This flag is used during C++20 aggregate
> +   CTAD.  */
> +#define CONSTRUCTOR_BRACES_ELIDED_P(NODE) \
> +  (CONSTRUCTOR_CHECK (NODE)->base.protected_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) \
> diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
> index b3671ee8956..9e257b32e18 100644
> --- a/gcc/cp/decl.c
> +++ b/gcc/cp/decl.c
> @@ -6650,7 +6650,8 @@ reshape_init_r (tree type, reshape_iter *d, tree first_initializer_p,
>     /* A non-aggregate type is always initialized with a single
>        initializer.  */
>     if (!CP_AGGREGATE_TYPE_P (type)
> -      /* As is an array with dependent bound.  */
> +      /* As is an array with dependent bound, which we can see
> +	 during C++20 aggregate CTAD.  */
>         || (cxx_dialect >= cxx20
>   	  && TREE_CODE (type) == ARRAY_TYPE
>   	  && uses_template_parms (TYPE_DOMAIN (type))))
> @@ -6767,6 +6768,7 @@ reshape_init_r (tree type, reshape_iter *d, tree first_initializer_p,
>        initializer already, and there is not a CONSTRUCTOR, it means that there
>        is a missing set of braces (that is, we are processing the case for
>        which reshape_init exists).  */
> +  bool braces_elided_p = false;
>     if (!first_initializer_p)
>       {
>         if (TREE_CODE (stripped_init) == CONSTRUCTOR)
> @@ -6802,17 +6804,25 @@ reshape_init_r (tree type, reshape_iter *d, tree first_initializer_p,
>   	warning (OPT_Wmissing_braces,
>   		 "missing braces around initializer for %qT",
>   		 type);
> +      braces_elided_p = true;
>       }
>   
>     /* Dispatch to specialized routines.  */
> +  tree new_init;
>     if (CLASS_TYPE_P (type))
> -    return reshape_init_class (type, d, first_initializer_p, complain);
> +    new_init = reshape_init_class (type, d, first_initializer_p, complain);
>     else if (TREE_CODE (type) == ARRAY_TYPE)
> -    return reshape_init_array (type, d, first_initializer_p, complain);
> +    new_init = reshape_init_array (type, d, first_initializer_p, complain);
>     else if (VECTOR_TYPE_P (type))
> -    return reshape_init_vector (type, d, complain);
> +    new_init = reshape_init_vector (type, d, complain);
>     else
>       gcc_unreachable();
> +
> +  if (braces_elided_p)
> +    if (TREE_CODE (new_init) == CONSTRUCTOR)
> +      CONSTRUCTOR_BRACES_ELIDED_P (new_init) = 1;
> +
> +  return new_init;
>   }
>   
>   /* Undo the brace-elision allowed by [dcl.init.aggr] in a
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index 5ac89901e22..65b06d978b6 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -28836,12 +28836,7 @@ collect_ctor_idx_types (tree ctor, tree list, tree elt = NULL_TREE)
>       {
>         tree ftype = elt ? elt : TREE_TYPE (idx);
>         if (BRACE_ENCLOSED_INITIALIZER_P (val)
> -	  && CONSTRUCTOR_NELTS (val)
> -	  /* As in reshape_init_r, a non-aggregate or array-of-dependent-bound
> -	     type gets a single initializer.  */
> -	  && CP_AGGREGATE_TYPE_P (ftype)
> -	  && !(TREE_CODE (ftype) == ARRAY_TYPE
> -	       && uses_template_parms (TYPE_DOMAIN (ftype))))
> +	  && CONSTRUCTOR_BRACES_ELIDED_P (val))
>   	{
>   	  tree subelt = NULL_TREE;
>   	  if (TREE_CODE (ftype) == ARRAY_TYPE)
> diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr11.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr11.C
> new file mode 100644
> index 00000000000..c4806de56af
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr11.C
> @@ -0,0 +1,29 @@
> +// PR c++/101344
> +// { dg-do compile { target c++20 } }
> +
> +template<class T=void>
> +struct A { int m; int t[2]; };
> +
> +A a1{1, {2, 3}}; // previously rejected
> +A a2{1, 2, 3};
> +
> +struct B { int x, y; };
> +
> +template<class T=void>
> +struct C { int m; struct { int x, y; } t; };
> +
> +A b1{1, {2, 3}}; // previously rejected
> +A b2{1, 2, 3};
> +
> +template<class T>
> +struct D { T t[2]; };
> +
> +D d1{1, 2};
> +D d2{{1, 2}}; // previously rejected
> +
> +template<class T>
> +struct E { T t[2][2]; };
> +
> +E e1{1, 2, 3, 4};
> +E e2{{{1, 2}, {3, 4}}}; // previously rejected
> +E e3{{1, 2, 3, 4}}; // { dg-error "deduction|no match" }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr12.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr12.C
> new file mode 100644
> index 00000000000..ebe73c1d817
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-aggr12.C
> @@ -0,0 +1,15 @@
> +// PR c++/101820
> +// { dg-do compile { target c++20 } }
> +
> +struct Inner { int i = 0; };
> +
> +template <typename T = void>
> +struct Outer { Inner s{}; };
> +
> +Outer o1{ .s = {} };                // works
> +Outer o2{ .s = Inner{ .i = 1} };    // works
> +Outer o3{ .s = { .i = 1} };         // does not
> +
> +Outer o4{ .s{} };                   // works
> +Outer o5{ .s{Inner{ .i = 1} } };    // works
> +Outer o6{ .s{ .i = 1} };            // does not
> 



More information about the Gcc-patches mailing list