diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index e7733d0517e..640e4948130 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,18 @@ +2020-04-22 Martin Sebor + Jason Merrill + + 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 PR c++/67825 diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 0b62a775c1b..924c0b9c790 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -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); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 1447b89e692..c8c2f080763 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -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 *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; } diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c index 9e39cfd8dba..090fb529a98 100644 --- a/gcc/cp/mangle.c +++ b/gcc/cp/mangle.c @@ -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 *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) diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 0fc5b24841f..7bf249cee5c 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -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 *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 (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 diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 092a2fab356..090c565c093 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -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. */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index be374fba120..921f81e9a88 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2020-04-22 Martin Sebor + + 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 PR c++/67825 diff --git a/gcc/testsuite/g++.dg/abi/mangle72.C b/gcc/testsuite/g++.dg/abi/mangle72.C index 656a0cae403..308865bd2c6 100644 --- a/gcc/testsuite/g++.dg/abi/mangle72.C +++ b/gcc/testsuite/g++.dg/abi/mangle72.C @@ -24,56 +24,50 @@ struct B { padm_t a[2]; }; template struct Y { }; void g__ (Y) { } -// { dg-final { scan-assembler "_Z3g__1YIXtl1BtlA2_M1AA2_iLS3_0EEEEE" } } +// { dg-final { scan-assembler "_Z3g__1YIXtl1BEEE" } } void g0_ (Y) { } -// { dg-final { scan-assembler "_Z3g0_1YIXtl1BtlA2_M1AA2_iLS3_0EEEEE" } } +// { dg-final { scan-assembler "_Z3g0_1YIXtl1BEEE" } } void g00 (Y) { } -// { dg-final { scan-assembler "_Z3g001YIXtl1BtlA2_M1AA2_iLS3_0EEEEE" } } +// { dg-final { scan-assembler "_Z3g001YIXtl1BEEE" } } void g0x (Y) { } -// 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) { } -// { 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 struct Z { }; void h___ (Z) { } -// { dg-final { scan-assembler "_Z4h___1ZIXtl1CtlA3_M1AA2_iLS3_0EEEEE" } } +// { dg-final { scan-assembler "_Z4h___1ZIXtl1CEEE" } } void h0__ (Z) { } -// { dg-final { scan-assembler "_Z4h0__1ZIXtl1CtlA3_M1AA2_iLS3_0EEEEE" } } +// { dg-final { scan-assembler "_Z4h0__1ZIXtl1CEEE" } } void h00_ (Z) { } -// { dg-final { scan-assembler "_Z4h00_1ZIXtl1CtlA3_M1AA2_iLS3_0EEEEE" } } +// { dg-final { scan-assembler "_Z4h00_1ZIXtl1CEEE" } } void h000 (Z) { } -// { dg-final { scan-assembler "_Z4h0001ZIXtl1CtlA3_M1AA2_iLS3_0EEEEE" } } +// { dg-final { scan-assembler "_Z4h0001ZIXtl1CEEE" } } void h00x (Z) { } -// 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) { } -// { dg-final { scan-assembler "_Z4h0x01ZIXtl1CtlA3_M1AA2_iLS3_0ELS3_0ELS3_0EEEEE" } } +// { dg-final { scan-assembler "_Z4h0x01ZIXtl1CtlA3_M1AA2_iLS3_0EadL_ZNS1_1aEEEEEE" } } void h0x_ (Z) { } -// { 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) { } -// 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) { } -// 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. diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class36.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class36.C new file mode 100644 index 00000000000..1c1e23c10a8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class36.C @@ -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 struct X { }; + +typedef X XB; +typedef X XB; +typedef X XB; +typedef X XB; +typedef X XB; +typedef X XB; +typedef X XB; +typedef X XB; +typedef X XB; +typedef X XB; + +typedef X XBp; +typedef X XBp; +typedef X XBp; +typedef X XBp; +typedef X XBp; +typedef X XBp; +typedef X XBp; +typedef X XBp; +typedef X XBp; +typedef X XBp; + +typedef X XBpp; +typedef X XBpp; +typedef X XBpp; +typedef X XBpp; +typedef X XBpp; +typedef X XBpp; +typedef X XBpp; +typedef X XBpp; +typedef X XBpp; +typedef X XBpp; + +typedef X XB0p; +typedef X XB0p; +typedef X XB0p; + +typedef X XB00p; +typedef X XB00p; +typedef X XB00p; +typedef X XB00p; +typedef X XB00p; +typedef X XB00p; +typedef X XB00p; +typedef X 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 XB; +typedef X XB; +typedef X XB; +typedef X XB; +typedef X XB; +typedef X XB; +typedef X XB; +typedef X XB; +typedef X XB; diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class37.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class37.C new file mode 100644 index 00000000000..5649fa2e6dc --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class37.C @@ -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 struct B { }; + +constexpr const char c0{ }; +constexpr const char c1{ 1 }; + +typedef B BA; +typedef B BA; +typedef B BA; +typedef B BA; +typedef B BA; +typedef B BA; +typedef B BA; +typedef B BA; +typedef B BA; +typedef B BA; +typedef B BA; +typedef B BA; +typedef B BA; + +typedef B BA1; +typedef B BA1; +typedef B BA1; +typedef B BA1; +typedef B BA1; +typedef B BA1; +typedef B BA1; +typedef B BA1; +typedef B BA1; +typedef B BA1; +typedef B BA1; +typedef B BA1; + +typedef B BA01; +typedef B BA01; +typedef B BA01; +typedef B BA01; +typedef B BA01; +typedef B BA01; +typedef B BA01; +typedef B BA01; +typedef B BA01; + + +struct C { int a[4]; }; +template struct D { }; + +constexpr const int i0{ }; + +typedef D DC; +typedef D DC; +typedef D DC; +typedef D DC; +typedef D DC; +typedef D DC; +typedef D DC; +typedef D DC; +typedef D DC; +typedef D DC; + + +constexpr const int i1{ 1 }; + +typedef D DC1; +typedef D DC1; +typedef D DC1; +typedef D DC1; +typedef D DC1; +typedef D DC1; + +typedef D DC01; +typedef D DC01; +typedef D DC01; +typedef D DC01; +typedef D DC01; +typedef D DC01; // { dg-bogus "conflicting declaration" "pr94567" { xfail *-*-* } } diff --git a/gcc/testsuite/g++.dg/init/array58.C b/gcc/testsuite/g++.dg/init/array58.C new file mode 100644 index 00000000000..70e86445c07 --- /dev/null +++ b/gcc/testsuite/g++.dg/init/array58.C @@ -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::\\\*\\\)\\\(\\\)' " } diff --git a/gcc/testsuite/g++.dg/init/array59.C b/gcc/testsuite/g++.dg/init/array59.C new file mode 100644 index 00000000000..e8680de9456 --- /dev/null +++ b/gcc/testsuite/g++.dg/init/array59.C @@ -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' " }