On Casts and Checked-Overflow

I initially leaned towards the strict interpretation as well, but after the discussion at the meeting, I am starting to feel conflicted. The major points made there were:

  1. Bitcasting is far more common in practice (unsubstantiated, but see below), so this could be annoying.
  2. Subtle rules may be more confusing than helpful. Probably as should either be infallible (as today) or strict.

I tried to do a brief survey of rustc to uncover the kinds of places we use as. This is not a super thorough survey and in particular I didn’t count instances (and we’d want to look at more than rustc, ideally). But in any case here is a listing of what I saw. These are reasons to use as, somewhat sorted by frequency of occurrence (but like I said, I didn’t count).

  1. To convert between usize and u32, usually for indices whose length is believed to be bounded to u32. In this case, I think that the “width oriented” approach is probably best. That is, you’d prefer a panic if converting from usize to u32 throws away some high-order bits when debugging.
  2. To convert from u32 to i32, for example in trans when converting offsets for use with a GEP statement. Here, strict is preferred.
  3. To upcast, in which case any interpretation is fine.
  4. Constant evaluation sometimes goes back and forth between i64/u64. I think in this case strict is preferred.
  5. To convert from i32 to u32 in binary encoding. Here, width, loose, or truncating is preferred.
  6. To select individual bytes ((x >> 16) as u8). Truncating is preferred.

One thing I did not see was a converting from i32 to u32 where we wanted to ensure that the value was not negative at that time. This probably does come up, but I don’t think I saw a case of that.

Anyway, here are some further concerns:

  1. If we keep as as it is, which I think is reasonable, then it does raise the question of whether we will offer stricter versions. If so, will they be tied to methods? It is perhaps inconsistent to say that + is strict by default but as uses a method. But there is clearly utility for at least some kind of checking to ensure we don’t throw away bits.
  2. If we convert as, it may be annoying.

This is connected to an existing mildly open question on how to make it more ergonomic to use wrapping operations. I am personally leaning towards something like C#'s checked blocks. Basically allowing you to say that you want wrapping semantics within a particular fn or block (but not module, I’d prefer to keep the annotation local). This would cause all lexically enclosed + operations to map to a WrappingAdd trait – and would also affect primitive operations. I’ve been meaning to write a separate post sketching this out. Anyway, having a convenient way to switch the interpretation of as may make the default stricter interpretation more appealing, though I feel like as is a bit different than + in that a series of wrapping operations often come together, but as conversions are frequently isolated (citation needed).

Overall, I feel conflicted! Usually when I feel conflicted, I lean towards the more conservative option, which suggests keeping as as infallible. Perhaps data is the key to making this decision.