Merge pull request #799 from kebby/master
[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 // Copyright 2011 Xamarin Inc
13 //
14
15 using System;
16 using SLE = System.Linq.Expressions;
17
18 #if STATIC
19 using IKVM.Reflection.Emit;
20 #else
21 using System.Reflection.Emit;
22 #endif
23         
24 namespace Mono.CSharp.Nullable
25 {
26         public class NullableType : TypeExpr
27         {
28                 readonly TypeSpec underlying;
29
30                 public NullableType (TypeSpec type, Location loc)
31                 {
32                         this.underlying = type;
33                         this.loc = loc;
34                 }
35
36                 public override TypeSpec ResolveAsType (IMemberContext ec)
37                 {
38                         eclass = ExprClass.Type;
39
40                         var otype = ec.Module.PredefinedTypes.Nullable.Resolve ();
41                         if (otype == null)
42                                 return null;
43
44                         TypeArguments args = new TypeArguments (new TypeExpression (underlying, loc));
45                         GenericTypeExpr ctype = new GenericTypeExpr (otype, args, loc);
46                         
47                         type = ctype.ResolveAsType (ec);
48                         return type;
49                 }
50         }
51
52         static class NullableInfo
53         {
54                 public static MethodSpec GetConstructor (TypeSpec nullableType)
55                 {
56                         return (MethodSpec) MemberCache.FindMember (nullableType,
57                                 MemberFilter.Constructor (ParametersCompiled.CreateFullyResolved (GetUnderlyingType (nullableType))), BindingRestriction.DeclaredOnly);
58                 }
59
60                 public static MethodSpec GetHasValue (TypeSpec nullableType)
61                 {
62                         return (MethodSpec) MemberCache.FindMember (nullableType,
63                                 MemberFilter.Method ("get_HasValue", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None);
64                 }
65
66                 public static MethodSpec GetGetValueOrDefault (TypeSpec nullableType)
67                 {
68                         return (MethodSpec) MemberCache.FindMember (nullableType,
69                                 MemberFilter.Method ("GetValueOrDefault", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None);
70                 }
71
72                 //
73                 // Don't use unless really required for correctness, see Unwrap::Emit
74                 //
75                 public static MethodSpec GetValue (TypeSpec nullableType)
76                 {
77                         return (MethodSpec) MemberCache.FindMember (nullableType,
78                                 MemberFilter.Method ("get_Value", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None);
79                 }
80
81                 public static TypeSpec GetUnderlyingType (TypeSpec nullableType)
82                 {
83                         return ((InflatedTypeSpec) nullableType).TypeArguments[0];
84                 }
85
86                 public static TypeSpec GetEnumUnderlyingType (ModuleContainer module, TypeSpec nullableEnum)
87                 {
88                         return module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (module,
89                                 new[] { EnumSpec.GetUnderlyingType (GetUnderlyingType (nullableEnum)) });
90                 }
91         }
92
93         public class Unwrap : Expression, IMemoryLocation
94         {
95                 Expression expr;
96
97                 LocalTemporary temp;
98                 Expression temp_field;
99                 readonly bool useDefaultValue;
100
101                 public Unwrap (Expression expr, bool useDefaultValue = true)
102                 {
103                         this.expr = expr;
104                         this.loc = expr.Location;
105                         this.useDefaultValue = useDefaultValue;
106
107                         type = NullableInfo.GetUnderlyingType (expr.Type);
108                         eclass = expr.eclass;
109                 }
110
111                 public override bool ContainsEmitWithAwait ()
112                 {
113                         return expr.ContainsEmitWithAwait ();
114                 }
115
116                 // TODO: REMOVE
117                 public static Expression Create (Expression expr)
118                 {
119                         //
120                         // Avoid unwraping and wraping of same type
121                         //
122                         Wrap wrap = expr as Wrap;
123                         if (wrap != null)
124                                 return wrap.Child;
125
126                         return Create (expr, false);
127                 }
128
129                 public static Expression CreateUnwrapped (Expression expr)
130                 {
131                         //
132                         // Avoid unwraping and wraping of same type
133                         //
134                         Wrap wrap = expr as Wrap;
135                         if (wrap != null)
136                                 return wrap.Child;
137
138                         return Create (expr, true);
139                 }
140
141                 public static Unwrap Create (Expression expr, bool useDefaultValue)
142                 {
143                         return new Unwrap (expr, useDefaultValue);
144                 }
145                 
146                 public override Expression CreateExpressionTree (ResolveContext ec)
147                 {
148                         return expr.CreateExpressionTree (ec);
149                 }
150
151                 protected override Expression DoResolve (ResolveContext ec)
152                 {
153                         return this;
154                 }
155
156                 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
157                 {
158                         expr = expr.DoResolveLValue (ec, right_side);
159                         return this;
160                 }
161
162                 public override void Emit (EmitContext ec)
163                 {
164                         Store (ec);
165
166                         var call = new CallEmitter ();
167                         call.InstanceExpression = this;
168
169                         //
170                         // Using GetGetValueOrDefault is prefered because JIT can possibly
171                         // inline it whereas Value property contains a throw which is very
172                         // unlikely to be inlined
173                         //
174                         if (useDefaultValue)
175                                 call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null);
176                         else
177                                 call.EmitPredefined (ec, NullableInfo.GetValue (expr.Type), null);
178                 }
179
180                 public void EmitCheck (EmitContext ec)
181                 {
182                         Store (ec);
183
184                         var call = new CallEmitter ();
185                         call.InstanceExpression = this;
186
187                         call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null);
188                 }
189
190                 public override Expression EmitToField (EmitContext ec)
191                 {
192                         if (temp_field == null)
193                                 temp_field = this.expr.EmitToField (ec);
194                         
195                         return this;
196                 }
197
198                 public override bool Equals (object obj)
199                 {
200                         Unwrap uw = obj as Unwrap;
201                         return uw != null && expr.Equals (uw.expr);
202                 }
203
204                 public Expression Original {
205                         get {
206                                 return expr;
207                         }
208                 }
209                 
210                 public override int GetHashCode ()
211                 {
212                         return expr.GetHashCode ();
213                 }
214
215                 public override bool IsNull {
216                         get {
217                                 return expr.IsNull;
218                         }
219                 }
220
221                 public void Store (EmitContext ec)
222                 {
223                         if (temp != null || temp_field != null)
224                                 return;
225
226                         if (expr is VariableReference)
227                                 return;
228
229                         expr.Emit (ec);
230                         LocalVariable.Store (ec);
231                 }
232
233                 public void Load (EmitContext ec)
234                 {
235                         if (temp_field != null)
236                                 temp_field.Emit (ec);
237                         else if (expr is VariableReference)
238                                 expr.Emit (ec);
239                         else
240                                 LocalVariable.Emit (ec);
241                 }
242
243                 public override SLE.Expression MakeExpression (BuilderContext ctx)
244                 {
245                         return expr.MakeExpression (ctx);
246                 }
247
248                 public void AddressOf (EmitContext ec, AddressOp mode)
249                 {
250                         IMemoryLocation ml;
251
252                         if (temp_field != null) {
253                                 ml = temp_field as IMemoryLocation;
254                                 if (ml == null) {
255                                         var lt = new LocalTemporary (temp_field.Type);
256                                         temp_field.Emit (ec);
257                                         lt.Store (ec);
258                                         ml = lt;
259                                 }
260                         } else {
261                                 ml = expr as VariableReference;
262                         }
263
264                         if (ml != null)
265                                 ml.AddressOf (ec, mode);
266                         else
267                                 LocalVariable.AddressOf (ec, mode);
268                 }
269
270                 //
271                 // Keeps result of non-variable expression
272                 //
273                 LocalTemporary LocalVariable {
274                         get {
275                                 if (temp == null && temp_field == null)
276                                         temp = new LocalTemporary (expr.Type);
277                                 return temp;
278                         }
279                 }
280         }
281
282         //
283         // Calls get_Value method on nullable expression
284         //
285         public class UnwrapCall : CompositeExpression
286         {
287                 public UnwrapCall (Expression expr)
288                         : base (expr)
289                 {
290                 }
291
292                 protected override Expression DoResolve (ResolveContext rc)
293                 {
294                         base.DoResolve (rc);
295
296                         if (type != null)
297                                 type = NullableInfo.GetUnderlyingType (type);
298
299                         return this;
300                 }
301
302                 public override void Emit (EmitContext ec)
303                 {
304                         var call = new CallEmitter ();
305                         call.InstanceExpression = Child;
306                         call.EmitPredefined (ec, NullableInfo.GetValue (Child.Type), null);
307                 }
308         }
309
310         public class Wrap : TypeCast
311         {
312                 private Wrap (Expression expr, TypeSpec type)
313                         : base (expr, type)
314                 {
315                         eclass = ExprClass.Value;
316                 }
317
318                 public override Expression CreateExpressionTree (ResolveContext ec)
319                 {
320                         TypeCast child_cast = child as TypeCast;
321                         if (child_cast != null) {
322                                 child.Type = type;
323                                 return child_cast.CreateExpressionTree (ec);
324                         }
325
326                         var user_cast = child as UserCast;
327                         if (user_cast != null) {
328                                 child.Type = type;
329                                 return user_cast.CreateExpressionTree (ec);
330                         }
331
332                         return base.CreateExpressionTree (ec);
333                 }
334
335                 public static Expression Create (Expression expr, TypeSpec type)
336                 {
337                         //
338                         // Avoid unwraping and wraping of the same type
339                         //
340                         Unwrap unwrap = expr as Unwrap;
341                         if (unwrap != null && expr.Type == NullableInfo.GetUnderlyingType (type))
342                                 return unwrap.Original;
343                 
344                         return new Wrap (expr, type);
345                 }
346                 
347                 public override void Emit (EmitContext ec)
348                 {
349                         child.Emit (ec);
350                         ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
351                 }
352         }
353
354         //
355         // Represents null literal lifted to nullable type
356         //
357         public class LiftedNull : NullConstant, IMemoryLocation
358         {
359                 private LiftedNull (TypeSpec nullable_type, Location loc)
360                         : base (nullable_type, loc)
361                 {
362                         eclass = ExprClass.Value;
363                 }
364
365                 public static Constant Create (TypeSpec nullable, Location loc)
366                 {
367                         return new LiftedNull (nullable, loc);
368                 }
369
370                 public static Constant CreateFromExpression (ResolveContext rc, Expression e)
371                 {
372                         if (!rc.HasSet (ResolveContext.Options.ExpressionTreeConversion)) {
373                                 rc.Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'",
374                                         e.Type.GetSignatureForError ());
375                         }
376
377                         return ReducedExpression.Create (Create (e.Type, e.Location), e);
378                 }
379
380                 public override void Emit (EmitContext ec)
381                 {
382                         // TODO: generate less temporary variables
383                         LocalTemporary value_target = new LocalTemporary (type);
384
385                         value_target.AddressOf (ec, AddressOp.Store);
386                         ec.Emit (OpCodes.Initobj, type);
387                         value_target.Emit (ec);
388                         value_target.Release (ec);
389                 }
390
391                 public void AddressOf (EmitContext ec, AddressOp Mode)
392                 {
393                         LocalTemporary value_target = new LocalTemporary (type);
394                                 
395                         value_target.AddressOf (ec, AddressOp.Store);
396                         ec.Emit (OpCodes.Initobj, type);
397                         value_target.AddressOf (ec, Mode);
398                 }
399         }
400
401         //
402         // Generic lifting expression, supports all S/S? -> T/T? cases
403         //
404         public class LiftedConversion : Expression, IMemoryLocation
405         {
406                 Expression expr, null_value;
407                 Unwrap unwrap;
408
409                 public LiftedConversion (Expression expr, Unwrap unwrap, TypeSpec type)
410                 {
411                         this.expr = expr;
412                         this.unwrap = unwrap;
413                         this.loc = expr.Location;
414                         this.type = type;
415                 }
416
417                 public LiftedConversion (Expression expr, Expression unwrap, TypeSpec type)
418                         : this (expr, unwrap as Unwrap, type)
419                 {
420                 }
421
422                 public override bool ContainsEmitWithAwait ()
423                 {
424                         return unwrap.ContainsEmitWithAwait ();
425                 }
426                 
427                 public override Expression CreateExpressionTree (ResolveContext ec)
428                 {
429                         return expr.CreateExpressionTree (ec);
430                 }                       
431
432                 protected override Expression DoResolve (ResolveContext ec)
433                 {
434                         //
435                         // It's null when lifting non-nullable type
436                         //
437                         if (unwrap == null) {
438                                 // S -> T? is wrap only
439                                 if (type.IsNullableType)
440                                         return Wrap.Create (expr, type);
441
442                                 // S -> T can be simplified
443                                 return expr;
444                         }
445
446                         // Wrap target for T?
447                         if (type.IsNullableType) {
448                                 if (!expr.Type.IsNullableType) {
449                                         expr = Wrap.Create (expr, type);
450                                         if (expr == null)
451                                                 return null;
452                                 }
453
454                                 null_value = LiftedNull.Create (type, loc);
455                         } else if (TypeSpec.IsValueType (type)) {
456                                 null_value = LiftedNull.Create (type, loc);
457                         } else {
458                                 null_value = new NullConstant (type, loc);
459                         }
460
461                         eclass = ExprClass.Value;
462                         return this;
463                 }
464
465                 public override void Emit (EmitContext ec)
466                 {
467                         Label is_null_label = ec.DefineLabel ();
468                         Label end_label = ec.DefineLabel ();
469
470                         unwrap.EmitCheck (ec);
471                         ec.Emit (OpCodes.Brfalse, is_null_label);
472
473                         expr.Emit (ec);
474
475                         ec.Emit (OpCodes.Br, end_label);
476                         ec.MarkLabel (is_null_label);
477
478                         null_value.Emit (ec);
479
480                         ec.MarkLabel (end_label);
481                 }
482
483                 public void AddressOf (EmitContext ec, AddressOp mode)
484                 {
485                         unwrap.AddressOf (ec, mode);
486                 }
487         }
488
489         public class LiftedUnaryOperator : Unary, IMemoryLocation
490         {
491                 Unwrap unwrap;
492                 Expression user_operator;
493
494                 public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc)
495                         : base (op, expr, loc)
496                 {
497                 }
498
499                 public void AddressOf (EmitContext ec, AddressOp mode)
500                 {
501                         unwrap.AddressOf (ec, mode);
502                 }
503
504                 public override Expression CreateExpressionTree (ResolveContext ec)
505                 {
506                         if (user_operator != null)
507                                 return user_operator.CreateExpressionTree (ec);
508
509                         if (Oper == Operator.UnaryPlus)
510                                 return Expr.CreateExpressionTree (ec);
511
512                         return base.CreateExpressionTree (ec);
513                 }
514
515                 protected override Expression DoResolve (ResolveContext ec)
516                 {
517                         unwrap = Unwrap.Create (Expr, false);
518                         if (unwrap == null)
519                                 return null;
520
521                         Expression res = base.ResolveOperator (ec, unwrap);
522                         if (res != this) {
523                                 if (user_operator == null)
524                                         return res;
525                         } else {
526                                 res = Expr = LiftExpression (ec, Expr);
527                         }
528
529                         if (res == null)
530                                 return null;
531
532                         eclass = ExprClass.Value;
533                         type = res.Type;
534                         return this;
535                 }
536
537                 public override void Emit (EmitContext ec)
538                 {
539                         Label is_null_label = ec.DefineLabel ();
540                         Label end_label = ec.DefineLabel ();
541
542                         unwrap.EmitCheck (ec);
543                         ec.Emit (OpCodes.Brfalse, is_null_label);
544
545                         if (user_operator != null) {
546                                 user_operator.Emit (ec);
547                         } else {
548                                 EmitOperator (ec, NullableInfo.GetUnderlyingType (type));
549                         }
550
551                         ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
552                         ec.Emit (OpCodes.Br_S, end_label);
553
554                         ec.MarkLabel (is_null_label);
555                         LiftedNull.Create (type, loc).Emit (ec);
556
557                         ec.MarkLabel (end_label);
558                 }
559
560                 static Expression LiftExpression (ResolveContext ec, Expression expr)
561                 {
562                         var lifted_type = new NullableType (expr.Type, expr.Location);
563                         if (lifted_type.ResolveAsType (ec) == null)
564                                 return null;
565
566                         expr.Type = lifted_type.Type;
567                         return expr;
568                 }
569
570                 protected override Expression ResolveEnumOperator (ResolveContext ec, Expression expr, TypeSpec[] predefined)
571                 {
572                         expr = base.ResolveEnumOperator (ec, expr, predefined);
573                         if (expr == null)
574                                 return null;
575
576                         Expr = LiftExpression (ec, Expr);
577                         return LiftExpression (ec, expr);
578                 }
579
580                 protected override Expression ResolveUserOperator (ResolveContext ec, Expression expr)
581                 {
582                         expr = base.ResolveUserOperator (ec, expr);
583                         if (expr == null)
584                                 return null;
585
586                         //
587                         // When a user operator is of non-nullable type
588                         //
589                         if (Expr is Unwrap) {
590                                 user_operator = LiftExpression (ec, expr);
591                                 return user_operator;
592                         }
593
594                         return expr;
595                 }
596         }
597
598         //
599         // Lifted version of binary operators
600         //
601         class LiftedBinaryOperator : Expression
602         {
603                 public LiftedBinaryOperator (Binary b)
604                 {
605                         this.Binary = b;
606                         this.loc = b.Location;
607                 }
608
609                 public Binary Binary { get; private set; }
610
611                 public Expression Left { get; set; }
612
613                 public Expression Right { get; set; }
614
615                 public Unwrap UnwrapLeft { get; set; }
616
617                 public Unwrap UnwrapRight { get; set; }
618
619                 public MethodSpec UserOperator { get; set; }
620
621                 bool IsBitwiseBoolean {
622                         get {
623                                 return (Binary.Oper == Binary.Operator.BitwiseAnd || Binary.Oper == Binary.Operator.BitwiseOr) &&
624                                 ((UnwrapLeft != null && UnwrapLeft.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) ||
625                                  (UnwrapRight != null && UnwrapRight.Type.BuiltinType == BuiltinTypeSpec.Type.Bool));
626                         }
627                 }
628
629                 public override bool ContainsEmitWithAwait ()
630                 {
631                         return Left.ContainsEmitWithAwait () || Right.ContainsEmitWithAwait ();
632                 }
633
634                 public override Expression CreateExpressionTree (ResolveContext rc)
635                 {
636                         if (UserOperator != null) {
637                                 Arguments args = new Arguments (2);
638                                 args.Add (new Argument (Binary.Left));
639                                 args.Add (new Argument (Binary.Right));
640
641                                 var method = new UserOperatorCall (UserOperator, args, Binary.CreateExpressionTree, loc);
642                                 return method.CreateExpressionTree (rc);
643                         }
644
645                         return Binary.CreateExpressionTree (rc);
646                 }
647
648                 protected override Expression DoResolve (ResolveContext rc)
649                 {
650                         if (rc.IsRuntimeBinder) {
651                                 if (UnwrapLeft == null && !Left.Type.IsNullableType)
652                                         Left = Wrap.Create (Left, rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { Left.Type }));
653
654                                 if (UnwrapRight == null && !Right.Type.IsNullableType)
655                                         Right = Wrap.Create (Right, rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { Right.Type }));
656                         } else {
657                                 if (UnwrapLeft == null && Left != null && Left.Type.IsNullableType) {
658                                         Left = Unwrap.CreateUnwrapped (Left);
659                                         UnwrapLeft = Left as Unwrap;
660                                 }
661
662                                 if (UnwrapRight == null && Right != null && Right.Type.IsNullableType) {
663                                         Right = Unwrap.CreateUnwrapped (Right);
664                                         UnwrapRight = Right as Unwrap;
665                                 }
666                         }
667
668                         type = Binary.Type;
669                         eclass = Binary.eclass; 
670
671                         return this;
672                 }
673
674                 public override void Emit (EmitContext ec)
675                 {
676                         if (IsBitwiseBoolean && UserOperator == null) {
677                                 EmitBitwiseBoolean (ec);
678                                 return;
679                         }
680
681                         if ((Binary.Oper & Binary.Operator.EqualityMask) != 0) {
682                                 EmitEquality (ec);
683                                 return;
684                         }
685
686                         Label is_null_label = ec.DefineLabel ();
687                         Label end_label = ec.DefineLabel ();
688
689                         if (ec.HasSet (BuilderContext.Options.AsyncBody) && Right.ContainsEmitWithAwait ()) {
690                                 Left = Left.EmitToField (ec);
691                                 Right = Right.EmitToField (ec);
692                         }
693
694                         if (UnwrapLeft != null) {
695                                 UnwrapLeft.EmitCheck (ec);
696                         }
697
698                         //
699                         // Don't emit HasValue check when left and right expressions are same
700                         //
701                         if (UnwrapRight != null && !Binary.Left.Equals (Binary.Right)) {
702                                 UnwrapRight.EmitCheck (ec);
703                                 if (UnwrapLeft != null) {
704                                         ec.Emit (OpCodes.And);
705                                 }
706                         }
707
708                         ec.Emit (OpCodes.Brfalse, is_null_label);
709
710                         if (UserOperator != null) {
711                                 var args = new Arguments (2);
712                                 args.Add (new Argument (Left));
713                                 args.Add (new Argument (Right));
714
715                                 var call = new CallEmitter ();
716                                 call.EmitPredefined (ec, UserOperator, args);
717                         } else {
718                                 Binary.EmitOperator (ec, Left, Right);
719                         }
720
721                         //
722                         // Wrap the result when the operator return type is nullable type
723                         //
724                         if (type.IsNullableType)
725                                 ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
726
727                         ec.Emit (OpCodes.Br_S, end_label);
728                         ec.MarkLabel (is_null_label);
729
730                         if ((Binary.Oper & Binary.Operator.ComparisonMask) != 0) {
731                                 ec.EmitInt (0);
732                         } else {
733                                 LiftedNull.Create (type, loc).Emit (ec);
734                         }
735
736                         ec.MarkLabel (end_label);
737                 }
738
739                 void EmitBitwiseBoolean (EmitContext ec)
740                 {
741                         Label load_left = ec.DefineLabel ();
742                         Label load_right = ec.DefineLabel ();
743                         Label end_label = ec.DefineLabel ();
744                         Label is_null_label = ec.DefineLabel ();
745
746                         bool or = Binary.Oper == Binary.Operator.BitwiseOr;
747
748                         //
749                         // Both operands are bool? types
750                         //
751                         if (UnwrapLeft != null && UnwrapRight != null) {
752                                 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) {
753                                         Left = Left.EmitToField (ec);
754                                         Right = Right.EmitToField (ec);
755                                 }
756
757                                 Left.Emit (ec);
758                                 ec.Emit (OpCodes.Brtrue_S, load_right);
759
760                                 Right.Emit (ec);
761                                 ec.Emit (OpCodes.Brtrue_S, load_left);
762
763                                 UnwrapLeft.EmitCheck (ec);
764                                 ec.Emit (OpCodes.Brfalse_S, load_right);
765
766                                 // load left
767                                 ec.MarkLabel (load_left);
768                                 if (or)
769                                         UnwrapRight.Load (ec);
770                                 else
771                                         UnwrapLeft.Load (ec);
772
773                                 ec.Emit (OpCodes.Br_S, end_label);
774
775                                 // load right
776                                 ec.MarkLabel (load_right);
777                                 if (or)
778                                         UnwrapLeft.Load (ec);
779                                 else
780                                         UnwrapRight.Load (ec);
781
782                                 ec.MarkLabel (end_label);
783                                 return;
784                         }
785
786                         //
787                         // Faster version when one operand is bool
788                         //
789                         if (UnwrapLeft == null) {
790                                 //
791                                 // (bool, bool?)
792                                 //
793                                 // Optimizes remaining (false & bool?), (true | bool?) which are not easy to handle
794                                 // in binary expression reduction
795                                 //
796                                 var c = Left as BoolConstant;
797                                 if (c != null) {
798                                         // Keep evaluation order
799                                         UnwrapRight.Store (ec);
800
801                                         ec.EmitInt (or ? 1 : 0);
802                                         ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
803                                 } else if (Left.IsNull) {
804                                         UnwrapRight.Emit (ec);
805                                         ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label);
806
807                                         UnwrapRight.Load (ec);
808                                         ec.Emit (OpCodes.Br_S, end_label);
809
810                                         ec.MarkLabel (is_null_label);
811                                         LiftedNull.Create (type, loc).Emit (ec);
812                                 } else {
813                                         Left.Emit (ec);
814                                         ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right);
815
816                                         ec.EmitInt (or ? 1 : 0);
817                                         ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
818
819                                         ec.Emit (OpCodes.Br_S, end_label);
820
821                                         ec.MarkLabel (load_right);
822                                         UnwrapRight.Original.Emit (ec);
823                                 }
824                         } else {
825                                 //
826                                 // (bool?, bool)
827                                 //
828                                 // Keep left-right evaluation order
829                                 UnwrapLeft.Store (ec);
830
831                                 //
832                                 // Optimizes remaining (bool? & false), (bool? | true) which are not easy to handle
833                                 // in binary expression reduction
834                                 //
835                                 var c = Right as BoolConstant;
836                                 if (c != null) {
837                                         ec.EmitInt (or ? 1 : 0);
838                                         ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
839                                 } else if (Right.IsNull) {
840                                         UnwrapLeft.Emit (ec);
841                                         ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, is_null_label);
842
843                                         UnwrapLeft.Load (ec);
844                                         ec.Emit (OpCodes.Br_S, end_label);
845
846                                         ec.MarkLabel (is_null_label);
847                                         LiftedNull.Create (type, loc).Emit (ec);
848                                 } else {
849                                         Right.Emit (ec);
850                                         ec.Emit (or ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, load_right);
851
852                                         ec.EmitInt (or ? 1 : 0);
853                                         ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
854
855                                         ec.Emit (OpCodes.Br_S, end_label);
856
857                                         ec.MarkLabel (load_right);
858
859                                         UnwrapLeft.Load (ec);
860                                 }
861                         }
862
863                         ec.MarkLabel (end_label);
864                 }
865
866                 //
867                 // Emits optimized equality or inequality operator when possible
868                 //
869                 void EmitEquality (EmitContext ec)
870                 {
871                         //
872                         // Either left or right is null
873                         // 
874                         if (UnwrapLeft != null && Binary.Right.IsNull) { // TODO: Optimize for EmitBranchable
875                                 //
876                                 // left.HasValue == false 
877                                 //
878                                 UnwrapLeft.EmitCheck (ec);
879                                 if (Binary.Oper == Binary.Operator.Equality) {
880                                         ec.EmitInt (0);
881                                         ec.Emit (OpCodes.Ceq);
882                                 }
883                                 return;
884                         }
885
886                         if (UnwrapRight != null && Binary.Left.IsNull) {
887                                 //
888                                 // right.HasValue == false 
889                                 //
890                                 UnwrapRight.EmitCheck (ec);
891                                 if (Binary.Oper == Binary.Operator.Equality) {
892                                         ec.EmitInt (0);
893                                         ec.Emit (OpCodes.Ceq);
894                                 }
895                                 return;
896                         }
897
898                         Label dissimilar_label = ec.DefineLabel ();
899                         Label end_label = ec.DefineLabel ();
900
901                         if (UserOperator != null) {
902                                 var left = Left;
903
904                                 if (UnwrapLeft != null) {
905                                         UnwrapLeft.EmitCheck (ec);
906                                 } else {
907                                         // Keep evaluation order same
908                                         if (!(Left is VariableReference)) {
909                                                 Left.Emit (ec);
910                                                 var lt = new LocalTemporary (Left.Type);
911                                                 lt.Store (ec);
912                                                 left = lt;
913                                         }
914                                 }
915
916                                 if (UnwrapRight != null) {
917                                         UnwrapRight.EmitCheck (ec);
918
919                                         if (UnwrapLeft != null) {
920                                                 ec.Emit (OpCodes.Bne_Un, dissimilar_label);
921
922                                                 Label compare_label = ec.DefineLabel ();
923                                                 UnwrapLeft.EmitCheck (ec);
924                                                 ec.Emit (OpCodes.Brtrue, compare_label);
925
926                                                 if (Binary.Oper == Binary.Operator.Equality)
927                                                         ec.EmitInt (1);
928                                                 else
929                                                         ec.EmitInt (0);
930
931                                                 ec.Emit (OpCodes.Br, end_label);
932
933                                                 ec.MarkLabel (compare_label);
934                                         } else {
935                                                 ec.Emit (OpCodes.Brfalse, dissimilar_label);
936                                         }
937                                 } else {
938                                         ec.Emit (OpCodes.Brfalse, dissimilar_label);
939                                 }
940
941                                 var args = new Arguments (2);
942                                 args.Add (new Argument (left));
943                                 args.Add (new Argument (Right));
944
945                                 var call = new CallEmitter ();
946                                 call.EmitPredefined (ec, UserOperator, args);
947                         } else {
948                                 if (ec.HasSet (BuilderContext.Options.AsyncBody) && Binary.Right.ContainsEmitWithAwait ()) {
949                                         Left = Left.EmitToField (ec);
950                                         Right = Right.EmitToField (ec);
951                                 }
952
953                                 //
954                                 // Emit underlying value comparison first.
955                                 //
956                                 // For this code: int? a = 1; bool b = a == 1;
957                                 //
958                                 // We emit something similar to this. Expressions with side effects have local
959                                 // variable created by Unwrap expression
960                                 //
961                                 //      left.GetValueOrDefault ()
962                                 //      right
963                                 //      bne.un.s   dissimilar_label
964                                 //  left.HasValue
965                                 //      br.s       end_label
966                                 // dissimilar_label:
967                                 //      ldc.i4.0
968                                 // end_label:
969                                 //
970
971                                 Left.Emit (ec);
972                                 Right.Emit (ec);
973
974                                 ec.Emit (OpCodes.Bne_Un_S, dissimilar_label);
975
976                                 //
977                                 // Check both left and right expressions for Unwrap call in which
978                                 // case we need to run get_HasValue() check because the type is
979                                 // nullable and could have null value
980                                 //
981                                 if (UnwrapLeft != null)
982                                         UnwrapLeft.EmitCheck (ec);
983
984                                 if (UnwrapRight != null)
985                                         UnwrapRight.EmitCheck (ec);
986
987                                 if (UnwrapLeft != null && UnwrapRight != null) {
988                                         if (Binary.Oper == Binary.Operator.Inequality)
989                                                 ec.Emit (OpCodes.Xor);
990                                         else
991                                                 ec.Emit (OpCodes.Ceq);
992                                 } else {
993                                         if (Binary.Oper == Binary.Operator.Inequality) {
994                                                 ec.EmitInt (0);
995                                                 ec.Emit (OpCodes.Ceq);
996                                         }
997                                 }
998                         }
999
1000                         ec.Emit (OpCodes.Br_S, end_label);
1001
1002                         ec.MarkLabel (dissimilar_label);
1003                         if (Binary.Oper == Binary.Operator.Inequality)
1004                                 ec.EmitInt (1);
1005                         else
1006                                 ec.EmitInt (0);
1007
1008                         ec.MarkLabel (end_label);
1009                 }
1010
1011                 public override SLE.Expression MakeExpression (BuilderContext ctx)
1012                 {
1013                         return Binary.MakeExpression (ctx, Left, Right);
1014                 }
1015         }
1016
1017         public class NullCoalescingOperator : Expression
1018         {
1019                 Expression left, right;
1020                 Unwrap unwrap;
1021
1022                 public NullCoalescingOperator (Expression left, Expression right)
1023                 {
1024                         this.left = left;
1025                         this.right = right;
1026                         this.loc = left.Location;
1027                 }
1028
1029                 public Expression LeftExpression {
1030                         get {
1031                                 return left;
1032                         }
1033                 }
1034
1035                 public Expression RightExpression {
1036                         get {
1037                                 return right;
1038                         }
1039                 }
1040                 
1041                 public override Expression CreateExpressionTree (ResolveContext ec)
1042                 {
1043                         if (left is NullLiteral)
1044                                 ec.Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side");
1045
1046                         UserCast uc = left as UserCast;
1047                         Expression conversion = null;
1048                         if (uc != null) {
1049                                 left = uc.Source;
1050
1051                                 Arguments c_args = new Arguments (2);
1052                                 c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
1053                                 c_args.Add (new Argument (left.CreateExpressionTree (ec)));
1054                                 conversion = CreateExpressionFactoryCall (ec, "Lambda", c_args);
1055                         }
1056
1057                         Arguments args = new Arguments (3);
1058                         args.Add (new Argument (left.CreateExpressionTree (ec)));
1059                         args.Add (new Argument (right.CreateExpressionTree (ec)));
1060                         if (conversion != null)
1061                                 args.Add (new Argument (conversion));
1062                         
1063                         return CreateExpressionFactoryCall (ec, "Coalesce", args);
1064                 }
1065
1066                 Expression ConvertExpression (ResolveContext ec)
1067                 {
1068                         // TODO: ImplicitConversionExists should take care of this
1069                         if (left.eclass == ExprClass.MethodGroup)
1070                                 return null;
1071
1072                         TypeSpec ltype = left.Type;
1073
1074                         //
1075                         // If left is a nullable type and an implicit conversion exists from right to underlying type of left,
1076                         // the result is underlying type of left
1077                         //
1078                         if (ltype.IsNullableType) {
1079                                 unwrap = Unwrap.Create (left, false);
1080                                 if (unwrap == null)
1081                                         return null;
1082
1083                                 //
1084                                 // Reduce (left ?? null) to left
1085                                 //
1086                                 if (right.IsNull)
1087                                         return ReducedExpression.Create (left, this);
1088
1089                                 if (Convert.ImplicitConversionExists (ec, right, unwrap.Type)) {
1090                                         left = unwrap;
1091                                         ltype = left.Type;
1092
1093                                         //
1094                                         // If right is a dynamic expression, the result type is dynamic
1095                                         //
1096                                         if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1097                                                 type = right.Type;
1098
1099                                                 // Need to box underlying value type
1100                                                 left = Convert.ImplicitBoxingConversion (left, ltype, type);
1101                                                 return this;
1102                                         }
1103
1104                                         right = Convert.ImplicitConversion (ec, right, ltype, loc);
1105                                         type = ltype;
1106                                         return this;
1107                                 }
1108                         } else if (TypeSpec.IsReferenceType (ltype)) {
1109                                 if (Convert.ImplicitConversionExists (ec, right, ltype)) {
1110                                         //
1111                                         // If right is a dynamic expression, the result type is dynamic
1112                                         //
1113                                         if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1114                                                 type = right.Type;
1115                                                 return this;
1116                                         }
1117
1118                                         //
1119                                         // Reduce ("foo" ?? expr) to expression
1120                                         //
1121                                         Constant lc = left as Constant;
1122                                         if (lc != null && !lc.IsDefaultValue)
1123                                                 return ReducedExpression.Create (lc, this);
1124
1125                                         //
1126                                         // Reduce (left ?? null) to left OR (null-constant ?? right) to right
1127                                         //
1128                                         if (right.IsNull || lc != null)
1129                                                 return ReducedExpression.Create (lc != null ? right : left, this);
1130
1131                                         right = Convert.ImplicitConversion (ec, right, ltype, loc);
1132                                         type = ltype;
1133                                         return this;
1134                                 }
1135
1136                                 //
1137                                 // Special case null ?? null
1138                                 //
1139                                 if (ltype == right.Type) {
1140                                         type = ltype;
1141                                         return this;
1142                                 }
1143                         } else {
1144                                 return null;
1145                         }
1146
1147                         TypeSpec rtype = right.Type;
1148                         if (!Convert.ImplicitConversionExists (ec, unwrap ?? left, rtype) || right.eclass == ExprClass.MethodGroup)
1149                                 return null;
1150
1151                         //
1152                         // Reduce (null ?? right) to right
1153                         //
1154                         if (left.IsNull)
1155                                 return ReducedExpression.Create (right, this).Resolve (ec);
1156
1157                         left = Convert.ImplicitConversion (ec, unwrap ?? left, rtype, loc);
1158                         type = rtype;
1159                         return this;
1160                 }
1161
1162                 public override bool ContainsEmitWithAwait ()
1163                 {
1164                         if (unwrap != null)
1165                                 return unwrap.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1166
1167                         return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1168                 }
1169
1170                 protected override Expression DoResolve (ResolveContext ec)
1171                 {
1172                         left = left.Resolve (ec);
1173                         right = right.Resolve (ec);
1174
1175                         if (left == null || right == null)
1176                                 return null;
1177
1178                         eclass = ExprClass.Value;
1179
1180                         Expression e = ConvertExpression (ec);
1181                         if (e == null) {
1182                                 Binary.Error_OperatorCannotBeApplied (ec, left, right, "??", loc);
1183                                 return null;
1184                         }
1185
1186                         return e;
1187                 }
1188
1189                 public override void Emit (EmitContext ec)
1190                 {
1191                         Label end_label = ec.DefineLabel ();
1192
1193                         if (unwrap != null) {
1194                                 Label is_null_label = ec.DefineLabel ();
1195
1196                                 unwrap.EmitCheck (ec);
1197                                 ec.Emit (OpCodes.Brfalse, is_null_label);
1198
1199                                 left.Emit (ec);
1200                                 ec.Emit (OpCodes.Br, end_label);
1201
1202                                 ec.MarkLabel (is_null_label);
1203                                 right.Emit (ec);
1204
1205                                 ec.MarkLabel (end_label);
1206                                 return;
1207                         }
1208
1209                         left.Emit (ec);
1210                         ec.Emit (OpCodes.Dup);
1211
1212                         // Only to make verifier happy
1213                         if (left.Type.IsGenericParameter)
1214                                 ec.Emit (OpCodes.Box, left.Type);
1215
1216                         ec.Emit (OpCodes.Brtrue, end_label);
1217
1218                         ec.Emit (OpCodes.Pop);
1219                         right.Emit (ec);
1220
1221                         ec.MarkLabel (end_label);
1222                 }
1223
1224                 protected override void CloneTo (CloneContext clonectx, Expression t)
1225                 {
1226                         NullCoalescingOperator target = (NullCoalescingOperator) t;
1227
1228                         target.left = left.Clone (clonectx);
1229                         target.right = right.Clone (clonectx);
1230                 }
1231                 
1232                 public override object Accept (StructuralVisitor visitor)
1233                 {
1234                         return visitor.Visit (this);
1235                 }
1236         }
1237
1238         class LiftedUnaryMutator : UnaryMutator
1239         {
1240                 public LiftedUnaryMutator (Mode mode, Expression expr, Location loc)
1241                         : base (mode, expr, loc)
1242                 {
1243                 }
1244
1245                 protected override Expression DoResolve (ResolveContext ec)
1246                 {
1247                         var orig_expr = expr;
1248
1249                         expr = Unwrap.Create (expr);
1250
1251                         var res = base.DoResolveOperation (ec);
1252
1253                         expr = orig_expr;
1254                         type = expr.Type;
1255
1256                         return res;
1257                 }
1258
1259                 protected override void EmitOperation (EmitContext ec)
1260                 {
1261                         Label is_null_label = ec.DefineLabel ();
1262                         Label end_label = ec.DefineLabel ();
1263
1264                         LocalTemporary lt = new LocalTemporary (type);
1265
1266                         // Value is on the stack
1267                         lt.Store (ec);
1268
1269                         var call = new CallEmitter ();
1270                         call.InstanceExpression = lt;
1271                         call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null);
1272
1273                         ec.Emit (OpCodes.Brfalse, is_null_label);
1274
1275                         call = new CallEmitter ();
1276                         call.InstanceExpression = lt;
1277                         call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null);
1278
1279                         lt.Release (ec);
1280
1281                         base.EmitOperation (ec);
1282
1283                         ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
1284                         ec.Emit (OpCodes.Br_S, end_label);
1285
1286                         ec.MarkLabel (is_null_label);
1287                         LiftedNull.Create (type, loc).Emit (ec);
1288
1289                         ec.MarkLabel (end_label);
1290                 }
1291         }
1292 }
1293