PR middle-end/91679 - missing -Warray-bounds accessing a member array in a local...
authorMartin Sebor <msebor@redhat.com>
Fri, 1 Nov 2019 21:09:20 +0000 (21:09 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Fri, 1 Nov 2019 21:09:20 +0000 (15:09 -0600)
PR middle-end/91679 - missing -Warray-bounds accessing a member array in a local buffer
PR middle-end/91647 - new FAILs for Warray-bounds-8 and Wstringop-overflow-3.C
PR middle-end/91463 - missing -Warray-bounds accessing past the end of a statically initialized flexible array member
PR middle-end/92312 - bogus -Wstringop-overflow storing into a trailing array backed by larger buffer

gcc/ChangeLog:

PR middle-end/91679
PR middle-end/91647
PR middle-end/91463
PR middle-end/92312
* c-family/c-pretty-print.c (direct_abstract_declarator): Print
bound in zero-length arrays.
* gcc/c-family/c.opt (-Wzero-length-bounds): New option.
* gcc/doc/invoke.texi (-Wzero-length-bounds): Document.
* gimple-match-head.c (try_conditional_simplification): Use memcpy
instead of a hand-rolled loop to avoid PR 92323.
* tree-vrp.c (vrp_prop::check_array_ref): Handle trailing arrays
with initializers.
(vrp_prop::check_mem_ref): Handle declared struct objects.
* tree.c (last_field): New function.
(array_at_struct_end_p): Handle MEM_REF.
(get_initializer_for): New helper.
(component_ref_size): Add argument.  Rename locals.  Call
get_initializer_for instead of fold_ctor_reference.  Correct handling
of flexible array members.
* wide-int.h (generic_wide_int <storage>::sign_mask): Assert invariant.

gcc/testsuite/ChangeLog:

PR middle-end/91679
PR middle-end/91647
PR middle-end/91463
PR middle-end/92312
* c-c++-common/Warray-bounds-2.c: Disable VRP.  Adjust expected messages.
* g++.dg/warn/Warray-bounds-8.C: Remove xfails.
* gcc.dg/Warray-bounds-48.c: New test.
* gcc.dg/Warray-bounds-49.c: New test.
* gcc.dg/Wstringop-overflow-16.c: Adjust text of expected messages.
* gcc.dg/Wstringop-overflow-21.c: New test.
* gcc.dg/Wzero-length-array-bounds.c: New test.
* gcc.dg/pr36902.c: Remove xfail.
* gcc.dg/strlenopt-57.c: Add an expected warning.

From-SVN: r277728

20 files changed:
gcc/ChangeLog
gcc/c-family/c-pretty-print.c
gcc/c-family/c.opt
gcc/doc/invoke.texi
gcc/gimple-match-head.c
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/Warray-bounds-2.c
gcc/testsuite/g++.dg/warn/Warray-bounds-8.C
gcc/testsuite/gcc.dg/Warray-bounds-46.c
gcc/testsuite/gcc.dg/Warray-bounds-48.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Warray-bounds-49.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-16.c
gcc/testsuite/gcc.dg/Wstringop-overflow-21.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wzero-length-array-bounds.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/pr36902.c
gcc/testsuite/gcc.dg/strlenopt-57.c
gcc/tree-vrp.c
gcc/tree.c
gcc/tree.h
gcc/wide-int.h

index bb7819e1fea85db00af16c3d5222c2395ad87b48..796f5e9ec9110c5ceb6347a60e2d1f8917080d51 100644 (file)
@@ -1,3 +1,26 @@
+2019-11-01  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/91679
+       PR middle-end/91647
+       PR middle-end/91463
+       PR middle-end/92312
+       * c-family/c-pretty-print.c (direct_abstract_declarator): Print
+       bound in zero-length arrays.
+       * gcc/c-family/c.opt (-Wzero-length-bounds): New option.
+       * gcc/doc/invoke.texi (-Wzero-length-bounds): Document.
+       * gimple-match-head.c (try_conditional_simplification): Use memcpy
+       instead of a hand-rolled loop to avoid PR 92323.
+       * tree-vrp.c (vrp_prop::check_array_ref): Handle trailing arrays
+       with initializers.
+       (vrp_prop::check_mem_ref): Handle declared struct objects.
+       * tree.c (last_field): New function.
+       (array_at_struct_end_p): Handle MEM_REF.
+       (get_initializer_for): New helper.
+       (component_ref_size): Add argument.  Rename locals.  Call
+       get_initializer_for instead of fold_ctor_reference.  Correct handling
+       of flexible array members.
+       * wide-int.h (generic_wide_int <storage>::sign_mask): Assert invariant.
+
 2019-11-01  Kewen Lin  <linkw@gcc.gnu.org>
 
        * config/rs6000/rs6000-modes.def (V2SF, V2SI): New modes.
index 1b06cc20f680c65c1034090e9ac3041f7a902cc5..bc7354559ba58d4740175b4c04fb6ca296a0e729 100644 (file)
@@ -581,16 +581,20 @@ c_pretty_printer::direct_abstract_declarator (tree t)
 
     case ARRAY_TYPE:
       pp_c_left_bracket (this);
-      if (TYPE_DOMAIN (t) && TYPE_MAX_VALUE (TYPE_DOMAIN (t)))
+      if (tree dom = TYPE_DOMAIN (t))
        {
-         tree maxval = TYPE_MAX_VALUE (TYPE_DOMAIN (t));
-         tree type = TREE_TYPE (maxval);
+         if (tree maxval = TYPE_MAX_VALUE (dom))
+           {
+             tree type = TREE_TYPE (maxval);
 
-         if (tree_fits_shwi_p (maxval))
-           pp_wide_integer (this, tree_to_shwi (maxval) + 1);
+             if (tree_fits_shwi_p (maxval))
+               pp_wide_integer (this, tree_to_shwi (maxval) + 1);
+             else
+               expression (fold_build2 (PLUS_EXPR, type, maxval,
+                                        build_int_cst (type, 1)));
+           }
          else
-           expression (fold_build2 (PLUS_EXPR, type, maxval,
-                                     build_int_cst (type, 1)));
+           pp_string (this, "0");
        }
       pp_c_right_bracket (this);
       direct_abstract_declarator (TREE_TYPE (t));
index 495eb16a58a2bbe3c257cea84435de8d23a00bd4..bb6eeaf1523bcd2385a0efc627c7d3173c565afb 100644 (file)
@@ -338,6 +338,10 @@ Warray-bounds=
 LangEnabledBy(C ObjC C++ LTO ObjC++,Wall,1,0)
 ; in common.opt
 
+Wzero-length-bounds
+C ObjC C++ ObjC++ Var(warn_zero_length_bounds) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
+Warn about accesses to interior zero-length array members.
+
 Wassign-intercept
 ObjC ObjC++ Var(warn_assign_intercept) Warning
 Warn whenever an Objective-C assignment is being intercepted by the garbage collector.
index d1eb317f43cd31156c80ef7dd910024c50f33deb..faa7fa95a0eb02d897423a0d4d43be66e2dc8969 100644 (file)
@@ -325,6 +325,7 @@ Objective-C and Objective-C++ Dialects}.
 -Winaccessible-base @gol
 -Winit-self  -Winline  -Wno-int-conversion  -Wint-in-bool-context @gol
 -Wno-int-to-pointer-cast  -Winvalid-memory-model  -Wno-invalid-offsetof @gol
+-Wzero-length-bounds @gol
 -Winvalid-pch  -Wlarger-than=@var{byte-size} @gol
 -Wlogical-op  -Wlogical-not-parentheses  -Wlong-long @gol
 -Wmain  -Wmaybe-uninitialized  -Wmemset-elt-size  -Wmemset-transposed-args @gol
@@ -4438,6 +4439,7 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}.
 -Wimplicit-int @r{(C and Objective-C only)} @gol
 -Wimplicit-function-declaration @r{(C and Objective-C only)} @gol
 -Winit-self @r{(only for C++)} @gol
+-Wzero-length-bounds @gol
 -Wlogical-not-parentheses @gol
 -Wmain @r{(only for C/ObjC and unless} @option{-ffreestanding}@r{)}  @gol
 -Wmaybe-uninitialized @gol
@@ -6330,6 +6332,33 @@ conversions.  This warning is about implicit conversions; for explicit
 conversions the warnings @option{-Wno-int-to-pointer-cast} and
 @option{-Wno-pointer-to-int-cast} may be used.
 
