+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
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);
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);
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);
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);
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;
}
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
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
{
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)
}
+/* 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. */
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
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. */
return false;
}
\f
-/* 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;
-}
-\f
#if defined ENABLE_TREE_CHECKING && (GCC_VERSION >= 2007)
/* Complain that some language-specific thing hanging off a tree
node has been accessed improperly. */
+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
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.
--- /dev/null
+/* 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;
--- /dev/null
+/* 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 *-*-* } }
--- /dev/null
+/* 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::\\\*\\\)\\\(\\\)' " }
--- /dev/null
+/* 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' " }