c++: reject scalar array initialization with nullptr [PR94510]

The change committed to GCC 9 to allow string literals as template arguments
caused the compiler to prune away, and thus miss diagnosing, conversion from
nullptr to int in an array initializer.  After looking at various approaches
to improving the pruning, we realized that the only place the pruning is
necessary is in the mangler.

gcc/cp/ChangeLog
2020-04-22  Martin Sebor  <msebor@redhat.com>
	    Jason Merrill  <jason@redhat.com>

	PR c++/94510
	* decl.c (reshape_init_array_1): Avoid stripping redundant trailing
	zero initializers...
	* mangle.c (write_expression): ...and handle them here even for
	pointers to members by calling zero_init_expr_p.
	* cp-tree.h (zero_init_expr_p): Declare.
	* tree.c (zero_init_expr_p): Define.
	(type_initializer_zero_p): Remove.
	* pt.c (tparm_obj_values): New hash_map.
	(get_template_parm_object): Store to it.
	(tparm_object_argument): New.

gcc/testsuite/ChangeLog
2020-04-22  Martin Sebor  <msebor@redhat.com>

	PR c++/94510
	* g++.dg/init/array58.C: New test.
	* g++.dg/init/array59.C: New test.
	* g++.dg/cpp2a/nontype-class34.C: New test.
	* g++.dg/cpp2a/nontype-class35.C: New test.
This commit is contained in:
Martin Sebor 2020-04-22 02:27:54 -04:00 committed by Jason Merrill
parent 0fe9eaaa08
commit 587970215f
12 changed files with 333 additions and 149 deletions

View File

@ -1,3 +1,18 @@
2020-04-22 Martin Sebor <msebor@redhat.com>
Jason Merrill <jason@redhat.com>
PR c++/94510
* decl.c (reshape_init_array_1): Avoid stripping redundant trailing
zero initializers...
* mangle.c (write_expression): ...and handle them here even for
pointers to members by calling zero_init_expr_p.
* cp-tree.h (zero_init_expr_p): Declare.
* tree.c (zero_init_expr_p): Define.
(type_initializer_zero_p): Remove.
* pt.c (tparm_obj_values): New hash_map.
(get_template_parm_object): Store to it.
(tparm_object_argument): New.
2020-04-22 Patrick Palka <ppalka@redhat.com>
PR c++/67825

View File

@ -7001,6 +7001,7 @@ enum { nt_opaque = false, nt_transparent = true };
extern tree alias_template_specialization_p (const_tree, bool);
extern tree dependent_alias_template_spec_p (const_tree, bool);
extern bool template_parm_object_p (const_tree);
extern tree tparm_object_argument (tree);
extern bool explicit_class_specialization_p (tree);
extern bool push_tinst_level (tree);
extern bool push_tinst_level_loc (tree, location_t);
@ -7375,6 +7376,7 @@ extern bool type_has_nontrivial_copy_init (const_tree);
extern void maybe_warn_parm_abi (tree, location_t);
extern bool class_tmpl_impl_spec_p (const_tree);
extern int zero_init_p (const_tree);
extern bool zero_init_expr_p (tree);
extern bool check_abi_tag_redeclaration (const_tree, const_tree,
const_tree);
extern bool check_abi_tag_args (tree, tree);
@ -7492,11 +7494,6 @@ extern tree cxx_copy_lang_qualifiers (const_tree, const_tree);
extern void cxx_print_statistics (void);
extern bool maybe_warn_zero_as_null_pointer_constant (tree, location_t);
/* Analogous to initializer_zerop but also examines the type for
which the initializer is being used. Unlike initializer_zerop,
considers empty strings to be zero initializers for arrays and
non-zero for pointers. */
extern bool type_initializer_zero_p (tree, tree);
/* in ptree.c */
extern void cxx_print_xnode (FILE *, tree, int);

View File

