diff mbox series

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

Message ID 20191004224207.GU2954@redhat.com
State New
Headers show
Series C++ PATCH for C++20 P0388R4 (conversions to arrays of unknown bounds) and CWG 1307 (c++/91364, c++/69531) | expand

Commit Message

Marek Polacek Oct. 4, 2019, 10:42 p.m. UTC
This patch implements P0388R4, Permit conversions to arrays of unknown bound,
<http://wg21.link/p0388r4>.  CWG 393 allowed references to arrays of unknown
bound and this C++20 feature allows conversions like

  void f(int(&)[]);
  int arr[1];

  void g() { f(arr); }
  int(&r)[] = arr;

The proposal seemed fairly straightforward but it turned out to be quite
shifty.  I found out that I needed to implement DR 2352 (done), and also
DR 1307 (done in this patch).  The latter DR added wording for
list-initialization ranking of references to arrays which this proposal
extends.

Considering "int[]" and "int[2]" similar types has interesting impact, as
observed in the libstdc++ testsuite.  Arrays are always a bit convoluted
because you can have multidimensional arrays, though here it wasn't so bad,
because only the first dimension can be blank.  But there are flexible
array members which are also boundless.

One thing I know of this patch doesn't attempt to handle is

  using U = A[2];
  A (&&t)[] = {U{}}; // should bind to U{} now
  
for which see reference_binding.  It didn't seem like too big a deal though.

Successfully built Boost and cmcstl2.
Bootstrapped/regtested on x86_64-linux, ok for trunk?

Jon, are you OK with the libstdc++ changes?

2019-10-04  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 false to comp_ptr_ttypes_const.
	(maybe_warn_array_conv): New.
	(convert_like_real): Call it.
	(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.
	* typeck.c (similar_type_p): Handle ARRAY_TYPE.
	(build_const_cast_1): Pass false to comp_ptr_ttypes_const.
	(comp_ptr_ttypes_real): Use comp_array_types.
	(comp_ptr_ttypes_const): New bool 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-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.
	* g++.old-deja/g++.bugs/900520_02.C: Likewise.
	* g++.old-deja/g++.other/typeck1.C: Likewise.

	* testsuite/20_util/shared_ptr/cons/array.cc: Adjust static_assert.
	* testsuite/experimental/memory/shared_ptr/cons/copy_ctor_neg.cc:
	Remove dg-error.
	* testsuite/experimental/memory/shared_ptr/cons/unique_ptr_ctor.cc:
	Adjust static_assert.
	* testsuite/experimental/memory/shared_ptr/cons/weak_ptr_ctor.cc:
	Likewise.

Comments

Tam S. B. Oct. 6, 2019, 3:39 p.m. UTC | #1
Hi, sorry for chiming in.

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.

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`.

Thanks.
Jason Merrill Oct. 7, 2019, 2:37 p.m. UTC | #2
On 10/6/19 11:39 AM, Tam S. B. wrote:
> Hi, sorry for chiming in.
> 
> 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.
> 
> 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`.

Agreed.

Jason
Marek Polacek Oct. 7, 2019, 5:42 p.m. UTC | #3
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.  */
@@ -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.  */
       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);
+		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 ();
+	}
       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;
+
 	  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 "" }
Jason Merrill Oct. 7, 2019, 6:56 p.m. UTC | #4
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 "" }
>
Marek Polacek Oct. 7, 2019, 11:26 p.m. UTC | #5
On Mon, Oct 07, 2019 at 02:56:10PM -0400, Jason Merrill wrote:
> > @@ -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.

Makes sense, it's just that const_cast shouldn't drop the bounds.

> > @@ -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...."

Can that be fixed in a follow up?

> >         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?

True, I've reworded the error mesage.

> > +		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?

Indeed.  I've fixed the logic (and added a test) there...

> > +	}
> >         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?

...and here.  If they both bind to array of unknown bound I don't return
but let it do the comp_cv_qualification thing, which will return 0.

We can get there because to_type1 and to_type2 will be int[1] but
ref_conv1->type will be int[] & and ref_conv2->type will be int[1] &.

Bootstrapped/regtested on x86_64-linux, 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-conv15.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/cp/call.c gcc/cp/call.c
index 6c9acac4614..d91bbed8a99 100644
--- gcc/cp/call.c
+++ 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_either))
 	/* In a C-style cast, we ignore CV-qualification because we
 	   are allowed to perform a static_cast followed by a
 	   const_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.  */
       if (CONSTRUCTOR_NELTS (expr) == 1)
 	{
 	  tree elt = CONSTRUCTOR_ELT (expr, 0)->value;
@@ -6983,6 +6993,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
@@ -7402,8 +7433,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, "cannot bind reference of type %qH to %qI "
+			    "due to different array bounds", totype, extype);
+		else
+		  error_at (loc, "binding reference of type %qH to %qI "
+			    "discards qualifiers", totype, extype);
+	      }
 	    else
 	      gcc_unreachable ();
 	    maybe_print_user_conv_context (convs);
@@ -7411,6 +7454,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. 
 
@@ -7494,7 +7539,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:
@@ -10084,6 +10132,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:
 
@@ -10197,6 +10289,38 @@ 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.  */
+	  bool c1 = conv_binds_to_array_of_unknown_bound (t1);
+	  bool c2 = conv_binds_to_array_of_unknown_bound (t2);
+	  if (c1 && !c2)
+	    return -1;
+	  else if (!c1 && c2)
+	    return 1;
+	  else
+	    return 0;
+	}
       else
 	{
 	  /* For ambiguous or aggregate conversions, use the target type as
@@ -10492,6 +10616,28 @@ 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.  */
+	  bool c1 = conv_binds_to_array_of_unknown_bound (ref_conv1);
+	  bool c2 = conv_binds_to_array_of_unknown_bound (ref_conv2);
+	  if (c1 && !c2)
+	    return -1;
+	  else if (!c1 && c2)
+	    return 1;
+
 	  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/cp/cp-tree.h gcc/cp/cp-tree.h
index b82b5808197..f6bc4ac54b4 100644
--- gcc/cp/cp-tree.h
+++ 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/cp/typeck.c gcc/cp/typeck.c
index d549450a605..1994520268f 100644
--- gcc/cp/typeck.c
+++ 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/testsuite/g++.dg/cpp0x/initlist-array3.C gcc/testsuite/g++.dg/cpp0x/initlist-array3.C
index 1a94f4ed55b..4140cd92d7b 100644
--- gcc/testsuite/g++.dg/cpp0x/initlist-array3.C
+++ 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/testsuite/g++.dg/cpp0x/initlist-array7.C gcc/testsuite/g++.dg/cpp0x/initlist-array7.C
new file mode 100644
index 00000000000..7a689c6675f
--- /dev/null
+++ 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/testsuite/g++.dg/cpp0x/initlist-array8.C gcc/testsuite/g++.dg/cpp0x/initlist-array8.C
new file mode 100644
index 00000000000..ac2774e06b4
--- /dev/null
+++ 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/testsuite/g++.dg/cpp2a/array-conv1.C gcc/testsuite/g++.dg/cpp2a/array-conv1.C
new file mode 100644
index 00000000000..e90b340b0d6
--- /dev/null
+++ 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/testsuite/g++.dg/cpp2a/array-conv10.C gcc/testsuite/g++.dg/cpp2a/array-conv10.C
new file mode 100644
index 00000000000..1ee1a771f63
--- /dev/null
+++ 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 "cannot bind reference" }
+int (&z)[3] = a; // { dg-error "cannot bind reference" }
+
+void f(int (*)[3]);
+void f2(int (&)[3]);
+
+void
+test ()
+{
+  f(b); // { dg-error "cannot convert" }
+  f2(c); // { dg-error "cannot bind reference" }
+}
diff --git gcc/testsuite/g++.dg/cpp2a/array-conv11.C gcc/testsuite/g++.dg/cpp2a/array-conv11.C
new file mode 100644
index 00000000000..a072b29191d
--- /dev/null
+++ 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/testsuite/g++.dg/cpp2a/array-conv12.C gcc/testsuite/g++.dg/cpp2a/array-conv12.C
new file mode 100644
index 00000000000..1156ea32df5
--- /dev/null
+++ 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/testsuite/g++.dg/cpp2a/array-conv13.C gcc/testsuite/g++.dg/cpp2a/array-conv13.C
new file mode 100644
index 00000000000..9908b7e9118
--- /dev/null
+++ 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/testsuite/g++.dg/cpp2a/array-conv14.C gcc/testsuite/g++.dg/cpp2a/array-conv14.C
new file mode 100644
index 00000000000..793e85d7b1c
--- /dev/null
+++ 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/testsuite/g++.dg/cpp2a/array-conv15.C gcc/testsuite/g++.dg/cpp2a/array-conv15.C
new file mode 100644
index 00000000000..033a74683a7
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/array-conv15.C
@@ -0,0 +1,23 @@
+// PR c++/69531 - DR 1307, Overload resolution based on size of array init-list.
+// { dg-do run { target c++2a } }
+
+int f(int, int const(&)[2]) { return 1; }
+int f(double, int const(&)[2]) { return 2; }
+
+int f2(int, int const(&)[1]) { return 1; }
+int f2(int, int const(&)[2]) { return 2; }
+
+int f3(int, int const(&)[]) { return 1; }
+int f3(double, int const(&)[]) { return 2; }
+
+int main ()
+{
+  if (f (1, {1}) != 1)
+    __builtin_abort ();
+
+  if (f2 (1, {1}) != 1)
+    __builtin_abort ();
+
+  if (f3 (1, {1}) != 1)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/array-conv2.C gcc/testsuite/g++.dg/cpp2a/array-conv2.C
new file mode 100644
index 00000000000..5245d830f1f
--- /dev/null
+++ 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/testsuite/g++.dg/cpp2a/array-conv3.C gcc/testsuite/g++.dg/cpp2a/array-conv3.C
new file mode 100644
index 00000000000..3d92b401247
--- /dev/null
+++ 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/testsuite/g++.dg/cpp2a/array-conv4.C gcc/testsuite/g++.dg/cpp2a/array-conv4.C
new file mode 100644
index 00000000000..979c69b0555
--- /dev/null
+++ 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/testsuite/g++.dg/cpp2a/array-conv5.C gcc/testsuite/g++.dg/cpp2a/array-conv5.C
new file mode 100644
index 00000000000..34678f5cead
--- /dev/null
+++ 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/testsuite/g++.dg/cpp2a/array-conv6.C gcc/testsuite/g++.dg/cpp2a/array-conv6.C
new file mode 100644
index 00000000000..c2389c82273
--- /dev/null
+++ 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/testsuite/g++.dg/cpp2a/array-conv7.C gcc/testsuite/g++.dg/cpp2a/array-conv7.C
new file mode 100644
index 00000000000..07c709ff10f
--- /dev/null
+++ 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/testsuite/g++.dg/cpp2a/array-conv8.C gcc/testsuite/g++.dg/cpp2a/array-conv8.C
new file mode 100644
index 00000000000..635c7679a21
--- /dev/null
+++ 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/testsuite/g++.dg/cpp2a/array-conv9.C gcc/testsuite/g++.dg/cpp2a/array-conv9.C
new file mode 100644
index 00000000000..82f615db2e7
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/array-conv9.C
@@ -0,0 +1,27 @@
+// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown bound.
+// { dg-do compile { target c++2a } }
+
+int arr[1];
+extern int arr2[];
+
+void
+test ()
+{
+  int (&r)[1] = const_cast<int(&)[1]>(arr);
+  int (&r2)[] = const_cast<int(&)[]>(arr); // { dg-error "invalid" }
+  int (&r3)[1] = (int(&)[1]) arr;
+  int (&r4)[] = (int(&)[]) arr;
+  int (&r5)[1] = static_cast<int(&)[1]>(arr);
+  int (&r6)[] = static_cast<int(&)[]>(arr);
+
+  // Try c_cast_p.
+  int(*p1)[] = (int(*)[]) &arr;
+  int(*p2)[1] = (int(*)[]) &arr; // { dg-error "cannot convert" }
+  int(*p3)[] = (int(*)[1]) &arr;
+  int(*p4)[] = (int(*)[1]) &arr2;
+  int(*p5)[] = (int(*)[]) (int(*)[1]) &arr;
+  int(*p6)[] = (int(*)[1]) (int(*)[]) &arr;
+  int(*p7)[] = static_cast<int(*)[]>(&arr);
+  int(*p8)[] = static_cast<int(*)[1]>(&arr);
+  int(*p9)[] = static_cast<int(*)[1]>(&arr2); // { dg-error "invalid" }
+}
diff --git gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C
index 6b52783c09b..c3b1ab56282 100644
--- gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C
+++ 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 "" }
Jason Merrill Oct. 8, 2019, 5:50 p.m. UTC | #6
On 10/7/19 7:26 PM, Marek Polacek wrote:
> On Mon, Oct 07, 2019 at 02:56:10PM -0400, Jason Merrill wrote:
>>> @@ -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.
> 
> Makes sense, it's just that const_cast shouldn't drop the bounds.
> 
>>> @@ -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...."
> 
> Can that be fixed in a follow up?

