~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to contrib/ICSharpCode.NRefactory.CSharp/Parser/mcs/assign.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
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 bool ContainsEmitWithAwait ()
314
 
                {
315
 
                        return target.ContainsEmitWithAwait () || source.ContainsEmitWithAwait ();
316
 
                }
317
 
 
318
 
                public override Expression CreateExpressionTree (ResolveContext ec)
319
 
                {
320
 
                        ec.Report.Error (832, loc, "An expression tree cannot contain an assignment operator");
321
 
                        return null;
322
 
                }
323
 
 
324
 
                protected override Expression DoResolve (ResolveContext ec)
325
 
                {
326
 
                        bool ok = true;
327
 
                        source = source.Resolve (ec);
328
 
                                                
329
 
                        if (source == null) {
330
 
                                ok = false;
331
 
                                source = EmptyExpression.Null;
332
 
                        }
333
 
 
334
 
                        target = target.ResolveLValue (ec, source);
335
 
 
336
 
                        if (target == null || !ok)
337
 
                                return null;
338
 
 
339
 
                        TypeSpec target_type = target.Type;
340
 
                        TypeSpec source_type = source.Type;
341
 
 
342
 
                        eclass = ExprClass.Value;
343
 
                        type = target_type;
344
 
 
345
 
                        if (!(target is IAssignMethod)) {
346
 
                                target.Error_ValueAssignment (ec, source);
347
 
                                return null;
348
 
                        }
349
 
 
350
 
                        if (target_type != source_type) {
351
 
                                Expression resolved = ResolveConversions (ec);
352
 
 
353
 
                                if (resolved != this)
354
 
                                        return resolved;
355
 
                        }
356
 
 
357
 
                        return this;
358
 
                }
359
 
 
360
 
#if NET_4_0 || MONODROID
361
 
                public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx)
362
 
                {
363
 
                        var tassign = target as IDynamicAssign;
364
 
                        if (tassign == null)
365
 
                                throw new InternalErrorException (target.GetType () + " does not support dynamic assignment");
366
 
 
367
 
                        var target_object = tassign.MakeAssignExpression (ctx, source);
368
 
 
369
 
                        //
370
 
                        // Some hacking is needed as DLR does not support void type and requires
371
 
                        // always have object convertible return type to support caching and chaining
372
 
                        //
373
 
                        // We do this by introducing an explicit block which returns RHS value when
374
 
                        // available or null
375
 
                        //
376
 
                        if (target_object.NodeType == System.Linq.Expressions.ExpressionType.Block)
377
 
                                return target_object;
378
 
 
379
 
                        System.Linq.Expressions.UnaryExpression source_object;
380
 
                        if (ctx.HasSet (BuilderContext.Options.CheckedScope)) {
381
 
                                source_object = System.Linq.Expressions.Expression.ConvertChecked (source.MakeExpression (ctx), target_object.Type);
382
 
                        } else {
383
 
                                source_object = System.Linq.Expressions.Expression.Convert (source.MakeExpression (ctx), target_object.Type);
384
 
                        }
385
 
 
386
 
                        return System.Linq.Expressions.Expression.Assign (target_object, source_object);
387
 
                }
388
 
#endif
389
 
                protected virtual Expression ResolveConversions (ResolveContext ec)
390
 
                {
391
 
                        source = Convert.ImplicitConversionRequired (ec, source, target.Type, source.Location);
392
 
                        if (source == null)
393
 
                                return null;
394
 
 
395
 
                        return this;
396
 
                }
397
 
 
398
 
                void Emit (EmitContext ec, bool is_statement)
399
 
                {
400
 
                        IAssignMethod t = (IAssignMethod) target;
401
 
                        t.EmitAssign (ec, source, !is_statement, this is CompoundAssign);
402
 
                }
403
 
 
404
 
                public override void Emit (EmitContext ec)
405
 
                {
406
 
                        Emit (ec, false);
407
 
                }
408
 
 
409
 
                public override void EmitStatement (EmitContext ec)
410
 
                {
411
 
                        Emit (ec, true);
412
 
                }
413
 
 
414
 
                protected override void CloneTo (CloneContext clonectx, Expression t)
