Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Subtracting uint and int and constant folding

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?

like image 380
InBetween Avatar asked Oct 15 '14 15:10

InBetween


1 Answers

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 type int can be converted to type sbyte, byte, short, ushort, uint, or ulong, provided the value of the constant-expression is within the range of the destination type.
• A constant-expression of type long can be converted to type ulong, 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 uints.

like image 136
Powerlord Avatar answered Sep 22 '22 07:09

Powerlord