[PATCH 3/3] c++: P1997 array-copy extensions: Assignment, return, etc. [PR103238]

Jason Merrill jason@redhat.com
Mon Nov 29 22:44:02 GMT 2021


On 11/21/21 21:51, Will Wray via Gcc-patches wrote:
> This second patch completes the work of the first 'array-copy' patch to
> provide first-cut implementations of all P1997 features. It adds:
> 
>   * Assignments to arrays from array values,    a = b;
>   * Placeholder auto in array declarations,     auto cp[] = a;
>   * Array as a return type from functions WIP,  auto f() -> T[N];
>   * Parsing of array pseudo-destructors         a.~A()
>     (only parsing for now, untested)

Let's not accept the pseudo-destructor and silently ignore it.  Either 
we should delay the parsing change until the semantics are implemented, 
or parse it and give a sorry().

> Assignments a = b were easily allowed by changing branch conditions.
> Assignments a = {e...} were trickier (a case not mentioned in P1997):
> 
>      int a[16]; a = {0,1,1,2}; a = {}; // assignments from init-lists
> 
> The semantics is the same as for struct aggregates:
> (1) Aggregate initialization of an rhs array of the lhs type
>      (so trailing elements with no initializer are value initialized)
> (2) Copy-initialization of the lhs from the rhs.
> 
> The special case of an optionally-braced array value is allowed so that
> a = b and a = {b} are generally equivalent for same type arrays a and b.
> However, the now special-special case of assignment from a braced string-
> literal currently only supports exact-match (same as for other arrays):
> 
>      char a[4]; a={"c++"} /* OK */; a={"c"} /* FAILs but should work */;
> 
> Array return from function is work in progress. The tests show what works.
> I'm stuck in unfamiliar territory so it's best to submit what I have to be
> reviewed for hints on how to progress.

Let's split out array return into a separate patch for now.  It also has 
ABI implications.

