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 return DoResolve (ec
);
134 public override void Emit (EmitContext ec
)
138 Invocation
.EmitCall (ec
, false, this, NullableInfo
.GetGetValueOrDefault (expr
.Type
), null, loc
);
140 Invocation
.EmitCall (ec
, false, this, NullableInfo
.GetValue (expr
.Type
), null, loc
);
143 public void EmitCheck (EmitContext ec
)
146 Invocation
.EmitCall (ec
, false, this, NullableInfo
.GetHasValue (expr
.Type
), null, loc
);
149 public override bool Equals (object obj
)
151 Unwrap uw
= obj
as Unwrap
;
152 return uw
!= null && expr
.Equals (uw
.expr
);
155 public Expression Original
{
161 public override int GetHashCode ()
163 return expr
.GetHashCode ();
166 public override bool IsNull
{
172 void Store (EmitContext ec
)
174 if (expr
is VariableReference
)
181 LocalVariable
.Store (ec
);
184 public void Load (EmitContext ec
)
186 if (expr
is VariableReference
)
189 LocalVariable
.Emit (ec
);
192 public override System
.Linq
.Expressions
.Expression
MakeExpression (BuilderContext ctx
)
194 return expr
.MakeExpression (ctx
);
197 public void AddressOf (EmitContext ec
, AddressOp mode
)
199 IMemoryLocation ml
= expr
as VariableReference
;
201 ml
.AddressOf (ec
, mode
);
203 LocalVariable
.AddressOf (ec
, mode
);
207 // Keeps result of non-variable expression
209 LocalTemporary LocalVariable
{
212 temp
= new LocalTemporary (expr
.Type
);
217 public void Emit (EmitContext ec
, bool leave_copy
)
225 public void EmitAssign (EmitContext ec
, Expression source
,
226 bool leave_copy
, bool prepare_for_load
)
228 InternalWrap wrap
= new InternalWrap (source
, expr
.Type
, loc
);
229 ((IAssignMethod
) expr
).EmitAssign (ec
, wrap
, leave_copy
, false);
232 class InternalWrap
: Expression
234 public Expression expr
;
236 public InternalWrap (Expression expr
, TypeSpec type
, Location loc
)
242 eclass
= ExprClass
.Value
;
245 public override Expression
CreateExpressionTree (ResolveContext ec
)
247 throw new NotSupportedException ("ET");
250 protected override Expression
DoResolve (ResolveContext ec
)
255 public override void Emit (EmitContext ec
)
258 ec
.Emit (OpCodes
.Newobj
, NullableInfo
.GetConstructor (type
));
263 public class Wrap
: TypeCast
265 private Wrap (Expression expr
, TypeSpec type
)
268 eclass
= ExprClass
.Value
;
271 public override Expression
CreateExpressionTree (ResolveContext ec
)
273 TypeCast child_cast
= child
as TypeCast
;
274 if (child_cast
!= null) {
276 return child_cast
.CreateExpressionTree (ec
);
279 return base.CreateExpressionTree (ec
);
282 public static Expression
Create (Expression expr
, TypeSpec type
)
285 // Avoid unwraping and wraping of the same type
287 Unwrap unwrap
= expr
as Unwrap
;
288 if (unwrap
!= null && TypeManager
.IsEqual (expr
.Type
, NullableInfo
.GetUnderlyingType (type
)))
289 return unwrap
.Original
;
291 return new Wrap (expr
, type
);
294 public override void Emit (EmitContext ec
)
297 ec
.Emit (OpCodes
.Newobj
, NullableInfo
.GetConstructor (type
));
302 // Represents null literal lifted to nullable type
304 public class LiftedNull
: NullConstant
, IMemoryLocation
306 private LiftedNull (TypeSpec nullable_type
, Location loc
)
307 : base (nullable_type
, loc
)
309 eclass
= ExprClass
.Value
;
312 public static Constant
Create (TypeSpec nullable
, Location loc
)
314 return new LiftedNull (nullable
, loc
);
317 public static Expression
CreateFromExpression (ResolveContext ec
, Expression e
)
319 ec
.Report
.Warning (458, 2, e
.Location
, "The result of the expression is always `null' of type `{0}'",
320 TypeManager
.CSharpName (e
.Type
));
322 return ReducedExpression
.Create (Create (e
.Type
, e
.Location
), e
);
325 public override void Emit (EmitContext ec
)
327 // TODO: generate less temporary variables
328 LocalTemporary value_target
= new LocalTemporary (type
);
330 value_target
.AddressOf (ec
, AddressOp
.Store
);
331 ec
.Emit (OpCodes
.Initobj
, type
);
332 value_target
.Emit (ec
);
335 public void AddressOf (EmitContext ec
, AddressOp Mode
)
337 LocalTemporary value_target
= new LocalTemporary (type
);
339 value_target
.AddressOf (ec
, AddressOp
.Store
);
340 ec
.Emit (OpCodes
.Initobj
, type
);
341 ((IMemoryLocation
) value_target
).AddressOf (ec
, Mode
);
346 // Generic lifting expression, supports all S/S? -> T/T? cases
348 public class Lifted
: Expression
, IMemoryLocation
350 Expression expr
, null_value
;
353 public Lifted (Expression expr
, Unwrap unwrap
, TypeSpec type
)
356 this.unwrap
= unwrap
;
357 this.loc
= expr
.Location
;
361 public Lifted (Expression expr
, Expression unwrap
, TypeSpec type
)
362 : this (expr
, unwrap
as Unwrap
, type
)
366 public override Expression
CreateExpressionTree (ResolveContext ec
)
368 return expr
.CreateExpressionTree (ec
);
371 protected override Expression
DoResolve (ResolveContext ec
)
374 // It's null when lifting non-nullable type
376 if (unwrap
== null) {
377 // S -> T? is wrap only
378 if (TypeManager
.IsNullableType (type
))
379 return Wrap
.Create (expr
, type
);
381 // S -> T can be simplified
385 // Wrap target for T?
386 if (TypeManager
.IsNullableType (type
)) {
387 expr
= Wrap
.Create (expr
, type
);
391 null_value
= LiftedNull
.Create (type
, loc
);
393 null_value
= new NullConstant (type
, loc
);
396 eclass
= ExprClass
.Value
;
400 public override void Emit (EmitContext ec
)
402 Label is_null_label
= ec
.DefineLabel ();
403 Label end_label
= ec
.DefineLabel ();
405 unwrap
.EmitCheck (ec
);
406 ec
.Emit (OpCodes
.Brfalse
, is_null_label
);
410 ec
.Emit (OpCodes
.Br
, end_label
);
411 ec
.MarkLabel (is_null_label
);
413 null_value
.Emit (ec
);
414 ec
.MarkLabel (end_label
);
417 public void AddressOf (EmitContext ec
, AddressOp mode
)
419 unwrap
.AddressOf (ec
, mode
);
423 public class LiftedUnaryOperator
: Unary
, IMemoryLocation
426 Expression user_operator
;
428 public LiftedUnaryOperator (Unary
.Operator op
, Expression expr
, Location loc
)
429 : base (op
, expr
, loc
)
433 public void AddressOf (EmitContext ec
, AddressOp mode
)
435 unwrap
.AddressOf (ec
, mode
);
438 public override Expression
CreateExpressionTree (ResolveContext ec
)
440 if (user_operator
!= null)
441 return user_operator
.CreateExpressionTree (ec
);
443 if (Oper
== Operator
.UnaryPlus
)
444 return Expr
.CreateExpressionTree (ec
);
446 return base.CreateExpressionTree (ec
);
449 protected override Expression
DoResolve (ResolveContext ec
)
451 unwrap
= Unwrap
.Create (Expr
, false);
455 Expression res
= base.ResolveOperator (ec
, unwrap
);
457 if (user_operator
== null)
460 res
= Expr
= LiftExpression (ec
, Expr
);
466 eclass
= ExprClass
.Value
;
471 public override void Emit (EmitContext ec
)
473 Label is_null_label
= ec
.DefineLabel ();
474 Label end_label
= ec
.DefineLabel ();
476 unwrap
.EmitCheck (ec
);
477 ec
.Emit (OpCodes
.Brfalse
, is_null_label
);
479 if (user_operator
!= null) {
480 user_operator
.Emit (ec
);
482 EmitOperator (ec
, NullableInfo
.GetUnderlyingType (type
));
485 ec
.Emit (OpCodes
.Newobj
, NullableInfo
.GetConstructor (type
));
486 ec
.Emit (OpCodes
.Br_S
, end_label
);
488 ec
.MarkLabel (is_null_label
);
489 LiftedNull
.Create (type
, loc
).Emit (ec
);
491 ec
.MarkLabel (end_label
);
494 Expression
LiftExpression (ResolveContext ec
, Expression expr
)
496 TypeExpr lifted_type
= new NullableType (expr
.Type
, expr
.Location
);
497 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
498 if (lifted_type
== null)
501 expr
.Type
= lifted_type
.Type
;
505 protected override Expression
ResolveEnumOperator (ResolveContext ec
, Expression expr
)
507 expr
= base.ResolveEnumOperator (ec
, expr
);
511 Expr
= LiftExpression (ec
, Expr
);
512 return LiftExpression (ec
, expr
);
515 protected override Expression
ResolveUserOperator (ResolveContext ec
, Expression expr
)
517 expr
= base.ResolveUserOperator (ec
, expr
);
522 // When a user operator is of non-nullable type
524 if (Expr
is Unwrap
) {
525 user_operator
= LiftExpression (ec
, expr
);
526 return user_operator
;
533 public class LiftedBinaryOperator
: Binary
535 Unwrap left_unwrap
, right_unwrap
;
536 bool left_null_lifted
, right_null_lifted
;
537 Expression left_orig
, right_orig
;
538 Expression user_operator
;
539 MethodSpec wrap_ctor
;
541 public LiftedBinaryOperator (Binary
.Operator op
, Expression left
, Expression right
, Location loc
)
542 : base (op
, left
, right
, loc
)
546 public override Expression
CreateExpressionTree (ResolveContext ec
)
548 if (user_operator
!= null)
549 return user_operator
.CreateExpressionTree (ec
);
551 return base.CreateExpressionTree (ec
);
555 // CSC 2 has this behavior, it allows structs to be compared
556 // with the null literal *outside* of a generics context and
557 // inlines that as true or false.
559 Expression
CreateNullConstant (ResolveContext ec
, Expression expr
)
561 // FIXME: Handle side effect constants
562 Constant c
= new BoolConstant (Oper
== Operator
.Inequality
, loc
).Resolve (ec
);
564 if ((Oper
& Operator
.EqualityMask
) != 0) {
565 ec
.Report
.Warning (472, 2, loc
, "The result of comparing value type `{0}' with null is `{1}'",
566 TypeManager
.CSharpName (expr
.Type
), c
.AsString ());
568 ec
.Report
.Warning (464, 2, loc
, "The result of comparing type `{0}' with null is always `{1}'",
569 TypeManager
.CSharpName (expr
.Type
), c
.AsString ());
572 return ReducedExpression
.Create (c
, this);
575 protected override Expression
DoResolve (ResolveContext ec
)
577 if ((Oper
& Operator
.LogicalMask
) != 0) {
578 Error_OperatorCannotBeApplied (ec
, left
, right
);
582 bool use_default_call
= (Oper
& (Operator
.BitwiseMask
| Operator
.EqualityMask
)) != 0;
584 if (TypeManager
.IsNullableType (left
.Type
)) {
585 left
= left_unwrap
= Unwrap
.Create (left
, use_default_call
);
591 if (TypeManager
.IsNullableType (right
.Type
)) {
592 right
= right_unwrap
= Unwrap
.Create (right
, use_default_call
);
598 // Some details are in 6.4.2, 7.2.7
599 // Arguments can be lifted for equal operators when the return type is bool and both
600 // arguments are of same type
602 if (left_orig
.IsNull
) {
604 left_null_lifted
= true;
605 type
= TypeManager
.bool_type
;
608 if (right_orig
.IsNull
) {
610 right_null_lifted
= true;
611 type
= TypeManager
.bool_type
;
614 eclass
= ExprClass
.Value
;
615 return DoResolveCore (ec
, left_orig
, right_orig
);
618 void EmitBitwiseBoolean (EmitContext ec
)
620 Label load_left
= ec
.DefineLabel ();
621 Label load_right
= ec
.DefineLabel ();
622 Label end_label
= ec
.DefineLabel ();
624 left_unwrap
.Emit (ec
);
625 ec
.Emit (OpCodes
.Brtrue_S
, load_right
);
627 right_unwrap
.Emit (ec
);
628 ec
.Emit (OpCodes
.Brtrue_S
, load_left
);
630 left_unwrap
.EmitCheck (ec
);
631 ec
.Emit (OpCodes
.Brfalse_S
, load_right
);
634 ec
.MarkLabel (load_left
);
636 if (Oper
== Operator
.BitwiseAnd
) {
637 left_unwrap
.Load (ec
);
639 right_unwrap
.Load (ec
);
640 right_unwrap
= left_unwrap
;
642 ec
.Emit (OpCodes
.Br_S
, end_label
);
645 ec
.MarkLabel (load_right
);
646 right_unwrap
.Load (ec
);
648 ec
.MarkLabel (end_label
);
652 // Emits optimized equality or inequality operator when possible
654 void EmitEquality (EmitContext ec
)
657 // Either left or right is null
659 if (left_unwrap
!= null && (right_null_lifted
|| right
.IsNull
)) {
660 left_unwrap
.EmitCheck (ec
);
661 if (Oper
== Binary
.Operator
.Equality
) {
662 ec
.Emit (OpCodes
.Ldc_I4_0
);
663 ec
.Emit (OpCodes
.Ceq
);
668 if (right_unwrap
!= null && (left_null_lifted
|| left
.IsNull
)) {
669 right_unwrap
.EmitCheck (ec
);
670 if (Oper
== Binary
.Operator
.Equality
) {
671 ec
.Emit (OpCodes
.Ldc_I4_0
);
672 ec
.Emit (OpCodes
.Ceq
);
677 Label dissimilar_label
= ec
.DefineLabel ();
678 Label end_label
= ec
.DefineLabel ();
680 if (user_operator
!= null) {
681 user_operator
.Emit (ec
);
682 ec
.Emit (Oper
== Operator
.Equality
? OpCodes
.Brfalse_S
: OpCodes
.Brtrue_S
, dissimilar_label
);
687 ec
.Emit (OpCodes
.Bne_Un_S
, dissimilar_label
);
690 if (left_unwrap
!= null)
691 left_unwrap
.EmitCheck (ec
);
693 if (right_unwrap
!= null)
694 right_unwrap
.EmitCheck (ec
);
696 if (left_unwrap
!= null && right_unwrap
!= null) {
697 if (Oper
== Operator
.Inequality
)
698 ec
.Emit (OpCodes
.Xor
);
700 ec
.Emit (OpCodes
.Ceq
);
702 if (Oper
== Operator
.Inequality
) {
703 ec
.Emit (OpCodes
.Ldc_I4_0
);
704 ec
.Emit (OpCodes
.Ceq
);
708 ec
.Emit (OpCodes
.Br_S
, end_label
);
710 ec
.MarkLabel (dissimilar_label
);
711 if (Oper
== Operator
.Inequality
)
712 ec
.Emit (OpCodes
.Ldc_I4_1
);
714 ec
.Emit (OpCodes
.Ldc_I4_0
);
716 ec
.MarkLabel (end_label
);
719 public override void EmitBranchable (EmitContext ec
, Label target
, bool onTrue
)
722 ec
.Emit (onTrue
? OpCodes
.Brtrue
: OpCodes
.Brfalse
, target
);
725 public override void Emit (EmitContext ec
)
728 // Optimize same expression operation
730 if (right_unwrap
!= null && right
.Equals (left
))
731 right_unwrap
= left_unwrap
;
733 if (user_operator
== null && IsBitwiseBoolean
) {
734 EmitBitwiseBoolean (ec
);
738 if ((Oper
& Operator
.EqualityMask
) != 0) {
743 Label is_null_label
= ec
.DefineLabel ();
744 Label end_label
= ec
.DefineLabel ();
746 if (left_unwrap
!= null) {
747 left_unwrap
.EmitCheck (ec
);
748 ec
.Emit (OpCodes
.Brfalse
, is_null_label
);
752 // Don't emit HasValue check when left and right expressions are same
754 if (right_unwrap
!= null && !left
.Equals (right
)) {
755 right_unwrap
.EmitCheck (ec
);
756 ec
.Emit (OpCodes
.Brfalse
, is_null_label
);
759 EmitOperator (ec
, left
.Type
);
761 if (wrap_ctor
!= null)
762 ec
.Emit (OpCodes
.Newobj
, wrap_ctor
);
764 ec
.Emit (OpCodes
.Br_S
, end_label
);
765 ec
.MarkLabel (is_null_label
);
767 if ((Oper
& Operator
.ComparisonMask
) != 0) {
768 ec
.Emit (OpCodes
.Ldc_I4_0
);
770 LiftedNull
.Create (type
, loc
).Emit (ec
);
773 ec
.MarkLabel (end_label
);
776 protected override void EmitOperator (EmitContext ec
, TypeSpec l
)
778 if (user_operator
!= null) {
779 user_operator
.Emit (ec
);
783 if (TypeManager
.IsNullableType (l
))
784 l
= TypeManager
.GetTypeArguments (l
) [0];
786 base.EmitOperator (ec
, l
);
789 bool IsBitwiseBoolean
{
791 return (Oper
& Operator
.BitwiseMask
) != 0 && left_unwrap
!= null && right_unwrap
!= null &&
792 left_unwrap
.Type
== TypeManager
.bool_type
&& right_unwrap
.Type
== TypeManager
.bool_type
;
796 Expression
LiftResult (ResolveContext ec
, Expression res_expr
)
798 TypeExpr lifted_type
;
801 // Avoid double conversion
803 if (left_unwrap
== null || left_null_lifted
|| !TypeManager
.IsEqual (left_unwrap
.Type
, left
.Type
) || (left_unwrap
!= null && right_null_lifted
)) {
804 lifted_type
= new NullableType (left
.Type
, loc
);
805 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
806 if (lifted_type
== null)
809 if (left
is UserCast
|| left
is TypeCast
)
810 left
.Type
= lifted_type
.Type
;
812 left
= EmptyCast
.Create (left
, lifted_type
.Type
);
815 if (left
!= right
&& (right_unwrap
== null || right_null_lifted
|| !TypeManager
.IsEqual (right_unwrap
.Type
, right
.Type
) || (right_unwrap
!= null && left_null_lifted
))) {
816 lifted_type
= new NullableType (right
.Type
, loc
);
817 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
818 if (lifted_type
== null)
821 if (right
is UserCast
|| right
is TypeCast
)
822 right
.Type
= lifted_type
.Type
;
824 right
= EmptyCast
.Create (right
, lifted_type
.Type
);
827 if ((Oper
& Operator
.ComparisonMask
) == 0) {
828 lifted_type
= new NullableType (res_expr
.Type
, loc
);
829 lifted_type
= lifted_type
.ResolveAsTypeTerminal (ec
, false);
830 if (lifted_type
== null)
833 wrap_ctor
= NullableInfo
.GetConstructor (lifted_type
.Type
);
834 type
= res_expr
.Type
= lifted_type
.Type
;
837 if (left_null_lifted
) {
838 left
= LiftedNull
.Create (right
.Type
, left
.Location
);
840 if ((Oper
& (Operator
.ArithmeticMask
| Operator
.ShiftMask
| Operator
.BitwiseMask
)) != 0)
841 return LiftedNull
.CreateFromExpression (ec
, res_expr
);
844 // Value types and null comparison
846 if (right_unwrap
== null || (Oper
& Operator
.RelationalMask
) != 0)
847 return CreateNullConstant (ec
, right_orig
).Resolve (ec
);
850 if (right_null_lifted
) {
851 right
= LiftedNull
.Create (left
.Type
, right
.Location
);
853 if ((Oper
& (Operator
.ArithmeticMask
| Operator
.ShiftMask
| Operator
.BitwiseMask
)) != 0)
854 return LiftedNull
.CreateFromExpression (ec
, res_expr
);
857 // Value types and null comparison
859 if (left_unwrap
== null || (Oper
& Operator
.RelationalMask
) != 0)
860 return CreateNullConstant (ec
, left_orig
);
866 protected override Expression
ResolveOperatorPredefined (ResolveContext ec
, Binary
.PredefinedOperator
[] operators
, bool primitives_only
, TypeSpec enum_type
)
868 Expression e
= base.ResolveOperatorPredefined (ec
, operators
, primitives_only
, enum_type
);
870 if (e
== this || enum_type
!= null)
871 return LiftResult (ec
, e
);
874 // 7.9.9 Equality operators and null
876 // The == and != operators permit one operand to be a value of a nullable type and
877 // the other to be the null literal, even if no predefined or user-defined operator
878 // (in unlifted or lifted form) exists for the operation.
880 if (e
== null && (Oper
& Operator
.EqualityMask
) != 0) {
881 if ((left_null_lifted
&& right_unwrap
!= null) || (right_null_lifted
&& left_unwrap
!= null))
882 return LiftResult (ec
, this);
888 protected override Expression
ResolveUserOperator (ResolveContext ec
, TypeSpec l
, TypeSpec r
)
890 Expression expr
= base.ResolveUserOperator (ec
, l
, r
);
894 expr
= LiftResult (ec
, expr
);
895 if (expr
is Constant
)
899 user_operator
= expr
;
904 public class NullCoalescingOperator
: Expression
906 Expression left
, right
;
909 public NullCoalescingOperator (Expression left
, Expression right
, Location loc
)
916 public override Expression
CreateExpressionTree (ResolveContext ec
)
918 if (left
.Type
== TypeManager
.null_type
)
919 ec
.Report
.Error (845, loc
, "An expression tree cannot contain a coalescing operator with null left side");
921 UserCast uc
= left
as UserCast
;
922 Expression conversion
= null;
926 Arguments c_args
= new Arguments (2);
927 c_args
.Add (new Argument (uc
.CreateExpressionTree (ec
)));
928 c_args
.Add (new Argument (left
.CreateExpressionTree (ec
)));
929 conversion
= CreateExpressionFactoryCall (ec
, "Lambda", c_args
);
932 Arguments args
= new Arguments (3);
933 args
.Add (new Argument (left
.CreateExpressionTree (ec
)));
934 args
.Add (new Argument (right
.CreateExpressionTree (ec
)));
935 if (conversion
!= null)
936 args
.Add (new Argument (conversion
));
938 return CreateExpressionFactoryCall (ec
, "Coalesce", args
);
941 Expression
ConvertExpression (ResolveContext ec
)
943 // TODO: ImplicitConversionExists should take care of this
944 if (left
.eclass
== ExprClass
.MethodGroup
)
947 TypeSpec ltype
= left
.Type
;
950 // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
951 // the result is underlying type of left
953 if (TypeManager
.IsNullableType (ltype
)) {
954 unwrap
= Unwrap
.Create (left
, false);
958 if (Convert
.ImplicitConversionExists (ec
, right
, unwrap
.Type
)) {
961 right
= Convert
.ImplicitConversion (ec
, right
, type
, loc
);
964 } else if (TypeManager
.IsReferenceType (ltype
)) {
965 if (Convert
.ImplicitConversionExists (ec
, right
, ltype
)) {
967 // Reduce (constant ?? expr) to constant
969 Constant lc
= left
as Constant
;
970 if (lc
!= null && !lc
.IsDefaultValue
)
971 return new SideEffectConstant (lc
, right
, loc
).Resolve (ec
);
974 // Reduce (left ?? null) to left OR (null-constant ?? right) to right
976 if (right
.IsNull
|| lc
!= null)
977 return ReducedExpression
.Create (lc
!= null ? right
: left
, this);
979 right
= Convert
.ImplicitConversion (ec
, right
, ltype
, loc
);
987 TypeSpec rtype
= right
.Type
;
988 if (!Convert
.ImplicitConversionExists (ec
, unwrap
!= null ? unwrap
: left
, rtype
) || right
.eclass
== ExprClass
.MethodGroup
)
992 // Reduce (null ?? right) to right
995 return ReducedExpression
.Create (right
, this);
997 left
= Convert
.ImplicitConversion (ec
, unwrap
!= null ? unwrap
: left
, rtype
, loc
);
1002 protected override Expression
DoResolve (ResolveContext ec
)
1004 left
= left
.Resolve (ec
);
1005 right
= right
.Resolve (ec
);
1007 if (left
== null || right
== null)
1010 eclass
= ExprClass
.Value
;
1012 Expression e
= ConvertExpression (ec
);
1014 Binary
.Error_OperatorCannotBeApplied (ec
, left
, right
, "??", loc
);
1021 public override void Emit (EmitContext ec
)
1023 Label end_label
= ec
.DefineLabel ();
1025 if (unwrap
!= null) {
1026 Label is_null_label
= ec
.DefineLabel ();
1028 unwrap
.EmitCheck (ec
);
1029 ec
.Emit (OpCodes
.Brfalse
, is_null_label
);
1032 ec
.Emit (OpCodes
.Br
, end_label
);
1034 ec
.MarkLabel (is_null_label
);
1037 ec
.MarkLabel (end_label
);
1043 ec
.Emit (OpCodes
.Dup
);
1044 ec
.Emit (OpCodes
.Brtrue
, end_label
);
1046 ec
.Emit (OpCodes
.Pop
);
1049 ec
.MarkLabel (end_label
);
1052 protected override void CloneTo (CloneContext clonectx
, Expression t
)
1054 NullCoalescingOperator target
= (NullCoalescingOperator
) t
;
1056 target
.left
= left
.Clone (clonectx
);
1057 target
.right
= right
.Clone (clonectx
);
1061 public class LiftedUnaryMutator
: ExpressionStatement
1063 public readonly UnaryMutator
.Mode Mode
;
1065 UnaryMutator underlying
;
1068 public LiftedUnaryMutator (UnaryMutator
.Mode mode
, Expression expr
, Location loc
)
1075 public override Expression
CreateExpressionTree (ResolveContext ec
)
1077 return new SimpleAssign (this, this).CreateExpressionTree (ec
);
1080 protected override Expression
DoResolve (ResolveContext ec
)
1082 expr
= expr
.Resolve (ec
);
1086 unwrap
= Unwrap
.Create (expr
, false);
1090 underlying
= (UnaryMutator
) new UnaryMutator (Mode
, unwrap
, loc
).Resolve (ec
);
1091 if (underlying
== null)
1095 eclass
= ExprClass
.Value
;
1100 void DoEmit (EmitContext ec
, bool is_expr
)
1102 Label is_null_label
= ec
.DefineLabel ();
1103 Label end_label
= ec
.DefineLabel ();
1105 unwrap
.EmitCheck (ec
);
1106 ec
.Emit (OpCodes
.Brfalse
, is_null_label
);
1109 underlying
.Emit (ec
);
1110 ec
.Emit (OpCodes
.Br_S
, end_label
);
1112 underlying
.EmitStatement (ec
);
1115 ec
.MarkLabel (is_null_label
);
1117 LiftedNull
.Create (type
, loc
).Emit (ec
);
1119 ec
.MarkLabel (end_label
);
1122 public override void Emit (EmitContext ec
)
1127 public override void EmitStatement (EmitContext ec
)