Live data from Hacker News

Falsehoods programmers believe about undefined behavior

predr.ag

51–60 of 233 posts

Re: Falsehoods programmers believe about undefined behavior

#51
post #22

13. But if the line with UB isn't executed, then the program will work normally as if the UB wasn't there. 14. Okay, but if the line with UB is unreachable (dead) code, then it's as if the UB wasn't there. 15. If the line with UB is unreachable code, then the program won't crash because of the UB. 16. If the line with UB is unreachable code, then the program will at least stop running somehow and at some point. This…

You have to understand that in the modern understanding of UB as applied by compiler writers, the compiler has every right to assume the program never triggers UB. This means that lines which would be UB if a variable had certain values can be used as proof that the variable doesn't have those values, thereby eliminating checks.

For example, if a program looks like this:

  void foo(int* const p) {
    if (p!=NULL) {
      bar(p);
    }
    *p = 1; //this line would be UB if p could be NULL
    //so we are free to assume p is not NULL in this scope
  }
It will very likely be optimized to this:

  void foo(int* const p) {
    bar(p);
    *p = 1;
  }
And even if bar() itself had a line checking if p was not NULL, that will get ellided as well if bar() gets inlined.

This is a good example of how UB on a later line can have an impact on earlier lines, or even in other places in the code.

Note that it's not executions which trigger UB, it is source code programs that do.

Edit: fixed const pointer syntax.

Re: Falsehoods programmers believe about undefined behavior

#52
post #42

Earlier quoted context omitted.

No, no, no. UB is a global property of your program. "All bets are off" even at compile time. "Anything at all can happen; the Standard imposes no requirements. The program may fail to compile, or it may execute incorrectly (either crashing or silently generating incorrect results), or it may fortuitously do exactly what the programmer intended." https://blog.regehr.org/archives/213

UB is typically a property of the execution of a program. A program may bump into UB for one set of inputs, but not for an other set.

UB is a property of the source code, or more specifically a property of how source code should be compiled into machine code. It means that there is no constraint on the machine code which will be produced if your source code has certain properties.

Re: Falsehoods programmers believe about undefined behavior

#53
post #44
post #6

The biggest problem with UB, as interpreted by C/C++ compilers is the unfounded belief that the spec says that you can assume any path that leads to UB is defacto incorrect and can be ignored. That's the bogus logic required for steps 13-16 to be true. That interpretation of the specification language is clearly bogus as that means more or less all C and C++ programs can be compiled to a single return instruction - w…

This is an absurd reduction of the current situation. > That interpretation of the specification language is clearly bogus as that means more or less all C and C++ programs can be compiled to a single return instruction - when compiler devs introduced this new new interpretation of UB, it turned out they had to then go through and add a bunch of exceptions for basics like memset, memcpy, and memmove could work withou…

Read the gcc mailing list. There is alot of user hostile "fix your broken program" from language lawyers.

I think aliasing rules messes up memcpy if you are dogmatic?

Re: Falsehoods programmers believe about undefined behavior

#54
post #32
post #22

13. But if the line with UB isn't executed, then the program will work normally as if the UB wasn't there. 14. Okay, but if the line with UB is unreachable (dead) code, then it's as if the UB wasn't there. 15. If the line with UB is unreachable code, then the program won't crash because of the UB. 16. If the line with UB is unreachable code, then the program will at least stop running somehow and at some point. This…

> This is nonsense. The line with UB has to be reached for the execution to be undefined. Check the footnote at item 14, it links to a page which shows one example of how a line with UB can be reached (basically: the optimizer can reorder the code so that parts of the line with UB will run much earlier than you'd expect).

I checked the footnote. It describes a case where invoking UB (transmuting "3" to a bool) can cause dead code to become live. But transmuting "3" to a bool is itself UB. So this fails to prove that execution becomes undefined without reaching a line containing UB.

Re: Falsehoods programmers believe about undefined behavior

#55
post #22

13. But if the line with UB isn't executed, then the program will work normally as if the UB wasn't there. 14. Okay, but if the line with UB is unreachable (dead) code, then it's as if the UB wasn't there. 15. If the line with UB is unreachable code, then the program won't crash because of the UB. 16. If the line with UB is unreachable code, then the program will at least stop running somehow and at some point. This…

Now look at a large code base, with lots of checks for inputs. With O0 it's usually straight-forward to read, but with heavy optimizations applied it can become a real nightmare. And in that scenario all it takes is some reordering of the UB before the DCE pass can prove the code to be unreachable (which might not happen on the first DCE pass, because the value analysis/constant propagation didn't work its magic, yet). Also static program analyses can be both forward and backward.