@ -6038,9 +6038,6 @@ reshape_init_array_1 (tree elt_type, tree max_index, reshape_iter *d,
max_index_cst = tree_to_uhwi (fold_convert (size_type_node, max_index));
}
/* Set to the index of the last element with a non-zero initializer.
Zero initializers for elements past this one can be dropped. */
unsigned HOST_WIDE_INT last_nonzero = -1;
/* Loop until there are no more initializers. */
for (index = 0;
d->cur != d->end && (!sized_array_p || index <= max_index_cst);
@ -6067,50 +6064,11 @@ reshape_init_array_1 (tree elt_type, tree max_index, reshape_iter *d,
if (!TREE_CONSTANT (elt_init))
TREE_CONSTANT (new_init) = false;
/* Pointers initialized to strings must be treated as non-zero
even if the string is empty. */
tree init_type = TREE_TYPE (elt_init);
if (POINTER_TYPE_P (elt_type) != POINTER_TYPE_P (init_type)
|| !type_initializer_zero_p (elt_type, elt_init))
last_nonzero = index;
/* This can happen with an invalid initializer (c++/54501). */
if (d->cur == old_cur && !sized_array_p)
break;
}
if (sized_array_p && trivial_type_p (elt_type))
{
/* Strip trailing zero-initializers from an array of a trivial
type of known size. They are redundant and get in the way
of telling them apart from those with implicit zero value. */
unsigned HOST_WIDE_INT nelts = CONSTRUCTOR_NELTS (new_init);
if (last_nonzero > nelts)
nelts = 0;
else if (last_nonzero < nelts - 1)
nelts = last_nonzero + 1;
/* Sharing a stripped constructor can get in the way of
overload resolution. E.g., initializing a class from
{{0}} might be invalid while initializing the same class
from {{}} might be valid. */
if (reuse && nelts < CONSTRUCTOR_NELTS (new_init))
{
vec<constructor_elt, va_gc> *v;
vec_alloc (v, nelts);
for (unsigned int i = 0; i < nelts; i++)
{
constructor_elt elt = *CONSTRUCTOR_ELT (new_init, i);
if (TREE_CODE (elt.value) == CONSTRUCTOR)
elt.value = unshare_constructor (elt.value);
v->quick_push (elt);
}
new_init = build_constructor (TREE_TYPE (new_init), v);
}
else
vec_safe_truncate (CONSTRUCTOR_ELTS (new_init), nelts);
}
return new_init;
}

View File

