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