Initializer element is not a compile-time constant but compiler doesn't throw error

129 Views Asked by At

I'm studying embedded systems and implementing a startup.c file from scratch. Upon creating the interrupt vector table, I get the following error on my editor (I'm using clangd as my LSP):

enter image description here

startup.c:

#include <stdint.h>

#define SRAM_START 0x20000000U                
#define SRAM_SIZE (128U * 1024U)              
#define SRAM_END ((SRAM_START) + (SRAM_SIZE)) 
#define STACK_START SRAM_END 

int main(void);
void reset_handler(void);
/* more stuff */

uint32_t vectors[] __attribute__((section(".isr_vectors"))) = {
    STACK_START,
    (uint32_t)reset_handler,
    /* more stuff */
}

void reset_handler(void){
    /* .data, .text and .bss */
    main();
}

Why is it that gcc allows this to compile?

2

There are 2 best solutions below

8
KamilCuk On

Why is it that gcc allows this to compile?

He doesn't. Just make the vectors an array of function pointers.

typedef void (*const pHandler)(void);
__attribute__((section(".isr_vectors"), used)) 
pHandler vectors[] = {
    (pHandler)STACK_START,
    reset_handler,
};

why changing the type of the elements solves the issue regarding the functions not being const initializers? – Gerhardh

The problem with OP code is the conversion from a function pointer &reset_handler to a uint32_t value at (uint32_t)reset_handler,. Below, I present a smaller code example highlighting the problem:

#include <stdint.h>
void reset_handler(void);
uint32_t c = reset_handler; // error - not able to compute reset_handler as uint32_t at compile time

By changing the type of the array, the conversion is no longer needed, so it can be removed. By removing the code causing the error, the issue is solved.

5
John Bollinger On

Why is it that gcc allows this to compile?

C implementations are not required to reject any particular code. They are required to emit a diagnostic message describing each constraint violation, but even for those, they can otherwise do whatever they want. Literally whatever: such code has no defined behavior as far as the standard is concerned. Many implementations implement extensions that involve accepting code that violates language constraints.

With that said, your IDE's message is misleading. The expression in question indeed does not satisfy the definition of an "integer constant expression", nor of any other type of constant expression that the spec explicitly permits to appear in an initializer for an object with static or thread storage duration. But without the cast it would be an address constant, which is allowed in initializers, and it's plausible that the cast could be evaluated at translation time, especially when building for a 32-bit architecture.

In that light, then, consider also that the spec explicitly allows implementations to accept other forms of constant expressions.

I can't say why your particular gcc accepts the code, but overall, nothing requires C implementations to reject it, and for some implementations, it would be entirely natural to accept it.