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
25 public NullableType (TypeExpr 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 (IMemberContext ec
)
39 if (TypeManager
.generic_nullable_type
== null) {
40 TypeManager
.generic_nullable_type
= TypeManager
.CoreLookupType (ec
.Compiler
,
41 "System", "Nullable`1", Kind
.Struct
, true);
44 TypeArguments args
= new TypeArguments (underlying
);
45 GenericTypeExpr ctype
= new GenericTypeExpr (TypeManager
.generic_nullable_type
, args
, loc
);
46 return ctype
.ResolveAsTypeTerminal (ec
, false);
49 public override TypeExpr
ResolveAsTypeTerminal (IMemberContext ec
, bool silent
)
51 return ResolveAsBaseTerminal (ec
, silent
);
55 public sealed class NullableInfo
57 public readonly Type Type
;
58 public readonly Type UnderlyingType
;
59 public MethodInfo HasValue
;
60 public MethodInfo Value
;
61 public MethodInfo GetValueOrDefault
;
62 public ConstructorInfo Constructor
;
64 public NullableInfo (Type type
)
67 UnderlyingType
= TypeManager
.TypeToCoreType (TypeManager
.GetTypeArguments (type
) [0]);
69 PropertyInfo has_value_pi
= TypeManager
.GetPredefinedProperty (type
, "HasValue", Location
.Null
, Type
.EmptyTypes
);
70 PropertyInfo value_pi
= TypeManager
.GetPredefinedProperty (type
, "Value", Location
.Null
, Type
.EmptyTypes
);
71 GetValueOrDefault
= TypeManager
.GetPredefinedMethod (type
, "GetValueOrDefault", Location
.Null
, Type
.EmptyTypes
);
73 HasValue
= has_value_pi
.GetGetMethod (false);
74 Value
= value_pi
.GetGetMethod (false);
76 // When compiling corlib
77 if (TypeManager
.IsBeingCompiled (type
)) {
78 TypeContainer tc
= TypeManager
.LookupGenericTypeContainer (type
);
80 // TODO: check for correct overload
81 Constructor c
= ((Constructor
) tc
.InstanceConstructors
[0]);
83 Constructor
= TypeBuilder
.GetConstructor (type
, c
.ConstructorBuilder
);
88 if (TypeManager
.IsBeingCompiled (UnderlyingType
)) {
89 ConstructorInfo cinfo
= TypeManager
.DropGenericTypeArguments (type
).GetConstructors ()[0];
90 Constructor
= TypeBuilder
.GetConstructor (type
, cinfo
);
95 Constructor
= type
.GetConstructor (new Type
[] { UnderlyingType }
);
99 public class Unwrap
: Expression
, IMemoryLocation
, IAssignMethod
105 readonly bool useDefaultValue
;
107 Unwrap (Expression expr
, bool useDefaultValue
)
110 this.loc
= expr
.Location
;
111 this.useDefaultValue
= useDefaultValue
;
113 info
= new NullableInfo (expr
.Type
);
114 type
= info
.UnderlyingType
;
115 eclass
= expr
.eclass
;
118 public static Expression
Create (Expression expr
)
121 // Avoid unwraping and wraping of same type
123 Wrap wrap
= expr
as Wrap
;
127 return Create (expr
, false);
130 public static Unwrap
Create (Expression expr
, bool useDefaultValue
)
132 return new Unwrap (expr
, useDefaultValue
);
135 public override Expression
CreateExpressionTree (ResolveContext ec
)
137 return expr
.CreateExpressionTree (ec
);
140 public override Expression
DoResolve (ResolveContext ec
)
145 public override Expression
DoResolveLValue (ResolveContext ec
, Expression right_side
)
147 return DoResolve (ec
);
150 public override void Emit (EmitContext ec
)
154 Invocation
.EmitCall (ec
, false, this, info
.GetValueOrDefault
, null, loc
);
156 Invocation
.EmitCall (ec
, false, this, info
.Value
, null, loc
);
159 public void EmitCheck (EmitContext ec
)
162 Invocation
.EmitCall (ec
, false, this, info
.HasValue
, null, loc
);
165 public override bool Equals (object obj
)
167 Unwrap uw
= obj
as Unwrap
;
168 return uw
!= null && expr
.Equals (uw
.expr
);
171 public Expression Original
{
177 public override int GetHashCode ()
179 return expr
.GetHashCode ();
182 public override bool IsNull
{
188 void Store (EmitContext ec
)
190 if (expr
is VariableReference
)
197 LocalVariable
.Store (ec
);
200 public void Load (EmitContext ec
)
202 if (expr
is VariableReference
)
205 LocalVariable
.Emit (ec
);
209 public override System
.Linq
.Expressions
.Expression
MakeExpression (BuilderContext ctx
)
211 return expr
.MakeExpression (ctx
);
215 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
217 type
= storey
.MutateType (type
);
218 info
.Constructor
= storey
.MutateConstructor (info
.Constructor
);
219 info
.HasValue
= storey
.MutateGenericMethod (info
.HasValue
);
220 info
.GetValueOrDefault
= storey
.MutateGenericMethod (info
.GetValueOrDefault
);
221 info
.Value
= storey
.MutateGenericMethod (info
.Value
);
224 public void AddressOf (EmitContext ec
, AddressOp mode
)
226 IMemoryLocation ml
= expr
as VariableReference
;
228 ml
.AddressOf (ec
, mode
);
230 LocalVariable
.AddressOf (ec
, mode
);
234 // Keeps result of non-variable expression
236 LocalTemporary LocalVariable
{
239 temp
= new LocalTemporary (info
.Type
);
244 public void Emit (EmitContext ec
, bool leave_copy
)
252 public void EmitAssign (EmitContext ec
, Expression source
,
253 bool leave_copy
, bool prepare_for_load
)
255 InternalWrap wrap
= new InternalWrap (source
, info
, loc
);
256 ((IAssignMethod
) expr
).EmitAssign (ec
, wrap
, leave_copy
, false);
259 protected class InternalWrap
: Expression
261 public Expression expr
;
262 public NullableInfo info
;
264 public InternalWrap (Expression expr
, NullableInfo info
, Location loc
)
271 eclass
= ExprClass
.Value
;
274 public override Expression
CreateExpressionTree (ResolveContext ec
)
276 throw new NotSupportedException ("ET");
279 public override Expression
DoResolve (ResolveContext ec
)
284 public override void Emit (EmitContext ec
)
287 ec
.ig
.Emit (OpCodes
.Newobj
, info
.Constructor
);
292 public class Wrap
: TypeCast
294 readonly NullableInfo info
;
296 protected Wrap (Expression expr
, Type type
)
299 info
= new NullableInfo (type
);
300 eclass
= ExprClass
.Value
;
303 public Expression Child
{
304 get { return child; }
307 public override Expression
CreateExpressionTree (ResolveContext ec
)
309 TypeCast child_cast
= child
as TypeCast
;
310 if (child_cast
!= null) {
312 return child_cast
.CreateExpressionTree (ec
);
315 return base.CreateExpressionTree (ec
);
318 public static Expression
Create (Expression expr
, Type type
)
321 // Avoid unwraping and wraping of the same type
323 Unwrap unwrap
= expr
as Unwrap
;
324 if (unwrap
!= null && TypeManager
.IsEqual (expr
.Type
, TypeManager
.TypeToCoreType (TypeManager
.GetTypeArguments (type
) [0])))
325 return unwrap
.Original
;
327 return new Wrap (expr
, type
);
330 public override void Emit (EmitContext ec
)
333 ec
.ig
.Emit (OpCodes
.Newobj
, info
.Constructor
);
338 // Represents null literal lifted to nullable type
340 public class LiftedNull
: NullConstant
, IMemoryLocation
342 private LiftedNull (Type nullable_type
, Location loc
)
343 : base (nullable_type
, loc
)
345 eclass
= ExprClass
.Value
;
348 public static Constant
Create (Type nullable
, Location loc
)
350 return new LiftedNull (nullable
, loc
);
353 public static Expression
CreateFromExpression (ResolveContext ec
, Expression e
)
355 ec
.Report
.Warning (458, 2, e
.Location
, "The result of the expression is always `null' of type `{0}'",
356 TypeManager
.CSharpName (e
.Type
));
358 return ReducedExpression
.Create (Create (e
.Type
, e
.Location
), e
);
361 public override void Emit (EmitContext ec
)
363 // TODO: generate less temporary variables
364 LocalTemporary value_target
= new LocalTemporary (type
);
366 value_target
.AddressOf (ec
, AddressOp
.Store
);
367 ec
.ig
.Emit (OpCodes
.Initobj
, type
);
368 value_target
.Emit (ec
);
371 public void AddressOf (EmitContext ec
, AddressOp Mode
)
373 LocalTemporary value_target
= new LocalTemporary (type
);
375 value_target
.AddressOf (ec
, AddressOp
.Store
);
376 ec
.ig
.Emit (OpCodes
.Initobj
, type
);
377 ((IMemoryLocation
) value_target
).AddressOf (ec
, Mode
);
382 // Generic lifting expression, supports all S/S? -> T/T? cases
384 public class Lifted
: Expression
, IMemoryLocation
386 Expression expr
, null_value
;
389 public Lifted (Expression expr
, Unwrap unwrap
, Type type
)
392 this.unwrap
= unwrap
;
393 this.loc
= expr
.Location
;
397 public Lifted (Expression expr
, Expression unwrap
, Type type
)
398 : this (expr
, unwrap
as Unwrap
, type
)
402 public override Expression
CreateExpressionTree (ResolveContext ec
)
404 return expr
.CreateExpressionTree (ec
);
407 public override Expression
DoResolve (ResolveContext ec
)
410 // It's null when lifting non-nullable type
412 if (unwrap
== null) {
413 // S -> T? is wrap only
414 if (TypeManager
.IsNullableType (type
))
415 return Wrap
.Create (expr
, type
);
417 // S -> T can be simplified
421 // Wrap target for T?
422 if (TypeManager
.IsNullableType (type
)) {
423 expr
= Wrap
.Create (expr
, type
);
427 null_value
= LiftedNull
.Create (type
, loc
);
429 null_value
= new NullConstant (type
, loc
);
432 eclass
= ExprClass
.Value
;
436 public override void Emit (EmitContext ec
)
438 ILGenerator ig
= ec
.ig
;
439 Label is_null_label
= ig
.DefineLabel ();
440 Label end_label
= ig
.DefineLabel ();
442 unwrap
.EmitCheck (ec
);
443 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
447 ig
.Emit (OpCodes
.Br
, end_label
);
448 ig
.MarkLabel (is_null_label
);
450 null_value
.Emit (ec
);
451 ig
.MarkLabel (end_label
);
454 public void AddressOf (EmitContext ec
, AddressOp mode
)
456 unwrap
.AddressOf (ec
, mode
);
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 (ResolveContext 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 (ResolveContext ec
)
488 if (eclass
!= ExprClass
.Invalid
)
491 unwrap
= Unwrap
.Create (Expr
, false);
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 (ResolveContext 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 (ResolveContext ec
, Expression expr
)
550 expr
= base.ResolveEnumOperator (ec
, expr
);
554 Expr
= LiftExpression (ec
, Expr
);
555 return LiftExpression (ec
, expr
);
558 protected override Expression
ResolveUserOperator (ResolveContext 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 (ResolveContext 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 (ResolveContext ec
, Expression expr
)
606 // FIXME: Handle side effect constants
607 Constant c
= new BoolConstant (Oper
== Operator
.Inequality
, loc
);
609 if ((Oper
& Operator
.EqualityMask
) != 0) {
610 ec
.Report
.Warning (472, 2, loc
, "The result of comparing value type `{0}' with null is `{1}'",
611 TypeManager
.CSharpName (expr
.Type
), c
.AsString ());
613 ec
.Report
.Warning (464, 2, loc
, "The result of comparing type `{0}' with null is always `{1}'",
614 TypeManager
.CSharpName (expr
.Type
), c
.AsString ());
617 return ReducedExpression
.Create (c
, this);
620 public override Expression
DoResolve (ResolveContext ec
)
622 if (eclass
!= ExprClass
.Invalid
)
625 if ((Oper
& Operator
.LogicalMask
) != 0) {
626 Error_OperatorCannotBeApplied (ec
, left
, right
);
630 bool use_default_call
= (Oper
& (Operator
.BitwiseMask
| Operator
.EqualityMask
)) != 0;
632 if (TypeManager
.IsNullableType (left
.Type
)) {
633 left
= left_unwrap
= Unwrap
.Create (left
, use_default_call
);
639 if (TypeManager
.IsNullableType (right
.Type
)) {
640 right
= right_unwrap
= Unwrap
.Create (right
, use_default_call
);
646 // Some details are in 6.4.2, 7.2.7
647 // Arguments can be lifted for equal operators when the return type is bool and both
648 // arguments are of same type
650 if (left_orig
.IsNull
) {
652 left_null_lifted
= true;
653 type
= TypeManager
.bool_type
;
656 if (right_orig
.IsNull
) {
658 right_null_lifted
= true;
659 type
= TypeManager
.bool_type
;
662 eclass
= ExprClass
.Value
;
663 return DoResolveCore (ec
, left_orig
, right_orig
);
666 void EmitBitwiseBoolean (EmitContext ec
)
668 ILGenerator ig
= ec
.ig
;
670 Label load_left
= ig
.DefineLabel ();
671 Label load_right
= ig
.DefineLabel ();
672 Label end_label
= ig
.DefineLabel ();
674 left_unwrap
.Emit (ec
);
675 ig
.Emit (OpCodes
.Brtrue_S
, load_right
);
677 right_unwrap
.Emit (ec
);
678 ig
.Emit (OpCodes
.Brtrue_S
, load_left
);
680 left_unwrap
.EmitCheck (ec
);
681 ig
.Emit (OpCodes
.Brfalse_S
, load_right
);
684 ig
.MarkLabel (load_left
);
686 if (Oper
== Operator
.BitwiseAnd
) {
687 left_unwrap
.Load (ec
);
689 right_unwrap
.Load (ec
);
690 right_unwrap
= left_unwrap
;
692 ig
.Emit (OpCodes
.Br_S
, end_label
);
695 ig
.MarkLabel (load_right
);
696 right_unwrap
.Load (ec
);
698 ig
.MarkLabel (end_label
);
702 // Emits optimized equality or inequality operator when possible
704 void EmitEquality (EmitContext ec
)
706 ILGenerator ig
= ec
.ig
;
709 // Either left or right is null
711 if (left_unwrap
!= null && (right_null_lifted
|| right
.IsNull
)) {
712 left_unwrap
.EmitCheck (ec
);
713 if (Oper
== Binary
.Operator
.Equality
) {
714 ig
.Emit (OpCodes
.Ldc_I4_0
);
715 ig
.Emit (OpCodes
.Ceq
);
720 if (right_unwrap
!= null && (left_null_lifted
|| left
.IsNull
)) {
721 right_unwrap
.EmitCheck (ec
);
722 if (Oper
== Binary
.Operator
.Equality
) {
723 ig
.Emit (OpCodes
.Ldc_I4_0
);
724 ig
.Emit (OpCodes
.Ceq
);
729 Label dissimilar_label
= ig
.DefineLabel ();
730 Label end_label
= ig
.DefineLabel ();
732 if (user_operator
!= null) {
733 user_operator
.Emit (ec
);
734 ig
.Emit (Oper
== Operator
.Equality
? OpCodes
.Brfalse_S
: OpCodes
.Brtrue_S
, dissimilar_label
);
739 ig
.Emit (OpCodes
.Bne_Un_S
, dissimilar_label
);
742 if (left_unwrap
!= null)
743 left_unwrap
.EmitCheck (ec
);
745 if (right_unwrap
!= null)
746 right_unwrap
.EmitCheck (ec
);
748 if (left_unwrap
!= null && right_unwrap
!= null) {
749 if (Oper
== Operator
.Inequality
)
750 ig
.Emit (OpCodes
.Xor
);
752 ig
.Emit (OpCodes
.Ceq
);
754 if (Oper
== Operator
.Inequality
) {
755 ig
.Emit (OpCodes
.Ldc_I4_0
);
756 ig
.Emit (OpCodes
.Ceq
);
760 ig
.Emit (OpCodes
.Br_S
, end_label
);
762 ig
.MarkLabel (dissimilar_label
);
763 if (Oper
== Operator
.Inequality
)
764 ig
.Emit (OpCodes
.Ldc_I4_1
);
766 ig
.Emit (OpCodes
.Ldc_I4_0
);
768 ig
.MarkLabel (end_label
);
771 public override void EmitBranchable (EmitContext ec
, Label target
, bool onTrue
)
774 ec
.ig
.Emit (onTrue
? OpCodes
.Brtrue
: OpCodes
.Brfalse
, target
);
777 public override void Emit (EmitContext ec
)
780 // Optimize same expression operation
782 if (right_unwrap
!= null && right
.Equals (left
))
783 right_unwrap
= left_unwrap
;
785 if (user_operator
== null && IsBitwiseBoolean
) {
786 EmitBitwiseBoolean (ec
);
790 if ((Oper
& Operator
.EqualityMask
) != 0) {
795 ILGenerator ig
= ec
.ig
;
797 Label is_null_label
= ig
.DefineLabel ();
798 Label end_label
= ig
.DefineLabel ();
800 if (left_unwrap
!= null) {
801 left_unwrap
.EmitCheck (ec
);
802 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
806 // Don't emit HasValue check when left and right expressions are same
808 if (right_unwrap
!= null && !left
.Equals (right
)) {
809 right_unwrap
.EmitCheck (ec
);
810 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
813 EmitOperator (ec
, left
.Type
);
815 if (wrap_ctor
!= null)
816 ig
.Emit (OpCodes
.Newobj
, wrap_ctor
);
818 ig
.Emit (OpCodes
.Br_S
, end_label
);
819 ig
.MarkLabel (is_null_label
);
821 if ((Oper
& Operator
.ComparisonMask
) != 0) {
822 ig
.Emit (OpCodes
.Ldc_I4_0
);
824 LiftedNull
.Create (type
, loc
).Emit (ec
);
827 ig
.MarkLabel (end_label
);
830 protected override void EmitOperator (EmitContext ec
, Type l
)
832 if (user_operator
!= null) {
833 user_operator
.Emit (ec
);
837 if (TypeManager
.IsNullableType (l
))
838 l
= TypeManager
.TypeToCoreType (TypeManager
.GetTypeArguments (l
) [0]);
840 base.EmitOperator (ec
, l
);
843 bool IsBitwiseBoolean
{
845 return (Oper
& Operator
.BitwiseMask
) != 0 && left_unwrap
!= null && right_unwrap
!= null &&
846 left_unwrap
.Type
== TypeManager
.bool_type
&& right_unwrap
.Type
== TypeManager
.bool_type
;
850 Expression
LiftResult (ResolveContext ec
, Expression res_expr
)
852 TypeExpr lifted_type
;
855 // Avoid double conversion
857 if (left_unwrap
== null || left_null_lifted
|| !TypeManager
.IsEqual (left_unwrap
.Type
, left
.Type
) || (left_unwrap
!= null && right_null_lifted
)) {
858 lifted_type
= new NullableType (left
.Type
, loc
);
859 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
860 if (lifted_type
== null)
863 if (left
is UserCast
|| left
is TypeCast
)
864 left
.Type
= lifted_type
.Type
;
866 left
= EmptyCast
.Create (left
, lifted_type
.Type
);
869 if (right_unwrap
== null || right_null_lifted
|| !TypeManager
.IsEqual (right_unwrap
.Type
, right
.Type
) || (right_unwrap
!= null && left_null_lifted
)) {
870 lifted_type
= new NullableType (right
.Type
, loc
);
871 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
872 if (lifted_type
== null)
875 if (right
is UserCast
|| right
is TypeCast
)
876 right
.Type
= lifted_type
.Type
;
878 right
= EmptyCast
.Create (right
, lifted_type
.Type
);
881 if ((Oper
& Operator
.ComparisonMask
) == 0) {
882 lifted_type
= new NullableType (res_expr
.Type
, loc
);
883 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
884 if (lifted_type
== null)
887 wrap_ctor
= new NullableInfo (lifted_type
.Type
).Constructor
;
888 type
= res_expr
.Type
= lifted_type
.Type
;
891 if (left_null_lifted
) {
892 left
= LiftedNull
.Create (right
.Type
, left
.Location
);
894 if ((Oper
& (Operator
.ArithmeticMask
| Operator
.ShiftMask
| Operator
.BitwiseMask
)) != 0)
895 return LiftedNull
.CreateFromExpression (ec
, res_expr
);
898 // Value types and null comparison
900 if (right_unwrap
== null || (Oper
& Operator
.RelationalMask
) != 0)
901 return CreateNullConstant (ec
, right_orig
).Resolve (ec
);
904 if (right_null_lifted
) {
905 right
= LiftedNull
.Create (left
.Type
, right
.Location
);
907 if ((Oper
& (Operator
.ArithmeticMask
| Operator
.ShiftMask
| Operator
.BitwiseMask
)) != 0)
908 return LiftedNull
.CreateFromExpression (ec
, res_expr
);
911 // Value types and null comparison
913 if (left_unwrap
== null || (Oper
& Operator
.RelationalMask
) != 0)
914 return CreateNullConstant (ec
, left_orig
).Resolve (ec
);
920 protected override Expression
ResolveOperatorPredefined (ResolveContext ec
, Binary
.PredefinedOperator
[] operators
, bool primitives_only
, Type enum_type
)
922 Expression e
= base.ResolveOperatorPredefined (ec
, operators
, primitives_only
, enum_type
);
924 if (e
== this || enum_type
!= null)
925 return LiftResult (ec
, e
);
928 // 7.9.9 Equality operators and null
930 // The == and != operators permit one operand to be a value of a nullable type and
931 // the other to be the null literal, even if no predefined or user-defined operator
932 // (in unlifted or lifted form) exists for the operation.
934 if (e
== null && (Oper
& Operator
.EqualityMask
) != 0) {
935 if ((left_null_lifted
&& right_unwrap
!= null) || (right_null_lifted
&& left_unwrap
!= null))
936 return LiftResult (ec
, this);
942 protected override Expression
ResolveUserOperator (ResolveContext ec
, Type l
, Type r
)
944 Expression expr
= base.ResolveUserOperator (ec
, l
, r
);
948 expr
= LiftResult (ec
, expr
);
949 if (expr
is Constant
)
953 user_operator
= expr
;
958 public class NullCoalescingOperator
: Expression
960 Expression left
, right
;
963 public NullCoalescingOperator (Expression left
, Expression right
, Location loc
)
970 public override Expression
CreateExpressionTree (ResolveContext ec
)
972 if (left
.Type
== TypeManager
.null_type
)
973 ec
.Report
.Error (845, loc
, "An expression tree cannot contain a coalescing operator with null left side");
975 UserCast uc
= left
as UserCast
;
976 Expression conversion
= null;
980 Arguments c_args
= new Arguments (2);
981 c_args
.Add (new Argument (uc
.CreateExpressionTree (ec
)));
982 c_args
.Add (new Argument (left
.CreateExpressionTree (ec
)));
983 conversion
= CreateExpressionFactoryCall (ec
, "Lambda", c_args
);
986 Arguments args
= new Arguments (3);
987 args
.Add (new Argument (left
.CreateExpressionTree (ec
)));
988 args
.Add (new Argument (right
.CreateExpressionTree (ec
)));
989 if (conversion
!= null)
990 args
.Add (new Argument (conversion
));
992 return CreateExpressionFactoryCall (ec
, "Coalesce", args
);
995 Expression
ConvertExpression (ResolveContext ec
)
997 // TODO: ImplicitConversionExists should take care of this
998 if (left
.eclass
== ExprClass
.MethodGroup
)
1001 Type ltype
= left
.Type
;
1004 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
1005 // the result is underlying type of left
1007 if (TypeManager
.IsNullableType (ltype
)) {
1008 unwrap
= Unwrap
.Create (left
, false);
1012 if (Convert
.ImplicitConversionExists (ec
, right
, unwrap
.Type
)) {
1015 right
= Convert
.ImplicitConversion (ec
, right
, type
, loc
);
1018 } else if (TypeManager
.IsReferenceType (ltype
)) {
1019 if (Convert
.ImplicitConversionExists (ec
, right
, ltype
)) {
1021 // Reduce (constant ?? expr) to constant
1023 Constant lc
= left
as Constant
;
1024 if (lc
!= null && !lc
.IsDefaultValue
)
1025 return new SideEffectConstant (lc
, right
, loc
).Resolve (ec
);
1028 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1030 if (right
.IsNull
|| lc
!= null)
1031 return ReducedExpression
.Create (lc
!= null ? right
: left
, this).Resolve (ec
);
1033 right
= Convert
.ImplicitConversion (ec
, right
, ltype
, loc
);
1041 Type rtype
= right
.Type
;
1042 if (!Convert
.ImplicitConversionExists (ec
, unwrap
!= null ? unwrap
: left
, rtype
) || right
.eclass
== ExprClass
.MethodGroup
)
1046 // Reduce (null ?? right) to right
1049 return ReducedExpression
.Create (right
, this).Resolve (ec
);
1051 left
= Convert
.ImplicitConversion (ec
, unwrap
!= null ? unwrap
: left
, rtype
, loc
);
1056 public override Expression
DoResolve (ResolveContext ec
)
1058 if (eclass
!= ExprClass
.Invalid
)
1061 left
= left
.Resolve (ec
);
1062 right
= right
.Resolve (ec
);
1064 if (left
== null || right
== null)
1067 eclass
= ExprClass
.Value
;
1069 Expression e
= ConvertExpression (ec
);
1071 Binary
.Error_OperatorCannotBeApplied (ec
, left
, right
, "??", loc
);
1078 public override void Emit (EmitContext ec
)
1080 ILGenerator ig
= ec
.ig
;
1082 Label end_label
= ig
.DefineLabel ();
1084 if (unwrap
!= null) {
1085 Label is_null_label
= ig
.DefineLabel ();
1087 unwrap
.EmitCheck (ec
);
1088 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
1091 ig
.Emit (OpCodes
.Br
, end_label
);
1093 ig
.MarkLabel (is_null_label
);
1096 ig
.MarkLabel (end_label
);
1102 ig
.Emit (OpCodes
.Dup
);
1103 ig
.Emit (OpCodes
.Brtrue
, end_label
);
1105 ig
.Emit (OpCodes
.Pop
);
1108 ig
.MarkLabel (end_label
);
1111 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1113 left
.MutateHoistedGenericType (storey
);
1114 right
.MutateHoistedGenericType (storey
);
1115 type
= storey
.MutateType (type
);
1118 protected override void CloneTo (CloneContext clonectx
, Expression t
)
1120 NullCoalescingOperator target
= (NullCoalescingOperator
) t
;
1122 target
.left
= left
.Clone (clonectx
);
1123 target
.right
= right
.Clone (clonectx
);
1127 public class LiftedUnaryMutator
: ExpressionStatement
1129 public readonly UnaryMutator
.Mode Mode
;
1131 UnaryMutator underlying
;
1134 public LiftedUnaryMutator (UnaryMutator
.Mode mode
, Expression expr
, Location loc
)
1140 eclass
= ExprClass
.Value
;
1143 public override Expression
CreateExpressionTree (ResolveContext ec
)
1145 return new SimpleAssign (this, this).CreateExpressionTree (ec
);
1148 public override Expression
DoResolve (ResolveContext ec
)
1150 expr
= expr
.Resolve (ec
);
1154 unwrap
= Unwrap
.Create (expr
, false);
1158 underlying
= (UnaryMutator
) new UnaryMutator (Mode
, unwrap
).Resolve (ec
);
1159 if (underlying
== null)
1166 void DoEmit (EmitContext ec
, bool is_expr
)
1168 ILGenerator ig
= ec
.ig
;
1169 Label is_null_label
= ig
.DefineLabel ();
1170 Label end_label
= ig
.DefineLabel ();
1172 unwrap
.EmitCheck (ec
);
1173 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
1176 underlying
.Emit (ec
);
1177 ig
.Emit (OpCodes
.Br_S
, end_label
);
1179 underlying
.EmitStatement (ec
);
1182 ig
.MarkLabel (is_null_label
);
1184 LiftedNull
.Create (type
, loc
).Emit (ec
);
1186 ig
.MarkLabel (end_label
);
1189 public override void Emit (EmitContext ec
)
1194 public override void EmitStatement (EmitContext ec
)