Based on this interesting question: Addition of int and uint and toying around with constant folding as mentioned in Nicholas Carey's answer, I've stumbled upon a seemingly inconsistent behavior of the compiler:
Consider the following code snippet:
int i = 1;
uint j = 2;
var k = i - j;
Here the compiler correctly resolves k
to long
. This particular behavior is well defined in the specifications as explained in the answers to the previously referred question.
What was surprising to me, is that the behavior changes when dealing with literal constants or constants in general. Reading Nicholas Carey's answer I realized that the behavior could be inconsistent so I checked and sure enough:
const int i = 1;
const uint j = 2;
var k = i - j; //Compile time error: The operation overflows at compile time in checked mode.
k = 1 - 2u; //Compile time error: The operation overflows at compile time in checked mode.
k
in this case is resolved to Uint32
.
Is there a reason for the behavior being different when dealing with constants or is this a small but unfortunate "bug" (lack of a better term) in the compiler?
From the C# specification version 5, section 6.1.9, Constant Expressions only allow the following implicit conversions
6.1.9 Implicit constant expression conversions
An implicit constant expression conversion permits the following conversions:
* A constant-expression (§7.19) of typeint
can be converted to typesbyte
,byte
,short
,ushort
,uint
, orulong
, provided the value of the constant-expression is within the range of the destination type.
• A constant-expression of typelong
can be converted to typeulong
, provided the value of the constant-expression is not negative.
Note that long
is not on the list of int
conversions.
The other half the problem is that only a small number of numeric promotions happen for binary operations:
(From Section 7.3.6.2 Binary numeric promotions):
- If either operand is of type decimal, the other operand is converted to type decimal, or a binding-time error occurs if the other operand is of type float or double.
- Otherwise, if either operand is of type double, the other operand is converted to type double.
- Otherwise, if either operand is of type float, the other operand is converted to type float.
- Otherwise, if either operand is of type ulong, the other operand is converted to type ulong, or a binding-time error occurs if the other operand is of type sbyte, short, int, or long.
- Otherwise, if either operand is of type long, the other operand is converted to type long.
- Otherwise, if either operand is of type uint and the other operand is of type sbyte, short, or int, both operands are converted to type long.
- Otherwise, if either operand is of type uint, the other operand is converted to type uint.
- Otherwise, both operands are converted to type int.
REMEMBER: The int
to long
conversion is forbidden for constants, meaning that both args are instead promoted to uint
s.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With