diff mbox series

avoid -Warray-bounds when indexing into array members of union (PR 94940)

Message ID 3cf04f96-a596-6401-7d13-a6a1bf788db1@gmail.com
State New
Headers show
Series avoid -Warray-bounds when indexing into array members of union (PR 94940) | expand

Commit Message

Martin Sebor May 8, 2020, 9:46 p.m. UTC
The improved detection of accesses to interior zero-length arrays
fails to consider the case when the array is a member of a union.
Such accesses are documented as supported so warning for them is
not expected.  The attached patch adjusts the component_ref_size
function to return the size of the enclosing union instead of
the member array.

The VRP bit of the patch remove a couple of redundant tests and
unreachable blocks from the check_mem_ref function.  The change
is not directly related to the fix except that I noticed them
while working on it and it seems safe either to include in this
patch or commit on its own.

Tested on x86_64-linux.

Martin

Comments

Li, Pan2 via Gcc-patches May 14, 2020, 5:14 p.m. UTC | #1
On Fri, 2020-05-08 at 15:46 -0600, Martin Sebor via Gcc-patches wrote:
> The improved detection of accesses to interior zero-length arrays
> fails to consider the case when the array is a member of a union.
> Such accesses are documented as supported so warning for them is
> not expected.  The attached patch adjusts the component_ref_size
> function to return the size of the enclosing union instead of
> the member array.
> 
> The VRP bit of the patch remove a couple of redundant tests and
> unreachable blocks from the check_mem_ref function.  The change
> is not directly related to the fix except that I noticed them
> while working on it and it seems safe either to include in this
> patch or commit on its own.
> 
> Tested on x86_64-linux.
> 
> PR middle-end/94940 - spurious -Warray-bounds for a zero length array member of
> union
> 
> gcc/testsuite/ChangeLog:
> 
> 	PR middle-end/94940
> 	* gcc.dg/Warray-bounds-61.c: New test.
> 
> gcc/ChangeLog:
> 
> 	PR middle-end/94940
> 	* tree-vrp.c (vrp_prop::check_mem_ref): Remove unreachable code.
> 	* tree.c (component_ref_size): Correct the handling or array members
> 	of unions.
> 	Drop a pointless test.
> 	Rename a local variable.
OK
jeff
diff mbox series

Patch

PR middle-end/94940 - spurious -Warray-bounds for a zero length array member of union

gcc/testsuite/ChangeLog:

	PR middle-end/94940
	* gcc.dg/Warray-bounds-61.c: New test.

gcc/ChangeLog:

	PR middle-end/94940
	* tree-vrp.c (vrp_prop::check_mem_ref): Remove unreachable code.
	* tree.c (component_ref_size): Correct the handling or array members
	of unions.
	Drop a pointless test.
	Rename a local variable.

diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-61.c b/gcc/testsuite/gcc.dg/Warray-bounds-61.c
new file mode 100644
index 00000000000..5b66cdc0aab
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-61.c
@@ -0,0 +1,190 @@ 
+/* PR middle-end/94940 - spurious -Warray-bounds for a zero length array
+   member of union
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+extern int n;
+
+extern union Ua3_a0 {
+  int a3[3];
+  int a2[2];                  // can only alias a3[0 - 2]
+  int a1[1];                  // can alias all of the union
+  int a0[0];                  // ditto
+} ua3_a0;
+
+void test_ua3_ua0_a0 (int i)
+{
+  ua3_a0.a0[0] = 0;           // { dg-bogus "\\\[-Warray-bounds" }
+  ua3_a0.a0[1] = 0;           // { dg-bogus "\\\[-Warray-bounds" }
+  ua3_a0.a0[2] = 0;           // { dg-bogus "\\\[-Warray-bounds" }
+  ua3_a0.a0[3] = 0;           // { dg-warning "\\\[-Warray-bounds" }
+  ua3_a0.a0[4] = 0;           // { dg-warning "\\\[-Warray-bounds" }
+  ua3_a0.a0[i] = 0;           // { dg-bogus "\\\[-Warray-bounds" }
+
+  if (i < __LINE__)
+    i = 5;
+  ua3_a0.a0[i] = 0;           // { dg-warning "\\\[-Warray-bounds" }
+
+  if (i > -1)
+    i = -1;
+  ua3_a0.a0[i] = 0;           // { dg-warning "\\\[-Warray-bounds" }
+}
+
+void test_ua3_ua0_a1 (int i)
+{
+  /* Abusing one-element array members the same way as those of
+     length zero is discouraged but so far acceted without warnings.
+     This should change at some point.  */
+
+  ua3_a0.a1[0] = 0;
+  ua3_a0.a1[1] = 0;
+  ua3_a0.a1[2] = 0;
+  ua3_a0.a1[3] = 0;           // { dg-warning "\\\[-Warray-bounds" }
+  ua3_a0.a1[i] = 0;
+
+  if (i > -1)
+    i = -1;
+  ua3_a0.a1[i] = 0;           // { dg-warning "\\\[-Warray-bounds" }
+
+  if (i < 7)
+    i = 7;
+  ua3_a0.a1[i] = 0;           // { dg-warning "\\\[-Warray-bounds" }
+}
+
+void test_ua3_ua0_a2 (int i)
+{
+  ua3_a0.a2[0] = 0;
+  ua3_a0.a2[1] = 0;
+  ua3_a0.a2[2] = 0;           // { dg-warning "\\\[-Warray-bounds" }
+  ua3_a0.a2[i] = 0;
+
+  if (i < __LINE__)
+    i = __LINE__;
+  ua3_a0.a2[i] = 0;           // { dg-warning "\\\[-Warray-bounds" }
+
+  if (i > -1)
+    i = -1;
+  ua3_a0.a2[i] = 0;           // { dg-warning "\\\[-Warray-bounds" }
+}
+
+
+extern union Ua2_a3 {
+  int a2[2];                  // can only alias a3[0 - 1]
+  int a3[3];
+} ua2_a3;
+
+void test_ua2_ua3 (int i)
+{
+  ua2_a3.a2[0] = 0;           // { dg-bogus "\\\[-Warray-bounds" }
+  ua2_a3.a2[1] = 0;           // { dg-bogus "\\\[-Warray-bounds" }
+  ua2_a3.a2[2] = 0;           // { dg-warning "\\\[-Warray-bounds" }
+  ua2_a3.a2[i] = 0;
+
+  if (i < __LINE__)
+    i = __LINE__;
+  ua2_a3.a2[i] = 0;           // { dg-warning "\\\[-Warray-bounds" }
+}
+
+
+extern struct SUa2_a0 {
+  union Ua2_a0 {
+    int a2[2];
+    int a0[0];
+  } u;
+} sua2_a0;
+
+void test_sua2_sua0 (int i)
+{
+  n += sua2_a0.u.a0[0];
+  n += sua2_a0.u.a0[1];
+  n += sua2_a0.u.a0[2];       // { dg-warning "\\\[-Warray-bounds" }
+  n += sua2_a0.u.a0[i];
+
+  if (i < __LINE__)
+    i = __LINE__;
+  n += sua2_a0.u.a0[i];       // { dg-warning "\\\[-Warray-bounds" }
+}
+
+void test_sua2_sua0_ptr (int i)
+{
+  union Ua2_a0 *p = &sua2_a0.u;
+
+  n += p->a0[0];
+  n += p->a0[1];
+  n += p->a0[2];              // { dg-warning "\\\[-Warray-bounds" }
+  n += p->a0[i];
+}
+
+
+extern struct SUSa3_a0 {
+  union USa3_a0 {
+    struct {
+      int a3[3];
+    } s;
+    int a2[2];                // can alias s.a3[0 - 2]
+    int a1[1];                // can alias s.a3[0 - 2]
+    int a0[0];                // can alias s.a3[0]
+  } u;
+} susa3_ua0;
+
+void test_susa3_sua0 (int i, int j)
+{
+  n += susa3_ua0.u.a0[0];
+  n += susa3_ua0.u.a0[1];
+  n += susa3_ua0.u.a0[2];
+  n += susa3_ua0.u.a0[3];     // { dg-warning "\\\[-Warray-bounds" }
+}
+
+void test_susa3_sua0_ptr (int i, int j)
+{
+  union USa3_a0 *p = &susa3_ua0.u;
+  n += p->a0[0];
+  n += p->a0[1];
+  n += p->a0[2];
+  n += p->a0[3];              // { dg-warning "\\\[-Warray-bounds" }
+}
+
+void test_susa3_sua1 (int i)
+{
+  n += susa3_ua0.u.a1[0];
+  n += susa3_ua0.u.a1[1];
+  n += susa3_ua0.u.a1[2];
+  n += susa3_ua0.u.a1[3];     // { dg-warning "\\\[-Warray-bounds" }
+
+  if (i < __LINE__)
+    i = __LINE__;
+  n += susa3_ua0.u.a1[i];     // { dg-warning "\\\[-Warray-bounds" }
+}
+
+void test_susa3_sua2 (void)
+{
+  n += susa3_ua0.u.a2[0];
+  n += susa3_ua0.u.a2[1];
+  n += susa3_ua0.u.a2[2];     // { dg-warning "\\\[-Warray-bounds" }
+  n += susa3_ua0.u.a2[3];     // { dg-warning "\\\[-Warray-bounds" }
+}
+
+
+extern struct {
+  union {
+    struct {
+      int a3[3];
+    } s1;
+    struct {
+      int a0[0];
+    } s2;
+  } u;
+} susa3_usa0;
+
+void test_susi3_susi0 (int i)
+{
+  n += susa3_usa0.u.s2.a0[0];
+  n += susa3_usa0.u.s2.a0[1];
+  n += susa3_usa0.u.s2.a0[2];
+  n += susa3_usa0.u.s2.a0[3]; // { dg-warning "\\\[-Warray-bounds" }
+  n += susa3_usa0.u.s2.a0[i];
+
+  if (i < __LINE__)
+    i = __LINE__;
+  n += susa3_usa0.u.s2.a0[i]; // { dg-warning "\\\[-Warray-bounds" }
+}
diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c
index a8861670790..7c50a9c95b8 100644
--- a/gcc/tree-vrp.c
+++ b/gcc/tree-vrp.c
@@ -3843,15 +3843,11 @@  vrp_prop::check_mem_ref (location_t location, tree ref,
       else
 	arrbounds[1] = wi::lrshift (maxobjsize, wi::floor_log2 (eltsize));
 
-      if (TREE_CODE (ref) == MEM_REF)
-	{
-	  /* For MEM_REF determine a tighter bound of the non-array
-	     element type.  */
-	  tree eltype = TREE_TYPE (reftype);
-	  while (TREE_CODE (eltype) == ARRAY_TYPE)
-	    eltype = TREE_TYPE (eltype);
-	  eltsize = wi::to_offset (TYPE_SIZE_UNIT (eltype));
-	}
+      /* Determine a tighter bound of the non-array element type.  */
+      tree eltype = TREE_TYPE (reftype);
+      while (TREE_CODE (eltype) == ARRAY_TYPE)
+	eltype = TREE_TYPE (eltype);
+      eltsize = wi::to_offset (TYPE_SIZE_UNIT (eltype));
     }
   else
     {
@@ -3884,27 +3880,17 @@  vrp_prop::check_mem_ref (location_t location, tree ref,
       if (TREE_CODE (reftype) != ARRAY_TYPE)
 	reftype = build_array_type_nelts (reftype, 1);
 
-      if (TREE_CODE (ref) == MEM_REF)
-	{
-	  /* Extract the element type out of MEM_REF and use its size
-	     to compute the index to print in the diagnostic; arrays
-	     in MEM_REF don't mean anything.  A type with no size like
-	     void is as good as having a size of 1.  */
-	  tree type = TREE_TYPE (ref);
-	  while (TREE_CODE (type) == ARRAY_TYPE)
-	    type = TREE_TYPE (type);
-	  if (tree size = TYPE_SIZE_UNIT (type))
-	    {
-	      offrange[0] = offrange[0] / wi::to_offset (size);
-	      offrange[1] = offrange[1] / wi::to_offset (size);
-	    }
-	}
-      else
+      /* Extract the element type out of MEM_REF and use its size
+	 to compute the index to print in the diagnostic; arrays
+	 in MEM_REF don't mean anything.  A type with no size like
+	 void is as good as having a size of 1.  */
+      tree type = TREE_TYPE (ref);
+      while (TREE_CODE (type) == ARRAY_TYPE)
+	type = TREE_TYPE (type);
+      if (tree size = TYPE_SIZE_UNIT (type))
 	{
-	  /* For anything other than MEM_REF, compute the index to
-	     print in the diagnostic as the offset over element size.  */
-	  offrange[0] = offrange[0] / eltsize;
-	  offrange[1] = offrange[1] / eltsize;
+	  offrange[0] = offrange[0] / wi::to_offset (size);
+	  offrange[1] = offrange[1] / wi::to_offset (size);
 	}
 
       bool warned;
diff --git a/gcc/tree.c b/gcc/tree.c
index 397474900ff..e470f034c8f 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -13645,6 +13645,10 @@  component_ref_size (tree ref, bool *interior_zero_length /* = NULL */)
   if (!interior_zero_length)
     interior_zero_length = &int_0_len;
 
+  /* The object/argument referenced by the COMPONENT_REF and its type.  */
+  tree arg = TREE_OPERAND (ref, 0);
+  tree argtype = TREE_TYPE (arg);
+  /* The referenced member.  */
   tree member = TREE_OPERAND (ref, 1);
 
   tree memsize = DECL_SIZE_UNIT (member);
@@ -13656,7 +13660,7 @@  component_ref_size (tree ref, bool *interior_zero_length /* = NULL */)
 
       bool trailing = array_at_struct_end_p (ref);
       bool zero_length = integer_zerop (memsize);
-      if (!trailing && (!interior_zero_length || !zero_length))
+      if (!trailing && !zero_length)
 	/* MEMBER is either an interior array or is an array with
 	   more than one element.  */
 	return memsize;
@@ -13675,9 +13679,14 @@  component_ref_size (tree ref, bool *interior_zero_length /* = NULL */)
 		  offset_int minidx = wi::to_offset (min);
 		  offset_int maxidx = wi::to_offset (max);
 		  if (maxidx - minidx > 0)
-		    /* MEMBER is an array with more than 1 element.  */
+		    /* MEMBER is an array with more than one element.  */
 		    return memsize;
 		}
+
+      /* For a refernce to a zero- or one-element array member of a union
+	 use the size of the union instead of the size of the member.  */
+      if (TREE_CODE (argtype) == UNION_TYPE)
+	memsize = TYPE_SIZE_UNIT (argtype);
     }
 
   /* MEMBER is either a bona fide flexible array member, or a zero-length
@@ -13692,28 +13701,27 @@  component_ref_size (tree ref, bool *interior_zero_length /* = NULL */)
       if (!*interior_zero_length)
 	return NULL_TREE;
 
