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

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

Reply via email to