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