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 MakeType (module, EnumSpec.GetUnderlyingType (GetUnderlyingType (nullableEnum)));
91 public static TypeSpec MakeType (ModuleContainer module, TypeSpec underlyingType)
93 return module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (module,
94 new[] { underlyingType });
99 public class Unwrap : Expression, IMemoryLocation
104 Expression temp_field;
105 readonly bool useDefaultValue;
107 public Unwrap (Expression expr, bool useDefaultValue = true)
110 this.loc = expr.Location;
111 this.useDefaultValue = useDefaultValue;
113 type = NullableInfo.GetUnderlyingType (expr.Type);
114 eclass = expr.eclass;
117 public override bool ContainsEmitWithAwait ()
119 return expr.ContainsEmitWithAwait ();
123 public static Expression Create (Expression expr)
126 // Avoid unwraping and wraping of same type
128 Wrap wrap = expr as Wrap;
132 return Create (expr, false);
135 public static Expression CreateUnwrapped (Expression expr)
138 // Avoid unwraping and wraping of same type
140 Wrap wrap = expr as Wrap;
144 return Create (expr, true);
147 public static Unwrap Create (Expression expr, bool useDefaultValue)
149 return new Unwrap (expr, useDefaultValue);
152 public override Expression CreateExpressionTree (ResolveContext ec)
154 return expr.CreateExpressionTree (ec);
157 protected override Expression DoResolve (ResolveContext ec)
162 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
164 expr = expr.DoResolveLValue (ec, right_side);
168 public override void Emit (EmitContext ec)
172 var call = new CallEmitter ();
173 call.InstanceExpression = this;
176 // Using GetGetValueOrDefault is prefered because JIT can possibly
177 // inline it whereas Value property contains a throw which is very
178 // unlikely to be inlined
181 call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null);
183 call.EmitPredefined (ec, NullableInfo.GetValue (expr.Type), null);
186 public void EmitCheck (EmitContext ec)
190 var call = new CallEmitter ();
191 call.InstanceExpression = this;
193 call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null);
196 public override void EmitSideEffect (EmitContext ec)
198 expr.EmitSideEffect (ec);
201 public override Expression EmitToField (EmitContext ec)
203 if (temp_field == null)
204 temp_field = this.expr.EmitToField (ec);
209 public override bool Equals (object obj)
211 Unwrap uw = obj as Unwrap;
212 return uw != null && expr.Equals (uw.expr);
215 public override void FlowAnalysis (FlowAnalysisContext fc)
217 expr.FlowAnalysis (fc);
220 public Expression Original {
226 public override int GetHashCode ()
228 return expr.GetHashCode ();
231 public override bool IsNull {
237 public void Store (EmitContext ec)
239 if (temp != null || temp_field != null)
242 if (expr is VariableReference)
246 LocalVariable.Store (ec);
249 public void Load (EmitContext ec)
251 if (temp_field != null)
252 temp_field.Emit (ec);
253 else if (expr is VariableReference)
256 LocalVariable.Emit (ec);
259 public override SLE.Expression MakeExpression (BuilderContext ctx)
261 return expr.MakeExpression (ctx);
264 public void AddressOf (EmitContext ec, AddressOp mode)
268 if (temp_field != null) {
269 ml = temp_field as IMemoryLocation;
271 var lt = new LocalTemporary (temp_field.Type);
272 temp_field.Emit (ec);
277 ml = expr as VariableReference;
281 ml.AddressOf (ec, mode);
283 LocalVariable.AddressOf (ec, mode);
287 // Keeps result of non-variable expression
289 LocalTemporary LocalVariable {
291 if (temp == null && temp_field == null)
292 temp = new LocalTemporary (expr.Type);
299 // Calls get_Value method on nullable expression
301 public class UnwrapCall : CompositeExpression
303 public UnwrapCall (Expression expr)
308 protected override Expression DoResolve (ResolveContext rc)
313 type = NullableInfo.GetUnderlyingType (type);
318 public override void Emit (EmitContext ec)
320 var call = new CallEmitter ();
321 call.InstanceExpression = Child;
322 call.EmitPredefined (ec, NullableInfo.GetValue (Child.Type), null);
326 public class Wrap : TypeCast
328 private Wrap (Expression expr, TypeSpec type)
331 eclass = ExprClass.Value;
334 public override Expression CreateExpressionTree (ResolveContext ec)
336 TypeCast child_cast = child as TypeCast;
337 if (child_cast != null) {
339 return child_cast.CreateExpressionTree (ec);
342 var user_cast = child as UserCast;
343 if (user_cast != null) {
345 return user_cast.CreateExpressionTree (ec);
348 return base.CreateExpressionTree (ec);
351 public static Expression Create (Expression expr, TypeSpec type)
354 // Avoid unwraping and wraping of the same type
356 Unwrap unwrap = expr as Unwrap;
357 if (unwrap != null && expr.Type == NullableInfo.GetUnderlyingType (type))
358 return unwrap.Original;
360 return new Wrap (expr, type);
363 public override void Emit (EmitContext ec)
366 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
371 // Represents null literal lifted to nullable type
373 public class LiftedNull : NullConstant, IMemoryLocation
375 private LiftedNull (TypeSpec nullable_type, Location loc)
376 : base (nullable_type, loc)
378 eclass = ExprClass.Value;
381 public static Constant Create (TypeSpec nullable, Location loc)
383 return new LiftedNull (nullable, loc);
386 public static Constant CreateFromExpression (ResolveContext rc, Expression e)
388 if (!rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) {
389 rc.Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'",
390 e.Type.GetSignatureForError ());
393 return ReducedExpression.Create (Create (e.Type, e.Location), e);
396 public override void Emit (EmitContext ec)
398 // TODO: generate less temporary variables
399 LocalTemporary value_target = new LocalTemporary (type);
401 value_target.AddressOf (ec, AddressOp.Store);
402 ec.Emit (OpCodes.Initobj, type);
403 value_target.Emit (ec);
404 value_target.Release (ec);
407 public void AddressOf (EmitContext ec, AddressOp Mode)
409 LocalTemporary value_target = new LocalTemporary (type);
411 value_target.AddressOf (ec, AddressOp.Store);
412 ec.Emit (OpCodes.Initobj, type);
413 value_target.AddressOf (ec, Mode);
418 // Generic lifting expression, supports all S/S? -> T/T? cases
420 public class LiftedConversion : Expression, IMemoryLocation
422 Expression expr, null_value;
425 public LiftedConversion (Expression expr, Unwrap unwrap, TypeSpec type)
428 this.unwrap = unwrap;
429 this.loc = expr.Location;
433 public LiftedConversion (Expression expr, Expression unwrap, TypeSpec type)
434 : this (expr, unwrap as Unwrap, type)
438 public override bool ContainsEmitWithAwait ()
440 return unwrap.ContainsEmitWithAwait ();
443 public override Expression CreateExpressionTree (ResolveContext ec)
445 return expr.CreateExpressionTree (ec);
448 protected override Expression DoResolve (ResolveContext ec)
451 // It's null when lifting non-nullable type
453 if (unwrap == null) {
454 // S -> T? is wrap only
455 if (type.IsNullableType)
456 return Wrap.Create (expr, type);
458 // S -> T can be simplified
462 // Wrap target for T?
463 if (type.IsNullableType) {
464 if (!expr.Type.IsNullableType) {
465 expr = Wrap.Create (expr, type);
470 null_value = LiftedNull.Create (type, loc);
471 } else if (TypeSpec.IsValueType (type)) {
472 null_value = LiftedNull.Create (type, loc);
474 null_value = new NullConstant (type, loc);
477 eclass = ExprClass.Value;
481 public override void Emit (EmitContext ec)
483 Label is_null_label = ec.DefineLabel ();
484 Label end_label = ec.DefineLabel ();
486 unwrap.EmitCheck (ec);
487 ec.Emit (OpCodes.Brfalse, is_null_label);
491 ec.Emit (OpCodes.Br, end_label);
492 ec.MarkLabel (is_null_label);
494 null_value.Emit (ec);
496 ec.MarkLabel (end_label);
499 public override void FlowAnalysis (FlowAnalysisContext fc)
501 expr.FlowAnalysis (fc);
504 public void AddressOf (EmitContext ec, AddressOp mode)
506 unwrap.AddressOf (ec, mode);
510 public class LiftedUnaryOperator : Unary, IMemoryLocation
513 Expression user_operator;
515 public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc)
516 : base (op, expr, loc)
520 public void AddressOf (EmitContext ec, AddressOp mode)
522 unwrap.AddressOf (ec, mode);
525 public override Expression CreateExpressionTree (ResolveContext ec)
527 if (user_operator != null)
528 return user_operator.CreateExpressionTree (ec);
530 if (Oper == Operator.UnaryPlus)
531 return Expr.CreateExpressionTree (ec);
533 return base.CreateExpressionTree (ec);
536 protected override Expression DoResolve (ResolveContext ec)
538 unwrap = Unwrap.Create (Expr, false);
542 Expression res = base.ResolveOperator (ec, unwrap);
544 Error_OperatorCannotBeApplied (ec, loc, OperName (Oper), Expr.Type);
549 if (user_operator == null)
552 res = Expr = LiftExpression (ec, Expr);
558 eclass = ExprClass.Value;
563 public override void Emit (EmitContext ec)
565 Label is_null_label = ec.DefineLabel ();
566 Label end_label = ec.DefineLabel ();
568 unwrap.EmitCheck (ec);
569 ec.Emit (OpCodes.Brfalse, is_null_label);
571 if (user_operator != null) {
572 user_operator.Emit (ec);
574 EmitOperator (ec, NullableInfo.GetUnderlyingType (type));
577 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
578 ec.Emit (OpCodes.Br_S, end_label);
580 ec.MarkLabel (is_null_label);
581 LiftedNull.Create (type, loc).Emit (ec);
583 ec.MarkLabel (end_label);
586 static Expression LiftExpression (ResolveContext ec, Expression expr)
588 var lifted_type = new NullableType (expr.Type, expr.Location);
589 if (lifted_type.ResolveAsType (ec) == null)
592 expr.Type = lifted_type.Type;
596 protected override Expression ResolveEnumOperator (ResolveContext ec, Expression expr, TypeSpec[] predefined)
598 expr = base.ResolveEnumOperator (ec, expr, predefined);
602 Expr = LiftExpression (ec, Expr);
603 return LiftExpression (ec, expr);
606 protected override Expression ResolveUserOperator (ResolveContext ec, Expression expr)
608 expr = base.ResolveUserOperator (ec, expr);
613 // When a user operator is of non-nullable type
615 if (Expr is Unwrap) {
616 user_operator = LiftExpression (ec, expr);
617 return user_operator;
625 // Lifted version of binary operators
627 class LiftedBinaryOperator : Expression
629 public LiftedBinaryOperator (Binary b)
632 this.loc = b.Location;
635 public Binary Binary { get; private set; }
637 public Expression Left { get; set; }
639 public Expression Right { get; set; }
641 public Unwrap UnwrapLeft { get; set; }
643 public Unwrap UnwrapRight { get; set; }
645 public MethodSpec UserOperator { get; set; }
647 bool IsBitwiseBoolean {
649 return (Binary.Oper == Binary.Operator.BitwiseAnd || Binary.Oper == Binary.Operator.BitwiseOr) &&
650 ((UnwrapLeft != null && UnwrapLeft.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) ||
651 (UnwrapRight != null && UnwrapRight.Type.BuiltinType == BuiltinTypeSpec.Type.Bool));
655 public override bool ContainsEmitWithAwait ()
657 return Left.ContainsEmitWithAwait () || Right.ContainsEmitWithAwait ();
660 public override Expression CreateExpressionTree (ResolveContext rc)
662 if (UserOperator != null) {
663 Arguments args = new Arguments (2);
664 args.Add (new Argument (Binary.Left));
665 args.Add (new Argument (Binary.Right));
667 var method = new UserOperatorCall (UserOperator, args, Binary.CreateExpressionTree, loc);
668 return method.CreateExpressionTree (rc);
671 return Binary.CreateExpressionTree (rc);
674 protected override Expression DoResolve (ResolveContext rc)
676 if (rc.IsRuntimeBinder) {
677 if (UnwrapLeft == null && !Left.Type.IsNullableType)
678 Left = LiftOperand (rc, Left);
680 if (UnwrapRight == null && !Right.Type.IsNullableType)
681 Right = LiftOperand (rc, Right);
683 if (UnwrapLeft == null && Left != null && Left.Type.IsNullableType) {
684 Left = Unwrap.CreateUnwrapped (Left);
685 UnwrapLeft = Left as Unwrap;
688 if (UnwrapRight == null && Right != null && Right.Type.IsNullableType) {
689 Right = Unwrap.CreateUnwrapped (Right);
690 UnwrapRight = Right as Unwrap;
695 eclass = Binary.eclass;
700 Expression LiftOperand (ResolveContext rc, Expression expr)
704 type = Left.IsNull ? Right.Type : Left.Type;
709 if (!type.IsNullableType)
710 type = NullableInfo.MakeType (rc.Module, type);
712 return Wrap.Create (expr, type);
715 public override void Emit (EmitContext ec)
717 if (IsBitwiseBoolean && UserOperator == null) {
718 EmitBitwiseBoolean (ec);
722 if ((Binary.Oper & Binary.Operator.EqualityMask) != 0) {
727 Label is_null_label = ec.DefineLabel ();
728 Label end_label = ec.DefineLabel ();
730 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Right.ContainsEmitWithAwait ()) {
731 Left = Left.EmitToField (ec);
732 Right = Right.EmitToField (ec);
735 if (UnwrapLeft != null) {
736 UnwrapLeft.EmitCheck (ec);
740 // Don't emit HasValue check when left and right expressions are same
742 if (UnwrapRight != null && !Binary.Left.Equals (Binary.Right)) {
743 UnwrapRight.EmitCheck (ec);
744 if (UnwrapLeft != null) {
745 ec.Emit (OpCodes.And);
749 ec.Emit (OpCodes.Brfalse, is_null_label);
751 if (UserOperator != null) {
752 var args = new Arguments (2);
753 args.Add (new Argument (Left));
754 args.Add (new Argument (Right));
756 var call = new CallEmitter ();
757 call.EmitPredefined (ec, UserOperator, args);
759 Binary.EmitOperator (ec, Left, Right);
763 // Wrap the result when the operator return type is nullable type
765 if (type.IsNullableType)
766 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
768 ec.Emit (OpCodes.Br_S, end_label);
769 ec.MarkLabel (is_null_label);
771 if ((Binary.Oper & Binary.Operator.ComparisonMask) != 0) {
774 LiftedNull.Create (type, loc).Emit (ec);
777 ec.MarkLabel (end_label);
780 void EmitBitwiseBoolean (EmitContext ec)
782 Label load_left = ec.DefineLabel ();
783 Label load_right = ec.DefineLabel ();
784 Label end_label = ec.DefineLabel ();
785 Label is_null_label = ec.DefineLabel ();
787 bool or = Binary.Oper == Binary.Operator.BitwiseOr;
790 // Both operands are bool? types
792 if (UnwrapLeft != null && UnwrapRight != null) {
793 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) {
794 Left = Left.EmitToField (ec);
795 Right = Right.EmitToField (ec);
797 UnwrapLeft.Store (ec);
798 UnwrapRight.Store (ec);
802 ec.Emit (OpCodes.Brtrue_S, load_right);
805 ec.Emit (OpCodes.Brtrue_S, load_left);
807 UnwrapLeft.EmitCheck (ec);
808 ec.Emit (OpCodes.Brfalse_S, load_right);
811 ec.MarkLabel (load_left);
813 UnwrapRight.Load (ec);
815 UnwrapLeft.Load (ec);
817 ec.Emit (OpCodes.Br_S, end_label);
820 ec.MarkLabel (load_right);
822 UnwrapLeft.Load (ec);
824 UnwrapRight.Load (ec);
826 ec.MarkLabel (end_label);
831 // Faster version when one operand is bool
833 if (UnwrapLeft == null) {
837 // Optimizes remaining (false & bool?), (true | bool?) which are not easy to handle
838 // in binary expression reduction
840 var c = Left as BoolConstant;
842 // Keep evaluation order
843 UnwrapRight.Store (ec);
845 ec.EmitInt (or ? 1 : 0);
846 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
847 } else if (Left.IsNull) {
848 UnwrapRight.Emit (ec);
849 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label);
851 UnwrapRight.Load (ec);
852 ec.Emit (OpCodes.Br_S, end_label);
854 ec.MarkLabel (is_null_label);
855 LiftedNull.Create (type, loc).Emit (ec);
858 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right);
860 ec.EmitInt (or ? 1 : 0);
861 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
863 ec.Emit (OpCodes.Br_S, end_label);
865 ec.MarkLabel (load_right);
866 UnwrapRight.Original.Emit (ec);
872 // Keep left-right evaluation order
873 UnwrapLeft.Store (ec);
876 // Optimizes remaining (bool? & false), (bool? | true) which are not easy to handle
877 // in binary expression reduction
879 var c = Right as BoolConstant;
881 ec.EmitInt (or ? 1 : 0);
882 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
883 } else if (Right.IsNull) {
884 UnwrapLeft.Emit (ec);
885 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label);
887 UnwrapLeft.Load (ec);
888 ec.Emit (OpCodes.Br_S, end_label);
890 ec.MarkLabel (is_null_label);
891 LiftedNull.Create (type, loc).Emit (ec);
894 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right);
896 ec.EmitInt (or ? 1 : 0);
897 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
899 ec.Emit (OpCodes.Br_S, end_label);
901 ec.MarkLabel (load_right);
903 UnwrapLeft.Load (ec);
907 ec.MarkLabel (end_label);
911 // Emits optimized equality or inequality operator when possible
913 void EmitEquality (EmitContext ec)
916 // Either left or right is null
918 if (UnwrapLeft != null && Binary.Right.IsNull) { // TODO: Optimize for EmitBranchable
920 // left.HasValue == false
922 UnwrapLeft.EmitCheck (ec);
923 if (Binary.Oper == Binary.Operator.Equality) {
925 ec.Emit (OpCodes.Ceq);
930 if (UnwrapRight != null && Binary.Left.IsNull) {
932 // right.HasValue == false
934 UnwrapRight.EmitCheck (ec);
935 if (Binary.Oper == Binary.Operator.Equality) {
937 ec.Emit (OpCodes.Ceq);
942 Label dissimilar_label = ec.DefineLabel ();
943 Label end_label = ec.DefineLabel ();
945 if (UserOperator != null) {
948 if (UnwrapLeft != null) {
949 UnwrapLeft.EmitCheck (ec);
951 // Keep evaluation order same
952 if (!(Left is VariableReference)) {
954 var lt = new LocalTemporary (Left.Type);
960 if (UnwrapRight != null) {
961 UnwrapRight.EmitCheck (ec);
963 if (UnwrapLeft != null) {
964 ec.Emit (OpCodes.Bne_Un, dissimilar_label);
966 Label compare_label = ec.DefineLabel ();
967 UnwrapLeft.EmitCheck (ec);
968 ec.Emit (OpCodes.Brtrue, compare_label);
970 if (Binary.Oper == Binary.Operator.Equality)
975 ec.Emit (OpCodes.Br, end_label);
977 ec.MarkLabel (compare_label);
979 ec.Emit (OpCodes.Brfalse, dissimilar_label);
982 ec.Emit (OpCodes.Brfalse, dissimilar_label);
985 var args = new Arguments (2);
986 args.Add (new Argument (left));
987 args.Add (new Argument (Right));
989 var call = new CallEmitter ();
990 call.EmitPredefined (ec, UserOperator, args);
992 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) {
993 Left = Left.EmitToField (ec);
994 Right = Right.EmitToField (ec);
998 // Emit underlying value comparison first.
1000 // For this code: int? a = 1; bool b = a == 1;
1002 // We emit something similar to this. Expressions with side effects have local
1003 // variable created by Unwrap expression
1005 // left.GetValueOrDefault ()
1007 // bne.un.s dissimilar_label
1010 // dissimilar_label:
1018 ec.Emit (OpCodes.Bne_Un_S, dissimilar_label);
1021 // Check both left and right expressions for Unwrap call in which
1022 // case we need to run get_HasValue() check because the type is
1023 // nullable and could have null value
1025 if (UnwrapLeft != null)
1026 UnwrapLeft.EmitCheck (ec);
1028 if (UnwrapRight != null)
1029 UnwrapRight.EmitCheck (ec);
1031 if (UnwrapLeft != null && UnwrapRight != null) {
1032 if (Binary.Oper == Binary.Operator.Inequality)
1033 ec.Emit (OpCodes.Xor);
1035 ec.Emit (OpCodes.Ceq);
1037 if (Binary.Oper == Binary.Operator.Inequality) {
1039 ec.Emit (OpCodes.Ceq);
1044 ec.Emit (OpCodes.Br_S, end_label);
1046 ec.MarkLabel (dissimilar_label);
1047 if (Binary.Oper == Binary.Operator.Inequality)
1052 ec.MarkLabel (end_label);
1055 public override void FlowAnalysis (FlowAnalysisContext fc)
1057 Binary.FlowAnalysis (fc);
1060 public override SLE.Expression MakeExpression (BuilderContext ctx)
1062 return Binary.MakeExpression (ctx, Left, Right);
1066 public class NullCoalescingOperator : Expression
1068 Expression left, right;
1071 public NullCoalescingOperator (Expression left, Expression right)
1075 this.loc = left.Location;
1078 public Expression LeftExpression {
1084 public Expression RightExpression {
1090 public override Expression CreateExpressionTree (ResolveContext ec)
1092 if (left is NullLiteral)
1093 ec.Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side");
1095 UserCast uc = left as UserCast;
1096 Expression conversion = null;
1100 Arguments c_args = new Arguments (2);
1101 c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
1102 c_args.Add (new Argument (left.CreateExpressionTree (ec)));
1103 conversion = CreateExpressionFactoryCall (ec, "Lambda", c_args);
1106 Arguments args = new Arguments (3);
1107 args.Add (new Argument (left.CreateExpressionTree (ec)));
1108 args.Add (new Argument (right.CreateExpressionTree (ec)));
1109 if (conversion != null)
1110 args.Add (new Argument (conversion));
1112 return CreateExpressionFactoryCall (ec, "Coalesce", args);
1115 Expression ConvertExpression (ResolveContext ec)
1117 // TODO: ImplicitConversionExists should take care of this
1118 if (left.eclass == ExprClass.MethodGroup)
1121 TypeSpec ltype = left.Type;
1124 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
1125 // the result is underlying type of left
1127 if (ltype.IsNullableType) {
1128 unwrap = Unwrap.Create (left, false);
1133 // Reduce (left ?? null) to left
1136 return ReducedExpression.Create (left, this);
1138 if (Convert.ImplicitConversionExists (ec, right, unwrap.Type)) {
1143 // If right is a dynamic expression, the result type is dynamic
1145 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1148 // Need to box underlying value type
1149 left = Convert.ImplicitBoxingConversion (left, ltype, type);
1153 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1157 } else if (TypeSpec.IsReferenceType (ltype)) {
1158 if (Convert.ImplicitConversionExists (ec, right, ltype)) {
1160 // If right is a dynamic expression, the result type is dynamic
1162 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1168 // Reduce ("foo" ?? expr) to expression
1170 Constant lc = left as Constant;
1171 if (lc != null && !lc.IsDefaultValue)
1172 return ReducedExpression.Create (lc, this, false);
1175 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1177 if (right.IsNull || lc != null) {
1179 // Special case null ?? null
1181 if (right.IsNull && ltype == right.Type)
1184 return ReducedExpression.Create (lc != null ? right : left, this, false);
1187 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1195 TypeSpec rtype = right.Type;
1196 if (!Convert.ImplicitConversionExists (ec, unwrap ?? left, rtype) || right.eclass == ExprClass.MethodGroup)
1200 // Reduce (null ?? right) to right
1203 return ReducedExpression.Create (right, this, false).Resolve (ec);
1205 left = Convert.ImplicitConversion (ec, unwrap ?? left, rtype, loc);
1207 if (TypeSpec.IsValueType (left.Type) && !left.Type.IsNullableType) {
1208 Warning_UnreachableExpression (ec, right.Location);
1209 return ReducedExpression.Create (left, this, false).Resolve (ec);
1216 public override bool ContainsEmitWithAwait ()
1219 return unwrap.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1221 return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1224 protected override Expression DoResolve (ResolveContext ec)
1226 left = left.Resolve (ec);
1227 right = right.Resolve (ec);
1229 if (left == null || right == null)
1232 eclass = ExprClass.Value;
1234 Expression e = ConvertExpression (ec);
1236 Binary.Error_OperatorCannotBeApplied (ec, left, right, "??", loc);
1243 public override void Emit (EmitContext ec)
1245 Label end_label = ec.DefineLabel ();
1247 if (unwrap != null) {
1248 Label is_null_label = ec.DefineLabel ();
1250 unwrap.EmitCheck (ec);
1251 ec.Emit (OpCodes.Brfalse, is_null_label);
1254 // When both expressions are nullable the unwrap
1255 // is needed only for null check not for value uwrap
1257 if (type.IsNullableType && TypeSpecComparer.IsEqual (NullableInfo.GetUnderlyingType (type), unwrap.Type))
1262 ec.Emit (OpCodes.Br, end_label);
1264 ec.MarkLabel (is_null_label);
1267 ec.MarkLabel (end_label);
1272 ec.Emit (OpCodes.Dup);
1274 // Only to make verifier happy
1275 if (left.Type.IsGenericParameter)
1276 ec.Emit (OpCodes.Box, left.Type);
1278 ec.Emit (OpCodes.Brtrue, end_label);
1280 ec.Emit (OpCodes.Pop);
1283 ec.MarkLabel (end_label);
1286 public override void FlowAnalysis (FlowAnalysisContext fc)
1288 left.FlowAnalysis (fc);
1289 var left_da = fc.BranchDefiniteAssignment ();
1290 right.FlowAnalysis (fc);
1291 fc.DefiniteAssignment = left_da;
1294 protected override void CloneTo (CloneContext clonectx, Expression t)
1296 NullCoalescingOperator target = (NullCoalescingOperator) t;
1298 target.left = left.Clone (clonectx);
1299 target.right = right.Clone (clonectx);
1302 public override object Accept (StructuralVisitor visitor)
1304 return visitor.Visit (this);
1308 class LiftedUnaryMutator : UnaryMutator
1310 public LiftedUnaryMutator (Mode mode, Expression expr, Location loc)
1311 : base (mode, expr, loc)
1315 protected override Expression DoResolve (ResolveContext ec)
1317 var orig_expr = expr;
1319 expr = Unwrap.Create (expr);
1321 var res = base.DoResolveOperation (ec);
1329 protected override void EmitOperation (EmitContext ec)
1331 Label is_null_label = ec.DefineLabel ();
1332 Label end_label = ec.DefineLabel ();
1334 LocalTemporary lt = new LocalTemporary (type);
1336 // Value is on the stack
1339 var call = new CallEmitter ();
1340 call.InstanceExpression = lt;
1341 call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null);
1343 ec.Emit (OpCodes.Brfalse, is_null_label);
1345 call = new CallEmitter ();
1346 call.InstanceExpression = lt;
1347 call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null);
1351 base.EmitOperation (ec);
1353 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
1354 ec.Emit (OpCodes.Br_S, end_label);
1356 ec.MarkLabel (is_null_label);
1357 LiftedNull.Create (type, loc).Emit (ec);
1359 ec.MarkLabel (end_label);