Minified testcase (almost every line is necessary to reproduce): ``` /* g++ -Warray-bounds -O2 repro.cc */ int longer[7] = {}; int shorter[2] = {}; int out[10] = {}; template <int N> void configure(const int(&in)[N], const int nrows = N) { if (nrows <= 10) { for (int i = 0; i < nrows; i++) { out[i] = in[i]; } } } int main() { configure(longer); configure(shorter); } ``` Output: ``` $ g++ -Warray-bounds -O2 repro.cc repro.cc: In function 'int main()': repro.cc:13:24: warning: array subscript 'const int [7][0]' is partly outside array bounds of 'int [2]' [-Warray-bounds] 13 | out[i] = in[i]; | ~~^ repro.cc:3:5: note: while referencing 'shorter' 3 | int shorter[2] = {}; | ^~~~~~~ repro.cc:13:24: warning: array subscript 'const int [7][0]' is partly outside array bounds of 'int [2]' [-Warray-bounds] 13 | out[i] = in[i]; | ~~^ repro.cc:3:5: note: while referencing 'shorter' 3 | int shorter[2] = {}; | ^~~~~~~ ``` Static analysis appears to be using the length of the longer array for the call using the shorter array. The warning disappears by: * commenting out the first call to configure() suppresses the warning * swapping the two calls to configure() * commenting out if statement also eliminates the warning * making longer and shorter the same size * using N as loop counter instead of nrows
Reproducible in Godbolt with any 11.x release as well as trunk: https://godbolt.org/z/zWb55P8G7
GCC 12.0.1 20220413 (Red Hat 12.0.1-0) gives a more helpful diagnostic with inlining backtrace: In function ‘void configure(const int (&)[N], int) [with int N = 7]’, inlined from ‘void configure(const int (&)[N], int) [with int N = 2]’ at testcase.cc:8:6, inlined from ‘int main()’ at gcc-array-bounds-bug-testcase.cc:22:12: testcase.cc:14:24: warning: array subscript ‘const int [7][0]’ is partly outside array bounds of ‘int [2]’ [-Warray-bounds] 14 | out[i] = in[i]; | ~~^ Note how configure<N=7>() was inlined into configure<N=7>().
Not sure if this is helpful, but the IPA dump also shows two calls to configure() with N=7 and none with N=2: IPA function summary for int main()/3 inlinable global time: 26.000000 self size: 9 global size: 9 min size: 6 self stack: 0 global stack: 0 size:0.000000, time:0.000000 size:3.000000, time:2.000000, executed if:(not inlined) calls: void configure(const int (&)[N], int) [with int N = 7]/6 function not considered for inlining freq:1.00 loop depth: 0 size: 3 time: 12 callee size: 5 stack: 0 op0 is compile time invariant op1 is compile time invariant void configure(const int (&)[N], int) [with int N = 7]/6 function not considered for inlining freq:1.00 loop depth: 0 size: 3 time: 12 callee size: 5 stack: 0 op0 is compile time invariant op1 is compile time invariant
Furthermore, after the inline pass main() has a reference to the shorter array with an annotation of int[7], which is clearly wrong: <bb 5> [local count: 939524097]: _5 = MEM[(const int[7] &)&shorter][i_4]; out[i_4] = _5; i_6 = i_4 + 1;
It's IPA ICF that merges the two functions. We then end up inlining but not using the original function body but the merged one.
(In reply to Richard Biener from comment #5) > It's IPA ICF that merges the two functions. We then end up inlining but not > using the original function body but the merged one. I wonder if we could get wrong code because of that ...
Btw. started with r11-4987-g602c6cfc79ce4ae6.
So the 2 following functions are merged: void _Z9configureILi7EEvRAT__Kii.part.0<7> (const int[7] & in, const int nrows) { ... void _Z9configureILi2EEvRAT__Kii.part.0<2> (const int[2] & in, const int nrows) { ... which have a different argument in. However I don't think one can't end with a wrong code as nrows argument would be different in: _Z9configureILi7EEvRAT__Kii.part.0<7> (&longer, 7); _Z9configureILi2EEvRAT__Kii.part.0<2> (&shorter, 2); I was unable to come up with a code that would be miscompiled.
Sth should be done about the argument type mismatch though - like changing it to a "common" type (with open bound?)
Still present on GCC 12.2. Could someone look into it please, or point me at the point in ipa-icf.cc where the array-bounds analysis information should have been updated after merging the template instantiations?
(In reply to Bernie Innocenti from comment #10) > Still present on GCC 12.2. > > Could someone look into it please, or point me at the point in ipa-icf.cc > where the array-bounds analysis information should have been updated after > merging the template instantiations? Well, you will need to modify all parameters and local variables at the end of sem_function::merge function.
GCC 11.4 is being released, retargeting bugs to GCC 11.5.
Still present on g++ 13.1.1 I discovered that -O2 is required to trigger this bug. These compile without warnings: g++ -O1 -Warray-bounds repro.cc g++ -O3 -Warray-bounds repro.cc g++ -Ofast -Warray-bounds repro.cc
GCC 13.2 is being released, retargeting bugs to GCC 13.3.
GCC 13.3 is being released, retargeting bugs to GCC 13.4.