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;
18 namespace Mono.CSharp.Nullable
20 public class NullableType : TypeExpr
24 public NullableType (TypeExpr underlying, Location l)
26 this.underlying = underlying;
29 eclass = ExprClass.Type;
32 public NullableType (Type type, Location loc)
33 : this (new TypeExpression (type, loc), loc)
36 protected override TypeExpr DoResolveAsTypeStep (IMemberContext ec)
38 if (TypeManager.generic_nullable_type == null) {
39 TypeManager.generic_nullable_type = TypeManager.CoreLookupType (ec.Compiler,
40 "System", "Nullable`1", Kind.Struct, true);
43 TypeArguments args = new TypeArguments (underlying);
44 GenericTypeExpr ctype = new GenericTypeExpr (TypeManager.generic_nullable_type, args, loc);
45 return ctype.ResolveAsTypeTerminal (ec, false);
48 public override TypeExpr ResolveAsTypeTerminal (IMemberContext ec, bool silent)
50 return ResolveAsBaseTerminal (ec, silent);
54 public sealed class NullableInfo
56 public readonly Type Type;
57 public readonly Type UnderlyingType;
58 public MethodInfo HasValue;
59 public MethodInfo Value;
60 public MethodInfo GetValueOrDefault;
61 public ConstructorInfo Constructor;
63 public NullableInfo (Type type)
66 UnderlyingType = TypeManager.TypeToCoreType (TypeManager.GetTypeArguments (type) [0]);
68 PropertyInfo has_value_pi = TypeManager.GetPredefinedProperty (type, "HasValue", Location.Null, Type.EmptyTypes);
69 PropertyInfo value_pi = TypeManager.GetPredefinedProperty (type, "Value", Location.Null, Type.EmptyTypes);
70 GetValueOrDefault = TypeManager.GetPredefinedMethod (type, "GetValueOrDefault", Location.Null, Type.EmptyTypes);
72 HasValue = has_value_pi.GetGetMethod (false);
73 Value = value_pi.GetGetMethod (false);
75 // When compiling corlib
76 if (TypeManager.IsBeingCompiled (type)) {
77 TypeContainer tc = TypeManager.LookupGenericTypeContainer (type);
79 // TODO: check for correct overload
80 Constructor c = ((Constructor) tc.InstanceConstructors [0]);
82 Constructor = TypeBuilder.GetConstructor (type, c.ConstructorBuilder);
87 if (TypeManager.IsBeingCompiled (UnderlyingType)) {
88 ConstructorInfo cinfo = TypeManager.DropGenericTypeArguments (type).GetConstructors ()[0];
89 Constructor = TypeBuilder.GetConstructor (type, cinfo);
94 Constructor = type.GetConstructor (new Type[] { UnderlyingType });
98 public class Unwrap : Expression, IMemoryLocation, IAssignMethod
104 readonly bool useDefaultValue;
106 Unwrap (Expression expr, bool useDefaultValue)
109 this.loc = expr.Location;
110 this.useDefaultValue = useDefaultValue;
112 info = new NullableInfo (expr.Type);
113 type = info.UnderlyingType;
114 eclass = expr.eclass;
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 Unwrap Create (Expression expr, bool useDefaultValue)
131 return new Unwrap (expr, useDefaultValue);
134 public override Expression CreateExpressionTree (ResolveContext ec)
136 return expr.CreateExpressionTree (ec);
139 protected override Expression DoResolve (ResolveContext ec)
144 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
146 return DoResolve (ec);
149 public override void Emit (EmitContext ec)
153 Invocation.EmitCall (ec, false, this, info.GetValueOrDefault, null, loc);
155 Invocation.EmitCall (ec, false, this, info.Value, null, loc);
158 public void EmitCheck (EmitContext ec)
161 Invocation.EmitCall (ec, false, this, info.HasValue, null, loc);
164 public override bool Equals (object obj)
166 Unwrap uw = obj as Unwrap;
167 return uw != null && expr.Equals (uw.expr);
170 public Expression Original {
176 public override int GetHashCode ()
178 return expr.GetHashCode ();
181 public override bool IsNull {
187 void Store (EmitContext ec)
189 if (expr is VariableReference)
196 LocalVariable.Store (ec);
199 public void Load (EmitContext ec)
201 if (expr is VariableReference)
204 LocalVariable.Emit (ec);
208 public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx)
210 return expr.MakeExpression (ctx);
214 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
216 type = storey.MutateType (type);
217 info.Constructor = storey.MutateConstructor (info.Constructor);
218 info.HasValue = storey.MutateGenericMethod (info.HasValue);
219 info.GetValueOrDefault = storey.MutateGenericMethod (info.GetValueOrDefault);
220 info.Value = storey.MutateGenericMethod (info.Value);
223 public void AddressOf (EmitContext ec, AddressOp mode)
225 IMemoryLocation ml = expr as VariableReference;
227 ml.AddressOf (ec, mode);
229 LocalVariable.AddressOf (ec, mode);
233 // Keeps result of non-variable expression
235 LocalTemporary LocalVariable {
238 temp = new LocalTemporary (info.Type);
243 public void Emit (EmitContext ec, bool leave_copy)
251 public void EmitAssign (EmitContext ec, Expression source,
252 bool leave_copy, bool prepare_for_load)
254 InternalWrap wrap = new InternalWrap (source, info, loc);
255 ((IAssignMethod) expr).EmitAssign (ec, wrap, leave_copy, false);
258 protected class InternalWrap : Expression
260 public Expression expr;
261 public NullableInfo info;
263 public InternalWrap (Expression expr, NullableInfo info, Location loc)
270 eclass = ExprClass.Value;
273 public override Expression CreateExpressionTree (ResolveContext ec)
275 throw new NotSupportedException ("ET");
278 protected override Expression DoResolve (ResolveContext ec)
283 public override void Emit (EmitContext ec)
286 ec.ig.Emit (OpCodes.Newobj, info.Constructor);
291 public class Wrap : TypeCast
293 readonly NullableInfo info;
295 protected Wrap (Expression expr, Type type)
298 info = new NullableInfo (type);
299 eclass = ExprClass.Value;
302 public Expression Child {
303 get { return child; }
306 public override Expression CreateExpressionTree (ResolveContext ec)
308 TypeCast child_cast = child as TypeCast;
309 if (child_cast != null) {
311 return child_cast.CreateExpressionTree (ec);
314 return base.CreateExpressionTree (ec);
317 public static Expression Create (Expression expr, Type type)
320 // Avoid unwraping and wraping of the same type
322 Unwrap unwrap = expr as Unwrap;
323 if (unwrap != null && TypeManager.IsEqual (expr.Type, TypeManager.TypeToCoreType (TypeManager.GetTypeArguments (type) [0])))
324 return unwrap.Original;
326 return new Wrap (expr, type);
329 public override void Emit (EmitContext ec)
332 ec.ig.Emit (OpCodes.Newobj, info.Constructor);
337 // Represents null literal lifted to nullable type
339 public class LiftedNull : NullConstant, IMemoryLocation
341 private LiftedNull (Type nullable_type, Location loc)
342 : base (nullable_type, loc)
344 eclass = ExprClass.Value;
347 public static Constant Create (Type nullable, Location loc)
349 return new LiftedNull (nullable, loc);
352 public static Expression CreateFromExpression (ResolveContext ec, Expression e)
354 ec.Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'",
355 TypeManager.CSharpName (e.Type));
357 return ReducedExpression.Create (Create (e.Type, e.Location), e);
360 public override void Emit (EmitContext ec)
362 // TODO: generate less temporary variables
363 LocalTemporary value_target = new LocalTemporary (type);
365 value_target.AddressOf (ec, AddressOp.Store);
366 ec.ig.Emit (OpCodes.Initobj, type);
367 value_target.Emit (ec);
370 public void AddressOf (EmitContext ec, AddressOp Mode)
372 LocalTemporary value_target = new LocalTemporary (type);
374 value_target.AddressOf (ec, AddressOp.Store);
375 ec.ig.Emit (OpCodes.Initobj, type);
376 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
381 // Generic lifting expression, supports all S/S? -> T/T? cases
383 public class Lifted : Expression, IMemoryLocation
385 Expression expr, null_value;
388 public Lifted (Expression expr, Unwrap unwrap, Type type)
391 this.unwrap = unwrap;
392 this.loc = expr.Location;
396 public Lifted (Expression expr, Expression unwrap, Type type)
397 : this (expr, unwrap as Unwrap, type)
401 public override Expression CreateExpressionTree (ResolveContext ec)
403 return expr.CreateExpressionTree (ec);
406 protected override Expression DoResolve (ResolveContext ec)
409 // It's null when lifting non-nullable type
411 if (unwrap == null) {
412 // S -> T? is wrap only
413 if (TypeManager.IsNullableType (type))
414 return Wrap.Create (expr, type);
416 // S -> T can be simplified
420 // Wrap target for T?
421 if (TypeManager.IsNullableType (type)) {
422 expr = Wrap.Create (expr, type);
426 null_value = LiftedNull.Create (type, loc);
428 null_value = new NullConstant (type, loc);
431 eclass = ExprClass.Value;
435 public override void Emit (EmitContext ec)
437 ILGenerator ig = ec.ig;
438 Label is_null_label = ig.DefineLabel ();
439 Label end_label = ig.DefineLabel ();
441 unwrap.EmitCheck (ec);
442 ig.Emit (OpCodes.Brfalse, is_null_label);
446 ig.Emit (OpCodes.Br, end_label);
447 ig.MarkLabel (is_null_label);
449 null_value.Emit (ec);
450 ig.MarkLabel (end_label);
453 public void AddressOf (EmitContext ec, AddressOp mode)
455 unwrap.AddressOf (ec, mode);
459 public class LiftedUnaryOperator : Unary, IMemoryLocation
462 Expression user_operator;
464 public LiftedUnaryOperator (Unary.Operator op, Expression expr)
469 public void AddressOf (EmitContext ec, AddressOp mode)
471 unwrap.AddressOf (ec, mode);
474 public override Expression CreateExpressionTree (ResolveContext ec)
476 if (user_operator != null)
477 return user_operator.CreateExpressionTree (ec);
479 if (Oper == Operator.UnaryPlus)
480 return Expr.CreateExpressionTree (ec);
482 return base.CreateExpressionTree (ec);
485 protected override Expression DoResolve (ResolveContext ec)
487 unwrap = Unwrap.Create (Expr, false);
491 Expression res = base.ResolveOperator (ec, unwrap);
493 if (user_operator == null)
496 res = Expr = LiftExpression (ec, Expr);
502 eclass = ExprClass.Value;
507 public override void Emit (EmitContext ec)
509 ILGenerator ig = ec.ig;
510 Label is_null_label = ig.DefineLabel ();
511 Label end_label = ig.DefineLabel ();
513 unwrap.EmitCheck (ec);
514 ig.Emit (OpCodes.Brfalse, is_null_label);
516 NullableInfo ni = new NullableInfo (type);
518 if (user_operator != null) {
519 user_operator.Emit (ec);
521 EmitOperator (ec, ni.UnderlyingType);
524 ig.Emit (OpCodes.Newobj, ni.Constructor);
525 ig.Emit (OpCodes.Br_S, end_label);
527 ig.MarkLabel (is_null_label);
528 LiftedNull.Create (type, loc).Emit (ec);
530 ig.MarkLabel (end_label);
533 Expression LiftExpression (ResolveContext ec, Expression expr)
535 TypeExpr lifted_type = new NullableType (expr.Type, expr.Location);
536 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
537 if (lifted_type == null)
540 expr.Type = lifted_type.Type;
544 protected override Expression ResolveEnumOperator (ResolveContext ec, Expression expr)
546 expr = base.ResolveEnumOperator (ec, expr);
550 Expr = LiftExpression (ec, Expr);
551 return LiftExpression (ec, expr);
554 protected override Expression ResolveUserOperator (ResolveContext ec, Expression expr)
556 expr = base.ResolveUserOperator (ec, expr);
561 // When a user operator is of non-nullable type
563 if (Expr is Unwrap) {
564 user_operator = LiftExpression (ec, expr);
565 return user_operator;
572 public class LiftedBinaryOperator : Binary
574 Unwrap left_unwrap, right_unwrap;
575 bool left_null_lifted, right_null_lifted;
576 Expression left_orig, right_orig;
577 Expression user_operator;
578 ConstructorInfo wrap_ctor;
580 public LiftedBinaryOperator (Binary.Operator op, Expression left, Expression right,
582 : base (op, left, right)
587 public override Expression CreateExpressionTree (ResolveContext ec)
589 if (user_operator != null)
590 return user_operator.CreateExpressionTree (ec);
592 return base.CreateExpressionTree (ec);
596 // CSC 2 has this behavior, it allows structs to be compared
597 // with the null literal *outside* of a generics context and
598 // inlines that as true or false.
600 Expression CreateNullConstant (ResolveContext ec, Expression expr)
602 // FIXME: Handle side effect constants
603 Constant c = new BoolConstant (Oper == Operator.Inequality, loc).Resolve (ec);
605 if ((Oper & Operator.EqualityMask) != 0) {
606 ec.Report.Warning (472, 2, loc, "The result of comparing value type `{0}' with null is `{1}'",
607 TypeManager.CSharpName (expr.Type), c.AsString ());
609 ec.Report.Warning (464, 2, loc, "The result of comparing type `{0}' with null is always `{1}'",
610 TypeManager.CSharpName (expr.Type), c.AsString ());
613 return ReducedExpression.Create (c, this);
616 protected override Expression DoResolve (ResolveContext ec)
618 if ((Oper & Operator.LogicalMask) != 0) {
619 Error_OperatorCannotBeApplied (ec, left, right);
623 bool use_default_call = (Oper & (Operator.BitwiseMask | Operator.EqualityMask)) != 0;
625 if (TypeManager.IsNullableType (left.Type)) {
626 left = left_unwrap = Unwrap.Create (left, use_default_call);
632 if (TypeManager.IsNullableType (right.Type)) {
633 right = right_unwrap = Unwrap.Create (right, use_default_call);
639 // Some details are in 6.4.2, 7.2.7
640 // Arguments can be lifted for equal operators when the return type is bool and both
641 // arguments are of same type
643 if (left_orig.IsNull) {
645 left_null_lifted = true;
646 type = TypeManager.bool_type;
649 if (right_orig.IsNull) {
651 right_null_lifted = true;
652 type = TypeManager.bool_type;
655 eclass = ExprClass.Value;
656 return DoResolveCore (ec, left_orig, right_orig);
659 void EmitBitwiseBoolean (EmitContext ec)
661 ILGenerator ig = ec.ig;
663 Label load_left = ig.DefineLabel ();
664 Label load_right = ig.DefineLabel ();
665 Label end_label = ig.DefineLabel ();
667 left_unwrap.Emit (ec);
668 ig.Emit (OpCodes.Brtrue_S, load_right);
670 right_unwrap.Emit (ec);
671 ig.Emit (OpCodes.Brtrue_S, load_left);
673 left_unwrap.EmitCheck (ec);
674 ig.Emit (OpCodes.Brfalse_S, load_right);
677 ig.MarkLabel (load_left);
679 if (Oper == Operator.BitwiseAnd) {
680 left_unwrap.Load (ec);
682 right_unwrap.Load (ec);
683 right_unwrap = left_unwrap;
685 ig.Emit (OpCodes.Br_S, end_label);
688 ig.MarkLabel (load_right);
689 right_unwrap.Load (ec);
691 ig.MarkLabel (end_label);
695 // Emits optimized equality or inequality operator when possible
697 void EmitEquality (EmitContext ec)
699 ILGenerator ig = ec.ig;
702 // Either left or right is null
704 if (left_unwrap != null && (right_null_lifted || right.IsNull)) {
705 left_unwrap.EmitCheck (ec);
706 if (Oper == Binary.Operator.Equality) {
707 ig.Emit (OpCodes.Ldc_I4_0);
708 ig.Emit (OpCodes.Ceq);
713 if (right_unwrap != null && (left_null_lifted || left.IsNull)) {
714 right_unwrap.EmitCheck (ec);
715 if (Oper == Binary.Operator.Equality) {
716 ig.Emit (OpCodes.Ldc_I4_0);
717 ig.Emit (OpCodes.Ceq);
722 Label dissimilar_label = ig.DefineLabel ();
723 Label end_label = ig.DefineLabel ();
725 if (user_operator != null) {
726 user_operator.Emit (ec);
727 ig.Emit (Oper == Operator.Equality ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, dissimilar_label);
732 ig.Emit (OpCodes.Bne_Un_S, dissimilar_label);
735 if (left_unwrap != null)
736 left_unwrap.EmitCheck (ec);
738 if (right_unwrap != null)
739 right_unwrap.EmitCheck (ec);
741 if (left_unwrap != null && right_unwrap != null) {
742 if (Oper == Operator.Inequality)
743 ig.Emit (OpCodes.Xor);
745 ig.Emit (OpCodes.Ceq);
747 if (Oper == Operator.Inequality) {
748 ig.Emit (OpCodes.Ldc_I4_0);
749 ig.Emit (OpCodes.Ceq);
753 ig.Emit (OpCodes.Br_S, end_label);
755 ig.MarkLabel (dissimilar_label);
756 if (Oper == Operator.Inequality)
757 ig.Emit (OpCodes.Ldc_I4_1);
759 ig.Emit (OpCodes.Ldc_I4_0);
761 ig.MarkLabel (end_label);
764 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
767 ec.ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
770 public override void Emit (EmitContext ec)
773 // Optimize same expression operation
775 if (right_unwrap != null && right.Equals (left))
776 right_unwrap = left_unwrap;
778 if (user_operator == null && IsBitwiseBoolean) {
779 EmitBitwiseBoolean (ec);
783 if ((Oper & Operator.EqualityMask) != 0) {
788 ILGenerator ig = ec.ig;
790 Label is_null_label = ig.DefineLabel ();
791 Label end_label = ig.DefineLabel ();
793 if (left_unwrap != null) {
794 left_unwrap.EmitCheck (ec);
795 ig.Emit (OpCodes.Brfalse, is_null_label);
799 // Don't emit HasValue check when left and right expressions are same
801 if (right_unwrap != null && !left.Equals (right)) {
802 right_unwrap.EmitCheck (ec);
803 ig.Emit (OpCodes.Brfalse, is_null_label);
806 EmitOperator (ec, left.Type);
808 if (wrap_ctor != null)
809 ig.Emit (OpCodes.Newobj, wrap_ctor);
811 ig.Emit (OpCodes.Br_S, end_label);
812 ig.MarkLabel (is_null_label);
814 if ((Oper & Operator.ComparisonMask) != 0) {
815 ig.Emit (OpCodes.Ldc_I4_0);
817 LiftedNull.Create (type, loc).Emit (ec);
820 ig.MarkLabel (end_label);
823 protected override void EmitOperator (EmitContext ec, Type l)
825 if (user_operator != null) {
826 user_operator.Emit (ec);
830 if (TypeManager.IsNullableType (l))
831 l = TypeManager.TypeToCoreType (TypeManager.GetTypeArguments (l) [0]);
833 base.EmitOperator (ec, l);
836 bool IsBitwiseBoolean {
838 return (Oper & Operator.BitwiseMask) != 0 && left_unwrap != null && right_unwrap != null &&
839 left_unwrap.Type == TypeManager.bool_type && right_unwrap.Type == TypeManager.bool_type;
843 Expression LiftResult (ResolveContext ec, Expression res_expr)
845 TypeExpr lifted_type;
848 // Avoid double conversion
850 if (left_unwrap == null || left_null_lifted || !TypeManager.IsEqual (left_unwrap.Type, left.Type) || (left_unwrap != null && right_null_lifted)) {
851 lifted_type = new NullableType (left.Type, loc);
852 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
853 if (lifted_type == null)
856 if (left is UserCast || left is TypeCast)
857 left.Type = lifted_type.Type;
859 left = EmptyCast.Create (left, lifted_type.Type);
862 if (right_unwrap == null || right_null_lifted || !TypeManager.IsEqual (right_unwrap.Type, right.Type) || (right_unwrap != null && left_null_lifted)) {
863 lifted_type = new NullableType (right.Type, loc);
864 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
865 if (lifted_type == null)
868 if (right is UserCast || right is TypeCast)
869 right.Type = lifted_type.Type;
871 right = EmptyCast.Create (right, lifted_type.Type);
874 if ((Oper & Operator.ComparisonMask) == 0) {
875 lifted_type = new NullableType (res_expr.Type, loc);
876 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
877 if (lifted_type == null)
880 wrap_ctor = new NullableInfo (lifted_type.Type).Constructor;
881 type = res_expr.Type = lifted_type.Type;
884 if (left_null_lifted) {
885 left = LiftedNull.Create (right.Type, left.Location);
887 if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0)
888 return LiftedNull.CreateFromExpression (ec, res_expr);
891 // Value types and null comparison
893 if (right_unwrap == null || (Oper & Operator.RelationalMask) != 0)
894 return CreateNullConstant (ec, right_orig).Resolve (ec);
897 if (right_null_lifted) {
898 right = LiftedNull.Create (left.Type, right.Location);
900 if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0)
901 return LiftedNull.CreateFromExpression (ec, res_expr);
904 // Value types and null comparison
906 if (left_unwrap == null || (Oper & Operator.RelationalMask) != 0)
907 return CreateNullConstant (ec, left_orig);
913 protected override Expression ResolveOperatorPredefined (ResolveContext ec, Binary.PredefinedOperator [] operators, bool primitives_only, Type enum_type)
915 Expression e = base.ResolveOperatorPredefined (ec, operators, primitives_only, enum_type);
917 if (e == this || enum_type != null)
918 return LiftResult (ec, e);
921 // 7.9.9 Equality operators and null
923 // The == and != operators permit one operand to be a value of a nullable type and
924 // the other to be the null literal, even if no predefined or user-defined operator
925 // (in unlifted or lifted form) exists for the operation.
927 if (e == null && (Oper & Operator.EqualityMask) != 0) {
928 if ((left_null_lifted && right_unwrap != null) || (right_null_lifted && left_unwrap != null))
929 return LiftResult (ec, this);
935 protected override Expression ResolveUserOperator (ResolveContext ec, Type l, Type r)
937 Expression expr = base.ResolveUserOperator (ec, l, r);
941 expr = LiftResult (ec, expr);
942 if (expr is Constant)
946 user_operator = expr;
951 public class NullCoalescingOperator : Expression
953 Expression left, right;
956 public NullCoalescingOperator (Expression left, Expression right, Location loc)
963 public override Expression CreateExpressionTree (ResolveContext ec)
965 if (left.Type == TypeManager.null_type)
966 ec.Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side");
968 UserCast uc = left as UserCast;
969 Expression conversion = null;
973 Arguments c_args = new Arguments (2);
974 c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
975 c_args.Add (new Argument (left.CreateExpressionTree (ec)));
976 conversion = CreateExpressionFactoryCall (ec, "Lambda", c_args);
979 Arguments args = new Arguments (3);
980 args.Add (new Argument (left.CreateExpressionTree (ec)));
981 args.Add (new Argument (right.CreateExpressionTree (ec)));
982 if (conversion != null)
983 args.Add (new Argument (conversion));
985 return CreateExpressionFactoryCall (ec, "Coalesce", args);
988 Expression ConvertExpression (ResolveContext ec)
990 // TODO: ImplicitConversionExists should take care of this
991 if (left.eclass == ExprClass.MethodGroup)
994 Type ltype = left.Type;
997 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
998 // the result is underlying type of left
1000 if (TypeManager.IsNullableType (ltype)) {
1001 unwrap = Unwrap.Create (left, false);
1005 if (Convert.ImplicitConversionExists (ec, right, unwrap.Type)) {
1008 right = Convert.ImplicitConversion (ec, right, type, loc);
1011 } else if (TypeManager.IsReferenceType (ltype)) {
1012 if (Convert.ImplicitConversionExists (ec, right, ltype)) {
1014 // Reduce (constant ?? expr) to constant
1016 Constant lc = left as Constant;
1017 if (lc != null && !lc.IsDefaultValue)
1018 return new SideEffectConstant (lc, right, loc).Resolve (ec);
1021 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1023 if (right.IsNull || lc != null)
1024 return ReducedExpression.Create (lc != null ? right : left, this);
1026 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1034 Type rtype = right.Type;
1035 if (!Convert.ImplicitConversionExists (ec, unwrap != null ? unwrap : left, rtype) || right.eclass == ExprClass.MethodGroup)
1039 // Reduce (null ?? right) to right
1042 return ReducedExpression.Create (right, this);
1044 left = Convert.ImplicitConversion (ec, unwrap != null ? unwrap : left, rtype, loc);
1049 protected override Expression DoResolve (ResolveContext ec)
1051 left = left.Resolve (ec);
1052 right = right.Resolve (ec);
1054 if (left == null || right == null)
1057 eclass = ExprClass.Value;
1059 Expression e = ConvertExpression (ec);
1061 Binary.Error_OperatorCannotBeApplied (ec, left, right, "??", loc);
1068 public override void Emit (EmitContext ec)
1070 ILGenerator ig = ec.ig;
1072 Label end_label = ig.DefineLabel ();
1074 if (unwrap != null) {
1075 Label is_null_label = ig.DefineLabel ();
1077 unwrap.EmitCheck (ec);
1078 ig.Emit (OpCodes.Brfalse, is_null_label);
1081 ig.Emit (OpCodes.Br, end_label);
1083 ig.MarkLabel (is_null_label);
1086 ig.MarkLabel (end_label);
1092 ig.Emit (OpCodes.Dup);
1093 ig.Emit (OpCodes.Brtrue, end_label);
1095 ig.Emit (OpCodes.Pop);
1098 ig.MarkLabel (end_label);
1101 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1103 left.MutateHoistedGenericType (storey);
1104 right.MutateHoistedGenericType (storey);
1105 type = storey.MutateType (type);
1108 protected override void CloneTo (CloneContext clonectx, Expression t)
1110 NullCoalescingOperator target = (NullCoalescingOperator) t;
1112 target.left = left.Clone (clonectx);
1113 target.right = right.Clone (clonectx);
1117 public class LiftedUnaryMutator : ExpressionStatement
1119 public readonly UnaryMutator.Mode Mode;
1121 UnaryMutator underlying;
1124 public LiftedUnaryMutator (UnaryMutator.Mode mode, Expression expr, Location loc)
1131 public override Expression CreateExpressionTree (ResolveContext ec)
1133 return new SimpleAssign (this, this).CreateExpressionTree (ec);
1136 protected override Expression DoResolve (ResolveContext ec)
1138 expr = expr.Resolve (ec);
1142 unwrap = Unwrap.Create (expr, false);
1146 underlying = (UnaryMutator) new UnaryMutator (Mode, unwrap).Resolve (ec);
1147 if (underlying == null)
1151 eclass = ExprClass.Value;
1156 void DoEmit (EmitContext ec, bool is_expr)
1158 ILGenerator ig = ec.ig;
1159 Label is_null_label = ig.DefineLabel ();
1160 Label end_label = ig.DefineLabel ();
1162 unwrap.EmitCheck (ec);
1163 ig.Emit (OpCodes.Brfalse, is_null_label);
1166 underlying.Emit (ec);
1167 ig.Emit (OpCodes.Br_S, end_label);
1169 underlying.EmitStatement (ec);
1172 ig.MarkLabel (is_null_label);
1174 LiftedNull.Create (type, loc).Emit (ec);
1176 ig.MarkLabel (end_label);
1179 public override void Emit (EmitContext ec)
1184 public override void EmitStatement (EmitContext ec)