Live data from Hacker News

Undefined behavior in C is a reading error

yodaiken.com

501–503 of 503 posts

Re: Undefined behavior in C is a reading error

#501

Earlier quoted context omitted.

> For example, please explain how to prevent this without also (inadvertently) preventing the removal of unnecessary null checks when functions are inlined. What about an unnecessary null check that's hidden inside a macro? If a null check is unnecessary, that's because it is reachable only from the not-null side of some previous null check. The compiler can track that information just fine, using the same tools it u…

This is still too simplistic. Think along the lines of: void foo(T *p) { if (!p) abort(); bar(p); } void bar(T *p) { use(*p); MACRO(p); } In other words, the first null-check and the latter check are in different functions that may not even be in the same compilation unit, or where the call sequence is hard to reason about due to function pointers etc.

Assuming bar isn't inlined (eg separate compilation unit, as you mentioned), the null check in MACRO is not unnecessary, because bar can be called from places other than foo, and those places might pass null pointer.

This is one of several situations where it might be useful for the compiler to excercise it's perogative to implement operations without regard for undefined behaviour to rewrite `use(*p);` as `if(!p) abort(); use(*p);`, which would make the null check in MACRO unnecessary, but unless it does so, the check is not unnecessary, just insufficient.

Re: Undefined behavior in C is a reading error

#502

Earlier quoted context omitted.

What would the misuse-resistant way be? Would you want the multiplication operator (*) to return 2 values: the number and a bool telling you whether it overflowed? Yeah, a standard library function that does this would be good. But many people would just use * instead of this function, and so the problem would partially remain.

mul_would_overflow(), mul_wrap(), mul_saturate(), mul_do_whatever_the_hardware_does_just_please_dont_break_my_code_by_assuming_ub. Can't help people who just use * and don't care about overflow without breaking compatibility, but at least the people who do care won't have to reinvent safe arithmetic.

Yeah, I agree with you. But it would be hard due to there being so many integer types. A binary operation needs to consider 3 types: the types of each argument and the type of the result.

In c++ it would be a little simpler due to templates, so the types of the arguments to the function can be derived. But the type of the result can still confuse programmers. Although maybe it's not so bad, because an overflow that happens during multiplication (or other math operations) is undefined-behavior, but an overflow that happens during assignment of the multiplication result to a variable that can't hold it is only implementation-defined behavior, not undefined behavior.

Re: Undefined behavior in C is a reading error

#503

Earlier quoted context omitted.

> Deferring to the hardware is what undefined behaviour is If that were the case, the Standard would say so. The entire reason people argue over this in the first place is because the Standard's definition of undefined behavior allows for multiple interpretations. In any case, you're still missing the point. It doesn't matter how good or bad the documentation of implementation-defined behavior may or may not be; the…

> the important part is that compilers cannot optimize under the assumption that control flow paths containing [undefined] behavior are never reached. Yes. That. Exactly that. Compilers cannot assume that, because (in the general case) it is not true .

> Yes. That. Exactly that.

When I said implementation-defined, I meant implementation-defined. This is because the applicability of UB-based optimization to implementation-defined behavior - namely, the lack thereof - is wholly uncontroversial. Thus, the diversion into the quality of documentation-defined behavior is not directly relevant here; the mere act of changing something from undefined behavior to implementation-defined behavior neatly renders irrelevant any argument about whether any particular UB-based optimization is valid.

> Compilers cannot assume that, because (in the general case) it is not true.

This is not necessarily true. For example, consider the semantics of the restrict keyword. The guarantees promised by a restrict-qualified pointer aren't true in the general case, but preventing optimizations because of that rather defeats the entire purpose of restricting a pointer in the first place.

More generally, the entire discussion about UB-based optimizations exists precisely because the Standard permits a reading such that compilers can make optimizations that don't hold true in the general case, precisely because the Standard imposes no requirements on programs that violate those assumptions.

Post reply on HN