diff mbox series

make handling of zero-length arrays in C++ pretty printer more robust (PR 97201)

Message ID 26ef4aff-92f2-2ec9-7074-c78413ab9777@gmail.com
State New
Headers show
Series make handling of zero-length arrays in C++ pretty printer more robust (PR 97201) | expand

Commit Message

Martin Sebor Sept. 25, 2020, 6:58 p.m. UTC
The C and C++ representations of zero-length arrays are different:
C uses a null upper bound of the type's domain while C++ uses
SIZE_MAX.  This makes the middle end logic more complicated (and
prone to mistakes) because it has to be prepared for both.  A recent
change to -Warray-bounds has the middle end create a zero-length
array to print in a warning message.  I forgot about this gotcha
and, as a result, when the warning triggers under these conditions
in C++, it causes an ICE in the C++ pretty printer that in turn
isn't prepared for the C form of the domain.

In my mind, the "right fix" is to make the representation the same
between the front ends, but I'm certain that such a change would
cause more problems before it solved them.  Another solution might
be to provide APIs for creating (and querying) arrays and have them
call language hooks in cases where the representation might differ.
But that would likely be quite intrusive as well.  So with that in
mind, for the time being, the attached patch just continues to deal
with the difference by teaching the C++ pretty printer to also
recognize the C form of the zero-length domain.

While testing the one line fix I noticed that -Warray-bounds (and
therefore, I assume also all other warnings that detect out of bounds
accesses to allocated objects) triggers only for the ordinary form of
operator new and not for the nothrow overload, for instance.  That's
because the ordinary form is recognized as a built-in which has
the alloc_size attribute attached to it.  But because the other forms
are neither built-in nor declared in <new> with the same attribute,
the warning doesn't trigger.  So the patch also adds the attribute
to the declarations of these overloads in <new>.  In addition, it
adds attribute malloc to a couple of overloads of the operator that
it's missing from.

Tested on x86_64-linux.

Martin

Comments

Martin Sebor Oct. 5, 2020, 4:36 p.m. UTC | #1
Ping: https://gcc.gnu.org/pipermail/gcc-patches/2020-September/554893.html

On 9/25/20 12:58 PM, Martin Sebor wrote:
> The C and C++ representations of zero-length arrays are different:
> C uses a null upper bound of the type's domain while C++ uses
> SIZE_MAX.  This makes the middle end logic more complicated (and
> prone to mistakes) because it has to be prepared for both.  A recent
> change to -Warray-bounds has the middle end create a zero-length
> array to print in a warning message.  I forgot about this gotcha
> and, as a result, when the warning triggers under these conditions
> in C++, it causes an ICE in the C++ pretty printer that in turn
> isn't prepared for the C form of the domain.
> 
> In my mind, the "right fix" is to make the representation the same
> between the front ends, but I'm certain that such a change would
> cause more problems before it solved them.  Another solution might
> be to provide APIs for creating (and querying) arrays and have them
> call language hooks in cases where the representation might differ.
> But that would likely be quite intrusive as well.  So with that in
> mind, for the time being, the attached patch just continues to deal
> with the difference by teaching the C++ pretty printer to also
> recognize the C form of the zero-length domain.
> 
> While testing the one line fix I noticed that -Warray-bounds (and
> therefore, I assume also all other warnings that detect out of bounds
> accesses to allocated objects) triggers only for the ordinary form of
> operator new and not for the nothrow overload, for instance.  That's
> because the ordinary form is recognized as a built-in which has
> the alloc_size attribute attached to it.  But because the other forms
> are neither built-in nor declared in <new> with the same attribute,
> the warning doesn't trigger.  So the patch also adds the attribute
> to the declarations of these overloads in <new>.  In addition, it
> adds attribute malloc to a couple of overloads of the operator that
> it's missing from.
> 
> Tested on x86_64-linux.
> 
> Martin
Jason Merrill Oct. 6, 2020, 9:30 p.m. UTC | #2
On 9/25/20 2:58 PM, Martin Sebor wrote:
> The C and C++ representations of zero-length arrays are different:
> C uses a null upper bound of the type's domain while C++ uses
> SIZE_MAX.  This makes the middle end logic more complicated (and
> prone to mistakes) because it has to be prepared for both.  A recent
> change to -Warray-bounds has the middle end create a zero-length
> array to print in a warning message.  I forgot about this gotcha
> and, as a result, when the warning triggers under these conditions
> in C++, it causes an ICE in the C++ pretty printer that in turn
> isn't prepared for the C form of the domain.
> 
> In my mind, the "right fix" is to make the representation the same
> between the front ends, but I'm certain that such a change would
> cause more problems before it solved them. > Another solution might
> be to provide APIs for creating (and querying) arrays and have them
> call language hooks in cases where the representation might differ.
> But that would likely be quite intrusive as well.  So with that in
> mind, for the time being, the attached patch just continues to deal
> with the difference by teaching the C++ pretty printer to also
> recognize the C form of the zero-length domain.
> 
> While testing the one line fix I noticed that -Warray-bounds (and
> therefore, I assume also all other warnings that detect out of bounds
> accesses to allocated objects) triggers only for the ordinary form of
> operator new and not for the nothrow overload, for instance.  That's
> because the ordinary form is recognized as a built-in which has
> the alloc_size attribute attached to it.  But because the other forms
> are neither built-in nor declared in <new> with the same attribute,
> the warning doesn't trigger.  So the patch also adds the attribute
> to the declarations of these overloads in <new>.  In addition, it
> adds attribute malloc to a couple of overloads of the operator that
> it's missing from.

