Extend -Warray-bounds to detect out-of-bounds accesses to array parameters.
authorMartin Sebor <msebor@redhat.com>
Sat, 19 Sep 2020 23:47:29 +0000 (17:47 -0600)
committerMartin Sebor <msebor@redhat.com>
Sat, 19 Sep 2020 23:56:13 +0000 (17:56 -0600)
gcc/ChangeLog:

PR middle-end/82608
PR middle-end/94195
PR c/50584
PR middle-end/84051
* gimple-array-bounds.cc (get_base_decl): New function.
(get_ref_size): New function.
(trailing_array): New function.
(array_bounds_checker::check_array_ref): Call them.  Handle arrays
declared in function parameters.
(array_bounds_checker::check_mem_ref):  Same.  Handle references to
dynamically allocated arrays.

gcc/testsuite/ChangeLog:

PR middle-end/82608
PR middle-end/94195
PR c/50584
PR middle-end/84051
* c-c++-common/Warray-bounds.c: Adjust.
* gcc.dg/Wbuiltin-declaration-mismatch-9.c: Adjust.
* gcc.dg/Warray-bounds-63.c: New test.
* gcc.dg/Warray-bounds-64.c: New test.
* gcc.dg/Warray-bounds-65.c: New test.
* gcc.dg/Warray-bounds-66.c: New test.
* gcc.dg/Warray-bounds-67.c: New test.

gcc/gimple-array-bounds.cc
gcc/testsuite/c-c++-common/Warray-bounds.c
gcc/testsuite/gcc.dg/Warray-bounds-63.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Warray-bounds-64.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Warray-bounds-65.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Warray-bounds-66.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Warray-bounds-67.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wbuiltin-declaration-mismatch-9.c

index c2dd666..b93ef7a 100644 (file)
@@ -36,6 +36,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "vr-values.h"
 #include "domwalk.h"
 #include "tree-cfg.h"
+#include "attribs.h"
+#include "builtins.h"
 
 // This purposely returns a value_range, not a value_range_equiv, to
 // break the dependency on equivalences for this pass.
@@ -46,19 +48,137 @@ array_bounds_checker::get_value_range (const_tree op)
   return ranges->get_value_range (op);
 }
 
+/* Try to determine the DECL that REF refers to.  Return the DECL or
+   the expression closest to it.  Used in informational notes pointing
+   to referenced objects or function parameters.  */
+
+static tree
+get_base_decl (tree ref)
+{
+  tree base = get_base_address (ref);
+  if (DECL_P (base))
+    return base;
+
+  if (TREE_CODE (base) == MEM_REF)
+    base = TREE_OPERAND (base, 0);
+
+  if (TREE_CODE (base) != SSA_NAME)
+    return base;
+
+  do
+    {
+      gimple *def = SSA_NAME_DEF_STMT (base);
+      if (gimple_assign_single_p (def))
+       {
+         base = gimple_assign_rhs1 (def);
+         if (TREE_CODE (base) != ASSERT_EXPR)
+           return base;
+
+         base = TREE_OPERAND (base, 0);
+         if (TREE_CODE (base) != SSA_NAME)
+           return base;
+
+         continue;
+       }
+
+      if (!gimple_nop_p (def))
+       return base;
+
+      break;
+    } while (true);
+
+  tree var = SSA_NAME_VAR (base);
+  if (TREE_CODE (var) != PARM_DECL)
+    return base;
+
+  return var;
+}
+
+/* Return the constant byte size of the object or type referenced by
+   the MEM_REF ARG.  On success, set *PREF to the DECL or expression
+   ARG refers to.  Otherwise return null.  */
+
+static tree
+get_ref_size (tree arg, tree *pref)
+{
+  if (TREE_CODE (arg) != MEM_REF)
+    return NULL_TREE;
+
+  arg = TREE_OPERAND (arg, 0);
+  tree type = TREE_TYPE (arg);
+  if (!POINTER_TYPE_P (type))
+    return NULL_TREE;
+
+  type = TREE_TYPE (type);
+  if (TREE_CODE (type) != ARRAY_TYPE)
+    return NULL_TREE;
+
+  tree nbytes = TYPE_SIZE_UNIT (type);
+  if (!nbytes || TREE_CODE (nbytes) != INTEGER_CST)
+    return NULL_TREE;
+
+  *pref = get_base_decl (arg);
+  return nbytes;
+}
+
+/* Return true if REF is (likely) an ARRAY_REF to a trailing array member
+   of a struct.  It refines array_at_struct_end_p by detecting a pointer
+   to an array and an array parameter declared using the [N] syntax (as
+   opposed to a pointer) and returning false.  Set *PREF to the decl or
+   expression REF refers to.  */
+
+static bool
+trailing_array (tree arg, tree *pref)
+{
+  tree ref = arg;
+  tree base = get_base_decl (arg);
+  while (TREE_CODE (ref) == ARRAY_REF || TREE_CODE (ref) == MEM_REF)
+    ref = TREE_OPERAND (ref, 0);
+
+  if (TREE_CODE (ref) == COMPONENT_REF)
+    {
+      *pref = TREE_OPERAND (ref, 1);
+      tree type = TREE_TYPE (*pref);
+      if (TREE_CODE (type) == ARRAY_TYPE)
+       {
+         /* A multidimensional trailing array is not considered special
+            no matter what its major bound is.  */
+         type = TREE_TYPE (type);
+         if (TREE_CODE (type) == ARRAY_TYPE)
+           return false;
+       }
+    }
+  else
+    *pref = base;
+
+  tree basetype = TREE_TYPE (base);
+  if (TREE_CODE (base) == PARM_DECL
+      && POINTER_TYPE_P (basetype))
+    {
+      tree ptype = TREE_TYPE (basetype);
+      if (TREE_CODE (ptype) == ARRAY_TYPE)
+       return false;
+    }
+
+  return array_at_struct_end_p (arg);
+}
+
 /* Checks one ARRAY_REF in REF, located at LOCUS. Ignores flexible
    arrays and "struct" hacks. If VRP can determine that the 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.  Returns true if a warning has been issued.  */
