Bug 15574. XML deserialization recursion: add array type to known_types?
[mono.git] / mcs / mcs / expression.cs
index a5c6653cbf9fe0f5b19471d92dfda278444728ec..05abac7fbc3703f4c178919c393977e6d88bdc98 100644 (file)
@@ -80,7 +80,12 @@ namespace Mono.CSharp
                public override void Emit (EmitContext ec)
                {
                        var call = new CallEmitter ();
-                       call.EmitPredefined (ec, oper, arguments);
+                       call.EmitPredefined (ec, oper, arguments, loc);
+               }
+
+               public override void FlowAnalysis (FlowAnalysisContext fc)
+               {
+                       arguments.FlowAnalysis (fc);
                }
 
                public override SLE.Expression MakeExpression (BuilderContext ctx)
@@ -95,10 +100,10 @@ namespace Mono.CSharp
 
        public class ParenthesizedExpression : ShimExpression
        {
-               public ParenthesizedExpression (Expression expr)
+               public ParenthesizedExpression (Expression expr, Location loc)
                        : base (expr)
                {
-                       loc = expr.Location;
+                       this.loc = loc;
                }
 
                protected override Expression DoResolve (ResolveContext ec)
@@ -572,6 +577,19 @@ namespace Mono.CSharp
                        Expr.EmitSideEffect (ec);
                }
 
+               public override void FlowAnalysis (FlowAnalysisContext fc)
+               {
+                       if (Oper == Operator.AddressOf) {
+                               var vr = Expr as VariableReference;
+                               if (vr != null && vr.VariableInfo != null)
+                                       fc.SetVariableAssigned (vr.VariableInfo);
+
+                               return;
+                       }
+
+                       Expr.FlowAnalysis (fc);
+               }
+
                //
                // Converts operator to System.Linq.Expressions.ExpressionType enum name
                //
@@ -824,6 +842,12 @@ namespace Mono.CSharp
                        get { return true; }
                }
 
+               public override Location StartLocation {
+                       get {
+                               return expr.StartLocation;
+                       }
+               }
+
                protected override void CloneTo (CloneContext clonectx, Expression t)
                {
                        Indirection target = (Indirection) t;
@@ -1043,6 +1067,12 @@ namespace Mono.CSharp
                        }
                }
 
+               public override Location StartLocation {
+                       get {
+                               return (mode & Mode.IsPost) != 0 ? expr.Location : loc;
+                       }
+               }
+
                public override bool ContainsEmitWithAwait ()
                {
                        return expr.ContainsEmitWithAwait ();
@@ -1262,6 +1292,11 @@ namespace Mono.CSharp
                        EmitCode (ec, false);
                }
 
+               public override void FlowAnalysis (FlowAnalysisContext fc)
+               {
+                       expr.FlowAnalysis (fc);
+               }
+
                //
                // Converts operator to System.Linq.Expressions.ExpressionType enum name
                //
@@ -1307,7 +1342,7 @@ namespace Mono.CSharp
                protected Expression expr;
                protected TypeSpec probe_type_expr;
                
-               public Probe (Expression expr, Expression probe_type, Location l)
+               protected Probe (Expression expr, Expression probe_type, Location l)
                {
                        ProbeType = probe_type;
                        loc = l;
@@ -1355,6 +1390,11 @@ namespace Mono.CSharp
                        return this;
                }
 
+               public override void FlowAnalysis (FlowAnalysisContext fc)
+               {
+                       expr.FlowAnalysis (fc);
+               }
+
                protected abstract string OperatorName { get; }
 
                protected override void CloneTo (CloneContext clonectx, Expression t)
@@ -1425,10 +1465,10 @@ namespace Mono.CSharp
                {
                        if (result)
                                ec.Report.Warning (183, 1, loc, "The given expression is always of the provided (`{0}') type",
-                                       TypeManager.CSharpName (probe_type_expr));
+                                       probe_type_expr.GetSignatureForError ());
                        else
                                ec.Report.Warning (184, 1, loc, "The given expression is never of the provided (`{0}') type",
-                                       TypeManager.CSharpName (probe_type_expr));
+                                       probe_type_expr.GetSignatureForError ());
 
                        return ReducedExpression.Create (new BoolConstant (ec.BuiltinTypes, result, loc), this);
                }
@@ -1493,9 +1533,16 @@ namespace Mono.CSharp
                                //
                                if (Convert.ExplicitReferenceConversionExists (d, t))
                                        return this;
+
+                               //
+                               // open generic type
+                               //
+                               if (d is InflatedTypeSpec && InflatedTypeSpec.ContainsTypeParameter (d))
+                                       return this;
                        } else {
-                               if (TypeManager.IsGenericParameter (t))
-                                       return ResolveGenericParameter (ec, d, (TypeParameterSpec) t);
+                               var tps = t as TypeParameterSpec;
+                               if (tps != null)
+                                       return ResolveGenericParameter (ec, d, tps);
 
                                if (t.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
                                        ec.Report.Warning (1981, 3, loc,
@@ -1517,11 +1564,17 @@ namespace Mono.CSharp
                                        }
                                } else {
                                        if (Convert.ImplicitReferenceConversionExists (d, t)) {
+                                               var c = expr as Constant;
+                                               if (c != null)
+                                                       return CreateConstantResult (ec, !c.IsNull);
+
                                                //
                                                // Do not optimize for imported type
                                                //
-                                               if (d.MemberDefinition.IsImported && d.BuiltinType != BuiltinTypeSpec.Type.None)
+                                               if (d.MemberDefinition.IsImported && d.BuiltinType != BuiltinTypeSpec.Type.None &&
+                                                       d.MemberDefinition.DeclaringAssembly != t.MemberDefinition.DeclaringAssembly) {
                                                        return this;
+                                               }
                                                
                                                //
                                                // Turn is check into simple null check for implicitly convertible reference types
@@ -1531,9 +1584,14 @@ namespace Mono.CSharp
                                                        this).Resolve (ec);
                                        }
 
-                                       if (Convert.ExplicitReferenceConversionExists (d, t)) {
+                                       if (Convert.ExplicitReferenceConversionExists (d, t))
+                                               return this;
+
+                                       //
+                                       // open generic type
+                                       //
+                                       if ((d is InflatedTypeSpec || d.IsArray) && InflatedTypeSpec.ContainsTypeParameter (d))
                                                return this;
-                                       }
                                }
                        }
 
@@ -1547,8 +1605,8 @@ namespace Mono.CSharp
                                        return CreateConstantResult (ec, false);
                        }
 
-                       if (TypeManager.IsGenericParameter (expr.Type)) {
-                               if (expr.Type == d && TypeSpec.IsValueType (t))
+                       if (expr.Type.IsGenericParameter) {
+                               if (expr.Type == d && TypeSpec.IsValueType (t) && TypeSpec.IsValueType (d))
                                        return CreateConstantResult (ec, true);
 
                                expr = new BoxedCast (expr, d);
@@ -1618,7 +1676,7 @@ namespace Mono.CSharp
                                } else {
                                        ec.Report.Error (77, loc,
                                                "The `as' operator cannot be used with a non-nullable value type `{0}'",
-                                               TypeManager.CSharpName (type));
+                                               type.GetSignatureForError ());
                                }
                                return null;
                        }
@@ -1650,8 +1708,10 @@ namespace Mono.CSharp
                                return this;
                        }
 
-                       ec.Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion",
-                               TypeManager.CSharpName (etype), TypeManager.CSharpName (type));
+                       if (etype != InternalType.ErrorType) {
+                               ec.Report.Error (39, loc, "Cannot convert type `{0}' to `{1}' via a built-in conversion",
+                                       etype.GetSignatureForError (), type.GetSignatureForError ());
+                       }
 
                        return null;
                }
@@ -1690,7 +1750,7 @@ namespace Mono.CSharp
                                return null;
 
                        if (type.IsStatic) {
-                               ec.Report.Error (716, loc, "Cannot convert to static type `{0}'", TypeManager.CSharpName (type));
+                               ec.Report.Error (716, loc, "Cannot convert to static type `{0}'", type.GetSignatureForError ());
                                return null;
                        }
 
@@ -1856,6 +1916,8 @@ namespace Mono.CSharp
                {
                        protected readonly TypeSpec left;
                        protected readonly TypeSpec right;
+                       protected readonly TypeSpec left_unwrap;
+                       protected readonly TypeSpec right_unwrap;
                        public readonly Operator OperatorsMask;
                        public TypeSpec ReturnType;
 
@@ -1879,45 +1941,213 @@ namespace Mono.CSharp
                                if ((op_mask & Operator.ValuesOnlyMask) != 0)
                                        throw new InternalErrorException ("Only masked values can be used");
 
+                               if ((op_mask & Operator.NullableMask) != 0) {
+                                       left_unwrap = Nullable.NullableInfo.GetUnderlyingType (ltype);
+                                       right_unwrap = Nullable.NullableInfo.GetUnderlyingType (rtype);
+                               } else {
+                                       left_unwrap = ltype;
+                                       right_unwrap = rtype;
+                               }
+
                                this.left = ltype;
                                this.right = rtype;
                                this.OperatorsMask = op_mask;
                                this.ReturnType = return_type;
                        }
 
-                       public virtual Expression ConvertResult (ResolveContext ec, Binary b)
+                       public bool IsLifted {
+                               get {
+                                       return (OperatorsMask & Operator.NullableMask) != 0;
+                               }
+                       }
+
+                       public virtual Expression ConvertResult (ResolveContext rc, Binary b)
                        {
+                               Constant c;
+
+                               var left_expr = b.left;
+                               var right_expr = b.right;
+
                                b.type = ReturnType;
 
-                               b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location);
-                               b.right = Convert.ImplicitConversion (ec, b.right, right, b.right.Location);
+                               if (IsLifted) {
+                                       if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) {
+                                               b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location);
+                                               b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location);
+                                       }
+
+                                       if (right_expr.IsNull) {
+                                               if ((b.oper & Operator.EqualityMask) != 0) {
+                                                       if (!left_expr.Type.IsNullableType && BuiltinTypeSpec.IsPrimitiveType (left_expr.Type))
+                                                               return b.CreateLiftedValueTypeResult (rc, left_expr.Type);
+                                               } else if ((b.oper & Operator.BitwiseMask) != 0) {
+                                                       if (left_unwrap.BuiltinType != BuiltinTypeSpec.Type.Bool)
+                                                               return Nullable.LiftedNull.CreateFromExpression (rc, b);
+                                               } else {
+                                                       b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location);
+                                                       b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location);
+
+                                                       if ((b.Oper & (Operator.ArithmeticMask | Operator.ShiftMask)) != 0)
+                                                               return Nullable.LiftedNull.CreateFromExpression (rc, b);
+
+                                                       return b.CreateLiftedValueTypeResult (rc, left);
+                                               }
+                                       } else if (left_expr.IsNull) {
+                                               if ((b.oper & Operator.EqualityMask) != 0) {
+                                                       if (!right_expr.Type.IsNullableType && BuiltinTypeSpec.IsPrimitiveType (right_expr.Type))
+                                                               return b.CreateLiftedValueTypeResult (rc, right_expr.Type);
+                                               } else if ((b.oper & Operator.BitwiseMask) != 0) {
+                                                       if (right_unwrap.BuiltinType != BuiltinTypeSpec.Type.Bool)
+                                                               return Nullable.LiftedNull.CreateFromExpression (rc, b);
+                                               } else {
+                                                       b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location);
+                                                       b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location);
+
+                                                       if ((b.Oper & (Operator.ArithmeticMask | Operator.ShiftMask)) != 0)
+                                                               return Nullable.LiftedNull.CreateFromExpression (rc, b);
+
+                                                       return b.CreateLiftedValueTypeResult (rc, right);
+                                               }
+                                       }
+                               }
 
                                //
                                // A user operators does not support multiple user conversions, but decimal type
                                // is considered to be predefined type therefore we apply predefined operators rules
                                // and then look for decimal user-operator implementation
                                //
-                               if (left.BuiltinType == BuiltinTypeSpec.Type.Decimal)
-                                       return b.ResolveUserOperator (ec, b.left, b.right);
+                               if (left.BuiltinType == BuiltinTypeSpec.Type.Decimal) {
+                                       b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location);
+                                       b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location);
+
+                                       return b.ResolveUserOperator (rc, b.left, b.right);
+                               }
 
-                               var c = b.right as Constant;
+                               c = right_expr as Constant;
                                if (c != null) {
-                                       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 (c.IsDefaultValue) {
+                                               //
+                                               // Optimizes
+                                               // 
+                                               // (expr + 0) to expr
+                                               // (expr - 0) to expr
+                                               // (bool? | false) to bool?
+                                               //
+                                               if (b.oper == Operator.Addition || b.oper == Operator.Subtraction ||
+                                                       (b.oper == Operator.BitwiseOr && left_unwrap.BuiltinType == BuiltinTypeSpec.Type.Bool && c is BoolConstant)) {
+                                                       b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location);
+                                                       return ReducedExpression.Create (b.left, b).Resolve (rc);
+                                               }
+
+                                               //
+                                               // Optimizes (value &/&& 0) to 0
+                                               //
+                                               if ((b.oper == Operator.BitwiseAnd || b.oper == Operator.LogicalAnd) && !IsLifted) {
+                                                       Constant side_effect = new SideEffectConstant (c, b.left, c.Location);
+                                                       return ReducedExpression.Create (side_effect, b);
+                                               }
+                                       } else {
+                                               //
+                                               // Optimizes (bool? & true) to bool?
+                                               //
+                                               if (IsLifted && left_unwrap.BuiltinType == BuiltinTypeSpec.Type.Bool && b.oper == Operator.BitwiseAnd) {
+                                                       return ReducedExpression.Create (b.left, b).Resolve (rc);
+                                               }
+                                       }
+
                                        if ((b.oper == Operator.Multiply || b.oper == Operator.Division) && c.IsOneInteger)
-                                               return ReducedExpression.Create (b.left, b).Resolve (ec);
-                                       return b;
+                                               return ReducedExpression.Create (b.left, b).Resolve (rc);
+
+                                       if ((b.oper & Operator.ShiftMask) != 0 && c is IntConstant) {
+                                               b.right = new IntConstant (rc.BuiltinTypes, ((IntConstant) c).Value & GetShiftMask (left_unwrap), b.right.Location);
+                                       }
                                }
 
                                c = b.left as Constant;
                                if (c != null) {
-                                       if (c.IsDefaultValue && (b.oper == Operator.Addition || (b.oper == Operator.BitwiseOr && !(b is Nullable.LiftedBinaryOperator))))
-                                               return ReducedExpression.Create (b.right, b).Resolve (ec);
+                                       if (c.IsDefaultValue) {
+                                               //
+                                               // Optimizes
+                                               // 
+                                               // (0 + expr) to expr
+                                               // (false | bool?) to bool?
+                                               //
+                                               if (b.oper == Operator.Addition ||
+                                                       (b.oper == Operator.BitwiseOr && right_unwrap.BuiltinType == BuiltinTypeSpec.Type.Bool && c is BoolConstant)) {
+                                                       b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location);
+                                                       return ReducedExpression.Create (b.right, b).Resolve (rc);
+                                               }
+
+                                               //
+                                               // Optimizes (false && expr) to false
+                                               //
+                                               if (b.oper == Operator.LogicalAnd && c.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) {
+                                                       // No rhs side-effects
+                                                       Expression.Warning_UnreachableExpression (rc, b.right.StartLocation);
+                                                       return ReducedExpression.Create (c, b);
+                                               }
+
+                                               //
+                                               // Optimizes (0 & value) to 0
+                                               //
+                                               if (b.oper == Operator.BitwiseAnd && !IsLifted) {
+                                                       Constant side_effect = new SideEffectConstant (c, b.right, c.Location);
+                                                       return ReducedExpression.Create (side_effect, b);
+                                               }
+                                       } else {
+                                               //
+                                               // Optimizes (true & bool?) to bool?
+                                               //
+                                               if (IsLifted && left_unwrap.BuiltinType == BuiltinTypeSpec.Type.Bool && b.oper == Operator.BitwiseAnd) {
+                                                       return ReducedExpression.Create (b.right, b).Resolve (rc);
+                                               }
+
+                                               //
+                                               // Optimizes (true || expr) to true
+                                               //
+                                               if (b.oper == Operator.LogicalOr && c.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) {
+                                                       // No rhs side-effects
+                                                       Expression.Warning_UnreachableExpression (rc, b.right.StartLocation);
+                                                       return ReducedExpression.Create (c, b);
+                                               }
+                                       }
+
                                        if (b.oper == Operator.Multiply && c.IsOneInteger)