415
 
                {
416
 
                        Assign _target = (Assign) t;
417
 
 
418
 
                        _target.target = target.Clone (clonectx);
419
 
                        _target.source = source.Clone (clonectx);
420
 
                }
421
 
 
422
 
                public override object Accept (StructuralVisitor visitor)
423
 
                {
424
 
                        return visitor.Visit (this);
425
 
                }
426
 
        }
427
 
 
428
 
        public class SimpleAssign : Assign
429
 
        {
430
 
                public SimpleAssign (Expression target, Expression source)
431
 
                        : this (target, source, target.Location)
432
 
                {
433
 
                }
434
 
 
435
 
                public SimpleAssign (Expression target, Expression source, Location loc)
436
 
                        : base (target, source, loc)
437
 
                {
438
 
                }
439
 
 
440
 
                bool CheckEqualAssign (Expression t)
441
 
                {
442
 
                        if (source is Assign) {
443
 
                                Assign a = (Assign) source;
444
 
                                if (t.Equals (a.Target))
445
 
                                        return true;
446
 
                                return a is SimpleAssign && ((SimpleAssign) a).CheckEqualAssign (t);
447
 
                        }
448
 
                        return t.Equals (source);
449
 
                }
450
 
 
451
 
                protected override Expression DoResolve (ResolveContext ec)
452
 
                {
453
 
                        Expression e = base.DoResolve (ec);
454
 
                        if (e == null || e != this)
455
 
                                return e;
456
 
 
457
 
                        if (CheckEqualAssign (target))
458
 
                                ec.Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?");
459
 
 
460
 
                        return this;
461
 
                }
462
 
 
463
 
                public override object Accept (StructuralVisitor visitor)
464
 
                {
465
 
                        return visitor.Visit (this);
466
 
                }
467
 
        }
468
 
 
469
 
        public class RuntimeExplicitAssign : Assign
470
 
        {
471
 
                public RuntimeExplicitAssign (Expression target, Expression source)
472
 
                        : base (target, source, target.Location)
473
 
                {
474
 
                }
475
 
 
476
 
                protected override Expression ResolveConversions (ResolveContext ec)
477
 
                {
478
 
                        source = EmptyCast.Create (source, target.Type);
479
 
                        return this;
480
 
                }
481
 
        }
482
 
 
483
 
        //
484
 
        // Compiler generated assign
485
 
        //
486
 
        class CompilerAssign : Assign
487
 
        {
488
 
                public CompilerAssign (Expression target, Expression source, Location loc)
489
 
                        : base (target, source, loc)
490
 
                {
491
 
                        if (target.Type != null) {
492
 
                                type = target.Type;
493
 
                                eclass = ExprClass.Value;
494
 
                        }
495
 
                }
496
 
 
497
 
                protected override Expression DoResolve (ResolveContext ec)
498
 
                {
499
 
                        var expr = base.DoResolve (ec);
500
 
                        var vr = target as VariableReference;
501
 
                        if (vr != null && vr.VariableInfo != null)
502
 
                                vr.VariableInfo.IsEverAssigned = false;
503
 
 
504
 
                        return expr;
505
 
                }
506
 
 
507
 
                public void UpdateSource (Expression source)
508
 
                {
509
 
                        base.source = source;
510
 
                }
511
 
        }
512
 
 
513
 
        //
514
 
        // Implements fields and events class initializers
515
 
        //
516
 
        public class FieldInitializer : Assign
