PR tree-optimization/91457 - inconsistent warning for writing past the end of an...
authorMartin Sebor <msebor@redhat.com>
Wed, 28 Aug 2019 16:43:56 +0000 (16:43 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Wed, 28 Aug 2019 16:43:56 +0000 (10:43 -0600)
gcc/ChangeLog:

PR tree-optimization/91457
* builtins.c (component_size): New function.
(compute_objsize): Add argument. Handle ARRAY_REF and COMPONENT_REF.
* builtins.h (compute_objsize): Add argument.
* tree-ssa-strlen.c (handle_store): Handle no-warning bit.
* tree-vrp.c (vrp_prop::check_array_ref): Return warning result.
(vrp_prop::check_mem_ref): Same.
(vrp_prop::search_for_addr_array): Set no-warning bit.
(check_array_bounds): Same.

gcc/testsuite/ChangeLog:

PR tree-optimization/91457
* c-c++-common/Wstringop-overflow-2.c: New test.
* g++.dg/warn/Warray-bounds-8.C: New test.
* g++.dg/warn/Wstringop-overflow-3.C: New test.
* gcc.dg/Wstringop-overflow-15.c: New test.

From-SVN: r274997

gcc/ChangeLog
gcc/builtins.c
gcc/builtins.h
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/Wstringop-overflow-2.c [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/Warray-bounds-8.C [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-15.c [new file with mode: 0644]
gcc/tree-ssa-strlen.c
gcc/tree-vrp.c

index 6fa40905144ff8c97c18313407fd2848a7af2d20..77dbf876a3a5d0c16fbbcce9f8d127a6d7baa280 100644 (file)
@@ -1,3 +1,15 @@
+2019-08-28  Martin Sebor  <msebor@redhat.com>
+
+       PR tree-optimization/91457
+       * builtins.c (component_size): New function.
+       (compute_objsize): Add argument. Handle ARRAY_REF and COMPONENT_REF.
+       * builtins.h (compute_objsize): Add argument.
+       * tree-ssa-strlen.c (handle_store): Handle no-warning bit.
+       * tree-vrp.c (vrp_prop::check_array_ref): Return warning result.
+       (vrp_prop::check_mem_ref): Same.
+       (vrp_prop::search_for_addr_array): Set no-warning bit.
+       (check_array_bounds): Same.
+
 2019-08-28  Martin Sebor  <msebor@redhat.com>
 
        PR driver/80545
index 0b25adc17a0ba68e73671b6e7ded189171e03e11..f8063c138a340a06d45b01c9bb7f43caf75e78b2 100644 (file)
@@ -72,6 +72,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "file-prefix-map.h" /* remap_macro_filename()  */
 #include "gomp-constants.h"
 #include "omp-general.h"
+#include "tree-dfa.h"
 
 struct target_builtins default_target_builtins;
 #if SWITCHABLE_TARGET
@@ -3561,6 +3562,54 @@ check_access (tree exp, tree, tree, tree dstwrite,
   return true;
 }
 
+/* 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.
+   Returns the size (which might be zero for an object with
+   an uninitialized flexible array member) or null if the size
+   cannot be determined.  */
+
+static tree
+component_size (tree ref)
+{
+  gcc_assert (TREE_CODE (ref) == COMPONENT_REF);
+
+  tree member = TREE_OPERAND (ref, 1);
+
+  /* If the member is not last or has a size greater than one, return
+     it.  Otherwise it's either a 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
+      && (!array_at_struct_end_p (ref)
+         || (!integer_zerop (size)
+             && !integer_onep (size))))
+    return size;
+
+  /* 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);
+  if (!base || !VAR_P (base))
+    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 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));
+      }
+
+  return DECL_EXTERNAL (base) ? NULL_TREE : integer_zero_node;
+}
+
 /* Helper to compute the size of the object referenced by the DEST
    expression which must have pointer type, using Object Size type
    OSTYPE (only the least significant 2 bits are used).  Return
@@ -3568,12 +3617,18 @@ check_access (tree exp, tree, tree, tree dstwrite,
    the size cannot be determined.  When the referenced object involves
    a non-constant offset in some range the returned value represents
    the largest size given the smallest non-negative offset in the
-   range.  The function is intended for diagnostics and should not
-   be used to influence code generation or optimization.  */
+   range.  If nonnull, set *PDECL to the decl of the referenced
+   subobject if it can be determined, or to null otherwise.
+   The function is intended for diagnostics and should not be used
+   to influence code generation or optimization.  */
 
 tree
-compute_objsize (tree dest, int ostype)
+compute_objsize (tree dest, int ostype, tree *pdecl /* = NULL */)
 {
+  tree dummy = NULL_TREE;
+  if (!pdecl)
+    pdecl = &dummy;
+
   unsigned HOST_WIDE_INT size;
 
   /* Only the two least significant bits are meaningful.  */
@@ -3600,7 +3655,7 @@ compute_objsize (tree dest, int ostype)
          tree off = gimple_assign_rhs2 (stmt);
          if (TREE_CODE (off) == INTEGER_CST)
            {
-             if (tree size = compute_objsize (dest, ostype))
+             if (tree size = compute_objsize (dest, ostype, pdecl))
                {
                  wide_int wioff = wi::to_wide (off);
                  wide_int wisiz = wi::to_wide (size);
@@ -3625,7 +3680,7 @@ compute_objsize (tree dest, int ostype)
 
              if (rng == VR_RANGE)
                {
-                 if (tree size = compute_objsize (dest, ostype))
+                 if (tree size = compute_objsize (dest, ostype, pdecl))
                    {
                      wide_int wisiz = wi::to_wide (size);
 
@@ -3653,12 +3708,31 @@ compute_objsize (tree dest, int ostype)
   if (!ostype)
     return NULL_TREE;
 
-  if (TREE_CODE (dest) == MEM_REF)
+  if (TREE_CODE (dest) == ARRAY_REF
+      || TREE_CODE (dest) == MEM_REF)
     {
       tree ref = TREE_OPERAND (dest, 0);
       tree off = TREE_OPERAND (dest, 1);
-      if (tree size = compute_objsize (ref, ostype))
+      if (tree size = compute_objsize (ref, ostype, pdecl))
        {
+         /* If the declaration of the destination object is known
+            to have zero size, return zero.  */
+         if (integer_zerop (size))
+           return integer_zero_node;
+
+         if (TREE_CODE (off) != INTEGER_CST
+             || TREE_CODE (size) != INTEGER_CST)
+           return NULL_TREE;
+
+         if (TREE_CODE (dest) == ARRAY_REF)
+           {
+             tree eltype = TREE_TYPE (dest);
+             if (tree tpsize = TYPE_SIZE_UNIT (eltype))
+               off = fold_build2 (MULT_EXPR, size_type_node, off, tpsize);
+             else
+               return NULL_TREE;
+           }
+
          if (tree_int_cst_lt (off, size))
            return fold_build2 (MINUS_EXPR, size_type_node, size, off);
          return integer_zero_node;
@@ -3667,9 +3741,22 @@ compute_objsize (tree dest, int ostype)
       return NULL_TREE;
     }
 
+  if (TREE_CODE (dest) == COMPONENT_REF)
+    {
+      *pdecl = TREE_OPERAND (dest, 1);
+      return component_size (dest);
+    }
+
   if (TREE_CODE (dest) != ADDR_EXPR)
     return NULL_TREE;
 
+  tree ref = TREE_OPERAND (dest, 0);
+  if (DECL_P (ref))
+    {
+      *pdecl = ref;
+      return DECL_SIZE_UNIT (ref);
+    }
+
   tree type = TREE_TYPE (dest);
   if (TREE_CODE (type) == POINTER_TYPE)
     type = TREE_TYPE (type);
@@ -3677,14 +3764,10 @@ compute_objsize (tree dest, int ostype)
   type = TYPE_MAIN_VARIANT (type);
 
   if (TREE_CODE (type) == ARRAY_TYPE
-      && !array_at_struct_end_p (TREE_OPERAND (dest, 0)))
-    {
-      /* Return the constant size unless it's zero (that's a zero-length
-        array likely at the end of a struct).  */
-      tree size = TYPE_SIZE_UNIT (type);
-      if (size && TREE_CODE (size) == INTEGER_CST
-         && !integer_zerop (size))
-       return size;
+      && !array_at_struct_end_p (ref))
+    {
+      if (tree size = TYPE_SIZE_UNIT (type))
+       return TREE_CODE (size) == INTEGER_CST ? size : NULL_TREE;
     }
 
   return NULL_TREE;
index 66c9295ff4a67fa91324799fc54301d885348406..1ad82e869634e737b3a70771c59a595c4eeb6d2a 100644 (file)
@@ -134,7 +134,7 @@ extern tree fold_call_stmt (gcall *, bool);
 extern void set_builtin_user_assembler_name (tree decl, const char *asmspec);
 extern bool is_simple_builtin (tree);
 extern bool is_inexpensive_builtin (tree);
-extern tree compute_objsize (tree, int);
+extern tree compute_objsize (tree, int, tree * = NULL);
 
 extern bool readonly_data_expr (tree exp);
 extern bool init_target_chars (void);
index 480362e9d8af6bdc23081a13ba3de7aab950b6d6..0e7c31b1ab70441e1f65dea47c8321adee05a87a 100644 (file)
@@ -1,3 +1,11 @@
+2019-08-28  Martin Sebor  <msebor@redhat.com>
+
+       PR tree-optimization/91457
+       * c-c++-common/Wstringop-overflow-2.c: New test.
+       * g++.dg/warn/Warray-bounds-8.C: New test.
+       * g++.dg/warn/Wstringop-overflow-3.C: New test.
+       * gcc.dg/Wstringop-overflow-15.c: New test.
+
 2019-08-16  Martin Liska  <mliska@suse.cz>
 
        PR c++/90613
diff --git a/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c b/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
new file mode 100644 (file)
index 0000000..d1aab48
--- /dev/null
@@ -0,0 +1,348 @@
+/* PR middle-end/91458 - inconsistent warning for writing past the end
+   of an array member
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-array-bounds" } */
+
+void sink (void*);
+
+// Exercise flexible array members.
+
+struct Ax
+{
+  char n;
+  char a[];                     // { dg-message "destination object declared here" }
+};
+
+// Verify warning for a definition with no initializer.
+struct Ax ax_;
+
+void gax_ (void)
+{
+  ax_.a[0] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  ax_.a[1] = 1;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  ax_.a[2] = 2;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+// Verify warning for access to a definition with an initializer that doesn't
+// initialize the flexible array member.
+struct Ax ax0 = { 0 };
+
+void gax0 (void)
+{
+  ax0.a[0] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  ax0.a[1] = 1;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  ax0.a[2] = 2;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+// Verify warning for access to a definition with an initializer that
+// initializes the flexible array member to empty.
+struct Ax ax0_ = { 0, { } };
+
+void gax0_ (void)
+{
+  ax0_.a[0] = 0;                // { dg-warning "\\\[-Wstringop-overflow" }
+  ax0_.a[1] = 1;                // { dg-warning "\\\[-Wstringop-overflow" }
+  ax0_.a[2] = 2;                // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+// Verify warning for out-of-bounds accesses to a definition with
+// an initializer.
+struct Ax ax1 = { 1, { 0 } };
+
+void gax1 (void)
+{
+  ax1.a[0] = 0;
+  ax1.a[1] = 1;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  ax1.a[2] = 2;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+struct Ax ax2 = { 2, { 1, 0 } };
+
+void gax2 (void)
+{
+  ax2.a[0] = 0;
+  ax2.a[1] = 1;
+  ax2.a[2] = 2;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+
+// Verify no warning for an unknown struct object.
+void gaxp (struct Ax *p)
+{
+  p->a[0] = 0;
+  p->a[3] = 3;
+  p->a[9] = 9;
+}
+
+
+// Verify no warning for an extern struct object whose array may be
+// initialized to any number of elements.
+extern struct Ax axx;
+
+void gaxx (void)
+{
+  axx.a[0] = 0;
+  axx.a[3] = 3;
+  axx.a[9] = 9;
+}
+
+// Exercise zero-length array members.
+
+struct A0
+{
+  char n;
+  char a[0];                    // { dg-message "destination object declared here" }
+};
+
+// Verify warning for a definition with no initializer.
+struct A0 a0_;
+
+void ga0_ (void)
+{
+  a0_.a[0] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  a0_.a[1] = 1;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  a0_.a[2] = 2;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+// Verify warning for access to a definition with an initializer that doesn't
+// initialize the flexible array member.
+struct A0 a00 = { 0 };
+
+void ga00 (void)
+{
+  a00.a[0] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  a00.a[1] = 1;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  a00.a[2] = 2;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+// Verify warning for access to a definition with an initializer that
+// initializes the flexible array member to empty.
+struct A0 a00_ = { 0, { } };
+
+void ga00_ (void)
+{
+  a00_.a[0] = 0;                // { dg-warning "\\\[-Wstringop-overflow" }
+  a00_.a[1] = 1;                // { dg-warning "\\\[-Wstringop-overflow" }
+  a00_.a[2] = 2;                // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+// The following are rejected with
+//   error: too many initializers for 'char [0]'
+// A0 a01 = { 1, { 0 } };
+// A0 a02 = { 2, { 1, 0 } };
+
+
+// Verify no warning for an unknown struct object.
+void ga0p (struct A0 *p)
+{
+  p->a[0] = 0;
+  p->a[3] = 3;
+  p->a[9] = 9;
+}
+
+
+// Verify warning for an extern struct object which (unlike a true
+// flexible array member) may not be initialized.
+extern struct A0 a0x;
+
+void ga0x (void)
+{
+  a0x.a[0] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  a0x.a[3] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  a0x.a[9] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+
+// Exercise trailing one-element array members.
+
+struct A1
+{
+  char n;
+  char a[1];                    // { dg-message "destination object declared here" }
+};
+
+// Verify warning for a definition with no initializer.
+struct A1 a1_;
+
+void ga1_ (void)
+{
+  a1_.a[0] = 0;
+  a1_.a[1] = 1;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  a1_.a[2] = 2;                 // { dg-warning "\\\[-Wstringop-overflow" }
+
+  struct A1 a;
+  a.a[0] = 0;
+  a.a[1] = 1;                   // { dg-warning "\\\[-Wstringop-overflow" }
+  a.a[2] = 2;                   // { dg-warning "\\\[-Wstringop-overflow" }
+  sink (&a);
+}
+
+// Verify warning for access to a definition with an initializer that doesn't
+// initialize the one-element array member.
+struct A1 a1__ = { 0 };
+
+void ga1__ (void)
+{
+  a1__.a[0] = 0;
+  a1__.a[1] = 1;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  a1__.a[2] = 2;                 // { dg-warning "\\\[-Wstringop-overflow" }
+
+  struct A1 a = { 1 };
+  a.a[0] = 0;
+  a.a[1] = 1;                    // { dg-warning "\\\[-Wstringop-overflow" }
+  a.a[2] = 2;                    // { dg-warning "\\\[-Wstringop-overflow" }
+  sink (&a);
+}
+
+// Verify warning for access to a definition with an initializer that
+// initializes the one-element array member to empty.
+struct A1 a1_0 = { 0, { } };
+
+void ga1_0_ (void)
+{
+  a1_0.a[0] = 0;
+  a1_0.a[1] = 1;                // { dg-warning "\\\[-Wstringop-overflow" }
+  a1_0.a[2] = 2;                // { dg-warning "\\\[-Wstringop-overflow" }
+
+  struct A1 a = { 1, { } };
+  a.a[0] = 0;
+  a.a[1] = 1;                   // { dg-warning "\\\[-Wstringop-overflow" }
+  a.a[2] = 2;                   // { dg-warning "\\\[-Wstringop-overflow" }
+  sink (&a);
+}
+
+// Verify warning for access to a definition with an initializer that
+// initializes the one-element array member.
+struct A1 a1_1 = { 0, { 1 } };
+
+void ga1_1 (void)
+{
+  a1_1.a[0] = 0;
+  a1_1.a[1] = 1;                // { dg-warning "\\\[-Wstringop-overflow" }
+  a1_1.a[2] = 2;                // { dg-warning "\\\[-Wstringop-overflow" }
+
+  struct A1 a = { 0, { 1 } };
+  a.a[0] = 0;
+  a.a[1] = 1;                   // { dg-warning "\\\[-Wstringop-overflow" }
+  a.a[2] = 2;                   // { dg-warning "\\\[-Wstringop-overflow" }
+  sink (&a);
+}
+
+
+// Verify no warning for an unknown struct object.
+void ga1p (struct A1 *p)
+{
+  p->a[0] = 0;
+  p->a[3] = 3;
+  p->a[9] = 9;
+}
+
+
+// Verify warning for an extern struct object.  Similar to the zero-length
+// array case, a one-element trailing array can be initialized to at most
+// a single element.
+extern struct A1 a1x;
+
+void ga1x (void)
+{
+  a1x.a[0] = 0;
+  a1x.a[3] = 3;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  a1x.a[9] = 9;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+// Exercise interior one-element array members (verify they're not
+// treated as trailing.
+
+struct A1i
+{
+  char n;
+  char a[1];                    // { dg-message "destination object declared here" }
+  char x;
+};
+
+// Verify warning for a definition with no initializer.
+struct A1i a1i_;
+
+void ga1i_ (void)
+{
+  a1i_.a[0] = 0;
+  a1i_.a[1] = 1;                // { dg-warning "\\\[-Wstringop-overflow" }
+  a1i_.a[2] = 2;                // { dg-warning "\\\[-Wstringop-overflow" }
+
+  struct A1i a;
+  a.a[0] = 1;
+  a.a[1] = 2;                   // { dg-warning "\\\[-Wstringop-overflow" }
+  a.a[2] = 3;                   // { dg-warning "\\\[-Wstringop-overflow" }
+  sink (&a);
+}
+
+// Verify warning for access to a definition with an initializer that doesn't
+// initialize the one-element array member.
+struct A1i a1i__ = { 0 };
+
+void ga1i__ (void)
+{
+  a1i__.a[0] = 0;
+  a1i__.a[1] = 1;                // { dg-warning "\\\[-Wstringop-overflow" }
+  a1i__.a[2] = 2;                // { dg-warning "\\\[-Wstringop-overflow" }
+
+  struct A1i a = { 0 };
+  a.a[0] = 0;
+  a.a[1] = 1;                    // { dg-warning "\\\[-Wstringop-overflow" }
+  a.a[2] = 2;                    // { dg-warning "\\\[-Wstringop-overflow" }
+  sink (&a);
+}
+
+// Verify warning for access to a definition with an initializer that
+// initializes the one-element array member to empty.
+struct A1 a1i_0 = { 0, { } };
+
+void ga1i_0_ (void)
+{
+  a1i_0.a[0] = 0;
+  a1i_0.a[1] = 1;               // { dg-warning "\\\[-Wstringop-overflow" }
+  a1i_0.a[2] = 2;               // { dg-warning "\\\[-Wstringop-overflow" }
+
+  struct A1 a = { 0, { } };
+  a.a[0] = 0;
+  a.a[1] = 1;                   // { dg-warning "\\\[-Wstringop-overflow" }
+  a.a[2] = 2;                   // { dg-warning "\\\[-Wstringop-overflow" }
+  sink (&a);
+}
+
+// Verify warning for access to a definition with an initializer that
+// initializes the one-element array member.
+struct A1 a1i_1 = { 0, { 1 } };
+
+void ga1i_1 (void)
+{
+  a1i_1.a[0] = 0;
+  a1i_1.a[1] = 1;               // { dg-warning "\\\[-Wstringop-overflow" }
+  a1i_1.a[2] = 2;               // { dg-warning "\\\[-Wstringop-overflow" }
+
+  struct A1 a = { 0, { 1 } };
+  a.a[0] = 1;
+  a.a[1] = 2;                   // { dg-warning "\\\[-Wstringop-overflow" }
+  a.a[2] = 3;                   // { dg-warning "\\\[-Wstringop-overflow" }
+  sink (&a);
+}
+
+
+// Verify no warning for an unknown struct object.
+void ga1ip (struct A1i *p)
+{
+  p->a[0] = 0;
+  p->a[3] = 3;                  // { dg-warning "\\\[-Wstringop-overflow" }
+  p->a[9] = 9;                  // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+
+// Verify no warning for an extern struct object.
+extern struct A1i a1ix;
+
+void ga1ix (void)
+{
+  a1ix.a[0] = 0;
+  a1ix.a[3] = 3;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  a1ix.a[9] = 9;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-8.C b/gcc/testsuite/g++.dg/warn/Warray-bounds-8.C
new file mode 100644 (file)
index 0000000..850414e
--- /dev/null
@@ -0,0 +1,388 @@
+/* PR middle-end/91458 - inconsistent warning for writing past the end
+   of an array member
+   See Wstringop-overflow-3.C for the same test that exercises the other
+   warning.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-stringop-overflow" } */
+
+void sink (void*);
+
+// Exercise flexible array members.
+
+struct Ax
+{
+  char n;
+  char a[];                     // { dg-message "while referencing .Ax::a." "pr91463" { xfail *-*-* } }
+};
+
+// Verify warning for a definition with no initializer.
+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 *-*-* } }
+}
+
+// Verify warning for access to a definition with an initializer that doesn't
+// initialize the flexible array member.
+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 *-*-* } }
+}
+
+// Verify warning for access to a definition with an initializer that
+// initializes the flexible array member to empty.
+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 *-*-* } }
+}
+
+// Verify warning for out-of-bounds accesses to a definition with
+// an initializer.
+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 *-*-* } }
+}
+
+Ax ax2 = { 2, { 1, 0 } };
+
+void gax2 ()
+{
+  ax2.a[0] = 0;
+  ax2.a[1] = 0;
+  ax2.a[2] = 0;                 // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } }
+}
+
+
+// Verify no warning for an unknown struct object.
+void gaxp (Ax *p)
+{
+  p->a[0] = 0;
+  p->a[3] = 0;
+  p->a[9] = 0;
+}
+
+
+// Verify no warning for an extern struct object whose array may be
+// initialized to any number of elements.
+extern Ax axx;
+
+void gaxx ()
+{
+  axx.a[0] = 0;
+  axx.a[3] = 0;
+  axx.a[9] = 0;
+}
+
+// Exercise zero-length array members.
+
+struct A0
+{
+  char n;
+  char a[0];                    // { dg-message "while referencing .A0::a." }
+};
+
+// Verify warning for a definition with no initializer.
+A0 a0_;
+
+void ga0_ ()
+{
+  a0_.a[0] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+  a0_.a[1] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+  a0_.a[2] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+}
+
+// Verify warning for access to a definition with an initializer that doesn't
+// initialize the flexible array member.
+A0 a00 = { 0 };
+
+void ga00 ()
+{
+  a00.a[0] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+  a00.a[1] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+  a00.a[2] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+}
+
+// Verify warning for access to a definition with an initializer that
+// initializes the flexible array member to empty.
+A0 a00_ = { 0, { } };
+
+void ga00_ ()
+{
+  a00_.a[0] = 0;                // { dg-warning "\\\[-Warray-bounds" }
+  a00_.a[1] = 0;                // { dg-warning "\\\[-Warray-bounds" }
+  a00_.a[2] = 0;                // { dg-warning "\\\[-Warray-bounds" }
+}
+
+// The following are rejected with
+//   error: too many initializers for 'char [0]'
+// A0 a01 = { 1, { 0 } };
+// A0 a02 = { 2, { 1, 0 } };
+
+
+// Verify no warning for an unknown struct object.
+void ga0p (A0 *p)
+{
+  p->a[0] = 0;
+  p->a[3] = 0;
+  p->a[9] = 0;
+}
+
+
+// Verify warning for an extern struct object which (unlike a true
+// flexible array member) may not be initialized.
+extern A0 a0x;
+
+void ga0x ()
+{
+  a0x.a[0] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+  a0x.a[3] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+  a0x.a[9] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+}
+
+
+// Exercise trailing one-element array members.
+
+struct A1
+{
+  char n;
+  char a[1];                    // { dg-message "while referencing .A1::a." }
+};
+
+// Verify warning for a definition with no initializer.
+A1 a1_;
+
+void ga1_ ()
+{
+  a1_.a[0] = 0;
+  a1_.a[1] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+  a1_.a[2] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+}
+
+// Verify warning for access to a definition with an initializer that doesn't
+// initialize the one-element array member.
+A1 a1__ = { 0 };
+
+void ga1__ ()
+{
+  a1__.a[0] = 0;
+  a1__.a[1] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+  a1__.a[2] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+}
+
+// Verify warning for access to a definition with an initializer that
+// initializes the one-element array member to empty.
+A1 a1_0 = { 0, { } };
+
+void ga1_0_ ()
+{
+  a1_0.a[0] = 0;
+  a1_0.a[1] = 0;                // { dg-warning "\\\[-Warray-bounds" }
+  a1_0.a[2] = 0;                // { dg-warning "\\\[-Warray-bounds" }
+}
+
+// Verify warning for access to a definition with an initializer that
+// initializes the one-element array member.
+A1 a1_1 = { 0, { 1 } };
+
+void ga1_1 ()
+{
+  a1_1.a[0] = 0;
+  a1_1.a[1] = 0;                // { dg-warning "\\\[-Warray-bounds" }
+  a1_1.a[2] = 0;                // { dg-warning "\\\[-Warray-bounds" }
+}
+
+
+// Verify no warning for an unknown struct object.
+void ga1p (A1 *p)
+{
+  p->a[0] = 0;
+  p->a[3] = 0;
+  p->a[9] = 0;
+}
+
+
+// Verify warning for an extern struct object.  Similar to the zero-length
+// array case, a one-element trailing array can be initialized to at most
+// a single element.
+extern A1 a1x;
+
+void ga1x ()
+{
+  a1x.a[0] = 0;
+  a1x.a[3] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+  a1x.a[9] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+}
+
+// Exercise interior one-element array members (verify they're not
+// treated as trailing.
+
+struct A1i
+{
+  char n;
+  char a[1];                    // { dg-message "while referencing .A1i::a." }
+  char x;
+};
+
+// Verify warning for a definition with no initializer.
+A1i a1i_;
+
+void ga1i_ ()
+{
+  a1i_.a[0] = 0;
+  a1i_.a[1] = 0;                // { dg-warning "\\\[-Warray-bounds" }
+  a1i_.a[2] = 0;                // { dg-warning "\\\[-Warray-bounds" }
+}
+
+// Verify warning for access to a definition with an initializer that doesn't
+// initialize the one-element array member.
+A1i a1i__ = { 0 };
+
+void ga1i__ ()
+{
+  a1i__.a[0] = 0;
+  a1i__.a[1] = 0;                // { dg-warning "\\\[-Warray-bounds" }
+  a1i__.a[2] = 0;                // { dg-warning "\\\[-Warray-bounds" }
+}
+
+// Verify warning for access to a definition with an initializer that
+// initializes the one-element array member to empty.
+A1 a1i_0 = { 0, { } };
+
+void ga1i_0_ ()
+{
+  a1i_0.a[0] = 0;
+  a1i_0.a[1] = 0;               // { dg-warning "\\\[-Warray-bounds" }
+  a1i_0.a[2] = 0;               // { dg-warning "\\\[-Warray-bounds" }
+}
+
+// Verify warning for access to a definition with an initializer that
+// initializes the one-element array member.
+A1 a1i_1 = { 0, { 1 } };
+
+void ga1i_1 ()
+{
+  a1i_1.a[0] = 0;
+  a1i_1.a[1] = 0;               // { dg-warning "\\\[-Warray-bounds" }
+  a1i_1.a[2] = 0;               // { dg-warning "\\\[-Warray-bounds" }
+}
+
+
+// Verify no warning for an unknown struct object.
+void ga1ip (A1i *p)
+{
+  p->a[0] = 0;
+  p->a[3] = 0;                  // { dg-warning "\\\[-Warray-bounds" }
+  p->a[9] = 0;                  // { dg-warning "\\\[-Warray-bounds" }
+}
+
+
+// Verify no warning for an extern struct object.
+extern A1i a1ix;
+
+void ga1ix ()
+{
+  a1ix.a[0] = 0;
+  a1ix.a[3] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+  a1ix.a[9] = 0;                 // { dg-warning "\\\[-Warray-bounds" }
+}
+
+
+// Verify non-POD classes with flexible array members.
+
+struct Bx
+{
+  char n;
+  char a[];                     // { dg-message "while referencing .Bx::a." "pr91463" { xfail *-*-* } }
+
+  // Verify the warning for a constant.
+  Bx () { a[0] = 0; }           // { dg-warning "\\\[-Warray-bounds" "pr91463" { xfail *-*-* } }
+
+  // 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 *-*-* } }
+};
+
+void gbx (void)
+{
+  struct Bx bx;
+  sink (&bx);
+}
+
+void gbxi (int i)
+{
+  struct Bx bxi (i);
+  sink (&bxi);
+}
+
+struct B0
+{
+  char n;
+  char a[0];                    // { dg-message "while referencing .B0::a." }
+
+  B0 () { a[0] = 0; }           // { dg-warning "\\\[-Warray-bounds" }
+};
+
+
+void gb0 (void)
+{
+  struct B0 b0;
+  sink (&b0);
+}
+
+
+struct B1
+{
+  char n;
+  char a[1];                    // { dg-message "while referencing .B1::a." }
+
+  B1 () { a[1] = 0; }           // { dg-warning "\\\[-Warray-bounds" }
+};
+
+void gb1 (void)
+{
+  struct B1 b1;
+  sink (&b1);
+}
+
+
+struct B123
+{
+  char a[123];                  // { dg-message "while referencing .B123::a." }
+
+  B123 () { a[123] = 0; }       // { dg-warning "\\\[-Warray-bounds" }
+};
+
+void gb123 (void)
+{
+  struct B123 b123;
+  sink (&b123);
+}
+
+
+struct B234
+{
+  char a[234];                  // { dg-message "while referencing .B234::a." }
+
+  B234 (int i) { a[i] = 0; }    // { dg-warning "\\\[-Warray-bounds" }
+};
+
+void g234 (void)
+{
+  struct B234 b234 (234);
+  sink (&b234);
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C b/gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C
new file mode 100644 (file)
index 0000000..99ce427
--- /dev/null
@@ -0,0 +1,386 @@
+/* PR middle-end/91458 - inconsistent warning for writing past the end
+   of an array member
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-array-bounds" } */
+
+void sink (void*);
+
+// Exercise flexible array members.
+
+struct Ax
+{
+  char n;
+  char a[];                     // { dg-message "destination object declared here" }
+};
+
+// Verify warning for a definition with no initializer.
+Ax ax_;
+
+void gax_ ()
+{
+  ax_.a[0] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  ax_.a[1] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  ax_.a[2] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+// Verify warning for access to a definition with an initializer that doesn't
+// initialize the flexible array member.
+Ax ax0 = { 0 };
+
+void gax0 ()
+{
+  ax0.a[0] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  ax0.a[1] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  ax0.a[2] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+// Verify warning for access to a definition with an initializer that
+// initializes the flexible array member to empty.
+Ax ax0_ = { 0, { } };
+
+void gax0_ ()
+{
+  ax0_.a[0] = 0;                // { dg-warning "\\\[-Wstringop-overflow" }
+  ax0_.a[1] = 0;                // { dg-warning "\\\[-Wstringop-overflow" }
+  ax0_.a[2] = 0;                // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+// Verify warning for out-of-bounds accesses to a definition with
+// an initializer.
+Ax ax1 = { 1, { 0 } };
+
+void gax1 ()
+{
+  ax1.a[0] = 0;
+  ax1.a[1] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  ax1.a[2] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+Ax ax2 = { 2, { 1, 0 } };
+
+void gax2 ()
+{
+  ax2.a[0] = 0;
+  ax2.a[1] = 0;
+  ax2.a[2] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+
+// Verify no warning for an unknown struct object.
+void gaxp (Ax *p)
+{
+  p->a[0] = 0;
+  p->a[3] = 0;
+  p->a[9] = 0;
+}
+
+
+// Verify no warning for an extern struct object whose array may be
+// initialized to any number of elements.
+extern Ax axx;
+
+void gaxx ()
+{
+  axx.a[0] = 0;
+  axx.a[3] = 0;
+  axx.a[9] = 0;
+}
+
+// Exercise zero-length array members.
+
+struct A0
+{
+  char n;
+  char a[0];                    // { dg-message "destination object declared here" }
+};
+
+// Verify warning for a definition with no initializer.
+A0 a0_;
+
+void ga0_ ()
+{
+  a0_.a[0] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  a0_.a[1] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  a0_.a[2] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+// Verify warning for access to a definition with an initializer that doesn't
+// initialize the flexible array member.
+A0 a00 = { 0 };
+
+void ga00 ()
+{
+  a00.a[0] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  a00.a[1] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  a00.a[2] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+// Verify warning for access to a definition with an initializer that
+// initializes the flexible array member to empty.
+A0 a00_ = { 0, { } };
+
+void ga00_ ()
+{
+  a00_.a[0] = 0;                // { dg-warning "\\\[-Wstringop-overflow" }
+  a00_.a[1] = 0;                // { dg-warning "\\\[-Wstringop-overflow" }
+  a00_.a[2] = 0;                // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+// The following are rejected with
+//   error: too many initializers for 'char [0]'
+// A0 a01 = { 1, { 0 } };
+// A0 a02 = { 2, { 1, 0 } };
+
+
+// Verify no warning for an unknown struct object.
+void ga0p (A0 *p)
+{
+  p->a[0] = 0;
+  p->a[3] = 0;
+  p->a[9] = 0;
+}
+
+
+// Verify warning for an extern struct object which (unlike a true
+// flexible array member) may not be initialized.
+extern A0 a0x;
+
+void ga0x ()
+{
+  a0x.a[0] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  a0x.a[3] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  a0x.a[9] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+
+// Exercise trailing one-element array members.
+
+struct A1
+{
+  char n;
+  char a[1];                    // { dg-message "destination object declared here" }
+};
+
+// Verify warning for a definition with no initializer.
+A1 a1_;
+
+void ga1_ ()
+{
+  a1_.a[0] = 0;
+  a1_.a[1] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  a1_.a[2] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+// Verify warning for access to a definition with an initializer that doesn't
+// initialize the one-element array member.
+A1 a1__ = { 0 };
+
+void ga1__ ()
+{
+  a1__.a[0] = 0;
+  a1__.a[1] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  a1__.a[2] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+// Verify warning for access to a definition with an initializer that
+// initializes the one-element array member to empty.
+A1 a1_0 = { 0, { } };
+
+void ga1_0_ ()
+{
+  a1_0.a[0] = 0;
+  a1_0.a[1] = 0;                // { dg-warning "\\\[-Wstringop-overflow" }
+  a1_0.a[2] = 0;                // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+// Verify warning for access to a definition with an initializer that
+// initializes the one-element array member.
+A1 a1_1 = { 0, { 1 } };
+
+void ga1_1 ()
+{
+  a1_1.a[0] = 0;
+  a1_1.a[1] = 0;                // { dg-warning "\\\[-Wstringop-overflow" }
+  a1_1.a[2] = 0;                // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+
+// Verify no warning for an unknown struct object.
+void ga1p (A1 *p)
+{
+  p->a[0] = 0;
+  p->a[3] = 0;
+  p->a[9] = 0;
+}
+
+
+// Verify warning for an extern struct object.  Similar to the zero-length
+// array case, a one-element trailing array can be initialized to at most
+// a single element.
+extern A1 a1x;
+
+void ga1x ()
+{
+  a1x.a[0] = 0;
+  a1x.a[3] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  a1x.a[9] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+// Exercise interior one-element array members (verify they're not
+// treated as trailing.
+
+struct A1i
+{
+  char n;
+  char a[1];                    // { dg-message "destination object declared here" }
+  char x;
+};
+
+// Verify warning for a definition with no initializer.
+A1i a1i_;
+
+void ga1i_ ()
+{
+  a1i_.a[0] = 0;
+  a1i_.a[1] = 0;                // { dg-warning "\\\[-Wstringop-overflow" }
+  a1i_.a[2] = 0;                // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+// Verify warning for access to a definition with an initializer that doesn't
+// initialize the one-element array member.
+A1i a1i__ = { 0 };
+
+void ga1i__ ()
+{
+  a1i__.a[0] = 0;
+  a1i__.a[1] = 0;                // { dg-warning "\\\[-Wstringop-overflow" }
+  a1i__.a[2] = 0;                // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+// Verify warning for access to a definition with an initializer that
+// initializes the one-element array member to empty.
+A1 a1i_0 = { 0, { } };
+
+void ga1i_0_ ()
+{
+  a1i_0.a[0] = 0;
+  a1i_0.a[1] = 0;               // { dg-warning "\\\[-Wstringop-overflow" }
+  a1i_0.a[2] = 0;               // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+// Verify warning for access to a definition with an initializer that
+// initializes the one-element array member.
+A1 a1i_1 = { 0, { 1 } };
+
+void ga1i_1 ()
+{
+  a1i_1.a[0] = 0;
+  a1i_1.a[1] = 0;               // { dg-warning "\\\[-Wstringop-overflow" }
+  a1i_1.a[2] = 0;               // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+
+// Verify no warning for an unknown struct object.
+void ga1ip (A1i *p)
+{
+  p->a[0] = 0;
+  p->a[3] = 0;                  // { dg-warning "\\\[-Wstringop-overflow" }
+  p->a[9] = 0;                  // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+
+// Verify no warning for an extern struct object.
+extern A1i a1ix;
+
+void ga1ix ()
+{
+  a1ix.a[0] = 0;
+  a1ix.a[3] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+  a1ix.a[9] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+
+// Verify non-POD classes with flexible array members.
+
+struct Bx
+{
+  char n;
+  char a[];                     // { dg-message "destination object declared here" }
+
+  // Verify the warning for a constant.
+  Bx () { a[0] = 0; }           // { dg-warning "\\\[-Wstringop-overflow" }
+
+  // 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 "\\\[-Wstringop-overflow" }
+};
+
+void gbx (void)
+{
+  struct Bx bx;
+  sink (&bx);
+}
+
+void gbxi (int i)
+{
+  struct Bx bxi (i);
+  sink (&bxi);
+}
+
+struct B0
+{
+  char n;
+  char a[0];                    // { dg-message "destination object declared here" }
+
+  B0 () { a[0] = 0; }           // { dg-warning "\\\[-Wstringop-overflow" }
+};
+
+
+void gb0 (void)
+{
+  struct B0 b0;
+  sink (&b0);
+}
+
+
+struct B1
+{
+  char n;
+  char a[1];                    // { dg-message "destination object declared here" }
+
+  B1 () { a[1] = 0; }           // { dg-warning "\\\[-Wstringop-overflow" }
+};
+
+void gb1 (void)
+{
+  struct B1 b1;
+  sink (&b1);
+}
+
+
+struct B123
+{
+  char a[123];                  // { dg-message "destination object declared here" }
+
+  B123 () { a[123] = 0; }       // { dg-warning "\\\[-Wstringop-overflow" }
+};
+
+void gb123 (void)
+{
+  struct B123 b123;
+  sink (&b123);
+}
+
+
+struct B234
+{
+  char a[234];                  // { dg-message "destination object declared here" }
+
+  B234 (int i) { a[i] = 0; }    // { dg-warning "\\\[-Wstringop-overflow" }
+};
+
+void g234 (void)
+{
+  struct B234 b234 (234);
+  sink (&b234);
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-15.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-15.c
new file mode 100644 (file)
index 0000000..12f8f9d
--- /dev/null
@@ -0,0 +1,62 @@
+/* PR middle-end/91458 - inconsistent warning for writing past the end
+   of an array member
+   Verify that the -Wstringop-overflow detection doesn't cause an ICE
+   for either kind of VLAs (member and non-member).
+   Diagnosing the accesses is the subject of pr82608.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-array-bounds" } */
+
+void sink (void*);
+
+void vla_unbounded (int n)
+{
+  char a[n];
+
+  a[0] = 0;
+  a[1] = 1;
+  a[n] = n;         // { dg-warning "\\\[-Wstringop-overflow" "pr82608" { xfail *-*-* } }
+
+  sink (&a);
+}
+
+void vla_bounded (int n)
+{
+  if (n > 32)
+    n = 32;
+
+  char a[n];
+
+  a[0] = 0;
+  a[1] = 1;
+  a[n] = n;         // { dg-warning "\\\[-Wstringop-overflow" "pr82608" { xfail *-*-* } }
+  a[69] = n;        // { dg-warning "\\\[-Wstringop-overflow" "pr82608" { xfail *-*-* } }
+
+  sink (&a);
+}
+
+
+void member_vla_unbounded (int n)
+{
+  struct S { char i, a[n]; } s;
+
+  s.a[0] = 0;
+  s.a[1] = 1;
+  s.a[n] = n;       // { dg-warning "\\\[-Wstringop-overflow" "pr82608" { xfail *-*-* } }
+
+  sink (&s);
+}
+
+void member_vla_bounded (int n)
+{
+  if (n > 32)
+    n = 32;
+
+  struct S { char i, a[n]; } s;
+
+  s.a[0] = 0;
+  s.a[1] = 1;
+  s.a[n] = n;       // { dg-warning "\\\[-Wstringop-overflow" "pr82608" { xfail *-*-* } }
+  s.a[69] = n;      // { dg-warning "\\\[-Wstringop-overflow" "pr82608" { xfail *-*-* } }
+
+  sink (&s);
+}
index d38352a0c4cc60a4f5a7d0511103739f8e946375..7bb5f52c1e5dded6a3e81a4086e4e3e79cef469d 100644 (file)
@@ -4026,16 +4026,30 @@ handle_store (gimple_stmt_iterator *gsi)
       rhs_minlen = lenrange[0];
       storing_nonzero_p = lenrange[1] > 0;
 
-      if (tree dstsize = compute_objsize (lhs, 1))
-       if (compare_tree_int (dstsize, lenrange[2]) < 0)
-         {
-           location_t loc = gimple_nonartificial_location (stmt);
-           warning_n (loc, OPT_Wstringop_overflow_,
-                      lenrange[2],
-                      "%Gwriting %u byte into a region of size %E",
-                      "%Gwriting %u bytes into a region of size %E",
-                      stmt, lenrange[2], dstsize);
-         }
+      /* Avoid issuing multiple warnings for the same LHS or statement.
+        For example, -Warray-bounds may have already been issued for
+        an out-of-bounds subscript.  */
+      if (!TREE_NO_WARNING (lhs) && !gimple_no_warning_p (stmt))
+       {
+         /* Set to the declaration referenced by LHS (if known).  */
+         tree decl = NULL_TREE;
+         if (tree dstsize = compute_objsize (lhs, 1, &decl))
+           if (compare_tree_int (dstsize, lenrange[2]) < 0)
+             {
+               location_t loc = gimple_nonartificial_location (stmt);
+               if (warning_n (loc, OPT_Wstringop_overflow_,
+                              lenrange[2],
+                              "%Gwriting %u byte into a region of size %E",
+                              "%Gwriting %u bytes into a region of size %E",
+                              stmt, lenrange[2], dstsize))
+                 {
+                   if (decl)
+                     inform (DECL_SOURCE_LOCATION (decl),
+                             "destination object declared here");
+                   gimple_set_no_warning (stmt, true);
+                 }
+             }
+       }
     }
   else
     {
index 5ec4d17f23b2d752e0455f4fb4d0e004a5409159..c95b5ade781e6c512a8f3e9621a5fa857306eede 100644 (file)
@@ -4388,8 +4388,8 @@ class vrp_prop : public ssa_propagation_engine
   void vrp_initialize (void);
   void vrp_finalize (bool);
   void check_all_array_refs (void);
-  void check_array_ref (location_t, tree, bool);
-  void check_mem_ref (location_t, tree, bool);
+  bool check_array_ref (location_t, tree, bool);
+  bool check_mem_ref (location_t, tree, bool);
   void search_for_addr_array (tree, location_t);
 
   class vr_values vr_values;
@@ -4415,9 +4415,10 @@ class vrp_prop : public ssa_propagation_engine
    array subscript is a constant, check if it is outside valid
    range. If the array subscript is a RANGE, warn if it is
    non-overlapping with valid range.
-   IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR.  */
+   IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR.
+   Returns true if a warning has been issued.  */
 
-void
+bool
 vrp_prop::check_array_ref (location_t location, tree ref,
                           bool ignore_off_by_one)
 {
@@ -4426,7 +4427,7 @@ vrp_prop::check_array_ref (location_t location, tree ref,
   tree low_bound, up_bound, up_bound_p1;
 
   if (TREE_NO_WARNING (ref))
-    return;
+    return false;
 
   low_sub = up_sub = TREE_OPERAND (ref, 1);
   up_bound = array_ref_up_bound (ref);
@@ -4541,12 +4542,16 @@ vrp_prop::check_array_ref (location_t location, tree ref,
   if (warned)
     {
       ref = TREE_OPERAND (ref, 0);
+      if (TREE_CODE (ref) == COMPONENT_REF)
+       ref = TREE_OPERAND (ref, 1);
 
       if (DECL_P (ref))
        inform (DECL_SOURCE_LOCATION (ref), "while referencing %qD", ref);
 
       TREE_NO_WARNING (ref) = 1;
     }
+
+  return warned;
 }
 
 /* Checks one MEM_REF in REF, located at LOCATION, for out-of-bounds
@@ -4556,14 +4561,15 @@ vrp_prop::check_array_ref (location_t location, tree ref,
    with valid range.
    IGNORE_OFF_BY_ONE is true if the MEM_REF is inside an ADDR_EXPR
    (used to allow one-past-the-end indices for code that takes
-   the address of the just-past-the-end element of an array).  */
+   the address of the just-past-the-end element of an array).
+   Returns true if a warning has been issued.  */
 
-void
+bool
 vrp_prop::check_mem_ref (location_t location, tree ref,
                         bool ignore_off_by_one)
 {
   if (TREE_NO_WARNING (ref))
-    return;
+    return false;
 
   tree arg = TREE_OPERAND (ref, 0);
   /* The constant and variable offset of the reference.  */
@@ -4615,7 +4621,7 @@ vrp_prop::check_mem_ref (location_t location, tree ref,
          continue;
        }
       else
-       return;
+       return false;
 
       /* VAROFF should always be a SSA_NAME here (and not even
         INTEGER_CST) but there's no point in taking chances.  */
@@ -4677,10 +4683,10 @@ vrp_prop::check_mem_ref (location_t location, tree ref,
       arg = TREE_OPERAND (arg, 0);
       if (TREE_CODE (arg) != STRING_CST
          && TREE_CODE (arg) != VAR_DECL)
-       return;
+       return false;
     }
   else
-    return;
+    return false;
 
   /* 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
@@ -4695,7 +4701,7 @@ vrp_prop::check_mem_ref (location_t location, tree ref,
       || !COMPLETE_TYPE_P (reftype)
       || TREE_CODE (TYPE_SIZE_UNIT (reftype)) != INTEGER_CST
       || RECORD_OR_UNION_TYPE_P (reftype))
-    return;
+    return false;
 
   offset_int eltsize;
   if (TREE_CODE (reftype) == ARRAY_TYPE)
@@ -4797,11 +4803,11 @@ vrp_prop::check_mem_ref (location_t location, tree ref,
 
       if (warned)
        TREE_NO_WARNING (ref) = 1;
-      return;
+      return warned;
     }
 
   if (warn_array_bounds < 2)
-    return;
+    return false;
 
   /* At level 2 check also intermediate offsets.  */
   int i = 0;
@@ -4812,8 +4818,13 @@ vrp_prop::check_mem_ref (location_t location, tree ref,
       if (warning_at (location, OPT_Warray_bounds,
                      "intermediate array offset %wi is outside array bounds "
                      "of %qT", tmpidx, reftype))
-       TREE_NO_WARNING (ref) = 1;
+       {
+         TREE_NO_WARNING (ref) = 1;
+         return true;
+       }
     }
+
+  return false;
 }
 
 /* Searches if the expr T, located at LOCATION computes
@@ -4825,10 +4836,14 @@ vrp_prop::search_for_addr_array (tree t, location_t location)
   /* Check each ARRAY_REF and MEM_REF in the reference chain. */
   do
     {
+      bool warned = false;
       if (TREE_CODE (t) == ARRAY_REF)
-       check_array_ref (location, t, true /*ignore_off_by_one*/);
+       warned = check_array_ref (location, t, true /*ignore_off_by_one*/);
       else if (TREE_CODE (t) == MEM_REF)
-       check_mem_ref (location, t, true /*ignore_off_by_one*/);
+       warned = check_mem_ref (location, t, true /*ignore_off_by_one*/);
+
+      if (warned)
+       TREE_NO_WARNING (t) = true;
 
       t = TREE_OPERAND (t, 0);
     }
@@ -4920,16 +4935,20 @@ check_array_bounds (tree *tp, int *walk_subtree, void *data)
 
   *walk_subtree = TRUE;
 
+  bool warned = false;
   vrp_prop *vrp_prop = (class vrp_prop *)wi->info;
   if (TREE_CODE (t) == ARRAY_REF)
-    vrp_prop->check_array_ref (location, t, false /*ignore_off_by_one*/);
+    warned = vrp_prop->check_array_ref (location, t, false/*ignore_off_by_one*/);
   else if (TREE_CODE (t) == MEM_REF)
-    vrp_prop->check_mem_ref (location, t, false /*ignore_off_by_one*/);
+    warned = vrp_prop->check_mem_ref (location, t, false /*ignore_off_by_one*/);
   else if (TREE_CODE (t) == ADDR_EXPR)
     {
       vrp_prop->search_for_addr_array (t, location);
       *walk_subtree = FALSE;
     }
+  /* Propagate the no-warning bit to the outer expression.  */
+  if (warned)
+    TREE_NO_WARNING (t) = true;
 
   return NULL_TREE;
 }