2010-02-20 Zoltan Varga <vargaz@gmail.com>
[mcs.git] / mcs / assign.cs
blob6c6594f32e5eff25bbd97e5f3ba5ab9d4c7a137b
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 public 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 public 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 if (TypeManager.IsDynamicType (source_type)) {
369 Arguments args = new Arguments (1);
370 args.Add (new Argument (source));
371 return new DynamicConversion (target_type, false, args, loc).Resolve (ec);
374 Expression resolved = ResolveConversions (ec);
376 if (resolved != this)
377 return resolved;
380 return this;
383 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
385 source.MutateHoistedGenericType (storey);
386 target.MutateHoistedGenericType (storey);
387 type = storey.MutateType (type);
390 protected virtual Expression ResolveConversions (ResolveContext ec)
392 source = Convert.ImplicitConversionRequired (ec, source, target.Type, loc);
393 if (source == null)
394 return null;
396 return this;
399 void Emit (EmitContext ec, bool is_statement)
401 IAssignMethod t = (IAssignMethod) target;
402 t.EmitAssign (ec, source, !is_statement, this is CompoundAssign);
405 public override void Emit (EmitContext ec)
407 Emit (ec, false);
410 public override void EmitStatement (EmitContext ec)
412 Emit (ec, true);
415 protected override void CloneTo (CloneContext clonectx, Expression t)
417 Assign _target = (Assign) t;
419 _target.target = target.Clone (clonectx);
420 _target.source = source.Clone (clonectx);
424 class SimpleAssign : Assign {
425 public SimpleAssign (Expression target, Expression source)
426 : this (target, source, target.Location)
430 public SimpleAssign (Expression target, Expression source, Location loc)
431 : base (target, source, loc)
435 bool CheckEqualAssign (Expression t)
437 if (source is Assign) {
438 Assign a = (Assign) source;
439 if (t.Equals (a.Target))
440 return true;
441 return a is SimpleAssign && ((SimpleAssign) a).CheckEqualAssign (t);
443 return t.Equals (source);
446 public override Expression DoResolve (ResolveContext ec)
448 Expression e = base.DoResolve (ec);
449 if (e == null || e != this)
450 return e;
452 if (CheckEqualAssign (target))
453 ec.Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?");
455 return this;
459 // This class implements fields and events class initializers
460 public class FieldInitializer : Assign
463 // Keep resolved value because field initializers have their own rules
465 ExpressionStatement resolved;
466 IMemberContext rc;
468 public FieldInitializer (FieldBuilder field, Expression expression, IMemberContext rc)
469 : base (new FieldExpr (field, expression.Location), expression, expression.Location)
471 this.rc = rc;
472 if (!field.IsStatic)
473 ((FieldExpr)target).InstanceExpression = CompilerGeneratedThis.Instance;
476 public override Expression DoResolve (ResolveContext ec)
478 // Field initializer can be resolved (fail) many times
479 if (source == null)
480 return null;
482 if (resolved == null) {
484 // Field initializers are tricky for partial classes. They have to
485 // share same costructor (block) but they have they own resolve scope.
488 IMemberContext old = ec.MemberContext;
489 ec.MemberContext = rc;
491 using (ec.Set (ResolveContext.Options.FieldInitializerScope)) {
492 resolved = base.DoResolve (ec) as ExpressionStatement;
495 ec.MemberContext = old;
498 return resolved;
501 public override void EmitStatement (EmitContext ec)
503 if (resolved == null)
504 return;
506 if (resolved != this)
507 resolved.EmitStatement (ec);
508 else
509 base.EmitStatement (ec);
512 public bool IsComplexInitializer {
513 get { return !(source is Constant); }
516 public bool IsDefaultInitializer {
517 get {
518 Constant c = source as Constant;
519 if (c == null)
520 return false;
522 FieldExpr fe = (FieldExpr)target;
523 return c.IsDefaultInitializer (fe.Type);
528 class EventAddOrRemove : ExpressionStatement {
529 EventExpr target;
530 Binary.Operator op;
531 Expression source;
533 public EventAddOrRemove (Expression target, Binary.Operator op, Expression source, Location loc)
535 this.target = target as EventExpr;
536 this.op = op;
537 this.source = source;
538 this.loc = loc;
541 public override Expression CreateExpressionTree (ResolveContext ec)
543 return new SimpleAssign (target, source).CreateExpressionTree (ec);
546 public override Expression DoResolve (ResolveContext ec)
548 if (op != Binary.Operator.Addition && op != Binary.Operator.Subtraction)
549 target.Error_AssignmentEventOnly (ec);
551 source = source.Resolve (ec);
552 if (source == null)
553 return null;
555 source = Convert.ImplicitConversionRequired (ec, source, target.Type, loc);
556 if (source == null)
557 return null;
559 eclass = ExprClass.Value;
560 type = TypeManager.void_type;
561 return this;
564 public override void Emit (EmitContext ec)
566 if (RootContext.EvalMode)
567 EmitStatement (ec);
568 else
569 throw new InternalErrorException ("don't know what to emit");
572 public override void EmitStatement (EmitContext ec)
574 target.EmitAddOrRemove (ec, op == Binary.Operator.Addition, source);
579 // This class is used for compound assignments.
581 public class CompoundAssign : Assign
583 // This is just a hack implemented for arrays only
584 public sealed class TargetExpression : Expression
586 Expression child;
587 public TargetExpression (Expression child)
589 this.child = child;
590 this.loc = child.Location;
593 public override Expression CreateExpressionTree (ResolveContext ec)
595 throw new NotSupportedException ("ET");
598 public override Expression DoResolve (ResolveContext ec)
600 type = child.Type;
601 eclass = ExprClass.Value;
602 return this;
605 public override void Emit (EmitContext ec)
607 child.Emit (ec);
611 // Used for underlying binary operator
612 readonly Binary.Operator op;
613 Expression right;
614 Expression left;
616 public CompoundAssign (Binary.Operator op, Expression target, Expression source)
617 : base (target, source, target.Location)
619 right = source;
620 this.op = op;
623 public CompoundAssign (Binary.Operator op, Expression target, Expression source, Expression left)
624 : this (op, target, source)
626 this.left = left;
629 public override Expression DoResolve (ResolveContext ec)
631 right = right.Resolve (ec);
632 if (right == null)
633 return null;
635 MemberAccess ma = target as MemberAccess;
636 using (ec.Set (ResolveContext.Options.CompoundAssignmentScope)) {
637 target = target.Resolve (ec);
640 if (target == null)
641 return null;
643 if (target is MethodGroupExpr){
644 ec.Report.Error (1656, loc,
645 "Cannot assign to `{0}' because it is a `{1}'",
646 ((MethodGroupExpr)target).Name, target.ExprClassName);
647 return null;
650 if (target is EventExpr)
651 return new EventAddOrRemove (target, op, right, loc).DoResolve (ec);
654 // Only now we can decouple the original source/target
655 // into a tree, to guarantee that we do not have side
656 // effects.
658 if (left == null)
659 left = new TargetExpression (target);
661 source = new Binary (op, left, right, true);
663 // TODO: TargetExpression breaks MemberAccess composition
664 if (target is DynamicMemberBinder) {
665 Arguments targs = ((DynamicMemberBinder) target).Arguments;
666 source = source.Resolve (ec);
668 Arguments args = new Arguments (2);
669 args.AddRange (targs);
670 args.Add (new Argument (source));
671 source = new DynamicMemberBinder (true, ma.Name, args, loc).Resolve (ec);
673 // Handles possible event addition/subtraction
674 if (op == Binary.Operator.Addition || op == Binary.Operator.Subtraction) {
675 args = new Arguments (2);
676 args.AddRange (targs);
677 args.Add (new Argument (right));
678 string method_prefix = op == Binary.Operator.Addition ?
679 Event.AEventAccessor.AddPrefix : Event.AEventAccessor.RemovePrefix;
681 Expression invoke = new DynamicInvocation (
682 new MemberAccess (right, method_prefix + ma.Name, loc), args, loc).Resolve (ec);
684 args = new Arguments (1);
685 args.AddRange (targs);
686 source = new DynamicEventCompoundAssign (ma.Name, args,
687 (ExpressionStatement) source, (ExpressionStatement) invoke, loc).Resolve (ec);
690 return source;
693 return base.DoResolve (ec);
696 #if NET_4_0
697 public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx)
699 var target_object = target.MakeExpression (ctx);
700 var source_object = System.Linq.Expressions.Expression.Convert (source.MakeExpression (ctx), target_object.Type);
701 return System.Linq.Expressions.Expression.Assign (target_object, source_object);
703 #endif
705 protected override Expression ResolveConversions (ResolveContext ec)
707 Type target_type = target.Type;
710 // 1. the return type is implicitly convertible to the type of target
712 if (Convert.ImplicitConversionExists (ec, source, target_type)) {
713 source = Convert.ImplicitConversion (ec, source, target_type, loc);
714 return this;
718 // Otherwise, if the selected operator is a predefined operator
720 Binary b = source as Binary;
721 if (b != null) {
723 // 2a. the operator is a shift operator
725 // 2b. the return type is explicitly convertible to the type of x, and
726 // y is implicitly convertible to the type of x
728 if ((b.Oper & Binary.Operator.ShiftMask) != 0 ||
729 Convert.ImplicitConversionExists (ec, right, target_type)) {
730 source = Convert.ExplicitConversion (ec, source, target_type, loc);
731 return this;
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);