517
 
        {
518
 
                //
519
 
                // Field initializers are tricky for partial classes. They have to
520
 
                // share same constructor (block) for expression trees resolve but
521
 
                // they have they own resolve scope
522
 
                //
523
 
                sealed class FieldInitializerContext : ResolveContext
524
 
                {
525
 
                        ExplicitBlock ctor_block;
526
 
 
527
 
                        public FieldInitializerContext (IMemberContext mc, ResolveContext constructorContext)
528
 
                                : base (mc, Options.FieldInitializerScope | Options.ConstructorScope)
529
 
                        {
530
 
                                this.ctor_block = constructorContext.CurrentBlock.Explicit;
531
 
                        }
532
 
 
533
 
                        public override ExplicitBlock ConstructorBlock {
534
 
                                get {
535
 
                                        return ctor_block;
536
 
                                }
537
 
                        }
538
 
                }
539
 
 
540
 
                //
541
 
                // Keep resolved value because field initializers have their own rules
542
 
                //
543
 
                ExpressionStatement resolved;
544
 
                IMemberContext mc;
545
 
 
546
 
                public FieldInitializer (FieldSpec spec, Expression expression, IMemberContext mc)
547
 
                        : base (new FieldExpr (spec, expression.Location), expression, expression.Location)
548
 
                {
549
 
                        this.mc = mc;
550
 
                        if (!spec.IsStatic)
551
 
                                ((FieldExpr)target).InstanceExpression = new CompilerGeneratedThis (mc.CurrentType, expression.Location);
552
 
                }
553
 
 
554
 
                protected override Expression DoResolve (ResolveContext ec)
555
 
                {
556
 
                        // Field initializer can be resolved (fail) many times
557
 
                        if (source == null)
558
 
                                return null;
559
 
 
560
 
                        if (resolved == null) {
561
 
                                var ctx = new FieldInitializerContext (mc, ec);
562
 
                                resolved = base.DoResolve (ctx) as ExpressionStatement;
563
 
                        }
564
 
 
565
 
                        return resolved;
566
 
                }
567
 
 
568
 
                public override void EmitStatement (EmitContext ec)
569
 
                {
570
 
                        if (resolved == null)
571
 
                                return;
572
 
 
573
 
                        //
574
 
                        // Emit sequence symbol info even if we are in compiler generated
575
 
                        // block to allow debugging field initializers when constructor is
576
 
                        // compiler generated
577
 
                        //
578
 
                        if (ec.HasSet (BuilderContext.Options.OmitDebugInfo) && ec.HasMethodSymbolBuilder) {
579
 
                                using (ec.With (BuilderContext.Options.OmitDebugInfo, false)) {
580
 
                                        ec.Mark (loc);
581
 
                                }
582
 
                        }
583
 
 
584
 
                        if (resolved != this)
585
 
                                resolved.EmitStatement (ec);
586
 
                        else
587
 
                                base.EmitStatement (ec);
588
 
                }
589
 
                
590
 
                public bool IsDefaultInitializer {
591
 
                        get {
592
 
                                Constant c = source as Constant;
593
 
                                if (c == null)
594
 
                                        return false;
595
 
                                
596
 
                                FieldExpr fe = (FieldExpr)target;
597
 
                                return c.IsDefaultInitializer (fe.Type);
598
 
                        }
599
 
                }
600
 
 
601
 
                public override bool IsSideEffectFree {
602
 
                        get {
603
 
                                return source.IsSideEffectFree;
604
 
                        }
605
 
                }
606
 
        }
607
 
 
608
 
        //
609
 
        // This class is used for compound assignments.
610
 
        //
611
 
        public class CompoundAssign : Assign
