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 protected 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 protected 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 protected 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 protected override Expression
DoResolve (ResolveContext ec
)
488 unwrap
= Unwrap
.Create (Expr
, false);
492 Expression res
= base.ResolveOperator (ec
, unwrap
);
494 if (user_operator
== null)
497 res
= Expr
= LiftExpression (ec
, Expr
);
503 eclass
= ExprClass
.Value
;
508 public override void Emit (EmitContext ec
)
510 ILGenerator ig
= ec
.ig
;
511 Label is_null_label
= ig
.DefineLabel ();
512 Label end_label
= ig
.DefineLabel ();
514 unwrap
.EmitCheck (ec
);
515 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
517 NullableInfo ni
= new NullableInfo (type
);
519 if (user_operator
!= null) {
520 user_operator
.Emit (ec
);
522 EmitOperator (ec
, ni
.UnderlyingType
);
525 ig
.Emit (OpCodes
.Newobj
, ni
.Constructor
);
526 ig
.Emit (OpCodes
.Br_S
, end_label
);
528 ig
.MarkLabel (is_null_label
);
529 LiftedNull
.Create (type
, loc
).Emit (ec
);
531 ig
.MarkLabel (end_label
);
534 Expression
LiftExpression (ResolveContext ec
, Expression expr
)
536 TypeExpr lifted_type
= new NullableType (expr
.Type
, expr
.Location
);
537 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
538 if (lifted_type
== null)
541 expr
.Type
= lifted_type
.Type
;
545 protected override Expression
ResolveEnumOperator (ResolveContext ec
, Expression expr
)
547 expr
= base.ResolveEnumOperator (ec
, expr
);
551 Expr
= LiftExpression (ec
, Expr
);
552 return LiftExpression (ec
, expr
);
555 protected override Expression
ResolveUserOperator (ResolveContext ec
, Expression expr
)
557 expr
= base.ResolveUserOperator (ec
, expr
);
562 // When a user operator is of non-nullable type
564 if (Expr
is Unwrap
) {
565 user_operator
= LiftExpression (ec
, expr
);
566 return user_operator
;
573 public class LiftedBinaryOperator
: Binary
575 Unwrap left_unwrap
, right_unwrap
;
576 bool left_null_lifted
, right_null_lifted
;
577 Expression left_orig
, right_orig
;
578 Expression user_operator
;
579 ConstructorInfo wrap_ctor
;
581 public LiftedBinaryOperator (Binary
.Operator op
, Expression left
, Expression right
,
583 : base (op
, left
, right
)
588 public override Expression
CreateExpressionTree (ResolveContext ec
)
590 if (user_operator
!= null)
591 return user_operator
.CreateExpressionTree (ec
);
593 return base.CreateExpressionTree (ec
);
597 // CSC 2 has this behavior, it allows structs to be compared
598 // with the null literal *outside* of a generics context and
599 // inlines that as true or false.
601 Expression
CreateNullConstant (ResolveContext ec
, Expression expr
)
603 // FIXME: Handle side effect constants
604 Constant c
= new BoolConstant (Oper
== Operator
.Inequality
, loc
).Resolve (ec
);
606 if ((Oper
& Operator
.EqualityMask
) != 0) {
607 ec
.Report
.Warning (472, 2, loc
, "The result of comparing value type `{0}' with null is `{1}'",
608 TypeManager
.CSharpName (expr
.Type
), c
.AsString ());
610 ec
.Report
.Warning (464, 2, loc
, "The result of comparing type `{0}' with null is always `{1}'",
611 TypeManager
.CSharpName (expr
.Type
), c
.AsString ());
614 return ReducedExpression
.Create (c
, this);
617 protected override Expression
DoResolve (ResolveContext ec
)
619 if ((Oper
& Operator
.LogicalMask
) != 0) {
620 Error_OperatorCannotBeApplied (ec
, left
, right
);
624 bool use_default_call
= (Oper
& (Operator
.BitwiseMask
| Operator
.EqualityMask
)) != 0;
626 if (TypeManager
.IsNullableType (left
.Type
)) {
627 left
= left_unwrap
= Unwrap
.Create (left
, use_default_call
);
633 if (TypeManager
.IsNullableType (right
.Type
)) {
634 right
= right_unwrap
= Unwrap
.Create (right
, use_default_call
);
640 // Some details are in 6.4.2, 7.2.7
641 // Arguments can be lifted for equal operators when the return type is bool and both
642 // arguments are of same type
644 if (left_orig
.IsNull
) {
646 left_null_lifted
= true;
647 type
= TypeManager
.bool_type
;
650 if (right_orig
.IsNull
) {
652 right_null_lifted
= true;
653 type
= TypeManager
.bool_type
;
656 eclass
= ExprClass
.Value
;
657 return DoResolveCore (ec
, left_orig
, right_orig
);
660 void EmitBitwiseBoolean (EmitContext ec
)
662 ILGenerator ig
= ec
.ig
;
664 Label load_left
= ig
.DefineLabel ();
665 Label load_right
= ig
.DefineLabel ();
666 Label end_label
= ig
.DefineLabel ();
668 left_unwrap
.Emit (ec
);
669 ig
.Emit (OpCodes
.Brtrue_S
, load_right
);
671 right_unwrap
.Emit (ec
);
672 ig
.Emit (OpCodes
.Brtrue_S
, load_left
);
674 left_unwrap
.EmitCheck (ec
);
675 ig
.Emit (OpCodes
.Brfalse_S
, load_right
);
678 ig
.MarkLabel (load_left
);
680 if (Oper
== Operator
.BitwiseAnd
) {
681 left_unwrap
.Load (ec
);
683 right_unwrap
.Load (ec
);
684 right_unwrap
= left_unwrap
;
686 ig
.Emit (OpCodes
.Br_S
, end_label
);
689 ig
.MarkLabel (load_right
);
690 right_unwrap
.Load (ec
);
692 ig
.MarkLabel (end_label
);
696 // Emits optimized equality or inequality operator when possible
698 void EmitEquality (EmitContext ec
)
700 ILGenerator ig
= ec
.ig
;
703 // Either left or right is null
705 if (left_unwrap
!= null && (right_null_lifted
|| right
.IsNull
)) {
706 left_unwrap
.EmitCheck (ec
);
707 if (Oper
== Binary
.Operator
.Equality
) {
708 ig
.Emit (OpCodes
.Ldc_I4_0
);
709 ig
.Emit (OpCodes
.Ceq
);
714 if (right_unwrap
!= null && (left_null_lifted
|| left
.IsNull
)) {
715 right_unwrap
.EmitCheck (ec
);
716 if (Oper
== Binary
.Operator
.Equality
) {
717 ig
.Emit (OpCodes
.Ldc_I4_0
);
718 ig
.Emit (OpCodes
.Ceq
);
723 Label dissimilar_label
= ig
.DefineLabel ();
724 Label end_label
= ig
.DefineLabel ();
726 if (user_operator
!= null) {
727 user_operator
.Emit (ec
);
728 ig
.Emit (Oper
== Operator
.Equality
? OpCodes
.Brfalse_S
: OpCodes
.Brtrue_S
, dissimilar_label
);
733 ig
.Emit (OpCodes
.Bne_Un_S
, dissimilar_label
);
736 if (left_unwrap
!= null)
737 left_unwrap
.EmitCheck (ec
);
739 if (right_unwrap
!= null)
740 right_unwrap
.EmitCheck (ec
);
742 if (left_unwrap
!= null && right_unwrap
!= null) {
743 if (Oper
== Operator
.Inequality
)
744 ig
.Emit (OpCodes
.Xor
);
746 ig
.Emit (OpCodes
.Ceq
);
748 if (Oper
== Operator
.Inequality
) {
749 ig
.Emit (OpCodes
.Ldc_I4_0
);
750 ig
.Emit (OpCodes
.Ceq
);
754 ig
.Emit (OpCodes
.Br_S
, end_label
);
756 ig
.MarkLabel (dissimilar_label
);
757 if (Oper
== Operator
.Inequality
)
758 ig
.Emit (OpCodes
.Ldc_I4_1
);
760 ig
.Emit (OpCodes
.Ldc_I4_0
);
762 ig
.MarkLabel (end_label
);
765 public override void EmitBranchable (EmitContext ec
, Label target
, bool onTrue
)
768 ec
.ig
.Emit (onTrue
? OpCodes
.Brtrue
: OpCodes
.Brfalse
, target
);
771 public override void Emit (EmitContext ec
)
774 // Optimize same expression operation
776 if (right_unwrap
!= null && right
.Equals (left
))
777 right_unwrap
= left_unwrap
;
779 if (user_operator
== null && IsBitwiseBoolean
) {
780 EmitBitwiseBoolean (ec
);
784 if ((Oper
& Operator
.EqualityMask
) != 0) {
789 ILGenerator ig
= ec
.ig
;
791 Label is_null_label
= ig
.DefineLabel ();
792 Label end_label
= ig
.DefineLabel ();
794 if (left_unwrap
!= null) {
795 left_unwrap
.EmitCheck (ec
);
796 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
800 // Don't emit HasValue check when left and right expressions are same
802 if (right_unwrap
!= null && !left
.Equals (right
)) {
803 right_unwrap
.EmitCheck (ec
);
804 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
807 EmitOperator (ec
, left
.Type
);
809 if (wrap_ctor
!= null)
810 ig
.Emit (OpCodes
.Newobj
, wrap_ctor
);
812 ig
.Emit (OpCodes
.Br_S
, end_label
);
813 ig
.MarkLabel (is_null_label
);
815 if ((Oper
& Operator
.ComparisonMask
) != 0) {
816 ig
.Emit (OpCodes
.Ldc_I4_0
);
818 LiftedNull
.Create (type
, loc
).Emit (ec
);
821 ig
.MarkLabel (end_label
);
824 protected override void EmitOperator (EmitContext ec
, Type l
)
826 if (user_operator
!= null) {
827 user_operator
.Emit (ec
);
831 if (TypeManager
.IsNullableType (l
))
832 l
= TypeManager
.TypeToCoreType (TypeManager
.GetTypeArguments (l
) [0]);
834 base.EmitOperator (ec
, l
);
837 bool IsBitwiseBoolean
{
839 return (Oper
& Operator
.BitwiseMask
) != 0 && left_unwrap
!= null && right_unwrap
!= null &&
840 left_unwrap
.Type
== TypeManager
.bool_type
&& right_unwrap
.Type
== TypeManager
.bool_type
;
844 Expression
LiftResult (ResolveContext ec
, Expression res_expr
)
846 TypeExpr lifted_type
;
849 // Avoid double conversion
851 if (left_unwrap
== null || left_null_lifted
|| !TypeManager
.IsEqual (left_unwrap
.Type
, left
.Type
) || (left_unwrap
!= null && right_null_lifted
)) {
852 lifted_type
= new NullableType (left
.Type
, loc
);
853 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
854 if (lifted_type
== null)
857 if (left
is UserCast
|| left
is TypeCast
)
858 left
.Type
= lifted_type
.Type
;
860 left
= EmptyCast
.Create (left
, lifted_type
.Type
);
863 if (right_unwrap
== null || right_null_lifted
|| !TypeManager
.IsEqual (right_unwrap
.Type
, right
.Type
) || (right_unwrap
!= null && left_null_lifted
)) {
864 lifted_type
= new NullableType (right
.Type
, loc
);
865 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
866 if (lifted_type
== null)
869 if (right
is UserCast
|| right
is TypeCast
)
870 right
.Type
= lifted_type
.Type
;
872 right
= EmptyCast
.Create (right
, lifted_type
.Type
);
875 if ((Oper
& Operator
.ComparisonMask
) == 0) {
876 lifted_type
= new NullableType (res_expr
.Type
, loc
);
877 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
878 if (lifted_type
== null)
881 wrap_ctor
= new NullableInfo (lifted_type
.Type
).Constructor
;
882 type
= res_expr
.Type
= lifted_type
.Type
;
885 if (left_null_lifted
) {
886 left
= LiftedNull
.Create (right
.Type
, left
.Location
);
888 if ((Oper
& (Operator
.ArithmeticMask
| Operator
.ShiftMask
| Operator
.BitwiseMask
)) != 0)
889 return LiftedNull
.CreateFromExpression (ec
, res_expr
);
892 // Value types and null comparison
894 if (right_unwrap
== null || (Oper
& Operator
.RelationalMask
) != 0)
895 return CreateNullConstant (ec
, right_orig
).Resolve (ec
);
898 if (right_null_lifted
) {
899 right
= LiftedNull
.Create (left
.Type
, right
.Location
);
901 if ((Oper
& (Operator
.ArithmeticMask
| Operator
.ShiftMask
| Operator
.BitwiseMask
)) != 0)
902 return LiftedNull
.CreateFromExpression (ec
, res_expr
);
905 // Value types and null comparison
907 if (left_unwrap
== null || (Oper
& Operator
.RelationalMask
) != 0)
908 return CreateNullConstant (ec
, left_orig
);
914 protected override Expression
ResolveOperatorPredefined (ResolveContext ec
, Binary
.PredefinedOperator
[] operators
, bool primitives_only
, Type enum_type
)
916 Expression e
= base.ResolveOperatorPredefined (ec
, operators
, primitives_only
, enum_type
);
918 if (e
== this || enum_type
!= null)
919 return LiftResult (ec
, e
);
922 // 7.9.9 Equality operators and null
924 // The == and != operators permit one operand to be a value of a nullable type and
925 // the other to be the null literal, even if no predefined or user-defined operator
926 // (in unlifted or lifted form) exists for the operation.
928 if (e
== null && (Oper
& Operator
.EqualityMask
) != 0) {
929 if ((left_null_lifted
&& right_unwrap
!= null) || (right_null_lifted
&& left_unwrap
!= null))
930 return LiftResult (ec
, this);
936 protected override Expression
ResolveUserOperator (ResolveContext ec
, Type l
, Type r
)
938 Expression expr
= base.ResolveUserOperator (ec
, l
, r
);
942 expr
= LiftResult (ec
, expr
);
943 if (expr
is Constant
)
947 user_operator
= expr
;
952 public class NullCoalescingOperator
: Expression
954 Expression left
, right
;
957 public NullCoalescingOperator (Expression left
, Expression right
, Location loc
)
964 public override Expression
CreateExpressionTree (ResolveContext ec
)
966 if (left
.Type
== TypeManager
.null_type
)
967 ec
.Report
.Error (845, loc
, "An expression tree cannot contain a coalescing operator with null left side");
969 UserCast uc
= left
as UserCast
;
970 Expression conversion
= null;
974 Arguments c_args
= new Arguments (2);
975 c_args
.Add (new Argument (uc
.CreateExpressionTree (ec
)));
976 c_args
.Add (new Argument (left
.CreateExpressionTree (ec
)));
977 conversion
= CreateExpressionFactoryCall (ec
, "Lambda", c_args
);
980 Arguments args
= new Arguments (3);
981 args
.Add (new Argument (left
.CreateExpressionTree (ec
)));
982 args
.Add (new Argument (right
.CreateExpressionTree (ec
)));
983 if (conversion
!= null)
984 args
.Add (new Argument (conversion
));
986 return CreateExpressionFactoryCall (ec
, "Coalesce", args
);
989 Expression
ConvertExpression (ResolveContext ec
)
991 // TODO: ImplicitConversionExists should take care of this
992 if (left
.eclass
== ExprClass
.MethodGroup
)
995 Type ltype
= left
.Type
;
998 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
999 // the result is underlying type of left
1001 if (TypeManager
.IsNullableType (ltype
)) {
1002 unwrap
= Unwrap
.Create (left
, false);
1006 if (Convert
.ImplicitConversionExists (ec
, right
, unwrap
.Type
)) {
1009 right
= Convert
.ImplicitConversion (ec
, right
, type
, loc
);
1012 } else if (TypeManager
.IsReferenceType (ltype
)) {
1013 if (Convert
.ImplicitConversionExists (ec
, right
, ltype
)) {
1015 // Reduce (constant ?? expr) to constant
1017 Constant lc
= left
as Constant
;
1018 if (lc
!= null && !lc
.IsDefaultValue
)
1019 return new SideEffectConstant (lc
, right
, loc
).Resolve (ec
);
1022 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1024 if (right
.IsNull
|| lc
!= null)
1025 return ReducedExpression
.Create (lc
!= null ? right
: left
, this);
1027 right
= Convert
.ImplicitConversion (ec
, right
, ltype
, loc
);
1035 Type rtype
= right
.Type
;
1036 if (!Convert
.ImplicitConversionExists (ec
, unwrap
!= null ? unwrap
: left
, rtype
) || right
.eclass
== ExprClass
.MethodGroup
)
1040 // Reduce (null ?? right) to right
1043 return ReducedExpression
.Create (right
, this);
1045 left
= Convert
.ImplicitConversion (ec
, unwrap
!= null ? unwrap
: left
, rtype
, loc
);
1050 protected override Expression
DoResolve (ResolveContext ec
)
1052 left
= left
.Resolve (ec
);
1053 right
= right
.Resolve (ec
);
1055 if (left
== null || right
== null)
1058 eclass
= ExprClass
.Value
;
1060 Expression e
= ConvertExpression (ec
);
1062 Binary
.Error_OperatorCannotBeApplied (ec
, left
, right
, "??", loc
);
1069 public override void Emit (EmitContext ec
)
1071 ILGenerator ig
= ec
.ig
;
1073 Label end_label
= ig
.DefineLabel ();
1075 if (unwrap
!= null) {
1076 Label is_null_label
= ig
.DefineLabel ();
1078 unwrap
.EmitCheck (ec
);
1079 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
1082 ig
.Emit (OpCodes
.Br
, end_label
);
1084 ig
.MarkLabel (is_null_label
);
1087 ig
.MarkLabel (end_label
);
1093 ig
.Emit (OpCodes
.Dup
);
1094 ig
.Emit (OpCodes
.Brtrue
, end_label
);
1096 ig
.Emit (OpCodes
.Pop
);
1099 ig
.MarkLabel (end_label
);
1102 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1104 left
.MutateHoistedGenericType (storey
);
1105 right
.MutateHoistedGenericType (storey
);
1106 type
= storey
.MutateType (type
);
1109 protected override void CloneTo (CloneContext clonectx
, Expression t
)
1111 NullCoalescingOperator target
= (NullCoalescingOperator
) t
;
1113 target
.left
= left
.Clone (clonectx
);
1114 target
.right
= right
.Clone (clonectx
);
1118 public class LiftedUnaryMutator
: ExpressionStatement
1120 public readonly UnaryMutator
.Mode Mode
;
1122 UnaryMutator underlying
;
1125 public LiftedUnaryMutator (UnaryMutator
.Mode mode
, Expression expr
, Location loc
)
1132 public override Expression
CreateExpressionTree (ResolveContext ec
)
1134 return new SimpleAssign (this, this).CreateExpressionTree (ec
);
1137 protected override Expression
DoResolve (ResolveContext ec
)
1139 expr
= expr
.Resolve (ec
);
1143 unwrap
= Unwrap
.Create (expr
, false);
1147 underlying
= (UnaryMutator
) new UnaryMutator (Mode
, unwrap
).Resolve (ec
);
1148 if (underlying
== null)
1152 eclass
= ExprClass
.Value
;
1157 void DoEmit (EmitContext ec
, bool is_expr
)
1159 ILGenerator ig
= ec
.ig
;
1160 Label is_null_label
= ig
.DefineLabel ();
1161 Label end_label
= ig
.DefineLabel ();
1163 unwrap
.EmitCheck (ec
);
1164 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
1167 underlying
.Emit (ec
);
1168 ig
.Emit (OpCodes
.Br_S
, end_label
);
1170 underlying
.EmitStatement (ec
);
1173 ig
.MarkLabel (is_null_label
);
1175 LiftedNull
.Create (type
, loc
).Emit (ec
);
1177 ig
.MarkLabel (end_label
);
1180 public override void Emit (EmitContext ec
)
1185 public override void EmitStatement (EmitContext ec
)