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
91 protected Unwrap (Expression expr)
94 this.loc = expr.Location;
97 public static Unwrap Create (Expression expr, EmitContext ec)
99 return new Unwrap (expr).Resolve (ec) as Unwrap;
102 public override Expression CreateExpressionTree (EmitContext ec)
104 return expr.CreateExpressionTree (ec);
107 public override Expression DoResolve (EmitContext ec)
112 info = new NullableInfo (expr.Type);
113 type = info.UnderlyingType;
114 eclass = expr.eclass;
118 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
120 return DoResolve (ec);
123 public override void Emit (EmitContext ec)
126 AddressOf (ec, AddressOp.LoadStore);
127 ec.ig.EmitCall (OpCodes.Call, info.Value, null);
130 public void EmitCheck (EmitContext ec)
133 AddressOf (ec, AddressOp.LoadStore);
134 ec.ig.EmitCall (OpCodes.Call, info.HasValue, null);
137 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 void Store (EmitContext ec)
169 if (expr is VariableReference)
176 LocalVariable.Store (ec);
179 public void Load (EmitContext ec)
181 if (expr is VariableReference)
184 LocalVariable.Emit (ec);
187 public void AddressOf (EmitContext ec, AddressOp mode)
189 IMemoryLocation ml = expr as VariableReference;
191 ml.AddressOf (ec, mode);
193 LocalVariable.AddressOf (ec, mode);
197 // Keeps result of non-variable expression
199 LocalTemporary LocalVariable {
202 temp = new LocalTemporary (info.Type);
207 public void Emit (EmitContext ec, bool leave_copy)
215 public void EmitAssign (EmitContext ec, Expression source,
216 bool leave_copy, bool prepare_for_load)
218 InternalWrap wrap = new InternalWrap (source, info, loc);
219 ((IAssignMethod) expr).EmitAssign (ec, wrap, leave_copy, false);
222 protected class InternalWrap : Expression
224 public Expression expr;
225 public NullableInfo info;
227 public InternalWrap (Expression expr, NullableInfo info, Location loc)
234 eclass = ExprClass.Value;
237 public override Expression CreateExpressionTree (EmitContext ec)
239 throw new NotSupportedException ("ET");
242 public override Expression DoResolve (EmitContext ec)
247 public override void Emit (EmitContext ec)
250 ec.ig.Emit (OpCodes.Newobj, info.Constructor);
255 public class Wrap : TypeCast
257 readonly NullableInfo info;
259 protected Wrap (Expression expr, Type type)
262 info = new NullableInfo (type);
263 eclass = ExprClass.Value;
266 public static Expression Create (Expression expr, Type type)
269 // Avoid unwraping and wraping of the same type
271 Unwrap unwrap = expr as Unwrap;
272 if (unwrap != null && TypeManager.IsEqual (expr.Type, TypeManager.GetTypeArguments (type) [0]))
273 return unwrap.Original;
275 return new Wrap (expr, type);
278 public override void Emit (EmitContext ec)
281 ec.ig.Emit (OpCodes.Newobj, info.Constructor);
286 // Represents null literal lifted to nullable type
288 public class LiftedNull : EmptyConstantCast, IMemoryLocation
290 private LiftedNull (Type nullable_type, Location loc)
291 : base (new NullLiteral (loc), nullable_type)
293 eclass = ExprClass.Value;
296 public static Constant Create (Type nullable, Location loc)
298 return new LiftedNull (nullable, loc);
301 public static Expression CreateFromExpression (Expression e)
303 Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'",
304 TypeManager.CSharpName (e.Type));
306 return ReducedExpression.Create (Create (e.Type, e.Location), e);
309 public override Expression CreateExpressionTree (EmitContext ec)
311 ArrayList args = new ArrayList (2);
312 args.Add (new Argument (this));
313 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
315 return CreateExpressionFactoryCall ("Constant", args);
318 public override void Emit (EmitContext ec)
320 // TODO: generate less temporary variables
321 LocalTemporary value_target = new LocalTemporary (type);
323 value_target.AddressOf (ec, AddressOp.Store);
324 ec.ig.Emit (OpCodes.Initobj, type);
325 value_target.Emit (ec);
328 public void AddressOf (EmitContext ec, AddressOp Mode)
330 LocalTemporary value_target = new LocalTemporary (type);
332 value_target.AddressOf (ec, AddressOp.Store);
333 ec.ig.Emit (OpCodes.Initobj, type);
334 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
338 public abstract class Lifted : Expression, IMemoryLocation
340 Expression expr, underlying, wrap, null_value;
343 protected Lifted (Expression expr, Location loc)
349 public override Expression CreateExpressionTree (EmitContext ec)
351 ArrayList args = new ArrayList (2);
352 args.Add (new Argument (expr.CreateExpressionTree (ec)));
353 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
354 return CreateExpressionFactoryCall ("Convert", args);
357 public override Expression DoResolve (EmitContext ec)
359 expr = expr.Resolve (ec);
363 unwrap = Unwrap.Create (expr, ec);
367 underlying = ResolveUnderlying (unwrap, ec);
368 if (underlying == null)
371 TypeExpr target_type = new NullableType (underlying.Type, loc);
372 target_type = target_type.ResolveAsTypeTerminal (ec, false);
373 if (target_type == null)
376 wrap = Wrap.Create (underlying, target_type.Type);
380 null_value = LiftedNull.Create (wrap.Type, loc);
383 eclass = ExprClass.Value;
387 protected abstract Expression ResolveUnderlying (Expression unwrap, EmitContext ec);
389 public override void Emit (EmitContext ec)
391 ILGenerator ig = ec.ig;
392 Label is_null_label = ig.DefineLabel ();
393 Label end_label = ig.DefineLabel ();
395 unwrap.EmitCheck (ec);
396 ig.Emit (OpCodes.Brfalse, is_null_label);
399 ig.Emit (OpCodes.Br, end_label);
401 ig.MarkLabel (is_null_label);
402 null_value.Emit (ec);
404 ig.MarkLabel (end_label);
407 public void AddressOf (EmitContext ec, AddressOp mode)
409 unwrap.AddressOf (ec, mode);
413 public class LiftedConversion : Lifted
415 public readonly bool IsUser;
416 public readonly bool IsExplicit;
417 public readonly Type TargetType;
419 public LiftedConversion (Expression expr, Type target_type, bool is_user,
420 bool is_explicit, Location loc)
423 this.IsUser = is_user;
424 this.IsExplicit = is_explicit;
425 this.TargetType = target_type;
428 protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec)
430 Type type = TypeManager.GetTypeArguments (TargetType) [0];
434 return Convert.ExplicitUserConversion (ec, unwrap, type, loc);
436 return Convert.ImplicitUserConversion (ec, unwrap, type, loc);
439 return Convert.ExplicitConversion (ec, unwrap, type, loc);
441 return Convert.ImplicitConversion (ec, unwrap, type, loc);
446 public class LiftedUnaryOperator : Unary, IMemoryLocation
449 Expression user_operator;
451 public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc)
452 : base (op, expr, loc)
456 public void AddressOf (EmitContext ec, AddressOp mode)
458 unwrap.AddressOf (ec, mode);
461 public override Expression CreateExpressionTree (EmitContext ec)
463 if (user_operator != null)
464 return user_operator.CreateExpressionTree (ec);
466 if (Oper == Operator.UnaryPlus)
467 return Expr.CreateExpressionTree (ec);
469 return base.CreateExpressionTree (ec);
472 public override Expression DoResolve (EmitContext ec)
474 if (eclass != ExprClass.Invalid)
477 unwrap = Unwrap.Create (Expr, ec);
481 Expression res = base.ResolveOperator (ec, unwrap);
483 if (user_operator == null)
486 res = Expr = LiftExpression (ec, Expr);
492 eclass = ExprClass.Value;
497 public override void Emit (EmitContext ec)
499 ILGenerator ig = ec.ig;
500 Label is_null_label = ig.DefineLabel ();
501 Label end_label = ig.DefineLabel ();
503 unwrap.EmitCheck (ec);
504 ig.Emit (OpCodes.Brfalse, is_null_label);
506 NullableInfo ni = new NullableInfo (type);
508 if (user_operator != null) {
509 user_operator.Emit (ec);
511 EmitOperator (ec, ni.UnderlyingType);
514 ig.Emit (OpCodes.Newobj, ni.Constructor);
515 ig.Emit (OpCodes.Br_S, end_label);
517 ig.MarkLabel (is_null_label);
518 LiftedNull.Create (type, loc).Emit (ec);
520 ig.MarkLabel (end_label);
523 Expression LiftExpression (EmitContext ec, Expression expr)
525 TypeExpr lifted_type = new NullableType (expr.Type, expr.Location);
526 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
527 if (lifted_type == null)
530 expr.Type = lifted_type.Type;
534 protected override Expression ResolveEnumOperator (EmitContext ec, Expression expr)
536 expr = base.ResolveEnumOperator (ec, expr);
540 Expr = LiftExpression (ec, Expr);
541 return LiftExpression (ec, expr);
544 protected override Expression ResolveUserOperator (EmitContext ec, Expression expr)
546 expr = base.ResolveUserOperator (ec, expr);
551 // When a user operator is of non-nullable type
553 if (Expr is Unwrap) {
554 user_operator = LiftExpression (ec, expr);
555 return user_operator;
562 public class LiftedBinaryOperator : Binary
564 Unwrap left_unwrap, right_unwrap;
565 bool left_null_lifted, right_null_lifted;
566 Expression left_orig, right_orig;
567 Expression user_operator;
568 ConstructorInfo wrap_ctor;
570 public LiftedBinaryOperator (Binary.Operator op, Expression left, Expression right,
572 : base (op, left, right)
577 public override Expression CreateExpressionTree (EmitContext ec)
579 if (user_operator != null)
580 return user_operator.CreateExpressionTree (ec);
582 return base.CreateExpressionTree (ec);
586 // CSC 2 has this behavior, it allows structs to be compared
587 // with the null literal *outside* of a generics context and
588 // inlines that as true or false.
590 Expression CreateNullConstant (Expression expr)
592 // FIXME: Handle side effect constants
593 Constant c = new BoolConstant (Oper == Operator.Inequality, loc);
595 if ((Oper & Operator.EqualityMask) != 0) {
596 Report.Warning (472, 2, loc, "The result of comparing `{0}' against null is always `{1}'. " +
597 "This operation is undocumented and it is temporary supported for compatibility reasons only",
598 expr.GetSignatureForError (), c.AsString ());
600 Report.Warning (464, 2, loc, "The result of comparing type `{0}' against null is always `{1}'",
601 expr.GetSignatureForError (), c.AsString ());
604 return ReducedExpression.Create (c, this);
607 public override Expression DoResolve (EmitContext ec)
609 if (eclass != ExprClass.Invalid)
612 if ((Oper & Operator.LogicalMask) != 0) {
613 Error_OperatorCannotBeApplied (left, right);
618 if (TypeManager.IsNullableType (left.Type)) {
619 left = left_unwrap = Unwrap.Create (left, ec);
625 if (TypeManager.IsNullableType (right.Type)) {
626 right = right_unwrap = Unwrap.Create (right, ec);
632 // Some details are in 6.4.2, 7.2.7
633 // Arguments can be lifted for equal operators when the return type is bool and both
634 // arguments are of same type
636 if (left is NullLiteral) {
638 left_null_lifted = true;
639 type = TypeManager.bool_type;
642 if (right is NullLiteral) {
644 right_null_lifted = true;
645 type = TypeManager.bool_type;
648 eclass = ExprClass.Value;
649 return DoResolveCore (ec, left_orig, right_orig);
652 void EmitBitwiseBoolean (EmitContext ec)
654 ILGenerator ig = ec.ig;
656 Label load_left = ig.DefineLabel ();
657 Label load_right = ig.DefineLabel ();
658 Label end_label = ig.DefineLabel ();
660 left_unwrap.EmitGetValueOrDefault (ec);
661 ig.Emit (OpCodes.Brtrue_S, load_right);
663 right_unwrap.EmitGetValueOrDefault (ec);
664 ig.Emit (OpCodes.Brtrue_S, load_left);
666 left_unwrap.EmitCheck (ec);
667 ig.Emit (OpCodes.Brfalse_S, load_right);
670 ig.MarkLabel (load_left);
672 if (Oper == Operator.BitwiseAnd) {
673 left_unwrap.Load (ec);
675 right_unwrap.Load (ec);
676 right_unwrap = left_unwrap;
678 ig.Emit (OpCodes.Br_S, end_label);
681 ig.MarkLabel (load_right);
682 right_unwrap.Load (ec);
684 ig.MarkLabel (end_label);
688 // Emits optimized equality or inequality operator when possible
690 bool EmitEquality (EmitContext ec)
692 ILGenerator ig = ec.ig;
695 // Either left or right is null
697 if (left_unwrap != null && (right_null_lifted || right.IsNull)) {
698 left_unwrap.EmitCheck (ec);
699 if (Oper == Binary.Operator.Equality) {
700 ig.Emit (OpCodes.Ldc_I4_0);
701 ig.Emit (OpCodes.Ceq);
706 if (right_unwrap != null && (left_null_lifted || left.IsNull)) {
707 right_unwrap.EmitCheck (ec);
708 if (Oper == Binary.Operator.Equality) {
709 ig.Emit (OpCodes.Ldc_I4_0);
710 ig.Emit (OpCodes.Ceq);
715 if (user_operator != null)
718 Label dissimilar_label = ig.DefineLabel ();
719 Label end_label = ig.DefineLabel ();
721 if (left_unwrap != null)
722 left_unwrap.EmitGetValueOrDefault (ec);
726 if (right_unwrap != null)
727 right_unwrap.EmitGetValueOrDefault (ec);
731 ig.Emit (OpCodes.Bne_Un_S, dissimilar_label);
733 if (left_unwrap != null)
734 left_unwrap.EmitCheck (ec);
735 if (right_unwrap != null)
736 right_unwrap.EmitCheck (ec);
738 if (left_unwrap != null && right_unwrap != null) {
739 if (Oper == Operator.Inequality)
740 ig.Emit (OpCodes.Xor);
742 ig.Emit (OpCodes.Ceq);
744 if (Oper == Operator.Inequality) {
745 ig.Emit (OpCodes.Ldc_I4_0);
746 ig.Emit (OpCodes.Ceq);
750 ig.Emit (OpCodes.Br_S, end_label);
752 ig.MarkLabel (dissimilar_label);
753 if (Oper == Operator.Inequality)
754 ig.Emit (OpCodes.Ldc_I4_1);
756 ig.Emit (OpCodes.Ldc_I4_0);
758 ig.MarkLabel (end_label);
762 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
765 ec.ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
768 public override void Emit (EmitContext ec)
771 // Optimize same expression operation
773 if (right_unwrap != null && right.Equals (left))
774 right_unwrap = left_unwrap;
776 if (user_operator == null && IsBitwiseBoolean) {
777 EmitBitwiseBoolean (ec);
781 if ((Oper & Operator.EqualityMask) != 0) {
782 if (EmitEquality (ec))
786 ILGenerator ig = ec.ig;
788 Label is_null_label = ig.DefineLabel ();
789 Label end_label = ig.DefineLabel ();
791 if (left_unwrap != null) {
792 left_unwrap.EmitCheck (ec);
793 ig.Emit (OpCodes.Brfalse, is_null_label);
797 // Don't emit HasValue check when left and right expressions are same
799 if (right_unwrap != null && !left.Equals (right)) {
800 right_unwrap.EmitCheck (ec);
801 ig.Emit (OpCodes.Brfalse, is_null_label);
804 EmitOperator (ec, left.Type);
806 if (wrap_ctor != null)
807 ig.Emit (OpCodes.Newobj, wrap_ctor);
809 ig.Emit (OpCodes.Br_S, end_label);
810 ig.MarkLabel (is_null_label);
812 if ((Oper & Operator.ComparisonMask) != 0) {
813 if (Oper == Operator.Equality)
814 ig.Emit (OpCodes.Ldc_I4_1);
816 ig.Emit (OpCodes.Ldc_I4_0);
818 LiftedNull.Create (type, loc).Emit (ec);
821 ig.MarkLabel (end_label);
824 protected override void EmitOperator (EmitContext ec, Type l)
826 if (user_operator != null) {
827 user_operator.Emit (ec);
831 if (TypeManager.IsNullableType (l))
832 l = TypeManager.GetTypeArguments (l) [0];
834 base.EmitOperator (ec, l);
837 bool IsBitwiseBoolean {
839 return (Oper & Operator.BitwiseMask) != 0 && left_unwrap != null && right_unwrap != null &&
840 left_unwrap.Type == TypeManager.bool_type && right_unwrap.Type == TypeManager.bool_type;
844 Expression LiftResult (EmitContext ec, Expression res_expr)
846 TypeExpr lifted_type;
849 // Avoid double conversion
851 if (left_unwrap == null || left_null_lifted || !TypeManager.IsEqual (left_unwrap.Type, left.Type) || (left_unwrap != null && right_null_lifted)) {
852 lifted_type = new NullableType (left.Type, loc);
853 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
854 if (lifted_type == null)
857 if (left is UserCast || left is TypeCast)
858 left.Type = lifted_type.Type;
860 left = EmptyCast.Create (left, lifted_type.Type);
863 if (right_unwrap == null || right_null_lifted || !TypeManager.IsEqual (right_unwrap.Type, right.Type) || (right_unwrap != null && left_null_lifted)) {
864 lifted_type = new NullableType (right.Type, loc);
865 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
866 if (lifted_type == null)
869 if (right is UserCast || right is TypeCast)
870 right.Type = lifted_type.Type;
872 right = EmptyCast.Create (right, lifted_type.Type);
875 if ((Oper & Operator.ComparisonMask) == 0) {
876 lifted_type = new NullableType (res_expr.Type, loc);
877 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
878 if (lifted_type == null)
881 wrap_ctor = new NullableInfo (lifted_type.Type).Constructor;
882 type = res_expr.Type = lifted_type.Type;
885 if (left_null_lifted) {
886 left = LiftedNull.Create (right.Type, left.Location);
888 if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask)) != 0)
889 return LiftedNull.CreateFromExpression (res_expr);
892 // Value types and null comparison
894 if (right_unwrap == null || (Oper & Operator.RelationalMask) != 0)
895 return CreateNullConstant (right_orig).Resolve (ec);
898 if (right_null_lifted) {
899 right = LiftedNull.Create (left.Type, right.Location);
901 if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask)) != 0)
902 return LiftedNull.CreateFromExpression (res_expr);
905 // Value types and null comparison
907 if (left_unwrap == null || (Oper & Operator.RelationalMask) != 0)
908 return CreateNullConstant (left_orig).Resolve (ec);
914 protected override Expression ResolveOperatorPredefined (EmitContext ec, Binary.PredefinedOperator [] operators, bool primitives_only, Type enum_type)
916 Expression e = base.ResolveOperatorPredefined (ec, operators, primitives_only, enum_type);
918 if (e == this || enum_type != null)
919 return LiftResult (ec, e);
922 // 7.9.9 Equality operators and null
924 // The == and != operators permit one operand to be a value of a nullable type and
925 // the other to be the null literal, even if no predefined or user-defined operator
926 // (in unlifted or lifted form) exists for the operation.
928 if (e == null && (Oper & Operator.EqualityMask) != 0) {
929 if ((left_null_lifted && right_unwrap != null) || (right_null_lifted && left_unwrap != null))
930 return LiftResult (ec, this);
936 protected override Expression ResolveUserOperator (EmitContext ec, Type l, Type r)
938 Expression expr = base.ResolveUserOperator (ec, l, r);
942 expr = LiftResult (ec, expr);
943 if (expr is Constant)
947 user_operator = expr;
952 public class NullCoalescingOperator : Expression
954 Expression left, right;
957 public NullCoalescingOperator (Expression left, Expression right, Location loc)
964 public override Expression CreateExpressionTree (EmitContext ec)
966 if (left is NullLiteral)
967 Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side");
969 UserCast uc = left as UserCast;
970 Expression conversion = null;
974 ArrayList c_args = new ArrayList (2);
975 c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
976 c_args.Add (new Argument (left.CreateExpressionTree (ec)));
977 conversion = CreateExpressionFactoryCall ("Lambda", c_args);
980 ArrayList args = new ArrayList (3);
981 args.Add (new Argument (left.CreateExpressionTree (ec)));
982 args.Add (new Argument (right.CreateExpressionTree (ec)));
983 if (conversion != null)
984 args.Add (new Argument (conversion));
986 return CreateExpressionFactoryCall ("Coalesce", args);
989 public override Expression DoResolve (EmitContext ec)
994 left = left.Resolve (ec);
995 right = right.Resolve (ec);
997 if (left == null || right == null)
1000 eclass = ExprClass.Value;
1001 Type ltype = left.Type, rtype = right.Type;
1004 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
1005 // the result is underlying type of left
1007 if (TypeManager.IsNullableType (ltype) && left.eclass != ExprClass.MethodGroup) {
1008 unwrap = Unwrap.Create (left, ec);
1012 if (Convert.ImplicitConversionExists (ec, right, unwrap.Type)) {
1015 right = Convert.ImplicitConversion (ec, right, type, loc);
1018 } else if (TypeManager.IsReferenceType (ltype) && right.eclass != ExprClass.MethodGroup) {
1019 if (Convert.ImplicitConversionExists (ec, right, ltype)) {
1021 // Reduce (constant ?? expr) to constant
1023 Constant lc = left as Constant;
1024 if (lc != null && !lc.IsDefaultValue)
1025 return new SideEffectConstant (lc, right, loc).Resolve (ec);
1028 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1030 if (right.IsNull || lc != null)
1031 return ReducedExpression.Create (lc != null ? right : left, this).Resolve (ec);
1033 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1038 Binary.Error_OperatorCannotBeApplied (left, right, "??", loc);
1042 if (!Convert.ImplicitConversionExists (ec, unwrap != null ? unwrap : left, rtype)) {
1043 Binary.Error_OperatorCannotBeApplied (left, right, "??", loc);
1048 // Reduce (null ?? right) to right
1051 return ReducedExpression.Create (right, this).Resolve (ec);
1053 left = Convert.ImplicitConversion (ec, unwrap != null ? unwrap : left, rtype, loc);
1058 public override void Emit (EmitContext ec)
1060 ILGenerator ig = ec.ig;
1062 Label end_label = ig.DefineLabel ();
1064 if (unwrap != null) {
1065 Label is_null_label = ig.DefineLabel ();
1067 unwrap.EmitCheck (ec);
1068 ig.Emit (OpCodes.Brfalse, is_null_label);
1071 ig.Emit (OpCodes.Br, end_label);
1073 ig.MarkLabel (is_null_label);
1076 ig.MarkLabel (end_label);
1082 ig.Emit (OpCodes.Dup);
1083 ig.Emit (OpCodes.Brtrue, end_label);
1085 ig.Emit (OpCodes.Pop);
1088 ig.MarkLabel (end_label);
1091 protected override void CloneTo (CloneContext clonectx, Expression t)
1093 NullCoalescingOperator target = (NullCoalescingOperator) t;
1095 target.left = left.Clone (clonectx);
1096 target.right = right.Clone (clonectx);
1100 public class LiftedUnaryMutator : ExpressionStatement
1102 public readonly UnaryMutator.Mode Mode;
1104 UnaryMutator underlying;
1107 public LiftedUnaryMutator (UnaryMutator.Mode mode, Expression expr, Location loc)
1113 eclass = ExprClass.Value;
1116 public override Expression CreateExpressionTree (EmitContext ec)
1118 return new SimpleAssign (this, this).CreateExpressionTree (ec);
1121 public override Expression DoResolve (EmitContext ec)
1123 expr = expr.Resolve (ec);
1127 unwrap = Unwrap.Create (expr, ec);
1131 underlying = (UnaryMutator) new UnaryMutator (Mode, unwrap, loc).Resolve (ec);
1132 if (underlying == null)
1139 void DoEmit (EmitContext ec, bool is_expr)
1141 ILGenerator ig = ec.ig;
1142 Label is_null_label = ig.DefineLabel ();
1143 Label end_label = ig.DefineLabel ();
1145 unwrap.EmitCheck (ec);
1146 ig.Emit (OpCodes.Brfalse, is_null_label);
1149 underlying.Emit (ec);
1150 ig.Emit (OpCodes.Br_S, end_label);
1152 underlying.EmitStatement (ec);
1155 ig.MarkLabel (is_null_label);
1157 LiftedNull.Create (type, loc).Emit (ec);
1159 ig.MarkLabel (end_label);
1162 public override void Emit (EmitContext ec)
1167 public override void EmitStatement (EmitContext ec)