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
12 // Copyright 2011 Xamarin Inc
18 using IKVM.Reflection.Emit;
20 using System.Reflection.Emit;
23 namespace Mono.CSharp.Nullable
25 public class NullableType : TypeExpr
27 readonly TypeSpec underlying;
29 public NullableType (TypeSpec type, Location loc)
31 this.underlying = type;
35 public override TypeSpec ResolveAsType (IMemberContext ec)
37 eclass = ExprClass.Type;
39 var otype = ec.Module.PredefinedTypes.Nullable.Resolve ();
43 TypeArguments args = new TypeArguments (new TypeExpression (underlying, loc));
44 GenericTypeExpr ctype = new GenericTypeExpr (otype, args, loc);
46 type = ctype.ResolveAsType (ec);
51 static class NullableInfo
53 public static MethodSpec GetConstructor (TypeSpec nullableType)
55 return (MethodSpec) MemberCache.FindMember (nullableType,
56 MemberFilter.Constructor (ParametersCompiled.CreateFullyResolved (GetUnderlyingType (nullableType))), BindingRestriction.DeclaredOnly);
59 public static MethodSpec GetHasValue (TypeSpec nullableType)
61 return (MethodSpec) MemberCache.FindMember (nullableType,
62 MemberFilter.Method ("get_HasValue", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None);
65 public static MethodSpec GetGetValueOrDefault (TypeSpec nullableType)
67 return (MethodSpec) MemberCache.FindMember (nullableType,
68 MemberFilter.Method ("GetValueOrDefault", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None);
71 public static MethodSpec GetValue (TypeSpec nullableType)
73 return (MethodSpec) MemberCache.FindMember (nullableType,
74 MemberFilter.Method ("get_Value", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None);
77 public static TypeSpec GetUnderlyingType (TypeSpec nullableType)
79 return ((InflatedTypeSpec) nullableType).TypeArguments[0];
83 public class Unwrap : Expression, IMemoryLocation
88 readonly bool useDefaultValue;
90 Unwrap (Expression expr, bool useDefaultValue)
93 this.loc = expr.Location;
94 this.useDefaultValue = useDefaultValue;
96 type = NullableInfo.GetUnderlyingType (expr.Type);
100 public override bool ContainsEmitWithAwait ()
102 return expr.ContainsEmitWithAwait ();
105 public static Expression Create (Expression expr)
108 // Avoid unwraping and wraping of same type
110 Wrap wrap = expr as Wrap;
114 return Create (expr, false);
117 public static Unwrap Create (Expression expr, bool useDefaultValue)
119 return new Unwrap (expr, useDefaultValue);
122 public override Expression CreateExpressionTree (ResolveContext ec)
124 return expr.CreateExpressionTree (ec);
127 protected override Expression DoResolve (ResolveContext ec)
132 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
134 expr = expr.DoResolveLValue (ec, right_side);
138 public override void Emit (EmitContext ec)
142 var call = new CallEmitter ();
143 call.InstanceExpression = this;
146 call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null);
148 call.EmitPredefined (ec, NullableInfo.GetValue (expr.Type), null);
151 public void EmitCheck (EmitContext ec)
155 var call = new CallEmitter ();
156 call.InstanceExpression = this;
158 call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null);
161 public override bool Equals (object obj)
163 Unwrap uw = obj as Unwrap;
164 return uw != null && expr.Equals (uw.expr);
167 public Expression Original {
173 public override int GetHashCode ()
175 return expr.GetHashCode ();
178 public override bool IsNull {
184 void Store (EmitContext ec)
189 if (expr is VariableReference)
193 LocalVariable.Store (ec);
196 public void Load (EmitContext ec)
198 if (expr is VariableReference)
201 LocalVariable.Emit (ec);
204 public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx)
206 return expr.MakeExpression (ctx);
209 public void AddressOf (EmitContext ec, AddressOp mode)
211 IMemoryLocation ml = expr as VariableReference;
213 ml.AddressOf (ec, mode);
215 LocalVariable.AddressOf (ec, mode);
219 // Keeps result of non-variable expression
221 LocalTemporary LocalVariable {
224 temp = new LocalTemporary (expr.Type);
231 // Calls get_Value method on nullable expression
233 public class UnwrapCall : CompositeExpression
235 public UnwrapCall (Expression expr)
240 protected override Expression DoResolve (ResolveContext rc)
245 type = NullableInfo.GetUnderlyingType (type);
250 public override void Emit (EmitContext ec)
252 var call = new CallEmitter ();
253 call.InstanceExpression = Child;
254 call.EmitPredefined (ec, NullableInfo.GetValue (Child.Type), null);
258 public class Wrap : TypeCast
260 private Wrap (Expression expr, TypeSpec type)
263 eclass = ExprClass.Value;
266 public override Expression CreateExpressionTree (ResolveContext ec)
268 TypeCast child_cast = child as TypeCast;
269 if (child_cast != null) {
271 return child_cast.CreateExpressionTree (ec);
274 return base.CreateExpressionTree (ec);
277 public static Expression Create (Expression expr, TypeSpec type)
280 // Avoid unwraping and wraping of the same type
282 Unwrap unwrap = expr as Unwrap;
283 if (unwrap != null && expr.Type == NullableInfo.GetUnderlyingType (type))
284 return unwrap.Original;
286 return new Wrap (expr, type);
289 public override void Emit (EmitContext ec)
292 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
297 // Represents null literal lifted to nullable type
299 public class LiftedNull : NullConstant, IMemoryLocation
301 private LiftedNull (TypeSpec nullable_type, Location loc)
302 : base (nullable_type, loc)
304 eclass = ExprClass.Value;
307 public static Constant Create (TypeSpec nullable, Location loc)
309 return new LiftedNull (nullable, loc);
312 public static Constant CreateFromExpression (ResolveContext ec, Expression e)
314 ec.Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'",
315 TypeManager.CSharpName (e.Type));
317 return ReducedExpression.Create (Create (e.Type, e.Location), e);
320 public override void Emit (EmitContext ec)
322 // TODO: generate less temporary variables
323 LocalTemporary value_target = new LocalTemporary (type);
325 value_target.AddressOf (ec, AddressOp.Store);
326 ec.Emit (OpCodes.Initobj, type);
327 value_target.Emit (ec);
328 value_target.Release (ec);
331 public void AddressOf (EmitContext ec, AddressOp Mode)
333 LocalTemporary value_target = new LocalTemporary (type);
335 value_target.AddressOf (ec, AddressOp.Store);
336 ec.Emit (OpCodes.Initobj, type);
337 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
342 // Generic lifting expression, supports all S/S? -> T/T? cases
344 public class Lifted : Expression, IMemoryLocation
346 Expression expr, null_value;
349 public Lifted (Expression expr, Unwrap unwrap, TypeSpec type)
352 this.unwrap = unwrap;
353 this.loc = expr.Location;
357 public Lifted (Expression expr, Expression unwrap, TypeSpec type)
358 : this (expr, unwrap as Unwrap, type)
362 public override bool ContainsEmitWithAwait ()
364 return unwrap.ContainsEmitWithAwait ();
367 public override Expression CreateExpressionTree (ResolveContext ec)
369 return expr.CreateExpressionTree (ec);
372 protected override Expression DoResolve (ResolveContext ec)
375 // It's null when lifting non-nullable type
377 if (unwrap == null) {
378 // S -> T? is wrap only
379 if (type.IsNullableType)
380 return Wrap.Create (expr, type);
382 // S -> T can be simplified
386 // Wrap target for T?
387 if (type.IsNullableType) {
388 expr = Wrap.Create (expr, type);
392 null_value = LiftedNull.Create (type, loc);
393 } else if (TypeSpec.IsValueType (type)) {
394 null_value = LiftedNull.Create (type, loc);
396 null_value = new NullConstant (type, loc);
399 eclass = ExprClass.Value;
403 public override void Emit (EmitContext ec)
405 Label is_null_label = ec.DefineLabel ();
406 Label end_label = ec.DefineLabel ();
408 unwrap.EmitCheck (ec);
409 ec.Emit (OpCodes.Brfalse, is_null_label);
413 ec.Emit (OpCodes.Br, end_label);
414 ec.MarkLabel (is_null_label);
416 null_value.Emit (ec);
417 ec.MarkLabel (end_label);
420 public void AddressOf (EmitContext ec, AddressOp mode)
422 unwrap.AddressOf (ec, mode);
426 public class LiftedUnaryOperator : Unary, IMemoryLocation
429 Expression user_operator;
431 public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc)
432 : base (op, expr, loc)
436 public void AddressOf (EmitContext ec, AddressOp mode)
438 unwrap.AddressOf (ec, mode);
441 public override Expression CreateExpressionTree (ResolveContext ec)
443 if (user_operator != null)
444 return user_operator.CreateExpressionTree (ec);
446 if (Oper == Operator.UnaryPlus)
447 return Expr.CreateExpressionTree (ec);
449 return base.CreateExpressionTree (ec);
452 protected override Expression DoResolve (ResolveContext ec)
454 unwrap = Unwrap.Create (Expr, false);
458 Expression res = base.ResolveOperator (ec, unwrap);
460 if (user_operator == null)
463 res = Expr = LiftExpression (ec, Expr);
469 eclass = ExprClass.Value;
474 public override void Emit (EmitContext ec)
476 Label is_null_label = ec.DefineLabel ();
477 Label end_label = ec.DefineLabel ();
479 unwrap.EmitCheck (ec);
480 ec.Emit (OpCodes.Brfalse, is_null_label);
482 if (user_operator != null) {
483 user_operator.Emit (ec);
485 EmitOperator (ec, NullableInfo.GetUnderlyingType (type));
488 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
489 ec.Emit (OpCodes.Br_S, end_label);
491 ec.MarkLabel (is_null_label);
492 LiftedNull.Create (type, loc).Emit (ec);
494 ec.MarkLabel (end_label);
497 Expression LiftExpression (ResolveContext ec, Expression expr)
499 var lifted_type = new NullableType (expr.Type, expr.Location);
500 if (lifted_type.ResolveAsType (ec) == null)
503 expr.Type = lifted_type.Type;
507 protected override Expression ResolveEnumOperator (ResolveContext ec, Expression expr, TypeSpec[] predefined)
509 expr = base.ResolveEnumOperator (ec, expr, predefined);
513 Expr = LiftExpression (ec, Expr);
514 return LiftExpression (ec, expr);
517 protected override Expression ResolveUserOperator (ResolveContext ec, Expression expr)
519 expr = base.ResolveUserOperator (ec, expr);
524 // When a user operator is of non-nullable type
526 if (Expr is Unwrap) {
527 user_operator = LiftExpression (ec, expr);
528 return user_operator;
535 public class LiftedBinaryOperator : Binary
537 Unwrap left_unwrap, right_unwrap;
538 Expression left_orig, right_orig;
539 Expression user_operator;
540 MethodSpec wrap_ctor;
542 public LiftedBinaryOperator (Binary.Operator op, Expression left, Expression right, Location loc)
543 : base (op, left, right, loc)
547 bool IsBitwiseBoolean {
549 return (Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) &&
550 ((left_unwrap != null && left_unwrap.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) ||
551 (right_unwrap != null && right_unwrap.Type.BuiltinType == BuiltinTypeSpec.Type.Bool));
555 bool IsLeftNullLifted {
557 return (state & State.LeftNullLifted) != 0;
561 bool IsRightNullLifted {
563 return (state & State.RightNullLifted) != 0;
567 public override Expression CreateExpressionTree (ResolveContext ec)
569 if (user_operator != null)
570 return user_operator.CreateExpressionTree (ec);
572 return base.CreateExpressionTree (ec);
576 // CSC 2 has this behavior, it allows structs to be compared
577 // with the null literal *outside* of a generics context and
578 // inlines that as true or false.
580 Constant CreateNullConstant (ResolveContext ec, Expression expr)
582 // FIXME: Handle side effect constants
583 Constant c = new BoolConstant (ec.BuiltinTypes, Oper == Operator.Inequality, loc);
585 if ((Oper & Operator.EqualityMask) != 0) {
586 ec.Report.Warning (472, 2, loc, "The result of comparing value type `{0}' with null is `{1}'",
587 TypeManager.CSharpName (expr.Type), c.GetValueAsLiteral ());
589 ec.Report.Warning (464, 2, loc, "The result of comparing type `{0}' with null is always `{1}'",
590 TypeManager.CSharpName (expr.Type), c.GetValueAsLiteral ());
593 return ReducedExpression.Create (c, this);
596 protected override Expression DoResolve (ResolveContext ec)
598 if ((Oper & Operator.LogicalMask) != 0) {
599 Error_OperatorCannotBeApplied (ec, left, right);
603 bool use_default_call = (Oper & (Operator.BitwiseMask | Operator.EqualityMask)) != 0;
605 if (left.Type.IsNullableType) {
606 left = left_unwrap = Unwrap.Create (left, use_default_call);
612 if (right.Type.IsNullableType) {
613 right = right_unwrap = Unwrap.Create (right, use_default_call);
619 // Some details are in 6.4.2, 7.2.7
620 // Arguments can be lifted for equal operators when the return type is bool and both
621 // arguments are of same type
623 if (left_orig is NullLiteral) {
625 state |= State.LeftNullLifted;
626 type = ec.BuiltinTypes.Bool;
629 if (right_orig.IsNull) {
630 if ((Oper & Operator.ShiftMask) != 0)
631 right = new EmptyExpression (ec.BuiltinTypes.Int);
635 state |= State.RightNullLifted;
636 type = ec.BuiltinTypes.Bool;
639 eclass = ExprClass.Value;
640 return DoResolveCore (ec, left_orig, right_orig);
643 void EmitBitwiseBoolean (EmitContext ec)
645 Label load_left = ec.DefineLabel ();
646 Label load_right = ec.DefineLabel ();
647 Label end_label = ec.DefineLabel ();
649 // null & value, null | value
650 if (left_unwrap == null) {
651 left_unwrap = right_unwrap;
656 left_unwrap.Emit (ec);
657 ec.Emit (OpCodes.Brtrue, load_right);
659 // value & null, value | null
660 if (right_unwrap != null) {
661 right_unwrap.Emit (ec);
662 ec.Emit (OpCodes.Brtrue_S, load_left);
665 left_unwrap.EmitCheck (ec);
666 ec.Emit (OpCodes.Brfalse_S, load_right);
669 ec.MarkLabel (load_left);
671 if (Oper == Operator.BitwiseAnd) {
672 left_unwrap.Load (ec);
674 if (right_unwrap == null) {
676 if (right is EmptyConstantCast || right is EmptyCast)
677 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
679 right_unwrap.Load (ec);
680 right_unwrap = left_unwrap;
683 ec.Emit (OpCodes.Br_S, end_label);
686 ec.MarkLabel (load_right);
687 if (right_unwrap == null) {
688 if (Oper == Operator.BitwiseAnd) {
690 if (right is EmptyConstantCast || right is EmptyCast)
691 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
693 left_unwrap.Load (ec);
696 right_unwrap.Load (ec);
699 ec.MarkLabel (end_label);
703 // Emits optimized equality or inequality operator when possible
705 void EmitEquality (EmitContext ec)
708 // Either left or right is null
710 if (left_unwrap != null && (IsRightNullLifted || right.IsNull)) {
711 left_unwrap.EmitCheck (ec);
712 if (Oper == Binary.Operator.Equality) {
714 ec.Emit (OpCodes.Ceq);
719 if (right_unwrap != null && (IsLeftNullLifted || left.IsNull)) {
720 right_unwrap.EmitCheck (ec);
721 if (Oper == Binary.Operator.Equality) {
723 ec.Emit (OpCodes.Ceq);
728 Label dissimilar_label = ec.DefineLabel ();
729 Label end_label = ec.DefineLabel ();
731 if (user_operator != null) {
732 user_operator.Emit (ec);
733 ec.Emit (Oper == Operator.Equality ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, dissimilar_label);
735 if (ec.HasSet (BuilderContext.Options.AsyncBody) && right.ContainsEmitWithAwait ()) {
736 left = left.EmitToField (ec);
737 right = right.EmitToField (ec);
743 ec.Emit (OpCodes.Bne_Un_S, dissimilar_label);
746 if (left_unwrap != null)
747 left_unwrap.EmitCheck (ec);
749 if (right_unwrap != null)
750 right_unwrap.EmitCheck (ec);
752 if (left_unwrap != null && right_unwrap != null) {
753 if (Oper == Operator.Inequality)
754 ec.Emit (OpCodes.Xor);
756 ec.Emit (OpCodes.Ceq);
758 if (Oper == Operator.Inequality) {
760 ec.Emit (OpCodes.Ceq);
764 ec.Emit (OpCodes.Br_S, end_label);
766 ec.MarkLabel (dissimilar_label);
767 if (Oper == Operator.Inequality)
772 ec.MarkLabel (end_label);
775 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
778 ec.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
781 public override void Emit (EmitContext ec)
784 // Optimize same expression operation
786 if (right_unwrap != null && right.Equals (left))
787 right_unwrap = left_unwrap;
789 if (user_operator == null && IsBitwiseBoolean) {
790 EmitBitwiseBoolean (ec);
794 if ((Oper & Operator.EqualityMask) != 0) {
799 Label is_null_label = ec.DefineLabel ();
800 Label end_label = ec.DefineLabel ();
802 if (left_unwrap != null) {
803 left_unwrap.EmitCheck (ec);
804 ec.Emit (OpCodes.Brfalse, is_null_label);
808 // Don't emit HasValue check when left and right expressions are same
810 if (right_unwrap != null && !left.Equals (right)) {
811 right_unwrap.EmitCheck (ec);
812 ec.Emit (OpCodes.Brfalse, is_null_label);
815 EmitOperator (ec, left.Type);
817 if (wrap_ctor != null)
818 ec.Emit (OpCodes.Newobj, wrap_ctor);
820 ec.Emit (OpCodes.Br_S, end_label);
821 ec.MarkLabel (is_null_label);
823 if ((Oper & Operator.ComparisonMask) != 0) {
826 LiftedNull.Create (type, loc).Emit (ec);
829 ec.MarkLabel (end_label);
832 protected override void EmitOperator (EmitContext ec, TypeSpec l)
834 if (user_operator != null) {
835 user_operator.Emit (ec);
839 if (left.Type.IsNullableType) {
840 l = NullableInfo.GetUnderlyingType (left.Type);
841 left = EmptyCast.Create (left, l);
844 if (right.Type.IsNullableType) {
845 right = EmptyCast.Create (right, NullableInfo.GetUnderlyingType (right.Type));
848 base.EmitOperator (ec, l);
851 Expression LiftResult (ResolveContext ec, Expression res_expr)
853 TypeSpec lifted_type;
856 // Avoid double conversion
858 if (left_unwrap == null || IsLeftNullLifted || left_unwrap.Type != left.Type || (left_unwrap != null && IsRightNullLifted)) {
859 lifted_type = new NullableType (left.Type, loc).ResolveAsType (ec);
860 if (lifted_type == null)
863 if (left is UserCast || left is TypeCast)
864 left.Type = lifted_type;
866 left = EmptyCast.Create (left, lifted_type);
869 if (left != right && (right_unwrap == null || IsRightNullLifted || right_unwrap.Type != right.Type || (right_unwrap != null && IsLeftNullLifted))) {
870 lifted_type = new NullableType (right.Type, loc).ResolveAsType (ec);
871 if (lifted_type == null)
875 if (r is ReducedExpression)
876 r = ((ReducedExpression) r).OriginalExpression;
878 if (r is UserCast || r is TypeCast)
879 r.Type = lifted_type;
881 right = EmptyCast.Create (right, lifted_type);
884 if ((Oper & Operator.ComparisonMask) == 0) {
885 lifted_type = new NullableType (res_expr.Type, loc).ResolveAsType (ec);
886 if (lifted_type == null)
889 wrap_ctor = NullableInfo.GetConstructor (lifted_type);
890 type = res_expr.Type = lifted_type;
893 if (IsLeftNullLifted) {
894 left = LiftedNull.Create (right.Type, left.Location);
897 // Special case for bool?, the result depends on both null right side and left side value
899 if ((Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) && NullableInfo.GetUnderlyingType (type).BuiltinType == BuiltinTypeSpec.Type.Bool) {
903 if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0)
904 return LiftedNull.CreateFromExpression (ec, res_expr);
907 // Value types and null comparison
909 if (right_unwrap == null || (Oper & Operator.RelationalMask) != 0)
910 return CreateNullConstant (ec, right_orig);
913 if (IsRightNullLifted) {
914 right = LiftedNull.Create (left.Type, right.Location);
917 // Special case for bool?, the result depends on both null right side and left side value
919 if ((Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) && NullableInfo.GetUnderlyingType (type).BuiltinType == BuiltinTypeSpec.Type.Bool) {
923 if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0)
924 return LiftedNull.CreateFromExpression (ec, res_expr);
927 // Value types and null comparison
929 if (left_unwrap == null || (Oper & Operator.RelationalMask) != 0)
930 return CreateNullConstant (ec, left_orig);
936 protected override Expression ResolveOperatorPredefined (ResolveContext ec, Binary.PredefinedOperator [] operators, bool primitives_only, TypeSpec enum_type)
938 Expression e = base.ResolveOperatorPredefined (ec, operators, primitives_only, enum_type);
940 if (e == this || enum_type != null)
941 return LiftResult (ec, e);
944 // 7.9.9 Equality operators and null
946 // The == and != operators permit one operand to be a value of a nullable type and
947 // the other to be the null literal, even if no predefined or user-defined operator
948 // (in unlifted or lifted form) exists for the operation.
950 if (e == null && (Oper & Operator.EqualityMask) != 0) {
951 if ((IsLeftNullLifted && right_unwrap != null) || (IsRightNullLifted && left_unwrap != null))
952 return LiftResult (ec, this);
958 protected override Expression ResolveUserOperator (ResolveContext ec, Expression left, Expression right)
961 // Try original types first for exact match without unwrapping
963 Expression expr = base.ResolveUserOperator (ec, left_orig, right_orig);
967 State orig_state = state;
970 // One side is a nullable type, try to match underlying types
972 if (left_unwrap != null || right_unwrap != null || (state & (State.RightNullLifted | State.LeftNullLifted)) != 0) {
973 expr = base.ResolveUserOperator (ec, left, right);
980 // Lift the result in the case it can be null and predefined or user operator
981 // result type is of a value type
983 if (!TypeSpec.IsValueType (expr.Type))
986 if (state != orig_state)
989 expr = LiftResult (ec, expr);
990 if (expr is Constant)
994 user_operator = expr;
999 public class NullCoalescingOperator : Expression
1001 Expression left, right;
1004 public NullCoalescingOperator (Expression left, Expression right, Location loc)
1011 public Expression LeftExpression {
1017 public Expression RightExpression {
1023 public override Expression CreateExpressionTree (ResolveContext ec)
1025 if (left is NullLiteral)
1026 ec.Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side");
1028 UserCast uc = left as UserCast;
1029 Expression conversion = null;
1033 Arguments c_args = new Arguments (2);
1034 c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
1035 c_args.Add (new Argument (left.CreateExpressionTree (ec)));
1036 conversion = CreateExpressionFactoryCall (ec, "Lambda", c_args);
1039 Arguments args = new Arguments (3);
1040 args.Add (new Argument (left.CreateExpressionTree (ec)));
1041 args.Add (new Argument (right.CreateExpressionTree (ec)));
1042 if (conversion != null)
1043 args.Add (new Argument (conversion));
1045 return CreateExpressionFactoryCall (ec, "Coalesce", args);
1048 Expression ConvertExpression (ResolveContext ec)
1050 // TODO: ImplicitConversionExists should take care of this
1051 if (left.eclass == ExprClass.MethodGroup)
1054 TypeSpec ltype = left.Type;
1057 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
1058 // the result is underlying type of left
1060 if (ltype.IsNullableType) {
1061 unwrap = Unwrap.Create (left, false);
1066 // Reduce (left ?? null) to left
1069 return ReducedExpression.Create (left, this);
1071 if (Convert.ImplicitConversionExists (ec, right, unwrap.Type)) {
1076 // If right is a dynamic expression, the result type is dynamic
1078 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1081 // Need to box underlying value type
1082 left = Convert.ImplicitBoxingConversion (left, ltype, type);
1086 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1090 } else if (TypeSpec.IsReferenceType (ltype)) {
1091 if (Convert.ImplicitConversionExists (ec, right, ltype)) {
1093 // If right is a dynamic expression, the result type is dynamic
1095 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1101 // Reduce ("foo" ?? expr) to expression
1103 Constant lc = left as Constant;
1104 if (lc != null && !lc.IsDefaultValue)
1105 return ReducedExpression.Create (lc, this);
1108 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1110 if (right.IsNull || lc != null)
1111 return ReducedExpression.Create (lc != null ? right : left, this);
1113 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1119 // Special case null ?? null
1121 if (ltype == right.Type) {
1129 TypeSpec rtype = right.Type;
1130 if (!Convert.ImplicitConversionExists (ec, unwrap != null ? unwrap : left, rtype) || right.eclass == ExprClass.MethodGroup)
1134 // Reduce (null ?? right) to right
1137 return ReducedExpression.Create (right, this).Resolve (ec);
1139 left = Convert.ImplicitConversion (ec, unwrap != null ? unwrap : left, rtype, loc);
1144 public override bool ContainsEmitWithAwait ()
1147 return unwrap.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1149 return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1152 protected override Expression DoResolve (ResolveContext ec)
1154 left = left.Resolve (ec);
1155 right = right.Resolve (ec);
1157 if (left == null || right == null)
1160 eclass = ExprClass.Value;
1162 Expression e = ConvertExpression (ec);
1164 Binary.Error_OperatorCannotBeApplied (ec, left, right, "??", loc);
1171 public override void Emit (EmitContext ec)
1173 Label end_label = ec.DefineLabel ();
1175 if (unwrap != null) {
1176 Label is_null_label = ec.DefineLabel ();
1178 unwrap.EmitCheck (ec);
1179 ec.Emit (OpCodes.Brfalse, is_null_label);
1182 ec.Emit (OpCodes.Br, end_label);
1184 ec.MarkLabel (is_null_label);
1187 ec.MarkLabel (end_label);
1192 ec.Emit (OpCodes.Dup);
1194 // Only to make verifier happy
1195 if (left.Type.IsGenericParameter)
1196 ec.Emit (OpCodes.Box, left.Type);
1198 ec.Emit (OpCodes.Brtrue, end_label);
1200 ec.Emit (OpCodes.Pop);
1203 ec.MarkLabel (end_label);
1206 protected override void CloneTo (CloneContext clonectx, Expression t)
1208 NullCoalescingOperator target = (NullCoalescingOperator) t;
1210 target.left = left.Clone (clonectx);
1211 target.right = right.Clone (clonectx);
1214 public override object Accept (StructuralVisitor visitor)
1216 return visitor.Visit (this);
1220 class LiftedUnaryMutator : UnaryMutator
1222 public LiftedUnaryMutator (Mode mode, Expression expr, Location loc)
1223 : base (mode, expr, loc)
1227 protected override Expression DoResolve (ResolveContext ec)
1229 var orig_expr = expr;
1231 expr = Unwrap.Create (expr);
1233 var res = base.DoResolveOperation (ec);
1241 protected override void EmitOperation (EmitContext ec)
1243 Label is_null_label = ec.DefineLabel ();
1244 Label end_label = ec.DefineLabel ();
1246 LocalTemporary lt = new LocalTemporary (type);
1248 // Value is on the stack
1251 var call = new CallEmitter ();
1252 call.InstanceExpression = lt;
1253 call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null);
1255 ec.Emit (OpCodes.Brfalse, is_null_label);
1257 call = new CallEmitter ();
1258 call.InstanceExpression = lt;
1259 call.EmitPredefined (ec, NullableInfo.GetValue (expr.Type), null);
1263 base.EmitOperation (ec);
1265 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
1266 ec.Emit (OpCodes.Br_S, end_label);
1268 ec.MarkLabel (is_null_label);
1269 LiftedNull.Create (type, loc).Emit (ec);
1271 ec.MarkLabel (end_label);