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