@ -3176,7 +3176,8 @@ write_expression (tree expr)
write_type (etype);
}
if (!initializer_zerop (expr) || !trivial_type_p (etype))
bool nontriv = !trivial_type_p (etype);
if (nontriv || !zero_init_expr_p (expr))
{
/* Convert braced initializer lists to STRING_CSTs so that
A<"Foo"> mangles the same as A<{'F', 'o', 'o', 0}> while
@ -3187,19 +3188,22 @@ write_expression (tree expr)
if (TREE_CODE (expr) == CONSTRUCTOR)
{
vec<constructor_elt, va_gc> *elts = CONSTRUCTOR_ELTS (expr);
unsigned last_nonzero = -1, i;
unsigned last_nonzero = UINT_MAX, i;
tree val;
FOR_EACH_CONSTRUCTOR_VALUE (elts, i, val)
if (!initializer_zerop (val))
last_nonzero = i;
if (!nontriv)
FOR_EACH_CONSTRUCTOR_VALUE (elts, i, val)
if (!zero_init_expr_p (val))
last_nonzero = i;
FOR_EACH_CONSTRUCTOR_VALUE (elts, i, val)
{
if (i > last_nonzero)
break;
write_expression (val);
}
if (nontriv || last_nonzero != UINT_MAX)
FOR_EACH_CONSTRUCTOR_VALUE (elts, i, val)
{
if (i > last_nonzero)
break;
/* FIXME handle RANGE_EXPR */
write_expression (val);
}
}
else
{
@ -3525,7 +3529,7 @@ write_template_arg (tree node)
if (template_parm_object_p (node))
/* We want to mangle the argument, not the var we stored it in. */
node = DECL_INITIAL (node);
node = tparm_object_argument (node);
/* Strip a conversion added by convert_nontype_argument. */
if (TREE_CODE (node) == IMPLICIT_CONV_EXPR)

View File

@ -7006,6 +7006,11 @@ invalid_tparm_referent_p (tree type, tree expr, tsubst_flags_t complain)
}
/* The template arguments corresponding to template parameter objects of types
that contain pointers to members. */
static GTY(()) hash_map<tree, tree> *tparm_obj_values;
/* Return a VAR_DECL for the C++20 template parameter object corresponding to
template argument EXPR. */
@ -7039,10 +7044,32 @@ get_template_parm_object (tree expr, tsubst_flags_t complain)
SET_DECL_ASSEMBLER_NAME (decl, name);
DECL_CONTEXT (decl) = global_namespace;
comdat_linkage (decl);
if (!zero_init_p (type))
{
/* If EXPR contains any PTRMEM_CST, they will get clobbered by
lower_var_init before we're done mangling. So store the original
value elsewhere. */
tree copy = unshare_constructor (expr);
hash_map_safe_put<hm_ggc> (tparm_obj_values, decl, copy);
}
pushdecl_top_level_and_finish (decl, expr);
return decl;
}
/* Return the actual template argument corresponding to template parameter
object VAR. */
tree
tparm_object_argument (tree var)
{
if (zero_init_p (TREE_TYPE (var)))
return DECL_INITIAL (var);
return *(tparm_obj_values->get (var));
}
/* Attempt to convert the non-type template parameter EXPR to the
indicated TYPE. If the conversion is successful, return the
converted value. If the conversion is unsuccessful, return

View File

@ -4478,6 +4478,33 @@ zero_init_p (const_tree t)
return 1;
}
/* Returns true if the expression or initializer T is the result of
zero-initialization for its type, taking pointers to members
into consideration. */
bool
zero_init_expr_p (tree t)
{
tree type = TREE_TYPE (t);
if (!type || dependent_type_p (type))
return false;
if (zero_init_p (type))
return initializer_zerop (t);
if (TYPE_PTRMEM_P (type))
return null_member_pointer_value_p (t);
if (TREE_CODE (t) == CONSTRUCTOR
&& CP_AGGREGATE_TYPE_P (type))
{
tree elt_init;
unsigned HOST_WIDE_INT i;
FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (t), i, elt_init)
if (!zero_init_expr_p (elt_init))
return false;
return true;
}
return false;
}
/* True IFF T is a C++20 structural type (P1907R1) that can be used as a
non-type template parameter. If EXPLAIN, explain why not. */
@ -5746,76 +5773,6 @@ maybe_warn_zero_as_null_pointer_constant (tree expr, location_t loc)
return false;
}
/* Given an initializer INIT for a TYPE, return true if INIT is zero
so that it can be replaced by value initialization. This function
distinguishes betwen empty strings as initializers for arrays and
for pointers (which make it return false). */
bool
type_initializer_zero_p (tree type, tree init)
{
if (type == error_mark_node || init == error_mark_node)
return false;
STRIP_NOPS (init);
if (POINTER_TYPE_P (type))
return TREE_CODE (init) != STRING_CST && initializer_zerop (init);
if (TREE_CODE (init) != CONSTRUCTOR)
{
/* A class can only be initialized by a non-class type if it has
a ctor that converts from that type. Such classes are excluded
since their semantics are unknown. */
if (RECORD_OR_UNION_TYPE_P (type)
&& !RECORD_OR_UNION_TYPE_P (TREE_TYPE (init)))
return false;
return initializer_zerop (init);
}
if (TREE_CODE (type) == ARRAY_TYPE)
{
tree elt_type = TREE_TYPE (type);
elt_type = TYPE_MAIN_VARIANT (elt_type);
if (elt_type == char_type_node)
return initializer_zerop (init);
tree elt_init;
unsigned HOST_WIDE_INT i;
FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (init), i, elt_init)
if (!type_initializer_zero_p (elt_type, elt_init))
return false;
return true;
}
if (TREE_CODE (type) != RECORD_TYPE)
return initializer_zerop (init);
if (TYPE_NON_AGGREGATE_CLASS (type))
return false;
tree fld = TYPE_FIELDS (type);
tree fld_init;
unsigned HOST_WIDE_INT i;
FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (init), i, fld_init)
{
fld = next_initializable_field (fld);
if (!fld)
return true;
tree fldtype = TREE_TYPE (fld);
if (!type_initializer_zero_p (fldtype, fld_init))
return false;
fld = DECL_CHAIN (fld);
if (!fld)
break;
}
return true;
}
#if defined ENABLE_TREE_CHECKING && (GCC_VERSION >= 2007)
/* Complain that some language-specific thing hanging off a tree
node has been accessed improperly. */

