2 // nullable.cs: Nullable types support
4 // Authors: Martin Baulig (martin@ximian.com)
5 // Miguel de Icaza (miguel@ximian.com)
6 // Marek Safar (marek.safar@gmail.com)
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
10 // Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
11 // Copyright 2004-2008 Novell, Inc
12 // Copyright 2011 Xamarin Inc
16 using SLE = System.Linq.Expressions;
19 using IKVM.Reflection.Emit;
21 using System.Reflection.Emit;
24 namespace Mono.CSharp.Nullable
26 public class NullableType : TypeExpr
28 readonly TypeSpec underlying;
30 public NullableType (TypeSpec type, Location loc)
32 this.underlying = type;
36 public override TypeSpec ResolveAsType (IMemberContext ec)
38 eclass = ExprClass.Type;
40 var otype = ec.Module.PredefinedTypes.Nullable.Resolve ();
44 TypeArguments args = new TypeArguments (new TypeExpression (underlying, loc));
45 GenericTypeExpr ctype = new GenericTypeExpr (otype, args, loc);
47 type = ctype.ResolveAsType (ec);
52 static class NullableInfo
54 public static MethodSpec GetConstructor (TypeSpec nullableType)
56 return (MethodSpec) MemberCache.FindMember (nullableType,
57 MemberFilter.Constructor (ParametersCompiled.CreateFullyResolved (GetUnderlyingType (nullableType))), BindingRestriction.DeclaredOnly);
60 public static MethodSpec GetHasValue (TypeSpec nullableType)
62 return (MethodSpec) MemberCache.FindMember (nullableType,
63 MemberFilter.Method ("get_HasValue", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None);
66 public static MethodSpec GetGetValueOrDefault (TypeSpec nullableType)
68 return (MethodSpec) MemberCache.FindMember (nullableType,
69 MemberFilter.Method ("GetValueOrDefault", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None);
73 // Don't use unless really required for correctness, see Unwrap::Emit
75 public static MethodSpec GetValue (TypeSpec nullableType)
77 return (MethodSpec) MemberCache.FindMember (nullableType,
78 MemberFilter.Method ("get_Value", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None);
81 public static TypeSpec GetUnderlyingType (TypeSpec nullableType)
83 return ((InflatedTypeSpec) nullableType).TypeArguments[0];
86 public static TypeSpec GetEnumUnderlyingType (ModuleContainer module, TypeSpec nullableEnum)
88 return module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (module,
89 new[] { EnumSpec.GetUnderlyingType (GetUnderlyingType (nullableEnum)) });
93 public class Unwrap : Expression, IMemoryLocation
98 Expression temp_field;
99 readonly bool useDefaultValue;
101 public Unwrap (Expression expr, bool useDefaultValue = true)
104 this.loc = expr.Location;
105 this.useDefaultValue = useDefaultValue;
107 type = NullableInfo.GetUnderlyingType (expr.Type);
108 eclass = expr.eclass;
111 public override bool ContainsEmitWithAwait ()
113 return expr.ContainsEmitWithAwait ();
117 public static Expression Create (Expression expr)
120 // Avoid unwraping and wraping of same type
122 Wrap wrap = expr as Wrap;
126 return Create (expr, false);
129 public static Expression CreateUnwrapped (Expression expr)
132 // Avoid unwraping and wraping of same type
134 Wrap wrap = expr as Wrap;
138 return Create (expr, true);
141 public static Unwrap Create (Expression expr, bool useDefaultValue)
143 return new Unwrap (expr, useDefaultValue);
146 public override Expression CreateExpressionTree (ResolveContext ec)
148 return expr.CreateExpressionTree (ec);
151 protected override Expression DoResolve (ResolveContext ec)
156 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
158 expr = expr.DoResolveLValue (ec, right_side);
162 public override void Emit (EmitContext ec)
166 var call = new CallEmitter ();
167 call.InstanceExpression = this;
170 // Using GetGetValueOrDefault is prefered because JIT can possibly
171 // inline it whereas Value property contains a throw which is very
172 // unlikely to be inlined
175 call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null);
177 call.EmitPredefined (ec, NullableInfo.GetValue (expr.Type), null);
180 public void EmitCheck (EmitContext ec)
184 var call = new CallEmitter ();
185 call.InstanceExpression = this;
187 call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null);
190 public override void EmitSideEffect (EmitContext ec)
192 expr.EmitSideEffect (ec);
195 public override Expression EmitToField (EmitContext ec)
197 if (temp_field == null)
198 temp_field = this.expr.EmitToField (ec);
203 public override bool Equals (object obj)
205 Unwrap uw = obj as Unwrap;
206 return uw != null && expr.Equals (uw.expr);
209 public override void FlowAnalysis (FlowAnalysisContext fc)
211 expr.FlowAnalysis (fc);
214 public Expression Original {
220 public override int GetHashCode ()
222 return expr.GetHashCode ();
225 public override bool IsNull {
231 public void Store (EmitContext ec)
233 if (temp != null || temp_field != null)
236 if (expr is VariableReference)
240 LocalVariable.Store (ec);
243 public void Load (EmitContext ec)
245 if (temp_field != null)
246 temp_field.Emit (ec);
247 else if (expr is VariableReference)
250 LocalVariable.Emit (ec);
253 public override SLE.Expression MakeExpression (BuilderContext ctx)
255 return expr.MakeExpression (ctx);
258 public void AddressOf (EmitContext ec, AddressOp mode)
262 if (temp_field != null) {
263 ml = temp_field as IMemoryLocation;
265 var lt = new LocalTemporary (temp_field.Type);
266 temp_field.Emit (ec);
271 ml = expr as VariableReference;
275 ml.AddressOf (ec, mode);
277 LocalVariable.AddressOf (ec, mode);
281 // Keeps result of non-variable expression
283 LocalTemporary LocalVariable {
285 if (temp == null && temp_field == null)
286 temp = new LocalTemporary (expr.Type);
293 // Calls get_Value method on nullable expression
295 public class UnwrapCall : CompositeExpression
297 public UnwrapCall (Expression expr)
302 protected override Expression DoResolve (ResolveContext rc)
307 type = NullableInfo.GetUnderlyingType (type);
312 public override void Emit (EmitContext ec)
314 var call = new CallEmitter ();
315 call.InstanceExpression = Child;
316 call.EmitPredefined (ec, NullableInfo.GetValue (Child.Type), null);
320 public class Wrap : TypeCast
322 private Wrap (Expression expr, TypeSpec type)
325 eclass = ExprClass.Value;
328 public override Expression CreateExpressionTree (ResolveContext ec)
330 TypeCast child_cast = child as TypeCast;
331 if (child_cast != null) {
333 return child_cast.CreateExpressionTree (ec);
336 var user_cast = child as UserCast;
337 if (user_cast != null) {
339 return user_cast.CreateExpressionTree (ec);
342 return base.CreateExpressionTree (ec);
345 public static Expression Create (Expression expr, TypeSpec type)
348 // Avoid unwraping and wraping of the same type
350 Unwrap unwrap = expr as Unwrap;
351 if (unwrap != null && expr.Type == NullableInfo.GetUnderlyingType (type))
352 return unwrap.Original;
354 return new Wrap (expr, type);
357 public override void Emit (EmitContext ec)
360 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
365 // Represents null literal lifted to nullable type
367 public class LiftedNull : NullConstant, IMemoryLocation
369 private LiftedNull (TypeSpec nullable_type, Location loc)
370 : base (nullable_type, loc)
372 eclass = ExprClass.Value;
375 public static Constant Create (TypeSpec nullable, Location loc)
377 return new LiftedNull (nullable, loc);
380 public static Constant CreateFromExpression (ResolveContext rc, Expression e)
382 if (!rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) {
383 rc.Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'",
384 e.Type.GetSignatureForError ());
387 return ReducedExpression.Create (Create (e.Type, e.Location), e);
390 public override void Emit (EmitContext ec)
392 // TODO: generate less temporary variables
393 LocalTemporary value_target = new LocalTemporary (type);
395 value_target.AddressOf (ec, AddressOp.Store);
396 ec.Emit (OpCodes.Initobj, type);
397 value_target.Emit (ec);
398 value_target.Release (ec);
401 public void AddressOf (EmitContext ec, AddressOp Mode)
403 LocalTemporary value_target = new LocalTemporary (type);
405 value_target.AddressOf (ec, AddressOp.Store);
406 ec.Emit (OpCodes.Initobj, type);
407 value_target.AddressOf (ec, Mode);
412 // Generic lifting expression, supports all S/S? -> T/T? cases
414 public class LiftedConversion : Expression, IMemoryLocation
416 Expression expr, null_value;
419 public LiftedConversion (Expression expr, Unwrap unwrap, TypeSpec type)
422 this.unwrap = unwrap;
423 this.loc = expr.Location;
427 public LiftedConversion (Expression expr, Expression unwrap, TypeSpec type)
428 : this (expr, unwrap as Unwrap, type)
432 public override bool ContainsEmitWithAwait ()
434 return unwrap.ContainsEmitWithAwait ();
437 public override Expression CreateExpressionTree (ResolveContext ec)
439 return expr.CreateExpressionTree (ec);
442 protected override Expression DoResolve (ResolveContext ec)
445 // It's null when lifting non-nullable type
447 if (unwrap == null) {
448 // S -> T? is wrap only
449 if (type.IsNullableType)
450 return Wrap.Create (expr, type);
452 // S -> T can be simplified
456 // Wrap target for T?
457 if (type.IsNullableType) {
458 if (!expr.Type.IsNullableType) {
459 expr = Wrap.Create (expr, type);
464 null_value = LiftedNull.Create (type, loc);
465 } else if (TypeSpec.IsValueType (type)) {
466 null_value = LiftedNull.Create (type, loc);
468 null_value = new NullConstant (type, loc);
471 eclass = ExprClass.Value;
475 public override void Emit (EmitContext ec)
477 Label is_null_label = ec.DefineLabel ();
478 Label end_label = ec.DefineLabel ();
480 unwrap.EmitCheck (ec);
481 ec.Emit (OpCodes.Brfalse, is_null_label);
485 ec.Emit (OpCodes.Br, end_label);
486 ec.MarkLabel (is_null_label);
488 null_value.Emit (ec);
490 ec.MarkLabel (end_label);
493 public override void FlowAnalysis (FlowAnalysisContext fc)
495 expr.FlowAnalysis (fc);
498 public void AddressOf (EmitContext ec, AddressOp mode)
500 unwrap.AddressOf (ec, mode);
504 public class LiftedUnaryOperator : Unary, IMemoryLocation
507 Expression user_operator;
509 public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc)
510 : base (op, expr, loc)
514 public void AddressOf (EmitContext ec, AddressOp mode)
516 unwrap.AddressOf (ec, mode);
519 public override Expression CreateExpressionTree (ResolveContext ec)
521 if (user_operator != null)
522 return user_operator.CreateExpressionTree (ec);
524 if (Oper == Operator.UnaryPlus)
525 return Expr.CreateExpressionTree (ec);
527 return base.CreateExpressionTree (ec);
530 protected override Expression DoResolve (ResolveContext ec)
532 unwrap = Unwrap.Create (Expr, false);
536 Expression res = base.ResolveOperator (ec, unwrap);
538 Error_OperatorCannotBeApplied (ec, loc, OperName (Oper), Expr.Type);
543 if (user_operator == null)
546 res = Expr = LiftExpression (ec, Expr);
552 eclass = ExprClass.Value;
557 public override void Emit (EmitContext ec)
559 Label is_null_label = ec.DefineLabel ();
560 Label end_label = ec.DefineLabel ();
562 unwrap.EmitCheck (ec);
563 ec.Emit (OpCodes.Brfalse, is_null_label);
565 if (user_operator != null) {
566 user_operator.Emit (ec);
568 EmitOperator (ec, NullableInfo.GetUnderlyingType (type));
571 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
572 ec.Emit (OpCodes.Br_S, end_label);
574 ec.MarkLabel (is_null_label);
575 LiftedNull.Create (type, loc).Emit (ec);
577 ec.MarkLabel (end_label);
580 static Expression LiftExpression (ResolveContext ec, Expression expr)
582 var lifted_type = new NullableType (expr.Type, expr.Location);
583 if (lifted_type.ResolveAsType (ec) == null)
586 expr.Type = lifted_type.Type;
590 protected override Expression ResolveEnumOperator (ResolveContext ec, Expression expr, TypeSpec[] predefined)
592 expr = base.ResolveEnumOperator (ec, expr, predefined);
596 Expr = LiftExpression (ec, Expr);
597 return LiftExpression (ec, expr);
600 protected override Expression ResolveUserOperator (ResolveContext ec, Expression expr)
602 expr = base.ResolveUserOperator (ec, expr);
607 // When a user operator is of non-nullable type
609 if (Expr is Unwrap) {
610 user_operator = LiftExpression (ec, expr);
611 return user_operator;
619 // Lifted version of binary operators
621 class LiftedBinaryOperator : Expression
623 public LiftedBinaryOperator (Binary b)
626 this.loc = b.Location;
629 public Binary Binary { get; private set; }
631 public Expression Left { get; set; }
633 public Expression Right { get; set; }
635 public Unwrap UnwrapLeft { get; set; }
637 public Unwrap UnwrapRight { get; set; }
639 public MethodSpec UserOperator { get; set; }
641 bool IsBitwiseBoolean {
643 return (Binary.Oper == Binary.Operator.BitwiseAnd || Binary.Oper == Binary.Operator.BitwiseOr) &&
644 ((UnwrapLeft != null && UnwrapLeft.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) ||
645 (UnwrapRight != null && UnwrapRight.Type.BuiltinType == BuiltinTypeSpec.Type.Bool));
649 public override bool ContainsEmitWithAwait ()
651 return Left.ContainsEmitWithAwait () || Right.ContainsEmitWithAwait ();
654 public override Expression CreateExpressionTree (ResolveContext rc)
656 if (UserOperator != null) {
657 Arguments args = new Arguments (2);
658 args.Add (new Argument (Binary.Left));
659 args.Add (new Argument (Binary.Right));
661 var method = new UserOperatorCall (UserOperator, args, Binary.CreateExpressionTree, loc);
662 return method.CreateExpressionTree (rc);
665 return Binary.CreateExpressionTree (rc);
668 protected override Expression DoResolve (ResolveContext rc)
670 if (rc.IsRuntimeBinder) {
671 if (UnwrapLeft == null && !Left.Type.IsNullableType)
672 Left = LiftOperand (rc, Left);
674 if (UnwrapRight == null && !Right.Type.IsNullableType)
675 Right = LiftOperand (rc, Right);
677 if (UnwrapLeft == null && Left != null && Left.Type.IsNullableType) {
678 Left = Unwrap.CreateUnwrapped (Left);
679 UnwrapLeft = Left as Unwrap;
682 if (UnwrapRight == null && Right != null && Right.Type.IsNullableType) {
683 Right = Unwrap.CreateUnwrapped (Right);
684 UnwrapRight = Right as Unwrap;
689 eclass = Binary.eclass;
694 Expression LiftOperand (ResolveContext rc, Expression expr)
698 type = Left.IsNull ? Right.Type : Left.Type;
703 if (!type.IsNullableType)
704 type = rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { type });
706 return Wrap.Create (expr, type);
709 public override void Emit (EmitContext ec)
711 if (IsBitwiseBoolean && UserOperator == null) {
712 EmitBitwiseBoolean (ec);
716 if ((Binary.Oper & Binary.Operator.EqualityMask) != 0) {
721 Label is_null_label = ec.DefineLabel ();
722 Label end_label = ec.DefineLabel ();
724 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Right.ContainsEmitWithAwait ()) {
725 Left = Left.EmitToField (ec);
726 Right = Right.EmitToField (ec);
729 if (UnwrapLeft != null) {
730 UnwrapLeft.EmitCheck (ec);
734 // Don't emit HasValue check when left and right expressions are same
736 if (UnwrapRight != null && !Binary.Left.Equals (Binary.Right)) {
737 UnwrapRight.EmitCheck (ec);
738 if (UnwrapLeft != null) {
739 ec.Emit (OpCodes.And);
743 ec.Emit (OpCodes.Brfalse, is_null_label);
745 if (UserOperator != null) {
746 var args = new Arguments (2);
747 args.Add (new Argument (Left));
748 args.Add (new Argument (Right));
750 var call = new CallEmitter ();
751 call.EmitPredefined (ec, UserOperator, args);
753 Binary.EmitOperator (ec, Left, Right);
757 // Wrap the result when the operator return type is nullable type
759 if (type.IsNullableType)
760 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
762 ec.Emit (OpCodes.Br_S, end_label);
763 ec.MarkLabel (is_null_label);
765 if ((Binary.Oper & Binary.Operator.ComparisonMask) != 0) {
768 LiftedNull.Create (type, loc).Emit (ec);
771 ec.MarkLabel (end_label);
774 void EmitBitwiseBoolean (EmitContext ec)
776 Label load_left = ec.DefineLabel ();
777 Label load_right = ec.DefineLabel ();
778 Label end_label = ec.DefineLabel ();
779 Label is_null_label = ec.DefineLabel ();
781 bool or = Binary.Oper == Binary.Operator.BitwiseOr;
784 // Both operands are bool? types
786 if (UnwrapLeft != null && UnwrapRight != null) {
787 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) {
788 Left = Left.EmitToField (ec);
789 Right = Right.EmitToField (ec);
791 UnwrapLeft.Store (ec);
792 UnwrapRight.Store (ec);
796 ec.Emit (OpCodes.Brtrue_S, load_right);
799 ec.Emit (OpCodes.Brtrue_S, load_left);
801 UnwrapLeft.EmitCheck (ec);
802 ec.Emit (OpCodes.Brfalse_S, load_right);
805 ec.MarkLabel (load_left);
807 UnwrapRight.Load (ec);
809 UnwrapLeft.Load (ec);
811 ec.Emit (OpCodes.Br_S, end_label);
814 ec.MarkLabel (load_right);
816 UnwrapLeft.Load (ec);
818 UnwrapRight.Load (ec);
820 ec.MarkLabel (end_label);
825 // Faster version when one operand is bool
827 if (UnwrapLeft == null) {
831 // Optimizes remaining (false & bool?), (true | bool?) which are not easy to handle
832 // in binary expression reduction
834 var c = Left as BoolConstant;
836 // Keep evaluation order
837 UnwrapRight.Store (ec);
839 ec.EmitInt (or ? 1 : 0);
840 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
841 } else if (Left.IsNull) {
842 UnwrapRight.Emit (ec);
843 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label);
845 UnwrapRight.Load (ec);
846 ec.Emit (OpCodes.Br_S, end_label);
848 ec.MarkLabel (is_null_label);
849 LiftedNull.Create (type, loc).Emit (ec);
852 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right);
854 ec.EmitInt (or ? 1 : 0);
855 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
857 ec.Emit (OpCodes.Br_S, end_label);
859 ec.MarkLabel (load_right);
860 UnwrapRight.Original.Emit (ec);
866 // Keep left-right evaluation order
867 UnwrapLeft.Store (ec);
870 // Optimizes remaining (bool? & false), (bool? | true) which are not easy to handle
871 // in binary expression reduction
873 var c = Right as BoolConstant;
875 ec.EmitInt (or ? 1 : 0);
876 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
877 } else if (Right.IsNull) {
878 UnwrapLeft.Emit (ec);
879 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label);
881 UnwrapLeft.Load (ec);
882 ec.Emit (OpCodes.Br_S, end_label);
884 ec.MarkLabel (is_null_label);
885 LiftedNull.Create (type, loc).Emit (ec);
888 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right);
890 ec.EmitInt (or ? 1 : 0);
891 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
893 ec.Emit (OpCodes.Br_S, end_label);
895 ec.MarkLabel (load_right);
897 UnwrapLeft.Load (ec);
901 ec.MarkLabel (end_label);
905 // Emits optimized equality or inequality operator when possible
907 void EmitEquality (EmitContext ec)
910 // Either left or right is null
912 if (UnwrapLeft != null && Binary.Right.IsNull) { // TODO: Optimize for EmitBranchable
914 // left.HasValue == false
916 UnwrapLeft.EmitCheck (ec);
917 if (Binary.Oper == Binary.Operator.Equality) {
919 ec.Emit (OpCodes.Ceq);
924 if (UnwrapRight != null && Binary.Left.IsNull) {
926 // right.HasValue == false
928 UnwrapRight.EmitCheck (ec);
929 if (Binary.Oper == Binary.Operator.Equality) {
931 ec.Emit (OpCodes.Ceq);
936 Label dissimilar_label = ec.DefineLabel ();
937 Label end_label = ec.DefineLabel ();
939 if (UserOperator != null) {
942 if (UnwrapLeft != null) {
943 UnwrapLeft.EmitCheck (ec);
945 // Keep evaluation order same
946 if (!(Left is VariableReference)) {
948 var lt = new LocalTemporary (Left.Type);
954 if (UnwrapRight != null) {
955 UnwrapRight.EmitCheck (ec);
957 if (UnwrapLeft != null) {
958 ec.Emit (OpCodes.Bne_Un, dissimilar_label);
960 Label compare_label = ec.DefineLabel ();
961 UnwrapLeft.EmitCheck (ec);
962 ec.Emit (OpCodes.Brtrue, compare_label);
964 if (Binary.Oper == Binary.Operator.Equality)
969 ec.Emit (OpCodes.Br, end_label);
971 ec.MarkLabel (compare_label);
973 ec.Emit (OpCodes.Brfalse, dissimilar_label);
976 ec.Emit (OpCodes.Brfalse, dissimilar_label);
979 var args = new Arguments (2);
980 args.Add (new Argument (left));
981 args.Add (new Argument (Right));
983 var call = new CallEmitter ();
984 call.EmitPredefined (ec, UserOperator, args);
986 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) {
987 Left = Left.EmitToField (ec);
988 Right = Right.EmitToField (ec);
992 // Emit underlying value comparison first.
994 // For this code: int? a = 1; bool b = a == 1;
996 // We emit something similar to this. Expressions with side effects have local
997 // variable created by Unwrap expression
999 // left.GetValueOrDefault ()
1001 // bne.un.s dissimilar_label
1004 // dissimilar_label:
1012 ec.Emit (OpCodes.Bne_Un_S, dissimilar_label);
1015 // Check both left and right expressions for Unwrap call in which
1016 // case we need to run get_HasValue() check because the type is
1017 // nullable and could have null value
1019 if (UnwrapLeft != null)
1020 UnwrapLeft.EmitCheck (ec);
1022 if (UnwrapRight != null)
1023 UnwrapRight.EmitCheck (ec);
1025 if (UnwrapLeft != null && UnwrapRight != null) {
1026 if (Binary.Oper == Binary.Operator.Inequality)
1027 ec.Emit (OpCodes.Xor);
1029 ec.Emit (OpCodes.Ceq);
1031 if (Binary.Oper == Binary.Operator.Inequality) {
1033 ec.Emit (OpCodes.Ceq);
1038 ec.Emit (OpCodes.Br_S, end_label);
1040 ec.MarkLabel (dissimilar_label);
1041 if (Binary.Oper == Binary.Operator.Inequality)
1046 ec.MarkLabel (end_label);
1049 public override void FlowAnalysis (FlowAnalysisContext fc)
1051 Binary.FlowAnalysis (fc);
1054 public override SLE.Expression MakeExpression (BuilderContext ctx)
1056 return Binary.MakeExpression (ctx, Left, Right);
1060 public class NullCoalescingOperator : Expression
1062 Expression left, right;
1065 public NullCoalescingOperator (Expression left, Expression right)
1069 this.loc = left.Location;
1072 public Expression LeftExpression {
1078 public Expression RightExpression {
1084 public override Expression CreateExpressionTree (ResolveContext ec)
1086 if (left is NullLiteral)
1087 ec.Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side");
1089 UserCast uc = left as UserCast;
1090 Expression conversion = null;
1094 Arguments c_args = new Arguments (2);
1095 c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
1096 c_args.Add (new Argument (left.CreateExpressionTree (ec)));
1097 conversion = CreateExpressionFactoryCall (ec, "Lambda", c_args);
1100 Arguments args = new Arguments (3);
1101 args.Add (new Argument (left.CreateExpressionTree (ec)));
1102 args.Add (new Argument (right.CreateExpressionTree (ec)));
1103 if (conversion != null)
1104 args.Add (new Argument (conversion));
1106 return CreateExpressionFactoryCall (ec, "Coalesce", args);
1109 Expression ConvertExpression (ResolveContext ec)
1111 // TODO: ImplicitConversionExists should take care of this
1112 if (left.eclass == ExprClass.MethodGroup)
1115 TypeSpec ltype = left.Type;
1118 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
1119 // the result is underlying type of left
1121 if (ltype.IsNullableType) {
1122 unwrap = Unwrap.Create (left, false);
1127 // Reduce (left ?? null) to left
1130 return ReducedExpression.Create (left, this);
1132 if (Convert.ImplicitConversionExists (ec, right, unwrap.Type)) {
1137 // If right is a dynamic expression, the result type is dynamic
1139 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1142 // Need to box underlying value type
1143 left = Convert.ImplicitBoxingConversion (left, ltype, type);
1147 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1151 } else if (TypeSpec.IsReferenceType (ltype)) {
1152 if (Convert.ImplicitConversionExists (ec, right, ltype)) {
1154 // If right is a dynamic expression, the result type is dynamic
1156 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1162 // Reduce ("foo" ?? expr) to expression
1164 Constant lc = left as Constant;
1165 if (lc != null && !lc.IsDefaultValue)
1166 return ReducedExpression.Create (lc, this, false);
1169 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1171 if (right.IsNull || lc != null) {
1173 // Special case null ?? null
1175 if (right.IsNull && ltype == right.Type)
1178 return ReducedExpression.Create (lc != null ? right : left, this, false);
1181 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1189 TypeSpec rtype = right.Type;
1190 if (!Convert.ImplicitConversionExists (ec, unwrap ?? left, rtype) || right.eclass == ExprClass.MethodGroup)
1194 // Reduce (null ?? right) to right
1197 return ReducedExpression.Create (right, this, false).Resolve (ec);
1199 left = Convert.ImplicitConversion (ec, unwrap ?? left, rtype, loc);
1204 public override bool ContainsEmitWithAwait ()
1207 return unwrap.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1209 return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1212 protected override Expression DoResolve (ResolveContext ec)
1214 left = left.Resolve (ec);
1215 right = right.Resolve (ec);
1217 if (left == null || right == null)
1220 eclass = ExprClass.Value;
1222 Expression e = ConvertExpression (ec);
1224 Binary.Error_OperatorCannotBeApplied (ec, left, right, "??", loc);
1231 public override void Emit (EmitContext ec)
1233 Label end_label = ec.DefineLabel ();
1235 if (unwrap != null) {
1236 Label is_null_label = ec.DefineLabel ();
1238 unwrap.EmitCheck (ec);
1239 ec.Emit (OpCodes.Brfalse, is_null_label);
1242 // When both expressions are nullable the unwrap
1243 // is needed only for null check not for value uwrap
1245 if (type.IsNullableType && TypeSpecComparer.IsEqual (NullableInfo.GetUnderlyingType (type), unwrap.Type))
1250 ec.Emit (OpCodes.Br, end_label);
1252 ec.MarkLabel (is_null_label);
1255 ec.MarkLabel (end_label);
1260 ec.Emit (OpCodes.Dup);
1262 // Only to make verifier happy
1263 if (left.Type.IsGenericParameter)
1264 ec.Emit (OpCodes.Box, left.Type);
1266 ec.Emit (OpCodes.Brtrue, end_label);
1268 ec.Emit (OpCodes.Pop);
1271 ec.MarkLabel (end_label);
1274 public override void FlowAnalysis (FlowAnalysisContext fc)
1276 left.FlowAnalysis (fc);
1277 var left_da = fc.BranchDefiniteAssignment ();
1278 right.FlowAnalysis (fc);
1279 fc.DefiniteAssignment = left_da;
1282 protected override void CloneTo (CloneContext clonectx, Expression t)
1284 NullCoalescingOperator target = (NullCoalescingOperator) t;
1286 target.left = left.Clone (clonectx);
1287 target.right = right.Clone (clonectx);
1290 public override object Accept (StructuralVisitor visitor)
1292 return visitor.Visit (this);
1296 class LiftedUnaryMutator : UnaryMutator
1298 public LiftedUnaryMutator (Mode mode, Expression expr, Location loc)
1299 : base (mode, expr, loc)
1303 protected override Expression DoResolve (ResolveContext ec)
1305 var orig_expr = expr;
1307 expr = Unwrap.Create (expr);
1309 var res = base.DoResolveOperation (ec);
1317 protected override void EmitOperation (EmitContext ec)
1319 Label is_null_label = ec.DefineLabel ();
1320 Label end_label = ec.DefineLabel ();
1322 LocalTemporary lt = new LocalTemporary (type);
1324 // Value is on the stack
1327 var call = new CallEmitter ();
1328 call.InstanceExpression = lt;
1329 call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null);
1331 ec.Emit (OpCodes.Brfalse, is_null_label);
1333 call = new CallEmitter ();
1334 call.InstanceExpression = lt;
1335 call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null);
1339 base.EmitOperation (ec);
1341 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
1342 ec.Emit (OpCodes.Br_S, end_label);
1344 ec.MarkLabel (is_null_label);
1345 LiftedNull.Create (type, loc).Emit (ec);
1347 ec.MarkLabel (end_label);