In my work on -Wrestrict, to issue meaningful warnings, I found
it important to detect both out of bounds array indices as well
as offsets in calls to restrict-qualified functions like strcpy.
GCC already detects some of these cases but my tests for
the enhanced warning exposed a few gaps.
The attached patch enhances -Warray-bounds to detect more instances
out-of-bounds indices and offsets to member arrays and non-array
members. For example, it detects the out-of-bounds offset in the
call to strcpy below.
The patch is meant to be applied on top posted here but not yet
committed:
https://gcc.gnu.org/ml/gcc-patches/2017-10/msg01304.html
Richard, since this also touches tree-vrp.c I look for your comments.
Jeff, this is the enhancement you were interested in when we spoke
last week.
Thanks
Martin
$ cat a.c && gcc -O2 -S -Wall a.c
struct A { char a[4]; void (*pf)(void); };
void f (struct A *p)
{
p->a[5] = 'x'; // existing -Warray-bounds
strcpy (p->a + 6, "y"); // enhanced -Warray-bounds
}
a.c: In function ‘f’:
a.c:7:3: warning: offset 6 is out of bounds of ‘char[4]’ [-Warray-bounds]
strcpy (p->a + 6, "y");
^~~~~~~~~~~~~~~~~~~~~~
a.c:1:17: note: member declared here
struct A { char a[4]; void (*pf)(void); };
^
a.c:5:7: warning: array subscript 5 is above array bounds of
‘char[4]’ [-Warray-bounds]
p->a[5] = 'x';
~~~~^~~
PR tree-optimization/82455 - missing -Warray-bounds on strcpy offset in an out-of-bounds range
gcc/ChangeLog:
PR tree-optimization/82455
* tree-ssa-forwprop.c (maybe_warn_offset_out_of_bounds): New function.
(forward_propagate_addr_expr_1): Call it.
* tree-vrp.c (check_array_ref): Return bool. Handle flexible array
members, string literals, and inner indices.
(search_for_addr_array): Return bool. Add an argument. Add detail
to diagnostics.
(check_array_bounds): New function.
* tree-vrp.h (check_array_bounds): Declare it.
* wide-int.h (sign_mask): Assert a precondition.
gcc/testsuite/ChangeLog:
PR tree-optimization/82455
* gcc.dg/Warray-bounds-23.c: New test.
* gcc.dg/Warray-bounds-24.c: New test.
diff --git a/gcc/tree-ssa-forwprop.c b/gcc/tree-ssa-forwprop.c
index 5569b98..364a3a2 100644
--- a/gcc/tree-ssa-forwprop.c
+++ b/gcc/tree-ssa-forwprop.c
@@ -46,6 +46,8 @@ along with GCC; see the file COPYING3. If not see
#include "tree-cfgcleanup.h"
#include "cfganal.h"
#include "optabs-tree.h"
+#include "diagnostic-core.h"
+#include "tree-vrp.h"
/* This pass propagates the RHS of assignment statements into use
sites of the LHS of the assignment. It's basically a specialized
@@ -641,6 +643,210 @@ tidy_after_forward_propagate_addr (gimple *stmt)
recompute_tree_invariant_for_addr_expr (gimple_assign_rhs1 (stmt));
}
+/* Check to see if the expression (char*)PTR + OFF is valid (i.e., it
+ doesn't overflow and the result is within the bounds of the object
+ pointed to by PTR. OFF is an offset in bytes. If it isn't valid,
+ issue a -Warray-bounds warning. */
+
+static bool
+maybe_warn_offset_out_of_bounds (location_t loc, tree ptr, tree off)
+{
+ offset_int cstoff = 0;
+
+ bool ignore_off_by_1 = false;
+
+ if (TREE_CODE (ptr) == SSA_NAME)
+ {
+ gimple *stmt = SSA_NAME_DEF_STMT (ptr);
+ if (is_gimple_assign (stmt))
+ {
+ tree_code code = gimple_assign_rhs_code (stmt);
+ if (code == ADDR_EXPR)
+ {
+ ptr = gimple_assign_rhs1 (stmt);
+ ignore_off_by_1 = true;
+ }
+ else if (code == POINTER_PLUS_EXPR)
+ {
+ ptr = gimple_assign_rhs1 (stmt);
+ tree offset = gimple_assign_rhs2 (stmt);
+ if (maybe_warn_offset_out_of_bounds (loc, ptr, offset))
+ return true;
+
+ /* To do: handle ranges. */
+ if (TREE_CODE (offset) != INTEGER_CST)
+ return false;
+
+ /* Convert the offset from sizetype to ptrdiff_t. */
+ offset = fold_convert (ptrdiff_type_node, offset);
+ cstoff = wi::to_offset (offset);
+ }
+ }
+ }
+
+ if (TREE_CODE (ptr) != ADDR_EXPR
+ || TREE_CODE (TREE_OPERAND (ptr, 0)) != ARRAY_REF)
+ ignore_off_by_1 = false;
+
+ if (check_array_bounds (ptr, ignore_off_by_1))
+ return true;
+
+ bool addr_expr = false;
+
+ if (TREE_CODE (ptr) == ADDR_EXPR)
+ {
+ ptr = TREE_OPERAND (ptr, 0);
+ addr_expr = true;
+ }
+
+ /* The type of the array to whichj the offset applies and the type
+ of the array element. For offsets to non-arrays (i.e., pointers)
+ the array is considered to have a single element of ELTYPE. */
+ tree atype = TREE_TYPE (ptr);
+ tree eltype = atype;
+
+ if (TREE_CODE (ptr) == ARRAY_REF)
+ {
+ tree idx = TREE_OPERAND (ptr, 1);
+
+ /* To do: handle ranges. */
+ if (TREE_CODE (idx) != INTEGER_CST)
+ return false;
+
+ ptr = TREE_OPERAND (ptr, 0);
+ eltype = TREE_TYPE (TREE_TYPE (ptr));
+ tree eltsize = TYPE_SIZE_UNIT (eltype);
+
+ idx = fold_convert (ptrdiff_type_node, idx);
+
+ if (TREE_CODE (eltsize) == INTEGER_CST)
+ cstoff += wi::to_offset (idx) * wi::to_offset (eltsize);
+ else
+ cstoff += wi::to_offset (idx);
+
+ atype = TREE_TYPE (ptr);
+ if (TREE_CODE (atype) == POINTER_TYPE)
+ atype = TREE_TYPE (atype);
+ }
+ else if (TREE_CODE (ptr) == MEM_REF)
+ addr_expr = false;
+
+ bool zero_low_bound;
+ tree ubound = NULL_TREE;
+
+ if (TREE_CODE (atype) == ARRAY_TYPE)
+ {
+ eltype = TREE_TYPE (atype);
+ zero_low_bound = true;
+
+ if (warn_array_bounds > 1
+ || !array_at_struct_end_p (ptr))
+ {
+ /* Determine the upper bound of the array and the element type
+ if it's known. Otherwise, use the maximum object size to
+ compute the most conservative upper bound. */
+ if (tree dom = TYPE_DOMAIN (atype))
+ ubound = TYPE_MAX_VALUE (dom);
+ }
+ }
+ else if (addr_expr)
+ {
+ /* Treat a singleton (non-array) variable as an array of a single
+ element. */
+ eltype = atype;
+ atype = build_array_type_nelts (eltype, 1);
+ ubound = size_zero_node;
+ zero_low_bound = true;
+ }
+ else
+ {
+ eltype = atype;
+ zero_low_bound = false;
+ }
+
+ offset_int elsize;
+ if (TREE_CODE (TYPE_SIZE_UNIT (eltype)) == INTEGER_CST)
+ elsize = wi::to_offset (TYPE_SIZE_UNIT (eltype));
+ else
+ elsize = 1;
+
+ /* Compute the maximum and minimum valid byte offsets. */
+ offset_int maxoff = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+ offset_int minoff = wi::to_offset (TYPE_MIN_VALUE (ptrdiff_type_node));
+
+ if (TREE_CODE (off) == SSA_NAME)
+ {
+ wide_int min, max;
+ value_range_type rng = get_range_info (off, &min, &max);
+ if (rng == VR_VARYING)
+ return false;
+
+ if (rng == VR_RANGE)
+ {
+ if (wi::lts_p (min, wi::zero (min.get_precision ())))
+ min = max;
+ }
+ else
+ min = max + 1;
+
+ off = wide_int_to_tree (TREE_TYPE (off), min);
+ }
+
+ /* Convert the unsigned byte offset into ptrdiff_t. */
+ off = fold_convert (ptrdiff_type_node, off);
+ offset_int wioff = wi::to_offset (off) + cstoff;
+
+ /* Determine the byte offset of the (member) reference and subtract
+ it from the upper bound on the offset. The lower bound stays
+ the same because its value doesn't contribute to the result. */
+ HOST_WIDE_INT byteoff;
+ tree base = get_addr_base_and_unit_offset (ptr, &byteoff);
+ if (base)
+ maxoff -= byteoff;
+
+ bool warned = false;
+
+ if (wi::lts_p (maxoff, wioff) || wi::lts_p (wioff, minoff))
+ {
+ warned = warning_at (loc, OPT_Warray_bounds,
+ "pointer overflow computing offset %lli from %qT",
+ (long long) wioff.to_shwi (), atype);
+ }
+ else
+ {
+ if (ubound && TREE_CODE (ubound) == INTEGER_CST)
+ maxoff = (wi::to_offset (ubound) + 1) * elsize;
+
+ if (zero_low_bound)
+ minoff = 0;
+
+ if (wi::lts_p (maxoff, wioff) || wi::lts_p (wioff, minoff))
+ warned = warning_at (loc, OPT_Warray_bounds,
+ "offset %lli is out of bounds of %qT",
+ (long long) wioff.to_shwi (), atype);
+ }
+
+ if (warned)
+ {
+ if (base && TREE_CODE (base) == SSA_NAME)
+ {
+ gimple *stmt = SSA_NAME_DEF_STMT (ptr);
+ if (is_gimple_assign (stmt)
+ && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
+ ptr = TREE_OPERAND (gimple_assign_rhs1 (stmt), 0);
+ }
+
+ if (TREE_CODE (ptr) == COMPONENT_REF)
+ {
+ ptr = TREE_OPERAND (ptr, 1);
+ inform (DECL_SOURCE_LOCATION (ptr), "member declared here");
+ }
+ return true;
+ }
+
+ return false;
+}
+
/* NAME is a SSA_NAME representing DEF_RHS which is of the form
ADDR_EXPR <whatever>.
@@ -717,10 +923,13 @@ forward_propagate_addr_expr_1 (tree name, tree def_rhs,
to make sure we can build a valid constant offsetted address
for further propagation. Simply rely on fold building that
and check after the fact. */
+ tree off = gimple_assign_rhs2 (use_stmt);
+ maybe_warn_offset_out_of_bounds (gimple_location (use_stmt),
+ rhs, off);
+
new_def_rhs = fold_build2 (MEM_REF, TREE_TYPE (TREE_TYPE (rhs)),
def_rhs,
- fold_convert (ptr_type_node,
- gimple_assign_rhs2 (use_stmt)));
+ fold_convert (ptr_type_node, off));
if (TREE_CODE (new_def_rhs) == MEM_REF
&& !is_gimple_mem_ref_addr (TREE_OPERAND (new_def_rhs, 0)))
return false;
diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c
index 3e93685..8097ea5 100644
--- a/gcc/tree-vrp.c
+++ b/gcc/tree-vrp.c
@@ -6664,7 +6664,7 @@ insert_range_assertions (void)
non-overlapping with valid range.
IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR. */
-static void
+static bool
check_array_ref (location_t location, tree ref, bool ignore_off_by_one)
{
value_range *vr = NULL;
@@ -6672,7 +6672,7 @@ check_array_ref (location_t location, tree ref, bool ignore_off_by_one)
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);
@@ -6691,7 +6691,7 @@ check_array_ref (location_t location, tree ref, bool ignore_off_by_one)
/* FIXME: Handle VLAs. */
if (TREE_CODE (eltsize) != INTEGER_CST)
- return;
+ return false;
tree maxbound = TYPE_MAX_VALUE (ptrdiff_type_node);
@@ -6702,11 +6702,12 @@ check_array_ref (location_t location, tree ref, bool ignore_off_by_one)
if (code == COMPONENT_REF)
{
HOST_WIDE_INT off;
- if (tree base = get_addr_base_and_unit_offset (arg, &off))
+ if (get_addr_base_and_unit_offset (arg, &off))
{
- tree size = TYPE_SIZE_UNIT (TREE_TYPE (base));
- if (TREE_CODE (size) == INTEGER_CST)
- up_bound_p1 = int_const_binop (MINUS_EXPR, up_bound_p1, size);
+ off /= tree_to_uhwi (eltsize);
+ up_bound_p1 = int_const_binop (MINUS_EXPR, up_bound_p1,
+ build_int_cst (ptrdiff_type_node,
+ off));
}
}
@@ -6719,21 +6720,28 @@ check_array_ref (location_t location, tree ref, bool ignore_off_by_one)
low_bound = array_ref_low_bound (ref);
+ if (location == UNKNOWN_LOCATION
+ && EXPR_HAS_LOCATION (ref))
+ location = EXPR_LOCATION (ref);
+
+ bool warned = false;
tree artype = TREE_TYPE (TREE_OPERAND (ref, 0));
/* Empty array. */
if (tree_int_cst_equal (low_bound, up_bound_p1))
{
- warning_at (location, OPT_Warray_bounds,
- "array subscript %E is above array bounds of %qT",
- low_bound, artype);
- TREE_NO_WARNING (ref) = 1;
+ warned = warning_at (location, OPT_Warray_bounds,
+ "array subscript %E is above array bounds of %qT",
+ low_bound, artype);
+ if (warned)
+ TREE_NO_WARNING (ref) = 1;
+ return warned;
}
if (TREE_CODE (low_sub) == SSA_NAME)
{
vr = get_value_range (low_sub);
- if (vr->type == VR_RANGE || vr->type == VR_ANTI_RANGE)
+ if (vr && (vr->type == VR_RANGE || vr->type == VR_ANTI_RANGE))
{
low_sub = vr->type == VR_RANGE ? vr->max : vr->min;
up_sub = vr->type == VR_RANGE ? vr->min : vr->max;
@@ -6748,13 +6756,11 @@ check_array_ref (location_t location, tree ref, bool ignore_off_by_one)
: tree_int_cst_le (up_bound, up_sub))
&& TREE_CODE (low_sub) == INTEGER_CST
&& tree_int_cst_le (low_sub, low_bound))
- {
- warning_at (location, OPT_Warray_bounds,
- "array subscript [%E, %E] is outside array bounds of %qT",
- low_sub, up_sub, artype);
- TREE_NO_WARNING (ref) = 1;
- }
- }
+ warned = warning_at (location, OPT_Warray_bounds,
+ "array subscript [%E, %E] is outside array "
+ "bounds of %qT",
+ low_sub, up_sub, artype);
+ }
else if (TREE_CODE (up_sub) == INTEGER_CST
&& (ignore_off_by_one
? !tree_int_cst_le (up_sub, up_bound_p1)
@@ -6766,10 +6772,9 @@ check_array_ref (location_t location, tree ref, bool ignore_off_by_one)
dump_generic_expr (MSG_NOTE, TDF_SLIM, ref);
fprintf (dump_file, "\n");
}
- warning_at (location, OPT_Warray_bounds,
- "array subscript %E is above array bounds of %qT",
- up_sub, artype);
- TREE_NO_WARNING (ref) = 1;
+ warned = warning_at (location, OPT_Warray_bounds,
+ "array subscript %E is above array bounds of %qT",
+ up_sub, artype);
}
else if (TREE_CODE (low_sub) == INTEGER_CST
&& tree_int_cst_lt (low_sub, low_bound))
@@ -6780,24 +6785,31 @@ check_array_ref (location_t location, tree ref, bool ignore_off_by_one)
dump_generic_expr (MSG_NOTE, TDF_SLIM, ref);
fprintf (dump_file, "\n");
}
- warning_at (location, OPT_Warray_bounds,
- "array subscript %E is below array bounds of %qT",
- low_sub, artype);
- TREE_NO_WARNING (ref) = 1;
+ warned = warning_at (location, OPT_Warray_bounds,
+ "array subscript %E is below array bounds of %qT",
+ low_sub, artype);
}
+
+ if (warned)
+ TREE_NO_WARNING (ref) = 1;
+
+ return warned;
}
-/* Searches if the expr T, located at LOCATION computes
- address of an ARRAY_REF, and call check_array_ref on it. */
+/* Search if the expr T, located at LOCATION, compute the address
+ of an ARRAY_REF, and call check_array_ref on it.
+ IGNORE_OFF_BY_ONE is true if the ARRAY_REF is inside a ADDR_EXPR. */
-static void
-search_for_addr_array (tree t, location_t location)
+static bool
+search_for_addr_array (tree t, location_t location, bool ignore_off_by_one)
{
+ bool warned = false;
+
/* Check each ARRAY_REFs in the reference chain. */
do
{
if (TREE_CODE (t) == ARRAY_REF)
- check_array_ref (location, t, true /*ignore_off_by_one*/);
+ warned |= check_array_ref (location, t, ignore_off_by_one);
t = TREE_OPERAND (t, 0);
}
@@ -6813,7 +6825,7 @@ search_for_addr_array (tree t, location_t location)
if (TREE_CODE (TREE_TYPE (tem)) != ARRAY_TYPE
|| TREE_CODE (TREE_TYPE (TREE_TYPE (tem))) == ARRAY_TYPE
|| !TYPE_DOMAIN (TREE_TYPE (tem)))
- return;
+ return warned;
low_bound = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (tem)));
up_bound = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (tem)));
@@ -6824,7 +6836,7 @@ search_for_addr_array (tree t, location_t location)
|| TREE_CODE (up_bound) != INTEGER_CST
|| !el_sz
|| TREE_CODE (el_sz) != INTEGER_CST)
- return;
+ return warned;
idx = mem_ref_offset (t);
idx = wi::sdiv_trunc (idx, wi::to_offset (el_sz));
@@ -6836,10 +6848,10 @@ search_for_addr_array (tree t, location_t location)
dump_generic_expr (MSG_NOTE, TDF_SLIM, t);
fprintf (dump_file, "\n");
}
- warning_at (location, OPT_Warray_bounds,
- "array subscript %wi is below array bounds of %qT",
- idx.to_shwi (), TREE_TYPE (tem));
- TREE_NO_WARNING (t) = 1;
+ warned |= warning_at (location, OPT_Warray_bounds,
+ "array subscript %wi is below array bounds "
+ "of %qT",
+ idx.to_shwi (), TREE_TYPE (tem));
}
else if (idx > (wi::to_offset (up_bound)
- wi::to_offset (low_bound) + 1))
@@ -6850,12 +6862,17 @@ search_for_addr_array (tree t, location_t location)
dump_generic_expr (MSG_NOTE, TDF_SLIM, t);
fprintf (dump_file, "\n");
}
- warning_at (location, OPT_Warray_bounds,
- "array subscript %wu is above array bounds of %qT",
- idx.to_uhwi (), TREE_TYPE (tem));
- TREE_NO_WARNING (t) = 1;
+ warned = warning_at (location, OPT_Warray_bounds,
+ "array subscript %wu is above array bounds "
+ "of %qT",
+ idx.to_uhwi (), TREE_TYPE (tem));
}
}
+
+ if (warned)
+ TREE_NO_WARNING (t) = 1;
+
+ return warned;
}
/* walk_tree() callback that checks if *TP is
@@ -6874,7 +6891,10 @@ check_array_bounds (tree *tp, int *walk_subtree, void *data)
if (EXPR_HAS_LOCATION (t))
location = EXPR_LOCATION (t);
else
- location = gimple_location (wi->stmt);
+ {
+ location_t *locp = (location_t *) wi->info;
+ location = *locp;
+ }
*walk_subtree = TRUE;
@@ -6883,13 +6903,31 @@ check_array_bounds (tree *tp, int *walk_subtree, void *data)
else if (TREE_CODE (t) == ADDR_EXPR)
{
- search_for_addr_array (t, location);
+ search_for_addr_array (t, location, true /*ignore_off_by_one*/);
*walk_subtree = FALSE;
}
return NULL_TREE;
}
+/* Check array indices in EXPR against the bounds of arrays they refer
+ to and issue warnings for any that are outside those bounds. When
+ IGNORE_OFF_BY_ONE is true, ignore indices that refer to one past
+ the last element of an array (used in &A[N] when N equals A's upper
+ bound. Return true if any warnings have been issued. */
+
+bool
+check_array_bounds (tree expr, bool ignore_off_by_one)
+{
+ if (TREE_CODE (expr) == ARRAY_REF)
+ return check_array_ref (UNKNOWN_LOCATION, expr, ignore_off_by_one);
+
+ if (TREE_CODE (expr) == ADDR_EXPR)
+ return search_for_addr_array (expr, UNKNOWN_LOCATION, ignore_off_by_one);
+
+ return false;
+}
+
/* Walk over all statements of all reachable BBs and call check_array_bounds
on them. */
@@ -6921,6 +6959,9 @@ check_all_array_refs (void)
memset (&wi, 0, sizeof (wi));
+ location_t loc = gimple_location (stmt);
+ wi.info = &loc;
+
walk_gimple_op (gsi_stmt (si),
check_array_bounds,
&wi);
diff --git a/gcc/tree-vrp.h b/gcc/tree-vrp.h
index f84403a..6587b57 100644
--- a/gcc/tree-vrp.h
+++ b/gcc/tree-vrp.h
@@ -59,5 +59,6 @@ extern void extract_range_from_unary_expr (value_range *vr,
tree type,
value_range *vr0_,
tree op0_type);
+extern bool check_array_bounds (tree, bool);
#endif /* GCC_TREE_VRP_H */
diff --git a/gcc/wide-int.h b/gcc/wide-int.h
index e17b016..7f431db 100644
--- a/gcc/wide-int.h
+++ b/gcc/wide-int.h
@@ -822,6 +822,8 @@ inline HOST_WIDE_INT
generic_wide_int <storage>::sign_mask () const
{
unsigned int len = this->get_len ();
+ gcc_assert (len != 0);
+
unsigned HOST_WIDE_INT high = this->get_val ()[len - 1];
if (!is_sign_extended)
{
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-23.c b/gcc/testsuite/gcc.dg/Warray-bounds-23.c
new file mode 100644
index 0000000..85071b0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-23.c
@@ -0,0 +1,16 @@
+/* PR tree-optimization/82588 - missing -Warray-bounds on an excessively
+ large index
+ { dg-do compile }
+ { dg-options "-O2 -Warray-bounds -ftrack-macro-expansion=0" } */
+
+#define SIZE_MAX __SIZE_MAX__
+#define SSIZE_MAX __PTRDIFF_MAX__
+#define SSIZE_MIN (-SSIZE_MAX - 1)
+
+
+int g (int i)
+{
+ int (*p)[2] = (int (*)[2])&i;
+
+ return (*p)[2];
+}
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-24.c b/gcc/testsuite/gcc.dg/Warray-bounds-24.c
new file mode 100644
index 0000000..3ab06ee
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-24.c
@@ -0,0 +1,246 @@
+/* PR tree-optimization/82581 - missing -Warray-bounds on writing past
+ the end of a member array
+ { dg-do compile }
+ { dg-options "-O2 -Warray-bounds -ftrack-macro-expansion=0" } */
+
+#define SMAX __PTRDIFF_MAX__
+#define SMIN (-SMAX -1)
+#define UMAX __SIZE_MAX__
+
+#define STATIC_ASSERT(expr) ((void)sizeof (char[1 - 2 * !!(expr)]))
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef __SIZE_TYPE__ size_t;
+
+struct A { char a3[3], b4[4]; };
+struct Ax { char a4[4]; char ax[]; };
+
+void sink (void*);
+#define T(expr) sink (expr)
+
+
+void test_char_memarray_1 (struct A *a, struct Ax *ax)
+{
+ T (a->a3 + SMIN); /* { dg-warning "offset -\[0-9\]+ is out of bounds of .char\\\[3]." } */
+ T (a->a3 - SMAX); /* { dg-warning "offset -\[0-9\]+ is out of bounds" } */
+ T (a->a3 - 1); /* { dg-warning "offset -1 is out of bounds" } */
+ T (a->a3 + 0);
+ T (a->a3 + 1);
+ T (a->a3 + 2);
+ T (a->a3 + 3);
+ T (a->a3 + 4); /* { dg-warning "offset 4 is out of bounds" } */
+ T (a->a3 + SMAX); /* { dg-warning "offset \[0-9\]+ is out of bounds" } */
+ T (a->a3 + SMAX + (size_t)1); /* { dg-warning "offset -\[0-9\]+ is out of bounds" } */
+
+ T (a->b4 + SMIN); /* { dg-warning "offset -\[0-9\]+ is out of bounds of .char\\\[4]." } */
+ T (a->b4 - SMAX); /* { dg-warning "offset -\[0-9\]+ is out of bounds" } */
+ T (a->b4 - 1); /* { dg-warning "offset -1 is out of bounds" } */
+ T (a->b4 + 0);
+ T (a->b4 + 1);
+ T (a->b4 + 2);
+ T (a->b4 + 3);
+ T (a->b4 + 4);
+ /* This is considered okay for an array at the end of a struct. */
+ T (a->b4 + 5);
+ T (a->b4 + 99);
+ T (a->b4 + SMAX - 4);
+ T (a->b4 + SMAX); /* { dg-warning "pointer overflow" } */
+ T (a->b4 + SMAX + (size_t)1); /* { dg-warning "offset -\[0-9\]+ is out of bounds " } */
+
+ T (ax->a4 + SMIN); /* { dg-warning "offset -\[0-9\]+ is out of bounds of .char\\\[4]." } */
+ T (ax->a4 - SMAX); /* { dg-warning "offset -\[0-9\]+ is out of bounds" } */
+ T (ax->a4 - 1); /* { dg-warning "offset -1 is out of bounds" } */
+ T (ax->a4 + 0);
+ T (ax->a4 + 1);
+ T (ax->a4 + 2);
+ T (ax->a4 + 3);
+ T (ax->a4 + 4);
+ T (ax->a4 + 5); /* { dg-warning "offset 5 is out of bounds" } */
+ T (ax->a4 + SMAX); /* { dg-warning "offset \[0-9\]+ is out of bounds" } */
+
+ T (ax->ax + SMIN); /* { dg-warning "offset -\[0-9\]+ is out of bounds." } */
+ T (ax->ax - SMAX); /* { dg-warning "offset -\[0-9\]+ is out of bounds" } */
+ T (ax->ax - 1); /* { dg-warning "offset -1 is out of bounds" } */
+ T (ax->ax + 0);
+ T (ax->ax + 1);
+ T (ax->ax + 2);
+ T (ax->ax + 3);
+ T (ax->ax + 4);
+ T (ax->ax + 99);
+ T (ax->ax + SMAX - 4);
+ T (ax->ax + SMAX - 3); /* { dg-warning "pointer overflow computing offset \[0-9\]+ from .char\\\[]." } */
+ T (ax->ax + SMAX); /* { dg-warning "pointer overflow computing offset \[0-9\]+ from .char\\\[]." } */
+ }
+
+struct B { struct A a[2]; };
+struct Bx { struct A a[2]; struct A ax[]; };
+
+void test_memarray_2 (struct B *b, struct Bx *bx)
+{
+ T (b[0].a + 0);
+ T (b[0].a + 1);
+ T (b[0].a + 2);
+ /* The following are okay for an array at the end of a struct. */
+ T (b[0].a + 3);
+ T (b[0].a + 99);
+ T (b[0].a + SMAX / sizeof b[0].a[0]);
+ /* The offset computation in following overflows too early to detect
+ it (with the current implementation) but because the offset gets
+ negative it is detected and diagnosed. */
+ T (b[0].a + SMAX / sizeof b[0].a[0] + 1); /* { dg-warning "offset -?\[0-9\]+ is out of bounds of .struct A\\\[2]." } */
+ T (b[0].a + SMAX / sizeof b[0].a[0] + 2); /* { dg-warning "offset -?\[0-9\]+ is out of bounds of .struct A\\\[2]." } */
+ T (b[0].a + SMAX / sizeof b[0].a[0] + 3); /* { dg-warning "offset -?\[0-9\]+ is out of bounds of .struct A\\\[2]." } */
+
+ T (b[0].a[0].b4 + 0);
+ T (b[0].a[0].b4 + 1);
+ T (b[0].a[0].b4 + 2);
+ T (b[0].a[0].b4 + 3);
+ T (b[0].a[0].b4 + 5); /* { dg-warning "offset 5 is out of bounds of .char\\\[4]." } */
+
+ T (b[0].a[1].b4 + 0);
+ T (b[0].a[1].b4 + 1);
+ T (b[0].a[1].b4 + 2);
+ T (b[0].a[1].b4 + 3);
+ T (b[0].a[1].b4 + 5); /* { dg-warning "offset 5 is out of bounds of .char\\\[4]." } */
+ T (b[0].a[1].b4 + 6); /* { dg-warning "offset 6 is out of bounds" } */
+ T (b[0].a[1].b4 + 7); /* { dg-warning "offset 7 is out of bounds" } */
+ T (b[0].a[1].b4 + 8); /* { dg-warning "offset 8 is out of bounds" } */
+ T (b[0].a[1].b4 + 9); /* { dg-warning "offset 9 is out of bounds" } */
+ T (b[0].a[1].b4 + 10); /* { dg-warning "offset 10 is out of bounds" } */
+ T (b[0].a[1].b4 + 11); /* { dg-warning "offset 11 is out of bounds" } */
+ T (b[0].a[1].b4 + 12); /* { dg-warning "offset 12 is out of bounds" } */
+ T (b[0].a[1].b4 + 13); /* { dg-warning "offset 13 is out of bounds" } */
+
+ T (b[1].a[0].b4 + 0);
+ T (b[1].a[0].b4 + 1);
+ T (b[1].a[0].b4 + 2);
+ T (b[1].a[0].b4 + 3);
+ T (b[1].a[0].b4 + 5); /* { dg-warning "offset 5 is out of bounds" } */
+
+ T (b[1].a[1].b4 + 0);
+ T (b[1].a[1].b4 + 1);
+ T (b[1].a[1].b4 + 2);
+ T (b[1].a[1].b4 + 3);
+ T (b[1].a[1].b4 + 5); /* { dg-warning "offset 5 is out of bounds" } */
+
+ STATIC_ASSERT (sizeof bx[0].a == 7);
+ T (bx[0].a + 0);
+ T (bx[0].a + 1);
+ T (bx[0].a + 2);
+ T (bx[0].a + 3); /* { dg-warning "offset 21 is out of bounds of .struct A\\\[2]." } */
+ T (bx[0].a + 99); /* { dg-warning "offset 693 is out of bounds of .struct A\\\[2]." } */
+ T (bx[0].a + SMAX); /* { dg-warning "offset -?\[0-9\]+ is out of bounds of .struct A\\\[2]." } */
+
+ T (bx[0].a[0].b4);
+ T (bx[0].a[0].b4 + 1);
+ T (bx[0].a[0].b4 + 2);
+ T (bx[0].a[0].b4 + 3);
+ T (bx[0].a[0].b4 + 99); /* { dg-warning "offset 99 is out of bounds of .char\\\[4]." } */
+ T (bx[0].a[0].b4 + SMAX); /* { dg-warning "pointer overflow" } */
+
+ T (bx[0].a[1].b4);
+ T (bx[0].a[1].b4 + 1);
+ T (bx[0].a[1].b4 + 2);
+ T (bx[0].a[1].b4 + 3);
+ T (bx[0].a[1].b4 + 99); /* { dg-warning "offset 99 is out of bounds of .char\\\[4]." } */
+ T (bx[0].a[1].b4 + SMAX); /* { dg-warning "pointer overflow computing offset -?\[0-9\]+ from .char\\\[4]." } */
+
+ T (bx[0].a[-1].b4); /* { dg-warning "array subscript -1 is below array bounds" } */
+ T (bx[0].a[2].b4);
+ T (bx[0].a[2].b4 + 1); /* { dg-warning "array subscript 2 is above array bounds" } */
+ T (bx[0].a[2].b4 + 2); /* { dg-warning "array subscript 2 is above array bounds" } */
+ T (bx[0].a[2].b4 + 3); /* { dg-warning "array subscript 2 is above array bounds" } */
+ T (bx[0].a[2].b4 + 99); /* { dg-warning "array subscript 2 is above array bounds" } */
+ T (bx[0].a[2].b4 + SMAX); /* { dg-warning "array subscript 2 is above array bounds" } */
+
+ T (bx[0].ax[0].b4 + 0);
+ T (bx[0].ax[0].b4 + 1);
+ T (bx[0].ax[0].b4 + 2);
+ T (bx[0].ax[0].b4 + 3);
+ T (bx[0].ax[0].b4 + 5); /* { dg-warning "offset 5 is out of bounds of .char\\\[4]." } */
+
+ T (bx[-99].ax[0].b4 + 0);
+ T (bx[ -1].ax[0].b4 + 0);
+ T (bx[ 0].ax[0].b4 + 1);
+ T (bx[ 1].ax[0].b4 + 2);
+ T (bx[ 2].ax[0].b4 + 3);
+ T (bx[ 99].ax[0].b4 + 5); /* { dg-warning "offset 5 is out of bounds of .char\\\[4]." } */
+
+ T (bx[-99].ax[-9].b4 + 0); /* { dg-warning "array subscript -9 is below array bounds" } */
+ T (bx[-99].ax[-1].b4 + 0); /* { dg-warning "array subscript -1 is below array bounds" } */
+ T (bx[-99].ax[11].b4 + 0);
+ T (bx[ -1].ax[22].b4 + 0);
+ T (bx[ 0].ax[33].b4 + 1);
+ T (bx[ 1].ax[44].b4 + 2);
+ T (bx[ 2].ax[55].b4 + 3);
+ T (bx[ 99].ax[99].b4 + 5); /* { dg-warning "offset 5 is out of bounds of .char\\\[4]." } */
+}
+
+struct Cx { struct B b2_3[2][3]; struct B bx_3[][3]; };
+
+void test_memarray_3 (struct Cx *cx)
+{
+ STATIC_ASSERT (sizeof cx->b2_3 == sizeof (struct B[3]));
+ STATIC_ASSERT (sizeof cx->b2_3 == 42);
+
+ T (cx->b2_3 - 1); /* { dg-warning "offset -42 is out of bounds of .struct B\\\[2]\\\[3]." } */
+ T (cx->b2_3 + 0);
+ T (cx->b2_3 + 1);
+ T (cx->b2_3 + 2);
+ T (cx->b2_3 + 3); /* { dg-warning "offset 126 is out of bounds of .struct B\\\[2]\\\[3]." } */
+}
+
+struct Memarray
+{
+ struct __attribute__ ((packed))
+ S2 { char a, b; }
+ s, *ps, a2[2], ax[];
+};
+
+void test_int_memarray (struct Memarray *p)
+{
+ T (&p->s);
+ T (&p->s + 0);
+ T (&p->s + 1);
+ T (&p->s - 1); /* { dg-warning "offset -2 is out of bounds of .struct S2\\\[1]." } */
+ T (&p->s + 2); /* { dg-warning "offset 4 is out of bounds of .struct S2\\\[1]." } */
+
+ T (&(&p->s)[0]);
+ T (&(&p->s)[1]);
+ T (&(&p->s)[-1]); /* { dg-warning "offset -2 is out of bounds of .struct S2\\\[1]." } */
+ T (&(&p->s)[2]); /* { dg-warning "offset 4 is out of bounds of .struct S2\\\[1]." } */
+
+ T (p->ps - 99);
+ T (p->ps - 1);
+ T (p->ps);
+ T (p->ps + 0);
+ T (p->ps + 1);
+ T (p->ps + 99);
+
+ T (&p->ps - 1); /* { dg-warning "offset -\[48\] is out of bounds of .struct S2\*." } */
+ T (&p->ps);
+ T (&p->ps + 1);
+ T (&p->ps + 2); /* { dg-warning "offset \(8|16\) is out of bounds of .struct S2\*." } */
+
+ T (&p->a2);
+ T (&p->a2 + 0);
+ T (&p->a2 + 1);
+ T (&p->a2 - 1); /* { dg-warning "offset -4 is out of bounds of .struct S2\\\[2]." } */
+ T (&p->a2 + 2); /* { dg-warning "offset 8 is out of bounds of .struct S2\\\[2]." } */
+ T (&p->a2 + 3); /* { dg-warning "offset 12 is out of bounds of .struct S2\\\[2]." } */
+
+ T (&p->a2[0] + 0);
+ T (&p->a2[0] + 1);
+ T (&p->a2[0] + 2);
+ T (&p->a2[0] + 3); /* { dg-warning "offset 6 is out of bounds of .struct S2\\\[2]." } */
+
+ T (&p->a2[1] + 0);
+ T (&p->a2[1] + 1);
+ T (&p->a2[1] + 2); /* { dg-warning "offset 6 is out of bounds of .struct S2\\\[2]." } */
+ T (&p->a2[1] + 3); /* { dg-warning "offset 8 is out of bounds of .struct S2\\\[2]." } */
+
+ T (&p->a2[2] + 0);
+ T (&p->a2[2] + 1); /* { dg-warning "offset 6 is out of bounds of .struct S2\\\[2]." } */
+ T (&p->a2[2] + 2); /* { dg-warning "offset 8 is out of bounds of .struct S2\\\[2]." } */
+ T (&p->a2[2] + 3); /* { dg-warning "offset 10 is out of bounds of .struct S2\\\[2]." } */
+}