View File

@ -1,3 +1,11 @@
2020-04-22 Martin Sebor <msebor@redhat.com>
PR c++/94510
* g++.dg/init/array58.C: New test.
* g++.dg/init/array59.C: New test.
* g++.dg/cpp2a/nontype-class34.C: New test.
* g++.dg/cpp2a/nontype-class35.C: New test.
2020-04-22 Patrick Palka <ppalka@redhat.com>
PR c++/67825

View File

@ -24,56 +24,50 @@ struct B { padm_t a[2]; };
template <B> struct Y { };
void g__ (Y<B{{ }}>) { }
// { dg-final { scan-assembler "_Z3g__1YIXtl1BtlA2_M1AA2_iLS3_0EEEEE" } }
// { dg-final { scan-assembler "_Z3g__1YIXtl1BEEE" } }
void g0_ (Y<B{{ 0 }}>) { }
// { dg-final { scan-assembler "_Z3g0_1YIXtl1BtlA2_M1AA2_iLS3_0EEEEE" } }
// { dg-final { scan-assembler "_Z3g0_1YIXtl1BEEE" } }
void g00 (Y<B{{ 0, 0 }}>) { }
// { dg-final { scan-assembler "_Z3g001YIXtl1BtlA2_M1AA2_iLS3_0EEEEE" } }
// { dg-final { scan-assembler "_Z3g001YIXtl1BEEE" } }
void g0x (Y<B{{ 0, &A::a }}>) { }
// FIXME: This needs to mangle differently from g00. The space at
// the end is intentional to make the directive fail so that the xfail
// can be reminder to change this once the mangling is fixed.
// { dg-final { scan-assembler "_Z3g0x1YIXtl1BtlA2_M1AA2_iLS3_0EEEEE " { xfail *-*-* } } }
// { dg-final { scan-assembler "_Z3g0x1YIXtl1BtlA2_M1AA2_iLS3_0EadL_ZNS1_1aEEEEEE" } }
void gx_ (Y<B{{ &A::a }}>) { }
// { dg-final { scan-assembler "_Z3gx_1YIXtl1BtlA2_M1AA2_iLS3_0ELS3_0EEEEE" } }
// { dg-final { scan-assembler "_Z3gx_1YIXtl1BtlA2_M1AA2_iadL_ZNS1_1aEEEEEE" } }
struct C { padm_t a[3]; };
template <C> struct Z { };
void h___ (Z<C{{ }}>) { }
// { dg-final { scan-assembler "_Z4h___1ZIXtl1CtlA3_M1AA2_iLS3_0EEEEE" } }
// { dg-final { scan-assembler "_Z4h___1ZIXtl1CEEE" } }
void h0__ (Z<C{{ 0 }}>) { }
// { dg-final { scan-assembler "_Z4h0__1ZIXtl1CtlA3_M1AA2_iLS3_0EEEEE" } }
// { dg-final { scan-assembler "_Z4h0__1ZIXtl1CEEE" } }
void h00_ (Z<C{{ 0, 0 }}>) { }
// { dg-final { scan-assembler "_Z4h00_1ZIXtl1CtlA3_M1AA2_iLS3_0EEEEE" } }
// { dg-final { scan-assembler "_Z4h00_1ZIXtl1CEEE" } }
void h000 (Z<C{{ 0, 0, 0 }}>) { }
// { dg-final { scan-assembler "_Z4h0001ZIXtl1CtlA3_M1AA2_iLS3_0EEEEE" } }
// { dg-final { scan-assembler "_Z4h0001ZIXtl1CEEE" } }
void h00x (Z<C{{ 0, 0, &A::a }}>) { }
// FIXME: This needs to mangle differently from hx0_ and hx__.
// { dg-final { scan-assembler "_Z4h00x1ZIXtl1CtlA3_M1AA2_iLS3_0ELS3_0EEEEE " { xfail *-*-*} } }
// { dg-final { scan-assembler "_Z4h00x1ZIXtl1CtlA3_M1AA2_iLS3_0ELS3_0EadL_ZNS1_1aEEEEEE" } }
void h0x0 (Z<C{{ 0, &A::a, 0 }}>) { }
// { dg-final { scan-assembler "_Z4h0x01ZIXtl1CtlA3_M1AA2_iLS3_0ELS3_0ELS3_0EEEEE" } }
// { dg-final { scan-assembler "_Z4h0x01ZIXtl1CtlA3_M1AA2_iLS3_0EadL_ZNS1_1aEEEEEE" } }
void h0x_ (Z<C{{ 0, &A::a }}>) { }
// { dg-final { scan-assembler "_Z4h0x_1ZIXtl1CtlA3_M1AA2_iLS3_0ELS3_0ELS3_0EEEEE" } }
// { dg-final { scan-assembler "_Z4h0x_1ZIXtl1CtlA3_M1AA2_iLS3_0EadL_ZNS1_1aEEEEEE" } }
void hx0_ (Z<C{{ &A::a, 0 }}>) { }
// FIXME: This needs to mangle differently from h00x and hx__.
// { dg-final { scan-assembler "_Z4hx0_1ZIXtl1CtlA3_M1AA2_iLS3_0ELS3_0EEEEE " { xfail *-*-*} } }
// { dg-final { scan-assembler "_Z4hx0_1ZIXtl1CtlA3_M1AA2_iadL_ZNS1_1aEEEEEE" } }
void hx__ (Z<C{{ &A::a }}>) { }
// FIXME: This needs to mangle differently from h00x and hx0_.
// { dg-final { scan-assembler "_Z4hx__1ZIXtl1CtlA3_M1AA2_iLS3_0ELS3_0EEEEE " { xfail *-*-* } } }
// { dg-final { scan-assembler "_Z4hx__1ZIXtl1CtlA3_M1AA2_iadL_ZNS1_1aEEEEEE" } }
// Exercise arrays of pointers to function members.

