X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fmcs%2Fnullable.cs;h=754c888e8b1158711b9f4200666e7a22e6ff273f;hb=20ab5bfe7ae2cf84cd9e6fdad9758ded9f687ca2;hp=210f5cb8ca6b9e7675e014ed079f6296bafd12e3;hpb=86c5ca89ac8f19726f46cac77c4f2f22c4078eaa;p=mono.git diff --git a/mcs/mcs/nullable.cs b/mcs/mcs/nullable.cs index 210f5cb8ca6..754c888e8b1 100644 --- a/mcs/mcs/nullable.cs +++ b/mcs/mcs/nullable.cs @@ -695,6 +695,20 @@ namespace Mono.CSharp.Nullable Right = Unwrap.CreateUnwrapped (Right); UnwrapRight = Right as Unwrap; } + + if (Left.Type.BuiltinType == BuiltinTypeSpec.Type.Decimal) { + var decimal_operators = MemberCache.GetUserOperator (Left.Type, Binary.ConvertBinaryToUserOperator (Binary.Oper), false); + + Arguments args = new Arguments (2); + args.Add (new Argument (Left)); + args.Add (new Argument (Right)); + + const OverloadResolver.Restrictions restr = OverloadResolver.Restrictions.ProbingOnly | + OverloadResolver.Restrictions.NoBaseMembers | OverloadResolver.Restrictions.BaseMembersIncluded; + + var res = new OverloadResolver (decimal_operators, restr, loc); + UserOperator = res.ResolveOperator (rc, ref args); + } } type = Binary.Type; @@ -795,7 +809,7 @@ namespace Mono.CSharp.Nullable // // Both operands are bool? types // - if (UnwrapLeft != null && UnwrapRight != null) { + if ((UnwrapLeft != null && !Left.IsNull) && (UnwrapRight != null && !Right.IsNull)) { if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) { Left = Left.EmitToField (ec); Right = Right.EmitToField (ec); @@ -861,6 +875,8 @@ namespace Mono.CSharp.Nullable LiftedNull.Create (type, loc).Emit (ec); } else { Left.Emit (ec); + UnwrapRight.Store (ec); + ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right); ec.EmitInt (or ? 1 : 0); @@ -869,7 +885,7 @@ namespace Mono.CSharp.Nullable ec.Emit (OpCodes.Br_S, end_label); ec.MarkLabel (load_right); - UnwrapRight.Original.Emit (ec); + UnwrapRight.Load (ec); } } else { // @@ -895,16 +911,28 @@ namespace Mono.CSharp.Nullable ec.MarkLabel (is_null_label); LiftedNull.Create (type, loc).Emit (ec); + } else if (Left.IsNull && UnwrapRight != null) { + UnwrapRight.Emit (ec); + + ec.Emit (or ? OpCodes.Brtrue_S : OpCodes.Brfalse_S, load_right); + + LiftedNull.Create (type, loc).Emit (ec); + + ec.Emit (OpCodes.Br_S, end_label); + + ec.MarkLabel (load_right); + + UnwrapRight.Load (ec); } else { Right.Emit (ec); - ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right); + ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_left); ec.EmitInt (or ? 1 : 0); ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type)); ec.Emit (OpCodes.Br_S, end_label); - ec.MarkLabel (load_right); + ec.MarkLabel (load_left); UnwrapLeft.Load (ec); } @@ -1073,6 +1101,7 @@ namespace Mono.CSharp.Nullable { Expression left, right; Unwrap unwrap; + bool user_conversion_left; public NullCoalescingOperator (Expression left, Expression right) { @@ -1195,7 +1224,7 @@ namespace Mono.CSharp.Nullable // // Special case null ?? null // - if (right.IsNull && ltype == right.Type) + if (right is NullLiteral && ltype == right.Type) return null; return ReducedExpression.Create (lc != null ? right : left, this, false); @@ -1220,6 +1249,7 @@ namespace Mono.CSharp.Nullable return ReducedExpression.Create (right, this, false).Resolve (ec); left = Convert.ImplicitConversion (ec, unwrap ?? left, rtype, loc); + user_conversion_left = left is UserCast; type = rtype; return this; } @@ -1283,10 +1313,14 @@ namespace Mono.CSharp.Nullable // Null check is done on original expression not after expression is converted to // result type. This is in most cases same but when user conversion is involved // we can end up in situation when user operator does the null handling which is - // not what the operator is supposed to do + // not what the operator is supposed to do. + // There is tricky case where cast of left expression is meant to be cast of + // whole source expression (null check is done on it) and cast from right-to-left + // conversion needs to do null check on unconverted source expression. // - var op_expr = left as UserCast; - if (op_expr != null) { + if (user_conversion_left) { + var op_expr = (UserCast) left; + op_expr.Source.Emit (ec); LocalTemporary temp;