61892ae1bce86a855c91022aa576cadfc5406cd3
[mono.git] / mcs / mcs / nullable.cs
1 //
2 // nullable.cs: Nullable types support
3 //
4 // Authors: Martin Baulig (martin@ximian.com)
5 //          Miguel de Icaza (miguel@ximian.com)
6 //          Marek Safar (marek.safar@gmail.com)
7 //
8 // Licensed under the terms of the GNU GPL
9 //
10 // (C) 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
11 // (C) 2004 Novell, Inc
12 //
13
14 using System;
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Collections;
18         
19 namespace Mono.CSharp {
20
21         public class NullableType : TypeExpr
22         {
23                 Expression underlying;
24
25                 public NullableType (Expression underlying, Location l)
26                 {
27                         this.underlying = underlying;
28                         loc = l;
29
30                         eclass = ExprClass.Type;
31                 }
32
33                 public NullableType (Type type, Location loc)
34                         : this (new TypeExpression (type, loc), loc)
35                 { }
36
37                 public override string Name {
38                         get { return underlying.ToString () + "?"; }
39                 }
40
41                 public override string FullName {
42                         get { return underlying.ToString () + "?"; }
43                 }
44
45                 protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec)
46                 {
47                         TypeArguments args = new TypeArguments (loc);
48                         args.Add (underlying);
49
50                         if (TypeManager.generic_nullable_type == null) {
51                                 TypeManager.generic_nullable_type = TypeManager.CoreLookupType (
52                                         "System", "Nullable`1", Kind.Struct, true);
53                         }
54
55                         ConstructedType ctype = new ConstructedType (TypeManager.generic_nullable_type, args, loc);
56                         return ctype.ResolveAsTypeTerminal (ec, false);
57                 }
58         }
59
60         public abstract class Nullable
61         {
62                 public sealed class NullableInfo
63                 {
64                         public readonly Type Type;
65                         public readonly Type UnderlyingType;
66                         public readonly MethodInfo HasValue;
67                         public readonly MethodInfo Value;
68                         public readonly MethodInfo GetValueOrDefault;
69                         public readonly ConstructorInfo Constructor;
70
71                         public NullableInfo (Type type)
72                         {
73                                 Type = type;
74                                 UnderlyingType = TypeManager.GetTypeArguments (type) [0];
75
76                                 PropertyInfo has_value_pi = TypeManager.GetPredefinedProperty (type, "HasValue", Location.Null);
77                                 PropertyInfo value_pi = TypeManager.GetPredefinedProperty (type, "Value", Location.Null);
78                                 GetValueOrDefault = TypeManager.GetPredefinedMethod (type, "GetValueOrDefault", Location.Null, Type.EmptyTypes);
79
80                                 HasValue = has_value_pi.GetGetMethod (false);
81                                 Value = value_pi.GetGetMethod (false);
82 #if MS_COMPATIBLE
83                                 if (UnderlyingType.Module == CodeGen.Module.Builder) {
84                                         Type o_type = TypeManager.DropGenericTypeArguments (type);
85                                         Constructor = TypeBuilder.GetConstructor (type,
86                                                 TypeManager.GetPredefinedConstructor (o_type, Location.Null, o_type.GetGenericArguments ()));
87                                         return;
88                                 }
89 #endif
90                                 Constructor = type.GetConstructor (new Type[] { UnderlyingType });
91                         }
92                 }
93                 
94                 public class HasValue : Expression
95                 {
96                         Expression expr;
97                         NullableInfo info;
98
99                         private HasValue (Expression expr)
100                         {
101                                 this.expr = expr;
102                         }
103                         
104                         public static Expression Create (Expression expr, EmitContext ec)
105                         {
106                                 return new HasValue (expr).Resolve (ec);
107                         }
108
109                         public override void Emit (EmitContext ec)
110                         {
111                                 IMemoryLocation memory_loc = expr as IMemoryLocation;
112                                 if (memory_loc == null) {
113                                         LocalTemporary temp = new LocalTemporary (expr.Type);
114                                         expr.Emit (ec);
115                                         temp.Store (ec);
116                                         memory_loc = temp;
117                                 }
118                                 memory_loc.AddressOf (ec, AddressOp.LoadStore);
119                                 ec.ig.EmitCall (OpCodes.Call, info.HasValue, null);
120                         }
121
122                         public override Expression DoResolve (EmitContext ec)
123                         {
124                                 this.info = new NullableInfo (expr.Type);
125
126                                 type = TypeManager.bool_type;
127                                 eclass = expr.eclass;
128                                 return this;
129                         }
130                 }               
131
132                 public class Unwrap : Expression, IMemoryLocation, IAssignMethod
133                 {
134                         Expression expr;
135                         NullableInfo info;
136
137                         LocalTemporary temp;
138                         bool has_temp;
139
140                         protected Unwrap (Expression expr)
141                         {
142                                 this.expr = expr;
143                                 this.loc = expr.Location;
144                         }
145
146                         public static Unwrap Create (Expression expr, EmitContext ec)
147                         {
148                                 return new Unwrap (expr).Resolve (ec) as Unwrap;
149                         }
150                         
151                         public override Expression CreateExpressionTree (EmitContext ec)
152                         {
153                                 return expr.CreateExpressionTree (ec);
154                         }                       
155
156                         public override Expression DoResolve (EmitContext ec)
157                         {
158                                 if (expr == null)
159                                         return null;
160
161                                 temp = new LocalTemporary (expr.Type);
162
163                                 info = new NullableInfo (expr.Type);
164                                 type = info.UnderlyingType;
165                                 eclass = expr.eclass;
166                                 return this;
167                         }
168                         
169                         public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
170                         {
171                                 return DoResolve (ec);
172                         }                       
173
174                         public override void Emit (EmitContext ec)
175                         {
176                                 AddressOf (ec, AddressOp.LoadStore);
177                                 ec.ig.EmitCall (OpCodes.Call, info.Value, null);
178                         }
179
180                         public void EmitCheck (EmitContext ec)
181                         {
182                                 AddressOf (ec, AddressOp.LoadStore);
183                                 ec.ig.EmitCall (OpCodes.Call, info.HasValue, null);
184                         }
185
186                         public void EmitGetValueOrDefault (EmitContext ec)
187                         {
188                                 AddressOf (ec, AddressOp.LoadStore);
189                                 ec.ig.EmitCall (OpCodes.Call, info.GetValueOrDefault, null);
190                         }
191
192                         public override bool Equals (object obj)
193                         {
194                                 Unwrap uw = obj as Unwrap;
195                                 return uw != null && expr.Equals (uw.expr);
196                         }
197
198                         public Expression Original {
199                                 get {
200                                         return expr;
201                                 }
202                         }
203                         
204                         public override int GetHashCode ()
205                         {
206                                 return expr.GetHashCode ();
207                         }
208
209                         public override bool IsNull {
210                                 get {
211                                         return expr.IsNull;
212                                 }
213                         }
214
215                         public void Store (EmitContext ec)
216                         {
217                                 create_temp (ec);
218                         }
219
220                         void create_temp (EmitContext ec)
221                         {
222                                 if ((temp != null) && !has_temp) {
223                                         expr.Emit (ec);
224                                         temp.Store (ec);
225                                         has_temp = true;
226                                 }
227                         }
228
229                         public void LoadTemporary (EmitContext ec)
230                         {
231                                 temp.Emit (ec);
232                         }
233
234                         public void AddressOf (EmitContext ec, AddressOp mode)
235                         {
236                                 create_temp (ec);
237                                 if (temp != null)
238                                         temp.AddressOf (ec, AddressOp.LoadStore);
239                                 else
240                                         ((IMemoryLocation) expr).AddressOf (ec, AddressOp.LoadStore);
241                         }
242
243                         public void Emit (EmitContext ec, bool leave_copy)
244                         {
245                                 create_temp (ec);
246                                 if (leave_copy) {
247                                         if (temp != null)
248                                                 temp.Emit (ec);
249                                         else
250                                                 expr.Emit (ec);
251                                 }
252
253                                 Emit (ec);
254                         }
255
256                         public void EmitAssign (EmitContext ec, Expression source,
257                                                 bool leave_copy, bool prepare_for_load)
258                         {
259                                 InternalWrap wrap = new InternalWrap (source, info, loc);
260                                 ((IAssignMethod) expr).EmitAssign (ec, wrap, leave_copy, false);
261                         }
262
263                         protected class InternalWrap : Expression
264                         {
265                                 public Expression expr;
266                                 public NullableInfo info;
267
268                                 public InternalWrap (Expression expr, NullableInfo info, Location loc)
269                                 {
270                                         this.expr = expr;
271                                         this.info = info;
272                                         this.loc = loc;
273
274                                         type = info.Type;
275                                         eclass = ExprClass.Value;
276                                 }
277
278                                 public override Expression DoResolve (EmitContext ec)
279                                 {
280                                         return this;
281                                 }
282
283                                 public override void Emit (EmitContext ec)
284                                 {
285                                         expr.Emit (ec);
286                                         ec.ig.Emit (OpCodes.Newobj, info.Constructor);
287                                 }
288                         }
289                 }
290
291                 public class Wrap : EmptyCast
292                 {
293                         readonly NullableInfo info;
294
295                         protected Wrap (Expression expr, Type type)
296                                 : base (expr, type)
297                         {
298                                 info = new NullableInfo (type);
299                                 eclass = ExprClass.Value;
300                         }
301
302                         public static new Expression Create (Expression expr, Type type)
303                         {
304                                 return new Wrap (expr, type);
305                         }
306                         
307                         public override void Emit (EmitContext ec)
308                         {
309                                 child.Emit (ec);
310                                 ec.ig.Emit (OpCodes.Newobj, info.Constructor);
311                         }
312                 }
313
314                 class LiftedWrap : Wrap
315                 {
316                         public LiftedWrap (Expression expr, Type type)
317                                 : base (expr, type)
318                         {
319                         }
320
321                         public override Expression CreateExpressionTree (EmitContext ec)
322                         {
323                                 return child.CreateExpressionTree (ec);
324                         }
325                 }
326
327                 //
328                 // Represents null literal lifted to nullable type
329                 //
330                 public class LiftedNull : EmptyConstantCast, IMemoryLocation
331                 {
332                         private LiftedNull (Type nullable_type, Location loc)
333                                 : base (new NullLiteral (loc), nullable_type)
334                         {
335                                 eclass = ExprClass.Value;
336                         }
337
338                         public static Constant Create (Type nullable, Location loc)
339                         {
340                                 return new LiftedNull (nullable, loc);
341                         }
342
343                         public override Expression CreateExpressionTree (EmitContext ec)
344                         {
345                                 ArrayList args = new ArrayList (2);
346                                 args.Add (new Argument (this));
347                                 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
348
349                                 return CreateExpressionFactoryCall ("Constant", args);
350                         }
351
352                         public override void Emit (EmitContext ec)
353                         {
354                                 // TODO: generate less temporary variables
355                                 LocalTemporary value_target = new LocalTemporary (type);
356
357                                 value_target.AddressOf (ec, AddressOp.Store);
358                                 ec.ig.Emit (OpCodes.Initobj, type);
359                                 value_target.Emit (ec);
360                         }
361
362                         public void AddressOf (EmitContext ec, AddressOp Mode)
363                         {
364                                 LocalTemporary value_target = new LocalTemporary (type);
365                                         
366                                 value_target.AddressOf (ec, AddressOp.Store);
367                                 ec.ig.Emit (OpCodes.Initobj, type);
368                                 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
369                         }
370                 }
371
372                 public abstract class Lifted : Expression, IMemoryLocation
373                 {
374                         Expression expr, underlying, wrap, null_value;
375                         Unwrap unwrap;
376
377                         protected Lifted (Expression expr, Location loc)
378                         {
379                                 this.expr = expr;
380                                 this.loc = loc;
381                         }
382                         
383                         public override Expression CreateExpressionTree (EmitContext ec)
384                         {
385                                 return underlying.CreateExpressionTree (ec);
386                         }                       
387
388                         public override Expression DoResolve (EmitContext ec)
389                         {
390                                 expr = expr.Resolve (ec);
391                                 if (expr == null)
392                                         return null;
393
394                                 unwrap = Unwrap.Create (expr, ec);
395                                 if (unwrap == null)
396                                         return null;
397
398                                 underlying = ResolveUnderlying (unwrap, ec);
399                                 if (underlying == null)
400                                         return null;
401
402                                 TypeExpr target_type = new NullableType (underlying.Type, loc);
403                                 target_type = target_type.ResolveAsTypeTerminal (ec, false);
404                                 if (target_type == null)
405                                         return null;
406
407                                 wrap = Wrap.Create (underlying, target_type.Type);
408                                 if (wrap == null)
409                                         return null;
410
411                                 null_value = LiftedNull.Create (wrap.Type, loc);
412
413                                 type = wrap.Type;
414                                 eclass = ExprClass.Value;
415                                 return this;
416                         }
417
418                         protected abstract Expression ResolveUnderlying (Expression unwrap, EmitContext ec);
419
420                         public override void Emit (EmitContext ec)
421                         {
422                                 ILGenerator ig = ec.ig;
423                                 Label is_null_label = ig.DefineLabel ();
424                                 Label end_label = ig.DefineLabel ();
425
426                                 unwrap.EmitCheck (ec);
427                                 ig.Emit (OpCodes.Brfalse, is_null_label);
428
429                                 wrap.Emit (ec);
430                                 ig.Emit (OpCodes.Br, end_label);
431
432                                 ig.MarkLabel (is_null_label);
433                                 null_value.Emit (ec);
434
435                                 ig.MarkLabel (end_label);
436                         }
437
438                         public void AddressOf (EmitContext ec, AddressOp mode)
439                         {
440                                 unwrap.AddressOf (ec, mode);
441                         }
442                 }
443
444                 public class LiftedConversion : Lifted
445                 {
446                         public readonly bool IsUser;
447                         public readonly bool IsExplicit;
448                         public readonly Type TargetType;
449
450                         public LiftedConversion (Expression expr, Type target_type, bool is_user,
451                                                  bool is_explicit, Location loc)
452                                 : base (expr, loc)
453                         {
454                                 this.IsUser = is_user;
455                                 this.IsExplicit = is_explicit;
456                                 this.TargetType = target_type;
457                         }
458
459                         protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec)
460                         {
461                                 Type type = TypeManager.GetTypeArguments (TargetType) [0];
462
463                                 if (IsUser) {
464                                         return Convert.UserDefinedConversion (ec, unwrap, type, loc, IsExplicit);
465                                 } else {
466                                         if (IsExplicit)
467                                                 return Convert.ExplicitConversion (ec, unwrap, type, loc);
468                                         else
469                                                 return Convert.ImplicitConversion (ec, unwrap, type, loc);
470                                 }
471                         }
472                 }
473
474                 public class LiftedUnaryOperator : Lifted
475                 {
476                         public readonly Unary.Operator Oper;
477
478                         public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc)
479                                 : base (expr, loc)
480                         {
481                                 this.Oper = op;
482                         }
483
484                         protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec)
485                         {
486                                 return new Unary (Oper, unwrap, loc).Resolve (ec);
487                         }
488                 }
489
490                 public class LiftedBinaryOperator : Binary
491                 {
492                         Unwrap left_unwrap, right_unwrap;
493                         bool left_null_lifted, right_null_lifted;
494                         Expression left_orig, right_orig;
495
496                         public LiftedBinaryOperator (Binary.Operator op, Expression left, Expression right,
497                                                      Location loc)
498                                 : base (op, left, right)
499                         {
500                                 this.loc = loc;
501                         }
502
503                         //
504                         // CSC 2 has this behavior, it allows structs to be compared
505                         // with the null literal *outside* of a generics context and
506                         // inlines that as true or false.
507                         //
508                         Expression CreateNullConstant (Expression expr)
509                         {
510                                 // FIXME: Handle side effect constants
511                                 Constant c = new BoolConstant (Oper == Operator.Inequality, loc);
512
513                                 if ((Oper & Operator.EqualityMask) != 0) {
514                                         Report.Warning (472, 2, loc, "The result of comparing `{0}' against null is always `{1}'. " +
515                                                         "This operation is undocumented and it is temporary supported for compatibility reasons only",
516                                                         expr.GetSignatureForError (), c.AsString ());
517                                 } else {
518                                         Report.Warning (464, 2, loc, "The result of comparing type `{0}' against null is always `{1}'",
519                                                         expr.GetSignatureForError (), c.AsString ());
520                                 }
521
522                                 return ReducedExpression.Create (c, this);
523                         }
524
525                         public override Expression DoResolve (EmitContext ec)
526                         {
527                                 if (eclass != ExprClass.Invalid)
528                                         return this;
529
530                                 // TODO: How does it work with use-operators?
531                                 if ((Oper == Binary.Operator.LogicalAnd) ||
532                                     (Oper == Binary.Operator.LogicalOr)) {
533                                         Error_OperatorCannotBeApplied (left, right);
534                                         return null;
535                                 }
536
537                                 left_orig = left;
538                                 if (TypeManager.IsNullableType (left.Type)) {
539                                         left = left_unwrap = Unwrap.Create (left, ec);
540                                         if (left == null)
541                                                 return null;
542                                 }
543
544                                 right_orig = right;
545                                 if (TypeManager.IsNullableType (right.Type)) {
546                                         right = right_unwrap = Unwrap.Create (right, ec);
547                                         if (right == null)
548                                                 return null;
549                                 }
550
551                                 if (left is NullLiteral) {
552                                         left = right;
553                                         left_null_lifted = true;
554                                 }
555
556                                 if (right is NullLiteral) {
557                                         right = left;
558                                         right_null_lifted = true;
559                                 }
560
561                                 eclass = ExprClass.Value;
562                                 Expression expr = DoResolveCore (ec, left_orig, right_orig);
563                                 if (expr != this || (Oper & Operator.ComparisonMask) != 0)
564                                         return expr;
565
566                                 TypeExpr target_type = new NullableType (type, loc);
567                                 target_type = target_type.ResolveAsTypeTerminal (ec, false);
568                                 if (target_type == null)
569                                         return null;
570
571                                 type = target_type.Type;
572                                 return this;
573                         }
574
575                         void EmitBitwiseBoolean (EmitContext ec)
576                         {
577                                 ILGenerator ig = ec.ig;
578
579                                 Label load_left = ig.DefineLabel ();
580                                 Label load_right = ig.DefineLabel ();
581                                 Label end_label = ig.DefineLabel ();
582
583                                 left_unwrap.EmitGetValueOrDefault (ec);
584                                 ig.Emit (OpCodes.Brtrue_S, load_right);
585
586                                 right_unwrap.EmitGetValueOrDefault (ec);
587                                 ig.Emit (OpCodes.Brtrue_S, load_left);
588
589                                 left_unwrap.EmitCheck (ec);
590                                 ig.Emit (OpCodes.Brfalse_S, load_right);
591
592                                 // load left
593                                 ig.MarkLabel (load_left);
594
595                                 if (Oper == Operator.BitwiseAnd) {
596                                         left_unwrap.LoadTemporary (ec);
597                                 } else {
598                                         right_unwrap.LoadTemporary (ec);
599                                         right_unwrap = left_unwrap;
600                                 }
601                                 ig.Emit (OpCodes.Br_S, end_label);
602
603                                 // load right
604                                 ig.MarkLabel (load_right);
605                                 right_unwrap.LoadTemporary (ec);
606
607                                 ig.MarkLabel (end_label);
608                         }
609
610                         void EmitEquality (EmitContext ec)
611                         {
612                                 ILGenerator ig = ec.ig;
613
614                                 Label both_have_value_label = ig.DefineLabel ();
615                                 Label end_label = ig.DefineLabel ();
616
617                                 //
618                                 // Both are nullable types
619                                 //
620                                 if (left_unwrap != null && right_unwrap != null && !right.IsNull && !left.IsNull) {
621                                         Label dissimilar_label = ig.DefineLabel ();
622
623                                         left_unwrap.EmitGetValueOrDefault (ec);
624                                         right_unwrap.EmitGetValueOrDefault (ec);
625                                         ig.Emit (OpCodes.Bne_Un_S, dissimilar_label);
626
627                                         left_unwrap.EmitCheck (ec);
628                                         right_unwrap.EmitCheck (ec);
629                                         if (Oper == Operator.Inequality)
630                                                 ig.Emit (OpCodes.Xor);
631                                         else
632                                                 ig.Emit (OpCodes.Ceq);
633
634                                         ig.Emit (OpCodes.Br_S, end_label);
635
636                                         ig.MarkLabel (dissimilar_label);
637                                         if (Oper == Operator.Inequality)
638                                                 ig.Emit (OpCodes.Ldc_I4_1);
639                                         else
640                                                 ig.Emit (OpCodes.Ldc_I4_0);
641
642                                         ig.MarkLabel (end_label);
643                                         return;
644                                 }
645
646                                 //
647                                 // Either left or right is nullable
648                                 //
649                                 if (left_unwrap != null) {
650                                         left_unwrap.EmitCheck (ec);
651                                         if (right.IsNull) {
652                                                 if (Oper == Binary.Operator.Equality) {
653                                                         ig.Emit (OpCodes.Ldc_I4_0);
654                                                         ig.Emit (OpCodes.Ceq);
655                                                 }
656                                                 return;
657                                         }
658                                         ig.Emit (OpCodes.Brtrue_S, both_have_value_label);
659                                 } else {
660                                         right_unwrap.EmitCheck (ec);
661                                         if (left.IsNull) {
662                                                 if (Oper == Binary.Operator.Equality) {
663                                                         ig.Emit (OpCodes.Ldc_I4_0);
664                                                         ig.Emit (OpCodes.Ceq);
665                                                 }
666                                                 return;
667                                         }
668                                         ig.Emit (OpCodes.Brtrue_S, both_have_value_label);
669                                 }
670
671                                 if (Oper == Binary.Operator.Equality)
672                                         ig.Emit (OpCodes.Ldc_I4_0);
673                                 else
674                                         ig.Emit (OpCodes.Ldc_I4_1);
675                                 ig.Emit (OpCodes.Br, end_label);
676
677                                 ig.MarkLabel (both_have_value_label);
678                                 EmitOperator (ec);
679
680                                 ig.MarkLabel (end_label);
681                         }
682
683                         void EmitComparision (EmitContext ec)
684                         {
685                                 ILGenerator ig = ec.ig;
686
687                                 Label is_null_label = ig.DefineLabel ();
688                                 Label end_label = ig.DefineLabel ();
689
690                                 if (left_unwrap != null) {
691                                         left_unwrap.EmitCheck (ec);
692                                         ig.Emit (OpCodes.Brfalse, is_null_label);
693                                 }
694
695                                 if (right_unwrap != null) {
696                                         right_unwrap.EmitCheck (ec);
697                                         ig.Emit (OpCodes.Brfalse, is_null_label);
698                                 }
699
700                                 EmitOperator (ec);
701                                 ig.Emit (OpCodes.Br_S, end_label);
702
703                                 ig.MarkLabel (is_null_label);
704                                 ig.Emit (OpCodes.Ldc_I4_0);
705
706                                 ig.MarkLabel (end_label);
707                         }
708                         
709                         public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
710                         {
711                                 Emit (ec);
712                                 ec.ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
713                         }                       
714
715                         public override void Emit (EmitContext ec)
716                         {
717                                 if (left_unwrap != null)
718                                         left_unwrap.Store (ec);
719                                 if (right_unwrap != null)
720                                         right_unwrap.Store (ec);
721
722                                 if (IsBitwiseBoolean) {
723                                         EmitBitwiseBoolean (ec);
724                                         return;
725                                 }
726
727                                 if ((Oper & Operator.EqualityMask) != 0) {
728                                         EmitEquality (ec);
729                                         return;
730                                 }
731
732                                 if ((Oper & Operator.ComparisonMask) != 0) {
733                                         EmitComparision (ec);
734                                         return;
735                                 }
736
737                                 ILGenerator ig = ec.ig;
738
739                                 Label is_null_label = ig.DefineLabel ();
740                                 Label end_label = ig.DefineLabel ();
741
742                                 if (left_unwrap != null) {
743                                         left_unwrap.EmitCheck (ec);
744                                         ig.Emit (OpCodes.Brfalse, is_null_label);
745                                 }
746
747                                 if (right_unwrap != null) {
748                                         right_unwrap.EmitCheck (ec);
749                                         ig.Emit (OpCodes.Brfalse, is_null_label);
750                                 }
751
752                                 base.EmitOperator (ec);
753
754                                 ig.Emit (OpCodes.Br_S, end_label);
755                                 ig.MarkLabel (is_null_label);
756                                 LiftedNull.Create (type, loc).Emit (ec);
757
758                                 ig.MarkLabel (end_label);
759                         }
760
761                         bool IsBitwiseBoolean {
762                                 get {
763                                         return (Oper & Operator.BitwiseMask) != 0 && left_unwrap != null && right_unwrap != null &&
764                                         left_unwrap.Type == TypeManager.bool_type && right_unwrap.Type == TypeManager.bool_type;
765                                 }
766                         }
767
768                         Expression LiftResult (EmitContext ec, Expression res_expr)
769                         {
770                                 TypeExpr lifted_type;
771
772                                 //
773                                 // Avoid double conversion
774                                 //
775                                 if (left_unwrap == null || left_null_lifted || !TypeManager.IsEqual (left_unwrap.Type, left.Type)) {
776                                         lifted_type = new NullableType (left.Type, loc);
777                                         lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
778                                         if (lifted_type == null)
779                                                 return null;
780
781                                         left = EmptyCast.Create (left, lifted_type.Type);
782                                 }
783
784                                 if (right_unwrap == null || right_null_lifted || !TypeManager.IsEqual (right_unwrap.Type, right.Type)) {
785                                         lifted_type = new NullableType (right.Type, loc);
786                                         lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
787                                         if (lifted_type == null)
788                                                 return null;
789
790                                         right = EmptyCast.Create (right, lifted_type.Type);
791                                 }
792
793                                 if (left_null_lifted) {
794                                         left = LiftedNull.Create (right.Type, left.Location);
795
796                                         //
797                                         // Value types and null comparison
798                                         //
799                                         if (right_unwrap == null || (Oper & Operator.RelationalMask) != 0)
800                                                 return CreateNullConstant (right_orig).Resolve (ec);
801                                 }
802
803                                 if (right_null_lifted) {
804                                         right = LiftedNull.Create (left.Type, right.Location);
805
806                                         //
807                                         // Value types and null comparison
808                                         //
809                                         if (left_unwrap == null || (Oper & Operator.RelationalMask) != 0)
810                                                 return CreateNullConstant (left_orig).Resolve (ec);
811                                 }
812
813                                 // TODO: Handle bitwise bool 
814                                 if ((Oper & Operator.ComparisonMask) != 0 || IsBitwiseBoolean)
815                                         return res_expr;
816
817                                 lifted_type = new NullableType (res_expr.Type, loc);
818                                 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
819                                 if (lifted_type == null)
820                                         return null;
821
822                                 return new LiftedWrap (res_expr, lifted_type.Type).Resolve (ec);
823                         }
824
825                         protected override Expression ResolveOperatorPredefined (EmitContext ec, Binary.PredefinedOperator [] operators, bool primitives_only)
826                         {
827                                 Expression e = base.ResolveOperatorPredefined (ec, operators, primitives_only);
828
829                                 if (e == this)
830                                         return LiftResult (ec, e);
831
832                                 //
833                                 // 7.9.9 Equality operators and null
834                                 //
835                                 // The == and != operators permit one operand to be a value of a nullable type and
836                                 // the other to be the null literal, even if no predefined or user-defined operator
837                                 // (in unlifted or lifted form) exists for the operation.
838                                 //
839                                 if (e == null && (Oper & Operator.EqualityMask) != 0) {
840                                         if ((left_null_lifted && right_unwrap != null) || (right_null_lifted && left_unwrap != null))
841                                                 return LiftResult (ec, this);
842                                 }
843
844                                 return e;
845                         }
846
847                         protected override Expression ResolveUserOperator (EmitContext ec, Type l, Type r)
848                         {
849                                 Expression expr = base.ResolveUserOperator (ec, l, r);
850                                 if (expr == null)
851                                         return null;
852
853                                 return LiftResult (ec, expr);
854                         }
855                 }
856
857                 public class NullCoalescingOperator : Expression
858                 {
859                         Expression left, right;
860                         Unwrap unwrap;
861
862                         public NullCoalescingOperator (Expression left, Expression right, Location loc)
863                         {
864                                 this.left = left;
865                                 this.right = right;
866                                 this.loc = loc;
867                         }
868                         
869                         public override Expression CreateExpressionTree (EmitContext ec)
870                         {
871                                 UserCast uc = left as UserCast;
872                                 Expression conversion = null;
873                                 if (uc != null) {
874                                         left = uc.Source;
875
876                                         ArrayList c_args = new ArrayList (2);
877                                         c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
878                                         c_args.Add (new Argument (left.CreateExpressionTree (ec)));
879                                         conversion = CreateExpressionFactoryCall ("Lambda", c_args);
880                                 }
881
882                                 ArrayList args = new ArrayList (3);
883                                 args.Add (new Argument (left.CreateExpressionTree (ec)));
884                                 args.Add (new Argument (right.CreateExpressionTree (ec)));
885                                 if (conversion != null)
886                                         args.Add (new Argument (conversion));
887                                 
888                                 return CreateExpressionFactoryCall ("Coalesce", args);
889                         }                       
890
891                         public override Expression DoResolve (EmitContext ec)
892                         {
893                                 if (type != null)
894                                         return this;
895
896                                 left = left.Resolve (ec);
897                                 right = right.Resolve (ec);
898
899                                 if (left == null || right == null)
900                                         return null;
901
902                                 eclass = ExprClass.Value;
903                                 Type ltype = left.Type, rtype = right.Type;
904                                 Expression expr;
905
906                                 if (TypeManager.IsNullableType (ltype)) {
907                                         NullableInfo info = new NullableInfo (ltype);
908
909                                         unwrap = Unwrap.Create (left, ec);
910                                         if (unwrap == null)
911                                                 return null;
912
913                                         expr = Convert.ImplicitConversion (ec, right, info.UnderlyingType, loc);
914                                         if (expr != null) {
915                                                 left = unwrap;
916                                                 right = expr;
917                                                 type = expr.Type;
918                                                 return this;
919                                         }
920                                 } else if (!TypeManager.IsReferenceType (ltype)) {
921                                         Binary.Error_OperatorCannotBeApplied (loc, "??", ltype, rtype);
922                                         return null;
923                                 }
924
925                                 expr = Convert.ImplicitConversion (ec, right, ltype, loc);
926                                 if (expr != null) {
927                                         type = expr.Type;
928                                         right = expr;
929                                         return this;
930                                 }
931
932                                 Expression left_null = unwrap != null ? unwrap : left;
933                                 expr = Convert.ImplicitConversion (ec, left_null, rtype, loc);
934                                 if (expr != null) {
935                                         left = expr;
936                                         type = rtype;
937                                         return this;
938                                 }
939
940                                 Binary.Error_OperatorCannotBeApplied (loc, "??", ltype, rtype);
941                                 return null;
942                         }
943
944                         public override void Emit (EmitContext ec)
945                         {
946                                 ILGenerator ig = ec.ig;
947
948                                 Label is_null_label = ig.DefineLabel ();
949                                 Label end_label = ig.DefineLabel ();
950
951                                 if (unwrap != null) {
952                                         unwrap.EmitCheck (ec);
953                                         ig.Emit (OpCodes.Brfalse, is_null_label);
954
955                                         left.Emit (ec);
956                                         ig.Emit (OpCodes.Br, end_label);
957
958                                         ig.MarkLabel (is_null_label);
959                                         right.Emit (ec);
960
961                                         ig.MarkLabel (end_label);
962                                 } else {
963                                         left.Emit (ec);
964                                         ig.Emit (OpCodes.Dup);
965                                         ig.Emit (OpCodes.Brtrue, end_label);
966
967                                         ig.MarkLabel (is_null_label);
968
969                                         ig.Emit (OpCodes.Pop);
970                                         right.Emit (ec);
971
972                                         ig.MarkLabel (end_label);
973                                 }
974                         }
975                         protected override void CloneTo (CloneContext clonectx, Expression t)
976                         {
977                                 NullCoalescingOperator target = (NullCoalescingOperator) t;
978
979                                 target.left = left.Clone (clonectx);
980                                 target.right = right.Clone (clonectx);
981                         }
982                 }
983
984                 public class LiftedUnaryMutator : ExpressionStatement
985                 {
986                         public readonly UnaryMutator.Mode Mode;
987                         Expression expr, null_value;
988                         UnaryMutator underlying;
989                         Unwrap unwrap;
990
991                         public LiftedUnaryMutator (UnaryMutator.Mode mode, Expression expr, Location loc)
992                         {
993                                 this.expr = expr;
994                                 this.Mode = mode;
995                                 this.loc = loc;
996
997                                 eclass = ExprClass.Value;
998                         }
999
1000                         public override Expression DoResolve (EmitContext ec)
1001                         {
1002                                 expr = expr.Resolve (ec);
1003                                 if (expr == null)
1004                                         return null;
1005
1006                                 unwrap = Unwrap.Create (expr, ec);
1007                                 if (unwrap == null)
1008                                         return null;
1009
1010                                 underlying = (UnaryMutator) new UnaryMutator (Mode, unwrap, loc).Resolve (ec);
1011                                 if (underlying == null)
1012                                         return null;
1013
1014                                 type = expr.Type;
1015                                 null_value = LiftedNull.Create (type, loc);
1016                                 return this;
1017                         }
1018
1019                         void DoEmit (EmitContext ec, bool is_expr)
1020                         {
1021                                 ILGenerator ig = ec.ig;
1022                                 Label is_null_label = ig.DefineLabel ();
1023                                 Label end_label = ig.DefineLabel ();
1024
1025                                 unwrap.EmitCheck (ec);
1026                                 ig.Emit (OpCodes.Brfalse, is_null_label);
1027
1028                                 if (is_expr)
1029                                         underlying.Emit (ec);
1030                                 else
1031                                         underlying.EmitStatement (ec);
1032                                 ig.Emit (OpCodes.Br, end_label);
1033
1034                                 ig.MarkLabel (is_null_label);
1035                                 if (is_expr)
1036                                         null_value.Emit (ec);
1037
1038                                 ig.MarkLabel (end_label);
1039                         }
1040
1041                         public override void Emit (EmitContext ec)
1042                         {
1043                                 DoEmit (ec, true);
1044                         }
1045
1046                         public override void EmitStatement (EmitContext ec)
1047                         {
1048                                 DoEmit (ec, false);
1049                         }
1050                 }
1051         }
1052 }
1053