C++ PATCH for C++20 P0388R4 (conversions to arrays of unknown bounds) and CWG 1307 (c++/91364, c++/69531)

Jason Merrill jason@redhat.com
Mon Oct 7 18:56:00 GMT 2019


On 10/7/19 1:42 PM, Marek Polacek wrote:
> On Sun, Oct 06, 2019 at 03:39:25PM +0000, Tam S. B. wrote:
>> Hi, sorry for chiming in.
> 
> Hi, no worries, comments are welcome!
> 
>> IIUC P0388R4 does not allow implicit conversion from `T(*)[]` to `T(*)[N]`. Per [conv.qual]/3,
>>
>>> A prvalue of type `T1` can be converted to type `T2` if the cv-combined type of `T1` and `T2` is `T2`.
>>
>> When T1 is `T(*)[]` and T2 is `T(*)[N]`, the "cv-combined type" is `T(*)[]`, which is not the same as T2 = `T(*)[N]`, so conversion should not be permitted.
>>
>> That is to say, either `comp_ptr_ttypes_real` should not use `comp_array_types`, or `comp_array_types` should be extended to handle this case correctly.
> 
> Indeed, I must've glossed over that.  Fixed by introducing compare_bounds_t,
> which says how we should compare arrays when one of them is [].
> 
> similar_type_p still must say "yes" for T(*)[] and T(*)[N] though.
> 
>> Also, does this patch permit conversion from `T(*)[N]` to `const T(*)[]`? I think [conv.qual] allows this conversion, but IIUC `comp_array_types` will return false in this case, as it uses `same_type_p`.
> 
> It does now, array-conv14.C tests it.
> 
> Thanks for looking and catching these problems!
> 
> Bootstrapped/regtested on x86_64-linux, built Boost and cmcstl2.
> 
> Ok for trunk?
> 
> 2019-10-07  Marek Polacek  <polacek@redhat.com>
> 
> 	PR c++/91364 - Implement P0388R4: Permit conversions to arrays of
>         	unknown bound.
> 	PR c++/69531 - Implement CWG 1307: Differently bounded array
> 	parameters.
> 	* call.c (build_array_conv): Build ck_identity at the beginning
> 	of the conversion.
> 	(standard_conversion): Pass bounds_none to comp_ptr_ttypes_const.
> 	(maybe_warn_array_conv): New.
> 	(convert_like_real): Call it.  Add an error message about converting
> 	from arrays of unknown bounds.
> 	(conv_get_original_expr): New.
> 	(nelts_initialized_by_list_init): New.
> 	(conv_binds_to_array_of_unknown_bound): New.
> 	(compare_ics): Implement list-initialization ranking based on
> 	array sizes, as specified in DR 1307 and P0388R.
> 	* cp-tree.h (comp_ptr_ttypes_const): Adjust declaration.
> 	(compare_bounds_t): New enum.
> 	* typeck.c (comp_array_types): New bool and compare_bounds_t
> 	parameters.  Use them.
> 	(structural_comptypes): Adjust the call to comp_array_types.
> 	(similar_type_p): Handle ARRAY_TYPE.
> 	(build_const_cast_1): Pass bounds_none to comp_ptr_ttypes_const.
> 	(comp_ptr_ttypes_real): Use comp_array_types.
> 	(comp_ptr_ttypes_const): New compare_bounds_t parameter.  Use
> 	comp_array_types.
> 
> 	* g++.dg/cpp0x/initlist-array3.C: Remove dg-error.
> 	* g++.dg/cpp0x/initlist-array7.C: New test.
> 	* g++.dg/cpp0x/initlist-array8.C: New test.
> 	* g++.dg/cpp2a/array-conv1.C: New test.
> 	* g++.dg/cpp2a/array-conv10.C: New test.
> 	* g++.dg/cpp2a/array-conv11.C: New test.
> 	* g++.dg/cpp2a/array-conv12.C: New test.
> 	* g++.dg/cpp2a/array-conv13.C: New test.
> 	* g++.dg/cpp2a/array-conv14.C: New test.
> 	* g++.dg/cpp2a/array-conv2.C: New test.
> 	* g++.dg/cpp2a/array-conv3.C: New test.
> 	* g++.dg/cpp2a/array-conv4.C: New test.
> 	* g++.dg/cpp2a/array-conv5.C: New test.
> 	* g++.dg/cpp2a/array-conv6.C: New test.
> 	* g++.dg/cpp2a/array-conv7.C: New test.
> 	* g++.dg/cpp2a/array-conv8.C: New test.
> 	* g++.dg/cpp2a/array-conv9.C: New test.
> 	* g++.old-deja/g++.bugs/900321_01.C: Adjust dg-error.
> 
> diff --git gcc/gcc/cp/call.c gcc/gcc/cp/call.c
> index 56dcbd391c1..1a9c296a812 100644
> --- gcc/gcc/cp/call.c
> +++ gcc/gcc/cp/call.c
> @@ -122,7 +122,8 @@ struct conversion {
>          of using this field directly.  */
>       conversion *next;
>       /* The expression at the beginning of the conversion chain.  This
> -       variant is used only if KIND is ck_identity or ck_ambig.  */
> +       variant is used only if KIND is ck_identity or ck_ambig.  You can
> +       use conv_get_original_expr to get this expression.  */
>       tree expr;
>       /* The array of conversions for an initializer_list, so this
>          variant is used only when KIN D is ck_list.  */
> @@ -223,6 +224,8 @@ static void add_candidates (tree, tree, const vec<tree, va_gc> *, tree, tree,
>   			    tsubst_flags_t);
>   static conversion *merge_conversion_sequences (conversion *, conversion *);
>   static tree build_temp (tree, tree, int, diagnostic_t *, tsubst_flags_t);
> +static conversion *build_identity_conv (tree, tree);
> +static inline bool conv_binds_to_array_of_unknown_bound (conversion *);
>   
>   /* Returns nonzero iff the destructor name specified in NAME matches BASETYPE.
>      NAME can take many forms...  */
> @@ -1078,7 +1081,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->u.next = NULL;
> +  c->u.next = build_identity_conv (TREE_TYPE (ctor), ctor);
>     return c;
>   }
>   
> @@ -1378,7 +1381,7 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
>   
>         if (same_type_p (from, to))
>   	/* OK */;
> -      else if (c_cast_p && comp_ptr_ttypes_const (to, from))
> +      else if (c_cast_p && comp_ptr_ttypes_const (to, from, bounds_none))
>   	/* In a C-style cast, we ignore CV-qualification because we
>   	   are allowed to perform a static_cast followed by a
>   	   const_cast.  */