+   a ADDR_EXPR.  Return  true if a warning has been issued or if
+   no-warning is set.  */
 
 bool
 array_bounds_checker::check_array_ref (location_t location, tree ref,
                                       bool ignore_off_by_one)
 {
   if (TREE_NO_WARNING (ref))
-    return false;
+    /* Return true to have the caller prevent warnings for enclosing
+       refs.  */
+    return true;
 
   tree low_sub = TREE_OPERAND (ref, 1);
   tree up_sub = low_sub;
@@ -74,8 +194,7 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
 
   if (!up_bound
       || TREE_CODE (up_bound) != INTEGER_CST
-      || (warn_array_bounds < 2
-         && array_at_struct_end_p (ref)))
+      || (warn_array_bounds < 2 && trailing_array (ref, &decl)))
     {
       /* Accesses to trailing arrays via pointers may access storage
         beyond the types array bounds.  For such arrays, or for flexible
@@ -116,7 +235,14 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
              poly_int64 off;
              if (tree base = get_addr_base_and_unit_offset (arg, &off))
                {
-                 if (!compref && DECL_P (base))
+                 if (TREE_CODE (base) == MEM_REF)
+                   {
+                     /* Try to determine the size from a pointer to
+                        an array if BASE is one.  */
+                     if (tree size = get_ref_size (base, &decl))
+                       maxbound = size;
+                   }
+                 else if (!compref && DECL_P (base))
                    if (tree basesize = DECL_SIZE_UNIT (base))
                      if (TREE_CODE (basesize) == INTEGER_CST)
                        {
@@ -217,7 +343,13 @@ array_bounds_checker::check_array_ref (location_t location, tree ref,
          fprintf (dump_file, "\n");
        }
 
-      ref = decl ? decl : TREE_OPERAND (ref, 0);
+      /* Avoid more warnings when checking more significant subscripts
+        of the same expression.  */
+      ref = TREE_OPERAND (ref, 0);
+      TREE_NO_WARNING (ref) = 1;
+
+      if (decl)
+       ref = decl;
 
       tree rec = NULL_TREE;
       if (TREE_CODE (ref) == COMPONENT_REF)
@@ -235,8 +367,6 @@ array_bounds_checker::check_array_ref (location_t location, tree 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;
     }
 
   return warned;
@@ -266,8 +396,8 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
 
   const offset_int maxobjsize = tree_to_shwi (max_object_size ());
 
-  /* The array or string constant bounds in bytes.  Initially set
-     to [-MAXOBJSIZE - 1, MAXOBJSIZE]  until a tighter bound is
+  /* The zero-based array or string constant bounds in bytes.  Initially
+     set to [-MAXOBJSIZE - 1, MAXOBJSIZE]  until a tighter bound is
      determined.  */
   offset_int arrbounds[2] = { -maxobjsize - 1, maxobjsize };
 
@@ -275,7 +405,7 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
      to be valid, not only does the final offset/subscript must be
      in bounds but all intermediate offsets should be as well.
      GCC may be able to deal gracefully with such out-of-bounds
-     offsets so the checking is only enbaled at -Warray-bounds=2
+     offsets so the checking is only enabled at -Warray-bounds=2
      where it may help detect bugs in uses of the intermediate
      offsets that could otherwise not be detectable.  */
   offset_int ioff = wi::to_offset (fold_convert (ptrdiff_type_node, cstoff));
@@ -284,7 +414,10 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
   /* The range of the byte offset into the reference.  */
   offset_int offrange[2] = { 0, 0 };
 
-  const value_range *vr = NULL;
+  /* The statement used to allocate the array or null.  */
+  gimple *alloc_stmt = NULL;
+  /* For an allocation statement, the low bound of the size range.  */
+  offset_int minbound = 0;
 
   /* Determine the offsets and increment OFFRANGE for the bounds of each.
      The loop computes the range of the final offset for expressions such
@@ -294,6 +427,35 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
   for (unsigned n = 0; TREE_CODE (arg) == SSA_NAME && n < limit; ++n)
     {
       gimple *def = SSA_NAME_DEF_STMT (arg);
+      if (is_gimple_call (def))
+       {
+         /* Determine the byte size of the array from an allocation call.  */
+         wide_int sizrng[2];
+         if (gimple_call_alloc_size (def, sizrng))
+           {
+             arrbounds[0] = 0;
+             arrbounds[1] = offset_int::from (sizrng[1], UNSIGNED);
+             minbound = offset_int::from (sizrng[0], UNSIGNED);
+             alloc_stmt = def;
+           }
+         break;
+       }
+
+      if (gimple_nop_p (def))
+       {
+         /* For a function argument try to determine the byte size
+            of the array from the current function declaratation
+            (e.g., attribute access or related).  */
+         wide_int wr[2];
+         tree ref = gimple_parm_array_size (arg, wr);
+         if (!ref)
+           break;
+         arrbounds[0] = offset_int::from (wr[0], UNSIGNED);
+         arrbounds[1] = offset_int::from (wr[1], UNSIGNED);
+         arg = ref;
+         break;
+       }
+
       if (!is_gimple_assign (def))
        break;
 
@@ -316,7 +478,7 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
       if (TREE_CODE (varoff) != SSA_NAME)
        break;
 
-      vr = get_value_range (varoff);
+      const value_range* const vr = get_value_range (varoff);
       if (!vr || vr->undefined_p () || vr->varying_p ())
        break;
 
@@ -366,79 +528,104 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
        offrange[1] = arrbounds[1];
     }
 
