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
.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 (type
.Module
== RootContext
.ToplevelTypes
.Builder
) {
78 TypeContainer tc
= TypeManager
.LookupGenericTypeContainer (type
);
80 // TODO: check for correct overload
81 Constructor c
= ((Constructor
) tc
.InstanceConstructors
[0]);
84 Constructor
= TypeBuilder
.GetConstructor (type
, c
.ConstructorBuilder
);
89 Constructor
= type
.GetConstructor (new Type
[] { UnderlyingType }
);
93 public class Unwrap
: Expression
, IMemoryLocation
, IAssignMethod
99 readonly bool useDefaultValue
;
101 Unwrap (Expression expr
, bool useDefaultValue
)
104 this.loc
= expr
.Location
;
105 this.useDefaultValue
= useDefaultValue
;
107 info
= new NullableInfo (expr
.Type
);
108 type
= info
.UnderlyingType
;
109 eclass
= expr
.eclass
;
112 public static Expression
Create (Expression expr
)
115 // Avoid unwraping and wraping of same type
117 Wrap wrap
= expr
as Wrap
;
121 return Create (expr
, false);
124 public static Unwrap
Create (Expression expr
, bool useDefaultValue
)
126 return new Unwrap (expr
, useDefaultValue
);
129 public override Expression
CreateExpressionTree (EmitContext ec
)
131 return expr
.CreateExpressionTree (ec
);
134 public override Expression
DoResolve (EmitContext ec
)
139 public override Expression
DoResolveLValue (EmitContext ec
, Expression right_side
)
141 return DoResolve (ec
);
144 public override void Emit (EmitContext ec
)
148 Invocation
.EmitCall (ec
, false, this, info
.GetValueOrDefault
, null, loc
);
150 Invocation
.EmitCall (ec
, false, this, info
.Value
, null, loc
);
153 public void EmitCheck (EmitContext ec
)
156 Invocation
.EmitCall (ec
, false, this, info
.HasValue
, null, loc
);
159 public override bool Equals (object obj
)
161 Unwrap uw
= obj
as Unwrap
;
162 return uw
!= null && expr
.Equals (uw
.expr
);
165 public Expression Original
{
171 public override int GetHashCode ()
173 return expr
.GetHashCode ();
176 public override bool IsNull
{
182 void Store (EmitContext ec
)
184 if (expr
is VariableReference
)
191 LocalVariable
.Store (ec
);
194 public void Load (EmitContext ec
)
196 if (expr
is VariableReference
)
199 LocalVariable
.Emit (ec
);
202 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
204 type
= storey
.MutateType (type
);
205 info
.Constructor
= storey
.MutateConstructor (info
.Constructor
);
206 info
.HasValue
= storey
.MutateGenericMethod (info
.HasValue
);
207 info
.GetValueOrDefault
= storey
.MutateGenericMethod (info
.GetValueOrDefault
);
208 info
.Value
= storey
.MutateGenericMethod (info
.Value
);
211 public void AddressOf (EmitContext ec
, AddressOp mode
)
213 IMemoryLocation ml
= expr
as VariableReference
;
215 ml
.AddressOf (ec
, mode
);
217 LocalVariable
.AddressOf (ec
, mode
);
221 // Keeps result of non-variable expression
223 LocalTemporary LocalVariable
{
226 temp
= new LocalTemporary (info
.Type
);
231 public void Emit (EmitContext ec
, bool leave_copy
)
239 public void EmitAssign (EmitContext ec
, Expression source
,
240 bool leave_copy
, bool prepare_for_load
)
242 InternalWrap wrap
= new InternalWrap (source
, info
, loc
);
243 ((IAssignMethod
) expr
).EmitAssign (ec
, wrap
, leave_copy
, false);
246 protected class InternalWrap
: Expression
248 public Expression expr
;
249 public NullableInfo info
;
251 public InternalWrap (Expression expr
, NullableInfo info
, Location loc
)
258 eclass
= ExprClass
.Value
;
261 public override Expression
CreateExpressionTree (EmitContext ec
)
263 throw new NotSupportedException ("ET");
266 public override Expression
DoResolve (EmitContext ec
)
271 public override void Emit (EmitContext ec
)
274 ec
.ig
.Emit (OpCodes
.Newobj
, info
.Constructor
);
279 public class Wrap
: TypeCast
281 readonly NullableInfo info
;
283 protected Wrap (Expression expr
, Type type
)
286 info
= new NullableInfo (type
);
287 eclass
= ExprClass
.Value
;
290 public Expression Child
{
291 get { return child; }
294 public override Expression
CreateExpressionTree (EmitContext ec
)
296 TypeCast child_cast
= child
as TypeCast
;
297 if (child_cast
!= null) {
299 return child_cast
.CreateExpressionTree (ec
);
302 return base.CreateExpressionTree (ec
);
305 public static Expression
Create (Expression expr
, Type type
)
308 // Avoid unwraping and wraping of the same type
310 Unwrap unwrap
= expr
as Unwrap
;
311 if (unwrap
!= null && TypeManager
.IsEqual (expr
.Type
, TypeManager
.TypeToCoreType (TypeManager
.GetTypeArguments (type
) [0])))
312 return unwrap
.Original
;
314 return new Wrap (expr
, type
);
317 public override void Emit (EmitContext ec
)
320 ec
.ig
.Emit (OpCodes
.Newobj
, info
.Constructor
);
325 // Represents null literal lifted to nullable type
327 public class LiftedNull
: EmptyConstantCast
, IMemoryLocation
329 private LiftedNull (Type nullable_type
, Location loc
)
330 : base (new NullLiteral (loc
), nullable_type
)
332 eclass
= ExprClass
.Value
;
335 public static Constant
Create (Type nullable
, Location loc
)
337 return new LiftedNull (nullable
, loc
);
340 public static Expression
CreateFromExpression (Expression e
)
342 Report
.Warning (458, 2, e
.Location
, "The result of the expression is always `null' of type `{0}'",
343 TypeManager
.CSharpName (e
.Type
));
345 return ReducedExpression
.Create (Create (e
.Type
, e
.Location
), e
);
348 public override Expression
CreateExpressionTree (EmitContext ec
)
350 Arguments args
= new Arguments (2);
351 args
.Add (new Argument (this));
352 args
.Add (new Argument (new TypeOf (new TypeExpression (type
, loc
), loc
)));
354 return CreateExpressionFactoryCall ("Constant", args
);
357 public override void Emit (EmitContext ec
)
359 // TODO: generate less temporary variables
360 LocalTemporary value_target
= new LocalTemporary (type
);
362 value_target
.AddressOf (ec
, AddressOp
.Store
);
363 ec
.ig
.Emit (OpCodes
.Initobj
, type
);
364 value_target
.Emit (ec
);
367 public void AddressOf (EmitContext ec
, AddressOp Mode
)
369 LocalTemporary value_target
= new LocalTemporary (type
);
371 value_target
.AddressOf (ec
, AddressOp
.Store
);
372 ec
.ig
.Emit (OpCodes
.Initobj
, type
);
373 ((IMemoryLocation
) value_target
).AddressOf (ec
, Mode
);
377 public class Lifted
: Expression
, IMemoryLocation
379 Expression expr
, wrap
, null_value
;
382 public Lifted (Expression expr
, Unwrap unwrap
, Type type
)
385 this.unwrap
= unwrap
;
386 this.loc
= expr
.Location
;
390 public Lifted (Expression expr
, Expression unwrap
, Type type
)
391 : this (expr
, unwrap
as Unwrap
, type
)
395 public override Expression
CreateExpressionTree (EmitContext ec
)
397 return wrap
.CreateExpressionTree (ec
);
400 public override Expression
DoResolve (EmitContext ec
)
402 wrap
= Wrap
.Create (expr
, type
);
407 // It's null when lifted conversion is transparent
412 null_value
= LiftedNull
.Create (type
, loc
);
414 eclass
= ExprClass
.Value
;
418 public override void Emit (EmitContext ec
)
420 ILGenerator ig
= ec
.ig
;
421 Label is_null_label
= ig
.DefineLabel ();
422 Label end_label
= ig
.DefineLabel ();
424 unwrap
.EmitCheck (ec
);
425 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
428 ig
.Emit (OpCodes
.Br
, end_label
);
430 ig
.MarkLabel (is_null_label
);
431 null_value
.Emit (ec
);
433 ig
.MarkLabel (end_label
);
436 public void AddressOf (EmitContext ec
, AddressOp mode
)
438 unwrap
.AddressOf (ec
, mode
);
442 public class LiftedUnaryOperator
: Unary
, IMemoryLocation
445 Expression user_operator
;
447 public LiftedUnaryOperator (Unary
.Operator op
, Expression expr
)
452 public void AddressOf (EmitContext ec
, AddressOp mode
)
454 unwrap
.AddressOf (ec
, mode
);
457 public override Expression
CreateExpressionTree (EmitContext ec
)
459 if (user_operator
!= null)
460 return user_operator
.CreateExpressionTree (ec
);
462 if (Oper
== Operator
.UnaryPlus
)
463 return Expr
.CreateExpressionTree (ec
);
465 return base.CreateExpressionTree (ec
);
468 public override Expression
DoResolve (EmitContext ec
)
470 if (eclass
!= ExprClass
.Invalid
)
473 unwrap
= Unwrap
.Create (Expr
, false);
477 Expression res
= base.ResolveOperator (ec
, unwrap
);
479 if (user_operator
== null)
482 res
= Expr
= LiftExpression (ec
, Expr
);
488 eclass
= ExprClass
.Value
;
493 public override void Emit (EmitContext ec
)
495 ILGenerator ig
= ec
.ig
;
496 Label is_null_label
= ig
.DefineLabel ();
497 Label end_label
= ig
.DefineLabel ();
499 unwrap
.EmitCheck (ec
);
500 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
502 NullableInfo ni
= new NullableInfo (type
);
504 if (user_operator
!= null) {
505 user_operator
.Emit (ec
);
507 EmitOperator (ec
, ni
.UnderlyingType
);
510 ig
.Emit (OpCodes
.Newobj
, ni
.Constructor
);
511 ig
.Emit (OpCodes
.Br_S
, end_label
);
513 ig
.MarkLabel (is_null_label
);
514 LiftedNull
.Create (type
, loc
).Emit (ec
);
516 ig
.MarkLabel (end_label
);
519 Expression
LiftExpression (EmitContext ec
, Expression expr
)
521 TypeExpr lifted_type
= new NullableType (expr
.Type
, expr
.Location
);
522 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
523 if (lifted_type
== null)
526 expr
.Type
= lifted_type
.Type
;
530 protected override Expression
ResolveEnumOperator (EmitContext ec
, Expression expr
)
532 expr
= base.ResolveEnumOperator (ec
, expr
);
536 Expr
= LiftExpression (ec
, Expr
);
537 return LiftExpression (ec
, expr
);
540 protected override Expression
ResolveUserOperator (EmitContext ec
, Expression expr
)
542 expr
= base.ResolveUserOperator (ec
, expr
);
547 // When a user operator is of non-nullable type
549 if (Expr
is Unwrap
) {
550 user_operator
= LiftExpression (ec
, expr
);
551 return user_operator
;
558 public class LiftedBinaryOperator
: Binary
560 Unwrap left_unwrap
, right_unwrap
;
561 bool left_null_lifted
, right_null_lifted
;
562 Expression left_orig
, right_orig
;
563 Expression user_operator
;
564 ConstructorInfo wrap_ctor
;
566 public LiftedBinaryOperator (Binary
.Operator op
, Expression left
, Expression right
,
568 : base (op
, left
, right
)
573 public override Expression
CreateExpressionTree (EmitContext ec
)
575 if (user_operator
!= null)
576 return user_operator
.CreateExpressionTree (ec
);
578 return base.CreateExpressionTree (ec
);
582 // CSC 2 has this behavior, it allows structs to be compared
583 // with the null literal *outside* of a generics context and
584 // inlines that as true or false.
586 Expression
CreateNullConstant (Expression expr
)
588 // FIXME: Handle side effect constants
589 Constant c
= new BoolConstant (Oper
== Operator
.Inequality
, loc
);
591 if ((Oper
& Operator
.EqualityMask
) != 0) {
592 Report
.Warning (472, 2, loc
, "The result of comparing value type `{0}' with null is `{1}'",
593 expr
.GetSignatureForError (), c
.AsString ());
595 Report
.Warning (464, 2, loc
, "The result of comparing type `{0}' with null is always `{1}'",
596 expr
.GetSignatureForError (), c
.AsString ());
599 return ReducedExpression
.Create (c
, this);
602 public override Expression
DoResolve (EmitContext ec
)
604 if (eclass
!= ExprClass
.Invalid
)
607 if ((Oper
& Operator
.LogicalMask
) != 0) {
608 Error_OperatorCannotBeApplied (left
, right
);
612 bool use_default_call
= (Oper
& (Operator
.BitwiseMask
| Operator
.EqualityMask
)) != 0;
614 if (TypeManager
.IsNullableType (left
.Type
)) {
615 left
= left_unwrap
= Unwrap
.Create (left
, use_default_call
);
621 if (TypeManager
.IsNullableType (right
.Type
)) {
622 right
= right_unwrap
= Unwrap
.Create (right
, use_default_call
);
628 // Some details are in 6.4.2, 7.2.7
629 // Arguments can be lifted for equal operators when the return type is bool and both
630 // arguments are of same type
632 if (left_orig
.IsNull
) {
634 left_null_lifted
= true;
635 type
= TypeManager
.bool_type
;
638 if (right_orig
.IsNull
) {
640 right_null_lifted
= true;
641 type
= TypeManager
.bool_type
;
644 eclass
= ExprClass
.Value
;
645 return DoResolveCore (ec
, left_orig
, right_orig
);
648 void EmitBitwiseBoolean (EmitContext ec
)
650 ILGenerator ig
= ec
.ig
;
652 Label load_left
= ig
.DefineLabel ();
653 Label load_right
= ig
.DefineLabel ();
654 Label end_label
= ig
.DefineLabel ();
656 left_unwrap
.Emit (ec
);
657 ig
.Emit (OpCodes
.Brtrue_S
, load_right
);
659 right_unwrap
.Emit (ec
);
660 ig
.Emit (OpCodes
.Brtrue_S
, load_left
);
662 left_unwrap
.EmitCheck (ec
);
663 ig
.Emit (OpCodes
.Brfalse_S
, load_right
);
666 ig
.MarkLabel (load_left
);
668 if (Oper
== Operator
.BitwiseAnd
) {
669 left_unwrap
.Load (ec
);
671 right_unwrap
.Load (ec
);
672 right_unwrap
= left_unwrap
;
674 ig
.Emit (OpCodes
.Br_S
, end_label
);
677 ig
.MarkLabel (load_right
);
678 right_unwrap
.Load (ec
);
680 ig
.MarkLabel (end_label
);
684 // Emits optimized equality or inequality operator when possible
686 void EmitEquality (EmitContext ec
)
688 ILGenerator ig
= ec
.ig
;
691 // Either left or right is null
693 if (left_unwrap
!= null && (right_null_lifted
|| right
.IsNull
)) {
694 left_unwrap
.EmitCheck (ec
);
695 if (Oper
== Binary
.Operator
.Equality
) {
696 ig
.Emit (OpCodes
.Ldc_I4_0
);
697 ig
.Emit (OpCodes
.Ceq
);
702 if (right_unwrap
!= null && (left_null_lifted
|| left
.IsNull
)) {
703 right_unwrap
.EmitCheck (ec
);
704 if (Oper
== Binary
.Operator
.Equality
) {
705 ig
.Emit (OpCodes
.Ldc_I4_0
);
706 ig
.Emit (OpCodes
.Ceq
);
711 Label dissimilar_label
= ig
.DefineLabel ();
712 Label end_label
= ig
.DefineLabel ();
714 if (user_operator
!= null) {
715 user_operator
.Emit (ec
);
716 ig
.Emit (Oper
== Operator
.Equality
? OpCodes
.Brfalse_S
: OpCodes
.Brtrue_S
, dissimilar_label
);
721 ig
.Emit (OpCodes
.Bne_Un_S
, dissimilar_label
);
724 if (left_unwrap
!= null)
725 left_unwrap
.EmitCheck (ec
);
727 if (right_unwrap
!= null)
728 right_unwrap
.EmitCheck (ec
);
730 if (left_unwrap
!= null && right_unwrap
!= null) {
731 if (Oper
== Operator
.Inequality
)
732 ig
.Emit (OpCodes
.Xor
);
734 ig
.Emit (OpCodes
.Ceq
);
736 if (Oper
== Operator
.Inequality
) {
737 ig
.Emit (OpCodes
.Ldc_I4_0
);
738 ig
.Emit (OpCodes
.Ceq
);
742 ig
.Emit (OpCodes
.Br_S
, end_label
);
744 ig
.MarkLabel (dissimilar_label
);
745 if (Oper
== Operator
.Inequality
)
746 ig
.Emit (OpCodes
.Ldc_I4_1
);
748 ig
.Emit (OpCodes
.Ldc_I4_0
);
750 ig
.MarkLabel (end_label
);
753 public override void EmitBranchable (EmitContext ec
, Label target
, bool onTrue
)
756 ec
.ig
.Emit (onTrue
? OpCodes
.Brtrue
: OpCodes
.Brfalse
, target
);
759 public override void Emit (EmitContext ec
)
762 // Optimize same expression operation
764 if (right_unwrap
!= null && right
.Equals (left
))
765 right_unwrap
= left_unwrap
;
767 if (user_operator
== null && IsBitwiseBoolean
) {
768 EmitBitwiseBoolean (ec
);
772 if ((Oper
& Operator
.EqualityMask
) != 0) {
777 ILGenerator ig
= ec
.ig
;
779 Label is_null_label
= ig
.DefineLabel ();
780 Label end_label
= ig
.DefineLabel ();
782 if (left_unwrap
!= null) {
783 left_unwrap
.EmitCheck (ec
);
784 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
788 // Don't emit HasValue check when left and right expressions are same
790 if (right_unwrap
!= null && !left
.Equals (right
)) {
791 right_unwrap
.EmitCheck (ec
);
792 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
795 EmitOperator (ec
, left
.Type
);
797 if (wrap_ctor
!= null)
798 ig
.Emit (OpCodes
.Newobj
, wrap_ctor
);
800 ig
.Emit (OpCodes
.Br_S
, end_label
);
801 ig
.MarkLabel (is_null_label
);
803 if ((Oper
& Operator
.ComparisonMask
) != 0) {
804 ig
.Emit (OpCodes
.Ldc_I4_0
);
806 LiftedNull
.Create (type
, loc
).Emit (ec
);
809 ig
.MarkLabel (end_label
);
812 protected override void EmitOperator (EmitContext ec
, Type l
)
814 if (user_operator
!= null) {
815 user_operator
.Emit (ec
);
819 if (TypeManager
.IsNullableType (l
))
820 l
= TypeManager
.TypeToCoreType (TypeManager
.GetTypeArguments (l
) [0]);
822 base.EmitOperator (ec
, l
);
825 bool IsBitwiseBoolean
{
827 return (Oper
& Operator
.BitwiseMask
) != 0 && left_unwrap
!= null && right_unwrap
!= null &&
828 left_unwrap
.Type
== TypeManager
.bool_type
&& right_unwrap
.Type
== TypeManager
.bool_type
;
832 Expression
LiftResult (EmitContext ec
, Expression res_expr
)
834 TypeExpr lifted_type
;
837 // Avoid double conversion
839 if (left_unwrap
== null || left_null_lifted
|| !TypeManager
.IsEqual (left_unwrap
.Type
, left
.Type
) || (left_unwrap
!= null && right_null_lifted
)) {
840 lifted_type
= new NullableType (left
.Type
, loc
);
841 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
842 if (lifted_type
== null)
845 if (left
is UserCast
|| left
is TypeCast
)
846 left
.Type
= lifted_type
.Type
;
848 left
= EmptyCast
.Create (left
, lifted_type
.Type
);
851 if (right_unwrap
== null || right_null_lifted
|| !TypeManager
.IsEqual (right_unwrap
.Type
, right
.Type
) || (right_unwrap
!= null && left_null_lifted
)) {
852 lifted_type
= new NullableType (right
.Type
, loc
);
853 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
854 if (lifted_type
== null)
857 if (right
is UserCast
|| right
is TypeCast
)
858 right
.Type
= lifted_type
.Type
;
860 right
= EmptyCast
.Create (right
, lifted_type
.Type
);
863 if ((Oper
& Operator
.ComparisonMask
) == 0) {
864 lifted_type
= new NullableType (res_expr
.Type
, loc
);
865 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
866 if (lifted_type
== null)
869 wrap_ctor
= new NullableInfo (lifted_type
.Type
).Constructor
;
870 type
= res_expr
.Type
= lifted_type
.Type
;
873 if (left_null_lifted
) {
874 left
= LiftedNull
.Create (right
.Type
, left
.Location
);
876 if ((Oper
& (Operator
.ArithmeticMask
| Operator
.ShiftMask
)) != 0)
877 return LiftedNull
.CreateFromExpression (res_expr
);
880 // Value types and null comparison
882 if (right_unwrap
== null || (Oper
& Operator
.RelationalMask
) != 0)
883 return CreateNullConstant (right_orig
).Resolve (ec
);
886 if (right_null_lifted
) {
887 right
= LiftedNull
.Create (left
.Type
, right
.Location
);
889 if ((Oper
& (Operator
.ArithmeticMask
| Operator
.ShiftMask
)) != 0)
890 return LiftedNull
.CreateFromExpression (res_expr
);
893 // Value types and null comparison
895 if (left_unwrap
== null || (Oper
& Operator
.RelationalMask
) != 0)
896 return CreateNullConstant (left_orig
).Resolve (ec
);
902 protected override Expression
ResolveOperatorPredefined (EmitContext ec
, Binary
.PredefinedOperator
[] operators
, bool primitives_only
, Type enum_type
)
904 Expression e
= base.ResolveOperatorPredefined (ec
, operators
, primitives_only
, enum_type
);
906 if (e
== this || enum_type
!= null)
907 return LiftResult (ec
, e
);
910 // 7.9.9 Equality operators and null
912 // The == and != operators permit one operand to be a value of a nullable type and
913 // the other to be the null literal, even if no predefined or user-defined operator
914 // (in unlifted or lifted form) exists for the operation.
916 if (e
== null && (Oper
& Operator
.EqualityMask
) != 0) {
917 if ((left_null_lifted
&& right_unwrap
!= null) || (right_null_lifted
&& left_unwrap
!= null))
918 return LiftResult (ec
, this);
924 protected override Expression
ResolveUserOperator (EmitContext ec
, Type l
, Type r
)
926 Expression expr
= base.ResolveUserOperator (ec
, l
, r
);
930 expr
= LiftResult (ec
, expr
);
931 if (expr
is Constant
)
935 user_operator
= expr
;
940 public class NullCoalescingOperator
: Expression
942 Expression left
, right
;
945 public NullCoalescingOperator (Expression left
, Expression right
, Location loc
)
952 public override Expression
CreateExpressionTree (EmitContext ec
)
954 if (left
.Type
== TypeManager
.null_type
)
955 Report
.Error (845, loc
, "An expression tree cannot contain a coalescing operator with null left side");
957 UserCast uc
= left
as UserCast
;
958 Expression conversion
= null;
962 Arguments c_args
= new Arguments (2);
963 c_args
.Add (new Argument (uc
.CreateExpressionTree (ec
)));
964 c_args
.Add (new Argument (left
.CreateExpressionTree (ec
)));
965 conversion
= CreateExpressionFactoryCall ("Lambda", c_args
);
968 Arguments args
= new Arguments (3);
969 args
.Add (new Argument (left
.CreateExpressionTree (ec
)));
970 args
.Add (new Argument (right
.CreateExpressionTree (ec
)));
971 if (conversion
!= null)
972 args
.Add (new Argument (conversion
));
974 return CreateExpressionFactoryCall ("Coalesce", args
);
977 Expression
ConvertExpression (EmitContext ec
)
979 // TODO: ImplicitConversionExists should take care of this
980 if (left
.eclass
== ExprClass
.MethodGroup
)
983 Type ltype
= left
.Type
;
986 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
987 // the result is underlying type of left
989 if (TypeManager
.IsNullableType (ltype
)) {
990 unwrap
= Unwrap
.Create (left
, false);
994 if (Convert
.ImplicitConversionExists (ec
, right
, unwrap
.Type
)) {
997 right
= Convert
.ImplicitConversion (ec
, right
, type
, loc
);
1000 } else if (TypeManager
.IsReferenceType (ltype
)) {
1001 if (Convert
.ImplicitConversionExists (ec
, right
, ltype
)) {
1003 // Reduce (constant ?? expr) to constant
1005 Constant lc
= left
as Constant
;
1006 if (lc
!= null && !lc
.IsDefaultValue
)
1007 return new SideEffectConstant (lc
, right
, loc
).Resolve (ec
);
1010 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1012 if (right
.IsNull
|| lc
!= null)
1013 return ReducedExpression
.Create (lc
!= null ? right
: left
, this).Resolve (ec
);
1015 right
= Convert
.ImplicitConversion (ec
, right
, ltype
, loc
);
1023 Type rtype
= right
.Type
;
1024 if (!Convert
.ImplicitConversionExists (ec
, unwrap
!= null ? unwrap
: left
, rtype
) || right
.eclass
== ExprClass
.MethodGroup
)
1028 // Reduce (null ?? right) to right
1031 return ReducedExpression
.Create (right
, this).Resolve (ec
);
1033 left
= Convert
.ImplicitConversion (ec
, unwrap
!= null ? unwrap
: left
, rtype
, loc
);
1038 public override Expression
DoResolve (EmitContext ec
)
1040 if (eclass
!= ExprClass
.Invalid
)
1043 left
= left
.Resolve (ec
);
1044 right
= right
.Resolve (ec
);
1046 if (left
== null || right
== null)
1049 eclass
= ExprClass
.Value
;
1051 Expression e
= ConvertExpression (ec
);
1053 Binary
.Error_OperatorCannotBeApplied (left
, right
, "??", loc
);
1060 public override void Emit (EmitContext ec
)
1062 ILGenerator ig
= ec
.ig
;
1064 Label end_label
= ig
.DefineLabel ();
1066 if (unwrap
!= null) {
1067 Label is_null_label
= ig
.DefineLabel ();
1069 unwrap
.EmitCheck (ec
);
1070 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
1073 ig
.Emit (OpCodes
.Br
, end_label
);
1075 ig
.MarkLabel (is_null_label
);
1078 ig
.MarkLabel (end_label
);
1084 ig
.Emit (OpCodes
.Dup
);
1085 ig
.Emit (OpCodes
.Brtrue
, end_label
);
1087 ig
.Emit (OpCodes
.Pop
);
1090 ig
.MarkLabel (end_label
);
1093 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1095 left
.MutateHoistedGenericType (storey
);
1096 right
.MutateHoistedGenericType (storey
);
1097 type
= storey
.MutateType (type
);
1100 protected override void CloneTo (CloneContext clonectx
, Expression t
)
1102 NullCoalescingOperator target
= (NullCoalescingOperator
) t
;
1104 target
.left
= left
.Clone (clonectx
);
1105 target
.right
= right
.Clone (clonectx
);
1109 public class LiftedUnaryMutator
: ExpressionStatement
1111 public readonly UnaryMutator
.Mode Mode
;
1113 UnaryMutator underlying
;
1116 public LiftedUnaryMutator (UnaryMutator
.Mode mode
, Expression expr
, Location loc
)
1122 eclass
= ExprClass
.Value
;
1125 public override Expression
CreateExpressionTree (EmitContext ec
)
1127 return new SimpleAssign (this, this).CreateExpressionTree (ec
);
1130 public override Expression
DoResolve (EmitContext ec
)
1132 expr
= expr
.Resolve (ec
);
1136 unwrap
= Unwrap
.Create (expr
, false);
1140 underlying
= (UnaryMutator
) new UnaryMutator (Mode
, unwrap
).Resolve (ec
);
1141 if (underlying
== null)
1148 void DoEmit (EmitContext ec
, bool is_expr
)
1150 ILGenerator ig
= ec
.ig
;
1151 Label is_null_label
= ig
.DefineLabel ();
1152 Label end_label
= ig
.DefineLabel ();
1154 unwrap
.EmitCheck (ec
);
1155 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
1158 underlying
.Emit (ec
);
1159 ig
.Emit (OpCodes
.Br_S
, end_label
);
1161 underlying
.EmitStatement (ec
);
1164 ig
.MarkLabel (is_null_label
);
1166 LiftedNull
.Create (type
, loc
).Emit (ec
);
1168 ig
.MarkLabel (end_label
);
1171 public override void Emit (EmitContext ec
)
1176 public override void EmitStatement (EmitContext ec
)