13

Using Casting null doesn't compile as inspiration, and from Eric Lippert's comment:

That demonstrates an interesting case. "uint x = (int)0;" would succeed even though int is not implicitly convertible to uint.

We know this doesn't work, because object can't be assigned to string:

string x = (object)null;

But this does, although intuitively it shouldn't:

uint x = (int)0;

Why does the compiler allow this case, when int isn't implicitly convertible to uint?

Community
  • 1
  • 1
Yuck
  • 49,664
  • 13
  • 105
  • 135
  • 1
    I suppose the same reason `unit x = 0` works. `0` is a signed int unless you specify `unit x = 0U`. – vcsjones Jan 25 '12 at 19:11
  • 6
    Probably 6.1.9 of the spec and discounting of the int cast, because 0 is already an int. While generally an int is not implicitly convertible to uint (6.1.2), a *constant expression* of type int can be converted. – Anthony Pegram Jan 25 '12 at 19:11
  • What value does `0` implicitly have as a constant expression then? Does the compiler just ignore the `(int)` explicit cast and treat `0` as a `uint` constant? – Yuck Jan 25 '12 at 19:12
  • See [this](http://stackoverflow.com/a/8419323/601179) Eric Lippert answer. – gdoron Jan 25 '12 at 19:14
  • 1
    @Yuck, the compiler sees an `int` constant. However, it also knows that a non-negative `int` constant can safely be converted into a `uint`. – Frédéric Hamidi Jan 25 '12 at 19:14
  • @Yuck It's not just `0`. Any numeric literal that is naturally an `int` and that fits in a `uint` will work here, and it certainly seems to be because the cast is elided. – dlev Jan 25 '12 at 19:14
  • possible duplicate of [Why does a compiler dislike implicitly casting to uint's?](http://stackoverflow.com/questions/860863/why-does-a-compiler-dislike-implicitly-casting-to-uints) – JonH Jan 25 '12 at 19:18
  • What a little stupid mistake I did at work cause... =) Poor Eric. – gdoron Jan 25 '12 at 19:23
  • @gdoron I think they're both good questions. It's important for .NET developers (those using frameworks in general) to understand what their code is *really* doing. – Yuck Jan 25 '12 at 19:25

3 Answers3

26

Integer constant conversions are treated as very special by the C# language; here's section 6.1.9 of the specification:

A constant expression 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.

This permits you to do things like:

byte x = 64;

which would otherwise require an ugly explicit conversion:

byte x = (byte)64; // gross
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • So in the case of `uint i = (int)0;`, the compiler just ignores the cast? Or is it just that the result of casting a constant expression is itself a constant expression? – dlev Jan 25 '12 at 19:16
  • 3
    @dlev: The latter. Casting a constant expression of type int to int is still a constant expression. – Eric Lippert Jan 25 '12 at 19:16
  • But isn't `(int)0` a variable\value and not a "constant expression"? – gdoron Jan 25 '12 at 19:18
  • 1
    @gdoron nope it's not. The compiler will know the result at compile time. – Wouter de Kort Jan 25 '12 at 19:19
  • 2
    @JonH: It is a reasonable question. The rule only applies if the expression is a *constant expression of type int* and the result is in the range. In this case, (int)0 *is* a constant expression. I note that we have made mistakes here in the past. For example, the spec says that *the literal zero* is implicitly convertible to any enum. But we also allow "E e = 0 + 0;" despite the fact that "0 + 0" is *not* "the literal zero" -- it is two literal zeros with a plus between them. – Eric Lippert Jan 25 '12 at 19:19
  • 2
    @gdoron: it is clearly not a *variable* because **a variable is by definition a storage location**. It is classified as a *value*; a *constant expression* is a particular kind of *value*. – Eric Lippert Jan 25 '12 at 19:21
  • One interesting point here is that an integer constant has indeed a certain type(`int`, `byte`) and isn't just an abstract "integer constant" type, which is important for type inference. – CodesInChaos Jan 25 '12 at 19:27
  • 1
    @CodeInChaos I don't think any numeric literals are of type `byte`. For integer literals I think the options are `int`, `uint`, `long` and `ulong`, based on the "smallest" type that can store the number. – dlev Jan 25 '12 at 19:28
  • With the enums the underlying design issue was reusing the literal `0` as default value of enums, instead of defining a new keyword such as `none`. Reminds me of how c treats `0` vs `null`, and how it was finally resolved using `nullptr` in C++11. – CodesInChaos Jan 25 '12 at 19:29
  • @dlev I'm talking about constants/constant expressions, not just literals. – CodesInChaos Jan 25 '12 at 19:29
9

The following code wil fail with the message "Cannot implicitly convert type 'int' to 'uint'. An explicit conversion exists (are you missing a cast?)"

int y = 0;
uint x = (int)y;

And this will fail with: "Constant value '-1' cannot be converted to a 'uint'"

uint x = (int)-1;

So the only reason uint x = (int)0; works is because the compiler sees that 0 (or any other value > 0) is a compile time constant that can be converted into a uint

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
Wouter de Kort
  • 39,090
  • 12
  • 84
  • 103
2

In general compilers have 4 steps in which the code is converted. Text is tokenized > Tokens are parsed > An AST is built + linking > the AST is converted to the target language.

The evaluation of constants such as numbers and strings occurs as a first step and the compiler probably treats 0 as a valid token and ignores the cast.

Mudx
  • 605
  • 5
  • 5