From: Martin Sebor Date: Wed, 22 Apr 2020 06:27:54 +0000 (-0400) Subject: c++: reject scalar array initialization with nullptr [PR94510] X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=587970215f4681def390e2a791aa3ba6adb65158;p=gcc.git 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 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. gcc/testsuite/ChangeLog 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. --- 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' " }