6 ms·
> The C standard actually does have text giving a list of acceptable behaviours for a compiler The exact opposite is explicitly stated in the standard (from C1
by moefh 3y ago
> The C standard actually does have text giving a list of acceptable behaviours for a compiler
The exact opposite is explicitly stated in the standard (from C11 section 3.4.3):
undefined behavior
behavior, upon use of a nonportable or erroneous program
construct or of erroneous data, for which this International
Standard imposes no requirements
The standard then lists some examples of undefined behavior, and it's true that "silently removing the code" is not in the list. Still, I think it's pretty clear that it's acceptable behavior, since the standard just stated it imposes no requirements.
- User23 3y agoI think you’re misinterpreting. Maybe it’s clearer if we elide the relative subclause: undefined behavior behavior … for which this International Standard imposes no requirements That is an obvious definition of what is undefined behavior. It’s not giving license to do whatever. That said the ship has sailed and what implementors do obviously matters more than what the standard says.
- iso8859-1 3y agoIf there are no requirements on what it's doing, how is that not a license to do whatever? There is not even a requirement that a theoretical program that contains e.g. only preceding code, would still maintain any invariants. So I don't see what an instance of "whatever" that violates "no requirements" would look like.
- User23 3y agoRead with an either an atypical or malicious degree of literalism the standard supports ransomwaring your machine and indeed your entire network in the face of undefined behavior. What it actually means is the standard doesn’t require doing the impossible, not that a program error is license to generate malicious code. That’s why it gives a (now formerly) normative list of possible approaches.
- nlewycky 3y agoNo maliciousness is required. "1 + 2" produces "3" only because the standard defines it so -- it is therefore required that 1 + 2 produce 3. When the standard does not say what to do, there is no requirement. You said "it's not a license to do whatever" which is only true because it isn't a license. The code can still do whatever, because there is no requirement that it do anything other than "whatever". You seem to be arguing from a position that the code has meaning unless somehow UB gets involved, but the reality is that these bits and bytes don't mean anything until the spec tells us what meaning it has.
- mpweiher 3y ago> until the spec tells us what meaning it has. That's a commonly held belief, but wrong. My first C compiler predated the ANSI C standard. Yet the code very much had meaning, just not in terms of the C standard. The C standard defines the minimum a C compiler has to do to be in compliance with the C standard. It is not a complete definition, and this is intentional.
- nlewycky 3y ago> > until the spec tells us what meaning it has. > That's a commonly held belief, but wrong. My first C compiler predated the ANSI C standard. Yet the code very much had meaning, just not in terms of the C standard. Much like /* plain English comments */ have meaning, just not in terms of the C standard. Talking about prestandard C isn't relevant because we're talking about what a standards conforming compiler can do with undefined behaviour per the text of the standard. I do not find your counterexample compelling. > The C standard defines the minimum a C compiler has to do to be in compliance with the C standard. The standard does not define what a compiler has to do. Instead, it defines the C abstract machine as well as syntax attached to behaviour in terms of that abstract machine. When a program which is restricted to that syntax is executed, it must have the effects (upon the C abstract machine*) as specified in the standard. Sometimes the standard doesn't specify anything for a given syntax in a given abstract machine state, and sometimes the standard explicitly specifies that "the behavior is undefined". > It is not a complete definition, and this is intentional. I'm not sure what you mean. Compiler extensions exist (and are generally regarded as a necessary evil), is that what you're referring to? Or are you referring to implementation-defined things (all of which are explicitly called out in the standard)? Or something else? A citation, if you can? * Notably the C language says nothing about the real machine. Supposing you have an I/O port at address 0x9000, accessing "*((char*)0x9000) = 1;" does not need to access memory address 0x9000 at all. Of course, compiler engineers are not silly and implemented it as accessing 0x9000 on the underlying machine. Similarly the standard committee is careful to ensure that the language it defines can be implemented without needed the overhead of a VM or interpreter, though sometimes they get that wrong. (For example, see the issues combining multiple alloca() and C99's "int foo[*];" in the same function.)*
- mpweiher 3y ago"Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message)." Note "permissible" and "ranges from ... to". Again, this used to be normative in the original ANSI standard. It was changed in later versions to no longer be normative. Exactly as I wrote.
- mike_hock 3y agoWhich is logically equivalent to imposing no requirements. "ignoring the situation completely with unpredictable results" does not meaningfully constrain the possible behaviors.
- mpweiher 3y agoThat turns out not to be the case. “Ignoring” is not “taking action based on” Ignoring is, for example, ignoring the fact that an array access is out of bounds and performing the array access. Ignoring is not noticing that there is undefined behavior and removing the access and the entire loop that contains the access. Or a safety check.
- mike_hock 3y agoSo, generally, optimizations shouldn't be allowed? Dead code removal shouldn't be allowed? Substituting constants shouldn't be allowed? Because "ignoring" UB is pretty much what the compiler did, and then let its optimization passes run to their conclusion. > ignoring the fact that an array access is out of bounds and performing the array access The idea that this is even meaningful is precisely the "portable assembler" misconception.
- mpweiher 3y agoExcept the misconception is that it is not. "Committee did not want to force programmers into writing portably, to preclude the use of C as a “high-level assembler:” https://www.open-std.org/JTC1/SC22/WG14/www/docs/n897.pdf https://www.open-std.org/JTC1/SC22/WG14/www/docs/n897.pdf p10, line 39 "C code can be portable. " line 30