Merge pull request #799 from kebby/master
[mono.git] / mcs / class / System.Core / System.Linq.Expressions / BinaryExpression.cs
1 //
2 // BinaryExpression.cs
3 //
4 // Author:
5 //   Jb Evain (jbevain@novell.com)
6 //   Miguel de Icaza (miguel@novell.com)
7 //
8 // Contains code from the Mono C# compiler:
9 //   Marek Safar (marek.safar@seznam.cz)
10 //   Martin Baulig (martin@ximian.com)
11 //   Raja Harinath (harinath@gmail.com)
12 //
13 // (C) 2001-2003 Ximian, Inc.
14 // (C) 2004-2008 Novell, Inc. (http://www.novell.com)
15 //
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
23 //
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 //
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 //
35
36 using System;
37 using System.Reflection;
38 #if !FULL_AOT_RUNTIME
39 using System.Reflection.Emit;
40 #endif
41
42 namespace System.Linq.Expressions {
43
44         public sealed class BinaryExpression : Expression {
45
46                 Expression left;
47                 Expression right;
48                 LambdaExpression conversion;
49                 MethodInfo method;
50                 bool lift_to_null, is_lifted;
51
52                 public Expression Left {
53                         get { return left; }
54                 }
55
56                 public Expression Right {
57                         get { return right; }
58                 }
59
60                 public MethodInfo Method {
61                         get { return method; }
62                 }
63
64                 public bool IsLifted {
65                         get { return is_lifted;  }
66                 }
67
68                 public bool IsLiftedToNull {
69                         get { return lift_to_null; }
70                 }
71
72                 public LambdaExpression Conversion {
73                         get { return conversion; }
74                 }
75
76                 internal BinaryExpression (ExpressionType node_type, Type type, Expression left, Expression right)
77                         : base (node_type, type)
78                 {
79                         this.left = left;
80                         this.right = right;
81                 }
82
83                 internal BinaryExpression (ExpressionType node_type, Type type, Expression left, Expression right, MethodInfo method)
84                         : base (node_type, type)
85                 {
86                         this.left = left;
87                         this.right = right;
88                         this.method = method;
89                 }
90
91                 internal BinaryExpression (ExpressionType node_type, Type type, Expression left, Expression right, bool lift_to_null,
92                         bool is_lifted, MethodInfo method, LambdaExpression conversion) : base (node_type, type)
93                 {
94                         this.left = left;
95                         this.right = right;
96                         this.method = method;
97                         this.conversion = conversion;
98                         this.lift_to_null = lift_to_null;
99                         this.is_lifted = is_lifted;
100                 }
101
102 #if !FULL_AOT_RUNTIME
103                 void EmitArrayAccess (EmitContext ec)
104                 {
105                         left.Emit (ec);
106                         right.Emit (ec);
107                         ec.ig.Emit (OpCodes.Ldelem, this.Type);
108                 }
109
110                 void EmitLogicalBinary (EmitContext ec)
111                 {
112                         switch (NodeType) {
113                         case ExpressionType.And:
114                         case ExpressionType.Or:
115                                 if (!IsLifted)
116                                         EmitLogical (ec);
117                                 else if (Type == typeof (bool?))
118                                         EmitLiftedLogical (ec);
119                                 else
120                                         EmitLiftedArithmeticBinary (ec);
121                                 break;
122                         case ExpressionType.AndAlso:
123                         case ExpressionType.OrElse:
124                                 if (!IsLifted)
125                                         EmitLogicalShortCircuit (ec);
126                                 else
127                                         EmitLiftedLogicalShortCircuit (ec);
128                                 break;
129                         }
130                 }
131
132                 void EmitLogical (EmitContext ec)
133                 {
134                         EmitNonLiftedBinary (ec);
135                 }
136
137                 void EmitLiftedLogical (EmitContext ec)
138                 {
139                         var ig = ec.ig;
140                         var and = NodeType == ExpressionType.And;
141                         var left = ec.EmitStored (this.left);
142                         var right = ec.EmitStored (this.right);
143
144                         var ret_from_left = ig.DefineLabel ();
145                         var ret_from_right = ig.DefineLabel ();
146                         var done = ig.DefineLabel ();
147
148                         ec.EmitNullableGetValueOrDefault (left);
149                         ig.Emit (OpCodes.Brtrue, ret_from_left);
150                         ec.EmitNullableGetValueOrDefault (right);
151                         ig.Emit (OpCodes.Brtrue, ret_from_right);
152
153                         ec.EmitNullableHasValue (left);
154                         ig.Emit (OpCodes.Brfalse, ret_from_left);
155
156                         ig.MarkLabel (ret_from_right);
157                         ec.EmitLoad (and ? left : right);
158                         ig.Emit (OpCodes.Br, done);
159
160                         ig.MarkLabel (ret_from_left);
161                         ec.EmitLoad (and ? right : left);
162
163                         ig.MarkLabel (done);
164                 }
165
166                 void EmitLogicalShortCircuit (EmitContext ec)
167                 {
168                         var ig = ec.ig;
169                         var and = NodeType == ExpressionType.AndAlso;
170                         var ret = ig.DefineLabel ();
171                         var done = ig.DefineLabel ();
172
173                         ec.Emit (left);
174                         ig.Emit (and ? OpCodes.Brfalse : OpCodes.Brtrue, ret);
175
176                         ec.Emit (right);
177
178                         ig.Emit (OpCodes.Br, done);
179
180                         ig.MarkLabel (ret);
181                         ig.Emit (and ? OpCodes.Ldc_I4_0 : OpCodes.Ldc_I4_1);
182
183                         ig.MarkLabel (done);
184                 }
185
186                 MethodInfo GetFalseOperator ()
187                 {
188                         return GetFalseOperator (left.Type.GetNotNullableType ());
189                 }
190
191                 MethodInfo GetTrueOperator ()
192                 {
193                         return GetTrueOperator (left.Type.GetNotNullableType ());
194                 }
195
196                 void EmitUserDefinedLogicalShortCircuit (EmitContext ec)
197                 {
198                         var ig = ec.ig;
199                         var and = NodeType == ExpressionType.AndAlso;
200
201                         var done = ig.DefineLabel ();
202
203                         var left = ec.EmitStored (this.left);
204
205                         ec.EmitLoad (left);
206                         ig.Emit (OpCodes.Dup);
207                         ec.EmitCall (and ? GetFalseOperator () : GetTrueOperator ());
208                         ig.Emit (OpCodes.Brtrue, done);
209
210                         ec.Emit (this.right);
211                         ec.EmitCall (method);
212
213                         ig.MarkLabel (done);
214                 }
215
216                 void EmitLiftedLogicalShortCircuit (EmitContext ec)
217                 {
218                         var ig = ec.ig;
219                         var and = NodeType == ExpressionType.AndAlso;
220                         var left_is_null = ig.DefineLabel ();
221                         var ret_from_left = ig.DefineLabel ();
222                         var ret_null = ig.DefineLabel ();
223                         var ret_new = ig.DefineLabel();
224                         var done = ig.DefineLabel();
225
226                         var left = ec.EmitStored (this.left);
227
228                         ec.EmitNullableHasValue (left);
229                         ig.Emit (OpCodes.Brfalse, left_is_null);
230
231                         ec.EmitNullableGetValueOrDefault (left);
232
233                         ig.Emit (OpCodes.Ldc_I4_0);
234                         ig.Emit (OpCodes.Ceq);
235                         ig.Emit (and ? OpCodes.Brtrue : OpCodes.Brfalse, ret_from_left);
236
237                         ig.MarkLabel (left_is_null);
238                         var right = ec.EmitStored (this.right);
239
240                         ec.EmitNullableHasValue (right);
241                         ig.Emit (OpCodes.Brfalse_S, ret_null);
242
243                         ec.EmitNullableGetValueOrDefault (right);
244
245                         ig.Emit (OpCodes.Ldc_I4_0);
246                         ig.Emit (OpCodes.Ceq);
247
248                         ig.Emit (and ? OpCodes.Brtrue : OpCodes.Brfalse, ret_from_left);
249
250                         ec.EmitNullableHasValue (left);
251                         ig.Emit (OpCodes.Brfalse, ret_null);
252
253                         ig.Emit (and ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
254                         ig.Emit (OpCodes.Br_S, ret_new);
255
256                         ig.MarkLabel (ret_from_left);
257                         ig.Emit (and ? OpCodes.Ldc_I4_0 : OpCodes.Ldc_I4_1);
258
259                         ig.MarkLabel (ret_new);
260                         ec.EmitNullableNew (Type);
261                         ig.Emit (OpCodes.Br, done);
262
263                         ig.MarkLabel (ret_null);
264                         var ret = ig.DeclareLocal (Type);
265                         ec.EmitNullableInitialize (ret);
266
267                         ig.MarkLabel (done);
268                 }
269
270                 void EmitCoalesce (EmitContext ec)
271                 {
272                         var ig = ec.ig;
273                         var done = ig.DefineLabel ();
274                         var load_right = ig.DefineLabel ();
275
276                         var left = ec.EmitStored (this.left);
277                         var left_is_nullable = left.LocalType.IsNullable ();
278
279                         if (left_is_nullable)
280                                 ec.EmitNullableHasValue (left);
281                         else
282                                 ec.EmitLoad (left);
283
284                         ig.Emit (OpCodes.Brfalse, load_right);
285
286                         if (left_is_nullable && !Type.IsNullable ())
287                                 ec.EmitNullableGetValue (left);
288                         else
289                                 ec.EmitLoad (left);
290
291                         ig.Emit (OpCodes.Br, done);
292
293                         ig.MarkLabel (load_right);
294                         ec.Emit (this.right);
295
296                         ig.MarkLabel (done);
297                 }
298
299                 void EmitConvertedCoalesce (EmitContext ec)
300                 {
301                         var ig = ec.ig;
302                         var done = ig.DefineLabel ();
303                         var load_right = ig.DefineLabel ();
304
305                         var left = ec.EmitStored (this.left);
306
307                         if (left.LocalType.IsNullable ())
308                                 ec.EmitNullableHasValue (left);
309                         else
310                                 ec.EmitLoad (left);
311
312                         ig.Emit (OpCodes.Brfalse, load_right);
313
314                         ec.Emit (conversion);
315                         ec.EmitLoad (left);
316                         ig.Emit (OpCodes.Callvirt, conversion.Type.GetInvokeMethod ());
317
318                         ig.Emit (OpCodes.Br, done);
319
320                         ig.MarkLabel (load_right);
321                         ec.Emit (this.right);
322
323                         ig.MarkLabel (done);
324                 }
325
326                 static bool IsInt32OrInt64 (Type type)
327                 {
328                         return type == typeof (int) || type == typeof (long);
329                 }
330
331                 static bool IsSingleOrDouble (Type type)
332                 {
333                         return type == typeof (float) || type == typeof (double);
334                 }
335
336                 void EmitBinaryOperator (EmitContext ec)
337                 {
338                         var ig = ec.ig;
339                         bool is_unsigned = IsUnsigned (left.Type);
340
341                         switch (NodeType) {
342                         case ExpressionType.Add:
343                                 ig.Emit (OpCodes.Add);
344                                 break;
345                         case ExpressionType.AddChecked:
346                                 if (IsInt32OrInt64 (left.Type))
347                                         ig.Emit (OpCodes.Add_Ovf);
348                                 else
349                                         ig.Emit (is_unsigned ? OpCodes.Add_Ovf_Un : OpCodes.Add);
350                                 break;
351                         case ExpressionType.Subtract:
352                                 ig.Emit (OpCodes.Sub);
353                                 break;
354                         case ExpressionType.SubtractChecked:
355                                 if (IsInt32OrInt64 (left.Type))
356                                         ig.Emit (OpCodes.Sub_Ovf);
357                                 else
358                                         ig.Emit (is_unsigned ? OpCodes.Sub_Ovf_Un : OpCodes.Sub);
359                                 break;
360                         case ExpressionType.Multiply:
361                                 ig.Emit (OpCodes.Mul);
362                                 break;
363                         case ExpressionType.MultiplyChecked:
364                                 if (IsInt32OrInt64 (left.Type))
365                                         ig.Emit (OpCodes.Mul_Ovf);
366                                 else
367                                         ig.Emit (is_unsigned ? OpCodes.Mul_Ovf_Un : OpCodes.Mul);
368                                 break;
369                         case ExpressionType.Divide:
370                                 ig.Emit (is_unsigned ? OpCodes.Div_Un : OpCodes.Div);
371                                 break;
372                         case ExpressionType.Modulo:
373                                 ig.Emit (is_unsigned ? OpCodes.Rem_Un : OpCodes.Rem);
374                                 break;
375                         case ExpressionType.RightShift:
376                         case ExpressionType.LeftShift:
377                                 ig.Emit (OpCodes.Ldc_I4, left.Type == typeof (int) ? 0x1f : 0x3f);
378                                 ig.Emit (OpCodes.And);
379                                 if (NodeType == ExpressionType.RightShift)
380                                         ig.Emit (is_unsigned ? OpCodes.Shr_Un : OpCodes.Shr);
381                                 else
382                                         ig.Emit (OpCodes.Shl);
383                                 break;
384                         case ExpressionType.And:
385                                 ig.Emit (OpCodes.And);
386                                 break;
387                         case ExpressionType.Or:
388                                 ig.Emit (OpCodes.Or);
389                                 break;
390                         case ExpressionType.ExclusiveOr:
391                                 ig.Emit (OpCodes.Xor);
392                                 break;
393                         case ExpressionType.GreaterThan:
394                                 ig.Emit (is_unsigned ? OpCodes.Cgt_Un : OpCodes.Cgt);
395                                 break;
396                         case ExpressionType.GreaterThanOrEqual:
397                                 if (is_unsigned || IsSingleOrDouble (left.Type))
398                                         ig.Emit (OpCodes.Clt_Un);
399                                 else
400                                         ig.Emit (OpCodes.Clt);
401
402                                 ig.Emit (OpCodes.Ldc_I4_0);
403                                 ig.Emit (OpCodes.Ceq);
404                                 break;
405                         case ExpressionType.LessThan:
406                                 ig.Emit (is_unsigned ? OpCodes.Clt_Un : OpCodes.Clt);
407                                 break;
408                         case ExpressionType.LessThanOrEqual:
409                                 if (is_unsigned || IsSingleOrDouble (left.Type))
410                                         ig.Emit (OpCodes.Cgt_Un);
411                                 else
412                                         ig.Emit (OpCodes.Cgt);
413
414                                 ig.Emit (OpCodes.Ldc_I4_0);
415                                 ig.Emit (OpCodes.Ceq);
416                                 break;
417                         case ExpressionType.Equal:
418                                 ig.Emit (OpCodes.Ceq);
419                                 break;
420                         case ExpressionType.NotEqual:
421                                 ig.Emit (OpCodes.Ceq);
422                                 ig.Emit (OpCodes.Ldc_I4_0);
423                                 ig.Emit (OpCodes.Ceq);
424                                 break;
425                         case ExpressionType.Power:
426                                 ig.Emit (OpCodes.Call, typeof (Math).GetMethod ("Pow"));
427                                 break;
428                         default:
429                                 throw new InvalidOperationException (
430                                         string.Format ("Internal error: BinaryExpression contains non-Binary nodetype {0}", NodeType));
431                         }
432                 }
433
434                 bool IsLeftLiftedBinary ()
435                 {
436                         return left.Type.IsNullable () && !right.Type.IsNullable ();
437                 }
438
439                 void EmitLeftLiftedToNullBinary (EmitContext ec)
440                 {
441                         var ig = ec.ig;
442
443                         var ret = ig.DefineLabel ();
444                         var done = ig.DefineLabel ();
445
446                         var left = ec.EmitStored (this.left);
447
448                         ec.EmitNullableHasValue (left);
449                         ig.Emit (OpCodes.Brfalse, ret);
450
451                         ec.EmitNullableGetValueOrDefault (left);
452                         ec.Emit (right);
453
454                         EmitBinaryOperator (ec);
455
456                         ec.EmitNullableNew (Type);
457
458                         ig.Emit (OpCodes.Br, done);
459
460                         ig.MarkLabel (ret);
461
462                         var temp = ig.DeclareLocal (Type);
463                         ec.EmitNullableInitialize (temp);
464
465                         ig.MarkLabel (done);
466                 }
467
468                 void EmitLiftedArithmeticBinary (EmitContext ec)
469                 {
470                         if (IsLeftLiftedBinary ())
471                                 EmitLeftLiftedToNullBinary (ec);
472                         else
473                                 EmitLiftedToNullBinary (ec);
474                 }
475
476                 void EmitLiftedToNullBinary (EmitContext ec)
477                 {
478                         var ig = ec.ig;
479                         var left = ec.EmitStored (this.left);
480                         var right = ec.EmitStored (this.right);
481                         var result = ig.DeclareLocal (Type);
482
483                         var has_value = ig.DefineLabel ();
484                         var done = ig.DefineLabel ();
485
486                         ec.EmitNullableHasValue (left);
487                         ec.EmitNullableHasValue (right);
488                         ig.Emit (OpCodes.And);
489                         ig.Emit (OpCodes.Brtrue, has_value);
490
491                         ec.EmitNullableInitialize (result);
492
493                         ig.Emit (OpCodes.Br, done);
494
495                         ig.MarkLabel (has_value);
496
497                         ec.EmitNullableGetValueOrDefault (left);
498                         ec.EmitNullableGetValueOrDefault (right);
499
500                         EmitBinaryOperator (ec);
501
502                         ec.EmitNullableNew (result.LocalType);
503
504                         ig.MarkLabel (done);
505                 }
506
507                 void EmitLiftedRelationalBinary (EmitContext ec)
508                 {
509                         var ig = ec.ig;
510                         var left = ec.EmitStored (this.left);
511                         var right = ec.EmitStored (this.right);
512
513                         var ret = ig.DefineLabel ();
514                         var done = ig.DefineLabel ();
515
516                         ec.EmitNullableGetValueOrDefault (left);
517                         ec.EmitNullableGetValueOrDefault (right);
518
519                         switch (NodeType) {
520                         case ExpressionType.Equal:
521                         case ExpressionType.NotEqual:
522                                 ig.Emit (OpCodes.Bne_Un, ret);
523                                 break;
524                         default:
525                                 EmitBinaryOperator (ec);
526                                 ig.Emit (OpCodes.Brfalse, ret);
527                                 break;
528                         }
529
530                         ec.EmitNullableHasValue (left);
531                         ec.EmitNullableHasValue (right);
532
533                         switch (NodeType) {
534                         case ExpressionType.Equal:
535                                 ig.Emit (OpCodes.Ceq);
536                                 break;
537                         case ExpressionType.NotEqual:
538                                 ig.Emit (OpCodes.Ceq);
539                                 ig.Emit (OpCodes.Ldc_I4_0);
540                                 ig.Emit (OpCodes.Ceq);
541                                 break;
542                         default:
543                                 ig.Emit (OpCodes.And);
544                                 break;
545                         }
546
547                         ig.Emit (OpCodes.Br, done);
548
549                         ig.MarkLabel (ret);
550
551                         ig.Emit (NodeType == ExpressionType.NotEqual ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
552
553                         ig.MarkLabel (done);
554                 }
555
556                 void EmitArithmeticBinary (EmitContext ec)
557                 {
558                         if (!IsLifted)
559                                 EmitNonLiftedBinary (ec);
560                         else
561                                 EmitLiftedArithmeticBinary (ec);
562                 }
563
564                 void EmitNonLiftedBinary (EmitContext ec)
565                 {
566                         ec.Emit (left);
567                         ec.Emit (right);
568                         EmitBinaryOperator (ec);
569                 }
570
571                 void EmitRelationalBinary (EmitContext ec)
572                 {
573                         if (!IsLifted) {
574                                 EmitNonLiftedBinary (ec);
575                                 return;
576                         }
577
578                         if (IsLiftedToNull) {
579                                 EmitLiftedToNullBinary (ec);
580                                 return;
581                         }
582
583                         if (ConstantExpression.IsNull (right) && !ConstantExpression.IsNull (left) && left.Type.IsNullable ()) {
584                                 EmitNullEquality (ec, left);
585                                 return;
586                         }
587
588                         if (ConstantExpression.IsNull (left) && !ConstantExpression.IsNull (right) && right.Type.IsNullable ()) {
589                                 EmitNullEquality (ec, right);
590                                 return;
591                         }
592
593                         EmitLiftedRelationalBinary (ec);
594                 }
595
596                 void EmitNullEquality (EmitContext ec, Expression e)
597                 {
598                         var ig = ec.ig;
599
600                         if (IsLiftedToNull) {
601                                 e.Emit (ec);
602                                 if (e.Type != typeof (void))
603                                         ig.Emit (OpCodes.Pop);
604
605                                 ec.EmitNullableNew (typeof (bool?));
606                                 return;
607                         }
608
609                         var se = ec.EmitStored (e);
610                         ec.EmitNullableHasValue (se);
611                         if (NodeType == ExpressionType.Equal) {
612                                 ig.Emit (OpCodes.Ldc_I4_0);
613                                 ig.Emit (OpCodes.Ceq);
614                         }               
615                 }
616
617                 void EmitLiftedUserDefinedOperator (EmitContext ec)
618                 {
619                         var ig = ec.ig;
620
621                         var ret_true = ig.DefineLabel ();
622                         var ret_false = ig.DefineLabel ();
623                         var done = ig.DefineLabel ();
624
625                         var left = ec.EmitStored (this.left);
626                         var right = ec.EmitStored (this.right);
627
628                         ec.EmitNullableHasValue (left);
629                         ec.EmitNullableHasValue (right);
630                         switch (NodeType) {
631                         case ExpressionType.Equal:
632                                 ig.Emit (OpCodes.Bne_Un, ret_false);
633                                 ec.EmitNullableHasValue (left);
634                                 ig.Emit (OpCodes.Brfalse, ret_true);
635                                 break;
636                         case ExpressionType.NotEqual:
637                                 ig.Emit (OpCodes.Bne_Un, ret_true);
638                                 ec.EmitNullableHasValue (left);
639                                 ig.Emit (OpCodes.Brfalse, ret_false);
640                                 break;
641                         default:
642                                 ig.Emit (OpCodes.And);
643                                 ig.Emit (OpCodes.Brfalse, ret_false);
644                                 break;
645                         }
646
647                         ec.EmitNullableGetValueOrDefault (left);
648                         ec.EmitNullableGetValueOrDefault (right);
649                         ec.EmitCall (method);
650                         ig.Emit (OpCodes.Br, done);
651
652                         ig.MarkLabel (ret_true);
653                         ig.Emit (OpCodes.Ldc_I4_1);
654                         ig.Emit (OpCodes.Br, done);
655
656                         ig.MarkLabel (ret_false);
657                         ig.Emit (OpCodes.Ldc_I4_0);
658                         ig.Emit (OpCodes.Br, done);
659
660                         ig.MarkLabel (done);
661                 }
662
663                 void EmitLiftedToNullUserDefinedOperator (EmitContext ec)
664                 {
665                         var ig = ec.ig;
666
667                         var ret = ig.DefineLabel ();
668                         var done = ig.DefineLabel ();
669
670                         var left = ec.EmitStored (this.left);
671                         var right = ec.EmitStored (this.right);
672
673                         ec.EmitNullableHasValue (left);
674                         ec.EmitNullableHasValue (right);
675                         ig.Emit (OpCodes.And);
676                         ig.Emit (OpCodes.Brfalse, ret);
677
678                         ec.EmitNullableGetValueOrDefault (left);
679                         ec.EmitNullableGetValueOrDefault (right);
680                         ec.EmitCall (method);
681                         ec.EmitNullableNew (Type);
682                         ig.Emit (OpCodes.Br, done);
683
684                         ig.MarkLabel (ret);
685                         var temp = ig.DeclareLocal (Type);
686                         ec.EmitNullableInitialize (temp);
687
688                         ig.MarkLabel (done);
689                 }
690
691                 void EmitUserDefinedLiftedLogicalShortCircuit (EmitContext ec)
692                 {
693                         var ig = ec.ig;
694                         var and = NodeType == ExpressionType.AndAlso;
695
696                         var left_is_null = ig.DefineLabel ();
697                         var ret_left = ig.DefineLabel ();
698                         var ret_null = ig.DefineLabel ();
699                         var done = ig.DefineLabel ();
700
701                         var left = ec.EmitStored (this.left);
702
703                         ec.EmitNullableHasValue (left);
704                         ig.Emit (OpCodes.Brfalse, and ? ret_null : left_is_null);
705
706                         ec.EmitNullableGetValueOrDefault (left);
707                         ec.EmitCall (and ? GetFalseOperator () : GetTrueOperator ());
708                         ig.Emit (OpCodes.Brtrue, ret_left);
709
710                         ig.MarkLabel (left_is_null);
711                         var right = ec.EmitStored (this.right);
712                         ec.EmitNullableHasValue (right);
713                         ig.Emit (OpCodes.Brfalse, ret_null);
714
715                         ec.EmitNullableGetValueOrDefault (left);
716                         ec.EmitNullableGetValueOrDefault (right);
717                         ec.EmitCall (method);
718
719                         ec.EmitNullableNew (Type);
720                         ig.Emit (OpCodes.Br, done);
721
722                         ig.MarkLabel (ret_left);
723                         ec.EmitLoad (left);
724                         ig.Emit (OpCodes.Br, done);
725
726                         ig.MarkLabel (ret_null);
727                         var ret = ig.DeclareLocal (Type);
728                         ec.EmitNullableInitialize (ret);
729
730                         ig.MarkLabel (done);
731                 }
732
733                 void EmitUserDefinedOperator (EmitContext ec)
734                 {
735                         if (!IsLifted) {
736                                 switch (NodeType) {
737                                 case ExpressionType.AndAlso:
738                                 case ExpressionType.OrElse:
739                                         EmitUserDefinedLogicalShortCircuit (ec);
740                                         break;
741                                 default:
742                                         left.Emit (ec);
743                                         right.Emit (ec);
744                                         ec.EmitCall (method);
745                                         break;
746                                 }
747                         } else if (IsLiftedToNull) {
748                                 switch (NodeType) {
749                                 case ExpressionType.AndAlso:
750                                 case ExpressionType.OrElse:
751                                         EmitUserDefinedLiftedLogicalShortCircuit (ec);
752                                         break;
753                                 default:
754                                         EmitLiftedToNullUserDefinedOperator (ec);
755                                         break;
756                                 }
757                         }  else
758                                 EmitLiftedUserDefinedOperator (ec);
759                 }
760
761                 internal override void Emit (EmitContext ec)
762                 {
763                         if (method != null) {
764                                 EmitUserDefinedOperator (ec);
765                                 return;
766                         }
767
768                         switch (NodeType){
769                         case ExpressionType.ArrayIndex:
770                                 EmitArrayAccess (ec);
771                                 return;
772                         case ExpressionType.Coalesce:
773                                 if (conversion != null)
774                                         EmitConvertedCoalesce (ec);
775                                 else
776                                         EmitCoalesce (ec);
777                                 return;
778                         case ExpressionType.Power:
779                         case ExpressionType.Add:
780                         case ExpressionType.AddChecked:
781                         case ExpressionType.Divide:
782                         case ExpressionType.ExclusiveOr:
783                         case ExpressionType.LeftShift:
784                         case ExpressionType.Modulo:
785                         case ExpressionType.Multiply:
786                         case ExpressionType.MultiplyChecked:
787                         case ExpressionType.RightShift:
788                         case ExpressionType.Subtract:
789                         case ExpressionType.SubtractChecked:
790                                 EmitArithmeticBinary (ec);
791                                 return;
792                         case ExpressionType.Equal:
793                         case ExpressionType.GreaterThan:
794                         case ExpressionType.GreaterThanOrEqual:
795                         case ExpressionType.LessThan:
796                         case ExpressionType.LessThanOrEqual:
797                         case ExpressionType.NotEqual:
798                                 EmitRelationalBinary (ec);
799                                 return;
800                         case ExpressionType.And:
801                         case ExpressionType.Or:
802                         case ExpressionType.AndAlso:
803                         case ExpressionType.OrElse:
804                                 EmitLogicalBinary (ec);
805                                 return;
806                         default:
807                                 throw new NotSupportedException (this.NodeType.ToString ());
808                         }
809                 }
810 #endif
811         }
812 }