X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fmcs%2Fnullable.cs;h=75048365c4ed5d25514508dafba7bcdd1fafac50;hb=9d8d49b74632949892b569446eecdba4a420f071;hp=a4b50362bdca8a0b7ff135c59407f9d63ec50821;hpb=a1745900a819ec6dce9b04597f0f446f53a0510e;p=mono.git diff --git a/mcs/mcs/nullable.cs b/mcs/mcs/nullable.cs index a4b50362bdc..75048365c4e 100644 --- a/mcs/mcs/nullable.cs +++ b/mcs/mcs/nullable.cs @@ -9,40 +9,42 @@ // // Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com) // Copyright 2004-2008 Novell, Inc +// Copyright 2011 Xamarin Inc // using System; -using System.Reflection; + +#if STATIC +using IKVM.Reflection.Emit; +#else using System.Reflection.Emit; +#endif namespace Mono.CSharp.Nullable { public class NullableType : TypeExpr { - TypeExpr underlying; + readonly TypeSpec underlying; - public NullableType (TypeExpr underlying, Location l) + public NullableType (TypeSpec type, Location loc) { - this.underlying = underlying; - loc = l; - - eclass = ExprClass.Type; + this.underlying = type; + this.loc = loc; } - public NullableType (TypeSpec type, Location loc) - : this (new TypeExpression (type, loc), loc) - { } - - protected override TypeExpr DoResolveAsTypeStep (IMemberContext ec) + public override TypeSpec ResolveAsType (IMemberContext ec) { - if (TypeManager.generic_nullable_type == null) { - TypeManager.generic_nullable_type = TypeManager.CoreLookupType (ec.Compiler, - "System", "Nullable", 1, MemberKind.Struct, true); - } + eclass = ExprClass.Type; + + var otype = ec.Module.PredefinedTypes.Nullable.Resolve (); + if (otype == null) + return null; - TypeArguments args = new TypeArguments (underlying); - GenericTypeExpr ctype = new GenericTypeExpr (TypeManager.generic_nullable_type, args, loc); - return ctype.ResolveAsTypeTerminal (ec, false); + TypeArguments args = new TypeArguments (new TypeExpression (underlying, loc)); + GenericTypeExpr ctype = new GenericTypeExpr (otype, args, loc); + + type = ctype.ResolveAsType (ec); + return type; } } @@ -50,7 +52,8 @@ namespace Mono.CSharp.Nullable { public static MethodSpec GetConstructor (TypeSpec nullableType) { - return TypeManager.GetPredefinedConstructor (nullableType, Location.Null, GetUnderlyingType (nullableType)); + return (MethodSpec) MemberCache.FindMember (nullableType, + MemberFilter.Constructor (ParametersCompiled.CreateFullyResolved (GetUnderlyingType (nullableType))), BindingRestriction.DeclaredOnly); } public static MethodSpec GetHasValue (TypeSpec nullableType) @@ -75,14 +78,9 @@ namespace Mono.CSharp.Nullable { return ((InflatedTypeSpec) nullableType).TypeArguments[0]; } - - public static bool IsNullableType (TypeSpec type) - { - throw new NotImplementedException ("net"); - } } - public class Unwrap : Expression, IMemoryLocation, IAssignMethod + public class Unwrap : Expression, IMemoryLocation { Expression expr; @@ -99,6 +97,11 @@ namespace Mono.CSharp.Nullable eclass = expr.eclass; } + public override bool ContainsEmitWithAwait () + { + return expr.ContainsEmitWithAwait (); + } + public static Expression Create (Expression expr) { // @@ -135,16 +138,24 @@ namespace Mono.CSharp.Nullable public override void Emit (EmitContext ec) { Store (ec); + + var call = new CallEmitter (); + call.InstanceExpression = this; + if (useDefaultValue) - Invocation.EmitCall (ec, this, NullableInfo.GetGetValueOrDefault (expr.Type), null, loc); + call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null); else - Invocation.EmitCall (ec, this, NullableInfo.GetValue (expr.Type), null, loc); + call.EmitPredefined (ec, NullableInfo.GetValue (expr.Type), null); } public void EmitCheck (EmitContext ec) { Store (ec); - Invocation.EmitCall (ec, this, NullableInfo.GetHasValue (expr.Type), null, loc); + + var call = new CallEmitter (); + call.InstanceExpression = this; + + call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null); } public override bool Equals (object obj) @@ -172,10 +183,10 @@ namespace Mono.CSharp.Nullable void Store (EmitContext ec) { - if (expr is VariableReference) + if (temp != null) return; - if (temp != null) + if (expr is VariableReference) return; expr.Emit (ec); @@ -214,51 +225,6 @@ namespace Mono.CSharp.Nullable return temp; } } - - public void Emit (EmitContext ec, bool leave_copy) - { - if (leave_copy) - Load (ec); - - Emit (ec); - } - - public void EmitAssign (EmitContext ec, Expression source, - bool leave_copy, bool prepare_for_load) - { - InternalWrap wrap = new InternalWrap (source, expr.Type, loc); - ((IAssignMethod) expr).EmitAssign (ec, wrap, leave_copy, false); - } - - class InternalWrap : Expression - { - public Expression expr; - - public InternalWrap (Expression expr, TypeSpec type, Location loc) - { - this.expr = expr; - this.loc = loc; - this.type = type; - - eclass = ExprClass.Value; - } - - public override Expression CreateExpressionTree (ResolveContext ec) - { - throw new NotSupportedException ("ET"); - } - - protected override Expression DoResolve (ResolveContext ec) - { - return this; - } - - public override void Emit (EmitContext ec) - { - expr.Emit (ec); - ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type)); - } - } } // @@ -283,7 +249,9 @@ namespace Mono.CSharp.Nullable public override void Emit (EmitContext ec) { - Invocation.EmitCall (ec, Child, NullableInfo.GetValue (Child.Type), null, loc); + var call = new CallEmitter (); + call.InstanceExpression = Child; + call.EmitPredefined (ec, NullableInfo.GetValue (Child.Type), null); } } @@ -357,6 +325,7 @@ namespace Mono.CSharp.Nullable value_target.AddressOf (ec, AddressOp.Store); ec.Emit (OpCodes.Initobj, type); value_target.Emit (ec); + value_target.Release (ec); } public void AddressOf (EmitContext ec, AddressOp Mode) @@ -389,6 +358,11 @@ namespace Mono.CSharp.Nullable : this (expr, unwrap as Unwrap, type) { } + + public override bool ContainsEmitWithAwait () + { + return unwrap.ContainsEmitWithAwait (); + } public override Expression CreateExpressionTree (ResolveContext ec) { @@ -402,7 +376,7 @@ namespace Mono.CSharp.Nullable // if (unwrap == null) { // S -> T? is wrap only - if (TypeManager.IsNullableType (type)) + if (type.IsNullableType) return Wrap.Create (expr, type); // S -> T can be simplified @@ -410,13 +384,13 @@ namespace Mono.CSharp.Nullable } // Wrap target for T? - if (TypeManager.IsNullableType (type)) { + if (type.IsNullableType) { expr = Wrap.Create (expr, type); if (expr == null) return null; null_value = LiftedNull.Create (type, loc); - } else if (TypeManager.IsValueType (type)) { + } else if (TypeSpec.IsValueType (type)) { null_value = LiftedNull.Create (type, loc); } else { null_value = new NullConstant (type, loc); @@ -522,18 +496,17 @@ namespace Mono.CSharp.Nullable 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) + var lifted_type = new NullableType (expr.Type, expr.Location); + if (lifted_type.ResolveAsType (ec) == null) return null; expr.Type = lifted_type.Type; return expr; } - protected override Expression ResolveEnumOperator (ResolveContext ec, Expression expr) + protected override Expression ResolveEnumOperator (ResolveContext ec, Expression expr, TypeSpec[] predefined) { - expr = base.ResolveEnumOperator (ec, expr); + expr = base.ResolveEnumOperator (ec, expr, predefined); if (expr == null) return null; @@ -574,8 +547,8 @@ namespace Mono.CSharp.Nullable bool IsBitwiseBoolean { get { return (Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) && - ((left_unwrap != null && left_unwrap.Type == TypeManager.bool_type) || - (right_unwrap != null && right_unwrap.Type == TypeManager.bool_type)); + ((left_unwrap != null && left_unwrap.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) || + (right_unwrap != null && right_unwrap.Type.BuiltinType == BuiltinTypeSpec.Type.Bool)); } } @@ -607,14 +580,14 @@ namespace Mono.CSharp.Nullable Constant CreateNullConstant (ResolveContext ec, Expression expr) { // FIXME: Handle side effect constants - Constant c = new BoolConstant (Oper == Operator.Inequality, loc).Resolve (ec); + Constant c = new BoolConstant (ec.BuiltinTypes, 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 ()); + TypeManager.CSharpName (expr.Type), c.GetValueAsLiteral ()); } else { ec.Report.Warning (464, 2, loc, "The result of comparing type `{0}' with null is always `{1}'", - TypeManager.CSharpName (expr.Type), c.AsString ()); + TypeManager.CSharpName (expr.Type), c.GetValueAsLiteral ()); } return ReducedExpression.Create (c, this); @@ -629,14 +602,14 @@ namespace Mono.CSharp.Nullable bool use_default_call = (Oper & (Operator.BitwiseMask | Operator.EqualityMask)) != 0; left_orig = left; - if (TypeManager.IsNullableType (left.Type)) { + if (left.Type.IsNullableType) { left = left_unwrap = Unwrap.Create (left, use_default_call); if (left == null) return null; } right_orig = right; - if (TypeManager.IsNullableType (right.Type)) { + if (right.Type.IsNullableType) { right = right_unwrap = Unwrap.Create (right, use_default_call); if (right == null) return null; @@ -647,16 +620,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_orig.IsNull) { + if (left_orig is NullLiteral) { left = right; state |= State.LeftNullLifted; - type = TypeManager.bool_type; + type = ec.BuiltinTypes.Bool; } if (right_orig.IsNull) { - right = left; + if ((Oper & Operator.ShiftMask) != 0) + right = new EmptyExpression (ec.BuiltinTypes.Int); + else + right = left; + state |= State.RightNullLifted; - type = TypeManager.bool_type; + type = ec.BuiltinTypes.Bool; } eclass = ExprClass.Value; @@ -677,7 +654,7 @@ namespace Mono.CSharp.Nullable } left_unwrap.Emit (ec); - ec.Emit (OpCodes.Brtrue_S, load_right); + ec.Emit (OpCodes.Brtrue, load_right); // value & null, value | null if (right_unwrap != null) { @@ -733,7 +710,7 @@ namespace Mono.CSharp.Nullable if (left_unwrap != null && (IsRightNullLifted || right.IsNull)) { left_unwrap.EmitCheck (ec); if (Oper == Binary.Operator.Equality) { - ec.Emit (OpCodes.Ldc_I4_0); + ec.EmitInt (0); ec.Emit (OpCodes.Ceq); } return; @@ -742,7 +719,7 @@ namespace Mono.CSharp.Nullable if (right_unwrap != null && (IsLeftNullLifted || left.IsNull)) { right_unwrap.EmitCheck (ec); if (Oper == Binary.Operator.Equality) { - ec.Emit (OpCodes.Ldc_I4_0); + ec.EmitInt (0); ec.Emit (OpCodes.Ceq); } return; @@ -755,6 +732,11 @@ namespace Mono.CSharp.Nullable user_operator.Emit (ec); ec.Emit (Oper == Operator.Equality ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, dissimilar_label); } else { + if (ec.HasSet (BuilderContext.Options.AsyncBody) && right.ContainsEmitWithAwait ()) { + left = left.EmitToField (ec); + right = right.EmitToField (ec); + } + left.Emit (ec); right.Emit (ec); @@ -774,7 +756,7 @@ namespace Mono.CSharp.Nullable ec.Emit (OpCodes.Ceq); } else { if (Oper == Operator.Inequality) { - ec.Emit (OpCodes.Ldc_I4_0); + ec.EmitInt (0); ec.Emit (OpCodes.Ceq); } } @@ -783,9 +765,9 @@ namespace Mono.CSharp.Nullable ec.MarkLabel (dissimilar_label); if (Oper == Operator.Inequality) - ec.Emit (OpCodes.Ldc_I4_1); + ec.EmitInt (1); else - ec.Emit (OpCodes.Ldc_I4_0); + ec.EmitInt (0); ec.MarkLabel (end_label); } @@ -839,7 +821,7 @@ namespace Mono.CSharp.Nullable ec.MarkLabel (is_null_label); if ((Oper & Operator.ComparisonMask) != 0) { - ec.Emit (OpCodes.Ldc_I4_0); + ec.EmitInt (0); } else { LiftedNull.Create (type, loc).Emit (ec); } @@ -854,34 +836,38 @@ namespace Mono.CSharp.Nullable return; } - if (TypeManager.IsNullableType (l)) - l = TypeManager.GetTypeArguments (l) [0]; + if (left.Type.IsNullableType) { + l = NullableInfo.GetUnderlyingType (left.Type); + left = EmptyCast.Create (left, l); + } + + if (right.Type.IsNullableType) { + right = EmptyCast.Create (right, NullableInfo.GetUnderlyingType (right.Type)); + } base.EmitOperator (ec, l); } Expression LiftResult (ResolveContext ec, Expression res_expr) { - TypeExpr lifted_type; + TypeSpec lifted_type; // // Avoid double conversion // if (left_unwrap == null || IsLeftNullLifted || left_unwrap.Type != left.Type || (left_unwrap != null && IsRightNullLifted)) { - lifted_type = new NullableType (left.Type, loc); - lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false); + lifted_type = new NullableType (left.Type, loc).ResolveAsType (ec); if (lifted_type == null) return null; if (left is UserCast || left is TypeCast) - left.Type = lifted_type.Type; + left.Type = lifted_type; else - left = EmptyCast.Create (left, lifted_type.Type); + left = EmptyCast.Create (left, lifted_type); } if (left != right && (right_unwrap == null || IsRightNullLifted || right_unwrap.Type != right.Type || (right_unwrap != null && IsLeftNullLifted))) { - lifted_type = new NullableType (right.Type, loc); - lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false); + lifted_type = new NullableType (right.Type, loc).ResolveAsType (ec); if (lifted_type == null) return null; @@ -890,19 +876,18 @@ namespace Mono.CSharp.Nullable r = ((ReducedExpression) r).OriginalExpression; if (r is UserCast || r is TypeCast) - r.Type = lifted_type.Type; + r.Type = lifted_type; else - right = EmptyCast.Create (right, lifted_type.Type); + right = EmptyCast.Create (right, lifted_type); } if ((Oper & Operator.ComparisonMask) == 0) { - lifted_type = new NullableType (res_expr.Type, loc); - lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false); + lifted_type = new NullableType (res_expr.Type, loc).ResolveAsType (ec); if (lifted_type == null) return null; - wrap_ctor = NullableInfo.GetConstructor (lifted_type.Type); - type = res_expr.Type = lifted_type.Type; + wrap_ctor = NullableInfo.GetConstructor (lifted_type); + type = res_expr.Type = lifted_type; } if (IsLeftNullLifted) { @@ -911,7 +896,7 @@ namespace Mono.CSharp.Nullable // // Special case for bool?, the result depends on both null right side and left side value // - if ((Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) && NullableInfo.GetUnderlyingType (type) == TypeManager.bool_type) { + if ((Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) && NullableInfo.GetUnderlyingType (type).BuiltinType == BuiltinTypeSpec.Type.Bool) { return res_expr; } @@ -922,7 +907,7 @@ namespace Mono.CSharp.Nullable // Value types and null comparison // if (right_unwrap == null || (Oper & Operator.RelationalMask) != 0) - return CreateNullConstant (ec, right_orig).Resolve (ec); + return CreateNullConstant (ec, right_orig); } if (IsRightNullLifted) { @@ -931,7 +916,7 @@ namespace Mono.CSharp.Nullable // // Special case for bool?, the result depends on both null right side and left side value // - if ((Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) && NullableInfo.GetUnderlyingType (type) == TypeManager.bool_type) { + if ((Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) && NullableInfo.GetUnderlyingType (type).BuiltinType == BuiltinTypeSpec.Type.Bool) { return res_expr; } @@ -995,7 +980,7 @@ namespace Mono.CSharp.Nullable // Lift the result in the case it can be null and predefined or user operator // result type is of a value type // - if (!TypeManager.IsValueType (expr.Type)) + if (!TypeSpec.IsValueType (expr.Type)) return null; if (state != orig_state) @@ -1022,6 +1007,18 @@ namespace Mono.CSharp.Nullable this.right = right; this.loc = loc; } + + public Expression LeftExpression { + get { + return left; + } + } + + public Expression RightExpression { + get { + return right; + } + } public override Expression CreateExpressionTree (ResolveContext ec) { @@ -1060,23 +1057,42 @@ namespace Mono.CSharp.Nullable // 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)) { + if (ltype.IsNullableType) { unwrap = Unwrap.Create (left, false); if (unwrap == null) return null; + // + // Reduce (left ?? null) to left + // + if (right.IsNull) + return ReducedExpression.Create (left, this); + if (Convert.ImplicitConversionExists (ec, right, unwrap.Type)) { left = unwrap; - type = left.Type; - right = Convert.ImplicitConversion (ec, right, type, loc); + ltype = left.Type; + + // + // If right is a dynamic expression, the result type is dynamic + // + if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { + type = right.Type; + + // Need to box underlying value type + left = Convert.ImplicitBoxingConversion (left, ltype, type); + return this; + } + + right = Convert.ImplicitConversion (ec, right, ltype, loc); + type = ltype; return this; } - } else if (TypeManager.IsReferenceType (ltype)) { + } else if (TypeSpec.IsReferenceType (ltype)) { if (Convert.ImplicitConversionExists (ec, right, ltype)) { // // If right is a dynamic expression, the result type is dynamic // - if (right.Type == InternalType.Dynamic) { + if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { type = right.Type; return this; } @@ -1086,7 +1102,7 @@ namespace Mono.CSharp.Nullable // Constant lc = left as Constant; if (lc != null && !lc.IsDefaultValue) - return ReducedExpression.Create (lc, this).Resolve (ec); + return ReducedExpression.Create (lc, this); // // Reduce (left ?? null) to left OR (null-constant ?? right) to right @@ -1095,7 +1111,15 @@ namespace Mono.CSharp.Nullable return ReducedExpression.Create (lc != null ? right : left, this); right = Convert.ImplicitConversion (ec, right, ltype, loc); - type = left.Type; + type = ltype; + return this; + } + + // + // Special case null ?? null + // + if (ltype == right.Type) { + type = ltype; return this; } } else { @@ -1110,13 +1134,21 @@ namespace Mono.CSharp.Nullable // Reduce (null ?? right) to right // if (left.IsNull) - return ReducedExpression.Create (right, this); + return ReducedExpression.Create (right, this).Resolve (ec); left = Convert.ImplicitConversion (ec, unwrap != null ? unwrap : left, rtype, loc); type = rtype; return this; } + public override bool ContainsEmitWithAwait () + { + if (unwrap != null) + return unwrap.ContainsEmitWithAwait () || right.ContainsEmitWithAwait (); + + return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait (); + } + protected override Expression DoResolve (ResolveContext ec) { left = left.Resolve (ec); @@ -1178,77 +1210,65 @@ namespace Mono.CSharp.Nullable 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) + + public override object Accept (StructuralVisitor visitor) { - this.expr = expr; - this.Mode = mode; - this.loc = loc; + return visitor.Visit (this); } + } - public override Expression CreateExpressionTree (ResolveContext ec) + class LiftedUnaryMutator : UnaryMutator + { + public LiftedUnaryMutator (Mode mode, Expression expr, Location loc) + : base (mode, expr, loc) { - return new SimpleAssign (this, this).CreateExpressionTree (ec); } protected override Expression DoResolve (ResolveContext ec) { - expr = expr.Resolve (ec); - if (expr == null) - return null; - - unwrap = Unwrap.Create (expr, false); - if (unwrap == null) - return null; + var orig_expr = expr; - underlying = (UnaryMutator) new UnaryMutator (Mode, unwrap, loc).Resolve (ec); - if (underlying == null) - return null; + expr = Unwrap.Create (expr); + var res = base.DoResolveOperation (ec); - eclass = ExprClass.Value; + expr = orig_expr; type = expr.Type; - return this; + + return res; } - void DoEmit (EmitContext ec, bool is_expr) + protected override void EmitOperation (EmitContext ec) { Label is_null_label = ec.DefineLabel (); Label end_label = ec.DefineLabel (); - unwrap.EmitCheck (ec); + LocalTemporary lt = new LocalTemporary (type); + + // Value is on the stack + lt.Store (ec); + + var call = new CallEmitter (); + call.InstanceExpression = lt; + call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null); + ec.Emit (OpCodes.Brfalse, is_null_label); - if (is_expr) { - underlying.Emit (ec); - ec.Emit (OpCodes.Br_S, end_label); - } else { - underlying.EmitStatement (ec); - } + call = new CallEmitter (); + call.InstanceExpression = lt; + call.EmitPredefined (ec, NullableInfo.GetValue (expr.Type), null); - ec.MarkLabel (is_null_label); - if (is_expr) - LiftedNull.Create (type, loc).Emit (ec); + lt.Release (ec); - ec.MarkLabel (end_label); - } + base.EmitOperation (ec); - public override void Emit (EmitContext ec) - { - DoEmit (ec, true); - } + ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type)); + ec.Emit (OpCodes.Br_S, end_label); - public override void EmitStatement (EmitContext ec) - { - DoEmit (ec, false); + ec.MarkLabel (is_null_label); + LiftedNull.Create (type, loc).Emit (ec); + + ec.MarkLabel (end_label); } } }