Hmm, I'd expect bounds_either for a C-style cast.

> @@ -1670,7 +1673,14 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags,
>         maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS);
>         /* DR 1288: Otherwise, if the initializer list has a single element
>   	 of type E and ... [T's] referenced type is reference-related to E,
> -	 the object or reference is initialized from that element... */
> +	 the object or reference is initialized from that element...
> +
> +	 ??? With P0388R4, we should bind 't' directly to U{}:
> +	   using U = A[2];
> +	   A (&&t)[] = {U{}};
> +	 because A[] and A[2] are reference-related.  But we don't do it
> +	 because grok_reference_init has deduced the array size (to 1), and
> +	 A[1] and A[2] aren't reference-related.  */

That sounds like a bug in grok_reference_init; it isn't properly 
implementing

"Otherwise, if the initializer list has a single element of type E and 
either T is not a reference type or its
referenced type is reference-related to E, the object or reference is 
initialized from that element...."

>         if (CONSTRUCTOR_NELTS (expr) == 1)
>   	{
>   	  tree elt = CONSTRUCTOR_ELT (expr, 0)->value;
> @@ -6982,6 +6992,27 @@ maybe_inform_about_fndecl_for_bogus_argument_init (tree fn, int argnum)
>   	    "  initializing argument %P of %qD", argnum, fn);
>   }
>   
> +/* Maybe warn about C++20 Conversions to arrays of unknown bound.  C is
> +   the conversion, EXPR is the expression we're converting.  */
> +
> +static void
> +maybe_warn_array_conv (location_t loc, conversion *c, tree expr)
> +{
> +  if (cxx_dialect >= cxx2a)
> +    return;
> +
> +  tree type = TREE_TYPE (expr);
> +  type = strip_pointer_operator (type);
> +
> +  if (TREE_CODE (type) != ARRAY_TYPE
> +      || TYPE_DOMAIN (type) == NULL_TREE)
> +    return;
> +
> +  if (conv_binds_to_array_of_unknown_bound (c))
> +    pedwarn (loc, OPT_Wpedantic, "conversions to arrays of unknown bound "
> +	     "are only available with %<-std=c++2a%> or %<-std=gnu++2a%>");
> +}
> +
>   /* Perform the conversions in CONVS on the expression EXPR.  FN and
>      ARGNUM are used for diagnostics.  ARGNUM is zero based, -1
>      indicates the `this' argument of a method.  INNER is nonzero when
> @@ -7401,8 +7432,20 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
>   	      error_at (loc, "cannot bind non-const lvalue reference of "
>   			"type %qH to an rvalue of type %qI", totype, extype);
>   	    else if (!reference_compatible_p (TREE_TYPE (totype), extype))
> -	      error_at (loc, "binding reference of type %qH to %qI "
> -			"discards qualifiers", totype, extype);
> +	      {
> +		/* If we're converting from T[] to T[N], don't talk
> +		   about discarding qualifiers.  (Converting from T[N] to
> +		   T[] is allowed by P0388R4.)  */
> +		if (TREE_CODE (extype) == ARRAY_TYPE
> +		    && TYPE_DOMAIN (extype) == NULL_TREE
> +		    && TREE_CODE (TREE_TYPE (totype)) == ARRAY_TYPE
> +		    && TYPE_DOMAIN (TREE_TYPE (totype)) != NULL_TREE)
> +		  error_at (loc, "binding reference of type %qH to %qI "
> +			    "discards array bounds", totype, extype);

If we're converting to T[N], that would be adding, not discarding, array 
bounds?

