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
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Collections;
19 namespace Mono.CSharp.Nullable
21 public class NullableType : TypeExpr
23 Expression underlying;
25 public NullableType (Expression underlying, Location l)
27 this.underlying = underlying;
30 eclass = ExprClass.Type;
33 public NullableType (Type type, Location loc)
34 : this (new TypeExpression (type, loc), loc)
37 protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec)
39 TypeArguments args = new TypeArguments (loc);
40 args.Add (underlying);
42 if (TypeManager.generic_nullable_type == null) {
43 TypeManager.generic_nullable_type = TypeManager.CoreLookupType (
44 "System", "Nullable`1", Kind.Struct, true);
47 ConstructedType ctype = new ConstructedType (TypeManager.generic_nullable_type, args, loc);
48 return ctype.ResolveAsTypeTerminal (ec, false);
52 public sealed class NullableInfo
54 public readonly Type Type;
55 public readonly Type UnderlyingType;
56 public readonly MethodInfo HasValue;
57 public readonly MethodInfo Value;
58 public readonly MethodInfo GetValueOrDefault;
59 public readonly ConstructorInfo Constructor;
61 public NullableInfo (Type type)
64 UnderlyingType = TypeManager.GetTypeArguments (type) [0];
66 PropertyInfo has_value_pi = TypeManager.GetPredefinedProperty (type, "HasValue", Location.Null);
67 PropertyInfo value_pi = TypeManager.GetPredefinedProperty (type, "Value", Location.Null);
68 GetValueOrDefault = TypeManager.GetPredefinedMethod (type, "GetValueOrDefault", Location.Null, Type.EmptyTypes);
70 HasValue = has_value_pi.GetGetMethod (false);
71 Value = value_pi.GetGetMethod (false);
73 if (UnderlyingType.Module == CodeGen.Module.Builder) {
74 Type o_type = TypeManager.DropGenericTypeArguments (type);
75 Constructor = TypeBuilder.GetConstructor (type,
76 TypeManager.GetPredefinedConstructor (o_type, Location.Null, o_type.GetGenericArguments ()));
80 Constructor = type.GetConstructor (new Type[] { UnderlyingType });
84 public class Unwrap : Expression, IMemoryLocation, IAssignMethod
92 protected Unwrap (Expression expr)
95 this.loc = expr.Location;
98 public static Unwrap Create (Expression expr, EmitContext ec)
100 return new Unwrap (expr).Resolve (ec) as Unwrap;
103 public override Expression CreateExpressionTree (EmitContext ec)
105 return expr.CreateExpressionTree (ec);
108 public override Expression DoResolve (EmitContext ec)
113 temp = new LocalTemporary (expr.Type);
115 info = new NullableInfo (expr.Type);
116 type = info.UnderlyingType;
117 eclass = expr.eclass;
121 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
123 return DoResolve (ec);
126 public override void Emit (EmitContext ec)
128 AddressOf (ec, AddressOp.LoadStore);
129 ec.ig.EmitCall (OpCodes.Call, info.Value, null);
132 public void EmitCheck (EmitContext ec)
134 AddressOf (ec, AddressOp.LoadStore);
135 ec.ig.EmitCall (OpCodes.Call, info.HasValue, null);
138 public void EmitGetValueOrDefault (EmitContext ec)
140 AddressOf (ec, AddressOp.LoadStore);
141 ec.ig.EmitCall (OpCodes.Call, info.GetValueOrDefault, null);
144 public override bool Equals (object obj)
146 Unwrap uw = obj as Unwrap;
147 return uw != null && expr.Equals (uw.expr);
150 public Expression Original {
156 public override int GetHashCode ()
158 return expr.GetHashCode ();
161 public override bool IsNull {
167 public void Store (EmitContext ec)
172 void create_temp (EmitContext ec)
174 if ((temp != null) && !has_temp) {
181 public void LoadTemporary (EmitContext ec)
186 public void AddressOf (EmitContext ec, AddressOp mode)
190 temp.AddressOf (ec, AddressOp.LoadStore);
192 ((IMemoryLocation) expr).AddressOf (ec, AddressOp.LoadStore);
195 public void Emit (EmitContext ec, bool leave_copy)
208 public void EmitAssign (EmitContext ec, Expression source,
209 bool leave_copy, bool prepare_for_load)
211 InternalWrap wrap = new InternalWrap (source, info, loc);
212 ((IAssignMethod) expr).EmitAssign (ec, wrap, leave_copy, false);
215 protected class InternalWrap : Expression
217 public Expression expr;
218 public NullableInfo info;
220 public InternalWrap (Expression expr, NullableInfo info, Location loc)
227 eclass = ExprClass.Value;
230 public override Expression CreateExpressionTree (EmitContext ec)
232 throw new NotSupportedException ("ET");
235 public override Expression DoResolve (EmitContext ec)
240 public override void Emit (EmitContext ec)
243 ec.ig.Emit (OpCodes.Newobj, info.Constructor);
248 public class Wrap : TypeCast
250 readonly NullableInfo info;
252 protected Wrap (Expression expr, Type type)
255 info = new NullableInfo (type);
256 eclass = ExprClass.Value;
259 public static Expression Create (Expression expr, Type type)
261 return new Wrap (expr, type);
264 public override void Emit (EmitContext ec)
267 ec.ig.Emit (OpCodes.Newobj, info.Constructor);
272 // Represents null literal lifted to nullable type
274 public class LiftedNull : EmptyConstantCast, IMemoryLocation
276 private LiftedNull (Type nullable_type, Location loc)
277 : base (new NullLiteral (loc), nullable_type)
279 eclass = ExprClass.Value;
282 public static Constant Create (Type nullable, Location loc)
284 return new LiftedNull (nullable, loc);
287 public static Expression CreateFromExpression (Expression e)
289 Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'",
290 TypeManager.CSharpName (e.Type));
292 return ReducedExpression.Create (Create (e.Type, e.Location), e);
295 public override Expression CreateExpressionTree (EmitContext ec)
297 ArrayList args = new ArrayList (2);
298 args.Add (new Argument (this));
299 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
301 return CreateExpressionFactoryCall ("Constant", args);
304 public override void Emit (EmitContext ec)
306 // TODO: generate less temporary variables
307 LocalTemporary value_target = new LocalTemporary (type);
309 value_target.AddressOf (ec, AddressOp.Store);
310 ec.ig.Emit (OpCodes.Initobj, type);
311 value_target.Emit (ec);
314 public void AddressOf (EmitContext ec, AddressOp Mode)
316 LocalTemporary value_target = new LocalTemporary (type);
318 value_target.AddressOf (ec, AddressOp.Store);
319 ec.ig.Emit (OpCodes.Initobj, type);
320 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
324 public abstract class Lifted : Expression, IMemoryLocation
326 Expression expr, underlying, wrap, null_value;
329 protected Lifted (Expression expr, Location loc)
335 public override Expression CreateExpressionTree (EmitContext ec)
337 ArrayList args = new ArrayList (2);
338 args.Add (new Argument (expr.CreateExpressionTree (ec)));
339 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
340 return CreateExpressionFactoryCall ("Convert", args);
343 public override Expression DoResolve (EmitContext ec)
345 expr = expr.Resolve (ec);
349 unwrap = Unwrap.Create (expr, ec);
353 underlying = ResolveUnderlying (unwrap, ec);
354 if (underlying == null)
357 TypeExpr target_type = new NullableType (underlying.Type, loc);
358 target_type = target_type.ResolveAsTypeTerminal (ec, false);
359 if (target_type == null)
362 wrap = Wrap.Create (underlying, target_type.Type);
366 null_value = LiftedNull.Create (wrap.Type, loc);
369 eclass = ExprClass.Value;
373 protected abstract Expression ResolveUnderlying (Expression unwrap, EmitContext ec);
375 public override void Emit (EmitContext ec)
377 ILGenerator ig = ec.ig;
378 Label is_null_label = ig.DefineLabel ();
379 Label end_label = ig.DefineLabel ();
381 unwrap.EmitCheck (ec);
382 ig.Emit (OpCodes.Brfalse, is_null_label);
385 ig.Emit (OpCodes.Br, end_label);
387 ig.MarkLabel (is_null_label);
388 null_value.Emit (ec);
390 ig.MarkLabel (end_label);
393 public void AddressOf (EmitContext ec, AddressOp mode)
395 unwrap.AddressOf (ec, mode);
399 public class LiftedConversion : Lifted
401 public readonly bool IsUser;
402 public readonly bool IsExplicit;
403 public readonly Type TargetType;
405 public LiftedConversion (Expression expr, Type target_type, bool is_user,
406 bool is_explicit, Location loc)
409 this.IsUser = is_user;
410 this.IsExplicit = is_explicit;
411 this.TargetType = target_type;
414 protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec)
416 Type type = TypeManager.GetTypeArguments (TargetType) [0];
420 return Convert.ExplicitUserConversion (ec, unwrap, type, loc);
422 return Convert.ImplicitUserConversion (ec, unwrap, type, loc);
425 return Convert.ExplicitConversion (ec, unwrap, type, loc);
427 return Convert.ImplicitConversion (ec, unwrap, type, loc);
432 public class LiftedUnaryOperator : Unary, IMemoryLocation
435 Expression user_operator;
437 public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc)
438 : base (op, expr, loc)
442 public void AddressOf (EmitContext ec, AddressOp mode)
444 unwrap.AddressOf (ec, mode);
447 public override Expression CreateExpressionTree (EmitContext ec)
449 if (user_operator != null)
450 return user_operator.CreateExpressionTree (ec);
452 if (Oper == Operator.UnaryPlus)
453 return Expr.CreateExpressionTree (ec);
455 return base.CreateExpressionTree (ec);
458 public override Expression DoResolve (EmitContext ec)
460 if (eclass != ExprClass.Invalid)
463 unwrap = Unwrap.Create (Expr, ec);
467 Expression res = base.ResolveOperator (ec, unwrap);
469 if (user_operator == null)
472 res = Expr = LiftExpression (ec, Expr);
478 eclass = ExprClass.Value;
483 public override void Emit (EmitContext ec)
485 ILGenerator ig = ec.ig;
486 Label is_null_label = ig.DefineLabel ();
487 Label end_label = ig.DefineLabel ();
489 unwrap.EmitCheck (ec);
490 ig.Emit (OpCodes.Brfalse, is_null_label);
492 NullableInfo ni = new NullableInfo (type);
494 if (user_operator != null) {
495 user_operator.Emit (ec);
497 EmitOperator (ec, ni.UnderlyingType);
500 ig.Emit (OpCodes.Newobj, ni.Constructor);
501 ig.Emit (OpCodes.Br_S, end_label);
503 ig.MarkLabel (is_null_label);
504 LiftedNull.Create (type, loc).Emit (ec);
506 ig.MarkLabel (end_label);
509 Expression LiftExpression (EmitContext ec, Expression expr)
511 TypeExpr lifted_type = new NullableType (expr.Type, expr.Location);
512 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
513 if (lifted_type == null)
516 expr.Type = lifted_type.Type;
520 protected override Expression ResolveEnumOperator (EmitContext ec, Expression expr)
522 expr = base.ResolveEnumOperator (ec, expr);
526 Expr = LiftExpression (ec, Expr);
527 return LiftExpression (ec, expr);
530 protected override Expression ResolveUserOperator (EmitContext ec, Expression expr)
532 expr = base.ResolveUserOperator (ec, expr);
536 user_operator = LiftExpression (ec, expr);
537 return user_operator;
541 public class LiftedBinaryOperator : Binary
543 Unwrap left_unwrap, right_unwrap;
544 bool left_null_lifted, right_null_lifted;
545 Expression left_orig, right_orig;
546 Expression user_operator;
547 ConstructorInfo wrap_ctor;
549 public LiftedBinaryOperator (Binary.Operator op, Expression left, Expression right,
551 : base (op, left, right)
556 public override Expression CreateExpressionTree (EmitContext ec)
558 if (user_operator != null)
559 return user_operator.CreateExpressionTree (ec);
561 return base.CreateExpressionTree (ec);
565 // CSC 2 has this behavior, it allows structs to be compared
566 // with the null literal *outside* of a generics context and
567 // inlines that as true or false.
569 Expression CreateNullConstant (Expression expr)
571 // FIXME: Handle side effect constants
572 Constant c = new BoolConstant (Oper == Operator.Inequality, loc);
574 if ((Oper & Operator.EqualityMask) != 0) {
575 Report.Warning (472, 2, loc, "The result of comparing `{0}' against null is always `{1}'. " +
576 "This operation is undocumented and it is temporary supported for compatibility reasons only",
577 expr.GetSignatureForError (), c.AsString ());
579 Report.Warning (464, 2, loc, "The result of comparing type `{0}' against null is always `{1}'",
580 expr.GetSignatureForError (), c.AsString ());
583 return ReducedExpression.Create (c, this);
586 public override Expression DoResolve (EmitContext ec)
588 if (eclass != ExprClass.Invalid)
591 if ((Oper & Operator.LogicalMask) != 0) {
592 Error_OperatorCannotBeApplied (left, right);
597 if (TypeManager.IsNullableType (left.Type)) {
598 left = left_unwrap = Unwrap.Create (left, ec);
604 if (TypeManager.IsNullableType (right.Type)) {
605 right = right_unwrap = Unwrap.Create (right, ec);
611 // Some details are in 6.4.2, 7.2.7
612 // Arguments can be lifted for equal operators when the return type is bool and both
613 // arguments are of same type
615 if (left is NullLiteral) {
617 left_null_lifted = true;
618 type = TypeManager.bool_type;
621 if (right is NullLiteral) {
623 right_null_lifted = true;
624 type = TypeManager.bool_type;
627 eclass = ExprClass.Value;
628 return DoResolveCore (ec, left_orig, right_orig);
631 void EmitBitwiseBoolean (EmitContext ec)
633 ILGenerator ig = ec.ig;
635 Label load_left = ig.DefineLabel ();
636 Label load_right = ig.DefineLabel ();
637 Label end_label = ig.DefineLabel ();
639 left_unwrap.EmitGetValueOrDefault (ec);
640 ig.Emit (OpCodes.Brtrue_S, load_right);
642 right_unwrap.EmitGetValueOrDefault (ec);
643 ig.Emit (OpCodes.Brtrue_S, load_left);
645 left_unwrap.EmitCheck (ec);
646 ig.Emit (OpCodes.Brfalse_S, load_right);
649 ig.MarkLabel (load_left);
651 if (Oper == Operator.BitwiseAnd) {
652 left_unwrap.LoadTemporary (ec);
654 right_unwrap.LoadTemporary (ec);
655 right_unwrap = left_unwrap;
657 ig.Emit (OpCodes.Br_S, end_label);
660 ig.MarkLabel (load_right);
661 right_unwrap.LoadTemporary (ec);
663 ig.MarkLabel (end_label);
667 // Emits optimized equality or inequality operator when possible
669 bool EmitEquality (EmitContext ec)
671 ILGenerator ig = ec.ig;
674 // Either left or right is null
676 if (left_unwrap != null && (right_null_lifted || right.IsNull)) {
677 left_unwrap.EmitCheck (ec);
678 if (Oper == Binary.Operator.Equality) {
679 ig.Emit (OpCodes.Ldc_I4_0);
680 ig.Emit (OpCodes.Ceq);
685 if (right_unwrap != null && (left_null_lifted || left.IsNull)) {
686 right_unwrap.EmitCheck (ec);
687 if (Oper == Binary.Operator.Equality) {
688 ig.Emit (OpCodes.Ldc_I4_0);
689 ig.Emit (OpCodes.Ceq);
694 if (user_operator != null)
697 Label dissimilar_label = ig.DefineLabel ();
698 Label end_label = ig.DefineLabel ();
700 if (left_unwrap != null)
701 left_unwrap.EmitGetValueOrDefault (ec);
705 if (right_unwrap != null)
706 right_unwrap.EmitGetValueOrDefault (ec);
710 ig.Emit (OpCodes.Bne_Un_S, dissimilar_label);
712 if (left_unwrap != null)
713 left_unwrap.EmitCheck (ec);
714 if (right_unwrap != null)
715 right_unwrap.EmitCheck (ec);
717 if (left_unwrap != null && right_unwrap != null) {
718 if (Oper == Operator.Inequality)
719 ig.Emit (OpCodes.Xor);
721 ig.Emit (OpCodes.Ceq);
723 if (Oper == Operator.Inequality) {
724 ig.Emit (OpCodes.Ldc_I4_0);
725 ig.Emit (OpCodes.Ceq);
729 ig.Emit (OpCodes.Br_S, end_label);
731 ig.MarkLabel (dissimilar_label);
732 if (Oper == Operator.Inequality)
733 ig.Emit (OpCodes.Ldc_I4_1);
735 ig.Emit (OpCodes.Ldc_I4_0);
737 ig.MarkLabel (end_label);
741 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
744 ec.ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
747 public override void Emit (EmitContext ec)
749 if (left_unwrap != null)
750 left_unwrap.Store (ec);
752 if (right_unwrap != null) {
753 if (right.Equals (left))
754 right_unwrap = left_unwrap;
756 right_unwrap.Store (ec);
759 if (user_operator == null && IsBitwiseBoolean) {
760 EmitBitwiseBoolean (ec);
764 if ((Oper & Operator.EqualityMask) != 0) {
765 if (EmitEquality (ec))
769 ILGenerator ig = ec.ig;
771 Label is_null_label = ig.DefineLabel ();
772 Label end_label = ig.DefineLabel ();
774 if (left_unwrap != null) {
775 left_unwrap.EmitCheck (ec);
776 ig.Emit (OpCodes.Brfalse, is_null_label);
780 // Don't emit HasValue check when left and right expressions are same
782 if (right_unwrap != null && !left.Equals (right)) {
783 right_unwrap.EmitCheck (ec);
784 ig.Emit (OpCodes.Brfalse, is_null_label);
787 EmitOperator (ec, left.Type);
789 if (wrap_ctor != null)
790 ig.Emit (OpCodes.Newobj, wrap_ctor);
792 ig.Emit (OpCodes.Br_S, end_label);
793 ig.MarkLabel (is_null_label);
795 if ((Oper & Operator.ComparisonMask) != 0) {
796 if (Oper == Operator.Equality)
797 ig.Emit (OpCodes.Ldc_I4_1);
799 ig.Emit (OpCodes.Ldc_I4_0);
801 LiftedNull.Create (type, loc).Emit (ec);
804 ig.MarkLabel (end_label);
807 protected override void EmitOperator (EmitContext ec, Type l)
809 if (user_operator != null) {
810 user_operator.Emit (ec);
814 if (TypeManager.IsNullableType (l))
815 l = TypeManager.GetTypeArguments (l) [0];
817 base.EmitOperator (ec, l);
820 bool IsBitwiseBoolean {
822 return (Oper & Operator.BitwiseMask) != 0 && left_unwrap != null && right_unwrap != null &&
823 left_unwrap.Type == TypeManager.bool_type && right_unwrap.Type == TypeManager.bool_type;
827 Expression LiftResult (EmitContext ec, Expression res_expr)
829 TypeExpr lifted_type;
832 // Avoid double conversion
834 if (left_unwrap == null || left_null_lifted || !TypeManager.IsEqual (left_unwrap.Type, left.Type)) {
835 lifted_type = new NullableType (left.Type, loc);
836 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
837 if (lifted_type == null)
840 if (left is UserCast || left is TypeCast)
841 left.Type = lifted_type.Type;
843 left = EmptyCast.Create (left, lifted_type.Type);
846 if (right_unwrap == null || right_null_lifted || !TypeManager.IsEqual (right_unwrap.Type, right.Type)) {
847 lifted_type = new NullableType (right.Type, loc);
848 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
849 if (lifted_type == null)
852 if (right is UserCast || right is TypeCast)
853 right.Type = lifted_type.Type;
855 right = EmptyCast.Create (right, lifted_type.Type);
858 if ((Oper & Operator.ComparisonMask) == 0) {
859 lifted_type = new NullableType (res_expr.Type, loc);
860 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
861 if (lifted_type == null)
864 wrap_ctor = new NullableInfo (lifted_type.Type).Constructor;
865 type = res_expr.Type = lifted_type.Type;
868 if (left_null_lifted) {
869 left = LiftedNull.Create (right.Type, left.Location);
871 if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask)) != 0)
872 return LiftedNull.CreateFromExpression (res_expr);
875 // Value types and null comparison
877 if (right_unwrap == null || (Oper & Operator.RelationalMask) != 0)
878 return CreateNullConstant (right_orig).Resolve (ec);
881 if (right_null_lifted) {
882 right = LiftedNull.Create (left.Type, right.Location);
884 if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask)) != 0)
885 return LiftedNull.CreateFromExpression (res_expr);
888 // Value types and null comparison
890 if (left_unwrap == null || (Oper & Operator.RelationalMask) != 0)
891 return CreateNullConstant (left_orig).Resolve (ec);
897 protected override Expression ResolveOperatorPredefined (EmitContext ec, Binary.PredefinedOperator [] operators, bool primitives_only, Type enum_type)
899 Expression e = base.ResolveOperatorPredefined (ec, operators, primitives_only, enum_type);
901 if (e == this || enum_type != null)
902 return LiftResult (ec, e);
905 // 7.9.9 Equality operators and null
907 // The == and != operators permit one operand to be a value of a nullable type and
908 // the other to be the null literal, even if no predefined or user-defined operator
909 // (in unlifted or lifted form) exists for the operation.
911 if (e == null && (Oper & Operator.EqualityMask) != 0) {
912 if ((left_null_lifted && right_unwrap != null) || (right_null_lifted && left_unwrap != null))
913 return LiftResult (ec, this);
919 protected override Expression ResolveUserOperator (EmitContext ec, Type l, Type r)
921 Expression expr = base.ResolveUserOperator (ec, l, r);
925 expr = LiftResult (ec, expr);
926 if (expr is Constant)
930 user_operator = expr;
935 public class NullCoalescingOperator : Expression
937 Expression left, right;
940 public NullCoalescingOperator (Expression left, Expression right, Location loc)
947 public override Expression CreateExpressionTree (EmitContext ec)
949 if (left is NullLiteral)
950 Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side");
952 UserCast uc = left as UserCast;
953 Expression conversion = null;
957 ArrayList c_args = new ArrayList (2);
958 c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
959 c_args.Add (new Argument (left.CreateExpressionTree (ec)));
960 conversion = CreateExpressionFactoryCall ("Lambda", c_args);
963 ArrayList args = new ArrayList (3);
964 args.Add (new Argument (left.CreateExpressionTree (ec)));
965 args.Add (new Argument (right.CreateExpressionTree (ec)));
966 if (conversion != null)
967 args.Add (new Argument (conversion));
969 return CreateExpressionFactoryCall ("Coalesce", args);
972 public override Expression DoResolve (EmitContext ec)
977 left = left.Resolve (ec);
978 right = right.Resolve (ec);
980 if (left == null || right == null)
983 eclass = ExprClass.Value;
984 Type ltype = left.Type, rtype = right.Type;
987 // If left is a nullable type and an implicit conversion exists from right to left,
988 // the result type is left
990 if (TypeManager.IsNullableType (ltype)) {
991 unwrap = Unwrap.Create (left, ec);
995 if (Convert.ImplicitConversionExists (ec, right, ltype)) {
997 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1001 } else if (TypeManager.IsReferenceType (ltype)) {
1002 if (Convert.ImplicitConversionExists (ec, right, ltype)) {
1004 // Reduce (left ?? null) to left
1007 return ReducedExpression.Create (left, this).Resolve (ec);
1009 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1014 Binary.Error_OperatorCannotBeApplied (loc, "??", ltype, rtype);
1018 if (!Convert.ImplicitConversionExists (ec, left, rtype)) {
1019 Binary.Error_OperatorCannotBeApplied (loc, "??", ltype, rtype);
1024 // Reduce (null ?? right) to right
1027 return ReducedExpression.Create (right, this).Resolve (ec);
1029 left = Convert.ImplicitConversion (ec, left, rtype, loc);
1034 public override void Emit (EmitContext ec)
1036 ILGenerator ig = ec.ig;
1038 Label end_label = ig.DefineLabel ();
1040 if (unwrap != null) {
1041 Label is_null_label = ig.DefineLabel ();
1043 unwrap.EmitCheck (ec);
1044 ig.Emit (OpCodes.Brfalse, is_null_label);
1047 ig.Emit (OpCodes.Br, end_label);
1049 ig.MarkLabel (is_null_label);
1052 ig.MarkLabel (end_label);
1058 ig.Emit (OpCodes.Dup);
1059 ig.Emit (OpCodes.Brtrue, end_label);
1061 ig.Emit (OpCodes.Pop);
1064 ig.MarkLabel (end_label);
1067 protected override void CloneTo (CloneContext clonectx, Expression t)
1069 NullCoalescingOperator target = (NullCoalescingOperator) t;
1071 target.left = left.Clone (clonectx);
1072 target.right = right.Clone (clonectx);
1076 public class LiftedUnaryMutator : ExpressionStatement
1078 public readonly UnaryMutator.Mode Mode;
1079 Expression expr, null_value;
1080 UnaryMutator underlying;
1083 public LiftedUnaryMutator (UnaryMutator.Mode mode, Expression expr, Location loc)
1089 eclass = ExprClass.Value;
1092 public override Expression CreateExpressionTree (EmitContext ec)
1094 return new SimpleAssign (this, this).CreateExpressionTree (ec);
1097 public override Expression DoResolve (EmitContext ec)
1099 expr = expr.Resolve (ec);
1103 unwrap = Unwrap.Create (expr, ec);
1107 underlying = (UnaryMutator) new UnaryMutator (Mode, unwrap, loc).Resolve (ec);
1108 if (underlying == null)
1112 null_value = LiftedNull.Create (type, loc);
1116 void DoEmit (EmitContext ec, bool is_expr)
1118 ILGenerator ig = ec.ig;
1119 Label is_null_label = ig.DefineLabel ();
1120 Label end_label = ig.DefineLabel ();
1122 unwrap.EmitCheck (ec);
1123 ig.Emit (OpCodes.Brfalse, is_null_label);
1126 underlying.Emit (ec);
1128 underlying.EmitStatement (ec);
1129 ig.Emit (OpCodes.Br, end_label);
1131 ig.MarkLabel (is_null_label);
1133 null_value.Emit (ec);
1135 ig.MarkLabel (end_label);
1138 public override void Emit (EmitContext ec)
1143 public override void EmitStatement (EmitContext ec)