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, bool allowUnboundTypeArguments = false)
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 IsNull {
444 public override bool ContainsEmitWithAwait ()
446 return unwrap.ContainsEmitWithAwait ();
449 public override Expression CreateExpressionTree (ResolveContext ec)
451 return expr.CreateExpressionTree (ec);
454 protected override Expression DoResolve (ResolveContext ec)
457 // It's null when lifting non-nullable type
459 if (unwrap == null) {
460 // S -> T? is wrap only
461 if (type.IsNullableType)
462 return Wrap.Create (expr, type);
464 // S -> T can be simplified
468 // Wrap target for T?
469 if (type.IsNullableType) {
470 if (!expr.Type.IsNullableType) {
471 expr = Wrap.Create (expr, type);
476 null_value = LiftedNull.Create (type, loc);
477 } else if (TypeSpec.IsValueType (type)) {
478 null_value = LiftedNull.Create (type, loc);
480 null_value = new NullConstant (type, loc);
483 eclass = ExprClass.Value;
487 public override void Emit (EmitContext ec)
489 Label is_null_label = ec.DefineLabel ();
490 Label end_label = ec.DefineLabel ();
492 unwrap.EmitCheck (ec);
493 ec.Emit (OpCodes.Brfalse, is_null_label);
497 ec.Emit (OpCodes.Br, end_label);
498 ec.MarkLabel (is_null_label);
500 null_value.Emit (ec);
502 ec.MarkLabel (end_label);
505 public override void FlowAnalysis (FlowAnalysisContext fc)
507 expr.FlowAnalysis (fc);
510 public void AddressOf (EmitContext ec, AddressOp mode)
512 unwrap.AddressOf (ec, mode);
516 public class LiftedUnaryOperator : Unary, IMemoryLocation
519 Expression user_operator;
521 public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc)
522 : base (op, expr, loc)
526 public void AddressOf (EmitContext ec, AddressOp mode)
528 unwrap.AddressOf (ec, mode);
531 public override Expression CreateExpressionTree (ResolveContext ec)
533 if (user_operator != null)
534 return user_operator.CreateExpressionTree (ec);
536 if (Oper == Operator.UnaryPlus)
537 return Expr.CreateExpressionTree (ec);
539 return base.CreateExpressionTree (ec);
542 protected override Expression DoResolve (ResolveContext ec)
544 unwrap = Unwrap.Create (Expr, false);
548 Expression res = base.ResolveOperator (ec, unwrap);
550 Error_OperatorCannotBeApplied (ec, loc, OperName (Oper), Expr.Type);
555 if (user_operator == null)
558 res = Expr = LiftExpression (ec, Expr);
564 eclass = ExprClass.Value;
569 public override void Emit (EmitContext ec)
571 Label is_null_label = ec.DefineLabel ();
572 Label end_label = ec.DefineLabel ();
574 unwrap.EmitCheck (ec);
575 ec.Emit (OpCodes.Brfalse, is_null_label);
577 if (user_operator != null) {
578 user_operator.Emit (ec);
580 EmitOperator (ec, NullableInfo.GetUnderlyingType (type));
583 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
584 ec.Emit (OpCodes.Br_S, end_label);
586 ec.MarkLabel (is_null_label);
587 LiftedNull.Create (type, loc).Emit (ec);
589 ec.MarkLabel (end_label);
592 static Expression LiftExpression (ResolveContext ec, Expression expr)
594 var lifted_type = new NullableType (expr.Type, expr.Location);
595 if (lifted_type.ResolveAsType (ec) == null)
598 expr.Type = lifted_type.Type;
602 protected override Expression ResolveEnumOperator (ResolveContext ec, Expression expr, TypeSpec[] predefined)
604 expr = base.ResolveEnumOperator (ec, expr, predefined);
608 Expr = LiftExpression (ec, Expr);
609 return LiftExpression (ec, expr);
612 protected override Expression ResolveUserOperator (ResolveContext ec, Expression expr)
614 expr = base.ResolveUserOperator (ec, expr);
619 // When a user operator is of non-nullable type
621 if (Expr is Unwrap) {
622 user_operator = LiftExpression (ec, expr);
623 return user_operator;
631 // Lifted version of binary operators
633 class LiftedBinaryOperator : Expression
635 public LiftedBinaryOperator (Binary b)
638 this.loc = b.Location;
641 public Binary Binary { get; private set; }
643 public Expression Left { get; set; }
645 public Expression Right { get; set; }
647 public Unwrap UnwrapLeft { get; set; }
649 public Unwrap UnwrapRight { get; set; }
651 public MethodSpec UserOperator { get; set; }
653 bool IsBitwiseBoolean {
655 return (Binary.Oper == Binary.Operator.BitwiseAnd || Binary.Oper == Binary.Operator.BitwiseOr) &&
656 ((UnwrapLeft != null && UnwrapLeft.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) ||
657 (UnwrapRight != null && UnwrapRight.Type.BuiltinType == BuiltinTypeSpec.Type.Bool));
661 public override bool ContainsEmitWithAwait ()
663 return Left.ContainsEmitWithAwait () || Right.ContainsEmitWithAwait ();
666 public override Expression CreateExpressionTree (ResolveContext rc)
668 if (UserOperator != null) {
669 Arguments args = new Arguments (2);
670 args.Add (new Argument (Binary.Left));
671 args.Add (new Argument (Binary.Right));
673 var method = new UserOperatorCall (UserOperator, args, Binary.CreateExpressionTree, loc);
674 return method.CreateExpressionTree (rc);
677 return Binary.CreateExpressionTree (rc);
680 protected override Expression DoResolve (ResolveContext rc)
682 if (rc.IsRuntimeBinder) {
683 if (UnwrapLeft == null && !Left.Type.IsNullableType)
684 Left = LiftOperand (rc, Left);
686 if (UnwrapRight == null && !Right.Type.IsNullableType)
687 Right = LiftOperand (rc, Right);
689 if (UnwrapLeft == null && Left != null && Left.Type.IsNullableType) {
690 Left = Unwrap.CreateUnwrapped (Left);
691 UnwrapLeft = Left as Unwrap;
694 if (UnwrapRight == null && Right != null && Right.Type.IsNullableType) {
695 Right = Unwrap.CreateUnwrapped (Right);
696 UnwrapRight = Right as Unwrap;
701 eclass = Binary.eclass;
706 Expression LiftOperand (ResolveContext rc, Expression expr)
710 type = Left.IsNull ? Right.Type : Left.Type;
715 if (!type.IsNullableType)
716 type = NullableInfo.MakeType (rc.Module, type);
718 return Wrap.Create (expr, type);
721 public override void Emit (EmitContext ec)
723 if (IsBitwiseBoolean && UserOperator == null) {
724 EmitBitwiseBoolean (ec);
728 if ((Binary.Oper & Binary.Operator.EqualityMask) != 0) {
733 Label is_null_label = ec.DefineLabel ();
734 Label end_label = ec.DefineLabel ();
736 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Right.ContainsEmitWithAwait ()) {
737 Left = Left.EmitToField (ec);
738 Right = Right.EmitToField (ec);
741 if (UnwrapLeft != null) {
742 UnwrapLeft.EmitCheck (ec);
746 // Don't emit HasValue check when left and right expressions are same
748 if (UnwrapRight != null && !Binary.Left.Equals (Binary.Right)) {
749 UnwrapRight.EmitCheck (ec);
750 if (UnwrapLeft != null) {
751 ec.Emit (OpCodes.And);
755 ec.Emit (OpCodes.Brfalse, is_null_label);
757 if (UserOperator != null) {
758 var args = new Arguments (2);
759 args.Add (new Argument (Left));
760 args.Add (new Argument (Right));
762 var call = new CallEmitter ();
763 call.EmitPredefined (ec, UserOperator, args);
765 Binary.EmitOperator (ec, Left, Right);
769 // Wrap the result when the operator return type is nullable type
771 if (type.IsNullableType)
772 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
774 ec.Emit (OpCodes.Br_S, end_label);
775 ec.MarkLabel (is_null_label);
777 if ((Binary.Oper & Binary.Operator.ComparisonMask) != 0) {
780 LiftedNull.Create (type, loc).Emit (ec);
783 ec.MarkLabel (end_label);
786 void EmitBitwiseBoolean (EmitContext ec)
788 Label load_left = ec.DefineLabel ();
789 Label load_right = ec.DefineLabel ();
790 Label end_label = ec.DefineLabel ();
791 Label is_null_label = ec.DefineLabel ();
793 bool or = Binary.Oper == Binary.Operator.BitwiseOr;
796 // Both operands are bool? types
798 if (UnwrapLeft != null && UnwrapRight != null) {
799 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) {
800 Left = Left.EmitToField (ec);
801 Right = Right.EmitToField (ec);
803 UnwrapLeft.Store (ec);
804 UnwrapRight.Store (ec);
808 ec.Emit (OpCodes.Brtrue_S, load_right);
811 ec.Emit (OpCodes.Brtrue_S, load_left);
813 UnwrapLeft.EmitCheck (ec);
814 ec.Emit (OpCodes.Brfalse_S, load_right);
817 ec.MarkLabel (load_left);
819 UnwrapRight.Load (ec);
821 UnwrapLeft.Load (ec);
823 ec.Emit (OpCodes.Br_S, end_label);
826 ec.MarkLabel (load_right);
828 UnwrapLeft.Load (ec);
830 UnwrapRight.Load (ec);
832 ec.MarkLabel (end_label);
837 // Faster version when one operand is bool
839 if (UnwrapLeft == null) {
843 // Optimizes remaining (false & bool?), (true | bool?) which are not easy to handle
844 // in binary expression reduction
846 var c = Left as BoolConstant;
848 // Keep evaluation order
849 UnwrapRight.Store (ec);
851 ec.EmitInt (or ? 1 : 0);
852 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
853 } else if (Left.IsNull) {
854 UnwrapRight.Emit (ec);
855 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label);
857 UnwrapRight.Load (ec);
858 ec.Emit (OpCodes.Br_S, end_label);
860 ec.MarkLabel (is_null_label);
861 LiftedNull.Create (type, loc).Emit (ec);
864 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right);
866 ec.EmitInt (or ? 1 : 0);
867 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
869 ec.Emit (OpCodes.Br_S, end_label);
871 ec.MarkLabel (load_right);
872 UnwrapRight.Original.Emit (ec);
878 // Keep left-right evaluation order
879 UnwrapLeft.Store (ec);
882 // Optimizes remaining (bool? & false), (bool? | true) which are not easy to handle
883 // in binary expression reduction
885 var c = Right as BoolConstant;
887 ec.EmitInt (or ? 1 : 0);
888 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
889 } else if (Right.IsNull) {
890 UnwrapLeft.Emit (ec);
891 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label);
893 UnwrapLeft.Load (ec);
894 ec.Emit (OpCodes.Br_S, end_label);
896 ec.MarkLabel (is_null_label);
897 LiftedNull.Create (type, loc).Emit (ec);
900 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right);
902 ec.EmitInt (or ? 1 : 0);
903 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
905 ec.Emit (OpCodes.Br_S, end_label);
907 ec.MarkLabel (load_right);
909 UnwrapLeft.Load (ec);
913 ec.MarkLabel (end_label);
917 // Emits optimized equality or inequality operator when possible
919 void EmitEquality (EmitContext ec)
922 // Either left or right is null
924 if (UnwrapLeft != null && Binary.Right.IsNull) { // TODO: Optimize for EmitBranchable
926 // left.HasValue == false
928 UnwrapLeft.EmitCheck (ec);
929 if (Binary.Oper == Binary.Operator.Equality) {
931 ec.Emit (OpCodes.Ceq);
936 if (UnwrapRight != null && Binary.Left.IsNull) {
938 // right.HasValue == false
940 UnwrapRight.EmitCheck (ec);
941 if (Binary.Oper == Binary.Operator.Equality) {
943 ec.Emit (OpCodes.Ceq);
948 Label dissimilar_label = ec.DefineLabel ();
949 Label end_label = ec.DefineLabel ();
951 if (UserOperator != null) {
954 if (UnwrapLeft != null) {
955 UnwrapLeft.EmitCheck (ec);
957 // Keep evaluation order same
958 if (!(Left is VariableReference)) {
960 var lt = new LocalTemporary (Left.Type);
966 if (UnwrapRight != null) {
967 UnwrapRight.EmitCheck (ec);
969 if (UnwrapLeft != null) {
970 ec.Emit (OpCodes.Bne_Un, dissimilar_label);
972 Label compare_label = ec.DefineLabel ();
973 UnwrapLeft.EmitCheck (ec);
974 ec.Emit (OpCodes.Brtrue, compare_label);
976 if (Binary.Oper == Binary.Operator.Equality)
981 ec.Emit (OpCodes.Br, end_label);
983 ec.MarkLabel (compare_label);
985 ec.Emit (OpCodes.Brfalse, dissimilar_label);
988 ec.Emit (OpCodes.Brfalse, dissimilar_label);
991 var args = new Arguments (2);
992 args.Add (new Argument (left));
993 args.Add (new Argument (Right));
995 var call = new CallEmitter ();
996 call.EmitPredefined (ec, UserOperator, args);
998 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) {
999 Left = Left.EmitToField (ec);
1000 Right = Right.EmitToField (ec);
1004 // Emit underlying value comparison first.
1006 // For this code: int? a = 1; bool b = a == 1;
1008 // We emit something similar to this. Expressions with side effects have local
1009 // variable created by Unwrap expression
1011 // left.GetValueOrDefault ()
1013 // bne.un.s dissimilar_label
1016 // dissimilar_label:
1024 ec.Emit (OpCodes.Bne_Un_S, dissimilar_label);
1027 // Check both left and right expressions for Unwrap call in which
1028 // case we need to run get_HasValue() check because the type is
1029 // nullable and could have null value
1031 if (UnwrapLeft != null)
1032 UnwrapLeft.EmitCheck (ec);
1034 if (UnwrapRight != null)
1035 UnwrapRight.EmitCheck (ec);
1037 if (UnwrapLeft != null && UnwrapRight != null) {
1038 if (Binary.Oper == Binary.Operator.Inequality)
1039 ec.Emit (OpCodes.Xor);
1041 ec.Emit (OpCodes.Ceq);
1043 if (Binary.Oper == Binary.Operator.Inequality) {
1045 ec.Emit (OpCodes.Ceq);
1050 ec.Emit (OpCodes.Br_S, end_label);
1052 ec.MarkLabel (dissimilar_label);
1053 if (Binary.Oper == Binary.Operator.Inequality)
1058 ec.MarkLabel (end_label);
1061 public override void FlowAnalysis (FlowAnalysisContext fc)
1063 Binary.FlowAnalysis (fc);
1066 public override SLE.Expression MakeExpression (BuilderContext ctx)
1068 return Binary.MakeExpression (ctx, Left, Right);
1072 public class NullCoalescingOperator : Expression
1074 Expression left, right;
1077 public NullCoalescingOperator (Expression left, Expression right)
1081 this.loc = left.Location;
1084 public Expression LeftExpression {
1090 public Expression RightExpression {
1096 public override Expression CreateExpressionTree (ResolveContext ec)
1098 if (left is NullLiteral)
1099 ec.Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side");
1101 UserCast uc = left as UserCast;
1102 Expression conversion = null;
1106 Arguments c_args = new Arguments (2);
1107 c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
1108 c_args.Add (new Argument (left.CreateExpressionTree (ec)));
1109 conversion = CreateExpressionFactoryCall (ec, "Lambda", c_args);
1112 Arguments args = new Arguments (3);
1113 args.Add (new Argument (left.CreateExpressionTree (ec)));
1114 args.Add (new Argument (right.CreateExpressionTree (ec)));
1115 if (conversion != null)
1116 args.Add (new Argument (conversion));
1118 return CreateExpressionFactoryCall (ec, "Coalesce", args);
1121 Expression ConvertExpression (ResolveContext ec)
1123 // TODO: ImplicitConversionExists should take care of this
1124 if (left.eclass == ExprClass.MethodGroup)
1127 TypeSpec ltype = left.Type;
1130 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
1131 // the result is underlying type of left
1133 if (ltype.IsNullableType) {
1134 unwrap = Unwrap.Create (left, false);
1139 // Reduce (left ?? null) to left
1142 return ReducedExpression.Create (left, this);
1145 if (right.Type.IsNullableType) {
1146 conv = right.Type == ltype ? right : Convert.ImplicitNulableConversion (ec, right, ltype);
1153 conv = Convert.ImplicitConversion (ec, right, unwrap.Type, loc);
1159 // If right is a dynamic expression, the result type is dynamic
1161 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1164 // Need to box underlying value type
1165 left = Convert.ImplicitBoxingConversion (left, ltype, type);
1174 } else if (TypeSpec.IsReferenceType (ltype)) {
1175 if (Convert.ImplicitConversionExists (ec, right, ltype)) {
1177 // If right is a dynamic expression, the result type is dynamic
1179 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1185 // Reduce ("foo" ?? expr) to expression
1187 Constant lc = left as Constant;
1188 if (lc != null && !lc.IsDefaultValue)
1189 return ReducedExpression.Create (lc, this, false);
1192 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1194 if (right.IsNull || lc != null) {
1196 // Special case null ?? null
1198 if (right.IsNull && ltype == right.Type)
1201 return ReducedExpression.Create (lc != null ? right : left, this, false);
1204 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1212 TypeSpec rtype = right.Type;
1213 if (!Convert.ImplicitConversionExists (ec, unwrap ?? left, rtype) || right.eclass == ExprClass.MethodGroup)
1217 // Reduce (null ?? right) to right
1220 return ReducedExpression.Create (right, this, false).Resolve (ec);
1222 left = Convert.ImplicitConversion (ec, unwrap ?? left, rtype, loc);
1227 public override bool ContainsEmitWithAwait ()
1230 return unwrap.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1232 return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1235 protected override Expression DoResolve (ResolveContext ec)
1237 left = left.Resolve (ec);
1238 right = right.Resolve (ec);
1240 if (left == null || right == null)
1243 eclass = ExprClass.Value;
1245 Expression e = ConvertExpression (ec);
1247 Binary.Error_OperatorCannotBeApplied (ec, left, right, "??", loc);
1254 public override void Emit (EmitContext ec)
1256 Label end_label = ec.DefineLabel ();
1258 if (unwrap != null) {
1259 Label is_null_label = ec.DefineLabel ();
1261 unwrap.EmitCheck (ec);
1262 ec.Emit (OpCodes.Brfalse, is_null_label);
1265 // When both expressions are nullable the unwrap
1266 // is needed only for null check not for value uwrap
1268 if (type.IsNullableType && TypeSpecComparer.IsEqual (NullableInfo.GetUnderlyingType (type), unwrap.Type))
1273 ec.Emit (OpCodes.Br, end_label);
1275 ec.MarkLabel (is_null_label);
1278 ec.MarkLabel (end_label);
1283 // Null check is done on original expression not after expression is converted to
1284 // result type. This is in most cases same but when user conversion is involved
1285 // we can end up in situation when use operator does the null handling which is
1286 // not what the operator is supposed to do
1288 var op_expr = left as UserCast;
1289 if (op_expr != null) {
1290 op_expr.Source.Emit (ec);
1291 LocalTemporary temp;
1293 // TODO: More load kinds can be special cased
1294 if (!(op_expr.Source is VariableReference)) {
1295 temp = new LocalTemporary (op_expr.Source.Type);
1298 op_expr.Source = temp;
1303 var right_label = ec.DefineLabel ();
1304 ec.Emit (OpCodes.Brfalse_S, right_label);
1306 ec.Emit (OpCodes.Br, end_label);
1307 ec.MarkLabel (right_label);
1313 // Common case where expression is not modified before null check and
1314 // we generate better/smaller code
1317 ec.Emit (OpCodes.Dup);
1319 // Only to make verifier happy
1320 if (left.Type.IsGenericParameter)
1321 ec.Emit (OpCodes.Box, left.Type);
1323 ec.Emit (OpCodes.Brtrue, end_label);
1325 ec.Emit (OpCodes.Pop);
1330 ec.MarkLabel (end_label);
1333 public override void FlowAnalysis (FlowAnalysisContext fc)
1335 left.FlowAnalysis (fc);
1336 var left_da = fc.BranchDefiniteAssignment ();
1337 right.FlowAnalysis (fc);
1338 fc.DefiniteAssignment = left_da;
1341 protected override void CloneTo (CloneContext clonectx, Expression t)
1343 NullCoalescingOperator target = (NullCoalescingOperator) t;
1345 target.left = left.Clone (clonectx);
1346 target.right = right.Clone (clonectx);
1349 public override object Accept (StructuralVisitor visitor)
1351 return visitor.Visit (this);
1355 class LiftedUnaryMutator : UnaryMutator
1357 public LiftedUnaryMutator (Mode mode, Expression expr, Location loc)
1358 : base (mode, expr, loc)
1362 protected override Expression DoResolve (ResolveContext ec)
1364 var orig_expr = expr;
1366 expr = Unwrap.Create (expr);
1368 var res = base.DoResolveOperation (ec);
1376 protected override void EmitOperation (EmitContext ec)
1378 Label is_null_label = ec.DefineLabel ();
1379 Label end_label = ec.DefineLabel ();
1381 LocalTemporary lt = new LocalTemporary (type);
1383 // Value is on the stack
1386 var call = new CallEmitter ();
1387 call.InstanceExpression = lt;
1388 call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null);
1390 ec.Emit (OpCodes.Brfalse, is_null_label);
1392 call = new CallEmitter ();
1393 call.InstanceExpression = lt;
1394 call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null);
1398 base.EmitOperation (ec);
1400 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
1401 ec.Emit (OpCodes.Br_S, end_label);
1403 ec.MarkLabel (is_null_label);
1404 LiftedNull.Create (type, loc).Emit (ec);
1406 ec.MarkLabel (end_label);