> +		else
> +		  error_at (loc, "binding reference of type %qH to %qI "
> +			    "discards qualifiers", totype, extype);
> +	      }
>   	    else
>   	      gcc_unreachable ();
>   	    maybe_print_user_conv_context (convs);
> @@ -7410,6 +7453,8 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
>   
>   	    return error_mark_node;
>   	  }
> +	else if (complain & tf_warning)
> +	  maybe_warn_array_conv (loc, convs, expr);
>   
>   	/* If necessary, create a temporary.
>   
> @@ -7493,7 +7538,10 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
>       case ck_qual:
>         /* Warn about deprecated conversion if appropriate.  */
>         if (complain & tf_warning)
> -	string_conv_p (totype, expr, 1);
> +	{
> +	  string_conv_p (totype, expr, 1);
> +	  maybe_warn_array_conv (loc, convs, expr);
> +	}
>         break;
>   
>       case ck_ptr:
> @@ -10083,6 +10131,50 @@ maybe_handle_ref_bind (conversion **ics)
>     return NULL;
>   }
>   
> +/* Get the expression at the beginning of the conversion chain C.  */
> +
> +static tree
> +conv_get_original_expr (conversion *c)
> +{
> +  for (; c; c = next_conversion (c))
> +    if (c->kind == ck_identity || c->kind == ck_ambig)
> +      return c->u.expr;
> +  return NULL_TREE;
> +}
> +
> +/* Return a tree representing the number of elements initialized by the
> +   list-initialization C.  The caller must check that C converts to an
> +   array type.  */
> +
> +static tree
> +nelts_initialized_by_list_init (conversion *c)
> +{
> +  /* If the array we're converting to has a dimension, we'll use that.  */
> +  if (TYPE_DOMAIN (c->type))
> +    return array_type_nelts_top (c->type);
> +  else
> +    {
> +      /* Otherwise, we look at how many elements the constructor we're
> +	 initializing from has.  */
> +      tree ctor = conv_get_original_expr (c);
> +      return size_int (CONSTRUCTOR_NELTS (ctor));
> +    }
> +}
> +
> +/* True iff C is a conversion that binds a reference or a pointer to
> +   an array of unknown bound.  */
> +
> +static inline bool
> +conv_binds_to_array_of_unknown_bound (conversion *c)
> +{
> +  /* ck_ref_bind won't have the reference stripped.  */
> +  tree type = non_reference (c->type);
> +  /* ck_qual won't have the pointer stripped.  */
> +  type = strip_pointer_operator (type);
> +  return (TREE_CODE (type) == ARRAY_TYPE
> +	  && TYPE_DOMAIN (type) == NULL_TREE);
> +}
> +
>   /* Compare two implicit conversion sequences according to the rules set out in
>      [over.ics.rank].  Return values:
>   
> @@ -10196,6 +10288,37 @@ compare_ics (conversion *ics1, conversion *ics2)
>   	  if (f1 != f2)
>   	    return 0;
>   	}
> +      /* List-initialization sequence L1 is a better conversion sequence than
> +	 list-initialization sequence L2 if
> +
> +	 -- L1 and L2 convert to arrays of the same element type, and either
> +	 the number of elements n1 initialized by L1 is less than the number
> +	 of elements n2 initialized by L2, or n1=n2 and L2 converts to an array
> +	 of unknown bound and L1 does not.  (Added in CWG 1307 and extended by
> +	 P0388R4.)  */
> +      else if (t1->kind == ck_aggr
> +	       && TREE_CODE (t1->type) == ARRAY_TYPE
> +	       && TREE_CODE (t2->type) == ARRAY_TYPE)
> +	{
> +	  /* The type of the array elements must be the same.  */
> +	  if (!same_type_p (TREE_TYPE (t1->type), TREE_TYPE (t2->type)))
> +	    return 0;
> +
> +	  tree n1 = nelts_initialized_by_list_init (t1);
> +	  tree n2 = nelts_initialized_by_list_init (t2);
> +	  if (tree_int_cst_lt (n1, n2))
> +	    return 1;
> +	  else if (tree_int_cst_lt (n2, n1))
> +	    return -1;
> +	  /* The n1 == n2 case.  */
> +	  else if (conv_binds_to_array_of_unknown_bound (t1))
> +	    return -1;
> +	  else if (conv_binds_to_array_of_unknown_bound (t2))
> +	    return 1;
> +	  else
> +	    /* They can't both bind to array of unknown bound.  */
> +	    gcc_unreachable ();
Can't we get here comparing two functions with the same 
reference-to-array parameter type?  It looks like if both are reference 
to array of unknown bound, we'll arbitrarily prefer one, and if they're 
reference to array with known bound, we'll abort?

> +	}
>         else
>   	{
>   	  /* For ambiguous or aggregate conversions, use the target type as
> @@ -10491,6 +10614,26 @@ compare_ics (conversion *ics1, conversion *ics2)
>   
>         if (same_type_ignoring_top_level_qualifiers_p (to_type1, to_type2))
>   	{
> +	  /* Per P0388R4:
> +
> +	    void f (int(&)[]),     // (1)
> +		 f (int(&)[1]),    // (2)
> +		 f (int*);	   // (3)
> +
> +	    (2) is better than (1), but (3) should be equal to (1) and to
> +	    (2).  For that reason we don't use ck_qual for (1) which would
> +	    give it the cr_exact rank while (3) remains ck_identity.
> +	    Therefore we compare (1) and (2) here.  For (1) we'll have
> +
> +	      ck_ref_bind <- ck_identity
> +		int[] &	       int[1]
> +
> +	    so to handle this we must look at ref_conv.  */
> +	  if (conv_binds_to_array_of_unknown_bound (ref_conv1))
> +	    return -1;
> +	  else if (conv_binds_to_array_of_unknown_bound (ref_conv2))
> +	    return 1;

How could this example satisfy the same_type_... test?

And again, what if both bind to array of unknown bound?

>   	  int q1 = cp_type_quals (TREE_TYPE (ref_conv1->type));
>   	  int q2 = cp_type_quals (TREE_TYPE (ref_conv2->type));
>   	  if (ref_conv1->bad_p)
> diff --git gcc/gcc/cp/cp-tree.h gcc/gcc/cp/cp-tree.h
> index b82b5808197..f6bc4ac54b4 100644
> --- gcc/gcc/cp/cp-tree.h
> +++ gcc/gcc/cp/cp-tree.h
> @@ -7374,6 +7374,10 @@ extern void cxx_print_error_function		(diagnostic_context *,
>   						 struct diagnostic_info *);
>   
>   /* in typeck.c */
> +/* Says how we should behave when comparing two arrays one of which
> +   has unknown bounds.  */
> +enum compare_bounds_t { bounds_none, bounds_either, bounds_first };
> +
>   extern bool cxx_mark_addressable		(tree, bool = false);
>   extern int string_conv_p			(const_tree, const_tree, int);
>   extern tree cp_truthvalue_conversion		(tree);
> @@ -7464,7 +7468,7 @@ extern tree convert_for_initialization		(tree, tree, tree, int,
>   						 impl_conv_rhs, tree, int,
>                                                    tsubst_flags_t);
>   extern int comp_ptr_ttypes			(tree, tree);
> -extern bool comp_ptr_ttypes_const		(tree, tree);
> +extern bool comp_ptr_ttypes_const		(tree, tree, compare_bounds_t);
>   extern bool error_type_p			(const_tree);
>   extern bool ptr_reasonably_similar		(const_tree, const_tree);
>   extern tree build_ptrmemfunc			(tree, tree, int, bool,
> diff --git gcc/gcc/cp/typeck.c gcc/gcc/cp/typeck.c
> index d549450a605..1994520268f 100644
> --- gcc/gcc/cp/typeck.c
> +++ gcc/gcc/cp/typeck.c
> @@ -54,7 +54,7 @@ static tree rationalize_conditional_expr (enum tree_code, tree,
>   					  tsubst_flags_t);
>   static int comp_ptr_ttypes_real (tree, tree, int);
>   static bool comp_except_types (tree, tree, bool);
> -static bool comp_array_types (const_tree, const_tree, bool);
> +static bool comp_array_types (const_tree, const_tree, compare_bounds_t, bool);
>   static tree pointer_diff (location_t, tree, tree, tree, tsubst_flags_t, tree *);
>   static tree get_delta_difference (tree, tree, bool, bool, tsubst_flags_t);
>   static void casts_away_constness_r (tree *, tree *, tsubst_flags_t);
> @@ -1084,11 +1084,16 @@ comp_except_specs (const_tree t1, const_tree t2, int exact)
>     return exact == ce_derived || base == NULL_TREE || length == list_length (t1);
>   }
>   
> -/* Compare the array types T1 and T2.  ALLOW_REDECLARATION is true if
> -   [] can match [size].  */
> +/* Compare the array types T1 and T2.  CB says how we should behave when
> +   comparing array bounds: bounds_none doesn't allow dimensionless arrays,
> +   bounds_either says than any array can be [], bounds_first means that
> +   onlt T1 can be an array with unknown bounds.  TLQ_MATCH is true if
> +   top-level qualifiers must match when comparing the types of the array
> +   elements.  */
>   
>   static bool
> -comp_array_types (const_tree t1, const_tree t2, bool allow_redeclaration)
> +comp_array_types (const_tree t1, const_tree t2, compare_bounds_t cb,
> +		  bool tlq_match)
>   {
>     tree d1;
>     tree d2;
> @@ -1098,7 +1103,10 @@ comp_array_types (const_tree t1, const_tree t2, bool allow_redeclaration)
>       return true;
>   
>     /* The type of the array elements must be the same.  */
> -  if (!same_type_p (TREE_TYPE (t1), TREE_TYPE (t2)))
> +  if (tlq_match
> +      ? !same_type_p (TREE_TYPE (t1), TREE_TYPE (t2))
> +      : !same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (t1),
> +						    TREE_TYPE (t2)))
>       return false;
>   
>     d1 = TYPE_DOMAIN (t1);
> @@ -1119,8 +1127,10 @@ comp_array_types (const_tree t1, const_tree t2, bool allow_redeclaration)
>          declarations for an array object can specify
>          array types that differ by the presence or absence of a major
>          array bound (_dcl.array_).  */
> -  if (!d1 || !d2)
> -    return allow_redeclaration;
> +  if (!d1 && d2)
> +    return cb >= bounds_either;
> +  else if (d1 && !d2)
> +    return cb == bounds_either;
>   
>     /* Check that the dimensions are the same.  */
>   
> @@ -1368,7 +1378,9 @@ structural_comptypes (tree t1, tree t2, int strict)
>   
>       case ARRAY_TYPE:
>         /* Target types must match incl. qualifiers.  */
> -      if (!comp_array_types (t1, t2, !!(strict & COMPARE_REDECLARATION)))
> +      if (!comp_array_types (t1, t2, ((strict & COMPARE_REDECLARATION)
> +				      ? bounds_either : bounds_none),
> +			     /*tlq_match=*/true))
>   	return false;
>         break;
>   
> @@ -1549,10 +1561,10 @@ similar_type_p (tree type1, tree type2)
>     if (same_type_ignoring_top_level_qualifiers_p (type1, type2))
>       return true;
>   
> -  /* FIXME This ought to handle ARRAY_TYPEs too.  */
>     if ((TYPE_PTR_P (type1) && TYPE_PTR_P (type2))
> -      || (TYPE_PTRDATAMEM_P (type1) && TYPE_PTRDATAMEM_P (type2)))
> -    return comp_ptr_ttypes_const (type1, type2);
> +      || (TYPE_PTRDATAMEM_P (type1) && TYPE_PTRDATAMEM_P (type2))
> +      || (TREE_CODE (type1) == ARRAY_TYPE && TREE_CODE (type2) == ARRAY_TYPE))
> +    return comp_ptr_ttypes_const (type1, type2, bounds_either);
>   
>     return false;
>   }
> @@ -7858,7 +7870,7 @@ build_const_cast_1 (tree dst_type, tree expr, tsubst_flags_t complain,
>   
>     if (TYPE_PTR_P (src_type) || TYPE_PTRDATAMEM_P (src_type))
>       {
> -      if (comp_ptr_ttypes_const (dst_type, src_type))
> +      if (comp_ptr_ttypes_const (dst_type, src_type, bounds_none))
>   	{
>   	  if (valid_p)
>   	    {
> @@ -9888,6 +9900,7 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
>   {
>     bool to_more_cv_qualified = false;
>     bool is_opaque_pointer = false;
> +  bool is_comp_array = false;
>   
>     for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from))
>       {
> @@ -9920,9 +9933,16 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
>         if (VECTOR_TYPE_P (to))
>   	is_opaque_pointer = vector_targets_convertible_p (to, from);
>   
> +      if (TREE_CODE (to) == ARRAY_TYPE)
> +	/* P0388R4 allows a conversion from int[N] to int[] but not the
> +	   other way round.  */
> +	is_comp_array = comp_array_types (to, from, bounds_first,
> +					  /*tlq_match=*/false);
> +
>         if (!TYPE_PTR_P (to) && !TYPE_PTRDATAMEM_P (to))
>   	return ((constp >= 0 || to_more_cv_qualified)
>   		&& (is_opaque_pointer
> +		    || is_comp_array
>   		    || same_type_ignoring_top_level_qualifiers_p (to, from)));
>       }
>   }
> @@ -10023,12 +10043,13 @@ ptr_reasonably_similar (const_tree to, const_tree from)
>   
>   /* Return true if TO and FROM (both of which are POINTER_TYPEs or
>      pointer-to-member types) are the same, ignoring cv-qualification at
> -   all levels.  */
> +   all levels.  CB says how we should behave when comparing array bounds.  */
>   
>   bool
> -comp_ptr_ttypes_const (tree to, tree from)
> +comp_ptr_ttypes_const (tree to, tree from, compare_bounds_t cb)
>   {
>     bool is_opaque_pointer = false;
> +  bool is_comp_array = false;
>   
>     for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from))
>       {
> @@ -10043,8 +10064,12 @@ comp_ptr_ttypes_const (tree to, tree from)
>         if (VECTOR_TYPE_P (to))
>   	is_opaque_pointer = vector_targets_convertible_p (to, from);
>   
> +      if (TREE_CODE (to) == ARRAY_TYPE)
> +	is_comp_array = comp_array_types (to, from, cb, /*tlq_match=*/false);
> +
>         if (!TYPE_PTR_P (to))
>   	return (is_opaque_pointer
> +		|| is_comp_array
>   		|| same_type_ignoring_top_level_qualifiers_p (to, from));
>       }
>   }
> diff --git gcc/gcc/testsuite/g++.dg/cpp0x/initlist-array3.C gcc/gcc/testsuite/g++.dg/cpp0x/initlist-array3.C
> index 1a94f4ed55b..4140cd92d7b 100644
> --- gcc/gcc/testsuite/g++.dg/cpp0x/initlist-array3.C
> +++ gcc/gcc/testsuite/g++.dg/cpp0x/initlist-array3.C
> @@ -6,5 +6,6 @@ void composite (int const (&) [3]);
>   
>   int main ()
>   {
> -  composite({0,1});		// { dg-error "ambiguous" }
> +  // Not ambiguous since CWG 1307.
> +  composite({0,1});
>   }
> diff --git gcc/gcc/testsuite/g++.dg/cpp0x/initlist-array7.C gcc/gcc/testsuite/g++.dg/cpp0x/initlist-array7.C
> new file mode 100644
> index 00000000000..7a689c6675f
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/cpp0x/initlist-array7.C
> @@ -0,0 +1,21 @@
> +// PR c++/69531 - DR 1307, Overload resolution based on size of array init-list.
> +// { dg-do run { target c++11 } }
> +
> +int f(int const(&)[2]) { return 1; }
> +int f(int const(&)[3]) { return 2; }
> +
> +int
> +main ()
> +{
> +   if (f({}) != 1)
> +    __builtin_abort ();
> +
> +   if (f({1}) != 1)
> +    __builtin_abort ();
> +
> +   if (f({1, 2}) != 1)
> +    __builtin_abort ();
> +
> +   if (f({1, 2, 3}) != 2)
> +    __builtin_abort ();
> +}
> diff --git gcc/gcc/testsuite/g++.dg/cpp0x/initlist-array8.C gcc/gcc/testsuite/g++.dg/cpp0x/initlist-array8.C
> new file mode 100644
> index 00000000000..ac2774e06b4
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/cpp0x/initlist-array8.C
> @@ -0,0 +1,35 @@
> +// PR c++/69531 - DR 1307, Overload resolution based on size of array init-list.
> +// { dg-do run { target c++2a } }
> +
> +int f(int (&)[1][1]) { return 1; }
> +int f(int (&)[1][2]) { return 2; }
> +
> +int g(int (&&)[2][1]) { return 1; }
> +int g(int (&&)[2][2]) { return 2; }
> +
> +int h(int (&&)[][1]) { return 1; }
> +int h(int (&&)[][2]) { return 2; }
> +
> +int
> +main ()
> +{
> +  int arr1[1][1];
> +  int arr2[1][2];
> +
> +  if (f(arr1) != 1)
> +    __builtin_abort ();
> +  if (f(arr2) != 2)
> +    __builtin_abort ();
> +
> +  if (g({ { 1, 2 }, { 3 } }) != 2)
> +    __builtin_abort ();
> +
> +  if (g({ { 1, 2 }, { 3, 4 } }) != 2)
> +    __builtin_abort ();
> +
> +  if (h({ { 1, 2 }, { 3 } }) != 2)
> +    __builtin_abort ();
> +
> +  if (h({ { 1, 2 }, { 3, 4 } }) != 2)
> +    __builtin_abort ();
> +}
> diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv1.C gcc/gcc/testsuite/g++.dg/cpp2a/array-conv1.C
> new file mode 100644
> index 00000000000..e90b340b0d6
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv1.C
> @@ -0,0 +1,33 @@
> +// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown bound.
> +// { dg-do compile { target c++17 } }
> +// { dg-options "-Wpedantic" }
> +// C++17, because that has CWG 393.
> +
> +void f(int(&)[]);
> +void fp(int(*)[]);
> +void f2(int(&)[][10]);
> +void fp2(int(*)[][10]);
> +int arr[10];
> +int arr2[10][10];
> +
> +void
> +g ()
> +{
> +  f (arr); // { dg-warning "conversions to arrays of unknown bound are only available" "" { target c++17_down } }
> +  fp (&arr); // { dg-warning "conversions to arrays of unknown bound are only available" "" { target c++17_down } }
> +  f2 (arr2);// { dg-warning "conversions to arrays of unknown bound are only available" "" { target c++17_down } }
> +  fp2 (&arr2);// { dg-warning "conversions to arrays of unknown bound are only available" "" { target c++17_down } }
> +}
> +
> +int(&r1)[] = arr;// { dg-warning "conversions to arrays of unknown bound are only available" "" { target c++17_down } }
> +int(&r2)[10] = arr;
> +int(&r3)[][10] = arr2;// { dg-warning "conversions to arrays of unknown bound are only available" "" { target c++17_down } }
> +/* Note that
> +   int (&r)[10][] = arr2;
> +   is invalid.  */
> +int(&r4)[10][10] = arr2;
> +
> +int(*p1)[] = &arr;// { dg-warning "conversions to arrays of unknown bound are only available" "" { target c++17_down } }
> +int(*p2)[10] = &arr;
> +int(*p3)[][10] = &arr2;// { dg-warning "conversions to arrays of unknown bound are only available" "" { target c++17_down } }
> +int(*p4)[10][10] = &arr2;
> diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv10.C gcc/gcc/testsuite/g++.dg/cpp2a/array-conv10.C
> new file mode 100644
> index 00000000000..5817c0bf39c
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv10.C
> @@ -0,0 +1,22 @@
> +// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown bound.
> +// { dg-do compile { target c++17 } }
> +// { dg-options "-Wpedantic" }
> +
> +// The other direction: converting from int[] to int(&)[3] is forbidden.
> +
> +extern int a[];
> +extern int (*b)[];
> +extern int (&c)[];
> +int (&y)[] = a;
> +int (&x)[3] = y; // { dg-error "discards array bounds" }
> +int (&z)[3] = a; // { dg-error "discards array bounds" }
> +
> +void f(int (*)[3]);
> +void f2(int (&)[3]);
> +
> +void
> +test ()
> +{
> +  f(b); // { dg-error "cannot convert" }
> +  f2(c); // { dg-error "discards array bounds" }
> +}
> diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv11.C gcc/gcc/testsuite/g++.dg/cpp2a/array-conv11.C
> new file mode 100644
> index 00000000000..a072b29191d
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv11.C
> @@ -0,0 +1,23 @@
> +// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown bound.
> +// { dg-do compile { target c++2a } }
> +// { dg-options "-Wpedantic" }
> +
> +// Test flexible array member.  Here we're binding int[] to int[].  This worked
> +// even before P0388R4.
> +
> +typedef int T[];
> +extern T arr;
> +T &t1 = arr;
> +
> +struct S {
> +  int i;
> +  int a[]; // { dg-warning "flexible array member" }
> +};
> +
> +void f (int (&)[]);
> +
> +void
> +test (S s)
> +{
> +  f (s.a);
> +}
> diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv12.C gcc/gcc/testsuite/g++.dg/cpp2a/array-conv12.C
> new file mode 100644
> index 00000000000..1156ea32df5
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv12.C
> @@ -0,0 +1,12 @@
> +// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown bound.
> +// { dg-do compile { target c++2a } }
> +// { dg-options "-Wpedantic" }
> +
> +int arr[1] = { 42 };
> +int(&r)[]{arr};
> +int(&r2)[] = {arr};
> +int(&&r3)[]{};
> +int(&&r4)[]{42};
> +int(&&r5)[] = {};
> +int(&&r6)[] = {42};
> +int(&r7)[](arr); // { dg-warning "conversions to arrays of unknown bound are only available" "" { target c++17_down } }
> diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv13.C gcc/gcc/testsuite/g++.dg/cpp2a/array-conv13.C
> new file mode 100644
> index 00000000000..9908b7e9118
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv13.C
> @@ -0,0 +1,17 @@
> +// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown bound.
> +// { dg-do compile { target c++2a } }
> +
> +template <typename T> void foo(T);
> +
> +template <typename F, typename T, typename = decltype(foo<T>(F()))>
> +void test(int) { }
> +
> +// No other overload, so if the above fails because of the conversion,
> +// we fail.
> +
> +void
> +fn ()
> +{
> +  test<int(*)[2], int(*)[]>(0);
> +  test<int(*)[], int(*)[]>(0);
> +}
> diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv14.C gcc/gcc/testsuite/g++.dg/cpp2a/array-conv14.C
> new file mode 100644
> index 00000000000..793e85d7b1c
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv14.C
> @@ -0,0 +1,17 @@
> +// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown bound.
> +// { dg-do compile { target c++2a } }
> +
> +void f(const int(*)[]);
> +void fb(const int(*)[3]);
> +void f2(const int(&)[]);
> +void fb2(const int(&)[3]);
> +
> +void
> +g ()
> +{
> +  int arr[3];
> +  f(&arr);
> +  fb(&arr);
> +  f2(arr);
> +  fb2(arr);
> +}
> diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv2.C gcc/gcc/testsuite/g++.dg/cpp2a/array-conv2.C
> new file mode 100644
> index 00000000000..5245d830f1f
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv2.C
> @@ -0,0 +1,26 @@
> +// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown bound.
> +// { dg-do compile { target c++2a } }
> +
> +struct A {
> +  A();
> +  A(const A(&)[2]);
> +};
> +
> +using T = A[];
> +using U = A[2];
> +
> +// t binds directly to U{} now.  Before it bound indirectly to a temporary
> +// A{U{}}.  ??? But we don't do it now; see reference_binding and the
> +// BRACE_ENCLOSED_INITIALIZER_P block.
> +A (&&t)[] = {U{}};
> +
> +U u{};
> +
> +T &
> +foo ()
> +{
> +  // This didn't compile before P0388R4: invalid initialization of non-const
> +  // reference of type 'A (&)[]' from an rvalue of type
> +  // '<brace-enclosed initializer list>'.
> +  return {u};
> +}
> diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv3.C gcc/gcc/testsuite/g++.dg/cpp2a/array-conv3.C
> new file mode 100644
> index 00000000000..3d92b401247
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv3.C
> @@ -0,0 +1,26 @@
> +// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown bound.
> +// { dg-do run { target c++2a } }
> +
> +// Ranking of reference initialization conversions
> +
> +int f(int(&)[]) { return 1; }	    // (1)
> +int f(int(&)[1]) { return 2; }	    // (2)
> +
> +int h(int(*)[]) { return 1; }	    // (a)
> +int h(int(*)[1]) { return 2; }	    // (b)
> +
> +// From P0388R4:
> +// (2) and (b) should clearly be better than (1) and (a), respectively,
> +// as the former overloads are more restricted.
> +// (a) should be worse than (b), which is implied by (a) necessitating
> +// a qualification conversion in that case.
> +
> +int
> +main ()
> +{
> +  int arr[1];
> +  if (f(arr) != 2)
> +    __builtin_abort ();
> +  if (h(&arr) != 2)
> +    __builtin_abort ();
> +}
> diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv4.C gcc/gcc/testsuite/g++.dg/cpp2a/array-conv4.C
> new file mode 100644
> index 00000000000..979c69b0555
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv4.C
> @@ -0,0 +1,24 @@
> +// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown bound.
> +// { dg-do compile { target c++2a } }
> +
> +// Ranking of reference initialization conversions
> +
> +void f(int(&)[]) {}	    // (1)
> +//void f(int(&)[1]) { }	    // (2)
> +void f(int*) { }	    // (3)
> +
> +//void f2(int(&)[]) { }	    // (1)
> +void f2(int(&)[1]) { }	    // (2)
> +void f2(int*) { }	    // (3)
> +
> +// From P0388R4:
> +// (3) should be equal to (1) (as it is to (2))
> +// Check that we get "ambiguous overload" errors.
> +
> +void
> +doit ()
> +{
> +  int arr[1];
> +  f(arr); // { dg-error "ambiguous" }
> +  f2(arr); // { dg-error "ambiguous" }
> +}
> diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv5.C gcc/gcc/testsuite/g++.dg/cpp2a/array-conv5.C
> new file mode 100644
> index 00000000000..34678f5cead
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv5.C
> @@ -0,0 +1,24 @@
> +// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown bound.
> +// { dg-do run { target c++2a } }
> +
> +// Ranking of list-initialization sequences
> +int b(int   (&&)[] ) { return 1; }   // #1
> +int b(long  (&&)[] ) { return 2; }   // #2
> +int b(int   (&&)[1]) { return 3; }   // #3
> +int b(long  (&&)[1]) { return 4; }   // #4
> +int b(int   (&&)[2]) { return 5; }   // #5
> +
> +/* Here,
> +   -- #1, #3 and #5 should rank better than both #2 and #4, as no promotion
> +      is necessitated.
> +   -- #1 should rank worse than #3, being far less specialized.
> +   -- #1 should rank better than #5, as the latter requires a larger array
> +      temporary.  (#3 also ranks better than #5 for the same reason--cf. core
> +      issue 1307).  */
> +
> +int
> +main ()
> +{
> +  if (b({1}) != 3)
> +    __builtin_abort ();
> +}
> diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv6.C gcc/gcc/testsuite/g++.dg/cpp2a/array-conv6.C
> new file mode 100644
> index 00000000000..c2389c82273
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv6.C
> @@ -0,0 +1,28 @@
> +// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown bound.
> +// { dg-do run { target c++2a } }
> +
> +// Ranking of reference initialization conversions
> +
> +int f1(const int(&)[]) { return 1; }
> +int f1(const int(&)[1]) { return 2; }
> +
> +int f2(const int(&)[]) { return 1; }
> +int f2(int(&)[1]) { return 2; }
> +
> +int f3(int(&)[]) { return 1; }
> +int f3(const int(&)[1]) { return 2; }
> +
> +const int arr[1] = { 42 };
> +
> +int
> +main ()
> +{
> +  if (f1(arr) != 2)
> +    __builtin_abort ();
> +
> +  if (f2(arr) != 1)
> +    __builtin_abort ();
> +
> +  if (f3(arr) != 2)
> +    __builtin_abort ();
> +}
> diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv7.C gcc/gcc/testsuite/g++.dg/cpp2a/array-conv7.C
> new file mode 100644
> index 00000000000..07c709ff10f
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv7.C
> @@ -0,0 +1,34 @@
> +// PR c++/69531 - DR 1307, Overload resolution based on size of array init-list.
> +// { dg-do run { target c++2a } }
> +
> +int f(int const(&)[]) { return 1; }
> +int f(int const(&)[2]) { return 2; }
> +
> +int f2(int const(&)[]) { return 1; }
> +int f2(int const(&)[1]) { return 2; }
> +
> +int f3(int const(&)[]) { return 1; }
> +int f3(int const(&)[1]) { return 2; }
> +int f3(int const(&)[2]) { return 3; }
> +
> +int main ()
> +{
> +  if (f ({}) != 1)
> +    __builtin_abort ();
> +  if (f ({1}) != 1)
> +    __builtin_abort ();
> +  if (f ({1, 2}) != 2)
> +    __builtin_abort ();
> +
> +  if (f2 ({}) != 1)
> +    __builtin_abort ();
> +  if (f2 ({1}) != 2)
> +    __builtin_abort ();
> +
> +  if (f3 ({}) != 1)
> +    __builtin_abort ();
> +  if (f3 ({1}) != 2)
> +    __builtin_abort ();
> +  if (f3 ({1, 2}) != 3)
> +    __builtin_abort ();
> +}
> diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv8.C gcc/gcc/testsuite/g++.dg/cpp2a/array-conv8.C
> new file mode 100644
> index 00000000000..635c7679a21
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv8.C
> @@ -0,0 +1,26 @@
> +// PR c++/69531 - DR 1307, Overload resolution based on size of array init-list.
> +// { dg-do run { target c++2a } }
> +// Example from [over.ics.rank].
> +
> +int f(int    (&&)[] ) { return 1; }    // #1
> +int f(double (&&)[] ) { return 2; }    // #2
> +int f(int    (&&)[2]) { return 3; }    // #3
> +
> +int
> +main ()
> +{
> +  // Calls #1: Better than #2 due to conversion, better than #3 due to bounds.
> +  if (f({1}) != 1)
> +     __builtin_abort ();
> +  // Calls #2: Identity conversion is better than floating-integral conversion.
> +  if (f({1.0}) != 2)
> +     __builtin_abort ();
> +  // Calls #2: Identity conversion is better than floating-integral conversion.
> +  if (f({1.0, 2.0}) != 2)
> +     __builtin_abort ();
> +  // Calls #3: Converting to array of known bound is better than to unknown
> +  // bound, and an identity conversion is better than floating-integral
> +  // conversion.
> +  if (f({1, 2}) != 3)
> +     __builtin_abort ();
> +}
> diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv9.C gcc/gcc/testsuite/g++.dg/cpp2a/array-conv9.C
> new file mode 100644
> index 00000000000..e56e4a3e54e
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv9.C
> @@ -0,0 +1,11 @@
> +// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown bound.
> +// { dg-do compile { target c++2a } }
> +
> +int arr[1];
> +
> +void
> +test ()
> +{
> +  int (&r)[1] = const_cast<int(&)[1]>(arr);
> +  int (&r2)[] = const_cast<int(&)[]>(arr); // { dg-error "invalid" }
> +}
> diff --git gcc/gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C gcc/gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C
> index 6b52783c09b..c3b1ab56282 100644
> --- gcc/gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C
> +++ gcc/gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C
> @@ -20,7 +20,7 @@ void function_0 ()
>   {
>     // we miss the first two because typeck.c (comp_array_types) deems
>     // it okay if one of the sizes is null
> -  ptr_to_array_of_ints = ptr_to_array_of_3_ints;	// { dg-error "" }
> +  ptr_to_array_of_ints = ptr_to_array_of_3_ints;	// { dg-error "conversions to arrays" "" { target c++17_down } }
>     ptr_to_array_of_3_ints = ptr_to_array_of_ints;	// { dg-error "" }
>   
>     ptr_to_array_of_3_ints = ptr_to_array_of_5_ints;	// { dg-error "" }
> 



More information about the Gcc-patches mailing list