https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91688
Bug ID: 91688 Summary: -Woverride-init could use an intermediate mode of operation Product: gcc Version: 9.2.1 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c Assignee: unassigned at gcc dot gnu.org Reporter: eblake at redhat dot com Target Milestone: --- Right now, -Woverride-init is so strong that it interferes with the use of gcc's extension of ranged-array initializer syntax as a common way to pre-initialize an array to a specific non-zero default value then override specific members in the array to a different value. Thus, it is not part of -Wall (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=24010#c4). However, this warning is so strong that the qemu project is unable to use -Woverride-init (it would fail to compile due to intentional reinitialization overrides of ranged defaults), but thus failed to diagnose a bug (a partial initialization caused zero-initialization of an unmentioned member, overriding an earlier explicit mention of that member: https://lists.gnu.org/archive/html/qemu-devel/2019-09/msg01143.html), at least without using pragmas to temporarily silence the warning. I am wondering if it would be possible to split this warning into two categories: -Woverride-init=1 (preferably hoisted into -Wall) which warns about obvious inadvertent overrides, and -Woverride-init=2 (only part of -Wextra, if at all) that warns about all overrides, where the difference depends on whether the second initializer occurs on a subobject that is larger in size than the first initializer's subobject. For some demonstrations of what I mean (and intentionally using -Wno-missing-braces): $ cat foo.c #include <stdio.h> struct A { int i; int j; }; struct B { struct A one; struct A two; struct A three; int four[2]; }; void dump (struct B *b) { printf ("one=[%d,%d],two=[%d,%d],three=[%d,%d],four=[%d,%d]\n", b->one.i, b->one.j, b->two.i, b->two.j, b->three.i, b->three.j, b->four[0], b->four[1]); } int main(void) { struct B b1 = { .one.i = 1, .two.j = 2, 3, .four[1] = 9, .one = { 5, 6 }, .two = { 7 }, .three = { .j = 8 }, .four[0 ... 1] = 4, }; struct B b2 = { .one = { 4, 5 }, .two = { 6 }, .three = { .j = 7 }, .four[0 ... 1] = 4, .one.i = 1, .two.j = 2, 3, .four[1] = 9, }; dump (&b1); dump (&b2); return 0; } $ gcc -Wall -Wextra -Wno-missing-braces -o foo foo.c foo.c: In function ‘main’: foo.c:28:11: warning: initialized field overwritten [-Woverride-init] 28 | .one = { 5, 6 }, | ^ foo.c:28:11: note: (near initialization for ‘b1.one’) foo.c:29:11: warning: initialized field overwritten [-Woverride-init] 29 | .two = { 7 }, | ^ foo.c:29:11: note: (near initialization for ‘b1.two’) foo.c:30:13: warning: initialized field overwritten [-Woverride-init] 30 | .three = { .j = 8 }, | ^ foo.c:30:13: note: (near initialization for ‘b1.three’) foo.c:31:21: warning: initialized field overwritten [-Woverride-init] 31 | .four[0 ... 1] = 4, | ^ foo.c:31:21: note: (near initialization for ‘b1.four[1]’) foo.c:38:13: warning: initialized field overwritten [-Woverride-init] 38 | .one.i = 1, | ^ foo.c:38:13: note: (near initialization for ‘b2.one.i’) foo.c:41:15: warning: initialized field overwritten [-Woverride-init] 41 | .four[1] = 9, | ^ foo.c:41:15: note: (near initialization for ‘b2.four[1]’) The four warnings for b1 all make sense at -Woverride-init=1: they are clearly inadvertent, in that the second half of initializers all touch a larger subset of the overall b2 object than the first half (providing both members for .one overrides the earlier specific setting of .one.i; providing a partial initializer for .two implies a zero-initialization override of the earlier .two.j; providing a partial initializer for .three implies a zero-initialization override of the earlier (non-designated) .three.i, and doing a ranged initializer over .four[0 ... 1] overrides the earlier specific initialization of .four[1].) However, while the first warning of b2 is reasonable (the explicit initialization of .one.i overrides the earlier explicit initialization of .one), this is a case of a more-specific override (.one.i is a smaller subset of b2 than .one), and the second one is the show-stopper that prevents the use of -Woverride-init in -Wall (it is intentional that a ranged initializer sets a non-zero default, and then later initializers of obvious smaller scope change that default). So the warnings emitted here would fit better in -Woverride-init=2. Perhaps we could even go one step further and have -Woverride-init=3 to catch the case of the double-initialization of b2.two.j (first to its implicit zero-initialization due to the partial initializer for .two, then again via the explicit designator .two.j) and b2.three.i (again, due to implicit zero-initializion from the partial initializer for .three, before the explicit initialization from the (non-designated) .three.i)- but it is GOOD that gcc does not warn about these cases under the existing -Woverride-init. At any rate, with this enhancement, qemu could use -Woverride-init=1 (perhaps even automatically as part of -Wall), without worrying about having to use pragmas around the cases where it does intentional overrides of a non-zero default. (qemu uses an explicit -Wno-initializer-overrides to overcome the clang spelling of the warning, because clang has the same 6 warnings as gcc on my example code, where the 6th one interferes with intentional use, and because clang sticks -Winitializer-overrides in -Wall, whereas gcc was smart enough to stick it only in -Wextra)