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  <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 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 "" }


Reply via email to