'How to avoid wrong array-bounds warning on a pointer in g++12?
For a bare metal app for a Raspberry Pi I have this code:
#define UART0_BASE 0x3F201000
void putc(char c) {
volatile unsigned int *UART0_DR = (volatile unsigned int *)(UART0_BASE);
volatile unsigned int *UART0_FR = (volatile unsigned int *)(UART0_BASE + 0x18);
while (*UART0_FR & (1 << 5) ) { }
*UART0_DR = c;
}
When I compile this with g++-12 I get (same for UART0_FR):
uart.cc:28:15: warning: array subscript 0 is outside array bounds of ‘volatile unsigned int [0]’ [-Warray-bounds]
28 | *UART0_DR = c;
| ^~~~~~~~~
Note: This is new in g++-12 (trunk on godbolt as of this writing) and 11.2 and before don't show it.
The pointer is not out of bounds and I know that the UART data register is at that place. So how do I tell the compiler the bounds for the pointer?
I'm actually looking for possibly 3 cases:
- pointer to a single volatile unsigned int
- pointer to an array of N volatile unsigned int
- pointer to a struct of volatile unsigned int
All 3 cases would be at a fixed address and not something returned by malloc/new where the compiler knows the bounds.
Update: The warning goes away if the pointer is global or static instead of local. So likely a compiler bug or at least inconsistent.
Solution 1:[1]
The problem here isn't with your code per se, the problem is with C++ permitting very open-ended pointer operations, and newer versions including static analysis tools to try to prevent some common problems that arise from it, which require you to use less open-ended code. For example, you could write your code as follows:
#define UART0_BASE 0x3F201000
void putc(char c) {
volatile unsigned int &UART0_DR = *(volatile unsigned int *)(UART0_BASE);
volatile unsigned int &UART0_FR = *(volatile unsigned int *)(UART0_BASE + 0x18);
while (UART0_FR & (1 << 5) ) { }
UART0_DR = c;
}
In the above the pointer is no longer a naked pointer with no length, it's a reference to a length one memory location of type uint32_t (which you should definitely be using over your types), so the compiler knows exactly how much memory is safely accessible.
pointer to an array of N volatile unsigned int
// you want to use standard spans here, since they carry both start, type and length information
auto mem = std::span { (unsigned int *)START_ADDRESS, N };
mem[2] = 50; // read and write as usual
pointer to a struct of volatile unsigned int
If I'm reading this right, it's exactly like the first case, cast it to a reference of your struct. Just make sure to set the packing correctly using #pragma pack or your compiler's flavor to match the hardware memory packing.
Again, I want to make sure you understand that the problem isn't with your code as is, it's that the compiler is telling you that if you provide to it additional information (pointer start, type and length, ie a span) it can help you avoid some common problems. It's up to you to decide whether it's worth it or not, but if you want my opinion, naked pointers are always the first place I look at when something isn't working, they cause most problems in C++ code. There are definitely better tools.
Solution 2:[2]
It's a compiler bug with the supposed workaround is to disable the warning for the affected function.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Blindy |
| Solution 2 | Goswin von Brederlow |
