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
17 using IKVM.Reflection.Emit;
19 using System.Reflection.Emit;
22 namespace Mono.CSharp.Nullable
24 public class NullableType : TypeExpr
28 public NullableType (TypeExpr underlying, Location l)
30 this.underlying = underlying;
33 eclass = ExprClass.Type;
36 public NullableType (TypeSpec type, Location loc)
37 : this (new TypeExpression (type, loc), loc)
40 protected override TypeExpr DoResolveAsTypeStep (IMemberContext ec)
42 var type = ec.Module.PredefinedTypes.Nullable.Resolve (loc);
46 TypeArguments args = new TypeArguments (underlying);
47 GenericTypeExpr ctype = new GenericTypeExpr (type, args, loc);
48 return ctype.ResolveAsTypeTerminal (ec, false);
52 static class NullableInfo
54 public static MethodSpec GetConstructor (TypeSpec nullableType)
56 return TypeManager.GetPredefinedConstructor (nullableType, Location.Null, GetUnderlyingType (nullableType));
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];
82 public static bool IsNullableType (TypeSpec type)
84 throw new NotImplementedException ("net");
88 public class Unwrap : Expression, IMemoryLocation, IAssignMethod
93 readonly bool useDefaultValue;
95 Unwrap (Expression expr, bool useDefaultValue)
98 this.loc = expr.Location;
99 this.useDefaultValue = useDefaultValue;
101 type = NullableInfo.GetUnderlyingType (expr.Type);
102 eclass = expr.eclass;
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 Invocation.EmitCall (ec, this, NullableInfo.GetGetValueOrDefault (expr.Type), null, loc);
144 Invocation.EmitCall (ec, this, NullableInfo.GetValue (expr.Type), null, loc);
147 public void EmitCheck (EmitContext ec)
150 Invocation.EmitCall (ec, this, NullableInfo.GetHasValue (expr.Type), null, loc);
153 public override bool Equals (object obj)
155 Unwrap uw = obj as Unwrap;
156 return uw != null && expr.Equals (uw.expr);
159 public Expression Original {
165 public override int GetHashCode ()
167 return expr.GetHashCode ();
170 public override bool IsNull {
176 void Store (EmitContext ec)
178 if (expr is VariableReference)
185 LocalVariable.Store (ec);
188 public void Load (EmitContext ec)
190 if (expr is VariableReference)
193 LocalVariable.Emit (ec);
196 public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx)
198 return expr.MakeExpression (ctx);
201 public void AddressOf (EmitContext ec, AddressOp mode)
203 IMemoryLocation ml = expr as VariableReference;
205 ml.AddressOf (ec, mode);
207 LocalVariable.AddressOf (ec, mode);
211 // Keeps result of non-variable expression
213 LocalTemporary LocalVariable {
216 temp = new LocalTemporary (expr.Type);
221 public void Emit (EmitContext ec, bool leave_copy)
229 public void EmitAssign (EmitContext ec, Expression source,
230 bool leave_copy, bool prepare_for_load)
232 InternalWrap wrap = new InternalWrap (source, expr.Type, loc);
233 ((IAssignMethod) expr).EmitAssign (ec, wrap, leave_copy, false);
236 class InternalWrap : Expression
238 public Expression expr;
240 public InternalWrap (Expression expr, TypeSpec type, Location loc)
246 eclass = ExprClass.Value;
249 public override Expression CreateExpressionTree (ResolveContext ec)
251 throw new NotSupportedException ("ET");
254 protected override Expression DoResolve (ResolveContext ec)
259 public override void Emit (EmitContext ec)
262 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
268 // Calls get_Value method on nullable expression
270 public class UnwrapCall : CompositeExpression
272 public UnwrapCall (Expression expr)
277 protected override Expression DoResolve (ResolveContext rc)
282 type = NullableInfo.GetUnderlyingType (type);
287 public override void Emit (EmitContext ec)
289 Invocation.EmitCall (ec, Child, NullableInfo.GetValue (Child.Type), null, loc);
293 public class Wrap : TypeCast
295 private Wrap (Expression expr, TypeSpec type)
298 eclass = ExprClass.Value;
301 public override Expression CreateExpressionTree (ResolveContext ec)
303 TypeCast child_cast = child as TypeCast;
304 if (child_cast != null) {
306 return child_cast.CreateExpressionTree (ec);
309 return base.CreateExpressionTree (ec);
312 public static Expression Create (Expression expr, TypeSpec type)
315 // Avoid unwraping and wraping of the same type
317 Unwrap unwrap = expr as Unwrap;
318 if (unwrap != null && expr.Type == NullableInfo.GetUnderlyingType (type))
319 return unwrap.Original;
321 return new Wrap (expr, type);
324 public override void Emit (EmitContext ec)
327 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
332 // Represents null literal lifted to nullable type
334 public class LiftedNull : NullConstant, IMemoryLocation
336 private LiftedNull (TypeSpec nullable_type, Location loc)
337 : base (nullable_type, loc)
339 eclass = ExprClass.Value;
342 public static Constant Create (TypeSpec nullable, Location loc)
344 return new LiftedNull (nullable, loc);
347 public static Constant CreateFromExpression (ResolveContext ec, Expression e)
349 ec.Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'",
350 TypeManager.CSharpName (e.Type));
352 return ReducedExpression.Create (Create (e.Type, e.Location), e);
355 public override void Emit (EmitContext ec)
357 // TODO: generate less temporary variables
358 LocalTemporary value_target = new LocalTemporary (type);
360 value_target.AddressOf (ec, AddressOp.Store);
361 ec.Emit (OpCodes.Initobj, type);
362 value_target.Emit (ec);
365 public void AddressOf (EmitContext ec, AddressOp Mode)
367 LocalTemporary value_target = new LocalTemporary (type);
369 value_target.AddressOf (ec, AddressOp.Store);
370 ec.Emit (OpCodes.Initobj, type);
371 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
376 // Generic lifting expression, supports all S/S? -> T/T? cases
378 public class Lifted : Expression, IMemoryLocation
380 Expression expr, null_value;
383 public Lifted (Expression expr, Unwrap unwrap, TypeSpec type)
386 this.unwrap = unwrap;
387 this.loc = expr.Location;
391 public Lifted (Expression expr, Expression unwrap, TypeSpec type)
392 : this (expr, unwrap as Unwrap, type)
396 public override Expression CreateExpressionTree (ResolveContext ec)
398 return expr.CreateExpressionTree (ec);
401 protected override Expression DoResolve (ResolveContext ec)
404 // It's null when lifting non-nullable type
406 if (unwrap == null) {
407 // S -> T? is wrap only
408 if (TypeManager.IsNullableType (type))
409 return Wrap.Create (expr, type);
411 // S -> T can be simplified
415 // Wrap target for T?
416 if (TypeManager.IsNullableType (type)) {
417 expr = Wrap.Create (expr, type);
421 null_value = LiftedNull.Create (type, loc);
422 } else if (TypeManager.IsValueType (type)) {
423 null_value = LiftedNull.Create (type, loc);
425 null_value = new NullConstant (type, loc);
428 eclass = ExprClass.Value;
432 public override void Emit (EmitContext ec)
434 Label is_null_label = ec.DefineLabel ();
435 Label end_label = ec.DefineLabel ();
437 unwrap.EmitCheck (ec);
438 ec.Emit (OpCodes.Brfalse, is_null_label);
442 ec.Emit (OpCodes.Br, end_label);
443 ec.MarkLabel (is_null_label);
445 null_value.Emit (ec);
446 ec.MarkLabel (end_label);
449 public void AddressOf (EmitContext ec, AddressOp mode)
451 unwrap.AddressOf (ec, mode);
455 public class LiftedUnaryOperator : Unary, IMemoryLocation
458 Expression user_operator;
460 public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc)
461 : base (op, expr, loc)
465 public void AddressOf (EmitContext ec, AddressOp mode)
467 unwrap.AddressOf (ec, mode);
470 public override Expression CreateExpressionTree (ResolveContext ec)
472 if (user_operator != null)
473 return user_operator.CreateExpressionTree (ec);
475 if (Oper == Operator.UnaryPlus)
476 return Expr.CreateExpressionTree (ec);
478 return base.CreateExpressionTree (ec);
481 protected override Expression DoResolve (ResolveContext ec)
483 unwrap = Unwrap.Create (Expr, false);
487 Expression res = base.ResolveOperator (ec, unwrap);
489 if (user_operator == null)
492 res = Expr = LiftExpression (ec, Expr);
498 eclass = ExprClass.Value;
503 public override void Emit (EmitContext ec)
505 Label is_null_label = ec.DefineLabel ();
506 Label end_label = ec.DefineLabel ();
508 unwrap.EmitCheck (ec);
509 ec.Emit (OpCodes.Brfalse, is_null_label);
511 if (user_operator != null) {
512 user_operator.Emit (ec);
514 EmitOperator (ec, NullableInfo.GetUnderlyingType (type));
517 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
518 ec.Emit (OpCodes.Br_S, end_label);
520 ec.MarkLabel (is_null_label);
521 LiftedNull.Create (type, loc).Emit (ec);
523 ec.MarkLabel (end_label);
526 Expression LiftExpression (ResolveContext ec, Expression expr)
528 TypeExpr lifted_type = new NullableType (expr.Type, expr.Location);
529 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
530 if (lifted_type == null)
533 expr.Type = lifted_type.Type;
537 protected override Expression ResolveEnumOperator (ResolveContext ec, Expression expr)
539 expr = base.ResolveEnumOperator (ec, expr);
543 Expr = LiftExpression (ec, Expr);
544 return LiftExpression (ec, expr);
547 protected override Expression ResolveUserOperator (ResolveContext ec, Expression expr)
549 expr = base.ResolveUserOperator (ec, expr);
554 // When a user operator is of non-nullable type
556 if (Expr is Unwrap) {
557 user_operator = LiftExpression (ec, expr);
558 return user_operator;
565 public class LiftedBinaryOperator : Binary
567 Unwrap left_unwrap, right_unwrap;
568 Expression left_orig, right_orig;
569 Expression user_operator;
570 MethodSpec wrap_ctor;
572 public LiftedBinaryOperator (Binary.Operator op, Expression left, Expression right, Location loc)
573 : base (op, left, right, loc)
577 bool IsBitwiseBoolean {
579 return (Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) &&
580 ((left_unwrap != null && left_unwrap.Type == TypeManager.bool_type) ||
581 (right_unwrap != null && right_unwrap.Type == TypeManager.bool_type));
585 bool IsLeftNullLifted {
587 return (state & State.LeftNullLifted) != 0;
591 bool IsRightNullLifted {
593 return (state & State.RightNullLifted) != 0;
597 public override Expression CreateExpressionTree (ResolveContext ec)
599 if (user_operator != null)
600 return user_operator.CreateExpressionTree (ec);
602 return base.CreateExpressionTree (ec);
606 // CSC 2 has this behavior, it allows structs to be compared
607 // with the null literal *outside* of a generics context and
608 // inlines that as true or false.
610 Constant CreateNullConstant (ResolveContext ec, Expression expr)
612 // FIXME: Handle side effect constants
613 Constant c = new BoolConstant (Oper == Operator.Inequality, loc).Resolve (ec);
615 if ((Oper & Operator.EqualityMask) != 0) {
616 ec.Report.Warning (472, 2, loc, "The result of comparing value type `{0}' with null is `{1}'",
617 TypeManager.CSharpName (expr.Type), c.AsString ());
619 ec.Report.Warning (464, 2, loc, "The result of comparing type `{0}' with null is always `{1}'",
620 TypeManager.CSharpName (expr.Type), c.AsString ());
623 return ReducedExpression.Create (c, this);
626 protected override Expression DoResolve (ResolveContext ec)
628 if ((Oper & Operator.LogicalMask) != 0) {
629 Error_OperatorCannotBeApplied (ec, left, right);
633 bool use_default_call = (Oper & (Operator.BitwiseMask | Operator.EqualityMask)) != 0;
635 if (TypeManager.IsNullableType (left.Type)) {
636 left = left_unwrap = Unwrap.Create (left, use_default_call);
642 if (TypeManager.IsNullableType (right.Type)) {
643 right = right_unwrap = Unwrap.Create (right, use_default_call);
649 // Some details are in 6.4.2, 7.2.7
650 // Arguments can be lifted for equal operators when the return type is bool and both
651 // arguments are of same type
653 if (left_orig is NullLiteral) {
655 state |= State.LeftNullLifted;
656 type = TypeManager.bool_type;
659 if (right_orig.IsNull) {
660 if ((Oper & Operator.ShiftMask) != 0)
661 right = new EmptyExpression (TypeManager.int32_type);
665 state |= State.RightNullLifted;
666 type = TypeManager.bool_type;
669 eclass = ExprClass.Value;
670 return DoResolveCore (ec, left_orig, right_orig);
673 void EmitBitwiseBoolean (EmitContext ec)
675 Label load_left = ec.DefineLabel ();
676 Label load_right = ec.DefineLabel ();
677 Label end_label = ec.DefineLabel ();
679 // null & value, null | value
680 if (left_unwrap == null) {
681 left_unwrap = right_unwrap;
686 left_unwrap.Emit (ec);
687 ec.Emit (OpCodes.Brtrue_S, load_right);
689 // value & null, value | null
690 if (right_unwrap != null) {
691 right_unwrap.Emit (ec);
692 ec.Emit (OpCodes.Brtrue_S, load_left);
695 left_unwrap.EmitCheck (ec);
696 ec.Emit (OpCodes.Brfalse_S, load_right);
699 ec.MarkLabel (load_left);
701 if (Oper == Operator.BitwiseAnd) {
702 left_unwrap.Load (ec);
704 if (right_unwrap == null) {
706 if (right is EmptyConstantCast || right is EmptyCast)
707 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
709 right_unwrap.Load (ec);
710 right_unwrap = left_unwrap;
713 ec.Emit (OpCodes.Br_S, end_label);
716 ec.MarkLabel (load_right);
717 if (right_unwrap == null) {
718 if (Oper == Operator.BitwiseAnd) {
720 if (right is EmptyConstantCast || right is EmptyCast)
721 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
723 left_unwrap.Load (ec);
726 right_unwrap.Load (ec);
729 ec.MarkLabel (end_label);
733 // Emits optimized equality or inequality operator when possible
735 void EmitEquality (EmitContext ec)
738 // Either left or right is null
740 if (left_unwrap != null && (IsRightNullLifted || right.IsNull)) {
741 left_unwrap.EmitCheck (ec);
742 if (Oper == Binary.Operator.Equality) {
743 ec.Emit (OpCodes.Ldc_I4_0);
744 ec.Emit (OpCodes.Ceq);
749 if (right_unwrap != null && (IsLeftNullLifted || left.IsNull)) {
750 right_unwrap.EmitCheck (ec);
751 if (Oper == Binary.Operator.Equality) {
752 ec.Emit (OpCodes.Ldc_I4_0);
753 ec.Emit (OpCodes.Ceq);
758 Label dissimilar_label = ec.DefineLabel ();
759 Label end_label = ec.DefineLabel ();
761 if (user_operator != null) {
762 user_operator.Emit (ec);
763 ec.Emit (Oper == Operator.Equality ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, dissimilar_label);
768 ec.Emit (OpCodes.Bne_Un_S, dissimilar_label);
771 if (left_unwrap != null)
772 left_unwrap.EmitCheck (ec);
774 if (right_unwrap != null)
775 right_unwrap.EmitCheck (ec);
777 if (left_unwrap != null && right_unwrap != null) {
778 if (Oper == Operator.Inequality)
779 ec.Emit (OpCodes.Xor);
781 ec.Emit (OpCodes.Ceq);
783 if (Oper == Operator.Inequality) {
784 ec.Emit (OpCodes.Ldc_I4_0);
785 ec.Emit (OpCodes.Ceq);
789 ec.Emit (OpCodes.Br_S, end_label);
791 ec.MarkLabel (dissimilar_label);
792 if (Oper == Operator.Inequality)
793 ec.Emit (OpCodes.Ldc_I4_1);
795 ec.Emit (OpCodes.Ldc_I4_0);
797 ec.MarkLabel (end_label);
800 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
803 ec.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
806 public override void Emit (EmitContext ec)
809 // Optimize same expression operation
811 if (right_unwrap != null && right.Equals (left))
812 right_unwrap = left_unwrap;
814 if (user_operator == null && IsBitwiseBoolean) {
815 EmitBitwiseBoolean (ec);
819 if ((Oper & Operator.EqualityMask) != 0) {
824 Label is_null_label = ec.DefineLabel ();
825 Label end_label = ec.DefineLabel ();
827 if (left_unwrap != null) {
828 left_unwrap.EmitCheck (ec);
829 ec.Emit (OpCodes.Brfalse, is_null_label);
833 // Don't emit HasValue check when left and right expressions are same
835 if (right_unwrap != null && !left.Equals (right)) {
836 right_unwrap.EmitCheck (ec);
837 ec.Emit (OpCodes.Brfalse, is_null_label);
840 EmitOperator (ec, left.Type);
842 if (wrap_ctor != null)
843 ec.Emit (OpCodes.Newobj, wrap_ctor);
845 ec.Emit (OpCodes.Br_S, end_label);
846 ec.MarkLabel (is_null_label);
848 if ((Oper & Operator.ComparisonMask) != 0) {
849 ec.Emit (OpCodes.Ldc_I4_0);
851 LiftedNull.Create (type, loc).Emit (ec);
854 ec.MarkLabel (end_label);
857 protected override void EmitOperator (EmitContext ec, TypeSpec l)
859 if (user_operator != null) {
860 user_operator.Emit (ec);
864 if (TypeManager.IsNullableType (l))
865 l = TypeManager.GetTypeArguments (l) [0];
867 base.EmitOperator (ec, l);
870 Expression LiftResult (ResolveContext ec, Expression res_expr)
872 TypeExpr lifted_type;
875 // Avoid double conversion
877 if (left_unwrap == null || IsLeftNullLifted || left_unwrap.Type != left.Type || (left_unwrap != null && IsRightNullLifted)) {
878 lifted_type = new NullableType (left.Type, loc);
879 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
880 if (lifted_type == null)
883 if (left is UserCast || left is TypeCast)
884 left.Type = lifted_type.Type;
886 left = EmptyCast.Create (left, lifted_type.Type);
889 if (left != right && (right_unwrap == null || IsRightNullLifted || right_unwrap.Type != right.Type || (right_unwrap != null && IsLeftNullLifted))) {
890 lifted_type = new NullableType (right.Type, loc);
891 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
892 if (lifted_type == null)
896 if (r is ReducedExpression)
897 r = ((ReducedExpression) r).OriginalExpression;
899 if (r is UserCast || r is TypeCast)
900 r.Type = lifted_type.Type;
902 right = EmptyCast.Create (right, lifted_type.Type);
905 if ((Oper & Operator.ComparisonMask) == 0) {
906 lifted_type = new NullableType (res_expr.Type, loc);
907 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
908 if (lifted_type == null)
911 wrap_ctor = NullableInfo.GetConstructor (lifted_type.Type);
912 type = res_expr.Type = lifted_type.Type;
915 if (IsLeftNullLifted) {
916 left = LiftedNull.Create (right.Type, left.Location);
919 // Special case for bool?, the result depends on both null right side and left side value
921 if ((Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) && NullableInfo.GetUnderlyingType (type) == TypeManager.bool_type) {
925 if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0)
926 return LiftedNull.CreateFromExpression (ec, res_expr);
929 // Value types and null comparison
931 if (right_unwrap == null || (Oper & Operator.RelationalMask) != 0)
932 return CreateNullConstant (ec, right_orig).Resolve (ec);
935 if (IsRightNullLifted) {
936 right = LiftedNull.Create (left.Type, right.Location);
939 // Special case for bool?, the result depends on both null right side and left side value
941 if ((Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) && NullableInfo.GetUnderlyingType (type) == TypeManager.bool_type) {
945 if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0)
946 return LiftedNull.CreateFromExpression (ec, res_expr);
949 // Value types and null comparison
951 if (left_unwrap == null || (Oper & Operator.RelationalMask) != 0)
952 return CreateNullConstant (ec, left_orig);
958 protected override Expression ResolveOperatorPredefined (ResolveContext ec, Binary.PredefinedOperator [] operators, bool primitives_only, TypeSpec enum_type)
960 Expression e = base.ResolveOperatorPredefined (ec, operators, primitives_only, enum_type);
962 if (e == this || enum_type != null)
963 return LiftResult (ec, e);
966 // 7.9.9 Equality operators and null
968 // The == and != operators permit one operand to be a value of a nullable type and
969 // the other to be the null literal, even if no predefined or user-defined operator
970 // (in unlifted or lifted form) exists for the operation.
972 if (e == null && (Oper & Operator.EqualityMask) != 0) {
973 if ((IsLeftNullLifted && right_unwrap != null) || (IsRightNullLifted && left_unwrap != null))
974 return LiftResult (ec, this);
980 protected override Expression ResolveUserOperator (ResolveContext ec, Expression left, Expression right)
983 // Try original types first for exact match without unwrapping
985 Expression expr = base.ResolveUserOperator (ec, left_orig, right_orig);
989 State orig_state = state;
992 // One side is a nullable type, try to match underlying types
994 if (left_unwrap != null || right_unwrap != null || (state & (State.RightNullLifted | State.LeftNullLifted)) != 0) {
995 expr = base.ResolveUserOperator (ec, left, right);
1002 // Lift the result in the case it can be null and predefined or user operator
1003 // result type is of a value type
1005 if (!TypeManager.IsValueType (expr.Type))
1008 if (state != orig_state)
1011 expr = LiftResult (ec, expr);
1012 if (expr is Constant)
1016 user_operator = expr;
1021 public class NullCoalescingOperator : Expression
1023 Expression left, right;
1026 public NullCoalescingOperator (Expression left, Expression right, Location loc)
1033 public override Expression CreateExpressionTree (ResolveContext ec)
1035 if (left is NullLiteral)
1036 ec.Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side");
1038 UserCast uc = left as UserCast;
1039 Expression conversion = null;
1043 Arguments c_args = new Arguments (2);
1044 c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
1045 c_args.Add (new Argument (left.CreateExpressionTree (ec)));
1046 conversion = CreateExpressionFactoryCall (ec, "Lambda", c_args);
1049 Arguments args = new Arguments (3);
1050 args.Add (new Argument (left.CreateExpressionTree (ec)));
1051 args.Add (new Argument (right.CreateExpressionTree (ec)));
1052 if (conversion != null)
1053 args.Add (new Argument (conversion));
1055 return CreateExpressionFactoryCall (ec, "Coalesce", args);
1058 Expression ConvertExpression (ResolveContext ec)
1060 // TODO: ImplicitConversionExists should take care of this
1061 if (left.eclass == ExprClass.MethodGroup)
1064 TypeSpec ltype = left.Type;
1067 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
1068 // the result is underlying type of left
1070 if (TypeManager.IsNullableType (ltype)) {
1071 unwrap = Unwrap.Create (left, false);
1076 // Reduce (left ?? null) to left
1079 return ReducedExpression.Create (left, this);
1081 if (Convert.ImplicitConversionExists (ec, right, unwrap.Type)) {
1086 // If right is a dynamic expression, the result type is dynamic
1088 if (right.Type == InternalType.Dynamic) {
1091 // Need to box underlying value type
1092 left = Convert.ImplicitBoxingConversion (left, ltype, type);
1096 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1100 } else if (TypeManager.IsReferenceType (ltype)) {
1101 if (Convert.ImplicitConversionExists (ec, right, ltype)) {
1103 // If right is a dynamic expression, the result type is dynamic
1105 if (right.Type == InternalType.Dynamic) {
1111 // Reduce ("foo" ?? expr) to expression
1113 Constant lc = left as Constant;
1114 if (lc != null && !lc.IsDefaultValue)
1115 return ReducedExpression.Create (lc, this).Resolve (ec);
1118 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1120 if (right.IsNull || lc != null)
1121 return ReducedExpression.Create (lc != null ? right : left, this).Resolve (ec);
1123 right = Convert.ImplicitConversion (ec, right, ltype, loc);
1131 TypeSpec rtype = right.Type;
1132 if (!Convert.ImplicitConversionExists (ec, unwrap != null ? unwrap : left, rtype) || right.eclass == ExprClass.MethodGroup)
1136 // Reduce (null ?? right) to right
1139 return ReducedExpression.Create (right, this).Resolve (ec);
1141 left = Convert.ImplicitConversion (ec, unwrap != null ? unwrap : left, rtype, loc);
1146 protected override Expression DoResolve (ResolveContext ec)
1148 left = left.Resolve (ec);
1149 right = right.Resolve (ec);
1151 if (left == null || right == null)
1154 eclass = ExprClass.Value;
1156 Expression e = ConvertExpression (ec);
1158 Binary.Error_OperatorCannotBeApplied (ec, left, right, "??", loc);
1165 public override void Emit (EmitContext ec)
1167 Label end_label = ec.DefineLabel ();
1169 if (unwrap != null) {
1170 Label is_null_label = ec.DefineLabel ();
1172 unwrap.EmitCheck (ec);
1173 ec.Emit (OpCodes.Brfalse, is_null_label);
1176 ec.Emit (OpCodes.Br, end_label);
1178 ec.MarkLabel (is_null_label);
1181 ec.MarkLabel (end_label);
1186 ec.Emit (OpCodes.Dup);
1188 // Only to make verifier happy
1189 if (left.Type.IsGenericParameter)
1190 ec.Emit (OpCodes.Box, left.Type);
1192 ec.Emit (OpCodes.Brtrue, end_label);
1194 ec.Emit (OpCodes.Pop);
1197 ec.MarkLabel (end_label);
1200 protected override void CloneTo (CloneContext clonectx, Expression t)
1202 NullCoalescingOperator target = (NullCoalescingOperator) t;
1204 target.left = left.Clone (clonectx);
1205 target.right = right.Clone (clonectx);
1209 public class LiftedUnaryMutator : ExpressionStatement
1211 public readonly UnaryMutator.Mode Mode;
1213 UnaryMutator underlying;
1216 public LiftedUnaryMutator (UnaryMutator.Mode mode, Expression expr, Location loc)
1223 public override Expression CreateExpressionTree (ResolveContext ec)
1225 return new SimpleAssign (this, this).CreateExpressionTree (ec);
1228 protected override Expression DoResolve (ResolveContext ec)
1230 expr = expr.Resolve (ec);
1234 unwrap = Unwrap.Create (expr, false);
1238 underlying = (UnaryMutator) new UnaryMutator (Mode, unwrap, loc).Resolve (ec);
1239 if (underlying == null)
1243 eclass = ExprClass.Value;
1248 void DoEmit (EmitContext ec, bool is_expr)
1250 Label is_null_label = ec.DefineLabel ();
1251 Label end_label = ec.DefineLabel ();
1253 unwrap.EmitCheck (ec);
1254 ec.Emit (OpCodes.Brfalse, is_null_label);
1257 underlying.Emit (ec);
1258 ec.Emit (OpCodes.Br_S, end_label);
1260 underlying.EmitStatement (ec);
1263 ec.MarkLabel (is_null_label);
1265 LiftedNull.Create (type, loc).Emit (ec);
1267 ec.MarkLabel (end_label);
1270 public override void Emit (EmitContext ec)
1275 public override void EmitStatement (EmitContext ec)