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)