4 ms·
> That's how you specify "exactly what must happen on integer overflow". So yes, like I said, you can force a particular behavior, and throw out any system for
by clarry 7y ago
> That's how you specify "exactly what must happen on integer overflow".
So yes, like I said, you can force a particular behavior, and throw out any system for which that doesn't come naturally - you just made the language more complex for them. And no, that is not simpler than "the behavior is undefined", just different (and more imposing).
> And voilà, we no longer care how a pointer was constructed then dereferenced, we only care about the address, and whether it pointed to an allocated region (and initialised with a compatible type etc, though that part could also be simplified).
Eh. You did nothing about undefined behavior. It's still there.
> A very specific scenario that may be, but it's a scenario we do encounter in real life.
So what was your proposal that doesn't make the language more complex? Because above, you just gave us undefined behavior, which is our starting square.
- loup-vaillant 7y ago> So yes, like I said, you can force a particular behavior, and throw out any system for which that doesn't come naturally Find me one system in current use (2020) that isn't 2's complement. > you just made the language more complex for them. Ah, but I was never talking about implementation complexity. I was talking about specification complexity. Imposing 2's complement makes the specification simpler. No special case, no boundary. Just a binary field. Undefined behaviour draws such a boundary. An extra detail to specify and remember. Some thing that reminds you you're not quite doing modular arithmetic. > Eh. You did nothing about undefined behavior. It's still there. I did. There's less of it. I've filled the bug, here's what's this about: https://github.com/jedisct1/libsodium/issues/937 https://github.com/jedisct1/libsodium/issues/937 Here's the relevant quote from annex J of the (heavy, I have printed it) C99 specifications: Addition or subtraction of a pointer into, or just beyond, an array object and an integer type produces a result that does not point into, or just beyond, the same array object. (6.5.6) That's what caused the bug: we were taking a pointer from an array, then used it to overflow past it. It didn't matter that we could access the memory there. What mattered is that we locally overflew that array. Had we computed the pointer by first working with the outer array, then pointed to the right element, we'd have the same pointer, except this time it wouldn't be undefined. Bonus note: you don't even have to dereference the pointer, computing its value is enough to trigger undefined behaviour. Here is some more undefined behaviour I have removed (it's subtle, relatively few programmers know this): Pointers that do not point into, or just beyond, the same array object are subtracted. (6.5.6) There go flat memory models. Though to be honest, even segmented memory models could define that. Just make the subtraction already, it doesn't have to mean anything. Make it unspecified or implementation defined at least. Pointers that do not point to the same aggregate or union (nor just beyond the same array object) are compared using relational operators (6.5.8) That's the reason we can't implement memmove() portably and efficiently. And depending which tool you ask, even converting the pointers to intptr_t first isn't enough. Real world example here: https://github.com/jedisct1/libsodium/commit/e7e378fad116c18c78274e9b268acbd8a653268b https://github.com/jedisct1/libsodium/commit/e7e378fad116c18... --- If you haven't already, go read the annex J from the C standard. Ponder the fact that compilers, unless you specifically ask not to (-fwrapv), will exploit every single one, even on platform that could define some reasonable behaviour (2's complement machines). It may hint at how hostile C is to programmers who care about correctness. Believe me, I know. I wrote a whole Crypto library[1], and that's one of the easiest things to get right, where C is concerned. I mean, the crypto itself is hard, but the total lack of dependency makes it pathologically easy to write in a portable way. Yet C still makes it hard. [1] https://monocypher.org https://monocypher.org
- beefhash 7y ago> Find me one system in current use (2020) that isn't 2's complement. Fortunately, C2x will stop with the meme assumption that two's complement hasn't taken over the world (see draft N2479). It will not, however, specify signed integer overflow, which remains UB. :^)
- loup-vaillant 7y agoCompiler writers are too hooked on loop optimisations now to give up on undefined signed overflow. I'm afraid the only clean fix is to augment the language with an actual `for` loop, with an immutable loop index and well defined start and finish values. Stuff like: for (size_t i : 0..10) {} // Will go from 0 to 9 included. for (size_t i : 10..0, -1) {} // Will go from 10 to 1 included... // ...or maybe from 9 to 0 included? // Not sure which is best.