Apologies if this has been suggested, but would it be possible to have a compiler switch that can add runtime checks and abort on overflow/underflow/carry for debugging purposes, but the default behavior is no check? IMO this would be the best of both worlds, because I would assume that one would really only care about checked math during testing and dev.
On Sun, Jun 22, 2014 at 5:05 PM, Daniel Micay <[email protected]> wrote: > 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. > > > _______________________________________________ > Rust-dev mailing list > [email protected] > https://mail.mozilla.org/listinfo/rust-dev > > -- "Historically, the most terrible things - war, genocide, and slavery - have resulted not from disobedience, but from obedience" -- Howard Zinn
_______________________________________________ Rust-dev mailing list [email protected] https://mail.mozilla.org/listinfo/rust-dev
