aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarek Polacek <polacek@redhat.com>2019-10-09 20:58:00 +0000
committerMarek Polacek <mpolacek@gcc.gnu.org>2019-10-09 20:58:00 +0000
commit89e0a492af5bec8ffa2ec5d99c4858df50d22c16 (patch)
treea667aef79edfb7885e24c98250e956116d0c8550
parent3b29211acb2b463a664875bf55cec0242e95d2ea (diff)
downloadgcc-89e0a492af5bec8ffa2ec5d99c4858df50d22c16.zip
gcc-89e0a492af5bec8ffa2ec5d99c4858df50d22c16.tar.gz
gcc-89e0a492af5bec8ffa2ec5d99c4858df50d22c16.tar.bz2
Implement C++20 P0388R4, DR 1307, and DR 330.
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. DR 330 was also implemented in this patch. PR c++/91364 - P0388R4: Permit conversions to arrays of unknown bound. PR c++/69531 - DR 1307: Differently bounded array parameters. PR c++/88128 - DR 330: Qual convs and pointers to arrays of pointers. * call.c (build_array_conv): Build ck_identity at the beginning of the conversion. (standard_conversion): Pass bounds_none to comp_ptr_ttypes_const. (maybe_warn_array_conv): New. (convert_like_real): Call it. Add an error message about converting from arrays of unknown bounds. (conv_get_original_expr): New. (nelts_initialized_by_list_init): New. (conv_binds_to_array_of_unknown_bound): New. (compare_ics): Implement list-initialization ranking based on array sizes, as specified in DR 1307 and P0388R. * cp-tree.h (comp_ptr_ttypes_const): Adjust declaration. (compare_bounds_t): New enum. * typeck.c (comp_array_types): New bool and compare_bounds_t parameters. Use them. (structural_comptypes): Adjust the call to comp_array_types. (similar_type_p): Handle ARRAY_TYPE. (build_const_cast_1): Pass bounds_none to comp_ptr_ttypes_const. (comp_ptr_ttypes_real): Don't check cv-quals of ARRAY_TYPEs. Use comp_array_types to compare array types. Look through arrays as per DR 330. (comp_ptr_ttypes_const): Use comp_array_types to compare array types. Look through arrays as per DR 330. * g++.dg/conversion/qual1.C: New test. * g++.dg/conversion/qual2.C: New test. * g++.dg/conversion/qual3.C: New test. * g++.dg/conversion/ref2.C: New test. * g++.dg/conversion/ref3.C: New test. * g++.dg/cpp0x/initlist-array3.C: Remove dg-error. * g++.dg/cpp0x/initlist-array7.C: New test. * g++.dg/cpp0x/initlist-array8.C: New test. * g++.dg/cpp2a/array-conv1.C: New test. * g++.dg/cpp2a/array-conv10.C: New test. * g++.dg/cpp2a/array-conv11.C: New test. * g++.dg/cpp2a/array-conv12.C: New test. * g++.dg/cpp2a/array-conv13.C: New test. * g++.dg/cpp2a/array-conv14.C: New test. * g++.dg/cpp2a/array-conv15.C: New test. * g++.dg/cpp2a/array-conv16.C: New test. * g++.dg/cpp2a/array-conv17.C: New test. * g++.dg/cpp2a/array-conv2.C: New test. * g++.dg/cpp2a/array-conv3.C: New test. * g++.dg/cpp2a/array-conv4.C: New test. * g++.dg/cpp2a/array-conv5.C: New test. * g++.dg/cpp2a/array-conv6.C: New test. * g++.dg/cpp2a/array-conv7.C: New test. * g++.dg/cpp2a/array-conv8.C: New test. * g++.dg/cpp2a/array-conv9.C: New test. * g++.old-deja/g++.bugs/900321_01.C: Adjust dg-error. * testsuite/23_containers/span/lwg3255.cc: Adjust test to match the post-P0388R4 behavior. From-SVN: r276771
-rw-r--r--gcc/cp/ChangeLog29
-rw-r--r--gcc/cp/call.c160
-rw-r--r--gcc/cp/cp-tree.h6
-rw-r--r--gcc/cp/typeck.c66
-rw-r--r--gcc/testsuite/ChangeLog32
-rw-r--r--gcc/testsuite/g++.dg/conversion/qual1.C51
-rw-r--r--gcc/testsuite/g++.dg/conversion/qual2.C14
-rw-r--r--gcc/testsuite/g++.dg/conversion/qual3.C53
-rw-r--r--gcc/testsuite/g++.dg/conversion/ref2.C29
-rw-r--r--gcc/testsuite/g++.dg/conversion/ref3.C4
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/initlist-array3.C3
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/initlist-array7.C21
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/initlist-array8.C35
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/array-conv1.C33
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/array-conv10.C22
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/array-conv11.C23
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/array-conv12.C12
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/array-conv13.C17
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/array-conv14.C17
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/array-conv15.C23
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/array-conv16.C16
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/array-conv17.C39
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/array-conv2.C26
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/array-conv3.C26
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/array-conv4.C24
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/array-conv5.C24
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/array-conv6.C28
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/array-conv7.C34
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/array-conv8.C26
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/array-conv9.C27
-rw-r--r--gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C2
-rw-r--r--libstdc++-v3/ChangeLog8
-rw-r--r--libstdc++-v3/testsuite/23_containers/span/lwg3255.cc3
33 files changed, 902 insertions, 31 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 4eb18294..e47e8ca 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,34 @@
2019-10-09 Marek Polacek <polacek@redhat.com>
+ PR c++/91364 - P0388R4: Permit conversions to arrays of unknown bound.
+ PR c++/69531 - DR 1307: Differently bounded array parameters.
+ PR c++/88128 - DR 330: Qual convs and pointers to arrays of pointers.
+ * call.c (build_array_conv): Build ck_identity at the beginning
+ of the conversion.
+ (standard_conversion): Pass bounds_none to comp_ptr_ttypes_const.
+ (maybe_warn_array_conv): New.
+ (convert_like_real): Call it. Add an error message about converting
+ from arrays of unknown bounds.
+ (conv_get_original_expr): New.
+ (nelts_initialized_by_list_init): New.
+ (conv_binds_to_array_of_unknown_bound): New.
+ (compare_ics): Implement list-initialization ranking based on
+ array sizes, as specified in DR 1307 and P0388R.
+ * cp-tree.h (comp_ptr_ttypes_const): Adjust declaration.
+ (compare_bounds_t): New enum.
+ * typeck.c (comp_array_types): New bool and compare_bounds_t
+ parameters. Use them.
+ (structural_comptypes): Adjust the call to comp_array_types.
+ (similar_type_p): Handle ARRAY_TYPE.
+ (build_const_cast_1): Pass bounds_none to comp_ptr_ttypes_const.
+ (comp_ptr_ttypes_real): Don't check cv-quals of ARRAY_TYPEs. Use
+ comp_array_types to compare array types. Look through arrays as per
+ DR 330.
+ (comp_ptr_ttypes_const): Use comp_array_types to compare array types.
+ Look through arrays as per DR 330.
+
+2019-10-09 Marek Polacek <polacek@redhat.com>
+
PR c++/92032 - DR 1601: Promotion of enum with fixed underlying type.
* call.c (standard_conversion): When converting an enumeration with
a fixed underlying type to the underlying type, give it the cr_promotion
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 33ec6a2..55d2aba 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -122,7 +122,8 @@ struct conversion {
of using this field directly. */
conversion *next;
/* The expression at the beginning of the conversion chain. This
- variant is used only if KIND is ck_identity or ck_ambig. */
+ variant is used only if KIND is ck_identity or ck_ambig. You can
+ use conv_get_original_expr to get this expression. */
tree expr;
/* The array of conversions for an initializer_list, so this
variant is used only when KIN D is ck_list. */
@@ -223,6 +224,8 @@ static void add_candidates (tree, tree, const vec<tree, va_gc> *, tree, tree,
tsubst_flags_t);
static conversion *merge_conversion_sequences (conversion *, conversion *);
static tree build_temp (tree, tree, int, diagnostic_t *, tsubst_flags_t);
+static conversion *build_identity_conv (tree, tree);
+static inline bool conv_binds_to_array_of_unknown_bound (conversion *);
/* Returns nonzero iff the destructor name specified in NAME matches BASETYPE.
NAME can take many forms... */
@@ -1066,7 +1069,7 @@ build_array_conv (tree type, tree ctor, int flags, tsubst_flags_t complain)
c->rank = rank;
c->user_conv_p = user;
c->bad_p = bad;
- c->u.next = NULL;
+ c->u.next = build_identity_conv (TREE_TYPE (ctor), ctor);
return c;
}
@@ -1366,7 +1369,7 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
if (same_type_p (from, to))
/* OK */;
- else if (c_cast_p && comp_ptr_ttypes_const (to, from))
+ else if (c_cast_p && comp_ptr_ttypes_const (to, from, bounds_either))
/* In a C-style cast, we ignore CV-qualification because we
are allowed to perform a static_cast followed by a
const_cast. */
@@ -1668,7 +1671,14 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags,
maybe_warn_cpp0x (CPP0X_INITIALIZER_LISTS);
/* DR 1288: Otherwise, if the initializer list has a single element
of type E and ... [T's] referenced type is reference-related to E,
- the object or reference is initialized from that element... */
+ the object or reference is initialized from that element...
+
+ ??? With P0388R4, we should bind 't' directly to U{}:
+ using U = A[2];
+ A (&&t)[] = {U{}};
+ because A[] and A[2] are reference-related. But we don't do it
+ because grok_reference_init has deduced the array size (to 1), and
+ A[1] and A[2] aren't reference-related. */
if (CONSTRUCTOR_NELTS (expr) == 1)
{
tree elt = CONSTRUCTOR_ELT (expr, 0)->value;
@@ -6958,6 +6968,27 @@ maybe_inform_about_fndecl_for_bogus_argument_init (tree fn, int argnum)
" initializing argument %P of %qD", argnum, fn);
}
+/* Maybe warn about C++20 Conversions to arrays of unknown bound. C is
+ the conversion, EXPR is the expression we're converting. */
+
+static void
+maybe_warn_array_conv (location_t loc, conversion *c, tree expr)
+{
+ if (cxx_dialect >= cxx2a)
+ return;
+
+ tree type = TREE_TYPE (expr);
+ type = strip_pointer_operator (type);
+
+ if (TREE_CODE (type) != ARRAY_TYPE
+ || TYPE_DOMAIN (type) == NULL_TREE)
+ return;
+
+ if (conv_binds_to_array_of_unknown_bound (c))
+ pedwarn (loc, OPT_Wpedantic, "conversions to arrays of unknown bound "
+ "are only available with %<-std=c++2a%> or %<-std=gnu++2a%>");
+}
+
/* Perform the conversions in CONVS on the expression EXPR. FN and
ARGNUM are used for diagnostics. ARGNUM is zero based, -1
indicates the `this' argument of a method. INNER is nonzero when
@@ -7377,8 +7408,20 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
error_at (loc, "cannot bind non-const lvalue reference of "
"type %qH to an rvalue of type %qI", totype, extype);
else if (!reference_compatible_p (TREE_TYPE (totype), extype))
- error_at (loc, "binding reference of type %qH to %qI "
- "discards qualifiers", totype, extype);
+ {
+ /* If we're converting from T[] to T[N], don't talk
+ about discarding qualifiers. (Converting from T[N] to
+ T[] is allowed by P0388R4.) */
+ if (TREE_CODE (extype) == ARRAY_TYPE
+ && TYPE_DOMAIN (extype) == NULL_TREE
+ && TREE_CODE (TREE_TYPE (totype)) == ARRAY_TYPE
+ && TYPE_DOMAIN (TREE_TYPE (totype)) != NULL_TREE)
+ error_at (loc, "cannot bind reference of type %qH to %qI "
+ "due to different array bounds", totype, extype);
+ else
+ error_at (loc, "binding reference of type %qH to %qI "
+ "discards qualifiers", totype, extype);
+ }
else
gcc_unreachable ();
maybe_print_user_conv_context (convs);
@@ -7386,6 +7429,8 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
return error_mark_node;
}
+ else if (complain & tf_warning)
+ maybe_warn_array_conv (loc, convs, expr);
/* If necessary, create a temporary.
@@ -7469,7 +7514,10 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
case ck_qual:
/* Warn about deprecated conversion if appropriate. */
if (complain & tf_warning)
- string_conv_p (totype, expr, 1);
+ {
+ string_conv_p (totype, expr, 1);
+ maybe_warn_array_conv (loc, convs, expr);
+ }
break;
case ck_ptr:
@@ -10061,6 +10109,50 @@ maybe_handle_ref_bind (conversion **ics)
return NULL;
}
+/* Get the expression at the beginning of the conversion chain C. */
+
+static tree
+conv_get_original_expr (conversion *c)
+{
+ for (; c; c = next_conversion (c))
+ if (c->kind == ck_identity || c->kind == ck_ambig)
+ return c->u.expr;
+ return NULL_TREE;
+}
+
+/* Return a tree representing the number of elements initialized by the
+ list-initialization C. The caller must check that C converts to an
+ array type. */
+
+static tree
+nelts_initialized_by_list_init (conversion *c)
+{
+ /* If the array we're converting to has a dimension, we'll use that. */
+ if (TYPE_DOMAIN (c->type))
+ return array_type_nelts_top (c->type);
+ else
+ {
+ /* Otherwise, we look at how many elements the constructor we're
+ initializing from has. */
+ tree ctor = conv_get_original_expr (c);
+ return size_int (CONSTRUCTOR_NELTS (ctor));
+ }
+}
+
+/* True iff C is a conversion that binds a reference or a pointer to
+ an array of unknown bound. */
+
+static inline bool
+conv_binds_to_array_of_unknown_bound (conversion *c)
+{
+ /* ck_ref_bind won't have the reference stripped. */
+ tree type = non_reference (c->type);
+ /* ck_qual won't have the pointer stripped. */
+ type = strip_pointer_operator (type);
+ return (TREE_CODE (type) == ARRAY_TYPE
+ && TYPE_DOMAIN (type) == NULL_TREE);
+}
+
/* Compare two implicit conversion sequences according to the rules set out in
[over.ics.rank]. Return values:
@@ -10174,6 +10266,38 @@ compare_ics (conversion *ics1, conversion *ics2)
if (f1 != f2)
return 0;
}
+ /* List-initialization sequence L1 is a better conversion sequence than
+ list-initialization sequence L2 if
+
+ -- L1 and L2 convert to arrays of the same element type, and either
+ the number of elements n1 initialized by L1 is less than the number
+ of elements n2 initialized by L2, or n1=n2 and L2 converts to an array
+ of unknown bound and L1 does not. (Added in CWG 1307 and extended by
+ P0388R4.) */
+ else if (t1->kind == ck_aggr
+ && TREE_CODE (t1->type) == ARRAY_TYPE
+ && TREE_CODE (t2->type) == ARRAY_TYPE)
+ {
+ /* The type of the array elements must be the same. */
+ if (!same_type_p (TREE_TYPE (t1->type), TREE_TYPE (t2->type)))
+ return 0;
+
+ tree n1 = nelts_initialized_by_list_init (t1);
+ tree n2 = nelts_initialized_by_list_init (t2);
+ if (tree_int_cst_lt (n1, n2))
+ return 1;
+ else if (tree_int_cst_lt (n2, n1))
+ return -1;
+ /* The n1 == n2 case. */
+ bool c1 = conv_binds_to_array_of_unknown_bound (t1);
+ bool c2 = conv_binds_to_array_of_unknown_bound (t2);
+ if (c1 && !c2)
+ return -1;
+ else if (!c1 && c2)
+ return 1;
+ else
+ return 0;
+ }
else
{
/* For ambiguous or aggregate conversions, use the target type as
@@ -10469,6 +10593,28 @@ compare_ics (conversion *ics1, conversion *ics2)
if (same_type_ignoring_top_level_qualifiers_p (to_type1, to_type2))
{
+ /* Per P0388R4:
+
+ void f (int(&)[]), // (1)
+ f (int(&)[1]), // (2)
+ f (int*); // (3)
+
+ (2) is better than (1), but (3) should be equal to (1) and to
+ (2). For that reason we don't use ck_qual for (1) which would
+ give it the cr_exact rank while (3) remains ck_identity.
+ Therefore we compare (1) and (2) here. For (1) we'll have
+
+ ck_ref_bind <- ck_identity
+ int[] & int[1]
+
+ so to handle this we must look at ref_conv. */
+ bool c1 = conv_binds_to_array_of_unknown_bound (ref_conv1);
+ bool c2 = conv_binds_to_array_of_unknown_bound (ref_conv2);
+ if (c1 && !c2)
+ return -1;
+ else if (!c1 && c2)
+ return 1;
+
int q1 = cp_type_quals (TREE_TYPE (ref_conv1->type));
int q2 = cp_type_quals (TREE_TYPE (ref_conv2->type));
if (ref_conv1->bad_p)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 9ff617b..c1301a4 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7372,6 +7372,10 @@ extern void cxx_print_error_function (diagnostic_context *,
struct diagnostic_info *);
/* in typeck.c */
+/* Says how we should behave when comparing two arrays one of which
+ has unknown bounds. */
+enum compare_bounds_t { bounds_none, bounds_either, bounds_first };
+
extern bool cxx_mark_addressable (tree, bool = false);
extern int string_conv_p (const_tree, const_tree, int);
extern tree cp_truthvalue_conversion (tree);
@@ -7462,7 +7466,7 @@ extern tree convert_for_initialization (tree, tree, tree, int,
impl_conv_rhs, tree, int,
tsubst_flags_t);
extern int comp_ptr_ttypes (tree, tree);
-extern bool comp_ptr_ttypes_const (tree, tree);
+extern bool comp_ptr_ttypes_const (tree, tree, compare_bounds_t);
extern bool error_type_p (const_tree);
extern bool ptr_reasonably_similar (const_tree, const_tree);
extern tree build_ptrmemfunc (tree, tree, int, bool,
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index ba334e7..a67dd4b 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -54,7 +54,7 @@ static tree rationalize_conditional_expr (enum tree_code, tree,
tsubst_flags_t);
static int comp_ptr_ttypes_real (tree, tree, int);
static bool comp_except_types (tree, tree, bool);
-static bool comp_array_types (const_tree, const_tree, bool);
+static bool comp_array_types (const_tree, const_tree, compare_bounds_t, bool);
static tree pointer_diff (location_t, tree, tree, tree, tsubst_flags_t, tree *);
static tree get_delta_difference (tree, tree, bool, bool, tsubst_flags_t);
static void casts_away_constness_r (tree *, tree *, tsubst_flags_t);
@@ -1084,11 +1084,15 @@ comp_except_specs (const_tree t1, const_tree t2, int exact)
return exact == ce_derived || base == NULL_TREE || length == list_length (t1);
}
-/* Compare the array types T1 and T2. ALLOW_REDECLARATION is true if
- [] can match [size]. */
+/* Compare the array types T1 and T2. CB says how we should behave when
+ comparing array bounds: bounds_none doesn't allow dimensionless arrays,
+ bounds_either says than any array can be [], bounds_first means that
+ onlt T1 can be an array with unknown bounds. STRICT is true if
+ qualifiers must match when comparing the types of the array elements. */
static bool
-comp_array_types (const_tree t1, const_tree t2, bool allow_redeclaration)
+comp_array_types (const_tree t1, const_tree t2, compare_bounds_t cb,
+ bool strict)
{
tree d1;
tree d2;
@@ -1098,7 +1102,9 @@ comp_array_types (const_tree t1, const_tree t2, bool allow_redeclaration)
return true;
/* The type of the array elements must be the same. */
- if (!same_type_p (TREE_TYPE (t1), TREE_TYPE (t2)))
+ if (strict
+ ? !same_type_p (TREE_TYPE (t1), TREE_TYPE (t2))
+ : !similar_type_p (TREE_TYPE (t1), TREE_TYPE (t2)))
return false;
d1 = TYPE_DOMAIN (t1);
@@ -1119,8 +1125,10 @@ comp_array_types (const_tree t1, const_tree t2, bool allow_redeclaration)
declarations for an array object can specify
array types that differ by the presence or absence of a major
array bound (_dcl.array_). */
- if (!d1 || !d2)
- return allow_redeclaration;
+ if (!d1 && d2)
+ return cb >= bounds_either;
+ else if (d1 && !d2)
+ return cb == bounds_either;
/* Check that the dimensions are the same. */
@@ -1368,7 +1376,9 @@ structural_comptypes (tree t1, tree t2, int strict)
case ARRAY_TYPE:
/* Target types must match incl. qualifiers. */
- if (!comp_array_types (t1, t2, !!(strict & COMPARE_REDECLARATION)))
+ if (!comp_array_types (t1, t2, ((strict & COMPARE_REDECLARATION)
+ ? bounds_either : bounds_none),
+ /*strict=*/true))
return false;
break;
@@ -1549,10 +1559,10 @@ similar_type_p (tree type1, tree type2)
if (same_type_ignoring_top_level_qualifiers_p (type1, type2))
return true;
- /* FIXME This ought to handle ARRAY_TYPEs too. */
if ((TYPE_PTR_P (type1) && TYPE_PTR_P (type2))
- || (TYPE_PTRDATAMEM_P (type1) && TYPE_PTRDATAMEM_P (type2)))
- return comp_ptr_ttypes_const (type1, type2);
+ || (TYPE_PTRDATAMEM_P (type1) && TYPE_PTRDATAMEM_P (type2))
+ || (TREE_CODE (type1) == ARRAY_TYPE && TREE_CODE (type2) == ARRAY_TYPE))
+ return comp_ptr_ttypes_const (type1, type2, bounds_either);
return false;
}
@@ -7867,7 +7877,7 @@ build_const_cast_1 (tree dst_type, tree expr, tsubst_flags_t complain,
if (TYPE_PTR_P (src_type) || TYPE_PTRDATAMEM_P (src_type))
{
- if (comp_ptr_ttypes_const (dst_type, src_type))
+ if (comp_ptr_ttypes_const (dst_type, src_type, bounds_none))
{
if (valid_p)
{
@@ -9909,9 +9919,10 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
TYPE_OFFSET_BASETYPE (to)))
return 0;
- /* Const and volatile mean something different for function types,
- so the usual checks are not appropriate. */
- if (!FUNC_OR_METHOD_TYPE_P (to))
+ /* Const and volatile mean something different for function and
+ array types, so the usual checks are not appropriate. We'll
+ check the array type elements in further iterations. */
+ if (!FUNC_OR_METHOD_TYPE_P (to) && TREE_CODE (to) != ARRAY_TYPE)
{
if (!at_least_as_qualified_p (to, from))
return 0;
@@ -9930,7 +9941,17 @@ comp_ptr_ttypes_real (tree to, tree from, int constp)
if (VECTOR_TYPE_P (to))
is_opaque_pointer = vector_targets_convertible_p (to, from);
- if (!TYPE_PTR_P (to) && !TYPE_PTRDATAMEM_P (to))
+ /* P0388R4 allows a conversion from int[N] to int[] but not the
+ other way round. When both arrays have bounds but they do
+ not match, then no conversion is possible. */
+ if (TREE_CODE (to) == ARRAY_TYPE
+ && !comp_array_types (to, from, bounds_first, /*strict=*/false))
+ return 0;
+
+ if (!TYPE_PTR_P (to)
+ && !TYPE_PTRDATAMEM_P (to)
+ /* CWG 330 says we need to look through arrays. */
+ && TREE_CODE (to) != ARRAY_TYPE)
return ((constp >= 0 || to_more_cv_qualified)
&& (is_opaque_pointer
|| same_type_ignoring_top_level_qualifiers_p (to, from)));
@@ -10033,10 +10054,10 @@ ptr_reasonably_similar (const_tree to, const_tree from)
/* Return true if TO and FROM (both of which are POINTER_TYPEs or
pointer-to-member types) are the same, ignoring cv-qualification at
- all levels. */
+ all levels. CB says how we should behave when comparing array bounds. */
bool
-comp_ptr_ttypes_const (tree to, tree from)
+comp_ptr_ttypes_const (tree to, tree from, compare_bounds_t cb)
{
bool is_opaque_pointer = false;
@@ -10053,7 +10074,14 @@ comp_ptr_ttypes_const (tree to, tree from)
if (VECTOR_TYPE_P (to))
is_opaque_pointer = vector_targets_convertible_p (to, from);
- if (!TYPE_PTR_P (to))
+ if (TREE_CODE (to) == ARRAY_TYPE
+ /* Ignore cv-qualification, but if we see e.g. int[3] and int[4],
+ we must fail. */
+ && !comp_array_types (to, from, cb, /*strict=*/false))
+ return false;
+
+ /* CWG 330 says we need to look through arrays. */
+ if (!TYPE_PTR_P (to) && TREE_CODE (to) != ARRAY_TYPE)
return (is_opaque_pointer
|| same_type_ignoring_top_level_qualifiers_p (to, from));
}
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index f1e3a99..5ee2a81 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,37 @@
2019-10-09 Marek Polacek <polacek@redhat.com>
+ PR c++/91364 - P0388R4: Permit conversions to arrays of unknown bound.
+ PR c++/69531 - DR 1307: Differently bounded array parameters.
+ PR c++/88128 - DR 330: Qual convs and pointers to arrays of pointers.
+ * g++.dg/conversion/qual1.C: New test.
+ * g++.dg/conversion/qual2.C: New test.
+ * g++.dg/conversion/qual3.C: New test.
+ * g++.dg/conversion/ref2.C: New test.
+ * g++.dg/conversion/ref3.C: New test.
+ * g++.dg/cpp0x/initlist-array3.C: Remove dg-error.
+ * g++.dg/cpp0x/initlist-array7.C: New test.
+ * g++.dg/cpp0x/initlist-array8.C: New test.
+ * g++.dg/cpp2a/array-conv1.C: New test.
+ * g++.dg/cpp2a/array-conv10.C: New test.
+ * g++.dg/cpp2a/array-conv11.C: New test.
+ * g++.dg/cpp2a/array-conv12.C: New test.
+ * g++.dg/cpp2a/array-conv13.C: New test.
+ * g++.dg/cpp2a/array-conv14.C: New test.
+ * g++.dg/cpp2a/array-conv15.C: New test.
+ * g++.dg/cpp2a/array-conv16.C: New test.
+ * g++.dg/cpp2a/array-conv17.C: New test.
+ * g++.dg/cpp2a/array-conv2.C: New test.
+ * g++.dg/cpp2a/array-conv3.C: New test.
+ * g++.dg/cpp2a/array-conv4.C: New test.
+ * g++.dg/cpp2a/array-conv5.C: New test.
+ * g++.dg/cpp2a/array-conv6.C: New test.
+ * g++.dg/cpp2a/array-conv7.C: New test.
+ * g++.dg/cpp2a/array-conv8.C: New test.
+ * g++.dg/cpp2a/array-conv9.C: New test.
+ * g++.old-deja/g++.bugs/900321_01.C: Adjust dg-error.
+
+2019-10-09 Marek Polacek <polacek@redhat.com>
+
PR c++/92032 - DR 1601: Promotion of enum with fixed underlying type.
* g++.dg/cpp0x/scoped_enum10.C: New test.
* g++.dg/cpp0x/scoped_enum11.C: New test.
diff --git a/gcc/testsuite/g++.dg/conversion/qual1.C b/gcc/testsuite/g++.dg/conversion/qual1.C
new file mode 100644
index 0000000..5022da9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/conversion/qual1.C
@@ -0,0 +1,51 @@
+// PR c++/88128 - Implement DR 330: Qualification conversions and pointers to
+// arrays of pointers.
+
+int *a[4];
+const int *const(*ap1)[4] = &a;
+/* if at some level k the P2 is more cv-qualified than P1, then there
+ must be a const at every single level (other than level zero) of P2
+ up until k. */
+const int *(*ap2)[4] = &a; // { dg-error "cannot convert" }
+int *const(*ap3)[4] = &a;
+int *(*ap4)[4] = &a;
+int *(*const ap5)[4] = &a;
+const int *const(*const ap6)[4] = &a;
+int *const(*const ap7)[4] = &a;
+int *(*const ap8)[4] = &a;
+
+const int *b[4];
+const int *const(*bp1)[4] = &b;
+const int *(*bp2)[4] = &b;
+int *const(*bp3)[4] = &b; // { dg-error "cannot convert" }
+int *(*bp4)[4] = &b; // { dg-error "cannot convert" }
+int *(*const bp5)[4] = &b; // { dg-error "cannot convert" }
+const int *const(*const bp6)[4] = &b;
+int *const(*const bp7)[4] = &b; // { dg-error "cannot convert" }
+int *(*const bp8)[4] = &b; // { dg-error "cannot convert" }
+
+int *c[2][3];
+int const *const (*cp1)[3] = c;
+int const *(*cp2)[3] = c; // { dg-error "cannot convert" }
+int const *const (*const cp3)[3] = c;
+int *const (*cp4)[3] = c;
+int *(*cp5)[3] = c;
+
+double *const (*d)[3];
+double const *const (*e)[3] = d;
+int *(*f)[3];
+const int *const (*g)[3] = f;
+
+// From PR88128.
+int* (*xx)[];
+const int* const(*yy)[] = xx;
+
+// From DR 330.
+int main()
+{
+ double *array2D[2][3];
+
+ double * (*array2DPtr1)[3] = array2D;
+ double * const (*array2DPtr2)[3] = array2DPtr1;
+ double const * const (*array2DPtr3)[3] = array2DPtr2;
+}
diff --git a/gcc/testsuite/g++.dg/conversion/qual2.C b/gcc/testsuite/g++.dg/conversion/qual2.C
new file mode 100644
index 0000000..8a063a0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/conversion/qual2.C
@@ -0,0 +1,14 @@
+// PR c++/88128 - DR 330: Qual convs and pointers to arrays of pointers.
+
+// Make sure we don't accept different bounds.
+
+int *a[4];
+const int *const(*ap1)[5] = &a; // { dg-error "cannot convert" }
+
+int *(*b)[3];
+const int *const (*bp1)[3] = &b; // { dg-error "cannot convert" }
+const int *const (*bp2)[4] = &b; // { dg-error "cannot convert" }
+int *(*bp3)[4] = &b; // { dg-error "cannot convert" }
+
+int *c[2][3];
+int const *const (*cp1)[4] = c; // { dg-error "cannot convert" }
diff --git a/gcc/testsuite/g++.dg/conversion/qual3.C b/gcc/testsuite/g++.dg/conversion/qual3.C
new file mode 100644
index 0000000..4b466d9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/conversion/qual3.C
@@ -0,0 +1,53 @@
+// PR c++/88128 - DR 330: Qual convs and pointers to arrays of pointers.
+// { dg-do compile { target c++17 } }
+
+using P = int *(*)[3];
+using Q = const int *const (*)[3];
+using Qi = const int *[3];
+using Q2 = Qi const *;
+using R = const int *const (*)[4];
+using S = const int *const (*)[];
+using T = const int *(*)[];
+
+void
+f (P p, Q q, Q2 q2, R r, S s, T t)
+{
+ q = p;
+ q2 = p;
+ r = p; // { dg-error "cannot convert" }
+ t = p; // { dg-error "cannot convert" }
+ s = t;
+ t = s; // { dg-error "invalid conversion" }
+
+ // Test const_cast.
+ const_cast<P>(q);
+ const_cast<P>(q2);
+ const_cast<Q>(p);
+ const_cast<Q2>(p);
+ const_cast<S>(p); // { dg-error "invalid .const_cast." }
+ const_cast<P>(s); // { dg-error "invalid .const_cast." }
+ const_cast<S>(q); // { dg-error "invalid .const_cast." }
+ const_cast<S>(q2); // { dg-error "invalid .const_cast." }
+ const_cast<Q>(s); // { dg-error "invalid .const_cast." }
+ const_cast<Q2>(s); // { dg-error "invalid .const_cast." }
+ const_cast<T>(s);
+ const_cast<S>(t);
+ const_cast<T>(q); // { dg-error "invalid .const_cast." }
+ const_cast<Q>(t); // { dg-error "invalid .const_cast." }
+
+ // Test reinterpret_cast.
+ reinterpret_cast<P>(q); // { dg-error "casts away qualifiers" }
+ reinterpret_cast<P>(q2); // { dg-error "casts away qualifiers" }
+ reinterpret_cast<Q>(p);
+ reinterpret_cast<Q2>(p);
+ reinterpret_cast<S>(p);
+ reinterpret_cast<P>(s); // { dg-error "casts away qualifiers" }
+ reinterpret_cast<S>(q);
+ reinterpret_cast<S>(q2);
+ reinterpret_cast<Q>(s);
+ reinterpret_cast<Q2>(s);
+ reinterpret_cast<T>(s); // { dg-error "casts away qualifiers" }
+ reinterpret_cast<S>(t);
+ reinterpret_cast<T>(q); // { dg-error "casts away qualifiers" }
+ reinterpret_cast<Q>(t);
+}
diff --git a/gcc/testsuite/g++.dg/conversion/ref2.C b/gcc/testsuite/g++.dg/conversion/ref2.C
new file mode 100644
index 0000000..418e711
--- /dev/null
+++ b/gcc/testsuite/g++.dg/conversion/ref2.C
@@ -0,0 +1,29 @@
+// PR c++/88128 - Implement DR 330: Qualification conversions and pointers to
+// arrays of pointers.
+
+int *ar[4];
+/* if at some level k the P2 is more cv-qualified than P1, then there
+ must be a const at every single level (other than level zero) of P2
+ up until k. */
+const int *(&arp)[4] = ar; // { dg-error "discards qualifiers" }
+const int *const(&arp2)[4] = ar;
+int *const(&arp3)[4] = ar;
+int *(&arp4)[4] = ar;
+
+const int *br[4];
+const int *(&brp)[4] = br;
+const int *const(&brp2)[4] = br;
+int *const(&brp3)[4] = br; // { dg-error "discards qualifiers" }
+int *(&brp4)[4] = br; // { dg-error "discards qualifiers" }
+
+int *c[2][3];
+int const *const (&cp1)[3] = *c;
+int const *(&cp2)[3] = *c; // { dg-error "discards qualifiers" }
+int *const (&cp3)[3] = *c;
+int *(&cp4)[3] = *c;
+
+double *const (*d)[3];
+double const *const (&e)[3] = *d;
+
+int *(*f)[3];
+const int *const (&g)[3] = *f;
diff --git a/gcc/testsuite/g++.dg/conversion/ref3.C b/gcc/testsuite/g++.dg/conversion/ref3.C
new file mode 100644
index 0000000..ca5326c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/conversion/ref3.C
@@ -0,0 +1,4 @@
+int a[2];
+const int (&rc)[2] = a;
+volatile int (&rv)[2] = a;
+const volatile int (&rcv)[2] = a;
diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist-array3.C b/gcc/testsuite/g++.dg/cpp0x/initlist-array3.C
index 1a94f4e..4140cd9 100644
--- a/gcc/testsuite/g++.dg/cpp0x/initlist-array3.C
+++ b/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 a/gcc/testsuite/g++.dg/cpp0x/initlist-array7.C b/gcc/testsuite/g++.dg/cpp0x/initlist-array7.C
new file mode 100644
index 0000000..7a689c66
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp0x/initlist-array8.C b/gcc/testsuite/g++.dg/cpp0x/initlist-array8.C
new file mode 100644
index 0000000..ac2774e
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/array-conv1.C b/gcc/testsuite/g++.dg/cpp2a/array-conv1.C
new file mode 100644
index 0000000..e90b340
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/array-conv10.C b/gcc/testsuite/g++.dg/cpp2a/array-conv10.C
new file mode 100644
index 0000000..1ee1a77
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/array-conv11.C b/gcc/testsuite/g++.dg/cpp2a/array-conv11.C
new file mode 100644
index 0000000..a072b29
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/array-conv12.C b/gcc/testsuite/g++.dg/cpp2a/array-conv12.C
new file mode 100644
index 0000000..1156ea3
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/array-conv13.C b/gcc/testsuite/g++.dg/cpp2a/array-conv13.C
new file mode 100644
index 0000000..9908b7e
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/array-conv14.C b/gcc/testsuite/g++.dg/cpp2a/array-conv14.C
new file mode 100644
index 0000000..793e85d
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/array-conv15.C b/gcc/testsuite/g++.dg/cpp2a/array-conv15.C
new file mode 100644
index 0000000..033a746
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/array-conv16.C b/gcc/testsuite/g++.dg/cpp2a/array-conv16.C
new file mode 100644
index 0000000..bfb39d1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/array-conv16.C
@@ -0,0 +1,16 @@
+// PR c++/91364 - P0388R4: Permit conversions to arrays of unknown bound.
+// { dg-do compile { target c++2a } }
+
+using P = int *(*)[3];
+using S = const int *const (*)[];
+using Q = const int *const (*)[3];
+using Qi = const int *[3];
+using Q2 = Qi const *;
+
+void
+f (P p, S s, Q q, Q2 q2)
+{
+ s = p;
+ s = q;
+ s = q2;
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/array-conv17.C b/gcc/testsuite/g++.dg/cpp2a/array-conv17.C
new file mode 100644
index 0000000..3313ed4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/array-conv17.C
@@ -0,0 +1,39 @@
+// PR c++/91364 - P0388R4: Permit conversions to arrays of unknown bound.
+// { dg-do compile { target c++2a } }
+
+// As conversion/qual1.C, but with [].
+
+int *a[4];
+const int *const(*ap1)[] = &a;
+/* if at some level k the P2 is more cv-qualified than P1, then there
+ must be a const at every single level (other than level zero) of P2
+ up until k. */
+const int *(*ap2)[] = &a; // { dg-error "cannot convert" }
+int *const(*ap3)[] = &a;
+int *(*ap4)[] = &a;
+int *(*const ap5)[] = &a;
+const int *const(*const ap6)[] = &a;
+int *const(*const ap7)[] = &a;
+int *(*const ap8)[] = &a;
+
+const int *b[4];
+const int *const(*bp1)[] = &b;
+const int *(*bp2)[] = &b;
+int *const(*bp3)[] = &b; // { dg-error "cannot convert" }
+int *(*bp4)[] = &b; // { dg-error "cannot convert" }
+int *(*const bp5)[] = &b; // { dg-error "cannot convert" }
+const int *const(*const bp6)[] = &b;
+int *const(*const bp7)[] = &b; // { dg-error "cannot convert" }
+int *(*const bp8)[] = &b; // { dg-error "cannot convert" }
+
+int *c[2][3];
+int const *const (*cp1)[] = c;
+int const *(*cp2)[] = c; // { dg-error "cannot convert" }
+int const *const (*const cp3)[] = c;
+int *const (*cp4)[] = c;
+int *(*cp5)[] = c;
+
+double *const (*d)[3];
+double const *const (*e)[] = d;
+int *(*f)[3];
+const int *const (*g)[] = f;
diff --git a/gcc/testsuite/g++.dg/cpp2a/array-conv2.C b/gcc/testsuite/g++.dg/cpp2a/array-conv2.C
new file mode 100644
index 0000000..5245d83
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/array-conv3.C b/gcc/testsuite/g++.dg/cpp2a/array-conv3.C
new file mode 100644
index 0000000..3d92b40
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/array-conv4.C b/gcc/testsuite/g++.dg/cpp2a/array-conv4.C
new file mode 100644
index 0000000..979c69b
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/array-conv5.C b/gcc/testsuite/g++.dg/cpp2a/array-conv5.C
new file mode 100644
index 0000000..34678f5
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/array-conv6.C b/gcc/testsuite/g++.dg/cpp2a/array-conv6.C
new file mode 100644
index 0000000..c2389c8
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/array-conv7.C b/gcc/testsuite/g++.dg/cpp2a/array-conv7.C
new file mode 100644
index 0000000..07c709f
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/array-conv8.C b/gcc/testsuite/g++.dg/cpp2a/array-conv8.C
new file mode 100644
index 0000000..635c767
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.dg/cpp2a/array-conv9.C b/gcc/testsuite/g++.dg/cpp2a/array-conv9.C
new file mode 100644
index 0000000..82f615d
--- /dev/null
+++ b/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 a/gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C b/gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C
index 6b52783..c3b1ab5 100644
--- a/gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C
+++ b/gcc/testsuite/g++.old-deja/g++.bugs/900321_01.C
@@ -20,7 +20,7 @@ void function_0 ()
{
// we miss the first two because typeck.c (comp_array_types) deems
// it okay if one of the sizes is null
- ptr_to_array_of_ints = ptr_to_array_of_3_ints; // { dg-error "" }
+ ptr_to_array_of_ints = ptr_to_array_of_3_ints; // { dg-error "conversions to arrays" "" { target c++17_down } }
ptr_to_array_of_3_ints = ptr_to_array_of_ints; // { dg-error "" }
ptr_to_array_of_3_ints = ptr_to_array_of_5_ints; // { dg-error "" }
diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog
index 045fbe3..8b7443c 100644
--- a/libstdc++-v3/ChangeLog
+++ b/libstdc++-v3/ChangeLog
@@ -1,3 +1,11 @@
+2019-10-09 Marek Polacek <polacek@redhat.com>
+
+ PR c++/91364 - P0388R4: Permit conversions to arrays of unknown bound.
+ PR c++/69531 - DR 1307: Differently bounded array parameters.
+ PR c++/88128 - DR 330: Qual convs and pointers to arrays of pointers.
+ * testsuite/23_containers/span/lwg3255.cc: Adjust test to match the
+ post-P0388R4 behavior.
+
2019-10-09 Jonathan Wakely <jwakely@redhat.com>
PR libstdc++/91057
diff --git a/libstdc++-v3/testsuite/23_containers/span/lwg3255.cc b/libstdc++-v3/testsuite/23_containers/span/lwg3255.cc
index 638c881..bab7da3 100644
--- a/libstdc++-v3/testsuite/23_containers/span/lwg3255.cc
+++ b/libstdc++-v3/testsuite/23_containers/span/lwg3255.cc
@@ -28,8 +28,7 @@ using std::is_constructible_v;
// LWG 3255 span's array constructor is too strict
-// FIXME: remove '!' from next line when P0388R4 is implemented:
-static_assert( ! is_constructible_v<span<const int* const>, array<int*, 2>> );
+static_assert( is_constructible_v<span<const int* const>, array<int*, 2>> );
static_assert( is_constructible_v<span<const int>, array<const int, 4>> );
static_assert( is_constructible_v<span<int, 1>, int(&)[1]> );