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