Consider this code in block scope:
struct foo { unsigned char a; unsigned char b; } x, y;
x.a = 0;
y = x;
C [N1570] 6.3.2.1 2 says “If the
Firstly, let's note that the quoted part of 6.3.2.1/2, the so-called "Itanium clause" is the only clause under which this code might have a problem. In other words, if this clause were not present, the code is fine. Structs may not have trap representations, so y = x;
is otherwise OK even if x
is entirely uninitialized. The resolution of DR 451 clarifies that indeterminate values may be propagated by assignment, without causing UB.
Back to the Itanium clause here. As you point out, the Standard does not clearly specify whether x.a = 0;
negates the precondition "x
is uninitialized".
IMO, this means we should turn to the rationale for the Itanium clause to determine the intent. The purpose of the wording of the standard document, in general, is to implement an intent; generally speaking, I don't agree with being dogmatic about minute detail of the standard: taking shades of meaning out of the wording that were not intended by those who created the wording.
This Q/A gives a good explanation of the rationale. The potential problem is that x
might be stored in a register with the NaT bit set, and then y = x
will cause a hardware exception due to reading a register that has that bit set.
So the question is: On IA64 does x.a = 0;
clear the NaT bit? I don't know and I guess we would need someone familar with that platform to give a conclusive answer here.
Naively, I imagine that if x
is in a register then, in general, x.a = 0;
will need to read the old value, and apply a mask to clear the bits for a
, thereby triggering the exception if x
was NaT
. However, x.a = 0;
cannot trigger UB, so that logic must be incorrect. Perhaps IA64 compilers never store a struct in a register, or perhaps they clear the NaT bit on declaration of one, or perhaps there's a hardware instruction to implement x.a = 0;
on a previously-NaT register, I don't know.