2009-11-24 Jb Evain <jbevain@novell.com>
[mcs.git] / mcs / assign.cs
bloba9faebde73a0cbe1b5fb4533523a3e85db4259d1
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;
17 using System.Collections;
19 namespace Mono.CSharp {
21 /// <summary>
22 /// This interface is implemented by expressions that can be assigned to.
23 /// </summary>
24 /// <remarks>
25 /// This interface is implemented by Expressions whose values can not
26 /// store the result on the top of the stack.
27 ///
28 /// Expressions implementing this (Properties, Indexers and Arrays) would
29 /// perform an assignment of the Expression "source" into its final
30 /// location.
31 ///
32 /// No values on the top of the stack are expected to be left by
33 /// invoking this method.
34 /// </remarks>
35 public interface IAssignMethod {
37 // This is an extra version of Emit. If leave_copy is `true'
38 // A copy of the expression will be left on the stack at the
39 // end of the code generated for EmitAssign
41 void Emit (EmitContext ec, bool leave_copy);
44 // This method does the assignment
45 // `source' will be stored into the location specified by `this'
46 // if `leave_copy' is true, a copy of `source' will be left on the stack
47 // if `prepare_for_load' is true, when `source' is emitted, there will
48 // be data on the stack that it can use to compuatate its value. This is
49 // for expressions like a [f ()] ++, where you can't call `f ()' twice.
51 void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load);
54 For simple assignments, this interface is very simple, EmitAssign is called with source
55 as the source expression and leave_copy and prepare_for_load false.
57 For compound assignments it gets complicated.
59 EmitAssign will be called as before, however, prepare_for_load will be
60 true. The @source expression will contain an expression
61 which calls Emit. So, the calls look like:
63 this.EmitAssign (ec, source, false, true) ->
64 source.Emit (ec); ->
65 [...] ->
66 this.Emit (ec, false); ->
67 end this.Emit (ec, false); ->
68 end [...]
69 end source.Emit (ec);
70 end this.EmitAssign (ec, source, false, true)
73 When prepare_for_load is true, EmitAssign emits a `token' on the stack that
74 Emit will use for its state.
76 Let's take FieldExpr as an example. assume we are emitting f ().y += 1;
78 Here is the call tree again. This time, each call is annotated with the IL
79 it produces:
81 this.EmitAssign (ec, source, false, true)
82 call f
83 dup
85 Binary.Emit ()
86 this.Emit (ec, false);
87 ldfld y
88 end this.Emit (ec, false);
90 IntConstant.Emit ()
91 ldc.i4.1
92 end IntConstant.Emit
94 add
95 end Binary.Emit ()
97 stfld
98 end this.EmitAssign (ec, source, false, true)
100 Observe two things:
101 1) EmitAssign left a token on the stack. It was the result of f ().
102 2) This token was used by Emit
104 leave_copy (in both EmitAssign and Emit) tells the compiler to leave a copy
105 of the expression at that point in evaluation. This is used for pre/post inc/dec
106 and for a = x += y. Let's do the above example with leave_copy true in EmitAssign
108 this.EmitAssign (ec, source, true, true)
109 call f
112 Binary.Emit ()
113 this.Emit (ec, false);
114 ldfld y
115 end this.Emit (ec, false);
117 IntConstant.Emit ()
118 ldc.i4.1
119 end IntConstant.Emit
122 end Binary.Emit ()
125 stloc temp
126 stfld
127 ldloc temp
128 end this.EmitAssign (ec, source, true, true)
130 And with it true in Emit
132 this.EmitAssign (ec, source, false, true)
133 call f
136 Binary.Emit ()
137 this.Emit (ec, true);
138 ldfld y
140 stloc temp
141 end this.Emit (ec, true);
143 IntConstant.Emit ()
144 ldc.i4.1
145 end IntConstant.Emit
148 end Binary.Emit ()
150 stfld
151 ldloc temp
152 end this.EmitAssign (ec, source, false, true)
154 Note that these two examples are what happens for ++x and x++, respectively.
158 /// <summary>
159 /// An Expression to hold a temporary value.
160 /// </summary>
161 /// <remarks>
162 /// The LocalTemporary class is used to hold temporary values of a given
163 /// type to "simulate" the expression semantics on property and indexer
164 /// access whose return values are void.
166 /// The local temporary is used to alter the normal flow of code generation
167 /// basically it creates a local variable, and its emit instruction generates
168 /// code to access this value, return its address or save its value.
170 /// If `is_address' is true, then the value that we store is the address to the
171 /// real value, and not the value itself.
173 /// This is needed for a value type, because otherwise you just end up making a
174 /// copy of the value on the stack and modifying it. You really need a pointer
175 /// to the origional value so that you can modify it in that location. This
176 /// Does not happen with a class because a class is a pointer -- so you always
177 /// get the indirection.
179 /// The `is_address' stuff is really just a hack. We need to come up with a better
180 /// way to handle it.
181 /// </remarks>
182 public class LocalTemporary : Expression, IMemoryLocation, IAssignMethod {
183 LocalBuilder builder;
184 bool is_address;
186 public LocalTemporary (Type t) : this (t, false) {}
188 public LocalTemporary (Type t, bool is_address)
190 type = t;
191 eclass = ExprClass.Value;
192 this.is_address = is_address;
195 public LocalTemporary (LocalBuilder b, Type t)
197 type = t;
198 eclass = ExprClass.Value;
199 loc = Location.Null;
200 builder = b;
203 public void Release (EmitContext ec)
205 ec.FreeTemporaryLocal (builder, type);
206 builder = null;
209 public override Expression CreateExpressionTree (ResolveContext ec)
211 Arguments args = new Arguments (1);
212 args.Add (new Argument (this));
213 return CreateExpressionFactoryCall (ec, "Constant", args);
216 protected override Expression DoResolve (ResolveContext ec)
218 return this;
221 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
223 return this;
226 public override void Emit (EmitContext ec)
228 ILGenerator ig = ec.ig;
230 if (builder == null)
231 throw new InternalErrorException ("Emit without Store, or after Release");
233 ig.Emit (OpCodes.Ldloc, builder);
234 // we need to copy from the pointer
235 if (is_address)
236 LoadFromPtr (ig, type);
239 #region IAssignMethod Members
241 public void Emit (EmitContext ec, bool leave_copy)
243 Emit (ec);
245 if (leave_copy)
246 Emit (ec);
249 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
251 if (prepare_for_load)
252 throw new NotImplementedException ();
254 source.Emit (ec);
256 Store (ec);
258 if (leave_copy)
259 Emit (ec);
262 #endregion
264 public LocalBuilder Builder {
265 get { return builder; }
268 // NB: if you have `is_address' on the stack there must
269 // be a managed pointer. Otherwise, it is the type from
270 // the ctor.
271 public void Store (EmitContext ec)
273 ILGenerator ig = ec.ig;
274 if (builder == null)
275 builder = ec.GetTemporaryLocal (is_address ? TypeManager.GetReferenceType (type): type);
277 ig.Emit (OpCodes.Stloc, builder);
280 public void AddressOf (EmitContext ec, AddressOp mode)
282 if (builder == null)
283 builder = ec.GetTemporaryLocal (is_address ? TypeManager.GetReferenceType (type): type);
285 // if is_address, than this is just the address anyways,
286 // so we just return this.
287 ILGenerator ig = ec.ig;
289 if (is_address)
290 ig.Emit (OpCodes.Ldloc, builder);
291 else
292 ig.Emit (OpCodes.Ldloca, builder);
295 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
297 type = storey.MutateType (type);
300 public bool PointsToAddress {
301 get {
302 return is_address;
307 /// <summary>
308 /// The Assign node takes care of assigning the value of source into
309 /// the expression represented by target.
310 /// </summary>
311 public abstract class Assign : ExpressionStatement {
312 protected Expression target, source;
314 protected Assign (Expression target, Expression source, Location loc)
316 this.target = target;
317 this.source = source;
318 this.loc = loc;
321 public override Expression CreateExpressionTree (ResolveContext ec)
323 ec.Report.Error (832, loc, "An expression tree cannot contain an assignment operator");
324 return null;
327 public Expression Target {
328 get { return target; }
331 public Expression Source {
332 get { return source; }
335 protected override Expression DoResolve (ResolveContext ec)
337 bool ok = true;
338 source = source.Resolve (ec);
340 if (source == null) {
341 ok = false;
342 source = EmptyExpression.Null;
345 target = target.ResolveLValue (ec, source);
347 if (target == null || !ok)
348 return null;
350 Type target_type = target.Type;
351 Type source_type = source.Type;
353 eclass = ExprClass.Value;
354 type = target_type;
356 if (!(target is IAssignMethod)) {
357 Error_ValueAssignment (ec, loc);
358 return null;
361 if ((RootContext.Version == LanguageVersion.ISO_1) &&
362 (source is MethodGroupExpr)){
363 ((MethodGroupExpr) source).ReportUsageError (ec);
364 return null;
367 if (!TypeManager.IsEqual (target_type, source_type)) {
368 Expression resolved = ResolveConversions (ec);
370 if (resolved != this)
371 return resolved;
374 return this;
377 #if NET_4_0
378 public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx)
380 var tassign = target as IDynamicAssign;
381 if (tassign == null)
382 throw new InternalErrorException (target.GetType () + " does not support dynamic assignment");
384 var target_object = tassign.MakeAssignExpression (ctx);
387 // Some hacking is needed as DLR does not support void type and requires
388 // always have object convertible return type to support caching and chaining
390 // We do this by introducing an explicit block which returns RHS value when
391 // available or null
393 if (target_object.NodeType == System.Linq.Expressions.ExpressionType.Block)
394 return target_object;
396 var source_object = System.Linq.Expressions.Expression.Convert (source.MakeExpression (ctx), target_object.Type);
397 return System.Linq.Expressions.Expression.Assign (target_object, source_object);
399 #endif
401 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
403 source.MutateHoistedGenericType (storey);
404 target.MutateHoistedGenericType (storey);
405 type = storey.MutateType (type);
408 protected virtual Expression ResolveConversions (ResolveContext ec)
410 source = Convert.ImplicitConversionRequired (ec, source, target.Type, loc);
411 if (source == null)
412 return null;
414 return this;
417 void Emit (EmitContext ec, bool is_statement)
419 IAssignMethod t = (IAssignMethod) target;
420 t.EmitAssign (ec, source, !is_statement, this is CompoundAssign);
423 public override void Emit (EmitContext ec)
425 Emit (ec, false);
428 public override void EmitStatement (EmitContext ec)
430 Emit (ec, true);
433 protected override void CloneTo (CloneContext clonectx, Expression t)
435 Assign _target = (Assign) t;
437 _target.target = target.Clone (clonectx);
438 _target.source = source.Clone (clonectx);
442 public class SimpleAssign : Assign {
443 public SimpleAssign (Expression target, Expression source)
444 : this (target, source, target.Location)
448 public SimpleAssign (Expression target, Expression source, Location loc)
449 : base (target, source, loc)
453 bool CheckEqualAssign (Expression t)
455 if (source is Assign) {
456 Assign a = (Assign) source;
457 if (t.Equals (a.Target))
458 return true;
459 return a is SimpleAssign && ((SimpleAssign) a).CheckEqualAssign (t);
461 return t.Equals (source);
464 protected override Expression DoResolve (ResolveContext ec)
466 Expression e = base.DoResolve (ec);
467 if (e == null || e != this)
468 return e;
470 if (CheckEqualAssign (target))
471 ec.Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?");
473 return this;
477 // This class implements fields and events class initializers
478 public class FieldInitializer : Assign
481 // Keep resolved value because field initializers have their own rules
483 ExpressionStatement resolved;
484 IMemberContext rc;
486 public FieldInitializer (FieldBuilder field, Expression expression, IMemberContext rc)
487 : base (new FieldExpr (field, expression.Location), expression, expression.Location)
489 this.rc = rc;
490 if (!field.IsStatic)
491 ((FieldExpr)target).InstanceExpression = CompilerGeneratedThis.Instance;
494 protected override Expression DoResolve (ResolveContext ec)
496 // Field initializer can be resolved (fail) many times
497 if (source == null)
498 return null;
500 if (resolved == null) {
502 // Field initializers are tricky for partial classes. They have to
503 // share same constructor (block) but they have they own resolve scope.
506 IMemberContext old = ec.MemberContext;
507 ec.MemberContext = rc;
509 using (ec.Set (ResolveContext.Options.FieldInitializerScope)) {
510 resolved = base.DoResolve (ec) as ExpressionStatement;
513 ec.MemberContext = old;
516 return resolved;
519 public override void EmitStatement (EmitContext ec)
521 if (resolved == null)
522 return;
524 if (resolved != this)
525 resolved.EmitStatement (ec);
526 else
527 base.EmitStatement (ec);
530 public bool IsComplexInitializer {
531 get { return !(source is Constant); }
534 public bool IsDefaultInitializer {
535 get {
536 Constant c = source as Constant;
537 if (c == null)
538 return false;
540 FieldExpr fe = (FieldExpr)target;
541 return c.IsDefaultInitializer (fe.Type);
546 class EventAddOrRemove : ExpressionStatement {
547 EventExpr target;
548 Binary.Operator op;
549 Expression source;
551 public EventAddOrRemove (Expression target, Binary.Operator op, Expression source, Location loc)
553 this.target = target as EventExpr;
554 this.op = op;
555 this.source = source;
556 this.loc = loc;
559 public override Expression CreateExpressionTree (ResolveContext ec)
561 return new SimpleAssign (target, source).CreateExpressionTree (ec);
564 protected override Expression DoResolve (ResolveContext ec)
566 if (op != Binary.Operator.Addition && op != Binary.Operator.Subtraction)
567 target.Error_AssignmentEventOnly (ec);
569 source = source.Resolve (ec);
570 if (source == null)
571 return null;
573 source = Convert.ImplicitConversionRequired (ec, source, target.Type, loc);
574 if (source == null)
575 return null;
577 eclass = ExprClass.Value;
578 type = TypeManager.void_type;
579 return this;
582 public override void Emit (EmitContext ec)
584 if (RootContext.EvalMode)
585 EmitStatement (ec);
586 else
587 throw new InternalErrorException ("don't know what to emit");
590 public override void EmitStatement (EmitContext ec)
592 target.EmitAddOrRemove (ec, op == Binary.Operator.Addition, source);
597 // This class is used for compound assignments.
599 public class CompoundAssign : Assign
601 // This is just a hack implemented for arrays only
602 public sealed class TargetExpression : Expression
604 Expression child;
605 public TargetExpression (Expression child)
607 this.child = child;
608 this.loc = child.Location;
611 public override Expression CreateExpressionTree (ResolveContext ec)
613 throw new NotSupportedException ("ET");
616 protected override Expression DoResolve (ResolveContext ec)
618 type = child.Type;
619 eclass = ExprClass.Value;
620 return this;
623 public override void Emit (EmitContext ec)
625 child.Emit (ec);
629 // Used for underlying binary operator
630 readonly Binary.Operator op;
631 Expression right;
632 Expression left;
634 public CompoundAssign (Binary.Operator op, Expression target, Expression source)
635 : base (target, source, target.Location)
637 right = source;
638 this.op = op;
641 public CompoundAssign (Binary.Operator op, Expression target, Expression source, Expression left)
642 : this (op, target, source)
644 this.left = left;
647 protected override Expression DoResolve (ResolveContext ec)
649 right = right.Resolve (ec);
650 if (right == null)
651 return null;
653 MemberAccess ma = target as MemberAccess;
654 using (ec.Set (ResolveContext.Options.CompoundAssignmentScope)) {
655 target = target.Resolve (ec);
658 if (target == null)
659 return null;
661 if (target is MethodGroupExpr){
662 ec.Report.Error (1656, loc,
663 "Cannot assign to `{0}' because it is a `{1}'",
664 ((MethodGroupExpr)target).Name, target.ExprClassName);
665 return null;
668 if (target is EventExpr)
669 return new EventAddOrRemove (target, op, right, loc).Resolve (ec);
672 // Only now we can decouple the original source/target
673 // into a tree, to guarantee that we do not have side
674 // effects.
676 if (left == null)
677 left = new TargetExpression (target);
679 source = new Binary (op, left, right, true);
681 if (target is DynamicMemberBinder) {
682 Arguments targs = ((DynamicMemberBinder) target).Arguments;
683 source = source.Resolve (ec);
685 Arguments args = new Arguments (2);
686 args.AddRange (targs);
687 args.Add (new Argument (source));
688 source = new DynamicMemberBinder (ma.Name, args, loc).ResolveLValue (ec, right);
690 // Handles possible event addition/subtraction
691 if (op == Binary.Operator.Addition || op == Binary.Operator.Subtraction) {
692 args = new Arguments (2);
693 args.AddRange (targs);
694 args.Add (new Argument (right));
695 string method_prefix = op == Binary.Operator.Addition ?
696 Event.AEventAccessor.AddPrefix : Event.AEventAccessor.RemovePrefix;
698 var invoke = DynamicInvocation.CreateSpecialNameInvoke (
699 new MemberAccess (right, method_prefix + ma.Name, loc), args, loc).Resolve (ec);
701 args = new Arguments (1);
702 args.AddRange (targs);
703 source = new DynamicEventCompoundAssign (ma.Name, args,
704 (ExpressionStatement) source, (ExpressionStatement) invoke, loc).Resolve (ec);
707 return source;
710 return base.DoResolve (ec);
713 protected override Expression ResolveConversions (ResolveContext ec)
715 Type target_type = target.Type;
718 // 1. the return type is implicitly convertible to the type of target
720 if (Convert.ImplicitConversionExists (ec, source, target_type)) {
721 source = Convert.ImplicitConversion (ec, source, target_type, loc);
722 return this;
726 // Otherwise, if the selected operator is a predefined operator
728 Binary b = source as Binary;
729 if (b != null) {
731 // 2a. the operator is a shift operator
733 // 2b. the return type is explicitly convertible to the type of x, and
734 // y is implicitly convertible to the type of x
736 if ((b.Oper & Binary.Operator.ShiftMask) != 0 ||
737 Convert.ImplicitConversionExists (ec, right, target_type)) {
738 source = Convert.ExplicitConversion (ec, source, target_type, loc);
739 return this;
743 if (TypeManager.IsDynamicType (source.Type)) {
744 Arguments arg = new Arguments (1);
745 arg.Add (new Argument (source));
746 return new SimpleAssign (target, new DynamicConversion (target_type, CSharpBinderFlags.ConvertExplicit, arg, loc), loc).Resolve (ec);
749 right.Error_ValueCannotBeConverted (ec, loc, target_type, false);
750 return null;
753 protected override void CloneTo (CloneContext clonectx, Expression t)
755 CompoundAssign ctarget = (CompoundAssign) t;
757 ctarget.right = ctarget.source = source.Clone (clonectx);
758 ctarget.target = target.Clone (clonectx);