Wrong compiler warning when comparing struct to null
Asked Answered
B

3

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.

Blueweed answered 24/10, 2012 at 9:10 Comment(4)
[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.Jeffryjeffy
May be helpful: blogs.msdn.com/b/abhinaba/archive/2005/12/11/501544.aspx and blogs.msdn.com/b/abhinaba/archive/2005/12/14/503533.aspxWasteful
Interestingly, DateTime.CompareTo(object) specifically returns 1 when you compare to null, whatever the type of object passed in.Dictate
The same warning appears in VS 2012 with .net 4.5 :)Gambrell
H
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.

Hipster answered 24/10, 2012 at 9:39 Comment(8)
But, in this > case, the result is false, not null. The type of the result is bool, not bool?.Dictate
@Dictate Is it? That sounds wrong, it should definitely be bool? since the operator is lifted.Hipster
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.Dictate
@Dictate Duh, of course – otherwise the code wouldn’t even compile. Hehe, funny – so apparently DateTime overloads the operator for nullHipster
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).Blueweed
@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).Hipster
I found out that Eric Lippert in an answer elsewhere 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.Blueweed
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.Blueweed
P
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/

Protease answered 19/12, 2012 at 0:26 Comment(1)
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.Blueweed
H
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.

Hipster answered 24/10, 2012 at 9:39 Comment(8)
But, in this > case, the result is false, not null. The type of the result is bool, not bool?.Dictate
@Dictate Is it? That sounds wrong, it should definitely be bool? since the operator is lifted.Hipster
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.Dictate
@Dictate Duh, of course – otherwise the code wouldn’t even compile. Hehe, funny – so apparently DateTime overloads the operator for nullHipster
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).Blueweed
@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).Hipster
I found out that Eric Lippert in an answer elsewhere 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.Blueweed
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.Blueweed
C
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.

Concision answered 24/10, 2012 at 9:19 Comment(1)
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?)Blueweed

© 2022 - 2024 — McMap. All rights reserved.