Sure.

>>>          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?
> 
> True, I've reworded the error mesage.
> 
>>> +		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?
> 
> Indeed.  I've fixed the logic (and added a test) there...
> 
>>> +	}
>>>          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?
> 
> ...and here.  If they both bind to array of unknown bound I don't return
> but let it do the comp_cv_qualification thing, which will return 0.
> 
> We can get there because to_type1 and to_type2 will be int[1] but
> ref_conv1->type will be int[] & and ref_conv2->type will be int[1] &.
> 
> Bootstrapped/regtested on x86_64-linux, 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-conv15.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/cp/call.c gcc/cp/call.c
> index 6c9acac4614..d91bbed8a99 100644
> --- gcc/cp/call.c
> +++ 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_either))
>   	/* In a C-style cast, we ignore CV-qualification because we
>   	   are allowed to perform a static_cast followed by a
>   	   const_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.  */
>         if (CONSTRUCTOR_NELTS (expr) == 1)
>   	{
>   	  tree elt = CONSTRUCTOR_ELT (expr, 0)->value;
> @@ -6983,6 +6993,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
> @@ -7402,8 +7433,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, "cannot bind reference of type %qH to %qI "
> +			    "due to different array bounds", totype, extype);
> +		else
> +		  error_at (loc, "binding reference of type %qH to %qI "
> +			    "discards qualifiers", totype, extype);
> +	      }
>   	    else
>   	      gcc_unreachable ();
>   	    maybe_print_user_conv_context (convs);
> @@ -7411,6 +7454,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.
>   
> @@ -7494,7 +7539,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:
> @@ -10084,6 +10132,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:
>   
> @@ -10197,6 +10289,38 @@ 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.  */
> +	  bool c1 = conv_binds_to_array_of_unknown_bound (t1);
> +	  bool c2 = conv_binds_to_array_of_unknown_bound (t2);
> +	  if (c1 && !c2)
> +	    return -1;
> +	  else if (!c1 && c2)
> +	    return 1;
> +	  else
> +	    return 0;
> +	}
>         else
>   	{
>   	  /* For ambiguous or aggregate conversions, use the target type as
> @@ -10492,6 +10616,28 @@ 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.  */
> +	  bool c1 = conv_binds_to_array_of_unknown_bound (ref_conv1);
> +	  bool c2 = conv_binds_to_array_of_unknown_bound (ref_conv2);
> +	  if (c1 && !c2)
> +	    return -1;
> +	  else if (!c1 && c2)
> +	    return 1;
> +
>   	  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/cp/cp-tree.h gcc/cp/cp-tree.h
> index b82b5808197..f6bc4ac54b4 100644
> --- gcc/cp/cp-tree.h
> +++ 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/cp/typeck.c gcc/cp/typeck.c
> index d549450a605..1994520268f 100644
> --- gcc/cp/typeck.c
> +++ 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);

This seems to stop iterating when we first see an array, but 7.3.5 
[conv.qual] seems to allow conversion to unknown bounds at multiple 
levels, and even qualification conversion of an array of pointers, e.g.

int *ar[4];
const int *(&arp)[] = ar;

> +
>         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));

Likewise.

>       }
>   }
> diff --git gcc/testsuite/g++.dg/cpp0x/initlist-array3.C gcc/testsuite/g++.dg/cpp0x/initlist-array3.C
> index 1a94f4ed55b..4140cd92d7b 100644
> --- gcc/testsuite/g++.dg/cpp0x/initlist-array3.C
> +++ 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/testsuite/g++.dg/cpp0x/initlist-array7.C gcc/testsuite/g++.dg/cpp0x/initlist-array7.C
> new file mode 100644
> index 00000000000..7a689c6675f
> --- /dev/null
> +++ 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/testsuite/g++.dg/cpp0x/initlist-array8.C gcc/testsuite/g++.dg/cpp0x/initlist-array8.C
> new file mode 100644
> index 00000000000..ac2774e06b4
> --- /dev/null
> +++ 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/testsuite/g++.dg/cpp2a/array-conv1.C gcc/testsuite/g++.dg/cpp2a/array-conv1.C
> new file mode 100644
> index 00000000000..e90b340b0d6
> --- /dev/null
> +++ 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/testsuite/g++.dg/cpp2a/array-conv10.C gcc/testsuite/g++.dg/cpp2a/array-conv10.C
> new file mode 100644
> index 00000000000..1ee1a771f63
> --- /dev/null
> +++ 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 "cannot bind reference" }
> +int (&z)[3] = a; // { dg-error "cannot bind reference" }
> +
> +void f(int (*)[3]);
> +void f2(int (&)[3]);
> +
> +void
> +test ()
> +{
> +  f(b); // { dg-error "cannot convert" }
> +  f2(c); // { dg-error "cannot bind reference" }
> +}
> diff --git gcc/testsuite/g++.dg/cpp2a/array-conv11.C gcc/testsuite/g++.dg/cpp2a/array-conv11.C
> new file mode 100644
> index 00000000000..a072b29191d
> --- /dev/null
> +++ 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/testsuite/g++.dg/cpp2a/array-conv12.C gcc/testsuite/g++.dg/cpp2a/array-conv12.C
> new file mode 100644
> index 00000000000..1156ea32df5
> --- /dev/null
> +++ 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/testsuite/g++.dg/cpp2a/array-conv13.C gcc/testsuite/g++.dg/cpp2a/array-conv13.C
> new file mode 100644
> index 00000000000..9908b7e9118
> --- /dev/null
> +++ 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/testsuite/g++.dg/cpp2a/array-conv14.C gcc/testsuite/g++.dg/cpp2a/array-conv14.C
> new file mode 100644
> index 00000000000..793e85d7b1c
> --- /dev/null
> +++ 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/testsuite/g++.dg/cpp2a/array-conv15.C gcc/testsuite/g++.dg/cpp2a/array-conv15.C
> new file mode 100644
> index 00000000000..033a74683a7
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/array-conv15.C
> @@ -0,0 +1,23 @@
> +// PR c++/69531 - DR 1307, Overload resolution based on size of array init-list.
> +// { dg-do run { target c++2a } }
> +
> +int f(int, int const(&)[2]) { return 1; }
> +int f(double, int const(&)[2]) { return 2; }
> +
> +int f2(int, int const(&)[1]) { return 1; }
> +int f2(int, int const(&)[2]) { return 2; }
> +
> +int f3(int, int const(&)[]) { return 1; }
> +int f3(double, int const(&)[]) { return 2; }
> +
> +int main ()
> +{
> +  if (f (1, {1}) != 1)
> +    __builtin_abort ();
> +
> +  if (f2 (1, {1}) != 1)
> +    __builtin_abort ();
> +
> +  if (f3 (1, {1}) != 1)
> +    __builtin_abort ();
> +}
> diff --git gcc/testsuite/g++.dg/cpp2a/array-conv2.C gcc/testsuite/g++.dg/cpp2a/array-conv2.C
> new file mode 100644
> index 00000000000..5245d830f1f
> --- /dev/null
> +++ 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/testsuite/g++.dg/cpp2a/array-conv3.C gcc/testsuite/g++.dg/cpp2a/array-conv3.C
> new file mode 100644
> index 00000000000..3d92b401247
> --- /dev/null
> +++ 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/testsuite/g++.dg/cpp2a/array-conv4.C gcc/testsuite/g++.dg/cpp2a/array-conv4.C
> new file mode 100644
> index 00000000000..979c69b0555
> --- /dev/null
> +++ 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/testsuite/g++.dg/cpp2a/array-conv5.C gcc/testsuite/g++.dg/cpp2a/array-conv5.C
> new file mode 100644
> index 00000000000..34678f5cead
> --- /dev/null
> +++ 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/testsuite/g++.dg/cpp2a/array-conv6.C gcc/testsuite/g++.dg/cpp2a/array-conv6.C
> new file mode 100644
> index 00000000000..c2389c82273
> --- /dev/null
> +++ 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/testsuite/g++.dg/cpp2a/array-conv7.C gcc/testsuite/g++.dg/cpp2a/array-conv7.C
> new file mode 100644
> index 00000000000..07c709ff10f
> --- /dev/null
> +++ 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/testsuite/g++.dg/cpp2a/array-conv8.C gcc/testsuite/g++.dg/cpp2a/array-conv8.C
> new file mode 100644
> index 00000000000..635c7679a21
> --- /dev/null
> +++ 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/testsuite/g++.dg/cpp2a/array-conv9.C gcc/testsuite/g++.dg/cpp2a/array-conv9.C
> new file mode 100644
> index 00000000000..82f615db2e7
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/array-conv9.C
> @@ -0,0 +1,27 @@
> +// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown bound.
> +// { dg-do compile { target c++2a } }
> +
> +int arr[1];
> +extern int arr2[];
> +
> +void
> +test ()
> +{
> +  int (&r)[1] = const_cast<int(&)[1]>(arr);
> +  int (&r2)[] = const_cast<int(&)[]>(arr); // { dg-error "invalid" }
> +  int (&r3)[1] = (int(&)[1]) arr;
> +  int (&r4)[] = (int(&)[]) arr;
> +  int (&r5)[1] = static_cast<int(&)[1]>(arr);
> +  int (&r6)[] = static_cast<int(&)[]>(arr);
> +
> +  // Try c_cast_p.
> +  int(*p1)[] = (int(*)[]) &arr;
> +  int(*p2)[1] = (int(*)[]) &arr; // { dg-error "cannot convert" }
> +  int(*p3)[] = (int(*)[1]) &arr;
> +  int(*p4)[] = (int(*)[1]) &arr2;
> +  int(*p5)[] = (int(*)[]) (int(*)[1]) &arr;
> +  int(*p6)[] = (int(*)[1]) (int(*)[]) &arr;
> +  int(*p7)[] = static_cast<int(*)[]>(&arr);
> +  int(*p8)[] = static_cast<int(*)[1]>(&arr);
> +  int(*p9)[] = static_cast<int(*)[1]>(&arr2); // { dg-error "invalid" }
> +}
> diff --git gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C
> index 6b52783c09b..c3b1ab56282 100644
> --- gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C
> +++ 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 "" }
>
Jonathan Wakely Oct. 9, 2019, 10:01 a.m. UTC | #7
On 07/10/19 14:56 -0400, Jason Merrill wrote:
>On 10/7/19 1:42 PM, Marek Polacek wrote:
>>@@ -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?

