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)
268 return new Wrap (expr, type);
271 public override void Emit (EmitContext ec)
274 ec.ig.Emit (OpCodes.Newobj, info.Constructor);
279 // Represents null literal lifted to nullable type
281 public class LiftedNull : EmptyConstantCast, IMemoryLocation
283 private LiftedNull (Type nullable_type, Location loc)
284 : base (new NullLiteral (loc), nullable_type)
286 eclass = ExprClass.Value;
289 public static Constant Create (Type nullable, Location loc)
291 return new LiftedNull (nullable, loc);
294 public static Expression CreateFromExpression (Expression e)
296 Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'",
297 TypeManager.CSharpName (e.Type));
299 return ReducedExpression.Create (Create (e.Type, e.Location), e);
302 public override Expression CreateExpressionTree (EmitContext ec)
304 ArrayList args = new ArrayList (2);
305 args.Add (new Argument (this));
306 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
308 return CreateExpressionFactoryCall ("Constant", args);
311 public override void Emit (EmitContext ec)
313 // TODO: generate less temporary variables
314 LocalTemporary value_target = new LocalTemporary (type);
316 value_target.AddressOf (ec, AddressOp.Store);
317 ec.ig.Emit (OpCodes.Initobj, type);
318 value_target.Emit (ec);
321 public void AddressOf (EmitContext ec, AddressOp Mode)
323 LocalTemporary value_target = new LocalTemporary (type);
325 value_target.AddressOf (ec, AddressOp.Store);
326 ec.ig.Emit (OpCodes.Initobj, type);
327 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
331 public abstract class Lifted : Expression, IMemoryLocation
333 Expression expr, underlying, wrap, null_value;
336 protected Lifted (Expression expr, Location loc)
342 public override Expression CreateExpressionTree (EmitContext ec)
344 ArrayList args = new ArrayList (2);
345 args.Add (new Argument (expr.CreateExpressionTree (ec)));
346 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
347 return CreateExpressionFactoryCall ("Convert", args);
350 public override Expression DoResolve (EmitContext ec)
352 expr = expr.Resolve (ec);
356 unwrap = Unwrap.Create (expr, ec);
360 underlying = ResolveUnderlying (unwrap, ec);
361 if (underlying == null)
364 TypeExpr target_type = new NullableType (underlying.Type, loc);
365 target_type = target_type.ResolveAsTypeTerminal (ec, false);
366 if (target_type == null)
369 wrap = Wrap.Create (underlying, target_type.Type);
373 null_value = LiftedNull.Create (wrap.Type, loc);
376 eclass = ExprClass.Value;
380 protected abstract Expression ResolveUnderlying (Expression unwrap, EmitContext ec);
382 public override void Emit (EmitContext ec)
384 ILGenerator ig = ec.ig;
385 Label is_null_label = ig.DefineLabel ();
386 Label end_label = ig.DefineLabel ();
388 unwrap.EmitCheck (ec);
389 ig.Emit (OpCodes.Brfalse, is_null_label);
392 ig.Emit (OpCodes.Br, end_label);
394 ig.MarkLabel (is_null_label);
395 null_value.Emit (ec);
397 ig.MarkLabel (end_label);
400 public void AddressOf (EmitContext ec, AddressOp mode)
402 unwrap.AddressOf (ec, mode);
406 public class LiftedConversion : Lifted
408 public readonly bool IsUser;
409 public readonly bool IsExplicit;
410 public readonly Type TargetType;
412 public LiftedConversion (Expression expr, Type target_type, bool is_user,
413 bool is_explicit, Location loc)
416 this.IsUser = is_user;
417 this.IsExplicit = is_explicit;
418 this.TargetType = target_type;
421 protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec)
423 Type type = TypeManager.GetTypeArguments (TargetType) [0];
427 return Convert.ExplicitUserConversion (ec, unwrap, type, loc);
429 return Convert.ImplicitUserConversion (ec, unwrap, type, loc);
432 return Convert.ExplicitConversion (ec, unwrap, type, loc);
434 return Convert.ImplicitConversion (ec, unwrap, type, loc);
439 public class LiftedUnaryOperator : Unary, IMemoryLocation
442 Expression user_operator;
444 public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc)
445 : base (op, expr, loc)
449 public void AddressOf (EmitContext ec, AddressOp mode)
451 unwrap.AddressOf (ec, mode);
454 public override Expression CreateExpressionTree (EmitContext ec)
456 if (user_operator != null)
457 return user_operator.CreateExpressionTree (ec);
459 if (Oper == Operator.UnaryPlus)
460 return Expr.CreateExpressionTree (ec);
462 return base.CreateExpressionTree (ec);
465 public override Expression DoResolve (EmitContext ec)
467 if (eclass != ExprClass.Invalid)
470 unwrap = Unwrap.Create (Expr, ec);
474 Expression res = base.ResolveOperator (ec, unwrap);
476 if (user_operator == null)
479 res = Expr = LiftExpression (ec, Expr);
485 eclass = ExprClass.Value;
490 public override void Emit (EmitContext ec)
492 ILGenerator ig = ec.ig;
493 Label is_null_label = ig.DefineLabel ();
494 Label end_label = ig.DefineLabel ();
496 unwrap.EmitCheck (ec);
497 ig.Emit (OpCodes.Brfalse, is_null_label);
499 NullableInfo ni = new NullableInfo (type);
501 if (user_operator != null) {
502 user_operator.Emit (ec);
504 EmitOperator (ec, ni.UnderlyingType);
507 ig.Emit (OpCodes.Newobj, ni.Constructor);
508 ig.Emit (OpCodes.Br_S, end_label);
510 ig.MarkLabel (is_null_label);
511 LiftedNull.Create (type, loc).Emit (ec);
513 ig.MarkLabel (end_label);
516 Expression LiftExpression (EmitContext ec, Expression expr)
518 TypeExpr lifted_type = new NullableType (expr.Type, expr.Location);
519 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
520 if (lifted_type == null)
523 expr.Type = lifted_type.Type;
527 protected override Expression ResolveEnumOperator (EmitContext ec, Expression expr)
529 expr = base.ResolveEnumOperator (ec, expr);
533 Expr = LiftExpression (ec, Expr);
534 return LiftExpression (ec, expr);
537 protected override Expression ResolveUserOperator (EmitContext ec, Expression expr)
539 expr = base.ResolveUserOperator (ec, expr);
543 user_operator = LiftExpression (ec, expr);
544 return user_operator;
548 public class LiftedBinaryOperator : Binary
550 Unwrap left_unwrap, right_unwrap;
551 bool left_null_lifted, right_null_lifted;
552 Expression left_orig, right_orig;
553 Expression user_operator;
554 ConstructorInfo wrap_ctor;
556 public LiftedBinaryOperator (Binary.Operator op, Expression left, Expression right,
558 : base (op, left, right)
563 public override Expression CreateExpressionTree (EmitContext ec)
565 if (user_operator != null)
566 return user_operator.CreateExpressionTree (ec);
568 return base.CreateExpressionTree (ec);
572 // CSC 2 has this behavior, it allows structs to be compared
573 // with the null literal *outside* of a generics context and
574 // inlines that as true or false.
576 Expression CreateNullConstant (Expression expr)
578 // FIXME: Handle side effect constants
579 Constant c = new BoolConstant (Oper == Operator.Inequality, loc);
581 if ((Oper & Operator.EqualityMask) != 0) {
582 Report.Warning (472, 2, loc, "The result of comparing `{0}' against null is always `{1}'. " +
583 "This operation is undocumented and it is temporary supported for compatibility reasons only",
584 expr.GetSignatureForError (), c.AsString ());
586 Report.Warning (464, 2, loc, "The result of comparing type `{0}' against null is always `{1}'",
587 expr.GetSignatureForError (), c.AsString ());
590 return ReducedExpression.Create (c, this);
593 public override Expression DoResolve (EmitContext ec)
595 if (eclass != ExprClass.Invalid)
598 if ((Oper & Operator.LogicalMask) != 0) {
599 Error_OperatorCannotBeApplied (left, right);
604 if (TypeManager.IsNullableType (left.Type)) {
605 left = left_unwrap = Unwrap.Create (left, ec);
611 if (TypeManager.IsNullableType (right.Type)) {
612 right = right_unwrap = Unwrap.Create (right, ec);
618 // Some details are in 6.4.2, 7.2.7
619 // Arguments can be lifted for equal operators when the return type is bool and both
620 // arguments are of same type
622 if (left is NullLiteral) {
624 left_null_lifted = true;
625 type = TypeManager.bool_type;
628 if (right is NullLiteral) {
630 right_null_lifted = true;
631 type = TypeManager.bool_type;
634 eclass = ExprClass.Value;
635 return DoResolveCore (ec, left_orig, right_orig);
638 void EmitBitwiseBoolean (EmitContext ec)
640 ILGenerator ig = ec.ig;
642 Label load_left = ig.DefineLabel ();
643 Label load_right = ig.DefineLabel ();
644 Label end_label = ig.DefineLabel ();
646 left_unwrap.EmitGetValueOrDefault (ec);
647 ig.Emit (OpCodes.Brtrue_S, load_right);
649 right_unwrap.EmitGetValueOrDefault (ec);
650 ig.Emit (OpCodes.Brtrue_S, load_left);
652 left_unwrap.EmitCheck (ec);
653 ig.Emit (OpCodes.Brfalse_S, load_right);
656 ig.MarkLabel (load_left);
658 if (Oper == Operator.BitwiseAnd) {
659 left_unwrap.Load (ec);
661 right_unwrap.Load (ec);
662 right_unwrap = left_unwrap;
664 ig.Emit (OpCodes.Br_S, end_label);
667 ig.MarkLabel (load_right);
668 right_unwrap.Load (ec);
670 ig.MarkLabel (end_label);
674 // Emits optimized equality or inequality operator when possible
676 bool EmitEquality (EmitContext ec)
678 ILGenerator ig = ec.ig;
681 // Either left or right is null
683 if (left_unwrap != null && (right_null_lifted || right.IsNull)) {
684 left_unwrap.EmitCheck (ec);
685 if (Oper == Binary.Operator.Equality) {
686 ig.Emit (OpCodes.Ldc_I4_0);
687 ig.Emit (OpCodes.Ceq);
692 if (right_unwrap != null && (left_null_lifted || left.IsNull)) {
693 right_unwrap.EmitCheck (ec);
694 if (Oper == Binary.Operator.Equality) {
695 ig.Emit (OpCodes.Ldc_I4_0);
696 ig.Emit (OpCodes.Ceq);
701 if (user_operator != null)
704 Label dissimilar_label = ig.DefineLabel ();
705 Label end_label = ig.DefineLabel ();
707 if (left_unwrap != null)
708 left_unwrap.EmitGetValueOrDefault (ec);
712 if (right_unwrap != null)
713 right_unwrap.EmitGetValueOrDefault (ec);
717 ig.Emit (OpCodes.Bne_Un_S, dissimilar_label);
719 if (left_unwrap != null)
720 left_unwrap.EmitCheck (ec);
721 if (right_unwrap != null)
722 right_unwrap.EmitCheck (ec);
724 if (left_unwrap != null && right_unwrap != null) {
725 if (Oper == Operator.Inequality)
726 ig.Emit (OpCodes.Xor);
728 ig.Emit (OpCodes.Ceq);
730 if (Oper == Operator.Inequality) {
731 ig.Emit (OpCodes.Ldc_I4_0);
732 ig.Emit (OpCodes.Ceq);
736 ig.Emit (OpCodes.Br_S, end_label);
738 ig.MarkLabel (dissimilar_label);
739 if (Oper == Operator.Inequality)
740 ig.Emit (OpCodes.Ldc_I4_1);
742 ig.Emit (OpCodes.Ldc_I4_0);
744 ig.MarkLabel (end_label);
748 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
751 ec.ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
754 public override void Emit (EmitContext ec)
757 // Optimize same expression operation
759 if (right_unwrap != null && right.Equals (left))
760 right_unwrap = left_unwrap;
762 if (user_operator == null && IsBitwiseBoolean) {
763 EmitBitwiseBoolean (ec);
767 if ((Oper & Operator.EqualityMask) != 0) {
768 if (EmitEquality (ec))
772 ILGenerator ig = ec.ig;
774 Label is_null_label = ig.DefineLabel ();
775 Label end_label = ig.DefineLabel ();
777 if (left_unwrap != null) {
778 left_unwrap.EmitCheck (ec);
779 ig.Emit (OpCodes.Brfalse, is_null_label);
783 // Don't emit HasValue check when left and right expressions are same
785 if (right_unwrap != null && !left.Equals (right)) {
786 right_unwrap.EmitCheck (ec);
787 ig.Emit (OpCodes.Brfalse, is_null_label);
790 EmitOperator (ec, left.Type);
792 if (wrap_ctor != null)
793 ig.Emit (OpCodes.Newobj, wrap_ctor);
795 ig.Emit (OpCodes.Br_S, end_label);
796 ig.MarkLabel (is_null_label);
798 if ((Oper & Operator.ComparisonMask) != 0) {
799 if (Oper == Operator.Equality)
800 ig.Emit (OpCodes.Ldc_I4_1);
802 ig.Emit (OpCodes.Ldc_I4_0);
804 LiftedNull.Create (type, loc).Emit (ec);
807 ig.MarkLabel (end_label);
810 protected override void EmitOperator (EmitContext ec, Type l)
812 if (user_operator != null) {
813 user_operator.Emit (ec);
817 if (TypeManager.IsNullableType (l))
818 l = TypeManager.GetTypeArguments (l) [0];
820 base.EmitOperator (ec, l);
823 bool IsBitwiseBoolean {
825 return (Oper & Operator.BitwiseMask) != 0 && left_unwrap != null && right_unwrap != null &&
826 left_unwrap.Type == TypeManager.bool_type && right_unwrap.Type == TypeManager.bool_type;
830 Expression LiftResult (EmitContext ec, Expression res_expr)
832 TypeExpr lifted_type;
835 // Avoid double conversion
837 if (left_unwrap == null || left_null_lifted || !TypeManager.IsEqual (left_unwrap.Type, left.Type)) {
838 lifted_type = new NullableType (left.Type, loc);
839 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
840 if (lifted_type == null)
843 if (left is UserCast || left is TypeCast)
844 left.Type = lifted_type.Type;
846 left = EmptyCast.Create (left, lifted_type.Type);
849 if (right_unwrap == null || right_null_lifted || !TypeManager.IsEqual (right_unwrap.Type, right.Type)) {
850 lifted_type = new NullableType (right.Type, loc);
851 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
852 if (lifted_type == null)
855 if (right is UserCast || right is TypeCast)
856 right.Type = lifted_type.Type;
858 right = EmptyCast.Create (right, lifted_type.Type);
861 if ((Oper & Operator.ComparisonMask) == 0) {
862 lifted_type = new NullableType (res_expr.Type, loc);
863 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
864 if (lifted_type == null)
867 wrap_ctor = new NullableInfo (lifted_type.Type).Constructor;
868 type = res_expr.Type = lifted_type.Type;
871 if (left_null_lifted) {
872 left = LiftedNull.Create (right.Type, left.Location);
874 if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask)) != 0)
875 return LiftedNull.CreateFromExpression (res_expr);
878 // Value types and null comparison
880 if (right_unwrap == null || (Oper & Operator.RelationalMask) != 0)
881 return CreateNullConstant (right_orig).Resolve (ec);
884 if (right_null_lifted) {
885 right = LiftedNull.Create (left.Type, right.Location);
887 if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask)) != 0)
888 return LiftedNull.CreateFromExpression (res_expr);
891 // Value types and null comparison
893 if (left_unwrap == null || (Oper & Operator.RelationalMask) != 0)
894 return CreateNullConstant (left_orig).Resolve (ec);
900 protected override Expression ResolveOperatorPredefined (EmitContext ec, Binary.PredefinedOperator [] operators, bool primitives_only, Type enum_type)
902 Expression e = base.ResolveOperatorPredefined (ec, operators, primitives_only, enum_type);
904 if (e == this || enum_type != null)
905 return LiftResult (ec, e);
908 // 7.9.9 Equality operators and null
910 // The == and != operators permit one operand to be a value of a nullable type and
911 // the other to be the null literal, even if no predefined or user-defined operator
912 // (in unlifted or lifted form) exists for the operation.
914 if (e == null && (Oper & Operator.EqualityMask) != 0) {
915 if ((left_null_lifted && right_unwrap != null) || (right_null_lifted && left_unwrap != null))
916 return LiftResult (ec, this);
922 protected override Expression ResolveUserOperator (EmitContext ec, Type l, Type r)
924 Expression expr = base.ResolveUserOperator (ec, l, r);
928 expr = LiftResult (ec, expr);
929 if (expr is Constant)
933 user_operator = expr;
938 public class NullCoalescingOperator : Expression
940 Expression left, right;
943 public NullCoalescingOperator (Expression left, Expression right, Location loc)
950 public override Expression CreateExpressionTree (EmitContext ec)
952 if (left is NullLiteral)
953 Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side");
955 UserCast uc = left as UserCast;
956 Expression conversion = null;
960 ArrayList c_args = new ArrayList (2);
961 c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
962 c_args.Add (new Argument (left.CreateExpressionTree (ec)));
963 conversion = CreateExpressionFactoryCall ("Lambda", c_args);
966 ArrayList args = new ArrayList (3);
967 args.Add (new Argument (left.CreateExpressionTree (ec)));
968 args.Add (new Argument (right.CreateExpressionTree (ec)));
969 if (conversion != null)
970 args.Add (new Argument (conversion));
972 return CreateExpressionFactoryCall ("Coalesce", args);
975 public override Expression DoResolve (EmitContext ec)
980 left = left.Resolve (ec);
981 right = right.Resolve (ec);
983 if (left == null || right == null)
986 eclass = ExprClass.Value;
987 Type ltype = left.Type, rtype = right.Type;
990 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
991 // the result is underlying type of left
993 if (TypeManager.IsNullableType (ltype) && left.eclass != ExprClass.MethodGroup) {
994 unwrap = Unwrap.Create (left, ec);
998 if (Convert.ImplicitConversionExists (ec, right, unwrap.Type)) {
1001 right = Convert.ImplicitConversion (ec, right, type, loc);
1004 } else if (TypeManager.IsReferenceType (ltype) && right.eclass != ExprClass.MethodGroup) {
1005 if (Convert.ImplicitConversionExists (ec, right, ltype)) {
1007 // Reduce (constant ?? expr) to constant
1009 Constant lc = left as Constant;
1010 if (lc != null && !lc.IsDefaultValue)
1011 return new SideEffectConstant (lc, right, loc).Resolve (ec);
1014 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1016 if (right.IsNull || lc != null)
1017 return ReducedExpression.Create (lc != null ? right : left, this).Resolve (ec);
1019 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1024 Binary.Error_OperatorCannotBeApplied (left, right, "??", loc);
1028 if (!Convert.ImplicitConversionExists (ec, unwrap != null ? unwrap : left, rtype)) {
1029 Binary.Error_OperatorCannotBeApplied (left, right, "??", loc);
1034 // Reduce (null ?? right) to right
1037 return ReducedExpression.Create (right, this).Resolve (ec);
1039 left = Convert.ImplicitConversion (ec, unwrap != null ? unwrap : left, rtype, loc);
1044 public override void Emit (EmitContext ec)
1046 ILGenerator ig = ec.ig;
1048 Label end_label = ig.DefineLabel ();
1050 if (unwrap != null) {
1051 Label is_null_label = ig.DefineLabel ();
1053 unwrap.EmitCheck (ec);
1054 ig.Emit (OpCodes.Brfalse, is_null_label);
1057 ig.Emit (OpCodes.Br, end_label);
1059 ig.MarkLabel (is_null_label);
1062 ig.MarkLabel (end_label);
1068 ig.Emit (OpCodes.Dup);
1069 ig.Emit (OpCodes.Brtrue, end_label);
1071 ig.Emit (OpCodes.Pop);
1074 ig.MarkLabel (end_label);
1077 protected override void CloneTo (CloneContext clonectx, Expression t)
1079 NullCoalescingOperator target = (NullCoalescingOperator) t;
1081 target.left = left.Clone (clonectx);
1082 target.right = right.Clone (clonectx);
1086 public class LiftedUnaryMutator : ExpressionStatement
1088 public readonly UnaryMutator.Mode Mode;
1090 UnaryMutator underlying;
1093 public LiftedUnaryMutator (UnaryMutator.Mode mode, Expression expr, Location loc)
1099 eclass = ExprClass.Value;
1102 public override Expression CreateExpressionTree (EmitContext ec)
1104 return new SimpleAssign (this, this).CreateExpressionTree (ec);
1107 public override Expression DoResolve (EmitContext ec)
1109 expr = expr.Resolve (ec);
1113 unwrap = Unwrap.Create (expr, ec);
1117 underlying = (UnaryMutator) new UnaryMutator (Mode, unwrap, loc).Resolve (ec);
1118 if (underlying == null)
1125 void DoEmit (EmitContext ec, bool is_expr)
1127 ILGenerator ig = ec.ig;
1128 Label is_null_label = ig.DefineLabel ();
1129 Label end_label = ig.DefineLabel ();
1131 unwrap.EmitCheck (ec);
1132 ig.Emit (OpCodes.Brfalse, is_null_label);
1135 underlying.Emit (ec);
1136 ig.Emit (OpCodes.Br_S, end_label);
1138 underlying.EmitStatement (ec);
1141 ig.MarkLabel (is_null_label);
1143 LiftedNull.Create (type, loc).Emit (ec);
1145 ig.MarkLabel (end_label);
1148 public override void Emit (EmitContext ec)
1153 public override void EmitStatement (EmitContext ec)