2009-12-09 Jb Evain <jbevain@novell.com>
[mcs.git] / mcs / nullable.cs
blob1c8c621dee5c43594af9cefc8cfe00795c5d876c
1 //
2 // nullable.cs: Nullable types support
3 //
4 // Authors: Martin Baulig (martin@ximian.com)
5 // Miguel de Icaza (miguel@ximian.com)
6 // Marek Safar (marek.safar@gmail.com)
7 //
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 //
10 // Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
11 // Copyright 2004-2008 Novell, Inc
14 using System;
15 using System.Reflection;
16 using System.Reflection.Emit;
18 namespace Mono.CSharp.Nullable
20 public class NullableType : TypeExpr
22 TypeExpr underlying;
24 public NullableType (TypeExpr underlying, Location l)
26 this.underlying = underlying;
27 loc = l;
29 eclass = ExprClass.Type;
32 public NullableType (Type type, Location loc)
33 : this (new TypeExpression (type, loc), loc)
34 { }
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)
65 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);
83 return;
86 #if MS_COMPATIBLE
87 if (TypeManager.IsBeingCompiled (UnderlyingType)) {
88 ConstructorInfo cinfo = TypeManager.DropGenericTypeArguments (type).GetConstructors ()[0];
89 Constructor = TypeBuilder.GetConstructor (type, cinfo);
90 return;
92 #endif
94 Constructor = type.GetConstructor (new Type[] { UnderlyingType });
98 public class Unwrap : Expression, IMemoryLocation, IAssignMethod
100 Expression expr;
101 NullableInfo info;
103 LocalTemporary temp;
104 readonly bool useDefaultValue;
106 Unwrap (Expression expr, bool useDefaultValue)
108 this.expr = expr;
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;
123 if (wrap != null)
124 return wrap.Child;
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)
141 return this;
144 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
146 return DoResolve (ec);
149 public override void Emit (EmitContext ec)
151 Store (ec);
152 if (useDefaultValue)
153 Invocation.EmitCall (ec, false, this, info.GetValueOrDefault, null, loc);
154 else
155 Invocation.EmitCall (ec, false, this, info.Value, null, loc);
158 public void EmitCheck (EmitContext ec)
160 Store (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 {
171 get {
172 return expr;
176 public override int GetHashCode ()
178 return expr.GetHashCode ();
181 public override bool IsNull {
182 get {
183 return expr.IsNull;
187 void Store (EmitContext ec)
189 if (expr is VariableReference)
190 return;
192 if (temp != null)
193 return;
195 expr.Emit (ec);
196 LocalVariable.Store (ec);
199 public void Load (EmitContext ec)
201 if (expr is VariableReference)
202 expr.Emit (ec);
203 else
204 LocalVariable.Emit (ec);
207 #if NET_4_0
208 public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx)
210 return expr.MakeExpression (ctx);
212 #endif
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;
226 if (ml != null)
227 ml.AddressOf (ec, mode);
228 else
229 LocalVariable.AddressOf (ec, mode);
233 // Keeps result of non-variable expression
235 LocalTemporary LocalVariable {
236 get {
237 if (temp == null)
238 temp = new LocalTemporary (info.Type);
239 return temp;
243 public void Emit (EmitContext ec, bool leave_copy)
245 if (leave_copy)
246 Load (ec);
248 Emit (ec);
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)
265 this.expr = expr;
266 this.info = info;
267 this.loc = loc;
269 type = info.Type;
270 eclass = ExprClass.Value;
273 public override Expression CreateExpressionTree (ResolveContext ec)
275 throw new NotSupportedException ("ET");
278 protected override Expression DoResolve (ResolveContext ec)
280 return this;
283 public override void Emit (EmitContext ec)
285 expr.Emit (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)
296 : base (expr, 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) {
310 child.Type = type;
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)
331 child.Emit (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;
386 Unwrap unwrap;
388 public Lifted (Expression expr, Unwrap unwrap, Type type)
390 this.expr = expr;
391 this.unwrap = unwrap;
392 this.loc = expr.Location;
393 this.type = type;
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
417 return expr;
420 // Wrap target for T?
421 if (TypeManager.IsNullableType (type)) {
422 expr = Wrap.Create (expr, type);
423 if (expr == null)
424 return null;
426 null_value = LiftedNull.Create (type, loc);
427 } else {
428 null_value = new NullConstant (type, loc);
431 eclass = ExprClass.Value;
432 return this;
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);
444 expr.Emit (ec);
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
461 Unwrap unwrap;
462 Expression user_operator;
464 public LiftedUnaryOperator (Unary.Operator op, Expression expr)
465 : base (op, 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);
488 if (unwrap == null)
489 return null;
491 Expression res = base.ResolveOperator (ec, unwrap);
492 if (res != this) {
493 if (user_operator == null)
494 return res;
495 } else {
496 res = Expr = LiftExpression (ec, Expr);
499 if (res == null)
500 return null;
502 eclass = ExprClass.Value;
503 type = res.Type;
504 return this;
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);
520 } else {
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)
538 return null;
540 expr.Type = lifted_type.Type;
541 return expr;
544 protected override Expression ResolveEnumOperator (ResolveContext ec, Expression expr)
546 expr = base.ResolveEnumOperator (ec, expr);
547 if (expr == null)
548 return null;
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);
557 if (expr == null)
558 return null;
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;
568 return expr;
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,
581 Location loc)
582 : base (op, left, right)
584 this.loc = loc;
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 ());
608 } else {
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);
620 return null;
623 bool use_default_call = (Oper & (Operator.BitwiseMask | Operator.EqualityMask)) != 0;
624 left_orig = left;
625 if (TypeManager.IsNullableType (left.Type)) {
626 left = left_unwrap = Unwrap.Create (left, use_default_call);
627 if (left == null)
628 return null;
631 right_orig = right;
632 if (TypeManager.IsNullableType (right.Type)) {
633 right = right_unwrap = Unwrap.Create (right, use_default_call);
634 if (right == null)
635 return null;
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) {
644 left = right;
645 left_null_lifted = true;
646 type = TypeManager.bool_type;
649 if (right_orig.IsNull) {
650 right = left;
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);
676 // load left
677 ig.MarkLabel (load_left);
679 if (Oper == Operator.BitwiseAnd) {
680 left_unwrap.Load (ec);
681 } else {
682 right_unwrap.Load (ec);
683 right_unwrap = left_unwrap;
685 ig.Emit (OpCodes.Br_S, end_label);
687 // load right
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);
710 return;
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);
719 return;
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);
728 } else {
729 left.Emit (ec);
730 right.Emit (ec);
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);
744 else
745 ig.Emit (OpCodes.Ceq);
746 } else {
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);
758 else
759 ig.Emit (OpCodes.Ldc_I4_0);
761 ig.MarkLabel (end_label);
764 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
766 Emit (ec);
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);
780 return;
783 if ((Oper & Operator.EqualityMask) != 0) {
784 EmitEquality (ec);
785 return;
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);
816 } else {
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);
827 return;
830 if (TypeManager.IsNullableType (l))
831 l = TypeManager.TypeToCoreType (TypeManager.GetTypeArguments (l) [0]);
833 base.EmitOperator (ec, l);
836 bool IsBitwiseBoolean {
837 get {
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)
854 return null;
856 if (left is UserCast || left is TypeCast)
857 left.Type = lifted_type.Type;
858 else
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)
866 return null;
868 if (right is UserCast || right is TypeCast)
869 right.Type = lifted_type.Type;
870 else
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)
878 return 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);
910 return res_expr;
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);
932 return e;
935 protected override Expression ResolveUserOperator (ResolveContext ec, Type l, Type r)
937 Expression expr = base.ResolveUserOperator (ec, l, r);
938 if (expr == null)
939 return null;
941 expr = LiftResult (ec, expr);
942 if (expr is Constant)
943 return expr;
945 type = expr.Type;
946 user_operator = expr;
947 return this;
951 public class NullCoalescingOperator : Expression
953 Expression left, right;
954 Unwrap unwrap;
956 public NullCoalescingOperator (Expression left, Expression right, Location loc)
958 this.left = left;
959 this.right = right;
960 this.loc = 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;
970 if (uc != null) {
971 left = uc.Source;
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)
992 return null;
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);
1002 if (unwrap == null)
1003 return null;
1005 if (Convert.ImplicitConversionExists (ec, right, unwrap.Type)) {
1006 left = unwrap;
1007 type = left.Type;
1008 right = Convert.ImplicitConversion (ec, right, type, loc);
1009 return this;
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);
1027 type = left.Type;
1028 return this;
1030 } else {
1031 return null;
1034 Type rtype = right.Type;
1035 if (!Convert.ImplicitConversionExists (ec, unwrap != null ? unwrap : left, rtype) || right.eclass == ExprClass.MethodGroup)
1036 return null;
1039 // Reduce (null ?? right) to right
1041 if (left.IsNull)
1042 return ReducedExpression.Create (right, this);
1044 left = Convert.ImplicitConversion (ec, unwrap != null ? unwrap : left, rtype, loc);
1045 type = rtype;
1046 return this;
1049 protected override Expression DoResolve (ResolveContext ec)
1051 left = left.Resolve (ec);
1052 right = right.Resolve (ec);
1054 if (left == null || right == null)
1055 return null;
1057 eclass = ExprClass.Value;
1059 Expression e = ConvertExpression (ec);
1060 if (e == null) {
1061 Binary.Error_OperatorCannotBeApplied (ec, left, right, "??", loc);
1062 return null;
1065 return e;
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);
1080 left.Emit (ec);
1081 ig.Emit (OpCodes.Br, end_label);
1083 ig.MarkLabel (is_null_label);
1084 right.Emit (ec);
1086 ig.MarkLabel (end_label);
1087 return;
1090 left.Emit (ec);
1092 ig.Emit (OpCodes.Dup);
1093 ig.Emit (OpCodes.Brtrue, end_label);
1095 ig.Emit (OpCodes.Pop);
1096 right.Emit (ec);
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;
1120 Expression expr;
1121 UnaryMutator underlying;
1122 Unwrap unwrap;
1124 public LiftedUnaryMutator (UnaryMutator.Mode mode, Expression expr, Location loc)
1126 this.expr = expr;
1127 this.Mode = mode;
1128 this.loc = 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);
1139 if (expr == null)
1140 return null;
1142 unwrap = Unwrap.Create (expr, false);
1143 if (unwrap == null)
1144 return null;
1146 underlying = (UnaryMutator) new UnaryMutator (Mode, unwrap).Resolve (ec);
1147 if (underlying == null)
1148 return null;
1151 eclass = ExprClass.Value;
1152 type = expr.Type;
1153 return this;
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);
1165 if (is_expr) {
1166 underlying.Emit (ec);
1167 ig.Emit (OpCodes.Br_S, end_label);
1168 } else {
1169 underlying.EmitStatement (ec);
1172 ig.MarkLabel (is_null_label);
1173 if (is_expr)
1174 LiftedNull.Create (type, loc).Emit (ec);
1176 ig.MarkLabel (end_label);
1179 public override void Emit (EmitContext ec)
1181 DoEmit (ec, true);
1184 public override void EmitStatement (EmitContext ec)
1186 DoEmit (ec, false);