X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fmcs%2Fnullable.cs;h=73f711a7e50394d00734d8c98ec2cc806d6582ce;hb=c055004266633e8bfbeaacee5b5c1c9cb574810d;hp=b29ecca7058b1ab9bde60f1307950a9f35396d1a;hpb=0fe6ab207986019717ebce9fb836b81aa0b2b17e;p=mono.git diff --git a/mcs/mcs/nullable.cs b/mcs/mcs/nullable.cs index b29ecca7058..73f711a7e50 100644 --- a/mcs/mcs/nullable.cs +++ b/mcs/mcs/nullable.cs @@ -5,10 +5,10 @@ // Miguel de Icaza (miguel@ximian.com) // Marek Safar (marek.safar@gmail.com) // -// Licensed under the terms of the GNU GPL +// Dual licensed under the terms of the MIT X11 or GNU GPL // -// (C) 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) -// (C) 2004 Novell, Inc +// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) +// Copyright 2004-2008 Novell, Inc // using System; @@ -16,8 +16,8 @@ using System.Reflection; using System.Reflection.Emit; using System.Collections; -namespace Mono.CSharp { - +namespace Mono.CSharp.Nullable +{ public class NullableType : TypeExpr { Expression underlying; @@ -34,14 +34,6 @@ namespace Mono.CSharp { : this (new TypeExpression (type, loc), loc) { } - public override string Name { - get { return underlying.ToString () + "?"; } - } - - public override string FullName { - get { return underlying.ToString () + "?"; } - } - protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec) { TypeArguments args = new TypeArguments (loc); @@ -57,920 +49,1124 @@ namespace Mono.CSharp { } } - public abstract class Nullable + public sealed class NullableInfo { - public sealed class NullableInfo + public readonly Type Type; + public readonly Type UnderlyingType; + public readonly MethodInfo HasValue; + public readonly MethodInfo Value; + public readonly MethodInfo GetValueOrDefault; + public readonly ConstructorInfo Constructor; + + public NullableInfo (Type type) { - public readonly Type Type; - public readonly Type UnderlyingType; - public readonly MethodInfo HasValue; - public readonly MethodInfo Value; - public readonly MethodInfo GetValueOrDefault; - public readonly ConstructorInfo Constructor; - - public NullableInfo (Type type) - { - Type = type; - UnderlyingType = TypeManager.GetTypeArguments (type) [0]; + Type = type; + UnderlyingType = TypeManager.GetTypeArguments (type) [0]; - PropertyInfo has_value_pi = TypeManager.GetPredefinedProperty (type, "HasValue", Location.Null); - PropertyInfo value_pi = TypeManager.GetPredefinedProperty (type, "Value", Location.Null); - GetValueOrDefault = TypeManager.GetPredefinedMethod (type, "GetValueOrDefault", Location.Null, Type.EmptyTypes); + PropertyInfo has_value_pi = TypeManager.GetPredefinedProperty (type, "HasValue", Location.Null); + PropertyInfo value_pi = TypeManager.GetPredefinedProperty (type, "Value", Location.Null); + GetValueOrDefault = TypeManager.GetPredefinedMethod (type, "GetValueOrDefault", Location.Null, Type.EmptyTypes); - HasValue = has_value_pi.GetGetMethod (false); - Value = value_pi.GetGetMethod (false); + HasValue = has_value_pi.GetGetMethod (false); + Value = value_pi.GetGetMethod (false); #if MS_COMPATIBLE - if (UnderlyingType.Module == CodeGen.Module.Builder) { - Type o_type = TypeManager.DropGenericTypeArguments (type); - Constructor = TypeBuilder.GetConstructor (type, - TypeManager.GetPredefinedConstructor (o_type, Location.Null, o_type.GetGenericArguments ())); - return; - } -#endif - Constructor = type.GetConstructor (new Type[] { UnderlyingType }); + if (UnderlyingType.Module == CodeGen.Module.Builder) { + Type o_type = TypeManager.DropGenericTypeArguments (type); + Constructor = TypeBuilder.GetConstructor (type, + TypeManager.GetPredefinedConstructor (o_type, Location.Null, o_type.GetGenericArguments ())); + return; } +#endif + Constructor = type.GetConstructor (new Type[] { UnderlyingType }); + } + } + + public class Unwrap : Expression, IMemoryLocation, IAssignMethod + { + Expression expr; + NullableInfo info; + + LocalTemporary temp; + + protected Unwrap (Expression expr) + { + this.expr = expr; + this.loc = expr.Location; + } + + public static Unwrap Create (Expression expr, EmitContext ec) + { + return new Unwrap (expr).Resolve (ec) as Unwrap; } - public class HasValue : Expression + public override Expression CreateExpressionTree (EmitContext ec) { - Expression expr; - NullableInfo info; + return expr.CreateExpressionTree (ec); + } - private HasValue (Expression expr) - { - this.expr = expr; - } - - public static Expression Create (Expression expr, EmitContext ec) - { - return new HasValue (expr).Resolve (ec); - } + public override Expression DoResolve (EmitContext ec) + { + if (expr == null) + return null; - public override void Emit (EmitContext ec) - { - IMemoryLocation memory_loc = expr as IMemoryLocation; - if (memory_loc == null) { - LocalTemporary temp = new LocalTemporary (expr.Type); - expr.Emit (ec); - temp.Store (ec); - memory_loc = temp; - } - memory_loc.AddressOf (ec, AddressOp.LoadStore); - ec.ig.EmitCall (OpCodes.Call, info.HasValue, null); - } + info = new NullableInfo (expr.Type); + type = info.UnderlyingType; + eclass = expr.eclass; + return this; + } + + public override Expression DoResolveLValue (EmitContext ec, Expression right_side) + { + return DoResolve (ec); + } - public override Expression DoResolve (EmitContext ec) - { - this.info = new NullableInfo (expr.Type); + public override void Emit (EmitContext ec) + { + Store (ec); + AddressOf (ec, AddressOp.LoadStore); + ec.ig.EmitCall (OpCodes.Call, info.Value, null); + } - type = TypeManager.bool_type; - eclass = expr.eclass; - return this; - } - } + public void EmitCheck (EmitContext ec) + { + Store (ec); + AddressOf (ec, AddressOp.LoadStore); + ec.ig.EmitCall (OpCodes.Call, info.HasValue, null); + } - public class Unwrap : Expression, IMemoryLocation, IAssignMethod + public void EmitGetValueOrDefault (EmitContext ec) { - Expression expr; - NullableInfo info; + Store (ec); + AddressOf (ec, AddressOp.LoadStore); + ec.ig.EmitCall (OpCodes.Call, info.GetValueOrDefault, null); + } - LocalTemporary temp; - bool has_temp; + public override bool Equals (object obj) + { + Unwrap uw = obj as Unwrap; + return uw != null && expr.Equals (uw.expr); + } - protected Unwrap (Expression expr) - { - this.expr = expr; - this.loc = expr.Location; + public Expression Original { + get { + return expr; } + } + + public override int GetHashCode () + { + return expr.GetHashCode (); + } - public static Unwrap Create (Expression expr, EmitContext ec) - { - return new Unwrap (expr).Resolve (ec) as Unwrap; + public override bool IsNull { + get { + return expr.IsNull; } - - public override Expression CreateExpressionTree (EmitContext ec) - { - return expr.CreateExpressionTree (ec); - } + } - public override Expression DoResolve (EmitContext ec) - { - if (expr == null) - return null; + void Store (EmitContext ec) + { + if (expr is VariableReference) + return; - temp = new LocalTemporary (expr.Type); + if (temp != null) + return; - info = new NullableInfo (expr.Type); - type = info.UnderlyingType; - eclass = expr.eclass; - return this; - } - - public override Expression DoResolveLValue (EmitContext ec, Expression right_side) - { - return DoResolve (ec); - } + expr.Emit (ec); + LocalVariable.Store (ec); + } - public override void Emit (EmitContext ec) - { - AddressOf (ec, AddressOp.LoadStore); - ec.ig.EmitCall (OpCodes.Call, info.Value, null); - } + public void Load (EmitContext ec) + { + if (expr is VariableReference) + expr.Emit (ec); + else + LocalVariable.Emit (ec); + } - public void EmitCheck (EmitContext ec) - { - AddressOf (ec, AddressOp.LoadStore); - ec.ig.EmitCall (OpCodes.Call, info.HasValue, null); - } + public void AddressOf (EmitContext ec, AddressOp mode) + { + IMemoryLocation ml = expr as VariableReference; + if (ml != null) + ml.AddressOf (ec, mode); + else + LocalVariable.AddressOf (ec, mode); + } - public void EmitGetValueOrDefault (EmitContext ec) - { - AddressOf (ec, AddressOp.LoadStore); - ec.ig.EmitCall (OpCodes.Call, info.GetValueOrDefault, null); + // + // Keeps result of non-variable expression + // + LocalTemporary LocalVariable { + get { + if (temp == null) + temp = new LocalTemporary (info.Type); + return temp; } + } - public override bool Equals (object obj) - { - Unwrap uw = obj as Unwrap; - return uw != null && expr.Equals (uw.expr); - } - - public override int GetHashCode () - { - return expr.GetHashCode (); - } + public void Emit (EmitContext ec, bool leave_copy) + { + if (leave_copy) + Load (ec); - public override bool IsNull { - get { - return expr.IsNull; - } - } + Emit (ec); + } - public void Store (EmitContext ec) + public void EmitAssign (EmitContext ec, Expression source, + bool leave_copy, bool prepare_for_load) + { + InternalWrap wrap = new InternalWrap (source, info, loc); + ((IAssignMethod) expr).EmitAssign (ec, wrap, leave_copy, false); + } + + protected class InternalWrap : Expression + { + public Expression expr; + public NullableInfo info; + + public InternalWrap (Expression expr, NullableInfo info, Location loc) { - create_temp (ec); + this.expr = expr; + this.info = info; + this.loc = loc; + + type = info.Type; + eclass = ExprClass.Value; } - void create_temp (EmitContext ec) + public override Expression CreateExpressionTree (EmitContext ec) { - if ((temp != null) && !has_temp) { - expr.Emit (ec); - temp.Store (ec); - has_temp = true; - } + throw new NotSupportedException ("ET"); } - public void LoadTemporary (EmitContext ec) + public override Expression DoResolve (EmitContext ec) { - temp.Emit (ec); + return this; } - public void AddressOf (EmitContext ec, AddressOp mode) + public override void Emit (EmitContext ec) { - create_temp (ec); - if (temp != null) - temp.AddressOf (ec, AddressOp.LoadStore); - else - ((IMemoryLocation) expr).AddressOf (ec, AddressOp.LoadStore); + expr.Emit (ec); + ec.ig.Emit (OpCodes.Newobj, info.Constructor); } + } + } - public void Emit (EmitContext ec, bool leave_copy) - { - create_temp (ec); - if (leave_copy) { - if (temp != null) - temp.Emit (ec); - else - expr.Emit (ec); - } + public class Wrap : TypeCast + { + readonly NullableInfo info; - Emit (ec); - } + protected Wrap (Expression expr, Type type) + : base (expr, type) + { + info = new NullableInfo (type); + eclass = ExprClass.Value; + } - public void EmitAssign (EmitContext ec, Expression source, - bool leave_copy, bool prepare_for_load) - { - InternalWrap wrap = new InternalWrap (source, info, loc); - ((IAssignMethod) expr).EmitAssign (ec, wrap, leave_copy, false); - } + public static Expression Create (Expression expr, Type type) + { + // + // Avoid unwraping and wraping of the same type + // + Unwrap unwrap = expr as Unwrap; + if (unwrap != null && TypeManager.IsEqual (expr.Type, TypeManager.GetTypeArguments (type) [0])) + return unwrap.Original; + + return new Wrap (expr, type); + } + + public override void Emit (EmitContext ec) + { + child.Emit (ec); + ec.ig.Emit (OpCodes.Newobj, info.Constructor); + } + } - protected class InternalWrap : Expression - { - public Expression expr; - public NullableInfo info; + // + // Represents null literal lifted to nullable type + // + public class LiftedNull : EmptyConstantCast, IMemoryLocation + { + private LiftedNull (Type nullable_type, Location loc) + : base (new NullLiteral (loc), nullable_type) + { + eclass = ExprClass.Value; + } - public InternalWrap (Expression expr, NullableInfo info, Location loc) - { - this.expr = expr; - this.info = info; - this.loc = loc; + public static Constant Create (Type nullable, Location loc) + { + return new LiftedNull (nullable, loc); + } - type = info.Type; - eclass = ExprClass.Value; - } + public static Expression CreateFromExpression (Expression e) + { + Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'", + TypeManager.CSharpName (e.Type)); - public override Expression DoResolve (EmitContext ec) - { - return this; - } + return ReducedExpression.Create (Create (e.Type, e.Location), e); + } - public override void Emit (EmitContext ec) - { - expr.Emit (ec); - ec.ig.Emit (OpCodes.Newobj, info.Constructor); - } - } + public override Expression CreateExpressionTree (EmitContext ec) + { + ArrayList args = new ArrayList (2); + args.Add (new Argument (this)); + args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc))); + + return CreateExpressionFactoryCall ("Constant", args); } - public class Wrap : EmptyCast + public override void Emit (EmitContext ec) { - readonly NullableInfo info; + // TODO: generate less temporary variables + LocalTemporary value_target = new LocalTemporary (type); - protected Wrap (Expression expr, Type type) - : base (expr, type) - { - info = new NullableInfo (type); - eclass = ExprClass.Value; - } + value_target.AddressOf (ec, AddressOp.Store); + ec.ig.Emit (OpCodes.Initobj, type); + value_target.Emit (ec); + } - public static new Expression Create (Expression expr, Type type) - { - return new Wrap (expr, type); - } - - public override void Emit (EmitContext ec) - { - child.Emit (ec); - ec.ig.Emit (OpCodes.Newobj, info.Constructor); - } + public void AddressOf (EmitContext ec, AddressOp Mode) + { + LocalTemporary value_target = new LocalTemporary (type); + + value_target.AddressOf (ec, AddressOp.Store); + ec.ig.Emit (OpCodes.Initobj, type); + ((IMemoryLocation) value_target).AddressOf (ec, Mode); } + } + + public abstract class Lifted : Expression, IMemoryLocation + { + Expression expr, underlying, wrap, null_value; + Unwrap unwrap; - class LiftedWrap : Wrap + protected Lifted (Expression expr, Location loc) { - public LiftedWrap (Expression expr, Type type) - : base (expr, type) - { - } + this.expr = expr; + this.loc = loc; + } + + public override Expression CreateExpressionTree (EmitContext ec) + { + ArrayList args = new ArrayList (2); + args.Add (new Argument (expr.CreateExpressionTree (ec))); + args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc))); + return CreateExpressionFactoryCall ("Convert", args); + } - public override Expression CreateExpressionTree (EmitContext ec) - { - return child.CreateExpressionTree (ec); - } + public override Expression DoResolve (EmitContext ec) + { + expr = expr.Resolve (ec); + if (expr == null) + return null; + + unwrap = Unwrap.Create (expr, ec); + if (unwrap == null) + return null; + + underlying = ResolveUnderlying (unwrap, ec); + if (underlying == null) + return null; + + TypeExpr target_type = new NullableType (underlying.Type, loc); + target_type = target_type.ResolveAsTypeTerminal (ec, false); + if (target_type == null) + return null; + + wrap = Wrap.Create (underlying, target_type.Type); + if (wrap == null) + return null; + + null_value = LiftedNull.Create (wrap.Type, loc); + + type = wrap.Type; + eclass = ExprClass.Value; + return this; } - // - // Represents null value converted to nullable type - // - public class Null : EmptyConstantCast, IMemoryLocation + protected abstract Expression ResolveUnderlying (Expression unwrap, EmitContext ec); + + public override void Emit (EmitContext ec) { - public Null (Type target_type, Location loc) - : base (new NullLiteral (loc), target_type) - { - eclass = ExprClass.Value; - } + ILGenerator ig = ec.ig; + Label is_null_label = ig.DefineLabel (); + Label end_label = ig.DefineLabel (); - public override void Emit (EmitContext ec) - { - LocalTemporary value_target = new LocalTemporary (type); + unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse, is_null_label); - value_target.AddressOf (ec, AddressOp.Store); - ec.ig.Emit (OpCodes.Initobj, type); - value_target.Emit (ec); - } + wrap.Emit (ec); + ig.Emit (OpCodes.Br, end_label); - public override bool IsNull { - get { - return true; - } - } + ig.MarkLabel (is_null_label); + null_value.Emit (ec); - public void AddressOf (EmitContext ec, AddressOp Mode) - { - LocalTemporary value_target = new LocalTemporary (type); - - value_target.AddressOf (ec, AddressOp.Store); - ec.ig.Emit (OpCodes.Initobj, type); - ((IMemoryLocation) value_target).AddressOf (ec, Mode); - } + ig.MarkLabel (end_label); + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + unwrap.AddressOf (ec, mode); } + } + + public class LiftedConversion : Lifted + { + public readonly bool IsUser; + public readonly bool IsExplicit; + public readonly Type TargetType; - public abstract class Lifted : Expression, IMemoryLocation + public LiftedConversion (Expression expr, Type target_type, bool is_user, + bool is_explicit, Location loc) + : base (expr, loc) { - Expression expr, underlying, wrap, null_value; - Unwrap unwrap; + this.IsUser = is_user; + this.IsExplicit = is_explicit; + this.TargetType = target_type; + } - protected Lifted (Expression expr, Location loc) - { - this.expr = expr; - this.loc = loc; + protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec) + { + Type type = TypeManager.GetTypeArguments (TargetType) [0]; + + if (IsUser) { + if (IsExplicit) + return Convert.ExplicitUserConversion (ec, unwrap, type, loc); + else + return Convert.ImplicitUserConversion (ec, unwrap, type, loc); + } else { + if (IsExplicit) + return Convert.ExplicitConversion (ec, unwrap, type, loc); + else + return Convert.ImplicitConversion (ec, unwrap, type, loc); } - - public override Expression CreateExpressionTree (EmitContext ec) - { - return underlying.CreateExpressionTree (ec); - } + } + } - public override Expression DoResolve (EmitContext ec) - { - expr = expr.Resolve (ec); - if (expr == null) - return null; + public class LiftedUnaryOperator : Unary, IMemoryLocation + { + Unwrap unwrap; + Expression user_operator; - unwrap = Unwrap.Create (expr, ec); - if (unwrap == null) - return null; + public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc) + : base (op, expr, loc) + { + } - underlying = ResolveUnderlying (unwrap, ec); - if (underlying == null) - return null; + public void AddressOf (EmitContext ec, AddressOp mode) + { + unwrap.AddressOf (ec, mode); + } - TypeExpr target_type = new NullableType (underlying.Type, loc); - target_type = target_type.ResolveAsTypeTerminal (ec, false); - if (target_type == null) - return null; + public override Expression CreateExpressionTree (EmitContext ec) + { + if (user_operator != null) + return user_operator.CreateExpressionTree (ec); - wrap = Wrap.Create (underlying, target_type.Type); - if (wrap == null) - return null; + if (Oper == Operator.UnaryPlus) + return Expr.CreateExpressionTree (ec); - null_value = new Null (wrap.Type, loc).Resolve (ec); - if (null_value == null) - return null; + return base.CreateExpressionTree (ec); + } - type = wrap.Type; - eclass = ExprClass.Value; + public override Expression DoResolve (EmitContext ec) + { + if (eclass != ExprClass.Invalid) return this; + + unwrap = Unwrap.Create (Expr, ec); + if (unwrap == null) + return null; + + Expression res = base.ResolveOperator (ec, unwrap); + if (res != this) { + if (user_operator == null) + return res; + } else { + res = Expr = LiftExpression (ec, Expr); } - protected abstract Expression ResolveUnderlying (Expression unwrap, EmitContext ec); + if (res == null) + return null; - public override void Emit (EmitContext ec) - { - ILGenerator ig = ec.ig; - Label is_null_label = ig.DefineLabel (); - Label end_label = ig.DefineLabel (); + eclass = ExprClass.Value; + type = res.Type; + return this; + } - unwrap.EmitCheck (ec); - ig.Emit (OpCodes.Brfalse, is_null_label); + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + Label is_null_label = ig.DefineLabel (); + Label end_label = ig.DefineLabel (); - wrap.Emit (ec); - ig.Emit (OpCodes.Br, end_label); + unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse, is_null_label); - ig.MarkLabel (is_null_label); - null_value.Emit (ec); + NullableInfo ni = new NullableInfo (type); - ig.MarkLabel (end_label); + if (user_operator != null) { + user_operator.Emit (ec); + } else { + EmitOperator (ec, ni.UnderlyingType); } - public void AddressOf (EmitContext ec, AddressOp mode) - { - unwrap.AddressOf (ec, mode); - } + ig.Emit (OpCodes.Newobj, ni.Constructor); + ig.Emit (OpCodes.Br_S, end_label); + + ig.MarkLabel (is_null_label); + LiftedNull.Create (type, loc).Emit (ec); + + ig.MarkLabel (end_label); } - public class LiftedConversion : Lifted + Expression LiftExpression (EmitContext ec, Expression expr) { - public readonly bool IsUser; - public readonly bool IsExplicit; - public readonly Type TargetType; + TypeExpr lifted_type = new NullableType (expr.Type, expr.Location); + lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false); + if (lifted_type == null) + return null; - public LiftedConversion (Expression expr, Type target_type, bool is_user, - bool is_explicit, Location loc) - : base (expr, loc) - { - this.IsUser = is_user; - this.IsExplicit = is_explicit; - this.TargetType = target_type; - } + expr.Type = lifted_type.Type; + return expr; + } - protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec) - { - Type type = TypeManager.GetTypeArguments (TargetType) [0]; - - if (IsUser) { - return Convert.UserDefinedConversion (ec, unwrap, type, loc, IsExplicit); - } else { - if (IsExplicit) - return Convert.ExplicitConversion (ec, unwrap, type, loc); - else - return Convert.ImplicitConversion (ec, unwrap, type, loc); - } - } + protected override Expression ResolveEnumOperator (EmitContext ec, Expression expr) + { + expr = base.ResolveEnumOperator (ec, expr); + if (expr == null) + return null; + + Expr = LiftExpression (ec, Expr); + return LiftExpression (ec, expr); } - public class LiftedUnaryOperator : Lifted + protected override Expression ResolveUserOperator (EmitContext ec, Expression expr) { - public readonly Unary.Operator Oper; + expr = base.ResolveUserOperator (ec, expr); + if (expr == null) + return null; - public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc) - : base (expr, loc) - { - this.Oper = op; + // + // When a user operator is of non-nullable type + // + if (Expr is Unwrap) { + user_operator = LiftExpression (ec, expr); + return user_operator; } - protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec) - { - return new Unary (Oper, unwrap, loc).Resolve (ec); - } + return expr; } + } - public class LiftedBinaryOperator : Binary + public class LiftedBinaryOperator : Binary + { + Unwrap left_unwrap, right_unwrap; + bool left_null_lifted, right_null_lifted; + Expression left_orig, right_orig; + Expression user_operator; + ConstructorInfo wrap_ctor; + + public LiftedBinaryOperator (Binary.Operator op, Expression left, Expression right, + Location loc) + : base (op, left, right) { - Unwrap left_unwrap, right_unwrap; + this.loc = loc; + } - public LiftedBinaryOperator (Binary.Operator op, Expression left, Expression right, - Location loc) - : base (op, left, right) - { - this.loc = loc; - } + public override Expression CreateExpressionTree (EmitContext ec) + { + if (user_operator != null) + return user_operator.CreateExpressionTree (ec); - public override Expression DoResolve (EmitContext ec) - { - if (eclass != ExprClass.Invalid) - return this; + return base.CreateExpressionTree (ec); + } - // TODO: How does it work with use-operators? - if ((Oper == Binary.Operator.LogicalAnd) || - (Oper == Binary.Operator.LogicalOr)) { - Error_OperatorCannotBeApplied (); - return null; - } + // + // CSC 2 has this behavior, it allows structs to be compared + // with the null literal *outside* of a generics context and + // inlines that as true or false. + // + Expression CreateNullConstant (Expression expr) + { + // FIXME: Handle side effect constants + Constant c = new BoolConstant (Oper == Operator.Inequality, loc); + + if ((Oper & Operator.EqualityMask) != 0) { + Report.Warning (472, 2, loc, "The result of comparing `{0}' against null is always `{1}'. " + + "This operation is undocumented and it is temporary supported for compatibility reasons only", + expr.GetSignatureForError (), c.AsString ()); + } else { + Report.Warning (464, 2, loc, "The result of comparing type `{0}' against null is always `{1}'", + expr.GetSignatureForError (), c.AsString ()); + } - if (TypeManager.IsNullableType (left.Type)) { - left = left_unwrap = Unwrap.Create (left, ec); - if (left == null) - return null; - } + return ReducedExpression.Create (c, this); + } - if (TypeManager.IsNullableType (right.Type)) { - right = right_unwrap = Unwrap.Create (right, ec); - if (right == null) - return null; - } + public override Expression DoResolve (EmitContext ec) + { + if (eclass != ExprClass.Invalid) + return this; - if ((Oper & Operator.ComparisonMask) != 0) - return base.DoResolve (ec); + if ((Oper & Operator.LogicalMask) != 0) { + Error_OperatorCannotBeApplied (left, right); + return null; + } - Expression expr = base.DoResolve (ec); - if (expr != this) - return expr; + left_orig = left; + if (TypeManager.IsNullableType (left.Type)) { + left = left_unwrap = Unwrap.Create (left, ec); + if (left == null) + return null; + } - TypeExpr target_type = new NullableType (type, loc); - target_type = target_type.ResolveAsTypeTerminal (ec, false); - if (target_type == null) + right_orig = right; + if (TypeManager.IsNullableType (right.Type)) { + right = right_unwrap = Unwrap.Create (right, ec); + if (right == null) return null; + } - type = target_type.Type; - eclass = ExprClass.Value; - return expr; + // + // Some details are in 6.4.2, 7.2.7 + // Arguments can be lifted for equal operators when the return type is bool and both + // arguments are of same type + // + if (left is NullLiteral) { + left = right; + left_null_lifted = true; + type = TypeManager.bool_type; } - void EmitBitwiseBoolean (EmitContext ec) - { - ILGenerator ig = ec.ig; + if (right is NullLiteral) { + right = left; + right_null_lifted = true; + type = TypeManager.bool_type; + } - Label load_left = ig.DefineLabel (); - Label load_right = ig.DefineLabel (); - Label end_label = ig.DefineLabel (); + eclass = ExprClass.Value; + return DoResolveCore (ec, left_orig, right_orig); + } - left_unwrap.EmitGetValueOrDefault (ec); - ig.Emit (OpCodes.Brtrue_S, load_right); + void EmitBitwiseBoolean (EmitContext ec) + { + ILGenerator ig = ec.ig; - right_unwrap.EmitGetValueOrDefault (ec); - ig.Emit (OpCodes.Brtrue_S, load_left); + Label load_left = ig.DefineLabel (); + Label load_right = ig.DefineLabel (); + Label end_label = ig.DefineLabel (); - left_unwrap.EmitCheck (ec); - ig.Emit (OpCodes.Brfalse_S, load_right); + left_unwrap.EmitGetValueOrDefault (ec); + ig.Emit (OpCodes.Brtrue_S, load_right); - // load left - ig.MarkLabel (load_left); + right_unwrap.EmitGetValueOrDefault (ec); + ig.Emit (OpCodes.Brtrue_S, load_left); - if (Oper == Operator.BitwiseAnd) { - left_unwrap.LoadTemporary (ec); - } else { - right_unwrap.LoadTemporary (ec); - right_unwrap = left_unwrap; - } - ig.Emit (OpCodes.Br_S, end_label); + left_unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse_S, load_right); - // load right - ig.MarkLabel (load_right); - right_unwrap.LoadTemporary (ec); + // load left + ig.MarkLabel (load_left); - ig.MarkLabel (end_label); + if (Oper == Operator.BitwiseAnd) { + left_unwrap.Load (ec); + } else { + right_unwrap.Load (ec); + right_unwrap = left_unwrap; } + ig.Emit (OpCodes.Br_S, end_label); - void EmitEquality (EmitContext ec) - { - ILGenerator ig = ec.ig; - - Label both_have_value_label = ig.DefineLabel (); - Label end_label = ig.DefineLabel (); + // load right + ig.MarkLabel (load_right); + right_unwrap.Load (ec); - // - // Both are nullable types - // - if (left_unwrap != null && right_unwrap != null && !right.IsNull && !left.IsNull) { - Label dissimilar_label = ig.DefineLabel (); - - left_unwrap.EmitGetValueOrDefault (ec); - right_unwrap.EmitGetValueOrDefault (ec); - ig.Emit (OpCodes.Bne_Un_S, dissimilar_label); - - left_unwrap.EmitCheck (ec); - right_unwrap.EmitCheck (ec); - if (Oper == Operator.Inequality) - ig.Emit (OpCodes.Xor); - else - ig.Emit (OpCodes.Ceq); - - ig.Emit (OpCodes.Br_S, end_label); + ig.MarkLabel (end_label); + } - ig.MarkLabel (dissimilar_label); - if (Oper == Operator.Inequality) - ig.Emit (OpCodes.Ldc_I4_1); - else - ig.Emit (OpCodes.Ldc_I4_0); + // + // Emits optimized equality or inequality operator when possible + // + bool EmitEquality (EmitContext ec) + { + ILGenerator ig = ec.ig; - ig.MarkLabel (end_label); - return; + // + // Either left or right is null + // + if (left_unwrap != null && (right_null_lifted || right.IsNull)) { + left_unwrap.EmitCheck (ec); + if (Oper == Binary.Operator.Equality) { + ig.Emit (OpCodes.Ldc_I4_0); + ig.Emit (OpCodes.Ceq); } + return true; + } - // - // Either left or right is nullable - // - if (left_unwrap != null) { - left_unwrap.EmitCheck (ec); - if (right.IsNull) { - if (Oper == Binary.Operator.Equality) { - ig.Emit (OpCodes.Ldc_I4_0); - ig.Emit (OpCodes.Ceq); - } - return; - } - ig.Emit (OpCodes.Brtrue_S, both_have_value_label); - } else { - right_unwrap.EmitCheck (ec); - if (left.IsNull) { - if (Oper == Binary.Operator.Equality) { - ig.Emit (OpCodes.Ldc_I4_0); - ig.Emit (OpCodes.Ceq); - } - return; - } - ig.Emit (OpCodes.Brtrue_S, both_have_value_label); + if (right_unwrap != null && (left_null_lifted || left.IsNull)) { + right_unwrap.EmitCheck (ec); + if (Oper == Binary.Operator.Equality) { + ig.Emit (OpCodes.Ldc_I4_0); + ig.Emit (OpCodes.Ceq); } + return true; + } - if (Oper == Binary.Operator.Equality) - ig.Emit (OpCodes.Ldc_I4_0); - else - ig.Emit (OpCodes.Ldc_I4_1); - ig.Emit (OpCodes.Br, end_label); + if (user_operator != null) + return false; - ig.MarkLabel (both_have_value_label); - EmitOperator (ec); + Label dissimilar_label = ig.DefineLabel (); + Label end_label = ig.DefineLabel (); - ig.MarkLabel (end_label); - } + if (left_unwrap != null) + left_unwrap.EmitGetValueOrDefault (ec); + else + left.Emit (ec); - void EmitComparision (EmitContext ec) - { - ILGenerator ig = ec.ig; + if (right_unwrap != null) + right_unwrap.EmitGetValueOrDefault (ec); + else + right.Emit (ec); - Label is_null_label = ig.DefineLabel (); - Label end_label = ig.DefineLabel (); + ig.Emit (OpCodes.Bne_Un_S, dissimilar_label); - if (left_unwrap != null) { - left_unwrap.EmitCheck (ec); - ig.Emit (OpCodes.Brfalse, is_null_label); - } + if (left_unwrap != null) + left_unwrap.EmitCheck (ec); + if (right_unwrap != null) + right_unwrap.EmitCheck (ec); - if (right_unwrap != null) { - right_unwrap.EmitCheck (ec); - ig.Emit (OpCodes.Brfalse, is_null_label); + if (left_unwrap != null && right_unwrap != null) { + if (Oper == Operator.Inequality) + ig.Emit (OpCodes.Xor); + else + ig.Emit (OpCodes.Ceq); + } else { + if (Oper == Operator.Inequality) { + ig.Emit (OpCodes.Ldc_I4_0); + ig.Emit (OpCodes.Ceq); } + } - EmitOperator (ec); - ig.Emit (OpCodes.Br_S, end_label); + ig.Emit (OpCodes.Br_S, end_label); - ig.MarkLabel (is_null_label); + ig.MarkLabel (dissimilar_label); + if (Oper == Operator.Inequality) + ig.Emit (OpCodes.Ldc_I4_1); + else ig.Emit (OpCodes.Ldc_I4_0); - ig.MarkLabel (end_label); - } - - public override void EmitBranchable (EmitContext ec, Label target, bool onTrue) - { - Emit (ec); - ec.ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target); - } + ig.MarkLabel (end_label); + return true; + } + + public override void EmitBranchable (EmitContext ec, Label target, bool onTrue) + { + Emit (ec); + ec.ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target); + } - public override void Emit (EmitContext ec) - { - if (left_unwrap != null) - left_unwrap.Store (ec); - if (right_unwrap != null) - right_unwrap.Store (ec); - - if (((Oper & Operator.BitwiseMask) != 0) && - left_unwrap.Type == TypeManager.bool_type && right_unwrap.Type == TypeManager.bool_type) { - EmitBitwiseBoolean (ec); - return; - } + public override void Emit (EmitContext ec) + { + // + // Optimize same expression operation + // + if (right_unwrap != null && right.Equals (left)) + right_unwrap = left_unwrap; + + if (user_operator == null && IsBitwiseBoolean) { + EmitBitwiseBoolean (ec); + return; + } - if ((Oper & Operator.EqualityMask) != 0) { - EmitEquality (ec); + if ((Oper & Operator.EqualityMask) != 0) { + if (EmitEquality (ec)) return; - } + } - if ((Oper & Operator.ComparisonMask) != 0) { - EmitComparision (ec); - return; - } + ILGenerator ig = ec.ig; - ILGenerator ig = ec.ig; + Label is_null_label = ig.DefineLabel (); + Label end_label = ig.DefineLabel (); - Label is_null_label = ig.DefineLabel (); - Label end_label = ig.DefineLabel (); + if (left_unwrap != null) { + left_unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse, is_null_label); + } - if (left_unwrap != null) { - left_unwrap.EmitCheck (ec); - ig.Emit (OpCodes.Brfalse, is_null_label); - } + // + // Don't emit HasValue check when left and right expressions are same + // + if (right_unwrap != null && !left.Equals (right)) { + right_unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse, is_null_label); + } - if (right_unwrap != null) { - right_unwrap.EmitCheck (ec); - ig.Emit (OpCodes.Brfalse, is_null_label); - } + EmitOperator (ec, left.Type); - base.EmitOperator (ec); + if (wrap_ctor != null) + ig.Emit (OpCodes.Newobj, wrap_ctor); - // TODO: this is LiftedWrap - NullableInfo info = new NullableInfo (type); - ig.Emit (OpCodes.Newobj, info.Constructor); + ig.Emit (OpCodes.Br_S, end_label); + ig.MarkLabel (is_null_label); - ig.Emit (OpCodes.Br_S, end_label); + if ((Oper & Operator.ComparisonMask) != 0) { + if (Oper == Operator.Equality) + ig.Emit (OpCodes.Ldc_I4_1); + else + ig.Emit (OpCodes.Ldc_I4_0); + } else { + LiftedNull.Create (type, loc).Emit (ec); + } - ig.MarkLabel (is_null_label); - new Null (type, loc).Emit (ec); + ig.MarkLabel (end_label); + } - ig.MarkLabel (end_label); + protected override void EmitOperator (EmitContext ec, Type l) + { + if (user_operator != null) { + user_operator.Emit (ec); + return; } - protected override Expression ResolveOperatorPredefined (EmitContext ec, Binary.PredefinedOperator [] operators, bool primitives_only) - { - Expression e = base.ResolveOperatorPredefined (ec, operators, primitives_only); - if (e != null) - return e; + if (TypeManager.IsNullableType (l)) + l = TypeManager.GetTypeArguments (l) [0]; - // - // 7.9.9 Equality operators and null - // - // 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 ((Oper & Operator.EqualityMask) != 0) { - if (left is NullLiteral) { - left = WrapNullExpression (ec, right, left); - return this; - } - - if (right is NullLiteral) { - right = WrapNullExpression (ec, left, right); - return this; - } - } + base.EmitOperator (ec, l); + } - return null; + bool IsBitwiseBoolean { + get { + return (Oper & Operator.BitwiseMask) != 0 && left_unwrap != null && right_unwrap != null && + left_unwrap.Type == TypeManager.bool_type && right_unwrap.Type == TypeManager.bool_type; } + } - protected override Expression ResolveUserOperator (EmitContext ec, Type l, Type r) - { - Expression expr = base.ResolveUserOperator (ec, l, r); - if (expr == null) + Expression LiftResult (EmitContext ec, Expression res_expr) + { + TypeExpr lifted_type; + + // + // Avoid double conversion + // + if (left_unwrap == null || left_null_lifted || !TypeManager.IsEqual (left_unwrap.Type, left.Type)) { + lifted_type = new NullableType (left.Type, loc); + lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false); + if (lifted_type == null) return null; - - // TODO: Handle bitwise bool - if ((Oper & Operator.ComparisonMask) != 0) - return expr; - TypeExpr target_type = new NullableType (expr.Type, loc); - target_type = target_type.ResolveAsTypeTerminal (ec, false); - if (target_type == null) + if (left is UserCast || left is TypeCast) + left.Type = lifted_type.Type; + else + left = EmptyCast.Create (left, lifted_type.Type); + } + + if (right_unwrap == null || right_null_lifted || !TypeManager.IsEqual (right_unwrap.Type, right.Type)) { + lifted_type = new NullableType (right.Type, loc); + lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false); + if (lifted_type == null) return null; - return new LiftedWrap (expr, target_type.Type).Resolve (ec); + if (right is UserCast || right is TypeCast) + right.Type = lifted_type.Type; + else + right = EmptyCast.Create (right, lifted_type.Type); } - Expression WrapNullExpression (EmitContext ec, Expression expr, Expression null_expr) - { - TypeExpr lifted_type = new NullableType (expr.Type, expr.Location); + if ((Oper & Operator.ComparisonMask) == 0) { + lifted_type = new NullableType (res_expr.Type, loc); lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false); if (lifted_type == null) return null; - return new Null (lifted_type.Type, null_expr.Location); + wrap_ctor = new NullableInfo (lifted_type.Type).Constructor; + type = res_expr.Type = lifted_type.Type; + } + + if (left_null_lifted) { + left = LiftedNull.Create (right.Type, left.Location); + + if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask)) != 0) + return LiftedNull.CreateFromExpression (res_expr); + + // + // Value types and null comparison + // + if (right_unwrap == null || (Oper & Operator.RelationalMask) != 0) + return CreateNullConstant (right_orig).Resolve (ec); + } + + if (right_null_lifted) { + right = LiftedNull.Create (left.Type, right.Location); + + if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask)) != 0) + return LiftedNull.CreateFromExpression (res_expr); + + // + // Value types and null comparison + // + if (left_unwrap == null || (Oper & Operator.RelationalMask) != 0) + return CreateNullConstant (left_orig).Resolve (ec); + } + + return res_expr; + } + + protected override Expression ResolveOperatorPredefined (EmitContext ec, Binary.PredefinedOperator [] operators, bool primitives_only, Type enum_type) + { + Expression e = base.ResolveOperatorPredefined (ec, operators, primitives_only, enum_type); + + if (e == this || enum_type != null) + return LiftResult (ec, e); + + // + // 7.9.9 Equality operators and null + // + // 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 (e == null && (Oper & Operator.EqualityMask) != 0) { + if ((left_null_lifted && right_unwrap != null) || (right_null_lifted && left_unwrap != null)) + return LiftResult (ec, this); } + + return e; } - public class NullCoalescingOperator : Expression + protected override Expression ResolveUserOperator (EmitContext ec, Type l, Type r) { - Expression left, right; - Unwrap unwrap; + Expression expr = base.ResolveUserOperator (ec, l, r); + if (expr == null) + return null; - public NullCoalescingOperator (Expression left, Expression right, Location loc) - { - this.left = left; - this.right = right; - this.loc = loc; + expr = LiftResult (ec, expr); + if (expr is Constant) + return expr; + + type = expr.Type; + user_operator = expr; + return this; + } + } + + public class NullCoalescingOperator : Expression + { + Expression left, right; + Unwrap unwrap; + + public NullCoalescingOperator (Expression left, Expression right, Location loc) + { + this.left = left; + this.right = right; + this.loc = loc; + } + + public override Expression CreateExpressionTree (EmitContext ec) + { + if (left is NullLiteral) + Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side"); + + UserCast uc = left as UserCast; + Expression conversion = null; + if (uc != null) { + left = uc.Source; + + ArrayList c_args = new ArrayList (2); + c_args.Add (new Argument (uc.CreateExpressionTree (ec))); + c_args.Add (new Argument (left.CreateExpressionTree (ec))); + conversion = CreateExpressionFactoryCall ("Lambda", c_args); } + + ArrayList args = new ArrayList (3); + args.Add (new Argument (left.CreateExpressionTree (ec))); + args.Add (new Argument (right.CreateExpressionTree (ec))); + if (conversion != null) + args.Add (new Argument (conversion)); - public override Expression CreateExpressionTree (EmitContext ec) - { - UserCast uc = left as UserCast; - Expression conversion = null; - if (uc != null) { - left = uc.Source; - - ArrayList c_args = new ArrayList (2); - c_args.Add (new Argument (uc.CreateExpressionTree (ec))); - c_args.Add (new Argument (left.CreateExpressionTree (ec))); - conversion = CreateExpressionFactoryCall ("Lambda", c_args); - } + return CreateExpressionFactoryCall ("Coalesce", args); + } - ArrayList args = new ArrayList (3); - args.Add (new Argument (left.CreateExpressionTree (ec))); - args.Add (new Argument (right.CreateExpressionTree (ec))); - if (conversion != null) - args.Add (new Argument (conversion)); - - return CreateExpressionFactoryCall ("Coalesce", args); - } + public override Expression DoResolve (EmitContext ec) + { + if (type != null) + return this; - public override Expression DoResolve (EmitContext ec) - { - if (type != null) - return this; + left = left.Resolve (ec); + right = right.Resolve (ec); - left = left.Resolve (ec); - right = right.Resolve (ec); + if (left == null || right == null) + return null; - if (left == null || right == null) - return null; + eclass = ExprClass.Value; + Type ltype = left.Type, rtype = right.Type; - eclass = ExprClass.Value; - Type ltype = left.Type, rtype = right.Type; - Expression expr; - - if (TypeManager.IsNullableType (ltype)) { - NullableInfo info = new NullableInfo (ltype); - - unwrap = Unwrap.Create (left, ec); - if (unwrap == null) - return null; - - expr = Convert.ImplicitConversion (ec, right, info.UnderlyingType, loc); - if (expr != null) { - left = unwrap; - right = expr; - type = expr.Type; - return this; - } - } else if (!TypeManager.IsReferenceType (ltype)) { - Binary.Error_OperatorCannotBeApplied (loc, "??", ltype, rtype); + // + // If left is a nullable type and an implicit conversion exists from right to underlying type of left, + // the result is underlying type of left + // + if (TypeManager.IsNullableType (ltype) && left.eclass != ExprClass.MethodGroup) { + unwrap = Unwrap.Create (left, ec); + if (unwrap == null) return null; - } - expr = Convert.ImplicitConversion (ec, right, ltype, loc); - if (expr != null) { - type = expr.Type; - right = expr; + if (Convert.ImplicitConversionExists (ec, right, unwrap.Type)) { + left = unwrap; + type = left.Type; + right = Convert.ImplicitConversion (ec, right, type, loc); return this; - } - - Expression left_null = unwrap != null ? unwrap : left; - expr = Convert.ImplicitConversion (ec, left_null, rtype, loc); - if (expr != null) { - left = expr; - type = rtype; + } + } else if (TypeManager.IsReferenceType (ltype) && right.eclass != ExprClass.MethodGroup) { + if (Convert.ImplicitConversionExists (ec, right, ltype)) { + // + // Reduce (constant ?? expr) to constant + // + Constant lc = left as Constant; + if (lc != null && !lc.IsDefaultValue) + return new SideEffectConstant (lc, right, loc).Resolve (ec); + + // + // Reduce (left ?? null) to left OR (null-constant ?? right) to right + // + if (right.IsNull || lc != null) + return ReducedExpression.Create (lc != null ? right : left, this).Resolve (ec); + + right = Convert.ImplicitConversion (ec, right, ltype, loc); + type = left.Type; return this; } + } else { + Binary.Error_OperatorCannotBeApplied (left, right, "??", loc); + return null; + } - Binary.Error_OperatorCannotBeApplied (loc, "??", ltype, rtype); + if (!Convert.ImplicitConversionExists (ec, unwrap != null ? unwrap : left, rtype)) { + Binary.Error_OperatorCannotBeApplied (left, right, "??", loc); return null; } - public override void Emit (EmitContext ec) - { - ILGenerator ig = ec.ig; + // + // Reduce (null ?? right) to right + // + if (left.IsNull) + return ReducedExpression.Create (right, this).Resolve (ec); - Label is_null_label = ig.DefineLabel (); - Label end_label = ig.DefineLabel (); + left = Convert.ImplicitConversion (ec, unwrap != null ? unwrap : left, rtype, loc); + type = rtype; + return this; + } - if (unwrap != null) { - unwrap.EmitCheck (ec); - ig.Emit (OpCodes.Brfalse, is_null_label); + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; - left.Emit (ec); - ig.Emit (OpCodes.Br, end_label); + Label end_label = ig.DefineLabel (); - ig.MarkLabel (is_null_label); - right.Emit (ec); + if (unwrap != null) { + Label is_null_label = ig.DefineLabel (); - ig.MarkLabel (end_label); - } else { - left.Emit (ec); - ig.Emit (OpCodes.Dup); - ig.Emit (OpCodes.Brtrue, end_label); + unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse, is_null_label); - ig.MarkLabel (is_null_label); + left.Emit (ec); + ig.Emit (OpCodes.Br, end_label); - ig.Emit (OpCodes.Pop); - right.Emit (ec); + ig.MarkLabel (is_null_label); + right.Emit (ec); - ig.MarkLabel (end_label); - } + ig.MarkLabel (end_label); + return; } - protected override void CloneTo (CloneContext clonectx, Expression t) - { - NullCoalescingOperator target = (NullCoalescingOperator) t; - target.left = left.Clone (clonectx); - target.right = right.Clone (clonectx); - } + left.Emit (ec); + + ig.Emit (OpCodes.Dup); + ig.Emit (OpCodes.Brtrue, end_label); + + ig.Emit (OpCodes.Pop); + right.Emit (ec); + + ig.MarkLabel (end_label); } - public class LiftedUnaryMutator : ExpressionStatement + protected override void CloneTo (CloneContext clonectx, Expression t) { - public readonly UnaryMutator.Mode Mode; - Expression expr, null_value; - UnaryMutator underlying; - Unwrap unwrap; + NullCoalescingOperator target = (NullCoalescingOperator) t; - public LiftedUnaryMutator (UnaryMutator.Mode mode, Expression expr, Location loc) - { - this.expr = expr; - this.Mode = mode; - this.loc = loc; + target.left = left.Clone (clonectx); + target.right = right.Clone (clonectx); + } + } - eclass = ExprClass.Value; - } + public class LiftedUnaryMutator : ExpressionStatement + { + public readonly UnaryMutator.Mode Mode; + Expression expr; + UnaryMutator underlying; + Unwrap unwrap; - public override Expression DoResolve (EmitContext ec) - { - expr = expr.Resolve (ec); - if (expr == null) - return null; + public LiftedUnaryMutator (UnaryMutator.Mode mode, Expression expr, Location loc) + { + this.expr = expr; + this.Mode = mode; + this.loc = loc; - unwrap = Unwrap.Create (expr, ec); - if (unwrap == null) - return null; + eclass = ExprClass.Value; + } - underlying = (UnaryMutator) new UnaryMutator (Mode, unwrap, loc).Resolve (ec); - if (underlying == null) - return null; + public override Expression CreateExpressionTree (EmitContext ec) + { + return new SimpleAssign (this, this).CreateExpressionTree (ec); + } - null_value = new Null (expr.Type, loc).Resolve (ec); - if (null_value == null) - return null; + public override Expression DoResolve (EmitContext ec) + { + expr = expr.Resolve (ec); + if (expr == null) + return null; - type = expr.Type; - return this; - } + unwrap = Unwrap.Create (expr, ec); + if (unwrap == null) + return null; - void DoEmit (EmitContext ec, bool is_expr) - { - ILGenerator ig = ec.ig; - Label is_null_label = ig.DefineLabel (); - Label end_label = ig.DefineLabel (); + underlying = (UnaryMutator) new UnaryMutator (Mode, unwrap, loc).Resolve (ec); + if (underlying == null) + return null; - unwrap.EmitCheck (ec); - ig.Emit (OpCodes.Brfalse, is_null_label); + type = expr.Type; + return this; + } - if (is_expr) - underlying.Emit (ec); - else - underlying.EmitStatement (ec); - ig.Emit (OpCodes.Br, end_label); + void DoEmit (EmitContext ec, bool is_expr) + { + ILGenerator ig = ec.ig; + Label is_null_label = ig.DefineLabel (); + Label end_label = ig.DefineLabel (); - ig.MarkLabel (is_null_label); - if (is_expr) - null_value.Emit (ec); + unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse, is_null_label); - ig.MarkLabel (end_label); + if (is_expr) { + underlying.Emit (ec); + ig.Emit (OpCodes.Br_S, end_label); + } else { + underlying.EmitStatement (ec); } - public override void Emit (EmitContext ec) - { - DoEmit (ec, true); - } + ig.MarkLabel (is_null_label); + if (is_expr) + LiftedNull.Create (type, loc).Emit (ec); - public override void EmitStatement (EmitContext ec) - { - DoEmit (ec, false); - } + ig.MarkLabel (end_label); + } + + public override void Emit (EmitContext ec) + { + DoEmit (ec, true); + } + + public override void EmitStatement (EmitContext ec) + { + DoEmit (ec, false); } } }