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;
699 if (Left.Type.BuiltinType == BuiltinTypeSpec.Type.Decimal) {
700 var decimal_operators = MemberCache.GetUserOperator (Left.Type, Binary.ConvertBinaryToUserOperator (Binary.Oper), false);
702 Arguments args = new Arguments (2);
703 args.Add (new Argument (Left));
704 args.Add (new Argument (Right));
706 const OverloadResolver.Restrictions restr = OverloadResolver.Restrictions.ProbingOnly |
707 OverloadResolver.Restrictions.NoBaseMembers | OverloadResolver.Restrictions.BaseMembersIncluded;
709 var res = new OverloadResolver (decimal_operators, restr, loc);
710 UserOperator = res.ResolveOperator (rc, ref args);
715 eclass = Binary.eclass;
720 Expression LiftOperand (ResolveContext rc, Expression expr)
724 type = Left.IsNull ? Right.Type : Left.Type;
729 if (!type.IsNullableType)
730 type = NullableInfo.MakeType (rc.Module, type);
732 return Wrap.Create (expr, type);
735 public override void Emit (EmitContext ec)
737 if (IsBitwiseBoolean && UserOperator == null) {
738 EmitBitwiseBoolean (ec);
742 if ((Binary.Oper & Binary.Operator.EqualityMask) != 0) {
747 Label is_null_label = ec.DefineLabel ();
748 Label end_label = ec.DefineLabel ();
750 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Right.ContainsEmitWithAwait ()) {
751 Left = Left.EmitToField (ec);
752 Right = Right.EmitToField (ec);
755 if (UnwrapLeft != null) {
756 UnwrapLeft.EmitCheck (ec);
760 // Don't emit HasValue check when left and right expressions are same
762 if (UnwrapRight != null && !Binary.Left.Equals (Binary.Right)) {
763 UnwrapRight.EmitCheck (ec);
764 if (UnwrapLeft != null) {
765 ec.Emit (OpCodes.And);
769 ec.Emit (OpCodes.Brfalse, is_null_label);
771 if (UserOperator != null) {
772 var args = new Arguments (2);
773 args.Add (new Argument (Left));
774 args.Add (new Argument (Right));
776 var call = new CallEmitter ();
777 call.EmitPredefined (ec, UserOperator, args);
779 Binary.EmitOperator (ec, Left, Right);
783 // Wrap the result when the operator return type is nullable type
785 if (type.IsNullableType)
786 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
788 ec.Emit (OpCodes.Br_S, end_label);
789 ec.MarkLabel (is_null_label);
791 if ((Binary.Oper & Binary.Operator.ComparisonMask) != 0) {
794 LiftedNull.Create (type, loc).Emit (ec);
797 ec.MarkLabel (end_label);
800 void EmitBitwiseBoolean (EmitContext ec)
802 Label load_left = ec.DefineLabel ();
803 Label load_right = ec.DefineLabel ();
804 Label end_label = ec.DefineLabel ();
805 Label is_null_label = ec.DefineLabel ();
807 bool or = Binary.Oper == Binary.Operator.BitwiseOr;
810 // Both operands are bool? types
812 if ((UnwrapLeft != null && !Left.IsNull) && (UnwrapRight != null && !Right.IsNull)) {
813 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) {
814 Left = Left.EmitToField (ec);
815 Right = Right.EmitToField (ec);
817 UnwrapLeft.Store (ec);
818 UnwrapRight.Store (ec);
822 ec.Emit (OpCodes.Brtrue_S, load_right);
825 ec.Emit (OpCodes.Brtrue_S, load_left);
827 UnwrapLeft.EmitCheck (ec);
828 ec.Emit (OpCodes.Brfalse_S, load_right);
831 ec.MarkLabel (load_left);
833 UnwrapRight.Load (ec);
835 UnwrapLeft.Load (ec);
837 ec.Emit (OpCodes.Br_S, end_label);
840 ec.MarkLabel (load_right);
842 UnwrapLeft.Load (ec);
844 UnwrapRight.Load (ec);
846 ec.MarkLabel (end_label);
851 // Faster version when one operand is bool
853 if (UnwrapLeft == null) {
857 // Optimizes remaining (false & bool?), (true | bool?) which are not easy to handle
858 // in binary expression reduction
860 var c = Left as BoolConstant;
862 // Keep evaluation order
863 UnwrapRight.Store (ec);
865 ec.EmitInt (or ? 1 : 0);
866 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
867 } else if (Left.IsNull) {
868 UnwrapRight.Emit (ec);
869 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label);
871 UnwrapRight.Load (ec);
872 ec.Emit (OpCodes.Br_S, end_label);
874 ec.MarkLabel (is_null_label);
875 LiftedNull.Create (type, loc).Emit (ec);
878 UnwrapRight.Store (ec);
880 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right);
882 ec.EmitInt (or ? 1 : 0);
883 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
885 ec.Emit (OpCodes.Br_S, end_label);
887 ec.MarkLabel (load_right);
888 UnwrapRight.Load (ec);
894 // Keep left-right evaluation order
895 UnwrapLeft.Store (ec);
898 // Optimizes remaining (bool? & false), (bool? | true) which are not easy to handle
899 // in binary expression reduction
901 var c = Right as BoolConstant;
903 ec.EmitInt (or ? 1 : 0);
904 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
905 } else if (Right.IsNull) {
906 UnwrapLeft.Emit (ec);
907 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label);
909 UnwrapLeft.Load (ec);
910 ec.Emit (OpCodes.Br_S, end_label);
912 ec.MarkLabel (is_null_label);
913 LiftedNull.Create (type, loc).Emit (ec);
914 } else if (Left.IsNull && UnwrapRight != null) {
915 UnwrapRight.Emit (ec);
917 ec.Emit (or ? OpCodes.Brtrue_S : OpCodes.Brfalse_S, load_right);
919 LiftedNull.Create (type, loc).Emit (ec);
921 ec.Emit (OpCodes.Br_S, end_label);
923 ec.MarkLabel (load_right);
925 UnwrapRight.Load (ec);
928 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_left);
930 ec.EmitInt (or ? 1 : 0);
931 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
933 ec.Emit (OpCodes.Br_S, end_label);
935 ec.MarkLabel (load_left);
937 UnwrapLeft.Load (ec);
941 ec.MarkLabel (end_label);
945 // Emits optimized equality or inequality operator when possible
947 void EmitEquality (EmitContext ec)
950 // Either left or right is null
952 if (UnwrapLeft != null && Binary.Right.IsNull) { // TODO: Optimize for EmitBranchable
954 // left.HasValue == false
956 UnwrapLeft.EmitCheck (ec);
957 if (Binary.Oper == Binary.Operator.Equality) {
959 ec.Emit (OpCodes.Ceq);
964 if (UnwrapRight != null && Binary.Left.IsNull) {
966 // right.HasValue == false
968 UnwrapRight.EmitCheck (ec);
969 if (Binary.Oper == Binary.Operator.Equality) {
971 ec.Emit (OpCodes.Ceq);
976 Label dissimilar_label = ec.DefineLabel ();
977 Label end_label = ec.DefineLabel ();
979 if (UserOperator != null) {
982 if (UnwrapLeft != null) {
983 UnwrapLeft.EmitCheck (ec);
985 // Keep evaluation order same
986 if (!(Left is VariableReference)) {
988 var lt = new LocalTemporary (Left.Type);
994 if (UnwrapRight != null) {
995 UnwrapRight.EmitCheck (ec);
997 if (UnwrapLeft != null) {
998 ec.Emit (OpCodes.Bne_Un, dissimilar_label);
1000 Label compare_label = ec.DefineLabel ();
1001 UnwrapLeft.EmitCheck (ec);
1002 ec.Emit (OpCodes.Brtrue, compare_label);
1004 if (Binary.Oper == Binary.Operator.Equality)
1009 ec.Emit (OpCodes.Br, end_label);
1011 ec.MarkLabel (compare_label);
1013 ec.Emit (OpCodes.Brfalse, dissimilar_label);
1016 ec.Emit (OpCodes.Brfalse, dissimilar_label);
1019 var args = new Arguments (2);
1020 args.Add (new Argument (left));
1021 args.Add (new Argument (Right));
1023 var call = new CallEmitter ();
1024 call.EmitPredefined (ec, UserOperator, args);
1026 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) {
1027 Left = Left.EmitToField (ec);
1028 Right = Right.EmitToField (ec);
1032 // Emit underlying value comparison first.
1034 // For this code: int? a = 1; bool b = a == 1;
1036 // We emit something similar to this. Expressions with side effects have local
1037 // variable created by Unwrap expression
1039 // left.GetValueOrDefault ()
1041 // bne.un.s dissimilar_label
1044 // dissimilar_label:
1052 ec.Emit (OpCodes.Bne_Un_S, dissimilar_label);
1055 // Check both left and right expressions for Unwrap call in which
1056 // case we need to run get_HasValue() check because the type is
1057 // nullable and could have null value
1059 if (UnwrapLeft != null)
1060 UnwrapLeft.EmitCheck (ec);
1062 if (UnwrapRight != null)
1063 UnwrapRight.EmitCheck (ec);
1065 if (UnwrapLeft != null && UnwrapRight != null) {
1066 if (Binary.Oper == Binary.Operator.Inequality)
1067 ec.Emit (OpCodes.Xor);
1069 ec.Emit (OpCodes.Ceq);
1071 if (Binary.Oper == Binary.Operator.Inequality) {
1073 ec.Emit (OpCodes.Ceq);
1078 ec.Emit (OpCodes.Br_S, end_label);
1080 ec.MarkLabel (dissimilar_label);
1081 if (Binary.Oper == Binary.Operator.Inequality)
1086 ec.MarkLabel (end_label);
1089 public override void FlowAnalysis (FlowAnalysisContext fc)
1091 Binary.FlowAnalysis (fc);
1094 public override SLE.Expression MakeExpression (BuilderContext ctx)
1096 return Binary.MakeExpression (ctx, Left, Right);
1100 public class NullCoalescingOperator : Expression
1102 Expression left, right;
1104 bool user_conversion_left;
1106 public NullCoalescingOperator (Expression left, Expression right)
1110 this.loc = left.Location;
1113 public Expression LeftExpression {
1119 public Expression RightExpression {
1125 public override Expression CreateExpressionTree (ResolveContext ec)
1127 if (left is NullLiteral)
1128 ec.Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side");
1130 UserCast uc = left as UserCast;
1131 Expression conversion = null;
1135 Arguments c_args = new Arguments (2);
1136 c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
1137 c_args.Add (new Argument (left.CreateExpressionTree (ec)));
1138 conversion = CreateExpressionFactoryCall (ec, "Lambda", c_args);
1141 Arguments args = new Arguments (3);
1142 args.Add (new Argument (left.CreateExpressionTree (ec)));
1143 args.Add (new Argument (right.CreateExpressionTree (ec)));
1144 if (conversion != null)
1145 args.Add (new Argument (conversion));
1147 return CreateExpressionFactoryCall (ec, "Coalesce", args);
1150 Expression ConvertExpression (ResolveContext ec)
1152 // TODO: ImplicitConversionExists should take care of this
1153 if (left.eclass == ExprClass.MethodGroup)
1156 TypeSpec ltype = left.Type;
1159 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
1160 // the result is underlying type of left
1162 if (ltype.IsNullableType) {
1163 unwrap = Unwrap.Create (left, false);
1168 // Reduce (left ?? null) to left
1171 return ReducedExpression.Create (left, this);
1174 if (right.Type.IsNullableType) {
1175 conv = right.Type == ltype ? right : Convert.ImplicitNulableConversion (ec, right, ltype);
1182 conv = Convert.ImplicitConversion (ec, right, unwrap.Type, loc);
1188 // If right is a dynamic expression, the result type is dynamic
1190 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1193 // Need to box underlying value type
1194 left = Convert.ImplicitBoxingConversion (left, ltype, type);
1203 } else if (TypeSpec.IsReferenceType (ltype)) {
1204 if (Convert.ImplicitConversionExists (ec, right, ltype)) {
1206 // If right is a dynamic expression, the result type is dynamic
1208 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1214 // Reduce ("foo" ?? expr) to expression
1216 Constant lc = left as Constant;
1217 if (lc != null && !lc.IsDefaultValue)
1218 return ReducedExpression.Create (lc, this, false);
1221 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1223 if (right.IsNull || lc != null) {
1225 // Special case null ?? null
1227 if (right is NullLiteral && ltype == right.Type)
1230 return ReducedExpression.Create (lc != null ? right : left, this, false);
1233 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1237 } else if (ltype == InternalType.ThrowExpr) {
1239 // LAMESPEC: I am not really sure what's point of allowing throw on left side
1241 return ReducedExpression.Create (right, this, false).Resolve (ec);
1246 TypeSpec rtype = right.Type;
1247 if (!Convert.ImplicitConversionExists (ec, unwrap ?? left, rtype) || right.eclass == ExprClass.MethodGroup)
1251 // Reduce (null ?? right) to right
1254 return ReducedExpression.Create (right, this, false).Resolve (ec);
1256 left = Convert.ImplicitConversion (ec, unwrap ?? left, rtype, loc);
1257 user_conversion_left = left is UserCast;
1262 public override bool ContainsEmitWithAwait ()
1265 return unwrap.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1267 return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1270 protected override Expression DoResolve (ResolveContext ec)
1272 left = left.Resolve (ec);
1273 right = right.Resolve (ec);
1275 if (left == null || right == null)
1278 eclass = ExprClass.Value;
1280 Expression e = ConvertExpression (ec);
1282 Binary.Error_OperatorCannotBeApplied (ec, left, right, "??", loc);
1289 public override void Emit (EmitContext ec)
1291 Label end_label = ec.DefineLabel ();
1293 if (unwrap != null) {
1294 Label is_null_label = ec.DefineLabel ();
1296 unwrap.EmitCheck (ec);
1297 ec.Emit (OpCodes.Brfalse, is_null_label);
1300 // When both expressions are nullable the unwrap
1301 // is needed only for null check not for value uwrap
1303 if (type.IsNullableType && TypeSpecComparer.IsEqual (NullableInfo.GetUnderlyingType (type), unwrap.Type))
1308 ec.Emit (OpCodes.Br, end_label);
1310 ec.MarkLabel (is_null_label);
1313 ec.MarkLabel (end_label);
1318 // Null check is done on original expression not after expression is converted to
1319 // result type. This is in most cases same but when user conversion is involved
1320 // we can end up in situation when user operator does the null handling which is
1321 // not what the operator is supposed to do.
1322 // There is tricky case where cast of left expression is meant to be cast of
1323 // whole source expression (null check is done on it) and cast from right-to-left
1324 // conversion needs to do null check on unconverted source expression.
1326 if (user_conversion_left) {
1327 var op_expr = (UserCast) left;
1329 op_expr.Source.Emit (ec);
1330 LocalTemporary temp;
1332 // TODO: More load kinds can be special cased
1333 if (!(op_expr.Source is VariableReference)) {
1334 temp = new LocalTemporary (op_expr.Source.Type);
1337 op_expr.Source = temp;
1342 var right_label = ec.DefineLabel ();
1343 ec.Emit (OpCodes.Brfalse_S, right_label);
1345 ec.Emit (OpCodes.Br, end_label);
1346 ec.MarkLabel (right_label);
1352 // Common case where expression is not modified before null check and
1353 // we generate better/smaller code
1356 ec.Emit (OpCodes.Dup);
1358 // Only to make verifier happy
1359 if (left.Type.IsGenericParameter)
1360 ec.Emit (OpCodes.Box, left.Type);
1362 ec.Emit (OpCodes.Brtrue, end_label);
1364 ec.Emit (OpCodes.Pop);
1369 ec.MarkLabel (end_label);
1372 public override void FlowAnalysis (FlowAnalysisContext fc)
1374 left.FlowAnalysis (fc);
1375 var left_da = fc.BranchDefiniteAssignment ();
1376 right.FlowAnalysis (fc);
1377 fc.DefiniteAssignment = left_da;
1380 protected override void CloneTo (CloneContext clonectx, Expression t)
1382 NullCoalescingOperator target = (NullCoalescingOperator) t;
1384 target.left = left.Clone (clonectx);
1385 target.right = right.Clone (clonectx);
1388 public override object Accept (StructuralVisitor visitor)
1390 return visitor.Visit (this);
1394 class LiftedUnaryMutator : UnaryMutator
1396 public LiftedUnaryMutator (Mode mode, Expression expr, Location loc)
1397 : base (mode, expr, loc)
1401 protected override Expression DoResolve (ResolveContext ec)
1403 var orig_expr = expr;
1405 expr = Unwrap.Create (expr);
1407 var res = base.DoResolveOperation (ec);
1415 protected override void EmitOperation (EmitContext ec)
1417 Label is_null_label = ec.DefineLabel ();
1418 Label end_label = ec.DefineLabel ();
1420 LocalTemporary lt = new LocalTemporary (type);
1422 // Value is on the stack
1425 var call = new CallEmitter ();
1426 call.InstanceExpression = lt;
1427 call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null);
1429 ec.Emit (OpCodes.Brfalse, is_null_label);
1431 call = new CallEmitter ();
1432 call.InstanceExpression = lt;
1433 call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null);
1437 base.EmitOperation (ec);
1439 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
1440 ec.Emit (OpCodes.Br_S, end_label);
1442 ec.MarkLabel (is_null_label);
1443 LiftedNull.Create (type, loc).Emit (ec);
1445 ec.MarkLabel (end_label);