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
;
18 namespace Mono
.CSharp
.Nullable
20 public class NullableType
: TypeExpr
24 public NullableType (TypeExpr underlying
, Location l
)
26 this.underlying
= underlying
;
29 eclass
= ExprClass
.Type
;
32 public NullableType (Type type
, Location loc
)
33 : this (new TypeExpression (type
, loc
), loc
)
36 protected override TypeExpr
DoResolveAsTypeStep (IMemberContext ec
)
38 if (TypeManager
.generic_nullable_type
== null) {
39 TypeManager
.generic_nullable_type
= TypeManager
.CoreLookupType (ec
.Compiler
,
40 "System", "Nullable`1", Kind
.Struct
, true);
43 TypeArguments args
= new TypeArguments (underlying
);
44 GenericTypeExpr ctype
= new GenericTypeExpr (TypeManager
.generic_nullable_type
, args
, loc
);
45 return ctype
.ResolveAsTypeTerminal (ec
, false);
48 public override TypeExpr
ResolveAsTypeTerminal (IMemberContext ec
, bool silent
)
50 return ResolveAsBaseTerminal (ec
, silent
);
54 public sealed class NullableInfo
56 public readonly Type Type
;
57 public readonly Type UnderlyingType
;
58 public MethodInfo HasValue
;
59 public MethodInfo Value
;
60 public MethodInfo GetValueOrDefault
;
61 public ConstructorInfo Constructor
;
63 public NullableInfo (Type type
)
66 UnderlyingType
= TypeManager
.TypeToCoreType (TypeManager
.GetTypeArguments (type
) [0]);
68 PropertyInfo has_value_pi
= TypeManager
.GetPredefinedProperty (type
, "HasValue", Location
.Null
, Type
.EmptyTypes
);
69 PropertyInfo value_pi
= TypeManager
.GetPredefinedProperty (type
, "Value", Location
.Null
, Type
.EmptyTypes
);
70 GetValueOrDefault
= TypeManager
.GetPredefinedMethod (type
, "GetValueOrDefault", Location
.Null
, Type
.EmptyTypes
);
72 HasValue
= has_value_pi
.GetGetMethod (false);
73 Value
= value_pi
.GetGetMethod (false);
75 // When compiling corlib
76 if (TypeManager
.IsBeingCompiled (type
)) {
77 TypeContainer tc
= TypeManager
.LookupGenericTypeContainer (type
);
79 // TODO: check for correct overload
80 Constructor c
= ((Constructor
) tc
.InstanceConstructors
[0]);
82 Constructor
= TypeBuilder
.GetConstructor (type
, c
.ConstructorBuilder
);
87 if (TypeManager
.IsBeingCompiled (UnderlyingType
)) {
88 ConstructorInfo cinfo
= TypeManager
.DropGenericTypeArguments (type
).GetConstructors ()[0];
89 Constructor
= TypeBuilder
.GetConstructor (type
, cinfo
);
94 Constructor
= type
.GetConstructor (new Type
[] { UnderlyingType }
);
98 public class Unwrap
: Expression
, IMemoryLocation
, IAssignMethod
104 readonly bool useDefaultValue
;
106 Unwrap (Expression expr
, bool useDefaultValue
)
109 this.loc
= expr
.Location
;
110 this.useDefaultValue
= useDefaultValue
;
112 info
= new NullableInfo (expr
.Type
);
113 type
= info
.UnderlyingType
;
114 eclass
= expr
.eclass
;
117 public static Expression
Create (Expression expr
)
120 // Avoid unwraping and wraping of same type
122 Wrap wrap
= expr
as Wrap
;
126 return Create (expr
, false);
129 public static Unwrap
Create (Expression expr
, bool useDefaultValue
)
131 return new Unwrap (expr
, useDefaultValue
);
134 public override Expression
CreateExpressionTree (ResolveContext ec
)
136 return expr
.CreateExpressionTree (ec
);
139 protected override Expression
DoResolve (ResolveContext ec
)
144 public override Expression
DoResolveLValue (ResolveContext ec
, Expression right_side
)
146 return DoResolve (ec
);
149 public override void Emit (EmitContext ec
)
153 Invocation
.EmitCall (ec
, false, this, info
.GetValueOrDefault
, null, loc
);
155 Invocation
.EmitCall (ec
, false, this, info
.Value
, null, loc
);
158 public void EmitCheck (EmitContext ec
)
161 Invocation
.EmitCall (ec
, false, this, info
.HasValue
, null, loc
);
164 public override bool Equals (object obj
)
166 Unwrap uw
= obj
as Unwrap
;
167 return uw
!= null && expr
.Equals (uw
.expr
);
170 public Expression Original
{
176 public override int GetHashCode ()
178 return expr
.GetHashCode ();
181 public override bool IsNull
{
187 void Store (EmitContext ec
)
189 if (expr
is VariableReference
)
196 LocalVariable
.Store (ec
);
199 public void Load (EmitContext ec
)
201 if (expr
is VariableReference
)
204 LocalVariable
.Emit (ec
);
208 public override System
.Linq
.Expressions
.Expression
MakeExpression (BuilderContext ctx
)
210 return expr
.MakeExpression (ctx
);
214 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
216 type
= storey
.MutateType (type
);
217 info
.Constructor
= storey
.MutateConstructor (info
.Constructor
);
218 info
.HasValue
= storey
.MutateGenericMethod (info
.HasValue
);
219 info
.GetValueOrDefault
= storey
.MutateGenericMethod (info
.GetValueOrDefault
);
220 info
.Value
= storey
.MutateGenericMethod (info
.Value
);
223 public void AddressOf (EmitContext ec
, AddressOp mode
)
225 IMemoryLocation ml
= expr
as VariableReference
;
227 ml
.AddressOf (ec
, mode
);
229 LocalVariable
.AddressOf (ec
, mode
);
233 // Keeps result of non-variable expression
235 LocalTemporary LocalVariable
{
238 temp
= new LocalTemporary (info
.Type
);
243 public void Emit (EmitContext ec
, bool leave_copy
)
251 public void EmitAssign (EmitContext ec
, Expression source
,
252 bool leave_copy
, bool prepare_for_load
)
254 InternalWrap wrap
= new InternalWrap (source
, info
, loc
);
255 ((IAssignMethod
) expr
).EmitAssign (ec
, wrap
, leave_copy
, false);
258 protected class InternalWrap
: Expression
260 public Expression expr
;
261 public NullableInfo info
;
263 public InternalWrap (Expression expr
, NullableInfo info
, Location loc
)
270 eclass
= ExprClass
.Value
;
273 public override Expression
CreateExpressionTree (ResolveContext ec
)
275 throw new NotSupportedException ("ET");
278 protected override Expression
DoResolve (ResolveContext ec
)
283 public override void Emit (EmitContext ec
)
286 ec
.ig
.Emit (OpCodes
.Newobj
, info
.Constructor
);
291 public class Wrap
: TypeCast
293 readonly NullableInfo info
;
295 protected Wrap (Expression expr
, Type type
)
298 info
= new NullableInfo (type
);
299 eclass
= ExprClass
.Value
;
302 public Expression Child
{
303 get { return child; }
306 public override Expression
CreateExpressionTree (ResolveContext ec
)
308 TypeCast child_cast
= child
as TypeCast
;
309 if (child_cast
!= null) {
311 return child_cast
.CreateExpressionTree (ec
);
314 return base.CreateExpressionTree (ec
);
317 public static Expression
Create (Expression expr
, Type type
)
320 // Avoid unwraping and wraping of the same type
322 Unwrap unwrap
= expr
as Unwrap
;
323 if (unwrap
!= null && TypeManager
.IsEqual (expr
.Type
, TypeManager
.TypeToCoreType (TypeManager
.GetTypeArguments (type
) [0])))
324 return unwrap
.Original
;
326 return new Wrap (expr
, type
);
329 public override void Emit (EmitContext ec
)
332 ec
.ig
.Emit (OpCodes
.Newobj
, info
.Constructor
);
337 // Represents null literal lifted to nullable type
339 public class LiftedNull
: NullConstant
, IMemoryLocation
341 private LiftedNull (Type nullable_type
, Location loc
)
342 : base (nullable_type
, loc
)
344 eclass
= ExprClass
.Value
;
347 public static Constant
Create (Type nullable
, Location loc
)
349 return new LiftedNull (nullable
, loc
);
352 public static Expression
CreateFromExpression (ResolveContext ec
, Expression e
)
354 ec
.Report
.Warning (458, 2, e
.Location
, "The result of the expression is always `null' of type `{0}'",
355 TypeManager
.CSharpName (e
.Type
));
357 return ReducedExpression
.Create (Create (e
.Type
, e
.Location
), e
);
360 public override void Emit (EmitContext ec
)
362 // TODO: generate less temporary variables
363 LocalTemporary value_target
= new LocalTemporary (type
);
365 value_target
.AddressOf (ec
, AddressOp
.Store
);
366 ec
.ig
.Emit (OpCodes
.Initobj
, type
);
367 value_target
.Emit (ec
);
370 public void AddressOf (EmitContext ec
, AddressOp Mode
)
372 LocalTemporary value_target
= new LocalTemporary (type
);
374 value_target
.AddressOf (ec
, AddressOp
.Store
);
375 ec
.ig
.Emit (OpCodes
.Initobj
, type
);
376 ((IMemoryLocation
) value_target
).AddressOf (ec
, Mode
);
381 // Generic lifting expression, supports all S/S? -> T/T? cases
383 public class Lifted
: Expression
, IMemoryLocation
385 Expression expr
, null_value
;
388 public Lifted (Expression expr
, Unwrap unwrap
, Type type
)
391 this.unwrap
= unwrap
;
392 this.loc
= expr
.Location
;
396 public Lifted (Expression expr
, Expression unwrap
, Type type
)
397 : this (expr
, unwrap
as Unwrap
, type
)
401 public override Expression
CreateExpressionTree (ResolveContext ec
)
403 return expr
.CreateExpressionTree (ec
);
406 protected override Expression
DoResolve (ResolveContext ec
)
409 // It's null when lifting non-nullable type
411 if (unwrap
== null) {
412 // S -> T? is wrap only
413 if (TypeManager
.IsNullableType (type
))
414 return Wrap
.Create (expr
, type
);
416 // S -> T can be simplified
420 // Wrap target for T?
421 if (TypeManager
.IsNullableType (type
)) {
422 expr
= Wrap
.Create (expr
, type
);
426 null_value
= LiftedNull
.Create (type
, loc
);
428 null_value
= new NullConstant (type
, loc
);
431 eclass
= ExprClass
.Value
;
435 public override void Emit (EmitContext ec
)
437 ILGenerator ig
= ec
.ig
;
438 Label is_null_label
= ig
.DefineLabel ();
439 Label end_label
= ig
.DefineLabel ();
441 unwrap
.EmitCheck (ec
);
442 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
446 ig
.Emit (OpCodes
.Br
, end_label
);
447 ig
.MarkLabel (is_null_label
);
449 null_value
.Emit (ec
);
450 ig
.MarkLabel (end_label
);
453 public void AddressOf (EmitContext ec
, AddressOp mode
)
455 unwrap
.AddressOf (ec
, mode
);
459 public class LiftedUnaryOperator
: Unary
, IMemoryLocation
462 Expression user_operator
;
464 public LiftedUnaryOperator (Unary
.Operator op
, Expression expr
)
469 public void AddressOf (EmitContext ec
, AddressOp mode
)
471 unwrap
.AddressOf (ec
, mode
);
474 public override Expression
CreateExpressionTree (ResolveContext ec
)
476 if (user_operator
!= null)
477 return user_operator
.CreateExpressionTree (ec
);
479 if (Oper
== Operator
.UnaryPlus
)
480 return Expr
.CreateExpressionTree (ec
);
482 return base.CreateExpressionTree (ec
);
485 protected override Expression
DoResolve (ResolveContext ec
)
487 unwrap
= Unwrap
.Create (Expr
, false);
491 Expression res
= base.ResolveOperator (ec
, unwrap
);
493 if (user_operator
== null)
496 res
= Expr
= LiftExpression (ec
, Expr
);
502 eclass
= ExprClass
.Value
;
507 public override void Emit (EmitContext ec
)
509 ILGenerator ig
= ec
.ig
;
510 Label is_null_label
= ig
.DefineLabel ();
511 Label end_label
= ig
.DefineLabel ();
513 unwrap
.EmitCheck (ec
);
514 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
516 NullableInfo ni
= new NullableInfo (type
);
518 if (user_operator
!= null) {
519 user_operator
.Emit (ec
);
521 EmitOperator (ec
, ni
.UnderlyingType
);
524 ig
.Emit (OpCodes
.Newobj
, ni
.Constructor
);
525 ig
.Emit (OpCodes
.Br_S
, end_label
);
527 ig
.MarkLabel (is_null_label
);
528 LiftedNull
.Create (type
, loc
).Emit (ec
);
530 ig
.MarkLabel (end_label
);
533 Expression
LiftExpression (ResolveContext ec
, Expression expr
)
535 TypeExpr lifted_type
= new NullableType (expr
.Type
, expr
.Location
);
536 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
537 if (lifted_type
== null)
540 expr
.Type
= lifted_type
.Type
;
544 protected override Expression
ResolveEnumOperator (ResolveContext ec
, Expression expr
)
546 expr
= base.ResolveEnumOperator (ec
, expr
);
550 Expr
= LiftExpression (ec
, Expr
);
551 return LiftExpression (ec
, expr
);
554 protected override Expression
ResolveUserOperator (ResolveContext ec
, Expression expr
)
556 expr
= base.ResolveUserOperator (ec
, expr
);
561 // When a user operator is of non-nullable type
563 if (Expr
is Unwrap
) {
564 user_operator
= LiftExpression (ec
, expr
);
565 return user_operator
;
572 public class LiftedBinaryOperator
: Binary
574 Unwrap left_unwrap
, right_unwrap
;
575 bool left_null_lifted
, right_null_lifted
;
576 Expression left_orig
, right_orig
;
577 Expression user_operator
;
578 ConstructorInfo wrap_ctor
;
580 public LiftedBinaryOperator (Binary
.Operator op
, Expression left
, Expression right
,
582 : base (op
, left
, right
)
587 public override Expression
CreateExpressionTree (ResolveContext ec
)
589 if (user_operator
!= null)
590 return user_operator
.CreateExpressionTree (ec
);
592 return base.CreateExpressionTree (ec
);
596 // CSC 2 has this behavior, it allows structs to be compared
597 // with the null literal *outside* of a generics context and
598 // inlines that as true or false.
600 Expression
CreateNullConstant (ResolveContext ec
, Expression expr
)
602 // FIXME: Handle side effect constants
603 Constant c
= new BoolConstant (Oper
== Operator
.Inequality
, loc
).Resolve (ec
);
605 if ((Oper
& Operator
.EqualityMask
) != 0) {
606 ec
.Report
.Warning (472, 2, loc
, "The result of comparing value type `{0}' with null is `{1}'",
607 TypeManager
.CSharpName (expr
.Type
), c
.AsString ());
609 ec
.Report
.Warning (464, 2, loc
, "The result of comparing type `{0}' with null is always `{1}'",
610 TypeManager
.CSharpName (expr
.Type
), c
.AsString ());
613 return ReducedExpression
.Create (c
, this);
616 protected override Expression
DoResolve (ResolveContext ec
)
618 if ((Oper
& Operator
.LogicalMask
) != 0) {
619 Error_OperatorCannotBeApplied (ec
, left
, right
);
623 bool use_default_call
= (Oper
& (Operator
.BitwiseMask
| Operator
.EqualityMask
)) != 0;
625 if (TypeManager
.IsNullableType (left
.Type
)) {
626 left
= left_unwrap
= Unwrap
.Create (left
, use_default_call
);
632 if (TypeManager
.IsNullableType (right
.Type
)) {
633 right
= right_unwrap
= Unwrap
.Create (right
, use_default_call
);
639 // Some details are in 6.4.2, 7.2.7
640 // Arguments can be lifted for equal operators when the return type is bool and both
641 // arguments are of same type
643 if (left_orig
.IsNull
) {
645 left_null_lifted
= true;
646 type
= TypeManager
.bool_type
;
649 if (right_orig
.IsNull
) {
651 right_null_lifted
= true;
652 type
= TypeManager
.bool_type
;
655 eclass
= ExprClass
.Value
;
656 return DoResolveCore (ec
, left_orig
, right_orig
);
659 void EmitBitwiseBoolean (EmitContext ec
)
661 ILGenerator ig
= ec
.ig
;
663 Label load_left
= ig
.DefineLabel ();
664 Label load_right
= ig
.DefineLabel ();
665 Label end_label
= ig
.DefineLabel ();
667 left_unwrap
.Emit (ec
);
668 ig
.Emit (OpCodes
.Brtrue_S
, load_right
);
670 right_unwrap
.Emit (ec
);
671 ig
.Emit (OpCodes
.Brtrue_S
, load_left
);
673 left_unwrap
.EmitCheck (ec
);
674 ig
.Emit (OpCodes
.Brfalse_S
, load_right
);
677 ig
.MarkLabel (load_left
);
679 if (Oper
== Operator
.BitwiseAnd
) {
680 left_unwrap
.Load (ec
);
682 right_unwrap
.Load (ec
);
683 right_unwrap
= left_unwrap
;
685 ig
.Emit (OpCodes
.Br_S
, end_label
);
688 ig
.MarkLabel (load_right
);
689 right_unwrap
.Load (ec
);
691 ig
.MarkLabel (end_label
);
695 // Emits optimized equality or inequality operator when possible
697 void EmitEquality (EmitContext ec
)
699 ILGenerator ig
= ec
.ig
;
702 // Either left or right is null
704 if (left_unwrap
!= null && (right_null_lifted
|| right
.IsNull
)) {
705 left_unwrap
.EmitCheck (ec
);
706 if (Oper
== Binary
.Operator
.Equality
) {
707 ig
.Emit (OpCodes
.Ldc_I4_0
);
708 ig
.Emit (OpCodes
.Ceq
);
713 if (right_unwrap
!= null && (left_null_lifted
|| left
.IsNull
)) {
714 right_unwrap
.EmitCheck (ec
);
715 if (Oper
== Binary
.Operator
.Equality
) {
716 ig
.Emit (OpCodes
.Ldc_I4_0
);
717 ig
.Emit (OpCodes
.Ceq
);
722 Label dissimilar_label
= ig
.DefineLabel ();
723 Label end_label
= ig
.DefineLabel ();
725 if (user_operator
!= null) {
726 user_operator
.Emit (ec
);
727 ig
.Emit (Oper
== Operator
.Equality
? OpCodes
.Brfalse_S
: OpCodes
.Brtrue_S
, dissimilar_label
);
732 ig
.Emit (OpCodes
.Bne_Un_S
, dissimilar_label
);
735 if (left_unwrap
!= null)
736 left_unwrap
.EmitCheck (ec
);
738 if (right_unwrap
!= null)
739 right_unwrap
.EmitCheck (ec
);
741 if (left_unwrap
!= null && right_unwrap
!= null) {
742 if (Oper
== Operator
.Inequality
)
743 ig
.Emit (OpCodes
.Xor
);
745 ig
.Emit (OpCodes
.Ceq
);
747 if (Oper
== Operator
.Inequality
) {
748 ig
.Emit (OpCodes
.Ldc_I4_0
);
749 ig
.Emit (OpCodes
.Ceq
);
753 ig
.Emit (OpCodes
.Br_S
, end_label
);
755 ig
.MarkLabel (dissimilar_label
);
756 if (Oper
== Operator
.Inequality
)
757 ig
.Emit (OpCodes
.Ldc_I4_1
);
759 ig
.Emit (OpCodes
.Ldc_I4_0
);
761 ig
.MarkLabel (end_label
);
764 public override void EmitBranchable (EmitContext ec
, Label target
, bool onTrue
)
767 ec
.ig
.Emit (onTrue
? OpCodes
.Brtrue
: OpCodes
.Brfalse
, target
);
770 public override void Emit (EmitContext ec
)
773 // Optimize same expression operation
775 if (right_unwrap
!= null && right
.Equals (left
))
776 right_unwrap
= left_unwrap
;
778 if (user_operator
== null && IsBitwiseBoolean
) {
779 EmitBitwiseBoolean (ec
);
783 if ((Oper
& Operator
.EqualityMask
) != 0) {
788 ILGenerator ig
= ec
.ig
;
790 Label is_null_label
= ig
.DefineLabel ();
791 Label end_label
= ig
.DefineLabel ();
793 if (left_unwrap
!= null) {
794 left_unwrap
.EmitCheck (ec
);
795 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
799 // Don't emit HasValue check when left and right expressions are same
801 if (right_unwrap
!= null && !left
.Equals (right
)) {
802 right_unwrap
.EmitCheck (ec
);
803 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
806 EmitOperator (ec
, left
.Type
);
808 if (wrap_ctor
!= null)
809 ig
.Emit (OpCodes
.Newobj
, wrap_ctor
);
811 ig
.Emit (OpCodes
.Br_S
, end_label
);
812 ig
.MarkLabel (is_null_label
);
814 if ((Oper
& Operator
.ComparisonMask
) != 0) {
815 ig
.Emit (OpCodes
.Ldc_I4_0
);
817 LiftedNull
.Create (type
, loc
).Emit (ec
);
820 ig
.MarkLabel (end_label
);
823 protected override void EmitOperator (EmitContext ec
, Type l
)
825 if (user_operator
!= null) {
826 user_operator
.Emit (ec
);
830 if (TypeManager
.IsNullableType (l
))
831 l
= TypeManager
.TypeToCoreType (TypeManager
.GetTypeArguments (l
) [0]);
833 base.EmitOperator (ec
, l
);
836 bool IsBitwiseBoolean
{
838 return (Oper
& Operator
.BitwiseMask
) != 0 && left_unwrap
!= null && right_unwrap
!= null &&
839 left_unwrap
.Type
== TypeManager
.bool_type
&& right_unwrap
.Type
== TypeManager
.bool_type
;
843 Expression
LiftResult (ResolveContext ec
, Expression res_expr
)
845 TypeExpr lifted_type
;
848 // Avoid double conversion
850 if (left_unwrap
== null || left_null_lifted
|| !TypeManager
.IsEqual (left_unwrap
.Type
, left
.Type
) || (left_unwrap
!= null && right_null_lifted
)) {
851 lifted_type
= new NullableType (left
.Type
, loc
);
852 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
853 if (lifted_type
== null)
856 if (left
is UserCast
|| left
is TypeCast
)
857 left
.Type
= lifted_type
.Type
;
859 left
= EmptyCast
.Create (left
, lifted_type
.Type
);
862 if (right_unwrap
== null || right_null_lifted
|| !TypeManager
.IsEqual (right_unwrap
.Type
, right
.Type
) || (right_unwrap
!= null && left_null_lifted
)) {
863 lifted_type
= new NullableType (right
.Type
, loc
);
864 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
865 if (lifted_type
== null)
868 if (right
is UserCast
|| right
is TypeCast
)
869 right
.Type
= lifted_type
.Type
;
871 right
= EmptyCast
.Create (right
, lifted_type
.Type
);
874 if ((Oper
& Operator
.ComparisonMask
) == 0) {
875 lifted_type
= new NullableType (res_expr
.Type
, loc
);
876 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
877 if (lifted_type
== null)
880 wrap_ctor
= new NullableInfo (lifted_type
.Type
).Constructor
;
881 type
= res_expr
.Type
= lifted_type
.Type
;
884 if (left_null_lifted
) {
885 left
= LiftedNull
.Create (right
.Type
, left
.Location
);
887 if ((Oper
& (Operator
.ArithmeticMask
| Operator
.ShiftMask
| Operator
.BitwiseMask
)) != 0)
888 return LiftedNull
.CreateFromExpression (ec
, res_expr
);
891 // Value types and null comparison
893 if (right_unwrap
== null || (Oper
& Operator
.RelationalMask
) != 0)
894 return CreateNullConstant (ec
, right_orig
).Resolve (ec
);
897 if (right_null_lifted
) {
898 right
= LiftedNull
.Create (left
.Type
, right
.Location
);
900 if ((Oper
& (Operator
.ArithmeticMask
| Operator
.ShiftMask
| Operator
.BitwiseMask
)) != 0)
901 return LiftedNull
.CreateFromExpression (ec
, res_expr
);
904 // Value types and null comparison
906 if (left_unwrap
== null || (Oper
& Operator
.RelationalMask
) != 0)
907 return CreateNullConstant (ec
, left_orig
);
913 protected override Expression
ResolveOperatorPredefined (ResolveContext ec
, Binary
.PredefinedOperator
[] operators
, bool primitives_only
, Type enum_type
)
915 Expression e
= base.ResolveOperatorPredefined (ec
, operators
, primitives_only
, enum_type
);
917 if (e
== this || enum_type
!= null)
918 return LiftResult (ec
, e
);
921 // 7.9.9 Equality operators and null
923 // The == and != operators permit one operand to be a value of a nullable type and
924 // the other to be the null literal, even if no predefined or user-defined operator
925 // (in unlifted or lifted form) exists for the operation.
927 if (e
== null && (Oper
& Operator
.EqualityMask
) != 0) {
928 if ((left_null_lifted
&& right_unwrap
!= null) || (right_null_lifted
&& left_unwrap
!= null))
929 return LiftResult (ec
, this);
935 protected override Expression
ResolveUserOperator (ResolveContext ec
, Type l
, Type r
)
937 Expression expr
= base.ResolveUserOperator (ec
, l
, r
);
941 expr
= LiftResult (ec
, expr
);
942 if (expr
is Constant
)
946 user_operator
= expr
;
951 public class NullCoalescingOperator
: Expression
953 Expression left
, right
;
956 public NullCoalescingOperator (Expression left
, Expression right
, Location loc
)
963 public override Expression
CreateExpressionTree (ResolveContext ec
)
965 if (left
.Type
== TypeManager
.null_type
)
966 ec
.Report
.Error (845, loc
, "An expression tree cannot contain a coalescing operator with null left side");
968 UserCast uc
= left
as UserCast
;
969 Expression conversion
= null;
973 Arguments c_args
= new Arguments (2);
974 c_args
.Add (new Argument (uc
.CreateExpressionTree (ec
)));
975 c_args
.Add (new Argument (left
.CreateExpressionTree (ec
)));
976 conversion
= CreateExpressionFactoryCall (ec
, "Lambda", c_args
);
979 Arguments args
= new Arguments (3);
980 args
.Add (new Argument (left
.CreateExpressionTree (ec
)));
981 args
.Add (new Argument (right
.CreateExpressionTree (ec
)));
982 if (conversion
!= null)
983 args
.Add (new Argument (conversion
));
985 return CreateExpressionFactoryCall (ec
, "Coalesce", args
);
988 Expression
ConvertExpression (ResolveContext ec
)
990 // TODO: ImplicitConversionExists should take care of this
991 if (left
.eclass
== ExprClass
.MethodGroup
)
994 Type ltype
= left
.Type
;
997 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
998 // the result is underlying type of left
1000 if (TypeManager
.IsNullableType (ltype
)) {
1001 unwrap
= Unwrap
.Create (left
, false);
1005 if (Convert
.ImplicitConversionExists (ec
, right
, unwrap
.Type
)) {
1008 right
= Convert
.ImplicitConversion (ec
, right
, type
, loc
);
1011 } else if (TypeManager
.IsReferenceType (ltype
)) {
1012 if (Convert
.ImplicitConversionExists (ec
, right
, ltype
)) {
1014 // Reduce (constant ?? expr) to constant
1016 Constant lc
= left
as Constant
;
1017 if (lc
!= null && !lc
.IsDefaultValue
)
1018 return new SideEffectConstant (lc
, right
, loc
).Resolve (ec
);
1021 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1023 if (right
.IsNull
|| lc
!= null)
1024 return ReducedExpression
.Create (lc
!= null ? right
: left
, this);
1026 right
= Convert
.ImplicitConversion (ec
, right
, ltype
, loc
);
1034 Type rtype
= right
.Type
;
1035 if (!Convert
.ImplicitConversionExists (ec
, unwrap
!= null ? unwrap
: left
, rtype
) || right
.eclass
== ExprClass
.MethodGroup
)
1039 // Reduce (null ?? right) to right
1042 return ReducedExpression
.Create (right
, this);
1044 left
= Convert
.ImplicitConversion (ec
, unwrap
!= null ? unwrap
: left
, rtype
, loc
);
1049 protected override Expression
DoResolve (ResolveContext ec
)
1051 left
= left
.Resolve (ec
);
1052 right
= right
.Resolve (ec
);
1054 if (left
== null || right
== null)
1057 eclass
= ExprClass
.Value
;
1059 Expression e
= ConvertExpression (ec
);
1061 Binary
.Error_OperatorCannotBeApplied (ec
, left
, right
, "??", loc
);
1068 public override void Emit (EmitContext ec
)
1070 ILGenerator ig
= ec
.ig
;
1072 Label end_label
= ig
.DefineLabel ();
1074 if (unwrap
!= null) {
1075 Label is_null_label
= ig
.DefineLabel ();
1077 unwrap
.EmitCheck (ec
);
1078 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
1081 ig
.Emit (OpCodes
.Br
, end_label
);
1083 ig
.MarkLabel (is_null_label
);
1086 ig
.MarkLabel (end_label
);
1092 ig
.Emit (OpCodes
.Dup
);
1093 ig
.Emit (OpCodes
.Brtrue
, end_label
);
1095 ig
.Emit (OpCodes
.Pop
);
1098 ig
.MarkLabel (end_label
);
1101 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1103 left
.MutateHoistedGenericType (storey
);
1104 right
.MutateHoistedGenericType (storey
);
1105 type
= storey
.MutateType (type
);
1108 protected override void CloneTo (CloneContext clonectx
, Expression t
)
1110 NullCoalescingOperator target
= (NullCoalescingOperator
) t
;
1112 target
.left
= left
.Clone (clonectx
);
1113 target
.right
= right
.Clone (clonectx
);
1117 public class LiftedUnaryMutator
: ExpressionStatement
1119 public readonly UnaryMutator
.Mode Mode
;
1121 UnaryMutator underlying
;
1124 public LiftedUnaryMutator (UnaryMutator
.Mode mode
, Expression expr
, Location loc
)
1131 public override Expression
CreateExpressionTree (ResolveContext ec
)
1133 return new SimpleAssign (this, this).CreateExpressionTree (ec
);
1136 protected override Expression
DoResolve (ResolveContext ec
)
1138 expr
= expr
.Resolve (ec
);
1142 unwrap
= Unwrap
.Create (expr
, false);
1146 underlying
= (UnaryMutator
) new UnaryMutator (Mode
, unwrap
).Resolve (ec
);
1147 if (underlying
== null)
1151 eclass
= ExprClass
.Value
;
1156 void DoEmit (EmitContext ec
, bool is_expr
)
1158 ILGenerator ig
= ec
.ig
;
1159 Label is_null_label
= ig
.DefineLabel ();
1160 Label end_label
= ig
.DefineLabel ();
1162 unwrap
.EmitCheck (ec
);
1163 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
1166 underlying
.Emit (ec
);
1167 ig
.Emit (OpCodes
.Br_S
, end_label
);
1169 underlying
.EmitStatement (ec
);
1172 ig
.MarkLabel (is_null_label
);
1174 LiftedNull
.Create (type
, loc
).Emit (ec
);
1176 ig
.MarkLabel (end_label
);
1179 public override void Emit (EmitContext ec
)
1184 public override void EmitStatement (EmitContext ec
)