10 ms·
Why don't compilers have an option to reject UB with an compiler error? Surely the compiler must know when it comes across a piece of source code whose semantic
by wereHamster 9y ago
Why don't compilers have an option to reject UB with an compiler error? Surely the compiler must know when it comes across a piece of source code whose semantic is undefined.
- adrianN 9y agoSome UB depends on the input (out of bounds access is a popular example). Some UB is created during optimization passes, for example by inlining. It's hard to produce useful error messages.
- chrisdew 9y agoSome undefined behaviours relate to runtime values. !iirc! signed 2+2 is safe but signed SIGNED_INT_MAX + 1 is not.
- Peaker 9y agoBut it is easy to give it a defined behavior on a particular architecture. For example, on x86-64, it can be defined to overflow just like unsigned does. The compiler can emit errors about the code if it is compiled to a different architecture where the same defined behavior is too expensive to implement.
- scatters 9y agoHow would that help? The behavior would be defined, but it still wouldn't be what you /want/.
- Peaker 9y agoDefined good behavior > defined bad behavior > undefined behavior. This article is about attacking the last category. Unsigned overflow is far less of a problem than signed overflow because it is defined. It is very cheap to make signed overflow defined. It is more expensive to detect it and fail at runtime.
- petergeoghegan 9y agoIt can be, but it isn't. All contemporary ISAs use two's complement arithmetic for signed integers, including x86-64, and so I think signed overflow could be made well defined on all archs without particularly disadvantaging one arch over the other. The reason that that doesn't happen, here as elsewhere, is that there are a number of specific compiler optimizations that rely on it being undefined. See GCC's -fwrapv flag.
- lomnakkus 9y ago> Surely the compiler must know when it comes across a piece of source code whose semantic is undefined. Absolutely not. Many instances of UB depend on runtime values. (E.g. overflows, out-of-bounds shifts, etc. etc.)
- pjmlp 9y agoBecause of benchmarks fights. UB is the result of C compiler vendors not wanting to give up on their precious optimizations or hardware configurations back when ANSI C was initially being done. They could have done like in other languages and have ANSI C without UB, but then they would need to give up on some hardware features or lose in one or other benchmark.
- dom0 9y agoBecause a lot of UB cannot be efficiently determined at compile time. Hence, UBSan.
- pjmlp 9y agoIt is very easy to be efficiently determined at compile time, other systems programming languages do it, by not having UB on their language standard to start with. If people prefer "performance trumps correctness" and "winning compiler benchmarks is what counts" then they should happily keep getting exploited.
- dom0 9y agoI do see your point, but that'd be a different language. With the standardised C we have, it's not easy.
- pjmlp 9y agoWhich is why we should move away from it. However I don't see it ever happening in the UNIX world, only on OSes whose architecture is not bound to the traditional UNIX/C culture. Which is why see as positive that Apple, Google and Microsoft are pushing C and C++ down into the very lowest layers of their OS stacks, mostly visible on Google's case. Even straight pure Assembly programming has less UB surprises.
- gpderetta 9y ago> It is very easy to be efficiently determined at compile time for many cases it is in fact equivalent to the halting problem. What you can do is check at runtime, which has performance implications (from trivial to expensive depending on the check).
- pjmlp 9y agoNot if the language standard doesn't allow for UB to begin with, then there isn't any halting problem to worry about. This is what the alternatives in the memory safe systems programming language since Algol days do. Surely they still allow for developers to disable some of those checks explicitly, but then it is the programmers fault to choose "performance trumps correctness" instead of "correctness trumps performance".
- fazkan 9y agoThey are called undefined behaviors because the compiler developers are free to do whatever they want when they come across a code that instigates an undefined behavior. Mostly new compilers now a days do handle such behavior with a compiler error or warning. So if you try to do an out of bound shift a new compiler might throw a warning, but according to the C standard it does not have to. In fact instead of throwing a warning it can wipe your complete hardware and that too is allowed according to the C standard... In contrast there is implementation defined behavior, and unspecified behavior. Where the compiler developers have to document and take care of certain things. I wrote a detailed article about the subtle difference you can check that out.... https://medium.com/@faizan10114/understanding-c-terminologies-a-dummies-perspective-695af2648289 https://medium.com/@faizan10114/understanding-c-terminologie...
- dom0 9y ago> So if you try to do an out of bound shift a new compiler might throw a warning, but according to the C standard it does not have to. Right, for doing so needs to determine that this is happening, which neither GCC and Clang can do for fairly trivial cases like for (i = 0; i < 100; i ++) { ... x << i ... }
- fazkan 9y agoYes they don't, I assumed they would, but they dont. I just wanted to emphasize the fact that they don't have to. Perhaps an unbound shift was a wrong example.. thanks for catching that...
- masklinn 9y agoBecause compiler don't generally look for UB then gleefully cackle as they fuck up your code. Rather they are written under the assumption that there are no UB in the program, and the chips fall where they may as these assumptions are used and propagated.
- maxlybbert 9y agoThe argument I've seen is "macros often expand into unintuitive code, especially when multiple macros are involved, so these kinds of warnings would be confusing if there were such macros." It might be possible to do the same thing if you didn't check any code generated from macros.
- jcelerier 9y agoThe compiler would reject the following function : int res(int a, int b) { return a + b; } since it can be UB, for instance if a + b > INT_MAX.
- wamaral 9y agoThen by all means reject it, way better than blowing up in production.
- sullyj3 9y agoI can't tell if this is satirical, but to be clear, you're proposing to disallow addition?
- wamaral 9y agoRather define the behaviour of what happens when addition of two ints exceed the maximum allowed value.
- rspeer 9y agoYou're changing your plan here. Maybe you could at least concede that the issue of UB is difficult and is not going to be solved by saying "why don't they just" on HN?
- sp332 9y agoYou could test whether overflow is going to happen first. Or use a math library that explicitly overflows with predictable results.
- dullgiulio 9y agoI know very few languages that do that. Even Rust doesn't when compiled for release. All for performance reasons.
- 9y ago