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
== RootContext
.ToplevelTypes
.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
93 readonly bool useDefaultValue
;
95 Unwrap (Expression expr
, bool useDefaultValue
)
98 this.loc
= expr
.Location
;
99 this.useDefaultValue
= useDefaultValue
;
101 info
= new NullableInfo (expr
.Type
);
102 type
= info
.UnderlyingType
;
103 eclass
= expr
.eclass
;
106 public static Expression
Create (Expression expr
)
109 // Avoid unwraping and wraping of same type
111 Wrap wrap
= expr
as Wrap
;
115 return Create (expr
, false);
118 public static Unwrap
Create (Expression expr
, bool useDefaultValue
)
120 return new Unwrap (expr
, useDefaultValue
);
123 public override Expression
CreateExpressionTree (EmitContext ec
)
125 return expr
.CreateExpressionTree (ec
);
128 public override Expression
DoResolve (EmitContext ec
)
133 public override Expression
DoResolveLValue (EmitContext ec
, Expression right_side
)
135 return DoResolve (ec
);
138 public override void Emit (EmitContext ec
)
142 Invocation
.EmitCall (ec
, false, this, info
.GetValueOrDefault
, null, loc
);
144 Invocation
.EmitCall (ec
, false, this, info
.Value
, null, loc
);
147 public void EmitCheck (EmitContext ec
)
150 Invocation
.EmitCall (ec
, false, this, info
.HasValue
, null, loc
);
153 public override bool Equals (object obj
)
155 Unwrap uw
= obj
as Unwrap
;
156 return uw
!= null && expr
.Equals (uw
.expr
);
159 public Expression Original
{
165 public override int GetHashCode ()
167 return expr
.GetHashCode ();
170 public override bool IsNull
{
176 void Store (EmitContext ec
)
178 if (expr
is VariableReference
)
185 LocalVariable
.Store (ec
);
188 public void Load (EmitContext ec
)
190 if (expr
is VariableReference
)
193 LocalVariable
.Emit (ec
);
196 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
198 type
= storey
.MutateType (type
);
199 info
.Constructor
= storey
.MutateConstructor (info
.Constructor
);
200 info
.HasValue
= storey
.MutateGenericMethod (info
.HasValue
);
201 info
.GetValueOrDefault
= storey
.MutateGenericMethod (info
.GetValueOrDefault
);
202 info
.Value
= storey
.MutateGenericMethod (info
.Value
);
205 public void AddressOf (EmitContext ec
, AddressOp mode
)
207 IMemoryLocation ml
= expr
as VariableReference
;
209 ml
.AddressOf (ec
, mode
);
211 LocalVariable
.AddressOf (ec
, mode
);
215 // Keeps result of non-variable expression
217 LocalTemporary LocalVariable
{
220 temp
= new LocalTemporary (info
.Type
);
225 public void Emit (EmitContext ec
, bool leave_copy
)
233 public void EmitAssign (EmitContext ec
, Expression source
,
234 bool leave_copy
, bool prepare_for_load
)
236 InternalWrap wrap
= new InternalWrap (source
, info
, loc
);
237 ((IAssignMethod
) expr
).EmitAssign (ec
, wrap
, leave_copy
, false);
240 protected class InternalWrap
: Expression
242 public Expression expr
;
243 public NullableInfo info
;
245 public InternalWrap (Expression expr
, NullableInfo info
, Location loc
)
252 eclass
= ExprClass
.Value
;
255 public override Expression
CreateExpressionTree (EmitContext ec
)
257 throw new NotSupportedException ("ET");
260 public override Expression
DoResolve (EmitContext ec
)
265 public override void Emit (EmitContext ec
)
268 ec
.ig
.Emit (OpCodes
.Newobj
, info
.Constructor
);
273 public class Wrap
: TypeCast
275 readonly NullableInfo info
;
277 protected Wrap (Expression expr
, Type type
)
280 info
= new NullableInfo (type
);
281 eclass
= ExprClass
.Value
;
284 public Expression Child
{
285 get { return child; }
288 public override Expression
CreateExpressionTree (EmitContext ec
)
290 TypeCast child_cast
= child
as TypeCast
;
291 if (child_cast
!= null) {
293 return child_cast
.CreateExpressionTree (ec
);
296 return base.CreateExpressionTree (ec
);
299 public static Expression
Create (Expression expr
, Type type
)
302 // Avoid unwraping and wraping of the same type
304 Unwrap unwrap
= expr
as Unwrap
;
305 if (unwrap
!= null && TypeManager
.IsEqual (expr
.Type
, TypeManager
.GetTypeArguments (type
) [0]))
306 return unwrap
.Original
;
308 return new Wrap (expr
, type
);
311 public override void Emit (EmitContext ec
)
314 ec
.ig
.Emit (OpCodes
.Newobj
, info
.Constructor
);
319 // Represents null literal lifted to nullable type
321 public class LiftedNull
: EmptyConstantCast
, IMemoryLocation
323 private LiftedNull (Type nullable_type
, Location loc
)
324 : base (new NullLiteral (loc
), nullable_type
)
326 eclass
= ExprClass
.Value
;
329 public static Constant
Create (Type nullable
, Location loc
)
331 return new LiftedNull (nullable
, loc
);
334 public static Expression
CreateFromExpression (Expression e
)
336 Report
.Warning (458, 2, e
.Location
, "The result of the expression is always `null' of type `{0}'",
337 TypeManager
.CSharpName (e
.Type
));
339 return ReducedExpression
.Create (Create (e
.Type
, e
.Location
), e
);
342 public override Expression
CreateExpressionTree (EmitContext ec
)
344 Arguments args
= new Arguments (2);
345 args
.Add (new Argument (this));
346 args
.Add (new Argument (new TypeOf (new TypeExpression (type
, loc
), loc
)));
348 return CreateExpressionFactoryCall ("Constant", args
);
351 public override void Emit (EmitContext ec
)
353 // TODO: generate less temporary variables
354 LocalTemporary value_target
= new LocalTemporary (type
);
356 value_target
.AddressOf (ec
, AddressOp
.Store
);
357 ec
.ig
.Emit (OpCodes
.Initobj
, type
);
358 value_target
.Emit (ec
);
361 public void AddressOf (EmitContext ec
, AddressOp Mode
)
363 LocalTemporary value_target
= new LocalTemporary (type
);
365 value_target
.AddressOf (ec
, AddressOp
.Store
);
366 ec
.ig
.Emit (OpCodes
.Initobj
, type
);
367 ((IMemoryLocation
) value_target
).AddressOf (ec
, Mode
);
371 public class Lifted
: Expression
, IMemoryLocation
373 Expression expr
, wrap
, null_value
;
376 public Lifted (Expression expr
, Unwrap unwrap
, Type type
)
379 this.unwrap
= unwrap
;
380 this.loc
= expr
.Location
;
384 public Lifted (Expression expr
, Expression unwrap
, Type type
)
385 : this (expr
, unwrap
as Unwrap
, type
)
389 public override Expression
CreateExpressionTree (EmitContext ec
)
391 return wrap
.CreateExpressionTree (ec
);
394 public override Expression
DoResolve (EmitContext ec
)
396 wrap
= Wrap
.Create (expr
, type
);
401 // It's null when lifted conversion is transparent
406 null_value
= LiftedNull
.Create (type
, loc
);
408 eclass
= ExprClass
.Value
;
412 public override void Emit (EmitContext ec
)
414 ILGenerator ig
= ec
.ig
;
415 Label is_null_label
= ig
.DefineLabel ();
416 Label end_label
= ig
.DefineLabel ();
418 unwrap
.EmitCheck (ec
);
419 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
422 ig
.Emit (OpCodes
.Br
, end_label
);
424 ig
.MarkLabel (is_null_label
);
425 null_value
.Emit (ec
);
427 ig
.MarkLabel (end_label
);
430 public void AddressOf (EmitContext ec
, AddressOp mode
)
432 unwrap
.AddressOf (ec
, mode
);
436 public class LiftedUnaryOperator
: Unary
, IMemoryLocation
439 Expression user_operator
;
441 public LiftedUnaryOperator (Unary
.Operator op
, Expression expr
)
446 public void AddressOf (EmitContext ec
, AddressOp mode
)
448 unwrap
.AddressOf (ec
, mode
);
451 public override Expression
CreateExpressionTree (EmitContext ec
)
453 if (user_operator
!= null)
454 return user_operator
.CreateExpressionTree (ec
);
456 if (Oper
== Operator
.UnaryPlus
)
457 return Expr
.CreateExpressionTree (ec
);
459 return base.CreateExpressionTree (ec
);
462 public override Expression
DoResolve (EmitContext ec
)
464 if (eclass
!= ExprClass
.Invalid
)
467 unwrap
= Unwrap
.Create (Expr
, false);
471 Expression res
= base.ResolveOperator (ec
, unwrap
);
473 if (user_operator
== null)
476 res
= Expr
= LiftExpression (ec
, Expr
);
482 eclass
= ExprClass
.Value
;
487 public override void Emit (EmitContext ec
)
489 ILGenerator ig
= ec
.ig
;
490 Label is_null_label
= ig
.DefineLabel ();
491 Label end_label
= ig
.DefineLabel ();
493 unwrap
.EmitCheck (ec
);
494 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
496 NullableInfo ni
= new NullableInfo (type
);
498 if (user_operator
!= null) {
499 user_operator
.Emit (ec
);
501 EmitOperator (ec
, ni
.UnderlyingType
);
504 ig
.Emit (OpCodes
.Newobj
, ni
.Constructor
);
505 ig
.Emit (OpCodes
.Br_S
, end_label
);
507 ig
.MarkLabel (is_null_label
);
508 LiftedNull
.Create (type
, loc
).Emit (ec
);
510 ig
.MarkLabel (end_label
);
513 Expression
LiftExpression (EmitContext ec
, Expression expr
)
515 TypeExpr lifted_type
= new NullableType (expr
.Type
, expr
.Location
);
516 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
517 if (lifted_type
== null)
520 expr
.Type
= lifted_type
.Type
;
524 protected override Expression
ResolveEnumOperator (EmitContext ec
, Expression expr
)
526 expr
= base.ResolveEnumOperator (ec
, expr
);
530 Expr
= LiftExpression (ec
, Expr
);
531 return LiftExpression (ec
, expr
);
534 protected override Expression
ResolveUserOperator (EmitContext ec
, Expression expr
)
536 expr
= base.ResolveUserOperator (ec
, expr
);
541 // When a user operator is of non-nullable type
543 if (Expr
is Unwrap
) {
544 user_operator
= LiftExpression (ec
, expr
);
545 return user_operator
;
552 public class LiftedBinaryOperator
: Binary
554 Unwrap left_unwrap
, right_unwrap
;
555 bool left_null_lifted
, right_null_lifted
;
556 Expression left_orig
, right_orig
;
557 Expression user_operator
;
558 ConstructorInfo wrap_ctor
;
560 public LiftedBinaryOperator (Binary
.Operator op
, Expression left
, Expression right
,
562 : base (op
, left
, right
)
567 public override Expression
CreateExpressionTree (EmitContext ec
)
569 if (user_operator
!= null)
570 return user_operator
.CreateExpressionTree (ec
);
572 return base.CreateExpressionTree (ec
);
576 // CSC 2 has this behavior, it allows structs to be compared
577 // with the null literal *outside* of a generics context and
578 // inlines that as true or false.
580 Expression
CreateNullConstant (Expression expr
)
582 // FIXME: Handle side effect constants
583 Constant c
= new BoolConstant (Oper
== Operator
.Inequality
, loc
);
585 if ((Oper
& Operator
.EqualityMask
) != 0) {
586 Report
.Warning (472, 2, loc
, "The result of comparing value type `{0}' with null is `{1}'",
587 expr
.GetSignatureForError (), c
.AsString ());
589 Report
.Warning (464, 2, loc
, "The result of comparing type `{0}' with null is always `{1}'",
590 expr
.GetSignatureForError (), c
.AsString ());
593 return ReducedExpression
.Create (c
, this);
596 public override Expression
DoResolve (EmitContext ec
)
598 if (eclass
!= ExprClass
.Invalid
)
601 if ((Oper
& Operator
.LogicalMask
) != 0) {
602 Error_OperatorCannotBeApplied (left
, right
);
606 bool use_default_call
= (Oper
& (Operator
.BitwiseMask
| Operator
.EqualityMask
)) != 0;
608 if (TypeManager
.IsNullableType (left
.Type
)) {
609 left
= left_unwrap
= Unwrap
.Create (left
, use_default_call
);
615 if (TypeManager
.IsNullableType (right
.Type
)) {
616 right
= right_unwrap
= Unwrap
.Create (right
, use_default_call
);
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
.Emit (ec
);
651 ig
.Emit (OpCodes
.Brtrue_S
, load_right
);
653 right_unwrap
.Emit (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 void 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 Label dissimilar_label
= ig
.DefineLabel ();
706 Label end_label
= ig
.DefineLabel ();
708 if (user_operator
!= null) {
709 user_operator
.Emit (ec
);
710 ig
.Emit (Oper
== Operator
.Equality
? OpCodes
.Brfalse_S
: OpCodes
.Brtrue_S
, dissimilar_label
);
715 ig
.Emit (OpCodes
.Bne_Un_S
, dissimilar_label
);
718 if (left_unwrap
!= null)
719 left_unwrap
.EmitCheck (ec
);
721 if (right_unwrap
!= null)
722 right_unwrap
.EmitCheck (ec
);
724 if (left_unwrap
!= null && right_unwrap
!= null) {
725 if (Oper
== Operator
.Inequality
)
726 ig
.Emit (OpCodes
.Xor
);
728 ig
.Emit (OpCodes
.Ceq
);
730 if (Oper
== Operator
.Inequality
) {
731 ig
.Emit (OpCodes
.Ldc_I4_0
);
732 ig
.Emit (OpCodes
.Ceq
);
736 ig
.Emit (OpCodes
.Br_S
, end_label
);
738 ig
.MarkLabel (dissimilar_label
);
739 if (Oper
== Operator
.Inequality
)
740 ig
.Emit (OpCodes
.Ldc_I4_1
);
742 ig
.Emit (OpCodes
.Ldc_I4_0
);
744 ig
.MarkLabel (end_label
);
747 public override void EmitBranchable (EmitContext ec
, Label target
, bool onTrue
)
750 ec
.ig
.Emit (onTrue
? OpCodes
.Brtrue
: OpCodes
.Brfalse
, target
);
753 public override void Emit (EmitContext ec
)
756 // Optimize same expression operation
758 if (right_unwrap
!= null && right
.Equals (left
))
759 right_unwrap
= left_unwrap
;
761 if (user_operator
== null && IsBitwiseBoolean
) {
762 EmitBitwiseBoolean (ec
);
766 if ((Oper
& Operator
.EqualityMask
) != 0) {
771 ILGenerator ig
= ec
.ig
;
773 Label is_null_label
= ig
.DefineLabel ();
774 Label end_label
= ig
.DefineLabel ();
776 if (left_unwrap
!= null) {
777 left_unwrap
.EmitCheck (ec
);
778 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
782 // Don't emit HasValue check when left and right expressions are same
784 if (right_unwrap
!= null && !left
.Equals (right
)) {
785 right_unwrap
.EmitCheck (ec
);
786 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
789 EmitOperator (ec
, left
.Type
);
791 if (wrap_ctor
!= null)
792 ig
.Emit (OpCodes
.Newobj
, wrap_ctor
);
794 ig
.Emit (OpCodes
.Br_S
, end_label
);
795 ig
.MarkLabel (is_null_label
);
797 if ((Oper
& Operator
.ComparisonMask
) != 0) {
798 ig
.Emit (OpCodes
.Ldc_I4_0
);
800 LiftedNull
.Create (type
, loc
).Emit (ec
);
803 ig
.MarkLabel (end_label
);
806 protected override void EmitOperator (EmitContext ec
, Type l
)
808 if (user_operator
!= null) {
809 user_operator
.Emit (ec
);
813 if (TypeManager
.IsNullableType (l
))
814 l
= TypeManager
.GetTypeArguments (l
) [0];
816 base.EmitOperator (ec
, l
);
819 bool IsBitwiseBoolean
{
821 return (Oper
& Operator
.BitwiseMask
) != 0 && left_unwrap
!= null && right_unwrap
!= null &&
822 left_unwrap
.Type
== TypeManager
.bool_type
&& right_unwrap
.Type
== TypeManager
.bool_type
;
826 Expression
LiftResult (EmitContext ec
, Expression res_expr
)
828 TypeExpr lifted_type
;
831 // Avoid double conversion
833 if (left_unwrap
== null || left_null_lifted
|| !TypeManager
.IsEqual (left_unwrap
.Type
, left
.Type
) || (left_unwrap
!= null && right_null_lifted
)) {
834 lifted_type
= new NullableType (left
.Type
, loc
);
835 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
836 if (lifted_type
== null)
839 if (left
is UserCast
|| left
is TypeCast
)
840 left
.Type
= lifted_type
.Type
;
842 left
= EmptyCast
.Create (left
, lifted_type
.Type
);
845 if (right_unwrap
== null || right_null_lifted
|| !TypeManager
.IsEqual (right_unwrap
.Type
, right
.Type
) || (right_unwrap
!= null && left_null_lifted
)) {
846 lifted_type
= new NullableType (right
.Type
, loc
);
847 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
848 if (lifted_type
== null)
851 if (right
is UserCast
|| right
is TypeCast
)
852 right
.Type
= lifted_type
.Type
;
854 right
= EmptyCast
.Create (right
, lifted_type
.Type
);
857 if ((Oper
& Operator
.ComparisonMask
) == 0) {
858 lifted_type
= new NullableType (res_expr
.Type
, loc
);
859 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
860 if (lifted_type
== null)
863 wrap_ctor
= new NullableInfo (lifted_type
.Type
).Constructor
;
864 type
= res_expr
.Type
= lifted_type
.Type
;
867 if (left_null_lifted
) {
868 left
= LiftedNull
.Create (right
.Type
, left
.Location
);
870 if ((Oper
& (Operator
.ArithmeticMask
| Operator
.ShiftMask
)) != 0)
871 return LiftedNull
.CreateFromExpression (res_expr
);
874 // Value types and null comparison
876 if (right_unwrap
== null || (Oper
& Operator
.RelationalMask
) != 0)
877 return CreateNullConstant (right_orig
).Resolve (ec
);
880 if (right_null_lifted
) {
881 right
= LiftedNull
.Create (left
.Type
, right
.Location
);
883 if ((Oper
& (Operator
.ArithmeticMask
| Operator
.ShiftMask
)) != 0)
884 return LiftedNull
.CreateFromExpression (res_expr
);
887 // Value types and null comparison
889 if (left_unwrap
== null || (Oper
& Operator
.RelationalMask
) != 0)
890 return CreateNullConstant (left_orig
).Resolve (ec
);
896 protected override Expression
ResolveOperatorPredefined (EmitContext ec
, Binary
.PredefinedOperator
[] operators
, bool primitives_only
, Type enum_type
)
898 Expression e
= base.ResolveOperatorPredefined (ec
, operators
, primitives_only
, enum_type
);
900 if (e
== this || enum_type
!= null)
901 return LiftResult (ec
, e
);
904 // 7.9.9 Equality operators and null
906 // The == and != operators permit one operand to be a value of a nullable type and
907 // the other to be the null literal, even if no predefined or user-defined operator
908 // (in unlifted or lifted form) exists for the operation.
910 if (e
== null && (Oper
& Operator
.EqualityMask
) != 0) {
911 if ((left_null_lifted
&& right_unwrap
!= null) || (right_null_lifted
&& left_unwrap
!= null))
912 return LiftResult (ec
, this);
918 protected override Expression
ResolveUserOperator (EmitContext ec
, Type l
, Type r
)
920 Expression expr
= base.ResolveUserOperator (ec
, l
, r
);
924 expr
= LiftResult (ec
, expr
);
925 if (expr
is Constant
)
929 user_operator
= expr
;
934 public class NullCoalescingOperator
: Expression
936 Expression left
, right
;
939 public NullCoalescingOperator (Expression left
, Expression right
, Location loc
)
946 public override Expression
CreateExpressionTree (EmitContext ec
)
948 if (left
.Type
== TypeManager
.null_type
)
949 Report
.Error (845, loc
, "An expression tree cannot contain a coalescing operator with null left side");
951 UserCast uc
= left
as UserCast
;
952 Expression conversion
= null;
956 Arguments c_args
= new Arguments (2);
957 c_args
.Add (new Argument (uc
.CreateExpressionTree (ec
)));
958 c_args
.Add (new Argument (left
.CreateExpressionTree (ec
)));
959 conversion
= CreateExpressionFactoryCall ("Lambda", c_args
);
962 Arguments args
= new Arguments (3);
963 args
.Add (new Argument (left
.CreateExpressionTree (ec
)));
964 args
.Add (new Argument (right
.CreateExpressionTree (ec
)));
965 if (conversion
!= null)
966 args
.Add (new Argument (conversion
));
968 return CreateExpressionFactoryCall ("Coalesce", args
);
971 Expression
ConvertExpression (EmitContext ec
)
973 // TODO: ImplicitConversionExists should take care of this
974 if (left
.eclass
== ExprClass
.MethodGroup
)
977 Type ltype
= left
.Type
;
980 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
981 // the result is underlying type of left
983 if (TypeManager
.IsNullableType (ltype
)) {
984 unwrap
= Unwrap
.Create (left
, false);
988 if (Convert
.ImplicitConversionExists (ec
, right
, unwrap
.Type
)) {
991 right
= Convert
.ImplicitConversion (ec
, right
, type
, loc
);
994 } else if (TypeManager
.IsReferenceType (ltype
)) {
995 if (Convert
.ImplicitConversionExists (ec
, right
, ltype
)) {
997 // Reduce (constant ?? expr) to constant
999 Constant lc
= left
as Constant
;
1000 if (lc
!= null && !lc
.IsDefaultValue
)
1001 return new SideEffectConstant (lc
, right
, loc
).Resolve (ec
);
1004 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1006 if (right
.IsNull
|| lc
!= null)
1007 return ReducedExpression
.Create (lc
!= null ? right
: left
, this).Resolve (ec
);
1009 right
= Convert
.ImplicitConversion (ec
, right
, ltype
, loc
);
1017 Type rtype
= right
.Type
;
1018 if (!Convert
.ImplicitConversionExists (ec
, unwrap
!= null ? unwrap
: left
, rtype
) || right
.eclass
== ExprClass
.MethodGroup
)
1022 // Reduce (null ?? right) to right
1025 return ReducedExpression
.Create (right
, this).Resolve (ec
);
1027 left
= Convert
.ImplicitConversion (ec
, unwrap
!= null ? unwrap
: left
, rtype
, loc
);
1032 public override Expression
DoResolve (EmitContext ec
)
1034 if (eclass
!= ExprClass
.Invalid
)
1037 left
= left
.Resolve (ec
);
1038 right
= right
.Resolve (ec
);
1040 if (left
== null || right
== null)
1043 eclass
= ExprClass
.Value
;
1045 Expression e
= ConvertExpression (ec
);
1047 Binary
.Error_OperatorCannotBeApplied (left
, right
, "??", loc
);
1054 public override void Emit (EmitContext ec
)
1056 ILGenerator ig
= ec
.ig
;
1058 Label end_label
= ig
.DefineLabel ();
1060 if (unwrap
!= null) {
1061 Label is_null_label
= ig
.DefineLabel ();
1063 unwrap
.EmitCheck (ec
);
1064 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
1067 ig
.Emit (OpCodes
.Br
, end_label
);
1069 ig
.MarkLabel (is_null_label
);
1072 ig
.MarkLabel (end_label
);
1078 ig
.Emit (OpCodes
.Dup
);
1079 ig
.Emit (OpCodes
.Brtrue
, end_label
);
1081 ig
.Emit (OpCodes
.Pop
);
1084 ig
.MarkLabel (end_label
);
1087 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1089 left
.MutateHoistedGenericType (storey
);
1090 right
.MutateHoistedGenericType (storey
);
1091 type
= storey
.MutateType (type
);
1094 protected override void CloneTo (CloneContext clonectx
, Expression t
)
1096 NullCoalescingOperator target
= (NullCoalescingOperator
) t
;
1098 target
.left
= left
.Clone (clonectx
);
1099 target
.right
= right
.Clone (clonectx
);
1103 public class LiftedUnaryMutator
: ExpressionStatement
1105 public readonly UnaryMutator
.Mode Mode
;
1107 UnaryMutator underlying
;
1110 public LiftedUnaryMutator (UnaryMutator
.Mode mode
, Expression expr
, Location loc
)
1116 eclass
= ExprClass
.Value
;
1119 public override Expression
CreateExpressionTree (EmitContext ec
)
1121 return new SimpleAssign (this, this).CreateExpressionTree (ec
);
1124 public override Expression
DoResolve (EmitContext ec
)
1126 expr
= expr
.Resolve (ec
);
1130 unwrap
= Unwrap
.Create (expr
, false);
1134 underlying
= (UnaryMutator
) new UnaryMutator (Mode
, unwrap
).Resolve (ec
);
1135 if (underlying
== null)
1142 void DoEmit (EmitContext ec
, bool is_expr
)
1144 ILGenerator ig
= ec
.ig
;
1145 Label is_null_label
= ig
.DefineLabel ();
1146 Label end_label
= ig
.DefineLabel ();
1148 unwrap
.EmitCheck (ec
);
1149 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
1152 underlying
.Emit (ec
);
1153 ig
.Emit (OpCodes
.Br_S
, end_label
);
1155 underlying
.EmitStatement (ec
);
1158 ig
.MarkLabel (is_null_label
);
1160 LiftedNull
.Create (type
, loc
).Emit (ec
);
1162 ig
.MarkLabel (end_label
);
1165 public override void Emit (EmitContext ec
)
1170 public override void EmitStatement (EmitContext ec
)