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 && !Left.IsNull) && (UnwrapRight != null && !Right.IsNull)) {
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 UnwrapRight.Store (ec);
866 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right);
868 ec.EmitInt (or ? 1 : 0);
869 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
871 ec.Emit (OpCodes.Br_S, end_label);
873 ec.MarkLabel (load_right);
874 UnwrapRight.Load (ec);
880 // Keep left-right evaluation order
881 UnwrapLeft.Store (ec);
884 // Optimizes remaining (bool? & false), (bool? | true) which are not easy to handle
885 // in binary expression reduction
887 var c = Right as BoolConstant;
889 ec.EmitInt (or ? 1 : 0);
890 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
891 } else if (Right.IsNull) {
892 UnwrapLeft.Emit (ec);
893 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label);
895 UnwrapLeft.Load (ec);
896 ec.Emit (OpCodes.Br_S, end_label);
898 ec.MarkLabel (is_null_label);
899 LiftedNull.Create (type, loc).Emit (ec);
902 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_left);
904 ec.EmitInt (or ? 1 : 0);
905 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
907 ec.Emit (OpCodes.Br_S, end_label);
909 ec.MarkLabel (load_left);
911 UnwrapLeft.Load (ec);
915 ec.MarkLabel (end_label);
919 // Emits optimized equality or inequality operator when possible
921 void EmitEquality (EmitContext ec)
924 // Either left or right is null
926 if (UnwrapLeft != null && Binary.Right.IsNull) { // TODO: Optimize for EmitBranchable
928 // left.HasValue == false
930 UnwrapLeft.EmitCheck (ec);
931 if (Binary.Oper == Binary.Operator.Equality) {
933 ec.Emit (OpCodes.Ceq);
938 if (UnwrapRight != null && Binary.Left.IsNull) {
940 // right.HasValue == false
942 UnwrapRight.EmitCheck (ec);
943 if (Binary.Oper == Binary.Operator.Equality) {
945 ec.Emit (OpCodes.Ceq);
950 Label dissimilar_label = ec.DefineLabel ();
951 Label end_label = ec.DefineLabel ();
953 if (UserOperator != null) {
956 if (UnwrapLeft != null) {
957 UnwrapLeft.EmitCheck (ec);
959 // Keep evaluation order same
960 if (!(Left is VariableReference)) {
962 var lt = new LocalTemporary (Left.Type);
968 if (UnwrapRight != null) {
969 UnwrapRight.EmitCheck (ec);
971 if (UnwrapLeft != null) {
972 ec.Emit (OpCodes.Bne_Un, dissimilar_label);
974 Label compare_label = ec.DefineLabel ();
975 UnwrapLeft.EmitCheck (ec);
976 ec.Emit (OpCodes.Brtrue, compare_label);
978 if (Binary.Oper == Binary.Operator.Equality)
983 ec.Emit (OpCodes.Br, end_label);
985 ec.MarkLabel (compare_label);
987 ec.Emit (OpCodes.Brfalse, dissimilar_label);
990 ec.Emit (OpCodes.Brfalse, dissimilar_label);
993 var args = new Arguments (2);
994 args.Add (new Argument (left));
995 args.Add (new Argument (Right));
997 var call = new CallEmitter ();
998 call.EmitPredefined (ec, UserOperator, args);
1000 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) {
1001 Left = Left.EmitToField (ec);
1002 Right = Right.EmitToField (ec);
1006 // Emit underlying value comparison first.
1008 // For this code: int? a = 1; bool b = a == 1;
1010 // We emit something similar to this. Expressions with side effects have local
1011 // variable created by Unwrap expression
1013 // left.GetValueOrDefault ()
1015 // bne.un.s dissimilar_label
1018 // dissimilar_label:
1026 ec.Emit (OpCodes.Bne_Un_S, dissimilar_label);
1029 // Check both left and right expressions for Unwrap call in which
1030 // case we need to run get_HasValue() check because the type is
1031 // nullable and could have null value
1033 if (UnwrapLeft != null)
1034 UnwrapLeft.EmitCheck (ec);
1036 if (UnwrapRight != null)
1037 UnwrapRight.EmitCheck (ec);
1039 if (UnwrapLeft != null && UnwrapRight != null) {
1040 if (Binary.Oper == Binary.Operator.Inequality)
1041 ec.Emit (OpCodes.Xor);
1043 ec.Emit (OpCodes.Ceq);
1045 if (Binary.Oper == Binary.Operator.Inequality) {
1047 ec.Emit (OpCodes.Ceq);
1052 ec.Emit (OpCodes.Br_S, end_label);
1054 ec.MarkLabel (dissimilar_label);
1055 if (Binary.Oper == Binary.Operator.Inequality)
1060 ec.MarkLabel (end_label);
1063 public override void FlowAnalysis (FlowAnalysisContext fc)
1065 Binary.FlowAnalysis (fc);
1068 public override SLE.Expression MakeExpression (BuilderContext ctx)
1070 return Binary.MakeExpression (ctx, Left, Right);
1074 public class NullCoalescingOperator : Expression
1076 Expression left, right;
1078 bool user_conversion_left;
1080 public NullCoalescingOperator (Expression left, Expression right)
1084 this.loc = left.Location;
1087 public Expression LeftExpression {
1093 public Expression RightExpression {
1099 public override Expression CreateExpressionTree (ResolveContext ec)
1101 if (left is NullLiteral)
1102 ec.Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side");
1104 UserCast uc = left as UserCast;
1105 Expression conversion = null;
1109 Arguments c_args = new Arguments (2);
1110 c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
1111 c_args.Add (new Argument (left.CreateExpressionTree (ec)));
1112 conversion = CreateExpressionFactoryCall (ec, "Lambda", c_args);
1115 Arguments args = new Arguments (3);
1116 args.Add (new Argument (left.CreateExpressionTree (ec)));
1117 args.Add (new Argument (right.CreateExpressionTree (ec)));
1118 if (conversion != null)
1119 args.Add (new Argument (conversion));
1121 return CreateExpressionFactoryCall (ec, "Coalesce", args);
1124 Expression ConvertExpression (ResolveContext ec)
1126 // TODO: ImplicitConversionExists should take care of this
1127 if (left.eclass == ExprClass.MethodGroup)
1130 TypeSpec ltype = left.Type;
1133 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
1134 // the result is underlying type of left
1136 if (ltype.IsNullableType) {
1137 unwrap = Unwrap.Create (left, false);
1142 // Reduce (left ?? null) to left
1145 return ReducedExpression.Create (left, this);
1148 if (right.Type.IsNullableType) {
1149 conv = right.Type == ltype ? right : Convert.ImplicitNulableConversion (ec, right, ltype);
1156 conv = Convert.ImplicitConversion (ec, right, unwrap.Type, loc);
1162 // If right is a dynamic expression, the result type is dynamic
1164 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1167 // Need to box underlying value type
1168 left = Convert.ImplicitBoxingConversion (left, ltype, type);
1177 } else if (TypeSpec.IsReferenceType (ltype)) {
1178 if (Convert.ImplicitConversionExists (ec, right, ltype)) {
1180 // If right is a dynamic expression, the result type is dynamic
1182 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1188 // Reduce ("foo" ?? expr) to expression
1190 Constant lc = left as Constant;
1191 if (lc != null && !lc.IsDefaultValue)
1192 return ReducedExpression.Create (lc, this, false);
1195 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1197 if (right.IsNull || lc != null) {
1199 // Special case null ?? null
1201 if (right.IsNull && ltype == right.Type)
1204 return ReducedExpression.Create (lc != null ? right : left, this, false);
1207 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1215 TypeSpec rtype = right.Type;
1216 if (!Convert.ImplicitConversionExists (ec, unwrap ?? left, rtype) || right.eclass == ExprClass.MethodGroup)
1220 // Reduce (null ?? right) to right
1223 return ReducedExpression.Create (right, this, false).Resolve (ec);
1225 left = Convert.ImplicitConversion (ec, unwrap ?? left, rtype, loc);
1226 user_conversion_left = left is UserCast;
1231 public override bool ContainsEmitWithAwait ()
1234 return unwrap.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1236 return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1239 protected override Expression DoResolve (ResolveContext ec)
1241 left = left.Resolve (ec);
1242 right = right.Resolve (ec);
1244 if (left == null || right == null)
1247 eclass = ExprClass.Value;
1249 Expression e = ConvertExpression (ec);
1251 Binary.Error_OperatorCannotBeApplied (ec, left, right, "??", loc);
1258 public override void Emit (EmitContext ec)
1260 Label end_label = ec.DefineLabel ();
1262 if (unwrap != null) {
1263 Label is_null_label = ec.DefineLabel ();
1265 unwrap.EmitCheck (ec);
1266 ec.Emit (OpCodes.Brfalse, is_null_label);
1269 // When both expressions are nullable the unwrap
1270 // is needed only for null check not for value uwrap
1272 if (type.IsNullableType && TypeSpecComparer.IsEqual (NullableInfo.GetUnderlyingType (type), unwrap.Type))
1277 ec.Emit (OpCodes.Br, end_label);
1279 ec.MarkLabel (is_null_label);
1282 ec.MarkLabel (end_label);
1287 // Null check is done on original expression not after expression is converted to
1288 // result type. This is in most cases same but when user conversion is involved
1289 // we can end up in situation when user operator does the null handling which is
1290 // not what the operator is supposed to do.
1291 // There is tricky case where cast of left expression is meant to be cast of
1292 // whole source expression (null check is done on it) and cast from right-to-left
1293 // conversion needs to do null check on unconverted source expression.
1295 if (user_conversion_left) {
1296 var op_expr = (UserCast) left;
1298 op_expr.Source.Emit (ec);
1299 LocalTemporary temp;
1301 // TODO: More load kinds can be special cased
1302 if (!(op_expr.Source is VariableReference)) {
1303 temp = new LocalTemporary (op_expr.Source.Type);
1306 op_expr.Source = temp;
1311 var right_label = ec.DefineLabel ();
1312 ec.Emit (OpCodes.Brfalse_S, right_label);
1314 ec.Emit (OpCodes.Br, end_label);
1315 ec.MarkLabel (right_label);
1321 // Common case where expression is not modified before null check and
1322 // we generate better/smaller code
1325 ec.Emit (OpCodes.Dup);
1327 // Only to make verifier happy
1328 if (left.Type.IsGenericParameter)
1329 ec.Emit (OpCodes.Box, left.Type);
1331 ec.Emit (OpCodes.Brtrue, end_label);
1333 ec.Emit (OpCodes.Pop);
1338 ec.MarkLabel (end_label);
1341 public override void FlowAnalysis (FlowAnalysisContext fc)
1343 left.FlowAnalysis (fc);
1344 var left_da = fc.BranchDefiniteAssignment ();
1345 right.FlowAnalysis (fc);
1346 fc.DefiniteAssignment = left_da;
1349 protected override void CloneTo (CloneContext clonectx, Expression t)
1351 NullCoalescingOperator target = (NullCoalescingOperator) t;
1353 target.left = left.Clone (clonectx);
1354 target.right = right.Clone (clonectx);
1357 public override object Accept (StructuralVisitor visitor)
1359 return visitor.Visit (this);
1363 class LiftedUnaryMutator : UnaryMutator
1365 public LiftedUnaryMutator (Mode mode, Expression expr, Location loc)
1366 : base (mode, expr, loc)
1370 protected override Expression DoResolve (ResolveContext ec)
1372 var orig_expr = expr;
1374 expr = Unwrap.Create (expr);
1376 var res = base.DoResolveOperation (ec);
1384 protected override void EmitOperation (EmitContext ec)
1386 Label is_null_label = ec.DefineLabel ();
1387 Label end_label = ec.DefineLabel ();
1389 LocalTemporary lt = new LocalTemporary (type);
1391 // Value is on the stack
1394 var call = new CallEmitter ();
1395 call.InstanceExpression = lt;
1396 call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null);
1398 ec.Emit (OpCodes.Brfalse, is_null_label);
1400 call = new CallEmitter ();
1401 call.InstanceExpression = lt;
1402 call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null);
1406 base.EmitOperation (ec);
1408 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
1409 ec.Emit (OpCodes.Br_S, end_label);
1411 ec.MarkLabel (is_null_label);
1412 LiftedNull.Create (type, loc).Emit (ec);
1414 ec.MarkLabel (end_label);