[jit] Fix the saving of the 'cfg->ret_var_set' flag when inlining, it was set to...
[mono.git] / mcs / mcs / assign.cs
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 //
15 using System;
16
17 #if STATIC
18 using IKVM.Reflection.Emit;
19 #else
20 using System.Reflection.Emit;
21 #endif
22
23 namespace 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                 public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx)
367                 {
368                         var tassign = target as IDynamicAssign;
369                         if (tassign == null)
370                                 throw new InternalErrorException (target.GetType () + " does not support dynamic assignment");
371
372                         var target_object = tassign.MakeAssignExpression (ctx, source);
373
374                         //
375                         // Some hacking is needed as DLR does not support void type and requires
376                         // always have object convertible return type to support caching and chaining
377                         //
378                         // We do this by introducing an explicit block which returns RHS value when
379                         // available or null
380                         //
381                         if (target_object.NodeType == System.Linq.Expressions.ExpressionType.Block)
382                                 return target_object;
383
384                         System.Linq.Expressions.UnaryExpression source_object;
385                         if (ctx.HasSet (BuilderContext.Options.CheckedScope)) {
386                                 source_object = System.Linq.Expressions.Expression.ConvertChecked (source.MakeExpression (ctx), target_object.Type);
387                         } else {
388                                 source_object = System.Linq.Expressions.Expression.Convert (source.MakeExpression (ctx), target_object.Type);
389                         }
390
391                         return System.Linq.Expressions.Expression.Assign (target_object, source_object);
392                 }
393
394                 protected virtual Expression ResolveConversions (ResolveContext ec)
395                 {
396                         source = Convert.ImplicitConversionRequired (ec, source, target.Type, source.Location);
397                         if (source == null)
398                                 return null;
399
400                         return this;
401                 }
402
403                 void Emit (EmitContext ec, bool is_statement)
404                 {
405                         IAssignMethod t = (IAssignMethod) target;
406                         t.EmitAssign (ec, source, !is_statement, this is CompoundAssign);
407                 }
408
409                 public override void Emit (EmitContext ec)
410                 {
411                         Emit (ec, false);
412                 }
413
414                 public override void EmitStatement (EmitContext ec)
415                 {
416                         Emit (ec, true);
417                 }
418
419                 public override void FlowAnalysis (FlowAnalysisContext fc)
420                 {
421                         source.FlowAnalysis (fc);
422
423                         if (target is ArrayAccess || target is IndexerExpr) {
424                                 target.FlowAnalysis (fc);
425                                 return;
426                         }
427
428                         var pe = target as PropertyExpr;
429                         if (pe != null && !pe.IsAutoPropertyAccess)
430                                 target.FlowAnalysis (fc);
431                 }
432
433                 protected override void CloneTo (CloneContext clonectx, Expression t)
434                 {
435                         Assign _target = (Assign) t;
436
437                         _target.target = target.Clone (clonectx);
438                         _target.source = source.Clone (clonectx);
439                 }
440
441                 public override object Accept (StructuralVisitor visitor)
442                 {
443                         return visitor.Visit (this);
444                 }
445         }
446
447         public class SimpleAssign : Assign
448         {
449                 public SimpleAssign (Expression target, Expression source)
450                         : this (target, source, target.Location)
451                 {
452                 }
453
454                 public SimpleAssign (Expression target, Expression source, Location loc)
455                         : base (target, source, loc)
456                 {
457                 }
458
459                 bool CheckEqualAssign (Expression t)
460                 {
461                         if (source is Assign) {
462                                 Assign a = (Assign) source;
463                                 if (t.Equals (a.Target))
464                                         return true;
465                                 return a is SimpleAssign && ((SimpleAssign) a).CheckEqualAssign (t);
466                         }
467                         return t.Equals (source);
468                 }
469
470                 protected override Expression DoResolve (ResolveContext ec)
471                 {
472                         Expression e = base.DoResolve (ec);
473                         if (e == null || e != this)
474                                 return e;
475
476                         if (CheckEqualAssign (target))
477                                 ec.Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?");
478
479                         return 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                         var pe = target as PropertyExpr;
501                         if (pe != null) {
502                                 pe.SetBackingFieldAssigned (fc);
503                                 return;
504                         }
505
506                         var td = target as TupleDeconstruct;
507                         if (td != null) {
508                                 td.SetGeneratedFieldAssigned (fc);
509                                 return;
510                         }
511                 }
512
513                 public override Reachability MarkReachable (Reachability rc)
514                 {
515                         return source.MarkReachable (rc);
516                 }
517         }
518
519         public class RuntimeExplicitAssign : Assign
520         {
521                 public RuntimeExplicitAssign (Expression target, Expression source)
522                         : base (target, source, target.Location)
523                 {
524                 }
525
526                 protected override Expression ResolveConversions (ResolveContext ec)
527                 {
528                         source = EmptyCast.Create (source, target.Type);
529                         return this;
530                 }
531         }
532
533         //
534         // Compiler generated assign
535         //
536         class CompilerAssign : Assign
537         {
538                 public CompilerAssign (Expression target, Expression source, Location loc)
539                         : base (target, source, loc)
540                 {
541                         if (target.Type != null) {
542                                 type = target.Type;
543                                 eclass = ExprClass.Value;
544                         }
545                 }
546
547                 protected override Expression DoResolve (ResolveContext ec)
548                 {
549                         var expr = base.DoResolve (ec);
550                         var vr = target as VariableReference;
551                         if (vr != null && vr.VariableInfo != null)
552                                 vr.VariableInfo.IsEverAssigned = false;
553
554                         return expr;
555                 }
556
557                 public void UpdateSource (Expression source)
558                 {
559                         base.source = source;
560                 }
561         }
562
563         //
564         // Implements fields and events class initializers
565         //
566         public class FieldInitializer : Assign
567         {
568                 //
569                 // Field initializers are tricky for partial classes. They have to
570                 // share same constructor (block) for expression trees resolve but
571                 // they have they own resolve scope
572                 //
573                 sealed class FieldInitializerContext : BlockContext
574                 {
575                         readonly ExplicitBlock ctor_block;
576
577                         public FieldInitializerContext (IMemberContext mc, BlockContext constructorContext)
578                                 : base (mc, null, constructorContext.ReturnType)
579                         {
580                                 flags |= Options.FieldInitializerScope | Options.ConstructorScope;
581                                 this.ctor_block = constructorContext.CurrentBlock.Explicit;
582
583                                 if (ctor_block.IsCompilerGenerated)
584                                         CurrentBlock = ctor_block;
585                         }
586
587                         public override ExplicitBlock ConstructorBlock {
588                             get {
589                                 return ctor_block;
590                             }
591                         }
592                 }
593
594                 //
595                 // Keep resolved value because field initializers have their own rules
596                 //
597                 ExpressionStatement resolved;
598                 FieldBase mc;
599
600                 public FieldInitializer (FieldBase mc, Expression expression, Location loc)
601                         : base (new FieldExpr (mc.Spec, expression.Location), expression, loc)
602                 {
603                         this.mc = mc;
604                         if (!mc.IsStatic)
605                                 ((FieldExpr)target).InstanceExpression = new CompilerGeneratedThis (mc.CurrentType, expression.Location);
606                 }
607
608                 public int AssignmentOffset { get; private set; }
609
610                 public FieldBase Field {
611                         get {
612                                 return mc;
613                         }
614                 }
615
616                 public override Location StartLocation {
617                         get {
618                                 return loc;
619                         }
620                 }
621
622                 protected override Expression DoResolve (ResolveContext rc)
623                 {
624                         // Field initializer can be resolved (fail) many times
625                         if (source == null)
626                                 return null;
627
628                         if (resolved == null) {
629                                 var bc = (BlockContext) rc;
630                                 var ctx = new FieldInitializerContext (mc, bc);
631                                 resolved = base.DoResolve (ctx) as ExpressionStatement;
632                                 AssignmentOffset = ctx.AssignmentInfoOffset - bc.AssignmentInfoOffset;
633                         }
634
635                         return resolved;
636                 }
637
638                 public override void EmitStatement (EmitContext ec)
639                 {
640                         if (resolved == null)
641                                 return;
642
643                         //
644                         // Emit sequence symbol info even if we are in compiler generated
645                         // block to allow debugging field initializers when constructor is
646                         // compiler generated
647                         //
648                         if (ec.HasSet (BuilderContext.Options.OmitDebugInfo) && ec.HasMethodSymbolBuilder) {
649                                 using (ec.With (BuilderContext.Options.OmitDebugInfo, false)) {
650                                         ec.Mark (loc);
651                                 }
652                         }
653
654                         if (resolved != this)
655                                 resolved.EmitStatement (ec);
656                         else
657                                 base.EmitStatement (ec);
658                 }
659
660                 public override void FlowAnalysis (FlowAnalysisContext fc)
661                 {
662                         source.FlowAnalysis (fc);
663                         ((FieldExpr) target).SetFieldAssigned (fc);
664                 }
665                 
666                 public bool IsDefaultInitializer {
667                         get {
668                                 Constant c = source as Constant;
669                                 if (c == null)
670                                         return false;
671                                 
672                                 FieldExpr fe = (FieldExpr)target;
673                                 return c.IsDefaultInitializer (fe.Type);
674                         }
675                 }
676
677                 public override bool IsSideEffectFree {
678                         get {
679                                 return source.IsSideEffectFree;
680                         }
681                 }
682         }
683
684         class PrimaryConstructorAssign : SimpleAssign
685         {
686                 readonly Field field;
687                 readonly Parameter parameter;
688
689                 public PrimaryConstructorAssign (Field field, Parameter parameter)
690                         : base (null, null, parameter.Location)
691                 {
692                         this.field = field;
693                         this.parameter = parameter;
694                 }
695
696                 protected override Expression DoResolve (ResolveContext rc)
697                 {
698                         target = new FieldExpr (field, loc);
699                         source = rc.CurrentBlock.ParametersBlock.GetParameterInfo (parameter).CreateReferenceExpression (rc, loc);
700                         return base.DoResolve (rc);
701                 }
702
703                 public override void EmitStatement (EmitContext ec)
704                 {
705                         using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
706                                 base.EmitStatement (ec);
707                         }
708                 }
709         }
710
711         //
712         // This class is used for compound assignments.
713         //
714         public class CompoundAssign : Assign
715         {
716                 // This is just a hack implemented for arrays only
717                 public sealed class TargetExpression : Expression
718                 {
719                         readonly Expression child;
720
721                         public TargetExpression (Expression child)
722                         {
723                                 this.child = child;
724                                 this.loc = child.Location;
725                         }
726
727                         public bool RequiresEmitWithAwait { get; set; }
728
729                         public override bool ContainsEmitWithAwait ()
730                         {
731                                 return RequiresEmitWithAwait || child.ContainsEmitWithAwait ();
732                         }
733
734                         public override Expression CreateExpressionTree (ResolveContext ec)
735                         {
736                                 throw new NotSupportedException ("ET");
737                         }
738
739                         protected override Expression DoResolve (ResolveContext ec)
740                         {
741                                 type = child.Type;
742                                 eclass = ExprClass.Value;
743                                 return this;
744                         }
745
746                         public override void Emit (EmitContext ec)
747                         {
748                                 child.Emit (ec);
749                         }
750
751                         public override Expression EmitToField (EmitContext ec)
752                         {
753                                 return child.EmitToField (ec);
754                         }
755                 }
756
757                 // Used for underlying binary operator
758                 readonly Binary.Operator op;
759                 Expression right;
760                 Expression left;
761
762                 public CompoundAssign (Binary.Operator op, Expression target, Expression source)
763                         : base (target, source, target.Location)
764                 {
765                         right = source;
766                         this.op = op;
767                 }
768
769                 public CompoundAssign (Binary.Operator op, Expression target, Expression source, Expression left)
770                         : this (op, target, source)
771                 {
772                         this.left = left;
773                 }
774
775                 public Binary.Operator Operator {
776                         get {
777                                 return op;
778                         }
779                 }
780
781                 protected override Expression DoResolve (ResolveContext ec)
782                 {
783                         right = right.Resolve (ec);
784                         if (right == null)
785                                 return null;
786
787                         MemberAccess ma = target as MemberAccess;
788                         using (ec.Set (ResolveContext.Options.CompoundAssignmentScope)) {
789                                 target = target.Resolve (ec);
790                         }
791                         
792                         if (target == null)
793                                 return null;
794
795                         if (target is MethodGroupExpr){
796                                 ec.Report.Error (1656, loc,
797                                         "Cannot assign to `{0}' because it is a `{1}'",
798                                         ((MethodGroupExpr)target).Name, target.ExprClassName);
799                                 return null;
800                         }
801
802                         var event_expr = target as EventExpr;
803                         if (event_expr != null) {
804                                 source = Convert.ImplicitConversionRequired (ec, right, target.Type, loc);
805                                 if (source == null)
806                                         return null;
807
808                                 Expression rside;
809                                 if (op == Binary.Operator.Addition)
810                                         rside = EmptyExpression.EventAddition;
811                                 else if (op == Binary.Operator.Subtraction)
812                                         rside = EmptyExpression.EventSubtraction;
813                                 else
814                                         rside = null;
815
816                                 target = target.ResolveLValue (ec, rside);
817                                 if (target == null)
818                                         return null;
819
820                                 eclass = ExprClass.Value;
821                                 type = event_expr.Operator.ReturnType;
822                                 return this;
823                         }
824
825                         //
826                         // Only now we can decouple the original source/target
827                         // into a tree, to guarantee that we do not have side
828                         // effects.
829                         //
830                         if (left == null)
831                                 left = new TargetExpression (target);
832
833                         source = new Binary (op, left, right, true);
834
835                         if (target is DynamicMemberAssignable) {
836                                 Arguments targs = ((DynamicMemberAssignable) target).Arguments;
837                                 source = source.Resolve (ec);
838
839                                 Arguments args = new Arguments (targs.Count + 1);
840                                 args.AddRange (targs);
841                                 args.Add (new Argument (source));
842
843                                 var binder_flags = CSharpBinderFlags.ValueFromCompoundAssignment;
844
845                                 //
846                                 // Compound assignment does target conversion using additional method
847                                 // call, set checked context as the binary operation can overflow
848                                 //
849                                 if (ec.HasSet (ResolveContext.Options.CheckedScope))
850                                         binder_flags |= CSharpBinderFlags.CheckedContext;
851
852                                 if (target is DynamicMemberBinder) {
853                                         source = new DynamicMemberBinder (ma.Name, binder_flags, args, loc).Resolve (ec);
854
855                                         // Handles possible event addition/subtraction
856                                         if (op == Binary.Operator.Addition || op == Binary.Operator.Subtraction) {
857                                                 args = new Arguments (targs.Count + 1);
858                                                 args.AddRange (targs);
859                                                 args.Add (new Argument (right));
860                                                 string method_prefix = op == Binary.Operator.Addition ?
861                                                         Event.AEventAccessor.AddPrefix : Event.AEventAccessor.RemovePrefix;
862
863                                                 var invoke = DynamicInvocation.CreateSpecialNameInvoke (
864                                                         new MemberAccess (right, method_prefix + ma.Name, loc), args, loc).Resolve (ec);
865
866                                                 args = new Arguments (targs.Count);
867                                                 args.AddRange (targs);
868                                                 source = new DynamicEventCompoundAssign (ma.Name, args,
869                                                         (ExpressionStatement) source, (ExpressionStatement) invoke, loc).Resolve (ec);
870                                         }
871                                 } else {
872                                         source = new DynamicIndexBinder (binder_flags, args, loc).Resolve (ec);
873                                 }
874
875                                 return source;
876                         }
877
878                         return base.DoResolve (ec);
879                 }
880
881                 public override void FlowAnalysis (FlowAnalysisContext fc)
882                 {
883                         target.FlowAnalysis (fc);
884                         source.FlowAnalysis (fc);
885                 }
886
887                 protected override Expression ResolveConversions (ResolveContext ec)
888                 {
889                         //
890                         // LAMESPEC: Under dynamic context no target conversion is happening
891                         // This allows more natual dynamic behaviour but breaks compatibility
892                         // with static binding
893                         //
894                         if (target is RuntimeValueExpression)
895                                 return this;
896
897                         TypeSpec target_type = target.Type;
898
899                         //
900                         // 1. the return type is implicitly convertible to the type of target
901                         //
902                         if (Convert.ImplicitConversionExists (ec, source, target_type)) {
903                                 source = Convert.ImplicitConversion (ec, source, target_type, loc);
904                                 return this;
905                         }
906
907                         //
908                         // Otherwise, if the selected operator is a predefined operator
909                         //
910                         Binary b = source as Binary;
911                         if (b == null) {
912                                 if (source is ReducedExpression)
913                                         b = ((ReducedExpression) source).OriginalExpression as Binary;
914                                 else if (source is ReducedExpression.ReducedConstantExpression) {
915                                         b = ((ReducedExpression.ReducedConstantExpression) source).OriginalExpression as Binary;
916                                 } else if (source is Nullable.LiftedBinaryOperator) {
917                                         var po = ((Nullable.LiftedBinaryOperator) source);
918                                         if (po.UserOperator == null)
919                                                 b = po.Binary;
920                                 } else if (source is TypeCast) {
921                                         b = ((TypeCast) source).Child as Binary;
922                                 }
923                         }
924
925                         if (b != null) {
926                                 //
927                                 // 2a. the operator is a shift operator
928                                 //
929                                 // 2b. the return type is explicitly convertible to the type of x, and
930                                 // y is implicitly convertible to the type of x
931                                 //
932                                 if ((b.Oper & Binary.Operator.ShiftMask) != 0 ||
933                                         Convert.ImplicitConversionExists (ec, right, target_type)) {
934                                         source = Convert.ExplicitConversion (ec, source, target_type, loc);
935                                         return this;
936                                 }
937                         }
938
939                         if (source.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
940                                 Arguments arg = new Arguments (1);
941                                 arg.Add (new Argument (source));
942                                 return new SimpleAssign (target, new DynamicConversion (target_type, CSharpBinderFlags.ConvertExplicit, arg, loc), loc).Resolve (ec);
943                         }
944
945                         right.Error_ValueCannotBeConverted (ec, target_type, false);
946                         return null;
947                 }
948
949                 protected override void CloneTo (CloneContext clonectx, Expression t)
950                 {
951                         CompoundAssign ctarget = (CompoundAssign) t;
952
953                         ctarget.right = ctarget.source = source.Clone (clonectx);
954                         ctarget.target = target.Clone (clonectx);
955                 }
956
957                 public override object Accept (StructuralVisitor visitor)
958                 {
959                         return visitor.Visit (this);
960                 }
961         }
962 }