do not check order sequence if option /order was not used
[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 using System.Reflection.Emit;
39
40 namespace System.Linq.Expressions {
41
42         public sealed class BinaryExpression : Expression {
43
44                 Expression left;
45                 Expression right;
46                 LambdaExpression conversion;
47                 MethodInfo method;
48                 bool lift_to_null, is_lifted;
49
50                 public Expression Left {
51                         get { return left; }
52                 }
53
54                 public Expression Right {
55                         get { return right; }
56                 }
57
58                 public MethodInfo Method {
59                         get { return method; }
60                 }
61
62                 public bool IsLifted {
63                         get { return is_lifted;  }
64                 }
65
66                 public bool IsLiftedToNull {
67                         get { return lift_to_null; }
68                 }
69
70                 public LambdaExpression Conversion {
71                         get { return conversion; }
72                 }
73
74                 internal BinaryExpression (ExpressionType node_type, Type type, Expression left, Expression right)
75                         : base (node_type, type)
76                 {
77                         this.left = left;
78                         this.right = right;
79                 }
80
81                 internal BinaryExpression (ExpressionType node_type, Type type, Expression left, Expression right, MethodInfo method)
82                         : base (node_type, type)
83                 {
84                         this.left = left;
85                         this.right = right;
86                         this.method = method;
87                 }
88
89                 internal BinaryExpression (ExpressionType node_type, Type type, Expression left, Expression right, bool lift_to_null,
90                         bool is_lifted, MethodInfo method, LambdaExpression conversion) : base (node_type, type)
91                 {
92                         this.left = left;
93                         this.right = right;
94                         this.method = method;
95                         this.conversion = conversion;
96                         this.lift_to_null = lift_to_null;
97                         this.is_lifted = is_lifted;
98                 }
99
100                 void EmitArrayAccess (EmitContext ec)
101                 {
102                         left.Emit (ec);
103                         right.Emit (ec);
104                         ec.ig.Emit (OpCodes.Ldelem, this.Type);
105                 }
106
107                 void EmitLogicalBinary (EmitContext ec)
108                 {
109                         switch (NodeType) {
110                         case ExpressionType.And:
111                         case ExpressionType.Or:
112                                 if (!IsLifted)
113                                         EmitLogical (ec);
114                                 else if (Type == typeof (bool?))
115                                         EmitLiftedLogical (ec);
116                                 else
117                                         EmitLiftedArithmeticBinary (ec);
118                                 break;
119                         case ExpressionType.AndAlso:
120                         case ExpressionType.OrElse:
121                                 if (!IsLifted)
122                                         EmitLogicalShortCircuit (ec);
123                                 else
124                                         EmitLiftedLogicalShortCircuit (ec);
125                                 break;
126                         }
127                 }
128
129                 void EmitLogical (EmitContext ec)
130                 {
131                         EmitNonLiftedBinary (ec);
132                 }
133
134                 void EmitLiftedLogical (EmitContext ec)
135                 {
136                         var ig = ec.ig;
137                         var and = NodeType == ExpressionType.And;
138                         var left = ec.EmitStored (this.left);
139                         var right = ec.EmitStored (this.right);
140
141                         var ret_from_left = ig.DefineLabel ();
142                         var ret_from_right = ig.DefineLabel ();
143                         var done = ig.DefineLabel ();
144
145                         ec.EmitNullableGetValueOrDefault (left);
146                         ig.Emit (OpCodes.Brtrue, ret_from_left);
147                         ec.EmitNullableGetValueOrDefault (right);
148                         ig.Emit (OpCodes.Brtrue, ret_from_right);
149
150                         ec.EmitNullableHasValue (left);
151                         ig.Emit (OpCodes.Brfalse, ret_from_left);
152
153                         ig.MarkLabel (ret_from_right);
154                         ec.EmitLoad (and ? left : right);
155                         ig.Emit (OpCodes.Br, done);
156
157                         ig.MarkLabel (ret_from_left);
158                         ec.EmitLoad (and ? right : left);
159
160                         ig.MarkLabel (done);
161                 }
162
163                 void EmitLogicalShortCircuit (EmitContext ec)
164                 {
165                         var ig = ec.ig;
166                         var and = NodeType == ExpressionType.AndAlso;
167                         var ret = ig.DefineLabel ();
168                         var done = ig.DefineLabel ();
169
170                         ec.Emit (left);
171                         ig.Emit (and ? OpCodes.Brfalse : OpCodes.Brtrue, ret);
172
173                         ec.Emit (right);
174
175                         ig.Emit (OpCodes.Br, done);
176
177                         ig.MarkLabel (ret);
178                         ig.Emit (and ? OpCodes.Ldc_I4_0 : OpCodes.Ldc_I4_1);
179
180                         ig.MarkLabel (done);
181                 }
182
183                 MethodInfo GetFalseOperator ()
184                 {
185                         return GetFalseOperator (left.Type.GetNotNullableType ());
186                 }
187
188                 MethodInfo GetTrueOperator ()
189                 {
190                         return GetTrueOperator (left.Type.GetNotNullableType ());
191                 }
192
193                 void EmitUserDefinedLogicalShortCircuit (EmitContext ec)
194                 {
195                         var ig = ec.ig;
196                         var and = NodeType == ExpressionType.AndAlso;
197
198                         var done = ig.DefineLabel ();
199
200                         var left = ec.EmitStored (this.left);
201
202                         ec.EmitLoad (left);
203                         ig.Emit (OpCodes.Dup);
204                         ec.EmitCall (and ? GetFalseOperator () : GetTrueOperator ());
205                         ig.Emit (OpCodes.Brtrue, done);
206
207                         ec.Emit (this.right);
208                         ec.EmitCall (method);
209
210                         ig.MarkLabel (done);
211                 }
212
213                 void EmitLiftedLogicalShortCircuit (EmitContext ec)
214                 {
215                         var ig = ec.ig;
216                         var and = NodeType == ExpressionType.AndAlso;
217                         var left_is_null = ig.DefineLabel ();
218                         var ret_from_left = ig.DefineLabel ();
219                         var ret_null = ig.DefineLabel ();
220                         var ret_new = ig.DefineLabel();
221                         var done = ig.DefineLabel();
222
223                         var left = ec.EmitStored (this.left);
224
225                         ec.EmitNullableHasValue (left);
226                         ig.Emit (OpCodes.Brfalse, left_is_null);
227
228                         ec.EmitNullableGetValueOrDefault (left);
229
230                         ig.Emit (OpCodes.Ldc_I4_0);
231                         ig.Emit (OpCodes.Ceq);
232                         ig.Emit (and ? OpCodes.Brtrue : OpCodes.Brfalse, ret_from_left);
233
234                         ig.MarkLabel (left_is_null);
235                         var right = ec.EmitStored (this.right);
236
237                         ec.EmitNullableHasValue (right);
238                         ig.Emit (OpCodes.Brfalse_S, ret_null);
239
240                         ec.EmitNullableGetValueOrDefault (right);
241
242                         ig.Emit (OpCodes.Ldc_I4_0);
243                         ig.Emit (OpCodes.Ceq);
244
245                         ig.Emit (and ? OpCodes.Brtrue : OpCodes.Brfalse, ret_from_left);
246
247                         ec.EmitNullableHasValue (left);
248                         ig.Emit (OpCodes.Brfalse, ret_null);
249
250                         ig.Emit (and ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
251                         ig.Emit (OpCodes.Br_S, ret_new);
252
253                         ig.MarkLabel (ret_from_left);
254                         ig.Emit (and ? OpCodes.Ldc_I4_0 : OpCodes.Ldc_I4_1);
255
256                         ig.MarkLabel (ret_new);
257                         ec.EmitNullableNew (Type);
258                         ig.Emit (OpCodes.Br, done);
259
260                         ig.MarkLabel (ret_null);
261                         var ret = ig.DeclareLocal (Type);
262                         ec.EmitNullableInitialize (ret);
263
264                         ig.MarkLabel (done);
265                 }
266
267                 void EmitCoalesce (EmitContext ec)
268                 {
269                         var ig = ec.ig;
270                         var done = ig.DefineLabel ();
271                         var load_right = ig.DefineLabel ();
272
273                         var left = ec.EmitStored (this.left);
274                         var left_is_nullable = left.LocalType.IsNullable ();
275
276                         if (left_is_nullable)
277                                 ec.EmitNullableHasValue (left);
278                         else
279                                 ec.EmitLoad (left);
280
281                         ig.Emit (OpCodes.Brfalse, load_right);
282
283                         if (left_is_nullable && !Type.IsNullable ())
284                                 ec.EmitNullableGetValue (left);
285                         else
286                                 ec.EmitLoad (left);
287
288                         ig.Emit (OpCodes.Br, done);
289
290                         ig.MarkLabel (load_right);
291                         ec.Emit (this.right);
292
293                         ig.MarkLabel (done);
294                 }
295
296                 void EmitConvertedCoalesce (EmitContext ec)
297                 {
298                         var ig = ec.ig;
299                         var done = ig.DefineLabel ();
300                         var load_right = ig.DefineLabel ();
301
302                         var left = ec.EmitStored (this.left);
303
304                         if (left.LocalType.IsNullable ())
305                                 ec.EmitNullableHasValue (left);
306                         else
307                                 ec.EmitLoad (left);
308
309                         ig.Emit (OpCodes.Brfalse, load_right);
310
311                         ec.Emit (conversion);
312                         ec.EmitLoad (left);
313                         ig.Emit (OpCodes.Callvirt, conversion.Type.GetInvokeMethod ());
314
315                         ig.Emit (OpCodes.Br, done);
316
317                         ig.MarkLabel (load_right);
318                         ec.Emit (this.right);
319
320                         ig.MarkLabel (done);
321                 }
322
323                 static bool IsInt32OrInt64 (Type type)
324                 {
325                         return type == typeof (int) || type == typeof (long);
326                 }
327
328                 static bool IsSingleOrDouble (Type type)
329                 {
330                         return type == typeof (float) || type == typeof (double);
331                 }
332
333                 void EmitBinaryOperator (EmitContext ec)
334                 {
335                         var ig = ec.ig;
336                         bool is_unsigned = IsUnsigned (left.Type);
337
338                         switch (NodeType) {
339                         case ExpressionType.Add:
340                                 ig.Emit (OpCodes.Add);
341                                 break;
342                         case ExpressionType.AddChecked:
343                                 if (IsInt32OrInt64 (left.Type))
344                                         ig.Emit (OpCodes.Add_Ovf);
345                                 else
346                                         ig.Emit (is_unsigned ? OpCodes.Add_Ovf_Un : OpCodes.Add);
347                                 break;
348                         case ExpressionType.Subtract:
349                                 ig.Emit (OpCodes.Sub);
350                                 break;
351                         case ExpressionType.SubtractChecked:
352                                 if (IsInt32OrInt64 (left.Type))
353                                         ig.Emit (OpCodes.Sub_Ovf);
354                                 else
355                                         ig.Emit (is_unsigned ? OpCodes.Sub_Ovf_Un : OpCodes.Sub);
356                                 break;
357                         case ExpressionType.Multiply:
358                                 ig.Emit (OpCodes.Mul);
359                                 break;
360                         case ExpressionType.MultiplyChecked:
361                                 if (IsInt32OrInt64 (left.Type))
362                                         ig.Emit (OpCodes.Mul_Ovf);
363                                 else
364                                         ig.Emit (is_unsigned ? OpCodes.Mul_Ovf_Un : OpCodes.Mul);
365                                 break;
366                         case ExpressionType.Divide:
367                                 ig.Emit (is_unsigned ? OpCodes.Div_Un : OpCodes.Div);
368                                 break;
369                         case ExpressionType.Modulo:
370                                 ig.Emit (is_unsigned ? OpCodes.Rem_Un : OpCodes.Rem);
371                                 break;
372                         case ExpressionType.RightShift:
373                         case ExpressionType.LeftShift:
374                                 ig.Emit (OpCodes.Ldc_I4, left.Type == typeof (int) ? 0x1f : 0x3f);
375                                 ig.Emit (OpCodes.And);
376                                 if (NodeType == ExpressionType.RightShift)
377                                         ig.Emit (is_unsigned ? OpCodes.Shr_Un : OpCodes.Shr);
378                                 else
379                                         ig.Emit (OpCodes.Shl);
380                                 break;
381                         case ExpressionType.And:
382                                 ig.Emit (OpCodes.And);
383                                 break;
384                         case ExpressionType.Or:
385                                 ig.Emit (OpCodes.Or);
386                                 break;
387                         case ExpressionType.ExclusiveOr:
388                                 ig.Emit (OpCodes.Xor);
389                                 break;
390                         case ExpressionType.GreaterThan:
391                                 ig.Emit (is_unsigned ? OpCodes.Cgt_Un : OpCodes.Cgt);
392                                 break;
393                         case ExpressionType.GreaterThanOrEqual:
394                                 if (is_unsigned || IsSingleOrDouble (left.Type))
395                                         ig.Emit (OpCodes.Clt_Un);
396                                 else
397                                         ig.Emit (OpCodes.Clt);
398
399                                 ig.Emit (OpCodes.Ldc_I4_0);
400                                 ig.Emit (OpCodes.Ceq);
401                                 break;
402                         case ExpressionType.LessThan:
403                                 ig.Emit (is_unsigned ? OpCodes.Clt_Un : OpCodes.Clt);
404                                 break;
405                         case ExpressionType.LessThanOrEqual:
406                                 if (is_unsigned || IsSingleOrDouble (left.Type))
407                                         ig.Emit (OpCodes.Cgt_Un);
408                                 else
409                                         ig.Emit (OpCodes.Cgt);
410
411                                 ig.Emit (OpCodes.Ldc_I4_0);
412                                 ig.Emit (OpCodes.Ceq);
413                                 break;
414                         case ExpressionType.Equal:
415                                 ig.Emit (OpCodes.Ceq);
416                                 break;
417                         case ExpressionType.NotEqual:
418                                 ig.Emit (OpCodes.Ceq);
419                                 ig.Emit (OpCodes.Ldc_I4_0);
420                                 ig.Emit (OpCodes.Ceq);
421                                 break;
422                         case ExpressionType.Power:
423                                 ig.Emit (OpCodes.Call, typeof (Math).GetMethod ("Pow"));
424                                 break;
425                         default:
426                                 throw new InvalidOperationException (
427                                         string.Format ("Internal error: BinaryExpression contains non-Binary nodetype {0}", NodeType));
428                         }
429                 }
430
431                 bool IsLeftLiftedBinary ()
432                 {
433                         return left.Type.IsNullable () && !right.Type.IsNullable ();
434                 }
435
436                 void EmitLeftLiftedToNullBinary (EmitContext ec)
437                 {
438                         var ig = ec.ig;
439
440                         var ret = ig.DefineLabel ();
441                         var done = ig.DefineLabel ();
442
443                         var left = ec.EmitStored (this.left);
444
445                         ec.EmitNullableHasValue (left);
446                         ig.Emit (OpCodes.Brfalse, ret);
447
448                         ec.EmitNullableGetValueOrDefault (left);
449                         ec.Emit (right);
450
451                         EmitBinaryOperator (ec);
452
453                         ec.EmitNullableNew (Type);
454
455                         ig.Emit (OpCodes.Br, done);
456
457                         ig.MarkLabel (ret);
458
459                         var temp = ig.DeclareLocal (Type);
460                         ec.EmitNullableInitialize (temp);
461
462                         ig.MarkLabel (done);
463                 }
464
465                 void EmitLiftedArithmeticBinary (EmitContext ec)
466                 {
467                         if (IsLeftLiftedBinary ())
468                                 EmitLeftLiftedToNullBinary (ec);
469                         else
470                                 EmitLiftedToNullBinary (ec);
471                 }
472
473                 void EmitLiftedToNullBinary (EmitContext ec)
474                 {
475                         var ig = ec.ig;
476                         var left = ec.EmitStored (this.left);
477                         var right = ec.EmitStored (this.right);
478                         var result = ig.DeclareLocal (Type);
479
480                         var has_value = ig.DefineLabel ();
481                         var done = ig.DefineLabel ();
482
483                         ec.EmitNullableHasValue (left);
484                         ec.EmitNullableHasValue (right);
485                         ig.Emit (OpCodes.And);
486                         ig.Emit (OpCodes.Brtrue, has_value);
487
488                         ec.EmitNullableInitialize (result);
489
490                         ig.Emit (OpCodes.Br, done);
491
492                         ig.MarkLabel (has_value);
493
494                         ec.EmitNullableGetValueOrDefault (left);
495                         ec.EmitNullableGetValueOrDefault (right);
496
497                         EmitBinaryOperator (ec);
498
499                         ec.EmitNullableNew (result.LocalType);
500
501                         ig.MarkLabel (done);
502                 }
503
504                 void EmitLiftedRelationalBinary (EmitContext ec)
505                 {
506                         var ig = ec.ig;
507                         var left = ec.EmitStored (this.left);
508                         var right = ec.EmitStored (this.right);
509
510                         var ret = ig.DefineLabel ();
511                         var done = ig.DefineLabel ();
512
513                         ec.EmitNullableGetValueOrDefault (left);
514                         ec.EmitNullableGetValueOrDefault (right);
515
516                         switch (NodeType) {
517                         case ExpressionType.Equal:
518                         case ExpressionType.NotEqual:
519                                 ig.Emit (OpCodes.Bne_Un, ret);
520                                 break;
521                         default:
522                                 EmitBinaryOperator (ec);
523                                 ig.Emit (OpCodes.Brfalse, ret);
524                                 break;
525                         }
526
527                         ec.EmitNullableHasValue (left);
528                         ec.EmitNullableHasValue (right);
529
530                         switch (NodeType) {
531                         case ExpressionType.Equal:
532                                 ig.Emit (OpCodes.Ceq);
533                                 break;
534                         case ExpressionType.NotEqual:
535                                 ig.Emit (OpCodes.Ceq);
536                                 ig.Emit (OpCodes.Ldc_I4_0);
537                                 ig.Emit (OpCodes.Ceq);
538                                 break;
539                         default:
540                                 ig.Emit (OpCodes.And);
541                                 break;
542                         }
543
544                         ig.Emit (OpCodes.Br, done);
545
546                         ig.MarkLabel (ret);
547
548                         ig.Emit (NodeType == ExpressionType.NotEqual ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
549
550                         ig.MarkLabel (done);
551                 }
552
553                 void EmitArithmeticBinary (EmitContext ec)
554                 {
555                         if (!IsLifted)
556                                 EmitNonLiftedBinary (ec);
557                         else
558                                 EmitLiftedArithmeticBinary (ec);
559                 }
560
561                 void EmitNonLiftedBinary (EmitContext ec)
562                 {
563                         ec.Emit (left);
564                         ec.Emit (right);
565                         EmitBinaryOperator (ec);
566                 }
567
568                 void EmitRelationalBinary (EmitContext ec)
569                 {
570                         if (!IsLifted)
571                                 EmitNonLiftedBinary (ec);
572                         else if (IsLiftedToNull)
573                                 EmitLiftedToNullBinary (ec);
574                         else
575                                 EmitLiftedRelationalBinary (ec);
576                 }
577
578                 void EmitLiftedUserDefinedOperator (EmitContext ec)
579                 {
580                         var ig = ec.ig;
581
582                         var ret_true = ig.DefineLabel ();
583                         var ret_false = ig.DefineLabel ();
584                         var done = ig.DefineLabel ();
585
586                         var left = ec.EmitStored (this.left);
587                         var right = ec.EmitStored (this.right);
588
589                         ec.EmitNullableHasValue (left);
590                         ec.EmitNullableHasValue (right);
591                         switch (NodeType) {
592                         case ExpressionType.Equal:
593                                 ig.Emit (OpCodes.Bne_Un, ret_false);
594                                 ec.EmitNullableHasValue (left);
595                                 ig.Emit (OpCodes.Brfalse, ret_true);
596                                 break;
597                         case ExpressionType.NotEqual:
598                                 ig.Emit (OpCodes.Bne_Un, ret_true);
599                                 ec.EmitNullableHasValue (left);
600                                 ig.Emit (OpCodes.Brfalse, ret_false);
601                                 break;
602                         default:
603                                 ig.Emit (OpCodes.And);
604                                 ig.Emit (OpCodes.Brfalse, ret_false);
605                                 break;
606                         }
607
608                         ec.EmitNullableGetValueOrDefault (left);
609                         ec.EmitNullableGetValueOrDefault (right);
610                         ec.EmitCall (method);
611                         ig.Emit (OpCodes.Br, done);
612
613                         ig.MarkLabel (ret_true);
614                         ig.Emit (OpCodes.Ldc_I4_1);
615                         ig.Emit (OpCodes.Br, done);
616
617                         ig.MarkLabel (ret_false);
618                         ig.Emit (OpCodes.Ldc_I4_0);
619                         ig.Emit (OpCodes.Br, done);
620
621                         ig.MarkLabel (done);
622                 }
623
624                 void EmitLiftedToNullUserDefinedOperator (EmitContext ec)
625                 {
626                         var ig = ec.ig;
627
628                         var ret = ig.DefineLabel ();
629                         var done = ig.DefineLabel ();
630
631                         var left = ec.EmitStored (this.left);
632                         var right = ec.EmitStored (this.right);
633
634                         ec.EmitNullableHasValue (left);
635                         ec.EmitNullableHasValue (right);
636                         ig.Emit (OpCodes.And);
637                         ig.Emit (OpCodes.Brfalse, ret);
638
639                         ec.EmitNullableGetValueOrDefault (left);
640                         ec.EmitNullableGetValueOrDefault (right);
641                         ec.EmitCall (method);
642                         ec.EmitNullableNew (Type);
643                         ig.Emit (OpCodes.Br, done);
644
645                         ig.MarkLabel (ret);
646                         var temp = ig.DeclareLocal (Type);
647                         ec.EmitNullableInitialize (temp);
648
649                         ig.MarkLabel (done);
650                 }
651
652                 void EmitUserDefinedLiftedLogicalShortCircuit (EmitContext ec)
653                 {
654                         var ig = ec.ig;
655                         var and = NodeType == ExpressionType.AndAlso;
656
657                         var left_is_null = ig.DefineLabel ();
658                         var ret_left = ig.DefineLabel ();
659                         var ret_null = ig.DefineLabel ();
660                         var done = ig.DefineLabel ();
661
662                         var left = ec.EmitStored (this.left);
663
664                         ec.EmitNullableHasValue (left);
665                         ig.Emit (OpCodes.Brfalse, and ? ret_null : left_is_null);
666
667                         ec.EmitNullableGetValueOrDefault (left);
668                         ec.EmitCall (and ? GetFalseOperator () : GetTrueOperator ());
669                         ig.Emit (OpCodes.Brtrue, ret_left);
670
671                         ig.MarkLabel (left_is_null);
672                         var right = ec.EmitStored (this.right);
673                         ec.EmitNullableHasValue (right);
674                         ig.Emit (OpCodes.Brfalse, ret_null);
675
676                         ec.EmitNullableGetValueOrDefault (left);
677                         ec.EmitNullableGetValueOrDefault (right);
678                         ec.EmitCall (method);
679
680                         ec.EmitNullableNew (Type);
681                         ig.Emit (OpCodes.Br, done);
682
683                         ig.MarkLabel (ret_left);
684                         ec.EmitLoad (left);
685                         ig.Emit (OpCodes.Br, done);
686
687                         ig.MarkLabel (ret_null);
688                         var ret = ig.DeclareLocal (Type);
689                         ec.EmitNullableInitialize (ret);
690
691                         ig.MarkLabel (done);
692                 }
693
694                 void EmitUserDefinedOperator (EmitContext ec)
695                 {
696                         if (!IsLifted) {
697                                 switch (NodeType) {
698                                 case ExpressionType.AndAlso:
699                                 case ExpressionType.OrElse:
700                                         EmitUserDefinedLogicalShortCircuit (ec);
701                                         break;
702                                 default:
703                                         left.Emit (ec);
704                                         right.Emit (ec);
705                                         ec.EmitCall (method);
706                                         break;
707                                 }
708                         } else if (IsLiftedToNull) {
709                                 switch (NodeType) {
710                                 case ExpressionType.AndAlso:
711                                 case ExpressionType.OrElse:
712                                         EmitUserDefinedLiftedLogicalShortCircuit (ec);
713                                         break;
714                                 default:
715                                         EmitLiftedToNullUserDefinedOperator (ec);
716                                         break;
717                                 }
718                         }  else
719                                 EmitLiftedUserDefinedOperator (ec);
720                 }
721
722                 internal override void Emit (EmitContext ec)
723                 {
724                         if (method != null) {
725                                 EmitUserDefinedOperator (ec);
726                                 return;
727                         }
728
729                         switch (NodeType){
730                         case ExpressionType.ArrayIndex:
731                                 EmitArrayAccess (ec);
732                                 return;
733                         case ExpressionType.Coalesce:
734                                 if (conversion != null)
735                                         EmitConvertedCoalesce (ec);
736                                 else
737                                         EmitCoalesce (ec);
738                                 return;
739                         case ExpressionType.Power:
740                         case ExpressionType.Add:
741                         case ExpressionType.AddChecked:
742                         case ExpressionType.Divide:
743                         case ExpressionType.ExclusiveOr:
744                         case ExpressionType.LeftShift:
745                         case ExpressionType.Modulo:
746                         case ExpressionType.Multiply:
747                         case ExpressionType.MultiplyChecked:
748                         case ExpressionType.RightShift:
749                         case ExpressionType.Subtract:
750                         case ExpressionType.SubtractChecked:
751                                 EmitArithmeticBinary (ec);
752                                 return;
753                         case ExpressionType.Equal:
754                         case ExpressionType.GreaterThan:
755                         case ExpressionType.GreaterThanOrEqual:
756                         case ExpressionType.LessThan:
757                         case ExpressionType.LessThanOrEqual:
758                         case ExpressionType.NotEqual:
759                                 EmitRelationalBinary (ec);
760                                 return;
761                         case ExpressionType.And:
762                         case ExpressionType.Or:
763                         case ExpressionType.AndAlso:
764                         case ExpressionType.OrElse:
765                                 EmitLogicalBinary (ec);
766                                 return;
767                         default:
768                                 throw new NotSupportedException (this.NodeType.ToString ());
769                         }
770                 }
771         }
772 }