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);
72 // Don't use unless really required for correctness, see Unwrap::Emit
74 public static MethodSpec GetValue (TypeSpec nullableType)
76 return (MethodSpec) MemberCache.FindMember (nullableType,
77 MemberFilter.Method ("get_Value", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None);
80 public static TypeSpec GetUnderlyingType (TypeSpec nullableType)
82 return ((InflatedTypeSpec) nullableType).TypeArguments[0];
86 public class Unwrap : Expression, IMemoryLocation
91 readonly bool useDefaultValue;
93 Unwrap (Expression expr, bool useDefaultValue)
96 this.loc = expr.Location;
97 this.useDefaultValue = useDefaultValue;
99 type = NullableInfo.GetUnderlyingType (expr.Type);
100 eclass = expr.eclass;
103 public override bool ContainsEmitWithAwait ()
105 return expr.ContainsEmitWithAwait ();
108 public static Expression Create (Expression expr)
111 // Avoid unwraping and wraping of same type
113 Wrap wrap = expr as Wrap;
117 return Create (expr, false);
120 public static Unwrap Create (Expression expr, bool useDefaultValue)
122 return new Unwrap (expr, useDefaultValue);
125 public override Expression CreateExpressionTree (ResolveContext ec)
127 return expr.CreateExpressionTree (ec);
130 protected override Expression DoResolve (ResolveContext ec)
135 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
137 expr = expr.DoResolveLValue (ec, right_side);
141 public override void Emit (EmitContext ec)
145 var call = new CallEmitter ();
146 call.InstanceExpression = this;
149 // Using GetGetValueOrDefault is prefered because JIT can possibly
150 // inline it whereas Value property contains a throw which is very
151 // unlikely to be inlined
154 call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null);
156 call.EmitPredefined (ec, NullableInfo.GetValue (expr.Type), null);
159 public void EmitCheck (EmitContext ec)
163 var call = new CallEmitter ();
164 call.InstanceExpression = this;
166 call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null);
169 public override bool Equals (object obj)
171 Unwrap uw = obj as Unwrap;
172 return uw != null && expr.Equals (uw.expr);
175 public Expression Original {
181 public override int GetHashCode ()
183 return expr.GetHashCode ();
186 public override bool IsNull {
192 void Store (EmitContext ec)
197 if (expr is VariableReference)
201 LocalVariable.Store (ec);
204 public void Load (EmitContext ec)
206 if (expr is VariableReference)
209 LocalVariable.Emit (ec);
212 public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx)
214 return expr.MakeExpression (ctx);
217 public void AddressOf (EmitContext ec, AddressOp mode)
219 IMemoryLocation ml = expr as VariableReference;
221 ml.AddressOf (ec, mode);
223 LocalVariable.AddressOf (ec, mode);
227 // Keeps result of non-variable expression
229 LocalTemporary LocalVariable {
232 temp = new LocalTemporary (expr.Type);
239 // Calls get_Value method on nullable expression
241 public class UnwrapCall : CompositeExpression
243 public UnwrapCall (Expression expr)
248 protected override Expression DoResolve (ResolveContext rc)
253 type = NullableInfo.GetUnderlyingType (type);
258 public override void Emit (EmitContext ec)
260 var call = new CallEmitter ();
261 call.InstanceExpression = Child;
262 call.EmitPredefined (ec, NullableInfo.GetValue (Child.Type), null);
266 public class Wrap : TypeCast
268 private Wrap (Expression expr, TypeSpec type)
271 eclass = ExprClass.Value;
274 public override Expression CreateExpressionTree (ResolveContext ec)
276 TypeCast child_cast = child as TypeCast;
277 if (child_cast != null) {
279 return child_cast.CreateExpressionTree (ec);
282 return base.CreateExpressionTree (ec);
285 public static Expression Create (Expression expr, TypeSpec type)
288 // Avoid unwraping and wraping of the same type
290 Unwrap unwrap = expr as Unwrap;
291 if (unwrap != null && expr.Type == NullableInfo.GetUnderlyingType (type))
292 return unwrap.Original;
294 return new Wrap (expr, type);
297 public override void Emit (EmitContext ec)
300 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
305 // Represents null literal lifted to nullable type
307 public class LiftedNull : NullConstant, IMemoryLocation
309 private LiftedNull (TypeSpec nullable_type, Location loc)
310 : base (nullable_type, loc)
312 eclass = ExprClass.Value;
315 public static Constant Create (TypeSpec nullable, Location loc)
317 return new LiftedNull (nullable, loc);
320 public static Constant CreateFromExpression (ResolveContext ec, Expression e)
322 ec.Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'",
323 TypeManager.CSharpName (e.Type));
325 return ReducedExpression.Create (Create (e.Type, e.Location), e);
328 public override void Emit (EmitContext ec)
330 // TODO: generate less temporary variables
331 LocalTemporary value_target = new LocalTemporary (type);
333 value_target.AddressOf (ec, AddressOp.Store);
334 ec.Emit (OpCodes.Initobj, type);
335 value_target.Emit (ec);
336 value_target.Release (ec);
339 public void AddressOf (EmitContext ec, AddressOp Mode)
341 LocalTemporary value_target = new LocalTemporary (type);
343 value_target.AddressOf (ec, AddressOp.Store);
344 ec.Emit (OpCodes.Initobj, type);
345 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
350 // Generic lifting expression, supports all S/S? -> T/T? cases
352 public class Lifted : Expression, IMemoryLocation
354 Expression expr, null_value;
357 public Lifted (Expression expr, Unwrap unwrap, TypeSpec type)
360 this.unwrap = unwrap;
361 this.loc = expr.Location;
365 public Lifted (Expression expr, Expression unwrap, TypeSpec type)
366 : this (expr, unwrap as Unwrap, type)
370 public override bool ContainsEmitWithAwait ()
372 return unwrap.ContainsEmitWithAwait ();
375 public override Expression CreateExpressionTree (ResolveContext ec)
377 return expr.CreateExpressionTree (ec);
380 protected override Expression DoResolve (ResolveContext ec)
383 // It's null when lifting non-nullable type
385 if (unwrap == null) {
386 // S -> T? is wrap only
387 if (type.IsNullableType)
388 return Wrap.Create (expr, type);
390 // S -> T can be simplified
394 // Wrap target for T?
395 if (type.IsNullableType) {
396 expr = Wrap.Create (expr, type);
400 null_value = LiftedNull.Create (type, loc);
401 } else if (TypeSpec.IsValueType (type)) {
402 null_value = LiftedNull.Create (type, loc);
404 null_value = new NullConstant (type, loc);
407 eclass = ExprClass.Value;
411 public override void Emit (EmitContext ec)
413 Label is_null_label = ec.DefineLabel ();
414 Label end_label = ec.DefineLabel ();
416 unwrap.EmitCheck (ec);
417 ec.Emit (OpCodes.Brfalse, is_null_label);
421 ec.Emit (OpCodes.Br, end_label);
422 ec.MarkLabel (is_null_label);
424 null_value.Emit (ec);
425 ec.MarkLabel (end_label);
428 public void AddressOf (EmitContext ec, AddressOp mode)
430 unwrap.AddressOf (ec, mode);
434 public class LiftedUnaryOperator : Unary, IMemoryLocation
437 Expression user_operator;
439 public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc)
440 : base (op, expr, loc)
444 public void AddressOf (EmitContext ec, AddressOp mode)
446 unwrap.AddressOf (ec, mode);
449 public override Expression CreateExpressionTree (ResolveContext ec)
451 if (user_operator != null)
452 return user_operator.CreateExpressionTree (ec);
454 if (Oper == Operator.UnaryPlus)
455 return Expr.CreateExpressionTree (ec);
457 return base.CreateExpressionTree (ec);
460 protected override Expression DoResolve (ResolveContext ec)
462 unwrap = Unwrap.Create (Expr, false);
466 Expression res = base.ResolveOperator (ec, unwrap);
468 if (user_operator == null)
471 res = Expr = LiftExpression (ec, Expr);
477 eclass = ExprClass.Value;
482 public override void Emit (EmitContext ec)
484 Label is_null_label = ec.DefineLabel ();
485 Label end_label = ec.DefineLabel ();
487 unwrap.EmitCheck (ec);
488 ec.Emit (OpCodes.Brfalse, is_null_label);
490 if (user_operator != null) {
491 user_operator.Emit (ec);
493 EmitOperator (ec, NullableInfo.GetUnderlyingType (type));
496 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
497 ec.Emit (OpCodes.Br_S, end_label);
499 ec.MarkLabel (is_null_label);
500 LiftedNull.Create (type, loc).Emit (ec);
502 ec.MarkLabel (end_label);
505 static Expression LiftExpression (ResolveContext ec, Expression expr)
507 var lifted_type = new NullableType (expr.Type, expr.Location);
508 if (lifted_type.ResolveAsType (ec) == null)
511 expr.Type = lifted_type.Type;
515 protected override Expression ResolveEnumOperator (ResolveContext ec, Expression expr, TypeSpec[] predefined)
517 expr = base.ResolveEnumOperator (ec, expr, predefined);
521 Expr = LiftExpression (ec, Expr);
522 return LiftExpression (ec, expr);
525 protected override Expression ResolveUserOperator (ResolveContext ec, Expression expr)
527 expr = base.ResolveUserOperator (ec, expr);
532 // When a user operator is of non-nullable type
534 if (Expr is Unwrap) {
535 user_operator = LiftExpression (ec, expr);
536 return user_operator;
543 public class LiftedBinaryOperator : Binary
545 Unwrap left_unwrap, right_unwrap;
546 Expression left_orig, right_orig;
547 Expression user_operator;
548 MethodSpec wrap_ctor;
550 public LiftedBinaryOperator (Binary.Operator op, Expression left, Expression right)
551 : base (op, left, right)
555 bool IsBitwiseBoolean {
557 return (Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) &&
558 ((left_unwrap != null && left_unwrap.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) ||
559 (right_unwrap != null && right_unwrap.Type.BuiltinType == BuiltinTypeSpec.Type.Bool));
563 bool IsLeftNullLifted {
565 return (state & State.LeftNullLifted) != 0;
569 bool IsRightNullLifted {
571 return (state & State.RightNullLifted) != 0;
575 public override Expression CreateExpressionTree (ResolveContext ec)
577 if (user_operator != null)
578 return user_operator.CreateExpressionTree (ec);
580 return base.CreateExpressionTree (ec);
584 // CSC 2 has this behavior, it allows structs to be compared
585 // with the null literal *outside* of a generics context and
586 // inlines that as true or false.
588 Constant CreateNullConstant (ResolveContext ec, Expression expr)
590 // FIXME: Handle side effect constants
591 Constant c = new BoolConstant (ec.BuiltinTypes, Oper == Operator.Inequality, loc);
593 if ((Oper & Operator.EqualityMask) != 0) {
594 ec.Report.Warning (472, 2, loc, "The result of comparing value type `{0}' with null is always `{1}'",
595 TypeManager.CSharpName (expr.Type), c.GetValueAsLiteral ());
597 ec.Report.Warning (464, 2, loc, "The result of comparing type `{0}' with null is always `{1}'",
598 TypeManager.CSharpName (expr.Type), c.GetValueAsLiteral ());
601 return ReducedExpression.Create (c, this);
604 protected override Expression DoResolve (ResolveContext ec)
606 if ((Oper & Operator.LogicalMask) != 0) {
607 Error_OperatorCannotBeApplied (ec, left, right);
611 bool use_default_call = (Oper & (Operator.BitwiseMask | Operator.EqualityMask)) != 0;
613 if (left.Type.IsNullableType) {
614 left = left_unwrap = Unwrap.Create (left, use_default_call);
620 if (right.Type.IsNullableType) {
621 right = right_unwrap = Unwrap.Create (right, use_default_call);
627 // Some details are in 6.4.2, 7.2.7
628 // Arguments can be lifted for equal operators when the return type is bool and both
629 // arguments are of same type
631 if (left_orig is NullLiteral) {
633 state |= State.LeftNullLifted;
634 type = ec.BuiltinTypes.Bool;
637 if (right_orig.IsNull) {
638 if ((Oper & Operator.ShiftMask) != 0)
639 right = new EmptyExpression (ec.BuiltinTypes.Int);
643 state |= State.RightNullLifted;
644 type = ec.BuiltinTypes.Bool;
647 eclass = ExprClass.Value;
648 return DoResolveCore (ec, left_orig, right_orig);
651 void EmitBitwiseBoolean (EmitContext ec)
653 Label load_left = ec.DefineLabel ();
654 Label load_right = ec.DefineLabel ();
655 Label end_label = ec.DefineLabel ();
657 // null & value, null | value
658 if (left_unwrap == null) {
659 left_unwrap = right_unwrap;
664 left_unwrap.Emit (ec);
665 ec.Emit (OpCodes.Brtrue, load_right);
667 // value & null, value | null
668 if (right_unwrap != null) {
669 right_unwrap.Emit (ec);
670 ec.Emit (OpCodes.Brtrue_S, load_left);
673 left_unwrap.EmitCheck (ec);
674 ec.Emit (OpCodes.Brfalse_S, load_right);
677 ec.MarkLabel (load_left);
679 if (Oper == Operator.BitwiseAnd) {
680 left_unwrap.Load (ec);
682 if (right_unwrap == null) {
684 if (right is EmptyConstantCast || right is EmptyCast)
685 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
687 right_unwrap.Load (ec);
688 right_unwrap = left_unwrap;
691 ec.Emit (OpCodes.Br_S, end_label);
694 ec.MarkLabel (load_right);
695 if (right_unwrap == null) {
696 if (Oper == Operator.BitwiseAnd) {
698 if (right is EmptyConstantCast || right is EmptyCast)
699 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
701 left_unwrap.Load (ec);
704 right_unwrap.Load (ec);
707 ec.MarkLabel (end_label);
711 // Emits optimized equality or inequality operator when possible
713 void EmitEquality (EmitContext ec)
716 // Either left or right is null
718 if (left_unwrap != null && (IsRightNullLifted || right.IsNull)) {
719 left_unwrap.EmitCheck (ec);
720 if (Oper == Binary.Operator.Equality) {
722 ec.Emit (OpCodes.Ceq);
727 if (right_unwrap != null && (IsLeftNullLifted || left.IsNull)) {
728 right_unwrap.EmitCheck (ec);
729 if (Oper == Binary.Operator.Equality) {
731 ec.Emit (OpCodes.Ceq);
736 Label dissimilar_label = ec.DefineLabel ();
737 Label end_label = ec.DefineLabel ();
739 if (user_operator != null) {
740 user_operator.Emit (ec);
741 ec.Emit (Oper == Operator.Equality ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, dissimilar_label);
743 if (ec.HasSet (BuilderContext.Options.AsyncBody) && right.ContainsEmitWithAwait ()) {
744 left = left.EmitToField (ec);
745 right = right.EmitToField (ec);
751 ec.Emit (OpCodes.Bne_Un_S, dissimilar_label);
754 if (left_unwrap != null)
755 left_unwrap.EmitCheck (ec);
757 if (right_unwrap != null)
758 right_unwrap.EmitCheck (ec);
760 if (left_unwrap != null && right_unwrap != null) {
761 if (Oper == Operator.Inequality)
762 ec.Emit (OpCodes.Xor);
764 ec.Emit (OpCodes.Ceq);
766 if (Oper == Operator.Inequality) {
768 ec.Emit (OpCodes.Ceq);
772 ec.Emit (OpCodes.Br_S, end_label);
774 ec.MarkLabel (dissimilar_label);
775 if (Oper == Operator.Inequality)
780 ec.MarkLabel (end_label);
783 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
786 ec.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
789 public override void Emit (EmitContext ec)
792 // Optimize same expression operation
794 if (right_unwrap != null && right.Equals (left))
795 right_unwrap = left_unwrap;
797 if (user_operator == null && IsBitwiseBoolean) {
798 EmitBitwiseBoolean (ec);
802 if ((Oper & Operator.EqualityMask) != 0) {
807 Label is_null_label = ec.DefineLabel ();
808 Label end_label = ec.DefineLabel ();
810 if (left_unwrap != null) {
811 left_unwrap.EmitCheck (ec);
812 ec.Emit (OpCodes.Brfalse, is_null_label);
816 // Don't emit HasValue check when left and right expressions are same
818 if (right_unwrap != null && !left.Equals (right)) {
819 right_unwrap.EmitCheck (ec);
820 ec.Emit (OpCodes.Brfalse, is_null_label);
823 EmitOperator (ec, left.Type);
825 if (wrap_ctor != null)
826 ec.Emit (OpCodes.Newobj, wrap_ctor);
828 ec.Emit (OpCodes.Br_S, end_label);
829 ec.MarkLabel (is_null_label);
831 if ((Oper & Operator.ComparisonMask) != 0) {
834 LiftedNull.Create (type, loc).Emit (ec);
837 ec.MarkLabel (end_label);
840 protected override void EmitOperator (EmitContext ec, TypeSpec l)
842 if (user_operator != null) {
843 user_operator.Emit (ec);
847 if (left.Type.IsNullableType) {
848 l = NullableInfo.GetUnderlyingType (left.Type);
849 left = EmptyCast.Create (left, l);
852 if (right.Type.IsNullableType) {
853 right = EmptyCast.Create (right, NullableInfo.GetUnderlyingType (right.Type));
856 base.EmitOperator (ec, l);
859 Expression LiftResult (ResolveContext ec, Expression res_expr)
861 TypeSpec lifted_type;
864 // Avoid double conversion
866 if (left_unwrap == null || IsLeftNullLifted || left_unwrap.Type != left.Type || (left_unwrap != null && IsRightNullLifted)) {
867 lifted_type = new NullableType (left.Type, loc).ResolveAsType (ec);
868 if (lifted_type == null)
871 if (left is UserCast || left is EmptyCast || left is OpcodeCast)
872 left.Type = lifted_type;
874 left = EmptyCast.Create (left, lifted_type);
877 if (left != right && (right_unwrap == null || IsRightNullLifted || right_unwrap.Type != right.Type || (right_unwrap != null && IsLeftNullLifted))) {
878 lifted_type = new NullableType (right.Type, loc).ResolveAsType (ec);
879 if (lifted_type == null)
883 if (r is ReducedExpression)
884 r = ((ReducedExpression) r).OriginalExpression;
886 if (r is UserCast || r is EmptyCast || r is OpcodeCast)
887 r.Type = lifted_type;
889 right = EmptyCast.Create (right, lifted_type);
892 if ((Oper & Operator.ComparisonMask) == 0) {
893 lifted_type = new NullableType (res_expr.Type, loc).ResolveAsType (ec);
894 if (lifted_type == null)
897 wrap_ctor = NullableInfo.GetConstructor (lifted_type);
898 type = res_expr.Type = lifted_type;
901 if (IsLeftNullLifted) {
902 left = LiftedNull.Create (right.Type, left.Location);
905 // Special case for bool?, the result depends on both null right side and left side value
907 if ((Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) && NullableInfo.GetUnderlyingType (type).BuiltinType == BuiltinTypeSpec.Type.Bool) {
911 if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0)
912 return LiftedNull.CreateFromExpression (ec, res_expr);
915 // Value types and null comparison
917 if (right_unwrap == null || (Oper & Operator.RelationalMask) != 0)
918 return CreateNullConstant (ec, right_orig);
921 if (IsRightNullLifted) {
922 right = LiftedNull.Create (left.Type, right.Location);
925 // Special case for bool?, the result depends on both null right side and left side value
927 if ((Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) && NullableInfo.GetUnderlyingType (type).BuiltinType == BuiltinTypeSpec.Type.Bool) {
931 if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0)
932 return LiftedNull.CreateFromExpression (ec, res_expr);
935 // Value types and null comparison
937 if (left_unwrap == null || (Oper & Operator.RelationalMask) != 0)
938 return CreateNullConstant (ec, left_orig);
944 protected override Expression ResolveOperatorPredefined (ResolveContext ec, Binary.PredefinedOperator [] operators, bool primitives_only, TypeSpec enum_type)
946 Expression e = base.ResolveOperatorPredefined (ec, operators, primitives_only, enum_type);
948 if (e == this || enum_type != null)
949 return LiftResult (ec, e);
952 // 7.9.9 Equality operators and null
954 // The == and != operators permit one operand to be a value of a nullable type and
955 // the other to be the null literal, even if no predefined or user-defined operator
956 // (in unlifted or lifted form) exists for the operation.
958 if (e == null && (Oper & Operator.EqualityMask) != 0) {
959 if ((IsLeftNullLifted && right_unwrap != null) || (IsRightNullLifted && left_unwrap != null))
960 return LiftResult (ec, this);
966 protected override Expression ResolveUserOperator (ResolveContext ec, Expression left, Expression right)
969 // Try original types first for exact match without unwrapping
971 Expression expr = base.ResolveUserOperator (ec, left_orig, right_orig);
975 State orig_state = state;
978 // One side is a nullable type, try to match underlying types
980 if (left_unwrap != null || right_unwrap != null || (state & (State.RightNullLifted | State.LeftNullLifted)) != 0) {
981 expr = base.ResolveUserOperator (ec, left, right);
988 // Lift the result in the case it can be null and predefined or user operator
989 // result type is of a value type
991 if (!TypeSpec.IsValueType (expr.Type))
994 if (state != orig_state)
997 expr = LiftResult (ec, expr);
998 if (expr is Constant)
1002 user_operator = expr;
1007 public class NullCoalescingOperator : Expression
1009 Expression left, right;
1012 public NullCoalescingOperator (Expression left, Expression right)
1016 this.loc = left.Location;
1019 public Expression LeftExpression {
1025 public Expression RightExpression {
1031 public override Expression CreateExpressionTree (ResolveContext ec)
1033 if (left is NullLiteral)
1034 ec.Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side");
1036 UserCast uc = left as UserCast;
1037 Expression conversion = null;
1041 Arguments c_args = new Arguments (2);
1042 c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
1043 c_args.Add (new Argument (left.CreateExpressionTree (ec)));
1044 conversion = CreateExpressionFactoryCall (ec, "Lambda", c_args);
1047 Arguments args = new Arguments (3);
1048 args.Add (new Argument (left.CreateExpressionTree (ec)));
1049 args.Add (new Argument (right.CreateExpressionTree (ec)));
1050 if (conversion != null)
1051 args.Add (new Argument (conversion));
1053 return CreateExpressionFactoryCall (ec, "Coalesce", args);
1056 Expression ConvertExpression (ResolveContext ec)
1058 // TODO: ImplicitConversionExists should take care of this
1059 if (left.eclass == ExprClass.MethodGroup)
1062 TypeSpec ltype = left.Type;
1065 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
1066 // the result is underlying type of left
1068 if (ltype.IsNullableType) {
1069 unwrap = Unwrap.Create (left, false);
1074 // Reduce (left ?? null) to left
1077 return ReducedExpression.Create (left, this);
1079 if (Convert.ImplicitConversionExists (ec, right, unwrap.Type)) {
1084 // If right is a dynamic expression, the result type is dynamic
1086 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1089 // Need to box underlying value type
1090 left = Convert.ImplicitBoxingConversion (left, ltype, type);
1094 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1098 } else if (TypeSpec.IsReferenceType (ltype)) {
1099 if (Convert.ImplicitConversionExists (ec, right, ltype)) {
1101 // If right is a dynamic expression, the result type is dynamic
1103 if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1109 // Reduce ("foo" ?? expr) to expression
1111 Constant lc = left as Constant;
1112 if (lc != null && !lc.IsDefaultValue)
1113 return ReducedExpression.Create (lc, this);
1116 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1118 if (right.IsNull || lc != null)
1119 return ReducedExpression.Create (lc != null ? right : left, this);
1121 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1127 // Special case null ?? null
1129 if (ltype == right.Type) {
1137 TypeSpec rtype = right.Type;
1138 if (!Convert.ImplicitConversionExists (ec, unwrap != null ? unwrap : left, rtype) || right.eclass == ExprClass.MethodGroup)
1142 // Reduce (null ?? right) to right
1145 return ReducedExpression.Create (right, this).Resolve (ec);
1147 left = Convert.ImplicitConversion (ec, unwrap != null ? unwrap : left, rtype, loc);
1152 public override bool ContainsEmitWithAwait ()
1155 return unwrap.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1157 return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1160 protected override Expression DoResolve (ResolveContext ec)
1162 left = left.Resolve (ec);
1163 right = right.Resolve (ec);
1165 if (left == null || right == null)
1168 eclass = ExprClass.Value;
1170 Expression e = ConvertExpression (ec);
1172 Binary.Error_OperatorCannotBeApplied (ec, left, right, "??", loc);
1179 public override void Emit (EmitContext ec)
1181 Label end_label = ec.DefineLabel ();
1183 if (unwrap != null) {
1184 Label is_null_label = ec.DefineLabel ();
1186 unwrap.EmitCheck (ec);
1187 ec.Emit (OpCodes.Brfalse, is_null_label);
1190 ec.Emit (OpCodes.Br, end_label);
1192 ec.MarkLabel (is_null_label);
1195 ec.MarkLabel (end_label);
1200 ec.Emit (OpCodes.Dup);
1202 // Only to make verifier happy
1203 if (left.Type.IsGenericParameter)
1204 ec.Emit (OpCodes.Box, left.Type);
1206 ec.Emit (OpCodes.Brtrue, end_label);
1208 ec.Emit (OpCodes.Pop);
1211 ec.MarkLabel (end_label);
1214 protected override void CloneTo (CloneContext clonectx, Expression t)
1216 NullCoalescingOperator target = (NullCoalescingOperator) t;
1218 target.left = left.Clone (clonectx);
1219 target.right = right.Clone (clonectx);
1222 public override object Accept (StructuralVisitor visitor)
1224 return visitor.Visit (this);
1228 class LiftedUnaryMutator : UnaryMutator
1230 public LiftedUnaryMutator (Mode mode, Expression expr, Location loc)
1231 : base (mode, expr, loc)
1235 protected override Expression DoResolve (ResolveContext ec)
1237 var orig_expr = expr;
1239 expr = Unwrap.Create (expr);
1241 var res = base.DoResolveOperation (ec);
1249 protected override void EmitOperation (EmitContext ec)
1251 Label is_null_label = ec.DefineLabel ();
1252 Label end_label = ec.DefineLabel ();
1254 LocalTemporary lt = new LocalTemporary (type);
1256 // Value is on the stack
1259 var call = new CallEmitter ();
1260 call.InstanceExpression = lt;
1261 call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null);
1263 ec.Emit (OpCodes.Brfalse, is_null_label);
1265 call = new CallEmitter ();
1266 call.InstanceExpression = lt;
1267 call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null);
1271 base.EmitOperation (ec);
1273 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
1274 ec.Emit (OpCodes.Br_S, end_label);
1276 ec.MarkLabel (is_null_label);
1277 LiftedNull.Create (type, loc).Emit (ec);
1279 ec.MarkLabel (end_label);