+@item -Wzero-length-bounds
+@opindex Wzero-length-bounds
+@opindex Wzero-length-bounds
+Warn about accesses to elements of zero-length array members that might
+overlap other members of the same object.  Declaring interior zero-length
+arrays is discouraged because accesses to them are undefined.  See
+@xref{Zero Length}.
+
+For example, the first two stores in function @code{bad} are diagnosed
+because the array elements overlap the subsequent members @code{b} and
+@code{c}.  The third store is diagnosed by @option{-Warray-bounds}
+because it is beyond the bounds of the enclosing object.
+
+@smallexample
+struct X @{ int a[0]; int b, c; @};
+struct X x;
+
+void bad (void)
+@{
+  x.a[0] = 0;   // -Wzero-length-bounds
+  x.a[1] = 1;   // -Wzero-length-bounds
+  x.a[2] = 2;   // -Warray-bounds
+@}
+@end smallexample
+
+Option @option{-Wzero-length-bounds} is enabled by @option{-Warray-bounds}.
+
 @item -Wno-div-by-zero
 @opindex Wno-div-by-zero
 @opindex Wdiv-by-zero
index 53278168a59f5ac10ce6760f04fd42589a0792e7..d7c74a1865ae4afc8c2da7a3d562a41b8242ccd8 100644 (file)
@@ -837,8 +837,8 @@ try_conditional_simplification (internal_fn ifn, gimple_match_op *res_op,
   gimple_match_op cond_op (gimple_match_cond (res_op->ops[0],
                                              res_op->ops[num_ops - 1]),
                           op, res_op->type, num_ops - 2);
-  for (unsigned int i = 1; i < num_ops - 1; ++i)
-    cond_op.ops[i - 1] = res_op->ops[i];
+
+  memcpy (cond_op.ops, res_op->ops + 1, (num_ops - 1) * sizeof *cond_op.ops);
   switch (num_ops - 2)
     {
     case 2:
index 8b03bd9913e5f3a8eff7a9032a49ac163f984c28..f4e18db61117bd4b35f83753eb168ef6798ee3e2 100644 (file)
@@ -1,3 +1,19 @@
+2019-11-01  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/91679
+       PR middle-end/91647
+       PR middle-end/91463
+       PR middle-end/92312
+       * c-c++-common/Warray-bounds-2.c: Disable VRP.  Adjust expected messages.
+       * g++.dg/warn/Warray-bounds-8.C: Remove xfails.
+       * gcc.dg/Warray-bounds-48.c: New test.
+       * gcc.dg/Warray-bounds-49.c: New test.
+       * gcc.dg/Wstringop-overflow-16.c: Adjust text of expected messages.
+       * gcc.dg/Wstringop-overflow-21.c: New test.
+       * gcc.dg/Wzero-length-array-bounds.c: New test.
+       * gcc.dg/pr36902.c: Remove xfail.
+       * gcc.dg/strlenopt-57.c: Add an expected warning.
+
 2019-11-01  Steven G. Kargl  <kargl@gcc.gnu.org>
 
        * gfortran.dg/byte_3.f: New test.
index ca2d1c98218ed1a5ac42ba220d90f3b1e308ed5e..7f925c34baeb6d0b58a4969d7e6c2b4121da8baa 100644 (file)
@@ -6,7 +6,7 @@
    source of the excessive array bound is in a different function than
    the call.
    { dg-do compile }
-   { dg-options "-O2 -Warray-bounds -Wno-stringop-overflow" } */
+   { dg-options "-O2 -Warray-bounds -Wno-stringop-overflow -fno-tree-vrp" } */
 
 #if __has_include (<stddef.h>)
 #  include <stddef.h>
@@ -216,13 +216,13 @@ void call_strncpy_dst_diff_max (const char *s, size_t n)
 static void
 wrap_strncpy_dstarray_diff_neg (char *d, const char *s, ptrdiff_t i, size_t n)
 {
-  strncpy (d + i, s, n);   /* { dg-bogus "offset -\[0-9\]+ is out of the bounds \\\[0, 90] of object .ar10. with type .(struct )?Array ?\\\[2]." "strncpy" } */
-}                         /* { dg-warning "array subscript -1 is outside array bounds" "" { target *-*-* } .-1 } */
+  strncpy (d + i, s, n);   /* { dg-warning "offset -\[0-9\]+ is out of the bounds \\\[0, 90] of object .ar10. with type .(struct )?Array ?\\\[2]." "strncpy" } */
+}
 
 void call_strncpy_dstarray_diff_neg (const char *s, size_t n)
 {
-  struct Array ar10[2];    /* { dg-bogus ".ar10. declared here" } */
-  sink (&ar10);                   /* { dg-message "while referencing" "" { target *-*-* } .-1 } */
+  struct Array ar10[2];    /* { dg-message ".ar10. declared here" } */
+  sink (&ar10);
 
   int off = (char*)ar10[1].a17 - (char*)ar10 + 1;
   wrap_strncpy_dstarray_diff_neg (ar10[1].a17, s, -off, n);
index 69226fa632a12ed345438fc688ee23d12928f7ca..6db20135668defc3027a69d432a28e527d7b930c 100644 (file)
@@ -13,7 +13,7 @@ void sink (void*);
 struct Ax
 {
   char n;
-  char a[];                     // { dg-message "while referencing .Ax::a." "pr91463" { xfail *-*-* } }
+  char a[];                     // { dg-message "while referencing .Ax::a." }
 };
 
 // Verify warning for a definition with no initializer.
@@ -21,9 +21,9 @@ Ax ax_;
 
 void gax_ ()
 {
-  ax_.a[0] = 0;                 // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } }
-  ax_.a[1] = 0;                 // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } }
-  ax_.a[2] = 0;                 // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } }
+  ax_.a[0] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+  ax_.a[1] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+  ax_.a[2] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
 }
 
 // Verify warning for access to a definition with an initializer that doesn't
@@ -32,9 +32,9 @@ Ax ax0 = { 0 };
 
 void gax0 ()
 {
-  ax0.a[0] = 0;                 // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } }
-  ax0.a[1] = 0;                 // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } }
-  ax0.a[2] = 0;                 // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } }
+  ax0.a[0] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+  ax0.a[1] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+  ax0.a[2] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
 }
 
 // Verify warning for access to a definition with an initializer that
@@ -43,9 +43,9 @@ Ax ax0_ = { 0, { } };
 
 void gax0_ ()
 {
-  ax0_.a[0] = 0;                // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } }
-  ax0_.a[1] = 0;                // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } }
-  ax0_.a[2] = 0;                // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } }
+  ax0_.a[0] = 0;                // { dg-warning "\\\[-Warray-bounds" }
+  ax0_.a[1] = 0;                // { dg-warning "\\\[-Warray-bounds" }
+  ax0_.a[2] = 0;                // { dg-warning "\\\[-Warray-bounds" }
 }
 
 // Verify warning for out-of-bounds accesses to a definition with
@@ -55,8 +55,8 @@ Ax ax1 = { 1, { 0 } };
 void gax1 ()
 {
   ax1.a[0] = 0;
-  ax1.a[1] = 0;                 // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } }
-  ax1.a[2] = 0;                 // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } }
+  ax1.a[1] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+  ax1.a[2] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
 }
 
 Ax ax2 = { 2, { 1, 0 } };
@@ -65,7 +65,7 @@ void gax2 ()
 {
   ax2.a[0] = 0;
   ax2.a[1] = 0;
-  ax2.a[2] = 0;                 // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } }
+  ax2.a[2] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
 }
 
 
@@ -308,14 +308,14 @@ void ga1ix ()
 struct Bx
 {
   char n;
-  char a[];                     // { dg-message "while referencing .Bx::a." "pr91463" { xfail *-*-* } }
+  char a[];                     // { dg-message "while referencing .Bx::a." }
 
   // Verify the warning for a constant.
-  Bx () { a[0] = 0; }           // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } }
+  Bx () { a[0] = 0; }           // { dg-warning "\\\[-Warray-bounds" }
 
   // And also for a non-constant.  Regardless of the subscript, the array
   // of the object in function gxi() below has a zero size.
-  Bx (int i) { a[i] = 0; }      // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } }
+  Bx (int i) { a[i] = 0; }      // { dg-warning "\\\[-Warray-bounds" }
 };
 
 void gbx (void)
index 09b577e63637cc3afd4900c8e17750ea5ad8f003..4980f93a4707466bf4d4e3e78adfd1214e67e961 100644 (file)
@@ -67,7 +67,7 @@ void strcpy_global (void)
 
   SA (__builtin_offsetof (struct MA17, ax) == 157);
 
-  T (gma.ax, 0);          // { dg-warning "'strcpy' offset 157 is out of the bounds \\\[0, 157] of object 'gma' with type 'struct MA17'" }
+  T (gma.ax, 0);          // { dg-warning "'strcpy' offset 157 from the object at 'gma' is out of the bounds of referenced subobject 'ax' with type 'char[]' at offset 157|'strcpy' offset 157 is out of the bounds \\\[0, 157] of object 'gma' with type 'struct MA17'" }
 }
 
 
@@ -92,16 +92,16 @@ void strcpy_global_array (void)
   T (gma2[0].a17, 16);
   T (gma2[0].a17, 17);    // { dg-warning "'strcpy' offset 157 from the object at 'gma2' is out of the bounds of referenced subobject 'a17' with type 'char\\\[17]' at offset 140" }
 
-  /* GMA2 is external buts because it's an array its definition in another
+  /* GMA2 is external but because it's an array its definition in another
      translation unit may not provide an initializer for the flexible array
      member.  Verify that a warning is issued for access to it.  */
-  T (gma2[0].ax, 1);      // { dg-warning "'strcpy' offset \\\[157, 158] from the object at 'gma2' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 157" }
-  T (gma2[0].ax, 7);      // { dg-warning "'strcpy' offset \\\[157, 164] from the object at 'gma2' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 157" }
+  T (gma2[0].ax, 1);      // { dg-warning "'strcpy' offset \\\[157, 158] from the object at 'gma2' is out of the bounds of referenced subobject 'ax' with type 'char\\\[0]' at offset 157" }
+  T (gma2[0].ax, 7);      // { dg-warning "'strcpy' offset \\\[157, 164] from the object at 'gma2' is out of the bounds of referenced subobject 'ax' with type 'char\\\[0]' at offset 157" }
 
   /* IGMA_ is internal and provides on definition for the flexible array
      member.  Verify that a warnin is issued for out-of-bounds accesses
      to it.  */
-  T (igma2_[0].ax, 1);    // { dg-warning "'strcpy' offset \\\[157, 158] from the object at 'igma2_' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 157" }
+  T (igma2_[0].ax, 1);    // { dg-warning "'strcpy' offset \\\[157, 158] from the object at 'igma2_' is out of the bounds of referenced subobject 'ax' with type 'char\\\[0]' at offset 157" }
 
   T (igma_3.ax, 0);
   T (igma_3.ax, 1);
@@ -134,7 +134,7 @@ void strcpy_local (void)
   T (lma.a17, 16);
   T (lma.a17, 17);        // { dg-warning "'strcpy' offset 157 from the object at 'lma' is out of the bounds of referenced subobject 'a17' with type 'char\\\[17]' at offset 140" }
 
-  T (lma.ax, 0);          // { dg-warning "'strcpy' offset 157 from the object at 'lma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 157" }
+  T (lma.ax, 0);          // { dg-warning "'strcpy' offset 157 from the object at 'lma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[0]' at offset 157" }
 }
 
 
