2009-01-18 Zoltan Varga <vargaz@gmail.com>
[mcs.git] / mcs / assign.cs
blob19a5e7e6dac9d5137a87f225cf383d69ef9d4768
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 (EmitContext ec)
211 ArrayList args = new ArrayList (1);
212 args.Add (new Argument (this));
213 return CreateExpressionFactoryCall ("Constant", args);
216 public override Expression DoResolve (EmitContext ec)
218 return this;
221 public override Expression DoResolveLValue (EmitContext 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 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 (EmitContext ec)
323 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 (EmitContext 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, Location);
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 (loc);
358 return null;
361 if ((source.eclass == ExprClass.Type) && (source is TypeExpr)) {
362 source.Error_UnexpectedKind (ec.DeclContainer, "variable or value", loc);
363 return null;
364 } else if ((RootContext.Version == LanguageVersion.ISO_1) &&
365 (source is MethodGroupExpr)){
366 ((MethodGroupExpr) source).ReportUsageError ();
367 return null;
370 if (!TypeManager.IsEqual (target_type, source_type)){
371 Expression resolved = ResolveConversions (ec);
373 if (resolved != this)
374 return resolved;
377 return this;
380 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
382 source.MutateHoistedGenericType (storey);
383 target.MutateHoistedGenericType (storey);
384 type = storey.MutateType (type);
387 protected virtual Expression ResolveConversions (EmitContext ec)
389 source = Convert.ImplicitConversionRequired (ec, source, target.Type, loc);
390 if (source == null)
391 return null;
393 return this;
396 void Emit (EmitContext ec, bool is_statement)
398 IAssignMethod t = (IAssignMethod) target;
399 t.EmitAssign (ec, source, !is_statement, this is CompoundAssign);
402 public override void Emit (EmitContext ec)
404 Emit (ec, false);
407 public override void EmitStatement (EmitContext ec)
409 Emit (ec, true);
412 protected override void CloneTo (CloneContext clonectx, Expression t)
414 Assign _target = (Assign) t;
416 _target.target = target.Clone (clonectx);
417 _target.source = source.Clone (clonectx);
421 class SimpleAssign : Assign {
422 public SimpleAssign (Expression target, Expression source)
423 : this (target, source, target.Location)
427 public SimpleAssign (Expression target, Expression source, Location loc)
428 : base (target, source, loc)
432 bool CheckEqualAssign (Expression t)
434 if (source is Assign) {
435 Assign a = (Assign) source;
436 if (t.Equals (a.Target))
437 return true;
438 return a is SimpleAssign && ((SimpleAssign) a).CheckEqualAssign (t);
440 return t.Equals (source);
443 public override Expression DoResolve (EmitContext ec)
445 Expression e = base.DoResolve (ec);
446 if (e == null || e != this)
447 return e;
449 if (CheckEqualAssign (target))
450 Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?");
452 return this;
456 // This class implements fields and events class initializers
457 public class FieldInitializer : Assign
460 // Keep resolved value because field initializers have their own rules
462 ExpressionStatement resolved;
463 IResolveContext rc;
465 public FieldInitializer (FieldBuilder field, Expression expression, IResolveContext rc)
466 : base (new FieldExpr (field, expression.Location), expression, expression.Location)
468 this.rc = rc;
469 if (!field.IsStatic)
470 ((FieldExpr)target).InstanceExpression = CompilerGeneratedThis.Instance;
473 public override Expression DoResolve (EmitContext ec)
475 // Field initializer can be resolved (fail) many times
476 if (source == null)
477 return null;
479 if (resolved == null) {
481 // Field initializers are tricky for partial classes. They have to
482 // share same costructor (block) but they have they own resolve scope.
485 // TODO: Use ResolveContext only
486 EmitContext f_ec = new EmitContext (rc, rc.DeclContainer, loc, null, TypeManager.void_type, 0, true);
487 f_ec.IsStatic = ec.IsStatic;
488 f_ec.CurrentBlock = ec.CurrentBlock;
490 EmitContext.Flags flags = EmitContext.Flags.InFieldInitializer;
491 if (ec.IsInUnsafeScope)
492 flags |= EmitContext.Flags.InUnsafe;
494 f_ec.Set (flags);
496 resolved = base.DoResolve (f_ec) as ExpressionStatement;
499 return resolved;
502 public override void EmitStatement (EmitContext ec)
504 if (resolved == null)
505 return;
507 if (resolved != this)
508 resolved.EmitStatement (ec);
509 else
510 base.EmitStatement (ec);
513 public bool IsComplexInitializer {
514 get { return !(source is Constant); }
517 public bool IsDefaultInitializer {
518 get {
519 Constant c = source as Constant;
520 if (c == null)
521 return false;
523 FieldExpr fe = (FieldExpr)target;
524 return c.IsDefaultInitializer (fe.Type);
529 class EventAddOrRemove : ExpressionStatement {
530 EventExpr target;
531 Binary.Operator op;
532 Expression source;
534 public EventAddOrRemove (Expression target, Binary.Operator op, Expression source, Location loc)
536 this.target = target as EventExpr;
537 this.op = op;
538 this.source = source;
539 this.loc = loc;
542 public override Expression CreateExpressionTree (EmitContext ec)
544 return new SimpleAssign (target, source).CreateExpressionTree (ec);
547 public override Expression DoResolve (EmitContext ec)
549 source = source.Resolve (ec);
550 if (source == null)
551 return null;
553 source = Convert.ImplicitConversionRequired (ec, source, target.Type, loc);
554 if (source == null)
555 return null;
557 eclass = ExprClass.Value;
558 type = TypeManager.void_type;
559 return this;
562 public override void Emit (EmitContext ec)
564 if (RootContext.EvalMode)
565 EmitStatement (ec);
566 else
567 throw new InternalErrorException ("don't know what to emit");
570 public override void EmitStatement (EmitContext ec)
572 target.EmitAddOrRemove (ec, op == Binary.Operator.Addition, source);
577 // This class is used for compound assignments.
579 class CompoundAssign : Assign {
580 Binary.Operator op;
581 Expression original_source;
583 public CompoundAssign (Binary.Operator op, Expression target, Expression source)
584 : base (target, source, target.Location)
586 original_source = source;
587 this.op = op;
590 public sealed class TargetExpression : Expression {
591 Expression child;
592 public TargetExpression (Expression child)
594 this.child = child;
595 this.loc = child.Location;
598 public override Expression CreateExpressionTree (EmitContext ec)
600 throw new NotSupportedException ("ET");
603 public override Expression DoResolve (EmitContext ec)
605 child = child.Resolve (ec);
606 if (child == null)
607 return null;
608 type = child.Type;
609 eclass = ExprClass.Value;
610 return this;
613 public override void Emit (EmitContext ec)
615 child.Emit (ec);
619 public override Expression DoResolve (EmitContext ec)
621 original_source = original_source.Resolve (ec);
622 if (original_source == null)
623 return null;
625 using (ec.Set (EmitContext.Flags.InCompoundAssignment)) {
626 target = target.Resolve (ec);
629 if (target == null)
630 return null;
632 if (target is MethodGroupExpr){
633 Error_CannotAssign (((MethodGroupExpr)target).Name, target.ExprClassName);
634 return null;
637 if (target is EventExpr)
638 return new EventAddOrRemove (target, op, original_source, loc).DoResolve (ec);
641 // Only now we can decouple the original source/target
642 // into a tree, to guarantee that we do not have side
643 // effects.
645 source = new Binary (op, new TargetExpression (target), original_source, true);
646 return base.DoResolve (ec);
649 protected override Expression ResolveConversions (EmitContext ec)
651 Type target_type = target.Type;
654 // 1. the return type is implicitly convertible to the type of target
656 if (Convert.ImplicitConversionExists (ec, source, target_type)) {
657 source = Convert.ImplicitConversion (ec, source, target_type, loc);
658 return this;
662 // Otherwise, if the selected operator is a predefined operator
664 Binary b = source as Binary;
665 if (b != null) {
667 // 2a. the operator is a shift operator
669 // 2b. the return type is explicitly convertible to the type of x, and
670 // y is implicitly convertible to the type of x
672 if ((b.Oper & Binary.Operator.ShiftMask) != 0 ||
673 Convert.ImplicitConversionExists (ec, original_source, target_type)) {
674 source = Convert.ExplicitConversion (ec, source, target_type, loc);
675 return this;
679 original_source.Error_ValueCannotBeConverted (ec, loc, target_type, false);
680 return null;
683 protected override void CloneTo (CloneContext clonectx, Expression t)
685 CompoundAssign ctarget = (CompoundAssign) t;
687 ctarget.original_source = ctarget.source = source.Clone (clonectx);
688 ctarget.target = target.Clone (clonectx);