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