Live data from Hacker News

Falsehoods programmers believe about undefined behavior

predr.ag

211–220 of 233 posts

Re: Falsehoods programmers believe about undefined behavior

#212

Earlier quoted context omitted.

He's not trying to define all behaviours, he wants a static assertion that the compiler will compile certain behaviours in a specific way. Seems very different.

That's why I said "falls under the hat of" :) I promise the talk is relevant. `-fwrapv` is an attempt toward doing such a thing for signed integer overflow specifically. I bet it's a pain to maintain because it significantly increases the surface area of the compiler that needs to be tested. I don't believe doing this for more cases is feasible at the level of the compiler. Things like CHERI where the hardware and OS…

Signed integer overflow flag is relatively painless to maintain, because compiler needs to be able to track it for unsigned integers anyway. That is, signed integer was modeled with a pair of flags (interpret bits as signed, can't overflow) and unsigned integer (interpret bits as unsigned, wrap on overflow), so signed integer under -fwrapv is just (interpret bits as signed, wrap on overflow).

Re: Falsehoods programmers believe about undefined behavior

#213

Earlier quoted context omitted.

That's from the spec POV. On x86 for example, signed int overflow is well defined.

Both "UB" and "IB" are terms defined in the language standards, and it's obvious that's what we're talking about here. x86 defines what specific instructions do, nothing so crass as "signed integer overflow" - that's part of why the language standard has to define it as such.

Yeah, except that they effectively aren't part of the language standard. Possible behavior is to ignore UB, behave in a documented manner, or issue a compiler error. Not remove other code or any other nonsense optimizing compilers do.

I'm not gonna wage a holy war against optimizing compilers, but let's not pretend that this is a language standard issue.

Re: Falsehoods programmers believe about undefined behavior

#214

Earlier quoted context omitted.

While theoretically true, there are a whole bunch of undefined behaviors that are not caught in constexpr context in practice. In particular unsequenced operations on the same object are typically not caught. It's still super useful though, and more common language UBs are caught.

That's a compiler bug though, right? Doesn't the standard disallow UB in constexpr?

It is. Or if it is unfeasible to implement then it can be considered to be a defect in the standard.

Consider that something like (++*ptr1) + (++*ptr2) may or may not be UB, depending where the pointer points to. The required additional bookkeeping might make compile time evaluation unpractically slow.

Alternatively the sequencing could be always defined between any two subexpressions within constexpr context.

Re: Falsehoods programmers believe about undefined behavior

#215
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…

Everyone's getting confused about the word "reach". If the execution of a program triggers UB at any point then its behavior is undefined at all points, even before the UB was triggered. But the compiler does have to respect and preserve the behavior of any completely UB-free execution of your program, even if other executions (say with different input) could trigger UB. So we could say, unreachable UB is fine, but i…

Point 14 explicitly says UB in dead code.

The implicit assumption is no UB in live code, otherwise the UB in dead code is a red herring.

So the UB is never reached, by the definition of dead code.

Unreachable UB is fine.

If this wasn't the intended message of point 14, then it's phrased wrong.

Re: Falsehoods programmers believe about undefined behavior

#216
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)…

Btw this is a nice example how this stuff is subtle.

My first answer was going to be that the transformation is not allowed, but then I thought about it more.

I couldn't get gcc to break it either though. Either it misses the optimization or tries to be conservative with reordering UB with side effects.

Re: Falsehoods programmers believe about undefined behavior

#217

Earlier quoted context omitted.

The compiler is required to successfully translate a program that contains unreachable UB, and such a program is conforming if undefined behavior is never reached. This is explicitly spelled out in https://www.open-std.org/jtc1/sc22/wg14/docs/rr/dr_109.html > Furthermore, if every possible execution of a given program would result in undefined behavior, the given program is not strictly conforming. A conforming imple…

Sad to see this downvoted here. To hammer it in, the consider the following: if(p) *p = 0; *p is undefined if p is NULL. But as long as that statement is never reached when p is NULL that is not a problem. That is trivially true in this case, but the same applies even for more complex relations between the conditional and the undefinedness of the statement. Or put another way, in int * p = 0; if(fermats_last_theorem_…

Yes, my example with Fermat's last theorem was completely wrong, not sure why it seemed plausible to me when I wrote it, it's obviously just a guard, and it obviously is not UB to have a pointer write behind an If...

Re: Falsehoods programmers believe about undefined behavior

#218

Earlier quoted context omitted.

The compiler isn't required to know if code is unreachable. For example, `if (fermats_last_theorem_is_wrong()) {*p == 1} ` will imply to a modern compiler that p is not NULL in the scope where it is defined, even if we now know that this code will never be reached (assuming that function is correctly written). Or, here is a better example: for(int i=0; i > n;) { } *p=1; Here the compiler will assume that the loop ter…

> For example, `if (fermats_last_theorem_is_wrong()) {*p == 1} ` will imply to a modern compiler that p is not NULL in the scope where it is defined, even if we now know that this code will never be reached (assuming that function is correctly written). No it won't. Otherwise all precondition checks would be meaningless. The compiler can only assume that it isn't null when fermats_last_theorem_is_wrong() is true. If…

Yes, the part about Fermat's last theorem I wrote is actually obviously wrong, I don't know what exactly I was thinking when I wrote it.

The example with the loop though is more to point out that our native understanding of "unreachable" may be different than the compiler's. The same problem could occur without invoking UB if we un-conditionally generate a SIGKILL to our own process and then dereference a NULL pointer - the compiler doesn't know what SIGKILL does, so it doesn't know the UB is unreachable in practice, so it may reorder etc.

Re: Falsehoods programmers believe about undefined behavior

#219

Earlier quoted context omitted.

The compiler isn't required to know if code is unreachable. For example, `if (fermats_last_theorem_is_wrong()) {*p == 1} ` will imply to a modern compiler that p is not NULL in the scope where it is defined, even if we now know that this code will never be reached (assuming that function is correctly written). Or, here is a better example: for(int i=0; i > n;) { } *p=1; Here the compiler will assume that the loop ter…

Presumably (and really don't take my word for this - I'm a Java bod) the p dereference needs to come before the p NULL check for the p NULL check to be ignored. (Otherwise, how can a NULL check protect a dereference.) And therefore, the NULL check would be unreachable as well, and therefore never executed and not relevant. Please correct my misunderstanding...

There's no misunderstanding on your part, just some muddled thinking on my part for the first example.

Re: Falsehoods programmers believe about undefined behavior

#220

Earlier quoted context omitted.

> It is true that when foo() is called with a NULL argument, the UB will be realized before bar() has had a chance to run. But the line with UB has already been proven reachable prior to the actual dereference, so the UB has in a sense already been invoked. The point of those numbered lines was to refer to how people normally think about UB. Someone seeing that function may be tempted to say that it's not UB to call…

> Someone seeing that function may be tempted to say that it's not UB to call with a NULL value as long as the dereference doesn't happen (say, bar() could call exit() or it could run an infinite loop). An infinite loop is UB for precisely this reason: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1528.htm So an infinite loop cannot prevent the invocation of UB, because an infinite loop is itself UB. I would expe…

The fact that bar could exit or not return doesn't matter (although it was indeed my initial line of reasoning) as if the value is null bar won't be called and UB happens; hence the value can't be null and bar can be called unconditionally. The value is still dereferenced only after calling bar.

Still gcc doesn't optimize the check away so it is possible there is some other line of reasoning that prevents the transformation.

Post reply on HN