X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fmcs%2Fnullable.cs;h=307ebfb5d32515543b1ad707ecb54863d979d330;hb=67dae6e4154448e87f29b3b4b13f8cc364b7ef83;hp=61892ae1bce86a855c91022aa576cadfc5406cd3;hpb=5e03f444b9b775136b0af5133d61d4ff921ab39b;p=mono.git diff --git a/mcs/mcs/nullable.cs b/mcs/mcs/nullable.cs index 61892ae1bce..307ebfb5d32 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,13 +16,13 @@ using System.Reflection; using System.Reflection.Emit; using System.Collections; -namespace Mono.CSharp { - +namespace Mono.CSharp.Nullable +{ public class NullableType : TypeExpr { - Expression underlying; + TypeExpr underlying; - public NullableType (Expression underlying, Location l) + public NullableType (TypeExpr underlying, Location l) { this.underlying = underlying; loc = l; @@ -34,1019 +34,1166 @@ 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) + protected override TypeExpr DoResolveAsTypeStep (IMemberContext ec) { - TypeArguments args = new TypeArguments (loc); - args.Add (underlying); - if (TypeManager.generic_nullable_type == null) { - TypeManager.generic_nullable_type = TypeManager.CoreLookupType ( + TypeManager.generic_nullable_type = TypeManager.CoreLookupType (ec.Compiler, "System", "Nullable`1", Kind.Struct, true); } - ConstructedType ctype = new ConstructedType (TypeManager.generic_nullable_type, args, loc); + TypeArguments args = new TypeArguments (underlying); + GenericTypeExpr ctype = new GenericTypeExpr (TypeManager.generic_nullable_type, args, loc); return ctype.ResolveAsTypeTerminal (ec, false); } + + public override TypeExpr ResolveAsTypeTerminal (IMemberContext ec, bool silent) + { + return ResolveAsBaseTerminal (ec, silent); + } } - public abstract class Nullable + public sealed class NullableInfo { - public sealed class NullableInfo + public readonly Type Type; + public readonly Type UnderlyingType; + public MethodInfo HasValue; + public MethodInfo Value; + public MethodInfo GetValueOrDefault; + public 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; + Type = type; + UnderlyingType = TypeManager.TypeToCoreType (TypeManager.GetTypeArguments (type) [0]); - public NullableInfo (Type type) - { - Type = type; - UnderlyingType = TypeManager.GetTypeArguments (type) [0]; + PropertyInfo has_value_pi = TypeManager.GetPredefinedProperty (type, "HasValue", Location.Null, Type.EmptyTypes); + PropertyInfo value_pi = TypeManager.GetPredefinedProperty (type, "Value", Location.Null, Type.EmptyTypes); + 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 }); - } - } - - public class HasValue : Expression - { - Expression expr; - NullableInfo info; + // When compiling corlib + if (TypeManager.IsBeingCompiled (type)) { + TypeContainer tc = TypeManager.LookupGenericTypeContainer (type); + + // TODO: check for correct overload + Constructor c = ((Constructor) tc.InstanceConstructors [0]); - private HasValue (Expression expr) - { - this.expr = expr; - } - - public static Expression Create (Expression expr, EmitContext ec) - { - return new HasValue (expr).Resolve (ec); + Constructor = TypeBuilder.GetConstructor (type, c.ConstructorBuilder); + return; } - 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); +#if MS_COMPATIBLE + if (TypeManager.IsBeingCompiled (UnderlyingType)) { + ConstructorInfo cinfo = TypeManager.DropGenericTypeArguments (type).GetConstructors ()[0]; + Constructor = TypeBuilder.GetConstructor (type, cinfo); + return; } +#endif - public override Expression DoResolve (EmitContext ec) - { - this.info = new NullableInfo (expr.Type); + Constructor = type.GetConstructor (new Type[] { UnderlyingType }); + } + } - type = TypeManager.bool_type; - eclass = expr.eclass; - return this; - } - } + public class Unwrap : Expression, IMemoryLocation, IAssignMethod + { + Expression expr; + NullableInfo info; - public class Unwrap : Expression, IMemoryLocation, IAssignMethod + LocalTemporary temp; + readonly bool useDefaultValue; + + Unwrap (Expression expr, bool useDefaultValue) { - Expression expr; - NullableInfo info; + this.expr = expr; + this.loc = expr.Location; + this.useDefaultValue = useDefaultValue; - LocalTemporary temp; - bool has_temp; + info = new NullableInfo (expr.Type); + type = info.UnderlyingType; + eclass = expr.eclass; + } - protected Unwrap (Expression expr) - { - this.expr = expr; - this.loc = expr.Location; - } + public static Expression Create (Expression expr) + { + // + // Avoid unwraping and wraping of same type + // + Wrap wrap = expr as Wrap; + if (wrap != null) + return wrap.Child; - public static Unwrap Create (Expression expr, EmitContext ec) - { - return new Unwrap (expr).Resolve (ec) as Unwrap; - } - - public override Expression CreateExpressionTree (EmitContext ec) - { - return expr.CreateExpressionTree (ec); - } + return Create (expr, false); + } - public override Expression DoResolve (EmitContext ec) - { - if (expr == null) - return null; + public static Unwrap Create (Expression expr, bool useDefaultValue) + { + return new Unwrap (expr, useDefaultValue); + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return expr.CreateExpressionTree (ec); + } - temp = new LocalTemporary (expr.Type); + public override Expression DoResolve (ResolveContext ec) + { + return this; + } - 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 DoResolveLValue (ResolveContext ec, Expression right_side) + { + return DoResolve (ec); + } - public override void Emit (EmitContext ec) - { - AddressOf (ec, AddressOp.LoadStore); - ec.ig.EmitCall (OpCodes.Call, info.Value, null); - } + public override void Emit (EmitContext ec) + { + Store (ec); + if (useDefaultValue) + Invocation.EmitCall (ec, false, this, info.GetValueOrDefault, null, loc); + else + Invocation.EmitCall (ec, false, this, info.Value, null, loc); + } - public void EmitCheck (EmitContext ec) - { - AddressOf (ec, AddressOp.LoadStore); - ec.ig.EmitCall (OpCodes.Call, info.HasValue, null); - } + public void EmitCheck (EmitContext ec) + { + Store (ec); + Invocation.EmitCall (ec, false, this, info.HasValue, null, loc); + } - public void EmitGetValueOrDefault (EmitContext ec) - { - AddressOf (ec, AddressOp.LoadStore); - ec.ig.EmitCall (OpCodes.Call, info.GetValueOrDefault, null); - } + public override bool Equals (object obj) + { + Unwrap uw = obj as Unwrap; + return uw != null && expr.Equals (uw.expr); + } - public override bool Equals (object obj) - { - Unwrap uw = obj as Unwrap; - return uw != null && expr.Equals (uw.expr); + public Expression Original { + get { + return expr; } + } + + public override int GetHashCode () + { + return expr.GetHashCode (); + } - public Expression Original { - get { - return expr; - } - } - - public override int GetHashCode () - { - return expr.GetHashCode (); + public override bool IsNull { + get { + return expr.IsNull; } + } - public override bool IsNull { - get { - return expr.IsNull; - } - } + void Store (EmitContext ec) + { + if (expr is VariableReference) + return; - public void Store (EmitContext ec) - { - create_temp (ec); - } + if (temp != null) + return; - void create_temp (EmitContext ec) - { - if ((temp != null) && !has_temp) { - expr.Emit (ec); - temp.Store (ec); - has_temp = true; - } - } + expr.Emit (ec); + LocalVariable.Store (ec); + } - public void LoadTemporary (EmitContext ec) - { - temp.Emit (ec); - } + public void Load (EmitContext ec) + { + if (expr is VariableReference) + expr.Emit (ec); + else + LocalVariable.Emit (ec); + } - public void AddressOf (EmitContext ec, AddressOp mode) - { - create_temp (ec); - if (temp != null) - temp.AddressOf (ec, AddressOp.LoadStore); - else - ((IMemoryLocation) expr).AddressOf (ec, AddressOp.LoadStore); - } +#if NET_4_0 + public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx) + { + return expr.MakeExpression (ctx); + } +#endif - 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 override void MutateHoistedGenericType (AnonymousMethodStorey storey) + { + type = storey.MutateType (type); + info.Constructor = storey.MutateConstructor (info.Constructor); + info.HasValue = storey.MutateGenericMethod (info.HasValue); + info.GetValueOrDefault = storey.MutateGenericMethod (info.GetValueOrDefault); + info.Value = storey.MutateGenericMethod (info.Value); + } - Emit (ec); - } + 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 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); + // + // Keeps result of non-variable expression + // + LocalTemporary LocalVariable { + get { + if (temp == null) + temp = new LocalTemporary (info.Type); + return temp; } + } - protected class InternalWrap : Expression - { - public Expression expr; - public NullableInfo info; - - public InternalWrap (Expression expr, NullableInfo info, Location loc) - { - this.expr = expr; - this.info = info; - this.loc = loc; - - type = info.Type; - eclass = ExprClass.Value; - } + public void Emit (EmitContext ec, bool leave_copy) + { + if (leave_copy) + Load (ec); - public override Expression DoResolve (EmitContext ec) - { - return this; - } + Emit (ec); + } - public override void Emit (EmitContext ec) - { - expr.Emit (ec); - ec.ig.Emit (OpCodes.Newobj, info.Constructor); - } - } + 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 class Wrap : EmptyCast + protected class InternalWrap : Expression { - readonly NullableInfo info; + public Expression expr; + public NullableInfo info; - protected Wrap (Expression expr, Type type) - : base (expr, type) + public InternalWrap (Expression expr, NullableInfo info, Location loc) { - info = new NullableInfo (type); + this.expr = expr; + this.info = info; + this.loc = loc; + + type = info.Type; eclass = ExprClass.Value; } - public static new Expression Create (Expression expr, Type type) + public override Expression CreateExpressionTree (ResolveContext ec) { - return new Wrap (expr, type); + throw new NotSupportedException ("ET"); } - + + public override Expression DoResolve (ResolveContext ec) + { + return this; + } + public override void Emit (EmitContext ec) { - child.Emit (ec); + expr.Emit (ec); ec.ig.Emit (OpCodes.Newobj, info.Constructor); } } + } + + public class Wrap : TypeCast + { + readonly NullableInfo info; - class LiftedWrap : Wrap + protected Wrap (Expression expr, Type type) + : base (expr, type) { - public LiftedWrap (Expression expr, Type type) - : base (expr, type) - { - } + info = new NullableInfo (type); + eclass = ExprClass.Value; + } - public override Expression CreateExpressionTree (EmitContext ec) - { - return child.CreateExpressionTree (ec); - } + public Expression Child { + get { return child; } } - // - // Represents null literal lifted to nullable type - // - public class LiftedNull : EmptyConstantCast, IMemoryLocation + public override Expression CreateExpressionTree (ResolveContext ec) { - private LiftedNull (Type nullable_type, Location loc) - : base (new NullLiteral (loc), nullable_type) - { - eclass = ExprClass.Value; + TypeCast child_cast = child as TypeCast; + if (child_cast != null) { + child.Type = type; + return child_cast.CreateExpressionTree (ec); } - public static Constant Create (Type nullable, Location loc) - { - return new LiftedNull (nullable, loc); - } + return base.CreateExpressionTree (ec); + } - 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))); + 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.TypeToCoreType (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); + } + } - return CreateExpressionFactoryCall ("Constant", args); - } + // + // Represents null literal lifted to nullable type + // + public class LiftedNull : NullConstant, IMemoryLocation + { + private LiftedNull (Type nullable_type, Location loc) + : base (nullable_type, loc) + { + eclass = ExprClass.Value; + } - public override void Emit (EmitContext ec) - { - // TODO: generate less temporary variables - LocalTemporary value_target = new LocalTemporary (type); + public static Constant Create (Type nullable, Location loc) + { + return new LiftedNull (nullable, loc); + } - value_target.AddressOf (ec, AddressOp.Store); - ec.ig.Emit (OpCodes.Initobj, type); - value_target.Emit (ec); - } + public static Expression CreateFromExpression (ResolveContext ec, Expression e) + { + ec.Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'", + TypeManager.CSharpName (e.Type)); - 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); - } + return ReducedExpression.Create (Create (e.Type, e.Location), e); } - public abstract class Lifted : Expression, IMemoryLocation + public override void Emit (EmitContext ec) { - Expression expr, underlying, wrap, null_value; - Unwrap unwrap; + // TODO: generate less temporary variables + LocalTemporary value_target = new LocalTemporary (type); - protected Lifted (Expression expr, Location loc) - { - this.expr = expr; - this.loc = loc; - } - - public override Expression CreateExpressionTree (EmitContext ec) - { - return underlying.CreateExpressionTree (ec); - } + value_target.AddressOf (ec, AddressOp.Store); + ec.ig.Emit (OpCodes.Initobj, type); + value_target.Emit (ec); + } - public override Expression DoResolve (EmitContext ec) - { - expr = expr.Resolve (ec); - if (expr == null) - return null; + 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); + } + } - unwrap = Unwrap.Create (expr, ec); - if (unwrap == null) - return null; + // + // Generic lifting expression, supports all S/S? -> T/T? cases + // + public class Lifted : Expression, IMemoryLocation + { + Expression expr, null_value; + Unwrap unwrap; - underlying = ResolveUnderlying (unwrap, ec); - if (underlying == null) - return null; + public Lifted (Expression expr, Unwrap unwrap, Type type) + { + this.expr = expr; + this.unwrap = unwrap; + this.loc = expr.Location; + this.type = type; + } - TypeExpr target_type = new NullableType (underlying.Type, loc); - target_type = target_type.ResolveAsTypeTerminal (ec, false); - if (target_type == null) - return null; + public Lifted (Expression expr, Expression unwrap, Type type) + : this (expr, unwrap as Unwrap, type) + { + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return expr.CreateExpressionTree (ec); + } - wrap = Wrap.Create (underlying, target_type.Type); - if (wrap == null) - return null; + public override Expression DoResolve (ResolveContext ec) + { + // + // It's null when lifting non-nullable type + // + if (unwrap == null) { + // S -> T? is wrap only + if (TypeManager.IsNullableType (type)) + return Wrap.Create (expr, type); - null_value = LiftedNull.Create (wrap.Type, loc); + // S -> T can be simplified + return expr; + } - type = wrap.Type; - eclass = ExprClass.Value; - return this; + // Wrap target for T? + if (TypeManager.IsNullableType (type)) { + expr = Wrap.Create (expr, type); + if (expr == null) + return null; + + null_value = LiftedNull.Create (type, loc); + } else { + null_value = new NullConstant (type, loc); } - protected abstract Expression ResolveUnderlying (Expression unwrap, EmitContext ec); + eclass = ExprClass.Value; + return this; + } - public override void Emit (EmitContext ec) - { - ILGenerator ig = ec.ig; - Label is_null_label = ig.DefineLabel (); - Label end_label = ig.DefineLabel (); + public override void Emit (EmitContext ec) + { + ILGenerator ig = ec.ig; + Label is_null_label = ig.DefineLabel (); + Label end_label = ig.DefineLabel (); - unwrap.EmitCheck (ec); - ig.Emit (OpCodes.Brfalse, is_null_label); + unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse, is_null_label); - wrap.Emit (ec); - ig.Emit (OpCodes.Br, end_label); + expr.Emit (ec); - ig.MarkLabel (is_null_label); - null_value.Emit (ec); + ig.Emit (OpCodes.Br, end_label); + ig.MarkLabel (is_null_label); - ig.MarkLabel (end_label); - } + null_value.Emit (ec); + ig.MarkLabel (end_label); + } - public void AddressOf (EmitContext ec, AddressOp mode) - { - unwrap.AddressOf (ec, mode); - } + public void AddressOf (EmitContext ec, AddressOp mode) + { + unwrap.AddressOf (ec, mode); } + } + + public class LiftedUnaryOperator : Unary, IMemoryLocation + { + Unwrap unwrap; + Expression user_operator; - public class LiftedConversion : Lifted + public LiftedUnaryOperator (Unary.Operator op, Expression expr) + : base (op, expr) { - public readonly bool IsUser; - public readonly bool IsExplicit; - public readonly Type TargetType; + } - 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; - } + public void AddressOf (EmitContext ec, AddressOp mode) + { + unwrap.AddressOf (ec, mode); + } - 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); - } - } + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (user_operator != null) + return user_operator.CreateExpressionTree (ec); + + if (Oper == Operator.UnaryPlus) + return Expr.CreateExpressionTree (ec); + + return base.CreateExpressionTree (ec); } - public class LiftedUnaryOperator : Lifted + public override Expression DoResolve (ResolveContext ec) { - public readonly Unary.Operator Oper; + if (eclass != ExprClass.Invalid) + return this; - public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc) - : base (expr, loc) - { - this.Oper = op; - } + unwrap = Unwrap.Create (Expr, false); + if (unwrap == null) + return null; - protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec) - { - return new Unary (Oper, unwrap, loc).Resolve (ec); + Expression res = base.ResolveOperator (ec, unwrap); + if (res != this) { + if (user_operator == null) + return res; + } else { + res = Expr = LiftExpression (ec, Expr); } + + if (res == null) + return null; + + eclass = ExprClass.Value; + type = res.Type; + return this; } - public class LiftedBinaryOperator : Binary + public override void Emit (EmitContext ec) { - Unwrap left_unwrap, right_unwrap; - bool left_null_lifted, right_null_lifted; - Expression left_orig, right_orig; + ILGenerator ig = ec.ig; + Label is_null_label = ig.DefineLabel (); + Label end_label = ig.DefineLabel (); - public LiftedBinaryOperator (Binary.Operator op, Expression left, Expression right, - Location loc) - : base (op, left, right) - { - this.loc = loc; - } + unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse, is_null_label); - // - // 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 ()); - } + NullableInfo ni = new NullableInfo (type); - return ReducedExpression.Create (c, this); + if (user_operator != null) { + user_operator.Emit (ec); + } else { + EmitOperator (ec, ni.UnderlyingType); } - public override Expression DoResolve (EmitContext ec) - { - if (eclass != ExprClass.Invalid) - return this; + ig.Emit (OpCodes.Newobj, ni.Constructor); + ig.Emit (OpCodes.Br_S, end_label); - // TODO: How does it work with use-operators? - if ((Oper == Binary.Operator.LogicalAnd) || - (Oper == Binary.Operator.LogicalOr)) { - Error_OperatorCannotBeApplied (left, right); - return null; - } + ig.MarkLabel (is_null_label); + LiftedNull.Create (type, loc).Emit (ec); - left_orig = left; - if (TypeManager.IsNullableType (left.Type)) { - left = left_unwrap = Unwrap.Create (left, ec); - if (left == null) - return null; - } + ig.MarkLabel (end_label); + } - right_orig = right; - if (TypeManager.IsNullableType (right.Type)) { - right = right_unwrap = Unwrap.Create (right, ec); - if (right == null) - return null; - } + Expression LiftExpression (ResolveContext ec, Expression expr) + { + TypeExpr lifted_type = new NullableType (expr.Type, expr.Location); + lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false); + if (lifted_type == null) + return null; - if (left is NullLiteral) { - left = right; - left_null_lifted = true; - } + expr.Type = lifted_type.Type; + return expr; + } - if (right is NullLiteral) { - right = left; - right_null_lifted = true; - } + protected override Expression ResolveEnumOperator (ResolveContext ec, Expression expr) + { + expr = base.ResolveEnumOperator (ec, expr); + if (expr == null) + return null; - eclass = ExprClass.Value; - Expression expr = DoResolveCore (ec, left_orig, right_orig); - if (expr != this || (Oper & Operator.ComparisonMask) != 0) - return expr; + Expr = LiftExpression (ec, Expr); + return LiftExpression (ec, expr); + } - TypeExpr target_type = new NullableType (type, loc); - target_type = target_type.ResolveAsTypeTerminal (ec, false); - if (target_type == null) - return null; + protected override Expression ResolveUserOperator (ResolveContext ec, Expression expr) + { + expr = base.ResolveUserOperator (ec, expr); + if (expr == null) + return null; - type = target_type.Type; - return this; + // + // When a user operator is of non-nullable type + // + if (Expr is Unwrap) { + user_operator = LiftExpression (ec, expr); + return user_operator; } - void EmitBitwiseBoolean (EmitContext ec) - { - ILGenerator ig = ec.ig; + return expr; + } + } - Label load_left = ig.DefineLabel (); - Label load_right = ig.DefineLabel (); - Label end_label = ig.DefineLabel (); + 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) + { + this.loc = loc; + } - left_unwrap.EmitGetValueOrDefault (ec); - ig.Emit (OpCodes.Brtrue_S, load_right); + public override Expression CreateExpressionTree (ResolveContext ec) + { + if (user_operator != null) + return user_operator.CreateExpressionTree (ec); - right_unwrap.EmitGetValueOrDefault (ec); - ig.Emit (OpCodes.Brtrue_S, load_left); + return base.CreateExpressionTree (ec); + } - left_unwrap.EmitCheck (ec); - ig.Emit (OpCodes.Brfalse_S, load_right); + // + // 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 (ResolveContext ec, Expression expr) + { + // FIXME: Handle side effect constants + Constant c = new BoolConstant (Oper == Operator.Inequality, loc); + + if ((Oper & Operator.EqualityMask) != 0) { + ec.Report.Warning (472, 2, loc, "The result of comparing value type `{0}' with null is `{1}'", + TypeManager.CSharpName (expr.Type), c.AsString ()); + } else { + ec.Report.Warning (464, 2, loc, "The result of comparing type `{0}' with null is always `{1}'", + TypeManager.CSharpName (expr.Type), c.AsString ()); + } - // load left - ig.MarkLabel (load_left); + return ReducedExpression.Create (c, this); + } - if (Oper == Operator.BitwiseAnd) { - left_unwrap.LoadTemporary (ec); - } else { - right_unwrap.LoadTemporary (ec); - right_unwrap = left_unwrap; - } - ig.Emit (OpCodes.Br_S, end_label); + public override Expression DoResolve (ResolveContext ec) + { + if (eclass != ExprClass.Invalid) + return this; - // load right - ig.MarkLabel (load_right); - right_unwrap.LoadTemporary (ec); + if ((Oper & Operator.LogicalMask) != 0) { + Error_OperatorCannotBeApplied (ec, left, right); + return null; + } - ig.MarkLabel (end_label); + bool use_default_call = (Oper & (Operator.BitwiseMask | Operator.EqualityMask)) != 0; + left_orig = left; + if (TypeManager.IsNullableType (left.Type)) { + left = left_unwrap = Unwrap.Create (left, use_default_call); + if (left == null) + return null; } - void EmitEquality (EmitContext ec) - { - ILGenerator ig = ec.ig; + right_orig = right; + if (TypeManager.IsNullableType (right.Type)) { + right = right_unwrap = Unwrap.Create (right, use_default_call); + if (right == null) + return null; + } - Label both_have_value_label = ig.DefineLabel (); - Label end_label = ig.DefineLabel (); + // + // 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_orig.IsNull) { + left = right; + left_null_lifted = true; + type = TypeManager.bool_type; + } - // - // 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 (dissimilar_label); - if (Oper == Operator.Inequality) - ig.Emit (OpCodes.Ldc_I4_1); - else - ig.Emit (OpCodes.Ldc_I4_0); - - ig.MarkLabel (end_label); - return; - } + if (right_orig.IsNull) { + right = left; + right_null_lifted = true; + type = TypeManager.bool_type; + } - // - // 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); - } + eclass = ExprClass.Value; + return DoResolveCore (ec, left_orig, right_orig); + } - if (Oper == Binary.Operator.Equality) - ig.Emit (OpCodes.Ldc_I4_0); - else - ig.Emit (OpCodes.Ldc_I4_1); - ig.Emit (OpCodes.Br, end_label); + void EmitBitwiseBoolean (EmitContext ec) + { + ILGenerator ig = ec.ig; - ig.MarkLabel (both_have_value_label); - EmitOperator (ec); + Label load_left = ig.DefineLabel (); + Label load_right = ig.DefineLabel (); + Label end_label = ig.DefineLabel (); - ig.MarkLabel (end_label); + left_unwrap.Emit (ec); + ig.Emit (OpCodes.Brtrue_S, load_right); + + right_unwrap.Emit (ec); + ig.Emit (OpCodes.Brtrue_S, load_left); + + left_unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse_S, load_right); + + // load left + ig.MarkLabel (load_left); + + 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 EmitComparision (EmitContext ec) - { - ILGenerator ig = ec.ig; + // load right + ig.MarkLabel (load_right); + right_unwrap.Load (ec); - Label is_null_label = ig.DefineLabel (); - Label end_label = ig.DefineLabel (); + ig.MarkLabel (end_label); + } - if (left_unwrap != null) { - left_unwrap.EmitCheck (ec); - ig.Emit (OpCodes.Brfalse, is_null_label); + // + // Emits optimized equality or inequality operator when possible + // + void EmitEquality (EmitContext ec) + { + ILGenerator ig = ec.ig; + + // + // 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; + } - if (right_unwrap != null) { - right_unwrap.EmitCheck (ec); - ig.Emit (OpCodes.Brfalse, is_null_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; + } - EmitOperator (ec); - ig.Emit (OpCodes.Br_S, end_label); + Label dissimilar_label = ig.DefineLabel (); + Label end_label = ig.DefineLabel (); - ig.MarkLabel (is_null_label); - ig.Emit (OpCodes.Ldc_I4_0); + if (user_operator != null) { + user_operator.Emit (ec); + ig.Emit (Oper == Operator.Equality ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, dissimilar_label); + } else { + left.Emit (ec); + right.Emit (ec); - ig.MarkLabel (end_label); + ig.Emit (OpCodes.Bne_Un_S, dissimilar_label); } - - 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 (IsBitwiseBoolean) { - EmitBitwiseBoolean (ec); - return; - } + if (left_unwrap != null) + left_unwrap.EmitCheck (ec); - if ((Oper & Operator.EqualityMask) != 0) { - EmitEquality (ec); - return; - } + if (right_unwrap != null) + right_unwrap.EmitCheck (ec); - if ((Oper & Operator.ComparisonMask) != 0) { - EmitComparision (ec); - return; + 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); } + } - ILGenerator ig = ec.ig; + ig.Emit (OpCodes.Br_S, end_label); - Label is_null_label = ig.DefineLabel (); - Label end_label = ig.DefineLabel (); + ig.MarkLabel (dissimilar_label); + if (Oper == Operator.Inequality) + ig.Emit (OpCodes.Ldc_I4_1); + else + ig.Emit (OpCodes.Ldc_I4_0); - if (left_unwrap != null) { - left_unwrap.EmitCheck (ec); - ig.Emit (OpCodes.Brfalse, is_null_label); - } + 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); + } - if (right_unwrap != null) { - right_unwrap.EmitCheck (ec); - ig.Emit (OpCodes.Brfalse, is_null_label); - } + public override void Emit (EmitContext ec) + { + // + // Optimize same expression operation + // + if (right_unwrap != null && right.Equals (left)) + right_unwrap = left_unwrap; - base.EmitOperator (ec); + if (user_operator == null && IsBitwiseBoolean) { + EmitBitwiseBoolean (ec); + return; + } - ig.Emit (OpCodes.Br_S, end_label); - ig.MarkLabel (is_null_label); - LiftedNull.Create (type, loc).Emit (ec); + if ((Oper & Operator.EqualityMask) != 0) { + EmitEquality (ec); + return; + } - ig.MarkLabel (end_label); + ILGenerator ig = ec.ig; + + 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); } - 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; - } + // + // 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); } - Expression LiftResult (EmitContext ec, Expression res_expr) - { - TypeExpr lifted_type; + EmitOperator (ec, left.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; + if (wrap_ctor != null) + ig.Emit (OpCodes.Newobj, wrap_ctor); - left = EmptyCast.Create (left, lifted_type.Type); - } + ig.Emit (OpCodes.Br_S, end_label); + ig.MarkLabel (is_null_label); - 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; + if ((Oper & Operator.ComparisonMask) != 0) { + ig.Emit (OpCodes.Ldc_I4_0); + } else { + LiftedNull.Create (type, loc).Emit (ec); + } - right = EmptyCast.Create (right, lifted_type.Type); - } + ig.MarkLabel (end_label); + } - if (left_null_lifted) { - left = LiftedNull.Create (right.Type, left.Location); + protected override void EmitOperator (EmitContext ec, Type l) + { + if (user_operator != null) { + user_operator.Emit (ec); + return; + } - // - // Value types and null comparison - // - if (right_unwrap == null || (Oper & Operator.RelationalMask) != 0) - return CreateNullConstant (right_orig).Resolve (ec); - } + if (TypeManager.IsNullableType (l)) + l = TypeManager.TypeToCoreType (TypeManager.GetTypeArguments (l) [0]); - if (right_null_lifted) { - right = LiftedNull.Create (left.Type, right.Location); + base.EmitOperator (ec, l); + } - // - // Value types and null comparison - // - if (left_unwrap == null || (Oper & Operator.RelationalMask) != 0) - return CreateNullConstant (left_orig).Resolve (ec); - } + bool IsBitwiseBoolean { + get { + return (Oper & Operator.BitwiseMask) != 0 && left_unwrap != null && right_unwrap != null && + left_unwrap.Type == TypeManager.bool_type && right_unwrap.Type == TypeManager.bool_type; + } + } + + Expression LiftResult (ResolveContext ec, Expression res_expr) + { + TypeExpr lifted_type; + + // + // Avoid double conversion + // + if (left_unwrap == null || left_null_lifted || !TypeManager.IsEqual (left_unwrap.Type, left.Type) || (left_unwrap != null && right_null_lifted)) { + 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 || IsBitwiseBoolean) - return res_expr; + 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) || (right_unwrap != null && left_null_lifted)) { + lifted_type = new NullableType (right.Type, loc); + lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false); + if (lifted_type == null) + return null; + if (right is UserCast || right is TypeCast) + right.Type = lifted_type.Type; + else + right = EmptyCast.Create (right, lifted_type.Type); + } + + 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 LiftedWrap (res_expr, lifted_type.Type).Resolve (ec); + wrap_ctor = new NullableInfo (lifted_type.Type).Constructor; + type = res_expr.Type = lifted_type.Type; } - protected override Expression ResolveOperatorPredefined (EmitContext ec, Binary.PredefinedOperator [] operators, bool primitives_only) - { - Expression e = base.ResolveOperatorPredefined (ec, operators, primitives_only); + if (left_null_lifted) { + left = LiftedNull.Create (right.Type, left.Location); - if (e == this) - return LiftResult (ec, e); + if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0) + return LiftedNull.CreateFromExpression (ec, res_expr); // - // 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. + // Value types and null comparison // - 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; + if (right_unwrap == null || (Oper & Operator.RelationalMask) != 0) + return CreateNullConstant (ec, right_orig).Resolve (ec); } - protected override Expression ResolveUserOperator (EmitContext ec, Type l, Type r) - { - Expression expr = base.ResolveUserOperator (ec, l, r); - if (expr == null) - return null; + if (right_null_lifted) { + right = LiftedNull.Create (left.Type, right.Location); + + if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0) + return LiftedNull.CreateFromExpression (ec, res_expr); - return LiftResult (ec, expr); + // + // Value types and null comparison + // + if (left_unwrap == null || (Oper & Operator.RelationalMask) != 0) + return CreateNullConstant (ec, left_orig).Resolve (ec); } + + return res_expr; } - public class NullCoalescingOperator : Expression + protected override Expression ResolveOperatorPredefined (ResolveContext ec, Binary.PredefinedOperator [] operators, bool primitives_only, Type enum_type) { - Expression left, right; - Unwrap unwrap; + Expression e = base.ResolveOperatorPredefined (ec, operators, primitives_only, enum_type); - public NullCoalescingOperator (Expression left, Expression right, Location loc) - { - this.left = left; - this.right = right; - this.loc = loc; + 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); } - - 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); - } - 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); - } + return e; + } - public override Expression DoResolve (EmitContext ec) - { - if (type != null) - return this; + protected override Expression ResolveUserOperator (ResolveContext ec, Type l, Type r) + { + Expression expr = base.ResolveUserOperator (ec, l, r); + if (expr == null) + return null; - left = left.Resolve (ec); - right = right.Resolve (ec); + expr = LiftResult (ec, expr); + if (expr is Constant) + return expr; - if (left == null || right == null) - return null; + type = expr.Type; + user_operator = expr; + return this; + } + } - 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); + 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 (ResolveContext ec) + { + if (left.Type == TypeManager.null_type) + ec.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; + + Arguments c_args = new Arguments (2); + c_args.Add (new Argument (uc.CreateExpressionTree (ec))); + c_args.Add (new Argument (left.CreateExpressionTree (ec))); + conversion = CreateExpressionFactoryCall (ec, "Lambda", c_args); + } + + Arguments args = new Arguments (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 (ec, "Coalesce", args); + } + + Expression ConvertExpression (ResolveContext ec) + { + // TODO: ImplicitConversionExists should take care of this + if (left.eclass == ExprClass.MethodGroup) + return null; + + Type ltype = left.Type; + + // + // 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)) { + unwrap = Unwrap.Create (left, false); + 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; } + } else if (TypeManager.IsReferenceType (ltype)) { + 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); - Expression left_null = unwrap != null ? unwrap : left; - expr = Convert.ImplicitConversion (ec, left_null, rtype, loc); - if (expr != null) { - left = expr; - type = rtype; + // + // 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; } - - Binary.Error_OperatorCannotBeApplied (loc, "??", ltype, rtype); + } else { return null; } - public override void Emit (EmitContext ec) - { - ILGenerator ig = ec.ig; - - Label is_null_label = ig.DefineLabel (); - Label end_label = ig.DefineLabel (); + Type rtype = right.Type; + if (!Convert.ImplicitConversionExists (ec, unwrap != null ? unwrap : left, rtype) || right.eclass == ExprClass.MethodGroup) + return null; - if (unwrap != null) { - unwrap.EmitCheck (ec); - ig.Emit (OpCodes.Brfalse, is_null_label); + // + // Reduce (null ?? right) to right + // + if (left.IsNull) + return ReducedExpression.Create (right, this).Resolve (ec); - left.Emit (ec); - ig.Emit (OpCodes.Br, end_label); + left = Convert.ImplicitConversion (ec, unwrap != null ? unwrap : left, rtype, loc); + type = rtype; + return this; + } - ig.MarkLabel (is_null_label); - right.Emit (ec); + public override Expression DoResolve (ResolveContext ec) + { + if (eclass != ExprClass.Invalid) + return this; - ig.MarkLabel (end_label); - } else { - left.Emit (ec); - ig.Emit (OpCodes.Dup); - ig.Emit (OpCodes.Brtrue, end_label); + left = left.Resolve (ec); + right = right.Resolve (ec); - ig.MarkLabel (is_null_label); + if (left == null || right == null) + return null; - ig.Emit (OpCodes.Pop); - right.Emit (ec); + eclass = ExprClass.Value; - ig.MarkLabel (end_label); - } + Expression e = ConvertExpression (ec); + if (e == null) { + Binary.Error_OperatorCannotBeApplied (ec, left, right, "??", loc); + return null; } - protected override void CloneTo (CloneContext clonectx, Expression t) - { - NullCoalescingOperator target = (NullCoalescingOperator) t; - target.left = left.Clone (clonectx); - target.right = right.Clone (clonectx); - } + return e; } - public class LiftedUnaryMutator : ExpressionStatement + public override void Emit (EmitContext ec) { - public readonly UnaryMutator.Mode Mode; - Expression expr, null_value; - UnaryMutator underlying; - Unwrap unwrap; - - public LiftedUnaryMutator (UnaryMutator.Mode mode, Expression expr, Location loc) - { - this.expr = expr; - this.Mode = mode; - this.loc = loc; + ILGenerator ig = ec.ig; - eclass = ExprClass.Value; - } - - 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; + Label end_label = ig.DefineLabel (); - underlying = (UnaryMutator) new UnaryMutator (Mode, unwrap, loc).Resolve (ec); - if (underlying == null) - return null; - - type = expr.Type; - null_value = LiftedNull.Create (type, loc); - return this; - } - - void DoEmit (EmitContext ec, bool is_expr) - { - ILGenerator ig = ec.ig; + if (unwrap != null) { Label is_null_label = ig.DefineLabel (); - Label end_label = ig.DefineLabel (); unwrap.EmitCheck (ec); ig.Emit (OpCodes.Brfalse, is_null_label); - if (is_expr) - underlying.Emit (ec); - else - underlying.EmitStatement (ec); + left.Emit (ec); ig.Emit (OpCodes.Br, end_label); ig.MarkLabel (is_null_label); - if (is_expr) - null_value.Emit (ec); + right.Emit (ec); ig.MarkLabel (end_label); + return; } - public override void Emit (EmitContext ec) - { - DoEmit (ec, true); - } + left.Emit (ec); - public override void EmitStatement (EmitContext ec) - { - DoEmit (ec, false); + ig.Emit (OpCodes.Dup); + ig.Emit (OpCodes.Brtrue, end_label); + + ig.Emit (OpCodes.Pop); + right.Emit (ec); + + ig.MarkLabel (end_label); + } + + public override void MutateHoistedGenericType (AnonymousMethodStorey storey) + { + left.MutateHoistedGenericType (storey); + right.MutateHoistedGenericType (storey); + type = storey.MutateType (type); + } + + protected override void CloneTo (CloneContext clonectx, Expression t) + { + NullCoalescingOperator target = (NullCoalescingOperator) t; + + target.left = left.Clone (clonectx); + target.right = right.Clone (clonectx); + } + } + + public class LiftedUnaryMutator : ExpressionStatement + { + public readonly UnaryMutator.Mode Mode; + Expression expr; + UnaryMutator underlying; + Unwrap unwrap; + + public LiftedUnaryMutator (UnaryMutator.Mode mode, Expression expr, Location loc) + { + this.expr = expr; + this.Mode = mode; + this.loc = loc; + + eclass = ExprClass.Value; + } + + public override Expression CreateExpressionTree (ResolveContext ec) + { + return new SimpleAssign (this, this).CreateExpressionTree (ec); + } + + public override Expression DoResolve (ResolveContext ec) + { + expr = expr.Resolve (ec); + if (expr == null) + return null; + + unwrap = Unwrap.Create (expr, false); + if (unwrap == null) + return null; + + underlying = (UnaryMutator) new UnaryMutator (Mode, unwrap).Resolve (ec); + if (underlying == null) + return null; + + type = expr.Type; + return this; + } + + void DoEmit (EmitContext ec, bool is_expr) + { + ILGenerator ig = ec.ig; + Label is_null_label = ig.DefineLabel (); + Label end_label = ig.DefineLabel (); + + unwrap.EmitCheck (ec); + ig.Emit (OpCodes.Brfalse, is_null_label); + + if (is_expr) { + underlying.Emit (ec); + ig.Emit (OpCodes.Br_S, end_label); + } else { + underlying.EmitStatement (ec); } + + ig.MarkLabel (is_null_label); + if (is_expr) + LiftedNull.Create (type, loc).Emit (ec); + + ig.MarkLabel (end_label); + } + + public override void Emit (EmitContext ec) + { + DoEmit (ec, true); + } + + public override void EmitStatement (EmitContext ec) + { + DoEmit (ec, false); } } }