@@ -191,11 +191,11 @@ void strcpy_ref (struct MA17 *pma)
      array.  The warning assumes that PMA doesn't point to the last element
      of the array which could in theory have nonzero elements without
      overlapping other objects.  */
-  T (pma[1].ax, 0);       // { dg-warning "'strcpy' offset 314 from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 314" }
-  T ((pma + 1)->ax, 1);   // { dg-warning "'strcpy' offset \\\[314, 315] from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 314" }
-  T ((pma + 1)[1].ax, 2); // { dg-warning "'strcpy' offset \\\[471, 473] from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 471" }
-  T ((*(pma + 2)).ax, 2); // { dg-warning "'strcpy' offset \\\[471, 473] from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 471" }
-  T (pma[3].ax, 9);       // { dg-warning "'strcpy' offset \\\[628, 637] from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[]' at offset 628" }
+  T (pma[1].ax, 0);       // { dg-warning "'strcpy' offset 314 from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[0]' at offset 314" }
+  T ((pma + 1)->ax, 1);   // { dg-warning "'strcpy' offset \\\[314, 315] from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[0]' at offset 314" }
+  T ((pma + 1)[1].ax, 2); // { dg-warning "'strcpy' offset \\\[471, 473] from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[0]' at offset 471" }
+  T ((*(pma + 2)).ax, 2); // { dg-warning "'strcpy' offset \\\[471, 473] from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[0]' at offset 471" }
+  T (pma[3].ax, 9);       // { dg-warning "'strcpy' offset \\\[628, 637] from the object at 'pma' is out of the bounds of referenced subobject 'ax' with type 'char\\\[0]' at offset 628" }
 
   T (pma[-1].a1, 0);
   T (pma[-1].a1, 1);      // { dg-warning "'strcpy' offset -152 from the object at 'pma' is out of the bounds of referenced subobject 'a1' with type 'char\\\[1]' at offset -153" }
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-48.c b/gcc/testsuite/gcc.dg/Warray-bounds-48.c
new file mode 100644 (file)
index 0000000..d6a327e
--- /dev/null
@@ -0,0 +1,363 @@
+/* PR middle-end/91647 - missing -Warray-bounds accessing a zero-length array
+   of a declared object
+   { dg-do "compile" }
+   { dg-options "-O2 -Wall" } */
+
+typedef __INT16_TYPE__ int16_t;
+typedef __INT32_TYPE__ int32_t;
+
+void sink (void*);
+
+/* Exercise a true flexible member.  */
+
+struct AX
+{
+  int32_t n;
+  int16_t ax[];     // { dg-message "while referencing 'ax'" "member" }
+};
+
+static void warn_ax_local (struct AX *p)
+{
+  p->ax[0] = 0;     // { dg-warning "\\\[-Warray-bounds" }
+  p->ax[1] = 1;     // { dg-warning "\\\[-Warray-bounds" }
+}
+
+static void nowarn_ax_extern (struct AX *p)
+{
+  p->ax[0] = 0; p->ax[99] = 99; p->ax[999] = 999; p->ax[9999] = 9999;
+}
+
+static void warn_ax_local_buf (struct AX *p)
+{
+  p->ax[0] = 4; p->ax[1] = 5;
+
+  p->ax[2] = 6;     // { dg-warning "\\\[-Warray-bounds" }
+  p->ax[3] = 7;     // { dg-warning "\\\[-Warray-bounds" }
+  p->ax[4] = 8;     // { dg-warning "\\\[-Warray-bounds" }
+}
+
+static void warn_ax_extern_buf (struct AX *p)
+{
+  p->ax[0] = 9; p->ax[1] = 10; p->ax[2] = 11;
+
+  p->ax[3] = 12;    // { dg-warning "\\\[-Warray-bounds" }
+  p->ax[4] = 13;    // { dg-warning "\\\[-Warray-bounds" }
+  p->ax[5] = 14;    // { dg-warning "\\\[-Warray-bounds" }
+}
+
+static void nowarn_ax_extern_bufx (struct AX *p)
+{
+  p->ax[0] = 0; p->ax[99] = 99; p->ax[999] = 999; p->ax[9999] = 9999;
+}
+
+static void nowarn_ax_ref (struct AX *p)
+{
+  p->ax[0] = 0; p->ax[99] = 99; p->ax[999] = 999; p->ax[9999] = 9999;
+}
+
+void test_ax (struct AX *p, unsigned n)
+{
+  {
+    struct AX sax;  // { dg-message "defined here" "struct definition" }
+    warn_ax_local (&sax);
+    sink (&sax);
+  }
+
+  {
+    extern
+      struct AX xsax;
+    nowarn_ax_extern (&xsax);
+    sink (&xsax);
+  }
+
+  {
+    /* Verify out-of-bounds access to the local BUF is diagnosed.  */
+    char ax_buf_p2[sizeof (struct AX) + 2 * sizeof (int16_t)];
+    warn_ax_local_buf ((struct AX*) ax_buf_p2);
+    sink (ax_buf_p2);
+  }
+
+  {
+    /* Verify out-of-bounds access to the extern BUF with a known
+       bound is diagnosed.  */
+    extern char ax_buf_p3[sizeof (struct AX) + 3 * sizeof (int16_t)];
+    warn_ax_extern_buf ((struct AX*) ax_buf_p3);
+    sink (ax_buf_p3);
+  }
+
+  {
+    /* Verify that accesses to BUFX with an unknown bound are not
+       diagnosed.  */
+    extern char bufx[];
+    nowarn_ax_extern_bufx ((struct AX*) bufx);
+    sink (bufx);
+  }
+
+  {
+    /* Verify that accesses to BUFN with a runtime bound are not
+       diagnosed.  */
+    char bufn[n];
+    nowarn_ax_extern_bufx ((struct AX*) bufn);
+    sink (bufn);
+  }
+
+  nowarn_ax_ref (p);
+}
+
+
+/* Exercise a zero-length trailing member array.  It's the same as above
+   except that extern declarations with no definitions are considered to
+   have zero elements (they can't be initialized to have any).  */
+
+struct A0
+{
+  int32_t n;
+  int16_t a0[0];    // { dg-message "while referencing 'a0'" "member" }
+};
+
+static void warn_a0_local (struct A0 *p)
+{
+  p->a0[0] = 0;     // { dg-warning "\\\[-Warray-bounds" }
+  p->a0[1] = 1;     // { dg-warning "\\\[-Warray-bounds" }
+}
+
+static void warn_a0_extern (struct A0 *p)
+{
+  p->a0[0] = 2;     // { dg-warning "\\\[-Warray-bounds" }
+  p->a0[1] = 3;     // { dg-warning "\\\[-Warray-bounds" }
+}
+
+static void warn_a0_local_buf (struct A0 *p)
+{
+  p->a0[0] = 4; p->a0[1] = 5;
+
+  p->a0[2] = 6;     // { dg-warning "\\\[-Warray-bounds" }
+  p->a0[3] = 7;     // { dg-warning "\\\[-Warray-bounds" }
+  p->a0[4] = 8;     // { dg-warning "\\\[-Warray-bounds" }
+}
+
+static void warn_a0_extern_buf (struct A0 *p)
+{
+  p->a0[0] = 9; p->a0[1] = 10; p->a0[2] = 11;
+
+  p->a0[3] = 12;    // { dg-warning "\\\[-Warray-bounds" }
+  p->a0[4] = 13;    // { dg-warning "\\\[-Warray-bounds" }
+  p->a0[5] = 14;    // { dg-warning "\\\[-Warray-bounds" }
+}
+
+static void nowarn_a0_extern_bufx (struct A0 *p)
+{
+  p->a0[0] = 0; p->a0[99] = 99; p->a0[999] = 999; p->a0[9999] = 9999;
+}
+
+static void nowarn_a0_ref (struct A0 *p)
+{
+  p->a0[0] = 0; p->a0[99] = 99; p->a0[999] = 999; p->a0[9999] = 9999;
+}
+
+void test_a0 (struct A0 *p, unsigned n)
+{
+  {
+    struct A0 sa0;  // { dg-message "defined here" "struct definition" }
+    warn_a0_local (&sa0);
+    sink (&sa0);
+  }
+
+  {
+    extern
+      struct A0 xsa0;  // { dg-message "defined here" "struct definition" }
+    warn_a0_extern (&xsa0);
+    sink (&xsa0);
+  }
+
+  {
+    /* Verify out-of-bounds access to the local BUF is diagnosed.  */
+    char a0_buf_p2[sizeof (struct A0) + 2 * sizeof (int16_t)];
+    warn_a0_local_buf ((struct A0*) a0_buf_p2);
+    sink (a0_buf_p2);
+  }
+
+  {
+    /* Verify out-of-bounds access to the extern BUF with a known
+       bound is diagnosed.  */
+    extern char a0_buf_p3[sizeof (struct A0) + 3 * sizeof (int16_t)];
+    warn_a0_extern_buf ((struct A0*) a0_buf_p3);
+    sink (a0_buf_p3);
+  }
+
+  {
+    /* Verify that accesses to BUFX with an unknown bound are not
+       diagnosed.  */
+    extern char bufx[];
+    nowarn_a0_extern_bufx ((struct A0*) bufx);
+    sink (bufx);
+  }
+
+  {
+    /* Verify that accesses to BUFN with a runtime bound are not
+       diagnosed.  */
+    char bufn[n];
+    nowarn_a0_extern_bufx ((struct A0*) bufn);
+    sink (bufn);
+  }
+
+  nowarn_a0_ref (p);
+}
+
+
+/* Exercise a one-element trailing member array.  It's the same as above
+   except that it has exactly one element.  */
+
+struct A1
+{
+  int32_t n;
+  int16_t a1[1];    // { dg-message "while referencing 'a1'" }
+};
+
+static void warn_a1_local_noinit (struct A1 *p)
+{
+  p->a1[0] = 0;
+  p->a1[1] = 1;     // { dg-warning "\\\[-Warray-bounds" }
+  p->a1[2] = 2;     // { dg-warning "\\\[-Warray-bounds" }
+}
+
+static void warn_a1_extern (struct A1 *p)
+{
+  p->a1[0] = 0;
+  p->a1[1] = 1;     // { dg-warning "\\\[-Warray-bounds" }
+  p->a1[2] = 2;     // { dg-warning "\\\[-Warray-bounds" }
+}
+
+static void warn_a1_init (struct A1 *p)
+{
+  p->a1[0] = 0;
+  p->a1[1] = 1;     // { dg-warning "\\\[-Warray-bounds" }
+  p->a1[2] = 2;     // { dg-warning "\\\[-Warray-bounds" }
+}
+
+static void warn_a1_local_buf (struct A1 *p)
+{
+  p->a1[0] = 0; p->a1[1] = 1; p->a1[2] = 2; p->a1[3] = 3;
+
+  p->a1[4] = 4;     // { dg-warning "\\\[-Warray-bounds" }
+}
+
+static void warn_a1_extern_buf (struct A1 *p)
+{
+  p->a1[0] = 0; p->a1[1] = 1; p->a1[2] = 2; p->a1[3] = 3; p->a1[4] = 4;
+
+  p->a1[5] = 5;     // { dg-warning "\\\[-Warray-bounds" }
+}
+
+static void nowarn_a1_extern_bufx (struct A1 *p)
+{
+  p->a1[0] = 0; p->a1[99] = 99; p->a1[999] = 999; p->a1[9999] = 9999;
+}
+
+static void nowarn_a1_ref (struct A1 *p)
+{
+  p->a1[0] = 0; p->a1[99] = 99; p->a1[999] = 999; p->a1[9999] = 9999;
+}
+
+void test_a1 (struct A1 *p, unsigned n)
+{
+  {
+    struct A1 a1;
+    warn_a1_local_noinit (&a1);
+    sink (&a1);
+  }
+
+  {
+    extern struct A1 a1x;
+    warn_a1_extern (&a1x);
+    sink (&a1x);
+}
+  {
+    struct A1 a1 = { 0, { 1 } };
+    warn_a1_init (&a1);
+    sink (&a1);
+  }
+
+  {
+    /* Verify out-of-bounds access to the local BUF is diagnosed.  */
+    char buf_p2[sizeof (struct A1) + 2 * sizeof (int16_t)];
+    warn_a1_local_buf ((struct A1*) buf_p2);
+    sink (buf_p2);
+  }
+
+  {
+    /* Verify out-of-bounds access to the extern BUF with a known
+       bound is diagnosed.  */
+    extern char a1_buf_p3[sizeof (struct A1) + 3 * sizeof (int16_t)];
+    warn_a1_extern_buf ((struct A1*) a1_buf_p3);
+    sink (a1_buf_p3);
+  }
+
+  {
+    /* Verify that accesses to BUFX with an unknown bound are not
+       diagnosed.  */
+    extern char bufx[];
+    nowarn_a1_extern_bufx ((struct A1*) bufx);
+    sink (bufx);
+  }
+
+  {
+    /* Verify that accesses to BUFN with a runtime bound are not
+       diagnosed.  */
+    char bufn[n];
+    nowarn_a1_extern_bufx ((struct A1*) bufn);
+    sink (bufn);
+  }
+
+  nowarn_a1_ref (p);
+}
+
+
+/* Exercise a two-element trailing member array.  It's treated
+   the same as an interior array member.  */
+
+struct A2
+{
+  int32_t n;
+  int16_t a2[2];    // { dg-message "while referencing 'a2'" }
+};
+
+static void warn_a2_noinit (struct A2 *p)
+{
+  p->a2[0] = 0; p->a2[1] = 1;
+
+  p->a2[2] = 2;     // { dg-warning "\\\[-Warray-bounds" }
+}
+
+static void warn_a2_init (struct A2 *p)
+{
+  p->a2[0] = 0; p->a2[1] = 1;
+
+  p->a2[2] = 2;     // { dg-warning "\\\[-Warray-bounds" }
+  p->a2[9] = 9;     // { dg-warning "\\\[-Warray-bounds" }
+}
+
+static void warn_a2_ref (struct A2 *p)
+{
+  p->a2[0] = 0; p->a2[1] = 1;
+
+  p->a2[2] = 2;     // { dg-warning "\\\[-Warray-bounds" }
+  p->a2[9] = 9;     // { dg-warning "\\\[-Warray-bounds" }
+}
+
+void test_a2 (struct A2 *p)
+{
+  {
+    struct A2 a2;
+    warn_a2_noinit (&a2);
+    sink (&a2);
+  }
+
+  {
+    struct A2 a2 = { 0, { 1, 2 } };
+    warn_a2_init (&a2);
+    sink (&a2);
+  }
+
+  warn_a2_ref (p);
+}
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-49.c b/gcc/testsuite/gcc.dg/Warray-bounds-49.c
new file mode 100644 (file)
index 0000000..7a847ac
--- /dev/null
@@ -0,0 +1,115 @@
+/* PR middle-end/91647 - missing -Warray-bounds accessing a zero-length array
+   of a declared object
+   { dg-do "compile" }
+   { dg-options "-O2 -Wall" } */
+
+struct __attribute__ ((aligned (16))) A16
+{
+  __INT64_TYPE__ i8;
+  __INT16_TYPE__ i2;
+  __INT16_TYPE__ a2[];
+};
+
+struct A16 a0 = { };
+
+void test_a0 (void)
+{
+  // The first three elements fit in the tail padding.
+  a0.a2[0] = 0; a0.a2[1] = 1; a0.a2[2] = 2;
+
+  a0.a2[3] = 3;     // { dg-warning "array subscript 3 is above array bounds of 'short int\\\[0]'" }
+}
+
+
+struct A16 a1 = { .a2 = { 1 } };
+
+void test_a1 (void)
+{
+  a1.a2[0] = 0; a1.a2[1] = 1; a1.a2[2] = 2;
+
+  a1.a2[3] = 3;     // { dg-warning "array subscript 3 is above array bounds of 'short int\\\[0]'" }
+}
+
+
+struct A16 a2 = { .a2 = { 1, 2 } };
+
+void test_a2 (void)
+{
+  a2.a2[0] = 0; a2.a2[1] = 1; a2.a2[2] = 2;
+
+  a2.a2[3] = 3;     // { dg-warning "array subscript 3 is above array bounds of 'short int\\\[0]'" }
+}
+
+
+struct A16 a3 = { .a2 = { 1, 2, 3 } };
+
+void test_a3 (void)
+{
+  a3.a2[0] = 0; a3.a2[1] = 1; a3.a2[2] = 2;
+
+  a3.a2[3] = 3;     // { dg-warning "array subscript 3 is above array bounds of 'short int\\\[0]'" }
+}
+
+
+struct A16 a4 = { .a2 = { 1, 2, 3, 4 } };
+
+void test_a4 (void)
+{
+  a4.a2[0] = 0; a4.a2[1] = 1; a4.a2[2] = 2; a4.a2[3] = 3;
+
+  a4.a2[4] = 4;     // { dg-warning "array subscript 4 is above array bounds of 'short int\\\[0]'" }
+}
+
+
+struct A16 a5 = { .a2 = { 1, 2, 3, 4, 5 } };
+
+void test_a5 (void)
+{
+  a5.a2[0] = 0; a5.a2[1] = 1; a5.a2[2] = 2; a5.a2[3] = 3; a5.a2[4] = 4;
+
+  a5.a2[5] = 5;     // { dg-warning "array subscript 5 is above array bounds of 'short int\\\[0]'" }
+}
+
+
+struct A16 a6 = { .a2 = { 1, 2, 3, 4, 5, 6 } };
+
+void test_a6 (void)
+{
+  a6.a2[0] = 0; a6.a2[1] = 1; a6.a2[2] = 2; a6.a2[3] = 3; a6.a2[4] = 4;
+  a6.a2[5] = 5;
+
+  a6.a2[6] = 6;     // { dg-warning "array subscript 6 is above array bounds of 'short int\\\[0]'" }
+}
+
+
+struct A16 a7 = { .a2 = { 1, 2, 3, 4, 5, 6, 7 } };
+
+void test_a7 (void)
+{
+  a7.a2[0] = 0; a7.a2[1] = 1; a7.a2[2] = 2; a7.a2[3] = 3; a7.a2[4] = 4;
+  a7.a2[5] = 5; a7.a2[5] = 5; a7.a2[6] = 6;
+
+  a7.a2[7] = 7;     // { dg-warning "array subscript 7 is above array bounds of 'short int\\\[0]'" }
+}
+
+
+struct A16 a8 = { .a2 = { 1, 2, 3, 4, 5, 6, 7, 8 } };
+
+void test_a8 (void)
+{
+  a8.a2[0] = 0; a8.a2[1] = 1; a8.a2[2] = 2; a8.a2[3] = 3; a8.a2[4] = 4;
+  a8.a2[5] = 5; a8.a2[5] = 5; a8.a2[6] = 6; a8.a2[7] = 7;
+
+  a8.a2[8] = 8;     // { dg-warning "array subscript 8 is above array bounds of 'short int\\\[0]'" }
+}
+
+
+struct A16 a9 = { .a2 = { 1, 2, 3, 4, 5, 6, 7, 8, 9 } };
+
+void test_a9 (void)
+{
+  a8.a2[0] = 8; a8.a2[1] = 7; a8.a2[2] = 6; a8.a2[3] = 5; a8.a2[4] = 4;
+  a8.a2[5] = 3; a8.a2[5] = 2; a8.a2[6] = 1; a8.a2[7] = 0;
+
+  a8.a2[9] = 8;     // { dg-warning "array subscript 9 is above array bounds of 'short int\\\[0]'" }
+}
index 74548a4f0fa35bc5c6bd18877c6b1ac610d83208..11fb05e730a799f58075e0031257df25ca994792 100644 (file)
@@ -3,7 +3,7 @@
    { dg-options "-O2 -Wall" } */
 
 struct charseq {
-  unsigned char bytes[0];         // { dg-message "object declared here" }
+  unsigned char bytes[0];         // { dg-message "while referencing|object declared here" }
 };
 
 struct locale_ctype_t {
@@ -15,7 +15,7 @@ void ctype_finish (struct locale_ctype_t *ctype)
   long unsigned int cnt;
   for (cnt = 0; cnt < 20; ++cnt) {
     static struct charseq replace[2];
-    replace[0].bytes[1] = '\0';   // { dg-warning "\\\[-Wstringop-overflow" }
+    replace[0].bytes[1] = '\0';   // { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" }
     ctype->mboutdigits[cnt] = &replace[0];
   }
 }
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-21.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-21.c
new file mode 100644 (file)
index 0000000..3a27460
--- /dev/null
@@ -0,0 +1,59 @@
+/* PR middle-end/92312 - bogus -Wstringop-overflow storing into a trailing
+   array backed by larger buffer
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-array-bounds" } */
+
+struct S0 { char a, b[0]; };
+
+void sink (void*);
+
+void test_memset_zero_length (void)
+{
+  char a[3];
+  struct S0 *p = (struct S0*)a;
+  p->a = 0;
+  __builtin_memset (p->b, 0, 2);
+  sink (p);
+
+  __builtin_memset (p->b, 0, 3);    // { dg-warning "\\\[-Wstringop-overflow" }
+  sink (p);
+}
+
+void test_store_zero_length (int i)
+{
+  char a[3];
+  struct S0 *p = (struct S0*)a;
+  p->a = 0;
+  p->b[0] = 0;
+  p->b[1] = 1;                      // { dg-bogus "\\\[-Wstringop-overflow" }
+  p->b[2] = 2;                      // { dg-warning "\\\[-Wstringop-overflow" }
+  p->b[i] = 2;
+  sink (p);
+}
+
+
+struct Sx { char a, b[]; };
+
+void test_memset_flexarray (int i)
+{
+  char a[3];
+  struct Sx *p = (struct Sx*)a;
+  p->a = 0;
+  __builtin_memset (p->b, 0, 2);
+  sink (p);
+
+  __builtin_memset (p->b, 0, 3);    // { dg-warning "\\\[-Wstringop-overflow" }
+  sink (p);
+}
+
+void test_store_flexarray (int i)
+{
+  char a[3];
+  struct Sx *p = (struct Sx*)a;
+  p->a = 0;
+  p->b[0] = 0;
+  p->b[1] = 1;                      // { dg-bogus "\\\[-Wstringop-overflow" }
+  p->b[2] = 1;                      // { dg-warning "\\\[-Wstringop-overflow" }
+  p->b[i] = 2;
+  sink (p);
+}
diff --git a/gcc/testsuite/gcc.dg/Wzero-length-array-bounds.c b/gcc/testsuite/gcc.dg/Wzero-length-array-bounds.c
new file mode 100644 (file)
index 0000000..8e880d9
--- /dev/null
@@ -0,0 +1,88 @@
+/* PR middle-end/91647 - missing -Warray-bounds accessing a zero-length array
+   of a declared object
+   Test to exercise -Wzero-length-bounds.
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+void sink (void*);
+
+struct X { int a[0]; int b, c; };
+
+extern struct X x;
+
+void bad (int i, int j)
+{
+  x.a[0] = 0;           // { dg-warning "\\\[-Wzero-length-bounds" }
+  x.a[1] = 1;           // { dg-warning "\\\[-Wzero-length-bounds" }
+  x.a[2] = 2;           // { dg-warning "\\\[-Warray-bounds" }
+
+  x.a[i] = 3;           // { dg-warning "\\\[-Wzero-length-bounds" }
+  x.a[j] = 4;           // { dg-warning "array subscript 'j' is outside the bounds of an interior zero-length array" }
+}
+
+void access_by_reference (struct X *p, int i)
+{
+  p->a[0] = 0;          // { dg-warning "\\\[-Wzero-length-bounds" }
+  p->a[1] = 0;          // { dg-warning "\\\[-Wzero-length-bounds" }
+  p->a[2] = 0;          // { dg-warning "\\\[-Wzero-length-bounds" }
+  p->a[i] = 0;          // { dg-warning "\\\[-Wzero-length-bounds" }
+}
+
+
+extern struct X a[2];
+
+void access_to_array (int i)
+{
+  a[0].a[0] = 0;        // { dg-warning "\\\[-Wzero-length-bounds" }
+  a[0].a[1] = 1;        // { dg-warning "\\\[-Wzero-length-bounds" }
+  /* Accesses to a subsequent element of the enclosing array seem like
+     a more sever problem than those to the next member of the same
+     struct and so might perhaps be better diagnosed by -Warray-bounds.
+     Then again, code that does this sort of crap might as well get what
+     it deserves if it disables -Wzero-length-bounds.  */
+  a[0].a[2] = 2;        // { dg-warning "\\\[-Wzero-length-bounds" }
+
+  a[0].a[i] = 3;        // { dg-warning "\\\[-Wzero-length-bounds" }
+  sink (a);
+
+  a[1].a[0] = 4;        // { dg-warning "\\\[-Wzero-length-bounds" }
+  a[1].a[1] = 5;        // { dg-warning "\\\[-Wzero-length-bounds" }
+  a[1].a[2] = 6;        // { dg-warning "\\\[-Warray-bounds" }
+
+  a[1].a[i] = 7;        // { dg-warning "\\\[-Wzero-length-bounds" }
+  sink (a);
+
+  a[i].a[0] = 8;        // { dg-warning "\\\[-Wzero-length-bounds" }
+  a[i].a[1] = 9;        // { dg-warning "\\\[-Wzero-length-bounds" }
+  a[i].a[2] = 0;        // { dg-warning "\\\[-Wzero-length-bounds" }
+}
+
+
+struct Y
+{
+  struct X a[2], b;
+  int c;
+};
+
+extern struct Y y;
+
+void access_to_member (int i)
+{
+  y.a[0].a[0] = 0;      // { dg-warning "\\\[-Wzero-length-bounds" }
+  y.a[0].a[1] = 0;      // { dg-warning "\\\[-Wzero-length-bounds" }
+  y.a[0].a[2] = 0;      // { dg-warning "\\\[-Wzero-length-bounds" }
+  sink (a);
+
+  y.a[1].a[0] = 0;      // { dg-warning "\\\[-Wzero-length-bounds" }
+  y.a[1].a[1] = 0;      // { dg-warning "\\\[-Wzero-length-bounds" }
+  /* Similar to the array case above, accesses to a subsequent member
+     of the "parent" struct seem like a more severe problem than those
+     to the next member of the same struct.  */
+  y.a[1].a[2] = 0;      // { dg-warning "\\\[-Wzero-length-bounds" }
+  sink (a);
+
+  y.b.a[0] = 0;         // { dg-warning "\\\[-Wzero-length-bounds" }
+  y.b.a[1] = 0;         // { dg-warning "\\\[-Wzero-length-bounds" }
+  y.b.a[2] = 0;         // { dg-warning "\\\[-Wzero-length-bounds" }
+  y.b.a[3] = 0;         // { dg-warning "\\\[-Warray-bounds" }
+}
index a065124ae71642946ce0a61ad84c465f884914bb..cc8650ccb3b0a7fcfe317116168060054c73388f 100644 (file)
@@ -44,7 +44,7 @@ foo2(unsigned char * to, const unsigned char * from, int n)
       *to = *from;
       break;
     case 5:
