Merge pull request #2536 from lambdageek/monoerror-mono_field_get_object
[mono.git] / mcs / mcs / nullable.cs
index 8621360a52f97b11b09f74ead6311b452982a322..dbfc6b0648da0b095787be343dc632f572e14ffe 100644 (file)
@@ -33,7 +33,7 @@ namespace Mono.CSharp.Nullable
                        this.loc = loc;
                }
 
-               public override TypeSpec ResolveAsType (IMemberContext ec)
+               public override TypeSpec ResolveAsType (IMemberContext ec, bool allowUnboundTypeArguments = false)
                {
                        eclass = ExprClass.Type;
 
@@ -435,6 +435,12 @@ namespace Mono.CSharp.Nullable
                {
                }
 
+               public override bool IsNull {
+                       get {
+                               return expr.IsNull;
+                       }
+               }
+
                public override bool ContainsEmitWithAwait ()
                {
                        return unwrap.ContainsEmitWithAwait ();
@@ -789,7 +795,7 @@ namespace Mono.CSharp.Nullable
                        //
                        // Both operands are bool? types
                        //
-                       if (UnwrapLeft != null && UnwrapRight != null) {
+                       if ((UnwrapLeft != null && !Left.IsNull) && (UnwrapRight != null && !Right.IsNull)) {
                                if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) {
                                        Left = Left.EmitToField (ec);
                                        Right = Right.EmitToField (ec);
@@ -855,6 +861,8 @@ namespace Mono.CSharp.Nullable
                                        LiftedNull.Create (type, loc).Emit (ec);
                                } else {
                                        Left.Emit (ec);
+                                       UnwrapRight.Store (ec);
+
                                        ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right);
 
                                        ec.EmitInt (or ? 1 : 0);
@@ -863,7 +871,7 @@ namespace Mono.CSharp.Nullable
                                        ec.Emit (OpCodes.Br_S, end_label);
 
                                        ec.MarkLabel (load_right);
-                                       UnwrapRight.Original.Emit (ec);
+                                       UnwrapRight.Load (ec);
                                }
                        } else {
                                //
@@ -891,14 +899,14 @@ namespace Mono.CSharp.Nullable
                                        LiftedNull.Create (type, loc).Emit (ec);
                                } else {
                                        Right.Emit (ec);
-                                       ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right);
+                                       ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_left);
 
                                        ec.EmitInt (or ? 1 : 0);
                                        ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
 
                                        ec.Emit (OpCodes.Br_S, end_label);
 
-                                       ec.MarkLabel (load_right);
+                                       ec.MarkLabel (load_left);
 
                                        UnwrapLeft.Load (ec);
                                }
@@ -1067,6 +1075,7 @@ namespace Mono.CSharp.Nullable
        {
                Expression left, right;
                Unwrap unwrap;
+               bool user_conversion_left;
 
                public NullCoalescingOperator (Expression left, Expression right)
                {
@@ -1135,24 +1144,35 @@ namespace Mono.CSharp.Nullable
                                if (right.IsNull)
                                        return ReducedExpression.Create (left, this);
 
-                               if (Convert.ImplicitConversionExists (ec, right, unwrap.Type)) {
-                                       left = unwrap;
-                                       ltype = left.Type;
+                               Expression conv;
+                               if (right.Type.IsNullableType) {
+                                       conv = right.Type == ltype ? right : Convert.ImplicitNulableConversion (ec, right, ltype);
+                                       if (conv != null) {
+                                               right = conv;
+                                               type = ltype;
+                                               return this;
+                                       }
+                               } else {
+                                       conv = Convert.ImplicitConversion (ec, right, unwrap.Type, loc);
+                                       if (conv != null) {
+                                               left = unwrap;
+                                               ltype = left.Type;
 
-                                       //
-                                       // If right is a dynamic expression, the result type is dynamic
-                                       //
-                                       if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
-                                               type = right.Type;
+                                               //
+                                               // If right is a dynamic expression, the result type is dynamic
+                                               //
+                                               if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
+                                                       type = right.Type;
+
+                                                       // Need to box underlying value type
+                                                       left = Convert.ImplicitBoxingConversion (left, ltype, type);
+                                                       return this;
+                                               }
 
-                                               // Need to box underlying value type
-                                               left = Convert.ImplicitBoxingConversion (left, ltype, type);
+                                               right = conv;
+                                               type = ltype;
                                                return this;
                                        }
-
-                                       right = Convert.ImplicitConversion (ec, right, ltype, loc);
-                                       type = ltype;
-                                       return this;
                                }
                        } else if (TypeSpec.IsReferenceType (ltype)) {
                                if (Convert.ImplicitConversionExists (ec, right, ltype)) {
@@ -1203,6 +1223,7 @@ namespace Mono.CSharp.Nullable
                                return ReducedExpression.Create (right, this, false).Resolve (ec);
 
                        left = Convert.ImplicitConversion (ec, unwrap ?? left, rtype, loc);
+                       user_conversion_left = left is UserCast;
                        type = rtype;
                        return this;
                }
@@ -1262,16 +1283,56 @@ namespace Mono.CSharp.Nullable
                                return;
                        }
 
-                       left.Emit (ec);
-                       ec.Emit (OpCodes.Dup);
+                       //
+                       // Null check is done on original expression not after expression is converted to
+                       // result type. This is in most cases same but when user conversion is involved
+                       // we can end up in situation when user operator does the null handling which is
+                       // not what the operator is supposed to do.
+                       // There is tricky case where cast of left expression is meant to be cast of
+                       // whole source expression (null check is done on it) and cast from right-to-left
+                       // conversion needs to do null check on unconverted source expression.
+                       //
+                       if (user_conversion_left) {
+                               var op_expr = (UserCast) left;
+
+                               op_expr.Source.Emit (ec);
+                               LocalTemporary temp;
+
+                               // TODO: More load kinds can be special cased
+                               if (!(op_expr.Source is VariableReference)) {
+                                       temp = new LocalTemporary (op_expr.Source.Type);
+                                       temp.Store (ec);
+                                       temp.Emit (ec);
+                                       op_expr.Source = temp;
+                               } else {
+                                       temp = null;
+                               }
 
-                       // Only to make verifier happy
-                       if (left.Type.IsGenericParameter)
-                               ec.Emit (OpCodes.Box, left.Type);
+                               var right_label = ec.DefineLabel ();
+                               ec.Emit (OpCodes.Brfalse_S, right_label);
+                               left.Emit (ec);
+                               ec.Emit (OpCodes.Br, end_label);
+                               ec.MarkLabel (right_label);
 
-                       ec.Emit (OpCodes.Brtrue, end_label);
+                               if (temp != null)
+                                       temp.Release (ec);
+                       } else {
+                               //
+                               // Common case where expression is not modified before null check and
+                               // we generate better/smaller code
+                               //
+                               left.Emit (ec);
+                               ec.Emit (OpCodes.Dup);
+
+                               // Only to make verifier happy
+                               if (left.Type.IsGenericParameter)
+                                       ec.Emit (OpCodes.Box, left.Type);
+
+                               ec.Emit (OpCodes.Brtrue, end_label);
+
+                               ec.Emit (OpCodes.Pop);
+                       }
 
-                       ec.Emit (OpCodes.Pop);
                        right.Emit (ec);
 
                        ec.MarkLabel (end_label);