I don't think the diagnostic would be very good if we say "adds array
bounds" though. How about being consistent with the existing error for
similar cases?

a.cc:4:17: error: invalid initialization of reference of type ‘int (&)[3]’ from expression of type ‘int []’
   int (&b)[3] = a;
                 ^
Marek Polacek Oct. 9, 2019, 8:26 p.m. UTC | #8
On Tue, Oct 08, 2019 at 01:50:17PM -0400, Jason Merrill wrote:
> > > 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...."
> > 
> > Can that be fixed in a follow up?
> 
> Sure.

I'll open a PR.

> > @@ -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);
> 
> This seems to stop iterating when we first see an array, but 7.3.5
> [conv.qual] seems to allow conversion to unknown bounds at multiple levels,
> and even qualification conversion of an array of pointers, e.g.
> 
> int *ar[4];
> const int *(&arp)[] = ar;

(Should be const int *const (&arp)[] = ar; I believe.)

Yup, this is us lacking DR 330 which I'd been meaning to fix separately, but the
following patch implements that DR too.

Changes from the previous patch are:
* comp_array_types uses similar_type_p when needed, not same_type_ignoring_...;
* comp_ptr_ttypes_const and comp_ptr_ttypes_real will now look through
  arrays.  Fixed a bug whereby we'd accepted a conversion even if it involed
  converting arrays of different bounds;
* a bunch of new tests (qual1.C, qual2.C, qual3.C, ref2.C, ref3.C) to cover
  CWG 330;
* more tests to cover CWG 330 and P0388R4: array-conv16.C, array-conv17.C
* 23_containers/span/lwg3255.cc specifically mentions P0388R4 in a FIXME, so
  trivially adjusted.

> > @@ -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));
> 
> Likewise.

Fixed too.

Bootstrapped/regtested on x86_64-linux, built Boost and cmcstl2.

Ok for trunk?

2019-10-09  Marek Polacek  <polacek@redhat.com>

	PR c++/91364 - P0388R4: Permit conversions to arrays of	unknown bound.
	PR c++/69531 - DR 1307: Differently bounded array parameters.
	PR c++/88128 - DR 330: Qual convs and pointers to arrays of pointers.
	* 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): Don't check cv-quals of ARRAY_TYPEs.  Use
	comp_array_types to compare array types.  Look through arrays as per
	DR 330.
	(comp_ptr_ttypes_const): Use comp_array_types to compare array types.
	Look through arrays as per DR 330.

	* g++.dg/conversion/qual1.C: New test.
	* g++.dg/conversion/qual2.C: New test.
	* g++.dg/conversion/qual3.C: New test.
	* g++.dg/conversion/ref2.C: New test.
	* g++.dg/conversion/ref3.C: New test.
	* 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-conv15.C: New test.
	* g++.dg/cpp2a/array-conv16.C: New test.
	* g++.dg/cpp2a/array-conv17.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.

	* testsuite/23_containers/span/lwg3255.cc: Adjust test to match the
	post-P0388R4 behavior.

diff --git gcc/gcc/cp/call.c gcc/gcc/cp/call.c
index 33ec6a2dcc0..55d2abaaddd 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...  */
@@ -1066,7 +1069,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;
 }
 
@@ -1366,7 +1369,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_either))
 	/* In a C-style cast, we ignore CV-qualification because we
 	   are allowed to perform a static_cast followed by a
 	   const_cast.  */
@@ -1668,7 +1671,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.  */
       if (CONSTRUCTOR_NELTS (expr) == 1)
 	{
 	  tree elt = CONSTRUCTOR_ELT (expr, 0)->value;
@@ -6958,6 +6968,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
@@ -7377,8 +7408,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, "cannot bind reference of type %qH to %qI "
+			    "due to different array bounds", totype, extype);
+		else
+		  error_at (loc, "binding reference of type %qH to %qI "
+			    "discards qualifiers", totype, extype);
+	      }
 	    else
 	      gcc_unreachable ();
 	    maybe_print_user_conv_context (convs);
@@ -7386,6 +7429,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. 
 
@@ -7469,7 +7514,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:
@@ -10061,6 +10109,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:
 
@@ -10174,6 +10266,38 @@ 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.  */
+	  bool c1 = conv_binds_to_array_of_unknown_bound (t1);
+	  bool c2 = conv_binds_to_array_of_unknown_bound (t2);
+	  if (c1 && !c2)
+	    return -1;
+	  else if (!c1 && c2)
+	    return 1;
+	  else
+	    return 0;
+	}
       else
 	{
 	  /* For ambiguous or aggregate conversions, use the target type as
@@ -10469,6 +10593,28 @@ 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.  */
+	  bool c1 = conv_binds_to_array_of_unknown_bound (ref_conv1);
+	  bool c2 = conv_binds_to_array_of_unknown_bound (ref_conv2);
+	  if (c1 && !c2)
+	    return -1;
+	  else if (!c1 && c2)
+	    return 1;
+
 	  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 9ff617be2d4..c1301a451df 100644
--- gcc/gcc/cp/cp-tree.h
+++ gcc/gcc/cp/cp-tree.h
@@ -7372,6 +7372,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);
@@ -7462,7 +7466,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 ba334e7d5cb..a67dd4b155a 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,15 @@ 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.  STRICT is true if
+   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 strict)
 {
   tree d1;
   tree d2;
@@ -1098,7 +1102,9 @@ 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 (strict
+      ? !same_type_p (TREE_TYPE (t1), TREE_TYPE (t2))
+      : !similar_type_p (TREE_TYPE (t1), TREE_TYPE (t2)))
     return false;
 
   d1 = TYPE_DOMAIN (t1);
@@ -1119,8 +1125,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 +1376,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),
+			     /*strict=*/true))
 	return false;
       break;
 
@@ -1549,10 +1559,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;
 }
@@ -7867,7 +7877,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)
 	    {
@@ -9909,9 +9919,10 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
 			   TYPE_OFFSET_BASETYPE (to)))
 	return 0;
 
