Implemented more combinations of nullable boolean logical operators
authorMarek Safar <marek.safar@gmail.com>
Sat, 25 Sep 2010 09:45:14 +0000 (10:45 +0100)
committerMarek Safar <marek.safar@gmail.com>
Sat, 25 Sep 2010 09:48:33 +0000 (10:48 +0100)
mcs/errors/cs1525-22.cs
mcs/mcs/cfold.cs
mcs/mcs/expression.cs
mcs/mcs/nullable.cs
mcs/tests/gtest-540.cs [new file with mode: 0644]
mcs/tests/ver-il-gmcs.xml

index 54c302e95c3e637ef55c8f849e21c4fdfe168747..b0f1e12ec32ecb9a229c15b2042df7afe68ee382 100644 (file)
@@ -1,4 +1,4 @@
-// CS1003: Syntax error, `.' expected
+// CS1525: Unexpected symbol `::', expecting `.' or `{'
 // Line: 4
 
 namespace a::b
index eb4ef94ecb08d45f9672cf65c505da5b28098992..3774272a4c54b828d7f535ffe0badefda5d66bcd 100644 (file)
@@ -193,6 +193,23 @@ namespace Mono.CSharp {
 
                        switch (oper){
                        case Binary.Operator.BitwiseOr:
+                               //
+                               // bool? operator &(bool? x, bool? y);
+                               //
+                               if ((lt == TypeManager.bool_type && right is NullLiteral) ||
+                                       (rt == TypeManager.bool_type && left is NullLiteral)) {
+                                       var b = new Nullable.LiftedBinaryOperator (oper, left, right, loc).Resolve (ec);
+
+                                       // false | null => null
+                                       // null | false => null
+                                       if ((right is NullLiteral && left.IsDefaultValue) || (left is NullLiteral && right.IsDefaultValue))
+                                               return Nullable.LiftedNull.CreateFromExpression (ec, b);
+
+                                       // true | null => true
+                                       // null | true => true
+                                       return ReducedExpression.Create (new BoolConstant (true, loc).Resolve (ec), b);                                 
+                               }
+
                                if (!DoBinaryNumericPromotions (ec, ref left, ref right))
                                        return null;
 
@@ -220,6 +237,23 @@ namespace Mono.CSharp {
                                break;
                                
                        case Binary.Operator.BitwiseAnd:
+                               //
+                               // bool? operator &(bool? x, bool? y);
+                               //
+                               if ((lt == TypeManager.bool_type && right is NullLiteral) ||
+                                       (rt == TypeManager.bool_type && left is NullLiteral)) {
+                                       var b = new Nullable.LiftedBinaryOperator (oper, left, right, loc).Resolve (ec);
+
+                                       // false & null => false
+                                       // null & false => false
+                                       if ((right is NullLiteral && left.IsDefaultValue) || (left is NullLiteral && right.IsDefaultValue))
+                                               return ReducedExpression.Create (new BoolConstant (false, loc).Resolve (ec), b);
+
+                                       // true & null => null
+                                       // null & true => null
+                                       return Nullable.LiftedNull.CreateFromExpression (ec, b);
+                               }
+
                                if (!DoBinaryNumericPromotions (ec, ref left, ref right))
                                        return null;
                                
@@ -848,7 +882,9 @@ namespace Mono.CSharp {
                                break;
 
                        case Binary.Operator.Equality:
-                               if (TypeManager.IsReferenceType (lt) && TypeManager.IsReferenceType (lt)) {
+                               if (TypeManager.IsReferenceType (lt) && TypeManager.IsReferenceType (rt) ||
+                                       (left is Nullable.LiftedNull && right.IsNull) ||
+                                       (right is Nullable.LiftedNull && left.IsNull)) {
                                        if (left.IsNull || right.IsNull) {
                                                return ReducedExpression.Create (
                                                        new BoolConstant (left.IsNull == right.IsNull, left.Location).Resolve (ec),
@@ -890,7 +926,9 @@ namespace Mono.CSharp {
                                return new BoolConstant (bool_res, left.Location);
 
                        case Binary.Operator.Inequality:
-                               if (TypeManager.IsReferenceType (lt) && TypeManager.IsReferenceType (lt)) {
+                               if (TypeManager.IsReferenceType (lt) && TypeManager.IsReferenceType (rt) ||
+                                       (left is Nullable.LiftedNull && right.IsNull) ||
+                                       (right is Nullable.LiftedNull && left.IsNull)) {
                                        if (left.IsNull || right.IsNull) {
                                                return ReducedExpression.Create (
                                                        new BoolConstant (left.IsNull != right.IsNull, left.Location).Resolve (ec),
index 5f4de1e464b45923a32059f0cb2cd50dd77f22a0..99eab4ce57ec829820492dbe15b07811684d305b 100644 (file)
@@ -1750,7 +1750,7 @@ namespace Mono.CSharp {
 
                                var c = b.right as Constant;
                                if (c != null) {
-                                       if (c.IsDefaultValue && (b.oper == Operator.Addition || b.oper == Operator.BitwiseOr || b.oper == Operator.Subtraction))
+                                       if (c.IsDefaultValue && (b.oper == Operator.Addition || b.oper == Operator.Subtraction || (b.oper == Operator.BitwiseOr && !(b is Nullable.LiftedBinaryOperator))))
                                                return ReducedExpression.Create (b.left, b).Resolve (ec);
                                        if ((b.oper == Operator.Multiply || b.oper == Operator.Division) && c.IsOneInteger)
                                                return ReducedExpression.Create (b.left, b).Resolve (ec);
@@ -1759,7 +1759,7 @@ namespace Mono.CSharp {
 
                                c = b.left as Constant;
                                if (c != null) {
-                                       if (c.IsDefaultValue && (b.oper == Operator.Addition || b.oper == Operator.BitwiseOr))
+                                       if (c.IsDefaultValue && (b.oper == Operator.Addition || b.oper == Operator.Subtraction || (b.oper == Operator.BitwiseOr && !(b is Nullable.LiftedBinaryOperator))))
                                                return ReducedExpression.Create (b.right, b).Resolve (ec);
                                        if (b.oper == Operator.Multiply && c.IsOneInteger)
                                                return ReducedExpression.Create (b.right, b).Resolve (ec);
@@ -3326,7 +3326,7 @@ namespace Mono.CSharp {
                        if (oper == Operator.BitwiseAnd || oper == Operator.LogicalAnd) {
                                Constant rc = right as Constant;
                                Constant lc = left as Constant;
-                               if ((lc != null && lc.IsDefaultValue) || (rc != null && rc.IsDefaultValue)) {
+                               if (((lc != null && lc.IsDefaultValue) || (rc != null && rc.IsDefaultValue)) && !(this is Nullable.LiftedBinaryOperator)) {
                                        //
                                        // The result is a constant with side-effect
                                        //
index 1f4df2e824f75df3260016457e2426862c07bf44..5c9d1940ab2a7e7b025555dc0cc7efd60c080a02 100644 (file)
@@ -341,7 +341,7 @@ namespace Mono.CSharp.Nullable
                        return new LiftedNull (nullable, loc);
                }
 
-               public static Expression CreateFromExpression (ResolveContext ec, Expression e)
+               public static Constant CreateFromExpression (ResolveContext ec, Expression e)
                {
                        ec.Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'",
                                TypeManager.CSharpName (e.Type));
@@ -573,8 +573,9 @@ namespace Mono.CSharp.Nullable
 
                bool IsBitwiseBoolean {
                        get {
-                               return (Oper & Operator.BitwiseMask) != 0 && left_unwrap != null && right_unwrap != null &&
-                               left_unwrap.Type == TypeManager.bool_type && right_unwrap.Type == TypeManager.bool_type;
+                               return (Oper & Operator.BitwiseMask) != 0 &&
+                               ((left_unwrap != null && left_unwrap.Type == TypeManager.bool_type) ||
+                                (right_unwrap != null && right_unwrap.Type == TypeManager.bool_type));
                        }
                }
 
@@ -603,7 +604,7 @@ namespace Mono.CSharp.Nullable
                // with the null literal *outside* of a generics context and
                // inlines that as true or false.
                //
-               Expression CreateNullConstant (ResolveContext ec, Expression expr)
+               Constant CreateNullConstant (ResolveContext ec, Expression expr)
                {
                        // FIXME: Handle side effect constants
                        Constant c = new BoolConstant (Oper == Operator.Inequality, loc).Resolve (ec);
@@ -668,11 +669,21 @@ namespace Mono.CSharp.Nullable
                        Label load_right = ec.DefineLabel ();
                        Label end_label = ec.DefineLabel ();
 
+                       // null & value, null | value
+                       if (left_unwrap == null) {
+                               left_unwrap = right_unwrap;
+                               right_unwrap = null;
+                               right = left;
+                       }
+
                        left_unwrap.Emit (ec);
                        ec.Emit (OpCodes.Brtrue_S, load_right);
 
-                       right_unwrap.Emit (ec);
-                       ec.Emit (OpCodes.Brtrue_S, load_left);
+                       // value & null, value | null
+                       if (right_unwrap != null) {
+                               right_unwrap.Emit (ec);
+                               ec.Emit (OpCodes.Brtrue_S, load_left);
+                       }
 
                        left_unwrap.EmitCheck (ec);
                        ec.Emit (OpCodes.Brfalse_S, load_right);
@@ -683,14 +694,30 @@ namespace Mono.CSharp.Nullable
                        if (Oper == Operator.BitwiseAnd) {
                                left_unwrap.Load (ec);
                        } else {
-                               right_unwrap.Load (ec);
-                               right_unwrap = left_unwrap;
+                               if (right_unwrap == null) {
+                                       right.Emit (ec);
+                                       if (right is EmptyConstantCast)
+                                               ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
+                               } else {
+                                       right_unwrap.Load (ec);
+                                       right_unwrap = left_unwrap;
+                               }
                        }
                        ec.Emit (OpCodes.Br_S, end_label);
 
                        // load right
                        ec.MarkLabel (load_right);
-                       right_unwrap.Load (ec);
+                       if (right_unwrap == null) {
+                               if (Oper == Operator.BitwiseAnd) {
+                                       right.Emit (ec);
+                                       if (right is EmptyConstantCast)
+                                               ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
+                               } else {
+                                       left_unwrap.Load (ec);
+                               }
+                       } else {
+                               right_unwrap.Load (ec);
+                       }
 
                        ec.MarkLabel (end_label);
                }
@@ -877,6 +904,13 @@ namespace Mono.CSharp.Nullable
                        if (IsLeftNullLifted) {
                                left = LiftedNull.Create (right.Type, left.Location);
 
+                               //
+                               // Special case for bool?, the result depends on both null right side and left side value
+                               //
+                               if ((Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) && NullableInfo.GetUnderlyingType (type) == TypeManager.bool_type) {
+                                       return res_expr;
+                               }
+
                                if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0)
                                        return LiftedNull.CreateFromExpression (ec, res_expr);
 
@@ -890,6 +924,13 @@ namespace Mono.CSharp.Nullable
                        if (IsRightNullLifted) {
                                right = LiftedNull.Create (left.Type, right.Location);
 
+                               //
+                               // Special case for bool?, the result depends on both null right side and left side value
+                               //
+                               if ((Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) && NullableInfo.GetUnderlyingType (type) == TypeManager.bool_type) {
+                                       return res_expr;
+                               }
+
                                if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0)
                                        return LiftedNull.CreateFromExpression (ec, res_expr);
 
diff --git a/mcs/tests/gtest-540.cs b/mcs/tests/gtest-540.cs
new file mode 100644 (file)
index 0000000..8c9c43a
--- /dev/null
@@ -0,0 +1,91 @@
+// lifted null binary operators
+
+using System;
+
+class C
+{
+       public static int Main ()
+       {
+               bool v;
+               v = (true & null) == null;
+               if (!v)
+                       return 1;
+
+               v = (false & null) != null;
+               if (!v)
+                       return 2;
+               
+               v = (null & true) == null;
+               if (!v)
+                       return 3;
+
+               v = (null & false) != null;
+               if (!v)
+                       return 4;
+
+               v = (true | null) == null;
+               if (v != false)
+                       return 11;
+
+               v = (false | null) != null;
+               if (v != false)
+                       return 12;
+
+               v = (null | true) == null;
+               if (v != false)
+                       return 13;
+
+               v = (null | false) != null;
+               if (v != false)
+                       return 14;
+               
+               v = (null & 1) == null;
+               if (v != true)
+                       return 20;
+               
+               v = (null & 0) != null;
+               if (v != false)
+                       return 21;
+
+               bool? a = false;
+               bool? b = true;
+
+               if ((a & null) != false)
+                       return 50;
+
+               if ((b & null) != null)
+                       return 51;
+               
+               if ((null & a) != false)
+                       return 52;
+               
+               if ((null & b) != null)
+                       return 53;
+
+               if ((a & true) != false)
+                       return 54;
+               
+               if ((true & a) != false)
+                       return 55;
+
+               if ((a | null) != null)
+                       return 60;
+
+               if ((b | null) != true)
+                       return 61;
+               
+               if ((null | a) != null)
+                       return 62;
+               
+               if ((null | b) != true)
+                       return 63;
+               
+               if ((a | true) != true)
+                       return 64;
+               
+               if ((true | a) != true)
+                       return 65;
+               
+               return 0;
+       }
+}
\ No newline at end of file
index 0af5797358a54a4ed641b6df34278ecf594624e6..aee7b8404a7b25eed650690e091cf3f06f57c588 100644 (file)
         <size>7</size>
       </method>
       <method name="Int32 Main()">
-        <size>94</size>
+        <size>74</size>
       </method>
     </type>
     <type name="D">
         <size>26</size>
       </method>
       <method name="Void Main()">
-        <size>98</size>
+        <size>88</size>
       </method>
     </type>
   </test>
       </method>
     </type>
   </test>
+  <test name="gtest-540.cs">
+    <type name="C">
+      <method name="Int32 Main()">
+        <size>809</size>
+      </method>
+      <method name="Void .ctor()">
+        <size>7</size>
+      </method>
+    </type>
+  </test>
   <test name="gtest-anon-1.cs">
     <type name="X">
       <method name="Void .ctor()">
         <size>7</size>
       </method>
       <method name="Void ZeroBasedReductions()">
-        <size>18</size>
+        <size>17</size>
       </method>
       <method name="Void Main()">
         <size>11</size>