Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Parser/mcs/nullable.cs @ 16240

Last change on this file since 16240 was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

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