On 4/7/20 1:50 PM, Marek Polacek wrote:
On Tue, Apr 07, 2020 at 12:50:48PM -0600, Martin Sebor via Gcc-patches wrote:
Among the numerous regressions introduced by the change committed
to GCC 9 to allow string literals as template arguments is a failure
to recognize the C++ nullptr and GCC's __null constants as pointers.
For one, I didn't realize that nullptr, being a null pointer constant,
doesn't have a pointer type, and two, I didn't think of __null (which
is a special integer constant that NULL sometimes expands to).

The attached patch adjusts the special handling of trailing zero
initializers in reshape_init_array_1 to recognize both kinds of
constants and avoid treating them as zeros of the array integer
element type.  This restores the expected diagnostics when either
constant is used in the initializer list.

Martin

PR c++/94510 - nullptr_t implicitly cast to zero twice in std::array

gcc/cp/ChangeLog:

        PR c++/94510
        * decl.c (reshape_init_array_1): Exclude mismatches with all kinds
        of pointers.

gcc/testsuite/ChangeLog:

        PR c++/94510
        * g++.dg/init/array57.C: New test.
        * g++.dg/init/array58.C: New test.

diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index a127734af69..692c8ed73f4 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -6041,9 +6041,14 @@ reshape_init_array_1 (tree elt_type, tree max_index, 
reshape_iter *d,
        TREE_CONSTANT (new_init) = false;
/* Pointers initialized to strings must be treated as non-zero
-        even if the string is empty.  */
+        even if the string is empty.  Handle all kinds of pointers,
+        including std::nullptr and GCC's __nullptr, neither of which
+        has a pointer type.  */
        tree init_type = TREE_TYPE (elt_init);
-      if (POINTER_TYPE_P (elt_type) != POINTER_TYPE_P (init_type)
+      bool init_is_ptr = (POINTER_TYPE_P (init_type)
+                         || NULLPTR_TYPE_P (init_type)
+                         || null_node_p (elt_init));
+      if (POINTER_TYPE_P (elt_type) != init_is_ptr
          || !type_initializer_zero_p (elt_type, elt_init))
        last_nonzero = index;

It looks like this still won't handle e.g. pointers to member functions,
e.g.

struct S { };
int arr[3] = { (void (S::*) ()) 0, 0, 0 };

would still be accepted.  You could use TYPE_PTR_OR_PTRMEM_P instead of
POINTER_TYPE_P to catch this case.

Good catch!  That doesn't fail because unlike null data member pointers
which are represented as -1, member function pointers are represented
as a zero.

I had looked for an API that would answer the question: "is this
expression a pointer?" without having to think of all the different
kinds of them but all I could find was null_node_p().  Is this a rare,
isolated case that having an API like that wouldn't be worth having
or should I add one like in the attached update?

Martin
PR c++/94510 - nullptr_t implicitly cast to zero twice in std::array

gcc/cp/ChangeLog:

	PR c++/94510
	* decl.c (reshape_init_array_1): Exclude mismatches with all kinds
	of pointers.
	* gcc/cp/cp-tree.h (null_pointer_constant_p): New function.

gcc/testsuite/ChangeLog:

	PR c++/94510
	* g++.dg/init/array57.C: New test.
	* g++.dg/init/array58.C: New test.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 63aaf615926..9ec6e3883c8 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -8022,6 +8022,22 @@ null_node_p (const_tree expr)
   return expr == null_node;
 }
 
+/* Returns true if EXPR is a null pointer constant of any type.  */
+
+inline bool
+null_pointer_constant_p (tree expr)
+{
+  STRIP_ANY_LOCATION_WRAPPER (expr);
+  if (expr == null_node)
+    return true;
+  tree type = TREE_TYPE (expr);
+  if (NULLPTR_TYPE_P (type))
+    return true;
+  if (POINTER_TYPE_P (type))
+    return integer_zerop (expr);
+  return null_member_pointer_value_p (expr);
+}
+
 /* True iff T is a variable template declaration. */
 inline bool
 variable_template_p (tree t)
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index a127734af69..5cf5b601d29 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -6041,9 +6041,13 @@ reshape_init_array_1 (tree elt_type, tree max_index, reshape_iter *d,
 	TREE_CONSTANT (new_init) = false;
 
       /* Pointers initialized to strings must be treated as non-zero
-	 even if the string is empty.  */
+	 even if the string is empty.  Handle all kinds of pointers,
+	 including std::nullptr and GCC's __nullptr, neither of which
+	 has a pointer type.  */
       tree init_type = TREE_TYPE (elt_init);
-      if (POINTER_TYPE_P (elt_type) != POINTER_TYPE_P (init_type)
+      bool init_is_ptr = (POINTER_TYPE_P (init_type)
+			  || null_pointer_constant_p (elt_init));
+      if (POINTER_TYPE_P (elt_type) != init_is_ptr
 	  || !type_initializer_zero_p (elt_type, elt_init))
 	last_nonzero = index;
 
diff --git a/gcc/testsuite/g++.dg/init/array57.C b/gcc/testsuite/g++.dg/init/array57.C
new file mode 100644
index 00000000000..70e86445c07
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/array57.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/array58.C b/gcc/testsuite/g++.dg/init/array58.C
new file mode 100644
index 00000000000..655e08fa600
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/array58.C
@@ -0,0 +1,21 @@
+/* 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'" }

Reply via email to