On 4/9/20 3:24 PM, Martin Sebor wrote:
On 4/9/20 1:03 PM, Jason Merrill wrote:
On 4/8/20 1:23 PM, Martin Sebor wrote:
On 4/7/20 3:36 PM, Marek Polacek wrote:
On Tue, Apr 07, 2020 at 02:46:52PM -0600, Martin Sebor wrote:
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.
(Drop the gcc/cp/.)
+/* 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);
+}
+
We already have a null_ptr_cst_p so it would be sort of confusing to
have
this as well. But are you really interested in whether it's a null
pointer,
not just a pointer?
The goal of the code is to detect a mismatch in "pointerness" between
an initializer expression and the type of the initialized element, so
it needs to know if the expression is a pointer (non-nulls pointers
are detected in type_initializer_zero_p). That means testing a number
of IMO unintuitive conditions:
TYPE_PTR_OR_PTRMEM_P (TREE_TYPE (expr))
|| NULLPTR_TYPE_P (TREE_TYPE (expr))
|| null_node_p (expr)
I don't know if this type of a query is common in the C++ FE but unless
this is an isolated use case then besides fixing the bug I thought it
would be nice to make it easier to get the test above right, or at least
come close to it.
Since null_pointer_constant_p already exists (but isn't suitable here
because it returns true for plain literal zeros)
Why is that unsuitable? A literal zero is a perfectly good
zero-initializer for a pointer.
Right, that's why it's not suitable here. Because a literal zero
is also not a pointer.
The question the code asks is: "is the initializer expression
a pointer (of any kind)?"
Why is that a question we want to ask? What we need here is to know
whether the initializer expression is equivalent to implicit
zero-initialization. For initializing a pointer, a literal 0 is
equivalent, so we don't want to update last_nonzero.
Also, why is the pointer check here rather than part of the
POINTER_TYPE_P handling in type_initializer_zero_p?
and I thought that might be common enough
to justify adding a helper function for. If it isn't then leaving
it open-coded as it is in the updated patch below is fine with me.
--- 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 = (TYPE_PTR_OR_PTRMEM_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;
Martin