-                                               return ReducedExpression.Create (b.right, b).Resolve (ec);
-                                       return b;
+                                               return ReducedExpression.Create (b.right, b).Resolve (rc);
+                               }
+
+                               if (IsLifted) {
+                                       var lifted = new Nullable.LiftedBinaryOperator (b);
+
+                                       TypeSpec ltype, rtype;
+                                       if (b.left.Type.IsNullableType) {
+                                               lifted.UnwrapLeft = new Nullable.Unwrap (b.left);
+                                               ltype = left_unwrap;
+                                       } else {
+                                               ltype = left;
+                                       }
+
+                                       if (b.right.Type.IsNullableType) {
+                                               lifted.UnwrapRight = new Nullable.Unwrap (b.right);
+                                               rtype = right_unwrap;
+                                       } else {
+                                               rtype = right;
+                                       }
+
+                                       lifted.Left = b.left.IsNull ?
+                                               b.left :
+                                               Convert.ImplicitConversion (rc, lifted.UnwrapLeft ?? b.left, ltype, b.left.Location);
+
+                                       lifted.Right = b.right.IsNull ?
+                                               b.right :
+                                               Convert.ImplicitConversion (rc, lifted.UnwrapRight ?? b.right, rtype, b.right.Location);
+
+                                       return lifted.Resolve (rc);
                                }
 
+                               b.left = Convert.ImplicitConversion (rc, b.left, left, b.left.Location);
+                               b.right = Convert.ImplicitConversion (rc, b.right, right, b.right.Location);
+
                                return b;
                        }
 
@@ -1941,16 +2171,22 @@ namespace Mono.CSharp
 
                        public PredefinedOperator ResolveBetterOperator (ResolveContext ec, PredefinedOperator best_operator)
                        {
+                               if ((OperatorsMask & Operator.DecomposedMask) != 0)
+                                       return best_operator;
+
+                               if ((best_operator.OperatorsMask & Operator.DecomposedMask) != 0)
+                                       return this;
+
                                int result = 0;
                                if (left != null && best_operator.left != null) {
-                                       result = OverloadResolver.BetterTypeConversion (ec, best_operator.left, left);
+                                       result = OverloadResolver.BetterTypeConversion (ec, best_operator.left_unwrap, left_unwrap);
                                }
 
                                //
                                // When second argument is same as the first one, the result is same
                                //
                                if (right != null && (left != right || best_operator.left != best_operator.right)) {
-                                       result |= OverloadResolver.BetterTypeConversion (ec, best_operator.right, right);
+                                       result |= OverloadResolver.BetterTypeConversion (ec, best_operator.right_unwrap, right_unwrap);
                                }
 
                                if (result == 0 || result > 2)
@@ -1995,44 +2231,6 @@ namespace Mono.CSharp
                        }
                }
 
-               sealed class PredefinedShiftOperator : PredefinedOperator
-               {
-                       public PredefinedShiftOperator (TypeSpec ltype, TypeSpec rtype, Operator op_mask)
-                               : base (ltype, rtype, op_mask)
-                       {
-                       }
-
-                       public override Expression ConvertResult (ResolveContext ec, Binary b)
-                       {
-                               b.left = Convert.ImplicitConversion (ec, b.left, left, b.left.Location);
-
-                               Expression expr_tree_expr = Convert.ImplicitConversion (ec, b.right, right, b.right.Location);
-
-                               int right_mask = left.BuiltinType == BuiltinTypeSpec.Type.Int || left.BuiltinType == BuiltinTypeSpec.Type.UInt ? 0x1f : 0x3f;
-
-                               //
-                               // b = b.left >> b.right & (0x1f|0x3f)
-                               //
-                               b.right = new Binary (Operator.BitwiseAnd,
-                                       b.right, new IntConstant (ec.BuiltinTypes, right_mask, b.right.Location)).Resolve (ec);
-
-                               //
-                               // Expression tree representation does not use & mask
-                               //
-                               b.right = ReducedExpression.Create (b.right, expr_tree_expr).Resolve (ec);
-                               b.type = ReturnType;
-
-                               //
-                               // Optimize shift by 0
-                               //
-                               var c = b.right as Constant;
-                               if (c != null && c.IsDefaultValue)
-                                       return ReducedExpression.Create (b.left, b).Resolve (ec);
-
-                               return b;
-                       }
-               }
-
                sealed class PredefinedEqualityOperator : PredefinedOperator
                {
                        MethodSpec equal_method, inequal_method;
@@ -2186,21 +2384,23 @@ namespace Mono.CSharp
                        LogicalMask             = 1 << 10,
                        AdditionMask    = 1 << 11,
                        SubtractionMask = 1 << 12,
-                       RelationalMask  = 1 << 13
+                       RelationalMask  = 1 << 13,
+
+                       DecomposedMask  = 1 << 19,
+                       NullableMask    = 1 << 20,
                }
 
-               protected enum State
+               [Flags]
+               enum State : byte
                {
                        None = 0,
                        Compound = 1 << 1,
-                       LeftNullLifted = 1 << 2,
-                       RightNullLifted = 1 << 3
                }
 
                readonly Operator oper;
-               protected Expression left, right;
-               protected State state;
-               Expression enum_conversion;
+               Expression left, right;
+               State state;
+               ConvCast.Mode enum_conversion;
 
                public Binary (Operator oper, Expression left, Expression right, bool isCompound)
                        : this (oper, left, right)
@@ -2243,6 +2443,12 @@ namespace Mono.CSharp
                        }
                }
 
+               public override Location StartLocation {
+                       get {
+                               return left.StartLocation;
+                       }
+               }
+
                #endregion
 
                /// <summary>
@@ -2328,18 +2534,24 @@ namespace Mono.CSharp
                                return;
 
                        string l, r;
-                       l = TypeManager.CSharpName (left.Type);
-                       r = TypeManager.CSharpName (right.Type);
+                       l = left.Type.GetSignatureForError ();
+                       r = right.Type.GetSignatureForError ();
 
                        ec.Report.Error (19, loc, "Operator `{0}' cannot be applied to operands of type `{1}' and `{2}'",
                                oper, l, r);
                }
                
-               protected void Error_OperatorCannotBeApplied (ResolveContext ec, Expression left, Expression right)
+               void Error_OperatorCannotBeApplied (ResolveContext ec, Expression left, Expression right)
                {
                        Error_OperatorCannotBeApplied (ec, left, right, OperName (oper), loc);
                }
 
+               public override void FlowAnalysis (FlowAnalysisContext fc)
+               {
+                       left.FlowAnalysis (fc);
+                       right.FlowAnalysis (fc);
+               }
+
                //
                // Converts operator to System.Linq.Expressions.ExpressionType enum name
                //
@@ -2434,7 +2646,7 @@ namespace Mono.CSharp
                        return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
                }
 
-               public static void EmitOperatorOpcode (EmitContext ec, Operator oper, TypeSpec l)
+               public static void EmitOperatorOpcode (EmitContext ec, Operator oper, TypeSpec l, Expression right)
                {
                        OpCode opcode;
 
@@ -2491,6 +2703,11 @@ namespace Mono.CSharp
                                break;
 
                        case Operator.RightShift:
+                               if (!(right is IntConstant)) {
+                                       ec.EmitInt (GetShiftMask (l));
+                                       ec.Emit (OpCodes.And);
+                               }
+
                                if (IsUnsigned (l))
                                        opcode = OpCodes.Shr_Un;
                                else
@@ -2498,6 +2715,11 @@ namespace Mono.CSharp
                                break;
                                
                        case Operator.LeftShift:
+                               if (!(right is IntConstant)) {
+                                       ec.EmitInt (GetShiftMask (l));
+                                       ec.Emit (OpCodes.And);
+                               }
+
                                opcode = OpCodes.Shl;
                                break;
 
@@ -2566,6 +2788,11 @@ namespace Mono.CSharp
                        ec.Emit (opcode);
                }
 
