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", MemberKind
.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 MethodSpec HasValue
;
59 public MethodSpec Value
;
60 public MethodSpec GetValueOrDefault
;
61 public MethodSpec Constructor
;
63 public NullableInfo (Type type
)
66 UnderlyingType
= TypeManager
.TypeToCoreType (TypeManager
.GetTypeArguments (type
) [0]);
68 var has_value_pi
= TypeManager
.GetPredefinedProperty (type
, "HasValue", Location
.Null
, Type
.EmptyTypes
);
69 var value_pi
= TypeManager
.GetPredefinedProperty (type
, "Value", Location
.Null
, Type
.EmptyTypes
);
70 GetValueOrDefault
= TypeManager
.GetPredefinedMethod (type
, "GetValueOrDefault", Location
.Null
, Type
.EmptyTypes
);
72 HasValue
= Import
.CreateMethod (has_value_pi
.MetaInfo
.GetGetMethod (false));
73 Value
= Import
.CreateMethod (value_pi
.MetaInfo
.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
= Import
.CreateMethod (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
= Import
.CreateMethod (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
);
207 public override System
.Linq
.Expressions
.Expression
MakeExpression (BuilderContext ctx
)
209 return expr
.MakeExpression (ctx
);
212 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
214 type
= storey
.MutateType (type
);
215 storey
.MutateConstructor (info
.Constructor
);
216 storey
.MutateGenericMethod (info
.HasValue
);
217 storey
.MutateGenericMethod (info
.GetValueOrDefault
);
218 storey
.MutateGenericMethod (info
.Value
);
221 public void AddressOf (EmitContext ec
, AddressOp mode
)
223 IMemoryLocation ml
= expr
as VariableReference
;
225 ml
.AddressOf (ec
, mode
);
227 LocalVariable
.AddressOf (ec
, mode
);
231 // Keeps result of non-variable expression
233 LocalTemporary LocalVariable
{
236 temp
= new LocalTemporary (info
.Type
);
241 public void Emit (EmitContext ec
, bool leave_copy
)
249 public void EmitAssign (EmitContext ec
, Expression source
,
250 bool leave_copy
, bool prepare_for_load
)
252 InternalWrap wrap
= new InternalWrap (source
, info
, loc
);
253 ((IAssignMethod
) expr
).EmitAssign (ec
, wrap
, leave_copy
, false);
256 protected class InternalWrap
: Expression
258 public Expression expr
;
259 public NullableInfo info
;
261 public InternalWrap (Expression expr
, NullableInfo info
, Location loc
)
268 eclass
= ExprClass
.Value
;
271 public override Expression
CreateExpressionTree (ResolveContext ec
)
273 throw new NotSupportedException ("ET");
276 protected override Expression
DoResolve (ResolveContext ec
)
281 public override void Emit (EmitContext ec
)
284 ec
.ig
.Emit (OpCodes
.Newobj
, (ConstructorInfo
) info
.Constructor
.MetaInfo
);
289 public class Wrap
: TypeCast
291 readonly NullableInfo info
;
293 protected Wrap (Expression expr
, Type type
)
296 info
= new NullableInfo (type
);
297 eclass
= ExprClass
.Value
;
300 public Expression Child
{
301 get { return child; }
304 public override Expression
CreateExpressionTree (ResolveContext ec
)
306 TypeCast child_cast
= child
as TypeCast
;
307 if (child_cast
!= null) {
309 return child_cast
.CreateExpressionTree (ec
);
312 return base.CreateExpressionTree (ec
);
315 public static Expression
Create (Expression expr
, Type type
)
318 // Avoid unwraping and wraping of the same type
320 Unwrap unwrap
= expr
as Unwrap
;
321 if (unwrap
!= null && TypeManager
.IsEqual (expr
.Type
, TypeManager
.TypeToCoreType (TypeManager
.GetTypeArguments (type
) [0])))
322 return unwrap
.Original
;
324 return new Wrap (expr
, type
);
327 public override void Emit (EmitContext ec
)
330 ec
.ig
.Emit (OpCodes
.Newobj
, (ConstructorInfo
) info
.Constructor
.MetaInfo
);
335 // Represents null literal lifted to nullable type
337 public class LiftedNull
: NullConstant
, IMemoryLocation
339 private LiftedNull (Type nullable_type
, Location loc
)
340 : base (nullable_type
, loc
)
342 eclass
= ExprClass
.Value
;
345 public static Constant
Create (Type nullable
, Location loc
)
347 return new LiftedNull (nullable
, loc
);
350 public static Expression
CreateFromExpression (ResolveContext ec
, Expression e
)
352 ec
.Report
.Warning (458, 2, e
.Location
, "The result of the expression is always `null' of type `{0}'",
353 TypeManager
.CSharpName (e
.Type
));
355 return ReducedExpression
.Create (Create (e
.Type
, e
.Location
), e
);
358 public override void Emit (EmitContext ec
)
360 // TODO: generate less temporary variables
361 LocalTemporary value_target
= new LocalTemporary (type
);
363 value_target
.AddressOf (ec
, AddressOp
.Store
);
364 ec
.ig
.Emit (OpCodes
.Initobj
, type
);
365 value_target
.Emit (ec
);
368 public void AddressOf (EmitContext ec
, AddressOp Mode
)
370 LocalTemporary value_target
= new LocalTemporary (type
);
372 value_target
.AddressOf (ec
, AddressOp
.Store
);
373 ec
.ig
.Emit (OpCodes
.Initobj
, type
);
374 ((IMemoryLocation
) value_target
).AddressOf (ec
, Mode
);
379 // Generic lifting expression, supports all S/S? -> T/T? cases
381 public class Lifted
: Expression
, IMemoryLocation
383 Expression expr
, null_value
;
386 public Lifted (Expression expr
, Unwrap unwrap
, Type type
)
389 this.unwrap
= unwrap
;
390 this.loc
= expr
.Location
;
394 public Lifted (Expression expr
, Expression unwrap
, Type type
)
395 : this (expr
, unwrap
as Unwrap
, type
)
399 public override Expression
CreateExpressionTree (ResolveContext ec
)
401 return expr
.CreateExpressionTree (ec
);
404 protected override Expression
DoResolve (ResolveContext ec
)
407 // It's null when lifting non-nullable type
409 if (unwrap
== null) {
410 // S -> T? is wrap only
411 if (TypeManager
.IsNullableType (type
))
412 return Wrap
.Create (expr
, type
);
414 // S -> T can be simplified
418 // Wrap target for T?
419 if (TypeManager
.IsNullableType (type
)) {
420 expr
= Wrap
.Create (expr
, type
);
424 null_value
= LiftedNull
.Create (type
, loc
);
426 null_value
= new NullConstant (type
, loc
);
429 eclass
= ExprClass
.Value
;
433 public override void Emit (EmitContext ec
)
435 ILGenerator ig
= ec
.ig
;
436 Label is_null_label
= ig
.DefineLabel ();
437 Label end_label
= ig
.DefineLabel ();
439 unwrap
.EmitCheck (ec
);
440 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
444 ig
.Emit (OpCodes
.Br
, end_label
);
445 ig
.MarkLabel (is_null_label
);
447 null_value
.Emit (ec
);
448 ig
.MarkLabel (end_label
);
451 public void AddressOf (EmitContext ec
, AddressOp mode
)
453 unwrap
.AddressOf (ec
, mode
);
457 public class LiftedUnaryOperator
: Unary
, IMemoryLocation
460 Expression user_operator
;
462 public LiftedUnaryOperator (Unary
.Operator op
, Expression expr
, Location loc
)
463 : base (op
, expr
, loc
)
467 public void AddressOf (EmitContext ec
, AddressOp mode
)
469 unwrap
.AddressOf (ec
, mode
);
472 public override Expression
CreateExpressionTree (ResolveContext ec
)
474 if (user_operator
!= null)
475 return user_operator
.CreateExpressionTree (ec
);
477 if (Oper
== Operator
.UnaryPlus
)
478 return Expr
.CreateExpressionTree (ec
);
480 return base.CreateExpressionTree (ec
);
483 protected override Expression
DoResolve (ResolveContext ec
)
485 unwrap
= Unwrap
.Create (Expr
, false);
489 Expression res
= base.ResolveOperator (ec
, unwrap
);
491 if (user_operator
== null)
494 res
= Expr
= LiftExpression (ec
, Expr
);
500 eclass
= ExprClass
.Value
;
505 public override void Emit (EmitContext ec
)
507 ILGenerator ig
= ec
.ig
;
508 Label is_null_label
= ig
.DefineLabel ();
509 Label end_label
= ig
.DefineLabel ();
511 unwrap
.EmitCheck (ec
);
512 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
514 NullableInfo ni
= new NullableInfo (type
);
516 if (user_operator
!= null) {
517 user_operator
.Emit (ec
);
519 EmitOperator (ec
, ni
.UnderlyingType
);
522 ig
.Emit (OpCodes
.Newobj
, (ConstructorInfo
) ni
.Constructor
.MetaInfo
);
523 ig
.Emit (OpCodes
.Br_S
, end_label
);
525 ig
.MarkLabel (is_null_label
);
526 LiftedNull
.Create (type
, loc
).Emit (ec
);
528 ig
.MarkLabel (end_label
);
531 Expression
LiftExpression (ResolveContext ec
, Expression expr
)
533 TypeExpr lifted_type
= new NullableType (expr
.Type
, expr
.Location
);
534 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
535 if (lifted_type
== null)
538 expr
.Type
= lifted_type
.Type
;
542 protected override Expression
ResolveEnumOperator (ResolveContext ec
, Expression expr
)
544 expr
= base.ResolveEnumOperator (ec
, expr
);
548 Expr
= LiftExpression (ec
, Expr
);
549 return LiftExpression (ec
, expr
);
552 protected override Expression
ResolveUserOperator (ResolveContext ec
, Expression expr
)
554 expr
= base.ResolveUserOperator (ec
, expr
);
559 // When a user operator is of non-nullable type
561 if (Expr
is Unwrap
) {
562 user_operator
= LiftExpression (ec
, expr
);
563 return user_operator
;
570 public class LiftedBinaryOperator
: Binary
572 Unwrap left_unwrap
, right_unwrap
;
573 bool left_null_lifted
, right_null_lifted
;
574 Expression left_orig
, right_orig
;
575 Expression user_operator
;
576 MethodSpec wrap_ctor
;
578 public LiftedBinaryOperator (Binary
.Operator op
, Expression left
, Expression right
, Location loc
)
579 : base (op
, left
, right
, loc
)
583 public override Expression
CreateExpressionTree (ResolveContext ec
)
585 if (user_operator
!= null)
586 return user_operator
.CreateExpressionTree (ec
);
588 return base.CreateExpressionTree (ec
);
592 // CSC 2 has this behavior, it allows structs to be compared
593 // with the null literal *outside* of a generics context and
594 // inlines that as true or false.
596 Expression
CreateNullConstant (ResolveContext ec
, Expression expr
)
598 // FIXME: Handle side effect constants
599 Constant c
= new BoolConstant (Oper
== Operator
.Inequality
, loc
).Resolve (ec
);
601 if ((Oper
& Operator
.EqualityMask
) != 0) {
602 ec
.Report
.Warning (472, 2, loc
, "The result of comparing value type `{0}' with null is `{1}'",
603 TypeManager
.CSharpName (expr
.Type
), c
.AsString ());
605 ec
.Report
.Warning (464, 2, loc
, "The result of comparing type `{0}' with null is always `{1}'",
606 TypeManager
.CSharpName (expr
.Type
), c
.AsString ());
609 return ReducedExpression
.Create (c
, this);
612 protected override Expression
DoResolve (ResolveContext ec
)
614 if ((Oper
& Operator
.LogicalMask
) != 0) {
615 Error_OperatorCannotBeApplied (ec
, left
, right
);
619 bool use_default_call
= (Oper
& (Operator
.BitwiseMask
| Operator
.EqualityMask
)) != 0;
621 if (TypeManager
.IsNullableType (left
.Type
)) {
622 left
= left_unwrap
= Unwrap
.Create (left
, use_default_call
);
628 if (TypeManager
.IsNullableType (right
.Type
)) {
629 right
= right_unwrap
= Unwrap
.Create (right
, use_default_call
);
635 // Some details are in 6.4.2, 7.2.7
636 // Arguments can be lifted for equal operators when the return type is bool and both
637 // arguments are of same type
639 if (left_orig
.IsNull
) {
641 left_null_lifted
= true;
642 type
= TypeManager
.bool_type
;
645 if (right_orig
.IsNull
) {
647 right_null_lifted
= true;
648 type
= TypeManager
.bool_type
;
651 eclass
= ExprClass
.Value
;
652 return DoResolveCore (ec
, left_orig
, right_orig
);
655 void EmitBitwiseBoolean (EmitContext ec
)
657 ILGenerator ig
= ec
.ig
;
659 Label load_left
= ig
.DefineLabel ();
660 Label load_right
= ig
.DefineLabel ();
661 Label end_label
= ig
.DefineLabel ();
663 left_unwrap
.Emit (ec
);
664 ig
.Emit (OpCodes
.Brtrue_S
, load_right
);
666 right_unwrap
.Emit (ec
);
667 ig
.Emit (OpCodes
.Brtrue_S
, load_left
);
669 left_unwrap
.EmitCheck (ec
);
670 ig
.Emit (OpCodes
.Brfalse_S
, load_right
);
673 ig
.MarkLabel (load_left
);
675 if (Oper
== Operator
.BitwiseAnd
) {
676 left_unwrap
.Load (ec
);
678 right_unwrap
.Load (ec
);
679 right_unwrap
= left_unwrap
;
681 ig
.Emit (OpCodes
.Br_S
, end_label
);
684 ig
.MarkLabel (load_right
);
685 right_unwrap
.Load (ec
);
687 ig
.MarkLabel (end_label
);
691 // Emits optimized equality or inequality operator when possible
693 void EmitEquality (EmitContext ec
)
695 ILGenerator ig
= ec
.ig
;
698 // Either left or right is null
700 if (left_unwrap
!= null && (right_null_lifted
|| right
.IsNull
)) {
701 left_unwrap
.EmitCheck (ec
);
702 if (Oper
== Binary
.Operator
.Equality
) {
703 ig
.Emit (OpCodes
.Ldc_I4_0
);
704 ig
.Emit (OpCodes
.Ceq
);
709 if (right_unwrap
!= null && (left_null_lifted
|| left
.IsNull
)) {
710 right_unwrap
.EmitCheck (ec
);
711 if (Oper
== Binary
.Operator
.Equality
) {
712 ig
.Emit (OpCodes
.Ldc_I4_0
);
713 ig
.Emit (OpCodes
.Ceq
);
718 Label dissimilar_label
= ig
.DefineLabel ();
719 Label end_label
= ig
.DefineLabel ();
721 if (user_operator
!= null) {
722 user_operator
.Emit (ec
);
723 ig
.Emit (Oper
== Operator
.Equality
? OpCodes
.Brfalse_S
: OpCodes
.Brtrue_S
, dissimilar_label
);
728 ig
.Emit (OpCodes
.Bne_Un_S
, dissimilar_label
);
731 if (left_unwrap
!= null)
732 left_unwrap
.EmitCheck (ec
);
734 if (right_unwrap
!= null)
735 right_unwrap
.EmitCheck (ec
);
737 if (left_unwrap
!= null && right_unwrap
!= null) {
738 if (Oper
== Operator
.Inequality
)
739 ig
.Emit (OpCodes
.Xor
);
741 ig
.Emit (OpCodes
.Ceq
);
743 if (Oper
== Operator
.Inequality
) {
744 ig
.Emit (OpCodes
.Ldc_I4_0
);
745 ig
.Emit (OpCodes
.Ceq
);
749 ig
.Emit (OpCodes
.Br_S
, end_label
);
751 ig
.MarkLabel (dissimilar_label
);
752 if (Oper
== Operator
.Inequality
)
753 ig
.Emit (OpCodes
.Ldc_I4_1
);
755 ig
.Emit (OpCodes
.Ldc_I4_0
);
757 ig
.MarkLabel (end_label
);
760 public override void EmitBranchable (EmitContext ec
, Label target
, bool onTrue
)
763 ec
.ig
.Emit (onTrue
? OpCodes
.Brtrue
: OpCodes
.Brfalse
, target
);
766 public override void Emit (EmitContext ec
)
769 // Optimize same expression operation
771 if (right_unwrap
!= null && right
.Equals (left
))
772 right_unwrap
= left_unwrap
;
774 if (user_operator
== null && IsBitwiseBoolean
) {
775 EmitBitwiseBoolean (ec
);
779 if ((Oper
& Operator
.EqualityMask
) != 0) {
784 ILGenerator ig
= ec
.ig
;
786 Label is_null_label
= ig
.DefineLabel ();
787 Label end_label
= ig
.DefineLabel ();
789 if (left_unwrap
!= null) {
790 left_unwrap
.EmitCheck (ec
);
791 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
795 // Don't emit HasValue check when left and right expressions are same
797 if (right_unwrap
!= null && !left
.Equals (right
)) {
798 right_unwrap
.EmitCheck (ec
);
799 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
802 EmitOperator (ec
, left
.Type
);
804 if (wrap_ctor
!= null)
805 ig
.Emit (OpCodes
.Newobj
, (ConstructorInfo
) wrap_ctor
.MetaInfo
);
807 ig
.Emit (OpCodes
.Br_S
, end_label
);
808 ig
.MarkLabel (is_null_label
);
810 if ((Oper
& Operator
.ComparisonMask
) != 0) {
811 ig
.Emit (OpCodes
.Ldc_I4_0
);
813 LiftedNull
.Create (type
, loc
).Emit (ec
);
816 ig
.MarkLabel (end_label
);
819 protected override void EmitOperator (EmitContext ec
, Type l
)
821 if (user_operator
!= null) {
822 user_operator
.Emit (ec
);
826 if (TypeManager
.IsNullableType (l
))
827 l
= TypeManager
.TypeToCoreType (TypeManager
.GetTypeArguments (l
) [0]);
829 base.EmitOperator (ec
, l
);
832 bool IsBitwiseBoolean
{
834 return (Oper
& Operator
.BitwiseMask
) != 0 && left_unwrap
!= null && right_unwrap
!= null &&
835 left_unwrap
.Type
== TypeManager
.bool_type
&& right_unwrap
.Type
== TypeManager
.bool_type
;
839 Expression
LiftResult (ResolveContext ec
, Expression res_expr
)
841 TypeExpr lifted_type
;
844 // Avoid double conversion
846 if (left_unwrap
== null || left_null_lifted
|| !TypeManager
.IsEqual (left_unwrap
.Type
, left
.Type
) || (left_unwrap
!= null && right_null_lifted
)) {
847 lifted_type
= new NullableType (left
.Type
, loc
);
848 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
849 if (lifted_type
== null)
852 if (left
is UserCast
|| left
is TypeCast
)
853 left
.Type
= lifted_type
.Type
;
855 left
= EmptyCast
.Create (left
, lifted_type
.Type
);
858 if (right_unwrap
== null || right_null_lifted
|| !TypeManager
.IsEqual (right_unwrap
.Type
, right
.Type
) || (right_unwrap
!= null && left_null_lifted
)) {
859 lifted_type
= new NullableType (right
.Type
, loc
);
860 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
861 if (lifted_type
== null)
864 if (right
is UserCast
|| right
is TypeCast
)
865 right
.Type
= lifted_type
.Type
;
867 right
= EmptyCast
.Create (right
, lifted_type
.Type
);
870 if ((Oper
& Operator
.ComparisonMask
) == 0) {
871 lifted_type
= new NullableType (res_expr
.Type
, loc
);
872 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
873 if (lifted_type
== null)
876 wrap_ctor
= new NullableInfo (lifted_type
.Type
).Constructor
;
877 type
= res_expr
.Type
= lifted_type
.Type
;
880 if (left_null_lifted
) {
881 left
= LiftedNull
.Create (right
.Type
, left
.Location
);
883 if ((Oper
& (Operator
.ArithmeticMask
| Operator
.ShiftMask
| Operator
.BitwiseMask
)) != 0)
884 return LiftedNull
.CreateFromExpression (ec
, res_expr
);
887 // Value types and null comparison
889 if (right_unwrap
== null || (Oper
& Operator
.RelationalMask
) != 0)
890 return CreateNullConstant (ec
, right_orig
).Resolve (ec
);
893 if (right_null_lifted
) {
894 right
= LiftedNull
.Create (left
.Type
, right
.Location
);
896 if ((Oper
& (Operator
.ArithmeticMask
| Operator
.ShiftMask
| Operator
.BitwiseMask
)) != 0)
897 return LiftedNull
.CreateFromExpression (ec
, res_expr
);
900 // Value types and null comparison
902 if (left_unwrap
== null || (Oper
& Operator
.RelationalMask
) != 0)
903 return CreateNullConstant (ec
, left_orig
);
909 protected override Expression
ResolveOperatorPredefined (ResolveContext ec
, Binary
.PredefinedOperator
[] operators
, bool primitives_only
, Type enum_type
)
911 Expression e
= base.ResolveOperatorPredefined (ec
, operators
, primitives_only
, enum_type
);
913 if (e
== this || enum_type
!= null)
914 return LiftResult (ec
, e
);
917 // 7.9.9 Equality operators and null
919 // The == and != operators permit one operand to be a value of a nullable type and
920 // the other to be the null literal, even if no predefined or user-defined operator
921 // (in unlifted or lifted form) exists for the operation.
923 if (e
== null && (Oper
& Operator
.EqualityMask
) != 0) {
924 if ((left_null_lifted
&& right_unwrap
!= null) || (right_null_lifted
&& left_unwrap
!= null))
925 return LiftResult (ec
, this);
931 protected override Expression
ResolveUserOperator (ResolveContext ec
, Type l
, Type r
)
933 Expression expr
= base.ResolveUserOperator (ec
, l
, r
);
937 expr
= LiftResult (ec
, expr
);
938 if (expr
is Constant
)
942 user_operator
= expr
;
947 public class NullCoalescingOperator
: Expression
949 Expression left
, right
;
952 public NullCoalescingOperator (Expression left
, Expression right
, Location loc
)
959 public override Expression
CreateExpressionTree (ResolveContext ec
)
961 if (left
.Type
== TypeManager
.null_type
)
962 ec
.Report
.Error (845, loc
, "An expression tree cannot contain a coalescing operator with null left side");
964 UserCast uc
= left
as UserCast
;
965 Expression conversion
= null;
969 Arguments c_args
= new Arguments (2);
970 c_args
.Add (new Argument (uc
.CreateExpressionTree (ec
)));
971 c_args
.Add (new Argument (left
.CreateExpressionTree (ec
)));
972 conversion
= CreateExpressionFactoryCall (ec
, "Lambda", c_args
);
975 Arguments args
= new Arguments (3);
976 args
.Add (new Argument (left
.CreateExpressionTree (ec
)));
977 args
.Add (new Argument (right
.CreateExpressionTree (ec
)));
978 if (conversion
!= null)
979 args
.Add (new Argument (conversion
));
981 return CreateExpressionFactoryCall (ec
, "Coalesce", args
);
984 Expression
ConvertExpression (ResolveContext ec
)
986 // TODO: ImplicitConversionExists should take care of this
987 if (left
.eclass
== ExprClass
.MethodGroup
)
990 Type ltype
= left
.Type
;
993 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
994 // the result is underlying type of left
996 if (TypeManager
.IsNullableType (ltype
)) {
997 unwrap
= Unwrap
.Create (left
, false);
1001 if (Convert
.ImplicitConversionExists (ec
, right
, unwrap
.Type
)) {
1004 right
= Convert
.ImplicitConversion (ec
, right
, type
, loc
);
1007 } else if (TypeManager
.IsReferenceType (ltype
)) {
1008 if (Convert
.ImplicitConversionExists (ec
, right
, ltype
)) {
1010 // Reduce (constant ?? expr) to constant
1012 Constant lc
= left
as Constant
;
1013 if (lc
!= null && !lc
.IsDefaultValue
)
1014 return new SideEffectConstant (lc
, right
, loc
).Resolve (ec
);
1017 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1019 if (right
.IsNull
|| lc
!= null)
1020 return ReducedExpression
.Create (lc
!= null ? right
: left
, this);
1022 right
= Convert
.ImplicitConversion (ec
, right
, ltype
, loc
);
1030 Type rtype
= right
.Type
;
1031 if (!Convert
.ImplicitConversionExists (ec
, unwrap
!= null ? unwrap
: left
, rtype
) || right
.eclass
== ExprClass
.MethodGroup
)
1035 // Reduce (null ?? right) to right
1038 return ReducedExpression
.Create (right
, this);
1040 left
= Convert
.ImplicitConversion (ec
, unwrap
!= null ? unwrap
: left
, rtype
, loc
);
1045 protected override Expression
DoResolve (ResolveContext ec
)
1047 left
= left
.Resolve (ec
);
1048 right
= right
.Resolve (ec
);
1050 if (left
== null || right
== null)
1053 eclass
= ExprClass
.Value
;
1055 Expression e
= ConvertExpression (ec
);
1057 Binary
.Error_OperatorCannotBeApplied (ec
, left
, right
, "??", loc
);
1064 public override void Emit (EmitContext ec
)
1066 ILGenerator ig
= ec
.ig
;
1068 Label end_label
= ig
.DefineLabel ();
1070 if (unwrap
!= null) {
1071 Label is_null_label
= ig
.DefineLabel ();
1073 unwrap
.EmitCheck (ec
);
1074 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
1077 ig
.Emit (OpCodes
.Br
, end_label
);
1079 ig
.MarkLabel (is_null_label
);
1082 ig
.MarkLabel (end_label
);
1088 ig
.Emit (OpCodes
.Dup
);
1089 ig
.Emit (OpCodes
.Brtrue
, end_label
);
1091 ig
.Emit (OpCodes
.Pop
);
1094 ig
.MarkLabel (end_label
);
1097 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
1099 left
.MutateHoistedGenericType (storey
);
1100 right
.MutateHoistedGenericType (storey
);
1101 type
= storey
.MutateType (type
);
1104 protected override void CloneTo (CloneContext clonectx
, Expression t
)
1106 NullCoalescingOperator target
= (NullCoalescingOperator
) t
;
1108 target
.left
= left
.Clone (clonectx
);
1109 target
.right
= right
.Clone (clonectx
);
1113 public class LiftedUnaryMutator
: ExpressionStatement
1115 public readonly UnaryMutator
.Mode Mode
;
1117 UnaryMutator underlying
;
1120 public LiftedUnaryMutator (UnaryMutator
.Mode mode
, Expression expr
, Location loc
)
1127 public override Expression
CreateExpressionTree (ResolveContext ec
)
1129 return new SimpleAssign (this, this).CreateExpressionTree (ec
);
1132 protected override Expression
DoResolve (ResolveContext ec
)
1134 expr
= expr
.Resolve (ec
);
1138 unwrap
= Unwrap
.Create (expr
, false);
1142 underlying
= (UnaryMutator
) new UnaryMutator (Mode
, unwrap
, loc
).Resolve (ec
);
1143 if (underlying
== null)
1147 eclass
= ExprClass
.Value
;
1152 void DoEmit (EmitContext ec
, bool is_expr
)
1154 ILGenerator ig
= ec
.ig
;
1155 Label is_null_label
= ig
.DefineLabel ();
1156 Label end_label
= ig
.DefineLabel ();
1158 unwrap
.EmitCheck (ec
);
1159 ig
.Emit (OpCodes
.Brfalse
, is_null_label
);
1162 underlying
.Emit (ec
);
1163 ig
.Emit (OpCodes
.Br_S
, end_label
);
1165 underlying
.EmitStatement (ec
);
1168 ig
.MarkLabel (is_null_label
);
1170 LiftedNull
.Create (type
, loc
).Emit (ec
);
1172 ig
.MarkLabel (end_label
);
1175 public override void Emit (EmitContext ec
)
1180 public override void EmitStatement (EmitContext ec
)