2008-04-01 Marek Safar <marek.safar@gmail.com>
authorMarek Safar <marek.safar@gmail.com>
Tue, 1 Apr 2008 16:00:02 +0000 (16:00 -0000)
committerMarek Safar <marek.safar@gmail.com>
Tue, 1 Apr 2008 16:00:02 +0000 (16:00 -0000)
* ecore.cs, convert.cs, constant.cs, nullable.cs, expression.cs: Implemented
lifting of null literal and user operators. Clean up of some temporary
nullable hacks.

svn path=/trunk/mcs/; revision=99535

mcs/mcs/ChangeLog
mcs/mcs/constant.cs
mcs/mcs/convert.cs
mcs/mcs/ecore.cs
mcs/mcs/expression.cs
mcs/mcs/nullable.cs

index 531a9f5881525db59fa544b275f114cf013b8a61..af1defa91693b73d6f7418deb9825029750384ea 100644 (file)
@@ -1,3 +1,9 @@
+2008-04-01  Marek Safar  <marek.safar@gmail.com>
+
+       * ecore.cs, convert.cs, constant.cs, nullable.cs, expression.cs: Implemented
+       lifting of null literal and user operators. Clean up of some temporary
+       nullable hacks.
+       
 2008-03-30  Raja R Harinath  <harinath@hurrynot.org>
 
        Fix #368224, test-629.cs
index 32d265202c94ef668bcb3df2f1904ff3ecafdeaf..17de204fd463ff7f1426ae8360495cca03c428cc 100644 (file)
@@ -154,7 +154,7 @@ namespace Mono.CSharp {
                        ArrayList args = new ArrayList (2);
                        args.Add (new Argument (this));
                        args.Add (new Argument (
-                               new TypeOf (new TypeExpression (type, Location), Location)));
+                               new TypeOf (new TypeExpression (type, loc), loc)));
 
                        return CreateExpressionFactoryCall ("Constant", args);
                }