+               static int GetShiftMask (TypeSpec type)
+               {
+                       return type.BuiltinType == BuiltinTypeSpec.Type.Int || type.BuiltinType == BuiltinTypeSpec.Type.UInt ? 0x1f : 0x3f;
+               }
+
                static bool IsUnsigned (TypeSpec t)
                {
                        switch (t.BuiltinType) {
@@ -2585,8 +2812,10 @@ namespace Mono.CSharp
                        return t.BuiltinType == BuiltinTypeSpec.Type.Float || t.BuiltinType == BuiltinTypeSpec.Type.Double;
                }
 
-               Expression ResolveOperator (ResolveContext ec)
+               public Expression ResolveOperator (ResolveContext rc)
                {
+                       eclass = ExprClass.Value;
+
                        TypeSpec l = left.Type;
                        TypeSpec r = right.Type;
                        Expression expr;
@@ -2595,57 +2824,109 @@ namespace Mono.CSharp
                        //
                        // Handles predefined primitive types
                        //
-                       if (BuiltinTypeSpec.IsPrimitiveType (l) && BuiltinTypeSpec.IsPrimitiveType (r)) {
+                       if ((BuiltinTypeSpec.IsPrimitiveType (l) || (l.IsNullableType && BuiltinTypeSpec.IsPrimitiveType (Nullable.NullableInfo.GetUnderlyingType (l)))) &&
+                               (BuiltinTypeSpec.IsPrimitiveType (r) || (r.IsNullableType && BuiltinTypeSpec.IsPrimitiveType (Nullable.NullableInfo.GetUnderlyingType (r))))) {
                                if ((oper & Operator.ShiftMask) == 0) {
-                                       if (l.BuiltinType != BuiltinTypeSpec.Type.Bool && !DoBinaryOperatorPromotion (ec))
+                                       if (!DoBinaryOperatorPromotion (rc))
                                                return null;
 
-                                       primitives_only = true;
+                                       primitives_only = BuiltinTypeSpec.IsPrimitiveType (l) && BuiltinTypeSpec.IsPrimitiveType (r);
                                }
                        } else {
                                // Pointers
                                if (l.IsPointer || r.IsPointer)
-                                       return ResolveOperatorPointer (ec, l, r);
+                                       return ResolveOperatorPointer (rc, l, r);
+
+                               // User operators
+                               expr = ResolveUserOperator (rc, left, right);
+                               if (expr != null)
+                                       return expr;
+
 
-                               // Enums
                                bool lenum = l.IsEnum;
                                bool renum = r.IsEnum;
-                               if (lenum || renum) {
-                                       expr = ResolveOperatorEnum (ec, lenum, renum, l, r);
+                               if ((oper & (Operator.ComparisonMask | Operator.BitwiseMask)) != 0) {
+                                       //
+                                       // Enumerations
+                                       //
+                                       if (IsEnumOrNullableEnum (l) || IsEnumOrNullableEnum (r)) {
+                                               expr = ResolveSingleEnumOperators (rc, lenum, renum, l, r);
 
-                                       if (expr != null)
-                                               return expr;
-                               }
+                                               if (expr == null)
+                                                       return null;
 
-                               // Delegates
-                               if ((oper == Operator.Addition || oper == Operator.Subtraction) && (l.IsDelegate || r.IsDelegate)) {
-                                               
-                                       expr = ResolveOperatorDelegate (ec, l, r);
+                                               if ((oper & Operator.BitwiseMask) != 0) {
+                                                       expr = EmptyCast.Create (expr, type);
+                                                       AddEnumResultCast (type);
 
-                                       // TODO: Can this be ambiguous
-                                       if (expr != null)
+                                                       if (oper == Operator.BitwiseAnd && left.Type.IsEnum && right.Type.IsEnum) {
+                                                               expr = OptimizeAndOperation (expr);
+                                                       }
+                                               }
+
+                                               left = ConvertEnumOperandToUnderlyingType (rc, left);
+                                               right = ConvertEnumOperandToUnderlyingType (rc, right);
                                                return expr;
-                               }
+                                       }
+                               } else if ((oper == Operator.Addition || oper == Operator.Subtraction)) {
+                                       if (IsEnumOrNullableEnum (l) || IsEnumOrNullableEnum (r)) {
+                                               //
+                                               // Enumerations
+                                               //
+                                               expr = ResolveEnumOperators (rc, lenum, renum, l, r);
 
-                               // User operators
-                               expr = ResolveUserOperator (ec, left, right);
-                               if (expr != null)
-                                       return expr;
+                                               //
+                                               // We cannot break here there is also Enum + String possible match
+                                               // which is not ambiguous with predefined enum operators
+                                               //
+                                               if (expr != null) {
+                                                       left = ConvertEnumOperandToUnderlyingType (rc, left);
+                                                       right = ConvertEnumOperandToUnderlyingType (rc, right);
 
-                               // Predefined reference types equality
-                               if ((oper & Operator.EqualityMask) != 0) {
-                                       expr = ResolveOperatorEquality (ec, l, r);
-                                       if (expr != null)
-                                               return expr;
+                                                       return expr;
+                                               }
+                                       } else if (l.IsDelegate || r.IsDelegate) {
+                                               //
+                                               // Delegates
+                                               //
+                                               expr = ResolveOperatorDelegate (rc, l, r);
+
+                                               // TODO: Can this be ambiguous
+                                               if (expr != null)
+                                                       return expr;
+                                       }
                                }
                        }
+                       
+                       //
+                       // Equality operators are more complicated
+                       //
+                       if ((oper & Operator.EqualityMask) != 0) {
+                               return ResolveEquality (rc, l, r, primitives_only);
+                       }
+
+                       expr = ResolveOperatorPredefined (rc, rc.BuiltinTypes.OperatorsBinaryStandard, primitives_only);
+                       if (expr != null)
+                               return expr;
+
+                       if (primitives_only)
+                               return null;
+
+                       //
+                       // Lifted operators have lower priority
+                       //
+                       return ResolveOperatorPredefined (rc, rc.Module.OperatorsBinaryLifted, false);
+               }
 
-                       return ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryStandard, primitives_only, null);
+               static bool IsEnumOrNullableEnum (TypeSpec type)
+               {
+                       return type.IsEnum || (type.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (type).IsEnum);
                }
 
+
                // at least one of 'left' or 'right' is an enumeration constant (EnumConstant or SideEffectConstant or ...)
                // if 'left' is not an enumeration constant, create one from the type of 'right'
-               Constant EnumLiftUp (ResolveContext ec, Constant left, Constant right, Location loc)
+               Constant EnumLiftUp (ResolveContext ec, Constant left, Constant right)
                {
                        switch (oper) {
                        case Operator.BitwiseOr:
@@ -2704,9 +2985,10 @@ namespace Mono.CSharp
 
                        // FIXME: consider constants
 
+                       var ltype = lcast != null ? lcast.UnderlyingType : rcast.UnderlyingType;
                        ec.Report.Warning (675, 3, loc,
                                "The operator `|' used on the sign-extended type `{0}'. Consider casting to a smaller unsigned type first",
-                               TypeManager.CSharpName (lcast != null ? lcast.UnderlyingType : rcast.UnderlyingType));
+                               ltype.GetSignatureForError ());
                }
 
                public static PredefinedOperator[] CreatePointerOperatorsTable (BuiltinTypes types)
@@ -2746,8 +3028,9 @@ namespace Mono.CSharp
                public static PredefinedOperator[] CreateStandardOperatorsTable (BuiltinTypes types)
                {
                        TypeSpec bool_type = types.Bool;
-                       return new PredefinedOperator[] {
-                               new PredefinedOperator (types.Int, Operator.ArithmeticMask | Operator.BitwiseMask),
+
+                       return new [] {
+                               new PredefinedOperator (types.Int, Operator.ArithmeticMask | Operator.BitwiseMask | Operator.ShiftMask),
                                new PredefinedOperator (types.UInt, Operator.ArithmeticMask | Operator.BitwiseMask),
                                new PredefinedOperator (types.Long, Operator.ArithmeticMask | Operator.BitwiseMask),
                                new PredefinedOperator (types.ULong, Operator.ArithmeticMask | Operator.BitwiseMask),
@@ -2764,15 +3047,64 @@ namespace Mono.CSharp
                                new PredefinedOperator (types.Decimal, Operator.ComparisonMask, bool_type),
 
                                new PredefinedStringOperator (types.String, Operator.AdditionMask, types.String),
-                               new PredefinedStringOperator (types.String, types.Object, Operator.AdditionMask, types.String),
-                               new PredefinedStringOperator (types.Object, types.String, Operator.AdditionMask, types.String),
+                               // Remaining string operators are in lifted tables
 
                                new PredefinedOperator (bool_type, Operator.BitwiseMask | Operator.LogicalMask | Operator.EqualityMask, bool_type),
 
-                               new PredefinedShiftOperator (types.Int, types.Int, Operator.ShiftMask),
-                               new PredefinedShiftOperator (types.UInt, types.Int, Operator.ShiftMask),
-                               new PredefinedShiftOperator (types.Long, types.Int, Operator.ShiftMask),
-                               new PredefinedShiftOperator (types.ULong, types.Int, Operator.ShiftMask)
+                               new PredefinedOperator (types.UInt, types.Int, Operator.ShiftMask),
+                               new PredefinedOperator (types.Long, types.Int, Operator.ShiftMask),
+                               new PredefinedOperator (types.ULong, types.Int, Operator.ShiftMask)
+                       };
+
+               }
+               public static PredefinedOperator[] CreateStandardLiftedOperatorsTable (ModuleContainer module)
+               {
+                       var nullable = module.PredefinedTypes.Nullable.TypeSpec;
+                       if (nullable == null)
+                               return new PredefinedOperator [0];
+
+                       var types = module.Compiler.BuiltinTypes;
+                       var bool_type = types.Bool;
+
+                       var nullable_bool = nullable.MakeGenericType (module, new[] { bool_type });
+                       var nullable_int = nullable.MakeGenericType (module, new[] { types.Int });
+                       var nullable_uint = nullable.MakeGenericType (module, new[] { types.UInt });
+                       var nullable_long = nullable.MakeGenericType (module, new[] { types.Long });
+                       var nullable_ulong = nullable.MakeGenericType (module, new[] { types.ULong });
+                       var nullable_float = nullable.MakeGenericType (module, new[] { types.Float });
+                       var nullable_double = nullable.MakeGenericType (module, new[] { types.Double });
+                       var nullable_decimal = nullable.MakeGenericType (module, new[] { types.Decimal });
+
+                       return new[] {
+                               new PredefinedOperator (nullable_int, Operator.NullableMask | Operator.ArithmeticMask | Operator.BitwiseMask | Operator.ShiftMask),
+                               new PredefinedOperator (nullable_uint, Operator.NullableMask | Operator.ArithmeticMask | Operator.BitwiseMask),
+                               new PredefinedOperator (nullable_long, Operator.NullableMask | Operator.ArithmeticMask | Operator.BitwiseMask),
+                               new PredefinedOperator (nullable_ulong, Operator.NullableMask | Operator.ArithmeticMask | Operator.BitwiseMask),
+                               new PredefinedOperator (nullable_float, Operator.NullableMask | Operator.ArithmeticMask),
+                               new PredefinedOperator (nullable_double, Operator.NullableMask | Operator.ArithmeticMask),
+                               new PredefinedOperator (nullable_decimal, Operator.NullableMask | Operator.ArithmeticMask),
+
+                               new PredefinedOperator (nullable_int, Operator.NullableMask | Operator.ComparisonMask, bool_type),
+                               new PredefinedOperator (nullable_uint, Operator.NullableMask | Operator.ComparisonMask, bool_type),
+                               new PredefinedOperator (nullable_long, Operator.NullableMask | Operator.ComparisonMask, bool_type),
+                               new PredefinedOperator (nullable_ulong, Operator.NullableMask | Operator.ComparisonMask, bool_type),
+                               new PredefinedOperator (nullable_float, Operator.NullableMask | Operator.ComparisonMask, bool_type),
+                               new PredefinedOperator (nullable_double, Operator.NullableMask | Operator.ComparisonMask, bool_type),
+                               new PredefinedOperator (nullable_decimal, Operator.NullableMask | Operator.ComparisonMask, bool_type),
+
+                               new PredefinedOperator (nullable_bool, Operator.NullableMask | Operator.BitwiseMask, nullable_bool),
+
+                               new PredefinedOperator (nullable_uint, nullable_int, Operator.NullableMask | Operator.ShiftMask),
+                               new PredefinedOperator (nullable_long, nullable_int, Operator.NullableMask | Operator.ShiftMask),
+                               new PredefinedOperator (nullable_ulong, nullable_int, Operator.NullableMask | Operator.ShiftMask),
+
+                               //
+                               // Not strictly lifted but need to be in second group otherwise expressions like
+                               // int + null would resolve to +(object, string) instead of +(int?, int?)
+                               //
+                               new PredefinedStringOperator (types.String, types.Object, Operator.AdditionMask, types.String),
+                               new PredefinedStringOperator (types.Object, types.String, Operator.AdditionMask, types.String),
+
                        };
                }
 
@@ -2780,115 +3112,170 @@ namespace Mono.CSharp
                {
                        TypeSpec bool_type = types.Bool;
 
-                       return new PredefinedOperator[] {
+                       return new[] {
                                new PredefinedEqualityOperator (types.String, bool_type),
                                new PredefinedEqualityOperator (types.Delegate, bool_type),
-                               new PredefinedOperator (bool_type, Operator.EqualityMask, bool_type)
+                               new PredefinedOperator (bool_type, Operator.EqualityMask, bool_type),
+                               new PredefinedOperator (types.Int, Operator.EqualityMask, bool_type),
+                               new PredefinedOperator (types.UInt, Operator.EqualityMask, bool_type),
+                               new PredefinedOperator (types.Long, Operator.EqualityMask, bool_type),
+                               new PredefinedOperator (types.ULong, Operator.EqualityMask, bool_type),
+                               new PredefinedOperator (types.Float, Operator.EqualityMask, bool_type),
+                               new PredefinedOperator (types.Double, Operator.EqualityMask, bool_type),
+                               new PredefinedOperator (types.Decimal, Operator.EqualityMask, bool_type),
+                       };
+               }
+
+               public static PredefinedOperator[] CreateEqualityLiftedOperatorsTable (ModuleContainer module)
+               {
+                       var nullable = module.PredefinedTypes.Nullable.TypeSpec;
+
+                       if (nullable == null)
+                               return new PredefinedOperator [0];
+
+                       var types = module.Compiler.BuiltinTypes;
+                       var bool_type = types.Bool;
+                       var nullable_bool = nullable.MakeGenericType (module, new [] { bool_type });
+                       var nullable_int = nullable.MakeGenericType (module, new[] { types.Int });
+                       var nullable_uint = nullable.MakeGenericType (module, new[] { types.UInt });
+                       var nullable_long = nullable.MakeGenericType (module, new[] { types.Long });
+                       var nullable_ulong = nullable.MakeGenericType (module, new[] { types.ULong });
+                       var nullable_float = nullable.MakeGenericType (module, new[] { types.Float });
+                       var nullable_double = nullable.MakeGenericType (module, new[] { types.Double });
+                       var nullable_decimal = nullable.MakeGenericType (module, new[] { types.Decimal });
+
+                       return new [] {
+                               new PredefinedOperator (nullable_bool, Operator.NullableMask | Operator.EqualityMask, bool_type),
+                               new PredefinedOperator (nullable_int, Operator.NullableMask | Operator.EqualityMask, bool_type),
+                               new PredefinedOperator (nullable_uint, Operator.NullableMask | Operator.EqualityMask, bool_type),
+                               new PredefinedOperator (nullable_long, Operator.NullableMask | Operator.EqualityMask, bool_type),
+                               new PredefinedOperator (nullable_ulong, Operator.NullableMask | Operator.EqualityMask, bool_type),
+                               new PredefinedOperator (nullable_float, Operator.NullableMask | Operator.EqualityMask, bool_type),
+                               new PredefinedOperator (nullable_double, Operator.NullableMask | Operator.EqualityMask, bool_type),
+                               new PredefinedOperator (nullable_decimal, Operator.NullableMask | Operator.EqualityMask, bool_type)
                        };
                }
 
                //
-               // Rules used during binary numeric promotion
+               // 7.2.6.2 Binary numeric promotions
                //
-               static bool DoNumericPromotion (ResolveContext rc, ref Expression prim_expr, ref Expression second_expr, TypeSpec type)
+               bool DoBinaryOperatorPromotion (ResolveContext rc)
                {
-                       Expression temp;
-
-                       Constant c = prim_expr as Constant;
-                       if (c != null) {
-                               temp = c.ConvertImplicitly (type);
-                               if (temp != null) {
-                                       prim_expr = temp;
-                                       return true;
-                               }
+                       TypeSpec ltype = left.Type;
+                       if (ltype.IsNullableType) {
+                               ltype = Nullable.NullableInfo.GetUnderlyingType (ltype);
                        }
 
-                       if (type.BuiltinType == BuiltinTypeSpec.Type.UInt) {
-                               switch (prim_expr.Type.BuiltinType) {
-                               case BuiltinTypeSpec.Type.Int:
-                               case BuiltinTypeSpec.Type.Short:
-                               case BuiltinTypeSpec.Type.SByte:
-                               case BuiltinTypeSpec.Type.Long:
-                                       type = rc.BuiltinTypes.Long;
+                       //
+                       // This is numeric promotion code only
+                       //
+                       if (ltype.BuiltinType == BuiltinTypeSpec.Type.Bool)
+                               return true;
 
-                                       if (type != second_expr.Type) {
-                                               c = second_expr as Constant;
-                                               if (c != null)
-                                                       temp = c.ConvertImplicitly (type);
-                                               else
-                                                       temp = Convert.ImplicitNumericConversion (second_expr, type);
-                                               if (temp == null)
-                                                       return false;
-                                               second_expr = temp;
-                                       }
-                                       break;
-                               }
-                       } else if (type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
-                               //
-                               // A compile-time error occurs if the other operand is of type sbyte, short, int, or long
-                               //
-                               switch (type.BuiltinType) {
-                               case BuiltinTypeSpec.Type.Int:
-                               case BuiltinTypeSpec.Type.Long:
-                               case BuiltinTypeSpec.Type.Short:
-                               case BuiltinTypeSpec.Type.SByte:
-                                       return false;
-                               }
+                       TypeSpec rtype = right.Type;
+                       if (rtype.IsNullableType) {
+                               rtype = Nullable.NullableInfo.GetUnderlyingType (rtype);
                        }
 
-                       temp = Convert.ImplicitNumericConversion (prim_expr, type);
-                       if (temp == null)
-                               return false;
-
-                       prim_expr = temp;
-                       return true;
-               }
+                       var lb = ltype.BuiltinType;
+                       var rb = rtype.BuiltinType;
+                       TypeSpec type;
+                       Expression expr;
 
-               //
-               // 7.2.6.2 Binary numeric promotions
-               //
-               public bool DoBinaryOperatorPromotion (ResolveContext ec)
-               {
-                       TypeSpec ltype = left.Type;
-                       TypeSpec rtype = right.Type;
-                       Expression temp;
+                       if (lb == BuiltinTypeSpec.Type.Decimal || rb == BuiltinTypeSpec.Type.Decimal) {
+                               type = rc.BuiltinTypes.Decimal;
+                       } else if (lb == BuiltinTypeSpec.Type.Double || rb == BuiltinTypeSpec.Type.Double) {
+                               type = rc.BuiltinTypes.Double;
+                       } else if (lb == BuiltinTypeSpec.Type.Float || rb == BuiltinTypeSpec.Type.Float) {
+                               type = rc.BuiltinTypes.Float;
+                       } else if (lb == BuiltinTypeSpec.Type.ULong || rb == BuiltinTypeSpec.Type.ULong) {
+                               type = rc.BuiltinTypes.ULong;
+
+                               if (IsSignedType (lb)) {
+                                       expr = ConvertSignedConstant (left, type);
+                                       if (expr == null)
+                                               return false;
+                                       left = expr;
+                               } else if (IsSignedType (rb)) {
+                                       expr = ConvertSignedConstant (right, type);
+                                       if (expr == null)
+                                               return false;
+                                       right = expr;
+                               }
 
-                       foreach (TypeSpec t in ec.BuiltinTypes.BinaryPromotionsTypes) {
-                               if (t == ltype)
-                                       return t == rtype || DoNumericPromotion (ec, ref right, ref left, t);
+                       } else if (lb == BuiltinTypeSpec.Type.Long || rb == BuiltinTypeSpec.Type.Long) {
+                               type = rc.BuiltinTypes.Long;
+                       } else if (lb == BuiltinTypeSpec.Type.UInt || rb == BuiltinTypeSpec.Type.UInt) {
+                               type = rc.BuiltinTypes.UInt;
 
-                               if (t == rtype)
-                                       return t == ltype || DoNumericPromotion (ec, ref left, ref right, t);
+                               if (IsSignedType (lb)) {
+                                       expr = ConvertSignedConstant (left, type);
+                                       if (expr == null)
+                                               type = rc.BuiltinTypes.Long;
+                               } else if (IsSignedType (rb)) {
+                                       expr = ConvertSignedConstant (right, type);
+                                       if (expr == null)
+                                               type = rc.BuiltinTypes.Long;
+                               }
+                       } else {
+                               type = rc.BuiltinTypes.Int;
                        }
 
-                       TypeSpec int32 = ec.BuiltinTypes.Int;
-                       if (ltype != int32) {
-                               Constant c = left as Constant;
-                               if (c != null)
-                                       temp = c.ConvertImplicitly (int32);
-                               else
-                                       temp = Convert.ImplicitNumericConversion (left, int32);
-
-                               if (temp == null)
+                       if (ltype != type) {
+                               expr = PromoteExpression (rc, left, type);
+                               if (expr == null)
                                        return false;
-                               left = temp;
-                       }
 
-                       if (rtype != int32) {
-                               Constant c = right as Constant;
-                               if (c != null)
-                                       temp = c.ConvertImplicitly (int32);
-                               else
-                                       temp = Convert.ImplicitNumericConversion (right, int32);
+                               left = expr;
+                       }
 
-                               if (temp == null)
+                       if (rtype != type) {
+                               expr = PromoteExpression (rc, right, type);
+                               if (expr == null)
                                        return false;
-                               right = temp;
+
+                               right = expr;
                        }
 
                        return true;
                }
 
+               static bool IsSignedType (BuiltinTypeSpec.Type type)
+               {
+                       switch (type) {
+                       case BuiltinTypeSpec.Type.Int:
+                       case BuiltinTypeSpec.Type.Short:
+                       case BuiltinTypeSpec.Type.SByte:
+                       case BuiltinTypeSpec.Type.Long:
+                               return true;
+                       default:
+                               return false;
+                       }
+               }
+
+               static Expression ConvertSignedConstant (Expression expr, TypeSpec type)
+               {
+                       var c = expr as Constant;
+                       if (c == null)
+                               return null;
+
+                       return c.ConvertImplicitly (type);
+               }
+
+               static Expression PromoteExpression (ResolveContext rc, Expression expr, TypeSpec type)
+               {
+                       if (expr.Type.IsNullableType) {
+                               return Convert.ImplicitConversionStandard (rc, expr,
+                                       rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc, new[] { type }), expr.Location);
+                       }
+
+                       var c = expr as Constant;
+                       if (c != null)
+                               return c.ConvertImplicitly (type);
+
+                       return Convert.ImplicitNumericConversion (expr, type);
+               }
+
                protected override Expression DoResolve (ResolveContext ec)
                {
                        if (left == null)
@@ -2910,31 +3297,18 @@ namespace Mono.CSharp
                        if (left == null)
                                return null;
 
-                       Constant lc = left as Constant;
-
-                       if (lc != null && lc.Type.BuiltinType == BuiltinTypeSpec.Type.Bool &&
-                               ((oper == Operator.LogicalAnd && lc.IsDefaultValue) ||
-                                (oper == Operator.LogicalOr && !lc.IsDefaultValue))) {
-
-                               // FIXME: resolve right expression as unreachable
-                               // right.Resolve (ec);
-
-                               ec.Report.Warning (429, 4, loc, "Unreachable expression code detected");
-                               return left;
-                       }
-
                        right = right.Resolve (ec);
                        if (right == null)
                                return null;
 
-                       eclass = ExprClass.Value;
+                       Constant lc = left as Constant;
                        Constant rc = right as Constant;
 
                        // The conversion rules are ignored in enum context but why
                        if (!ec.HasSet (ResolveContext.Options.EnumScope) && lc != null && rc != null && (left.Type.IsEnum || right.Type.IsEnum)) {
-                               lc = EnumLiftUp (ec, lc, rc, loc);
+                               lc = EnumLiftUp (ec, lc, rc);
                                if (lc != null)
-                                       rc = EnumLiftUp (ec, rc, lc, loc);
+                                       rc = EnumLiftUp (ec, rc, lc);
                        }
 
                        if (rc != null && lc != null) {
@@ -2953,93 +3327,87 @@ namespace Mono.CSharp
                                CheckOutOfRangeComparison (ec, rc, left.Type);
                        }
 
-                       if (left.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic || right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
-                               var lt = left.Type;
-                               var rt = right.Type;
-                               if (lt.Kind == MemberKind.Void || lt == InternalType.MethodGroup || lt == InternalType.AnonymousMethod ||
-                                       rt.Kind == MemberKind.Void || rt == InternalType.MethodGroup || rt == InternalType.AnonymousMethod) {
-                                       Error_OperatorCannotBeApplied (ec, left, right);
-                                       return null;
-                               }
+                       if (left.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic || right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic)
+                               return DoResolveDynamic (ec);
 
-                               Arguments args;
+                       return DoResolveCore (ec, left, right);
+               }
 
-                               //
-                               // Special handling for logical boolean operators which require rhs not to be
-                               // evaluated based on lhs value
-                               //
-                               if ((oper & Operator.LogicalMask) != 0) {
-                                       Expression cond_left, cond_right, expr;
+               Expression DoResolveDynamic (ResolveContext rc)
+               {
+                       var lt = left.Type;
+                       var rt = right.Type;
+                       if (lt.Kind == MemberKind.Void || lt == InternalType.MethodGroup || lt == InternalType.AnonymousMethod ||
+                               rt.Kind == MemberKind.Void || rt == InternalType.MethodGroup || rt == InternalType.AnonymousMethod) {
+                               Error_OperatorCannotBeApplied (rc, left, right);
+                               return null;
+                       }
 
-                                       args = new Arguments (2);
+                       Arguments args;
 
-                                       if (lt.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
-                                               LocalVariable temp = LocalVariable.CreateCompilerGenerated (lt, ec.CurrentBlock, loc);
+                       //
+                       // Special handling for logical boolean operators which require rhs not to be
+                       // evaluated based on lhs value
+                       //
+                       if ((oper & Operator.LogicalMask) != 0) {
+                               Expression cond_left, cond_right, expr;
 
-                                               var cond_args = new Arguments (1);
-                                               cond_args.Add (new Argument (new SimpleAssign (temp.CreateReferenceExpression (ec, loc), left).Resolve (ec)));
+                               args = new Arguments (2);
 
-                                               //
-                                               // dynamic && bool => IsFalse (temp = left) ? temp : temp && right;
-                                               // dynamic || bool => IsTrue (temp = left) ? temp : temp || right;
-                                               //
-                                               left = temp.CreateReferenceExpression (ec, loc);
-                                               if (oper == Operator.LogicalAnd) {
-                                                       expr = DynamicUnaryConversion.CreateIsFalse (ec, cond_args, loc);
-                                                       cond_left = left;
-                                               } else {
-                                                       expr = DynamicUnaryConversion.CreateIsTrue (ec, cond_args, loc);
-                                                       cond_left = left;
-                                               }
+                               if (lt.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
+                                       LocalVariable temp = LocalVariable.CreateCompilerGenerated (lt, rc.CurrentBlock, loc);
 
-                                               args.Add (new Argument (left));
-                                               args.Add (new Argument (right));
-                                               cond_right = new DynamicExpressionStatement (this, args, loc);
+                                       var cond_args = new Arguments (1);
+                                       cond_args.Add (new Argument (new SimpleAssign (temp.CreateReferenceExpression (rc, loc), left).Resolve (rc)));
+
+                                       //
+                                       // dynamic && bool => IsFalse (temp = left) ? temp : temp && right;
+                                       // dynamic || bool => IsTrue (temp = left) ? temp : temp || right;
+                                       //
+                                       left = temp.CreateReferenceExpression (rc, loc);
+                                       if (oper == Operator.LogicalAnd) {
+                                               expr = DynamicUnaryConversion.CreateIsFalse (rc, cond_args, loc);
+                                               cond_left = left;
                                        } else {
-                                               LocalVariable temp = LocalVariable.CreateCompilerGenerated (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
+                                               expr = DynamicUnaryConversion.CreateIsTrue (rc, cond_args, loc);
+                                               cond_left = left;
+                                       }
 
-                                               args.Add (new Argument (temp.CreateReferenceExpression (ec, loc).Resolve (ec)));
-                                               args.Add (new Argument (right));
-                                               right = new DynamicExpressionStatement (this, args, loc);
+                                       args.Add (new Argument (left));
+                                       args.Add (new Argument (right));
+                                       cond_right = new DynamicExpressionStatement (this, args, loc);
+                               } else {
+                                       LocalVariable temp = LocalVariable.CreateCompilerGenerated (rc.BuiltinTypes.Bool, rc.CurrentBlock, loc);
 
-                                               //
-                                               // bool && dynamic => (temp = left) ? temp && right : temp;
-                                               // bool || dynamic => (temp = left) ? temp : temp || right;
-                                               //
-                                               if (oper == Operator.LogicalAnd) {
-                                                       cond_left = right;
-                                                       cond_right = temp.CreateReferenceExpression (ec, loc);
-                                               } else {
-                                                       cond_left = temp.CreateReferenceExpression (ec, loc);
-                                                       cond_right = right;
-                                               }
+                                       args.Add (new Argument (temp.CreateReferenceExpression (rc, loc).Resolve (rc)));
+                                       args.Add (new Argument (right));
+                                       right = new DynamicExpressionStatement (this, args, loc);
 
-                                               expr = new BooleanExpression (new SimpleAssign (temp.CreateReferenceExpression (ec, loc), left));
+                                       //
+                                       // bool && dynamic => (temp = left) ? temp && right : temp;
+                                       // bool || dynamic => (temp = left) ? temp : temp || right;
+                                       //
+                                       if (oper == Operator.LogicalAnd) {
+                                               cond_left = right;
+                                               cond_right = temp.CreateReferenceExpression (rc, loc);
+                                       } else {
+                                               cond_left = temp.CreateReferenceExpression (rc, loc);
+                                               cond_right = right;
                                        }
 
-                                       return new Conditional (expr, cond_left, cond_right, loc).Resolve (ec);
+                                       expr = new BooleanExpression (new SimpleAssign (temp.CreateReferenceExpression (rc, loc), left));
                                }
 
-                               args = new Arguments (2);
-                               args.Add (new Argument (left));
-                               args.Add (new Argument (right));
-                               return new DynamicExpressionStatement (this, args, loc).Resolve (ec);
-                       }
-
-                       if (ec.Module.Compiler.Settings.Version >= LanguageVersion.ISO_2 &&
-                               ((left.Type.IsNullableType && (right is NullLiteral || right.Type.IsNullableType || TypeSpec.IsValueType (right.Type))) ||
-                               (TypeSpec.IsValueType (left.Type) && right is NullLiteral) ||
-                               (right.Type.IsNullableType && (left is NullLiteral || left.Type.IsNullableType || TypeSpec.IsValueType (left.Type))) ||
-                               (TypeSpec.IsValueType (right.Type) && left is NullLiteral))) {
-                               var lifted = new Nullable.LiftedBinaryOperator (oper, left, right);
-                               lifted.state = state;
-                               return lifted.Resolve (ec);
+                               return new Conditional (expr, cond_left, cond_right, loc).Resolve (rc);
                        }
 
-                       return DoResolveCore (ec, left, right);
+                       args = new Arguments (2);
+                       args.Add (new Argument (left));
+                       args.Add (new Argument (right));
+                       return new DynamicExpressionStatement (this, args, loc).Resolve (rc);
                }
 
-               protected Expression DoResolveCore (ResolveContext ec, Expression left_orig, Expression right_orig)
+               Expression DoResolveCore (ResolveContext ec, Expression left_orig, Expression right_orig)
                {
                        Expression expr = ResolveOperator (ec);
                        if (expr == null)
@@ -3055,6 +3423,11 @@ namespace Mono.CSharp
                }
 
                public override SLE.Expression MakeExpression (BuilderContext ctx)
+               {
+                       return MakeExpression (ctx, left, right);
+               }
+
+               public SLE.Expression MakeExpression (BuilderContext ctx, Expression left, Expression right)
                {
                        var le = left.MakeExpression (ctx);
                        var re = right.MakeExpression (ctx);
@@ -3141,15 +3514,14 @@ namespace Mono.CSharp
                        if (method == null)
                                return new EmptyExpression (ec.BuiltinTypes.Decimal);
 
-                       MethodGroupExpr mg = MethodGroupExpr.CreatePredefined (method, ec.BuiltinTypes.Delegate, loc);
-                       Expression expr = new UserOperatorCall (mg.BestCandidate, args, CreateExpressionTree, loc);
+                       Expression expr = new UserOperatorCall (method, args, CreateExpressionTree, loc);
                        return new ClassCast (expr, l);
                }
 
                //
-               // Enumeration operators
+               // Resolves enumeration operators where only single predefined overload exists, handles lifted versions too
                //
-               Expression ResolveOperatorEnum (ResolveContext ec, bool lenum, bool renum, TypeSpec ltype, TypeSpec rtype)
+               Expression ResolveSingleEnumOperators (ResolveContext rc, bool lenum, bool renum, TypeSpec ltype, TypeSpec rtype)
                {
                        //
                        // bool operator == (E x, E y);
@@ -3163,264 +3535,393 @@ namespace Mono.CSharp
                        // E operator | (E x, E y);
                        // E operator ^ (E x, E y);
                        //
-                       // U operator - (E e, E f)
-                       // E operator - (E e, U x)
-                       // E operator - (U x, E e)      // LAMESPEC: Not covered by the specification
-                       //
-                       // E operator + (E e, U x)
-                       // E operator + (U x, E e)
-                       //
-                       Expression ltemp = left;
-                       Expression rtemp = right;
-                       TypeSpec underlying_type;
-                       TypeSpec underlying_type_result;
-                       TypeSpec res_type;
                        Expression expr;
-                       
-                       //
-                       // LAMESPEC: There is never ambiguous conversion between enum operators
-                       // the one which contains more enum parameters always wins even if there
-                       // is an implicit conversion involved
-                       //
-                       if ((oper & (Operator.ComparisonMask | Operator.BitwiseMask)) != 0) {
-                               if (renum) {
-                                       underlying_type = EnumSpec.GetUnderlyingType (rtype);
-                                       expr = Convert.ImplicitConversion (ec, left, rtype, loc);
-                                       if (expr == null)
-                                               return null;
+                       if ((oper & Operator.ComparisonMask) != 0) {
+                               type = rc.BuiltinTypes.Bool;
+                       } else {
+                               if (lenum)
+                                       type = ltype;
+                               else if (renum)
+                                       type = rtype;
+                               else if (ltype.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (ltype).IsEnum)
+                                       type = ltype;
+                               else
+                                       type = rtype;
+                       }
 
-                                       left = expr;
-                                       ltype = expr.Type;
-                               } else if (lenum) {
-                                       underlying_type = EnumSpec.GetUnderlyingType (ltype);
-                                       expr = Convert.ImplicitConversion (ec, right, ltype, loc);
-                                       if (expr == null)
-                                               return null;
+                       if (ltype == rtype) {
+                               if (lenum || renum)
+                                       return this;
 
+                               var lifted = new Nullable.LiftedBinaryOperator (this);
+                               lifted.Left = left;
+                               lifted.Right = right;
+                               return lifted.Resolve (rc);
+                       }
+
+                       if (renum && !ltype.IsNullableType) {
+                               expr = Convert.ImplicitConversion (rc, left, rtype, loc);
+                               if (expr != null) {
+                                       left = expr;
+                                       return this;
+                               }
+                       } else if (lenum && !rtype.IsNullableType) {
+                               expr = Convert.ImplicitConversion (rc, right, ltype, loc);
+                               if (expr != null) {
                                        right = expr;
-                                       rtype = expr.Type;
-                               } else {
-                                       return null;
+                                       return this;
                                }
+                       }
 
-                               if ((oper & Operator.BitwiseMask) != 0) {
-                                       res_type = ltype;
-                                       underlying_type_result = underlying_type;
-                               } else {
-                                       res_type = null;
-                                       underlying_type_result = null;
-                               }
-                       } else if (oper == Operator.Subtraction) {
-                               if (renum) {
-                                       underlying_type = EnumSpec.GetUnderlyingType (rtype);
-                                       if (ltype != rtype) {
-                                               expr = Convert.ImplicitConversion (ec, left, rtype, left.Location);
-                                               if (expr == null) {
-                                                       expr = Convert.ImplicitConversion (ec, left, underlying_type, left.Location);
-                                                       if (expr == null)
-                                                               return null;
-
-                                                       res_type = rtype;
-                                               } else {
-                                                       res_type = underlying_type;
-                                               }
+                       //
+                       // Now try lifted version of predefined operator
+                       //
+                       var nullable_type = rc.Module.PredefinedTypes.Nullable.TypeSpec;
+                       if (nullable_type != null) {
+                               if (renum && !ltype.IsNullableType) {
+                                       var lifted_type = nullable_type.MakeGenericType (rc.Module, new[] { rtype });
 
+                                       expr = Convert.ImplicitConversion (rc, left, lifted_type, loc);
+                                       if (expr != null) {
                                                left = expr;
-                                       } else {
-                                               res_type = underlying_type;
+                                               right = Convert.ImplicitConversion (rc, right, lifted_type, loc);
                                        }
 
-                                       underlying_type_result = underlying_type;
-                               } else if (lenum) {
-                                       underlying_type = EnumSpec.GetUnderlyingType (ltype);
-                                       expr = Convert.ImplicitConversion (ec, right, ltype, right.Location);
-                                       if (expr == null || expr is EnumConstant) {
-                                               expr = Convert.ImplicitConversion (ec, right, underlying_type, right.Location);
-                                               if (expr == null)
-                                                       return null;
+                                       if ((oper & Operator.BitwiseMask) != 0)
+                                               type = lifted_type;
 
-                                               res_type = ltype;
-                                       } else {
-                                               res_type = underlying_type;
+                                       if (left.IsNull) {
+                                               if ((oper & Operator.BitwiseMask) != 0)
+                                                       return Nullable.LiftedNull.CreateFromExpression (rc, this);
+
+                                               return CreateLiftedValueTypeResult (rc, rtype);
                                        }
 
-                                       right = expr;
-                                       underlying_type_result = underlying_type;
-                               } else {
-                                       return null;
-                               }
-                       } else if (oper == Operator.Addition) {
-                               if (lenum) {
-                                       underlying_type = EnumSpec.GetUnderlyingType (ltype);
-                                       res_type = ltype;
+                                       if (expr != null) {
+                                               var lifted = new Nullable.LiftedBinaryOperator (this);
+                                               lifted.Left = expr;
+                                               lifted.Right = right;
+                                               return lifted.Resolve (rc);
+                                       }
+                               } else if (lenum && !rtype.IsNullableType) {
+                                       var lifted_type = nullable_type.MakeGenericType (rc.Module, new[] { ltype });
+
+                                       expr = Convert.ImplicitConversion (rc, right, lifted_type, loc);
+                                       if (expr != null) {
+                                               right = expr;
+                                               left = Convert.ImplicitConversion (rc, left, lifted_type, loc);
+                                       }
+
+                                       if ((oper & Operator.BitwiseMask) != 0)
+                                               type = lifted_type;
+
+                                       if (right.IsNull) {
+                                               if ((oper & Operator.BitwiseMask) != 0)
+                                                       return Nullable.LiftedNull.CreateFromExpression (rc, this);
+
+                                               return CreateLiftedValueTypeResult (rc, ltype);
+                                       }
+
+                                       if (expr != null) {
+                                               var lifted = new Nullable.LiftedBinaryOperator (this);
+                                               lifted.Left = left;
+                                               lifted.Right = expr;
+                                               return lifted.Resolve (rc);
+                                       }
+                               } else if (rtype.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (rtype).IsEnum) {
+                                       if (left.IsNull) {
+                                               if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
+                                                       left = Convert.ImplicitConversion (rc, left, rtype, left.Location);
 
-                                       if (rtype != underlying_type && (state & (State.RightNullLifted | State.LeftNullLifted)) == 0) {
-                                               expr = Convert.ImplicitConversion (ec, right, underlying_type, right.Location);
+                                               if ((oper & Operator.RelationalMask) != 0)
+                                                       return CreateLiftedValueTypeResult (rc, rtype);
+
+                                               if ((oper & Operator.BitwiseMask) != 0)
+                                                       return Nullable.LiftedNull.CreateFromExpression (rc, this);
+
+                                               // Equality operators are valid between E? and null
+                                               expr = left;
+                                       } else {
+                                               expr = Convert.ImplicitConversion (rc, left, Nullable.NullableInfo.GetUnderlyingType (rtype), loc);
                                                if (expr == null)
                                                        return null;
+                                       }
 
-                                               right = expr;
+                                       if (expr != null) {
+                                               var lifted = new Nullable.LiftedBinaryOperator (this);
+                                               lifted.Left = expr;
+                                               lifted.Right = right;
+                                               return lifted.Resolve (rc);
                                        }
-                               } else {
-                                       underlying_type = EnumSpec.GetUnderlyingType (rtype);
-                                       res_type = rtype;
-                                       if (ltype != underlying_type) {
-                                               expr = Convert.ImplicitConversion (ec, left, underlying_type, left.Location);
+                               } else if (ltype.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (ltype).IsEnum) {
+                                       if (right.IsNull) {
+                                               if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
+                                                       right = Convert.ImplicitConversion (rc, right, ltype, right.Location);
+
+                                               if ((oper & Operator.RelationalMask) != 0)
+                                                       return CreateLiftedValueTypeResult (rc, ltype);
+
+                                               if ((oper & Operator.BitwiseMask) != 0)
+                                                       return Nullable.LiftedNull.CreateFromExpression (rc, this);
+
+                                               // Equality operators are valid between E? and null
+                                               expr = right;
+                                       } else {
+                                               expr = Convert.ImplicitConversion (rc, right, Nullable.NullableInfo.GetUnderlyingType (ltype), loc);
                                                if (expr == null)
                                                        return null;
+                                       }
 
-                                               left = expr;
+                                       if (expr != null) {
+                                               var lifted = new Nullable.LiftedBinaryOperator (this);
+                                               lifted.Left = left;
+                                               lifted.Right = expr;
+                                               return lifted.Resolve (rc);
                                        }
                                }
-
-                               underlying_type_result = underlying_type;
-                       } else {
-                               return null;
                        }
 
-                       // Unwrap the constant correctly, so DoBinaryOperatorPromotion can do the magic
-                       // with constants and expressions
-                       if (left.Type != underlying_type) {
-                               if (left is Constant)
-                                       left = ((Constant) left).ConvertExplicitly (false, underlying_type);
+                       return null;
+               }
+
+               static Expression ConvertEnumOperandToUnderlyingType (ResolveContext rc, Expression expr)
+               {
+                       TypeSpec underlying_type;
+                       if (expr.Type.IsNullableType) {
+                               var nt = Nullable.NullableInfo.GetUnderlyingType (expr.Type);
+                               if (nt.IsEnum)
+                                       underlying_type = EnumSpec.GetUnderlyingType (nt);
                                else
-                                       left = EmptyCast.Create (left, underlying_type);
+                                       underlying_type = nt;
+                       } else if (expr.Type.IsEnum) {
+                               underlying_type = EnumSpec.GetUnderlyingType (expr.Type);
+                       } else {
+                               underlying_type = expr.Type;
                        }
 
-                       if (right.Type != underlying_type) {
-                               if (right is Constant)
-                                       right = ((Constant) right).ConvertExplicitly (false, underlying_type);
-                               else
-                                       right = EmptyCast.Create (right, underlying_type);
+                       switch (underlying_type.BuiltinType) {
+                       case BuiltinTypeSpec.Type.SByte:
+                       case BuiltinTypeSpec.Type.Byte:
+                       case BuiltinTypeSpec.Type.Short:
+                       case BuiltinTypeSpec.Type.UShort:
+                               underlying_type = rc.BuiltinTypes.Int;
+                               break;
                        }
 
+                       if (expr.Type.IsNullableType)
+                               underlying_type = rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { underlying_type });
+
+                       if (expr.Type == underlying_type)
+                               return expr;
+
+                       return EmptyCast.Create (expr, underlying_type);
+               }
+
+               Expression ResolveEnumOperators (ResolveContext rc, bool lenum, bool renum, TypeSpec ltype, TypeSpec rtype)
+               {
                        //
-                       // C# specification uses explicit cast syntax which means binary promotion
-                       // should happen, however it seems that csc does not do that
+                       // U operator - (E e, E f)
+                       // E operator - (E e, U x)  // Internal decomposition operator
+                       // E operator - (U x, E e)      // Internal decomposition operator
                        //
-                       if (!DoBinaryOperatorPromotion (ec)) {
-                               left = ltemp;
-                               right = rtemp;
-                               return null;
+                       // E operator + (E e, U x)
+                       // E operator + (U x, E e)
+                       //
+
+                       TypeSpec enum_type;
+
+                       if (lenum)
+                               enum_type = ltype;
+                       else if (renum)
+                               enum_type = rtype;
+                       else if (ltype.IsNullableType && Nullable.NullableInfo.GetUnderlyingType (ltype).IsEnum)
+                               enum_type = ltype;
+                       else
+                               enum_type = rtype;
+
+                       Expression expr;
+                       if (!enum_type.IsNullableType) {
+                               expr = ResolveOperatorPredefined (rc, rc.Module.GetPredefinedEnumAritmeticOperators (enum_type, false), false);
+                               if (expr != null) {
+                                       if (oper == Operator.Subtraction)
+                                               expr = ConvertEnumSubtractionResult (rc, expr);
+                                       else
+                                               expr = ConvertEnumAdditionalResult (expr, enum_type);
+
+                                       AddEnumResultCast (expr.Type);
+
+                                       return expr;
+                               }
+
+                               enum_type = rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { enum_type });
                        }
 
-                       if (underlying_type_result != null && left.Type != underlying_type_result) {
-                               enum_conversion = Convert.ExplicitNumericConversion (ec, new EmptyExpression (left.Type), underlying_type_result);
+                       expr = ResolveOperatorPredefined (rc, rc.Module.GetPredefinedEnumAritmeticOperators (enum_type, true), false);
+                       if (expr != null) {
+                               if (oper == Operator.Subtraction)
+                                       expr = ConvertEnumSubtractionResult (rc, expr);
+                               else
+                                       expr = ConvertEnumAdditionalResult (expr, enum_type);
+
+                               AddEnumResultCast (expr.Type);
                        }
 
-                       expr = ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryStandard, true, res_type);
-                       if (expr == null)
-                               return null;
+                       return expr;
+               }
 
-                       if (!IsCompound)
-                               return expr;
+               static Expression ConvertEnumAdditionalResult (Expression expr, TypeSpec enumType)
+               {
+                       return EmptyCast.Create (expr, enumType);
+               }
 
+               Expression ConvertEnumSubtractionResult (ResolveContext rc, Expression expr)
+               {
                        //
-                       // Section: 7.16.2
+                       // Enumeration subtraction has different result type based on
+                       // best overload
                        //
+                       TypeSpec result_type;
+                       if (left.Type == right.Type) {
+                               var c = right as EnumConstant;
+                               if (c != null && c.IsZeroInteger && !right.Type.IsEnum) {
+                                       //
+                                       // LAMESPEC: This is quite unexpected for expression E - 0 the return type is
+                                       // E which is not what expressions E - 1 or 0 - E return
+                                       //
+                                       result_type = left.Type;
+                               } else {
+                                       result_type = left.Type.IsNullableType ?
+                                               Nullable.NullableInfo.GetEnumUnderlyingType (rc.Module, left.Type) :
+                                               EnumSpec.GetUnderlyingType (left.Type);
+                               }
+                       } else {
+                               if (IsEnumOrNullableEnum (left.Type)) {
+                                       result_type = left.Type;
+                               } else {
+                                       result_type = right.Type;
+                               }
 
-                       //
-                       // If the return type of the selected operator is implicitly convertible to the type of x
-                       //
-                       if (Convert.ImplicitConversionExists (ec, expr, ltype))
-                               return expr;
+                               if (expr is Nullable.LiftedBinaryOperator && !result_type.IsNullableType)
+                                       result_type = rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { result_type });
+                       }
 
-                       //
-                       // Otherwise, if the selected operator is a predefined operator, if the return type of the
-                       // selected operator is explicitly convertible to the type of x, and if y is implicitly
-                       // convertible to the type of x or the operator is a shift operator, then the operation
-                       // is evaluated as x = (T)(x op y), where T is the type of x
-                       //
-                       expr = Convert.ExplicitConversion (ec, expr, ltype, loc);
-                       if (expr == null)
-                               return null;
+                       return EmptyCast.Create (expr, result_type);
+               }
 
-                       if (Convert.ImplicitConversionExists (ec, ltemp, ltype))
-                               return expr;
+               void AddEnumResultCast (TypeSpec type)
+               {
+                       if (type.IsNullableType)
+                               type = Nullable.NullableInfo.GetUnderlyingType (type);
 
-                       return null;
+                       if (type.IsEnum)
+                               type = EnumSpec.GetUnderlyingType (type);
+
+                       switch (type.BuiltinType) {
+                       case BuiltinTypeSpec.Type.SByte:
+                               enum_conversion = ConvCast.Mode.I4_I1;
+                               break;
+                       case BuiltinTypeSpec.Type.Byte:
+                               enum_conversion = ConvCast.Mode.I4_U1;
+                               break;
+                       case BuiltinTypeSpec.Type.Short:
+                               enum_conversion = ConvCast.Mode.I4_I2;
+                               break;
+                       case BuiltinTypeSpec.Type.UShort:
+                               enum_conversion = ConvCast.Mode.I4_U2;
+                               break;
+                       }
                }
 
                //
-               // 7.9.6 Reference type equality operators
+               // Equality operators rules
                //
-               Expression ResolveOperatorEquality (ResolveContext ec, TypeSpec l, TypeSpec r)
+               Expression ResolveEquality (ResolveContext ec, TypeSpec l, TypeSpec r, bool primitives_only)
                {
                        Expression result;
                        type = ec.BuiltinTypes.Bool;
+                       bool no_arg_conv = false;
 
-                       //
-                       // a, Both operands are reference-type values or the value null
-                       // b, One operand is a value of type T where T is a type-parameter and
-                       // the other operand is the value null. Furthermore T does not have the
-                       // value type constraint
-                       //
-                       // LAMESPEC: Very confusing details in the specification, basically any
-                       // reference like type-parameter is allowed
-                       //
-                       var tparam_l = l as TypeParameterSpec;
-                       var tparam_r = r as TypeParameterSpec;
-                       if (tparam_l != null) {
-                               if (right is NullLiteral && !tparam_l.HasSpecialStruct) {
-                                       left = new BoxedCast (left, ec.BuiltinTypes.Object);
-                                       return this;
-                               }
+                       if (!primitives_only) {
 
-                               if (!tparam_l.IsReferenceType)
-                                       return null;
+                               //
+                               // a, Both operands are reference-type values or the value null
+                               // b, One operand is a value of type T where T is a type-parameter and
+                               // the other operand is the value null. Furthermore T does not have the
+                               // value type constraint
+                               //
+                               // LAMESPEC: Very confusing details in the specification, basically any
+                               // reference like type-parameter is allowed
+                               //
+                               var tparam_l = l as TypeParameterSpec;
+                               var tparam_r = r as TypeParameterSpec;
+                               if (tparam_l != null) {
+                                       if (right is NullLiteral) {
+                                               if (tparam_l.GetEffectiveBase ().BuiltinType == BuiltinTypeSpec.Type.ValueType)
+                                                       return null;
 
-                               l = tparam_l.GetEffectiveBase ();
-                               left = new BoxedCast (left, l);
-                       } else if (left is NullLiteral && tparam_r == null) {
-                               if (!TypeSpec.IsReferenceType (r) || r.Kind == MemberKind.InternalCompilerType)
-                                       return null;
+                                               left = new BoxedCast (left, ec.BuiltinTypes.Object);
+                                               return this;
+                                       }
 
-                               return this;
-                       }
+                                       if (!tparam_l.IsReferenceType)
+                                               return null;
 
-                       if (tparam_r != null) {
-                               if (left is NullLiteral && !tparam_r.HasSpecialStruct) {
-                                       right = new BoxedCast (right, ec.BuiltinTypes.Object);
-                                       return this;
+                                       l = tparam_l.GetEffectiveBase ();
+                                       left = new BoxedCast (left, l);
+                               } else if (left is NullLiteral && tparam_r == null) {
+                                       if (TypeSpec.IsReferenceType (r))
+                                               return this;
+
+                                       if (r.Kind == MemberKind.InternalCompilerType)
+                                               return null;
                                }
 
-                               if (!tparam_r.IsReferenceType)
-                                       return null;
+                               if (tparam_r != null) {
+                                       if (left is NullLiteral) {
+                                               if (tparam_r.GetEffectiveBase ().BuiltinType == BuiltinTypeSpec.Type.ValueType)
+                                                       return null;
 
-                               r = tparam_r.GetEffectiveBase ();
-                               right = new BoxedCast (right, r);
-                       } else if (right is NullLiteral) {
-                               if (!TypeSpec.IsReferenceType (l) || l.Kind == MemberKind.InternalCompilerType)
-                                       return null;
+                                               right = new BoxedCast (right, ec.BuiltinTypes.Object);
+                                               return this;
+                                       }
 
-                               return this;
-                       }
+                                       if (!tparam_r.IsReferenceType)
+                                               return null;
 
-                       bool no_arg_conv = false;
+                                       r = tparam_r.GetEffectiveBase ();
+                                       right = new BoxedCast (right, r);
+                               } else if (right is NullLiteral) {
+                                       if (TypeSpec.IsReferenceType (l))
+                                               return this;
 
-                       //
-                       // LAMESPEC: method groups can be compared when they convert to other side delegate
-                       //
-                       if (l.IsDelegate) {
-                               if (right.eclass == ExprClass.MethodGroup) {
-                                       result = Convert.ImplicitConversion (ec, right, l, loc);
+                                       if (l.Kind == MemberKind.InternalCompilerType)
+                                               return null;
+                               }
+
+                               //
+                               // LAMESPEC: method groups can be compared when they convert to other side delegate
+                               //
+                               if (l.IsDelegate) {
+                                       if (right.eclass == ExprClass.MethodGroup) {
+                                               result = Convert.ImplicitConversion (ec, right, l, loc);
+                                               if (result == null)
+                                                       return null;
+
+                                               right = result;
+                                               r = l;
+                                       } else if (r.IsDelegate && l != r) {
+                                               return null;
+                                       }
+                               } else if (left.eclass == ExprClass.MethodGroup && r.IsDelegate) {
+                                       result = Convert.ImplicitConversionRequired (ec, left, r, loc);
                                        if (result == null)
                                                return null;
 
-                                       right = result;
-                                       r = l;
-                               } else if (r.IsDelegate && l != r) {
-                                       return null;
+                                       left = result;
+                                       l = r;
+                               } else {
+                                       no_arg_conv = l == r && !l.IsStruct;
                                }
-                       } else if (left.eclass == ExprClass.MethodGroup && r.IsDelegate) {
-                               result = Convert.ImplicitConversionRequired (ec, left, r, loc);
-                               if (result == null)
-                                       return null;
-
-                               left = result;
-                               l = r;
-                       } else {
-                               no_arg_conv = l == r && !l.IsStruct;
                        }
 
                        //
@@ -3438,9 +3939,34 @@ namespace Mono.CSharp
                        // not apply when both operands are of same reference type
                        //
                        if (r.BuiltinType != BuiltinTypeSpec.Type.Object && l.BuiltinType != BuiltinTypeSpec.Type.Object) {
-                               result = ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryEquality, no_arg_conv, null);
+                               result = ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryEquality, no_arg_conv);  
                                if (result != null)
                                        return result;
+
+                               //
+                               // Now try lifted version of predefined operators
+                               //
+                               if (no_arg_conv && !l.IsNullableType) {
+                                       //
+                                       // Optimizes cases which won't match
+                                       //
+                               } else {
+                                       result = ResolveOperatorPredefined (ec, ec.Module.OperatorsBinaryEqualityLifted, no_arg_conv);
+                                       if (result != null)
+                                               return result;
+                               }
+
+                               //
+                               // The == and != operators permit one operand to be a value of a nullable
+                               // type and 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 ((l.IsNullableType && right.IsNull) || (r.IsNullableType && left.IsNull)) {
+                                       var lifted = new Nullable.LiftedBinaryOperator (this);
+                                       lifted.Left = left;
+                                       lifted.Right = right;
+                                       return lifted.Resolve (ec);
+                               }
                        }
 
                        //
@@ -3508,13 +4034,13 @@ namespace Mono.CSharp
                                return this;
                        }
 
-                       return ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryUnsafe, false, null);
+                       return ResolveOperatorPredefined (ec, ec.BuiltinTypes.OperatorsBinaryUnsafe, false);
                }
 
                //
                // Build-in operators method overloading
                //
-               protected virtual Expression ResolveOperatorPredefined (ResolveContext ec, PredefinedOperator [] operators, bool primitives_only, TypeSpec enum_type)
+               Expression ResolveOperatorPredefined (ResolveContext ec, PredefinedOperator [] operators, bool primitives_only)
                {
                        PredefinedOperator best_operator = null;
                        TypeSpec l = left.Type;
@@ -3545,7 +4071,7 @@ namespace Mono.CSharp
 
                                if (best_operator == null) {
                                        ec.Report.Error (34, loc, "Operator `{0}' is ambiguous on operands of type `{1}' and `{2}'",
-                                               OperName (oper), TypeManager.CSharpName (l), TypeManager.CSharpName (r));
+                                               OperName (oper), l.GetSignatureForError (), r.GetSignatureForError ());
 
                                        best_operator = po;
                                        break;
@@ -3555,41 +4081,62 @@ namespace Mono.CSharp
                        if (best_operator == null)
                                return null;
 
-                       Expression expr = best_operator.ConvertResult (ec, this);
+                       return best_operator.ConvertResult (ec, this);
+               }
 
-                       //
-                       // Optimize &/&& constant expressions with 0 value
-                       //
-                       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)) && !(this is Nullable.LiftedBinaryOperator)) {
-                                       //
-                                       // The result is a constant with side-effect
-                                       //
-                                       Constant side_effect = rc == null ?
-                                               new SideEffectConstant (lc, right, loc) :
-                                               new SideEffectConstant (rc, left, loc);
+               //
+               // Optimize & constant expressions with 0 value
+               //
+               Expression OptimizeAndOperation (Expression expr)
+               {
+                       Constant rc = right as Constant;
+                       Constant lc = left as Constant;
+                       if ((lc != null && lc.IsDefaultValue) || (rc != null && rc.IsDefaultValue)) {
+                               //
+                               // The result is a constant with side-effect
+                               //
+                               Constant side_effect = rc == null ?
+                                       new SideEffectConstant (lc, right, loc) :
+                                       new SideEffectConstant (rc, left, loc);
 
-                                       return ReducedExpression.Create (side_effect, expr);
-                               }
+                               return ReducedExpression.Create (side_effect, expr);
                        }
 
-                       if (enum_type == null)
-                               return expr;
+                       return expr;
+               }
 
-                       //
-                       // HACK: required by enum_conversion
-                       //
-                       expr.Type = enum_type;
-                       return EmptyCast.Create (expr, enum_type);
+               //
+               // Value types can be compared with the null literal because of the lifting
+               // language rules. However the result is always true or false.
+               //
+               public Expression CreateLiftedValueTypeResult (ResolveContext rc, TypeSpec valueType)
+               {
+                       if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) {
+                               type = rc.BuiltinTypes.Bool;
+                               return this;
+                       }
+
+                       // FIXME: Handle side effect constants
+                       Constant c = new BoolConstant (rc.BuiltinTypes, Oper == Operator.Inequality, loc);
+
+                       if ((Oper & Operator.EqualityMask) != 0) {
+                               rc.Report.Warning (472, 2, loc, "The result of comparing value type `{0}' with null is always `{1}'",
+                                       valueType.GetSignatureForError (), c.GetValueAsLiteral ());
+                       } else {
+                               rc.Report.Warning (464, 2, loc, "The result of comparing type `{0}' with null is always `{1}'",
+                                       valueType.GetSignatureForError (), c.GetValueAsLiteral ());
+                       }
+
+                       return c;
                }
 
                //
                // Performs user-operator overloading
                //
-               protected virtual Expression ResolveUserOperator (ResolveContext ec, Expression left, Expression right)
+               Expression ResolveUserOperator (ResolveContext rc, Expression left, Expression right)
                {
+                       Expression oper_expr;
+
                        var op = ConvertBinaryToUserOperator (oper);
                        var l = left.Type;
                        if (l.IsNullableType)
@@ -3601,72 +4148,220 @@ namespace Mono.CSharp
                        IList<MemberSpec> left_operators = MemberCache.GetUserOperator (l, op, false);
                        IList<MemberSpec> right_operators = null;
 
-                       if (l != r) {
-                               right_operators = MemberCache.GetUserOperator (r, op, false);
-                               if (right_operators == null && left_operators == null)
-                                       return null;
-                       } else if (left_operators == null) {
-                               return null;
+                       if (l != r) {
+                               right_operators = MemberCache.GetUserOperator (r, op, false);
+                               if (right_operators == null && left_operators == null)
+                                       return null;
+                       } else if (left_operators == null) {
+                               return null;
+                       }
+
+                       Arguments args = new Arguments (2);
+                       Argument larg = new Argument (left);
+                       args.Add (larg);        
+                       Argument rarg = new Argument (right);
+                       args.Add (rarg);
+
+                       //
+                       // User-defined operator implementations always take precedence
+                       // over predefined operator implementations
+                       //
+                       if (left_operators != null && right_operators != null) {
+                               left_operators = CombineUserOperators (left_operators, right_operators);
+                       } else if (right_operators != null) {
+                               left_operators = right_operators;
+                       }
+
+                       const OverloadResolver.Restrictions restr = OverloadResolver.Restrictions.ProbingOnly |
+                               OverloadResolver.Restrictions.NoBaseMembers | OverloadResolver.Restrictions.BaseMembersIncluded;
+
+                       var res = new OverloadResolver (left_operators, restr, loc);
+
+                       var oper_method = res.ResolveOperator (rc, ref args);
+                       if (oper_method == null) {
+                               //
+                               // Logical && and || cannot be lifted
+                               //
+                               if ((oper & Operator.LogicalMask) != 0)
+                                       return null;
+
+                               //
+                               // Apply lifted user operators only for liftable types. Implicit conversion
+                               // to nullable types is not allowed
+                               //
+                               if (!IsLiftedOperatorApplicable ())
+                                       return null;
+
+                               // TODO: Cache the result in module container
+                               var lifted_methods = CreateLiftedOperators (rc, left_operators);
+                               if (lifted_methods == null)
+                                       return null;
+
+                               res = new OverloadResolver (lifted_methods, restr | OverloadResolver.Restrictions.ProbingOnly, loc);
+
+                               oper_method = res.ResolveOperator (rc, ref args);
+                               if (oper_method == null)
+                                       return null;
+
+                               MethodSpec best_original = null;
+                               foreach (MethodSpec ms in left_operators) {
+                                       if (ms.MemberDefinition == oper_method.MemberDefinition) {
+                                               best_original = ms;
+                                               break;
+                                       }
+                               }
+
+                               if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) {
+                                       //
+                                       // Expression trees use lifted notation in this case
+                                       //
+                                       this.left = Convert.ImplicitConversion (rc, left, oper_method.Parameters.Types[0], left.Location);
+                                       this.right = Convert.ImplicitConversion (rc, right, oper_method.Parameters.Types[1], left.Location);
+                               }
+
+                               var ptypes = best_original.Parameters.Types;
+
+                               if (left.IsNull || right.IsNull) {
+                                       //
+                                       // The lifted operator produces the value false if one or both operands are null for
+                                       // relational operators.
+                                       //
+                                       if ((oper & Operator.ComparisonMask) != 0) {
+                                               //
+                                               // CSC BUG: This should be different warning, csc reports CS0458 with bool? which is wrong
+                                               // because return type is actually bool
+                                               //
+                                               // For some reason CSC does not report this warning for equality operators
+                                               //
+                                               return CreateLiftedValueTypeResult (rc, left.IsNull ? ptypes [1] : ptypes [0]);
+                                       }
+
+                                       // The lifted operator produces a null value if one or both operands are null
+                                       //
+                                       if ((oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0) {
+                                               type = oper_method.ReturnType;
+                                               return Nullable.LiftedNull.CreateFromExpression (rc, this);
+                                       }
+                               }
+
+                               type = oper_method.ReturnType;
+                               var lifted = new Nullable.LiftedBinaryOperator (this);
+                               lifted.UserOperator = best_original;
+
+                               if (left.Type.IsNullableType && !ptypes[0].IsNullableType) {
+                                       lifted.UnwrapLeft = new Nullable.Unwrap (left);
+                               }
+
+                               if (right.Type.IsNullableType && !ptypes[1].IsNullableType) {
+                                       lifted.UnwrapRight = new Nullable.Unwrap (right);
+                               }
+
+                               lifted.Left = Convert.ImplicitConversion (rc, lifted.UnwrapLeft ?? left, ptypes[0], left.Location);
+                               lifted.Right = Convert.ImplicitConversion (rc, lifted.UnwrapRight ?? right, ptypes[1], right.Location);
+
+                               return lifted.Resolve (rc);
+                       }
+                       
+                       if ((oper & Operator.LogicalMask) != 0) {
+                               // TODO: CreateExpressionTree is allocated every time           
+                               oper_expr = new ConditionalLogicalOperator (oper_method, args, CreateExpressionTree,
+                                       oper == Operator.LogicalAnd, loc).Resolve (rc);
+                       } else {
+                               oper_expr = new UserOperatorCall (oper_method, args, CreateExpressionTree, loc);
+                       }
+
+                       this.left = larg.Expr;
+                       this.right = rarg.Expr;
+
+                       return oper_expr;
+               }
+
+               bool IsLiftedOperatorApplicable ()
+               {
+                       if (left.Type.IsNullableType) {
+                               if ((oper & Operator.EqualityMask) != 0)
+                                       return !right.IsNull;
+
+                               return true;
                        }
 
-                       Arguments args = new Arguments (2);
-                       Argument larg = new Argument (left);
-                       args.Add (larg);
-                       Argument rarg = new Argument (right);
-                       args.Add (rarg);
+                       if (right.Type.IsNullableType) {
+                               if ((oper & Operator.EqualityMask) != 0)
+                                       return !left.IsNull;
 
-                       //
-                       // User-defined operator implementations always take precedence
-                       // over predefined operator implementations
-                       //
-                       if (left_operators != null && right_operators != null) {
-                               left_operators = CombineUserOperators (left_operators, right_operators);
-                       } else if (right_operators != null) {
-                               left_operators = right_operators;
+                               return true;
                        }
 
-                       var res = new OverloadResolver (left_operators, OverloadResolver.Restrictions.ProbingOnly | 
-                               OverloadResolver.Restrictions.NoBaseMembers | OverloadResolver.Restrictions.BaseMembersIncluded, loc);
+                       if (TypeSpec.IsValueType (left.Type))
+                               return right.IsNull;
+
+                       if (TypeSpec.IsValueType (right.Type))
+                               return left.IsNull;
+
+                       return false;
+               }
 
-                       var oper_method = res.ResolveOperator (ec, ref args);
-                       if (oper_method == null)
+               List<MemberSpec> CreateLiftedOperators (ResolveContext rc, IList<MemberSpec> operators)
+               {
+                       var nullable_type = rc.Module.PredefinedTypes.Nullable.TypeSpec;
+                       if (nullable_type == null)
                                return null;
 
-                       var llifted = (state & State.LeftNullLifted) != 0;
-                       var rlifted = (state & State.RightNullLifted) != 0;
-                       if ((Oper & Operator.EqualityMask) != 0) {
-                               var parameters = oper_method.Parameters;
-                               // LAMESPEC: No idea why this is not allowed
-                               if ((left is Nullable.Unwrap || right is Nullable.Unwrap) && parameters.Types [0] != parameters.Types [1])
-                                       return null;
+                       //
+                       // Lifted operators permit predefined and user-defined operators that operate
+                       // on non-nullable value types to also be used with nullable forms of those types.
+                       // Lifted operators are constructed from predefined and user-defined operators
+                       // that meet certain requirements
+                       //
+                       List<MemberSpec> lifted = null;
+                       foreach (MethodSpec oper in operators) {
+                               TypeSpec rt;
+                               if ((Oper & Operator.ComparisonMask) != 0) {
+                                       //
+                                       // Result type must be of type bool for lifted comparison operators
+                                       //
+                                       rt = oper.ReturnType;
+                                       if (rt.BuiltinType != BuiltinTypeSpec.Type.Bool)
+                                               continue;
+                               } else {
+                                       if (!TypeSpec.IsNonNullableValueType (oper.ReturnType))
+                                               continue;
 
-                               // Binary operation was lifted but we have found a user operator
-                               // which requires value-type argument, we downgrade ourself back to
-                               // binary operation
-                               // LAMESPEC: The user operator is not called (it cannot be we are passing null to struct)
-                               // but compilation succeeds
-                               if ((llifted && !parameters.Types[0].IsStruct) || (rlifted && !parameters.Types[1].IsStruct)) {
-                                       state &= ~(State.LeftNullLifted | State.RightNullLifted);
+                                       rt = null;
                                }
-                       }
 
-                       Expression oper_expr;
+                               var ptypes = oper.Parameters.Types;
+                               if (!TypeSpec.IsNonNullableValueType (ptypes [0]) || !TypeSpec.IsNonNullableValueType (ptypes [1]))
+                                       continue;
 
-                       // TODO: CreateExpressionTree is allocated every time
-                       if ((oper & Operator.LogicalMask) != 0) {
-                               oper_expr = new ConditionalLogicalOperator (oper_method, args, CreateExpressionTree,
-                                       oper == Operator.LogicalAnd, loc).Resolve (ec);
-                       } else {
-                               oper_expr = new UserOperatorCall (oper_method, args, CreateExpressionTree, loc);
-                       }
+                               //
+                               // LAMESPEC: I am not sure why but for equality operators to be lifted
+                               // both types have to match
+                               //
+                               if ((Oper & Operator.EqualityMask) != 0 && ptypes [0] != ptypes [1])
+                                       continue;
+
+                               if (lifted == null)
+                                       lifted = new List<MemberSpec> ();
+
+                               //
+                               // The lifted form is constructed by adding a single ? modifier to each operand and
+                               // result type except for comparison operators where return type is bool
+                               //
+                               if (rt == null)
+                                       rt = nullable_type.MakeGenericType (rc.Module, new[] { oper.ReturnType });
 
-                       if (!llifted)
-                               this.left = larg.Expr;
+                               var parameters = ParametersCompiled.CreateFullyResolved (
+                                       nullable_type.MakeGenericType (rc.Module, new [] { ptypes[0] }),
+                                       nullable_type.MakeGenericType (rc.Module, new [] { ptypes[1] }));
 
-                       if (!rlifted)
-                               this.right = rarg.Expr;
+                               var lifted_op = new MethodSpec (oper.Kind, oper.DeclaringType, oper.MemberDefinition,
+                                       rt, parameters, oper.Modifiers);
 
-                       return oper_expr;
+                               lifted.Add (lifted_op);
+                       }
+
+                       return lifted;
                }
 
                //
@@ -3701,7 +4396,7 @@ namespace Mono.CSharp
                                } catch (OverflowException) {
                                        ec.Report.Warning (652, 2, loc,
                                                "A comparison between a constant and a variable is useless. The constant is out of the range of the variable type `{0}'",
-                                               TypeManager.CSharpName (type));
+                                               type.GetSignatureForError ());
                                }
                        }
                }
@@ -3716,6 +4411,14 @@ namespace Mono.CSharp
                /// </remarks>
                public override void EmitBranchable (EmitContext ec, Label target, bool on_true)
                {
+                       if (ec.HasSet (BuilderContext.Options.AsyncBody) && right.ContainsEmitWithAwait ()) {
+                               left = left.EmitToField (ec);
+
+                               if ((oper & Operator.LogicalMask) == 0) {
+                                       right = right.EmitToField (ec);
+                               }
+                       }
+
                        //
                        // This is more complicated than it looks, but its just to avoid
                        // duplicated tests: basically, we allow ==, !=, >, <, >= and <=
@@ -3868,11 +4571,6 @@ namespace Mono.CSharp
                }
                
                public override void Emit (EmitContext ec)
-               {
-                       EmitOperator (ec, left.Type);
-               }
-
-               protected virtual void EmitOperator (EmitContext ec, TypeSpec l)
                {
                        if (ec.HasSet (BuilderContext.Options.AsyncBody) && right.ContainsEmitWithAwait ()) {
                                left = left.EmitToField (ec);
@@ -3913,16 +4611,22 @@ namespace Mono.CSharp
                                }
                        }
 
+                       EmitOperator (ec, left, right);
+               }
+
+               public void EmitOperator (EmitContext ec, Expression left, Expression right)
+               {
                        left.Emit (ec);
                        right.Emit (ec);
-                       EmitOperatorOpcode (ec, oper, l);
+
+                       EmitOperatorOpcode (ec, oper, left.Type, right);
 
                        //
-                       // Nullable enum could require underlying type cast and we cannot simply wrap binary
-                       // expression because that would wrap lifted binary operation
+                       // Emit result enumerable conversion this way because it's quite complicated get it
+                       // to resolved tree because expression tree cannot see it.
                        //
-                       if (enum_conversion != null)
-                               enum_conversion.Emit (ec);
+                       if (enum_conversion != 0)
+                               ConvCast.Emit (ec, enum_conversion);
                }
 
                public override void EmitSideEffect (EmitContext ec)
@@ -3992,7 +4696,7 @@ namespace Mono.CSharp
                        return CreateExpressionTree (ec, null);
                }
 
-               Expression CreateExpressionTree (ResolveContext ec, Expression method)          
+               public Expression CreateExpressionTree (ResolveContext ec, Expression method)           
                {
                        string method_name;
                        bool lift_arg = false;
@@ -4220,6 +4924,11 @@ namespace Mono.CSharp
                        }
                }
 
+               public override void FlowAnalysis (FlowAnalysisContext fc)
+               {
+                       arguments.FlowAnalysis (fc);
+               }
+
                public override SLE.Expression MakeExpression (BuilderContext ctx)
                {
                        if (arguments.Count != 2)
@@ -4261,7 +4970,7 @@ namespace Mono.CSharp
                        if (op_true == null || op_false == null) {
                                ec.Report.Error (218, loc,
                                        "The type `{0}' must have operator `true' and operator `false' defined when `{1}' is used as a short circuit operator",
-                                       TypeManager.CSharpName (type), oper.GetSignatureForError ());
+                                       type.GetSignatureForError (), oper.GetSignatureForError ());
                                return null;
                        }
 
@@ -4309,7 +5018,7 @@ namespace Mono.CSharp
 
        public class PointerArithmetic : Expression {
                Expression left, right;
-               Binary.Operator op;
+               readonly Binary.Operator op;
 
                //
                // We assume that `l' is always a pointer
@@ -4441,7 +5150,7 @@ namespace Mono.CSharp
                                        if (rtype.BuiltinType == BuiltinTypeSpec.Type.Long || rtype.BuiltinType == BuiltinTypeSpec.Type.ULong)
                                                ec.Emit (OpCodes.Conv_I8);
 
-                                       Binary.EmitOperatorOpcode (ec, Binary.Operator.Multiply, rtype);
+                                       Binary.EmitOperatorOpcode (ec, Binary.Operator.Multiply, rtype, right);
                                }
 
                                if (left_const == null) {
@@ -4450,7 +5159,7 @@ namespace Mono.CSharp
                                        else if (rtype.BuiltinType == BuiltinTypeSpec.Type.ULong)
                                                ec.Emit (OpCodes.Conv_U);
 
-                                       Binary.EmitOperatorOpcode (ec, op, op_type);
+                                       Binary.EmitOperatorOpcode (ec, op, op_type, right);
                                }
                        }
                }
@@ -4589,32 +5298,8 @@ namespace Mono.CSharp
                protected override Expression DoResolve (ResolveContext ec)
                {
                        expr = expr.Resolve (ec);
-
-                       //
-                       // Unreachable code needs different resolve path. For instance for await
-                       // expression to not generate unreachable resumable statement
-                       //
-                       Constant c = expr as Constant;
-                       if (c != null && ec.CurrentBranching != null) {
-                               bool unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
-
-                               if (c.IsDefaultValue) {
-                                       ec.CurrentBranching.CurrentUsageVector.IsUnreachable = true;
-                                       true_expr = true_expr.Resolve (ec);
-                                       ec.CurrentBranching.CurrentUsageVector.IsUnreachable = unreachable;
-
-                                       false_expr = false_expr.Resolve (ec);
-                               } else {
-                                       true_expr = true_expr.Resolve (ec);
-
-                                       ec.CurrentBranching.CurrentUsageVector.IsUnreachable = true;
-                                       false_expr = false_expr.Resolve (ec);
-                                       ec.CurrentBranching.CurrentUsageVector.IsUnreachable = unreachable;
-                               }
-                       } else {
-                               true_expr = true_expr.Resolve (ec);
-                               false_expr = false_expr.Resolve (ec);
-                       }
+                       true_expr = true_expr.Resolve (ec);
+                       false_expr = false_expr.Resolve (ec);
 
                        if (true_expr == null || false_expr == null || expr == null)
                                return null;
@@ -4672,11 +5357,12 @@ namespace Mono.CSharp
                                } else {
                                        ec.Report.Error (173, true_expr.Location,
                                                "Type of conditional expression cannot be determined because there is no implicit conversion between `{0}' and `{1}'",
-                                               TypeManager.CSharpName (true_type), TypeManager.CSharpName (false_type));
+                                               true_type.GetSignatureForError (), false_type.GetSignatureForError ());
                                        return null;
                                }
-                       }                       
+                       }
 
+                       Constant c = expr as Constant;
                        if (c != null) {
                                bool is_false = c.IsDefaultValue;
 
@@ -4684,8 +5370,8 @@ namespace Mono.CSharp
                                // Don't issue the warning for constant expressions
                                //
                                if (!(is_false ? true_expr is Constant : false_expr is Constant)) {
-                                       ec.Report.Warning (429, 4, is_false ? true_expr.Location : false_expr.Location,
-                                               "Unreachable expression code detected");
+                                       // CSC: Missing warning
+                                       Warning_UnreachableExpression (ec, is_false ? true_expr.Location : false_expr.Location);
                                }
 
                                return ReducedExpression.Create (
@@ -4722,6 +5408,21 @@ namespace Mono.CSharp
                        ec.MarkLabel (end_target);
                }
 
+               public override void FlowAnalysis (FlowAnalysisContext fc)
+               {
+                       expr.FlowAnalysis (fc);
+                       var expr_fc = fc.DefiniteAssignment;
+
+                       fc.DefiniteAssignment = new DefiniteAssignmentBitSet (expr_fc);
+                       true_expr.FlowAnalysis (fc);
+                       var true_fc = fc.DefiniteAssignment;
+
+                       fc.DefiniteAssignment = new DefiniteAssignmentBitSet (expr_fc);
+                       false_expr.FlowAnalysis (fc);
+
+                       fc.DefiniteAssignment &= true_fc;
+               }
+
                protected override void CloneTo (CloneContext clonectx, Expression t)
                {
                        Conditional target = (Conditional) t;
@@ -4739,7 +5440,6 @@ namespace Mono.CSharp
                #region Abstract
                public abstract HoistedVariable GetHoistedVariable (AnonymousExpression ae);
                public abstract void SetHasAddressTaken ();
-               public abstract void VerifyAssigned (ResolveContext rc);
 
                public abstract bool IsLockedByStatement { get; set; }
 
@@ -4972,17 +5672,17 @@ namespace Mono.CSharp
 
                #endregion
 
-               public override void VerifyAssigned (ResolveContext rc)
+               public override void FlowAnalysis (FlowAnalysisContext fc)
                {
-                       VariableInfo variable_info = local_info.VariableInfo;
+                       VariableInfo variable_info = VariableInfo;
                        if (variable_info == null)
                                return;
 
-                       if (variable_info.IsAssigned (rc))
+                       if (fc.IsDefinitelyAssigned (variable_info))
                                return;
 
-                       rc.Report.Error (165, loc, "Use of unassigned local variable `{0}'", Name);
-                       variable_info.SetAssigned (rc);
+                       fc.Report.Error (165, loc, "Use of unassigned local variable `{0}'", Name);
+                       variable_info.SetAssigned (fc.DefiniteAssignment, true);
                }
 
                public override void SetHasAddressTaken ()
@@ -5019,8 +5719,6 @@ namespace Mono.CSharp
                {
                        local_info.SetIsUsed ();
 
-                       VerifyAssigned (ec);
-
                        DoResolveBase (ec);
                        return this;
                }
@@ -5035,22 +5733,22 @@ namespace Mono.CSharp
                                local_info.SetIsUsed ();
 
                        if (local_info.IsReadonly && !ec.HasAny (ResolveContext.Options.FieldInitializerScope | ResolveContext.Options.UsingInitializerScope)) {
-                               int code;
-                               string msg;
-                               if (rhs == EmptyExpression.OutAccess) {
-                                       code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
-                               } else if (rhs == EmptyExpression.LValueMemberAccess) {
-                                       code = 1654; msg = "Cannot assign to members of `{0}' because it is a `{1}'";
-                               } else if (rhs == EmptyExpression.LValueMemberOutAccess) {
-                                       code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
-                               } else if (rhs == EmptyExpression.UnaryAddress) {
-                                       code = 459; msg = "Cannot take the address of {1} `{0}'";
+                               if (rhs == EmptyExpression.LValueMemberAccess) {
+                                       // CS1654 already reported
                                } else {
-                                       code = 1656; msg = "Cannot assign to `{0}' because it is a `{1}'";
+                                       int code;
+                                       string msg;
+                                       if (rhs == EmptyExpression.OutAccess) {
+                                               code = 1657; msg = "Cannot pass `{0}' as a ref or out argument because it is a `{1}'";
+                                       } else if (rhs == EmptyExpression.LValueMemberOutAccess) {
+                                               code = 1655; msg = "Cannot pass members of `{0}' as ref or out arguments because it is a `{1}'";
+                                       } else if (rhs == EmptyExpression.UnaryAddress) {
+                                               code = 459; msg = "Cannot take the address of {1} `{0}'";
+                                       } else {
+                                               code = 1656; msg = "Cannot assign to `{0}' because it is a `{1}'";
+                                       }
+                                       ec.Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
                                }
-                               ec.Report.Error (code, loc, msg, Name, local_info.GetReadOnlyContext ());
-                       } else if (VariableInfo != null) {
-                               VariableInfo.SetAssigned (ec);
                        }
 
                        if (eclass == ExprClass.Unresolved)
@@ -5170,12 +5868,6 @@ namespace Mono.CSharp
                        Parameter.HasAddressTaken = true;
                }
 
-               void SetAssigned (ResolveContext ec)
-               {
-                       if (HasOutModifier && ec.DoFlowAnalysis)
-                               ec.CurrentBranching.SetAssigned (VariableInfo);
-               }
-
                bool DoResolveBase (ResolveContext ec)
                {
                        if (eclass != ExprClass.Unresolved)
@@ -5236,24 +5928,11 @@ namespace Mono.CSharp
                        return Parameter.ExpressionTreeVariableReference ();
                }
 
-               //
-               // Notice that for ref/out parameters, the type exposed is not the
-               // same type exposed externally.
-               //
-               // for "ref int a":
-               //   externally we expose "int&"
-               //   here we expose       "int".
-               //
-               // We record this in "is_ref".  This means that the type system can treat
-               // the type as it is expected, but when we generate the code, we generate
-               // the alternate kind of code.
-               //
                protected override Expression DoResolve (ResolveContext ec)
                {
                        if (!DoResolveBase (ec))
                                return null;
 
-                       VerifyAssigned (ec);
                        return this;
                }
 
@@ -5262,19 +5941,23 @@ namespace Mono.CSharp
                        if (!DoResolveBase (ec))
                                return null;
 
-                       SetAssigned (ec);
+                       if (Parameter.HoistedVariant != null)
+                               Parameter.HoistedVariant.IsAssigned = true;
+
                        return base.DoResolveLValue (ec, right_side);
                }
 
-               public override void VerifyAssigned (ResolveContext rc)
+               public override void FlowAnalysis (FlowAnalysisContext fc)
                {
-                       // HACK: Variables are not captured in probing mode
-                       if (rc.IsInProbingMode)
+                       VariableInfo variable_info = VariableInfo;
+                       if (variable_info == null)
                                return;
 
-                       if (HasOutModifier && !VariableInfo.IsAssigned (rc)) {
-                               rc.Report.Error (269, loc, "Use of unassigned out parameter `{0}'", Name);
-                       }
+                       if (fc.IsDefinitelyAssigned (variable_info))
+                               return;
+
+                       fc.Report.Error (269, loc, "Use of unassigned out parameter `{0}'", Name);
+                       fc.SetVariableAssigned (variable_info);
                }
        }
        
@@ -5292,8 +5975,7 @@ namespace Mono.CSharp
                        this.expr = expr;               
                        this.arguments = arguments;
                        if (expr != null) {
-                               var ma = expr as MemberAccess;
-                               loc = ma != null ? ma.GetLeftExpressionLocation () : expr.Location;
+                               loc = expr.Location;
                        }
                }
 
@@ -5315,8 +5997,59 @@ namespace Mono.CSharp
                                return mg;
                        }
                }
