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 (IResolveContext ec
)
39 if (TypeManager
.generic_nullable_type
== null) {
40 TypeManager
.generic_nullable_type
= TypeManager
.CoreLookupType (
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 (IResolveContext 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
.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 if (UnderlyingType
.Module
== CodeGen
.Module
.Builder
) {
77 Type o_type
= TypeManager
.DropGenericTypeArguments (type
);
78 Constructor
= TypeBuilder
.GetConstructor (type
,
79 TypeManager
.GetPredefinedConstructor (o_type
, Location
.Null
, o_type
.GetGenericArguments ()));
83 Constructor
= type
.GetConstructor (new Type
[] { UnderlyingType }
);
87 public class Unwrap
: Expression
, IMemoryLocation
, IAssignMethod
94 protected Unwrap (Expression expr
)
97 this.loc
= expr
.Location
;
99 info
= new NullableInfo (expr
.Type
);
100 type
= info
.UnderlyingType
;
101 eclass
= expr
.eclass
;
104 public static Expression
Create (Expression expr
)
107 // Avoid unwraping and wraping of same type
109 Wrap wrap
= expr
as Wrap
;
113 return Create (expr
, null);
116 public static Unwrap
Create (Expression expr
, EmitContext ec
)
118 return new Unwrap (expr
);
121 public override Expression
CreateExpressionTree (EmitContext ec
)
123 return expr
.CreateExpressionTree (ec
);
126 public override Expression
DoResolve (EmitContext ec
)
131 public override Expression
DoResolveLValue (EmitContext ec
, Expression right_side
)
133 return DoResolve (ec
);
136 public override void Emit (EmitContext ec
)
139 Invocation
.EmitCall (ec
, false, this, info
.Value
, null, loc
);
142 public void EmitCheck (EmitContext ec
)
145 Invocation
.EmitCall (ec
, false, this, info
.HasValue
, null, loc
);
148 public void EmitGetValueOrDefault (EmitContext ec
)
151 Invocation
.EmitCall (ec
, false, this, info
.GetValueOrDefault
, null, loc
);
154 public override bool Equals (object obj
)
156 Unwrap uw
= obj
as Unwrap
;
157 return uw
!= null && expr
.Equals (uw
.expr
);
160 public Expression Original
{
166 public override int GetHashCode ()
168 return expr
.GetHashCode ();
171 public override bool IsNull
{
177 void Store (EmitContext ec
)
179 if (expr
is VariableReference
)
186 LocalVariable
.Store (ec
);
189 public void Load (EmitContext ec
)
191 if (expr
is VariableReference
)
194 LocalVariable
.Emit (ec
);
197 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
199 type
= storey
.MutateType (type
);
200 info
.Constructor
= storey
.MutateConstructor (info
.Constructor
);
201 info
.HasValue
= storey
.MutateGenericMethod (info
.HasValue
);
202 info
.GetValueOrDefault
= storey
.MutateGenericMethod (info
.GetValueOrDefault
);
203 info
.Value
= storey
.MutateGenericMethod (info
.Value
);
206 public void AddressOf (EmitContext ec
, AddressOp mode
)
208 IMemoryLocation ml
= expr
as VariableReference
;
210 ml
.AddressOf (ec
, mode
);
212 LocalVariable
.AddressOf (ec
, mode
);
216 // Keeps result of non-variable expression
218 LocalTemporary LocalVariable
{
221 temp
= new LocalTemporary (info
.Type
);
226 public void Emit (EmitContext ec
, bool leave_copy
)
234 public void EmitAssign (EmitContext ec
, Expression source
,
235 bool leave_copy
, bool prepare_for_load
)
237 InternalWrap wrap
= new InternalWrap (source
, info
, loc
);
238 ((IAssignMethod
) expr
).EmitAssign (ec
, wrap
, leave_copy
, false);
241 protected class InternalWrap
: Expression
243 public Expression expr
;
244 public NullableInfo info
;
246 public InternalWrap (Expression expr
, NullableInfo info
, Location loc
)
253 eclass
= ExprClass
.Value
;
256 public override Expression
CreateExpressionTree (EmitContext ec
)
258 throw new NotSupportedException ("ET");
261 public override Expression
DoResolve (EmitContext ec
)
266 public override void Emit (EmitContext ec
)
269 ec
.ig
.Emit (OpCodes
.Newobj
, info
.Constructor
);
274 public class Wrap
: TypeCast
276 readonly NullableInfo info
;
278 protected Wrap (Expression expr
, Type type
)
281 info
= new NullableInfo (type
);
282 eclass
= ExprClass
.Value
;
285 public Expression Child
{
286 get { return child; }
289 public override Expression
CreateExpressionTree (EmitContext ec
)
291 TypeCast child_cast
= child
as TypeCast
;
292 if (child_cast
!= null) {
294 return child_cast
.CreateExpressionTree (ec
);
297 return base.CreateExpressionTree (ec
);
300 public static Expression
Create (Expression expr
, Type type
)
303 // Avoid unwraping and wraping of the same type
305 Unwrap unwrap
= expr
as Unwrap
;
306 if (unwrap
!= null && TypeManager
.IsEqual (expr
.Type
, TypeManager
.GetTypeArguments (type
) [0]))
307 return unwrap
.Original
;
309 return new Wrap (expr
, type
);
312 public override void Emit (EmitContext ec
)
315 ec
.ig
.Emit (OpCodes
.Newobj
, info
.Constructor
);
320 // Represents null literal lifted to nullable type
322 public class LiftedNull
: EmptyConstantCast
, IMemoryLocation
324 private LiftedNull (Type nullable_type
, Location loc
)
325 : base (new NullLiteral (loc
), nullable_type
)
327 eclass
= ExprClass
.Value
;
330 public static Constant
Create (Type nullable
, Location loc
)
332 return new LiftedNull (nullable
, loc
);
335 public static Expression
CreateFromExpression (Expression e
)
337 Report
.Warning (458, 2, e
.Location
, "The result of the expression is always `null' of type `{0}'",
338 TypeManager
.CSharpName (e
.Type
));
340 return ReducedExpression
.Create (Create (e
.Type
, e
.Location
), e
);
343 public override Expression
CreateExpressionTree (EmitContext ec
)
345 ArrayList args
= new ArrayList (2);
346 args
.Add (new Argument (this));
347 args
.Add (new Argument (new TypeOf (new TypeExpression (type
, loc
), loc
)));
349 return CreateExpressionFactoryCall ("Constant", args
);
352 public override void Emit (EmitContext ec
)
354 // TODO: generate less temporary variables
355 LocalTemporary value_target
= new LocalTemporary (type
);
357 value_target
.AddressOf (ec
, AddressOp
.Store
);
358 ec
.ig
.Emit (OpCodes
.Initobj
, type
);
359 value_target
.Emit (ec
);
362 public void AddressOf (EmitContext ec
, AddressOp Mode
)
364 LocalTemporary value_target
= new LocalTemporary (type
);
366 value_target
.AddressOf (ec
, AddressOp
.Store
);
367 ec
.ig
.Emit (OpCodes
.Initobj
, type
);
368 ((IMemoryLocation
) value_target
).AddressOf (ec
, Mode
);
372 public class Lifted
: Expression
, IMemoryLocation
374 Expression expr
, wrap
, null_value
;
377 public Lifted (Expression expr
, Unwrap unwrap
, Type type
)
380 this.unwrap
= unwrap
;
381 this.loc
= expr
.Location
;
385 public Lifted (Expression expr
, Expression unwrap
, Type type
)
386 : this (expr
, unwrap
as Unwrap
, type
)
390 public override Expression
CreateExpressionTree (EmitContext ec
)
392 return wrap
.CreateExpressionTree (ec
);
395 public override Expression
DoResolve (EmitContext ec
)
397 wrap
= Wrap
.Create (expr
, type
);
402 // It's null when lifted conversion is transparent
407 null_value
= LiftedNull
.Create (type
, loc
);
409 eclass
= ExprClass
.Value
;
413 public override void Emit (EmitContext ec
)
415 ILGenerator ig
= ec
.ig
;
416 Label is_null_label
= ig
.DefineLabel ();
417 Label end_label
= ig
.DefineLabel ();
419 unwrap
.EmitCheck (ec
);
420 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
423 ig
.Emit (OpCodes
.Br
, end_label
);
425 ig
.MarkLabel (is_null_label
);
426 null_value
.Emit (ec
);
428 ig
.MarkLabel (end_label
);
431 public void AddressOf (EmitContext ec
, AddressOp mode
)
433 unwrap
.AddressOf (ec
, mode
);
437 public class LiftedUnaryOperator
: Unary
, IMemoryLocation
440 Expression user_operator
;
442 public LiftedUnaryOperator (Unary
.Operator op
, Expression expr
)
447 public void AddressOf (EmitContext ec
, AddressOp mode
)
449 unwrap
.AddressOf (ec
, mode
);
452 public override Expression
CreateExpressionTree (EmitContext ec
)
454 if (user_operator
!= null)
455 return user_operator
.CreateExpressionTree (ec
);
457 if (Oper
== Operator
.UnaryPlus
)
458 return Expr
.CreateExpressionTree (ec
);
460 return base.CreateExpressionTree (ec
);
463 public override Expression
DoResolve (EmitContext ec
)
465 if (eclass
!= ExprClass
.Invalid
)
468 unwrap
= Unwrap
.Create (Expr
, ec
);
472 Expression res
= base.ResolveOperator (ec
, unwrap
);
474 if (user_operator
== null)
477 res
= Expr
= LiftExpression (ec
, Expr
);
483 eclass
= ExprClass
.Value
;
488 public override void Emit (EmitContext ec
)
490 ILGenerator ig
= ec
.ig
;
491 Label is_null_label
= ig
.DefineLabel ();
492 Label end_label
= ig
.DefineLabel ();
494 unwrap
.EmitCheck (ec
);
495 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
497 NullableInfo ni
= new NullableInfo (type
);
499 if (user_operator
!= null) {
500 user_operator
.Emit (ec
);
502 EmitOperator (ec
, ni
.UnderlyingType
);
505 ig
.Emit (OpCodes
.Newobj
, ni
.Constructor
);
506 ig
.Emit (OpCodes
.Br_S
, end_label
);
508 ig
.MarkLabel (is_null_label
);
509 LiftedNull
.Create (type
, loc
).Emit (ec
);
511 ig
.MarkLabel (end_label
);
514 Expression
LiftExpression (EmitContext ec
, Expression expr
)
516 TypeExpr lifted_type
= new NullableType (expr
.Type
, expr
.Location
);
517 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
518 if (lifted_type
== null)
521 expr
.Type
= lifted_type
.Type
;
525 protected override Expression
ResolveEnumOperator (EmitContext ec
, Expression expr
)
527 expr
= base.ResolveEnumOperator (ec
, expr
);
531 Expr
= LiftExpression (ec
, Expr
);
532 return LiftExpression (ec
, expr
);
535 protected override Expression
ResolveUserOperator (EmitContext ec
, Expression expr
)
537 expr
= base.ResolveUserOperator (ec
, expr
);
542 // When a user operator is of non-nullable type
544 if (Expr
is Unwrap
) {
545 user_operator
= LiftExpression (ec
, expr
);
546 return user_operator
;
553 public class LiftedBinaryOperator
: Binary
555 Unwrap left_unwrap
, right_unwrap
;
556 bool left_null_lifted
, right_null_lifted
;
557 Expression left_orig
, right_orig
;
558 Expression user_operator
;
559 ConstructorInfo wrap_ctor
;
561 public LiftedBinaryOperator (Binary
.Operator op
, Expression left
, Expression right
,
563 : base (op
, left
, right
)
568 public override Expression
CreateExpressionTree (EmitContext ec
)
570 if (user_operator
!= null)
571 return user_operator
.CreateExpressionTree (ec
);
573 return base.CreateExpressionTree (ec
);
577 // CSC 2 has this behavior, it allows structs to be compared
578 // with the null literal *outside* of a generics context and
579 // inlines that as true or false.
581 Expression
CreateNullConstant (Expression expr
)
583 // FIXME: Handle side effect constants
584 Constant c
= new BoolConstant (Oper
== Operator
.Inequality
, loc
);
586 if ((Oper
& Operator
.EqualityMask
) != 0) {
587 Report
.Warning (472, 2, loc
, "The result of comparing value type `{0}' with null is `{1}'",
588 expr
.GetSignatureForError (), c
.AsString ());
590 Report
.Warning (464, 2, loc
, "The result of comparing type `{0}' with null is always `{1}'",
591 expr
.GetSignatureForError (), c
.AsString ());
594 return ReducedExpression
.Create (c
, this);
597 public override Expression
DoResolve (EmitContext ec
)
599 if (eclass
!= ExprClass
.Invalid
)
602 if ((Oper
& Operator
.LogicalMask
) != 0) {
603 Error_OperatorCannotBeApplied (left
, right
);
608 if (TypeManager
.IsNullableType (left
.Type
)) {
609 left
= left_unwrap
= Unwrap
.Create (left
, ec
);
615 if (TypeManager
.IsNullableType (right
.Type
)) {
616 right
= right_unwrap
= Unwrap
.Create (right
, ec
);
622 // Some details are in 6.4.2, 7.2.7
623 // Arguments can be lifted for equal operators when the return type is bool and both
624 // arguments are of same type
626 if (left_orig
.IsNull
) {
628 left_null_lifted
= true;
629 type
= TypeManager
.bool_type
;
632 if (right_orig
.IsNull
) {
634 right_null_lifted
= true;
635 type
= TypeManager
.bool_type
;
638 eclass
= ExprClass
.Value
;
639 return DoResolveCore (ec
, left_orig
, right_orig
);
642 void EmitBitwiseBoolean (EmitContext ec
)
644 ILGenerator ig
= ec
.ig
;
646 Label load_left
= ig
.DefineLabel ();
647 Label load_right
= ig
.DefineLabel ();
648 Label end_label
= ig
.DefineLabel ();
650 left_unwrap
.EmitGetValueOrDefault (ec
);
651 ig
.Emit (OpCodes
.Brtrue_S
, load_right
);
653 right_unwrap
.EmitGetValueOrDefault (ec
);
654 ig
.Emit (OpCodes
.Brtrue_S
, load_left
);
656 left_unwrap
.EmitCheck (ec
);
657 ig
.Emit (OpCodes
.Brfalse_S
, load_right
);
660 ig
.MarkLabel (load_left
);
662 if (Oper
== Operator
.BitwiseAnd
) {
663 left_unwrap
.Load (ec
);
665 right_unwrap
.Load (ec
);
666 right_unwrap
= left_unwrap
;
668 ig
.Emit (OpCodes
.Br_S
, end_label
);
671 ig
.MarkLabel (load_right
);
672 right_unwrap
.Load (ec
);
674 ig
.MarkLabel (end_label
);
678 // Emits optimized equality or inequality operator when possible
680 bool EmitEquality (EmitContext ec
)
682 ILGenerator ig
= ec
.ig
;
685 // Either left or right is null
687 if (left_unwrap
!= null && (right_null_lifted
|| right
.IsNull
)) {
688 left_unwrap
.EmitCheck (ec
);
689 if (Oper
== Binary
.Operator
.Equality
) {
690 ig
.Emit (OpCodes
.Ldc_I4_0
);
691 ig
.Emit (OpCodes
.Ceq
);
696 if (right_unwrap
!= null && (left_null_lifted
|| left
.IsNull
)) {
697 right_unwrap
.EmitCheck (ec
);
698 if (Oper
== Binary
.Operator
.Equality
) {
699 ig
.Emit (OpCodes
.Ldc_I4_0
);
700 ig
.Emit (OpCodes
.Ceq
);
705 if (user_operator
!= null)
708 if (left
is UserCast
|| right
is UserCast
)
711 Label dissimilar_label
= ig
.DefineLabel ();
712 Label end_label
= ig
.DefineLabel ();
714 if (left_unwrap
!= null)
715 left_unwrap
.EmitGetValueOrDefault (ec
);
719 if (right_unwrap
!= null)
720 right_unwrap
.EmitGetValueOrDefault (ec
);
724 ig
.Emit (OpCodes
.Bne_Un_S
, dissimilar_label
);
726 if (left_unwrap
!= null)
727 left_unwrap
.EmitCheck (ec
);
728 if (right_unwrap
!= null)
729 right_unwrap
.EmitCheck (ec
);
731 if (left_unwrap
!= null && right_unwrap
!= null) {
732 if (Oper
== Operator
.Inequality
)
733 ig
.Emit (OpCodes
.Xor
);
735 ig
.Emit (OpCodes
.Ceq
);
737 if (Oper
== Operator
.Inequality
) {
738 ig
.Emit (OpCodes
.Ldc_I4_0
);
739 ig
.Emit (OpCodes
.Ceq
);
743 ig
.Emit (OpCodes
.Br_S
, end_label
);
745 ig
.MarkLabel (dissimilar_label
);
746 if (Oper
== Operator
.Inequality
)
747 ig
.Emit (OpCodes
.Ldc_I4_1
);
749 ig
.Emit (OpCodes
.Ldc_I4_0
);
751 ig
.MarkLabel (end_label
);
755 public override void EmitBranchable (EmitContext ec
, Label target
, bool onTrue
)
758 ec
.ig
.Emit (onTrue
? OpCodes
.Brtrue
: OpCodes
.Brfalse
, target
);
761 public override void Emit (EmitContext ec
)
764 // Optimize same expression operation
766 if (right_unwrap
!= null && right
.Equals (left
))
767 right_unwrap
= left_unwrap
;
769 if (user_operator
== null && IsBitwiseBoolean
) {
770 EmitBitwiseBoolean (ec
);
774 if ((Oper
& Operator
.EqualityMask
) != 0) {
775 if (EmitEquality (ec
))
779 ILGenerator ig
= ec
.ig
;
781 Label is_null_label
= ig
.DefineLabel ();
782 Label end_label
= ig
.DefineLabel ();
784 if (left_unwrap
!= null) {
785 left_unwrap
.EmitCheck (ec
);
786 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
790 // Don't emit HasValue check when left and right expressions are same
792 if (right_unwrap
!= null && !left
.Equals (right
)) {
793 right_unwrap
.EmitCheck (ec
);
794 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
797 EmitOperator (ec
, left
.Type
);
799 if (wrap_ctor
!= null)
800 ig
.Emit (OpCodes
.Newobj
, wrap_ctor
);
802 ig
.Emit (OpCodes
.Br_S
, end_label
);
803 ig
.MarkLabel (is_null_label
);
805 if ((Oper
& Operator
.ComparisonMask
) != 0) {
807 // Emit true when equality operator both operands are same
808 // or inequality operator operands are not
810 if ((Oper
== Operator
.Equality
&& left_unwrap
== right_unwrap
) ||
811 (Oper
== Operator
.Inequality
&& left_unwrap
!= right_unwrap
))
812 ig
.Emit (OpCodes
.Ldc_I4_1
);
814 ig
.Emit (OpCodes
.Ldc_I4_0
);
816 LiftedNull
.Create (type
, loc
).Emit (ec
);
819 ig
.MarkLabel (end_label
);
822 protected override void EmitOperator (EmitContext ec
, Type l
)
824 if (user_operator
!= null) {
825 user_operator
.Emit (ec
);
829 if (TypeManager
.IsNullableType (l
))
830 l
= TypeManager
.GetTypeArguments (l
) [0];
832 base.EmitOperator (ec
, l
);
835 bool IsBitwiseBoolean
{
837 return (Oper
& Operator
.BitwiseMask
) != 0 && left_unwrap
!= null && right_unwrap
!= null &&
838 left_unwrap
.Type
== TypeManager
.bool_type
&& right_unwrap
.Type
== TypeManager
.bool_type
;
842 Expression
LiftResult (EmitContext ec
, Expression res_expr
)
844 TypeExpr lifted_type
;
847 // Avoid double conversion
849 if (left_unwrap
== null || left_null_lifted
|| !TypeManager
.IsEqual (left_unwrap
.Type
, left
.Type
) || (left_unwrap
!= null && right_null_lifted
)) {
850 lifted_type
= new NullableType (left
.Type
, loc
);
851 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
852 if (lifted_type
== null)
855 if (left
is UserCast
|| left
is TypeCast
)
856 left
.Type
= lifted_type
.Type
;
858 left
= EmptyCast
.Create (left
, lifted_type
.Type
);
861 if (right_unwrap
== null || right_null_lifted
|| !TypeManager
.IsEqual (right_unwrap
.Type
, right
.Type
) || (right_unwrap
!= null && left_null_lifted
)) {
862 lifted_type
= new NullableType (right
.Type
, loc
);
863 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
864 if (lifted_type
== null)
867 if (right
is UserCast
|| right
is TypeCast
)
868 right
.Type
= lifted_type
.Type
;
870 right
= EmptyCast
.Create (right
, lifted_type
.Type
);
873 if ((Oper
& Operator
.ComparisonMask
) == 0) {
874 lifted_type
= new NullableType (res_expr
.Type
, loc
);
875 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
876 if (lifted_type
== null)
879 wrap_ctor
= new NullableInfo (lifted_type
.Type
).Constructor
;
880 type
= res_expr
.Type
= lifted_type
.Type
;
883 if (left_null_lifted
) {
884 left
= LiftedNull
.Create (right
.Type
, left
.Location
);
886 if ((Oper
& (Operator
.ArithmeticMask
| Operator
.ShiftMask
)) != 0)
887 return LiftedNull
.CreateFromExpression (res_expr
);
890 // Value types and null comparison
892 if (right_unwrap
== null || (Oper
& Operator
.RelationalMask
) != 0)
893 return CreateNullConstant (right_orig
).Resolve (ec
);
896 if (right_null_lifted
) {
897 right
= LiftedNull
.Create (left
.Type
, right
.Location
);
899 if ((Oper
& (Operator
.ArithmeticMask
| Operator
.ShiftMask
)) != 0)
900 return LiftedNull
.CreateFromExpression (res_expr
);
903 // Value types and null comparison
905 if (left_unwrap
== null || (Oper
& Operator
.RelationalMask
) != 0)
906 return CreateNullConstant (left_orig
).Resolve (ec
);
912 protected override Expression
ResolveOperatorPredefined (EmitContext ec
, Binary
.PredefinedOperator
[] operators
, bool primitives_only
, Type enum_type
)
914 Expression e
= base.ResolveOperatorPredefined (ec
, operators
, primitives_only
, enum_type
);
916 if (e
== this || enum_type
!= null)
917 return LiftResult (ec
, e
);
920 // 7.9.9 Equality operators and null
922 // The == and != operators permit one operand to be a value of a nullable type and
923 // the other to be the null literal, even if no predefined or user-defined operator
924 // (in unlifted or lifted form) exists for the operation.
926 if (e
== null && (Oper
& Operator
.EqualityMask
) != 0) {
927 if ((left_null_lifted
&& right_unwrap
!= null) || (right_null_lifted
&& left_unwrap
!= null))
928 return LiftResult (ec
, this);
934 protected override Expression
ResolveUserOperator (EmitContext ec
, Type l
, Type r
)
936 Expression expr
= base.ResolveUserOperator (ec
, l
, r
);
940 expr
= LiftResult (ec
, expr
);
941 if (expr
is Constant
)
945 user_operator
= expr
;
950 public class NullCoalescingOperator
: Expression
952 Expression left
, right
;
955 public NullCoalescingOperator (Expression left
, Expression right
, Location loc
)
962 public override Expression
CreateExpressionTree (EmitContext ec
)
964 if (left
.Type
== TypeManager
.null_type
)
965 Report
.Error (845, loc
, "An expression tree cannot contain a coalescing operator with null left side");
967 UserCast uc
= left
as UserCast
;
968 Expression conversion
= null;
972 ArrayList c_args
= new ArrayList (2);
973 c_args
.Add (new Argument (uc
.CreateExpressionTree (ec
)));
974 c_args
.Add (new Argument (left
.CreateExpressionTree (ec
)));
975 conversion
= CreateExpressionFactoryCall ("Lambda", c_args
);
978 ArrayList args
= new ArrayList (3);
979 args
.Add (new Argument (left
.CreateExpressionTree (ec
)));
980 args
.Add (new Argument (right
.CreateExpressionTree (ec
)));
981 if (conversion
!= null)
982 args
.Add (new Argument (conversion
));
984 return CreateExpressionFactoryCall ("Coalesce", args
);
987 Expression
ConvertExpression (EmitContext ec
)
989 // TODO: ImplicitConversionExists should take care of this
990 if (left
.eclass
== ExprClass
.MethodGroup
)
993 Type ltype
= left
.Type
;
996 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
997 // the result is underlying type of left
999 if (TypeManager
.IsNullableType (ltype
)) {
1000 unwrap
= Unwrap
.Create (left
, ec
);
1004 if (Convert
.ImplicitConversionExists (ec
, right
, unwrap
.Type
)) {
1007 right
= Convert
.ImplicitConversion (ec
, right
, type
, loc
);
1010 } else if (TypeManager
.IsReferenceType (ltype
)) {
1011 if (Convert
.ImplicitConversionExists (ec
, right
, ltype
)) {
1013 // Reduce (constant ?? expr) to constant
1015 Constant lc
= left
as Constant
;
1016 if (lc
!= null && !lc
.IsDefaultValue
)
1017 return new SideEffectConstant (lc
, right
, loc
).Resolve (ec
);
1020 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1022 if (right
.IsNull
|| lc
!= null)
1023 return ReducedExpression
.Create (lc
!= null ? right
: left
, this).Resolve (ec
);
1025 right
= Convert
.ImplicitConversion (ec
, right
, ltype
, loc
);
1033 Type rtype
= right
.Type
;
1034 if (!Convert
.ImplicitConversionExists (ec
, unwrap
!= null ? unwrap
: left
, rtype
) || right
.eclass
== ExprClass
.MethodGroup
)
1038 // Reduce (null ?? right) to right
1041 return ReducedExpression
.Create (right
, this).Resolve (ec
);
1043 left
= Convert
.ImplicitConversion (ec
, unwrap
!= null ? unwrap
: left
, rtype
, loc
);
1048 public override Expression
DoResolve (EmitContext ec
)
1050 if (eclass
!= ExprClass
.Invalid
)
1053 left
= left
.Resolve (ec
);
1054 right
= right
.Resolve (ec
);
1056 if (left
== null || right
== null)
1059 eclass
= ExprClass
.Value
;
1061 Expression e
= ConvertExpression (ec
);
1063 Binary
.Error_OperatorCannotBeApplied (left
, right
, "??", loc
);
1070 public override void Emit (EmitContext ec
)
1072 ILGenerator ig
= ec
.ig
;
1074 Label end_label
= ig
.DefineLabel ();
1076 if (unwrap
!= null) {
1077 Label is_null_label
= ig
.DefineLabel ();
1079 unwrap
.EmitCheck (ec
);
1080 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
1083 ig
.Emit (OpCodes
.Br
, end_label
);
1085 ig
.MarkLabel (is_null_label
);
1088 ig
.MarkLabel (end_label
);
1094 ig
.Emit (OpCodes
.Dup
);
1095 ig
.Emit (OpCodes
.Brtrue
, end_label
);
1097 ig
.Emit (OpCodes
.Pop
);
1100 ig
.MarkLabel (end_label
);
1103 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1105 left
.MutateHoistedGenericType (storey
);
1106 right
.MutateHoistedGenericType (storey
);
1107 type
= storey
.MutateType (type
);
1110 protected override void CloneTo (CloneContext clonectx
, Expression t
)
1112 NullCoalescingOperator target
= (NullCoalescingOperator
) t
;
1114 target
.left
= left
.Clone (clonectx
);
1115 target
.right
= right
.Clone (clonectx
);
1119 public class LiftedUnaryMutator
: ExpressionStatement
1121 public readonly UnaryMutator
.Mode Mode
;
1123 UnaryMutator underlying
;
1126 public LiftedUnaryMutator (UnaryMutator
.Mode mode
, Expression expr
, Location loc
)
1132 eclass
= ExprClass
.Value
;
1135 public override Expression
CreateExpressionTree (EmitContext ec
)
1137 return new SimpleAssign (this, this).CreateExpressionTree (ec
);
1140 public override Expression
DoResolve (EmitContext ec
)
1142 expr
= expr
.Resolve (ec
);
1146 unwrap
= Unwrap
.Create (expr
, ec
);
1150 underlying
= (UnaryMutator
) new UnaryMutator (Mode
, unwrap
).Resolve (ec
);
1151 if (underlying
== null)
1158 void DoEmit (EmitContext ec
, bool is_expr
)
1160 ILGenerator ig
= ec
.ig
;
1161 Label is_null_label
= ig
.DefineLabel ();
1162 Label end_label
= ig
.DefineLabel ();
1164 unwrap
.EmitCheck (ec
);
1165 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
1168 underlying
.Emit (ec
);
1169 ig
.Emit (OpCodes
.Br_S
, end_label
);
1171 underlying
.EmitStatement (ec
);
1174 ig
.MarkLabel (is_null_label
);
1176 LiftedNull
.Create (type
, loc
).Emit (ec
);
1178 ig
.MarkLabel (end_label
);
1181 public override void Emit (EmitContext ec
)
1186 public override void EmitStatement (EmitContext ec
)