-      /* Const and volatile mean something different for function types,
-	 so the usual checks are not appropriate.  */
-      if (!FUNC_OR_METHOD_TYPE_P (to))
+      /* Const and volatile mean something different for function and
+	 array types, so the usual checks are not appropriate.  We'll
+	 check the array type elements in further iterations.  */
+      if (!FUNC_OR_METHOD_TYPE_P (to) && TREE_CODE (to) != ARRAY_TYPE)
 	{
 	  if (!at_least_as_qualified_p (to, from))
 	    return 0;
@@ -9930,7 +9941,17 @@ 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 (!TYPE_PTR_P (to) && !TYPE_PTRDATAMEM_P (to))
+      /* P0388R4 allows a conversion from int[N] to int[] but not the
+	 other way round.  When both arrays have bounds but they do
+	 not match, then no conversion is possible.  */
+      if (TREE_CODE (to) == ARRAY_TYPE
+	  && !comp_array_types (to, from, bounds_first, /*strict=*/false))
+	return 0;
+
+      if (!TYPE_PTR_P (to)
+	  && !TYPE_PTRDATAMEM_P (to)
+	  /* CWG 330 says we need to look through arrays.  */
+	  && TREE_CODE (to) != ARRAY_TYPE)
 	return ((constp >= 0 || to_more_cv_qualified)
 		&& (is_opaque_pointer
 		    || same_type_ignoring_top_level_qualifiers_p (to, from)));
@@ -10033,10 +10054,10 @@ 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;
 
@@ -10053,7 +10074,14 @@ comp_ptr_ttypes_const (tree to, tree from)
       if (VECTOR_TYPE_P (to))
 	is_opaque_pointer = vector_targets_convertible_p (to, from);
 
-      if (!TYPE_PTR_P (to))
+      if (TREE_CODE (to) == ARRAY_TYPE
+	  /* Ignore cv-qualification, but if we see e.g. int[3] and int[4],
+	     we must fail.  */
+	  && !comp_array_types (to, from, cb, /*strict=*/false))
+	return false;
+
+      /* CWG 330 says we need to look through arrays.  */
+      if (!TYPE_PTR_P (to) && TREE_CODE (to) != ARRAY_TYPE)
 	return (is_opaque_pointer
 		|| same_type_ignoring_top_level_qualifiers_p (to, from));
     }
diff --git gcc/gcc/testsuite/g++.dg/conversion/qual1.C gcc/gcc/testsuite/g++.dg/conversion/qual1.C
new file mode 100644
index 00000000000..5022da9da47
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/conversion/qual1.C
@@ -0,0 +1,51 @@
+// PR c++/88128 - Implement DR 330: Qualification conversions and pointers to
+// arrays of pointers.
+
+int *a[4];
+const int *const(*ap1)[4] = &a;
+/* if at some level k the P2 is more cv-qualified than P1, then there
+   must be a const at every single level (other than level zero) of P2
+   up until k.  */
+const int *(*ap2)[4] = &a; // { dg-error "cannot convert" }
+int *const(*ap3)[4] = &a;
+int *(*ap4)[4] = &a;
+int *(*const ap5)[4] = &a;
+const int *const(*const ap6)[4] = &a;
+int *const(*const ap7)[4] = &a;
+int *(*const ap8)[4] = &a;
+
+const int *b[4];
+const int *const(*bp1)[4] = &b;
+const int *(*bp2)[4] = &b;
+int *const(*bp3)[4] = &b; // { dg-error "cannot convert" }
+int *(*bp4)[4] = &b; // { dg-error "cannot convert" }
+int *(*const bp5)[4] = &b; // { dg-error "cannot convert" }
+const int *const(*const bp6)[4] = &b;
+int *const(*const bp7)[4] = &b; // { dg-error "cannot convert" }
+int *(*const bp8)[4] = &b; // { dg-error "cannot convert" }
+
+int *c[2][3];
+int const *const (*cp1)[3] = c;
+int const *(*cp2)[3] = c; // { dg-error "cannot convert" }
+int const *const (*const cp3)[3] = c;
+int *const (*cp4)[3] = c;
+int *(*cp5)[3] = c;
+
+double *const (*d)[3];
+double const *const (*e)[3] = d;
+int *(*f)[3];
+const int *const (*g)[3] = f;
+
+// From PR88128.
+int* (*xx)[];
+const int* const(*yy)[] = xx;
+
+// From DR 330.
+int main()
+{
+   double *array2D[2][3];
+
+   double       *       (*array2DPtr1)[3] = array2D;
+   double       * const (*array2DPtr2)[3] = array2DPtr1;
+   double const * const (*array2DPtr3)[3] = array2DPtr2;
+}
diff --git gcc/gcc/testsuite/g++.dg/conversion/qual2.C gcc/gcc/testsuite/g++.dg/conversion/qual2.C
new file mode 100644
index 00000000000..8a063a03e79
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/conversion/qual2.C
@@ -0,0 +1,14 @@
+// PR c++/88128 - DR 330: Qual convs and pointers to arrays of pointers.
+
+// Make sure we don't accept different bounds.
+
+int *a[4];
+const int *const(*ap1)[5] = &a; // { dg-error "cannot convert" }
+
+int *(*b)[3];
+const int *const (*bp1)[3] = &b; // { dg-error "cannot convert" }
+const int *const (*bp2)[4] = &b; // { dg-error "cannot convert" }
+int *(*bp3)[4] = &b; // { dg-error "cannot convert" }
+
+int *c[2][3];
+int const *const (*cp1)[4] = c; // { dg-error "cannot convert" }
diff --git gcc/gcc/testsuite/g++.dg/conversion/qual3.C gcc/gcc/testsuite/g++.dg/conversion/qual3.C
new file mode 100644
index 00000000000..4b466d9ea5d
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/conversion/qual3.C
@@ -0,0 +1,53 @@
+// PR c++/88128 - DR 330: Qual convs and pointers to arrays of pointers.
+// { dg-do compile { target c++17 } }
+
+using P = int *(*)[3];
+using Q = const int *const (*)[3];
+using Qi = const int *[3];
+using Q2 = Qi const *;
+using R = const int *const (*)[4];
+using S = const int *const (*)[];
+using T = const int *(*)[];
+
+void
+f (P p, Q q, Q2 q2, R r, S s, T t)
+{
+  q = p;
+  q2 = p;
+  r = p; // { dg-error "cannot convert" }
+  t = p; // { dg-error "cannot convert" }
+  s = t;
+  t = s; // { dg-error "invalid conversion" }
+
+  // Test const_cast.
+  const_cast<P>(q);
+  const_cast<P>(q2);
+  const_cast<Q>(p);
+  const_cast<Q2>(p);
+  const_cast<S>(p); // { dg-error "invalid .const_cast." }
+  const_cast<P>(s); // { dg-error "invalid .const_cast." }
+  const_cast<S>(q); // { dg-error "invalid .const_cast." }
+  const_cast<S>(q2); // { dg-error "invalid .const_cast." }
+  const_cast<Q>(s); // { dg-error "invalid .const_cast." }
+  const_cast<Q2>(s); // { dg-error "invalid .const_cast." }
+  const_cast<T>(s);
+  const_cast<S>(t);
+  const_cast<T>(q); // { dg-error "invalid .const_cast." }
+  const_cast<Q>(t); // { dg-error "invalid .const_cast." }
+
+  // Test reinterpret_cast.
+  reinterpret_cast<P>(q); // { dg-error "casts away qualifiers" }
+  reinterpret_cast<P>(q2); // { dg-error "casts away qualifiers" }
+  reinterpret_cast<Q>(p);
+  reinterpret_cast<Q2>(p);
+  reinterpret_cast<S>(p);
+  reinterpret_cast<P>(s); // { dg-error "casts away qualifiers" }
+  reinterpret_cast<S>(q);
+  reinterpret_cast<S>(q2);
+  reinterpret_cast<Q>(s);
+  reinterpret_cast<Q2>(s);
+  reinterpret_cast<T>(s); // { dg-error "casts away qualifiers" }
+  reinterpret_cast<S>(t);
+  reinterpret_cast<T>(q); // { dg-error "casts away qualifiers" }
+  reinterpret_cast<Q>(t);
+}
diff --git gcc/gcc/testsuite/g++.dg/conversion/ref2.C gcc/gcc/testsuite/g++.dg/conversion/ref2.C
new file mode 100644
index 00000000000..418e711946f
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/conversion/ref2.C
@@ -0,0 +1,29 @@
+// PR c++/88128 - Implement DR 330: Qualification conversions and pointers to
+// arrays of pointers.
+
+int *ar[4];
+/* if at some level k the P2 is more cv-qualified than P1, then there
+   must be a const at every single level (other than level zero) of P2
+   up until k.  */
+const int *(&arp)[4] = ar; // { dg-error "discards qualifiers" }
+const int *const(&arp2)[4] = ar;
+int *const(&arp3)[4] = ar;
+int *(&arp4)[4] = ar;
+
+const int *br[4];
+const int *(&brp)[4] = br;
+const int *const(&brp2)[4] = br;
+int *const(&brp3)[4] = br; // { dg-error "discards qualifiers" }
+int *(&brp4)[4] = br; // { dg-error "discards qualifiers" }
+
+int *c[2][3];
+int const *const (&cp1)[3] = *c;
+int const *(&cp2)[3] = *c; // { dg-error "discards qualifiers" }
+int *const (&cp3)[3] = *c;
+int *(&cp4)[3] = *c;
+
+double *const (*d)[3];
+double const *const (&e)[3] = *d;
+
+int *(*f)[3];
+const int *const (&g)[3] = *f;
diff --git gcc/gcc/testsuite/g++.dg/conversion/ref3.C gcc/gcc/testsuite/g++.dg/conversion/ref3.C
new file mode 100644
index 00000000000..ca5326ce8e8
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/conversion/ref3.C
@@ -0,0 +1,4 @@
+int a[2];
+const int (&rc)[2] = a;
+volatile int (&rv)[2] = a;
+const volatile int (&rcv)[2] = a;
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..1ee1a771f63
--- /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 "cannot bind reference" }
+int (&z)[3] = a; // { dg-error "cannot bind reference" }
+
+void f(int (*)[3]);
+void f2(int (&)[3]);
+
+void
+test ()
+{
+  f(b); // { dg-error "cannot convert" }
+  f2(c); // { dg-error "cannot bind reference" }
+}
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-conv15.C gcc/gcc/testsuite/g++.dg/cpp2a/array-conv15.C
new file mode 100644
index 00000000000..033a74683a7
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv15.C
@@ -0,0 +1,23 @@
+// PR c++/69531 - DR 1307, Overload resolution based on size of array init-list.
+// { dg-do run { target c++2a } }
+
+int f(int, int const(&)[2]) { return 1; }
+int f(double, int const(&)[2]) { return 2; }
+
+int f2(int, int const(&)[1]) { return 1; }
+int f2(int, int const(&)[2]) { return 2; }
+
+int f3(int, int const(&)[]) { return 1; }
+int f3(double, int const(&)[]) { return 2; }
+
+int main ()
+{
+  if (f (1, {1}) != 1)
+    __builtin_abort ();
+
+  if (f2 (1, {1}) != 1)
+    __builtin_abort ();
+
+  if (f3 (1, {1}) != 1)
+    __builtin_abort ();
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv16.C gcc/gcc/testsuite/g++.dg/cpp2a/array-conv16.C
new file mode 100644
index 00000000000..bfb39d1c12c
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv16.C
@@ -0,0 +1,16 @@
+// PR c++/91364 - P0388R4: Permit conversions to arrays of unknown bound.
+// { dg-do compile { target c++2a } }
+
+using P = int *(*)[3];
+using S = const int *const (*)[];
+using Q = const int *const (*)[3];
+using Qi = const int *[3];
+using Q2 = Qi const *;
+
+void
+f (P p, S s, Q q, Q2 q2)
+{
+  s = p;
+  s = q;
+  s = q2;
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv17.C gcc/gcc/testsuite/g++.dg/cpp2a/array-conv17.C
new file mode 100644
index 00000000000..3313ed466fb
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv17.C
@@ -0,0 +1,39 @@
+// PR c++/91364 - P0388R4: Permit conversions to arrays of unknown bound.
+// { dg-do compile { target c++2a } }
+
+// As conversion/qual1.C, but with [].
+
+int *a[4];
+const int *const(*ap1)[] = &a;
+/* if at some level k the P2 is more cv-qualified than P1, then there
+   must be a const at every single level (other than level zero) of P2
+   up until k.  */
+const int *(*ap2)[] = &a; // { dg-error "cannot convert" }
+int *const(*ap3)[] = &a;
+int *(*ap4)[] = &a;
+int *(*const ap5)[] = &a;
+const int *const(*const ap6)[] = &a;
+int *const(*const ap7)[] = &a;
+int *(*const ap8)[] = &a;
+
+const int *b[4];
+const int *const(*bp1)[] = &b;
+const int *(*bp2)[] = &b;
+int *const(*bp3)[] = &b; // { dg-error "cannot convert" }
+int *(*bp4)[] = &b; // { dg-error "cannot convert" }
+int *(*const bp5)[] = &b; // { dg-error "cannot convert" }
+const int *const(*const bp6)[] = &b;
+int *const(*const bp7)[] = &b; // { dg-error "cannot convert" }
+int *(*const bp8)[] = &b; // { dg-error "cannot convert" }
+
+int *c[2][3];
+int const *const (*cp1)[] = c;
+int const *(*cp2)[] = c; // { dg-error "cannot convert" }
+int const *const (*const cp3)[] = c;
+int *const (*cp4)[] = c;
+int *(*cp5)[] = c;
+
+double *const (*d)[3];
+double const *const (*e)[] = d;
+int *(*f)[3];
+const int *const (*g)[] = f;
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..82f615db2e7
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv9.C
@@ -0,0 +1,27 @@
+// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown bound.
+// { dg-do compile { target c++2a } }
+
+int arr[1];
+extern int arr2[];
+
+void
+test ()
+{
+  int (&r)[1] = const_cast<int(&)[1]>(arr);
+  int (&r2)[] = const_cast<int(&)[]>(arr); // { dg-error "invalid" }
+  int (&r3)[1] = (int(&)[1]) arr;
+  int (&r4)[] = (int(&)[]) arr;
+  int (&r5)[1] = static_cast<int(&)[1]>(arr);
+  int (&r6)[] = static_cast<int(&)[]>(arr);
+
+  // Try c_cast_p.
+  int(*p1)[] = (int(*)[]) &arr;
+  int(*p2)[1] = (int(*)[]) &arr; // { dg-error "cannot convert" }
+  int(*p3)[] = (int(*)[1]) &arr;
+  int(*p4)[] = (int(*)[1]) &arr2;
+  int(*p5)[] = (int(*)[]) (int(*)[1]) &arr;
+  int(*p6)[] = (int(*)[1]) (int(*)[]) &arr;
+  int(*p7)[] = static_cast<int(*)[]>(&arr);
+  int(*p8)[] = static_cast<int(*)[1]>(&arr);
+  int(*p9)[] = static_cast<int(*)[1]>(&arr2); // { 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 "" } 
diff --git gcc/libstdc++-v3/testsuite/23_containers/span/lwg3255.cc gcc/libstdc++-v3/testsuite/23_containers/span/lwg3255.cc
index 638c88101f9..bab7da3bf19 100644
--- gcc/libstdc++-v3/testsuite/23_containers/span/lwg3255.cc
+++ gcc/libstdc++-v3/testsuite/23_containers/span/lwg3255.cc
@@ -28,8 +28,7 @@ using std::is_constructible_v;
 
 // LWG 3255 span's array constructor is too strict
 
-// FIXME: remove '!' from next line when P0388R4 is implemented:
-static_assert( ! is_constructible_v<span<const int* const>, array<int*, 2>> );
+static_assert( is_constructible_v<span<const int* const>, array<int*, 2>> );
 static_assert( is_constructible_v<span<const int>, array<const int, 4>> );
 
 static_assert( is_constructible_v<span<int, 1>, int(&)[1]> );
Marek Polacek Oct. 9, 2019, 8:29 p.m. UTC | #9
On Wed, Oct 09, 2019 at 11:01:39AM +0100, Jonathan Wakely wrote:
> On 07/10/19 14:56 -0400, Jason Merrill wrote:
> > On 10/7/19 1:42 PM, Marek Polacek wrote:
> > > @@ -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?
> 
> I don't think the diagnostic would be very good if we say "adds array
> bounds" though. How about being consistent with the existing error for
> similar cases?
> 
> a.cc:4:17: error: invalid initialization of reference of type ‘int (&)[3]’ from expression of type ‘int []’
>   int (&b)[3] = a;
>                 ^

In my latest patch the error message reads "cannot bind reference of type T to U due to different array bounds".
It'd be trivial to adjust it if anyone hates that.

Marek
Jason Merrill Oct. 9, 2019, 8:42 p.m. UTC | #10
On 10/9/19 4:26 PM, Marek Polacek wrote:
> On Tue, Oct 08, 2019 at 01:50:17PM -0400, Jason Merrill wrote:
>>>> 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...."
>>>
>>> Can that be fixed in a follow up?
>>
>> Sure.
> 
> I'll open a PR.
> 
>>> @@ -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);
>>
>> This seems to stop iterating when we first see an array, but 7.3.5
>> [conv.qual] seems to allow conversion to unknown bounds at multiple levels,
>> and even qualification conversion of an array of pointers, e.g.
>>
>> int *ar[4];
>> const int *(&arp)[] = ar;
> 
> (Should be const int *const (&arp)[] = ar; I believe.)
> 
> Yup, this is us lacking DR 330 which I'd been meaning to fix separately, but the
> following patch implements that DR too.
> 
> Changes from the previous patch are:
> * comp_array_types uses similar_type_p when needed, not same_type_ignoring_...;
> * comp_ptr_ttypes_const and comp_ptr_ttypes_real will now look through
>    arrays.  Fixed a bug whereby we'd accepted a conversion even if it involed
>    converting arrays of different bounds;
> * a bunch of new tests (qual1.C, qual2.C, qual3.C, ref2.C, ref3.C) to cover
>    CWG 330;
> * more tests to cover CWG 330 and P0388R4: array-conv16.C, array-conv17.C
> * 23_containers/span/lwg3255.cc specifically mentions P0388R4 in a FIXME, so
>    trivially adjusted.
> 
>>> @@ -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));
>>
>> Likewise.
> 
> Fixed too.
> 
> Bootstrapped/regtested on x86_64-linux, built Boost and cmcstl2.
> 
> Ok for trunk?