+
+               public override Location StartLocation {
+                       get {
+                               return expr.StartLocation;
+                       }
+               }
+
                #endregion
 
+               public override MethodGroupExpr CanReduceLambda (AnonymousMethodBody body)
+               {
+                       if (MethodGroup == null)
+                               return null;
+
+                       var candidate = MethodGroup.BestCandidate;
+                       if (candidate == null || !(candidate.IsStatic || Exp is This))
+                               return null;
+
+                       var args_count = arguments == null ? 0 : arguments.Count;
+                       if (args_count != body.Parameters.Count)
+                               return null;
+
+                       var lambda_parameters = body.Block.Parameters.FixedParameters;
+                       for (int i = 0; i < args_count; ++i) {
+                               var pr = arguments[i].Expr as ParameterReference;
+                               if (pr == null)
+                                       return null;
+
+                               if (lambda_parameters[i] != pr.Parameter)
+                                       return null;
+
+                               if ((lambda_parameters[i].ModFlags & Parameter.Modifier.RefOutMask) != (pr.Parameter.ModFlags & Parameter.Modifier.RefOutMask))
+                                       return null;
+                       }
+
+                       var emg = MethodGroup as ExtensionMethodGroupExpr;
+                       if (emg != null) {
+                               var mg = MethodGroupExpr.CreatePredefined (candidate, candidate.DeclaringType, MethodGroup.Location);
+                               if (candidate.IsGeneric) {
+                                       var targs = new TypeExpression [candidate.Arity];
+                                       for (int i = 0; i < targs.Length; ++i) {
+                                               targs[i] = new TypeExpression (candidate.TypeArguments[i], MethodGroup.Location);
+                                       }
+
+                                       mg.SetTypeArguments (null, new TypeArguments (targs));
+                               }
+
+                               return mg;
+                       }
+
+                       return MethodGroup;
+               }
+
                protected override void CloneTo (CloneContext clonectx, Expression t)
                {
                        Invocation target = (Invocation) t;
@@ -5357,7 +6090,7 @@ namespace Mono.CSharp
                                if (member_expr != null)
                                        member_expr = member_expr.Resolve (ec);
                        } else {
-                               member_expr = expr.Resolve (ec, ResolveFlags.VariableOrValue | ResolveFlags.MethodGroup);
+                               member_expr = expr.Resolve (ec);
                        }
 
                        if (member_expr == null)