-      if (TREE_CODE (TREE_OPERAND (ref, 0)) != COMPONENT_REF)
+      if (TREE_CODE (arg) != COMPONENT_REF)
 	return NULL_TREE;
 
-      base = TREE_OPERAND (ref, 0);
+      base = arg;
       while (TREE_CODE (base) == COMPONENT_REF)
 	base = TREE_OPERAND (base, 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 cast to REFTYPE (e.g., a char buffer used to store
-     a REFTYPE object).  */
-  tree reftype = TREE_TYPE (TREE_OPERAND (ref, 0));
+     or that is cast to ARGTYPE (e.g., a char buffer used to store
+     an ARGTYPE object).  */
   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.  */
+     the same as ARGTYPE 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);
+  bool typematch = useless_type_conversion_p (argtype, bt);
   if (memsize && typematch)
     return memsize;
 
@@ -13729,7 +13737,7 @@  component_ref_size (tree ref, bool *interior_zero_length /* = NULL */)
 	  if (init)
 	    {
 	      memsize = TYPE_SIZE_UNIT (TREE_TYPE (init));
-	      if (tree refsize = TYPE_SIZE_UNIT (reftype))
+	      if (tree refsize = TYPE_SIZE_UNIT (argtype))
 		{
 		  /* Use the larger of the initializer size and the tail
 		     padding in the enclosing struct.  */