OK, thanks.

> 2019-10-09  Marek Polacek  <polacek@redhat.com>
> 
> 	PR c++/91364 - P0388R4: Permit conversions to arrays of	unknown bound.
> 	PR c++/69531 - DR 1307: Differently bounded array parameters.
> 	PR c++/88128 - DR 330: Qual convs and pointers to arrays of pointers.
> 	* 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): Don't check cv-quals of ARRAY_TYPEs.  Use
> 	comp_array_types to compare array types.  Look through arrays as per
> 	DR 330.
> 	(comp_ptr_ttypes_const): Use comp_array_types to compare array types.
> 	Look through arrays as per DR 330.
> 
> 	* g++.dg/conversion/qual1.C: New test.
> 	* g++.dg/conversion/qual2.C: New test.
> 	* g++.dg/conversion/qual3.C: New test.
> 	* g++.dg/conversion/ref2.C: New test.
> 	* g++.dg/conversion/ref3.C: New test.
> 	* 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-conv15.C: New test.
> 	* g++.dg/cpp2a/array-conv16.C: New test.
> 	* g++.dg/cpp2a/array-conv17.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.
> 
> 	* testsuite/23_containers/span/lwg3255.cc: Adjust test to match the
> 	post-P0388R4 behavior.
> 
> diff --git gcc/gcc/cp/call.c gcc/gcc/cp/call.c
> index 33ec6a2dcc0..55d2abaaddd 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...  */
> @@ -1066,7 +1069,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;
>   }
>   
> @@ -1366,7 +1369,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_either))
>   	/* In a C-style cast, we ignore CV-qualification because we
>   	   are allowed to perform a static_cast followed by a
>   	   const_cast.  */
> @@ -1668,7 +1671,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.  */
>         if (CONSTRUCTOR_NELTS (expr) == 1)
>   	{
>   	  tree elt = CONSTRUCTOR_ELT (expr, 0)->value;
> @@ -6958,6 +6968,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
> @@ -7377,8 +7408,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, "cannot bind reference of type %qH to %qI "
> +			    "due to different array bounds", totype, extype);
> +		else
> +		  error_at (loc, "binding reference of type %qH to %qI "
> +			    "discards qualifiers", totype, extype);
> +	      }
>   	    else
>   	      gcc_unreachable ();
>   	    maybe_print_user_conv_context (convs);
> @@ -7386,6 +7429,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.
>   
> @@ -7469,7 +7514,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:
> @@ -10061,6 +10109,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:
>   
> @@ -10174,6 +10266,38 @@ 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.  */
> +	  bool c1 = conv_binds_to_array_of_unknown_bound (t1);
> +	  bool c2 = conv_binds_to_array_of_unknown_bound (t2);
> +	  if (c1 && !c2)
> +	    return -1;
> +	  else if (!c1 && c2)
> +	    return 1;
> +	  else
> +	    return 0;
> +	}
>         else
>   	{
>   	  /* For ambiguous or aggregate conversions, use the target type as
> @@ -10469,6 +10593,28 @@ 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.  */
> +	  bool c1 = conv_binds_to_array_of_unknown_bound (ref_conv1);
> +	  bool c2 = conv_binds_to_array_of_unknown_bound (ref_conv2);
> +	  if (c1 && !c2)
> +	    return -1;
> +	  else if (!c1 && c2)
> +	    return 1;
> +
>   	  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 9ff617be2d4..c1301a451df 100644
> --- gcc/gcc/cp/cp-tree.h
> +++ gcc/gcc/cp/cp-tree.h
> @@ -7372,6 +7372,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);
> @@ -7462,7 +7466,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 ba334e7d5cb..a67dd4b155a 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,15 @@ 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.  STRICT is true if
> +   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 strict)
>   {
>     tree d1;
>     tree d2;
> @@ -1098,7 +1102,9 @@ 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 (strict
> +      ? !same_type_p (TREE_TYPE (t1), TREE_TYPE (t2))
> +      : !similar_type_p (TREE_TYPE (t1), TREE_TYPE (t2)))
>       return false;
>   
>     d1 = TYPE_DOMAIN (t1);
> @@ -1119,8 +1125,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 +1376,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),
> +			     /*strict=*/true))
>   	return false;
>         break;
>   
> @@ -1549,10 +1559,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;
>   }
> @@ -7867,7 +7877,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)
>   	    {
> @@ -9909,9 +9919,10 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
>   			   TYPE_OFFSET_BASETYPE (to)))
>   	return 0;
>   
> -      /* Const and volatile mean something different for function types,
> -	 so the usual checks are not appropriate.  */
> -      if (!FUNC_OR_METHOD_TYPE_P (to))
> +      /* Const and volatile mean something different for function and
> +	 array types, so the usual checks are not appropriate.  We'll
> +	 check the array type elements in further iterations.  */
> +      if (!FUNC_OR_METHOD_TYPE_P (to) && TREE_CODE (to) != ARRAY_TYPE)
>   	{
>   	  if (!at_least_as_qualified_p (to, from))
>   	    return 0;
> @@ -9930,7 +9941,17 @@ 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 (!TYPE_PTR_P (to) && !TYPE_PTRDATAMEM_P (to))
> +      /* P0388R4 allows a conversion from int[N] to int[] but not the
> +	 other way round.  When both arrays have bounds but they do
> +	 not match, then no conversion is possible.  */
> +      if (TREE_CODE (to) == ARRAY_TYPE
> +	  && !comp_array_types (to, from, bounds_first, /*strict=*/false))
> +	return 0;
> +
> +      if (!TYPE_PTR_P (to)
> +	  && !TYPE_PTRDATAMEM_P (to)
> +	  /* CWG 330 says we need to look through arrays.  */
> +	  && TREE_CODE (to) != ARRAY_TYPE)
>   	return ((constp >= 0 || to_more_cv_qualified)
>   		&& (is_opaque_pointer
>   		    || same_type_ignoring_top_level_qualifiers_p (to, from)));
> @@ -10033,10 +10054,10 @@ 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;
>   
> @@ -10053,7 +10074,14 @@ comp_ptr_ttypes_const (tree to, tree from)
>         if (VECTOR_TYPE_P (to))
>   	is_opaque_pointer = vector_targets_convertible_p (to, from);
>   
> -      if (!TYPE_PTR_P (to))
> +      if (TREE_CODE (to) == ARRAY_TYPE
> +	  /* Ignore cv-qualification, but if we see e.g. int[3] and int[4],
> +	     we must fail.  */
> +	  && !comp_array_types (to, from, cb, /*strict=*/false))
> +	return false;
> +
> +      /* CWG 330 says we need to look through arrays.  */
> +      if (!TYPE_PTR_P (to) && TREE_CODE (to) != ARRAY_TYPE)


>   	return (is_opaque_pointer
>   		|| same_type_ignoring_top_level_qualifiers_p (to, from));
>       }
> diff --git gcc/gcc/testsuite/g++.dg/conversion/qual1.C gcc/gcc/testsuite/g++.dg/conversion/qual1.C
> new file mode 100644
> index 00000000000..5022da9da47
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/conversion/qual1.C
> @@ -0,0 +1,51 @@
> +// PR c++/88128 - Implement DR 330: Qualification conversions and pointers to
> +// arrays of pointers.
> +
> +int *a[4];
> +const int *const(*ap1)[4] = &a;
> +/* if at some level k the P2 is more cv-qualified than P1, then there
> +   must be a const at every single level (other than level zero) of P2
> +   up until k.  */
> +const int *(*ap2)[4] = &a; // { dg-error "cannot convert" }
> +int *const(*ap3)[4] = &a;
> +int *(*ap4)[4] = &a;
> +int *(*const ap5)[4] = &a;
> +const int *const(*const ap6)[4] = &a;
> +int *const(*const ap7)[4] = &a;
> +int *(*const ap8)[4] = &a;
> +
> +const int *b[4];
> +const int *const(*bp1)[4] = &b;
> +const int *(*bp2)[4] = &b;
> +int *const(*bp3)[4] = &b; // { dg-error "cannot convert" }
> +int *(*bp4)[4] = &b; // { dg-error "cannot convert" }
> +int *(*const bp5)[4] = &b; // { dg-error "cannot convert" }
> +const int *const(*const bp6)[4] = &b;
> +int *const(*const bp7)[4] = &b; // { dg-error "cannot convert" }
> +int *(*const bp8)[4] = &b; // { dg-error "cannot convert" }
> +
> +int *c[2][3];
> +int const *const (*cp1)[3] = c;
> +int const *(*cp2)[3] = c; // { dg-error "cannot convert" }
> +int const *const (*const cp3)[3] = c;
> +int *const (*cp4)[3] = c;
> +int *(*cp5)[3] = c;
> +
> +double *const (*d)[3];
> +double const *const (*e)[3] = d;
> +int *(*f)[3];
> +const int *const (*g)[3] = f;
> +
> +// From PR88128.
> +int* (*xx)[];
> +const int* const(*yy)[] = xx;
> +
> +// From DR 330.
> +int main()
> +{
> +   double *array2D[2][3];
> +
> +   double       *       (*array2DPtr1)[3] = array2D;
> +   double       * const (*array2DPtr2)[3] = array2DPtr1;
> +   double const * const (*array2DPtr3)[3] = array2DPtr2;
> +}
> diff --git gcc/gcc/testsuite/g++.dg/conversion/qual2.C gcc/gcc/testsuite/g++.dg/conversion/qual2.C
> new file mode 100644
> index 00000000000..8a063a03e79
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/conversion/qual2.C
> @@ -0,0 +1,14 @@
> +// PR c++/88128 - DR 330: Qual convs and pointers to arrays of pointers.
> +
> +// Make sure we don't accept different bounds.
> +
> +int *a[4];
> +const int *const(*ap1)[5] = &a; // { dg-error "cannot convert" }
> +
> +int *(*b)[3];
> +const int *const (*bp1)[3] = &b; // { dg-error "cannot convert" }
> +const int *const (*bp2)[4] = &b; // { dg-error "cannot convert" }
> +int *(*bp3)[4] = &b; // { dg-error "cannot convert" }
> +
> +int *c[2][3];
> +int const *const (*cp1)[4] = c; // { dg-error "cannot convert" }
> diff --git gcc/gcc/testsuite/g++.dg/conversion/qual3.C gcc/gcc/testsuite/g++.dg/conversion/qual3.C
> new file mode 100644
> index 00000000000..4b466d9ea5d
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/conversion/qual3.C
> @@ -0,0 +1,53 @@
> +// PR c++/88128 - DR 330: Qual convs and pointers to arrays of pointers.
> +// { dg-do compile { target c++17 } }
> +
> +using P = int *(*)[3];
> +using Q = const int *const (*)[3];
> +using Qi = const int *[3];
> +using Q2 = Qi const *;
> +using R = const int *const (*)[4];
> +using S = const int *const (*)[];
> +using T = const int *(*)[];
> +
> +void
> +f (P p, Q q, Q2 q2, R r, S s, T t)
> +{
> +  q = p;
> +  q2 = p;
> +  r = p; // { dg-error "cannot convert" }
> +  t = p; // { dg-error "cannot convert" }
> +  s = t;
> +  t = s; // { dg-error "invalid conversion" }
> +
> +  // Test const_cast.
> +  const_cast<P>(q);
> +  const_cast<P>(q2);
> +  const_cast<Q>(p);
> +  const_cast<Q2>(p);
> +  const_cast<S>(p); // { dg-error "invalid .const_cast." }
> +  const_cast<P>(s); // { dg-error "invalid .const_cast." }
> +  const_cast<S>(q); // { dg-error "invalid .const_cast." }
> +  const_cast<S>(q2); // { dg-error "invalid .const_cast." }
> +  const_cast<Q>(s); // { dg-error "invalid .const_cast." }
> +  const_cast<Q2>(s); // { dg-error "invalid .const_cast." }
> +  const_cast<T>(s);
> +  const_cast<S>(t);
> +  const_cast<T>(q); // { dg-error "invalid .const_cast." }
> +  const_cast<Q>(t); // { dg-error "invalid .const_cast." }
> +
> +  // Test reinterpret_cast.
> +  reinterpret_cast<P>(q); // { dg-error "casts away qualifiers" }
> +  reinterpret_cast<P>(q2); // { dg-error "casts away qualifiers" }
> +  reinterpret_cast<Q>(p);
> +  reinterpret_cast<Q2>(p);
> +  reinterpret_cast<S>(p);
> +  reinterpret_cast<P>(s); // { dg-error "casts away qualifiers" }
> +  reinterpret_cast<S>(q);
> +  reinterpret_cast<S>(q2);
> +  reinterpret_cast<Q>(s);
> +  reinterpret_cast<Q2>(s);
> +  reinterpret_cast<T>(s); // { dg-error "casts away qualifiers" }
> +  reinterpret_cast<S>(t);
> +  reinterpret_cast<T>(q); // { dg-error "casts away qualifiers" }
> +  reinterpret_cast<Q>(t);
> +}
> diff --git gcc/gcc/testsuite/g++.dg/conversion/ref2.C gcc/gcc/testsuite/g++.dg/conversion/ref2.C
> new file mode 100644
> index 00000000000..418e711946f
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/conversion/ref2.C
> @@ -0,0 +1,29 @@
> +// PR c++/88128 - Implement DR 330: Qualification conversions and pointers to
> +// arrays of pointers.
> +
> +int *ar[4];
> +/* if at some level k the P2 is more cv-qualified than P1, then there
> +   must be a const at every single level (other than level zero) of P2
> +   up until k.  */
> +const int *(&arp)[4] = ar; // { dg-error "discards qualifiers" }
> +const int *const(&arp2)[4] = ar;
> +int *const(&arp3)[4] = ar;
> +int *(&arp4)[4] = ar;
> +
> +const int *br[4];
> +const int *(&brp)[4] = br;
> +const int *const(&brp2)[4] = br;
> +int *const(&brp3)[4] = br; // { dg-error "discards qualifiers" }
> +int *(&brp4)[4] = br; // { dg-error "discards qualifiers" }
> +
> +int *c[2][3];
> +int const *const (&cp1)[3] = *c;
> +int const *(&cp2)[3] = *c; // { dg-error "discards qualifiers" }
> +int *const (&cp3)[3] = *c;
> +int *(&cp4)[3] = *c;
> +
> +double *const (*d)[3];
> +double const *const (&e)[3] = *d;
> +
> +int *(*f)[3];
> +const int *const (&g)[3] = *f;
> diff --git gcc/gcc/testsuite/g++.dg/conversion/ref3.C gcc/gcc/testsuite/g++.dg/conversion/ref3.C
> new file mode 100644
> index 00000000000..ca5326ce8e8
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/conversion/ref3.C
> @@ -0,0 +1,4 @@
> +int a[2];
> +const int (&rc)[2] = a;
> +volatile int (&rv)[2] = a;
> +const volatile int (&rcv)[2] = a;
> 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..1ee1a771f63
> --- /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 "cannot bind reference" }
> +int (&z)[3] = a; // { dg-error "cannot bind reference" }
> +
> +void f(int (*)[3]);
> +void f2(int (&)[3]);
> +
> +void
> +test ()
> +{
> +  f(b); // { dg-error "cannot convert" }
> +  f2(c); // { dg-error "cannot bind reference" }
> +}
> 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-conv15.C gcc/gcc/testsuite/g++.dg/cpp2a/array-conv15.C
> new file mode 100644
> index 00000000000..033a74683a7
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv15.C
> @@ -0,0 +1,23 @@
> +// PR c++/69531 - DR 1307, Overload resolution based on size of array init-list.
> +// { dg-do run { target c++2a } }
> +
> +int f(int, int const(&)[2]) { return 1; }
> +int f(double, int const(&)[2]) { return 2; }
> +
> +int f2(int, int const(&)[1]) { return 1; }
> +int f2(int, int const(&)[2]) { return 2; }
> +
> +int f3(int, int const(&)[]) { return 1; }
> +int f3(double, int const(&)[]) { return 2; }
> +
> +int main ()
> +{
> +  if (f (1, {1}) != 1)
> +    __builtin_abort ();
> +
> +  if (f2 (1, {1}) != 1)
> +    __builtin_abort ();
> +
> +  if (f3 (1, {1}) != 1)
> +    __builtin_abort ();
> +}
> diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv16.C gcc/gcc/testsuite/g++.dg/cpp2a/array-conv16.C
> new file mode 100644
> index 00000000000..bfb39d1c12c
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv16.C
> @@ -0,0 +1,16 @@
> +// PR c++/91364 - P0388R4: Permit conversions to arrays of unknown bound.
> +// { dg-do compile { target c++2a } }
> +
> +using P = int *(*)[3];
> +using S = const int *const (*)[];
> +using Q = const int *const (*)[3];
> +using Qi = const int *[3];
> +using Q2 = Qi const *;
> +
> +void
> +f (P p, S s, Q q, Q2 q2)
> +{
> +  s = p;
> +  s = q;
> +  s = q2;
> +}
> diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv17.C gcc/gcc/testsuite/g++.dg/cpp2a/array-conv17.C
> new file mode 100644
> index 00000000000..3313ed466fb
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv17.C
> @@ -0,0 +1,39 @@
> +// PR c++/91364 - P0388R4: Permit conversions to arrays of unknown bound.
> +// { dg-do compile { target c++2a } }
> +
> +// As conversion/qual1.C, but with [].
> +
> +int *a[4];
> +const int *const(*ap1)[] = &a;
> +/* if at some level k the P2 is more cv-qualified than P1, then there
> +   must be a const at every single level (other than level zero) of P2
> +   up until k.  */
> +const int *(*ap2)[] = &a; // { dg-error "cannot convert" }
> +int *const(*ap3)[] = &a;
> +int *(*ap4)[] = &a;
> +int *(*const ap5)[] = &a;
> +const int *const(*const ap6)[] = &a;
> +int *const(*const ap7)[] = &a;
> +int *(*const ap8)[] = &a;
> +
> +const int *b[4];
> +const int *const(*bp1)[] = &b;
> +const int *(*bp2)[] = &b;
> +int *const(*bp3)[] = &b; // { dg-error "cannot convert" }
> +int *(*bp4)[] = &b; // { dg-error "cannot convert" }
> +int *(*const bp5)[] = &b; // { dg-error "cannot convert" }
> +const int *const(*const bp6)[] = &b;
> +int *const(*const bp7)[] = &b; // { dg-error "cannot convert" }
> +int *(*const bp8)[] = &b; // { dg-error "cannot convert" }
> +
> +int *c[2][3];
> +int const *const (*cp1)[] = c;
> +int const *(*cp2)[] = c; // { dg-error "cannot convert" }
> +int const *const (*const cp3)[] = c;
> +int *const (*cp4)[] = c;
> +int *(*cp5)[] = c;
> +
> +double *const (*d)[3];
> +double const *const (*e)[] = d;
> +int *(*f)[3];
> +const int *const (*g)[] = f;
> 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..82f615db2e7
> --- /dev/null
> +++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv9.C
> @@ -0,0 +1,27 @@
> +// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown bound.
> +// { dg-do compile { target c++2a } }
> +
> +int arr[1];
> +extern int arr2[];
> +
> +void
> +test ()
> +{
> +  int (&r)[1] = const_cast<int(&)[1]>(arr);
> +  int (&r2)[] = const_cast<int(&)[]>(arr); // { dg-error "invalid" }
> +  int (&r3)[1] = (int(&)[1]) arr;
> +  int (&r4)[] = (int(&)[]) arr;
> +  int (&r5)[1] = static_cast<int(&)[1]>(arr);
> +  int (&r6)[] = static_cast<int(&)[]>(arr);
> +
> +  // Try c_cast_p.
> +  int(*p1)[] = (int(*)[]) &arr;
> +  int(*p2)[1] = (int(*)[]) &arr; // { dg-error "cannot convert" }
> +  int(*p3)[] = (int(*)[1]) &arr;
> +  int(*p4)[] = (int(*)[1]) &arr2;
> +  int(*p5)[] = (int(*)[]) (int(*)[1]) &arr;
> +  int(*p6)[] = (int(*)[1]) (int(*)[]) &arr;
> +  int(*p7)[] = static_cast<int(*)[]>(&arr);
> +  int(*p8)[] = static_cast<int(*)[1]>(&arr);
> +  int(*p9)[] = static_cast<int(*)[1]>(&arr2); // { 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 "" }
> diff --git gcc/libstdc++-v3/testsuite/23_containers/span/lwg3255.cc gcc/libstdc++-v3/testsuite/23_containers/span/lwg3255.cc
> index 638c88101f9..bab7da3bf19 100644
> --- gcc/libstdc++-v3/testsuite/23_containers/span/lwg3255.cc
> +++ gcc/libstdc++-v3/testsuite/23_containers/span/lwg3255.cc
> @@ -28,8 +28,7 @@ using std::is_constructible_v;
>   
>   // LWG 3255 span's array constructor is too strict
>   
> -// FIXME: remove '!' from next line when P0388R4 is implemented:
> -static_assert( ! is_constructible_v<span<const int* const>, array<int*, 2>> );
> +static_assert( is_constructible_v<span<const int* const>, array<int*, 2>> );
>   static_assert( is_constructible_v<span<const int>, array<const int, 4>> );
>   
>   static_assert( is_constructible_v<span<int, 1>, int(&)[1]> );
>
Jonathan Wakely Oct. 10, 2019, 7:08 a.m. UTC | #11
On 09/10/19 16:29 -0400, Marek Polacek wrote:
>On Wed, Oct 09, 2019 at 11:01:39AM +0100, Jonathan Wakely wrote:
>> On 07/10/19 14:56 -0400, Jason Merrill wrote:
>> > On 10/7/19 1:42 PM, Marek Polacek wrote:
>> > > @@ -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?
>>
>> I don't think the diagnostic would be very good if we say "adds array
>> bounds" though. How about being consistent with the existing error for
>> similar cases?
>>
>> a.cc:4:17: error: invalid initialization of reference of type ‘int (&)[3]’ from expression of type ‘int []’
>>   int (&b)[3] = a;
>>                 ^
>
>In my latest patch the error message reads "cannot bind reference of type T to U due to different array bounds".
>It'd be trivial to adjust it if anyone hates that.

Works for me.
diff mbox series

Patch

diff --git gcc/gcc/cp/call.c gcc/gcc/cp/call.c
index 56dcbd391c1..7002a9ba57f 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,9 @@  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,
+					 /*allow_redeclaration=*/false))
 	/* In a C-style cast, we ignore CV-qualification because we
 	   are allowed to perform a static_cast followed by a
 	   const_cast.  */
