Bug 91688 - -Woverride-init could use an intermediate mode of operation
Summary: -Woverride-init could use an intermediate mode of operation
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: c (show other bugs)
Version: 9.2.1
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: diagnostic
Depends on:
Blocks:
 
Reported: 2019-09-06 16:02 UTC by Eric Blake
Modified: 2019-12-06 15:20 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Eric Blake 2019-09-06 16:02:54 UTC
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)