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; diff --git a/gcc/testsuite/g++.dg/init/array57.C b/gcc/testsuite/g++.dg/init/array57.C new file mode 100644 index 00000000000..fdd7e76eb18 --- /dev/null +++ b/gcc/testsuite/g++.dg/init/array57.C @@ -0,0 +1,15 @@ +/* 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" } 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'" }