@@ -1670,7 +1675,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.  */
       if (CONSTRUCTOR_NELTS (expr) == 1)
 	{
 	  tree elt = CONSTRUCTOR_ELT (expr, 0)->value;
@@ -6982,6 +6994,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)
+    return;
+
+  if (conv_binds_to_array_of_unknown_bound (c)
+      ^ (TYPE_DOMAIN (type) == NULL_TREE))
+    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
@@ -7410,6 +7443,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 +7528,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 +10121,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 +10278,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 ();
+	}
       else
 	{
 	  /* For ambiguous or aggregate conversions, use the target type as
@@ -10491,6 +10604,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;
+
 	  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 be1a44e4373..f812c235c11 100644
--- gcc/gcc/cp/cp-tree.h
+++ gcc/gcc/cp/cp-tree.h
@@ -7456,7 +7456,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, bool);
 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..e821f5dfdaf 100644
--- gcc/gcc/cp/typeck.c
+++ gcc/gcc/cp/typeck.c
@@ -1549,10 +1549,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, /*allow_redeclaration=*/true);
 
   return false;
 }
@@ -7858,7 +7858,8 @@  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,
+				 /*allow_redeclaration=*/false))
 	{
 	  if (valid_p)
 	    {
@@ -9888,6 +9889,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 +9922,14 @@  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)
+	is_comp_array = comp_array_types (to, from,
+					  /*allow_redeclaration=*/true);
+
       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 +10030,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.  ALLOW_REDECLARATION is true if [] can match [size].  */
 
 bool
-comp_ptr_ttypes_const (tree to, tree from)
+comp_ptr_ttypes_const (tree to, tree from, bool allow_redeclaration)
 {
   bool is_opaque_pointer = false;
+  bool is_comp_array = false;
 
   for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from))
     {
@@ -10043,8 +10051,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, allow_redeclaration);
+
       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..f009e1e66b5
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv10.C
@@ -0,0 +1,21 @@ 
+// 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].
+
+extern int a[];
+extern int (*b)[];
+extern int (&c)[];
+int (&y)[] = a;
+int (&x)[3] = y; // { dg-warning "conversions to arrays of unknown bound are only available" "" { target c++17_down } }
+
+void f(int (*)[3]);
+void f2(int (&)[3]);
+
+void
+test ()
+{
+  f(b); // { dg-warning "conversions to arrays of unknown bound are only available" "" { target c++17_down } }
+  f2(c); // { dg-warning "conversions to arrays of unknown bound are only available" "" { target c++17_down } }
+}
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-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..3575f1f5a4d 100644
--- gcc/gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C
+++ gcc/gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C
@@ -20,8 +20,8 @@  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_3_ints = ptr_to_array_of_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 "conversions to arrays" "" { target c++17_down } }
 
   ptr_to_array_of_3_ints = ptr_to_array_of_5_ints;	// { dg-error "" } 
   ptr_to_array_of_5_ints = ptr_to_array_of_3_ints;	// { dg-error "" } 