@@ -5386,7 +6119,7 @@ namespace Mono.CSharp
                                } else {
                                        if (member_expr is RuntimeValueExpression) {
                                                ec.Report.Error (Report.RuntimeErrorId, loc, "Cannot invoke a non-delegate type `{0}'",
-                                                       member_expr.Type.GetSignatureForError ()); ;
+                                                       member_expr.Type.GetSignatureForError ());
                                                return null;
                                        }
 
@@ -5488,6 +6221,17 @@ namespace Mono.CSharp
                        return mg.OverloadResolve (ec, ref arguments, null, OverloadResolver.Restrictions.None);
                }
 
+               public override void FlowAnalysis (FlowAnalysisContext fc)
+               {
+                       if (mg.IsConditionallyExcluded)
+                               return;
+
+                       mg.FlowAnalysis (fc);
+
+                       if (arguments != null)
+                               arguments.FlowAnalysis (fc);
+               }
+
                public override string GetSignatureForError ()
                {
                        return mg.GetSignatureForError ();
@@ -5528,6 +6272,9 @@ namespace Mono.CSharp
 
                public override void Emit (EmitContext ec)
                {
+                       if (mg.IsConditionallyExcluded)
+                               return;
+
                        mg.EmitCall (ec, arguments);
                }
                
@@ -5705,7 +6452,7 @@ namespace Mono.CSharp
 
                        if (type.IsPointer) {
                                ec.Report.Error (1919, loc, "Unsafe type `{0}' cannot be used in an object creation expression",
-                                       TypeManager.CSharpName (type));
+                                       type.GetSignatureForError ());
                                return null;
                        }
 
