The result of DECL_SIZE_UNIT doesn't always reflect the size
of data members of virtual classes.  This can lead to objects
of such types appearing smaller than they are to warnings like
-Warray-bounds or -Wstringop-overflow, causing false positives.

To avoid these false positives, the attached replaces the use
of DECL_SIZE_UNIT in component_ref_size in the middle end with
TYPE_SIZE_UNIT.

Tested on x86_64-linux.

Martin
PR middle-end/97595 - bogus -Wstringop-overflow due to DECL_SIZE_UNIT underreporting field size

gcc/ChangeLog:

	PR middle-end/97595
	* tree.c (component_ref_size): Use TYPE_SIZE_UNIT.
	* tree.g (DECL_SIZE): Update comment.

gcc/testsuite/ChangeLog:

	PR middle-end/97595
	* g++.dg/warn/Warray-bounds-14.C: New test.
	* g++.dg/warn/Wstringop-overflow-6.C: New test.

diff --git a/gcc/testsuite/g++.dg/warn/Warray-bounds-14.C b/gcc/testsuite/g++.dg/warn/Warray-bounds-14.C
new file mode 100644
index 00000000000..0812f833d74
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Warray-bounds-14.C
@@ -0,0 +1,25 @@
+/* PR middle-end/97595 - bogus -Wstringop-overflow due to DECL_SIZE_UNIT
+   underreporting field size
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+struct A { char a[32]; };
+struct B: virtual A { };
+struct C: B { };
+
+struct D
+{
+  B &b;
+  D (B&);
+};
+
+D::D (B &b): b (b) { }        // { dg-bogus "-Warray-bounds" }
+
+void f (void*);
+
+void g ()
+{
+  C c;
+  D d (c);
+  f (&d);
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wstringop-overflow-6.C b/gcc/testsuite/g++.dg/warn/Wstringop-overflow-6.C
new file mode 100644
index 00000000000..8173e601d4b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wstringop-overflow-6.C
@@ -0,0 +1,8 @@
+/* PR middle-end/97595 - bogus -Wstringop-overflow due to DECL_SIZE_UNIT
+   underreporting field size
+   { dg-do compile { target c++11 } }
+   { dg-options "-O2 -Wall -Wsystem-headers" } */
+
+#include <iostream>
+
+template void std::basic_iostream<char>::swap (basic_iostream&);
diff --git a/gcc/tree.c b/gcc/tree.c
index 1ad4ad5a5f7..027cf871a67 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -13725,7 +13725,10 @@ component_ref_size (tree ref, special_array_member *sam /* = NULL */)
     {
       tree memtype = TREE_TYPE (member);
       if (TREE_CODE (memtype) != ARRAY_TYPE)
-	return memsize;
+	/* DECL_SIZE may be less than TYPE_SIZE in C++ when referring
+	   to the type of a virtual base class which doesn't reflect
+	   the size of the virtual's members (see pr97595).  */
+	return TYPE_SIZE_UNIT (memtype);
 
       bool trailing = array_at_struct_end_p (ref);
       bool zero_length = integer_zerop (memsize);
diff --git a/gcc/tree.h b/gcc/tree.h
index 9a713cdb0c7..671512358bd 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2518,7 +2518,10 @@ extern tree vector_element_bits_tree (const_tree);
 #define DECL_INITIAL(NODE) (DECL_COMMON_CHECK (NODE)->decl_common.initial)
 
 /* Holds the size of the datum, in bits, as a tree expression.
-   Need not be constant and may be null.  */
+   Need not be constant and may be null.  May be less than
+   TYPE_SIZE for a NODE of the same type (in C++, DECL_SIZE of
+   an object of a virtual base class doesn't include the sizes
+   of data members of that class).  */
 #define DECL_SIZE(NODE) (DECL_COMMON_CHECK (NODE)->decl_common.size)
 /* Likewise for the size in bytes.  */
 #define DECL_SIZE_UNIT(NODE) (DECL_COMMON_CHECK (NODE)->decl_common.size_unit)

Reply via email to