2008-04-01 Marek Safar <marek.safar@gmail.com>
[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 static Expression CreateFromExpression (Expression e)
342                 {
343                         Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'",
344                                 TypeManager.CSharpName (e.Type));
345
346                         return ReducedExpression.Create (Create (e.Type, e.Location) , e);
347                 }
348
349                 public override Expression CreateExpressionTree (EmitContext ec)
350                 {
351                         ArrayList args = new ArrayList (2);
352                         args.Add (new Argument (this));
353                         args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
354
355                         return CreateExpressionFactoryCall ("Constant", args);
356                 }
357
358                 public override void Emit (EmitContext ec)
359                 {
360                         // TODO: generate less temporary variables
361                         LocalTemporary value_target = new LocalTemporary (type);
362
363                         value_target.AddressOf (ec, AddressOp.Store);
364                         ec.ig.Emit (OpCodes.Initobj, type);
365                         value_target.Emit (ec);
366                 }
367
368                 public void AddressOf (EmitContext ec, AddressOp Mode)
369                 {
370                         LocalTemporary value_target = new LocalTemporary (type);
371                                 
372                         value_target.AddressOf (ec, AddressOp.Store);
373                         ec.ig.Emit (OpCodes.Initobj, type);
374                         ((IMemoryLocation) value_target).AddressOf (ec, Mode);
375                 }
376         }
377
378         public abstract class Lifted : Expression, IMemoryLocation
379         {
380                 Expression expr, underlying, wrap, null_value;
381                 Unwrap unwrap;
382
383                 protected Lifted (Expression expr, Location loc)
384                 {
385                         this.expr = expr;
386                         this.loc = loc;
387                 }
388                 
389                 public override Expression CreateExpressionTree (EmitContext ec)
390                 {
391                         return underlying.CreateExpressionTree (ec);
392                 }                       
393
394                 public override Expression DoResolve (EmitContext ec)
395                 {
396                         expr = expr.Resolve (ec);
397                         if (expr == null)
398                                 return null;
399
400                         unwrap = Unwrap.Create (expr, ec);
401                         if (unwrap == null)
402                                 return null;
403
404                         underlying = ResolveUnderlying (unwrap, ec);
405                         if (underlying == null)
406                                 return null;
407
408                         TypeExpr target_type = new NullableType (underlying.Type, loc);
409                         target_type = target_type.ResolveAsTypeTerminal (ec, false);
410                         if (target_type == null)
411                                 return null;
412
413                         wrap = Wrap.Create (underlying, target_type.Type);
414                         if (wrap == null)
415                                 return null;
416
417                         null_value = LiftedNull.Create (wrap.Type, loc);
418
419                         type = wrap.Type;
420                         eclass = ExprClass.Value;
421                         return this;
422                 }
423
424                 protected abstract Expression ResolveUnderlying (Expression unwrap, EmitContext ec);
425
426                 public override void Emit (EmitContext ec)
427                 {
428                         ILGenerator ig = ec.ig;
429                         Label is_null_label = ig.DefineLabel ();
430                         Label end_label = ig.DefineLabel ();
431
432                         unwrap.EmitCheck (ec);
433                         ig.Emit (OpCodes.Brfalse, is_null_label);
434
435                         wrap.Emit (ec);
436                         ig.Emit (OpCodes.Br, end_label);
437
438                         ig.MarkLabel (is_null_label);
439                         null_value.Emit (ec);
440
441                         ig.MarkLabel (end_label);
442                 }
443
444                 public void AddressOf (EmitContext ec, AddressOp mode)
445                 {
446                         unwrap.AddressOf (ec, mode);
447                 }
448         }
449
450         public class LiftedConversion : Lifted
451         {
452                 public readonly bool IsUser;
453                 public readonly bool IsExplicit;
454                 public readonly Type TargetType;
455
456                 public LiftedConversion (Expression expr, Type target_type, bool is_user,
457                                          bool is_explicit, Location loc)
458                         : base (expr, loc)
459                 {
460                         this.IsUser = is_user;
461                         this.IsExplicit = is_explicit;
462                         this.TargetType = target_type;
463                 }
464
465                 protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec)
466                 {
467                         Type type = TypeManager.GetTypeArguments (TargetType) [0];
468
469                         if (IsUser) {
470                                 return Convert.UserDefinedConversion (ec, unwrap, type, loc, IsExplicit);
471                         } else {
472                                 if (IsExplicit)
473                                         return Convert.ExplicitConversion (ec, unwrap, type, loc);
474                                 else
475                                         return Convert.ImplicitConversion (ec, unwrap, type, loc);
476                         }
477                 }
478         }
479
480         public class LiftedUnaryOperator : Lifted
481         {
482                 public readonly Unary.Operator Oper;
483
484                 public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc)
485                         : base (expr, loc)
486                 {
487                         this.Oper = op;
488                 }
489
490                 protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec)
491                 {
492                         return new Unary (Oper, unwrap, loc).Resolve (ec);
493                 }
494         }
495
496         public class LiftedBinaryOperator : Binary
497         {
498                 Unwrap left_unwrap, right_unwrap;
499                 bool left_null_lifted, right_null_lifted;
500                 Expression left_orig, right_orig;
501
502                 public LiftedBinaryOperator (Binary.Operator op, Expression left, Expression right,
503                                              Location loc)
504                         : base (op, left, right)
505                 {
506                         this.loc = loc;
507                 }
508
509                 //
510                 // CSC 2 has this behavior, it allows structs to be compared
511                 // with the null literal *outside* of a generics context and
512                 // inlines that as true or false.
513                 //
514                 Expression CreateNullConstant (Expression expr)
515                 {
516                         // FIXME: Handle side effect constants
517                         Constant c = new BoolConstant (Oper == Operator.Inequality, loc);
518
519                         if ((Oper & Operator.EqualityMask) != 0) {
520                                 Report.Warning (472, 2, loc, "The result of comparing `{0}' against null is always `{1}'. " +
521                                                 "This operation is undocumented and it is temporary supported for compatibility reasons only",
522                                                 expr.GetSignatureForError (), c.AsString ());
523                         } else {
524                                 Report.Warning (464, 2, loc, "The result of comparing type `{0}' against null is always `{1}'",
525                                                 expr.GetSignatureForError (), c.AsString ());
526                         }
527
528                         return ReducedExpression.Create (c, this);
529                 }
530
531                 public override Expression DoResolve (EmitContext ec)
532                 {
533                         if (eclass != ExprClass.Invalid)
534                                 return this;
535
536                         // TODO: How does it work with use-operators?
537                         if ((Oper == Binary.Operator.LogicalAnd) ||
538                             (Oper == Binary.Operator.LogicalOr)) {
539                                 Error_OperatorCannotBeApplied (left, right);
540                                 return null;
541                         }
542
543                         left_orig = left;
544                         if (TypeManager.IsNullableType (left.Type)) {
545                                 left = left_unwrap = Unwrap.Create (left, ec);
546                                 if (left == null)
547                                         return null;
548                         }
549
550                         right_orig = right;
551                         if (TypeManager.IsNullableType (right.Type)) {
552                                 right = right_unwrap = Unwrap.Create (right, ec);
553                                 if (right == null)
554                                         return null;
555                         }
556
557                         //
558                         // Some details are in 6.4.2, 7.2.7
559                         // Arguments can be lifted for equal operators when the return type is bool and both
560                         // arguments are of same type
561                         //      
562                         if (left is NullLiteral) {
563                                 left = right;
564                                 left_null_lifted = true;
565                                 type = TypeManager.bool_type;
566                         }
567
568                         if (right is NullLiteral) {
569                                 right = left;
570                                 right_null_lifted = true;
571                                 type = TypeManager.bool_type;
572                         }
573
574                         eclass = ExprClass.Value;
575                         Expression expr = DoResolveCore (ec, left_orig, right_orig);
576                         if (expr != this || (Oper & Operator.ComparisonMask) != 0)
577                                 return expr;
578
579                         TypeExpr target_type = new NullableType (type, loc);
580                         target_type = target_type.ResolveAsTypeTerminal (ec, false);
581                         if (target_type == null)
582                                 return null;
583
584                         type = target_type.Type;
585                         return this;
586                 }
587
588                 void EmitBitwiseBoolean (EmitContext ec)
589                 {
590                         ILGenerator ig = ec.ig;
591
592                         Label load_left = ig.DefineLabel ();
593                         Label load_right = ig.DefineLabel ();
594                         Label end_label = ig.DefineLabel ();
595
596                         left_unwrap.EmitGetValueOrDefault (ec);
597                         ig.Emit (OpCodes.Brtrue_S, load_right);
598
599                         right_unwrap.EmitGetValueOrDefault (ec);
600                         ig.Emit (OpCodes.Brtrue_S, load_left);
601
602                         left_unwrap.EmitCheck (ec);
603                         ig.Emit (OpCodes.Brfalse_S, load_right);
604
605                         // load left
606                         ig.MarkLabel (load_left);
607
608                         if (Oper == Operator.BitwiseAnd) {
609                                 left_unwrap.LoadTemporary (ec);
610                         } else {
611                                 right_unwrap.LoadTemporary (ec);
612                                 right_unwrap = left_unwrap;
613                         }
614                         ig.Emit (OpCodes.Br_S, end_label);
615
616                         // load right
617                         ig.MarkLabel (load_right);
618                         right_unwrap.LoadTemporary (ec);
619
620                         ig.MarkLabel (end_label);
621                 }
622
623                 void EmitEquality (EmitContext ec)
624                 {
625                         ILGenerator ig = ec.ig;
626
627                         Label both_have_value_label = ig.DefineLabel ();
628                         Label end_label = ig.DefineLabel ();
629
630                         //
631                         // Both are nullable types
632                         //
633                         if (left_unwrap != null && right_unwrap != null && !right.IsNull && !left.IsNull) {
634                                 Label dissimilar_label = ig.DefineLabel ();
635
636                                 left_unwrap.EmitGetValueOrDefault (ec);
637                                 right_unwrap.EmitGetValueOrDefault (ec);
638                                 ig.Emit (OpCodes.Bne_Un_S, dissimilar_label);
639
640                                 left_unwrap.EmitCheck (ec);
641                                 right_unwrap.EmitCheck (ec);
642                                 if (Oper == Operator.Inequality)
643                                         ig.Emit (OpCodes.Xor);
644                                 else
645                                         ig.Emit (OpCodes.Ceq);
646
647                                 ig.Emit (OpCodes.Br_S, end_label);
648
649                                 ig.MarkLabel (dissimilar_label);
650                                 if (Oper == Operator.Inequality)
651                                         ig.Emit (OpCodes.Ldc_I4_1);
652                                 else
653                                         ig.Emit (OpCodes.Ldc_I4_0);
654
655                                 ig.MarkLabel (end_label);
656                                 return;
657                         }
658
659                         //
660                         // Either left or right is nullable
661                         //
662                         if (left_unwrap != null) {
663                                 left_unwrap.EmitCheck (ec);
664                                 if (right.IsNull) {
665                                         if (Oper == Binary.Operator.Equality) {
666                                                 ig.Emit (OpCodes.Ldc_I4_0);
667                                                 ig.Emit (OpCodes.Ceq);
668                                         }
669                                         return;
670                                 }
671                                 ig.Emit (OpCodes.Brtrue_S, both_have_value_label);
672                         } else {
673                                 right_unwrap.EmitCheck (ec);
674                                 if (left.IsNull) {
675                                         if (Oper == Binary.Operator.Equality) {
676                                                 ig.Emit (OpCodes.Ldc_I4_0);
677                                                 ig.Emit (OpCodes.Ceq);
678                                         }
679                                         return;
680                                 }
681                                 ig.Emit (OpCodes.Brtrue_S, both_have_value_label);
682                         }
683
684                         if (Oper == Binary.Operator.Equality)
685                                 ig.Emit (OpCodes.Ldc_I4_0);
686                         else
687                                 ig.Emit (OpCodes.Ldc_I4_1);
688                         ig.Emit (OpCodes.Br, end_label);
689
690                         ig.MarkLabel (both_have_value_label);
691                         EmitOperator (ec);
692
693                         ig.MarkLabel (end_label);
694                 }
695
696                 void EmitComparision (EmitContext ec)
697                 {
698                         ILGenerator ig = ec.ig;
699
700                         Label is_null_label = ig.DefineLabel ();
701                         Label end_label = ig.DefineLabel ();
702
703                         if (left_unwrap != null) {
704                                 left_unwrap.EmitCheck (ec);
705                                 ig.Emit (OpCodes.Brfalse, is_null_label);
706                         }
707
708                         if (right_unwrap != null) {
709                                 right_unwrap.EmitCheck (ec);
710                                 ig.Emit (OpCodes.Brfalse, is_null_label);
711                         }
712
713                         EmitOperator (ec);
714                         ig.Emit (OpCodes.Br_S, end_label);
715
716                         ig.MarkLabel (is_null_label);
717                         ig.Emit (OpCodes.Ldc_I4_0);
718
719                         ig.MarkLabel (end_label);
720                 }
721                 
722                 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
723                 {
724                         Emit (ec);
725                         ec.ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
726                 }                       
727
728                 public override void Emit (EmitContext ec)
729                 {
730                         if (left_unwrap != null)
731                                 left_unwrap.Store (ec);
732                         if (right_unwrap != null)
733                                 right_unwrap.Store (ec);
734
735                         if (IsBitwiseBoolean) {
736                                 EmitBitwiseBoolean (ec);
737                                 return;
738                         }
739
740                         if ((Oper & Operator.EqualityMask) != 0) {
741                                 EmitEquality (ec);
742                                 return;
743                         }
744
745                         if ((Oper & Operator.ComparisonMask) != 0) {
746                                 EmitComparision (ec);
747                                 return;
748                         }
749
750                         ILGenerator ig = ec.ig;
751
752                         Label is_null_label = ig.DefineLabel ();
753                         Label end_label = ig.DefineLabel ();
754
755                         if (left_unwrap != null) {
756                                 left_unwrap.EmitCheck (ec);
757                                 ig.Emit (OpCodes.Brfalse, is_null_label);
758                         }
759
760                         if (right_unwrap != null) {
761                                 right_unwrap.EmitCheck (ec);
762                                 ig.Emit (OpCodes.Brfalse, is_null_label);
763                         }
764
765                         base.EmitOperator (ec);
766
767                         ig.Emit (OpCodes.Br_S, end_label);
768                         ig.MarkLabel (is_null_label);
769                         LiftedNull.Create (type, loc).Emit (ec);
770
771                         ig.MarkLabel (end_label);
772                 }
773
774                 bool IsBitwiseBoolean {
775                         get {
776                                 return (Oper & Operator.BitwiseMask) != 0 && left_unwrap != null && right_unwrap != null &&
777                                 left_unwrap.Type == TypeManager.bool_type && right_unwrap.Type == TypeManager.bool_type;
778                         }
779                 }
780
781                 Expression LiftResult (EmitContext ec, Expression res_expr)
782                 {
783                         TypeExpr lifted_type;
784
785                         //
786                         // Avoid double conversion
787                         //
788                         if (left_unwrap == null || left_null_lifted || !TypeManager.IsEqual (left_unwrap.Type, left.Type)) {
789                                 lifted_type = new NullableType (left.Type, loc);
790                                 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
791                                 if (lifted_type == null)
792                                         return null;
793
794                                 left = EmptyCast.Create (left, lifted_type.Type);
795                         }
796
797                         if (right_unwrap == null || right_null_lifted || !TypeManager.IsEqual (right_unwrap.Type, right.Type)) {
798                                 lifted_type = new NullableType (right.Type, loc);
799                                 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
800                                 if (lifted_type == null)
801                                         return null;
802
803                                 right = EmptyCast.Create (right, lifted_type.Type);
804                         }
805
806                         // TODO: Handle bitwise bool 
807                         if ((Oper & Operator.ComparisonMask) == 0 && !IsBitwiseBoolean) {
808                                 lifted_type = new NullableType (res_expr.Type, loc);
809                                 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
810                                 if (lifted_type == null)
811                                         return null;
812
813                                 res_expr = new LiftedWrap (res_expr, lifted_type.Type).Resolve (ec);
814                         }
815
816                         if (left_null_lifted) {
817                                 left = LiftedNull.Create (right.Type, left.Location);
818
819                                 //
820                                 // Value types and null comparison
821                                 //
822                                 if (right_unwrap == null || (Oper & Operator.RelationalMask) != 0)
823                                         return CreateNullConstant (right_orig).Resolve (ec);
824
825                                 if ((Oper & Operator.ArithmeticMask) != 0)
826                                         return LiftedNull.CreateFromExpression (res_expr);
827                         }
828
829                         if (right_null_lifted) {
830                                 right = LiftedNull.Create (left.Type, right.Location);
831
832                                 //
833                                 // Value types and null comparison
834                                 //
835                                 if (left_unwrap == null || (Oper & Operator.RelationalMask) != 0)
836                                         return CreateNullConstant (left_orig).Resolve (ec);
837
838                                 if ((Oper & Operator.ArithmeticMask) != 0)
839                                         return LiftedNull.CreateFromExpression (res_expr);
840                         }
841
842                         return res_expr;
843                 }
844
845                 protected override Expression ResolveOperatorPredefined (EmitContext ec, Binary.PredefinedOperator [] operators, bool primitives_only)
846                 {
847                         Expression e = base.ResolveOperatorPredefined (ec, operators, primitives_only);
848
849                         if (e == this)
850                                 return LiftResult (ec, e);
851
852                         //
853                         // 7.9.9 Equality operators and null
854                         //
855                         // The == and != operators permit one operand to be a value of a nullable type and
856                         // the other to be the null literal, even if no predefined or user-defined operator
857                         // (in unlifted or lifted form) exists for the operation.
858                         //
859                         if (e == null && (Oper & Operator.EqualityMask) != 0) {
860                                 if ((left_null_lifted && right_unwrap != null) || (right_null_lifted && left_unwrap != null))
861                                         return LiftResult (ec, this);
862                         }
863
864                         return e;
865                 }
866
867                 protected override Expression ResolveUserOperator (EmitContext ec, Type l, Type r)
868                 {
869                         Expression expr = base.ResolveUserOperator (ec, l, r);
870                         if (expr == null)
871                                 return null;
872
873                         //
874                         // When lifting null literal and user operator exists, no call is made
875                         //
876                         if (left_null_lifted || right_null_lifted)
877                                 expr = ReducedExpression.Create (this, expr).Resolve (ec);
878
879                         return LiftResult (ec, expr);
880                 }
881         }
882
883         public class NullCoalescingOperator : Expression
884         {
885                 Expression left, right;
886                 Unwrap unwrap;
887
888                 public NullCoalescingOperator (Expression left, Expression right, Location loc)
889                 {
890                         this.left = left;
891                         this.right = right;
892                         this.loc = loc;
893                 }
894                 
895                 public override Expression CreateExpressionTree (EmitContext ec)
896                 {
897                         UserCast uc = left as UserCast;
898                         Expression conversion = null;
899                         if (uc != null) {
900                                 left = uc.Source;
901
902                                 ArrayList c_args = new ArrayList (2);
903                                 c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
904                                 c_args.Add (new Argument (left.CreateExpressionTree (ec)));
905                                 conversion = CreateExpressionFactoryCall ("Lambda", c_args);
906                         }
907
908                         ArrayList args = new ArrayList (3);
909                         args.Add (new Argument (left.CreateExpressionTree (ec)));
910                         args.Add (new Argument (right.CreateExpressionTree (ec)));
911                         if (conversion != null)
912                                 args.Add (new Argument (conversion));
913                         
914                         return CreateExpressionFactoryCall ("Coalesce", args);
915                 }                       
916
917                 public override Expression DoResolve (EmitContext ec)
918                 {
919                         if (type != null)
920                                 return this;
921
922                         left = left.Resolve (ec);
923                         right = right.Resolve (ec);
924
925                         if (left == null || right == null)
926                                 return null;
927
928                         eclass = ExprClass.Value;
929                         Type ltype = left.Type, rtype = right.Type;
930                         Expression expr;
931
932                         if (TypeManager.IsNullableType (ltype)) {
933                                 NullableInfo info = new NullableInfo (ltype);
934
935                                 unwrap = Unwrap.Create (left, ec);
936                                 if (unwrap == null)
937                                         return null;
938
939                                 expr = Convert.ImplicitConversion (ec, right, info.UnderlyingType, loc);
940                                 if (expr != null) {
941                                         left = unwrap;
942                                         right = expr;
943                                         type = expr.Type;
944                                         return this;
945                                 }
946                         } else if (!TypeManager.IsReferenceType (ltype)) {
947                                 Binary.Error_OperatorCannotBeApplied (loc, "??", ltype, rtype);
948                                 return null;
949                         }
950
951                         expr = Convert.ImplicitConversion (ec, right, ltype, loc);
952                         if (expr != null) {
953                                 type = expr.Type;
954                                 right = expr;
955                                 return this;
956                         }
957
958                         Expression left_null = unwrap != null ? unwrap : left;
959                         expr = Convert.ImplicitConversion (ec, left_null, rtype, loc);
960                         if (expr != null) {
961                                 left = expr;
962                                 type = rtype;
963                                 return this;
964                         }
965
966                         Binary.Error_OperatorCannotBeApplied (loc, "??", ltype, rtype);
967                         return null;
968                 }
969
970                 public override void Emit (EmitContext ec)
971                 {
972                         ILGenerator ig = ec.ig;
973
974                         Label is_null_label = ig.DefineLabel ();
975                         Label end_label = ig.DefineLabel ();
976
977                         if (unwrap != null) {
978                                 unwrap.EmitCheck (ec);
979                                 ig.Emit (OpCodes.Brfalse, is_null_label);
980
981                                 left.Emit (ec);
982                                 ig.Emit (OpCodes.Br, end_label);
983
984                                 ig.MarkLabel (is_null_label);
985                                 right.Emit (ec);
986
987                                 ig.MarkLabel (end_label);
988                         } else {
989                                 left.Emit (ec);
990                                 ig.Emit (OpCodes.Dup);
991                                 ig.Emit (OpCodes.Brtrue, end_label);
992
993                                 ig.MarkLabel (is_null_label);
994
995                                 ig.Emit (OpCodes.Pop);
996                                 right.Emit (ec);
997
998                                 ig.MarkLabel (end_label);
999                         }
1000                 }
1001                 protected override void CloneTo (CloneContext clonectx, Expression t)
1002                 {
1003                         NullCoalescingOperator target = (NullCoalescingOperator) t;
1004
1005                         target.left = left.Clone (clonectx);
1006                         target.right = right.Clone (clonectx);
1007                 }
1008         }
1009
1010         public class LiftedUnaryMutator : ExpressionStatement
1011         {
1012                 public readonly UnaryMutator.Mode Mode;
1013                 Expression expr, null_value;
1014                 UnaryMutator underlying;
1015                 Unwrap unwrap;
1016
1017                 public LiftedUnaryMutator (UnaryMutator.Mode mode, Expression expr, Location loc)
1018                 {
1019                         this.expr = expr;
1020                         this.Mode = mode;
1021                         this.loc = loc;
1022
1023                         eclass = ExprClass.Value;
1024                 }
1025
1026                 public override Expression DoResolve (EmitContext ec)
1027                 {
1028                         expr = expr.Resolve (ec);
1029                         if (expr == null)
1030                                 return null;
1031
1032                         unwrap = Unwrap.Create (expr, ec);
1033                         if (unwrap == null)
1034                                 return null;
1035
1036                         underlying = (UnaryMutator) new UnaryMutator (Mode, unwrap, loc).Resolve (ec);
1037                         if (underlying == null)
1038                                 return null;
1039
1040                         type = expr.Type;
1041                         null_value = LiftedNull.Create (type, loc);
1042                         return this;
1043                 }
1044
1045                 void DoEmit (EmitContext ec, bool is_expr)
1046                 {
1047                         ILGenerator ig = ec.ig;
1048                         Label is_null_label = ig.DefineLabel ();
1049                         Label end_label = ig.DefineLabel ();
1050
1051                         unwrap.EmitCheck (ec);
1052                         ig.Emit (OpCodes.Brfalse, is_null_label);
1053
1054                         if (is_expr)
1055                                 underlying.Emit (ec);
1056                         else
1057                                 underlying.EmitStatement (ec);
1058                         ig.Emit (OpCodes.Br, end_label);
1059
1060                         ig.MarkLabel (is_null_label);
1061                         if (is_expr)
1062                                 null_value.Emit (ec);
1063
1064                         ig.MarkLabel (end_label);
1065                 }
1066
1067                 public override void Emit (EmitContext ec)
1068                 {
1069                         DoEmit (ec, true);
1070                 }
1071
1072                 public override void EmitStatement (EmitContext ec)
1073                 {
1074                         DoEmit (ec, false);
1075                 }
1076         }
1077 }
1078