@@ -5728,13 +6475,13 @@ namespace Mono.CSharp
                                if ((tparam.SpecialConstraint & (SpecialConstraint.Struct | SpecialConstraint.Constructor)) == 0 && !TypeSpec.IsValueType (tparam)) {
                                        ec.Report.Error (304, loc,
                                                "Cannot create an instance of the variable type `{0}' because it does not have the new() constraint",
-                                               TypeManager.CSharpName (type));
+                                               type.GetSignatureForError ());
                                }
 
                                if ((arguments != null) && (arguments.Count != 0)) {
                                        ec.Report.Error (417, loc,
                                                "`{0}': cannot provide arguments when creating an instance of a variable type",
-                                               TypeManager.CSharpName (type));
+                                               type.GetSignatureForError ());
                                }
 
                                return this;
@@ -5742,7 +6489,7 @@ namespace Mono.CSharp
 
                        if (type.IsStatic) {
                                ec.Report.SymbolRelatedToPreviousError (type);
-                               ec.Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", TypeManager.CSharpName (type));
+                               ec.Report.Error (712, loc, "Cannot create an instance of the static class `{0}'", type.GetSignatureForError ());
                                return null;
                        }
 
@@ -5754,7 +6501,7 @@ namespace Mono.CSharp
                                }
                                
                                ec.Report.SymbolRelatedToPreviousError (type);
