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