c++: reject scalar array initialization with nullptr [PR94510]
authorMartin Sebor <msebor@gmail.com>
Wed, 22 Apr 2020 06:27:54 +0000 (02:27 -0400)
committerJason Merrill <jason@redhat.com>
Wed, 22 Apr 2020 06:27:54 +0000 (02:27 -0400)
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.

12 files changed:
gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/mangle.c
gcc/cp/pt.c
gcc/cp/tree.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/abi/mangle72.C
gcc/testsuite/g++.dg/cpp2a/nontype-class36.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/nontype-class37.C [new file with mode: 0644]
gcc/testsuite/g++.dg/init/array58.C [new file with mode: 0644]
gcc/testsuite/g++.dg/init/array59.C [new file with mode: 0644]

index e7733d0517ef478633a7319470a5dc72f9f69d85..640e4948130ec42493946c75f215c424a9e237e8 100644 (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
index 0b62a775c1bf0dfc524b2682f5642a43f1cf772e..924c0b9c79076711b0a8fb45db9eb67f9d0fd8d8 100644 (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);
index 1447b89e69286851c5b5c127e0745227feee322e..c8c2f080763de2f12c37a0696e0ba4792f90a5c8 100644 (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;
 }
 
index 9e39cfd8dbad6c69f80c4f180de10b54d8a2a764..090fb529a98507ce24f8d1b02de456f3dcc4b8d8 100644 (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)
index 0fc5b24841f0b6779011b93e1717d52b0114981a..7bf249cee5ce494ef863e3f38706d46f728c3eb5 100644 (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
index 092a2fab35679f17aae03aacc7a53edfbfb7cf45..090c565c093436f0d462c0fb7f4b9226f36ecca4 100644 (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;
 }
 \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.  */
index be374fba120b8b1e7641d953181cfad728693b9d..921f81e9a8872d26693fde80b45d4c8f918ae350 100644 (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
index 656a0cae403f72ec9034217c1c28340527034559..308865bd2c6f55712432a0b8cdf3c98e6d2a9f4f 100644 (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.
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class36.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class36.C
new file mode 100644 (file)
index 0000000..1c1e23c
--- /dev/null
@@ -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;
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class37.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class37.C
new file mode 100644 (file)
index 0000000..5649fa2
--- /dev/null
@@ -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 *-*-* } }
diff --git a/gcc/testsuite/g++.dg/init/array58.C b/gcc/testsuite/g++.dg/init/array58.C
new file mode 100644 (file)
index 0000000..70e8644
--- /dev/null
@@ -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 (file)
index 0000000..e8680de
--- /dev/null
@@ -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' " }