View File

@ -0,0 +1,76 @@
/* PR c++/94510 - nullptr_t implicitly cast to zero twice in std::array
{ dg-do compile { target c++2a } }
{ dg-options "-Wall" } */
struct A { int i; int f (); };
typedef int A::*MemPtr;
typedef int (A::*MemFuncPtr)();
struct B { MemPtr a[3]; MemFuncPtr b[3]; };
static const constexpr MemPtr mp0 = { 0 };
static const constexpr MemPtr mpn = { nullptr };
static const constexpr MemPtr mp_ = { };
static const constexpr MemPtr mpi = { &A::i };
template <B> struct X { };
typedef X<B{ }> XB;
typedef X<B{ 0 }> XB;
typedef X<B{{ 0 }}> XB;
typedef X<B{{ MemPtr{ }}}> XB;
typedef X<B{{ MemPtr{ 0 }}}> XB;
typedef X<B{{ MemPtr () }}> XB;
typedef X<B{{ MemPtr{ nullptr }}}> XB;
typedef X<B{{ mp_ }}> XB;
typedef X<B{{ mpn }}> XB;
typedef X<B{{ mp0 }}> XB;
typedef X<B{ mpi }> XBp;
typedef X<B{ mpi, 0 }> XBp;
typedef X<B{{ mpi, 0 }}> XBp;
typedef X<B{{ mpi, MemPtr{ }}}> XBp;
typedef X<B{{ mpi, MemPtr{ 0 }}}> XBp;
typedef X<B{{ mpi, MemPtr () }}> XBp;
typedef X<B{{ mpi, MemPtr{ nullptr }}}> XBp;
typedef X<B{{ mpi, mp_ }}> XBp;
typedef X<B{{ mpi, mpn }}> XBp;
typedef X<B{{ mpi, mp0 }}> XBp;
typedef X<B{ mpi, mpi }> XBpp;
typedef X<B{ mpi, mpi, 0 }> XBpp;
typedef X<B{{ mpi, mpi, 0 }}> XBpp;
typedef X<B{{ mpi, mpi, MemPtr{ }}}> XBpp;
typedef X<B{{ mpi, mpi, MemPtr{ 0 }}}> XBpp;
typedef X<B{{ mpi, mpi, MemPtr () }}> XBpp;
typedef X<B{{ mpi, mpi, MemPtr{ nullptr }}}> XBpp;
typedef X<B{{ mpi, mpi, mp_ }}> XBpp;
typedef X<B{{ mpi, mpi, mpn }}> XBpp;
typedef X<B{{ mpi, mpi, mp0 }}> XBpp;
typedef X<B{ 0, mpi }> XB0p;
typedef X<B{ nullptr, mpi, 0 }> XB0p;
typedef X<B{ mp0, mpi, 0 }> XB0p;
typedef X<B{ 0, 0, mpi }> XB00p;
typedef X<B{ 0, nullptr, mpi }> XB00p;
typedef X<B{ nullptr, 0, mpi }> XB00p;
typedef X<B{ nullptr, nullptr, mpi }> XB00p;
typedef X<B{ MemPtr{ }, MemPtr{ }, mpi }> XB00p;
typedef X<B{ mp0, MemPtr{ }, mpi }> XB00p;
typedef X<B{ mpn, mpn, mpi }> XB00p;
typedef X<B{ mpn, mp_, mpi }> XB00p; // { dg-bogus "conflicting declaration" "pr94568" { xfail *-*-* } }
static const constexpr MemFuncPtr mfp0 = { 0 };
static const constexpr MemFuncPtr mfpn = { nullptr };
static const constexpr MemFuncPtr mfp_ = { };
typedef X<B{{ }, { }}> XB;
typedef X<B{{ }, { 0 }}> XB;
typedef X<B{{ }, { MemFuncPtr{ }}}> XB;
typedef X<B{{ }, { MemFuncPtr{ 0 }}}> XB;
typedef X<B{{ }, { MemFuncPtr () }}> XB;
typedef X<B{{ }, { MemFuncPtr{ nullptr }}}> XB;
typedef X<B{{ }, { mfp_ }}> XB;
typedef X<B{{ }, { mfpn }}> XB;
typedef X<B{{ }, { mfp0 }}> XB;