I'm afraid it's not nonsense, that's why it's a common misunderstanding ;)

Re: Falsehoods programmers believe about undefined behavior

#57

> "If my program contains UB, and the compiler produced a binary that does X, is that a compiler bug?" It's not a compiler bug. True, if the language spec is the only thing in the universe you care about. But there are better and worse things compilers can do in the presence of UB, and the big one is warning you about it when it possibly can . There's another big discussion on the front page about signed integer over…

> But there are better and worse things compilers can do in the presence of UB, and the big one is warning you about it when it possibly can. The problem is that this is really hard to do without producing either too much or too little warnings, as undefined behaviour is usually input-triggered; and the compiler usually doesn't know all constraints on the arguments that the programmer knows about. For example, the fu…

> This is a lose-lose situation for the compiler: it can warn, but people will find the unnecessary warnings annoying and turn them off, while if it doesn't warn people will complain about perceivedly bad optimizations.

This could still be greatly alleaviated with the use of asserts: A compiler could default to the most conservative set of assumptions for an input value - e.g. that a parameter of a global function assumes the full value range of its type - unless the programmer provides more assumptions through an assert statement.

In debug builds, you could compile those asserts to actual runtime checks, so the program can be appropriately tested. Then, in production builds, the asserts would simply be skipped.

The second improvement would be warnings about "nonensical" statements (from the compiler's POV) that will be optimised away. E.g., many of the most egregious examples of UB are from code where the programmer inserted a bounds check to protect against UB at another location - and then the compiler optimized away that same bounds check as a "nasal demon" action, caused by the UB it was supposed to protect against. In those cases, it would be useful to warn "hey, this bounds check can never fail because that other statement implies that either we're in bounds or there is UB. You might want to check for UB here."

Re: Falsehoods programmers believe about undefined behavior

#58
And if each of those outcomes had a percentage associated with them, I suspect that most undefined behavior would cluster around less than 5 outcomes matching the expectations of even the most junior C programmer.

The bunch of amateur bards trying to mythicize undefined behavior really need to find something better to do with their and our time…

Re: Falsehoods programmers believe about undefined behavior

#59

> "If my program contains UB, and the compiler produced a binary that does X, is that a compiler bug?" It's not a compiler bug. True, if the language spec is the only thing in the universe you care about. But there are better and worse things compilers can do in the presence of UB, and the big one is warning you about it when it possibly can . There's another big discussion on the front page about signed integer over…

> But there are better and worse things compilers can do in the presence of UB, and the big one is warning you about it when it possibly can. The problem is that this is really hard to do without producing either too much or too little warnings, as undefined behaviour is usually input-triggered; and the compiler usually doesn't know all constraints on the arguments that the programmer knows about. For example, the fu…

We're re-litigating that other thread... but the function in the other thread had a conditional (trying to check for overflow after the fact) which would never be true, regardless of x. And it could statically prove this.

And that proof, the compiler then decided to use to remove the code, ignoring the red flag that the programmer undoubtedly had tried (and failed!) to achieve something with it. I would have preferred a warning here. I don't turn off "condition is always true"-type checks.

Re: Falsehoods programmers believe about undefined behavior

#60
post #49

I am always amazed by articles like these. This guy thoroughly covers this topic. I cannot say I have ever written anything that ran into or caused undefined behavior. And I have programmed for a long time. Is this just an interesting topic or do people actively push the compiler to see what it will do? Maybe I am a boring/simple programmer. :)

> I cannot say I have ever written anything that ran into or caused undefined behavior. And I have programmed for a long time. Assuming you're working with C or C++, you've never: - Dereferenced a pointer? - written a loop with a signed index? - Accessed an index in an array or a vector? - Used a variable? - Used an optimising compiler? Compilers use undefined behaviour in all of the above scenarios as much as possib…

Compilers use reasoning based on UB to optimize and simplify code. But that's not a complete argument why it's highly likely that a given code exhibits UB at run time.
Post reply on HN