-      to[4] = from [4]; /* { dg-warning "array subscript is above array bounds" "" { xfail *-*-* } } */
+      to[4] = from [4]; /* { dg-warning "\\\[-Warray-bounds } */
       break;
     }
   return to;
index 49dc8cd6fbbfecf50a7d289714bd56f91672f6b8..b7212bcf7957da946b3ea6add25e8d9c50c72d86 100644 (file)
@@ -21,7 +21,7 @@ void test_var_flexarray_cst_off (void)
 {
   /* Use arbitrary constants greater than 16 in case GCC ever starts
      unrolling strlen() calls with small array arguments.  */
-  a[0] = 17 < strlen (a0.a + 1);
+  a[0] = 17 < strlen (a0.a + 1);        // { dg-warning "\\\[-Warray-bounds" }
   a[1] = 19 < strlen (a1.a + 1);
   a[2] = 23 < strlen (a9.a + 9);
   a[3] = 29 < strlen (ax.a + 3);
index ad9be74daf0a1918159d5eb8caf5f97fc1aa9b33..31258615247c8ed7a74745bab09095955ecd635a 100644 (file)
@@ -4122,7 +4122,6 @@ bool
 vrp_prop::check_array_ref (location_t location, tree ref,
                           bool ignore_off_by_one)
 {
-  const value_range *vr = NULL;
   tree low_sub, up_sub;
   tree low_bound, up_bound, up_bound_p1;
 
@@ -4132,6 +4131,9 @@ vrp_prop::check_array_ref (location_t location, tree ref,
   low_sub = up_sub = TREE_OPERAND (ref, 1);
   up_bound = array_ref_up_bound (ref);
 
+  /* Set for accesses to interior zero-length arrays.  */
+  bool interior_zero_len = false;
+
   if (!up_bound
       || TREE_CODE (up_bound) != INTEGER_CST
       || (warn_array_bounds < 2
@@ -4152,11 +4154,22 @@ vrp_prop::check_array_ref (location_t location, tree ref,
        }
       else
        {
-         tree maxbound = TYPE_MAX_VALUE (ptrdiff_type_node);
+         tree ptrdiff_max = TYPE_MAX_VALUE (ptrdiff_type_node);
+         tree maxbound = ptrdiff_max;
          tree arg = TREE_OPERAND (ref, 0);
          poly_int64 off;
 
-         if (get_addr_base_and_unit_offset (arg, &off) && known_gt (off, 0))
+         if (TREE_CODE (arg) == COMPONENT_REF)
+           {
+             /* Try to determine the size of the trailing array from
+                its initializer (if it has one).  */
+             if (tree refsize = component_ref_size (arg, &interior_zero_len))
+               maxbound = refsize;
+           }
+
+         if (maxbound == ptrdiff_max
+             && get_addr_base_and_unit_offset (arg, &off)
+             && known_gt (off, 0))
            maxbound = wide_int_to_tree (sizetype,
                                         wi::sub (wi::to_wide (maxbound),
                                                  off));
@@ -4185,6 +4198,7 @@ vrp_prop::check_array_ref (location_t location, tree ref,
                         "array subscript %E is above array bounds of %qT",
                         low_bound, artype);
 
+  const value_range *vr = NULL;
   if (TREE_CODE (low_sub) == SSA_NAME)
     {
       vr = get_value_range (low_sub);
@@ -4195,7 +4209,9 @@ vrp_prop::check_array_ref (location_t location, tree ref,
         }
     }
 
-  if (vr && vr->kind () == VR_ANTI_RANGE)
+  if (warned)
+    ; /* Do nothing.  */
+  else if (vr && vr->kind () == VR_ANTI_RANGE)
     {
       if (up_bound
          && TREE_CODE (up_sub) == INTEGER_CST
@@ -4214,19 +4230,25 @@ vrp_prop::check_array_ref (location_t location, tree ref,
           && (ignore_off_by_one
               ? !tree_int_cst_le (up_sub, up_bound_p1)
               : !tree_int_cst_le (up_sub, up_bound)))
-    {
-      if (dump_file && (dump_flags & TDF_DETAILS))
-       {
-         fprintf (dump_file, "Array bound warning for ");
-         dump_generic_expr (MSG_NOTE, TDF_SLIM, ref);
-         fprintf (dump_file, "\n");
-       }
-      warned = warning_at (location, OPT_Warray_bounds,
-                          "array subscript %E is above array bounds of %qT",
-                          up_sub, artype);
-    }
+    warned = warning_at (location, OPT_Warray_bounds,
+                        "array subscript %E is above array bounds of %qT",
+                        up_sub, artype);
   else if (TREE_CODE (low_sub) == INTEGER_CST
            && tree_int_cst_lt (low_sub, low_bound))
+    warned = warning_at (location, OPT_Warray_bounds,
+                        "array subscript %E is below array bounds of %qT",
+                        low_sub, artype);
+
+  if (!warned && interior_zero_len)
+    warned = warning_at (location, OPT_Wzero_length_bounds,
+                        (TREE_CODE (low_sub) == INTEGER_CST
+                         ? G_("array subscript %E is outside the bounds "
+                              "of an interior zero-length array %qT")
+                         : G_("array subscript %qE is outside the bounds "
+                              "of an interior zero-length array %qT")),
+                        low_sub, artype);
+
+  if (warned)
     {
       if (dump_file && (dump_flags & TDF_DETAILS))
        {
@@ -4234,19 +4256,25 @@ vrp_prop::check_array_ref (location_t location, tree ref,
          dump_generic_expr (MSG_NOTE, TDF_SLIM, ref);
          fprintf (dump_file, "\n");
        }
-      warned = warning_at (location, OPT_Warray_bounds,
-                          "array subscript %E is below array bounds of %qT",
-                          low_sub, artype);
-    }
 
-  if (warned)
-    {
       ref = TREE_OPERAND (ref, 0);
+
+      tree rec = NULL_TREE;
       if (TREE_CODE (ref) == COMPONENT_REF)
-       ref = TREE_OPERAND (ref, 1);
+       {
+         /* For a reference to a member of a struct object also mention
+            the object if it's known.  It may be defined in a different
+            function than the out-of-bounds access.  */
+         rec = TREE_OPERAND (ref, 0);
+         if (!VAR_P (rec))
+           rec = NULL_TREE;
+         ref = TREE_OPERAND (ref, 1);
+       }
 
       if (DECL_P (ref))
        inform (DECL_SOURCE_LOCATION (ref), "while referencing %qD", ref);
+      if (rec && DECL_P (rec))
+       inform (DECL_SOURCE_LOCATION (rec), "defined here %qD", rec);
 
       TREE_NO_WARNING (ref) = 1;
     }
@@ -4391,16 +4419,21 @@ vrp_prop::check_mem_ref (location_t location, tree ref,
   /* The type of the object being referred to.  It can be an array,
      string literal, or a non-array type when the MEM_REF represents
      a reference/subscript via a pointer to an object that is not
-     an element of an array.  References to members of structs and
-     unions are excluded because MEM_REF doesn't make it possible
-     to identify the member where the reference originated.
-     Incomplete types are excluded as well because their size is
-     not known.  */
+     an element of an array.  Incomplete types are excluded as well
+     because their size is not known.  */
   tree reftype = TREE_TYPE (arg);
   if (POINTER_TYPE_P (reftype)
       || !COMPLETE_TYPE_P (reftype)
-      || TREE_CODE (TYPE_SIZE_UNIT (reftype)) != INTEGER_CST
-      || RECORD_OR_UNION_TYPE_P (reftype))
+      || TREE_CODE (TYPE_SIZE_UNIT (reftype)) != INTEGER_CST)
+    return false;
+
+  /* Except in declared objects, references to trailing array members
+     of structs and union objects are excluded because MEM_REF doesn't
+     make it possible to identify the member where the reference
+     originated.  */
+  if (RECORD_OR_UNION_TYPE_P (reftype)
+      && (!VAR_P (arg)
+         || (DECL_EXTERNAL (arg) && array_at_struct_end_p (ref))))
     return false;
 
   arrbounds[0] = 0;
@@ -4412,7 +4445,14 @@ vrp_prop::check_mem_ref (location_t location, tree ref,
       if (tree dom = TYPE_DOMAIN (reftype))
        {
          tree bnds[] = { TYPE_MIN_VALUE (dom), TYPE_MAX_VALUE (dom) };
-         if (array_at_struct_end_p (arg) || !bnds[0] || !bnds[1])
+         if (TREE_CODE (arg) == COMPONENT_REF)
+           {
+             offset_int size = maxobjsize;
+             if (tree fldsize = component_ref_size (arg))
+               size = wi::to_offset (fldsize);
+             arrbounds[1] = wi::lrshift (size, wi::floor_log2 (eltsize));
+           }
+         else if (array_at_struct_end_p (arg) || !bnds[0] || !bnds[1])
            arrbounds[1] = wi::lrshift (maxobjsize, wi::floor_log2 (eltsize));
          else
            arrbounds[1] = (wi::to_offset (bnds[1]) - wi::to_offset (bnds[0])
@@ -4434,7 +4474,13 @@ vrp_prop::check_mem_ref (location_t location, tree ref,
   else
     {
       eltsize = 1;
-      arrbounds[1] = wi::to_offset (TYPE_SIZE_UNIT (reftype));
+      tree size = TYPE_SIZE_UNIT (reftype);
+      if (VAR_P (arg))
+       if (tree initsize = DECL_SIZE_UNIT (arg))
+         if (tree_int_cst_lt (size, initsize))
+           size = initsize;
+
+      arrbounds[1] = wi::to_offset (size);
     }
 
   offrange[0] += ioff;
index 741f7a2ce659ab6a0386d36a6565bb021a522043..3299b344ea609b129c2e448e2cb8e21e73cec823 100644 (file)
@@ -3089,6 +3089,25 @@ first_field (const_tree type)
   return t;
 }
 
+/* Returns the last FIELD_DECL in the TYPE_FIELDS of the RECORD_TYPE or
+   UNION_TYPE TYPE, or NULL_TREE if none.  */
+
+tree
+last_field (const_tree type)
+{
+  tree last = NULL_TREE;
+
+  for (tree fld = TYPE_FIELDS (type); fld; fld = TREE_CHAIN (fld))
+    {
+      if (TREE_CODE (fld) != FIELD_DECL)
+       continue;
+
+      last = fld;
+    }
+
+  return last;
+}
+
 /* Concatenate two chains of nodes (chained through TREE_CHAIN)
    by modifying the last node in chain 1 to point to chain 2.
    This is the Lisp primitive `nconc'.  */
@@ -13363,8 +13382,8 @@ array_ref_up_bound (tree exp)
   return NULL_TREE;
 }
 
-/* Returns true if REF is an array reference or a component reference
-   to an array at the end of a structure.
+/* Returns true if REF is an array reference, component reference,
+   or memory reference to an array at the end of a structure.
    If this is the case, the array may be allocated larger
    than its upper bound implies.  */
 
@@ -13382,6 +13401,28 @@ array_at_struct_end_p (tree ref)
   else if (TREE_CODE (ref) == COMPONENT_REF
           && TREE_CODE (TREE_TYPE (TREE_OPERAND (ref, 1))) == ARRAY_TYPE)
     atype = TREE_TYPE (TREE_OPERAND (ref, 1));
+  else if (TREE_CODE (ref) == MEM_REF)
+    {
+      tree arg = TREE_OPERAND (ref, 0);
+      if (TREE_CODE (arg) == ADDR_EXPR)
+       arg = TREE_OPERAND (arg, 0);
+      tree argtype = TREE_TYPE (arg);
+      if (TREE_CODE (argtype) == RECORD_TYPE)
+       {
+         if (tree fld = last_field (argtype))
+           {
+             atype = TREE_TYPE (fld);
+             if (TREE_CODE (atype) != ARRAY_TYPE)
+               return false;
+             if (VAR_P (arg) && DECL_SIZE (fld))
+               return false;
+           }
+         else
+           return false;
+       }
+      else
+       return false;
+    }
   else
     return false;
 
@@ -13498,33 +13539,72 @@ component_ref_field_offset (tree exp)
     return SUBSTITUTE_PLACEHOLDER_IN_EXPR (DECL_FIELD_OFFSET (field), exp);
 }
 
+/* Given the initializer INIT, return the initializer for the field
+   DECL if it exists, otherwise null.  Used to obtain the initializer
+   for a flexible array member and determine its size.  */
+
+static tree
+get_initializer_for (tree init, tree decl)
+{
+  STRIP_NOPS (init);
+
+  tree fld, fld_init;
+  unsigned HOST_WIDE_INT i;
+  FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (init), i, fld, fld_init)
+    {
+      if (decl == fld)
+       return fld_init;
+
+      if (TREE_CODE (fld) == CONSTRUCTOR)
+       {
+         fld_init = get_initializer_for (fld_init, decl);
+         if (fld_init)
+           return fld_init;
+       }
+    }
+
+  return NULL_TREE;
+}
+
 /* Determines the size of the member referenced by the COMPONENT_REF
    REF, using its initializer expression if necessary in order to
    determine the size of an initialized flexible array member.
+   If non-null, *INTERIOR_ZERO_LENGTH is set when REF refers to
+   an interior zero-length array.
    Returns the size (which might be zero for an object with
    an uninitialized flexible array member) or null if the size
    cannot be determined.  */
 
 tree
-component_ref_size (tree ref)
+component_ref_size (tree ref, bool *interior_zero_length /* = NULL */)
 {
   gcc_assert (TREE_CODE (ref) == COMPONENT_REF);
 
+  bool int_0_len = false;
+  if (!interior_zero_length)
+    interior_zero_length = &int_0_len;
+
   tree member = TREE_OPERAND (ref, 1);
 
-  /* If the member is not an array, or is not last, or is an array with
-     more than one element, return its size.  Otherwise it's either
-     a bona fide flexible array member, or a zero-length array member,
-     or an array of length one treated as such.  */
-  tree size = DECL_SIZE_UNIT (member);
-  if (size)
+  tree memsize = DECL_SIZE_UNIT (member);
+  if (memsize)
     {
       tree memtype = TREE_TYPE (member);
-      if (TREE_CODE (memtype) != ARRAY_TYPE
-         || !array_at_struct_end_p (ref))
-       return size;
+      if (TREE_CODE (memtype) != ARRAY_TYPE)
+       return memsize;
+
+      bool trailing = array_at_struct_end_p (ref);
+      bool zero_length = integer_zerop (memsize);
+      if (!trailing && (!interior_zero_length || !zero_length))
+       /* MEMBER is either an interior array or is an array with
+          more than one element.  */
+       return memsize;
+
+      *interior_zero_length = zero_length && !trailing;
+      if (*interior_zero_length)
+       memsize = NULL_TREE;
 
-      if (!integer_zerop (size))
+      if (!zero_length)
        if (tree dom = TYPE_DOMAIN (memtype))
          if (tree min = TYPE_MIN_VALUE (dom))
            if (tree max = TYPE_MAX_VALUE (dom))
@@ -13533,37 +13613,120 @@ component_ref_size (tree ref)
                {
                  offset_int minidx = wi::to_offset (min);
                  offset_int maxidx = wi::to_offset (max);
-                 if (maxidx - minidx > 1)
-                   return size;
+                 if (maxidx - minidx > 0)
+                   /* MEMBER is an array with more than 1 element.  */
+                   return memsize;
                }
     }
 
+  /* MEMBER is either a bona fide flexible array member, or a zero-length
+     array member, or an array of length one treated as such.  */
+
   /* If the reference is to a declared object and the member a true
      flexible array, try to determine its size from its initializer.  */
-  poly_int64 off = 0;
-  tree base = get_addr_base_and_unit_offset (ref, &off);
+  poly_int64 baseoff = 0;
+  tree base = get_addr_base_and_unit_offset (ref, &baseoff);
   if (!base || !VAR_P (base))
-    return NULL_TREE;
+    {
+      if (!*interior_zero_length)
+       return NULL_TREE;
 
-  /* The size of any member of a declared object other than a flexible
-     array member is that obtained above.  */
-  if (size)
-    return size;
+      if (TREE_CODE (TREE_OPERAND (ref, 0)) != COMPONENT_REF)
+       return NULL_TREE;
 
-  if (tree init = DECL_INITIAL (base))
-    if (TREE_CODE (init) == CONSTRUCTOR)
-      {
-       off <<= LOG2_BITS_PER_UNIT;
-       init = fold_ctor_reference (NULL_TREE, init, off, 0, base);
-       if (init)
-         return TYPE_SIZE_UNIT (TREE_TYPE (init));
-      }
+      base = TREE_OPERAND (ref, 0);
+      baseoff = tree_to_poly_int64 (byte_position (TREE_OPERAND (ref, 1)));
+    }
+
+  /* BASE is the declared object of which MEMBER is either a member
+     or that is is cast to REFTYPE (e.g., a char buffer used to store
+     a REFTYPE object).  */
+  tree reftype = TREE_TYPE (TREE_OPERAND (ref, 0));
+  tree basetype = TREE_TYPE (base);
+
+  /* Determine the base type of the referenced object.  If it's
+     the same as REFTYPE and MEMBER has a known size, return it.  */
+  tree bt = basetype;
+  if (!*interior_zero_length)
+    while (TREE_CODE (bt) == ARRAY_TYPE)
+      bt = TREE_TYPE (bt);
+  bool typematch = useless_type_conversion_p (reftype, bt);
+  if (memsize && typematch)
+    return memsize;
+
+  memsize = NULL_TREE;
+
+  /* MEMBER is a true flexible array member.  Compute its size from
+     the initializer of the BASE object if it has one.  */
+  if (tree init = DECL_P (base) ? DECL_INITIAL (base) : NULL_TREE)
+    {
+      init = get_initializer_for (init, member);
+      if (init)
+       {
+         memsize = TYPE_SIZE_UNIT (TREE_TYPE (init));
+         if (tree refsize = TYPE_SIZE_UNIT (reftype))
+           {
+             /* Use the larger of the initializer size and the tail
+                padding in the enclosing struct.  */
+             poly_int64 rsz = tree_to_poly_int64 (refsize);
+             rsz -= baseoff;
+             if (known_lt (tree_to_poly_int64 (memsize), rsz))
+               memsize = wide_int_to_tree (TREE_TYPE (memsize), rsz);
+           }
+
+         baseoff = 0;
+       }
+    }
+
+  if (!memsize)
+    {
+      if (typematch)
+       {
+         if (DECL_P (base)
+             && DECL_EXTERNAL (base)
+             && bt == basetype
+             && !*interior_zero_length)
+           /* The size of a flexible array member of an extern struct
+              with no initializer cannot be determined (it's defined
+              in another translation unit and can have an initializer
+              witth an arbitrary number of elements).  */
+           return NULL_TREE;
+
+         /* Use the size of the base struct or, for interior zero-length
+            arrays, the size of the enclosing type.  */
+         memsize = TYPE_SIZE_UNIT (bt);
+       }
+      else
+       /* Use the size of the BASE object (possibly an array of some
+          other type such as char used to store the struct).  */
+       memsize = DECL_SIZE_UNIT (base);
+    }
+
+  /* If the flexible array member has a known size use the greater
+     of it and the tail padding in the enclosing struct.
+     Otherwise, when the size of the flexible array member is unknown
+     and the referenced object is not a struct, use the size of its
+     type when known.  This detects sizes of array buffers when cast
+     to struct types with flexible array members.  */
+  if (memsize)
+    {
+      poly_int64 memsz64 = memsize ? tree_to_poly_int64 (memsize) : 0;
+      if (known_lt (baseoff, memsz64))
+       {
+         memsz64 -= baseoff;
+         return wide_int_to_tree (TREE_TYPE (memsize), memsz64);
+       }
+      return integer_zero_node;
+    }
 
   /* Return "don't know" for an external non-array object since its
      flexible array member can be initialized to have any number of
      elements.  Otherwise, return zero because the flexible array
      member has no elements.  */
-  return (DECL_EXTERNAL (base) && TREE_CODE (TREE_TYPE (base)) != ARRAY_TYPE
+  return (DECL_P (base)
+         && DECL_EXTERNAL (base)
+         && (!typematch
+             || TREE_CODE (basetype) != ARRAY_TYPE)
          ? NULL_TREE : integer_zero_node);
 }
 
index 81351548ecdf505b71bbe349489f69959abed08a..2ce001d546862b4cb2a9e7eaa4b9f89b9752e31a 100644 (file)
@@ -5262,7 +5262,7 @@ extern tree component_ref_field_offset (tree);
    of an initialized flexible array member.  The size might be zero for
    an object with an uninitialized flexible array member or null if it
    cannot be determined.  */
-extern tree component_ref_size (tree);
+extern tree component_ref_size (tree, bool * = NULL);
 
 extern int tree_map_base_eq (const void *, const void *);
 extern unsigned int tree_map_base_hash (const void *);
index 862079a854922e7970e7759e165f5be4d0158e0b..5e0f444f9a6d050718c4ab0c6dea88c01ef88e6a 100644 (file)
@@ -852,6 +852,8 @@ inline HOST_WIDE_INT
 generic_wide_int <storage>::sign_mask () const
 {
   unsigned int len = this->get_len ();
+  gcc_assert (len > 0);
+
   unsigned HOST_WIDE_INT high = this->get_val ()[len - 1];
   if (!is_sign_extended)
     {