Gordon said:
The fact that something compiles never proves that it has defined
translation behavior. The fact that something has defined behavior
proves that it has defined translation behavior. If something has
undefined behavior, it *ALWAYS* has undefined behavior, even if
what it does is the obvious and expected result. One of the most
insidious and evil types of undefined translation behavior is for
the compiler to terminate normally with no error messages and produce
apparently-valid object code or an executable.
While I appreciate your response, it is critically lacking context.
This sub-discussion was in regards to whether or not the original post's
code could have undefined behaviour during translation.
My recollection of the context and my perspective on the two statements
you quote were that:
- We agree that it has well-defined translation behaviour
- We agree that it has well-defined translation behaviour
The second statement was meant as an equivalence, not a conclusion.
If you wish to dispute whether or not the original post's code has
Standard-defined translation behaviour, I'd enjoy reading about it. One
possible point of interest might be that since an implementation defines
what constitutes a 'volatile' access, is the implementation allowed to
deem that it is a violation of 6.5p2 and abort the translation?
I read and enjoyed your examples of translation-time implementation bugs
and quirks.
Multiple writes to the same register could, conceivably, translate
to two (tristate: drive to 1, drive to 0, or don't drive anywhere)
gates driving the same wire, one to a binary 1 and the other to a
binary 0. This may violate gate current limits. Over time, this
might cause heating and eventual permanent damage to the gates,
although they tend to make gates robust enough to avoid self-destructing
(slight timing variations make it likely that turning off output
drivers for one output and turning on another may result in a very
short (e.g. nanoseconds or picoseconds) overlap when both are active.
Nobody's supposed to be reading the result until it has settled
down.) What gets read may be a "wired-OR" or "wired-AND" of the
inputs or something else. Blown fuses or hardware damage are
unlikely but not impossible.
Thanks for agreeing.
It could also generate code that attempts to give each possible
order of side effects once on each subsequent call, and an infinite
loop after all possibilities have been used. This might fail when
the code gets too large to fit or too complex for the compiler to
handle.
What I was trying to get at was that it is a tricky determination to
make at translation-time... That is, if you can design something which
a compiler cannot prove will lead to undefined behaviour, then what
license does the [conforming] compiler have to refuse to translate? I'd
suggest that the compiler must translate _as_if_ there was no undefined
behaviour expected; the UB would come from whatever terrible things
happened at run-time.
There are plenty of *syntactically* valid statements that make
no sense semantically.
You are adding emphasis here where I do not perceive a particular need
for emphasis. Do you realize that we are in agreement? It seems to me
that a conforming implementation has a license to reject a translation
if there are semantic violations and lack of definitions, as well. Do
you perceive any in the original post's code?
For example:
int x,y;
... initialize x somehow ...;
switch(x) {
case 1: y = 2; break;
case 1: y = -2; break;
case 1: y = 0; break;
}
(Most compilers give a "duplicate value in switch" or similar error)
Yes. This is a violation of 6.8.4.2p3.
OR
struct tm t; /* *NOT* a pointer */
... fill in some elements of t ...;
if (t) { ... }
Yes. This is a violation of 6.8.4.1p1.
Doing a complete job of this requires that it be at runtime,
Thanks for agreeing.
since
user input may be required to determine whether it's a problem.
Thanks for agreeing.
Some of the more obvious ones like
i = ++i + i++;
can easily be done at compile time.
No, it's not, to do a complete job, especially when you have to
figure out whether two array subscripts are/could be the same at
compile time (I think this is equivalent to the halting problem).
For some of the simpler cases, it is easy.
I believe that you have responded to the context of the code of the
original post when the quoted discussion was in regards to Mr. T. St
Denis' code, which, by co-incidence, very nearly resembles the code
you've given just above. There is an assertion of ease in your code
example followed immediately by a denial of ease for nearly the same
example. Oops.
In Mr. St Denis' code, it seems like [a violation of 6.5p2] would be an
easy determination to make. I believe that we are in agreement
_yet_again_. We have rapport.
For a typical machine with memory-mapped I/O registers, and/or
control blocks that might be modified asynchronously by the OS and/or
interrupt handlers, I propose these rules for volatile variables.
They also apply to I/O mapped accesses if the compiler has a built-in
or library method of accessing them. Something like inp8(0x3fe)
would have sequence points at the call of inp8() and before the return.
... ... ...
This is probably not a complete list of appropriate rules even for
one specific platform but it's a start.
I read and enjoyed your example rules for sensible handling of
'volatile' in a conforming implementation. Thank you.
Since you've offered up such great rules, how might you design an
implementation to deal with the code of the original post? If you were
capable of precise scheduling control for the side effects, might you
allow for both writes to be simultaneous, as a speed optimization?
Also, would you consider the entire array object to be a single,
'volatile' object or just each element?
Anything that unconditionally causes UB could cause compilation
error messages,
Agreed.
compiler infinite loops,
Agreed.
compiler abort()s,
Agreed.
compiler
faults (e.g. segmentation or division by zero),
Agreed.
deletion of the
source code,
Agreed but unlikely.
or retroactive deletion of the author of the source
code from conception
I disagree.
(the Standard does not require temporal
causality, nor does it include the laws of physics).
I disagree. I strongly urge you to reconsider this.
If the compiler
can determine the UB to be unconditional, it could try to evaluate
it at compile time.
Agreed. So can it be determined to be unconditional? Your rules do not
appear to allow for that.
This might destabilize the compiler, or simply
involve the compiler picking an order of side effects happening.
Sure.