-  if (TREE_CODE (arg) == ADDR_EXPR)
+  tree reftype = NULL_TREE;
+  offset_int eltsize = -1;
+  if (arrbounds[0] >= 0)
+    {
+      /* The byte size of the array has already been determined above
+        based on a pointer ARG.  Set ELTSIZE to the size of the type
+        it points to and REFTYPE to the array with the size, rounded
+        down as necessary.  */
+      reftype = TREE_TYPE (TREE_TYPE (arg));
+      if (TREE_CODE (reftype) == ARRAY_TYPE)
+       reftype = TREE_TYPE (reftype);
+      if (tree refsize = TYPE_SIZE_UNIT (reftype))
+       if (TREE_CODE (refsize) == INTEGER_CST)
+         eltsize = wi::to_offset (refsize);
+
+      if (eltsize < 0)
+       return false;
+
+      offset_int nelts = arrbounds[1] / eltsize;
+      reftype = build_array_type_nelts (reftype, nelts.to_uhwi ());
+    }
+  else if (TREE_CODE (arg) == ADDR_EXPR)
     {
       arg = TREE_OPERAND (arg, 0);
       if (TREE_CODE (arg) != STRING_CST
          && TREE_CODE (arg) != PARM_DECL
          && TREE_CODE (arg) != VAR_DECL)
        return false;
-    }
-  else
-    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
-     a reference/subscript via a pointer to an object that is not
-     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)
-    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
+        a reference/subscript via a pointer to an object that is not
+        an element of an array.  Incomplete types are excluded as well
+        because their size is not known.  */
+      reftype = TREE_TYPE (arg);
+      if (POINTER_TYPE_P (reftype)
+         || !COMPLETE_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;
+      /* 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;
+      /* FIXME: Should this be 1 for Fortran?  */
+      arrbounds[0] = 0;
 
-  offset_int eltsize;
-  if (TREE_CODE (reftype) == ARRAY_TYPE)
-    {
-      eltsize = wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (reftype)));
-      if (tree dom = TYPE_DOMAIN (reftype))
+      if (TREE_CODE (reftype) == ARRAY_TYPE)
        {
-         tree bnds[] = { TYPE_MIN_VALUE (dom), TYPE_MAX_VALUE (dom) };
-         if (TREE_CODE (arg) == COMPONENT_REF)
+         /* Set to the size of the array element (and adjust below).  */
+         eltsize = wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (reftype)));
+         /* Use log2 of size to convert the array byte size in to its
+            upper bound in elements.  */
+         const offset_int eltsizelog2 = wi::floor_log2 (eltsize);
+         if (tree dom = TYPE_DOMAIN (reftype))
            {
-             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));
+             tree bnds[] = { TYPE_MIN_VALUE (dom), TYPE_MAX_VALUE (dom) };
+             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, eltsizelog2);
+               }
+             else if (array_at_struct_end_p (arg) || !bnds[0] || !bnds[1])
+               arrbounds[1] = wi::lrshift (maxobjsize, eltsizelog2);
+             else
+               arrbounds[1] = (wi::to_offset (bnds[1]) - wi::to_offset (bnds[0])
+                               + 1) * 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])
-                           + 1) * eltsize;
+           arrbounds[1] = wi::lrshift (maxobjsize, eltsizelog2);
+
+         /* Determine a tighter bound of the non-array element type.  */
+         tree eltype = TREE_TYPE (reftype);
+         while (TREE_CODE (eltype) == ARRAY_TYPE)
+           eltype = TREE_TYPE (eltype);
+         eltsize = wi::to_offset (TYPE_SIZE_UNIT (eltype));
        }
       else
-       arrbounds[1] = wi::lrshift (maxobjsize, wi::floor_log2 (eltsize));
-
-      /* Determine a tighter bound of the non-array element type.  */
-      tree eltype = TREE_TYPE (reftype);
-      while (TREE_CODE (eltype) == ARRAY_TYPE)
-       eltype = TREE_TYPE (eltype);
-      eltsize = wi::to_offset (TYPE_SIZE_UNIT (eltype));
+       {
+         eltsize = 1;
+         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);
+       }
     }
   else
-    {
-      eltsize = 1;
-      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);
-    }
+    return false;
 
   offrange[0] += ioff;
   offrange[1] += ioff;
@@ -448,11 +635,25 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
      of an array) but always use the stricter bound in diagnostics. */
   offset_int ubound = arrbounds[1];
   if (ignore_off_by_one)
-    ubound += 1;
+    ubound += eltsize;
 
-  if (arrbounds[0] == arrbounds[1]
-      || offrange[0] >= ubound
-      || offrange[1] < arrbounds[0])
+  bool warned = false;
+  /* Set if the lower bound of the subscript is out of bounds.  */
+  const bool lboob = (arrbounds[0] == arrbounds[1]
+                     || offrange[0] >= ubound
+                     || offrange[1] < arrbounds[0]);
+  /* Set if only the upper bound of the subscript is out of bounds.
+     This can happen when using a bigger type to index into an array
+     of a smaller type, as is common with unsigned char.  */
+  tree axstype = TREE_TYPE (ref);
+  offset_int axssize = 0;
+  if (TREE_CODE (axstype) != UNION_TYPE)
+    if (tree access_size = TYPE_SIZE_UNIT (axstype))
+      if (TREE_CODE (access_size) == INTEGER_CST)
+       axssize = wi::to_offset (access_size);
+
+  const bool uboob = !lboob && offrange[0] + axssize > ubound;
+  if (lboob || uboob)
     {
       /* Treat a reference to a non-array object as one to an array
         of a single element.  */
@@ -471,8 +672,10 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
          offrange[0] = offrange[0] / wi::to_offset (size);
          offrange[1] = offrange[1] / wi::to_offset (size);
        }
+    }
 
-      bool warned;
+  if (lboob)
+    {
       if (offrange[0] == offrange[1])
        warned = warning_at (location, OPT_Warray_bounds,
                             "array subscript %wi is outside array bounds "
@@ -484,12 +687,66 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
                             "array bounds of %qT",
                             offrange[0].to_shwi (),
                             offrange[1].to_shwi (), reftype);
-      if (warned && DECL_P (arg))
+    }
+  else if (uboob && !ignore_off_by_one)
+    {
+      tree backtype = reftype;
+      if (alloc_stmt)
+       /* If the memory was dynamically allocated refer to it as if
+          it were an untyped array of bytes.  */
+       backtype = build_array_type_nelts (unsigned_char_type_node,
+                                          arrbounds[1].to_uhwi ());
+
+      warned = warning_at (location, OPT_Warray_bounds,
+                          "array subscript %<%T[%wi]%> is partly "
+                          "outside array bounds of %qT",
+                          axstype, offrange[0].to_shwi (), backtype);
+    }
+
+  if (warned)
+    {
+      if (DECL_P (arg))
        inform (DECL_SOURCE_LOCATION (arg), "while referencing %qD", arg);
+      else if (alloc_stmt)
+       {
+         location_t loc = gimple_location (alloc_stmt);
+         if (gimple_call_builtin_p (alloc_stmt, BUILT_IN_ALLOCA_WITH_ALIGN))
+           {
+             if (minbound == arrbounds[1])
+               inform (loc, "referencing a variable length array "
+                       "of size %wu", minbound.to_uhwi ());
+             else
+               inform (loc, "referencing a variable length array "
+                       "of size between %wu and %wu",
+                       minbound.to_uhwi (), arrbounds[1].to_uhwi ());
+           }
+         else if (tree fndecl = gimple_call_fndecl (alloc_stmt))
+           {
+             if (minbound == arrbounds[1])
+               inform (loc, "referencing an object of size %wu "
+                       "allocated by %qD",
+                       minbound.to_uhwi (), fndecl);
+             else
+               inform (loc, "referencing an object of size between "
+                       "%wu and %wu allocated by %qD",
+                       minbound.to_uhwi (), arrbounds[1].to_uhwi (), fndecl);
+           }
+         else
+           {
+             tree fntype = gimple_call_fntype (alloc_stmt);
+             if (minbound == arrbounds[1])
+               inform (loc, "referencing an object of size %wu "
+                       "allocated by %qT",
+                       minbound.to_uhwi (), fntype);
+             else
+               inform (loc, "referencing an object of size between "
+                       "%wu and %wu allocated by %qT",
+                       minbound.to_uhwi (), arrbounds[1].to_uhwi (), fntype);
+           }
+       }
 
-      if (warned)
-       TREE_NO_WARNING (ref) = 1;
-      return warned;
+      TREE_NO_WARNING (ref) = 1;
+      return true;
     }
 
   if (warn_array_bounds < 2)
