Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2817-BinPackingSpeedup/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Parser/mcs/assign.cs

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

#2077: created branch and added first version

File size: 23.7 KB
Line 
1//
2// assign.cs: Assignments.
3//
4// Author:
5//   Miguel de Icaza (miguel@ximian.com)
6//   Martin Baulig (martin@ximian.com)
7//   Marek Safar (marek.safar@gmail.com) 
8//
9// Dual licensed under the terms of the MIT X11 or GNU GPL
10//
11// Copyright 2001, 2002, 2003 Ximian, Inc.
12// Copyright 2004-2008 Novell, Inc
13// Copyright 2011 Xamarin Inc
14//
15using System;
16
17#if STATIC
18using IKVM.Reflection.Emit;
19#else
20using System.Reflection.Emit;
21#endif
22
23namespace Mono.CSharp {
24
25  /// <summary>
26  ///   This interface is implemented by expressions that can be assigned to.
27  /// </summary>
28  /// <remarks>
29  ///   This interface is implemented by Expressions whose values can not
30  ///   store the result on the top of the stack.
31  ///
32  ///   Expressions implementing this (Properties, Indexers and Arrays) would
33  ///   perform an assignment of the Expression "source" into its final
34  ///   location.
35  ///
36  ///   No values on the top of the stack are expected to be left by
37  ///   invoking this method.
38  /// </remarks>
39  public interface IAssignMethod {
40    //
41    // This is an extra version of Emit. If leave_copy is `true'
42    // A copy of the expression will be left on the stack at the
43    // end of the code generated for EmitAssign
44    //
45    void Emit (EmitContext ec, bool leave_copy);
46
47    //
48    // This method does the assignment
49    // `source' will be stored into the location specified by `this'
50    // if `leave_copy' is true, a copy of `source' will be left on the stack
51    // if `prepare_for_load' is true, when `source' is emitted, there will
52    // be data on the stack that it can use to compuatate its value. This is
53    // for expressions like a [f ()] ++, where you can't call `f ()' twice.
54    //
55    void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound);
56
57    /*
58    For simple assignments, this interface is very simple, EmitAssign is called with source
59    as the source expression and leave_copy and prepare_for_load false.
60
61    For compound assignments it gets complicated.
62
63    EmitAssign will be called as before, however, prepare_for_load will be
64    true. The @source expression will contain an expression
65    which calls Emit. So, the calls look like:
66
67    this.EmitAssign (ec, source, false, true) ->
68      source.Emit (ec); ->
69        [...] ->
70          this.Emit (ec, false); ->
71          end this.Emit (ec, false); ->
72        end [...]
73      end source.Emit (ec);
74    end this.EmitAssign (ec, source, false, true)
75
76
77    When prepare_for_load is true, EmitAssign emits a `token' on the stack that
78    Emit will use for its state.
79
80    Let's take FieldExpr as an example. assume we are emitting f ().y += 1;
81
82    Here is the call tree again. This time, each call is annotated with the IL
83    it produces:
84
85    this.EmitAssign (ec, source, false, true)
86      call f
87      dup
88
89      Binary.Emit ()
90        this.Emit (ec, false);
91        ldfld y
92        end this.Emit (ec, false);
93
94        IntConstant.Emit ()
95        ldc.i4.1
96        end IntConstant.Emit
97
98        add
99      end Binary.Emit ()
100
101      stfld
102    end this.EmitAssign (ec, source, false, true)
103
104    Observe two things:
105      1) EmitAssign left a token on the stack. It was the result of f ().
106      2) This token was used by Emit
107
108    leave_copy (in both EmitAssign and Emit) tells the compiler to leave a copy
109    of the expression at that point in evaluation. This is used for pre/post inc/dec
110    and for a = x += y. Let's do the above example with leave_copy true in EmitAssign
111
112    this.EmitAssign (ec, source, true, true)
113      call f
114      dup
115
116      Binary.Emit ()
117        this.Emit (ec, false);
118        ldfld y
119        end this.Emit (ec, false);
120
121        IntConstant.Emit ()
122        ldc.i4.1
123        end IntConstant.Emit
124
125        add
126      end Binary.Emit ()
127
128      dup
129      stloc temp
130      stfld
131      ldloc temp
132    end this.EmitAssign (ec, source, true, true)
133
134    And with it true in Emit
135
136    this.EmitAssign (ec, source, false, true)
137      call f
138      dup
139
140      Binary.Emit ()
141        this.Emit (ec, true);
142        ldfld y
143        dup
144        stloc temp
145        end this.Emit (ec, true);
146
147        IntConstant.Emit ()
148        ldc.i4.1
149        end IntConstant.Emit
150
151        add
152      end Binary.Emit ()
153
154      stfld
155      ldloc temp
156    end this.EmitAssign (ec, source, false, true)
157
158    Note that these two examples are what happens for ++x and x++, respectively.
159    */
160  }
161
162  /// <summary>
163  ///   An Expression to hold a temporary value.
164  /// </summary>
165  /// <remarks>
166  ///   The LocalTemporary class is used to hold temporary values of a given
167  ///   type to "simulate" the expression semantics. The local variable is
168  ///   never captured.
169  ///
170  ///   The local temporary is used to alter the normal flow of code generation
171  ///   basically it creates a local variable, and its emit instruction generates
172  ///   code to access this value, return its address or save its value.
173  ///
174  ///   If `is_address' is true, then the value that we store is the address to the
175  ///   real value, and not the value itself.
176  ///
177  ///   This is needed for a value type, because otherwise you just end up making a
178  ///   copy of the value on the stack and modifying it. You really need a pointer
179  ///   to the origional value so that you can modify it in that location. This
180  ///   Does not happen with a class because a class is a pointer -- so you always
181  ///   get the indirection.
182  ///
183  /// </remarks>
184  public class LocalTemporary : Expression, IMemoryLocation, IAssignMethod {
185    LocalBuilder builder;
186
187    public LocalTemporary (TypeSpec t)
188    {
189      type = t;
190      eclass = ExprClass.Value;
191    }
192
193    public LocalTemporary (LocalBuilder b, TypeSpec t)
194      : this (t)
195    {
196      builder = b;
197    }
198
199    public void Release (EmitContext ec)
200    {
201      ec.FreeTemporaryLocal (builder, type);
202      builder = null;
203    }
204
205    public override bool ContainsEmitWithAwait ()
206    {
207      return false;
208    }
209
210    public override Expression CreateExpressionTree (ResolveContext ec)
211    {
212      Arguments args = new Arguments (1);
213      args.Add (new Argument (this));
214      return CreateExpressionFactoryCall (ec, "Constant", args);
215    }
216
217    protected override Expression DoResolve (ResolveContext ec)
218    {
219      return this;
220    }
221
222    public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
223    {
224      return this;
225    }
226
227    public override void Emit (EmitContext ec)
228    {
229      if (builder == null)
230        throw new InternalErrorException ("Emit without Store, or after Release");
231
232      ec.Emit (OpCodes.Ldloc, builder);
233    }
234
235    #region IAssignMethod Members
236
237    public void Emit (EmitContext ec, bool leave_copy)
238    {
239      Emit (ec);
240
241      if (leave_copy)
242        Emit (ec);
243    }
244
245    public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
246    {
247      if (isCompound)
248        throw new NotImplementedException ();
249
250      source.Emit (ec);
251
252      Store (ec);
253
254      if (leave_copy)
255        Emit (ec);
256    }
257
258    #endregion
259
260    public LocalBuilder Builder {
261      get { return builder; }
262    }
263
264    public void Store (EmitContext ec)
265    {
266      if (builder == null)
267        builder = ec.GetTemporaryLocal (type);
268
269      ec.Emit (OpCodes.Stloc, builder);
270    }
271
272    public void AddressOf (EmitContext ec, AddressOp mode)
273    {
274      if (builder == null)
275        builder = ec.GetTemporaryLocal (type);
276
277      if (builder.LocalType.IsByRef) {
278        //
279        // if is_address, than this is just the address anyways,
280        // so we just return this.
281        //
282        ec.Emit (OpCodes.Ldloc, builder);
283      } else {
284        ec.Emit (OpCodes.Ldloca, builder);
285      }
286    }
287  }
288
289  /// <summary>
290  ///   The Assign node takes care of assigning the value of source into
291  ///   the expression represented by target.
292  /// </summary>
293  public abstract class Assign : ExpressionStatement {
294    protected Expression target, source;
295
296    protected Assign (Expression target, Expression source, Location loc)
297    {
298      this.target = target;
299      this.source = source;
300      this.loc = loc;
301    }
302   
303    public Expression Target {
304      get { return target; }
305    }
306
307    public Expression Source {
308      get {
309        return source;
310      }
311    }
312
313    public override Location StartLocation {
314      get {
315        return target.StartLocation;
316      }
317    }
318
319    public override bool ContainsEmitWithAwait ()
320    {
321      return target.ContainsEmitWithAwait () || source.ContainsEmitWithAwait ();
322    }
323
324    public override Expression CreateExpressionTree (ResolveContext ec)
325    {
326      ec.Report.Error (832, loc, "An expression tree cannot contain an assignment operator");
327      return null;
328    }
329
330    protected override Expression DoResolve (ResolveContext ec)
331    {
332      bool ok = true;
333      source = source.Resolve (ec);
334           
335      if (source == null) {
336        ok = false;
337        source = ErrorExpression.Instance;
338      }
339
340      target = target.ResolveLValue (ec, source);
341
342      if (target == null || !ok)
343        return null;
344
345      TypeSpec target_type = target.Type;
346      TypeSpec source_type = source.Type;
347
348      eclass = ExprClass.Value;
349      type = target_type;
350
351      if (!(target is IAssignMethod)) {
352        target.Error_ValueAssignment (ec, source);
353        return null;
354      }
355
356      if (target_type != source_type) {
357        Expression resolved = ResolveConversions (ec);
358
359        if (resolved != this)
360          return resolved;
361      }
362
363      return this;
364    }
365
366#if NET_4_0 || MOBILE_DYNAMIC
367    public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx)
368    {
369      var tassign = target as IDynamicAssign;
370      if (tassign == null)
371        throw new InternalErrorException (target.GetType () + " does not support dynamic assignment");
372
373      var target_object = tassign.MakeAssignExpression (ctx, source);
374
375      //
376      // Some hacking is needed as DLR does not support void type and requires
377      // always have object convertible return type to support caching and chaining
378      //
379      // We do this by introducing an explicit block which returns RHS value when
380      // available or null
381      //
382      if (target_object.NodeType == System.Linq.Expressions.ExpressionType.Block)
383        return target_object;
384
385      System.Linq.Expressions.UnaryExpression source_object;
386      if (ctx.HasSet (BuilderContext.Options.CheckedScope)) {
387        source_object = System.Linq.Expressions.Expression.ConvertChecked (source.MakeExpression (ctx), target_object.Type);
388      } else {
389        source_object = System.Linq.Expressions.Expression.Convert (source.MakeExpression (ctx), target_object.Type);
390      }
391
392      return System.Linq.Expressions.Expression.Assign (target_object, source_object);
393    }
394#endif
395    protected virtual Expression ResolveConversions (ResolveContext ec)
396    {
397      source = Convert.ImplicitConversionRequired (ec, source, target.Type, source.Location);
398      if (source == null)
399        return null;
400
401      return this;
402    }
403
404    void Emit (EmitContext ec, bool is_statement)
405    {
406      IAssignMethod t = (IAssignMethod) target;
407      t.EmitAssign (ec, source, !is_statement, this is CompoundAssign);
408    }
409
410    public override void Emit (EmitContext ec)
411    {
412      Emit (ec, false);
413    }
414
415    public override void EmitStatement (EmitContext ec)
416    {
417      Emit (ec, true);
418    }
419
420    public override void FlowAnalysis (FlowAnalysisContext fc)
421    {
422      source.FlowAnalysis (fc);
423
424      if (target is ArrayAccess || target is IndexerExpr || target is PropertyExpr)
425        target.FlowAnalysis (fc);
426    }
427
428    protected override void CloneTo (CloneContext clonectx, Expression t)
429    {
430      Assign _target = (Assign) t;
431
432      _target.target = target.Clone (clonectx);
433      _target.source = source.Clone (clonectx);
434    }
435
436    public override object Accept (StructuralVisitor visitor)
437    {
438      return visitor.Visit (this);
439    }
440  }
441
442  public class SimpleAssign : Assign
443  {
444    public SimpleAssign (Expression target, Expression source)
445      : this (target, source, target.Location)
446    {
447    }
448
449    public SimpleAssign (Expression target, Expression source, Location loc)
450      : base (target, source, loc)
451    {
452    }
453
454    bool CheckEqualAssign (Expression t)
455    {
456      if (source is Assign) {
457        Assign a = (Assign) source;
458        if (t.Equals (a.Target))
459          return true;
460        return a is SimpleAssign && ((SimpleAssign) a).CheckEqualAssign (t);
461      }
462      return t.Equals (source);
463    }
464
465    protected override Expression DoResolve (ResolveContext ec)
466    {
467      Expression e = base.DoResolve (ec);
468      if (e == null || e != this)
469        return e;
470
471      if (CheckEqualAssign (target))
472        ec.Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?");
473
474      return this;
475    }
476
477    public override object Accept (StructuralVisitor visitor)
478    {
479      return visitor.Visit (this);
480    }
481
482    public override void FlowAnalysis (FlowAnalysisContext fc)
483    {
484      base.FlowAnalysis (fc);
485
486      var vr = target as VariableReference;
487      if (vr != null) {
488        if (vr.VariableInfo != null)
489          fc.SetVariableAssigned (vr.VariableInfo);
490
491        return;
492      }
493
494      var fe = target as FieldExpr;
495      if (fe != null) {
496        fe.SetFieldAssigned (fc);
497        return;
498      }
499    }
500
501    public override void MarkReachable (Reachability rc)
502    {
503      var es = source as ExpressionStatement;
504      if (es != null)
505        es.MarkReachable (rc);
506    }
507  }
508
509  public class RuntimeExplicitAssign : Assign
510  {
511    public RuntimeExplicitAssign (Expression target, Expression source)
512      : base (target, source, target.Location)
513    {
514    }
515
516    protected override Expression ResolveConversions (ResolveContext ec)
517    {
518      source = EmptyCast.Create (source, target.Type);
519      return this;
520    }
521  }
522
523  //
524  // Compiler generated assign
525  //
526  class CompilerAssign : Assign
527  {
528    public CompilerAssign (Expression target, Expression source, Location loc)
529      : base (target, source, loc)
530    {
531      if (target.Type != null) {
532        type = target.Type;
533        eclass = ExprClass.Value;
534      }
535    }
536
537    protected override Expression DoResolve (ResolveContext ec)
538    {
539      var expr = base.DoResolve (ec);
540      var vr = target as VariableReference;
541      if (vr != null && vr.VariableInfo != null)
542        vr.VariableInfo.IsEverAssigned = false;
543
544      return expr;
545    }
546
547    public void UpdateSource (Expression source)
548    {
549      base.source = source;
550    }
551  }
552
553  //
554  // Implements fields and events class initializers
555  //
556  public class FieldInitializer : Assign
557  {
558    //
559    // Field initializers are tricky for partial classes. They have to
560    // share same constructor (block) for expression trees resolve but
561    // they have they own resolve scope
562    //
563    sealed class FieldInitializerContext : BlockContext
564    {
565      readonly ExplicitBlock ctor_block;
566
567      public FieldInitializerContext (IMemberContext mc, BlockContext constructorContext)
568        : base (mc, null, constructorContext.ReturnType)
569      {
570        flags |= Options.FieldInitializerScope | Options.ConstructorScope;
571        this.ctor_block = constructorContext.CurrentBlock.Explicit;
572
573        if (ctor_block.IsCompilerGenerated)
574          CurrentBlock = ctor_block;
575      }
576
577      public override ExplicitBlock ConstructorBlock {
578          get {
579              return ctor_block;
580          }
581      }
582    }
583
584    //
585    // Keep resolved value because field initializers have their own rules
586    //
587    ExpressionStatement resolved;
588    FieldBase mc;
589
590    public FieldInitializer (FieldBase mc, Expression expression, Location loc)
591      : base (new FieldExpr (mc.Spec, expression.Location), expression, loc)
592    {
593      this.mc = mc;
594      if (!mc.IsStatic)
595        ((FieldExpr)target).InstanceExpression = new CompilerGeneratedThis (mc.CurrentType, expression.Location);
596    }
597
598    public int AssignmentOffset { get; private set; }
599
600    public FieldBase Field {
601      get {
602        return mc;
603      }
604    }
605
606    public override Location StartLocation {
607      get {
608        return loc;
609      }
610    }
611
612    protected override Expression DoResolve (ResolveContext rc)
613    {
614      // Field initializer can be resolved (fail) many times
615      if (source == null)
616        return null;
617
618      if (resolved == null) {
619        var bc = (BlockContext) rc;
620        var ctx = new FieldInitializerContext (mc, bc);
621        resolved = base.DoResolve (ctx) as ExpressionStatement;
622        AssignmentOffset = ctx.AssignmentInfoOffset - bc.AssignmentInfoOffset;
623      }
624
625      return resolved;
626    }
627
628    public override void EmitStatement (EmitContext ec)
629    {
630      if (resolved == null)
631        return;
632
633      //
634      // Emit sequence symbol info even if we are in compiler generated
635      // block to allow debugging field initializers when constructor is
636      // compiler generated
637      //
638      if (ec.HasSet (BuilderContext.Options.OmitDebugInfo) && ec.HasMethodSymbolBuilder) {
639        using (ec.With (BuilderContext.Options.OmitDebugInfo, false)) {
640          ec.Mark (loc);
641        }
642      }
643
644      if (resolved != this)
645        resolved.EmitStatement (ec);
646      else
647        base.EmitStatement (ec);
648    }
649
650    public override void FlowAnalysis (FlowAnalysisContext fc)
651    {
652      source.FlowAnalysis (fc);
653      ((FieldExpr) target).SetFieldAssigned (fc);
654    }
655   
656    public bool IsDefaultInitializer {
657      get {
658        Constant c = source as Constant;
659        if (c == null)
660          return false;
661       
662        FieldExpr fe = (FieldExpr)target;
663        return c.IsDefaultInitializer (fe.Type);
664      }
665    }
666
667    public override bool IsSideEffectFree {
668      get {
669        return source.IsSideEffectFree;
670      }
671    }
672  }
673
674  class PrimaryConstructorAssign : SimpleAssign
675  {
676    readonly Field field;
677    readonly Parameter parameter;
678
679    public PrimaryConstructorAssign (Field field, Parameter parameter)
680      : base (null, null, parameter.Location)
681    {
682      this.field = field;
683      this.parameter = parameter;
684    }
685
686    protected override Expression DoResolve (ResolveContext rc)
687    {
688      target = new FieldExpr (field, loc);
689      source = rc.CurrentBlock.ParametersBlock.GetParameterInfo (parameter).CreateReferenceExpression (rc, loc);
690      return base.DoResolve (rc);
691    }
692
693    public override void EmitStatement (EmitContext ec)
694    {
695      using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
696        base.EmitStatement (ec);
697      }
698    }
699  }
700
701  //
702  // This class is used for compound assignments.
703  //
704  public class CompoundAssign : Assign
705  {
706    // This is just a hack implemented for arrays only
707    public sealed class TargetExpression : Expression
708    {
709      readonly Expression child;
710
711      public TargetExpression (Expression child)
712      {
713        this.child = child;
714        this.loc = child.Location;
715      }
716
717      public override bool ContainsEmitWithAwait ()
718      {
719        return child.ContainsEmitWithAwait ();
720      }
721
722      public override Expression CreateExpressionTree (ResolveContext ec)
723      {
724        throw new NotSupportedException ("ET");
725      }
726
727      protected override Expression DoResolve (ResolveContext ec)
728      {
729        type = child.Type;
730        eclass = ExprClass.Value;
731        return this;
732      }
733
734      public override void Emit (EmitContext ec)
735      {
736        child.Emit (ec);
737      }
738
739      public override Expression EmitToField (EmitContext ec)
740      {
741        return child.EmitToField (ec);
742      }
743    }
744
745    // Used for underlying binary operator
746    readonly Binary.Operator op;
747    Expression right;
748    Expression left;
749   
750    public Binary.Operator Op {
751      get {
752        return op;
753      }
754    }
755
756    public CompoundAssign (Binary.Operator op, Expression target, Expression source)
757      : base (target, source, target.Location)
758    {
759      right = source;
760      this.op = op;
761    }
762
763    public CompoundAssign (Binary.Operator op, Expression target, Expression source, Expression left)
764      : this (op, target, source)
765    {
766      this.left = left;
767    }
768
769    public Binary.Operator Operator {
770      get {
771        return op;
772      }
773    }
774
775    protected override Expression DoResolve (ResolveContext ec)
776    {
777      right = right.Resolve (ec);
778      if (right == null)
779        return null;
780
781      MemberAccess ma = target as MemberAccess;
782      using (ec.Set (ResolveContext.Options.CompoundAssignmentScope)) {
783        target = target.Resolve (ec);
784      }
785     
786      if (target == null)
787        return null;
788
789      if (target is MethodGroupExpr){
790        ec.Report.Error (1656, loc,
791          "Cannot assign to `{0}' because it is a `{1}'",
792          ((MethodGroupExpr)target).Name, target.ExprClassName);
793        return null;
794      }
795
796      var event_expr = target as EventExpr;
797      if (event_expr != null) {
798        source = Convert.ImplicitConversionRequired (ec, right, target.Type, loc);
799        if (source == null)
800          return null;
801
802        Expression rside;
803        if (op == Binary.Operator.Addition)
804          rside = EmptyExpression.EventAddition;
805        else if (op == Binary.Operator.Subtraction)
806          rside = EmptyExpression.EventSubtraction;
807        else
808          rside = null;
809
810        target = target.ResolveLValue (ec, rside);
811        if (target == null)
812          return null;
813
814        eclass = ExprClass.Value;
815        type = event_expr.Operator.ReturnType;
816        return this;
817      }
818
819      //
820      // Only now we can decouple the original source/target
821      // into a tree, to guarantee that we do not have side
822      // effects.
823      //
824      if (left == null)
825        left = new TargetExpression (target);
826
827      source = new Binary (op, left, right, true);
828
829      if (target is DynamicMemberAssignable) {
830        Arguments targs = ((DynamicMemberAssignable) target).Arguments;
831        source = source.Resolve (ec);
832
833        Arguments args = new Arguments (targs.Count + 1);
834        args.AddRange (targs);
835        args.Add (new Argument (source));
836
837        var binder_flags = CSharpBinderFlags.ValueFromCompoundAssignment;
838
839        //
840        // Compound assignment does target conversion using additional method
841        // call, set checked context as the binary operation can overflow
842        //
843        if (ec.HasSet (ResolveContext.Options.CheckedScope))
844          binder_flags |= CSharpBinderFlags.CheckedContext;
845
846        if (target is DynamicMemberBinder) {
847          source = new DynamicMemberBinder (ma.Name, binder_flags, args, loc).Resolve (ec);
848
849          // Handles possible event addition/subtraction
850          if (op == Binary.Operator.Addition || op == Binary.Operator.Subtraction) {
851            args = new Arguments (targs.Count + 1);
852            args.AddRange (targs);
853            args.Add (new Argument (right));
854            string method_prefix = op == Binary.Operator.Addition ?
855              Event.AEventAccessor.AddPrefix : Event.AEventAccessor.RemovePrefix;
856
857            var invoke = DynamicInvocation.CreateSpecialNameInvoke (
858              new MemberAccess (right, method_prefix + ma.Name, loc), args, loc).Resolve (ec);
859
860            args = new Arguments (targs.Count);
861            args.AddRange (targs);
862            source = new DynamicEventCompoundAssign (ma.Name, args,
863              (ExpressionStatement) source, (ExpressionStatement) invoke, loc).Resolve (ec);
864          }
865        } else {
866          source = new DynamicIndexBinder (binder_flags, args, loc).Resolve (ec);
867        }
868
869        return source;
870      }
871
872      return base.DoResolve (ec);
873    }
874
875    public override void FlowAnalysis (FlowAnalysisContext fc)
876    {
877      target.FlowAnalysis (fc);
878      source.FlowAnalysis (fc);
879    }
880
881    protected override Expression ResolveConversions (ResolveContext ec)
882    {
883      //
884      // LAMESPEC: Under dynamic context no target conversion is happening
885      // This allows more natual dynamic behaviour but breaks compatibility
886      // with static binding
887      //
888      if (target is RuntimeValueExpression)
889        return this;
890
891      TypeSpec target_type = target.Type;
892
893      //
894      // 1. the return type is implicitly convertible to the type of target
895      //
896      if (Convert.ImplicitConversionExists (ec, source, target_type)) {
897        source = Convert.ImplicitConversion (ec, source, target_type, loc);
898        return this;
899      }
900
901      //
902      // Otherwise, if the selected operator is a predefined operator
903      //
904      Binary b = source as Binary;
905      if (b == null) {
906        if (source is ReducedExpression)
907          b = ((ReducedExpression) source).OriginalExpression as Binary;
908        else if (source is ReducedExpression.ReducedConstantExpression) {
909          b = ((ReducedExpression.ReducedConstantExpression) source).OriginalExpression as Binary;
910        } else if (source is Nullable.LiftedBinaryOperator) {
911          var po = ((Nullable.LiftedBinaryOperator) source);
912          if (po.UserOperator == null)
913            b = po.Binary;
914        } else if (source is TypeCast) {
915          b = ((TypeCast) source).Child as Binary;
916        }
917      }
918
919      if (b != null) {
920        //
921        // 2a. the operator is a shift operator
922        //
923        // 2b. the return type is explicitly convertible to the type of x, and
924        // y is implicitly convertible to the type of x
925        //
926        if ((b.Oper & Binary.Operator.ShiftMask) != 0 ||
927          Convert.ImplicitConversionExists (ec, right, target_type)) {
928          source = Convert.ExplicitConversion (ec, source, target_type, loc);
929          return this;
930        }
931      }
932
933      if (source.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
934        Arguments arg = new Arguments (1);
935        arg.Add (new Argument (source));
936        return new SimpleAssign (target, new DynamicConversion (target_type, CSharpBinderFlags.ConvertExplicit, arg, loc), loc).Resolve (ec);
937      }
938
939      right.Error_ValueCannotBeConverted (ec, target_type, false);
940      return null;
941    }
942
943    protected override void CloneTo (CloneContext clonectx, Expression t)
944    {
945      CompoundAssign ctarget = (CompoundAssign) t;
946
947      ctarget.right = ctarget.source = source.Clone (clonectx);
948      ctarget.target = target.Clone (clonectx);
949    }
950
951    public override object Accept (StructuralVisitor visitor)
952    {
953      return visitor.Visit (this);
954    }
955  }
956}
Note: See TracBrowser for help on using the repository browser.