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);
900 } else if (Left.IsNull && UnwrapRight != null) {
901 UnwrapRight.Emit (ec);
903 ec.Emit (or ? OpCodes.Brtrue_S : OpCodes.Brfalse_S, load_right);
905 LiftedNull.Create (type, loc).Emit (ec);
907 ec.Emit (OpCodes.Br_S, end_label);
909 ec.MarkLabel (load_right);
911 UnwrapRight.Load (ec);
914 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_left);
916 ec.EmitInt (or ? 1 : 0);
917 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
919 ec.Emit (OpCodes.Br_S, end_label);
921 ec.MarkLabel (load_left);
923 UnwrapLeft.Load (ec);
927 ec.MarkLabel (end_label);
931 // Emits optimized equality or inequality operator when possible
933 void EmitEquality (EmitContext ec)
936 // Either left or right is null
938 if (UnwrapLeft != null && Binary.Right.IsNull) { // TODO: Optimize for EmitBranchable
940 // left.HasValue == false
942 UnwrapLeft.EmitCheck (ec);
943 if (Binary.Oper == Binary.Operator.Equality) {
945 ec.Emit (OpCodes.Ceq);
950 if (UnwrapRight != null && Binary.Left.IsNull) {
952 // right.HasValue == false
954 UnwrapRight.EmitCheck (ec);
955 if (Binary.Oper == Binary.Operator.Equality) {
957 ec.Emit (OpCodes.Ceq);
962 Label dissimilar_label = ec.DefineLabel ();
963 Label end_label = ec.DefineLabel ();
965 if (UserOperator != null) {
968 if (UnwrapLeft != null) {
969 UnwrapLeft.EmitCheck (ec);
971 // Keep evaluation order same
972 if (!(Left is VariableReference)) {
974 var lt = new LocalTemporary (Left.Type);
980 if (UnwrapRight != null) {
981 UnwrapRight.EmitCheck (ec);
983 if (UnwrapLeft != null) {
984 ec.Emit (OpCodes.Bne_Un, dissimilar_label);
986 Label compare_label = ec.DefineLabel ();
987 UnwrapLeft.EmitCheck (ec);
988 ec.Emit (OpCodes.Brtrue, compare_label);
990 if (Binary.Oper == Binary.Operator.Equality)
995 ec.Emit (OpCodes.Br, end_label);
997 ec.MarkLabel (compare_label);
999 ec.Emit (OpCodes.Brfalse, dissimilar_label);
1002 ec.Emit (OpCodes.Brfalse, dissimilar_label);
1005 var args = new Arguments (2);
1006 args.Add (new Argument (left));
1007 args.Add (new Argument (Right));
1009 var call = new CallEmitter ();
1010 call.EmitPredefined (ec, UserOperator, args);
1012 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) {
1013 Left = Left.EmitToField (ec);
1014 Right = Right.EmitToField (ec);
1018 // Emit underlying value comparison first.
1020 // For this code: int? a = 1; bool b = a == 1;
1022 // We emit something similar to this. Expressions with side effects have local
1023 // variable created by Unwrap expression
1025 // left.GetValueOrDefault ()
1027 // bne.un.s dissimilar_label
1030 // dissimilar_label:
1038 ec.Emit (OpCodes.Bne_Un_S, dissimilar_label);
1041 // Check both left and right expressions for Unwrap call in which
1042 // case we need to run get_HasValue() check because the type is
1043 // nullable and could have null value
1045 if (UnwrapLeft != null)
1046 UnwrapLeft.EmitCheck (ec);
1048 if (UnwrapRight != null)
1049 UnwrapRight.EmitCheck (ec);
1051 if (UnwrapLeft != null && UnwrapRight != null) {
1052 if (Binary.Oper == Binary.Operator.Inequality)
1053 ec.Emit (OpCodes.Xor);
1055 ec.Emit (OpCodes.Ceq);
1057 if (Binary.Oper == Binary.Operator.Inequality) {
1059 ec.Emit (OpCodes.Ceq);
1064 ec.Emit (OpCodes.Br_S, end_label);
1066 ec.MarkLabel (dissimilar_label);
1067 if (Binary.Oper == Binary.Operator.Inequality)
1072 ec.MarkLabel (end_label);
1075 public override void FlowAnalysis (FlowAnalysisContext fc)
1077 Binary.FlowAnalysis (fc);
1080 public override SLE.Expression MakeExpression (BuilderContext ctx)
1082 return Binary.MakeExpression (ctx, Left, Right);
1086 public class NullCoalescingOperator : Expression
1088 Expression left, right;
1090 bool user_conversion_left;
1092 public NullCoalescingOperator (Expression left, Expression right)
1096 this.loc = left.Location;
1099 public Expression LeftExpression {
1105 public Expression RightExpression {
1111 public override Expression CreateExpressionTree (ResolveContext ec)
1113 if (left is NullLiteral)
1114 ec.Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side");
1116 UserCast uc = left as UserCast;
1117 Expression conversion = null;
1121 Arguments c_args = new Arguments (2);
1122 c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
1123 c_args.Add (new Argument (left.CreateExpressionTree (ec)));
1124 conversion = CreateExpressionFactoryCall (ec, "Lambda", c_args);
1127 Arguments args = new Arguments (3);
1128 args.Add (new Argument (left.CreateExpressionTree (ec)));
1129 args.Add (new Argument (right.CreateExpressionTree (ec)));
1130 if (conversion != null)
1131 args.Add (new Argument (conversion));
1133 return CreateExpressionFactoryCall (ec, "Coalesce", args);
1136 Expression ConvertExpression (ResolveContext ec)
1138 // TODO: ImplicitConversionExists should take care of this
1139 if (left.eclass == ExprClass.MethodGroup)
1142 TypeSpec ltype = left.Type;
1145 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
1146 // the result is underlying type of left
1148 if (ltype.IsNullableType) {
1149 unwrap = Unwrap.Create (left, false);
1154 // Reduce (left ?? null) to left
1157 return ReducedExpression.Create (left, this);
1160 if (right.Type.IsNullableType) {
1161 conv = right.Type == ltype ? right : Convert.ImplicitNulableConversion (ec, right, ltype);
1168 conv = Convert.ImplicitConversion (ec, right, unwrap.Type, loc);
1174 // If right is a dynamic expression, the result type is dynamic
1176 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1179 // Need to box underlying value type
1180 left = Convert.ImplicitBoxingConversion (left, ltype, type);
1189 } else if (TypeSpec.IsReferenceType (ltype)) {
1190 if (Convert.ImplicitConversionExists (ec, right, ltype)) {
1192 // If right is a dynamic expression, the result type is dynamic
1194 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1200 // Reduce ("foo" ?? expr) to expression
1202 Constant lc = left as Constant;
1203 if (lc != null && !lc.IsDefaultValue)
1204 return ReducedExpression.Create (lc, this, false);
1207 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1209 if (right.IsNull || lc != null) {
1211 // Special case null ?? null
1213 if (right.IsNull && ltype == right.Type)
1216 return ReducedExpression.Create (lc != null ? right : left, this, false);
1219 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1227 TypeSpec rtype = right.Type;
1228 if (!Convert.ImplicitConversionExists (ec, unwrap ?? left, rtype) || right.eclass == ExprClass.MethodGroup)
1232 // Reduce (null ?? right) to right
1235 return ReducedExpression.Create (right, this, false).Resolve (ec);
1237 left = Convert.ImplicitConversion (ec, unwrap ?? left, rtype, loc);
1238 user_conversion_left = left is UserCast;
1243 public override bool ContainsEmitWithAwait ()
1246 return unwrap.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1248 return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1251 protected override Expression DoResolve (ResolveContext ec)
1253 left = left.Resolve (ec);
1254 right = right.Resolve (ec);
1256 if (left == null || right == null)
1259 eclass = ExprClass.Value;
1261 Expression e = ConvertExpression (ec);
1263 Binary.Error_OperatorCannotBeApplied (ec, left, right, "??", loc);
1270 public override void Emit (EmitContext ec)
1272 Label end_label = ec.DefineLabel ();
1274 if (unwrap != null) {
1275 Label is_null_label = ec.DefineLabel ();
1277 unwrap.EmitCheck (ec);
1278 ec.Emit (OpCodes.Brfalse, is_null_label);
1281 // When both expressions are nullable the unwrap
1282 // is needed only for null check not for value uwrap
1284 if (type.IsNullableType && TypeSpecComparer.IsEqual (NullableInfo.GetUnderlyingType (type), unwrap.Type))
1289 ec.Emit (OpCodes.Br, end_label);
1291 ec.MarkLabel (is_null_label);
1294 ec.MarkLabel (end_label);
1299 // Null check is done on original expression not after expression is converted to
1300 // result type. This is in most cases same but when user conversion is involved
1301 // we can end up in situation when user operator does the null handling which is
1302 // not what the operator is supposed to do.
1303 // There is tricky case where cast of left expression is meant to be cast of
1304 // whole source expression (null check is done on it) and cast from right-to-left
1305 // conversion needs to do null check on unconverted source expression.
1307 if (user_conversion_left) {
1308 var op_expr = (UserCast) left;
1310 op_expr.Source.Emit (ec);
1311 LocalTemporary temp;
1313 // TODO: More load kinds can be special cased
1314 if (!(op_expr.Source is VariableReference)) {
1315 temp = new LocalTemporary (op_expr.Source.Type);
1318 op_expr.Source = temp;
1323 var right_label = ec.DefineLabel ();
1324 ec.Emit (OpCodes.Brfalse_S, right_label);
1326 ec.Emit (OpCodes.Br, end_label);
1327 ec.MarkLabel (right_label);
1333 // Common case where expression is not modified before null check and
1334 // we generate better/smaller code
1337 ec.Emit (OpCodes.Dup);
1339 // Only to make verifier happy
1340 if (left.Type.IsGenericParameter)
1341 ec.Emit (OpCodes.Box, left.Type);
1343 ec.Emit (OpCodes.Brtrue, end_label);
1345 ec.Emit (OpCodes.Pop);
1350 ec.MarkLabel (end_label);
1353 public override void FlowAnalysis (FlowAnalysisContext fc)
1355 left.FlowAnalysis (fc);
1356 var left_da = fc.BranchDefiniteAssignment ();
1357 right.FlowAnalysis (fc);
1358 fc.DefiniteAssignment = left_da;
1361 protected override void CloneTo (CloneContext clonectx, Expression t)
1363 NullCoalescingOperator target = (NullCoalescingOperator) t;
1365 target.left = left.Clone (clonectx);
1366 target.right = right.Clone (clonectx);
1369 public override object Accept (StructuralVisitor visitor)
1371 return visitor.Visit (this);
1375 class LiftedUnaryMutator : UnaryMutator
1377 public LiftedUnaryMutator (Mode mode, Expression expr, Location loc)
1378 : base (mode, expr, loc)
1382 protected override Expression DoResolve (ResolveContext ec)
1384 var orig_expr = expr;
1386 expr = Unwrap.Create (expr);
1388 var res = base.DoResolveOperation (ec);
1396 protected override void EmitOperation (EmitContext ec)
1398 Label is_null_label = ec.DefineLabel ();
1399 Label end_label = ec.DefineLabel ();
1401 LocalTemporary lt = new LocalTemporary (type);
1403 // Value is on the stack
1406 var call = new CallEmitter ();
1407 call.InstanceExpression = lt;
1408 call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null);
1410 ec.Emit (OpCodes.Brfalse, is_null_label);
1412 call = new CallEmitter ();
1413 call.InstanceExpression = lt;
1414 call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null);
1418 base.EmitOperation (ec);
1420 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
1421 ec.Emit (OpCodes.Br_S, end_label);
1423 ec.MarkLabel (is_null_label);
1424 LiftedNull.Create (type, loc).Emit (ec);
1426 ec.MarkLabel (end_label);