OK, thanks.

Jason
diff mbox series

Patch

PR c++/97201 - ICE in -Warray-bounds writing to result of operator new(0)

gcc/cp/ChangeLog:

	PR c++/97201
	* error.c (dump_type_suffix): Handle both the C and C++ forms of
	zero-length arrays.

libstdc++-v3/ChangeLog:

	PR c++/97201
	* libsupc++/new (operator new): Add attribute alloc_size and malloc.

gcc/testsuite/ChangeLog:

	PR c++/97201
	* g++.dg/warn/Warray-bounds-10.C: New test.
	* g++.dg/warn/Warray-bounds-11.C: New test.
	* g++.dg/warn/Warray-bounds-12.C: New test.
	* g++.dg/warn/Warray-bounds-13.C: New test.


diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index ecb41e82d8c..11ed3aedc8d 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -951,8 +951,11 @@  dump_type_suffix (cxx_pretty_printer *pp, tree t, int flags)
       if (tree dtype = TYPE_DOMAIN (t))
 	{
 	  tree max = TYPE_MAX_VALUE (dtype);
-	  /* Zero-length arrays have an upper bound of SIZE_MAX.  */
-	  if (integer_all_onesp (max))
+	  /* Zero-length arrays have a null upper bound in C and SIZE_MAX
+	     in C++.  Handle both since the type might be constructed by
+	     the middle end and end up here as a result of a warning (see
+	     PR c++/97201).  */
+	  if (!max || integer_all_onesp (max))
 	    pp_character (pp, '0');
 	  else if (tree_fits_shwi_p (max))
 	    pp_wide_integer (pp, tree_to_shwi (max) + 1);
diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-10.C b/gcc/testsuite/g++.dg/warn/Warray-bounds-10.C
new file mode 100644
index 00000000000..22466977b68
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-10.C
@@ -0,0 +1,64 @@ 
+/* PR c++/97201 - ICE in -Warray-bounds writing to result of operator new(0)
+   Verify that out-of-bounds accesses to memory returned by default operator
+   new() are diagnosed.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Warray-bounds -ftrack-macro-expansion=0" } */
+
+typedef __INT32_TYPE__ int32_t;
+
+void sink (void*);
+
+#define OP_NEW(n)  operator new (n)
+#define T(T, n, i) do {				\
+    T *p = (T*) OP_NEW (n);			\
+    p[i] = 0;					\
+    sink (p);					\
+  } while (0)
+
+void warn_op_new ()
+{
+  T (int32_t, 0, 0);          // { dg-warning "array subscript 0 is outside array bounds of 'int32_t \\\[0]'" }
+                              // { dg-message "referencing an object of size \\d allocated by 'void\\\* operator new\\\(\(long \)?unsigned int\\\)'" "note" { target *-*-* } .-1 }
+  T (int32_t, 1, 0);          // { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[1]'" }
+  T (int32_t, 2, 0);         //  { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[2]'" }
+  T (int32_t, 3, 0);         // { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[3]'" }
+
+  T (int32_t, 4, 0);
+
+  T (int32_t, 0, 1);          // { dg-warning "array subscript 1 is outside array bounds of 'int32_t \\\[0]'" }
+  T (int32_t, 1, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 2, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 3, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 4, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 5, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[5]" }
+  T (int32_t, 6, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[6]" }
+  T (int32_t, 7, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[7]" }
+
+  T (int32_t, 8, 1);
+}
+
+
+void warn_op_array_new ()
+{
+#undef OP_NEW
+#define OP_NEW(n)  operator new[] (n)
+
+  T (int32_t, 0, 0);          // { dg-warning "array subscript 0 is outside array bounds of 'int32_t \\\[0]'" }
+                              // { dg-message "referencing an object of size \\d allocated by 'void\\\* operator new \\\[]\\\(\(long \)?unsigned int\\\)'" "note" { target *-*-* } .-1 }
+  T (int32_t, 1, 0);          // { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[1]'" }
+  T (int32_t, 2, 0);         //  { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[2]'" }
+  T (int32_t, 3, 0);         // { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[3]'" }
+
+  T (int32_t, 4, 0);
+
+  T (int32_t, 0, 1);          // { dg-warning "array subscript 1 is outside array bounds of 'int32_t \\\[0]'" }
+  T (int32_t, 1, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 2, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 3, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 4, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 5, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[5]" }
+  T (int32_t, 6, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[6]" }
+  T (int32_t, 7, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[7]" }
+
+  T (int32_t, 8, 1);
+}
diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-11.C b/gcc/testsuite/g++.dg/warn/Warray-bounds-11.C
new file mode 100644
index 00000000000..9875e29085d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-11.C
@@ -0,0 +1,66 @@ 
+/* PR c++/97201 - ICE in -Warray-bounds writing to result of operator new(0)
+   Verify that out-of-bounds accesses to memory returned by nothrow operator
+   new() are diagnosed.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Warray-bounds -ftrack-macro-expansion=0" } */
+
+#include <new>
+
+typedef __INT32_TYPE__ int32_t;
+
+void sink (void*);
+
+#define OP_NEW(n)  operator new (n, std::nothrow)
+#define T(T, n, i) do {				\
+    T *p = (T*) OP_NEW (n);			\
+    p[i] = 0;					\
+    sink (p);					\
+  } while (0)
+
+void warn_op_new ()
+{
+  T (int32_t, 0, 0);          // { dg-warning "array subscript 0 is outside array bounds of 'int32_t \\\[0]'" }
+                              // { dg-message "referencing an object of size \\d allocated by 'void\\\* operator new\\\(std::size_t, const std::nothrow_t.\\\)'" "note" { target *-*-* } .-1 }
+  T (int32_t, 1, 0);          // { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[1]'" }
+  T (int32_t, 2, 0);         //  { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[2]'" }
+  T (int32_t, 3, 0);         // { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[3]'" }
+
+  T (int32_t, 4, 0);
+
+  T (int32_t, 0, 1);          // { dg-warning "array subscript 1 is outside array bounds of 'int32_t \\\[0]'" }
+  T (int32_t, 1, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 2, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 3, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 4, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 5, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[5]" }
+  T (int32_t, 6, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[6]" }
+  T (int32_t, 7, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[7]" }
+
+  T (int32_t, 8, 1);
+}
+
+
+void warn_op_array_new ()
+{
+#undef OP_NEW
+#define OP_NEW(n)  operator new[] (n, std::nothrow)
+
+  T (int32_t, 0, 0);          // { dg-warning "array subscript 0 is outside array bounds of 'int32_t \\\[0]'" }
+                              // { dg-message "referencing an object of size \\d allocated by 'void\\\* operator new \\\[]\\\(std::size_t, const std::nothrow_t&\\\)'" "note" { target *-*-* } .-1 }
+  T (int32_t, 1, 0);          // { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[1]'" }
+  T (int32_t, 2, 0);         //  { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[2]'" }
+  T (int32_t, 3, 0);         // { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[3]'" }
+
+  T (int32_t, 4, 0);
+
+  T (int32_t, 0, 1);          // { dg-warning "array subscript 1 is outside array bounds of 'int32_t \\\[0]'" }
+  T (int32_t, 1, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 2, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 3, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 4, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 5, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[5]" }
+  T (int32_t, 6, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[6]" }
+  T (int32_t, 7, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[7]" }
+
+  T (int32_t, 8, 1);
+}
diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-12.C b/gcc/testsuite/g++.dg/warn/Warray-bounds-12.C
new file mode 100644
index 00000000000..9e8b6048944
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-12.C
@@ -0,0 +1,66 @@ 
+/* PR c++/97201 - ICE in -Warray-bounds writing to result of operator new(0)
+   Verify that out-of-bounds accesses to memory returned by the new expression
+   are diagnosed.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Warray-bounds -ftrack-macro-expansion=0" } */
+
+typedef __INT32_TYPE__ int32_t;
+
+template <int N> struct S { char a[N]; };
+
+void sink (void*);
+
+#define NEW(n)  new S<n>
+#define T(T, n, i) do {				\
+    T *p = (T*)NEW (n);				\
+    p[i] = 0;					\
+    sink (p);					\
+  } while (0)
+
+void warn_new ()
+{
+  T (int32_t, 0, 0);          // { dg-warning "array subscript 0 is outside array bounds of 'int32_t \\\[0]'" }
+                              // { dg-message "referencing an object of size \\d allocated by 'void\\\* operator new\\\(\(long \)?unsigned int\\\)'" "note" { target *-*-* } .-1 }
+  T (int32_t, 1, 0);          // { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[1]'" }
+  T (int32_t, 2, 0);         //  { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[2]'" }
+  T (int32_t, 3, 0);         // { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[3]'" }
+
+  T (int32_t, 4, 0);
+
+  T (int32_t, 0, 1);          // { dg-warning "array subscript 1 is outside array bounds of 'int32_t \\\[0]'" }
+  T (int32_t, 1, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 2, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 3, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 4, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 5, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[5]" }
+  T (int32_t, 6, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[6]" }
+  T (int32_t, 7, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[7]" }
+
+  T (int32_t, 8, 1);
+}
+
+
+void warn_array_new ()
+{
+#undef NEW
+#define NEW(n)  new char [n]
+
+  T (int32_t, 0, 0);          // { dg-warning "array subscript 0 is outside array bounds of 'int32_t \\\[0]'" }
+                              // { dg-message "referencing an object of size \\d allocated by 'void\\\* operator new \\\[]\\\(\(long \)?unsigned int\\\)'" "note" { target *-*-* } .-1 }
+  T (int32_t, 1, 0);          // { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[1]'" }
+  T (int32_t, 2, 0);         //  { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[2]'" }
+  T (int32_t, 3, 0);         // { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[3]'" }
+
+  T (int32_t, 4, 0);
+
+  T (int32_t, 0, 1);          // { dg-warning "array subscript 1 is outside array bounds of 'int32_t \\\[0]'" }
+  T (int32_t, 1, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 2, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 3, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 4, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 5, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[5]" }
+  T (int32_t, 6, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[6]" }
+  T (int32_t, 7, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[7]" }
+
+  T (int32_t, 8, 1);
+}
diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-13.C b/gcc/testsuite/g++.dg/warn/Warray-bounds-13.C
new file mode 100644
index 00000000000..42fb809de3c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-13.C
@@ -0,0 +1,70 @@ 
+/* PR c++/97201 - ICE in -Warray-bounds writing to result of operator new(0)
+   Verify that out-of-bounds accesses to memory returned by the nothrow form
+   of the new expression are diagnosed.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Warray-bounds -ftrack-macro-expansion=0" } */
+
+#include <new>
+
+typedef __INT32_TYPE__ int32_t;
+
+void sink (void*);
+
+template <int N> struct S { char a[N]; };
+
+void sink (void*);
+
+#define NEW(n)  new (std::nothrow) S<n>
+#define T(T, n, i) do {				\
+    T *p = (T*)NEW (n);				\
+    p[i] = 0;					\
+    sink (p);					\
+  } while (0)
+
+void warn_nothrow_new ()
+{
+  T (int32_t, 0, 0);          // { dg-warning "array subscript 0 is outside array bounds of 'int32_t \\\[0]'" }
+                              // { dg-message "referencing an object of size \\d allocated by 'void\\\* operator new\\\(std::size_t, const std::nothrow_t.\\\)'" "note" { target *-*-* } .-1 }
+  T (int32_t, 1, 0);          // { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[1]'" }
+  T (int32_t, 2, 0);         //  { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[2]'" }
+  T (int32_t, 3, 0);         // { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[3]'" }
+
+  T (int32_t, 4, 0);
+
+  T (int32_t, 0, 1);          // { dg-warning "array subscript 1 is outside array bounds of 'int32_t \\\[0]'" }
+  T (int32_t, 1, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 2, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 3, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 4, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 5, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[5]" }
+  T (int32_t, 6, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[6]" }
+  T (int32_t, 7, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[7]" }
+
+  T (int32_t, 8, 1);
+}
+
+
+void warn_nothrow_array_new ()
+{
+#undef NEW
+#define NEW(n)  new (std::nothrow) char [n]
+
+  T (int32_t, 0, 0);          // { dg-warning "array subscript 0 is outside array bounds of 'int32_t \\\[0]'" }
+                              // { dg-message "referencing an object of size \\d allocated by 'void\\\* operator new \\\[]\\\(std::size_t, const std::nothrow_t&\\\)'" "note" { target *-*-* } .-1 }
+  T (int32_t, 1, 0);          // { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[1]'" }
+  T (int32_t, 2, 0);         //  { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[2]'" }
+  T (int32_t, 3, 0);         // { dg-warning "array subscript 'int32_t {aka int}\\\[0]' is partly outside array bounds of 'unsigned char \\\[3]'" }
+
+  T (int32_t, 4, 0);
+
+  T (int32_t, 0, 1);          // { dg-warning "array subscript 1 is outside array bounds of 'int32_t \\\[0]'" }
+  T (int32_t, 1, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 2, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 3, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 4, 1);          // { dg-warning "array subscript 1 is outside array bounds " }
+  T (int32_t, 5, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[5]" }
+  T (int32_t, 6, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[6]" }
+  T (int32_t, 7, 1);          // { dg-warning "array subscript 'int32_t {aka int}\\\[1]' is partly outside array bounds of 'unsigned char \\\[7]" }
+
+  T (int32_t, 8, 1);
+}
diff --git a/libstdc++-v3/libsupc++/new b/libstdc++-v3/libsupc++/new
index 21848a573d1..15edf8ad6a9 100644
--- a/libstdc++-v3/libsupc++/new
+++ b/libstdc++-v3/libsupc++/new
@@ -138,26 +138,26 @@  void operator delete[](void*, std::size_t) _GLIBCXX_USE_NOEXCEPT
   __attribute__((__externally_visible__));
 #endif
 _GLIBCXX_NODISCARD void* operator new(std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
-  __attribute__((__externally_visible__, __malloc__));
+  __attribute__((__externally_visible__, __alloc_size__ (1), __malloc__));
 _GLIBCXX_NODISCARD void* operator new[](std::size_t, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
-  __attribute__((__externally_visible__, __malloc__));
+  __attribute__((__externally_visible__, __alloc_size__ (1), __malloc__));
 void operator delete(void*, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
   __attribute__((__externally_visible__));
 void operator delete[](void*, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
   __attribute__((__externally_visible__));
 #if __cpp_aligned_new
 _GLIBCXX_NODISCARD void* operator new(std::size_t, std::align_val_t)
-  __attribute__((__externally_visible__));
+  __attribute__((__externally_visible__, __alloc_size__ (1), __malloc__));
 _GLIBCXX_NODISCARD void* operator new(std::size_t, std::align_val_t, const std::nothrow_t&)
-  _GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__, __malloc__));
+  _GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__, __alloc_size__ (1), __malloc__));
 void operator delete(void*, std::align_val_t)
   _GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__));
 void operator delete(void*, std::align_val_t, const std::nothrow_t&)
   _GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__));
 _GLIBCXX_NODISCARD void* operator new[](std::size_t, std::align_val_t)
-  __attribute__((__externally_visible__));
+  __attribute__((__externally_visible__, __alloc_size__ (1), __malloc__));
 _GLIBCXX_NODISCARD void* operator new[](std::size_t, std::align_val_t, const std::nothrow_t&)
-  _GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__, __malloc__));
+  _GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__, __alloc_size__ (1), __malloc__));
 void operator delete[](void*, std::align_val_t)
   _GLIBCXX_USE_NOEXCEPT __attribute__((__externally_visible__));
 void operator delete[](void*, std::align_val_t, const std::nothrow_t&)