> Please try the patch; play, stress it, and report the FAILS.
> 
> 	PR c++/103238
> 
> gcc/c/ChangeLog:
> 
> 	* c-decl.c (grokdeclarator): Don't complain of array returns.
> 
> gcc/cp/ChangeLog:
> 
> 	* call.c (can_convert_array): Extend to include array inits.
> 	(standard_conversion): No decay for same-type array. Call build_conv.
> 	(implicit_conversion_1): Call reshape_init for arrays too.
> 	* decl.c (grokdeclarator): Don't complain of array returns.
> 	* parser.c (cp_parser_postfix_dot_deref_expression): parse array ~A().
> 	* pt.c (tsubst_function_type): Array type return is not a failure.
> 	(do_auto_deduction): Placeholder auto deduction of array element type.
> 	* tree.c (lvalue_kind): clk_class should include array (I think?).
> 	* typeck.c (cp_build_modify_expr): Call reshape init to strip optional
> 	braces. Allow NOP_EXPR for array assignment.
> 	(convert_for_assignment): New if-block for same-type array convert,
> 	strips optional braces, but rejects STRING_CST rhs shorter than lhs.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/init/array-copy10.C: New test. auto[] deduce 'after' PASSes
> 	* g++.dg/init/array-copy11.C: New test. Array return 'before' XFAILs
> 	* g++.dg/init/array-copy12.C: New test. Array return 'after' PASSes
> 	* g++.dg/init/array-copy7.C: New test. Array assign 'before' XFAILs
> 	* g++.dg/init/array-copy8.C: New test. Array assign 'after' PASSes
> 	* g++.dg/init/array-copy9.C: New test. auto[] deduce 'before' XFAILs
> ---
>   gcc/c/c-decl.c                           |  2 +-
>   gcc/cp/call.c                            | 43 +++++++++++------
>   gcc/cp/decl.c                            |  2 +-
>   gcc/cp/parser.c                          |  4 +-
>   gcc/cp/pt.c                              | 13 +++++-
>   gcc/cp/tree.c                            |  3 +-
>   gcc/cp/typeck.c                          | 26 +++++++++--
>   gcc/testsuite/g++.dg/init/array-copy10.C | 57 +++++++++++++++++++++++
>   gcc/testsuite/g++.dg/init/array-copy11.C | 13 ++++++
>   gcc/testsuite/g++.dg/init/array-copy12.C | 79 ++++++++++++++++++++++++++++++++
>   gcc/testsuite/g++.dg/init/array-copy7.C  | 40 ++++++++++++++++
>   gcc/testsuite/g++.dg/init/array-copy8.C  | 56 ++++++++++++++++++++++
>   gcc/testsuite/g++.dg/init/array-copy9.C  | 57 +++++++++++++++++++++++
>   13 files changed, 372 insertions(+), 23 deletions(-)
> 
> diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
> index 3e28a038095..031c43d189f 100644
> --- a/gcc/c/c-decl.c
> +++ b/gcc/c/c-decl.c
> @@ -7055,7 +7055,7 @@ grokdeclarator (const struct c_declarator *declarator,
>   			    "returning a function");
>   		type = integer_type_node;
>   	      }
> -	    if (TREE_CODE (type) == ARRAY_TYPE)
> +	    if (TREE_CODE (type) == ARRAY_TYPE && !flag_array_copy)
>   	      {
>   		if (name)
>   		  error_at (loc, "%qE declared as function returning an array",
> diff --git a/gcc/cp/call.c b/gcc/cp/call.c
> index 4ee21c7bdbd..c73fb73d86e 100644
> --- a/gcc/cp/call.c
> +++ b/gcc/cp/call.c
> @@ -908,29 +908,34 @@ static bool
>   can_convert_array (tree atype, tree from, int flags, tsubst_flags_t complain)
>   {
>     tree elttype = TREE_TYPE (atype);
> -  unsigned i;
>   
>     if (TREE_CODE (from) == CONSTRUCTOR)
>       {
> -      for (i = 0; i < CONSTRUCTOR_NELTS (from); ++i)
> +      for (auto&& ce : CONSTRUCTOR_ELTS (from))
>   	{
> -	  tree val = CONSTRUCTOR_ELT (from, i)->value;
> -	  bool ok;
> -	  if (TREE_CODE (elttype) == ARRAY_TYPE)
> -	    ok = can_convert_array (elttype, val, flags, complain);
> -	  else
> -	    ok = can_convert_arg (elttype, TREE_TYPE (val), val, flags,
> -				  complain);
> -	  if (!ok)
> +	  tree val = tree_strip_any_location_wrapper (ce.value);
> +	  if ((TREE_CODE (val) == STRING_CST
> +	       && !can_convert_array (elttype, val, flags, complain))
> +	      || (TREE_CODE (elttype) == ARRAY_TYPE
> +		  ? !can_convert_array (elttype, val, flags, complain)
> +		  : !can_convert_arg (elttype, TREE_TYPE (val), val, flags,
> +				      complain)))

I don't understand this change; you add STRING_CST handling but handle 
it with the same call as an array elttype, with which the old code 
should have already handled string constants.  What are you trying to do 
here?

>   	    return false;
>   	}
>         return true;
>       }
>   
> +  from = tree_strip_any_location_wrapper (from);
> +
>     if (char_type_p (TYPE_MAIN_VARIANT (elttype))
> -      && TREE_CODE (tree_strip_any_location_wrapper (from)) == STRING_CST)
> +      && TREE_CODE (from) == STRING_CST)
>       return array_string_literal_compatible_p (atype, from);
>   
> +  if (flag_array_copy
> +      && TREE_CODE (from) == ARRAY_TYPE)
> +    return same_type_ignoring_top_level_qualifiers_p (atype,
> +			   tree_strip_any_location_wrapper (from));

Previously 'from' was an expression; here you're treating it as a type. 
  This 'if' will never succeed.

> +
>     /* No other valid way to aggregate initialize an array.  */
>     return false;
>   }
> @@ -1241,7 +1246,10 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
>     tcode = TREE_CODE (to);
>   
>     conv = build_identity_conv (from, expr);
> -  if (fcode == FUNCTION_TYPE || fcode == ARRAY_TYPE)
> +  if (fcode == FUNCTION_TYPE
> +      || (fcode == ARRAY_TYPE
> +	  && !(flag_array_copy && tcode == ARRAY_TYPE
> +	       && same_type_ignoring_top_level_qualifiers_p (to, from))))
>       {
>         from = type_decays_to (from);
>         fcode = TREE_CODE (from);
> @@ -1538,6 +1546,10 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
>     else if (fcode == VECTOR_TYPE && tcode == VECTOR_TYPE
>   	   && vector_types_convertible_p (from, to, false))
>       return build_conv (ck_std, to, conv);
> +  else if (flag_array_copy
> +	   && fcode == ARRAY_TYPE && tcode == ARRAY_TYPE
> +	   && same_type_ignoring_top_level_qualifiers_p (from, to))
> +    return build_conv (ck_std, to, conv);

Rather than change two places in this function, let's do this just 
before the call to type_decays_to in the previous hunk.

>     else if (MAYBE_CLASS_TYPE_P (to) && MAYBE_CLASS_TYPE_P (from)
>   	   && is_properly_derived_from (from, to))
>       {
> @@ -2015,9 +2027,10 @@ implicit_conversion_1 (tree to, tree from, tree expr, bool c_cast_p,
>   
>     /* Call reshape_init early to remove redundant braces.  */
>     if (expr && BRACE_ENCLOSED_INITIALIZER_P (expr)
> -      && CLASS_TYPE_P (to)
> -      && COMPLETE_TYPE_P (complete_type (to))
> -      && !CLASSTYPE_NON_AGGREGATE (to))
> +      && ((CLASS_TYPE_P (to)
> +	  && COMPLETE_TYPE_P (complete_type (to))
> +	  && !CLASSTYPE_NON_AGGREGATE (to))
> +	 || (flag_array_copy && TREE_CODE (to) == ARRAY_TYPE)))
>       {
>         expr = reshape_init (to, expr, complain);
>         if (expr == error_mark_node)
> diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
> index 1010fa2c53f..872403b1ac1 100644
> --- a/gcc/cp/decl.c
> +++ b/gcc/cp/decl.c
> @@ -12719,7 +12719,7 @@ grokdeclarator (const cp_declarator *declarator,
>   			  "a function", name);
>   		return error_mark_node;
>   	      }
> -	    if (TREE_CODE (type) == ARRAY_TYPE)
> +	    if (TREE_CODE (type) == ARRAY_TYPE && !flag_array_copy)
>   	      {
>   		error_at (typespec_loc, "%qs declared as function returning "
>   			  "an array", name);
> diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
> index 65f0f112011..5dd6126dc50 100644
> --- a/gcc/cp/parser.c
> +++ b/gcc/cp/parser.c
> @@ -8120,7 +8120,9 @@ cp_parser_postfix_dot_deref_expression (cp_parser *parser,
>        we must be looking at a pseudo-destructor-name.  If POSTFIX_EXPRESSION
>        is type dependent, it can be pseudo-destructor-name or something else.
>        Try to parse it as pseudo-destructor-name first.  */
> -  if ((scope && SCALAR_TYPE_P (scope)) || dependent_p)
> +  if ((scope && (SCALAR_TYPE_P (scope)
> +		 || (flag_array_copy && TREE_CODE (scope) == ARRAY_TYPE)))
> +      || dependent_p) /* TODO: P1997 array pseudo-destructor.  */
>       {
>         tree s;
>         tree type;
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index b27eea33272..66ed90699cd 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -15152,7 +15152,7 @@ tsubst_function_type (tree t,
>       return error_mark_node;
>     /* DR 486 clarifies that creation of a function type with an
>        invalid return type is a deduction failure.  */
> -  if (TREE_CODE (return_type) == ARRAY_TYPE
> +  if ((TREE_CODE (return_type) == ARRAY_TYPE && !flag_array_copy)
>         || TREE_CODE (return_type) == FUNCTION_TYPE)
>       {
>         if (complain & tf_error)
> @@ -29812,6 +29812,17 @@ do_auto_deduction (tree type, tree init, tree auto_node,
>         targs = make_tree_vec (1);
>         TREE_VEC_ELT (targs, 0) = TREE_TYPE (init);
>       }
> +  else if (flag_array_copy
> +	   && (context == adc_variable_type
> +	       || context == adc_return_type)
> +	   && auto_node == TREE_TYPE (type)
> +	   && !TYPE_REF_P (type)

I think this line should check TREE_CODE (type) == ARRAY_TYPE; we also 
don't want this case to handle auto*.

This isn't doing what the proposal says, which involves template 
argument deduction of reference-to-array type, but the wording in the 
proposal won't handle deducing the array bound, so it's fine to go with 
this simple approach for now.

> +	   && init != error_mark_node
> +	   && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE)
> +    {
> +      targs = make_tree_vec (1);
> +      TREE_VEC_ELT (targs, 0) = TREE_TYPE (TREE_TYPE (init));
> +    }
>     else if (AUTO_IS_DECLTYPE (auto_node))
>       {
>         tree stripped_init = tree_strip_any_location_wrapper (init);
> diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
> index 32ddf835a91..e10c574de94 100644
> --- a/gcc/cp/tree.c
> +++ b/gcc/cp/tree.c
> @@ -280,7 +280,8 @@ lvalue_kind (const_tree ref)
>   
>       case CALL_EXPR:
>         /* We can see calls outside of TARGET_EXPR in templates.  */
> -      if (CLASS_TYPE_P (TREE_TYPE (ref)))
> +      if (CLASS_TYPE_P (TREE_TYPE (ref))
> +	  || TREE_CODE (TREE_TYPE (ref)) == ARRAY_TYPE)
>   	return clk_class;
>         return clk_none;
>   
> diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
> index 8e96a925186..e1a9446fd0f 100644
> --- a/gcc/cp/typeck.c
> +++ b/gcc/cp/typeck.c
> @@ -9105,9 +9105,11 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
>       {
>         int from_array;
>   
> +      if (flag_array_copy && BRACE_ENCLOSED_INITIALIZER_P (newrhs))
> +	newrhs = reshape_init (lhstype, newrhs, complain);

Let's move this into the BRACE_ENCLOSED_INITIALIZER_P block just below.

>         if (BRACE_ENCLOSED_INITIALIZER_P (newrhs))
>   	{
> -	  if (modifycode != INIT_EXPR)
> +	  if (modifycode != INIT_EXPR && !flag_array_copy)
>   	    {
>   	      if (complain & tf_error)
>   		error_at (loc,
> @@ -9127,7 +9129,8 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
>         else if ((TREE_CODE (tree_strip_any_location_wrapper (newrhs))
>   		== STRING_CST)
>   	       && char_type_p (TREE_TYPE (TYPE_MAIN_VARIANT (lhstype)))
> -	       && modifycode == INIT_EXPR)
> +	       && (modifycode == INIT_EXPR
> +		   || (modifycode == NOP_EXPR && flag_array_copy)))
>   	{
>   	  newrhs = digest_init (lhstype, newrhs, complain);
>   	  if (newrhs == error_mark_node)

I'm surprised there's no change to the following code, a bit lower in 
the function:
>       /* Allow array assignment in compiler-generated code.  */
>       else if (!current_function_decl
>                || !DECL_DEFAULTED_FN (current_function_decl))

> @@ -9682,7 +9685,24 @@ convert_for_assignment (tree type, tree rhs,
>         rhs = mark_rvalue_use (rhs);
>         return convert (type, rhs);
>       }
> -
> +  /* Deal with array-valued rhs of same type as lhs 'type', optionally braced.
> +     This includes STRING_CST, but only of same type - i.e. same size;
> +     TODO: P1997 convert STRING_CST shorter than 'type' to full size.  */
> +  if (flag_array_copy && TREE_CODE (type) == ARRAY_TYPE)
> +    {
> +      if (same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (rhs)))
> +	{
> +	  rhs = mark_rvalue_use (rhs);
> +	  return convert (type, rhs);
> +	}
> +      if (BRACE_ENCLOSED_INITIALIZER_P (rhs)
> +	  && CONSTRUCTOR_NELTS (rhs) == 1
> +	  && same_type_ignoring_top_level_qualifiers_p (type,
> +	     TREE_TYPE (tree_strip_any_location_wrapper (
> +					CONSTRUCTOR_ELT (rhs, 0)->value))))
> +	return convert (type, mark_rvalue_use (
> +					CONSTRUCTOR_ELT (rhs, 0)->value));
> +    }

This hunk shouldn't be needed; I think instead you want to change the 
code in reshape_init_r that handles { e }:
>   /* "If T is a class type and the initializer list has a single element of                                    
>      type cv U, where U is T or a class derived from T, the object is                                          
>      initialized from that element."  Even if T is an aggregate.  */


>     if (rhs == error_mark_node || rhstype == error_mark_node)
>       return error_mark_node;
>     if (TREE_CODE (rhs) == TREE_LIST && TREE_VALUE (rhs) == error_mark_node)
> diff --git a/gcc/testsuite/g++.dg/init/array-copy10.C b/gcc/testsuite/g++.dg/init/array-copy10.C
> new file mode 100644
> index 00000000000..5fc2df727fa
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/init/array-copy10.C
> @@ -0,0 +1,57 @@
> +// PR c++/103238 (array-copy extensions for P1997)
> +// Array auto placeholder element deduction compile tests
> +// (c.f. array-copy9.C for compile fail tests)
> +
> +// { dg-do compile { target c++11 } }
> +// { dg-additional-options "-farray-copy" }
> +
> +template <typename...> struct same{ enum:bool{v=false}; };
> +template <typename T> struct same<T,T>{ enum:bool{v=true}; };
> +template <typename T, typename X, typename...Y>
> +struct same<T,X,Y...>{ enum:bool{v = same<T,X>::v && same<T,Y...>::v}; };
> +template<typename...T>
> +constexpr bool all_same_t(T const&...){return same<T...>::v;}
> +
> +typedef int int2[2];
> +int2 a {11,66};
> +
> +auto er[2] {1,2};
> +// { dg-error "direct-list-initialization of .auto. requires exactly one element .\\-fpermissive." "" { target *-*-* } .-1 }
> +// { dg-error "deducing from brace-enclosed initializer list requires" "" { target *-*-* } .-2 }

These errors are wrong with this extension, we certainly shouldn't test 
for them.

> +auto il[2] = {1,2};
> +// { dg-error "deducing from brace-enclosed initializer list requires" "" { target *-*-* } .-1 }
> +auto um[2] = {a};
> +// { dg-error "deducing from brace-enclosed initializer list requires" "" { target *-*-* } .-1 }
> +
> +// Initializations from from rvalue array
> +auto g[2] = int2{11,66};
> +auto j[2] ( int2{11,66} );  // P0960
> +auto l[2] { int2{11,66} };
> +// auto + size deduction :
> +auto m[] = int2{11,66};
> +auto n[] ( int2{11,66} );  // P0960
> +auto p[] { int2{11,66} };
> +
> +static_assert( all_same_t(a,g,j,l,m,n,p), "");
> +
> +auto str[] = "str";
> +
> +auto cpy[4] = str;
> +auto cpp[4] (str); // P0960
> +auto cpw[4] {str};
> +// auto + size deduction :
> +auto cpu[] = str;
> +auto cpv[] (str); // P0960
> +auto cpx[] {str};
> +
> +static_assert( all_same_t(str,cpy,cpp,cpw,cpu,cpv,cpx), "");
> +
> +auto const& ref = "str";
> +
> +auto rpy[4] = ref;
> +auto rpp[4] (ref); // P0960
> +auto rpw[4] {ref};
> +// auto + size deduction :
> +auto rpu[] = ref;
> +auto rpv[] (ref); // P0960
> +auto rpx[] {ref};
> diff --git a/gcc/testsuite/g++.dg/init/array-copy11.C b/gcc/testsuite/g++.dg/init/array-copy11.C
> new file mode 100644
> index 00000000000..ac4d0c0dcb8
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/init/array-copy11.C
> @@ -0,0 +1,13 @@
> +// PR c++/103238 (array-copy extensions for P1997)
> +// Array function return nocompile tests -
> +// should give same errors before and after the array-copy patch.
> +
> +// { dg-do compile { target c++11 } }
> +//
> +
> +constexpr auto a() -> char[2] {return "a";} // { dg-error "declared as function returning an array" }
> +
> +constexpr char char2(char,char)[2]; // { dg-error "declared as function returning an array" }
> +constexpr auto char2(char,char) -> char[2]; // { dg-error "declared as function returning an array" }
> +
> +constexpr char char2(char a,char b)[2] {return{a,b};} // { dg-error "declared as function returning an array" }
> diff --git a/gcc/testsuite/g++.dg/init/array-copy12.C b/gcc/testsuite/g++.dg/init/array-copy12.C
> new file mode 100644
> index 00000000000..f6588b002ab
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/init/array-copy12.C
> @@ -0,0 +1,79 @@
> +// PR c++/103238 (array-copy extensions for P1997)
> +// Array function return compile tests -
> +// (c.f. array-copy11.C for compile fail tests)
> +
> +// { dg-do run { target c++11 } }
> +// { dg-additional-options "-farray-copy" }
> +
> +constexpr bool equal(const char(&a)[2]
> +                    ,const char(&b)[2])
> +{
> +  return a[0]==b[0]
> +      && a[1]==b[1];
> +}
> +
> +template<int N>
> +using charN = char[N];
> +using chars = char[];
> +
> +// Note a() return is const qualified, else constexpr test below fails FIXME
> +constexpr auto a() -> const char[2] {return "a";}
> +constexpr auto b() -> char[2] {return{'b'};} // init-list not folding causes ICE
> +constexpr auto c() -> char[2] {return{"c"};}
> +constexpr auto d() -> char[2] {return chars{"d"};}
> +
> +// Note the lambda return is not const qualified yet works constexpr below
> +auto e = []() -> char[2] {char ce[2]{'e'}; return ce;};
> +
> +static_assert( equal(a(),"a"), "");
> +//static_assert( equal(b(),"b"), ""); // ICE gcc assert
> +static_assert( equal(c(),"c"), "");
> +static_assert( equal(d(),"d"), "");
> +static_assert( equal(e(),"e"), "");
> +// { dg-error "non-constant condition for static assertion" "" { target c++14_down } .-1 }
> +// { dg-error "call to non-.constexpr. function ..lambda" "" { target c++14_down } .-2 }
> +
> +constexpr auto bc[] = b();
> +static_assert( equal(bc,"b"), "");
> +
> +constexpr char char2(char,char)[2];
> +constexpr auto char2(char,char) -> char[2]; // same type redeclaration
> +constexpr char char2(char a,char b)[2] {return{a,b};} // .. definition
> +
> +//static_assert( equal(char2('a','b'),{'a','b'}), ""); // ICE gcc assert
> +
> +constexpr charN<2> char2cp(const charN<2>& a) {return a;}
> +constexpr charN<2> char2cb(const charN<2>& a) {return{a};}
> +
> +constexpr char c2a[2] = char2('a',0);
> +constexpr char cpa[] = char2cp("a");
> +constexpr auto cpb[] = char2cb("a");
> +
> +static_assert( equal( c2a, "a") ,"");
> +static_assert( equal( cpa, "a") ,"");
> +static_assert( equal( cpb, "a") ,"");
> +
> +constexpr auto f8(const float(&a)[8]) -> float[8] {return{};}
> +
> +template <int N>
> +constexpr int atoi()[N] { return {__integer_pack(N)...}; }
> +
> +template <typename T, int N>
> +constexpr int ind(const T (&r)[N], int i) { return r[i]; }
> +
> +constexpr auto atoi6[] = atoi<6>();
> +
> +static_assert(ind(atoi6,4) == 4, "");
> +
> +// FAILs below, can't pass array return to a reference argument
> +
> +//static_assert(ind(atoi<6>(),4) == 4, ""); // ICE
> +
> +int main() {
> +  if (!equal(a(),"a")) __builtin_abort();
> +  // if (!equal(b(),"b")) __builtin_abort(); // ICE
> +  if (!equal(c(),"c")) __builtin_abort();
> +  if (!equal(d(),"d")) __builtin_abort();
> +  if (!equal(e(),"e")) __builtin_abort();
> +   // if (ind(atoi<6>(),4) != 4) __builtin_abort(); // ICE
> +}
> diff --git a/gcc/testsuite/g++.dg/init/array-copy7.C b/gcc/testsuite/g++.dg/init/array-copy7.C
> new file mode 100644
> index 00000000000..9cee097d560
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/init/array-copy7.C
> @@ -0,0 +1,40 @@
> +// PR c++/103238 (array-copy extensions for P1997)
> +// Array assignment nocompile tests
> +// should give same errors before and after the array-copy patch.
> +
> +// { dg-do compile { target c++11 } }
> +//
> +
> +int aggr_ass()
> +{
> +  int a[2] = {11,66}, b[2] = {};
> +
> +  b = a; // { dg-error "invalid array assignment" }
> +  b = {a}; // { dg-error "assigning to an array from an initializer list" }
> +  b = {11,66}; // { dg-error "assigning to an array from an initializer list" }
> +  b = {}; // { dg-error "assigning to an array from an initializer list" }
> +  a = {11}; // { dg-error "assigning to an array from an initializer list" }
> +  return a[1] + b[0] + b[1];
> +}
> +
> +char clr()
> +{
> +  char abc[] = {'a','b','c'};
> +  abc = {""}; // { dg-error "assigning to an array from an initializer list" }
> +  return abc[0] + abc[1] + abc[2];
> +}
> +
> +struct A
> +{
> +  int a[2];
> +  A(const int(&b)[2]) { a = b; } // { dg-error "invalid array assignment" }
> +};
> +
> +char strtab[4][8] = {"hello","world"};
> +
> +void str_ass()
> +{
> +  strtab = {"Hello","World"}; // { dg-error "assigning to an array from an initializer list" }
> +  strtab[0] = {"hi"}; // { dg-error "assigning to an array from an initializer list" }
> +  strtab[1] = strtab[0]; // { dg-error "invalid array assignment" }
> +}
> diff --git a/gcc/testsuite/g++.dg/init/array-copy8.C b/gcc/testsuite/g++.dg/init/array-copy8.C
> new file mode 100644
> index 00000000000..582f35f4340
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/init/array-copy8.C
> @@ -0,0 +1,56 @@
> +// PR c++/103238 (array-copy extensions for P1997)
> +// Array assignment compile tests -
> +// (c.f. array-copy7.C for compile fail tests)
> +
> +// { dg-do run { target c++11 } }
> +// { dg-additional-options "-farray-copy" }
> +
> +int aggr_ass()
> +{
> +  int a[2] = {11,66}, b[2] = {};
> +
> +  b = a;
> +  b = {a};
> +  b = {11,66};
> +  b = {};
> +  a = {11};
> +  return a[1] + b[0] + b[1];
> +}
> +
> +char clr()
> +{
> +  char abc[] = {'a','b','c'};
> +  abc = {""};

It looks like you don't test e.g.

abc = "";

anywhere.

> +  return abc[0] + abc[1] + abc[2];
> +}
> +
> +struct A
> +{
> +  int a[2];
> +  A(const int(&b)[2]) { a = b; }
> +};
> +
> +char strtab[4][8] = {"hello","world"};
> +
> +void str_ass()
> +{
> +  strtab = {"Hello","World"};
> +  strtab[0] = {"hi"};
> +  strtab[1] = strtab[0];
> +}
> +
> +int main()
> +{
> +  char hi[8] = "hi";
> +  str_ass();
> +  if (__builtin_memcmp(strtab[0],hi,8) != 0
> +   || __builtin_memcmp(strtab[1],hi,8) != 0)
> +    __builtin_abort();
> +
> +  if (aggr_ass() || clr())
> +    __builtin_abort();
> +
> +  A a({11,66});
> +  if (a.a[0] != 11 || a.a[1] != 66)
> +   __builtin_abort();
> +}
> diff --git a/gcc/testsuite/g++.dg/init/array-copy9.C b/gcc/testsuite/g++.dg/init/array-copy9.C
> new file mode 100644
> index 00000000000..7bbc615a204
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/init/array-copy9.C
> @@ -0,0 +1,57 @@
> +// PR c++/103238 (array-copy extensions for P1997)
> +// Array auto placeholder element deduction nocompile tests
> +// should give same errors before and after the array-copy patch.
> +
> +// { dg-do compile { target c++11 } }
> +//
> +
> +template <typename...> struct same{ enum:bool{v=false}; };
> +template <typename T> struct same<T,T>{ enum:bool{v=true}; };
> +template <typename T, typename X, typename...Y>
> +struct same<T,X,Y...>{ enum:bool{v = same<T,X>::v && same<T,Y...>::v}; };
> +template<typename...T>
> +constexpr bool all_same_t(T const&...){return same<T...>::v;}
> +
> +typedef int int2[2];
> +int2 a {11,66};
> +
> +auto er[2] {1,2};
> +// { dg-error "direct-list-initialization of .auto. requires exactly one element .\\-fpermissive." "" { target *-*-* } .-1 }
> +// { dg-error "deducing from brace-enclosed initializer list requires" "" { target *-*-* } .-2 }
> +auto il[2] = {1,2};
> +// { dg-error "deducing from brace-enclosed initializer list requires" "" { target *-*-* } .-1 }
> +auto um[2] = {a};
> +// { dg-error "deducing from brace-enclosed initializer list requires" "" { target *-*-* } .-1 }
> +
> +// Initializations from from rvalue array
> +auto g[2] = int2{11,66}; // { dg-error "unable to deduce" }
> +auto j[2] ( int2{11,66} ); // { dg-error "unable to deduce" }
> +auto l[2] { int2{11,66} }; // { dg-error "unable to deduce" }
> +// auto + size deduction :
> +auto m[] = int2{11,66}; // { dg-error "unable to deduce" }
> +auto n[] ( int2{11,66} ); // { dg-error "unable to deduce" }
> +auto p[] { int2{11,66} }; // { dg-error "unable to deduce" }
> +
> +static_assert( all_same_t(a,g,j,l,m,n,p), "");
> +
> +auto stp[] = "str"; // { dg-error "unable to deduce" }
> +char str[] = "str";
> +auto cpy[4] = str; // { dg-error "unable to deduce" }
> +auto cpp[4] (str); // { dg-error "unable to deduce" }
> +auto cpw[4] {str}; // { dg-error "unable to deduce" }
> +// auto + size deduction :
> +auto cpu[] = str; // { dg-error "unable to deduce" }
> +auto cpv[] (str); // { dg-error "unable to deduce" }
> +auto cpx[] {str}; // { dg-error "unable to deduce" }
> +
> +static_assert( all_same_t(str,cpy,cpp,cpw,cpu,cpv,cpx), "");
> +
> +auto const& ref = "str";
> +
> +auto rpy[4] = ref; // { dg-error "unable to deduce" }
> +auto rpp[4] (ref); // { dg-error "unable to deduce" }
> +auto rpw[4] {ref}; // { dg-error "unable to deduce" }
> +// auto + size deduction :
> +auto rpu[] = ref; // { dg-error "unable to deduce" }
> +auto rpv[] (ref); // { dg-error "unable to deduce" }
> +auto rpx[] {ref}; // { dg-error "unable to deduce" }
> 



More information about the Gcc-patches mailing list