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.
signature.asc
Description: OpenPGP digital signature
_______________________________________________ Rust-dev mailing list [email protected] https://mail.mozilla.org/listinfo/rust-dev
