Open skejeton opened 3 months ago
Yes, all arithmetical and bitwise operators always operate on full-size (64-bit) temporary values, so ~uint16(0)
exceeds the uint16
range. You can use ~uint16(0) & 0xFFFF
instead.
Can we just compile it to 0xFFFF ~ num
?
@skejeton You essentially mean that Umka should respect the actual operand widths even in expressions. To do it, Umka should apply some widening rules when two operands are of different widths. What would such rules look like? The "least common width" rule? It would have its own adverse effects:
var a: uint8 = 200
var b: uint16 = 65500
var c: uint = a + b // Overflow or what?
var a: uint8 = 0
var b: uint16 = 0
var c: bool = ~a == ~b // true or false?
@skejeton You essentially mean that Umka should respect the actual operand widths even in expressions. To do it, Umka should apply some widening rules when two operands are of different widths. What would such rules look like? The "least common width" rule? It would have its own adverse effects:
var a: uint8 = 200 var b: uint16 = 65500 var c: uint = a + b // Overflow or what?
var a: uint8 = 0 var b: uint16 = 0 var c: bool = ~a == ~b // true or false?
I think it should only apply to unary operations, which one of them is negation.
And so ~a != ~b
in my second example, even though both a
and b
are zero, right?
And so
~a != ~b
in my second example, even though botha
andb
are zero, right?
Good question. I think so. I can't think of any practical reason why it shouldn't be this way.
In a perfect world, if a == b
, I could substitute a
for b
in any expression (e.g., ~b
) and get the same result. In reality, I'm afraid, I will never achieve this unless I forbid implicit type casts.
Yeah, I'm not sure, C does behave as you say: https://godbolt.org/z/cqPh6nEKq
But the promotion rules are different, from what I know it promotes to the highest bit type in the expression instead of straight up uint
, and of course it doesn't do overflow checks. Personally I'm not against either idea.
An idea I have is to have some sort of context in expression, where if you explicitly cast to uint16
, it will only promote as high as uint16
, because casting anything higher than uint16
wouldn't make a lot of sense.
@skejeton
it promotes to the highest bit type in the expression instead of straight up uint
C is weird. It always promotes everything to int
, but not to int64_t
, unless one of the operands is already int64_t
.
The standard says:
If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.
And a comment from Stack Overflow:
The harsh reality caused by the integer promotions means that almost no operation in C can be carried out on small types like char or short. Operations are always carried out on int or larger types.
Now let's see what Compiler Explorer will do:
uint8_t a = 200;
uint16_t b = 0xFFFF;
uint64_t c = a + b;
printf("%d\n", c); // 65735 - no overflow
uint8_t a = 200;
uint32_t b = 0xFFFFFFFF;
uint64_t c = a + b;
printf("%d\n", c); // 199 - overflow
You're right. I think to make the semantics less complicated/arbitrary, in an expression that expects uint16, cast to uint16 at most, and so on.