I have the following code:
// in global scope:
int x = x;
This code compiles with no warnings for both GCC and clang to:
x:
.zero 4
Is this well-defined, and how does it work?
I have the following code:
// in global scope:
int x = x;
This code compiles with no warnings for both GCC and clang to:
x:
.zero 4
Is this well-defined, and how does it work?
Yes, this is well-defined, and equivalent to:
int x = 0;
If x
was a local variable, then your code would be UB, because x
would be accessed before its lifetime has begun.
However, this is not the case for global variables, because:
Constant initialization is performed if a variable or temporary object with static or thread storage duration is constant-initialized. If constant initialization is not performed, a variable with static storage duration or thread storage duration is zero-initialized. Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. All static initialization strongly happens before any dynamic initialization.
int x
is not constant-initialized, because x
is not a constant expression. Therefore, it is zero-initialized, and this strongly happens before any dynamic initialization.
This tells us that int x = x;
is equivalent to:
int x = 0;
// assume this is called at some point before main():
void __dynamic_init__() {
x = x;
}
The obvious question now is:
If this is performing dynamic initialization, why does the compiler output no code that looks like dynamic initialization, but simply emits
.zero 4
?
One explanation is that the dynamic initialization can be optimized out as per the "as-if rule". Another explanation is this:
An implementation is permitted to perform the initialization of a variable with static or thread storage duration as a static initialization even if such initialization is not required to be done statically, provided that
- (3.1) the dynamic version of the initialization does not change the value of any other object of static or thread storage duration prior to its initialization, and
- (3.2) the static version of the initialization produces the same value in the initialized variable as would be produced by the dynamic initialization if all variables not required to be initialized statically were initialized dynamically.
(3.1) is obviously true, because the expression x
does not modify any objects. (3.2) is also true, because if int x
was initialized dynamically, then the expression x
would always be zero.
Therefore, this dynamic initialization can also be turned into static initialization to zero.
According to [basic.life] p1.2, the lifetime of an object begins once "initialization (if any) is complete".
It is slightly unclear whether the lifetime of x
begins after zero-initialization, or after dynamic initialization.
If it began after dynamic initialization, then the expression x
during dynamic initialization would access x
before its lifetime has begun, which is undefined behavior.
However, since static initialization is a form of initialization, this seems like more of a wording issue, then the intended behavior. After all, this paragraph doesn't acknowledge at all that multiple initializations take place.