-                               ec.Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", TypeManager.CSharpName (type));
+                               ec.Report.Error (144, loc, "Cannot create an instance of the abstract class or interface `{0}'", type.GetSignatureForError ());
                                return null;
                        }
 
@@ -5875,14 +6622,16 @@ namespace Mono.CSharp
                                }
 
                                if (vr != null) {
+                                       ec.MarkCallEntry (loc);
                                        ec.Emit (OpCodes.Call, method);
                                        return false;
                                }
                        }
                        
                        if (type is TypeParameterSpec)
-                               return DoEmitTypeParameter (ec);                        
+                               return DoEmitTypeParameter (ec);
 
+                       ec.MarkCallEntry (loc);
                        ec.Emit (OpCodes.Newobj, method);
                        return true;
                }
@@ -5911,6 +6660,12 @@ namespace Mono.CSharp
                                ec.Emit (OpCodes.Pop);
                }
 
+               public override void FlowAnalysis (FlowAnalysisContext fc)
+               {
+                       if (arguments != null)
+                               arguments.FlowAnalysis (fc);
+               }
+
                public void AddressOf (EmitContext ec, AddressOp mode)
                {
                        EmitAddressOf (ec, mode);
@@ -5976,7 +6731,7 @@ namespace Mono.CSharp
        public class ArrayInitializer : Expression
        {
                List<Expression> elements;
-               BlockVariableDeclaration variable;
+               BlockVariable variable;
 
                public ArrayInitializer (List<Expression> init, Location loc)
                {
@@ -6012,7 +6767,7 @@ namespace Mono.CSharp
                        }
                }
 
-               public BlockVariableDeclaration VariableDeclaration {
+               public BlockVariable VariableDeclaration {
                        get {
                                return variable;
                        }
@@ -6071,6 +6826,11 @@ namespace Mono.CSharp
                {
                        throw new InternalErrorException ("Missing Resolve call");
                }
+
+               public override void FlowAnalysis (FlowAnalysisContext fc)
+               {
+                       throw new InternalErrorException ("Missing Resolve call");
+               }
                
                public override object Accept (StructuralVisitor visitor)
                {
@@ -6100,7 +6860,7 @@ namespace Mono.CSharp
                protected List<Expression> arguments;
                
                protected TypeSpec array_element_type;
-               int num_arguments = 0;
+               int num_arguments;
                protected int dimensions;
                protected readonly ComposedTypeSpecifier rank;
                Expression first_emit;
@@ -6332,6 +7092,17 @@ namespace Mono.CSharp
                        ec.Report.Error (248, loc, "Cannot create an array with a negative size");
                }
 
+               public override void FlowAnalysis (FlowAnalysisContext fc)
+               {
+                       foreach (var arg in arguments)
+                               arg.FlowAnalysis (fc);
+
+                       if (array_data != null) {
+                               foreach (var ad in array_data)
+                                       ad.FlowAnalysis (fc);
+                       }
+               }
+
                bool InitializersContainAwait ()
                {
                        if (array_data == null)
@@ -6976,7 +7747,7 @@ namespace Mono.CSharp
 
                #endregion
 
-               public void CheckStructThisDefiniteAssignment (ResolveContext rc)
+               void CheckStructThisDefiniteAssignment (FlowAnalysisContext fc)
                {
                        //
                        // It's null for all cases when we don't need to check `this'
@@ -6985,13 +7756,10 @@ namespace Mono.CSharp
                        if (variable_info == null)
                                return;
 
-                       if (rc.OmitStructFlowAnalysis)
+                       if (fc.IsDefinitelyAssigned (variable_info))
                                return;
 
-                       if (!variable_info.IsAssigned (rc)) {
-                               rc.Report.Error (188, loc,
-                                       "The `this' object cannot be used before all of its fields are assigned to");
-                       }
+                       fc.Report.Error (188, loc, "The `this' object cannot be used before all of its fields are assigned to");
                }
 
                protected virtual void Error_ThisNotAvailable (ResolveContext ec)
@@ -7007,6 +7775,11 @@ namespace Mono.CSharp
                        }
                }
 
+               public override void FlowAnalysis (FlowAnalysisContext fc)
+               {
+                       CheckStructThisDefiniteAssignment (fc);
+               }
+
                public override HoistedVariable GetHoistedVariable (AnonymousExpression ae)
                {
                        if (ae == null)
@@ -7064,9 +7837,6 @@ namespace Mono.CSharp
                protected override Expression DoResolve (ResolveContext ec)
                {
                        ResolveBase (ec);
-
-                       CheckStructThisDefiniteAssignment (ec);
-
                        return this;
                }
 
@@ -7075,9 +7845,6 @@ namespace Mono.CSharp
                        if (eclass == ExprClass.Unresolved)
                                ResolveBase (ec);
 
-                       if (variable_info != null)
-                               variable_info.SetAssigned (ec);
-
                        if (type.IsClass){
                                if (right_side == EmptyExpression.UnaryAddress)
                                        ec.Report.Error (459, loc, "Cannot take the address of `this' because it is read-only");
@@ -7113,10 +7880,6 @@ namespace Mono.CSharp
                {
                        // Nothing
                }
-
-               public override void VerifyAssigned (ResolveContext rc)
-               {
-               }
                
                public override object Accept (StructuralVisitor visitor)
                {
@@ -7742,7 +8505,7 @@ namespace Mono.CSharp
                        if (!ec.IsUnsafe) {
                                ec.Report.Error (233, loc,
                                        "`{0}' does not have a predefined size, therefore sizeof can only be used in an unsafe context (consider using System.Runtime.InteropServices.Marshal.SizeOf)",
-                                       TypeManager.CSharpName (type_queried));
+                                       type_queried.GetSignatureForError ());
                        }
                        
                        type = ec.BuiltinTypes.Int;
@@ -7891,52 +8654,33 @@ namespace Mono.CSharp
                        }
                }
 
-               protected override Expression DoResolve (ResolveContext rc)
-               {
-                       var e = DoResolveName (rc, null);
-
-                       if (!rc.OmitStructFlowAnalysis) {
-                               var fe = e as FieldExpr;
-                               if (fe != null) {
-                                       fe.VerifyAssignedStructField (rc, null);
-                               }
+               public override Location StartLocation {
+                       get {
+                               return expr == null ? loc : expr.StartLocation;
                        }
-
-                       return e;
                }
 
-               public override Expression DoResolveLValue (ResolveContext rc, Expression rhs)
+               protected override Expression DoResolve (ResolveContext rc)
                {
-                       var e = DoResolveName (rc, rhs);
-
-                       if (!rc.OmitStructFlowAnalysis) {
-                               var fe = e as FieldExpr;
-                               if (fe != null && fe.InstanceExpression is FieldExpr) {
-                                       fe = (FieldExpr) fe.InstanceExpression;
-                                       fe.VerifyAssignedStructField (rc, rhs);
-                               }
-                       }
+                       var e = LookupNameExpression (rc, MemberLookupRestrictions.ReadAccess);
+                       if (e != null)
+                               e = e.Resolve (rc, ResolveFlags.VariableOrValue | ResolveFlags.Type | ResolveFlags.MethodGroup);
 
                        return e;
                }
 
-               Expression DoResolveName (ResolveContext rc, Expression right_side)
+               public override Expression DoResolveLValue (ResolveContext rc, Expression rhs)
                {
-                       Expression e = LookupNameExpression (rc, right_side == null ? MemberLookupRestrictions.ReadAccess : MemberLookupRestrictions.None);
-                       if (e == null)
-                               return null;
+                       var e = LookupNameExpression (rc, MemberLookupRestrictions.None);
 
-                       if (right_side != null) {
-                               if (e is TypeExpr) {
-                                       e.Error_UnexpectedKind (rc, ResolveFlags.VariableOrValue, loc);
-                                       return null;
-                               }
-
-                               e = e.ResolveLValue (rc, right_side);
-                       } else {
-                               e = e.Resolve (rc, ResolveFlags.VariableOrValue | ResolveFlags.Type);
+                       if (e is TypeExpr) {
+                               e.Error_UnexpectedKind (rc, ResolveFlags.VariableOrValue, loc);
+                               return null;
                        }
 
+                       if (e != null)
+                               e = e.ResolveLValue (rc, rhs);
+
                        return e;
                }
 
@@ -7948,18 +8692,6 @@ namespace Mono.CSharp
                                expr.Error_OperatorCannotBeApplied (rc, loc, ".", type);
                }
 
-               public Location GetLeftExpressionLocation ()
-               {
-                       Expression expr = LeftExpression;
-                       MemberAccess ma = expr as MemberAccess;
-                       while (ma != null && ma.LeftExpression != null) {
-                               expr = ma.LeftExpression;
-                               ma = expr as MemberAccess;
-                       }
-
-                       return expr == null ? Location : expr.Location;
-               }
-
                public static bool IsValidDotExpression (TypeSpec type)
                {
                        const MemberKind dot_kinds = MemberKind.Class | MemberKind.Struct | MemberKind.Delegate | MemberKind.Enum |
@@ -7973,32 +8705,22 @@ namespace Mono.CSharp
                        var sn = expr as SimpleName;
                        const ResolveFlags flags = ResolveFlags.VariableOrValue | ResolveFlags.Type;
 
-                       //
-                       // Resolve the expression with flow analysis turned off, we'll do the definite
-                       // assignment checks later.  This is because we don't know yet what the expression
-                       // will resolve to - it may resolve to a FieldExpr and in this case we must do the
-                       // definite assignment check on the actual field and not on the whole struct.
-                       //
-                       using (rc.Set (ResolveContext.Options.OmitStructFlowAnalysis)) {
-                               if (sn != null) {
-                                       expr = sn.LookupNameExpression (rc, MemberLookupRestrictions.ReadAccess | MemberLookupRestrictions.ExactArity);
+                       if (sn != null) {
+                               expr = sn.LookupNameExpression (rc, MemberLookupRestrictions.ReadAccess | MemberLookupRestrictions.ExactArity);
 
-                                       //
-                                       // Resolve expression which does have type set as we need expression type
-                                       // with disable flow analysis as we don't know whether left side expression
-                                       // is used as variable or type
-                                       //
-                                       if (expr is VariableReference || expr is ConstantExpr || expr is Linq.TransparentMemberAccess) {
-                                               using (rc.With (ResolveContext.Options.DoFlowAnalysis, false)) {
-                                                       expr = expr.Resolve (rc);
-                                               }
-                                       } else if (expr is TypeParameterExpr) {
-                                               expr.Error_UnexpectedKind (rc, flags, sn.Location);
-                                               expr = null;
-                                       }
-                               } else {
-                                       expr = expr.Resolve (rc, flags);
+                               //
+                               // Resolve expression which does have type set as we need expression type
+                               // with disable flow analysis as we don't know whether left side expression
+                               // is used as variable or type
+                               //
+                               if (expr is VariableReference || expr is ConstantExpr || expr is Linq.TransparentMemberAccess) {
+                                       expr = expr.Resolve (rc);
+                               } else if (expr is TypeParameterExpr) {
+                                       expr.Error_UnexpectedKind (rc, flags, sn.Location);
+                                       expr = null;
                                }
+                       } else {
+                               expr = expr.Resolve (rc, flags);
                        }
 
                        if (expr == null)
@@ -8026,16 +8748,6 @@ namespace Mono.CSharp
                                if (me != null)
                                        me.ResolveInstanceExpression (rc, null);
 
-                               //
-                               // Run defined assigned checks on expressions resolved with
-                               // disabled flow-analysis
-                               //
-                               if (sn != null) {
-                                       var vr = expr as VariableReference;
-                                       if (vr != null)
-                                               vr.VerifyAssigned (rc);
-                               }
-
                                Arguments args = new Arguments (1);
                                args.Add (new Argument (expr));
                                return new DynamicMemberBinder (Name, args, loc);
@@ -8066,16 +8778,6 @@ namespace Mono.CSharp
                                                                emg.SetTypeArguments (rc, targs);
                                                        }
 
-                                                       //
-                                                       // Run defined assigned checks on expressions resolved with
-                                                       // disabled flow-analysis
-                                                       //
-                                                       if (sn != null && !errorMode) {
-                                                               var vr = expr as VariableReference;
-                                                               if (vr != null)
-                                                                       vr.VerifyAssigned (rc);
-                                                       }
-
                                                        // TODO: it should really skip the checks bellow
                                                        return emg.Resolve (rc);
                                                }
@@ -8148,16 +8850,6 @@ namespace Mono.CSharp
                                me.SetTypeArguments (rc, targs);
                        }
 
-                       //
-                       // Run defined assigned checks on expressions resolved with
-                       // disabled flow-analysis
-                       //
-                       if (sn != null && !(me is FieldExpr && TypeSpec.IsValueType (expr_type))) {
-                               var vr = expr as VariableReference;
-                               if (vr != null)
-                                       vr.VerifyAssigned (rc);
-                       }
-
                        return me;
                }
 
@@ -8258,13 +8950,13 @@ namespace Mono.CSharp
                        var nested = MemberCache.FindNestedType (expr_type, Name, -System.Math.Max (1, Arity));
 
                        if (nested != null) {
-                               Error_TypeArgumentsCannotBeUsed (rc, nested, Arity, expr.Location);
+                               Error_TypeArgumentsCannotBeUsed (rc, nested, expr.Location);
                                return;
                        }
 
                        var any_other_member = MemberLookup (rc, false, expr_type, Name, 0, MemberLookupRestrictions.None, loc);
                        if (any_other_member != null) {
-                               any_other_member.Error_UnexpectedKind (rc, any_other_member, "type", any_other_member.ExprClassName, loc);
+                               Error_UnexpectedKind (rc, any_other_member, "type", any_other_member.ExprClassName, loc);
                                return;
                        }
 
@@ -8282,7 +8974,7 @@ namespace Mono.CSharp
                        if (ec.Module.Compiler.Settings.Version > LanguageVersion.ISO_2 && !ec.IsRuntimeBinder && MethodGroupExpr.IsExtensionMethodArgument (expr)) {
                                ec.Report.SymbolRelatedToPreviousError (type);
 
-                               var cand = ec.Module.GlobalRootNamespace.FindExtensionMethodNamespaces (ec, type, name, Arity);
+                               var cand = ec.Module.GlobalRootNamespace.FindExtensionMethodNamespaces (ec, name, Arity);
                                string missing;
                                // a using directive or an assembly reference
                                if (cand != null) {
@@ -8370,6 +9062,11 @@ namespace Mono.CSharp
                                Expr.EmitBranchable (ec, target, on_true);
                }
 
+               public override void FlowAnalysis (FlowAnalysisContext fc)
+               {
+                       Expr.FlowAnalysis (fc);
+               }
+
                public override SLE.Expression MakeExpression (BuilderContext ctx)
                {
                        using (ctx.With (BuilderContext.Options.CheckedScope, true)) {
@@ -8442,6 +9139,11 @@ namespace Mono.CSharp
                                Expr.EmitBranchable (ec, target, on_true);
                }
 
+               public override void FlowAnalysis (FlowAnalysisContext fc)
+               {
+                       Expr.FlowAnalysis (fc);
+               }
+
                protected override void CloneTo (CloneContext clonectx, Expression t)
                {
                        UnCheckedExpr target = (UnCheckedExpr) t;
@@ -8473,6 +9175,12 @@ namespace Mono.CSharp
                        this.Arguments = args;
                }
 
+               public override Location StartLocation {
+                       get {
+                               return Expr.StartLocation;
+                       }
+               }
+
                public override bool ContainsEmitWithAwait ()
                {
                        return Expr.ContainsEmitWithAwait () || Arguments.ContainsEmitWithAwait ();
@@ -8574,6 +9282,12 @@ namespace Mono.CSharp
                        Report.Error (1742, na.Location, "An element access expression cannot use named argument");
                }
 
+               public override void FlowAnalysis (FlowAnalysisContext fc)
+               {
+                       Expr.FlowAnalysis (fc);
+                       Arguments.FlowAnalysis (fc);
+               }
+
                public override string GetSignatureForError ()
                {
                        return Expr.GetSignatureForError ();
@@ -8676,6 +9390,11 @@ namespace Mono.CSharp
                        ec.Report.Warning (251, 2, loc, "Indexing an array with a negative index (array indices always start at zero)");
                }
 
+               public override void FlowAnalysis (FlowAnalysisContext fc)
+               {
+                       ea.FlowAnalysis (fc);
+               }
+
                //
                // Load the array arguments into the stack.
                //
@@ -8973,6 +9692,13 @@ namespace Mono.CSharp
                        }
                }
 
+               public override void FlowAnalysis (FlowAnalysisContext fc)
+               {
+                       // TODO: Check the order
+                       base.FlowAnalysis (fc);
+                       arguments.FlowAnalysis (fc);
+               }
+
                public override string GetSignatureForError ()
                {
                        return best_candidate.GetSignatureForError ();
@@ -9379,9 +10105,15 @@ namespace Mono.CSharp
                public override void Emit (EmitContext ec)
                {
                        source.Emit (ec);
+                       ec.MarkCallEntry (loc);
                        ec.Emit (OpCodes.Call, method);
                }
 
+               public override void FlowAnalysis (FlowAnalysisContext fc)
+               {
+                       source.FlowAnalysis (fc);
+               }
+
                public override string GetSignatureForError ()
                {
                        return TypeManager.CSharpSignature (method);
@@ -9895,6 +10627,12 @@ namespace Mono.CSharp
                        this.loc = loc;
                }
 
+               public CollectionElementInitializer (Location loc)
+                       : base (null, null)
+               {
+                       this.loc = loc;
+               }
+
                public override Expression CreateExpressionTree (ResolveContext ec)
                {
                        Arguments args = new Arguments (2);
@@ -10021,8 +10759,8 @@ namespace Mono.CSharp
                                                        ec.Report.Error (1922, loc, "A field or property `{0}' cannot be initialized with a collection " +
                                                                "object initializer because type `{1}' does not implement `{2}' interface",
                                                                ec.CurrentInitializerVariable.GetSignatureForError (),
-                                                               TypeManager.CSharpName (ec.CurrentInitializerVariable.Type),
-                                                               TypeManager.CSharpName (ec.BuiltinTypes.IEnumerable));
+                                                               ec.CurrentInitializerVariable.Type.GetSignatureForError (),
+                                                               ec.BuiltinTypes.IEnumerable.GetSignatureForError ());
                                                        return null;
                                                }
                                                is_collection_initialization = true;
@@ -10056,7 +10794,7 @@ namespace Mono.CSharp
                        if (is_collection_initialization) {
                                if (TypeManager.HasElementType (type)) {
                                        ec.Report.Error (1925, loc, "Cannot initialize object of type `{0}' with a collection initializer",
-                                               TypeManager.CSharpName (type));
+                                               type.GetSignatureForError ());
                                }
                        }
 
@@ -10077,6 +10815,12 @@ namespace Mono.CSharp
                                e.EmitStatement (ec);
                        }
                }
+
+               public override void FlowAnalysis (FlowAnalysisContext fc)
+               {
+                       foreach (var initializer in initializers)
+                               initializer.FlowAnalysis (fc);
+               }
        }
        
        //
@@ -10271,6 +11015,12 @@ namespace Mono.CSharp
                        return instance;
                }
 
+               public override void FlowAnalysis (FlowAnalysisContext fc)
+               {
+                       base.FlowAnalysis (fc);
+                       initializers.FlowAnalysis (fc);
+               }
+
                public override object Accept (StructuralVisitor visitor)
                {
                        return visitor.Visit (this);
@@ -10393,11 +11143,6 @@ namespace Mono.CSharp
                        eclass = ExprClass.Value;
                        return this;
                }
-
-               public override void EmitStatement (EmitContext ec)
-               {
-                       base.EmitStatement (ec);
-               }
                
                public override object Accept (StructuralVisitor visitor)
                {