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);
51 public override TypeExpr
ResolveAsTypeTerminal (IResolveContext ec
, bool silent
)
53 return ResolveAsBaseTerminal (ec
, silent
);
57 public sealed class NullableInfo
59 public readonly Type Type
;
60 public readonly Type UnderlyingType
;
61 public MethodInfo HasValue
;
62 public MethodInfo Value
;
63 public MethodInfo GetValueOrDefault
;
64 public ConstructorInfo Constructor
;
66 public NullableInfo (Type type
)
69 UnderlyingType
= TypeManager
.GetTypeArguments (type
) [0];
71 PropertyInfo has_value_pi
= TypeManager
.GetPredefinedProperty (type
, "HasValue", Location
.Null
, Type
.EmptyTypes
);
72 PropertyInfo value_pi
= TypeManager
.GetPredefinedProperty (type
, "Value", Location
.Null
, Type
.EmptyTypes
);
73 GetValueOrDefault
= TypeManager
.GetPredefinedMethod (type
, "GetValueOrDefault", Location
.Null
, Type
.EmptyTypes
);
75 HasValue
= has_value_pi
.GetGetMethod (false);
76 Value
= value_pi
.GetGetMethod (false);
78 if (UnderlyingType
.Module
== CodeGen
.Module
.Builder
) {
79 Type o_type
= TypeManager
.DropGenericTypeArguments (type
);
80 Constructor
= TypeBuilder
.GetConstructor (type
,
81 TypeManager
.GetPredefinedConstructor (o_type
, Location
.Null
, o_type
.GetGenericArguments ()));
85 Constructor
= type
.GetConstructor (new Type
[] { UnderlyingType }
);
89 public class Unwrap
: Expression
, IMemoryLocation
, IAssignMethod
96 protected Unwrap (Expression expr
)
99 this.loc
= expr
.Location
;
102 public static Unwrap
Create (Expression expr
, EmitContext ec
)
104 return new Unwrap (expr
).Resolve (ec
) as Unwrap
;
107 public override Expression
CreateExpressionTree (EmitContext ec
)
109 return expr
.CreateExpressionTree (ec
);
112 public override Expression
DoResolve (EmitContext ec
)
117 info
= new NullableInfo (expr
.Type
);
118 type
= info
.UnderlyingType
;
119 eclass
= expr
.eclass
;
123 public override Expression
DoResolveLValue (EmitContext ec
, Expression right_side
)
125 return DoResolve (ec
);
128 public override void Emit (EmitContext ec
)
131 AddressOf (ec
, AddressOp
.LoadStore
);
132 ec
.ig
.EmitCall (OpCodes
.Call
, info
.Value
, null);
135 public void EmitCheck (EmitContext ec
)
138 AddressOf (ec
, AddressOp
.LoadStore
);
139 ec
.ig
.EmitCall (OpCodes
.Call
, info
.HasValue
, null);
142 public void EmitGetValueOrDefault (EmitContext ec
)
145 AddressOf (ec
, AddressOp
.LoadStore
);
146 ec
.ig
.EmitCall (OpCodes
.Call
, info
.GetValueOrDefault
, null);
149 public override bool Equals (object obj
)
151 Unwrap uw
= obj
as Unwrap
;
152 return uw
!= null && expr
.Equals (uw
.expr
);
155 public Expression Original
{
161 public override int GetHashCode ()
163 return expr
.GetHashCode ();
166 public override bool IsNull
{
172 void Store (EmitContext ec
)
174 if (expr
is VariableReference
)
181 LocalVariable
.Store (ec
);
184 public void Load (EmitContext ec
)
186 if (expr
is VariableReference
)
189 LocalVariable
.Emit (ec
);
192 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
194 type
= storey
.MutateType (type
);
195 info
.Constructor
= storey
.MutateConstructor (info
.Constructor
);
196 info
.HasValue
= storey
.MutateGenericMethod (info
.HasValue
);
197 info
.GetValueOrDefault
= storey
.MutateGenericMethod (info
.GetValueOrDefault
);
198 info
.Value
= storey
.MutateGenericMethod (info
.Value
);
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 (info
.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
, info
, loc
);
233 ((IAssignMethod
) expr
).EmitAssign (ec
, wrap
, leave_copy
, false);
236 protected class InternalWrap
: Expression
238 public Expression expr
;
239 public NullableInfo info
;
241 public InternalWrap (Expression expr
, NullableInfo info
, Location loc
)
248 eclass
= ExprClass
.Value
;
251 public override Expression
CreateExpressionTree (EmitContext ec
)
253 throw new NotSupportedException ("ET");
256 public override Expression
DoResolve (EmitContext ec
)
261 public override void Emit (EmitContext ec
)
264 ec
.ig
.Emit (OpCodes
.Newobj
, info
.Constructor
);
269 public class Wrap
: TypeCast
271 readonly NullableInfo info
;
273 protected Wrap (Expression expr
, Type type
)
276 info
= new NullableInfo (type
);
277 eclass
= ExprClass
.Value
;
280 public static Expression
Create (Expression expr
, Type type
)
283 // Avoid unwraping and wraping of the same type
285 Unwrap unwrap
= expr
as Unwrap
;
286 if (unwrap
!= null && TypeManager
.IsEqual (expr
.Type
, TypeManager
.GetTypeArguments (type
) [0]))
287 return unwrap
.Original
;
289 return new Wrap (expr
, type
);
292 public override void Emit (EmitContext ec
)
295 ec
.ig
.Emit (OpCodes
.Newobj
, info
.Constructor
);
300 // Represents null literal lifted to nullable type
302 public class LiftedNull
: EmptyConstantCast
, IMemoryLocation
304 private LiftedNull (Type nullable_type
, Location loc
)
305 : base (new NullLiteral (loc
), nullable_type
)
307 eclass
= ExprClass
.Value
;
310 public static Constant
Create (Type nullable
, Location loc
)
312 return new LiftedNull (nullable
, loc
);
315 public static Expression
CreateFromExpression (Expression e
)
317 Report
.Warning (458, 2, e
.Location
, "The result of the expression is always `null' of type `{0}'",
318 TypeManager
.CSharpName (e
.Type
));
320 return ReducedExpression
.Create (Create (e
.Type
, e
.Location
), e
);
323 public override Expression
CreateExpressionTree (EmitContext ec
)
325 ArrayList args
= new ArrayList (2);
326 args
.Add (new Argument (this));
327 args
.Add (new Argument (new TypeOf (new TypeExpression (type
, loc
), loc
)));
329 return CreateExpressionFactoryCall ("Constant", args
);
332 public override void Emit (EmitContext ec
)
334 // TODO: generate less temporary variables
335 LocalTemporary value_target
= new LocalTemporary (type
);
337 value_target
.AddressOf (ec
, AddressOp
.Store
);
338 ec
.ig
.Emit (OpCodes
.Initobj
, type
);
339 value_target
.Emit (ec
);
342 public void AddressOf (EmitContext ec
, AddressOp Mode
)
344 LocalTemporary value_target
= new LocalTemporary (type
);
346 value_target
.AddressOf (ec
, AddressOp
.Store
);
347 ec
.ig
.Emit (OpCodes
.Initobj
, type
);
348 ((IMemoryLocation
) value_target
).AddressOf (ec
, Mode
);
352 public abstract class Lifted
: Expression
, IMemoryLocation
354 Expression expr
, underlying
, wrap
, null_value
;
357 protected Lifted (Expression expr
, Location loc
)
363 public override Expression
CreateExpressionTree (EmitContext ec
)
365 ArrayList args
= new ArrayList (2);
366 args
.Add (new Argument (expr
.CreateExpressionTree (ec
)));
367 args
.Add (new Argument (new TypeOf (new TypeExpression (type
, loc
), loc
)));
368 return CreateExpressionFactoryCall ("Convert", args
);
371 public override Expression
DoResolve (EmitContext ec
)
373 expr
= expr
.Resolve (ec
);
377 unwrap
= Unwrap
.Create (expr
, ec
);
381 underlying
= ResolveUnderlying (unwrap
, ec
);
382 if (underlying
== null)
385 TypeExpr target_type
= new NullableType (underlying
.Type
, loc
);
386 target_type
= target_type
.ResolveAsTypeTerminal (ec
, false);
387 if (target_type
== null)
390 wrap
= Wrap
.Create (underlying
, target_type
.Type
);
394 null_value
= LiftedNull
.Create (wrap
.Type
, loc
);
397 eclass
= ExprClass
.Value
;
401 protected abstract Expression
ResolveUnderlying (Expression unwrap
, EmitContext ec
);
403 public override void Emit (EmitContext ec
)
405 ILGenerator ig
= ec
.ig
;
406 Label is_null_label
= ig
.DefineLabel ();
407 Label end_label
= ig
.DefineLabel ();
409 unwrap
.EmitCheck (ec
);
410 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
413 ig
.Emit (OpCodes
.Br
, end_label
);
415 ig
.MarkLabel (is_null_label
);
416 null_value
.Emit (ec
);
418 ig
.MarkLabel (end_label
);
421 public void AddressOf (EmitContext ec
, AddressOp mode
)
423 unwrap
.AddressOf (ec
, mode
);
427 public class LiftedConversion
: Lifted
429 public readonly bool IsUser
;
430 public readonly bool IsExplicit
;
431 public readonly Type TargetType
;
433 public LiftedConversion (Expression expr
, Type target_type
, bool is_user
,
434 bool is_explicit
, Location loc
)
437 this.IsUser
= is_user
;
438 this.IsExplicit
= is_explicit
;
439 this.TargetType
= target_type
;
442 protected override Expression
ResolveUnderlying (Expression unwrap
, EmitContext ec
)
444 Type type
= TypeManager
.GetTypeArguments (TargetType
) [0];
448 return Convert
.ExplicitUserConversion (ec
, unwrap
, type
, loc
);
450 return Convert
.ImplicitUserConversion (ec
, unwrap
, type
, loc
);
453 return Convert
.ExplicitConversion (ec
, unwrap
, type
, loc
);
455 return Convert
.ImplicitConversion (ec
, unwrap
, type
, loc
);
460 public class LiftedUnaryOperator
: Unary
, IMemoryLocation
463 Expression user_operator
;
465 public LiftedUnaryOperator (Unary
.Operator op
, Expression expr
)
470 public void AddressOf (EmitContext ec
, AddressOp mode
)
472 unwrap
.AddressOf (ec
, mode
);
475 public override Expression
CreateExpressionTree (EmitContext ec
)
477 if (user_operator
!= null)
478 return user_operator
.CreateExpressionTree (ec
);
480 if (Oper
== Operator
.UnaryPlus
)
481 return Expr
.CreateExpressionTree (ec
);
483 return base.CreateExpressionTree (ec
);
486 public override Expression
DoResolve (EmitContext ec
)
488 if (eclass
!= ExprClass
.Invalid
)
491 unwrap
= Unwrap
.Create (Expr
, ec
);
495 Expression res
= base.ResolveOperator (ec
, unwrap
);
497 if (user_operator
== null)
500 res
= Expr
= LiftExpression (ec
, Expr
);
506 eclass
= ExprClass
.Value
;
511 public override void Emit (EmitContext ec
)
513 ILGenerator ig
= ec
.ig
;
514 Label is_null_label
= ig
.DefineLabel ();
515 Label end_label
= ig
.DefineLabel ();
517 unwrap
.EmitCheck (ec
);
518 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
520 NullableInfo ni
= new NullableInfo (type
);
522 if (user_operator
!= null) {
523 user_operator
.Emit (ec
);
525 EmitOperator (ec
, ni
.UnderlyingType
);
528 ig
.Emit (OpCodes
.Newobj
, ni
.Constructor
);
529 ig
.Emit (OpCodes
.Br_S
, end_label
);
531 ig
.MarkLabel (is_null_label
);
532 LiftedNull
.Create (type
, loc
).Emit (ec
);
534 ig
.MarkLabel (end_label
);
537 Expression
LiftExpression (EmitContext ec
, Expression expr
)
539 TypeExpr lifted_type
= new NullableType (expr
.Type
, expr
.Location
);
540 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
541 if (lifted_type
== null)
544 expr
.Type
= lifted_type
.Type
;
548 protected override Expression
ResolveEnumOperator (EmitContext ec
, Expression expr
)
550 expr
= base.ResolveEnumOperator (ec
, expr
);
554 Expr
= LiftExpression (ec
, Expr
);
555 return LiftExpression (ec
, expr
);
558 protected override Expression
ResolveUserOperator (EmitContext ec
, Expression expr
)
560 expr
= base.ResolveUserOperator (ec
, expr
);
565 // When a user operator is of non-nullable type
567 if (Expr
is Unwrap
) {
568 user_operator
= LiftExpression (ec
, expr
);
569 return user_operator
;
576 public class LiftedBinaryOperator
: Binary
578 Unwrap left_unwrap
, right_unwrap
;
579 bool left_null_lifted
, right_null_lifted
;
580 Expression left_orig
, right_orig
;
581 Expression user_operator
;
582 ConstructorInfo wrap_ctor
;
584 public LiftedBinaryOperator (Binary
.Operator op
, Expression left
, Expression right
,
586 : base (op
, left
, right
)
591 public override Expression
CreateExpressionTree (EmitContext ec
)
593 if (user_operator
!= null)
594 return user_operator
.CreateExpressionTree (ec
);
596 return base.CreateExpressionTree (ec
);
600 // CSC 2 has this behavior, it allows structs to be compared
601 // with the null literal *outside* of a generics context and
602 // inlines that as true or false.
604 Expression
CreateNullConstant (Expression expr
)
606 // FIXME: Handle side effect constants
607 Constant c
= new BoolConstant (Oper
== Operator
.Inequality
, loc
);
609 if ((Oper
& Operator
.EqualityMask
) != 0) {
610 Report
.Warning (472, 2, loc
, "The result of comparing value type `{0}' with null is `{1}'",
611 expr
.GetSignatureForError (), c
.AsString ());
613 Report
.Warning (464, 2, loc
, "The result of comparing type `{0}' with null is always `{1}'",
614 expr
.GetSignatureForError (), c
.AsString ());
617 return ReducedExpression
.Create (c
, this);
620 public override Expression
DoResolve (EmitContext ec
)
622 if (eclass
!= ExprClass
.Invalid
)
625 if ((Oper
& Operator
.LogicalMask
) != 0) {
626 Error_OperatorCannotBeApplied (left
, right
);
631 if (TypeManager
.IsNullableType (left
.Type
)) {
632 left
= left_unwrap
= Unwrap
.Create (left
, ec
);
638 if (TypeManager
.IsNullableType (right
.Type
)) {
639 right
= right_unwrap
= Unwrap
.Create (right
, ec
);
645 // Some details are in 6.4.2, 7.2.7
646 // Arguments can be lifted for equal operators when the return type is bool and both
647 // arguments are of same type
649 if (left
is NullLiteral
) {
651 left_null_lifted
= true;
652 type
= TypeManager
.bool_type
;
655 if (right
is NullLiteral
) {
657 right_null_lifted
= true;
658 type
= TypeManager
.bool_type
;
661 eclass
= ExprClass
.Value
;
662 return DoResolveCore (ec
, left_orig
, right_orig
);
665 void EmitBitwiseBoolean (EmitContext ec
)
667 ILGenerator ig
= ec
.ig
;
669 Label load_left
= ig
.DefineLabel ();
670 Label load_right
= ig
.DefineLabel ();
671 Label end_label
= ig
.DefineLabel ();
673 left_unwrap
.EmitGetValueOrDefault (ec
);
674 ig
.Emit (OpCodes
.Brtrue_S
, load_right
);
676 right_unwrap
.EmitGetValueOrDefault (ec
);
677 ig
.Emit (OpCodes
.Brtrue_S
, load_left
);
679 left_unwrap
.EmitCheck (ec
);
680 ig
.Emit (OpCodes
.Brfalse_S
, load_right
);
683 ig
.MarkLabel (load_left
);
685 if (Oper
== Operator
.BitwiseAnd
) {
686 left_unwrap
.Load (ec
);
688 right_unwrap
.Load (ec
);
689 right_unwrap
= left_unwrap
;
691 ig
.Emit (OpCodes
.Br_S
, end_label
);
694 ig
.MarkLabel (load_right
);
695 right_unwrap
.Load (ec
);
697 ig
.MarkLabel (end_label
);
701 // Emits optimized equality or inequality operator when possible
703 bool EmitEquality (EmitContext ec
)
705 ILGenerator ig
= ec
.ig
;
708 // Either left or right is null
710 if (left_unwrap
!= null && (right_null_lifted
|| right
.IsNull
)) {
711 left_unwrap
.EmitCheck (ec
);
712 if (Oper
== Binary
.Operator
.Equality
) {
713 ig
.Emit (OpCodes
.Ldc_I4_0
);
714 ig
.Emit (OpCodes
.Ceq
);
719 if (right_unwrap
!= null && (left_null_lifted
|| left
.IsNull
)) {
720 right_unwrap
.EmitCheck (ec
);
721 if (Oper
== Binary
.Operator
.Equality
) {
722 ig
.Emit (OpCodes
.Ldc_I4_0
);
723 ig
.Emit (OpCodes
.Ceq
);
728 if (user_operator
!= null)
731 Label dissimilar_label
= ig
.DefineLabel ();
732 Label end_label
= ig
.DefineLabel ();
734 if (left_unwrap
!= null)
735 left_unwrap
.EmitGetValueOrDefault (ec
);
739 if (right_unwrap
!= null)
740 right_unwrap
.EmitGetValueOrDefault (ec
);
744 ig
.Emit (OpCodes
.Bne_Un_S
, dissimilar_label
);
746 if (left_unwrap
!= null)
747 left_unwrap
.EmitCheck (ec
);
748 if (right_unwrap
!= null)
749 right_unwrap
.EmitCheck (ec
);
751 if (left_unwrap
!= null && right_unwrap
!= null) {
752 if (Oper
== Operator
.Inequality
)
753 ig
.Emit (OpCodes
.Xor
);
755 ig
.Emit (OpCodes
.Ceq
);
757 if (Oper
== Operator
.Inequality
) {
758 ig
.Emit (OpCodes
.Ldc_I4_0
);
759 ig
.Emit (OpCodes
.Ceq
);
763 ig
.Emit (OpCodes
.Br_S
, end_label
);
765 ig
.MarkLabel (dissimilar_label
);
766 if (Oper
== Operator
.Inequality
)
767 ig
.Emit (OpCodes
.Ldc_I4_1
);
769 ig
.Emit (OpCodes
.Ldc_I4_0
);
771 ig
.MarkLabel (end_label
);
775 public override void EmitBranchable (EmitContext ec
, Label target
, bool onTrue
)
778 ec
.ig
.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) {
795 if (EmitEquality (ec
))
799 ILGenerator ig
= ec
.ig
;
801 Label is_null_label
= ig
.DefineLabel ();
802 Label end_label
= ig
.DefineLabel ();
804 if (left_unwrap
!= null) {
805 left_unwrap
.EmitCheck (ec
);
806 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
810 // Don't emit HasValue check when left and right expressions are same
812 if (right_unwrap
!= null && !left
.Equals (right
)) {
813 right_unwrap
.EmitCheck (ec
);
814 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
817 EmitOperator (ec
, left
.Type
);
819 if (wrap_ctor
!= null)
820 ig
.Emit (OpCodes
.Newobj
, wrap_ctor
);
822 ig
.Emit (OpCodes
.Br_S
, end_label
);
823 ig
.MarkLabel (is_null_label
);
825 if ((Oper
& Operator
.ComparisonMask
) != 0) {
827 // Emit true when equality operator both operands are null
828 // or inequality operator operands has only one null
830 if ((Oper
== Operator
.Equality
&& left_unwrap
!= null && right_unwrap
!= null) ||
831 (Oper
== Operator
.Inequality
&& left_unwrap
!= right_unwrap
))
832 ig
.Emit (OpCodes
.Ldc_I4_1
);
834 ig
.Emit (OpCodes
.Ldc_I4_0
);
836 LiftedNull
.Create (type
, loc
).Emit (ec
);
839 ig
.MarkLabel (end_label
);
842 protected override void EmitOperator (EmitContext ec
, Type l
)
844 if (user_operator
!= null) {
845 user_operator
.Emit (ec
);
849 if (TypeManager
.IsNullableType (l
))
850 l
= TypeManager
.GetTypeArguments (l
) [0];
852 base.EmitOperator (ec
, l
);
855 bool IsBitwiseBoolean
{
857 return (Oper
& Operator
.BitwiseMask
) != 0 && left_unwrap
!= null && right_unwrap
!= null &&
858 left_unwrap
.Type
== TypeManager
.bool_type
&& right_unwrap
.Type
== TypeManager
.bool_type
;
862 Expression
LiftResult (EmitContext ec
, Expression res_expr
)
864 TypeExpr lifted_type
;
867 // Avoid double conversion
869 if (left_unwrap
== null || left_null_lifted
|| !TypeManager
.IsEqual (left_unwrap
.Type
, left
.Type
) || (left_unwrap
!= null && right_null_lifted
)) {
870 lifted_type
= new NullableType (left
.Type
, loc
);
871 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
872 if (lifted_type
== null)
875 if (left
is UserCast
|| left
is TypeCast
)
876 left
.Type
= lifted_type
.Type
;
878 left
= EmptyCast
.Create (left
, lifted_type
.Type
);
881 if (right_unwrap
== null || right_null_lifted
|| !TypeManager
.IsEqual (right_unwrap
.Type
, right
.Type
) || (right_unwrap
!= null && left_null_lifted
)) {
882 lifted_type
= new NullableType (right
.Type
, loc
);
883 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
884 if (lifted_type
== null)
887 if (right
is UserCast
|| right
is TypeCast
)
888 right
.Type
= lifted_type
.Type
;
890 right
= EmptyCast
.Create (right
, lifted_type
.Type
);
893 if ((Oper
& Operator
.ComparisonMask
) == 0) {
894 lifted_type
= new NullableType (res_expr
.Type
, loc
);
895 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
896 if (lifted_type
== null)
899 wrap_ctor
= new NullableInfo (lifted_type
.Type
).Constructor
;
900 type
= res_expr
.Type
= lifted_type
.Type
;
903 if (left_null_lifted
) {
904 left
= LiftedNull
.Create (right
.Type
, left
.Location
);
906 if ((Oper
& (Operator
.ArithmeticMask
| Operator
.ShiftMask
)) != 0)
907 return LiftedNull
.CreateFromExpression (res_expr
);
910 // Value types and null comparison
912 if (right_unwrap
== null || (Oper
& Operator
.RelationalMask
) != 0)
913 return CreateNullConstant (right_orig
).Resolve (ec
);
916 if (right_null_lifted
) {
917 right
= LiftedNull
.Create (left
.Type
, right
.Location
);
919 if ((Oper
& (Operator
.ArithmeticMask
| Operator
.ShiftMask
)) != 0)
920 return LiftedNull
.CreateFromExpression (res_expr
);
923 // Value types and null comparison
925 if (left_unwrap
== null || (Oper
& Operator
.RelationalMask
) != 0)
926 return CreateNullConstant (left_orig
).Resolve (ec
);
932 protected override Expression
ResolveOperatorPredefined (EmitContext ec
, Binary
.PredefinedOperator
[] operators
, bool primitives_only
, Type enum_type
)
934 Expression e
= base.ResolveOperatorPredefined (ec
, operators
, primitives_only
, enum_type
);
936 if (e
== this || enum_type
!= null)
937 return LiftResult (ec
, e
);
940 // 7.9.9 Equality operators and null
942 // The == and != operators permit one operand to be a value of a nullable type and
943 // the other to be the null literal, even if no predefined or user-defined operator
944 // (in unlifted or lifted form) exists for the operation.
946 if (e
== null && (Oper
& Operator
.EqualityMask
) != 0) {
947 if ((left_null_lifted
&& right_unwrap
!= null) || (right_null_lifted
&& left_unwrap
!= null))
948 return LiftResult (ec
, this);
954 protected override Expression
ResolveUserOperator (EmitContext ec
, Type l
, Type r
)
956 Expression expr
= base.ResolveUserOperator (ec
, l
, r
);
960 expr
= LiftResult (ec
, expr
);
961 if (expr
is Constant
)
965 user_operator
= expr
;
970 public class NullCoalescingOperator
: Expression
972 Expression left
, right
;
975 public NullCoalescingOperator (Expression left
, Expression right
, Location loc
)
982 public override Expression
CreateExpressionTree (EmitContext ec
)
984 if (left
is NullLiteral
)
985 Report
.Error (845, loc
, "An expression tree cannot contain a coalescing operator with null left side");
987 UserCast uc
= left
as UserCast
;
988 Expression conversion
= null;
992 ArrayList c_args
= new ArrayList (2);
993 c_args
.Add (new Argument (uc
.CreateExpressionTree (ec
)));
994 c_args
.Add (new Argument (left
.CreateExpressionTree (ec
)));
995 conversion
= CreateExpressionFactoryCall ("Lambda", c_args
);
998 ArrayList args
= new ArrayList (3);
999 args
.Add (new Argument (left
.CreateExpressionTree (ec
)));
1000 args
.Add (new Argument (right
.CreateExpressionTree (ec
)));
1001 if (conversion
!= null)
1002 args
.Add (new Argument (conversion
));
1004 return CreateExpressionFactoryCall ("Coalesce", args
);
1007 Expression
ConvertExpression (EmitContext ec
)
1009 // TODO: ImplicitConversionExists should take care of this
1010 if (left
.eclass
== ExprClass
.MethodGroup
)
1013 Type ltype
= left
.Type
;
1016 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
1017 // the result is underlying type of left
1019 if (TypeManager
.IsNullableType (ltype
)) {
1020 unwrap
= Unwrap
.Create (left
, ec
);
1024 if (Convert
.ImplicitConversionExists (ec
, right
, unwrap
.Type
)) {
1027 right
= Convert
.ImplicitConversion (ec
, right
, type
, loc
);
1030 } else if (TypeManager
.IsReferenceType (ltype
)) {
1031 if (Convert
.ImplicitConversionExists (ec
, right
, ltype
)) {
1033 // Reduce (constant ?? expr) to constant
1035 Constant lc
= left
as Constant
;
1036 if (lc
!= null && !lc
.IsDefaultValue
)
1037 return new SideEffectConstant (lc
, right
, loc
).Resolve (ec
);
1040 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1042 if (right
.IsNull
|| lc
!= null)
1043 return ReducedExpression
.Create (lc
!= null ? right
: left
, this).Resolve (ec
);
1045 right
= Convert
.ImplicitConversion (ec
, right
, ltype
, loc
);
1053 Type rtype
= right
.Type
;
1054 if (!Convert
.ImplicitConversionExists (ec
, unwrap
!= null ? unwrap
: left
, rtype
) || right
.eclass
== ExprClass
.MethodGroup
)
1058 // Reduce (null ?? right) to right
1061 return ReducedExpression
.Create (right
, this).Resolve (ec
);
1063 left
= Convert
.ImplicitConversion (ec
, unwrap
!= null ? unwrap
: left
, rtype
, loc
);
1068 public override Expression
DoResolve (EmitContext ec
)
1070 if (eclass
!= ExprClass
.Invalid
)
1073 left
= left
.Resolve (ec
);
1074 right
= right
.Resolve (ec
);
1076 if (left
== null || right
== null)
1079 eclass
= ExprClass
.Value
;
1081 Expression e
= ConvertExpression (ec
);
1083 Binary
.Error_OperatorCannotBeApplied (left
, right
, "??", loc
);
1090 public override void Emit (EmitContext ec
)
1092 ILGenerator ig
= ec
.ig
;
1094 Label end_label
= ig
.DefineLabel ();
1096 if (unwrap
!= null) {
1097 Label is_null_label
= ig
.DefineLabel ();
1099 unwrap
.EmitCheck (ec
);
1100 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
1103 ig
.Emit (OpCodes
.Br
, end_label
);
1105 ig
.MarkLabel (is_null_label
);
1108 ig
.MarkLabel (end_label
);
1114 ig
.Emit (OpCodes
.Dup
);
1115 ig
.Emit (OpCodes
.Brtrue
, end_label
);
1117 ig
.Emit (OpCodes
.Pop
);
1120 ig
.MarkLabel (end_label
);
1123 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1125 left
.MutateHoistedGenericType (storey
);
1126 right
.MutateHoistedGenericType (storey
);
1127 type
= storey
.MutateType (type
);
1130 protected override void CloneTo (CloneContext clonectx
, Expression t
)
1132 NullCoalescingOperator target
= (NullCoalescingOperator
) t
;
1134 target
.left
= left
.Clone (clonectx
);
1135 target
.right
= right
.Clone (clonectx
);
1139 public class LiftedUnaryMutator
: ExpressionStatement
1141 public readonly UnaryMutator
.Mode Mode
;
1143 UnaryMutator underlying
;
1146 public LiftedUnaryMutator (UnaryMutator
.Mode mode
, Expression expr
, Location loc
)
1152 eclass
= ExprClass
.Value
;
1155 public override Expression
CreateExpressionTree (EmitContext ec
)
1157 return new SimpleAssign (this, this).CreateExpressionTree (ec
);
1160 public override Expression
DoResolve (EmitContext ec
)
1162 expr
= expr
.Resolve (ec
);
1166 unwrap
= Unwrap
.Create (expr
, ec
);
1170 underlying
= (UnaryMutator
) new UnaryMutator (Mode
, unwrap
).Resolve (ec
);
1171 if (underlying
== null)
1178 void DoEmit (EmitContext ec
, bool is_expr
)
1180 ILGenerator ig
= ec
.ig
;
1181 Label is_null_label
= ig
.DefineLabel ();
1182 Label end_label
= ig
.DefineLabel ();
1184 unwrap
.EmitCheck (ec
);
1185 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
1188 underlying
.Emit (ec
);
1189 ig
.Emit (OpCodes
.Br_S
, end_label
);
1191 underlying
.EmitStatement (ec
);
1194 ig
.MarkLabel (is_null_label
);
1196 LiftedNull
.Create (type
, loc
).Emit (ec
);
1198 ig
.MarkLabel (end_label
);
1201 public override void Emit (EmitContext ec
)
1206 public override void EmitStatement (EmitContext ec
)