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