From a208b628e967d943f52b0af06981093910d23905 Mon Sep 17 00:00:00 2001 From: Marek Safar Date: Fri, 16 Aug 2013 15:18:49 +0200 Subject: [PATCH] Implements lifted nullable comparison with null --- .../BinaryExpression.cs | 44 +++++++++++++++++-- .../ConstantExpression.cs | 6 +++ .../System.Linq.Expressions/Expression.cs | 16 +++++-- .../ExpressionTest_Equal.cs | 18 ++++++++ 4 files changed, 77 insertions(+), 7 deletions(-) diff --git a/mcs/class/System.Core/System.Linq.Expressions/BinaryExpression.cs b/mcs/class/System.Core/System.Linq.Expressions/BinaryExpression.cs index 03f5f5ee492..b0528917b2c 100644 --- a/mcs/class/System.Core/System.Linq.Expressions/BinaryExpression.cs +++ b/mcs/class/System.Core/System.Linq.Expressions/BinaryExpression.cs @@ -570,12 +570,48 @@ namespace System.Linq.Expressions { void EmitRelationalBinary (EmitContext ec) { - if (!IsLifted) + if (!IsLifted) { EmitNonLiftedBinary (ec); - else if (IsLiftedToNull) + return; + } + + if (IsLiftedToNull) { EmitLiftedToNullBinary (ec); - else - EmitLiftedRelationalBinary (ec); + return; + } + + if (ConstantExpression.IsNull (right) && !ConstantExpression.IsNull (left) && left.Type.IsNullable ()) { + EmitNullEquality (ec, left); + return; + } + + if (ConstantExpression.IsNull (left) && !ConstantExpression.IsNull (right) && right.Type.IsNullable ()) { + EmitNullEquality (ec, right); + return; + } + + EmitLiftedRelationalBinary (ec); + } + + void EmitNullEquality (EmitContext ec, Expression e) + { + var ig = ec.ig; + + if (IsLiftedToNull) { + e.Emit (ec); + if (e.Type != typeof (void)) + ig.Emit (OpCodes.Pop); + + ec.EmitNullableNew (typeof (bool?)); + return; + } + + var se = ec.EmitStored (e); + ec.EmitNullableHasValue (se); + if (NodeType == ExpressionType.Equal) { + ig.Emit (OpCodes.Ldc_I4_0); + ig.Emit (OpCodes.Ceq); + } } void EmitLiftedUserDefinedOperator (EmitContext ec) diff --git a/mcs/class/System.Core/System.Linq.Expressions/ConstantExpression.cs b/mcs/class/System.Core/System.Linq.Expressions/ConstantExpression.cs index 10dd69ce8f6..2ff30c8b81c 100644 --- a/mcs/class/System.Core/System.Linq.Expressions/ConstantExpression.cs +++ b/mcs/class/System.Core/System.Linq.Expressions/ConstantExpression.cs @@ -51,6 +51,12 @@ namespace System.Linq.Expressions { { this.value = value; } + + internal static bool IsNull (Expression e) + { + var c = e as ConstantExpression; + return c != null && c.value == null; + } #if !FULL_AOT_RUNTIME internal override void Emit (EmitContext ec) diff --git a/mcs/class/System.Core/System.Linq.Expressions/Expression.cs b/mcs/class/System.Core/System.Linq.Expressions/Expression.cs index f79156284c2..696da9ff91c 100644 --- a/mcs/class/System.Core/System.Linq.Expressions/Expression.cs +++ b/mcs/class/System.Core/System.Linq.Expressions/Expression.cs @@ -272,6 +272,12 @@ namespace System.Linq.Expressions { if (ltype == rtype && ultype == typeof (bool)) return null; + + if (ltype.IsNullable () && ConstantExpression.IsNull (right) && !ConstantExpression.IsNull (left)) + return null; + + if (rtype.IsNullable () && ConstantExpression.IsNull (left) && !ConstantExpression.IsNull (right)) + return null; } if (oper_name == "op_LeftShift" || oper_name == "op_RightShift") { @@ -392,12 +398,16 @@ namespace System.Linq.Expressions { if (!left.Type.IsNullable () && !right.Type.IsNullable ()) { is_lifted = false; liftToNull = false; - type = typeof (bool); + type = typeof(bool); } else if (left.Type.IsNullable () && right.Type.IsNullable ()) { is_lifted = true; - type = liftToNull ? typeof (bool?) : typeof (bool); - } else + type = liftToNull ? typeof(bool?) : typeof(bool); + } else if (ConstantExpression.IsNull (left) || ConstantExpression.IsNull (right)) { + is_lifted = true; + type = typeof (bool); + } else { throw new InvalidOperationException (); + } } else { var parameters = method.GetParameters (); diff --git a/mcs/class/System.Core/Test/System.Linq.Expressions/ExpressionTest_Equal.cs b/mcs/class/System.Core/Test/System.Linq.Expressions/ExpressionTest_Equal.cs index 06612bdfa18..749d78e40f8 100644 --- a/mcs/class/System.Core/Test/System.Linq.Expressions/ExpressionTest_Equal.cs +++ b/mcs/class/System.Core/Test/System.Linq.Expressions/ExpressionTest_Equal.cs @@ -458,5 +458,23 @@ namespace MonoTests.System.Linq.Expressions Assert.AreEqual (false, eq (Foo.Bar, null)); Assert.AreEqual (true, eq (null, null)); } + + [Test] + public void NullableNullEqual () + { + var param = Expression.Parameter (typeof (DateTime?), "x"); + + var node = Expression.Equal (param, Expression.Constant (null)); + + Assert.IsTrue (node.IsLifted); + Assert.IsFalse (node.IsLiftedToNull); + Assert.AreEqual (typeof (bool), node.Type); + Assert.IsNull (node.Method); + + var eq = Expression.Lambda> (node, new [] { param }).Compile (); + + Assert.AreEqual (true, eq (null)); + Assert.AreEqual (false, eq (DateTime.Now)); + } } } -- 2.25.1