[rust-dev] Integer overflow, round -2147483648

Daniel Micay danielmicay at gmail.com
Sun Jun 22 14:05:03 PDT 2014


On 22/06/14 09:31 AM, Gábor Lehel wrote:
> 
> The prospect of future architectures with cheaper (free) overflow
> checking isn't my primary motivation, though if we also end up better
> prepared for them as a side effect, that's icing on the cake.

It's never going to be free or even cheap. Replacing very common pure
operations with impure ones in most code will destroy performance. Rust
relies heavily on compiler optimizations to even come close to C level
performance. Anyway, no one has offered an explanation of how they're
planning on integrating this into LLVM and how they propose turning a
trapping operation into unwinding across various platforms.

> My primary motivation is that, outside of a couple of specialized cases
> like hashing and checksums, wraparound semantics on overflow is
> **wrong**. It may be well-defined behavior, and it may be fast, but it's
> **wrong**. What's the value of a well-defined, performant semantics
> which does the wrong thing?

Few functions check all of the necessary preconditions. For example, a
binary search implementation doesn't check to see if the array is
sorted.  It's not incorrect to require a precondition from the caller
and overflow is only one of countless cases of this.

Choosing to enforce invariants in the type system or checking them at
runtime is always a compromise, and Rust eschews runtime checks not
strictly required for memory safety.

In some cases, the type system has been leveraged to enforce invariants
at compile-time (Ord vs. PartialOrd) but even though that's quite easy
to sidestep, it's not without drawbacks.

> I also agree that performance is non-negotiable in this case, however.
> The only good thing about always wrong is that it's not that hard to do
> better.
> 
> Given the circumstances, I think the least bad outcome we could achieve,
> and which we *should* aim to achieve, would be this:
> 
>  * Where performance is known to not be a requirement, Rust code in the
> wild uses either overflow-checked arithmetic or unbounded integer types,
> with the choice between them depending on ergonomic and semantic
> considerations.
> 
>  * When the performance requirement can't be ruled out, Rust code in the
> wild uses arithmetic for which overflow checking can be turned on or off
> with a compiler flag. For testing and debugging, it is turned on. For
> production and benchmarks, it is turned off.

The Rust developers have been consistently opposed to introducing
dialects of the language via compiler switches. I brought up the issue
of macros and syntax extensions but you've chosen to ignore that.

>  * For code where wraparound semantics is desired, the appropriate
> facilities are also available.
> 
> Given the discussion so far, the design I'd be leaning toward to
> accomplish the above might be something like this:
> 
>  * Two sets of fixed-sized integer types are available in the `prelude`.
> 
>  * `u8`..`u64`, `i8`..`i64`, `int`, and `uint` have unspecified results
> on overflow (**not** undefined behavior). A compiler flag turns overflow
> checks on or off. Essentially, the checks are `debug_assert`s, though
> whether they should be controlled by the same flag is open to debate.
>
>  * `uc8`..`uc64`, `ic8`..`ic64`, `intc`, and `uintc` are *always*
> checked for overflow, regardless of flags. (Names are of course open to
> bikeshedding.)
> 
>  * Given that these are not really different semantically, automatic
> coercions between corresponding types can be considered. (Even then, for
> `a + b` where `a: int` and `b: intc`, explicit disambiguation would
> presumably still be required.)
> 
>  * Unbounded integer types using owned memory allocation are available
> in the `prelude`. I might prefer to call them `Integer` and `Natural`
> instead of `BigInt` and `BigUint`.
> 
>  * Types and/or operations which wrap around on overflow are available
> in the standard library. Given how specialized the use cases for these
> seem to be, perhaps they could even go directly in the `hash` module.
> It's not clear to me yet whether a separate set of types (`uw8`..`uw64`,
> `iw8`..`iw64`) or just a separate set of operations on the `prelude`
> types (e.g. `trait WrappingAdd`) would be preferable.

A `Vec<u32>` and `Vec<uw32>` would be entirely distinct types. That
alone is going to cause performance issues and will make the language
more painful to use. It's already pushing the boundaries of what people
will be willing to accept with features like strictly checked move
semantics and reference lifetimes.

>  * Unbounded integer types which use garbage collected allocation are
> available in the `gc` module.

It doesn't sense to have 3 separate implementations of big integers for
reference counting, atomic reference counting and task-local tracing
garbage collection.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: OpenPGP digital signature
URL: <http://mail.mozilla.org/pipermail/rust-dev/attachments/20140622/b1f6892f/attachment.sig>


More information about the Rust-dev mailing list