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 Expression EmitToField (EmitContext ec)
192 if (temp_field == null)
193 temp_field = this.expr.EmitToField (ec);
198 public override bool Equals (object obj)
200 Unwrap uw = obj as Unwrap;
201 return uw != null && expr.Equals (uw.expr);
204 public Expression Original {
210 public override int GetHashCode ()
212 return expr.GetHashCode ();
215 public override bool IsNull {
221 public void Store (EmitContext ec)
223 if (temp != null || temp_field != null)
226 if (expr is VariableReference)
230 LocalVariable.Store (ec);
233 public void Load (EmitContext ec)
235 if (temp_field != null)
236 temp_field.Emit (ec);
237 else if (expr is VariableReference)
240 LocalVariable.Emit (ec);
243 public override SLE.Expression MakeExpression (BuilderContext ctx)
245 return expr.MakeExpression (ctx);
248 public void AddressOf (EmitContext ec, AddressOp mode)
252 if (temp_field != null) {
253 ml = temp_field as IMemoryLocation;
255 var lt = new LocalTemporary (temp_field.Type);
256 temp_field.Emit (ec);
261 ml = expr as VariableReference;
265 ml.AddressOf (ec, mode);
267 LocalVariable.AddressOf (ec, mode);
271 // Keeps result of non-variable expression
273 LocalTemporary LocalVariable {
275 if (temp == null && temp_field == null)
276 temp = new LocalTemporary (expr.Type);
283 // Calls get_Value method on nullable expression
285 public class UnwrapCall : CompositeExpression
287 public UnwrapCall (Expression expr)
292 protected override Expression DoResolve (ResolveContext rc)
297 type = NullableInfo.GetUnderlyingType (type);
302 public override void Emit (EmitContext ec)
304 var call = new CallEmitter ();
305 call.InstanceExpression = Child;
306 call.EmitPredefined (ec, NullableInfo.GetValue (Child.Type), null);
310 public class Wrap : TypeCast
312 private Wrap (Expression expr, TypeSpec type)
315 eclass = ExprClass.Value;
318 public override Expression CreateExpressionTree (ResolveContext ec)
320 TypeCast child_cast = child as TypeCast;
321 if (child_cast != null) {
323 return child_cast.CreateExpressionTree (ec);
326 var user_cast = child as UserCast;
327 if (user_cast != null) {
329 return user_cast.CreateExpressionTree (ec);
332 return base.CreateExpressionTree (ec);
335 public static Expression Create (Expression expr, TypeSpec type)
338 // Avoid unwraping and wraping of the same type
340 Unwrap unwrap = expr as Unwrap;
341 if (unwrap != null && expr.Type == NullableInfo.GetUnderlyingType (type))
342 return unwrap.Original;
344 return new Wrap (expr, type);
347 public override void Emit (EmitContext ec)
350 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
355 // Represents null literal lifted to nullable type
357 public class LiftedNull : NullConstant, IMemoryLocation
359 private LiftedNull (TypeSpec nullable_type, Location loc)
360 : base (nullable_type, loc)
362 eclass = ExprClass.Value;
365 public static Constant Create (TypeSpec nullable, Location loc)
367 return new LiftedNull (nullable, loc);
370 public static Constant CreateFromExpression (ResolveContext rc, Expression e)
372 if (!rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) {
373 rc.Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'",
374 e.Type.GetSignatureForError ());
377 return ReducedExpression.Create (Create (e.Type, e.Location), e);
380 public override void Emit (EmitContext ec)
382 // TODO: generate less temporary variables
383 LocalTemporary value_target = new LocalTemporary (type);
385 value_target.AddressOf (ec, AddressOp.Store);
386 ec.Emit (OpCodes.Initobj, type);
387 value_target.Emit (ec);
388 value_target.Release (ec);
391 public void AddressOf (EmitContext ec, AddressOp Mode)
393 LocalTemporary value_target = new LocalTemporary (type);
395 value_target.AddressOf (ec, AddressOp.Store);
396 ec.Emit (OpCodes.Initobj, type);
397 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
402 // Generic lifting expression, supports all S/S? -> T/T? cases
404 public class Lifted : Expression, IMemoryLocation
406 Expression expr, null_value;
409 public Lifted (Expression expr, Unwrap unwrap, TypeSpec type)
412 this.unwrap = unwrap;
413 this.loc = expr.Location;
417 public Lifted (Expression expr, Expression unwrap, TypeSpec type)
418 : this (expr, unwrap as Unwrap, type)
422 public override bool ContainsEmitWithAwait ()
424 return unwrap.ContainsEmitWithAwait ();
427 public override Expression CreateExpressionTree (ResolveContext ec)
429 return expr.CreateExpressionTree (ec);
432 protected override Expression DoResolve (ResolveContext ec)
435 // It's null when lifting non-nullable type
437 if (unwrap == null) {
438 // S -> T? is wrap only
439 if (type.IsNullableType)
440 return Wrap.Create (expr, type);
442 // S -> T can be simplified
446 // Wrap target for T?
447 if (type.IsNullableType) {
448 expr = Wrap.Create (expr, type);
452 null_value = LiftedNull.Create (type, loc);
453 } else if (TypeSpec.IsValueType (type)) {
454 null_value = LiftedNull.Create (type, loc);
456 null_value = new NullConstant (type, loc);
459 eclass = ExprClass.Value;
463 public override void Emit (EmitContext ec)
465 Label is_null_label = ec.DefineLabel ();
466 Label end_label = ec.DefineLabel ();
468 unwrap.EmitCheck (ec);
469 ec.Emit (OpCodes.Brfalse, is_null_label);
473 ec.Emit (OpCodes.Br, end_label);
474 ec.MarkLabel (is_null_label);
476 null_value.Emit (ec);
477 ec.MarkLabel (end_label);
480 public void AddressOf (EmitContext ec, AddressOp mode)
482 unwrap.AddressOf (ec, mode);
486 public class LiftedUnaryOperator : Unary, IMemoryLocation
489 Expression user_operator;
491 public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc)
492 : base (op, expr, loc)
496 public void AddressOf (EmitContext ec, AddressOp mode)
498 unwrap.AddressOf (ec, mode);
501 public override Expression CreateExpressionTree (ResolveContext ec)
503 if (user_operator != null)
504 return user_operator.CreateExpressionTree (ec);
506 if (Oper == Operator.UnaryPlus)
507 return Expr.CreateExpressionTree (ec);
509 return base.CreateExpressionTree (ec);
512 protected override Expression DoResolve (ResolveContext ec)
514 unwrap = Unwrap.Create (Expr, false);
518 Expression res = base.ResolveOperator (ec, unwrap);
520 if (user_operator == null)
523 res = Expr = LiftExpression (ec, Expr);
529 eclass = ExprClass.Value;
534 public override void Emit (EmitContext ec)
536 Label is_null_label = ec.DefineLabel ();
537 Label end_label = ec.DefineLabel ();
539 unwrap.EmitCheck (ec);
540 ec.Emit (OpCodes.Brfalse, is_null_label);
542 if (user_operator != null) {
543 user_operator.Emit (ec);
545 EmitOperator (ec, NullableInfo.GetUnderlyingType (type));
548 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
549 ec.Emit (OpCodes.Br_S, end_label);
551 ec.MarkLabel (is_null_label);
552 LiftedNull.Create (type, loc).Emit (ec);
554 ec.MarkLabel (end_label);
557 static Expression LiftExpression (ResolveContext ec, Expression expr)
559 var lifted_type = new NullableType (expr.Type, expr.Location);
560 if (lifted_type.ResolveAsType (ec) == null)
563 expr.Type = lifted_type.Type;
567 protected override Expression ResolveEnumOperator (ResolveContext ec, Expression expr, TypeSpec[] predefined)
569 expr = base.ResolveEnumOperator (ec, expr, predefined);
573 Expr = LiftExpression (ec, Expr);
574 return LiftExpression (ec, expr);
577 protected override Expression ResolveUserOperator (ResolveContext ec, Expression expr)
579 expr = base.ResolveUserOperator (ec, expr);
584 // When a user operator is of non-nullable type
586 if (Expr is Unwrap) {
587 user_operator = LiftExpression (ec, expr);
588 return user_operator;
596 // Lifted version of binary operators
598 class LiftedBinaryOperator : Expression
600 public LiftedBinaryOperator (Binary b)
603 this.loc = b.Location;
606 public Binary Binary { get; private set; }
608 public Expression Left { get; set; }
610 public Expression Right { get; set; }
612 public Unwrap UnwrapLeft { get; set; }
614 public Unwrap UnwrapRight { get; set; }
616 public MethodSpec UserOperator { get; set; }
618 bool IsBitwiseBoolean {
620 return (Binary.Oper == Binary.Operator.BitwiseAnd || Binary.Oper == Binary.Operator.BitwiseOr) &&
621 ((UnwrapLeft != null && UnwrapLeft.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) ||
622 (UnwrapRight != null && UnwrapRight.Type.BuiltinType == BuiltinTypeSpec.Type.Bool));
626 public override bool ContainsEmitWithAwait ()
628 return Left.ContainsEmitWithAwait () || Right.ContainsEmitWithAwait ();
631 public override Expression CreateExpressionTree (ResolveContext rc)
633 if (UserOperator != null) {
634 Arguments args = new Arguments (2);
635 args.Add (new Argument (Binary.Left));
636 args.Add (new Argument (Binary.Right));
638 var method = new UserOperatorCall (UserOperator, args, Binary.CreateExpressionTree, loc);
639 return method.CreateExpressionTree (rc);
642 return Binary.CreateExpressionTree (rc);
645 protected override Expression DoResolve (ResolveContext rc)
647 if (rc.IsRuntimeBinder) {
648 if (UnwrapLeft == null && !Left.Type.IsNullableType)
649 Left = Wrap.Create (Left, rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { Left.Type }));
651 if (UnwrapRight == null && !Right.Type.IsNullableType)
652 Right = Wrap.Create (Right, rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { Right.Type }));
654 if (UnwrapLeft == null && Left != null && Left.Type.IsNullableType) {
655 Left = Unwrap.CreateUnwrapped (Left);
656 UnwrapLeft = Left as Unwrap;
659 if (UnwrapRight == null && Right != null && Right.Type.IsNullableType) {
660 Right = Unwrap.CreateUnwrapped (Right);
661 UnwrapRight = Right as Unwrap;
666 eclass = Binary.eclass;
671 public override void Emit (EmitContext ec)
673 if (IsBitwiseBoolean && UserOperator == null) {
674 EmitBitwiseBoolean (ec);
678 if ((Binary.Oper & Binary.Operator.EqualityMask) != 0) {
683 Label is_null_label = ec.DefineLabel ();
684 Label end_label = ec.DefineLabel ();
686 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Right.ContainsEmitWithAwait ()) {
687 Left = Left.EmitToField (ec);
688 Right = Right.EmitToField (ec);
691 if (UnwrapLeft != null) {
692 UnwrapLeft.EmitCheck (ec);
696 // Don't emit HasValue check when left and right expressions are same
698 if (UnwrapRight != null && !Binary.Left.Equals (Binary.Right)) {
699 UnwrapRight.EmitCheck (ec);
700 if (UnwrapLeft != null) {
701 ec.Emit (OpCodes.And);
705 ec.Emit (OpCodes.Brfalse, is_null_label);
707 if (UserOperator != null) {
708 var args = new Arguments (2);
709 args.Add (new Argument (Left));
710 args.Add (new Argument (Right));
712 var call = new CallEmitter ();
713 call.EmitPredefined (ec, UserOperator, args);
715 Binary.EmitOperator (ec, Left, Right);
719 // Wrap the result when the operator return type is nullable type
721 if (type.IsNullableType)
722 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
724 ec.Emit (OpCodes.Br_S, end_label);
725 ec.MarkLabel (is_null_label);
727 if ((Binary.Oper & Binary.Operator.ComparisonMask) != 0) {
730 LiftedNull.Create (type, loc).Emit (ec);
733 ec.MarkLabel (end_label);
736 void EmitBitwiseBoolean (EmitContext ec)
738 Label load_left = ec.DefineLabel ();
739 Label load_right = ec.DefineLabel ();
740 Label end_label = ec.DefineLabel ();
741 Label is_null_label = ec.DefineLabel ();
743 bool or = Binary.Oper == Binary.Operator.BitwiseOr;
746 // Both operands are bool? types
748 if (UnwrapLeft != null && UnwrapRight != null) {
749 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) {
750 Left = Left.EmitToField (ec);
751 Right = Right.EmitToField (ec);
755 ec.Emit (OpCodes.Brtrue_S, load_right);
758 ec.Emit (OpCodes.Brtrue_S, load_left);
760 UnwrapLeft.EmitCheck (ec);
761 ec.Emit (OpCodes.Brfalse_S, load_right);
764 ec.MarkLabel (load_left);
766 UnwrapRight.Load (ec);
768 UnwrapLeft.Load (ec);
770 ec.Emit (OpCodes.Br_S, end_label);
773 ec.MarkLabel (load_right);
775 UnwrapLeft.Load (ec);
777 UnwrapRight.Load (ec);
779 ec.MarkLabel (end_label);
784 // Faster version when one operand is bool
786 if (UnwrapLeft == null) {
790 // Optimizes remaining (false & bool?), (true | bool?) which are not easy to handle
791 // in binary expression reduction
793 var c = Left as BoolConstant;
795 // Keep evaluation order
796 UnwrapRight.Store (ec);
798 ec.EmitInt (or ? 1 : 0);
799 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
800 } else if (Left.IsNull) {
801 UnwrapRight.Emit (ec);
802 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label);
804 UnwrapRight.Load (ec);
805 ec.Emit (OpCodes.Br_S, end_label);
807 ec.MarkLabel (is_null_label);
808 LiftedNull.Create (type, loc).Emit (ec);
811 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right);
813 ec.EmitInt (or ? 1 : 0);
814 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
816 ec.Emit (OpCodes.Br_S, end_label);
818 ec.MarkLabel (load_right);
819 UnwrapRight.Original.Emit (ec);
825 // Keep left-right evaluation order
826 UnwrapLeft.Store (ec);
829 // Optimizes remaining (bool? & false), (bool? | true) which are not easy to handle
830 // in binary expression reduction
832 var c = Right as BoolConstant;
834 ec.EmitInt (or ? 1 : 0);
835 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
836 } else if (Right.IsNull) {
837 UnwrapLeft.Emit (ec);
838 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label);
840 UnwrapLeft.Load (ec);
841 ec.Emit (OpCodes.Br_S, end_label);
843 ec.MarkLabel (is_null_label);
844 LiftedNull.Create (type, loc).Emit (ec);
847 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right);
849 ec.EmitInt (or ? 1 : 0);
850 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
852 ec.Emit (OpCodes.Br_S, end_label);
854 ec.MarkLabel (load_right);
856 UnwrapLeft.Load (ec);
860 ec.MarkLabel (end_label);
864 // Emits optimized equality or inequality operator when possible
866 void EmitEquality (EmitContext ec)
869 // Either left or right is null
871 if (UnwrapLeft != null && Binary.Right.IsNull) { // TODO: Optimize for EmitBranchable
873 // left.HasValue == false
875 UnwrapLeft.EmitCheck (ec);
876 if (Binary.Oper == Binary.Operator.Equality) {
878 ec.Emit (OpCodes.Ceq);
883 if (UnwrapRight != null && Binary.Left.IsNull) {
885 // right.HasValue == false
887 UnwrapRight.EmitCheck (ec);
888 if (Binary.Oper == Binary.Operator.Equality) {
890 ec.Emit (OpCodes.Ceq);
895 Label dissimilar_label = ec.DefineLabel ();
896 Label end_label = ec.DefineLabel ();
898 if (UserOperator != null) {
901 if (UnwrapLeft != null) {
902 UnwrapLeft.EmitCheck (ec);
904 // Keep evaluation order same
905 if (!(Left is VariableReference)) {
907 var lt = new LocalTemporary (Left.Type);
913 if (UnwrapRight != null) {
914 UnwrapRight.EmitCheck (ec);
916 if (UnwrapLeft != null) {
917 ec.Emit (OpCodes.Bne_Un, dissimilar_label);
919 Label compare_label = ec.DefineLabel ();
920 UnwrapLeft.EmitCheck (ec);
921 ec.Emit (OpCodes.Brtrue, compare_label);
923 if (Binary.Oper == Binary.Operator.Equality)
928 ec.Emit (OpCodes.Br, end_label);
930 ec.MarkLabel (compare_label);
932 ec.Emit (OpCodes.Brfalse, dissimilar_label);
935 ec.Emit (OpCodes.Brfalse, dissimilar_label);
938 var args = new Arguments (2);
939 args.Add (new Argument (left));
940 args.Add (new Argument (Right));
942 var call = new CallEmitter ();
943 call.EmitPredefined (ec, UserOperator, args);
945 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) {
946 Left = Left.EmitToField (ec);
947 Right = Right.EmitToField (ec);
951 // Emit underlying value comparison first.
953 // For this code: int? a = 1; bool b = a == 1;
955 // We emit something similar to this. Expressions with side effects have local
956 // variable created by Unwrap expression
958 // left.GetValueOrDefault ()
960 // bne.un.s dissimilar_label
971 ec.Emit (OpCodes.Bne_Un_S, dissimilar_label);
974 // Check both left and right expressions for Unwrap call in which
975 // case we need to run get_HasValue() check because the type is
976 // nullable and could have null value
978 if (UnwrapLeft != null)
979 UnwrapLeft.EmitCheck (ec);
981 if (UnwrapRight != null)
982 UnwrapRight.EmitCheck (ec);
984 if (UnwrapLeft != null && UnwrapRight != null) {
985 if (Binary.Oper == Binary.Operator.Inequality)
986 ec.Emit (OpCodes.Xor);
988 ec.Emit (OpCodes.Ceq);
990 if (Binary.Oper == Binary.Operator.Inequality) {
992 ec.Emit (OpCodes.Ceq);
997 ec.Emit (OpCodes.Br_S, end_label);
999 ec.MarkLabel (dissimilar_label);
1000 if (Binary.Oper == Binary.Operator.Inequality)
1005 ec.MarkLabel (end_label);
1008 public override SLE.Expression MakeExpression (BuilderContext ctx)
1010 Console.WriteLine (":{0} x {1}", Left.GetType (), Right.GetType ());
1012 return Binary.MakeExpression (ctx, Left, Right);
1016 public class NullCoalescingOperator : Expression
1018 Expression left, right;
1021 public NullCoalescingOperator (Expression left, Expression right)
1025 this.loc = left.Location;
1028 public Expression LeftExpression {
1034 public Expression RightExpression {
1040 public override Expression CreateExpressionTree (ResolveContext ec)
1042 if (left is NullLiteral)
1043 ec.Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side");
1045 UserCast uc = left as UserCast;
1046 Expression conversion = null;
1050 Arguments c_args = new Arguments (2);
1051 c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
1052 c_args.Add (new Argument (left.CreateExpressionTree (ec)));
1053 conversion = CreateExpressionFactoryCall (ec, "Lambda", c_args);
1056 Arguments args = new Arguments (3);
1057 args.Add (new Argument (left.CreateExpressionTree (ec)));
1058 args.Add (new Argument (right.CreateExpressionTree (ec)));
1059 if (conversion != null)
1060 args.Add (new Argument (conversion));
1062 return CreateExpressionFactoryCall (ec, "Coalesce", args);
1065 Expression ConvertExpression (ResolveContext ec)
1067 // TODO: ImplicitConversionExists should take care of this
1068 if (left.eclass == ExprClass.MethodGroup)
1071 TypeSpec ltype = left.Type;
1074 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
1075 // the result is underlying type of left
1077 if (ltype.IsNullableType) {
1078 unwrap = Unwrap.Create (left, false);
1083 // Reduce (left ?? null) to left
1086 return ReducedExpression.Create (left, this);
1088 if (Convert.ImplicitConversionExists (ec, right, unwrap.Type)) {
1093 // If right is a dynamic expression, the result type is dynamic
1095 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1098 // Need to box underlying value type
1099 left = Convert.ImplicitBoxingConversion (left, ltype, type);
1103 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1107 } else if (TypeSpec.IsReferenceType (ltype)) {
1108 if (Convert.ImplicitConversionExists (ec, right, ltype)) {
1110 // If right is a dynamic expression, the result type is dynamic
1112 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1118 // Reduce ("foo" ?? expr) to expression
1120 Constant lc = left as Constant;
1121 if (lc != null && !lc.IsDefaultValue)
1122 return ReducedExpression.Create (lc, this);
1125 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1127 if (right.IsNull || lc != null)
1128 return ReducedExpression.Create (lc != null ? right : left, this);
1130 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1136 // Special case null ?? null
1138 if (ltype == right.Type) {
1146 TypeSpec rtype = right.Type;
1147 if (!Convert.ImplicitConversionExists (ec, unwrap != null ? unwrap : left, rtype) || right.eclass == ExprClass.MethodGroup)
1151 // Reduce (null ?? right) to right
1154 return ReducedExpression.Create (right, this).Resolve (ec);
1156 left = Convert.ImplicitConversion (ec, unwrap != null ? unwrap : left, rtype, loc);
1161 public override bool ContainsEmitWithAwait ()
1164 return unwrap.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1166 return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1169 protected override Expression DoResolve (ResolveContext ec)
1171 left = left.Resolve (ec);
1172 right = right.Resolve (ec);
1174 if (left == null || right == null)
1177 eclass = ExprClass.Value;
1179 Expression e = ConvertExpression (ec);
1181 Binary.Error_OperatorCannotBeApplied (ec, left, right, "??", loc);
1188 public override void Emit (EmitContext ec)
1190 Label end_label = ec.DefineLabel ();
1192 if (unwrap != null) {
1193 Label is_null_label = ec.DefineLabel ();
1195 unwrap.EmitCheck (ec);
1196 ec.Emit (OpCodes.Brfalse, is_null_label);
1199 ec.Emit (OpCodes.Br, end_label);
1201 ec.MarkLabel (is_null_label);
1204 ec.MarkLabel (end_label);
1209 ec.Emit (OpCodes.Dup);
1211 // Only to make verifier happy
1212 if (left.Type.IsGenericParameter)
1213 ec.Emit (OpCodes.Box, left.Type);
1215 ec.Emit (OpCodes.Brtrue, end_label);
1217 ec.Emit (OpCodes.Pop);
1220 ec.MarkLabel (end_label);
1223 protected override void CloneTo (CloneContext clonectx, Expression t)
1225 NullCoalescingOperator target = (NullCoalescingOperator) t;
1227 target.left = left.Clone (clonectx);
1228 target.right = right.Clone (clonectx);
1231 public override object Accept (StructuralVisitor visitor)
1233 return visitor.Visit (this);
1237 class LiftedUnaryMutator : UnaryMutator
1239 public LiftedUnaryMutator (Mode mode, Expression expr, Location loc)
1240 : base (mode, expr, loc)
1244 protected override Expression DoResolve (ResolveContext ec)
1246 var orig_expr = expr;
1248 expr = Unwrap.Create (expr);
1250 var res = base.DoResolveOperation (ec);
1258 protected override void EmitOperation (EmitContext ec)
1260 Label is_null_label = ec.DefineLabel ();
1261 Label end_label = ec.DefineLabel ();
1263 LocalTemporary lt = new LocalTemporary (type);
1265 // Value is on the stack
1268 var call = new CallEmitter ();
1269 call.InstanceExpression = lt;
1270 call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null);
1272 ec.Emit (OpCodes.Brfalse, is_null_label);
1274 call = new CallEmitter ();
1275 call.InstanceExpression = lt;
1276 call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null);
1280 base.EmitOperation (ec);
1282 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
1283 ec.Emit (OpCodes.Br_S, end_label);
1285 ec.MarkLabel (is_null_label);
1286 LiftedNull.Create (type, loc).Emit (ec);
1288 ec.MarkLabel (end_label);