index 391e636..815badc 100644 (file)
@@ -2,7 +2,7 @@
    large index
    { dg-do compile }
    { dg-require-effective-target alloca }
-   { dg-options "-O2 -Warray-bounds -ftrack-macro-expansion=0" }  */
+   { dg-options "-O2 -Warray-bounds -Wno-stringop-overread -ftrack-macro-expansion=0" }  */
 
 #include "../gcc.dg/range.h"
 
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-63.c b/gcc/testsuite/gcc.dg/Warray-bounds-63.c
new file mode 100644 (file)
index 0000000..0583d23
--- /dev/null
@@ -0,0 +1,53 @@
+/* PR middle-end/94195 - missing warning reading a smaller object via
+   an lvalue of a larger type
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+typedef __INT16_TYPE__ int16_t;
+typedef __SIZE_TYPE__  size_t;
+
+void* alloca (size_t);
+
+void sink (void*);
+
+
+void byte_store_to_decl (void)
+{
+  struct S6 { char a[6]; } s;   // { dg-message "referencing 's'" }
+
+  char *p = (char*)&s;
+
+  p[0] = 0; p[1] = 1; p[2] = 2; p[3] = 3; p[4] = 4; p[5] = 5;
+  p[6] = 6;                     // { dg-warning "array subscript 6 is outside array bounds of 'struct S6\\\[1]" }
+
+  sink (&s);
+}
+
+
+void word_store_to_decl (void)
+{
+  struct S6 { char a[6]; } s;   // { dg-message "referencing 's'" }
+
+  char *p = (char*)&s;
+
+  int16_t *q = (int16_t*)(p + 1);
+
+  q[0] = 0; q[1] = 1;
+  q[2] = 2;                     // { dg-warning "array subscript 'int16_t {aka short int}\\\[2]' is partly outside array bounds of 'struct S6\\\[1]'" }
+
+  sink (&s);
+}
+
+
+void word_store_to_alloc (void)
+{
+  struct S6 { char a[6]; } *p;
+  p = alloca (sizeof *p);       // { dg-message "referencing an object of size 6 allocated by 'alloca'" }
+
+  int16_t *q = (int16_t*)((char*)p + 1);
+
+  q[0] = 0; q[1] = 1;
+  q[2] = 2;                     // { dg-warning "array subscript 'int16_t {aka short int}\\\[2]' is partly outside array bounds of 'unsigned char\\\[6]'" }
+
+  sink (p);
+}
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-64.c b/gcc/testsuite/gcc.dg/Warray-bounds-64.c
new file mode 100644 (file)
index 0000000..88b88de
--- /dev/null
@@ -0,0 +1,60 @@
+/* PR c/50584 - No warning for passing small array to C99 static array
+   declarator
+
+   Verify that out-of-bounds accesses to array arguments are diagnosed,
+   both to ordinary array parameters with constant bounds and to array
+   parameters declared static.  This is the converse of what PR 50584
+   asks for.
+
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Warray-parameter -Wno-vla-paramater" } */
+
+#define NOIPA __attribute__  ((noipa))
+
+void sink (void*, ...);
+
+#define T(...) sink (0, __VA_ARGS__)
+
+
+NOIPA void fca1 (char a[1])
+{
+  T (a[0]);
+  T (a[1]);                   // { dg-warning "-Warray-bounds" }
+}
+
+NOIPA void fcas1 (char a[static 1])
+{
+  T (a[0]);
+  T (a[1]);                   // { dg-warning "-Warray-bounds" }
+}
+
+NOIPA void fca2 (char a[2])
+{
+  T (a[0]); T (a[1]);
+  T (a[2]);                   // { dg-warning "-Warray-bounds" }
+}
+
+NOIPA void fcas2 (char a[static 2])
+{
+  T (a[0]); T (a[1]);
+  T (a[2]);                   // { dg-warning "-Warray-bounds" }
+}
+
+NOIPA void fca3 (char a[3])
+{
+  T (a[0]); T (a[1]); T (a[2]);
+  T (a[3]);                   // { dg-warning "-Warray-bounds" }
+}
+
+NOIPA void fcas3 (char a[static 3])
+{
+  T (a[0]); T (a[1]); T (a[2]);
+  T (a[3]);                   // { dg-warning "-Warray-bounds" }
+}
+
+
+NOIPA void fca1_1 (char a[1][1])
+{
+  T (a[0][0]);
+  T (a[0][1]);                // { dg-warning "-Warray-bounds" }
+}
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-65.c b/gcc/testsuite/gcc.dg/Warray-bounds-65.c
new file mode 100644 (file)
index 0000000..6bd50d0
--- /dev/null
@@ -0,0 +1,202 @@
+/* PR middle-end/84051 - missing -Warray-bounds on an out-of-bounds access
+   via an array pointer
+   { dg-do compile }
+   { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+void sink (void*, ...);
+#define T(x) sink (0, x)
+
+void
+test_note (int (*pia3)[3])    // { dg-message "while referencing 'pia3'" }
+{
+  int i = 0;
+  T ((*pia3)[i++]);
+  T ((*pia3)[i++]);
+  T ((*pia3)[i++]);
+  T ((*pia3)[i++]);           // { dg-warning "array subscript 3 is (above|outside) array bounds of 'int\\\[3]'" }
+  T ((*pia3)[i++]);           // { dg-warning "array subscript 4 is (above|outside) array bounds of 'int\\\[3]'" }
+
+  {
+    /* Regrettably, the following isn't diagnosed because it's represented
+       the same as the possibly valid access below:
+         MEM[(int *)a_1(D) + 36B] = 0;  */
+    int *p0 = pia3[0];
+    T (p0[3]);                // { dg-warning "array subscript 3 is (above|outside) array bounds of 'int\\\[3]'" "pr?????" { xfail *-*-* } }
+
+    int *p1 = pia3[3];
+    T (p1[0]);                // okay
+  }
+}
+
+void test_a1_cst (_Bool (*pba0)[0], char (*pca1)[1],
+                 short (*psa2)[2], int (*pia3)[3])
+{
+  T ((*pba0)[-1]);            // { dg-warning "array subscript -1 is (above|outside) array bounds of '_Bool\\\[0]'" }
+  T ((*pba0)[0]);             // { dg-warning "array subscript 0 is (above|outside) array bounds of '_Bool\\\[0]'" }
+  T ((*pba0)[1]);             // { dg-warning "array subscript 1 is (above|outside) array bounds of '_Bool\\\[0]'" }
+  T ((*pba0)[2]);             // { dg-warning "array subscript 2 is (above|outside) array bounds of '_Bool\\\[0]'" }
+  T ((*pba0)[12]);            // { dg-warning "array subscript 12 is (above|outside) array bounds of '_Bool\\\[0]'" }
+
+  T ((*pca1)[-1]);            // { dg-warning "array subscript -1 is (below|outside) array bounds of 'char\\\[1]'" }
+  T ((*pca1)[0]);
+  T ((*pca1)[1]);             // { dg-warning "array subscript 1 is (above|outside) array bounds of 'char\\\[1]'" }
+  T ((*pca1)[2]);             // { dg-warning "array subscript 2 is (above|outside) array bounds of 'char\\\[1]'" }
+  T ((*pca1)[123]);           // { dg-warning "array subscript 123 is (above|outside) array bounds of 'char\\\[1]'" }
+
+  T ((*psa2)[-1]);            // { dg-warning "array subscript -1 is (below|outside) array bounds of 'short int\\\[2]'" }
+  T ((*psa2)[0]);
+  T ((*psa2)[1]);
+  T ((*psa2)[2]);             // { dg-warning "array subscript 2 is (above|outside) array bounds of 'short int\\\[2]'" }
+  T ((*psa2)[1234]);          // { dg-warning "array subscript 1234 is (above|outside) array bounds of 'short int\\\[2]'" }
+
+  T ((*pia3)[-1]);            // { dg-warning "array subscript -1 is (below|outside) array bounds of 'int\\\[3]'" }
+  T ((*pia3)[0]);
+  T ((*pia3)[1]);
+  T ((*pia3)[2]);
+  T ((*pia3)[3]);             // { dg-warning "array subscript 3 is (above|outside) array bounds of 'int\\\[3]'" }
+  T ((*pia3)[12345]);         // { dg-warning "array subscript 12345 is (above|outside) array bounds of 'int\\\[3]'" }
+}
+
+
+void test_a2_cst (_Bool (*pba0_1)[0][1], char (*pca1_2)[1][2],
+                 short (*psa2_3)[2][3], int (*pia3_4)[3][4])
+{
+  T ((*pba0_1)[-1][-1]);        // { dg-warning "array subscript -1 is (below|outside) array bounds of '_Bool\\\[1]'" }
+  T ((*pba0_1)[-1][0]);         // { dg-warning "array subscript -1 is (above|outside) array bounds of '_Bool\\\[0]\\\[1]'" }
+
+  T ((*pba0_1)[0][-1]);         // { dg-warning "array subscript -1 is (below|outside) array bounds of '_Bool\\\[1]'" }
+  T ((*pba0_1)[0][0]);          // { dg-warning "array subscript 0 is (above|outside) array bounds of '_Bool\\\[0]\\\[1]'" }
+  T ((*pba0_1)[0][1]);          // { dg-warning "array subscript 1 is (above|outside) array bounds of '_Bool\\\[1]'" }
+  T ((*pba0_1)[0][2]);          // { dg-warning "array subscript 2 is (above|outside) array bounds of '_Bool\\\[1]'" }
+  T ((*pba0_1)[0][12]);         // { dg-warning "array subscript 12 is (above|outside) array bounds of '_Bool\\\[1]'" }
+
+  T ((*pba0_1)[1][-1]);         // { dg-warning "array subscript -1 is (below|outside) array bounds of '_Bool\\\[1]'" }
+  T ((*pba0_1)[1][0]);          // { dg-warning "array subscript 1 is (above|outside) array bounds of '_Bool\\\[0]\\\[1]'" }
+  T ((*pba0_1)[1][1]);          // { dg-warning "array subscript 1 is (above|outside) array bounds of '_Bool\\\[1]'" }
+  T ((*pba0_1)[1][2]);          // { dg-warning "array subscript 2 is (above|outside) array bounds of '_Bool\\\[1]'" }
+  T ((*pba0_1)[1][12]);         // { dg-warning "array subscript 12 is (above|outside) array bounds of '_Bool\\\[1]'" }
+
+
+  T ((*pca1_2)[0][0]);
+  T ((*pca1_2)[0][1]);
+  T ((*pca1_2)[0][2]);          // { dg-warning "array subscript 2 is (above|outside) array bounds of 'char\\\[2]'" }
+
+  T ((*pca1_2)[1][0]);          // { dg-warning "array subscript 1 is (above|outside) array bounds of 'char\\\[1]\\\[2]'" }
+  T ((*pca1_2)[1][1]);          // { dg-warning "array subscript 1 is (above|outside) array bounds of 'char\\\[1]\\\[2]'" }
+  T ((*pca1_2)[1][2]);          // { dg-warning "array subscript 2 is (above|outside) array bounds of 'char\\\[2]'" }
+
+
+  T ((*psa2_3)[0][0]);
+  T ((*psa2_3)[0][1]);
+  T ((*psa2_3)[0][2]);
+  T ((*psa2_3)[0][3]);          // { dg-warning "array subscript 3 is (above|outside) array bounds of 'short int\\\[3]'" }
+
+  T ((*psa2_3)[1][0]);
+  T ((*psa2_3)[1][1]);
+  T ((*psa2_3)[1][2]);
+  T ((*psa2_3)[1][3]);          // { dg-warning "array subscript 3 is (above|outside) array bounds of 'short int\\\[3]'" }
+
+  T ((*psa2_3)[2][0]);          // { dg-warning "array subscript 2 is (above|outside) array bounds of 'short int\\\[2]\\\[3]'" }
+  T ((*psa2_3)[2][1]);          // { dg-warning "array subscript 2 is (above|outside) array bounds of 'short int\\\[2]\\\[3]'" }
+  T ((*psa2_3)[2][2]);          // { dg-warning "array subscript 2 is (above|outside) array bounds of 'short int\\\[2]\\\[3]'" }
+  T ((*psa2_3)[2][3]);          // { dg-warning "array subscript 3 is (above|outside) array bounds of 'short int\\\[3]'" }
+
+
+  T ((*pia3_4)[0][0]);
+  T ((*pia3_4)[0][1]);
+  T ((*pia3_4)[0][2]);
+  T ((*pia3_4)[0][3]);
+  T ((*pia3_4)[0][4]);          // { dg-warning "array subscript 4 is (above|outside) array bounds of 'int\\\[4]'" }
+
+  T ((*pia3_4)[1][0]);
+  T ((*pia3_4)[1][1]);
+  T ((*pia3_4)[1][2]);
+  T ((*pia3_4)[1][3]);
+  T ((*pia3_4)[1][4]);          // { dg-warning "array subscript 4 is (above|outside) array bounds of 'int\\\[4]'" }
+
+  T ((*pia3_4)[2][0]);
+  T ((*pia3_4)[2][1]);
+  T ((*pia3_4)[2][2]);
+  T ((*pia3_4)[2][3]);
+  T ((*pia3_4)[2][4]);          // { dg-warning "array subscript 4 is (above|outside) array bounds of 'int\\\[4]'" }
+
+  T ((*pia3_4)[3][0]);          // { dg-warning "array subscript 3 is (above|outside) array bounds of 'int\\\[3]\\\[4]'" }
+  T ((*pia3_4)[3][1]);          // { dg-warning "array subscript 3 is (above|outside) array bounds of 'int\\\[3]\\\[4]'" }
+  T ((*pia3_4)[3][2]);          // { dg-warning "array subscript 3 is (above|outside) array bounds of 'int\\\[3]\\\[4]'" }
+  T ((*pia3_4)[3][3]);          // { dg-warning "array subscript 3 is (above|outside) array bounds of 'int\\\[3]\\\[4]'" }
+  T ((*pia3_4)[3][4]);          // { dg-warning "array subscript 4 is (above|outside) array bounds of 'int\\\[4]'" }
+}
+
+
+typedef int IA4[4];
+typedef IA4 IA3_4[3];
+
+void test_a2_var (IA3_4 *pia3_4)
+{
+  {
+    IA4 *pia4 = &(*pia3_4)[0];
+
+    T ((*pia4)[-1]);            // { dg-warning "array subscript -1 is (below|outside) array bounds of 'IA4'" }
+    T ((*pia4)[0]);
+    T ((*pia4)[1]);
+    T ((*pia4)[2]);
+    T ((*pia4)[3]);
+    T ((*pia4)[4]);             // { dg-warning "array subscript 4 is (above|outside) array bounds of 'IA4'" }
+  }
+
+  {
+    IA4 *pia4 = &(*pia3_4)[1];
+
+    T ((*pia4)[-1]);            // { dg-warning "array subscript -1 is (below|outside) array bounds of 'IA4'" }
+    T ((*pia4)[0]);
+    T ((*pia4)[1]);
+    T ((*pia4)[2]);
+    T ((*pia4)[3]);
+    T ((*pia4)[4]);             // { dg-warning "array subscript 4 is (above|outside) array bounds of 'IA4'" }
+  }
+
+  {
+    IA4 *pia4 = &(*pia3_4)[2];
+
+    T ((*pia4)[-1]);            // { dg-warning "array subscript -1 is (below|outside) array bounds of 'IA4'" }
+    T ((*pia4)[0]);
+    T ((*pia4)[1]);
+    T ((*pia4)[2]);
+    T ((*pia4)[3]);
+    T ((*pia4)[4]);             // { dg-warning "array subscript 4 is (above|outside) array bounds of 'IA4'" }
+  }
+
+  {
+    IA4 *pia4 = &(*pia3_4)[3];
+
+    T ((*pia4)[-1]);            // { dg-warning "\\\[-Warray-bounds" }
+    /* The following aren't diagnosed unless N itself is out of bounds
+       because thanks to the MEM_REF they're indistinguishable from
+       possibly valid accesses:
+         MEM[(int[4] *)pia3_4_2(D) + 48B][N];  */
+    T ((*pia4)[0]);             // { dg-warning "\\\[-Warray-bounds" "pr?????" { xfail *-*-* } }
+    T ((*pia4)[1]);             // { dg-warning "\\\[-Warray-bounds" "pr?????" { xfail *-*-* } }
+    T ((*pia4)[2]);             // { dg-warning "\\\[-Warray-bounds" "pr?????" { xfail *-*-* } }
+    T ((*pia4)[3]);             // { dg-warning "\\\[-Warray-bounds" "pr?????" { xfail *-*-* } }
+    T ((*pia4)[4]);             // { dg-warning "\\\[-Warray-bounds" }
+  }
+}
+
+
+struct S { IA3_4 *pia3_4; };
+typedef struct S S5[5];
+typedef S5 S5_7[7];
+
+void test_s5_7 (S5_7 *ps5_7)
+{
+  {
+    S5 *ps5 = &(*ps5_7)[0];
+    T ((*ps5)[0]);
+    T ((*(*ps5)[0].pia3_4)[0][0]);
+    T ((*(*ps5)[0].pia3_4)[2][3]);
+    T ((*(*ps5)[0].pia3_4)[2][4]);    // { dg-warning "array subscript 4 is above array bounds of 'IA4'" }
+
+    T ((*(*ps5)[1].pia3_4)[2][3]);
+    T ((*(*ps5)[5].pia3_4)[2][3]);    // { dg-warning "array subscript 5 is above array bounds of 'S5'" }
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-66.c b/gcc/testsuite/gcc.dg/Warray-bounds-66.c
new file mode 100644 (file)
index 0000000..d9bb2a2
--- /dev/null
@@ -0,0 +1,256 @@
+/* PR middle-end/82608 - missing -Warray-bounds on an out-of-bounds VLA index
+ { dg-do compile }
+ { dg-options "-O2 -Wall -Wno-uninitialized -ftrack-macro-expansion=0" } */
+
+#include "range.h"
+
+typedef __INT16_TYPE__ int16_t;
+
+#define alloca(n) __builtin_alloca (n)
+
+void* calloc (size_t, size_t);
+void* malloc (size_t);
+
+void sink (void*, ...);
+#define sink(...) sink (0, __VA_ARGS__)
+
+#define T(x) (sink (x))
+
+__attribute__ ((alloc_size (1))) void* alloc (size_t);
+
+
+void test_alloca_cst (void)
+{
+  {
+    char *p = alloca (1);
+    sink (p);
+    T (p[0]);
+    T (p[1]);                 // { dg-warning "subscript 1 is outside array bounds of 'char\\\[1\\\]'" }
+  }
+
+  {
+    char *p = alloca (2);
+    sink (p);
+    T (p[0]), T (p[1]);
+    T (p[2]);                 // { dg-warning "subscript 2 is outside array bounds of 'char\\\[2\\\]'" }
+  }
+
+  {
+    char *p = alloca (3);
+    sink (p);
+    T (p[0]), T (p[1]), T (p[2]);
+    T (p[3]);                 // { dg-warning "subscript 3 is outside array bounds of 'char\\\[3\\\]'" }
+  }
+}
+
+
+void test_alloca_char_range (int i, unsigned n, size_t sz)
+{
+  {
+    // Be sure to exercise signed as well as unsigned arguments.
+    char *p = alloca (i);
+    sink (p);
+    T (p[0]), T (p[1]), T (p[12345]);
+    T (p[-1]);                // { dg-warning "subscript -1 is outside array bounds of 'char\\\[" }
+  }
+
+  {
+    char *p = alloca (n);
+    sink (p);
+    T (p[0]), T (p[1]), T (p[12345]);
+    T (p[-1]);                // { dg-warning "subscript -1 is outside array bounds of 'char\\\[" }
+  }
+
+  {
+    char *p = alloca (sz);
+    sink (p);
+    T (p[0]), T (p[1]), T (p[23456]);
+    T (p[-1]);                // { dg-warning "subscript -1 is outside array bounds of 'char\\\[" }
+  }
+
+  {
+    char *p = alloca (UR (0, 1));
+    sink (p);
+    T (p[0]);
+    T (p[1]);                 // { dg-warning "subscript 1 is outside array bounds of 'char\\\[1\\\]'" }
+  }
+
+  {
+    char *p = alloca (UR (0, 2));
+    sink (p);
+    sink (p[0], p[1]);
+    sink (p[2]);              // { dg-warning "subscript 2 is outside array bounds of 'char\\\[2\\\]'" }
+  }
+
+  {
+    char *p = alloca (UR (0, 3));
+    sink (p);
+    T (p[0]), T (p[1]), T (p[2]);
+    T (p[3]);                 // { dg-warning "subscript 3 is outside array bounds of 'char\\\[3\\\]'" }
+  }
+
+  {
+    char *p = alloca (UR (1, 3));
+    sink (p);
+    T (p[0]), T (p[1]), T (p[2]);
+    T (p[3]);                 // { dg-warning "subscript 3 is outside array bounds of 'char\\\[3\\\]'" }
+  }
+
+  {
+    char *p = alloca (UR (2, 3));
+    sink (p);
+    T (p[0]), T (p[1]), T (p[2]);
+    T (p[3]);                 // { dg-warning "subscript 3 is outside array bounds of 'char\\\[3\\\]'" }
+  }
+}
+
+
+void test_alloca_int16_range (unsigned n)
+{
+  int16_t *p;
+  {
+    p = alloca (n);           // { dg-message "allocated by " }
+    sink (p);
+    T (p[0]), T (p[1]), T (p[12345]);
+    T (p[-1]);                // { dg-warning "subscript -1 is outside array bounds of 'int16_t\\\[" }
+  }
+
+  {
+    p = alloca (UR (0, 1));   // { dg-message "object of size between 0 and 1 allocated by '__builtin_alloca'" }
+    sink (p);
+    T (p[0]);                 // { dg-warning "subscript 'int16_t {aka short int}\\\[0\\\]' is partly outside array bounds of 'unsigned char\\\[1]'" }
+    T (p[1]);                 // { dg-warning "subscript 1 is outside array bounds of 'int16_t\\\[0]'" }
+  }
+
+  {
+    p = alloca (UR (0, 2));   // { dg-message "object of size between 0 and 2 allocated by '__builtin_alloca'" }
+    sink (p);
+    sink (p[0]);
+    sink (p[1]);              // { dg-warning "subscript 1 is outside array bounds of 'int16_t\\\[1]'" }
+    sink (p[2]);              // { dg-warning "subscript 2 is outside array bounds of 'int16_t\\\[1\\\]'" }
+  }
+
+  {
+    p = alloca (UR (0, 3));   // { dg-message "object of size between 0 and 3 allocated by '__builtin_alloca'" }
+    sink (p);
+    T (p[0]);
+    T (p[1]);                 // { dg-warning "subscript 'int16_t {aka short int}\\\[1\\\]' is partly outside array bounds of 'unsigned char\\\[3]'" }
+    T (p[2]);                 // { dg-warning "subscript 2 is outside array bounds of 'int16_t\\\[1\\\]'" }
+    T (p[3]);                 // { dg-warning "subscript 3 is outside array bounds of 'int16_t\\\[1\\\]'" }
+  }
+
+  {
+    p = alloca (UR (1, 3));    // { dg-message "object of size between 1 and 3 allocated by '__builtin_alloca'" }
+    sink (p);
+    T (p[0]);
+    T (p[1]);                 // { dg-warning "subscript 'int16_t {aka short int}\\\[1\\\]' is partly outside array bounds of 'unsigned char\\\[3]'" }
+    T (p[2]);                 // { dg-warning "subscript 2 is outside array bounds of 'int16_t\\\[1\\\]'" }
+    T (p[3]);                 // { dg-warning "subscript 3 is outside array bounds of 'int16_t\\\[1\\\]'" }
+  }
+
+  {
+    p = alloca (UR (2, 3));    // { dg-message "object of size between 2 and 3 allocated by '__builtin_alloca'" }
+    sink (p);
+    T (p[0]);
+    T (p[1]);                 // { dg-warning "subscript 'int16_t {aka short int}\\\[1\\\]' is partly outside array bounds of 'unsigned char\\\[3]'" }
+    T (p[2]);                 // { dg-warning "subscript 2 is outside array bounds of 'int16_t\\\[1\\\]'" }
+    T (p[3]);                 // { dg-warning "subscript 3 is outside array bounds of 'int16_t\\\[1\\\]'" }
+  }
+
+  {
+    p = alloca (UR (3, 4));    // { dg-message "object of size between 3 and 4 allocated by '__builtin_alloca'" }
+    sink (p);
+    T (p[0]);
+    T (p[1]);
+    T (p[2]);                 // { dg-warning "subscript 2 is outside array bounds of 'int16_t\\\[2\\\]'" }
+    T (p[3]);                 // { dg-warning "subscript 3 is outside array bounds of 'int16_t\\\[2\\\]'" }
+  }
+}
+
+
+void test_vla_cst (void)
+{
+  int n = 1;
+  {
+    char a[n];
+    sink (a);
+    T (a[0]);
+    T (a[1]);                 // { dg-warning "subscript 1 is (above|outside) array bounds " }
+  }
+
+  {
+    n = 2;
+    char a[n];
+    sink (a);
+    T (a[0]), T (a[1]);
+    T (a[2]);                 // { dg-warning "subscript 2 is (above|outside) array bounds " }
+  }
+
+  {
+    n = 3;
+    char a[n], *p = a;
+    sink (p);
+    T (p[0]), T (p[1]), T (p[2]);
+    T (p[3]);                 // { dg-warning "subscript 3 is (above|outside) array bounds " }
+  }
+}
+
+
+void test_vla_char_range (int i, unsigned n, size_t sz)
+{
+  {
+    char a[i];
+    sink (a);
+    T (a[0]), T (a[1]), T (a[12345]);
+    T (a[-1]);                // { dg-warning "subscript -1 is (below|outside) array bounds of 'char\\\[" }
+  }
+
+  {
+    char a[n];
+    sink (a);
+    T (a[0]), T (a[1]), T (a[12345]);
+    T (a[-1]);                // { dg-warning "subscript -1 is (below|outside) array bounds of 'char\\\[" }
+  }
+
+  {
+    char a[sz];
+    sink (a);
+    T (a[0]), T (a[1]), T (a[23456]);
+    T (a[-1]);                // { dg-warning "subscript -1 is (below|outside) array bounds of 'char\\\[" }
+  }
+
+  {
+    char a[UR (0, 1)];
+    sink (a);
+    T (a[0]);
+    T (a[1]);                 // { dg-warning "subscript 1 is outside array bounds of 'char\\\[1\\\]'" "pr82608" { xfail *-*-* } }
+  }
+
+  {
+    char a[UR (0, 2)];
+    sink (a);
+    sink (a[0], a[1]);
+    sink (a[2]);              // { dg-warning "subscript 2 is outside array bounds of 'char\\\[2\\\]'" "pr82608" { xfail *-*-* } }
+  }
+
+  {
+    char a[UR (0, 3)];
+    sink (a);
+    T (a[0]), T (a[1]), T (a[2]);
+    T (a[3]);                 // { dg-warning "subscript 3 is outside array bounds of 'char\\\[3\\\]'" "pr82608" { xfail *-*-* } }
+  }
+
+  {
+    char a[UR (1, 3)];
+    sink (a);
+    T (a[0]), T (a[1]), T (a[2]);
+    T (a[3]);                 // { dg-warning "subscript 3 is outside array bounds of 'char\\\[3\\\]'" "pr82608" { xfail *-*-* } }
+  }
+
+  {
+    char a[UR (2, 3)];
+    sink (a);
+    T (a[0]), T (a[1]), T (a[2]);
+    T (a[3]);                 // { dg-warning "subscript 3 is outside array bounds of 'char\\\[3\\\]'" "pr82608" { xfail *-*-* } }
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-67.c b/gcc/testsuite/gcc.dg/Warray-bounds-67.c
new file mode 100644 (file)
index 0000000..a9b9ff7
--- /dev/null
@@ -0,0 +1,36 @@
+/* Verify warnings fpr accesses to trailing one-element array members
+   of a struct that's a member of either a struct or a union.  Both
+   are obviously undefined but GCC relies on these hacks so the test
+   verifies that -Warray-bounds doesn't trigger for it.
+   { do-do compile }
+   { dg-options "-O2 -Wall" } */
+
+
+typedef union tree_node *tree;
+
+struct tree_exp { int i; tree operands[1]; };
+
+union tree_node
+{
+  struct tree_exp exp;
+};
+
+tree test_nowarn (tree t)
+{
+  return t->exp.operands[3];    // { dg-bogus "\\\[-Warray-bounds" }
+}
+
+
+typedef struct shrub_node *shrub;
+
+struct shrub_exp { int i; shrub operands[1]; };
+
+struct shrub_node
+{
+  struct shrub_exp exp;
+};
+
+shrub test_warn (shrub s)
+{
+  return s->exp.operands[3];    // { dg-warning "\\\[-Warray-bounds" "pr96346" { xfail *-*-* } }
+}
index da767b8..56a827a 100644 (file)
@@ -11,5 +11,5 @@ void a (void)
          );
 }
 
-/* The scanf call may also trigger:
-   { dg-prune-output "-Wstringop-overflow" } */
+/* The invalid scanf call may also trigger:
+   { dg-prune-output "accessing 4 bytes in a region of size 1" } */