index 4e6399aabc7f68fe1a80b30dc545d49f84a5eafc..99459ab6c420c4fee599e930ac4d863d5184d210 100644 (file)
@@ -1284,7 +1284,7 @@ namespace Mono.CSharp {
                                // From null to any nullable type
                                //
                                if (expr_type == TypeManager.null_type)
-                                       return new Nullable.Null (target_type, loc);
+                                       return Nullable.LiftedNull.Create (target_type, loc);
                                
                                Type target = TypeManager.GetTypeArguments (target_type) [0];
 
index 6beb5fab57d9b4711adb2684915f30f65b17a96f..4f9d32f5cb248cdbe3474411b3bb4292b719e3b1 100644 (file)
@@ -1321,21 +1321,6 @@ namespace Mono.CSharp {
                        if (c != null)
                                return new EmptyConstantCast (c, type);
 
-                       //
-                       // No double conversion required when wrapping nullable types
-                       //
-                       if (TypeManager.IsNullableType (type)) {
-                               EmptyCast empty_cast = child as EmptyCast;
-                               if (empty_cast != null) {
-                                       if (TypeManager.IsNullableTypeOf (empty_cast.type, type))
-                                               throw new InternalErrorException ("Missing nullable underlying type conversion {0} != {1}",
-                                                       TypeManager.CSharpName (empty_cast.type), TypeManager.CSharpName (type));
-
-                                       empty_cast.type = type;
-                                       return empty_cast;
-                               }
-                       }
-                       
                        return new EmptyCast (child, type);
                }
 
@@ -1574,7 +1559,7 @@ namespace Mono.CSharp {
 
                public override bool IsZeroInteger {
                        get { return child.IsZeroInteger; }
-               }               
+               }
                
                public override void Emit (EmitContext ec)
                {
@@ -2009,12 +1994,6 @@ namespace Mono.CSharp {
                        second_valid = true;
                }
 
-               public override Expression CreateExpressionTree (EmitContext ec)
-               {
-                       // A cast has no expresion tree representation
-                       return child.CreateExpressionTree (ec);
-               }
-
                public override Expression DoResolve (EmitContext ec)
                {
                        // This should never be invoked, we are born in fully
index 7a9cc02b0da75548ccf3697ecf4a0b5698685158..5e4e71a56f94dca8f05fca5d76e2a563f0e3da19 100644 (file)
@@ -1545,7 +1545,6 @@ namespace Mono.CSharp {
                protected class PredefinedOperator {
                        protected readonly Type left;
                        protected readonly Type right;
-                       Type left_lifted, right_lifted;
                        public readonly Operator OperatorsMask;
                        public Type ReturnType;
 
@@ -1575,88 +1574,19 @@ namespace Mono.CSharp {
                                this.ReturnType = return_type;
                        }
 
-                       //
-                       // CSC 2 has this behavior, it allows structs to be compared
-                       // with the null literal *outside* of a generics context and
-                       // inlines that as true or false.
-                       //
-                       Expression CreateNullConstant (Binary b, Expression expr)
-                       {
-                               // FIXME: Handle side effect constants
-                               Constant c = new BoolConstant (b.oper == Operator.Inequality, b.loc);
-
-                               if ((b.oper & Operator.EqualityMask) != 0) {
-                                       Report.Warning (472, 2, b.loc, "The result of comparing `{0}' against null is always `{1}'. " +
-                                                       "This operation is undocumented and it is temporary supported for compatibility reasons only",
-                                                       expr.GetSignatureForError (), c.AsString ());
-                               } else {
-                                       Report.Warning (464, 2, b.loc, "The result of comparing type `{0}' against null is always `{1}'",
-                                                       expr.GetSignatureForError (), c.AsString ());
-                               }
-
-                               return ReducedExpression.Create (c, b);
-                       }
-
                        public virtual Expression ConvertResult (EmitContext ec, Binary b)
                        {
                                b.type = ReturnType;
 
-                               bool lifted = b is Nullable.LiftedBinaryOperator;
-                               if (lifted) {
-                                       if (left_lifted == null)
-                                               left_lifted = GetLiftedType (ec, left, b.left);
-                                       if (right_lifted == null)
-                                               right_lifted = GetLiftedType (ec, right, b.right);
-                               }
-
-                               if (left != null) {
-                                       if (b.left is NullLiteral) {
-                                               if (!lifted)
-                                                       return CreateNullConstant (b, b.right).Resolve (ec);
-
-                                               if ((b.oper & Operator.RelationalMask) != 0)
-                                                       return CreateNullConstant (b, b.right).Resolve (ec);
+                               if (left != null)
+                                       b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location);
 
-                                               b.left = new Nullable.Null (left_lifted, b.left.Location);
-                                       } else {
-                                               b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location);
-
-                                               if (lifted)
-                                                       b.left = EmptyCast.Create (b.left, left_lifted);
-                                       }
-                               }
-
-                               if (right != null) {
-                                       if (b.right is NullLiteral) {
-                                               if (!lifted)
-                                                       return CreateNullConstant (b, b.left).Resolve (ec);
-
-                                               if ((b.oper & Operator.RelationalMask) != 0)
-                                                       return CreateNullConstant (b, b.left).Resolve (ec);
-
-                                               b.right = new Nullable.Null (right_lifted, b.right.Location);
-                                       } else {
-                                               b.right = Convert.ImplicitConversion (ec, b.right, right, b.right.Location);
-
-                                               if (lifted)
-                                                       b.right = EmptyCast.Create (b.right, right_lifted);
-                                       }
-                               }
+                               if (right != null)
+                                       b.right = Convert.ImplicitConversion (ec, b.right, right, b.right.Location);
 
                                return b;
                        }
 
-                       Type GetLiftedType (EmitContext ec, Type type, Expression expr)
-                       {
-#if GMCS_SOURCE                        
-                               TypeExpr lifted_type = new NullableType (type, expr.Location);
-                               lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
-                               if (lifted_type != null)
-                                       return lifted_type.Type;
-#endif
-                               return null;
-                       }
-
                        public bool IsPrimitiveApplicable (Type type)
                        {
                                //
@@ -1716,7 +1646,19 @@ namespace Mono.CSharp {
 
                        public override Expression ConvertResult (EmitContext ec, Binary b)
                        {
-                               base.ConvertResult (ec, b);
+                               //
+                               // Use original expression for nullable arguments
+                               //
+                               Nullable.Unwrap unwrap = b.left as Nullable.Unwrap;
+                               if (unwrap != null)
+                                       b.left = unwrap.Original;
+
+                               unwrap = b.right as Nullable.Unwrap;
+                               if (unwrap != null)
+                                       b.right = unwrap.Original;
+
+                               b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location);
+                               b.right = Convert.ImplicitConversion (ec, b.right, right, b.right.Location);
 
                                //
                                // Start a new concat expression using converted expression
@@ -1979,7 +1921,7 @@ namespace Mono.CSharp {
                                name, left, right);
                }
                
-               protected void Error_OperatorCannotBeApplied ()
+               protected void Error_OperatorCannotBeApplied (Expression left, Expression right)
                {
                        string l, r;
                        // TODO: This should be handled as Type of method group in CSharpName
@@ -2101,7 +2043,7 @@ namespace Mono.CSharp {
                                        break;
                                return left;
                        }
-                       Error_OperatorCannotBeApplied ();
+                       Error_OperatorCannotBeApplied (this.left, this.right);
                        return null;
                }
 
@@ -2362,7 +2304,7 @@ namespace Mono.CSharp {
                                        ((lc != null && lc.IsDefaultValue) || (rc != null && rc.IsDefaultValue))) {
 
                                        if ((ResolveOperator (ec)) == null) {
-                                               Error_OperatorCannotBeApplied ();
+                                               Error_OperatorCannotBeApplied (left, right);
                                                return null;
                                        }
 
@@ -2381,14 +2323,6 @@ namespace Mono.CSharp {
                                }
                        }
 
-#if GMCS_SOURCE
-                       if ((left is NullLiteral || left.Type.IsValueType) &&
-                           (right is NullLiteral || right.Type.IsValueType) &&
-                           !(left is NullLiteral && right is NullLiteral) &&
-                           (TypeManager.IsNullableType (left.Type) || TypeManager.IsNullableType (right.Type)))
-                               return new Nullable.LiftedBinaryOperator (oper, left, right, loc).Resolve (ec);
-#endif
-
                        // Comparison warnings
                        if ((oper & Operator.ComparisonMask) != 0) {
                                if (left.Equals (right)) {
@@ -2398,9 +2332,19 @@ namespace Mono.CSharp {
                                CheckUselessComparison (rc, left.Type);
                        }
 
+                       if ((TypeManager.IsNullableType (left.Type) || TypeManager.IsNullableType (right.Type) ||
+                               (left is NullLiteral && right.Type.IsValueType) || (right is NullLiteral && left.Type.IsValueType)) &&
+                               !(this is Nullable.LiftedBinaryOperator))
+                               return new Nullable.LiftedBinaryOperator (oper, left, right, loc).Resolve (ec);
+
+                       return DoResolveCore (ec, left, right);
+               }
+
+               protected Expression DoResolveCore (EmitContext ec, Expression left_orig, Expression right_orig)
+               {
                        Expression expr = ResolveOperator (ec);
                        if (expr == null)
-                               Error_OperatorCannotBeApplied ();
+                               Error_OperatorCannotBeApplied (left_orig, right_orig);
 
                        if (left == null || right == null)
                                throw new InternalErrorException ("Invalid conversion");
@@ -2582,6 +2526,9 @@ namespace Mono.CSharp {
                                if (l == TypeManager.anonymous_method_type)
                                        return null;
 
+                               if (TypeManager.IsValueType (l))
+                                       return null;
+
                                return this;
                        }
 
index d1998688c9641171e84acbb8e4a2dbf2731d11b7..61892ae1bce86a855c91022aa576cadfc5406cd3 100644 (file)
@@ -194,6 +194,12 @@ namespace Mono.CSharp {
                                Unwrap uw = obj as Unwrap;
                                return uw != null && expr.Equals (uw.expr);
                        }
+
+                       public Expression Original {
+                               get {
+                                       return expr;
+                               }
+                       }
                        
                        public override int GetHashCode ()
                        {
@@ -319,18 +325,33 @@ namespace Mono.CSharp {
                }
 
                //
-               // Represents null value converted to nullable type
+               // Represents null literal lifted to nullable type
                //
-               public class Null : EmptyConstantCast, IMemoryLocation
+               public class LiftedNull : EmptyConstantCast, IMemoryLocation
                {
-                       public Null (Type target_type, Location loc)
-                               : base (new NullLiteral (loc), target_type)
+                       private LiftedNull (Type nullable_type, Location loc)
+                               : base (new NullLiteral (loc), nullable_type)
                        {
                                eclass = ExprClass.Value;
                        }
 
+                       public static Constant Create (Type nullable, Location loc)
+                       {
+                               return new LiftedNull (nullable, loc);
+                       }
+
+                       public override Expression CreateExpressionTree (EmitContext ec)
+                       {
+                               ArrayList args = new ArrayList (2);
+                               args.Add (new Argument (this));
+                               args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
+
+                               return CreateExpressionFactoryCall ("Constant", args);
+                       }
+
                        public override void Emit (EmitContext ec)
                        {
+                               // TODO: generate less temporary variables
                                LocalTemporary value_target = new LocalTemporary (type);
 
                                value_target.AddressOf (ec, AddressOp.Store);
@@ -338,12 +359,6 @@ namespace Mono.CSharp {
                                value_target.Emit (ec);
                        }
 
-                       public override bool IsNull {
-                               get {
-                                       return true;
-                               }
-                       }                       
-
                        public void AddressOf (EmitContext ec, AddressOp Mode)
                        {
                                LocalTemporary value_target = new LocalTemporary (type);
@@ -393,9 +408,7 @@ namespace Mono.CSharp {
                                if (wrap == null)
                                        return null;
 
-                               null_value = new Null (wrap.Type, loc).Resolve (ec);
-                               if (null_value == null)
-                                       return null;
+                               null_value = LiftedNull.Create (wrap.Type, loc);
 
                                type = wrap.Type;
                                eclass = ExprClass.Value;
@@ -477,6 +490,8 @@ namespace Mono.CSharp {
                public class LiftedBinaryOperator : Binary
                {
                        Unwrap left_unwrap, right_unwrap;
+                       bool left_null_lifted, right_null_lifted;
+                       Expression left_orig, right_orig;
 
                        public LiftedBinaryOperator (Binary.Operator op, Expression left, Expression right,
                                                     Location loc)
@@ -485,6 +500,28 @@ namespace Mono.CSharp {
                                this.loc = loc;
                        }
 
+                       //
+                       // CSC 2 has this behavior, it allows structs to be compared
+                       // with the null literal *outside* of a generics context and
+                       // inlines that as true or false.
+                       //
+                       Expression CreateNullConstant (Expression expr)
+                       {
+                               // FIXME: Handle side effect constants
+                               Constant c = new BoolConstant (Oper == Operator.Inequality, loc);
+
+                               if ((Oper & Operator.EqualityMask) != 0) {
+                                       Report.Warning (472, 2, loc, "The result of comparing `{0}' against null is always `{1}'. " +
+                                                       "This operation is undocumented and it is temporary supported for compatibility reasons only",
+                                                       expr.GetSignatureForError (), c.AsString ());
+                               } else {
+                                       Report.Warning (464, 2, loc, "The result of comparing type `{0}' against null is always `{1}'",
+                                                       expr.GetSignatureForError (), c.AsString ());
+                               }
+
+                               return ReducedExpression.Create (c, this);
+                       }
+
                        public override Expression DoResolve (EmitContext ec)
                        {
                                if (eclass != ExprClass.Invalid)
@@ -493,27 +530,37 @@ namespace Mono.CSharp {
                                // TODO: How does it work with use-operators?
                                if ((Oper == Binary.Operator.LogicalAnd) ||
                                    (Oper == Binary.Operator.LogicalOr)) {
-                                       Error_OperatorCannotBeApplied ();
+                                       Error_OperatorCannotBeApplied (left, right);
                                        return null;
                                }
 
+                               left_orig = left;
                                if (TypeManager.IsNullableType (left.Type)) {
                                        left = left_unwrap = Unwrap.Create (left, ec);
                                        if (left == null)
                                                return null;
                                }
 
+                               right_orig = right;
                                if (TypeManager.IsNullableType (right.Type)) {
                                        right = right_unwrap = Unwrap.Create (right, ec);
                                        if (right == null)
                                                return null;
                                }
 
-                               if ((Oper & Operator.ComparisonMask) != 0)
-                                       return base.DoResolve (ec);
+                               if (left is NullLiteral) {
+                                       left = right;
+                                       left_null_lifted = true;
+                               }
 
-                               Expression expr = base.DoResolve (ec);
-                               if (expr != this)
+                               if (right is NullLiteral) {
+                                       right = left;
+                                       right_null_lifted = true;
+                               }
+
+                               eclass = ExprClass.Value;
+                               Expression expr = DoResolveCore (ec, left_orig, right_orig);
+                               if (expr != this || (Oper & Operator.ComparisonMask) != 0)
                                        return expr;
 
                                TypeExpr target_type = new NullableType (type, loc);
@@ -522,8 +569,7 @@ namespace Mono.CSharp {
                                        return null;
 
                                type = target_type.Type;
-                               eclass = ExprClass.Value;
-                               return expr;
+                               return this;
                        }
 
                        void EmitBitwiseBoolean (EmitContext ec)
@@ -673,8 +719,7 @@ namespace Mono.CSharp {
                                if (right_unwrap != null)
                                        right_unwrap.Store (ec);
 
-                               if ((Oper & Operator.BitwiseMask) != 0 && left_unwrap != null && right_unwrap != null &&
-                                       left_unwrap.Type == TypeManager.bool_type && right_unwrap.Type == TypeManager.bool_type) {
+                               if (IsBitwiseBoolean) {
                                        EmitBitwiseBoolean (ec);
                                        return;
                                }
@@ -706,23 +751,83 @@ namespace Mono.CSharp {
 
                                base.EmitOperator (ec);
 
-                               // TODO: this is LiftedWrap
-                               NullableInfo info = new NullableInfo (type);
-                               ig.Emit (OpCodes.Newobj, info.Constructor);
-
                                ig.Emit (OpCodes.Br_S, end_label);
-
                                ig.MarkLabel (is_null_label);
-                               new Null (type, loc).Emit (ec);
+                               LiftedNull.Create (type, loc).Emit (ec);
 
                                ig.MarkLabel (end_label);
                        }
 
+                       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;
+                               }
+                       }
+
+                       Expression LiftResult (EmitContext ec, Expression res_expr)
+                       {
+                               TypeExpr lifted_type;
+
+                               //
+                               // Avoid double conversion
+                               //
+                               if (left_unwrap == null || left_null_lifted || !TypeManager.IsEqual (left_unwrap.Type, left.Type)) {
+                                       lifted_type = new NullableType (left.Type, loc);
+                                       lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
+                                       if (lifted_type == null)
+                                               return null;
+
+                                       left = EmptyCast.Create (left, lifted_type.Type);
+                               }
+
+                               if (right_unwrap == null || right_null_lifted || !TypeManager.IsEqual (right_unwrap.Type, right.Type)) {
+                                       lifted_type = new NullableType (right.Type, loc);
+                                       lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
+                                       if (lifted_type == null)
+                                               return null;
+
+                                       right = EmptyCast.Create (right, lifted_type.Type);
+                               }
+
+                               if (left_null_lifted) {
+                                       left = LiftedNull.Create (right.Type, left.Location);
+
+                                       //
+                                       // Value types and null comparison
+                                       //
+                                       if (right_unwrap == null || (Oper & Operator.RelationalMask) != 0)
+                                               return CreateNullConstant (right_orig).Resolve (ec);
+                               }
+
+                               if (right_null_lifted) {
+                                       right = LiftedNull.Create (left.Type, right.Location);
+
+                                       //
+                                       // Value types and null comparison
+                                       //
+                                       if (left_unwrap == null || (Oper & Operator.RelationalMask) != 0)
+                                               return CreateNullConstant (left_orig).Resolve (ec);
+                               }
+
+                               // TODO: Handle bitwise bool 
+                               if ((Oper & Operator.ComparisonMask) != 0 || IsBitwiseBoolean)
+                                       return res_expr;
+
+                               lifted_type = new NullableType (res_expr.Type, loc);
+                               lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
+                               if (lifted_type == null)
+                                       return null;
+
+                               return new LiftedWrap (res_expr, lifted_type.Type).Resolve (ec);
+                       }
+
                        protected override Expression ResolveOperatorPredefined (EmitContext ec, Binary.PredefinedOperator [] operators, bool primitives_only)
                        {
                                Expression e = base.ResolveOperatorPredefined (ec, operators, primitives_only);
-                               if (e != null)
-                                       return e;
+
+                               if (e == this)
+                                       return LiftResult (ec, e);
 
                                //
                                // 7.9.9 Equality operators and null
@@ -731,19 +836,12 @@ namespace Mono.CSharp {
                                // the other to be the null literal, even if no predefined or user-defined operator
                                // (in unlifted or lifted form) exists for the operation.
                                //
-                               if ((Oper & Operator.EqualityMask) != 0) {
-                                       if (left is NullLiteral) {
-                                               left = WrapNullExpression (ec, right, left);
-                                               return this;
-                                       }
-
-                                       if (right is NullLiteral) {
-                                               right = WrapNullExpression (ec, left, right);
-                                               return this;
-                                       }
+                               if (e == null && (Oper & Operator.EqualityMask) != 0) {
+                                       if ((left_null_lifted && right_unwrap != null) || (right_null_lifted && left_unwrap != null))
+                                               return LiftResult (ec, this);
                                }
 
-                               return null;
+                               return e;
                        }
 
                        protected override Expression ResolveUserOperator (EmitContext ec, Type l, Type r)
@@ -751,27 +849,8 @@ namespace Mono.CSharp {
                                Expression expr = base.ResolveUserOperator (ec, l, r);
                                if (expr == null)
                                        return null;
-                               
-                               // TODO: Handle bitwise bool 
-                               if ((Oper & Operator.ComparisonMask) != 0)
-                                       return expr;
-
-                               TypeExpr target_type = new NullableType (expr.Type, loc);
-                               target_type = target_type.ResolveAsTypeTerminal (ec, false);
-                               if (target_type == null)
-                                       return null;
 
-                               return new LiftedWrap (expr, target_type.Type).Resolve (ec);
-                       }
-
-                       Expression WrapNullExpression (EmitContext ec, Expression expr, Expression null_expr)
-                       {
-                               TypeExpr lifted_type = new NullableType (expr.Type, expr.Location);
-                               lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
-                               if (lifted_type == null)
-                                       return null;
-
-                               return new Null (lifted_type.Type, null_expr.Location);
+                               return LiftResult (ec, expr);
                        }
                }
 
@@ -932,11 +1011,8 @@ namespace Mono.CSharp {
                                if (underlying == null)
                                        return null;
 
-                               null_value = new Null (expr.Type, loc).Resolve (ec);
-                               if (null_value == null)
-                                       return null;
-
                                type = expr.Type;
+                               null_value = LiftedNull.Create (type, loc);
                                return this;
                        }