11

Consider the following code:

DateTime t = DateTime.Today;

bool isGreater = t > null;

With Visual Studio 2010 (C# 4, .NET 4.0), I get the following warning:

warning CS0458: The result of the expression is always 'null' of type 'bool?'

This is incorrect; the result is always false (of type bool):

Now, the struct DateTime overloads the > (greater than) operator. Any non-nullable struct (like DateTime) is implicitly convertible to the corresponding Nullable<> type. The above expression is exactly equivalent to

bool isGreater = (DateTime?)t > (DateTime?)null;

which also generates the same wrong warning. Here the > operator is the lifted operator. This works by returning false if HasValue of any of its two operands is false. Otherwise, the lifted operator would proceed to unwrap the two operands to the underlying struct, and then call the overload of > defined by that struct (but this is not necessary in this case where one operand does not HasValue).

Can you reproduce this bug, and is this bug well-known? Have I misunderstood something?

This is the same for all struct types (not simple types like int, and not enum types) which overload the operator in question.

(Now if we use == instead of >, everything ought to be entirely similar (because DateTime also overloads the == operator). But it's not similar. If I say

DateTime t = DateTime.Today;

bool isEqual = t == null;

I get no warning ☹ Sometimes you see people accidentally check a variable or parameter for null, not realizing that the type of their variable is a struct (which overloads == and which is not a simple type like int). It would be better if they got a warning.)


Update: With the C# 6.0 compiler (based on Roslyn) of Visual Studio 2015, the incorrect message with isGreater above is changed into a CS0464 with a correct and helpful warning message. Also, the lack of warning with isEqual above is fixed in VS2015's compiler, but only if you compile with /features:strict.

Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181
  • `[anything] > null` doesn't make any sense to begin with (to me, at least). Interesting question nonetheless, I think it should warn about the `bool` always ending up as `false`. – Alex Oct 24 '12 at 09:20
  • 1
    May be helpful: http://blogs.msdn.com/b/abhinaba/archive/2005/12/11/501544.aspx and http://blogs.msdn.com/b/abhinaba/archive/2005/12/14/503533.aspx – Habib Oct 24 '12 at 09:21
  • Interestingly, `DateTime.CompareTo(object)` _specifically_ returns `1` when you compare to `null`, whatever the type of object passed in. – Rawling Oct 24 '12 at 09:55
  • 1
    The same warning appears in VS 2012 with .net 4.5 :) – Arman Dec 27 '12 at 11:50

3 Answers3

9

I discovered this error independently while implementing lifted operator behaviour in Roslyn, and I fixed it in Roslyn before I left.

Sorry that I didn't see this when you posted it back in October. Thanks for submitting it to Connect! And many apologies for the error; it is a long-standing error in operator semantic analysis.

Incidentally, I'll be discussing how Roslyn optimizes lifted expressions on http://ericlippert.com later this month (December 2012), so if this subject interests you, check it out:

http://ericlippert.com/2012/12/20/nullable-micro-optimizations-part-one/

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 3
    There's no need to apologize for not seeing every thread on Stack Overflow. I hope I remember to check your upcoming post on lifted operators. – Jeppe Stig Nielsen Dec 19 '12 at 07:35
5

You are correct: this is a bug in Visual Studio. The C# 4.0 standard (§ 7.3.7 Lifted operators) has this to say:

For the relational operators

<  >  <=  >=

[…] The lifted operator produces the value false if one or both operands are null.

And in fact, in MonoDevelop, you get the following warning instead:

The result of comparing type System.DateTime with null is always false.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • But, in this `>` case, the result is false, not null. The type of the result is `bool`, not `bool?`. – Rawling Oct 24 '12 at 09:44
  • @Rawling Is it? That sounds wrong, it should definitely be `bool?` since the operator is lifted. – Konrad Rudolph Oct 24 '12 at 09:45
  • Visual Studio seems to think so, but `DateTime.Today > null` gives me `false` of type `bool`, and the asker gets the same, apparently. I agree it's weird. – Rawling Oct 24 '12 at 09:47
  • @Rawling Duh, of course – otherwise the code wouldn’t even compile. Hehe, funny – so apparently `DateTime` overloads the operator for `null` … – Konrad Rudolph Oct 24 '12 at 09:48
  • 1
    You: _this is generally true for all operators, not just addition._ No. Please read the section "Lifted operators" of the C# Language Specification. The lifted operators `==` and `>` return `bool` just like the original operator. That's different from the `+` operator. The lifted `+` operator (unary and binary) returns `DateTime?` (nullable) because the unlifted form returns `DateTime` (non-nullable). – Jeppe Stig Nielsen Oct 24 '12 at 10:38
  • @Jeppe Damn, I remembered that completely wrong. Well, I’ve removed the wrong part of the answer. The note about the VS bug still stands. In that you are definitely correct, and MonoDevelop’s behaviour nicely demonstrates it (even though it doesn’t constitute incontrovertible truth, obviously). – Konrad Rudolph Oct 24 '12 at 11:37
  • I found out that Eric Lippert in [an answer elsewhere](http://stackoverflow.com/a/4400231/1336654) explains the decision process they had when they introduced nullable types. It **was** considered to make things the way you thought they were (so that `>` would return `null`, not `false`), but eventually they decided to do it differently. It looks like they forgot to get rid of some old warnings from before this decision. – Jeppe Stig Nielsen Oct 24 '12 at 14:05
  • 1
    Since this answer is the better one, I'll mark it accepted. After your edits it's a good answer. Nobody gave reference if this was a known bug, so I submitted it. See [this feeback item at Connect](https://connect.microsoft.com/VisualStudio/feedback/details/768940/). – Jeppe Stig Nielsen Oct 28 '12 at 08:24
1
DateTime t = DateTime.Today;

bool isGreater = (DateTime?)t > (DateTime?)null;

In this scenario what the warning is about is the t > null. That is never going to be true. Because It can't be evaluated.

In this scenario:

bool isGreater = (DateTime?)t > (DateTime?)null;

We are evaluating (DateTime?)t > (DateTime?)null;

Or essentially in the best case scenario t > null; same as before. DateTime.Now can never be greater than Undefined, hence the warning.

matthewr
  • 4,679
  • 5
  • 30
  • 40
awright18
  • 2,255
  • 2
  • 23
  • 22
  • I agree it's relevant with a warning, but have you read the warning **message**? It's the text of the warning that is disturbingly wrong, not the fact that a warning is emitted. (Maybe at some time during the development of nullable types they planned to have the lifted operators of comparisons (`>`, `==`, and so on) behave differently, but then changed their minds?) – Jeppe Stig Nielsen Oct 24 '12 at 10:45