https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103542
Bug ID: 103542 Summary: bogus -Warray-bounds while index is limited by switch/case Product: gcc Version: 11.2.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: other Assignee: unassigned at gcc dot gnu.org Reporter: patrickdepinguin at gmail dot com Target Milestone: --- gcc 11.2.0 reports the following on a reduced test case: $ powerpc-linux-gcc -c array-bounds-fruit.c -O2 -Wall -Werror array-bounds-fruit.c: In function 'get_default_config.part.0': array-bounds-fruit.c:69:37: error: array subscript 4 is above array bounds of 'struct fruit_config[4]' [-Werror=array-bounds] 69 | do_something(id, &config[id].num_lemons); | ~~~~~~^~~~ array-bounds-fruit.c:19:28: note: while referencing 'config' 19 | static struct fruit_config config[4]; | ^~~~~~ cc1: all warnings being treated as errors Above is for powerpc, but I have the same problem with ARM. The offending line is inside a switch/case, within the block where 'id' is tested to be 0, 1, 2, or 3. gcc/g++ is considering a case where 'id' becomes 4, which is not possible in this code. If I make any more changes (even seemingly unrelated changes) to the test case, the error disappears. Test code: #include <stddef.h> #include <stdbool.h> #include <stdint.h> enum { ID_0 = 0, ID_1 = 1, ID_2 = 2, ID_3 = 3, MAX_IDS, }; #define MAX_ENTRIES 256 struct fruit_config { uint32_t num_apples; uint32_t num_lemons; uint32_t * lemons; }; static struct fruit_config config[4]; static uint32_t unrelated_table[MAX_IDS][MAX_ENTRIES]; uint32_t do_something(const uint32_t id, uint32_t * number_of_entries) { uint32_t error = 0; switch (id) { /* merging these case statements with identical body removes the issue */ case ID_0: { *number_of_entries = 0; break; } case ID_1: { *number_of_entries = 0; break; } case ID_2: { *number_of_entries = 0; break; } case ID_3: { *number_of_entries = 0; break; } default: { error = 0xff; *number_of_entries = 0; break; } } return error; } struct fruit_config * get_default_config(const uint32_t id) { switch (id) { case ID_0: case ID_1: case ID_2: case ID_3: { uint32_t entry = 0; for (entry = 0; entry <config[id].num_apples ; entry++) { unrelated_table[0][0] = 0; } config[id].num_apples = 0; do_something(id, &config[id].num_lemons); /* removing following two lines removes the issue, even though * the error already occurs above */ config[id].num_lemons = 0; config[id].lemons = NULL; /* removing use of error removes the issue */ extern void foo(uint32_t arg); uint32_t error = 0; foo(error); break; } default: { break; } } return NULL; } void func_start(void) { uint32_t i = 0; for (i = 0; i < MAX_IDS; i++) { get_default_config(i); } }