2009-12-09 Jb Evain <jbevain@novell.com>
[mcs.git] / mcs / assign.cs
blob8d52f017facf9dcef1662847a541b04da1db0675
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
11 // Copyright 2001, 2002, 2003 Ximian, Inc.
12 // Copyright 2004-2008 Novell, Inc
14 using System;
15 using System.Reflection;
16 using System.Reflection.Emit;
18 namespace Mono.CSharp {
20 /// <summary>
21 /// This interface is implemented by expressions that can be assigned to.
22 /// </summary>
23 /// <remarks>
24 /// This interface is implemented by Expressions whose values can not
25 /// store the result on the top of the stack.
26 ///
27 /// Expressions implementing this (Properties, Indexers and Arrays) would
28 /// perform an assignment of the Expression "source" into its final
29 /// location.
30 ///
31 /// No values on the top of the stack are expected to be left by
32 /// invoking this method.
33 /// </remarks>
34 public interface IAssignMethod {
36 // This is an extra version of Emit. If leave_copy is `true'
37 // A copy of the expression will be left on the stack at the
38 // end of the code generated for EmitAssign
40 void Emit (EmitContext ec, bool leave_copy);
43 // This method does the assignment
44 // `source' will be stored into the location specified by `this'
45 // if `leave_copy' is true, a copy of `source' will be left on the stack
46 // if `prepare_for_load' is true, when `source' is emitted, there will
47 // be data on the stack that it can use to compuatate its value. This is
48 // for expressions like a [f ()] ++, where you can't call `f ()' twice.
50 void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load);
53 For simple assignments, this interface is very simple, EmitAssign is called with source
54 as the source expression and leave_copy and prepare_for_load false.
56 For compound assignments it gets complicated.
58 EmitAssign will be called as before, however, prepare_for_load will be
59 true. The @source expression will contain an expression
60 which calls Emit. So, the calls look like:
62 this.EmitAssign (ec, source, false, true) ->
63 source.Emit (ec); ->
64 [...] ->
65 this.Emit (ec, false); ->
66 end this.Emit (ec, false); ->
67 end [...]
68 end source.Emit (ec);
69 end this.EmitAssign (ec, source, false, true)
72 When prepare_for_load is true, EmitAssign emits a `token' on the stack that
73 Emit will use for its state.
75 Let's take FieldExpr as an example. assume we are emitting f ().y += 1;
77 Here is the call tree again. This time, each call is annotated with the IL
78 it produces:
80 this.EmitAssign (ec, source, false, true)
81 call f
82 dup
84 Binary.Emit ()
85 this.Emit (ec, false);
86 ldfld y
87 end this.Emit (ec, false);
89 IntConstant.Emit ()
90 ldc.i4.1
91 end IntConstant.Emit
93 add
94 end Binary.Emit ()
96 stfld
97 end this.EmitAssign (ec, source, false, true)
99 Observe two things:
100 1) EmitAssign left a token on the stack. It was the result of f ().
101 2) This token was used by Emit
103 leave_copy (in both EmitAssign and Emit) tells the compiler to leave a copy
104 of the expression at that point in evaluation. This is used for pre/post inc/dec
105 and for a = x += y. Let's do the above example with leave_copy true in EmitAssign
107 this.EmitAssign (ec, source, true, true)
108 call f
111 Binary.Emit ()
112 this.Emit (ec, false);
113 ldfld y
114 end this.Emit (ec, false);
116 IntConstant.Emit ()
117 ldc.i4.1
118 end IntConstant.Emit
121 end Binary.Emit ()
124 stloc temp
125 stfld
126 ldloc temp
127 end this.EmitAssign (ec, source, true, true)
129 And with it true in Emit
131 this.EmitAssign (ec, source, false, true)
132 call f
135 Binary.Emit ()
136 this.Emit (ec, true);
137 ldfld y
139 stloc temp
140 end this.Emit (ec, true);
142 IntConstant.Emit ()
143 ldc.i4.1
144 end IntConstant.Emit
147 end Binary.Emit ()
149 stfld
150 ldloc temp
151 end this.EmitAssign (ec, source, false, true)
153 Note that these two examples are what happens for ++x and x++, respectively.
157 /// <summary>
158 /// An Expression to hold a temporary value.
159 /// </summary>
160 /// <remarks>
161 /// The LocalTemporary class is used to hold temporary values of a given
162 /// type to "simulate" the expression semantics on property and indexer
163 /// access whose return values are void.
165 /// The local temporary is used to alter the normal flow of code generation
166 /// basically it creates a local variable, and its emit instruction generates
167 /// code to access this value, return its address or save its value.
169 /// If `is_address' is true, then the value that we store is the address to the
170 /// real value, and not the value itself.
172 /// This is needed for a value type, because otherwise you just end up making a
173 /// copy of the value on the stack and modifying it. You really need a pointer
174 /// to the origional value so that you can modify it in that location. This
175 /// Does not happen with a class because a class is a pointer -- so you always
176 /// get the indirection.
178 /// </remarks>
179 public class LocalTemporary : Expression, IMemoryLocation, IAssignMethod {
180 LocalBuilder builder;
182 public LocalTemporary (Type t)
184 type = t;
185 eclass = ExprClass.Value;
188 public LocalTemporary (LocalBuilder b, Type t)
189 : this (t)
191 builder = b;
194 public void Release (EmitContext ec)
196 ec.FreeTemporaryLocal (builder, type);
197 builder = null;
200 public override Expression CreateExpressionTree (ResolveContext ec)
202 Arguments args = new Arguments (1);
203 args.Add (new Argument (this));
204 return CreateExpressionFactoryCall (ec, "Constant", args);
207 protected override Expression DoResolve (ResolveContext ec)
209 return this;
212 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
214 return this;
217 public override void Emit (EmitContext ec)
219 ILGenerator ig = ec.ig;
221 if (builder == null)
222 throw new InternalErrorException ("Emit without Store, or after Release");
224 ig.Emit (OpCodes.Ldloc, builder);
226 // we need to copy from the pointer
227 if (builder.LocalType.IsByRef)
228 LoadFromPtr (ig, type);
231 #region IAssignMethod Members
233 public void Emit (EmitContext ec, bool leave_copy)
235 Emit (ec);
237 if (leave_copy)
238 Emit (ec);
241 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
243 if (prepare_for_load)
244 throw new NotImplementedException ();
246 source.Emit (ec);
248 Store (ec);
250 if (leave_copy)
251 Emit (ec);
254 #endregion
256 public LocalBuilder Builder {
257 get { return builder; }
260 public void Store (EmitContext ec)
262 ILGenerator ig = ec.ig;
263 if (builder == null)
264 builder = ec.GetTemporaryLocal (type);
266 ig.Emit (OpCodes.Stloc, builder);
269 public void AddressOf (EmitContext ec, AddressOp mode)
271 if (builder == null)
272 builder = ec.GetTemporaryLocal (type);
274 ILGenerator ig = ec.ig;
276 if (builder.LocalType.IsByRef) {
278 // if is_address, than this is just the address anyways,
279 // so we just return this.
281 ig.Emit (OpCodes.Ldloc, builder);
282 } else {
283 ig.Emit (OpCodes.Ldloca, builder);
287 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
289 type = storey.MutateType (type);
293 /// <summary>
294 /// The Assign node takes care of assigning the value of source into
295 /// the expression represented by target.
296 /// </summary>
297 public abstract class Assign : ExpressionStatement {
298 protected Expression target, source;
300 protected Assign (Expression target, Expression source, Location loc)
302 this.target = target;
303 this.source = source;
304 this.loc = loc;
307 public override Expression CreateExpressionTree (ResolveContext ec)
309 ec.Report.Error (832, loc, "An expression tree cannot contain an assignment operator");
310 return null;
313 public Expression Target {
314 get { return target; }
317 public Expression Source {
318 get { return source; }
321 protected override Expression DoResolve (ResolveContext ec)
323 bool ok = true;
324 source = source.Resolve (ec);
326 if (source == null) {
327 ok = false;
328 source = EmptyExpression.Null;
331 target = target.ResolveLValue (ec, source);
333 if (target == null || !ok)
334 return null;
336 Type target_type = target.Type;
337 Type source_type = source.Type;
339 eclass = ExprClass.Value;
340 type = target_type;
342 if (!(target is IAssignMethod)) {
343 Error_ValueAssignment (ec, loc);
344 return null;
347 if ((RootContext.Version == LanguageVersion.ISO_1) &&
348 (source is MethodGroupExpr)){
349 ((MethodGroupExpr) source).ReportUsageError (ec);
350 return null;
353 if (!TypeManager.IsEqual (target_type, source_type)) {
354 Expression resolved = ResolveConversions (ec);
356 if (resolved != this)
357 return resolved;
360 return this;
363 #if NET_4_0
364 public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx)
366 var tassign = target as IDynamicAssign;
367 if (tassign == null)
368 throw new InternalErrorException (target.GetType () + " does not support dynamic assignment");
370 var target_object = tassign.MakeAssignExpression (ctx);
373 // Some hacking is needed as DLR does not support void type and requires
374 // always have object convertible return type to support caching and chaining
376 // We do this by introducing an explicit block which returns RHS value when
377 // available or null
379 if (target_object.NodeType == System.Linq.Expressions.ExpressionType.Block)
380 return target_object;
382 var source_object = System.Linq.Expressions.Expression.Convert (source.MakeExpression (ctx), target_object.Type);
383 return System.Linq.Expressions.Expression.Assign (target_object, source_object);
385 #endif
387 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
389 source.MutateHoistedGenericType (storey);
390 target.MutateHoistedGenericType (storey);
391 type = storey.MutateType (type);
394 protected virtual Expression ResolveConversions (ResolveContext ec)
396 source = Convert.ImplicitConversionRequired (ec, source, target.Type, loc);
397 if (source == null)
398 return null;
400 return this;
403 void Emit (EmitContext ec, bool is_statement)
405 IAssignMethod t = (IAssignMethod) target;
406 t.EmitAssign (ec, source, !is_statement, this is CompoundAssign);
409 public override void Emit (EmitContext ec)
411 Emit (ec, false);
414 public override void EmitStatement (EmitContext ec)
416 Emit (ec, true);
419 protected override void CloneTo (CloneContext clonectx, Expression t)
421 Assign _target = (Assign) t;
423 _target.target = target.Clone (clonectx);
424 _target.source = source.Clone (clonectx);
428 public class SimpleAssign : Assign {
429 public SimpleAssign (Expression target, Expression source)
430 : this (target, source, target.Location)
434 public SimpleAssign (Expression target, Expression source, Location loc)
435 : base (target, source, loc)
439 bool CheckEqualAssign (Expression t)
441 if (source is Assign) {
442 Assign a = (Assign) source;
443 if (t.Equals (a.Target))
444 return true;
445 return a is SimpleAssign && ((SimpleAssign) a).CheckEqualAssign (t);
447 return t.Equals (source);
450 protected override Expression DoResolve (ResolveContext ec)
452 Expression e = base.DoResolve (ec);
453 if (e == null || e != this)
454 return e;
456 if (CheckEqualAssign (target))
457 ec.Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?");
459 return this;
463 // This class implements fields and events class initializers
464 public class FieldInitializer : Assign
467 // Keep resolved value because field initializers have their own rules
469 ExpressionStatement resolved;
470 IMemberContext rc;
472 public FieldInitializer (FieldBuilder field, Expression expression, IMemberContext rc)
473 : base (new FieldExpr (field, expression.Location), expression, expression.Location)
475 this.rc = rc;
476 if (!field.IsStatic)
477 ((FieldExpr)target).InstanceExpression = CompilerGeneratedThis.Instance;
480 protected override Expression DoResolve (ResolveContext ec)
482 // Field initializer can be resolved (fail) many times
483 if (source == null)
484 return null;
486 if (resolved == null) {
488 // Field initializers are tricky for partial classes. They have to
489 // share same constructor (block) but they have they own resolve scope.
492 IMemberContext old = ec.MemberContext;
493 ec.MemberContext = rc;
495 using (ec.Set (ResolveContext.Options.FieldInitializerScope)) {
496 resolved = base.DoResolve (ec) as ExpressionStatement;
499 ec.MemberContext = old;
502 return resolved;
505 public override void EmitStatement (EmitContext ec)
507 if (resolved == null)
508 return;
510 if (resolved != this)
511 resolved.EmitStatement (ec);
512 else
513 base.EmitStatement (ec);
516 public bool IsComplexInitializer {
517 get { return !(source is Constant); }
520 public bool IsDefaultInitializer {
521 get {
522 Constant c = source as Constant;
523 if (c == null)
524 return false;
526 FieldExpr fe = (FieldExpr)target;
527 return c.IsDefaultInitializer (fe.Type);
532 class EventAddOrRemove : ExpressionStatement {
533 EventExpr target;
534 Binary.Operator op;
535 Expression source;
537 public EventAddOrRemove (Expression target, Binary.Operator op, Expression source, Location loc)
539 this.target = target as EventExpr;
540 this.op = op;
541 this.source = source;
542 this.loc = loc;
545 public override Expression CreateExpressionTree (ResolveContext ec)
547 return new SimpleAssign (target, source).CreateExpressionTree (ec);
550 protected override Expression DoResolve (ResolveContext ec)
552 if (op != Binary.Operator.Addition && op != Binary.Operator.Subtraction)
553 target.Error_AssignmentEventOnly (ec);
555 source = source.Resolve (ec);
556 if (source == null)
557 return null;
559 source = Convert.ImplicitConversionRequired (ec, source, target.Type, loc);
560 if (source == null)
561 return null;
563 eclass = ExprClass.Value;
564 type = TypeManager.void_type;
565 return this;
568 public override void Emit (EmitContext ec)
570 if (RootContext.EvalMode)
571 EmitStatement (ec);
572 else
573 throw new InternalErrorException ("don't know what to emit");
576 public override void EmitStatement (EmitContext ec)
578 target.EmitAddOrRemove (ec, op == Binary.Operator.Addition, source);
583 // This class is used for compound assignments.
585 public class CompoundAssign : Assign
587 // This is just a hack implemented for arrays only
588 public sealed class TargetExpression : Expression
590 Expression child;
591 public TargetExpression (Expression child)
593 this.child = child;
594 this.loc = child.Location;
597 public override Expression CreateExpressionTree (ResolveContext ec)
599 throw new NotSupportedException ("ET");
602 protected override Expression DoResolve (ResolveContext ec)
604 type = child.Type;
605 eclass = ExprClass.Value;
606 return this;
609 public override void Emit (EmitContext ec)
611 child.Emit (ec);
615 // Used for underlying binary operator
616 readonly Binary.Operator op;
617 Expression right;
618 Expression left;
620 public CompoundAssign (Binary.Operator op, Expression target, Expression source)
621 : base (target, source, target.Location)
623 right = source;
624 this.op = op;
627 public CompoundAssign (Binary.Operator op, Expression target, Expression source, Expression left)
628 : this (op, target, source)
630 this.left = left;
633 protected override Expression DoResolve (ResolveContext ec)
635 right = right.Resolve (ec);
636 if (right == null)
637 return null;
639 MemberAccess ma = target as MemberAccess;
640 using (ec.Set (ResolveContext.Options.CompoundAssignmentScope)) {
641 target = target.Resolve (ec);
644 if (target == null)
645 return null;
647 if (target is MethodGroupExpr){
648 ec.Report.Error (1656, loc,
649 "Cannot assign to `{0}' because it is a `{1}'",
650 ((MethodGroupExpr)target).Name, target.ExprClassName);
651 return null;
654 if (target is EventExpr)
655 return new EventAddOrRemove (target, op, right, loc).Resolve (ec);
658 // Only now we can decouple the original source/target
659 // into a tree, to guarantee that we do not have side
660 // effects.
662 if (left == null)
663 left = new TargetExpression (target);
665 source = new Binary (op, left, right, true);
667 if (target is DynamicMemberBinder) {
668 Arguments targs = ((DynamicMemberBinder) target).Arguments;
669 source = source.Resolve (ec);
671 Arguments args = new Arguments (2);
672 args.AddRange (targs);
673 args.Add (new Argument (source));
674 source = new DynamicMemberBinder (ma.Name, args, loc).ResolveLValue (ec, right);
676 // Handles possible event addition/subtraction
677 if (op == Binary.Operator.Addition || op == Binary.Operator.Subtraction) {
678 args = new Arguments (2);
679 args.AddRange (targs);
680 args.Add (new Argument (right));
681 string method_prefix = op == Binary.Operator.Addition ?
682 Event.AEventAccessor.AddPrefix : Event.AEventAccessor.RemovePrefix;
684 var invoke = DynamicInvocation.CreateSpecialNameInvoke (
685 new MemberAccess (right, method_prefix + ma.Name, loc), args, loc).Resolve (ec);
687 args = new Arguments (1);
688 args.AddRange (targs);
689 source = new DynamicEventCompoundAssign (ma.Name, args,
690 (ExpressionStatement) source, (ExpressionStatement) invoke, loc).Resolve (ec);
693 return source;
696 return base.DoResolve (ec);
699 protected override Expression ResolveConversions (ResolveContext ec)
701 Type target_type = target.Type;
704 // 1. the return type is implicitly convertible to the type of target
706 if (Convert.ImplicitConversionExists (ec, source, target_type)) {
707 source = Convert.ImplicitConversion (ec, source, target_type, loc);
708 return this;
712 // Otherwise, if the selected operator is a predefined operator
714 Binary b = source as Binary;
715 if (b != null) {
717 // 2a. the operator is a shift operator
719 // 2b. the return type is explicitly convertible to the type of x, and
720 // y is implicitly convertible to the type of x
722 if ((b.Oper & Binary.Operator.ShiftMask) != 0 ||
723 Convert.ImplicitConversionExists (ec, right, target_type)) {
724 source = Convert.ExplicitConversion (ec, source, target_type, loc);
725 return this;
729 if (TypeManager.IsDynamicType (source.Type)) {
730 Arguments arg = new Arguments (1);
731 arg.Add (new Argument (source));
732 return new SimpleAssign (target, new DynamicConversion (target_type, CSharpBinderFlags.ConvertExplicit, arg, loc), loc).Resolve (ec);
735 right.Error_ValueCannotBeConverted (ec, loc, target_type, false);
736 return null;
739 protected override void CloneTo (CloneContext clonectx, Expression t)
741 CompoundAssign ctarget = (CompoundAssign) t;
743 ctarget.right = ctarget.source = source.Clone (clonectx);
744 ctarget.target = target.Clone (clonectx);