diff --git gcc/gcc/testsuite/g++.old-deja/g++.bugs/900520_02.C gcc/gcc/testsuite/g++.old-deja/g++.bugs/900520_02.C
index 69e0440c120..7d0541d936f 100644
--- gcc/gcc/testsuite/g++.old-deja/g++.bugs/900520_02.C
+++ gcc/gcc/testsuite/g++.old-deja/g++.bugs/900520_02.C
@@ -9,17 +9,17 @@  typedef int u_array[];
 typedef b_array &b_array_ref;
 typedef u_array &u_array_ref;
 
-void take_b_array_ref (b_array_ref arg) { } // { dg-message "" } passed to here
+void take_b_array_ref (b_array_ref arg) { }
 
 extern u_array u_array_gbl_obj;
 
 u_array_ref u_array_ref_gbl_obj0 = u_array_gbl_obj;
 
-b_array_ref b_array_ref_gbl_obj0 = u_array_ref_gbl_obj0; // { dg-error "" } invalid declaration
+b_array_ref b_array_ref_gbl_obj0 = u_array_ref_gbl_obj0; // { dg-error "conversions to arrays" "" { target c++17_down } }
 
 void test_passing ()
 {
-  take_b_array_ref (u_array_ref_gbl_obj0); // { dg-error "" } invalid call
+  take_b_array_ref (u_array_ref_gbl_obj0); // { dg-error "conversions to arrays" "" { target c++17_down } }
 }
 
 b_array u_array_gbl_obj;
