chromium / subspace

A concept-centered standard library for C++20, enabling safer and more reliable products and a more modern feel for C++ code.; Also home of Subdoc the code-documentation generator.
https://suslib.cc
Apache License 2.0
89 stars 15 forks source link

Allow integer shifting by u64 #393

Closed danakj closed 1 year ago

danakj commented 1 year ago

Rust shift operators take a rhs value of u32, which we emulated, however this introduces a sharp edge in C++ where you shift by something like a sizeof() expression. That evaluates to a size_t which used to implicitly narrow to unsigned in order to shift. But with safer types there is no implicit narrowing and you need to write

u32::try_from(shift).unwrap()

Which is then fed to the shift operator which will check (again) that the shift amount is in range.

Nowhere near the full range of values of u32 is in range for a shift operation, so the size of the type is somewhat arbitrary. So the value inside must be checked regardless.

By allowing u64/usize types to be used on the rhs of the shift, we avoid having to write conversions where they aren't holding weight.

This makes the C++ operators accept things that the Rust operators do not. Why is C++ different here?

The answer here is that 1) casting in rust is much simpler, a sizeof<T>() as u32 expression is simpler than the noise introduced by conversions in C++ like sus::cast<u23>(sizeof(T)) 2) we need to rewrite a lot of code that already assumes it's fine to shift by a size_t 3) Rust type deduction allows Rust code to avoid naming the type of things at all and it can pick a u32, whereas C++ will need to pick a type and it will often have picked a size_t/usize.

What mitigates this difference?

And the extra expressivity does not allow C++ to do different things or change expectations wrt the behaviour of the shift operators.