Thread: gcc 15 "array subscript 0" warning at level -O3
Whilst poking at Erik Rijkers' nearby report, I found that Fedora 42's gcc 15.0.1 will produce this complaint if you select optimization level -O3: In file included from ../../../../src/include/access/htup_details.h:22, from pl_exec.c:21: In function 'assign_simple_var', inlined from 'exec_set_found' at pl_exec.c:8609:2: ../../../../src/include/varatt.h:230:36: warning: array subscript 0 is outside array bounds of 'char[0]' [-Warray-bounds=] 230 | (((varattrib_1b_e *) (PTR))->va_tag) | ^ ../../../../src/include/varatt.h:94:12: note: in definition of macro 'VARTAG_IS_EXPANDED' 94 | (((tag) & ~1) == VARTAG_EXPANDED_RO) | ^~~ ../../../../src/include/varatt.h:284:57: note: in expansion of macro 'VARTAG_1B_E' 284 | #define VARTAG_EXTERNAL(PTR) VARTAG_1B_E(PTR) | ^~~~~~~~~~~ ../../../../src/include/varatt.h:301:57: note: in expansion of macro 'VARTAG_EXTERNAL' 301 | (VARATT_IS_EXTERNAL(PTR) && !VARTAG_IS_EXPANDED(VARTAG_EXTERNAL(PTR))) | ^~~~~~~~~~~~~~~ pl_exec.c:8797:17: note: in expansion of macro 'VARATT_IS_EXTERNAL_NON_EXPANDED' 8797 | VARATT_IS_EXTERNAL_NON_EXPANDED(DatumGetPointer(newvalue))) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In function 'exec_set_found': cc1: note: source object is likely at address zero Buildfarm member serinus has been producing the identical warning for some time. I'd been ignoring that because it runs "experimental gcc", but I guess the experiment has leaked out to production distros. What seems to be happening here is that after inlining assign_simple_var into exec_set_found, the compiler decides that "newvalue" might be zero (since it's a BoolGetDatum result), and then it warns -- in a rather strange way -- about the potential null dereference. The dereference is not reachable because of the preceding "var->datatype->typlen == -1" check, but that's not stopping the optimizer from bitching. I experimented with modifying exec_set_found thus: var = (PLpgSQL_var *) (estate->datums[estate->found_varno]); + Assert(var->datatype->typlen == 1); assign_simple_var(estate, var, BoolGetDatum(state), false, false); which should be OK since we're expecting the "found" variable to be boolean. That does silence the warning, but of course only in --enable-cassert builds. Anybody have an idea about how to silence it more effectively? There are going to be more people seeing this as gcc 15 propagates. regards, tom lane
I wrote: > Anybody have an idea about how to silence it more effectively? > There are going to be more people seeing this as gcc 15 propagates. Meh. I tried this: diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index bb99781c56e..ea489db89c9 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -8794,6 +8794,7 @@ assign_simple_var(PLpgSQL_execstate *estate, PLpgSQL_var *var, * not a problem since all array entries are always detoasted.) */ if (!estate->atomic && !isnull && var->datatype->typlen == -1 && + DatumGetPointer(newvalue) != NULL && VARATT_IS_EXTERNAL_NON_EXPANDED(DatumGetPointer(newvalue))) { MemoryContext oldcxt; and was rewarded with *two* copies of the warning. So that makes it smell more like a compiler bug than anything else. (I now vaguely recall Andres opining that that's what it was on serinus, though I can't find any such email.) regards, tom lane
Hi, On 2025-04-25 13:37:15 -0400, Tom Lane wrote: > Whilst poking at Erik Rijkers' nearby report, I found that > Fedora 42's gcc 15.0.1 will produce this complaint if you > select optimization level -O3: > > In file included from ../../../../src/include/access/htup_details.h:22, > from pl_exec.c:21: > In function 'assign_simple_var', > inlined from 'exec_set_found' at pl_exec.c:8609:2: > ../../../../src/include/varatt.h:230:36: warning: array subscript 0 is outside array bounds of 'char[0]' [-Warray-bounds=] > 230 | (((varattrib_1b_e *) (PTR))->va_tag) > | ^ > ../../../../src/include/varatt.h:94:12: note: in definition of macro 'VARTAG_IS_EXPANDED' > 94 | (((tag) & ~1) == VARTAG_EXPANDED_RO) > | ^~~ > ../../../../src/include/varatt.h:284:57: note: in expansion of macro 'VARTAG_1B_E' > 284 | #define VARTAG_EXTERNAL(PTR) VARTAG_1B_E(PTR) > | ^~~~~~~~~~~ > ../../../../src/include/varatt.h:301:57: note: in expansion of macro 'VARTAG_EXTERNAL' > 301 | (VARATT_IS_EXTERNAL(PTR) && !VARTAG_IS_EXPANDED(VARTAG_EXTERNAL(PTR))) > | ^~~~~~~~~~~~~~~ > pl_exec.c:8797:17: note: in expansion of macro 'VARATT_IS_EXTERNAL_NON_EXPANDED' > 8797 | VARATT_IS_EXTERNAL_NON_EXPANDED(DatumGetPointer(newvalue))) > | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > In function 'exec_set_found': > cc1: note: source object is likely at address zero FWIW, I've seen this even before GCC 15. > Buildfarm member serinus has been producing the identical warning for > some time. I'd been ignoring that because it runs "experimental gcc", > but I guess the experiment has leaked out to production distros. > > What seems to be happening here is that after inlining > assign_simple_var into exec_set_found, the compiler decides that > "newvalue" might be zero (since it's a BoolGetDatum result), > and then it warns -- in a rather strange way -- about the > potential null dereference. I don't think it actually is complaining about a null dereference - it thinks we're interpreting a boolean as a pointer (for which it obviously is not wide enough) > The dereference is not reachable > because of the preceding "var->datatype->typlen == -1" check, > but that's not stopping the optimizer from bitching. > I experimented with modifying exec_set_found thus: > > var = (PLpgSQL_var *) (estate->datums[estate->found_varno]); > + Assert(var->datatype->typlen == 1); > assign_simple_var(estate, var, BoolGetDatum(state), false, false); > > which should be OK since we're expecting the "found" variable to > be boolean. That does silence the warning, but of course only > in --enable-cassert builds. One way to address this is outlined here: https://postgr.es/m/20230316172818.x6375uvheom3ibt2%40awork3.anarazel.de https://postgr.es/m/20240207203138.sknifhlppdtgtxnk%40awork3.anarazel.de I've been wondering about adding wrapping something like that in a pg_assume(expr) or such. Greetings, Andres Freund