On Sun, Oct 06, 2019 at 03:39:25PM +0000, Tam S. B. wrote:
Hi, sorry for chiming in.
Hi, no worries, comments are welcome!
IIUC P0388R4 does not allow implicit conversion from `T(*)[]` to `T(*)[N]`. Per
[conv.qual]/3,
A prvalue of type `T1` can be converted to type `T2` if the cv-combined type of
`T1` and `T2` is `T2`.
When T1 is `T(*)[]` and T2 is `T(*)[N]`, the "cv-combined type" is `T(*)[]`,
which is not the same as T2 = `T(*)[N]`, so conversion should not be permitted.
That is to say, either `comp_ptr_ttypes_real` should not use
`comp_array_types`, or `comp_array_types` should be extended to handle this
case correctly.
Indeed, I must've glossed over that. Fixed by introducing compare_bounds_t,
which says how we should compare arrays when one of them is [].
similar_type_p still must say "yes" for T(*)[] and T(*)[N] though.
Also, does this patch permit conversion from `T(*)[N]` to `const T(*)[]`? I
think [conv.qual] allows this conversion, but IIUC `comp_array_types` will
return false in this case, as it uses `same_type_p`.
It does now, array-conv14.C tests it.
Thanks for looking and catching these problems!
Bootstrapped/regtested on x86_64-linux, built Boost and cmcstl2.
Ok for trunk?
2019-10-07 Marek Polacek <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-conv2.C: New test.
* g++.dg/cpp2a/array-conv3.C: New test.
* g++.dg/cpp2a/array-conv4.C: New test.
* g++.dg/cpp2a/array-conv5.C: New test.
* g++.dg/cpp2a/array-conv6.C: New test.
* g++.dg/cpp2a/array-conv7.C: New test.
* g++.dg/cpp2a/array-conv8.C: New test.
* g++.dg/cpp2a/array-conv9.C: New test.
* g++.old-deja/g++.bugs/900321_01.C: Adjust dg-error.
diff --git gcc/gcc/cp/call.c gcc/gcc/cp/call.c
index 56dcbd391c1..1a9c296a812 100644
--- gcc/gcc/cp/call.c
+++ gcc/gcc/cp/call.c
@@ -122,7 +122,8 @@ struct conversion {
of using this field directly. */
conversion *next;
/* The expression at the beginning of the conversion chain. This
- variant is used only if KIND is ck_identity or ck_ambig. */
+ variant is used only if KIND is ck_identity or ck_ambig. You can
+ use conv_get_original_expr to get this expression. */
tree expr;
/* The array of conversions for an initializer_list, so this
variant is used only when KIN D is ck_list. */
@@ -223,6 +224,8 @@ static void add_candidates (tree, tree, const vec<tree,
va_gc> *, tree, tree,
tsubst_flags_t);
static conversion *merge_conversion_sequences (conversion *, conversion *);
static tree build_temp (tree, tree, int, diagnostic_t *, tsubst_flags_t);
+static conversion *build_identity_conv (tree, tree);
+static inline bool conv_binds_to_array_of_unknown_bound (conversion *);
/* Returns nonzero iff the destructor name specified in NAME matches BASETYPE.
NAME can take many forms... */
@@ -1078,7 +1081,7 @@ build_array_conv (tree type, tree ctor, int flags,
tsubst_flags_t complain)
c->rank = rank;
c->user_conv_p = user;
c->bad_p = bad;
- c->u.next = NULL;
+ c->u.next = build_identity_conv (TREE_TYPE (ctor), ctor);
return c;
}
@@ -1378,7 +1381,7 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
if (same_type_p (from, to))
/* OK */;
- else if (c_cast_p && comp_ptr_ttypes_const (to, from))
+ else if (c_cast_p && comp_ptr_ttypes_const (to, from, bounds_none))
/* In a C-style cast, we ignore CV-qualification because we
are allowed to perform a static_cast followed by a
const_cast. */
int q1 = cp_type_quals (TREE_TYPE (ref_conv1->type));
int q2 = cp_type_quals (TREE_TYPE (ref_conv2->type));
if (ref_conv1->bad_p)
diff --git gcc/gcc/cp/cp-tree.h gcc/gcc/cp/cp-tree.h
index b82b5808197..f6bc4ac54b4 100644
--- gcc/gcc/cp/cp-tree.h
+++ gcc/gcc/cp/cp-tree.h
@@ -7374,6 +7374,10 @@ extern void cxx_print_error_function
(diagnostic_context *,
struct diagnostic_info *);
/* in typeck.c */
+/* Says how we should behave when comparing two arrays one of which
+ has unknown bounds. */
+enum compare_bounds_t { bounds_none, bounds_either, bounds_first };
+
extern bool cxx_mark_addressable (tree, bool = false);
extern int string_conv_p (const_tree, const_tree, int);
extern tree cp_truthvalue_conversion (tree);
@@ -7464,7 +7468,7 @@ extern tree convert_for_initialization (tree,
tree, tree, int,
impl_conv_rhs, tree, int,
tsubst_flags_t);
extern int comp_ptr_ttypes (tree, tree);
-extern bool comp_ptr_ttypes_const (tree, tree);
+extern bool comp_ptr_ttypes_const (tree, tree, compare_bounds_t);
extern bool error_type_p (const_tree);
extern bool ptr_reasonably_similar (const_tree, const_tree);
extern tree build_ptrmemfunc (tree, tree, int, bool,
diff --git gcc/gcc/cp/typeck.c gcc/gcc/cp/typeck.c
index d549450a605..1994520268f 100644
--- gcc/gcc/cp/typeck.c
+++ gcc/gcc/cp/typeck.c
@@ -54,7 +54,7 @@ static tree rationalize_conditional_expr (enum tree_code,
tree,
tsubst_flags_t);
static int comp_ptr_ttypes_real (tree, tree, int);
static bool comp_except_types (tree, tree, bool);
-static bool comp_array_types (const_tree, const_tree, bool);
+static bool comp_array_types (const_tree, const_tree, compare_bounds_t, bool);
static tree pointer_diff (location_t, tree, tree, tree, tsubst_flags_t, tree
*);
static tree get_delta_difference (tree, tree, bool, bool, tsubst_flags_t);
static void casts_away_constness_r (tree *, tree *, tsubst_flags_t);
@@ -1084,11 +1084,16 @@ comp_except_specs (const_tree t1, const_tree t2, int
exact)
return exact == ce_derived || base == NULL_TREE || length == list_length
(t1);
}
-/* Compare the array types T1 and T2. ALLOW_REDECLARATION is true if
- [] can match [size]. */
+/* Compare the array types T1 and T2. CB says how we should behave when
+ comparing array bounds: bounds_none doesn't allow dimensionless arrays,
+ bounds_either says than any array can be [], bounds_first means that
+ onlt T1 can be an array with unknown bounds. TLQ_MATCH is true if
+ top-level qualifiers must match when comparing the types of the array
+ elements. */
static bool
-comp_array_types (const_tree t1, const_tree t2, bool allow_redeclaration)
+comp_array_types (const_tree t1, const_tree t2, compare_bounds_t cb,
+ bool tlq_match)
{
tree d1;
tree d2;
@@ -1098,7 +1103,10 @@ comp_array_types (const_tree t1, const_tree t2, bool
allow_redeclaration)
return true;
/* The type of the array elements must be the same. */
- if (!same_type_p (TREE_TYPE (t1), TREE_TYPE (t2)))
+ if (tlq_match
+ ? !same_type_p (TREE_TYPE (t1), TREE_TYPE (t2))
+ : !same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (t1),
+ TREE_TYPE (t2)))
return false;
d1 = TYPE_DOMAIN (t1);
@@ -1119,8 +1127,10 @@ comp_array_types (const_tree t1, const_tree t2, bool
allow_redeclaration)
declarations for an array object can specify
array types that differ by the presence or absence of a major
array bound (_dcl.array_). */
- if (!d1 || !d2)
- return allow_redeclaration;
+ if (!d1 && d2)
+ return cb >= bounds_either;
+ else if (d1 && !d2)
+ return cb == bounds_either;
/* Check that the dimensions are the same. */
@@ -1368,7 +1378,9 @@ structural_comptypes (tree t1, tree t2, int strict)
case ARRAY_TYPE:
/* Target types must match incl. qualifiers. */
- if (!comp_array_types (t1, t2, !!(strict & COMPARE_REDECLARATION)))
+ if (!comp_array_types (t1, t2, ((strict & COMPARE_REDECLARATION)
+ ? bounds_either : bounds_none),
+ /*tlq_match=*/true))
return false;
break;
@@ -1549,10 +1561,10 @@ similar_type_p (tree type1, tree type2)
if (same_type_ignoring_top_level_qualifiers_p (type1, type2))
return true;
- /* FIXME This ought to handle ARRAY_TYPEs too. */
if ((TYPE_PTR_P (type1) && TYPE_PTR_P (type2))
- || (TYPE_PTRDATAMEM_P (type1) && TYPE_PTRDATAMEM_P (type2)))
- return comp_ptr_ttypes_const (type1, type2);
+ || (TYPE_PTRDATAMEM_P (type1) && TYPE_PTRDATAMEM_P (type2))
+ || (TREE_CODE (type1) == ARRAY_TYPE && TREE_CODE (type2) == ARRAY_TYPE))
+ return comp_ptr_ttypes_const (type1, type2, bounds_either);
return false;
}
@@ -7858,7 +7870,7 @@ build_const_cast_1 (tree dst_type, tree expr,
tsubst_flags_t complain,
if (TYPE_PTR_P (src_type) || TYPE_PTRDATAMEM_P (src_type))
{
- if (comp_ptr_ttypes_const (dst_type, src_type))
+ if (comp_ptr_ttypes_const (dst_type, src_type, bounds_none))
{
if (valid_p)
{
@@ -9888,6 +9900,7 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
{
bool to_more_cv_qualified = false;
bool is_opaque_pointer = false;
+ bool is_comp_array = false;
for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from))
{
@@ -9920,9 +9933,16 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
if (VECTOR_TYPE_P (to))
is_opaque_pointer = vector_targets_convertible_p (to, from);
+ if (TREE_CODE (to) == ARRAY_TYPE)
+ /* P0388R4 allows a conversion from int[N] to int[] but not the
+ other way round. */
+ is_comp_array = comp_array_types (to, from, bounds_first,
+ /*tlq_match=*/false);
+
if (!TYPE_PTR_P (to) && !TYPE_PTRDATAMEM_P (to))
return ((constp >= 0 || to_more_cv_qualified)
&& (is_opaque_pointer
+ || is_comp_array
|| same_type_ignoring_top_level_qualifiers_p (to, from)));
}
}
@@ -10023,12 +10043,13 @@ ptr_reasonably_similar (const_tree to, const_tree
from)
/* Return true if TO and FROM (both of which are POINTER_TYPEs or
pointer-to-member types) are the same, ignoring cv-qualification at
- all levels. */
+ all levels. CB says how we should behave when comparing array bounds. */
bool
-comp_ptr_ttypes_const (tree to, tree from)
+comp_ptr_ttypes_const (tree to, tree from, compare_bounds_t cb)
{
bool is_opaque_pointer = false;
+ bool is_comp_array = false;
for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from))
{
@@ -10043,8 +10064,12 @@ comp_ptr_ttypes_const (tree to, tree from)
if (VECTOR_TYPE_P (to))
is_opaque_pointer = vector_targets_convertible_p (to, from);
+ if (TREE_CODE (to) == ARRAY_TYPE)
+ is_comp_array = comp_array_types (to, from, cb, /*tlq_match=*/false);
+
if (!TYPE_PTR_P (to))
return (is_opaque_pointer
+ || is_comp_array
|| same_type_ignoring_top_level_qualifiers_p (to, from));
}
}
diff --git gcc/gcc/testsuite/g++.dg/cpp0x/initlist-array3.C
gcc/gcc/testsuite/g++.dg/cpp0x/initlist-array3.C
index 1a94f4ed55b..4140cd92d7b 100644
--- gcc/gcc/testsuite/g++.dg/cpp0x/initlist-array3.C
+++ gcc/gcc/testsuite/g++.dg/cpp0x/initlist-array3.C
@@ -6,5 +6,6 @@ void composite (int const (&) [3]);
int main ()
{
- composite({0,1}); // { dg-error "ambiguous" }
+ // Not ambiguous since CWG 1307.
+ composite({0,1});
}
diff --git gcc/gcc/testsuite/g++.dg/cpp0x/initlist-array7.C
gcc/gcc/testsuite/g++.dg/cpp0x/initlist-array7.C
new file mode 100644
index 00000000000..7a689c6675f
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp0x/initlist-array7.C
@@ -0,0 +1,21 @@
+// PR c++/69531 - DR 1307, Overload resolution based on size of array
init-list.
+// { dg-do run { target c++11 } }
+
+int f(int const(&)[2]) { return 1; }
+int f(int const(&)[3]) { return 2; }
+
+int
+main ()
+{
+ if (f({}) != 1)
+ __builtin_abort ();
+
+ if (f({1}) != 1)
+ __builtin_abort ();
+
+ if (f({1, 2}) != 1)
+ __builtin_abort ();
+
+ if (f({1, 2, 3}) != 2)
+ __builtin_abort ();
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp0x/initlist-array8.C
gcc/gcc/testsuite/g++.dg/cpp0x/initlist-array8.C
new file mode 100644
index 00000000000..ac2774e06b4
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp0x/initlist-array8.C
@@ -0,0 +1,35 @@
+// PR c++/69531 - DR 1307, Overload resolution based on size of array
init-list.
+// { dg-do run { target c++2a } }
+
+int f(int (&)[1][1]) { return 1; }
+int f(int (&)[1][2]) { return 2; }
+
+int g(int (&&)[2][1]) { return 1; }
+int g(int (&&)[2][2]) { return 2; }
+
+int h(int (&&)[][1]) { return 1; }
+int h(int (&&)[][2]) { return 2; }
+
+int
+main ()
+{
+ int arr1[1][1];
+ int arr2[1][2];
+
+ if (f(arr1) != 1)
+ __builtin_abort ();
+ if (f(arr2) != 2)
+ __builtin_abort ();
+
+ if (g({ { 1, 2 }, { 3 } }) != 2)
+ __builtin_abort ();
+
+ if (g({ { 1, 2 }, { 3, 4 } }) != 2)
+ __builtin_abort ();
+
+ if (h({ { 1, 2 }, { 3 } }) != 2)
+ __builtin_abort ();
+
+ if (h({ { 1, 2 }, { 3, 4 } }) != 2)
+ __builtin_abort ();
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv1.C
gcc/gcc/testsuite/g++.dg/cpp2a/array-conv1.C
new file mode 100644
index 00000000000..e90b340b0d6
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv1.C
@@ -0,0 +1,33 @@
+// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown
bound.
+// { dg-do compile { target c++17 } }
+// { dg-options "-Wpedantic" }
+// C++17, because that has CWG 393.
+
+void f(int(&)[]);
+void fp(int(*)[]);
+void f2(int(&)[][10]);
+void fp2(int(*)[][10]);
+int arr[10];
+int arr2[10][10];
+
+void
+g ()
+{
+ f (arr); // { dg-warning "conversions to arrays of unknown bound are only available"
"" { target c++17_down } }
+ fp (&arr); // { dg-warning "conversions to arrays of unknown bound are only available"
"" { target c++17_down } }
+ f2 (arr2);// { dg-warning "conversions to arrays of unknown bound are only available"
"" { target c++17_down } }
+ fp2 (&arr2);// { dg-warning "conversions to arrays of unknown bound are only
available" "" { target c++17_down } }
+}
+
+int(&r1)[] = arr;// { dg-warning "conversions to arrays of unknown bound are only
available" "" { target c++17_down } }
+int(&r2)[10] = arr;
+int(&r3)[][10] = arr2;// { dg-warning "conversions to arrays of unknown bound are only
available" "" { target c++17_down } }
+/* Note that
+ int (&r)[10][] = arr2;
+ is invalid. */
+int(&r4)[10][10] = arr2;
+
+int(*p1)[] = &arr;// { dg-warning "conversions to arrays of unknown bound are only
available" "" { target c++17_down } }
+int(*p2)[10] = &arr;
+int(*p3)[][10] = &arr2;// { dg-warning "conversions to arrays of unknown bound are only
available" "" { target c++17_down } }
+int(*p4)[10][10] = &arr2;
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv10.C
gcc/gcc/testsuite/g++.dg/cpp2a/array-conv10.C
new file mode 100644
index 00000000000..5817c0bf39c
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv10.C
@@ -0,0 +1,22 @@
+// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown
bound.
+// { dg-do compile { target c++17 } }
+// { dg-options "-Wpedantic" }
+
+// The other direction: converting from int[] to int(&)[3] is forbidden.
+
+extern int a[];
+extern int (*b)[];
+extern int (&c)[];
+int (&y)[] = a;
+int (&x)[3] = y; // { dg-error "discards array bounds" }
+int (&z)[3] = a; // { dg-error "discards array bounds" }
+
+void f(int (*)[3]);
+void f2(int (&)[3]);
+
+void
+test ()
+{
+ f(b); // { dg-error "cannot convert" }
+ f2(c); // { dg-error "discards array bounds" }
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv11.C
gcc/gcc/testsuite/g++.dg/cpp2a/array-conv11.C
new file mode 100644
index 00000000000..a072b29191d
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv11.C
@@ -0,0 +1,23 @@
+// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown
bound.
+// { dg-do compile { target c++2a } }
+// { dg-options "-Wpedantic" }
+
+// Test flexible array member. Here we're binding int[] to int[]. This worked
+// even before P0388R4.
+
+typedef int T[];
+extern T arr;
+T &t1 = arr;
+
+struct S {
+ int i;
+ int a[]; // { dg-warning "flexible array member" }
+};
+
+void f (int (&)[]);
+
+void
+test (S s)
+{
+ f (s.a);
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv12.C
gcc/gcc/testsuite/g++.dg/cpp2a/array-conv12.C
new file mode 100644
index 00000000000..1156ea32df5
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv12.C
@@ -0,0 +1,12 @@
+// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown
bound.
+// { dg-do compile { target c++2a } }
+// { dg-options "-Wpedantic" }
+
+int arr[1] = { 42 };
+int(&r)[]{arr};
+int(&r2)[] = {arr};
+int(&&r3)[]{};
+int(&&r4)[]{42};
+int(&&r5)[] = {};
+int(&&r6)[] = {42};
+int(&r7)[](arr); // { dg-warning "conversions to arrays of unknown bound are only
available" "" { target c++17_down } }
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv13.C
gcc/gcc/testsuite/g++.dg/cpp2a/array-conv13.C
new file mode 100644
index 00000000000..9908b7e9118
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv13.C
@@ -0,0 +1,17 @@
+// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown
bound.
+// { dg-do compile { target c++2a } }
+
+template <typename T> void foo(T);
+
+template <typename F, typename T, typename = decltype(foo<T>(F()))>
+void test(int) { }
+
+// No other overload, so if the above fails because of the conversion,
+// we fail.
+
+void
+fn ()
+{
+ test<int(*)[2], int(*)[]>(0);
+ test<int(*)[], int(*)[]>(0);
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv14.C
gcc/gcc/testsuite/g++.dg/cpp2a/array-conv14.C
new file mode 100644
index 00000000000..793e85d7b1c
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv14.C
@@ -0,0 +1,17 @@
+// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown
bound.
+// { dg-do compile { target c++2a } }
+
+void f(const int(*)[]);
+void fb(const int(*)[3]);
+void f2(const int(&)[]);
+void fb2(const int(&)[3]);
+
+void
+g ()
+{
+ int arr[3];
+ f(&arr);
+ fb(&arr);
+ f2(arr);
+ fb2(arr);
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv2.C
gcc/gcc/testsuite/g++.dg/cpp2a/array-conv2.C
new file mode 100644
index 00000000000..5245d830f1f
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv2.C
@@ -0,0 +1,26 @@
+// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown
bound.
+// { dg-do compile { target c++2a } }
+
+struct A {
+ A();
+ A(const A(&)[2]);
+};
+
+using T = A[];
+using U = A[2];
+
+// t binds directly to U{} now. Before it bound indirectly to a temporary
+// A{U{}}. ??? But we don't do it now; see reference_binding and the
+// BRACE_ENCLOSED_INITIALIZER_P block.
+A (&&t)[] = {U{}};
+
+U u{};
+
+T &
+foo ()
+{
+ // This didn't compile before P0388R4: invalid initialization of non-const
+ // reference of type 'A (&)[]' from an rvalue of type
+ // '<brace-enclosed initializer list>'.
+ return {u};
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv3.C
gcc/gcc/testsuite/g++.dg/cpp2a/array-conv3.C
new file mode 100644
index 00000000000..3d92b401247
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv3.C
@@ -0,0 +1,26 @@
+// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown
bound.
+// { dg-do run { target c++2a } }
+
+// Ranking of reference initialization conversions
+
+int f(int(&)[]) { return 1; } // (1)
+int f(int(&)[1]) { return 2; } // (2)
+
+int h(int(*)[]) { return 1; } // (a)
+int h(int(*)[1]) { return 2; } // (b)
+
+// From P0388R4:
+// (2) and (b) should clearly be better than (1) and (a), respectively,
+// as the former overloads are more restricted.
+// (a) should be worse than (b), which is implied by (a) necessitating
+// a qualification conversion in that case.
+
+int
+main ()
+{
+ int arr[1];
+ if (f(arr) != 2)
+ __builtin_abort ();
+ if (h(&arr) != 2)
+ __builtin_abort ();
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv4.C
gcc/gcc/testsuite/g++.dg/cpp2a/array-conv4.C
new file mode 100644
index 00000000000..979c69b0555
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv4.C
@@ -0,0 +1,24 @@
+// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown
bound.
+// { dg-do compile { target c++2a } }
+
+// Ranking of reference initialization conversions
+
+void f(int(&)[]) {} // (1)
+//void f(int(&)[1]) { } // (2)
+void f(int*) { } // (3)
+
+//void f2(int(&)[]) { } // (1)
+void f2(int(&)[1]) { } // (2)
+void f2(int*) { } // (3)
+
+// From P0388R4:
+// (3) should be equal to (1) (as it is to (2))
+// Check that we get "ambiguous overload" errors.
+
+void
+doit ()
+{
+ int arr[1];
+ f(arr); // { dg-error "ambiguous" }
+ f2(arr); // { dg-error "ambiguous" }
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv5.C
gcc/gcc/testsuite/g++.dg/cpp2a/array-conv5.C
new file mode 100644
index 00000000000..34678f5cead
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv5.C
@@ -0,0 +1,24 @@
+// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown
bound.
+// { dg-do run { target c++2a } }
+
+// Ranking of list-initialization sequences
+int b(int (&&)[] ) { return 1; } // #1
+int b(long (&&)[] ) { return 2; } // #2
+int b(int (&&)[1]) { return 3; } // #3
+int b(long (&&)[1]) { return 4; } // #4
+int b(int (&&)[2]) { return 5; } // #5
+
+/* Here,
+ -- #1, #3 and #5 should rank better than both #2 and #4, as no promotion
+ is necessitated.
+ -- #1 should rank worse than #3, being far less specialized.
+ -- #1 should rank better than #5, as the latter requires a larger array
+ temporary. (#3 also ranks better than #5 for the same reason--cf. core
+ issue 1307). */
+
+int
+main ()
+{
+ if (b({1}) != 3)
+ __builtin_abort ();
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv6.C
gcc/gcc/testsuite/g++.dg/cpp2a/array-conv6.C
new file mode 100644
index 00000000000..c2389c82273
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv6.C
@@ -0,0 +1,28 @@
+// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown
bound.
+// { dg-do run { target c++2a } }
+
+// Ranking of reference initialization conversions
+
+int f1(const int(&)[]) { return 1; }
+int f1(const int(&)[1]) { return 2; }
+
+int f2(const int(&)[]) { return 1; }
+int f2(int(&)[1]) { return 2; }
+
+int f3(int(&)[]) { return 1; }
+int f3(const int(&)[1]) { return 2; }
+
+const int arr[1] = { 42 };
+
+int
+main ()
+{
+ if (f1(arr) != 2)
+ __builtin_abort ();
+
+ if (f2(arr) != 1)
+ __builtin_abort ();
+
+ if (f3(arr) != 2)
+ __builtin_abort ();
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv7.C
gcc/gcc/testsuite/g++.dg/cpp2a/array-conv7.C
new file mode 100644
index 00000000000..07c709ff10f
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv7.C
@@ -0,0 +1,34 @@
+// PR c++/69531 - DR 1307, Overload resolution based on size of array
init-list.
+// { dg-do run { target c++2a } }
+
+int f(int const(&)[]) { return 1; }
+int f(int const(&)[2]) { return 2; }
+
+int f2(int const(&)[]) { return 1; }
+int f2(int const(&)[1]) { return 2; }
+
+int f3(int const(&)[]) { return 1; }
+int f3(int const(&)[1]) { return 2; }
+int f3(int const(&)[2]) { return 3; }
+
+int main ()
+{
+ if (f ({}) != 1)
+ __builtin_abort ();
+ if (f ({1}) != 1)
+ __builtin_abort ();
+ if (f ({1, 2}) != 2)
+ __builtin_abort ();
+
+ if (f2 ({}) != 1)
+ __builtin_abort ();
+ if (f2 ({1}) != 2)
+ __builtin_abort ();
+
+ if (f3 ({}) != 1)
+ __builtin_abort ();
+ if (f3 ({1}) != 2)
+ __builtin_abort ();
+ if (f3 ({1, 2}) != 3)
+ __builtin_abort ();
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv8.C
gcc/gcc/testsuite/g++.dg/cpp2a/array-conv8.C
new file mode 100644
index 00000000000..635c7679a21
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv8.C
@@ -0,0 +1,26 @@
+// PR c++/69531 - DR 1307, Overload resolution based on size of array
init-list.
+// { dg-do run { target c++2a } }
+// Example from [over.ics.rank].
+
+int f(int (&&)[] ) { return 1; } // #1
+int f(double (&&)[] ) { return 2; } // #2
+int f(int (&&)[2]) { return 3; } // #3
+
+int
+main ()
+{
+ // Calls #1: Better than #2 due to conversion, better than #3 due to bounds.
+ if (f({1}) != 1)
+ __builtin_abort ();
+ // Calls #2: Identity conversion is better than floating-integral conversion.
+ if (f({1.0}) != 2)
+ __builtin_abort ();
+ // Calls #2: Identity conversion is better than floating-integral conversion.
+ if (f({1.0, 2.0}) != 2)
+ __builtin_abort ();
+ // Calls #3: Converting to array of known bound is better than to unknown
+ // bound, and an identity conversion is better than floating-integral
+ // conversion.
+ if (f({1, 2}) != 3)
+ __builtin_abort ();
+}
diff --git gcc/gcc/testsuite/g++.dg/cpp2a/array-conv9.C
gcc/gcc/testsuite/g++.dg/cpp2a/array-conv9.C
new file mode 100644
index 00000000000..e56e4a3e54e
--- /dev/null
+++ gcc/gcc/testsuite/g++.dg/cpp2a/array-conv9.C
@@ -0,0 +1,11 @@
+// PR c++/91364 - Implement P0388R4: Permit conversions to arrays of unknown
bound.
+// { dg-do compile { target c++2a } }
+
+int arr[1];
+
+void
+test ()
+{
+ int (&r)[1] = const_cast<int(&)[1]>(arr);
+ int (&r2)[] = const_cast<int(&)[]>(arr); // { dg-error "invalid" }
+}
diff --git gcc/gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C
gcc/gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C
index 6b52783c09b..c3b1ab56282 100644
--- gcc/gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C
+++ gcc/gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C
@@ -20,7 +20,7 @@ void function_0 ()
{
// we miss the first two because typeck.c (comp_array_types) deems
// it okay if one of the sizes is null
- ptr_to_array_of_ints = ptr_to_array_of_3_ints; // { dg-error "" }
+ ptr_to_array_of_ints = ptr_to_array_of_3_ints; // { dg-error "conversions to
arrays" "" { target c++17_down } }
ptr_to_array_of_3_ints = ptr_to_array_of_ints; // { dg-error "" }
ptr_to_array_of_3_ints = ptr_to_array_of_5_ints; // { dg-error "" }