2 // nullable.cs: Nullable types support
4 // Authors: Martin Baulig (martin@ximian.com)
5 // Miguel de Icaza (miguel@ximian.com)
6 // Marek Safar (marek.safar@gmail.com)
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
10 // Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
11 // Copyright 2004-2008 Novell, Inc
12 // Copyright 2011 Xamarin Inc
16 using SLE = System.Linq.Expressions;
19 using IKVM.Reflection.Emit;
21 using System.Reflection.Emit;
24 namespace Mono.CSharp.Nullable
26 public class NullableType : TypeExpr
28 readonly TypeSpec underlying;
30 public NullableType (TypeSpec type, Location loc)
32 this.underlying = type;
36 public override TypeSpec ResolveAsType (IMemberContext ec)
38 eclass = ExprClass.Type;
40 var otype = ec.Module.PredefinedTypes.Nullable.Resolve ();
44 TypeArguments args = new TypeArguments (new TypeExpression (underlying, loc));
45 GenericTypeExpr ctype = new GenericTypeExpr (otype, args, loc);
47 type = ctype.ResolveAsType (ec);
52 static class NullableInfo
54 public static MethodSpec GetConstructor (TypeSpec nullableType)
56 return (MethodSpec) MemberCache.FindMember (nullableType,
57 MemberFilter.Constructor (ParametersCompiled.CreateFullyResolved (GetUnderlyingType (nullableType))), BindingRestriction.DeclaredOnly);
60 public static MethodSpec GetHasValue (TypeSpec nullableType)
62 return (MethodSpec) MemberCache.FindMember (nullableType,
63 MemberFilter.Method ("get_HasValue", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None);
66 public static MethodSpec GetGetValueOrDefault (TypeSpec nullableType)
68 return (MethodSpec) MemberCache.FindMember (nullableType,
69 MemberFilter.Method ("GetValueOrDefault", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None);
73 // Don't use unless really required for correctness, see Unwrap::Emit
75 public static MethodSpec GetValue (TypeSpec nullableType)
77 return (MethodSpec) MemberCache.FindMember (nullableType,
78 MemberFilter.Method ("get_Value", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None);
81 public static TypeSpec GetUnderlyingType (TypeSpec nullableType)
83 return ((InflatedTypeSpec) nullableType).TypeArguments[0];
86 public static TypeSpec GetEnumUnderlyingType (ModuleContainer module, TypeSpec nullableEnum)
88 return module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (module,
89 new[] { EnumSpec.GetUnderlyingType (GetUnderlyingType (nullableEnum)) });
93 public class Unwrap : Expression, IMemoryLocation
98 Expression temp_field;
99 readonly bool useDefaultValue;
101 public Unwrap (Expression expr, bool useDefaultValue = true)
104 this.loc = expr.Location;
105 this.useDefaultValue = useDefaultValue;
107 type = NullableInfo.GetUnderlyingType (expr.Type);
108 eclass = expr.eclass;
111 public override bool ContainsEmitWithAwait ()
113 return expr.ContainsEmitWithAwait ();
117 public static Expression Create (Expression expr)
120 // Avoid unwraping and wraping of same type
122 Wrap wrap = expr as Wrap;
126 return Create (expr, false);
129 public static Expression CreateUnwrapped (Expression expr)
132 // Avoid unwraping and wraping of same type
134 Wrap wrap = expr as Wrap;
138 return Create (expr, true);
141 public static Unwrap Create (Expression expr, bool useDefaultValue)
143 return new Unwrap (expr, useDefaultValue);
146 public override Expression CreateExpressionTree (ResolveContext ec)
148 return expr.CreateExpressionTree (ec);
151 protected override Expression DoResolve (ResolveContext ec)
156 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
158 expr = expr.DoResolveLValue (ec, right_side);
162 public override void Emit (EmitContext ec)
166 var call = new CallEmitter ();
167 call.InstanceExpression = this;
170 // Using GetGetValueOrDefault is prefered because JIT can possibly
171 // inline it whereas Value property contains a throw which is very
172 // unlikely to be inlined
175 call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null);
177 call.EmitPredefined (ec, NullableInfo.GetValue (expr.Type), null);
180 public void EmitCheck (EmitContext ec)
184 var call = new CallEmitter ();
185 call.InstanceExpression = this;
187 call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null);
190 public override Expression EmitToField (EmitContext ec)
192 if (temp_field == null)
193 temp_field = this.expr.EmitToField (ec);
198 public override bool Equals (object obj)
200 Unwrap uw = obj as Unwrap;
201 return uw != null && expr.Equals (uw.expr);
204 public override void FlowAnalysis (FlowAnalysisContext fc)
206 expr.FlowAnalysis (fc);
209 public Expression Original {
215 public override int GetHashCode ()
217 return expr.GetHashCode ();
220 public override bool IsNull {
226 public void Store (EmitContext ec)
228 if (temp != null || temp_field != null)
231 if (expr is VariableReference)
235 LocalVariable.Store (ec);
238 public void Load (EmitContext ec)
240 if (temp_field != null)
241 temp_field.Emit (ec);
242 else if (expr is VariableReference)
245 LocalVariable.Emit (ec);
248 public override SLE.Expression MakeExpression (BuilderContext ctx)
250 return expr.MakeExpression (ctx);
253 public void AddressOf (EmitContext ec, AddressOp mode)
257 if (temp_field != null) {
258 ml = temp_field as IMemoryLocation;
260 var lt = new LocalTemporary (temp_field.Type);
261 temp_field.Emit (ec);
266 ml = expr as VariableReference;
270 ml.AddressOf (ec, mode);
272 LocalVariable.AddressOf (ec, mode);
276 // Keeps result of non-variable expression
278 LocalTemporary LocalVariable {
280 if (temp == null && temp_field == null)
281 temp = new LocalTemporary (expr.Type);
288 // Calls get_Value method on nullable expression
290 public class UnwrapCall : CompositeExpression
292 public UnwrapCall (Expression expr)
297 protected override Expression DoResolve (ResolveContext rc)
302 type = NullableInfo.GetUnderlyingType (type);
307 public override void Emit (EmitContext ec)
309 var call = new CallEmitter ();
310 call.InstanceExpression = Child;
311 call.EmitPredefined (ec, NullableInfo.GetValue (Child.Type), null);
315 public class Wrap : TypeCast
317 private Wrap (Expression expr, TypeSpec type)
320 eclass = ExprClass.Value;
323 public override Expression CreateExpressionTree (ResolveContext ec)
325 TypeCast child_cast = child as TypeCast;
326 if (child_cast != null) {
328 return child_cast.CreateExpressionTree (ec);
331 var user_cast = child as UserCast;
332 if (user_cast != null) {
334 return user_cast.CreateExpressionTree (ec);
337 return base.CreateExpressionTree (ec);
340 public static Expression Create (Expression expr, TypeSpec type)
343 // Avoid unwraping and wraping of the same type
345 Unwrap unwrap = expr as Unwrap;
346 if (unwrap != null && expr.Type == NullableInfo.GetUnderlyingType (type))
347 return unwrap.Original;
349 return new Wrap (expr, type);
352 public override void Emit (EmitContext ec)
355 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
360 // Represents null literal lifted to nullable type
362 public class LiftedNull : NullConstant, IMemoryLocation
364 private LiftedNull (TypeSpec nullable_type, Location loc)
365 : base (nullable_type, loc)
367 eclass = ExprClass.Value;
370 public static Constant Create (TypeSpec nullable, Location loc)
372 return new LiftedNull (nullable, loc);
375 public static Constant CreateFromExpression (ResolveContext rc, Expression e)
377 if (!rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) {
378 rc.Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'",
379 e.Type.GetSignatureForError ());
382 return ReducedExpression.Create (Create (e.Type, e.Location), e);
385 public override void Emit (EmitContext ec)
387 // TODO: generate less temporary variables
388 LocalTemporary value_target = new LocalTemporary (type);
390 value_target.AddressOf (ec, AddressOp.Store);
391 ec.Emit (OpCodes.Initobj, type);
392 value_target.Emit (ec);
393 value_target.Release (ec);
396 public void AddressOf (EmitContext ec, AddressOp Mode)
398 LocalTemporary value_target = new LocalTemporary (type);
400 value_target.AddressOf (ec, AddressOp.Store);
401 ec.Emit (OpCodes.Initobj, type);
402 value_target.AddressOf (ec, Mode);
407 // Generic lifting expression, supports all S/S? -> T/T? cases
409 public class LiftedConversion : Expression, IMemoryLocation
411 Expression expr, null_value;
414 public LiftedConversion (Expression expr, Unwrap unwrap, TypeSpec type)
417 this.unwrap = unwrap;
418 this.loc = expr.Location;
422 public LiftedConversion (Expression expr, Expression unwrap, TypeSpec type)
423 : this (expr, unwrap as Unwrap, type)
427 public override bool ContainsEmitWithAwait ()
429 return unwrap.ContainsEmitWithAwait ();
432 public override Expression CreateExpressionTree (ResolveContext ec)
434 return expr.CreateExpressionTree (ec);
437 protected override Expression DoResolve (ResolveContext ec)
440 // It's null when lifting non-nullable type
442 if (unwrap == null) {
443 // S -> T? is wrap only
444 if (type.IsNullableType)
445 return Wrap.Create (expr, type);
447 // S -> T can be simplified
451 // Wrap target for T?
452 if (type.IsNullableType) {
453 if (!expr.Type.IsNullableType) {
454 expr = Wrap.Create (expr, type);
459 null_value = LiftedNull.Create (type, loc);
460 } else if (TypeSpec.IsValueType (type)) {
461 null_value = LiftedNull.Create (type, loc);
463 null_value = new NullConstant (type, loc);
466 eclass = ExprClass.Value;
470 public override void Emit (EmitContext ec)
472 Label is_null_label = ec.DefineLabel ();
473 Label end_label = ec.DefineLabel ();
475 unwrap.EmitCheck (ec);
476 ec.Emit (OpCodes.Brfalse, is_null_label);
480 ec.Emit (OpCodes.Br, end_label);
481 ec.MarkLabel (is_null_label);
483 null_value.Emit (ec);
485 ec.MarkLabel (end_label);
488 public override void FlowAnalysis (FlowAnalysisContext fc)
490 expr.FlowAnalysis (fc);
493 public void AddressOf (EmitContext ec, AddressOp mode)
495 unwrap.AddressOf (ec, mode);
499 public class LiftedUnaryOperator : Unary, IMemoryLocation
502 Expression user_operator;
504 public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc)
505 : base (op, expr, loc)
509 public void AddressOf (EmitContext ec, AddressOp mode)
511 unwrap.AddressOf (ec, mode);
514 public override Expression CreateExpressionTree (ResolveContext ec)
516 if (user_operator != null)
517 return user_operator.CreateExpressionTree (ec);
519 if (Oper == Operator.UnaryPlus)
520 return Expr.CreateExpressionTree (ec);
522 return base.CreateExpressionTree (ec);
525 protected override Expression DoResolve (ResolveContext ec)
527 unwrap = Unwrap.Create (Expr, false);
531 Expression res = base.ResolveOperator (ec, unwrap);
533 Error_OperatorCannotBeApplied (ec, loc, OperName (Oper), Expr.Type);
538 if (user_operator == null)
541 res = Expr = LiftExpression (ec, Expr);
547 eclass = ExprClass.Value;
552 public override void Emit (EmitContext ec)
554 Label is_null_label = ec.DefineLabel ();
555 Label end_label = ec.DefineLabel ();
557 unwrap.EmitCheck (ec);
558 ec.Emit (OpCodes.Brfalse, is_null_label);
560 if (user_operator != null) {
561 user_operator.Emit (ec);
563 EmitOperator (ec, NullableInfo.GetUnderlyingType (type));
566 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
567 ec.Emit (OpCodes.Br_S, end_label);
569 ec.MarkLabel (is_null_label);
570 LiftedNull.Create (type, loc).Emit (ec);
572 ec.MarkLabel (end_label);
575 static Expression LiftExpression (ResolveContext ec, Expression expr)
577 var lifted_type = new NullableType (expr.Type, expr.Location);
578 if (lifted_type.ResolveAsType (ec) == null)
581 expr.Type = lifted_type.Type;
585 protected override Expression ResolveEnumOperator (ResolveContext ec, Expression expr, TypeSpec[] predefined)
587 expr = base.ResolveEnumOperator (ec, expr, predefined);
591 Expr = LiftExpression (ec, Expr);
592 return LiftExpression (ec, expr);
595 protected override Expression ResolveUserOperator (ResolveContext ec, Expression expr)
597 expr = base.ResolveUserOperator (ec, expr);
602 // When a user operator is of non-nullable type
604 if (Expr is Unwrap) {
605 user_operator = LiftExpression (ec, expr);
606 return user_operator;
614 // Lifted version of binary operators
616 class LiftedBinaryOperator : Expression
618 public LiftedBinaryOperator (Binary b)
621 this.loc = b.Location;
624 public Binary Binary { get; private set; }
626 public Expression Left { get; set; }
628 public Expression Right { get; set; }
630 public Unwrap UnwrapLeft { get; set; }
632 public Unwrap UnwrapRight { get; set; }
634 public MethodSpec UserOperator { get; set; }
636 bool IsBitwiseBoolean {
638 return (Binary.Oper == Binary.Operator.BitwiseAnd || Binary.Oper == Binary.Operator.BitwiseOr) &&
639 ((UnwrapLeft != null && UnwrapLeft.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) ||
640 (UnwrapRight != null && UnwrapRight.Type.BuiltinType == BuiltinTypeSpec.Type.Bool));
644 public override bool ContainsEmitWithAwait ()
646 return Left.ContainsEmitWithAwait () || Right.ContainsEmitWithAwait ();
649 public override Expression CreateExpressionTree (ResolveContext rc)
651 if (UserOperator != null) {
652 Arguments args = new Arguments (2);
653 args.Add (new Argument (Binary.Left));
654 args.Add (new Argument (Binary.Right));
656 var method = new UserOperatorCall (UserOperator, args, Binary.CreateExpressionTree, loc);
657 return method.CreateExpressionTree (rc);
660 return Binary.CreateExpressionTree (rc);
663 protected override Expression DoResolve (ResolveContext rc)
665 if (rc.IsRuntimeBinder) {
666 if (UnwrapLeft == null && !Left.Type.IsNullableType)
667 Left = LiftOperand (rc, Left);
669 if (UnwrapRight == null && !Right.Type.IsNullableType)
670 Right = LiftOperand (rc, Right);
672 if (UnwrapLeft == null && Left != null && Left.Type.IsNullableType) {
673 Left = Unwrap.CreateUnwrapped (Left);
674 UnwrapLeft = Left as Unwrap;
677 if (UnwrapRight == null && Right != null && Right.Type.IsNullableType) {
678 Right = Unwrap.CreateUnwrapped (Right);
679 UnwrapRight = Right as Unwrap;
684 eclass = Binary.eclass;
689 Expression LiftOperand (ResolveContext rc, Expression expr)
693 type = Left.IsNull ? Right.Type : Left.Type;
698 if (!type.IsNullableType)
699 type = rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { type });
701 return Wrap.Create (expr, type);
704 public override void Emit (EmitContext ec)
706 if (IsBitwiseBoolean && UserOperator == null) {
707 EmitBitwiseBoolean (ec);
711 if ((Binary.Oper & Binary.Operator.EqualityMask) != 0) {
716 Label is_null_label = ec.DefineLabel ();
717 Label end_label = ec.DefineLabel ();
719 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Right.ContainsEmitWithAwait ()) {
720 Left = Left.EmitToField (ec);
721 Right = Right.EmitToField (ec);
724 if (UnwrapLeft != null) {
725 UnwrapLeft.EmitCheck (ec);
729 // Don't emit HasValue check when left and right expressions are same
731 if (UnwrapRight != null && !Binary.Left.Equals (Binary.Right)) {
732 UnwrapRight.EmitCheck (ec);
733 if (UnwrapLeft != null) {
734 ec.Emit (OpCodes.And);
738 ec.Emit (OpCodes.Brfalse, is_null_label);
740 if (UserOperator != null) {
741 var args = new Arguments (2);
742 args.Add (new Argument (Left));
743 args.Add (new Argument (Right));
745 var call = new CallEmitter ();
746 call.EmitPredefined (ec, UserOperator, args);
748 Binary.EmitOperator (ec, Left, Right);
752 // Wrap the result when the operator return type is nullable type
754 if (type.IsNullableType)
755 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
757 ec.Emit (OpCodes.Br_S, end_label);
758 ec.MarkLabel (is_null_label);
760 if ((Binary.Oper & Binary.Operator.ComparisonMask) != 0) {
763 LiftedNull.Create (type, loc).Emit (ec);
766 ec.MarkLabel (end_label);
769 void EmitBitwiseBoolean (EmitContext ec)
771 Label load_left = ec.DefineLabel ();
772 Label load_right = ec.DefineLabel ();
773 Label end_label = ec.DefineLabel ();
774 Label is_null_label = ec.DefineLabel ();
776 bool or = Binary.Oper == Binary.Operator.BitwiseOr;
779 // Both operands are bool? types
781 if (UnwrapLeft != null && UnwrapRight != null) {
782 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) {
783 Left = Left.EmitToField (ec);
784 Right = Right.EmitToField (ec);
786 UnwrapLeft.Store (ec);
787 UnwrapRight.Store (ec);
791 ec.Emit (OpCodes.Brtrue_S, load_right);
794 ec.Emit (OpCodes.Brtrue_S, load_left);
796 UnwrapLeft.EmitCheck (ec);
797 ec.Emit (OpCodes.Brfalse_S, load_right);
800 ec.MarkLabel (load_left);
802 UnwrapRight.Load (ec);
804 UnwrapLeft.Load (ec);
806 ec.Emit (OpCodes.Br_S, end_label);
809 ec.MarkLabel (load_right);
811 UnwrapLeft.Load (ec);
813 UnwrapRight.Load (ec);
815 ec.MarkLabel (end_label);
820 // Faster version when one operand is bool
822 if (UnwrapLeft == null) {
826 // Optimizes remaining (false & bool?), (true | bool?) which are not easy to handle
827 // in binary expression reduction
829 var c = Left as BoolConstant;
831 // Keep evaluation order
832 UnwrapRight.Store (ec);
834 ec.EmitInt (or ? 1 : 0);
835 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
836 } else if (Left.IsNull) {
837 UnwrapRight.Emit (ec);
838 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label);
840 UnwrapRight.Load (ec);
841 ec.Emit (OpCodes.Br_S, end_label);
843 ec.MarkLabel (is_null_label);
844 LiftedNull.Create (type, loc).Emit (ec);
847 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right);
849 ec.EmitInt (or ? 1 : 0);
850 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
852 ec.Emit (OpCodes.Br_S, end_label);
854 ec.MarkLabel (load_right);
855 UnwrapRight.Original.Emit (ec);
861 // Keep left-right evaluation order
862 UnwrapLeft.Store (ec);
865 // Optimizes remaining (bool? & false), (bool? | true) which are not easy to handle
866 // in binary expression reduction
868 var c = Right as BoolConstant;
870 ec.EmitInt (or ? 1 : 0);
871 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
872 } else if (Right.IsNull) {
873 UnwrapLeft.Emit (ec);
874 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label);
876 UnwrapLeft.Load (ec);
877 ec.Emit (OpCodes.Br_S, end_label);
879 ec.MarkLabel (is_null_label);
880 LiftedNull.Create (type, loc).Emit (ec);
883 ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right);
885 ec.EmitInt (or ? 1 : 0);
886 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
888 ec.Emit (OpCodes.Br_S, end_label);
890 ec.MarkLabel (load_right);
892 UnwrapLeft.Load (ec);
896 ec.MarkLabel (end_label);
900 // Emits optimized equality or inequality operator when possible
902 void EmitEquality (EmitContext ec)
905 // Either left or right is null
907 if (UnwrapLeft != null && Binary.Right.IsNull) { // TODO: Optimize for EmitBranchable
909 // left.HasValue == false
911 UnwrapLeft.EmitCheck (ec);
912 if (Binary.Oper == Binary.Operator.Equality) {
914 ec.Emit (OpCodes.Ceq);
919 if (UnwrapRight != null && Binary.Left.IsNull) {
921 // right.HasValue == false
923 UnwrapRight.EmitCheck (ec);
924 if (Binary.Oper == Binary.Operator.Equality) {
926 ec.Emit (OpCodes.Ceq);
931 Label dissimilar_label = ec.DefineLabel ();
932 Label end_label = ec.DefineLabel ();
934 if (UserOperator != null) {
937 if (UnwrapLeft != null) {
938 UnwrapLeft.EmitCheck (ec);
940 // Keep evaluation order same
941 if (!(Left is VariableReference)) {
943 var lt = new LocalTemporary (Left.Type);
949 if (UnwrapRight != null) {
950 UnwrapRight.EmitCheck (ec);
952 if (UnwrapLeft != null) {
953 ec.Emit (OpCodes.Bne_Un, dissimilar_label);
955 Label compare_label = ec.DefineLabel ();
956 UnwrapLeft.EmitCheck (ec);
957 ec.Emit (OpCodes.Brtrue, compare_label);
959 if (Binary.Oper == Binary.Operator.Equality)
964 ec.Emit (OpCodes.Br, end_label);
966 ec.MarkLabel (compare_label);
968 ec.Emit (OpCodes.Brfalse, dissimilar_label);
971 ec.Emit (OpCodes.Brfalse, dissimilar_label);
974 var args = new Arguments (2);
975 args.Add (new Argument (left));
976 args.Add (new Argument (Right));
978 var call = new CallEmitter ();
979 call.EmitPredefined (ec, UserOperator, args);
981 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) {
982 Left = Left.EmitToField (ec);
983 Right = Right.EmitToField (ec);
987 // Emit underlying value comparison first.
989 // For this code: int? a = 1; bool b = a == 1;
991 // We emit something similar to this. Expressions with side effects have local
992 // variable created by Unwrap expression
994 // left.GetValueOrDefault ()
996 // bne.un.s dissimilar_label
1007 ec.Emit (OpCodes.Bne_Un_S, dissimilar_label);
1010 // Check both left and right expressions for Unwrap call in which
1011 // case we need to run get_HasValue() check because the type is
1012 // nullable and could have null value
1014 if (UnwrapLeft != null)
1015 UnwrapLeft.EmitCheck (ec);
1017 if (UnwrapRight != null)
1018 UnwrapRight.EmitCheck (ec);
1020 if (UnwrapLeft != null && UnwrapRight != null) {
1021 if (Binary.Oper == Binary.Operator.Inequality)
1022 ec.Emit (OpCodes.Xor);
1024 ec.Emit (OpCodes.Ceq);
1026 if (Binary.Oper == Binary.Operator.Inequality) {
1028 ec.Emit (OpCodes.Ceq);
1033 ec.Emit (OpCodes.Br_S, end_label);
1035 ec.MarkLabel (dissimilar_label);
1036 if (Binary.Oper == Binary.Operator.Inequality)
1041 ec.MarkLabel (end_label);
1044 public override void FlowAnalysis (FlowAnalysisContext fc)
1046 Binary.FlowAnalysis (fc);
1049 public override SLE.Expression MakeExpression (BuilderContext ctx)
1051 return Binary.MakeExpression (ctx, Left, Right);
1055 public class NullCoalescingOperator : Expression
1057 Expression left, right;
1060 public NullCoalescingOperator (Expression left, Expression right)
1064 this.loc = left.Location;
1067 public Expression LeftExpression {
1073 public Expression RightExpression {
1079 public override Expression CreateExpressionTree (ResolveContext ec)
1081 if (left is NullLiteral)
1082 ec.Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side");
1084 UserCast uc = left as UserCast;
1085 Expression conversion = null;
1089 Arguments c_args = new Arguments (2);
1090 c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
1091 c_args.Add (new Argument (left.CreateExpressionTree (ec)));
1092 conversion = CreateExpressionFactoryCall (ec, "Lambda", c_args);
1095 Arguments args = new Arguments (3);
1096 args.Add (new Argument (left.CreateExpressionTree (ec)));
1097 args.Add (new Argument (right.CreateExpressionTree (ec)));
1098 if (conversion != null)
1099 args.Add (new Argument (conversion));
1101 return CreateExpressionFactoryCall (ec, "Coalesce", args);
1104 Expression ConvertExpression (ResolveContext ec)
1106 // TODO: ImplicitConversionExists should take care of this
1107 if (left.eclass == ExprClass.MethodGroup)
1110 TypeSpec ltype = left.Type;
1113 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
1114 // the result is underlying type of left
1116 if (ltype.IsNullableType) {
1117 unwrap = Unwrap.Create (left, false);
1122 // Reduce (left ?? null) to left
1125 return ReducedExpression.Create (left, this);
1127 if (Convert.ImplicitConversionExists (ec, right, unwrap.Type)) {
1132 // If right is a dynamic expression, the result type is dynamic
1134 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1137 // Need to box underlying value type
1138 left = Convert.ImplicitBoxingConversion (left, ltype, type);
1142 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1146 } else if (TypeSpec.IsReferenceType (ltype)) {
1147 if (Convert.ImplicitConversionExists (ec, right, ltype)) {
1149 // If right is a dynamic expression, the result type is dynamic
1151 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1157 // Reduce ("foo" ?? expr) to expression
1159 Constant lc = left as Constant;
1160 if (lc != null && !lc.IsDefaultValue)
1161 return ReducedExpression.Create (lc, this);
1164 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1166 if (right.IsNull || lc != null)
1167 return ReducedExpression.Create (lc != null ? right : left, this);
1169 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1175 // Special case null ?? null
1177 if (ltype == right.Type) {
1185 TypeSpec rtype = right.Type;
1186 if (!Convert.ImplicitConversionExists (ec, unwrap ?? left, rtype) || right.eclass == ExprClass.MethodGroup)
1190 // Reduce (null ?? right) to right
1193 return ReducedExpression.Create (right, this).Resolve (ec);
1195 left = Convert.ImplicitConversion (ec, unwrap ?? left, rtype, loc);
1200 public override bool ContainsEmitWithAwait ()
1203 return unwrap.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1205 return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1208 protected override Expression DoResolve (ResolveContext ec)
1210 left = left.Resolve (ec);
1211 right = right.Resolve (ec);
1213 if (left == null || right == null)
1216 eclass = ExprClass.Value;
1218 Expression e = ConvertExpression (ec);
1220 Binary.Error_OperatorCannotBeApplied (ec, left, right, "??", loc);
1227 public override void Emit (EmitContext ec)
1229 Label end_label = ec.DefineLabel ();
1231 if (unwrap != null) {
1232 Label is_null_label = ec.DefineLabel ();
1234 unwrap.EmitCheck (ec);
1235 ec.Emit (OpCodes.Brfalse, is_null_label);
1238 ec.Emit (OpCodes.Br, end_label);
1240 ec.MarkLabel (is_null_label);
1243 ec.MarkLabel (end_label);
1248 ec.Emit (OpCodes.Dup);
1250 // Only to make verifier happy
1251 if (left.Type.IsGenericParameter)
1252 ec.Emit (OpCodes.Box, left.Type);
1254 ec.Emit (OpCodes.Brtrue, end_label);
1256 ec.Emit (OpCodes.Pop);
1259 ec.MarkLabel (end_label);
1262 public override void FlowAnalysis (FlowAnalysisContext fc)
1264 left.FlowAnalysis (fc);
1265 var left_da = fc.BranchDefiniteAssignment ();
1266 right.FlowAnalysis (fc);
1267 fc.DefiniteAssignment = left_da;
1270 protected override void CloneTo (CloneContext clonectx, Expression t)
1272 NullCoalescingOperator target = (NullCoalescingOperator) t;
1274 target.left = left.Clone (clonectx);
1275 target.right = right.Clone (clonectx);
1278 public override object Accept (StructuralVisitor visitor)
1280 return visitor.Visit (this);
1284 class LiftedUnaryMutator : UnaryMutator
1286 public LiftedUnaryMutator (Mode mode, Expression expr, Location loc)
1287 : base (mode, expr, loc)
1291 protected override Expression DoResolve (ResolveContext ec)
1293 var orig_expr = expr;
1295 expr = Unwrap.Create (expr);
1297 var res = base.DoResolveOperation (ec);
1305 protected override void EmitOperation (EmitContext ec)
1307 Label is_null_label = ec.DefineLabel ();
1308 Label end_label = ec.DefineLabel ();
1310 LocalTemporary lt = new LocalTemporary (type);
1312 // Value is on the stack
1315 var call = new CallEmitter ();
1316 call.InstanceExpression = lt;
1317 call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null);
1319 ec.Emit (OpCodes.Brfalse, is_null_label);
1321 call = new CallEmitter ();
1322 call.InstanceExpression = lt;
1323 call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null);
1327 base.EmitOperation (ec);
1329 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
1330 ec.Emit (OpCodes.Br_S, end_label);
1332 ec.MarkLabel (is_null_label);
1333 LiftedNull.Create (type, loc).Emit (ec);
1335 ec.MarkLabel (end_label);