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 MethodInfo HasValue;
57 public MethodInfo Value;
58 public MethodInfo GetValueOrDefault;
59 public ConstructorInfo Constructor;
61 public NullableInfo (Type type)
64 UnderlyingType = TypeManager.GetTypeArguments (type) [0];
66 PropertyInfo has_value_pi = TypeManager.GetPredefinedProperty (type, "HasValue", Location.Null, Type.EmptyTypes);
67 PropertyInfo value_pi = TypeManager.GetPredefinedProperty (type, "Value", Location.Null, Type.EmptyTypes);
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 override void MutateHoistedGenericType (AnonymousMethodStorey storey)
189 type = storey.MutateType (type);
190 info.Constructor = storey.MutateConstructor (info.Constructor);
191 info.HasValue = storey.MutateGenericMethod (info.HasValue);
192 info.GetValueOrDefault = storey.MutateGenericMethod (info.GetValueOrDefault);
193 info.Value = storey.MutateGenericMethod (info.Value);
196 public void AddressOf (EmitContext ec, AddressOp mode)
198 IMemoryLocation ml = expr as VariableReference;
200 ml.AddressOf (ec, mode);
202 LocalVariable.AddressOf (ec, mode);
206 // Keeps result of non-variable expression
208 LocalTemporary LocalVariable {
211 temp = new LocalTemporary (info.Type);
216 public void Emit (EmitContext ec, bool leave_copy)
224 public void EmitAssign (EmitContext ec, Expression source,
225 bool leave_copy, bool prepare_for_load)
227 InternalWrap wrap = new InternalWrap (source, info, loc);
228 ((IAssignMethod) expr).EmitAssign (ec, wrap, leave_copy, false);
231 protected class InternalWrap : Expression
233 public Expression expr;
234 public NullableInfo info;
236 public InternalWrap (Expression expr, NullableInfo info, Location loc)
243 eclass = ExprClass.Value;
246 public override Expression CreateExpressionTree (EmitContext ec)
248 throw new NotSupportedException ("ET");
251 public override Expression DoResolve (EmitContext ec)
256 public override void Emit (EmitContext ec)
259 ec.ig.Emit (OpCodes.Newobj, info.Constructor);
264 public class Wrap : TypeCast
266 readonly NullableInfo info;
268 protected Wrap (Expression expr, Type type)
271 info = new NullableInfo (type);
272 eclass = ExprClass.Value;
275 public static Expression Create (Expression expr, Type type)
278 // Avoid unwraping and wraping of the same type
280 Unwrap unwrap = expr as Unwrap;
281 if (unwrap != null && TypeManager.IsEqual (expr.Type, TypeManager.GetTypeArguments (type) [0]))
282 return unwrap.Original;
284 return new Wrap (expr, type);
287 public override void Emit (EmitContext ec)
290 ec.ig.Emit (OpCodes.Newobj, info.Constructor);
295 // Represents null literal lifted to nullable type
297 public class LiftedNull : EmptyConstantCast, IMemoryLocation
299 private LiftedNull (Type nullable_type, Location loc)
300 : base (new NullLiteral (loc), nullable_type)
302 eclass = ExprClass.Value;
305 public static Constant Create (Type nullable, Location loc)
307 return new LiftedNull (nullable, loc);
310 public static Expression CreateFromExpression (Expression e)
312 Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'",
313 TypeManager.CSharpName (e.Type));
315 return ReducedExpression.Create (Create (e.Type, e.Location), e);
318 public override Expression CreateExpressionTree (EmitContext ec)
320 ArrayList args = new ArrayList (2);
321 args.Add (new Argument (this));
322 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
324 return CreateExpressionFactoryCall ("Constant", args);
327 public override void Emit (EmitContext ec)
329 // TODO: generate less temporary variables
330 LocalTemporary value_target = new LocalTemporary (type);
332 value_target.AddressOf (ec, AddressOp.Store);
333 ec.ig.Emit (OpCodes.Initobj, type);
334 value_target.Emit (ec);
337 public void AddressOf (EmitContext ec, AddressOp Mode)
339 LocalTemporary value_target = new LocalTemporary (type);
341 value_target.AddressOf (ec, AddressOp.Store);
342 ec.ig.Emit (OpCodes.Initobj, type);
343 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
347 public abstract class Lifted : Expression, IMemoryLocation
349 Expression expr, underlying, wrap, null_value;
352 protected Lifted (Expression expr, Location loc)
358 public override Expression CreateExpressionTree (EmitContext ec)
360 ArrayList args = new ArrayList (2);
361 args.Add (new Argument (expr.CreateExpressionTree (ec)));
362 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
363 return CreateExpressionFactoryCall ("Convert", args);
366 public override Expression DoResolve (EmitContext ec)
368 expr = expr.Resolve (ec);
372 unwrap = Unwrap.Create (expr, ec);
376 underlying = ResolveUnderlying (unwrap, ec);
377 if (underlying == null)
380 TypeExpr target_type = new NullableType (underlying.Type, loc);
381 target_type = target_type.ResolveAsTypeTerminal (ec, false);
382 if (target_type == null)
385 wrap = Wrap.Create (underlying, target_type.Type);
389 null_value = LiftedNull.Create (wrap.Type, loc);
392 eclass = ExprClass.Value;
396 protected abstract Expression ResolveUnderlying (Expression unwrap, EmitContext ec);
398 public override void Emit (EmitContext ec)
400 ILGenerator ig = ec.ig;
401 Label is_null_label = ig.DefineLabel ();
402 Label end_label = ig.DefineLabel ();
404 unwrap.EmitCheck (ec);
405 ig.Emit (OpCodes.Brfalse, is_null_label);
408 ig.Emit (OpCodes.Br, end_label);
410 ig.MarkLabel (is_null_label);
411 null_value.Emit (ec);
413 ig.MarkLabel (end_label);
416 public void AddressOf (EmitContext ec, AddressOp mode)
418 unwrap.AddressOf (ec, mode);
422 public class LiftedConversion : Lifted
424 public readonly bool IsUser;
425 public readonly bool IsExplicit;
426 public readonly Type TargetType;
428 public LiftedConversion (Expression expr, Type target_type, bool is_user,
429 bool is_explicit, Location loc)
432 this.IsUser = is_user;
433 this.IsExplicit = is_explicit;
434 this.TargetType = target_type;
437 protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec)
439 Type type = TypeManager.GetTypeArguments (TargetType) [0];
443 return Convert.ExplicitUserConversion (ec, unwrap, type, loc);
445 return Convert.ImplicitUserConversion (ec, unwrap, type, loc);
448 return Convert.ExplicitConversion (ec, unwrap, type, loc);
450 return Convert.ImplicitConversion (ec, unwrap, type, loc);
455 public class LiftedUnaryOperator : Unary, IMemoryLocation
458 Expression user_operator;
460 public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc)
461 : base (op, expr, loc)
465 public void AddressOf (EmitContext ec, AddressOp mode)
467 unwrap.AddressOf (ec, mode);
470 public override Expression CreateExpressionTree (EmitContext ec)
472 if (user_operator != null)
473 return user_operator.CreateExpressionTree (ec);
475 if (Oper == Operator.UnaryPlus)
476 return Expr.CreateExpressionTree (ec);
478 return base.CreateExpressionTree (ec);
481 public override Expression DoResolve (EmitContext ec)
483 if (eclass != ExprClass.Invalid)
486 unwrap = Unwrap.Create (Expr, ec);
490 Expression res = base.ResolveOperator (ec, unwrap);
492 if (user_operator == null)
495 res = Expr = LiftExpression (ec, Expr);
501 eclass = ExprClass.Value;
506 public override void Emit (EmitContext ec)
508 ILGenerator ig = ec.ig;
509 Label is_null_label = ig.DefineLabel ();
510 Label end_label = ig.DefineLabel ();
512 unwrap.EmitCheck (ec);
513 ig.Emit (OpCodes.Brfalse, is_null_label);
515 NullableInfo ni = new NullableInfo (type);
517 if (user_operator != null) {
518 user_operator.Emit (ec);
520 EmitOperator (ec, ni.UnderlyingType);
523 ig.Emit (OpCodes.Newobj, ni.Constructor);
524 ig.Emit (OpCodes.Br_S, end_label);
526 ig.MarkLabel (is_null_label);
527 LiftedNull.Create (type, loc).Emit (ec);
529 ig.MarkLabel (end_label);
532 Expression LiftExpression (EmitContext ec, Expression expr)
534 TypeExpr lifted_type = new NullableType (expr.Type, expr.Location);
535 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
536 if (lifted_type == null)
539 expr.Type = lifted_type.Type;
543 protected override Expression ResolveEnumOperator (EmitContext ec, Expression expr)
545 expr = base.ResolveEnumOperator (ec, expr);
549 Expr = LiftExpression (ec, Expr);
550 return LiftExpression (ec, expr);
553 protected override Expression ResolveUserOperator (EmitContext ec, Expression expr)
555 expr = base.ResolveUserOperator (ec, expr);
560 // When a user operator is of non-nullable type
562 if (Expr is Unwrap) {
563 user_operator = LiftExpression (ec, expr);
564 return user_operator;
571 public class LiftedBinaryOperator : Binary
573 Unwrap left_unwrap, right_unwrap;
574 bool left_null_lifted, right_null_lifted;
575 Expression left_orig, right_orig;
576 Expression user_operator;
577 ConstructorInfo wrap_ctor;
579 public LiftedBinaryOperator (Binary.Operator op, Expression left, Expression right,
581 : base (op, left, right)
586 public override Expression CreateExpressionTree (EmitContext ec)
588 if (user_operator != null)
589 return user_operator.CreateExpressionTree (ec);
591 return base.CreateExpressionTree (ec);
595 // CSC 2 has this behavior, it allows structs to be compared
596 // with the null literal *outside* of a generics context and
597 // inlines that as true or false.
599 Expression CreateNullConstant (Expression expr)
601 // FIXME: Handle side effect constants
602 Constant c = new BoolConstant (Oper == Operator.Inequality, loc);
604 if ((Oper & Operator.EqualityMask) != 0) {
605 Report.Warning (472, 2, loc, "The result of comparing value type `{0}' with null is `{1}'",
606 expr.GetSignatureForError (), c.AsString ());
608 Report.Warning (464, 2, loc, "The result of comparing type `{0}' with null is always `{1}'",
609 expr.GetSignatureForError (), c.AsString ());
612 return ReducedExpression.Create (c, this);
615 public override Expression DoResolve (EmitContext ec)
617 if (eclass != ExprClass.Invalid)
620 if ((Oper & Operator.LogicalMask) != 0) {
621 Error_OperatorCannotBeApplied (left, right);
626 if (TypeManager.IsNullableType (left.Type)) {
627 left = left_unwrap = Unwrap.Create (left, ec);
633 if (TypeManager.IsNullableType (right.Type)) {
634 right = right_unwrap = Unwrap.Create (right, ec);
640 // Some details are in 6.4.2, 7.2.7
641 // Arguments can be lifted for equal operators when the return type is bool and both
642 // arguments are of same type
644 if (left is NullLiteral) {
646 left_null_lifted = true;
647 type = TypeManager.bool_type;
650 if (right is NullLiteral) {
652 right_null_lifted = true;
653 type = TypeManager.bool_type;
656 eclass = ExprClass.Value;
657 return DoResolveCore (ec, left_orig, right_orig);
660 void EmitBitwiseBoolean (EmitContext ec)
662 ILGenerator ig = ec.ig;
664 Label load_left = ig.DefineLabel ();
665 Label load_right = ig.DefineLabel ();
666 Label end_label = ig.DefineLabel ();
668 left_unwrap.EmitGetValueOrDefault (ec);
669 ig.Emit (OpCodes.Brtrue_S, load_right);
671 right_unwrap.EmitGetValueOrDefault (ec);
672 ig.Emit (OpCodes.Brtrue_S, load_left);
674 left_unwrap.EmitCheck (ec);
675 ig.Emit (OpCodes.Brfalse_S, load_right);
678 ig.MarkLabel (load_left);
680 if (Oper == Operator.BitwiseAnd) {
681 left_unwrap.Load (ec);
683 right_unwrap.Load (ec);
684 right_unwrap = left_unwrap;
686 ig.Emit (OpCodes.Br_S, end_label);
689 ig.MarkLabel (load_right);
690 right_unwrap.Load (ec);
692 ig.MarkLabel (end_label);
696 // Emits optimized equality or inequality operator when possible
698 bool EmitEquality (EmitContext ec)
700 ILGenerator ig = ec.ig;
703 // Either left or right is null
705 if (left_unwrap != null && (right_null_lifted || right.IsNull)) {
706 left_unwrap.EmitCheck (ec);
707 if (Oper == Binary.Operator.Equality) {
708 ig.Emit (OpCodes.Ldc_I4_0);
709 ig.Emit (OpCodes.Ceq);
714 if (right_unwrap != null && (left_null_lifted || left.IsNull)) {
715 right_unwrap.EmitCheck (ec);
716 if (Oper == Binary.Operator.Equality) {
717 ig.Emit (OpCodes.Ldc_I4_0);
718 ig.Emit (OpCodes.Ceq);
723 if (user_operator != null)
726 Label dissimilar_label = ig.DefineLabel ();
727 Label end_label = ig.DefineLabel ();
729 if (left_unwrap != null)
730 left_unwrap.EmitGetValueOrDefault (ec);
734 if (right_unwrap != null)
735 right_unwrap.EmitGetValueOrDefault (ec);
739 ig.Emit (OpCodes.Bne_Un_S, dissimilar_label);
741 if (left_unwrap != null)
742 left_unwrap.EmitCheck (ec);
743 if (right_unwrap != null)
744 right_unwrap.EmitCheck (ec);
746 if (left_unwrap != null && right_unwrap != null) {
747 if (Oper == Operator.Inequality)
748 ig.Emit (OpCodes.Xor);
750 ig.Emit (OpCodes.Ceq);
752 if (Oper == Operator.Inequality) {
753 ig.Emit (OpCodes.Ldc_I4_0);
754 ig.Emit (OpCodes.Ceq);
758 ig.Emit (OpCodes.Br_S, end_label);
760 ig.MarkLabel (dissimilar_label);
761 if (Oper == Operator.Inequality)
762 ig.Emit (OpCodes.Ldc_I4_1);
764 ig.Emit (OpCodes.Ldc_I4_0);
766 ig.MarkLabel (end_label);
770 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
773 ec.ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
776 public override void Emit (EmitContext ec)
779 // Optimize same expression operation
781 if (right_unwrap != null && right.Equals (left))
782 right_unwrap = left_unwrap;
784 if (user_operator == null && IsBitwiseBoolean) {
785 EmitBitwiseBoolean (ec);
789 if ((Oper & Operator.EqualityMask) != 0) {
790 if (EmitEquality (ec))
794 ILGenerator ig = ec.ig;
796 Label is_null_label = ig.DefineLabel ();
797 Label end_label = ig.DefineLabel ();
799 if (left_unwrap != null) {
800 left_unwrap.EmitCheck (ec);
801 ig.Emit (OpCodes.Brfalse, is_null_label);
805 // Don't emit HasValue check when left and right expressions are same
807 if (right_unwrap != null && !left.Equals (right)) {
808 right_unwrap.EmitCheck (ec);
809 ig.Emit (OpCodes.Brfalse, is_null_label);
812 EmitOperator (ec, left.Type);
814 if (wrap_ctor != null)
815 ig.Emit (OpCodes.Newobj, wrap_ctor);
817 ig.Emit (OpCodes.Br_S, end_label);
818 ig.MarkLabel (is_null_label);
820 if ((Oper & Operator.ComparisonMask) != 0) {
822 // Emit true when equality operator both operands are null
823 // or inequality operator operands has only one null
825 if ((Oper == Operator.Equality && left_unwrap != null && right_unwrap != null) ||
826 (Oper == Operator.Inequality && left_unwrap != right_unwrap))
827 ig.Emit (OpCodes.Ldc_I4_1);
829 ig.Emit (OpCodes.Ldc_I4_0);
831 LiftedNull.Create (type, loc).Emit (ec);
834 ig.MarkLabel (end_label);
837 protected override void EmitOperator (EmitContext ec, Type l)
839 if (user_operator != null) {
840 user_operator.Emit (ec);
844 if (TypeManager.IsNullableType (l))
845 l = TypeManager.GetTypeArguments (l) [0];
847 base.EmitOperator (ec, l);
850 bool IsBitwiseBoolean {
852 return (Oper & Operator.BitwiseMask) != 0 && left_unwrap != null && right_unwrap != null &&
853 left_unwrap.Type == TypeManager.bool_type && right_unwrap.Type == TypeManager.bool_type;
857 Expression LiftResult (EmitContext ec, Expression res_expr)
859 TypeExpr lifted_type;
862 // Avoid double conversion
864 if (left_unwrap == null || left_null_lifted || !TypeManager.IsEqual (left_unwrap.Type, left.Type) || (left_unwrap != null && right_null_lifted)) {
865 lifted_type = new NullableType (left.Type, loc);
866 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
867 if (lifted_type == null)
870 if (left is UserCast || left is TypeCast)
871 left.Type = lifted_type.Type;
873 left = EmptyCast.Create (left, lifted_type.Type);
876 if (right_unwrap == null || right_null_lifted || !TypeManager.IsEqual (right_unwrap.Type, right.Type) || (right_unwrap != null && left_null_lifted)) {
877 lifted_type = new NullableType (right.Type, loc);
878 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
879 if (lifted_type == null)
882 if (right is UserCast || right is TypeCast)
883 right.Type = lifted_type.Type;
885 right = EmptyCast.Create (right, lifted_type.Type);
888 if ((Oper & Operator.ComparisonMask) == 0) {
889 lifted_type = new NullableType (res_expr.Type, loc);
890 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
891 if (lifted_type == null)
894 wrap_ctor = new NullableInfo (lifted_type.Type).Constructor;
895 type = res_expr.Type = lifted_type.Type;
898 if (left_null_lifted) {
899 left = LiftedNull.Create (right.Type, left.Location);
901 if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask)) != 0)
902 return LiftedNull.CreateFromExpression (res_expr);
905 // Value types and null comparison
907 if (right_unwrap == null || (Oper & Operator.RelationalMask) != 0)
908 return CreateNullConstant (right_orig).Resolve (ec);
911 if (right_null_lifted) {
912 right = LiftedNull.Create (left.Type, right.Location);
914 if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask)) != 0)
915 return LiftedNull.CreateFromExpression (res_expr);
918 // Value types and null comparison
920 if (left_unwrap == null || (Oper & Operator.RelationalMask) != 0)
921 return CreateNullConstant (left_orig).Resolve (ec);
927 protected override Expression ResolveOperatorPredefined (EmitContext ec, Binary.PredefinedOperator [] operators, bool primitives_only, Type enum_type)
929 Expression e = base.ResolveOperatorPredefined (ec, operators, primitives_only, enum_type);
931 if (e == this || enum_type != null)
932 return LiftResult (ec, e);
935 // 7.9.9 Equality operators and null
937 // The == and != operators permit one operand to be a value of a nullable type and
938 // the other to be the null literal, even if no predefined or user-defined operator
939 // (in unlifted or lifted form) exists for the operation.
941 if (e == null && (Oper & Operator.EqualityMask) != 0) {
942 if ((left_null_lifted && right_unwrap != null) || (right_null_lifted && left_unwrap != null))
943 return LiftResult (ec, this);
949 protected override Expression ResolveUserOperator (EmitContext ec, Type l, Type r)
951 Expression expr = base.ResolveUserOperator (ec, l, r);
955 expr = LiftResult (ec, expr);
956 if (expr is Constant)
960 user_operator = expr;
965 public class NullCoalescingOperator : Expression
967 Expression left, right;
970 public NullCoalescingOperator (Expression left, Expression right, Location loc)
977 public override Expression CreateExpressionTree (EmitContext ec)
979 if (left is NullLiteral)
980 Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side");
982 UserCast uc = left as UserCast;
983 Expression conversion = null;
987 ArrayList c_args = new ArrayList (2);
988 c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
989 c_args.Add (new Argument (left.CreateExpressionTree (ec)));
990 conversion = CreateExpressionFactoryCall ("Lambda", c_args);
993 ArrayList args = new ArrayList (3);
994 args.Add (new Argument (left.CreateExpressionTree (ec)));
995 args.Add (new Argument (right.CreateExpressionTree (ec)));
996 if (conversion != null)
997 args.Add (new Argument (conversion));
999 return CreateExpressionFactoryCall ("Coalesce", args);
1002 public override Expression DoResolve (EmitContext ec)
1007 left = left.Resolve (ec);
1008 right = right.Resolve (ec);
1010 if (left == null || right == null)
1013 eclass = ExprClass.Value;
1014 Type ltype = left.Type, rtype = right.Type;
1017 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
1018 // the result is underlying type of left
1020 if (TypeManager.IsNullableType (ltype) && left.eclass != ExprClass.MethodGroup) {
1021 unwrap = Unwrap.Create (left, ec);
1025 if (Convert.ImplicitConversionExists (ec, right, unwrap.Type)) {
1028 right = Convert.ImplicitConversion (ec, right, type, loc);
1031 } else if (TypeManager.IsReferenceType (ltype) && right.eclass != ExprClass.MethodGroup) {
1032 if (Convert.ImplicitConversionExists (ec, right, ltype)) {
1034 // Reduce (constant ?? expr) to constant
1036 Constant lc = left as Constant;
1037 if (lc != null && !lc.IsDefaultValue)
1038 return new SideEffectConstant (lc, right, loc).Resolve (ec);
1041 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1043 if (right.IsNull || lc != null)
1044 return ReducedExpression.Create (lc != null ? right : left, this).Resolve (ec);
1046 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1051 Binary.Error_OperatorCannotBeApplied (left, right, "??", loc);
1055 if (!Convert.ImplicitConversionExists (ec, unwrap != null ? unwrap : left, rtype)) {
1056 Binary.Error_OperatorCannotBeApplied (left, right, "??", loc);
1061 // Reduce (null ?? right) to right
1064 return ReducedExpression.Create (right, this).Resolve (ec);
1066 left = Convert.ImplicitConversion (ec, unwrap != null ? unwrap : left, rtype, loc);
1071 public override void Emit (EmitContext ec)
1073 ILGenerator ig = ec.ig;
1075 Label end_label = ig.DefineLabel ();
1077 if (unwrap != null) {
1078 Label is_null_label = ig.DefineLabel ();
1080 unwrap.EmitCheck (ec);
1081 ig.Emit (OpCodes.Brfalse, is_null_label);
1084 ig.Emit (OpCodes.Br, end_label);
1086 ig.MarkLabel (is_null_label);
1089 ig.MarkLabel (end_label);
1095 ig.Emit (OpCodes.Dup);
1096 ig.Emit (OpCodes.Brtrue, end_label);
1098 ig.Emit (OpCodes.Pop);
1101 ig.MarkLabel (end_label);
1104 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1106 left.MutateHoistedGenericType (storey);
1107 right.MutateHoistedGenericType (storey);
1108 type = storey.MutateType (type);
1111 protected override void CloneTo (CloneContext clonectx, Expression t)
1113 NullCoalescingOperator target = (NullCoalescingOperator) t;
1115 target.left = left.Clone (clonectx);
1116 target.right = right.Clone (clonectx);
1120 public class LiftedUnaryMutator : ExpressionStatement
1122 public readonly UnaryMutator.Mode Mode;
1124 UnaryMutator underlying;
1127 public LiftedUnaryMutator (UnaryMutator.Mode mode, Expression expr, Location loc)
1133 eclass = ExprClass.Value;
1136 public override Expression CreateExpressionTree (EmitContext ec)
1138 return new SimpleAssign (this, this).CreateExpressionTree (ec);
1141 public override Expression DoResolve (EmitContext ec)
1143 expr = expr.Resolve (ec);
1147 unwrap = Unwrap.Create (expr, ec);
1151 underlying = (UnaryMutator) new UnaryMutator (Mode, unwrap, loc).Resolve (ec);
1152 if (underlying == null)
1159 void DoEmit (EmitContext ec, bool is_expr)
1161 ILGenerator ig = ec.ig;
1162 Label is_null_label = ig.DefineLabel ();
1163 Label end_label = ig.DefineLabel ();
1165 unwrap.EmitCheck (ec);
1166 ig.Emit (OpCodes.Brfalse, is_null_label);
1169 underlying.Emit (ec);
1170 ig.Emit (OpCodes.Br_S, end_label);
1172 underlying.EmitStatement (ec);
1175 ig.MarkLabel (is_null_label);
1177 LiftedNull.Create (type, loc).Emit (ec);
1179 ig.MarkLabel (end_label);
1182 public override void Emit (EmitContext ec)
1187 public override void EmitStatement (EmitContext ec)