From: Martin Sebor Date: Wed, 28 Aug 2019 16:43:56 +0000 (+0000) Subject: PR tree-optimization/91457 - inconsistent warning for writing past the end of an... X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=464969eb9b47eb2f24403c74c16769a58dbaa638;p=gcc.git PR tree-optimization/91457 - inconsistent warning for writing past the end of an array member 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 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6fa40905144..77dbf876a3a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,15 @@ +2019-08-28 Martin Sebor + + 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 PR driver/80545 diff --git a/gcc/builtins.c b/gcc/builtins.c index 0b25adc17a0..f8063c138a3 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -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; diff --git a/gcc/builtins.h b/gcc/builtins.h index 66c9295ff4a..1ad82e86963 100644 --- a/gcc/builtins.h +++ b/gcc/builtins.h @@ -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); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 480362e9d8a..0e7c31b1ab7 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2019-08-28 Martin Sebor + + 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 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 index 00000000000..d1aab4805e9 --- /dev/null +++ b/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c @@ -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 index 00000000000..850414ede4b --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-8.C @@ -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 index 00000000000..99ce427c1b5 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C @@ -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 index 00000000000..12f8f9d353b --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-15.c @@ -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); +} diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index d38352a0c4c..7bb5f52c1e5 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -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 { diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index 5ec4d17f23b..c95b5ade781 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -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; }