diff --git gcc/gcc/testsuite/g++.old-deja/g++.other/typeck1.C gcc/gcc/testsuite/g++.old-deja/g++.other/typeck1.C
index d520667b63d..bb4db80e214 100644
--- gcc/gcc/testsuite/g++.old-deja/g++.other/typeck1.C
+++ gcc/gcc/testsuite/g++.old-deja/g++.other/typeck1.C
@@ -13,5 +13,5 @@  extern int m[7];    // OK
 void f(int (*j)[3])
 {
   extern int (*k)[];
-  f(k);             // { dg-error "" } passing wrong type
+  f(k); // { dg-error "conversions to arrays" "" { target c++17_down } }
 }
diff --git gcc/libstdc++-v3/testsuite/20_util/shared_ptr/cons/array.cc gcc/libstdc++-v3/testsuite/20_util/shared_ptr/cons/array.cc
index ef51e2c63bc..2d05b1d0cb8 100644
--- gcc/libstdc++-v3/testsuite/20_util/shared_ptr/cons/array.cc
+++ gcc/libstdc++-v3/testsuite/20_util/shared_ptr/cons/array.cc
@@ -35,7 +35,9 @@  static_assert( check<int, int[]>() );
 static_assert( check<int, int[2]>() );
 static_assert( check<int[2], void>() );
 static_assert( check<int[2], int>() );
-static_assert( check<int[2], int[]>() );
+// This is fine since P0388R4: we can form a conversion from
+// int[] to int[2].
+static_assert( !check<int[2], int[]>() );
 static_assert( check<int[], void>() );
 static_assert( check<int[], int>() );
 
diff --git gcc/libstdc++-v3/testsuite/experimental/memory/shared_ptr/cons/copy_ctor_neg.cc gcc/libstdc++-v3/testsuite/experimental/memory/shared_ptr/cons/copy_ctor_neg.cc
index 89f7fc9bb12..bcc26d3766b 100644
--- gcc/libstdc++-v3/testsuite/experimental/memory/shared_ptr/cons/copy_ctor_neg.cc
+++ gcc/libstdc++-v3/testsuite/experimental/memory/shared_ptr/cons/copy_ctor_neg.cc
@@ -40,7 +40,7 @@  void
 test02()
 {
   std::experimental::shared_ptr<A[]> a(new A[3]);
-  std::experimental::shared_ptr<A[2]> spa(a); // { dg-error "no matching" }
+  std::experimental::shared_ptr<A[2]> spa(a);
 }
 
 int
diff --git gcc/libstdc++-v3/testsuite/experimental/memory/shared_ptr/cons/unique_ptr_ctor.cc gcc/libstdc++-v3/testsuite/experimental/memory/shared_ptr/cons/unique_ptr_ctor.cc
index be23571e0ef..570f82377e3 100644
--- gcc/libstdc++-v3/testsuite/experimental/memory/shared_ptr/cons/unique_ptr_ctor.cc
+++ gcc/libstdc++-v3/testsuite/experimental/memory/shared_ptr/cons/unique_ptr_ctor.cc
@@ -49,7 +49,8 @@  static_assert( !constructible< A,    A[]  >(), "A -> A[] not compatible" );
 static_assert( !constructible< A,    A[1] >(), "A -> A[1] not compatible" );
 static_assert( !constructible< A[],  A    >(), "A[] -> A not compatible" );
 static_assert(  constructible< A[],  A[]  >(), "A[] -> A[] compatible" );
-static_assert( !constructible< A[],  A[1] >(), "A[] -> A[1] not compatible" );
+// Changed in P0388R4.
+static_assert(  constructible< A[],  A[1] >(), "A[] -> A[1] not compatible" );
 
 static_assert(  constructible< B,    A    >(), "B -> A compatible" );
 static_assert( !constructible< B,    A[]  >(), "B -> A[] not compatible" );
diff --git gcc/libstdc++-v3/testsuite/experimental/memory/shared_ptr/cons/weak_ptr_ctor.cc gcc/libstdc++-v3/testsuite/experimental/memory/shared_ptr/cons/weak_ptr_ctor.cc
index 08df647d77e..bbe47e487a7 100644
--- gcc/libstdc++-v3/testsuite/experimental/memory/shared_ptr/cons/weak_ptr_ctor.cc
+++ gcc/libstdc++-v3/testsuite/experimental/memory/shared_ptr/cons/weak_ptr_ctor.cc
@@ -42,7 +42,8 @@  static_assert( !constructible< A,    A[]  >(), "A -> A[] not compatible" );
 static_assert( !constructible< A,    A[1] >(), "A -> A[1] not compatible" );
 static_assert( !constructible< A[],  A    >(), "A[] -> A not compatible" );
 static_assert(  constructible< A[],  A[]  >(), "A[] -> A[] compatible" );
-static_assert( !constructible< A[],  A[1] >(), "A[] -> A[1] not compatible" );
+// Changed in P0388R4.
+static_assert(  constructible< A[],  A[1] >(), "A[] -> A[1] not compatible" );
 static_assert( !constructible< A[1], A    >(), "A[1] -> A not compatible" );
 static_assert(  constructible< A[1], A[]  >(), "A[1] -> A[] compatible" );
 static_assert(  constructible< A[1], A[1] >(), "A[1] -> A[1] compatible" );