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
This commit is contained in:
Marek Polacek 2019-10-09 20:58:00 +00:00 committed by Marek Polacek
parent 3b29211acb
commit 89e0a492af
33 changed files with 902 additions and 31 deletions

View File

@ -1,3 +1,32 @@
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.

View File

@ -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)

View File

@ -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,

View File

@ -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));
}

View File

@ -1,3 +1,35 @@
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.

View File

@ -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;
}

View File

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

View File

@ -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);
}

View File

@ -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;

View File

@ -0,0 +1,4 @@
int a[2];
const int (&rc)[2] = a;
volatile int (&rv)[2] = a;
const volatile int (&rcv)[2] = a;

View File

@ -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});
}

View File

@ -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 ();
}

View File

@ -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 ();
}

View File

@ -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;

View File

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

View File

@ -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);
}

View File

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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 ();
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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};
}

View File

@ -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 ();
}

View File

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

View File

@ -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 ();
}

View File

@ -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 ();
}

View File

@ -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 ();
}

View File

@ -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 ();
}

View File

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

View File

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

View File

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

View File

@ -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]> );