View File

@ -0,0 +1,80 @@
/* PR c++/94510 - nullptr_t implicitly cast to zero twice in std::array
{ dg-do compile { target c++2a } }
{ dg-options "-Wall" } */
struct A { char a[4]; };
template <A> struct B { };
constexpr const char c0{ };
constexpr const char c1{ 1 };
typedef B<A{ }> BA;
typedef B<A{ { } }> BA;
typedef B<A{ { 0 } }> BA;
typedef B<A{ { c0 } }> BA;
typedef B<A{ { 0, 0 } }> BA;
typedef B<A{ { 0, 0, 0 } }> BA;
typedef B<A{ { 0, 0, 0, 0 } }> BA;
typedef B<A{ { c0, c0, c0 } }> BA;
typedef B<A{ { c0, c0, c0, c0 } }> BA;
typedef B<A{ "" }> BA;
typedef B<A{ "\0" }> BA;
typedef B<A{ "\0\0" }> BA;
typedef B<A{ "\0\0\0" }> BA;
typedef B<A{ 1 }> BA1;
typedef B<A{ { 1 } }> BA1;
typedef B<A{ { 1, 0 } }> BA1;
typedef B<A{ { 1, 0, 0 } }> BA1;
typedef B<A{ { 1, 0, 0, 0 } }> BA1;
typedef B<A{ { c1 } }> BA1;
typedef B<A{ { c1, c0 } }> BA1;
typedef B<A{ { c1, c0, c0 } }> BA1;
typedef B<A{ { c1, c0, c0, c0 } }> BA1;
typedef B<A{ "\1" }> BA1;
typedef B<A{ "\1\0" }> BA1;
typedef B<A{ "\1\0\0" }> BA1;
typedef B<A{ 0, 1 }> BA01;
typedef B<A{ { 0, 1 } }> BA01;
typedef B<A{ { 0, 1, 0 } }> BA01;
typedef B<A{ { 0, 1, 0, 0 } }> BA01;
typedef B<A{ { c0, c1 } }> BA01;
typedef B<A{ { c0, c1, c0 } }> BA01;
typedef B<A{ { c0, c1, c0, c0 } }> BA01;
typedef B<A{ "\0\1" }> BA01;
typedef B<A{ "\0\1\0" }> BA01;
struct C { int a[4]; };
template <C> struct D { };
constexpr const int i0{ };
typedef D<C{ }> DC;
typedef D<C{ { } }> DC;
typedef D<C{ { 0 } }> DC;
typedef D<C{ { 0, 0 } }> DC;
typedef D<C{ { 0, 0, 0 } }> DC;
typedef D<C{ { 0, 0, 0, 0 } }> DC;
typedef D<C{ { i0 } }> DC;
typedef D<C{ { i0, i0 } }> DC;
typedef D<C{ { i0, i0, i0 } }> DC;
typedef D<C{ { i0, i0, i0, i0 } }> DC;
constexpr const int i1{ 1 };
typedef D<C{ 1 }> DC1;
typedef D<C{ { 1 } }> DC1;
typedef D<C{ { 1, 0 } }> DC1;
typedef D<C{ { 1, 0, 0 } }> DC1;
typedef D<C{ { 1, 0, 0, 0 } }> DC1;
typedef D<C{ { i1, i0, i0, i0 } }> DC1;
typedef D<C{ 0, 1 }> DC01;
typedef D<C{ { 0, 1 } }> DC01;
typedef D<C{ { 0, 1, 0 } }> DC01;
typedef D<C{ { 0, 1, 0, 0 } }> DC01;
typedef D<C{ { 0, i1, 0, 0 } }> DC01;
typedef D<C{ { i0, i1, i0, i0 } }> DC01; // { dg-bogus "conflicting declaration" "pr94567" { xfail *-*-* } }