612
 
        {
613
 
                // This is just a hack implemented for arrays only
614
 
                public sealed class TargetExpression : Expression
615
 
                {
616
 
                        readonly Expression child;
617
 
 
618
 
                        public TargetExpression (Expression child)
619
 
                        {
620
 
                                this.child = child;
621
 
                                this.loc = child.Location;
622
 
                        }
623
 
 
624
 
                        public override bool ContainsEmitWithAwait ()
625
 
                        {
626
 
                                return child.ContainsEmitWithAwait ();
627
 
                        }
628
 
 
629
 
                        public override Expression CreateExpressionTree (ResolveContext ec)
630
 
                        {
631
 
                                throw new NotSupportedException ("ET");
632
 
                        }
633
 
 
634
 
                        protected override Expression DoResolve (ResolveContext ec)
635
 
                        {
636
 
                                type = child.Type;
637
 
                                eclass = ExprClass.Value;
638
 
                                return this;
639
 
                        }
640
 
 
641
 
                        public override void Emit (EmitContext ec)
642
 
                        {
643
 
                                child.Emit (ec);
644
 
                        }
645
 
 
646
 
                        public override Expression EmitToField (EmitContext ec)
647
 
                        {
648
 
                                return child.EmitToField (ec);
649
 
                        }
650
 
                }
651
 
 
652
 
                // Used for underlying binary operator
653
 
                readonly Binary.Operator op;
654
 
                Expression right;
655
 
                Expression left;
656
 
                
657
 
                public Binary.Operator Op {
658
 
                        get {
659
 
                                return op;
660
 
                        }
661
 
                }
662
 
 
663
 
                public CompoundAssign (Binary.Operator op, Expression target, Expression source, Location loc)
664
 
                        : base (target, source, loc)
665
 
                {
666
 
                        right = source;
667
 
                        this.op = op;
668
 
                }
669
 
 
670
 
                public CompoundAssign (Binary.Operator op, Expression target, Expression source, Expression left, Location loc)
671
 
                        : this (op, target, source, loc)
672
 
                {
673
 
                        this.left = left;
674
 
                }
675
 
 
676
 
                public Binary.Operator Operator {
677
 
                        get {
678
 
                                return op;
679
 
                        }
680
 
                }
681
 
 
682
 
                protected override Expression DoResolve (ResolveContext ec)
683
 
                {
684
 
                        right = right.Resolve (ec);
685
 
                        if (right == null)
686
 
                                return null;
687
 
 
688
 
                        MemberAccess ma = target as MemberAccess;
689
 
                        using (ec.Set (ResolveContext.Options.CompoundAssignmentScope)) {
690
 
                                target = target.Resolve (ec);
691
 
                        }
692
 
                        
693
 
                        if (target == null)
694
 
                                return null;
695
 
 
696
 
                        if (target is MethodGroupExpr){
697
 
                                ec.Report.Error (1656, loc,
698
 
                                        "Cannot assign to `{0}' because it is a `{1}'",
699
 
                                        ((MethodGroupExpr)target).Name, target.ExprClassName);
700
 
                                return null;
701
 
                        }
702
 
 
703
 
                        var event_expr = target as EventExpr;
704
 
                        if (event_expr != null) {
705
 
                                source = Convert.ImplicitConversionRequired (ec, right, target.Type, loc);
706
 
                                if (source == null)
707
 
                                        return null;
708
 
 
709
 
                                Expression rside;
710
 
                                if (op == Binary.Operator.Addition)
711
 
                                        rside = EmptyExpression.EventAddition;
712
 
                                else if (op == Binary.Operator.Subtraction)
713
 
                                        rside = EmptyExpression.EventSubtraction;
714
 
                                else
715
 
                                        rside = null;
716
 
 
717
 
                                target = target.ResolveLValue (ec, rside);
718
 
                                if (target == null)
719
 
                                        return null;
720
 
 
721
 
                                eclass = ExprClass.Value;
722
 
                                type = event_expr.Operator.ReturnType;
723
 
                                return this;
724
 
                        }
725
 
 
726
 
                        //
727
 
                        // Only now we can decouple the original source/target
728
 
                        // into a tree, to guarantee that we do not have side
729
 
                        // effects.
730
 
                        //
731
 
                        if (left == null)
732
 
                                left = new TargetExpression (target);
733
 
 
734
 
                        source = new Binary (op, left, right, true, loc);
735
 
 
736
 
                        if (target is DynamicMemberAssignable) {
737
 
                                Arguments targs = ((DynamicMemberAssignable) target).Arguments;
738
 
                                source = source.Resolve (ec);
739
 
 
740
 
                                Arguments args = new Arguments (targs.Count + 1);
741
 
                                args.AddRange (targs);
742
 
                                args.Add (new Argument (source));
743
 
 
744
 
                                var binder_flags = CSharpBinderFlags.ValueFromCompoundAssignment;
745
 
 
746
 
                                //
747
 
                                // Compound assignment does target conversion using additional method
748
 
                                // call, set checked context as the binary operation can overflow
749
 
                                //
750
 
                                if (ec.HasSet (ResolveContext.Options.CheckedScope))
751
 
                                        binder_flags |= CSharpBinderFlags.CheckedContext;
752
 
 
753
 
                                if (target is DynamicMemberBinder) {
754
 
                                        source = new DynamicMemberBinder (ma.Name, binder_flags, args, loc).Resolve (ec);
755
 
 
756
 
                                        // Handles possible event addition/subtraction
757
 
                                        if (op == Binary.Operator.Addition || op == Binary.Operator.Subtraction) {
758
 
                                                args = new Arguments (targs.Count + 1);
759
 
                                                args.AddRange (targs);
760
 
                                                args.Add (new Argument (right));
761
 
                                                string method_prefix = op == Binary.Operator.Addition ?
762
 
                                                        Event.AEventAccessor.AddPrefix : Event.AEventAccessor.RemovePrefix;
763
 
 
764
 
                                                var invoke = DynamicInvocation.CreateSpecialNameInvoke (
765
 
                                                        new MemberAccess (right, method_prefix + ma.Name, loc), args, loc).Resolve (ec);
766
 
 
767
 
                                                args = new Arguments (targs.Count);
768
 
                                                args.AddRange (targs);
769
 
                                                source = new DynamicEventCompoundAssign (ma.Name, args,
770
 
                                                        (ExpressionStatement) source, (ExpressionStatement) invoke, loc).Resolve (ec);
771
 
                                        }
772
 
                                } else {
773
 
                                        source = new DynamicIndexBinder (binder_flags, args, loc).Resolve (ec);
774
 
                                }
775
 
 
776
 
                                return source;
777
 
                        }
778
 
 
779
 
                        return base.DoResolve (ec);
780
 
                }
781
 
 
782
 
                protected override Expression ResolveConversions (ResolveContext ec)
783
 
                {
784
 
                        //
785
 
                        // LAMESPEC: Under dynamic context no target conversion is happening
786
 
                        // This allows more natual dynamic behaviour but breaks compatibility
787
 
                        // with static binding
788
 
                        //
789
 
                        if (target is RuntimeValueExpression)
790
 
                                return this;
791
 
 
792
 
                        TypeSpec target_type = target.Type;
793
 
 
794
 
                        //
795
 
                        // 1. the return type is implicitly convertible to the type of target
796
 
                        //
797
 
                        if (Convert.ImplicitConversionExists (ec, source, target_type)) {
798
 
                                source = Convert.ImplicitConversion (ec, source, target_type, loc);
799
 
                                return this;
800
 
                        }
801
 
 
802
 
                        //
803
 
                        // Otherwise, if the selected operator is a predefined operator
804
 
                        //
805
 
                        Binary b = source as Binary;
806
 
                        if (b == null && source is ReducedExpression)
807
 
                                b = ((ReducedExpression) source).OriginalExpression as Binary;
808
 
 
809
 
                        if (b != null) {
810
 
                                //
811
 
                                // 2a. the operator is a shift operator
812
 
                                //
813
 
                                // 2b. the return type is explicitly convertible to the type of x, and
814
 
                                // y is implicitly convertible to the type of x
815
 
                                //
816
 
                                if ((b.Oper & Binary.Operator.ShiftMask) != 0 ||
817
 
                                        Convert.ImplicitConversionExists (ec, right, target_type)) {
818
 
                                        source = Convert.ExplicitConversion (ec, source, target_type, loc);
819
 
                                        return this;
820
 
                                }
821
 
                        }
822
 
 
823
 
                        if (source.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
824
 
                                Arguments arg = new Arguments (1);
825
 
                                arg.Add (new Argument (source));
826
 
                                return new SimpleAssign (target, new DynamicConversion (target_type, CSharpBinderFlags.ConvertExplicit, arg, loc), loc).Resolve (ec);
827
 
                        }
828
 
 
829
 
                        right.Error_ValueCannotBeConverted (ec, target_type, false);
830
 
                        return null;
831
 
                }
832
 
 
833
 
                protected override void CloneTo (CloneContext clonectx, Expression t)
834
 
                {
835
 
                        CompoundAssign ctarget = (CompoundAssign) t;
836
 
 
837
 
                        ctarget.right = ctarget.source = source.Clone (clonectx);
838
 
                        ctarget.target = target.Clone (clonectx);
839
 
                }
840
 
 
841
 
                public override object Accept (StructuralVisitor visitor)
842
 
                {
843
 
                        return visitor.Visit (this);
844
 
                }
845
 
        }
846
 
}