X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fmcs%2Fnullable.cs;h=1fa708dff18b70c1d642e4044061f901e0fa4d5f;hb=b335b2bec654c7eed2b27fd2e5911d97bf60b588;hp=d82e29ad3c0eb8e3bb0abade7a3d6f647b9d1215;hpb=ff347b86e3c15c9a17045ece1f87284efa63f6cb;p=mono.git diff --git a/mcs/mcs/nullable.cs b/mcs/mcs/nullable.cs index d82e29ad3c0..1fa708dff18 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; @@ -20,9 +20,9 @@ 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,45 +34,40 @@ namespace Mono.CSharp.Nullable : 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); - args.Add (underlying); - if (TypeManager.generic_nullable_type == null) { TypeManager.generic_nullable_type = TypeManager.CoreLookupType ( "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 (IResolveContext ec, bool silent) + { + return ResolveAsBaseTerminal (ec, silent); + } } 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 MethodInfo HasValue; + public MethodInfo Value; + public MethodInfo GetValueOrDefault; + public ConstructorInfo Constructor; public NullableInfo (Type type) { 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); + 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); HasValue = has_value_pi.GetGetMethod (false); @@ -88,44 +83,6 @@ namespace Mono.CSharp.Nullable Constructor = type.GetConstructor (new Type[] { UnderlyingType }); } } - - public class HasValue : Expression - { - Expression expr; - NullableInfo info; - - private HasValue (Expression expr) - { - this.expr = expr; - } - - public static Expression Create (Expression expr, EmitContext ec) - { - return new HasValue (expr).Resolve (ec); - } - - 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); - } - - public override Expression DoResolve (EmitContext ec) - { - this.info = new NullableInfo (expr.Type); - - type = TypeManager.bool_type; - eclass = expr.eclass; - return this; - } - } public class Unwrap : Expression, IMemoryLocation, IAssignMethod { @@ -133,56 +90,66 @@ namespace Mono.CSharp.Nullable NullableInfo info; LocalTemporary temp; - bool has_temp; protected Unwrap (Expression expr) { this.expr = expr; this.loc = expr.Location; + + info = new NullableInfo (expr.Type); + type = info.UnderlyingType; + eclass = expr.eclass; + } + + public static Expression Create (Expression expr) + { + // + // Avoid unwraping and wraping of same type + // + Wrap wrap = expr as Wrap; + if (wrap != null) + return wrap.Child; + + return Create (expr, null); } public static Unwrap Create (Expression expr, EmitContext ec) { - return new Unwrap (expr).Resolve (ec) as Unwrap; + return new Unwrap (expr); } public override Expression CreateExpressionTree (EmitContext ec) { return expr.CreateExpressionTree (ec); - } + } public override Expression DoResolve (EmitContext ec) { - if (expr == null) - return null; - - temp = new LocalTemporary (expr.Type); - - 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 void Emit (EmitContext ec) { + Store (ec); AddressOf (ec, AddressOp.LoadStore); ec.ig.EmitCall (OpCodes.Call, info.Value, null); } public void EmitCheck (EmitContext ec) { + Store (ec); AddressOf (ec, AddressOp.LoadStore); ec.ig.EmitCall (OpCodes.Call, info.HasValue, null); } public void EmitGetValueOrDefault (EmitContext ec) { + Store (ec); AddressOf (ec, AddressOp.LoadStore); ec.ig.EmitCall (OpCodes.Call, info.GetValueOrDefault, null); } @@ -210,43 +177,59 @@ namespace Mono.CSharp.Nullable } } - public void Store (EmitContext ec) + void Store (EmitContext ec) { - create_temp (ec); + if (expr is VariableReference) + return; + + if (temp != null) + return; + + expr.Emit (ec); + LocalVariable.Store (ec); } - void create_temp (EmitContext ec) + public void Load (EmitContext ec) { - if ((temp != null) && !has_temp) { + if (expr is VariableReference) expr.Emit (ec); - temp.Store (ec); - has_temp = true; - } + else + LocalVariable.Emit (ec); } - public void LoadTemporary (EmitContext ec) + public override void MutateHoistedGenericType (AnonymousMethodStorey storey) { - temp.Emit (ec); + 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); } public void AddressOf (EmitContext ec, AddressOp mode) { - create_temp (ec); - if (temp != null) - temp.AddressOf (ec, AddressOp.LoadStore); + IMemoryLocation ml = expr as VariableReference; + if (ml != null) + ml.AddressOf (ec, mode); else - ((IMemoryLocation) expr).AddressOf (ec, AddressOp.LoadStore); + LocalVariable.AddressOf (ec, mode); + } + + // + // Keeps result of non-variable expression + // + LocalTemporary LocalVariable { + get { + if (temp == null) + temp = new LocalTemporary (info.Type); + return temp; + } } public void Emit (EmitContext ec, bool leave_copy) { - create_temp (ec); - if (leave_copy) { - if (temp != null) - temp.Emit (ec); - else - expr.Emit (ec); - } + if (leave_copy) + Load (ec); Emit (ec); } @@ -273,6 +256,11 @@ namespace Mono.CSharp.Nullable eclass = ExprClass.Value; } + public override Expression CreateExpressionTree (EmitContext ec) + { + throw new NotSupportedException ("ET"); + } + public override Expression DoResolve (EmitContext ec) { return this; @@ -286,7 +274,7 @@ namespace Mono.CSharp.Nullable } } - public class Wrap : EmptyCast + public class Wrap : TypeCast { readonly NullableInfo info; @@ -297,8 +285,30 @@ namespace Mono.CSharp.Nullable eclass = ExprClass.Value; } - public static new Expression Create (Expression expr, Type type) + public Expression Child { + get { return child; } + } + + public override Expression CreateExpressionTree (EmitContext ec) + { + TypeCast child_cast = child as TypeCast; + if (child_cast != null) { + child.Type = type; + return child_cast.CreateExpressionTree (ec); + } + + return base.CreateExpressionTree (ec); + } + + 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); } @@ -309,19 +319,6 @@ namespace Mono.CSharp.Nullable } } - class LiftedWrap : Wrap - { - public LiftedWrap (Expression expr, Type type) - : base (expr, type) - { - } - - public override Expression CreateExpressionTree (EmitContext ec) - { - return child.CreateExpressionTree (ec); - } - } - // // Represents null literal lifted to nullable type // @@ -343,7 +340,7 @@ namespace Mono.CSharp.Nullable Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'", TypeManager.CSharpName (e.Type)); - return ReducedExpression.Create (Create (e.Type, e.Location) , e); + return ReducedExpression.Create (Create (e.Type, e.Location), e); } public override Expression CreateExpressionTree (EmitContext ec) @@ -375,54 +372,47 @@ namespace Mono.CSharp.Nullable } } - public abstract class Lifted : Expression, IMemoryLocation + public class Lifted : Expression, IMemoryLocation { - Expression expr, underlying, wrap, null_value; + Expression expr, wrap, null_value; Unwrap unwrap; - protected Lifted (Expression expr, Location loc) + public Lifted (Expression expr, Unwrap unwrap, Type type) { this.expr = expr; - this.loc = loc; + this.unwrap = unwrap; + this.loc = expr.Location; + this.type = type; + } + + public Lifted (Expression expr, Expression unwrap, Type type) + : this (expr, unwrap as Unwrap, type) + { } public override Expression CreateExpressionTree (EmitContext ec) { - return underlying.CreateExpressionTree (ec); + return wrap.CreateExpressionTree (ec); } public override Expression DoResolve (EmitContext ec) { - expr = expr.Resolve (ec); - if (expr == null) + wrap = Wrap.Create (expr, type); + if (wrap == null) return null; - unwrap = Unwrap.Create (expr, ec); + // + // It's null when lifted conversion is transparent + // 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; + return wrap; - wrap = Wrap.Create (underlying, target_type.Type); - if (wrap == null) - return null; - - null_value = LiftedNull.Create (wrap.Type, loc); + null_value = LiftedNull.Create (type, loc); - type = wrap.Type; eclass = ExprClass.Value; return this; } - protected abstract Expression ResolveUnderlying (Expression unwrap, EmitContext ec); - public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; @@ -447,49 +437,119 @@ namespace Mono.CSharp.Nullable } } - public class LiftedConversion : Lifted + public class LiftedUnaryOperator : Unary, IMemoryLocation { - public readonly bool IsUser; - public readonly bool IsExplicit; - public readonly Type TargetType; + Unwrap unwrap; + Expression user_operator; - public LiftedConversion (Expression expr, Type target_type, bool is_user, - bool is_explicit, Location loc) - : base (expr, loc) + public LiftedUnaryOperator (Unary.Operator op, Expression expr) + : base (op, expr) { - this.IsUser = is_user; - this.IsExplicit = is_explicit; - this.TargetType = target_type; } - protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec) + public void AddressOf (EmitContext ec, AddressOp mode) + { + unwrap.AddressOf (ec, mode); + } + + public override Expression CreateExpressionTree (EmitContext ec) { - Type type = TypeManager.GetTypeArguments (TargetType) [0]; + if (user_operator != null) + return user_operator.CreateExpressionTree (ec); - if (IsUser) { - return Convert.UserDefinedConversion (ec, unwrap, type, loc, IsExplicit); + if (Oper == Operator.UnaryPlus) + return Expr.CreateExpressionTree (ec); + + return base.CreateExpressionTree (ec); + } + + 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 { - if (IsExplicit) - return Convert.ExplicitConversion (ec, unwrap, type, loc); - else - return Convert.ImplicitConversion (ec, unwrap, type, loc); + res = Expr = LiftExpression (ec, Expr); + } + + if (res == null) + return null; + + eclass = ExprClass.Value; + type = res.Type; + return this; + } + + 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); + + NullableInfo ni = new NullableInfo (type); + + if (user_operator != null) { + user_operator.Emit (ec); + } else { + EmitOperator (ec, ni.UnderlyingType); } + + 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 LiftedUnaryOperator : Lifted - { - public readonly Unary.Operator Oper; + Expression LiftExpression (EmitContext 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; + + expr.Type = lifted_type.Type; + return expr; + } - public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc) - : base (expr, loc) + protected override Expression ResolveEnumOperator (EmitContext ec, Expression expr) { - this.Oper = op; + expr = base.ResolveEnumOperator (ec, expr); + if (expr == null) + return null; + + Expr = LiftExpression (ec, Expr); + return LiftExpression (ec, expr); } - protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec) + protected override Expression ResolveUserOperator (EmitContext ec, Expression expr) { - return new Unary (Oper, unwrap, loc).Resolve (ec); + expr = base.ResolveUserOperator (ec, expr); + if (expr == null) + return null; + + // + // When a user operator is of non-nullable type + // + if (Expr is Unwrap) { + user_operator = LiftExpression (ec, expr); + return user_operator; + } + + return expr; } } @@ -498,6 +558,8 @@ namespace Mono.CSharp.Nullable 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) @@ -506,6 +568,14 @@ namespace Mono.CSharp.Nullable this.loc = loc; } + public override Expression CreateExpressionTree (EmitContext ec) + { + if (user_operator != null) + return user_operator.CreateExpressionTree (ec); + + return base.CreateExpressionTree (ec); + } + // // CSC 2 has this behavior, it allows structs to be compared // with the null literal *outside* of a generics context and @@ -517,11 +587,10 @@ namespace Mono.CSharp.Nullable 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", + Report.Warning (472, 2, loc, "The result of comparing value type `{0}' with null is `{1}'", expr.GetSignatureForError (), c.AsString ()); } else { - Report.Warning (464, 2, loc, "The result of comparing type `{0}' against null is always `{1}'", + Report.Warning (464, 2, loc, "The result of comparing type `{0}' with null is always `{1}'", expr.GetSignatureForError (), c.AsString ()); } @@ -533,9 +602,7 @@ namespace Mono.CSharp.Nullable if (eclass != ExprClass.Invalid) return this; - // TODO: How does it work with use-operators? - if ((Oper == Binary.Operator.LogicalAnd) || - (Oper == Binary.Operator.LogicalOr)) { + if ((Oper & Operator.LogicalMask) != 0) { Error_OperatorCannotBeApplied (left, right); return null; } @@ -559,30 +626,20 @@ namespace Mono.CSharp.Nullable // Arguments can be lifted for equal operators when the return type is bool and both // arguments are of same type // - if (left is NullLiteral) { + if (left_orig.IsNull) { left = right; left_null_lifted = true; type = TypeManager.bool_type; } - if (right is NullLiteral) { + if (right_orig.IsNull) { right = left; right_null_lifted = true; type = TypeManager.bool_type; } eclass = ExprClass.Value; - Expression expr = DoResolveCore (ec, left_orig, right_orig); - if (expr != this || (Oper & Operator.ComparisonMask) != 0) - return expr; - - TypeExpr target_type = new NullableType (type, loc); - target_type = target_type.ResolveAsTypeTerminal (ec, false); - if (target_type == null) - return null; - - type = target_type.Type; - return this; + return DoResolveCore (ec, left_orig, right_orig); } void EmitBitwiseBoolean (EmitContext ec) @@ -606,117 +663,96 @@ namespace Mono.CSharp.Nullable ig.MarkLabel (load_left); if (Oper == Operator.BitwiseAnd) { - left_unwrap.LoadTemporary (ec); + left_unwrap.Load (ec); } else { - right_unwrap.LoadTemporary (ec); + right_unwrap.Load (ec); right_unwrap = left_unwrap; } ig.Emit (OpCodes.Br_S, end_label); // load right ig.MarkLabel (load_right); - right_unwrap.LoadTemporary (ec); + right_unwrap.Load (ec); ig.MarkLabel (end_label); } - void EmitEquality (EmitContext ec) + // + // Emits optimized equality or inequality operator when possible + // + bool EmitEquality (EmitContext ec) { ILGenerator ig = ec.ig; - Label both_have_value_label = ig.DefineLabel (); - Label end_label = ig.DefineLabel (); - // - // Both are nullable types + // Either left or right is null // - 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); - + if (left_unwrap != null && (right_null_lifted || right.IsNull)) { 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 + if (Oper == Binary.Operator.Equality) { ig.Emit (OpCodes.Ldc_I4_0); - - ig.MarkLabel (end_label); - return; + 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 { + if (right_unwrap != null && (left_null_lifted || left.IsNull)) { right_unwrap.EmitCheck (ec); - if (left.IsNull) { - if (Oper == Binary.Operator.Equality) { - ig.Emit (OpCodes.Ldc_I4_0); - ig.Emit (OpCodes.Ceq); - } - return; + if (Oper == Binary.Operator.Equality) { + ig.Emit (OpCodes.Ldc_I4_0); + ig.Emit (OpCodes.Ceq); } - ig.Emit (OpCodes.Brtrue_S, both_have_value_label); + 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); + if (left is UserCast || right is UserCast) + return false; - ig.MarkLabel (end_label); - } + Label dissimilar_label = ig.DefineLabel (); + Label end_label = ig.DefineLabel (); - void EmitComparision (EmitContext ec) - { - ILGenerator ig = ec.ig; + if (left_unwrap != null) + left_unwrap.EmitGetValueOrDefault (ec); + else + left.Emit (ec); - Label is_null_label = ig.DefineLabel (); - Label end_label = ig.DefineLabel (); + if (right_unwrap != null) + right_unwrap.EmitGetValueOrDefault (ec); + else + right.Emit (ec); - if (left_unwrap != null) { - left_unwrap.EmitCheck (ec); - ig.Emit (OpCodes.Brfalse, is_null_label); - } + ig.Emit (OpCodes.Bne_Un_S, dissimilar_label); - if (right_unwrap != null) { + if (left_unwrap != null) + left_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.MarkLabel (is_null_label); - ig.Emit (OpCodes.Ldc_I4_0); + 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 true; } public override void EmitBranchable (EmitContext ec, Label target, bool onTrue) @@ -727,24 +763,20 @@ namespace Mono.CSharp.Nullable public override void Emit (EmitContext ec) { - if (left_unwrap != null) - left_unwrap.Store (ec); - if (right_unwrap != null) - right_unwrap.Store (ec); + // + // Optimize same expression operation + // + if (right_unwrap != null && right.Equals (left)) + right_unwrap = left_unwrap; - if (IsBitwiseBoolean) { + if (user_operator == null && IsBitwiseBoolean) { EmitBitwiseBoolean (ec); return; } if ((Oper & Operator.EqualityMask) != 0) { - EmitEquality (ec); - return; - } - - if ((Oper & Operator.ComparisonMask) != 0) { - EmitComparision (ec); - return; + if (EmitEquality (ec)) + return; } ILGenerator ig = ec.ig; @@ -757,20 +789,52 @@ namespace Mono.CSharp.Nullable ig.Emit (OpCodes.Brfalse, is_null_label); } - if (right_unwrap != null) { + // + // 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); } - base.EmitOperator (ec); + EmitOperator (ec, left.Type); + + if (wrap_ctor != null) + ig.Emit (OpCodes.Newobj, wrap_ctor); ig.Emit (OpCodes.Br_S, end_label); ig.MarkLabel (is_null_label); - LiftedNull.Create (type, loc).Emit (ec); + + if ((Oper & Operator.ComparisonMask) != 0) { + // + // Emit true when equality operator both operands are same + // or inequality operator operands are not + // + if ((Oper == Operator.Equality && left_unwrap == right_unwrap) || + (Oper == Operator.Inequality && left_unwrap != right_unwrap)) + ig.Emit (OpCodes.Ldc_I4_1); + else + ig.Emit (OpCodes.Ldc_I4_0); + } else { + LiftedNull.Create (type, loc).Emit (ec); + } ig.MarkLabel (end_label); } + protected override void EmitOperator (EmitContext ec, Type l) + { + if (user_operator != null) { + user_operator.Emit (ec); + return; + } + + if (TypeManager.IsNullableType (l)) + l = TypeManager.GetTypeArguments (l) [0]; + + base.EmitOperator (ec, l); + } + bool IsBitwiseBoolean { get { return (Oper & Operator.BitwiseMask) != 0 && left_unwrap != null && right_unwrap != null && @@ -785,68 +849,74 @@ namespace Mono.CSharp.Nullable // // Avoid double conversion // - if (left_unwrap == null || left_null_lifted || !TypeManager.IsEqual (left_unwrap.Type, left.Type)) { + 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; - left = EmptyCast.Create (left, lifted_type.Type); + 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)) { + 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; - right = EmptyCast.Create (right, lifted_type.Type); + if (right is UserCast || right is TypeCast) + right.Type = lifted_type.Type; + else + right = EmptyCast.Create (right, lifted_type.Type); } - // TODO: Handle bitwise bool - if ((Oper & Operator.ComparisonMask) == 0 && !IsBitwiseBoolean) { + 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; - res_expr = new LiftedWrap (res_expr, lifted_type.Type).Resolve (ec); + 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 ((Oper & Operator.ArithmeticMask) != 0) - return LiftedNull.CreateFromExpression (res_expr); } 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); - - if ((Oper & Operator.ArithmeticMask) != 0) - return LiftedNull.CreateFromExpression (res_expr); } return res_expr; } - protected override Expression ResolveOperatorPredefined (EmitContext ec, Binary.PredefinedOperator [] operators, bool primitives_only) + protected override Expression ResolveOperatorPredefined (EmitContext ec, Binary.PredefinedOperator [] operators, bool primitives_only, Type enum_type) { - Expression e = base.ResolveOperatorPredefined (ec, operators, primitives_only); + Expression e = base.ResolveOperatorPredefined (ec, operators, primitives_only, enum_type); - if (e == this) + if (e == this || enum_type != null) return LiftResult (ec, e); // @@ -870,13 +940,13 @@ namespace Mono.CSharp.Nullable if (expr == null) return null; - // - // When lifting null literal and user operator exists, no call is made - // - if (left_null_lifted || right_null_lifted) - expr = ReducedExpression.Create (this, expr).Resolve (ec); + expr = LiftResult (ec, expr); + if (expr is Constant) + return expr; - return LiftResult (ec, expr); + type = expr.Type; + user_operator = expr; + return this; } } @@ -894,6 +964,9 @@ namespace Mono.CSharp.Nullable 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) { @@ -912,69 +985,100 @@ namespace Mono.CSharp.Nullable args.Add (new Argument (conversion)); return CreateExpressionFactoryCall ("Coalesce", args); - } + } - public override Expression DoResolve (EmitContext ec) + Expression ConvertExpression (EmitContext ec) { - if (type != null) - return this; - - left = left.Resolve (ec); - right = right.Resolve (ec); - - if (left == null || right == null) + // TODO: ImplicitConversionExists should take care of this + if (left.eclass == ExprClass.MethodGroup) return null; - eclass = ExprClass.Value; - Type ltype = left.Type, rtype = right.Type; - Expression expr; + 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)) { - 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) { + if (Convert.ImplicitConversionExists (ec, right, unwrap.Type)) { left = unwrap; - right = expr; - type = expr.Type; + 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); + + // + // 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 if (!TypeManager.IsReferenceType (ltype)) { - Binary.Error_OperatorCannotBeApplied (loc, "??", ltype, rtype); + } else { return null; } - expr = Convert.ImplicitConversion (ec, right, ltype, loc); - if (expr != null) { - type = expr.Type; - right = expr; - return this; - } + Type rtype = right.Type; + if (!Convert.ImplicitConversionExists (ec, unwrap != null ? unwrap : left, rtype) || right.eclass == ExprClass.MethodGroup) + return null; + + // + // Reduce (null ?? right) to right + // + if (left.IsNull) + return ReducedExpression.Create (right, this).Resolve (ec); + + left = Convert.ImplicitConversion (ec, unwrap != null ? unwrap : left, rtype, loc); + type = rtype; + return this; + } - Expression left_null = unwrap != null ? unwrap : left; - expr = Convert.ImplicitConversion (ec, left_null, rtype, loc); - if (expr != null) { - left = expr; - type = rtype; + public override Expression DoResolve (EmitContext ec) + { + if (eclass != ExprClass.Invalid) return this; + + left = left.Resolve (ec); + right = right.Resolve (ec); + + if (left == null || right == null) + return null; + + eclass = ExprClass.Value; + + Expression e = ConvertExpression (ec); + if (e == null) { + Binary.Error_OperatorCannotBeApplied (left, right, "??", loc); + return null; } - Binary.Error_OperatorCannotBeApplied (loc, "??", ltype, rtype); - return null; + return e; } public override void Emit (EmitContext ec) { ILGenerator ig = ec.ig; - Label is_null_label = ig.DefineLabel (); Label end_label = ig.DefineLabel (); if (unwrap != null) { + Label is_null_label = ig.DefineLabel (); + unwrap.EmitCheck (ec); ig.Emit (OpCodes.Brfalse, is_null_label); @@ -985,19 +1089,27 @@ namespace Mono.CSharp.Nullable right.Emit (ec); ig.MarkLabel (end_label); - } else { - left.Emit (ec); - ig.Emit (OpCodes.Dup); - ig.Emit (OpCodes.Brtrue, end_label); + return; + } - ig.MarkLabel (is_null_label); + left.Emit (ec); - ig.Emit (OpCodes.Pop); - right.Emit (ec); + ig.Emit (OpCodes.Dup); + ig.Emit (OpCodes.Brtrue, end_label); - ig.MarkLabel (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; @@ -1010,7 +1122,7 @@ namespace Mono.CSharp.Nullable public class LiftedUnaryMutator : ExpressionStatement { public readonly UnaryMutator.Mode Mode; - Expression expr, null_value; + Expression expr; UnaryMutator underlying; Unwrap unwrap; @@ -1023,6 +1135,11 @@ namespace Mono.CSharp.Nullable eclass = ExprClass.Value; } + public override Expression CreateExpressionTree (EmitContext ec) + { + return new SimpleAssign (this, this).CreateExpressionTree (ec); + } + public override Expression DoResolve (EmitContext ec) { expr = expr.Resolve (ec); @@ -1033,12 +1150,11 @@ namespace Mono.CSharp.Nullable if (unwrap == null) return null; - underlying = (UnaryMutator) new UnaryMutator (Mode, unwrap, loc).Resolve (ec); + underlying = (UnaryMutator) new UnaryMutator (Mode, unwrap).Resolve (ec); if (underlying == null) return null; type = expr.Type; - null_value = LiftedNull.Create (type, loc); return this; } @@ -1051,15 +1167,16 @@ namespace Mono.CSharp.Nullable unwrap.EmitCheck (ec); ig.Emit (OpCodes.Brfalse, is_null_label); - if (is_expr) + if (is_expr) { underlying.Emit (ec); - else + ig.Emit (OpCodes.Br_S, end_label); + } else { underlying.EmitStatement (ec); - ig.Emit (OpCodes.Br, end_label); + } ig.MarkLabel (is_null_label); if (is_expr) - null_value.Emit (ec); + LiftedNull.Create (type, loc).Emit (ec); ig.MarkLabel (end_label); }