View File

@ -0,0 +1,26 @@
/* PR c++/94510 - nullptr_t implicitly cast to zero twice in std::array
{ dg-do compile } */
int ia1[2] = { (void*)0 }; // { dg-error "invalid conversion from 'void\\\*'" }
int ia2[2] = { (void*)0, 0 }; // { dg-error "invalid conversion from 'void\\\*'" }
int ia3[] = { (void*)0, 0 }; // { dg-error "invalid conversion from 'void\\\*'" }
int ia4[2] = { __null }; // { dg-warning "\\\[-Wconversion-null" }
int ia5[2] = { __null, 0 }; // { dg-warning "\\\[-Wconversion-null" }
int ia6[] = { __null, 0 }; // { dg-warning "\\\[-Wconversion-null" }
const char ca1[2] = { (char*)0, 0 }; // { dg-error "invalid conversion from 'char\\\*'" }
const char ca2[2] = { __null, 0 }; // { dg-warning "\\\[-Wconversion-null" }
typedef void Func ();
const char ca6[2] = { (Func*)0, 0 }; // { dg-error "invalid conversion from 'void \\\(\\\*\\\)\\\(\\\)' to 'char'" }
struct S;
typedef int S::*MemPtr;
typedef int (S::*MemFuncPtr)();
const char ca4[2] = { (MemPtr)0, 0 }; // { dg-error "cannot convert 'MemPtr' " }
const char ca5[2] = { (MemFuncPtr)0, 0 }; // { dg-error "cannot convert 'int \\\(S::\\\*\\\)\\\(\\\)' " }

