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 (TypeSpec 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);
49 static class NullableInfo
51 public static MethodSpec
GetConstructor (TypeSpec nullableType
)
53 return TypeManager
.GetPredefinedConstructor (nullableType
, Location
.Null
, GetUnderlyingType (nullableType
));
56 public static MethodSpec
GetHasValue (TypeSpec nullableType
)
58 return (MethodSpec
) MemberCache
.FindMember (nullableType
,
59 MemberFilter
.Method ("get_HasValue", 0, ParametersCompiled
.EmptyReadOnlyParameters
, null), BindingRestriction
.None
);
62 public static MethodSpec
GetGetValueOrDefault (TypeSpec nullableType
)
64 return (MethodSpec
) MemberCache
.FindMember (nullableType
,
65 MemberFilter
.Method ("GetValueOrDefault", 0, ParametersCompiled
.EmptyReadOnlyParameters
, null), BindingRestriction
.None
);
68 public static MethodSpec
GetValue (TypeSpec nullableType
)
70 return (MethodSpec
) MemberCache
.FindMember (nullableType
,
71 MemberFilter
.Method ("get_Value", 0, ParametersCompiled
.EmptyReadOnlyParameters
, null), BindingRestriction
.None
);
74 public static TypeSpec
GetUnderlyingType (TypeSpec nullableType
)
76 return ((InflatedTypeSpec
) nullableType
).TypeArguments
[0];
79 public static bool IsNullableType (TypeSpec type
)
81 throw new NotImplementedException ("net");
85 public class Unwrap
: Expression
, IMemoryLocation
, IAssignMethod
90 readonly bool useDefaultValue
;
92 Unwrap (Expression expr
, bool useDefaultValue
)
95 this.loc
= expr
.Location
;
96 this.useDefaultValue
= useDefaultValue
;
98 type
= NullableInfo
.GetUnderlyingType (expr
.Type
);
102 public static Expression
Create (Expression expr
)
105 // Avoid unwraping and wraping of same type
107 Wrap wrap
= expr
as Wrap
;
111 return Create (expr
, false);
114 public static Unwrap
Create (Expression expr
, bool useDefaultValue
)
116 return new Unwrap (expr
, useDefaultValue
);
119 public override Expression
CreateExpressionTree (ResolveContext ec
)
121 return expr
.CreateExpressionTree (ec
);
124 protected override Expression
DoResolve (ResolveContext ec
)
129 public override Expression
DoResolveLValue (ResolveContext ec
, Expression right_side
)
131 expr
= expr
.DoResolveLValue (ec
, right_side
);
135 public override void Emit (EmitContext ec
)
139 Invocation
.EmitCall (ec
, false, this, NullableInfo
.GetGetValueOrDefault (expr
.Type
), null, loc
);
141 Invocation
.EmitCall (ec
, false, this, NullableInfo
.GetValue (expr
.Type
), null, loc
);
144 public void EmitCheck (EmitContext ec
)
147 Invocation
.EmitCall (ec
, false, this, NullableInfo
.GetHasValue (expr
.Type
), null, loc
);
150 public override bool Equals (object obj
)
152 Unwrap uw
= obj
as Unwrap
;
153 return uw
!= null && expr
.Equals (uw
.expr
);
156 public Expression Original
{
162 public override int GetHashCode ()
164 return expr
.GetHashCode ();
167 public override bool IsNull
{
173 void Store (EmitContext ec
)
175 if (expr
is VariableReference
)
182 LocalVariable
.Store (ec
);
185 public void Load (EmitContext ec
)
187 if (expr
is VariableReference
)
190 LocalVariable
.Emit (ec
);
193 public override System
.Linq
.Expressions
.Expression
MakeExpression (BuilderContext ctx
)
195 return expr
.MakeExpression (ctx
);
198 public void AddressOf (EmitContext ec
, AddressOp mode
)
200 IMemoryLocation ml
= expr
as VariableReference
;
202 ml
.AddressOf (ec
, mode
);
204 LocalVariable
.AddressOf (ec
, mode
);
208 // Keeps result of non-variable expression
210 LocalTemporary LocalVariable
{
213 temp
= new LocalTemporary (expr
.Type
);
218 public void Emit (EmitContext ec
, bool leave_copy
)
226 public void EmitAssign (EmitContext ec
, Expression source
,
227 bool leave_copy
, bool prepare_for_load
)
229 InternalWrap wrap
= new InternalWrap (source
, expr
.Type
, loc
);
230 ((IAssignMethod
) expr
).EmitAssign (ec
, wrap
, leave_copy
, false);
233 class InternalWrap
: Expression
235 public Expression expr
;
237 public InternalWrap (Expression expr
, TypeSpec type
, Location loc
)
243 eclass
= ExprClass
.Value
;
246 public override Expression
CreateExpressionTree (ResolveContext ec
)
248 throw new NotSupportedException ("ET");
251 protected override Expression
DoResolve (ResolveContext ec
)
256 public override void Emit (EmitContext ec
)
259 ec
.Emit (OpCodes
.Newobj
, NullableInfo
.GetConstructor (type
));
264 public class Wrap
: TypeCast
266 private Wrap (Expression expr
, TypeSpec type
)
269 eclass
= ExprClass
.Value
;
272 public override Expression
CreateExpressionTree (ResolveContext ec
)
274 TypeCast child_cast
= child
as TypeCast
;
275 if (child_cast
!= null) {
277 return child_cast
.CreateExpressionTree (ec
);
280 return base.CreateExpressionTree (ec
);
283 public static Expression
Create (Expression expr
, TypeSpec type
)
286 // Avoid unwraping and wraping of the same type
288 Unwrap unwrap
= expr
as Unwrap
;
289 if (unwrap
!= null && TypeManager
.IsEqual (expr
.Type
, NullableInfo
.GetUnderlyingType (type
)))
290 return unwrap
.Original
;
292 return new Wrap (expr
, type
);
295 public override void Emit (EmitContext ec
)
298 ec
.Emit (OpCodes
.Newobj
, NullableInfo
.GetConstructor (type
));
303 // Represents null literal lifted to nullable type
305 public class LiftedNull
: NullConstant
, IMemoryLocation
307 private LiftedNull (TypeSpec nullable_type
, Location loc
)
308 : base (nullable_type
, loc
)
310 eclass
= ExprClass
.Value
;
313 public static Constant
Create (TypeSpec nullable
, Location loc
)
315 return new LiftedNull (nullable
, loc
);
318 public static Expression
CreateFromExpression (ResolveContext ec
, Expression e
)
320 ec
.Report
.Warning (458, 2, e
.Location
, "The result of the expression is always `null' of type `{0}'",
321 TypeManager
.CSharpName (e
.Type
));
323 return ReducedExpression
.Create (Create (e
.Type
, e
.Location
), e
);
326 public override void Emit (EmitContext ec
)
328 // TODO: generate less temporary variables
329 LocalTemporary value_target
= new LocalTemporary (type
);
331 value_target
.AddressOf (ec
, AddressOp
.Store
);
332 ec
.Emit (OpCodes
.Initobj
, type
);
333 value_target
.Emit (ec
);
336 public void AddressOf (EmitContext ec
, AddressOp Mode
)
338 LocalTemporary value_target
= new LocalTemporary (type
);
340 value_target
.AddressOf (ec
, AddressOp
.Store
);
341 ec
.Emit (OpCodes
.Initobj
, type
);
342 ((IMemoryLocation
) value_target
).AddressOf (ec
, Mode
);
347 // Generic lifting expression, supports all S/S? -> T/T? cases
349 public class Lifted
: Expression
, IMemoryLocation
351 Expression expr
, null_value
;
354 public Lifted (Expression expr
, Unwrap unwrap
, TypeSpec type
)
357 this.unwrap
= unwrap
;
358 this.loc
= expr
.Location
;
362 public Lifted (Expression expr
, Expression unwrap
, TypeSpec type
)
363 : this (expr
, unwrap
as Unwrap
, type
)
367 public override Expression
CreateExpressionTree (ResolveContext ec
)
369 return expr
.CreateExpressionTree (ec
);
372 protected override Expression
DoResolve (ResolveContext ec
)
375 // It's null when lifting non-nullable type
377 if (unwrap
== null) {
378 // S -> T? is wrap only
379 if (TypeManager
.IsNullableType (type
))
380 return Wrap
.Create (expr
, type
);
382 // S -> T can be simplified
386 // Wrap target for T?
387 if (TypeManager
.IsNullableType (type
)) {
388 expr
= Wrap
.Create (expr
, type
);
392 null_value
= LiftedNull
.Create (type
, loc
);
393 } else if (TypeManager
.IsValueType (type
)) {
394 null_value
= LiftedNull
.Create (type
, loc
);
396 null_value
= new NullConstant (type
, loc
);
399 eclass
= ExprClass
.Value
;
403 public override void Emit (EmitContext ec
)
405 Label is_null_label
= ec
.DefineLabel ();
406 Label end_label
= ec
.DefineLabel ();
408 unwrap
.EmitCheck (ec
);
409 ec
.Emit (OpCodes
.Brfalse
, is_null_label
);
413 ec
.Emit (OpCodes
.Br
, end_label
);
414 ec
.MarkLabel (is_null_label
);
416 null_value
.Emit (ec
);
417 ec
.MarkLabel (end_label
);
420 public void AddressOf (EmitContext ec
, AddressOp mode
)
422 unwrap
.AddressOf (ec
, mode
);
426 public class LiftedUnaryOperator
: Unary
, IMemoryLocation
429 Expression user_operator
;
431 public LiftedUnaryOperator (Unary
.Operator op
, Expression expr
, Location loc
)
432 : base (op
, expr
, loc
)
436 public void AddressOf (EmitContext ec
, AddressOp mode
)
438 unwrap
.AddressOf (ec
, mode
);
441 public override Expression
CreateExpressionTree (ResolveContext ec
)
443 if (user_operator
!= null)
444 return user_operator
.CreateExpressionTree (ec
);
446 if (Oper
== Operator
.UnaryPlus
)
447 return Expr
.CreateExpressionTree (ec
);
449 return base.CreateExpressionTree (ec
);
452 protected override Expression
DoResolve (ResolveContext ec
)
454 unwrap
= Unwrap
.Create (Expr
, false);
458 Expression res
= base.ResolveOperator (ec
, unwrap
);
460 if (user_operator
== null)
463 res
= Expr
= LiftExpression (ec
, Expr
);
469 eclass
= ExprClass
.Value
;
474 public override void Emit (EmitContext ec
)
476 Label is_null_label
= ec
.DefineLabel ();
477 Label end_label
= ec
.DefineLabel ();
479 unwrap
.EmitCheck (ec
);
480 ec
.Emit (OpCodes
.Brfalse
, is_null_label
);
482 if (user_operator
!= null) {
483 user_operator
.Emit (ec
);
485 EmitOperator (ec
, NullableInfo
.GetUnderlyingType (type
));
488 ec
.Emit (OpCodes
.Newobj
, NullableInfo
.GetConstructor (type
));
489 ec
.Emit (OpCodes
.Br_S
, end_label
);
491 ec
.MarkLabel (is_null_label
);
492 LiftedNull
.Create (type
, loc
).Emit (ec
);
494 ec
.MarkLabel (end_label
);
497 Expression
LiftExpression (ResolveContext ec
, Expression expr
)
499 TypeExpr lifted_type
= new NullableType (expr
.Type
, expr
.Location
);
500 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
501 if (lifted_type
== null)
504 expr
.Type
= lifted_type
.Type
;
508 protected override Expression
ResolveEnumOperator (ResolveContext ec
, Expression expr
)
510 expr
= base.ResolveEnumOperator (ec
, expr
);
514 Expr
= LiftExpression (ec
, Expr
);
515 return LiftExpression (ec
, expr
);
518 protected override Expression
ResolveUserOperator (ResolveContext ec
, Expression expr
)
520 expr
= base.ResolveUserOperator (ec
, expr
);
525 // When a user operator is of non-nullable type
527 if (Expr
is Unwrap
) {
528 user_operator
= LiftExpression (ec
, expr
);
529 return user_operator
;
536 public class LiftedBinaryOperator
: Binary
538 Unwrap left_unwrap
, right_unwrap
;
539 bool left_null_lifted
, right_null_lifted
;
540 Expression left_orig
, right_orig
;
541 Expression user_operator
;
542 MethodSpec wrap_ctor
;
544 public LiftedBinaryOperator (Binary
.Operator op
, Expression left
, Expression right
, Location loc
)
545 : base (op
, left
, right
, loc
)
549 public override Expression
CreateExpressionTree (ResolveContext ec
)
551 if (user_operator
!= null)
552 return user_operator
.CreateExpressionTree (ec
);
554 return base.CreateExpressionTree (ec
);
558 // CSC 2 has this behavior, it allows structs to be compared
559 // with the null literal *outside* of a generics context and
560 // inlines that as true or false.
562 Expression
CreateNullConstant (ResolveContext ec
, Expression expr
)
564 // FIXME: Handle side effect constants
565 Constant c
= new BoolConstant (Oper
== Operator
.Inequality
, loc
).Resolve (ec
);
567 if ((Oper
& Operator
.EqualityMask
) != 0) {
568 ec
.Report
.Warning (472, 2, loc
, "The result of comparing value type `{0}' with null is `{1}'",
569 TypeManager
.CSharpName (expr
.Type
), c
.AsString ());
571 ec
.Report
.Warning (464, 2, loc
, "The result of comparing type `{0}' with null is always `{1}'",
572 TypeManager
.CSharpName (expr
.Type
), c
.AsString ());
575 return ReducedExpression
.Create (c
, this);
578 protected override Expression
DoResolve (ResolveContext ec
)
580 if ((Oper
& Operator
.LogicalMask
) != 0) {
581 Error_OperatorCannotBeApplied (ec
, left
, right
);
585 bool use_default_call
= (Oper
& (Operator
.BitwiseMask
| Operator
.EqualityMask
)) != 0;
587 if (TypeManager
.IsNullableType (left
.Type
)) {
588 left
= left_unwrap
= Unwrap
.Create (left
, use_default_call
);
594 if (TypeManager
.IsNullableType (right
.Type
)) {
595 right
= right_unwrap
= Unwrap
.Create (right
, use_default_call
);
601 // Some details are in 6.4.2, 7.2.7
602 // Arguments can be lifted for equal operators when the return type is bool and both
603 // arguments are of same type
605 if (left_orig
.IsNull
) {
607 left_null_lifted
= true;
608 type
= TypeManager
.bool_type
;
611 if (right_orig
.IsNull
) {
613 right_null_lifted
= true;
614 type
= TypeManager
.bool_type
;
617 eclass
= ExprClass
.Value
;
618 return DoResolveCore (ec
, left_orig
, right_orig
);
621 void EmitBitwiseBoolean (EmitContext ec
)
623 Label load_left
= ec
.DefineLabel ();
624 Label load_right
= ec
.DefineLabel ();
625 Label end_label
= ec
.DefineLabel ();
627 left_unwrap
.Emit (ec
);
628 ec
.Emit (OpCodes
.Brtrue_S
, load_right
);
630 right_unwrap
.Emit (ec
);
631 ec
.Emit (OpCodes
.Brtrue_S
, load_left
);
633 left_unwrap
.EmitCheck (ec
);
634 ec
.Emit (OpCodes
.Brfalse_S
, load_right
);
637 ec
.MarkLabel (load_left
);
639 if (Oper
== Operator
.BitwiseAnd
) {
640 left_unwrap
.Load (ec
);
642 right_unwrap
.Load (ec
);
643 right_unwrap
= left_unwrap
;
645 ec
.Emit (OpCodes
.Br_S
, end_label
);
648 ec
.MarkLabel (load_right
);
649 right_unwrap
.Load (ec
);
651 ec
.MarkLabel (end_label
);
655 // Emits optimized equality or inequality operator when possible
657 void EmitEquality (EmitContext ec
)
660 // Either left or right is null
662 if (left_unwrap
!= null && (right_null_lifted
|| right
.IsNull
)) {
663 left_unwrap
.EmitCheck (ec
);
664 if (Oper
== Binary
.Operator
.Equality
) {
665 ec
.Emit (OpCodes
.Ldc_I4_0
);
666 ec
.Emit (OpCodes
.Ceq
);
671 if (right_unwrap
!= null && (left_null_lifted
|| left
.IsNull
)) {
672 right_unwrap
.EmitCheck (ec
);
673 if (Oper
== Binary
.Operator
.Equality
) {
674 ec
.Emit (OpCodes
.Ldc_I4_0
);
675 ec
.Emit (OpCodes
.Ceq
);
680 Label dissimilar_label
= ec
.DefineLabel ();
681 Label end_label
= ec
.DefineLabel ();
683 if (user_operator
!= null) {
684 user_operator
.Emit (ec
);
685 ec
.Emit (Oper
== Operator
.Equality
? OpCodes
.Brfalse_S
: OpCodes
.Brtrue_S
, dissimilar_label
);
690 ec
.Emit (OpCodes
.Bne_Un_S
, dissimilar_label
);
693 if (left_unwrap
!= null)
694 left_unwrap
.EmitCheck (ec
);
696 if (right_unwrap
!= null)
697 right_unwrap
.EmitCheck (ec
);
699 if (left_unwrap
!= null && right_unwrap
!= null) {
700 if (Oper
== Operator
.Inequality
)
701 ec
.Emit (OpCodes
.Xor
);
703 ec
.Emit (OpCodes
.Ceq
);
705 if (Oper
== Operator
.Inequality
) {
706 ec
.Emit (OpCodes
.Ldc_I4_0
);
707 ec
.Emit (OpCodes
.Ceq
);
711 ec
.Emit (OpCodes
.Br_S
, end_label
);
713 ec
.MarkLabel (dissimilar_label
);
714 if (Oper
== Operator
.Inequality
)
715 ec
.Emit (OpCodes
.Ldc_I4_1
);
717 ec
.Emit (OpCodes
.Ldc_I4_0
);
719 ec
.MarkLabel (end_label
);
722 public override void EmitBranchable (EmitContext ec
, Label target
, bool onTrue
)
725 ec
.Emit (onTrue
? OpCodes
.Brtrue
: OpCodes
.Brfalse
, target
);
728 public override void Emit (EmitContext ec
)
731 // Optimize same expression operation
733 if (right_unwrap
!= null && right
.Equals (left
))
734 right_unwrap
= left_unwrap
;
736 if (user_operator
== null && IsBitwiseBoolean
) {
737 EmitBitwiseBoolean (ec
);
741 if ((Oper
& Operator
.EqualityMask
) != 0) {
746 Label is_null_label
= ec
.DefineLabel ();
747 Label end_label
= ec
.DefineLabel ();
749 if (left_unwrap
!= null) {
750 left_unwrap
.EmitCheck (ec
);
751 ec
.Emit (OpCodes
.Brfalse
, is_null_label
);
755 // Don't emit HasValue check when left and right expressions are same
757 if (right_unwrap
!= null && !left
.Equals (right
)) {
758 right_unwrap
.EmitCheck (ec
);
759 ec
.Emit (OpCodes
.Brfalse
, is_null_label
);
762 EmitOperator (ec
, left
.Type
);
764 if (wrap_ctor
!= null)
765 ec
.Emit (OpCodes
.Newobj
, wrap_ctor
);
767 ec
.Emit (OpCodes
.Br_S
, end_label
);
768 ec
.MarkLabel (is_null_label
);
770 if ((Oper
& Operator
.ComparisonMask
) != 0) {
771 ec
.Emit (OpCodes
.Ldc_I4_0
);
773 LiftedNull
.Create (type
, loc
).Emit (ec
);
776 ec
.MarkLabel (end_label
);
779 protected override void EmitOperator (EmitContext ec
, TypeSpec l
)
781 if (user_operator
!= null) {
782 user_operator
.Emit (ec
);
786 if (TypeManager
.IsNullableType (l
))
787 l
= TypeManager
.GetTypeArguments (l
) [0];
789 base.EmitOperator (ec
, l
);
792 bool IsBitwiseBoolean
{
794 return (Oper
& Operator
.BitwiseMask
) != 0 && left_unwrap
!= null && right_unwrap
!= null &&
795 left_unwrap
.Type
== TypeManager
.bool_type
&& right_unwrap
.Type
== TypeManager
.bool_type
;
799 Expression
LiftResult (ResolveContext ec
, Expression res_expr
)
801 TypeExpr lifted_type
;
804 // Avoid double conversion
806 if (left_unwrap
== null || left_null_lifted
|| !TypeManager
.IsEqual (left_unwrap
.Type
, left
.Type
) || (left_unwrap
!= null && right_null_lifted
)) {
807 lifted_type
= new NullableType (left
.Type
, loc
);
808 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
809 if (lifted_type
== null)
812 if (left
is UserCast
|| left
is TypeCast
)
813 left
.Type
= lifted_type
.Type
;
815 left
= EmptyCast
.Create (left
, lifted_type
.Type
);
818 if (left
!= right
&& (right_unwrap
== null || right_null_lifted
|| !TypeManager
.IsEqual (right_unwrap
.Type
, right
.Type
) || (right_unwrap
!= null && left_null_lifted
))) {
819 lifted_type
= new NullableType (right
.Type
, loc
);
820 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
821 if (lifted_type
== null)
824 if (right
is UserCast
|| right
is TypeCast
)
825 right
.Type
= lifted_type
.Type
;
827 right
= EmptyCast
.Create (right
, lifted_type
.Type
);
830 if ((Oper
& Operator
.ComparisonMask
) == 0) {
831 lifted_type
= new NullableType (res_expr
.Type
, loc
);
832 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
833 if (lifted_type
== null)
836 wrap_ctor
= NullableInfo
.GetConstructor (lifted_type
.Type
);
837 type
= res_expr
.Type
= lifted_type
.Type
;
840 if (left_null_lifted
) {
841 left
= LiftedNull
.Create (right
.Type
, left
.Location
);
843 if ((Oper
& (Operator
.ArithmeticMask
| Operator
.ShiftMask
| Operator
.BitwiseMask
)) != 0)
844 return LiftedNull
.CreateFromExpression (ec
, res_expr
);
847 // Value types and null comparison
849 if (right_unwrap
== null || (Oper
& Operator
.RelationalMask
) != 0)
850 return CreateNullConstant (ec
, right_orig
).Resolve (ec
);
853 if (right_null_lifted
) {
854 right
= LiftedNull
.Create (left
.Type
, right
.Location
);
856 if ((Oper
& (Operator
.ArithmeticMask
| Operator
.ShiftMask
| Operator
.BitwiseMask
)) != 0)
857 return LiftedNull
.CreateFromExpression (ec
, res_expr
);
860 // Value types and null comparison
862 if (left_unwrap
== null || (Oper
& Operator
.RelationalMask
) != 0)
863 return CreateNullConstant (ec
, left_orig
);
869 protected override Expression
ResolveOperatorPredefined (ResolveContext ec
, Binary
.PredefinedOperator
[] operators
, bool primitives_only
, TypeSpec enum_type
)
871 Expression e
= base.ResolveOperatorPredefined (ec
, operators
, primitives_only
, enum_type
);
873 if (e
== this || enum_type
!= null)
874 return LiftResult (ec
, e
);
877 // 7.9.9 Equality operators and null
879 // The == and != operators permit one operand to be a value of a nullable type and
880 // the other to be the null literal, even if no predefined or user-defined operator
881 // (in unlifted or lifted form) exists for the operation.
883 if (e
== null && (Oper
& Operator
.EqualityMask
) != 0) {
884 if ((left_null_lifted
&& right_unwrap
!= null) || (right_null_lifted
&& left_unwrap
!= null))
885 return LiftResult (ec
, this);
891 protected override Expression
ResolveUserOperator (ResolveContext ec
, TypeSpec l
, TypeSpec r
)
893 Expression expr
= base.ResolveUserOperator (ec
, l
, r
);
897 expr
= LiftResult (ec
, expr
);
898 if (expr
is Constant
)
902 user_operator
= expr
;
907 public class NullCoalescingOperator
: Expression
909 Expression left
, right
;
912 public NullCoalescingOperator (Expression left
, Expression right
, Location loc
)
919 public override Expression
CreateExpressionTree (ResolveContext ec
)
921 if (left
.Type
== TypeManager
.null_type
)
922 ec
.Report
.Error (845, loc
, "An expression tree cannot contain a coalescing operator with null left side");
924 UserCast uc
= left
as UserCast
;
925 Expression conversion
= null;
929 Arguments c_args
= new Arguments (2);
930 c_args
.Add (new Argument (uc
.CreateExpressionTree (ec
)));
931 c_args
.Add (new Argument (left
.CreateExpressionTree (ec
)));
932 conversion
= CreateExpressionFactoryCall (ec
, "Lambda", c_args
);
935 Arguments args
= new Arguments (3);
936 args
.Add (new Argument (left
.CreateExpressionTree (ec
)));
937 args
.Add (new Argument (right
.CreateExpressionTree (ec
)));
938 if (conversion
!= null)
939 args
.Add (new Argument (conversion
));
941 return CreateExpressionFactoryCall (ec
, "Coalesce", args
);
944 Expression
ConvertExpression (ResolveContext ec
)
946 // TODO: ImplicitConversionExists should take care of this
947 if (left
.eclass
== ExprClass
.MethodGroup
)
950 TypeSpec ltype
= left
.Type
;
953 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
954 // the result is underlying type of left
956 if (TypeManager
.IsNullableType (ltype
)) {
957 unwrap
= Unwrap
.Create (left
, false);
961 if (Convert
.ImplicitConversionExists (ec
, right
, unwrap
.Type
)) {
964 right
= Convert
.ImplicitConversion (ec
, right
, type
, loc
);
967 } else if (TypeManager
.IsReferenceType (ltype
)) {
968 if (Convert
.ImplicitConversionExists (ec
, right
, ltype
)) {
970 // Reduce (constant ?? expr) to constant
972 Constant lc
= left
as Constant
;
973 if (lc
!= null && !lc
.IsDefaultValue
)
974 return new SideEffectConstant (lc
, right
, loc
).Resolve (ec
);
977 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
979 if (right
.IsNull
|| lc
!= null)
980 return ReducedExpression
.Create (lc
!= null ? right
: left
, this);
982 right
= Convert
.ImplicitConversion (ec
, right
, ltype
, loc
);
990 TypeSpec rtype
= right
.Type
;
991 if (!Convert
.ImplicitConversionExists (ec
, unwrap
!= null ? unwrap
: left
, rtype
) || right
.eclass
== ExprClass
.MethodGroup
)
995 // Reduce (null ?? right) to right
998 return ReducedExpression
.Create (right
, this);
1000 left
= Convert
.ImplicitConversion (ec
, unwrap
!= null ? unwrap
: left
, rtype
, loc
);
1005 protected override Expression
DoResolve (ResolveContext ec
)
1007 left
= left
.Resolve (ec
);
1008 right
= right
.Resolve (ec
);
1010 if (left
== null || right
== null)
1013 eclass
= ExprClass
.Value
;
1015 Expression e
= ConvertExpression (ec
);
1017 Binary
.Error_OperatorCannotBeApplied (ec
, left
, right
, "??", loc
);
1024 public override void Emit (EmitContext ec
)
1026 Label end_label
= ec
.DefineLabel ();
1028 if (unwrap
!= null) {
1029 Label is_null_label
= ec
.DefineLabel ();
1031 unwrap
.EmitCheck (ec
);
1032 ec
.Emit (OpCodes
.Brfalse
, is_null_label
);
1035 ec
.Emit (OpCodes
.Br
, end_label
);
1037 ec
.MarkLabel (is_null_label
);
1040 ec
.MarkLabel (end_label
);
1046 ec
.Emit (OpCodes
.Dup
);
1047 ec
.Emit (OpCodes
.Brtrue
, end_label
);
1049 ec
.Emit (OpCodes
.Pop
);
1052 ec
.MarkLabel (end_label
);
1055 protected override void CloneTo (CloneContext clonectx
, Expression t
)
1057 NullCoalescingOperator target
= (NullCoalescingOperator
) t
;
1059 target
.left
= left
.Clone (clonectx
);
1060 target
.right
= right
.Clone (clonectx
);
1064 public class LiftedUnaryMutator
: ExpressionStatement
1066 public readonly UnaryMutator
.Mode Mode
;
1068 UnaryMutator underlying
;
1071 public LiftedUnaryMutator (UnaryMutator
.Mode mode
, Expression expr
, Location loc
)
1078 public override Expression
CreateExpressionTree (ResolveContext ec
)
1080 return new SimpleAssign (this, this).CreateExpressionTree (ec
);
1083 protected override Expression
DoResolve (ResolveContext ec
)
1085 expr
= expr
.Resolve (ec
);
1089 unwrap
= Unwrap
.Create (expr
, false);
1093 underlying
= (UnaryMutator
) new UnaryMutator (Mode
, unwrap
, loc
).Resolve (ec
);
1094 if (underlying
== null)
1098 eclass
= ExprClass
.Value
;
1103 void DoEmit (EmitContext ec
, bool is_expr
)
1105 Label is_null_label
= ec
.DefineLabel ();
1106 Label end_label
= ec
.DefineLabel ();
1108 unwrap
.EmitCheck (ec
);
1109 ec
.Emit (OpCodes
.Brfalse
, is_null_label
);
1112 underlying
.Emit (ec
);
1113 ec
.Emit (OpCodes
.Br_S
, end_label
);
1115 underlying
.EmitStatement (ec
);
1118 ec
.MarkLabel (is_null_label
);
1120 LiftedNull
.Create (type
, loc
).Emit (ec
);
1122 ec
.MarkLabel (end_label
);
1125 public override void Emit (EmitContext ec
)
1130 public override void EmitStatement (EmitContext ec
)