The short answer is: yes, that's a bug. I've put a minimal repro and a short analysis below.
My apologies. I wrote a lot of that code and so it was likely my bad.
I have sent a repro off to the Roslyn development, test and program management teams. I doubt this reproduces in Roslyn, but they'll verify that it does not and decide whether this makes the bar for a C# 5 service pack.
Feel free to enter an issue on connect.microsoft.com as well if you want it tracked there as well.
Minimal repro:
using System;
using System.Linq.Expressions;
struct S<T>
{
public static bool operator ==(S<T> a, S<T> b) { return false; }
public static bool operator !=(S<T> a, S<T> b) { return false; }
}
class Program
{
static void Main()
{
Expression<Func<S<int>?, S<int>, bool>> x = (a, b) => a == b;
}
}
The code that is generated in the minimal repro is equivalent to
ParameterExpression pa = Expression.Parameter(typeof(S<int>?), "a");
ParameterExpression pb = Expression.Parameter(typeof(S<int>), "b");
Expression.Lambda<Func<S<int>?, S<int>, bool>>(
Expression.Equal(pa, pb, false, infoof(S<int>.op_Equality)
new ParameterExpression[2] { pa, pb } );
Where infoof
is a fake operator that gets a MethodInfo
for the given method.
The correct code would be:
ParameterExpression pa = Expression.Parameter(typeof(S<int>?), "a");
ParameterExpression pb = Expression.Parameter(typeof(S<int>), "b");
Expression.Lambda<Func<S<int>?, S<int>, bool>>(
Expression.Equal(pa, Expression.Convert(pb, typeof(S<int>?), false, infoof(S<int>.op_Equality)
new ParameterExpression[2] { pa, pb } );
The Equal
method cannot deal with one nullable, one non-nullable operands. It requires that either both are nullable or neither is.
(Note that the false
is correct. This Boolean controls whether the result of a lifted equality is a lifted Boolean; in C# it is not, in VB it is.)