View File

@ -0,0 +1,42 @@
/* PR c++/94510 - nullptr_t implicitly cast to zero twice in std::array
{ dg-do compile { target c++11 } } */
namespace std {
typedef __typeof__ (nullptr) nullptr_t;
}
int ia1[2] = { nullptr }; // { dg-error "cannot convert 'std::nullptr_t' to 'int'" }
int ia2[2] = { nullptr, 0 }; // { dg-error "cannot convert 'std::nullptr_t' to 'int'" }
int ia3[] = { nullptr, 0 }; // { dg-error "cannot convert 'std::nullptr_t' to 'int'" }
int ia4[2] = { (std::nullptr_t)0 }; // { dg-error "cannot convert 'std::nullptr_t' to 'int'" }
int ia5[2] = { (std::nullptr_t)0, 0 }; // { dg-error "cannot convert 'std::nullptr_t' to 'int'" }
int ia6[] = { (std::nullptr_t)0, 0 }; // { dg-error "cannot convert 'std::nullptr_t' to 'int'" }
const char ca1[2] = { nullptr, 0 }; // { dg-error "cannot convert 'std::nullptr_t' to 'const char'" }
const char ca2[2] = { (char*)nullptr, 0 };// { dg-error "invalid conversion from 'char\\\*' to 'char'" }
const char ca3[2] = { std::nullptr_t () };// { dg-error "cannot convert 'std::nullptr_t'" }
/* Verify that arrays of member pointers can be initialized by a literal
zero as well as nullptr. */
struct S { };
typedef int S::*MemPtr;
typedef int (S::*MemFuncPtr)();
MemPtr mp1[3] = { 0, nullptr, (MemPtr)0 };
MemPtr mp2[3] = { 0, std::nullptr_t (), MemPtr () };
MemPtr mp3[3] = { 0, (void*)0 }; // { dg-error "cannot convert 'void\\\*' to 'MemPtr' " }
MemPtr mp4[3] = { 0, (S*)0 }; // { dg-error "cannot convert 'S\\\*' to 'MemPtr' " }
MemPtr mp5[3] = { 0, S () }; // { dg-error "cannot convert 'S' to 'MemPtr' " }
MemFuncPtr mfp1[3] = { 0, nullptr, (MemFuncPtr)0 };
MemFuncPtr mfp2[3] = { 0, std::nullptr_t (), MemFuncPtr () };
MemFuncPtr mfp3[3] = { 0, (void*)0 }; // { dg-error "cannot convert 'void\\\*' to 'MemFuncPtr' " }
MemFuncPtr mfp4[3] = { 0, (S*)0 }; // { dg-error "cannot convert 'S\\\*' to 'MemFuncPtr' " }
MemFuncPtr mfp5[3] = { 0, S () }; // { dg-error "cannot convert 'S' to 'MemFuncPtr' " }