2010-04-07 Jb Evain <jbevain@novell.com>
[mcs.git] / mcs / assign.cs
blob2022af1d14ef5f1fc5b63934251b7b76273a23bf
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);
227 #region IAssignMethod Members
229 public void Emit (EmitContext ec, bool leave_copy)
231 Emit (ec);
233 if (leave_copy)
234 Emit (ec);
237 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
239 if (prepare_for_load)
240 throw new NotImplementedException ();
242 source.Emit (ec);
244 Store (ec);
246 if (leave_copy)
247 Emit (ec);
250 #endregion
252 public LocalBuilder Builder {
253 get { return builder; }
256 public void Store (EmitContext ec)
258 ILGenerator ig = ec.ig;
259 if (builder == null)
260 builder = ec.GetTemporaryLocal (type);
262 ig.Emit (OpCodes.Stloc, builder);
265 public void AddressOf (EmitContext ec, AddressOp mode)
267 if (builder == null)
268 builder = ec.GetTemporaryLocal (type);
270 ILGenerator ig = ec.ig;
272 if (builder.LocalType.IsByRef) {
274 // if is_address, than this is just the address anyways,
275 // so we just return this.
277 ig.Emit (OpCodes.Ldloc, builder);
278 } else {
279 ig.Emit (OpCodes.Ldloca, builder);
283 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
285 type = storey.MutateType (type);
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;
296 protected Assign (Expression target, Expression source, Location loc)
298 this.target = target;
299 this.source = source;
300 this.loc = loc;
303 public override Expression CreateExpressionTree (ResolveContext ec)
305 ec.Report.Error (832, loc, "An expression tree cannot contain an assignment operator");
306 return null;
309 public Expression Target {
310 get { return target; }
313 public Expression Source {
314 get { return source; }
317 protected override Expression DoResolve (ResolveContext ec)
319 bool ok = true;
320 source = source.Resolve (ec);
322 if (source == null) {
323 ok = false;
324 source = EmptyExpression.Null;
327 target = target.ResolveLValue (ec, source);
329 if (target == null || !ok)
330 return null;
332 Type target_type = target.Type;
333 Type source_type = source.Type;
335 eclass = ExprClass.Value;
336 type = target_type;
338 if (!(target is IAssignMethod)) {
339 Error_ValueAssignment (ec, loc);
340 return null;
343 if ((RootContext.Version == LanguageVersion.ISO_1) && (source is MethodGroupExpr)){
344 ((MethodGroupExpr) source).ReportUsageError (ec);
345 return null;
348 if (!TypeManager.IsEqual (target_type, source_type)) {
349 Expression resolved = ResolveConversions (ec);
351 if (resolved != this)
352 return resolved;
355 return this;
358 #if NET_4_0
359 public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx)
361 var tassign = target as IDynamicAssign;
362 if (tassign == null)
363 throw new InternalErrorException (target.GetType () + " does not support dynamic assignment");
365 var target_object = tassign.MakeAssignExpression (ctx);
368 // Some hacking is needed as DLR does not support void type and requires
369 // always have object convertible return type to support caching and chaining
371 // We do this by introducing an explicit block which returns RHS value when
372 // available or null
374 if (target_object.NodeType == System.Linq.Expressions.ExpressionType.Block)
375 return target_object;
377 var source_object = System.Linq.Expressions.Expression.Convert (source.MakeExpression (ctx), target_object.Type);
378 return System.Linq.Expressions.Expression.Assign (target_object, source_object);
380 #endif
382 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
384 source.MutateHoistedGenericType (storey);
385 target.MutateHoistedGenericType (storey);
386 type = storey.MutateType (type);
389 protected virtual Expression ResolveConversions (ResolveContext ec)
391 source = Convert.ImplicitConversionRequired (ec, source, target.Type, loc);
392 if (source == null)
393 return null;
395 return this;
398 void Emit (EmitContext ec, bool is_statement)
400 IAssignMethod t = (IAssignMethod) target;
401 t.EmitAssign (ec, source, !is_statement, this is CompoundAssign);
404 public override void Emit (EmitContext ec)
406 Emit (ec, false);
409 public override void EmitStatement (EmitContext ec)
411 Emit (ec, true);
414 protected override void CloneTo (CloneContext clonectx, Expression t)
416 Assign _target = (Assign) t;
418 _target.target = target.Clone (clonectx);
419 _target.source = source.Clone (clonectx);
423 public class SimpleAssign : Assign {
424 public SimpleAssign (Expression target, Expression source)
425 : this (target, source, target.Location)
429 public SimpleAssign (Expression target, Expression source, Location loc)
430 : base (target, source, loc)
434 bool CheckEqualAssign (Expression t)
436 if (source is Assign) {
437 Assign a = (Assign) source;
438 if (t.Equals (a.Target))
439 return true;
440 return a is SimpleAssign && ((SimpleAssign) a).CheckEqualAssign (t);
442 return t.Equals (source);
445 protected override Expression DoResolve (ResolveContext ec)
447 Expression e = base.DoResolve (ec);
448 if (e == null || e != this)
449 return e;
451 if (CheckEqualAssign (target))
452 ec.Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?");
454 return this;
458 // This class implements fields and events class initializers
459 public class FieldInitializer : Assign
462 // Keep resolved value because field initializers have their own rules
464 ExpressionStatement resolved;
465 IMemberContext rc;
467 public FieldInitializer (FieldBase field, Expression expression, IMemberContext rc)
468 : base (new FieldExpr (field, expression.Location), expression, expression.Location)
470 this.rc = rc;
471 if (!field.IsStatic)
472 ((FieldExpr)target).InstanceExpression = CompilerGeneratedThis.Instance;
475 protected override Expression DoResolve (ResolveContext ec)
477 // Field initializer can be resolved (fail) many times
478 if (source == null)
479 return null;
481 if (resolved == null) {
483 // Field initializers are tricky for partial classes. They have to
484 // share same constructor (block) but they have they own resolve scope.
487 IMemberContext old = ec.MemberContext;
488 ec.MemberContext = rc;
490 using (ec.Set (ResolveContext.Options.FieldInitializerScope)) {
491 resolved = base.DoResolve (ec) as ExpressionStatement;
494 ec.MemberContext = old;
497 return resolved;
500 public override void EmitStatement (EmitContext ec)
502 if (resolved == null)
503 return;
505 if (resolved != this)
506 resolved.EmitStatement (ec);
507 else
508 base.EmitStatement (ec);
511 public bool IsComplexInitializer {
512 get { return !(source is Constant); }
515 public bool IsDefaultInitializer {
516 get {
517 Constant c = source as Constant;
518 if (c == null)
519 return false;
521 FieldExpr fe = (FieldExpr)target;
522 return c.IsDefaultInitializer (fe.Type);
527 class EventAddOrRemove : ExpressionStatement {
528 EventExpr target;
529 Binary.Operator op;
530 Expression source;
532 public EventAddOrRemove (Expression target, Binary.Operator op, Expression source, Location loc)
534 this.target = target as EventExpr;
535 this.op = op;
536 this.source = source;
537 this.loc = loc;
540 public override Expression CreateExpressionTree (ResolveContext ec)
542 return new SimpleAssign (target, source).CreateExpressionTree (ec);
545 protected override Expression DoResolve (ResolveContext ec)
547 if (op != Binary.Operator.Addition && op != Binary.Operator.Subtraction)
548 target.Error_AssignmentEventOnly (ec);
550 source = source.Resolve (ec);
551 if (source == null)
552 return null;
554 source = Convert.ImplicitConversionRequired (ec, source, target.Type, loc);
555 if (source == null)
556 return null;
558 eclass = ExprClass.Value;
559 type = TypeManager.void_type;
560 return this;
563 public override void Emit (EmitContext ec)
565 if (RootContext.EvalMode)
566 EmitStatement (ec);
567 else
568 throw new InternalErrorException ("don't know what to emit");
571 public override void EmitStatement (EmitContext ec)
573 target.EmitAddOrRemove (ec, op == Binary.Operator.Addition, source);
578 // This class is used for compound assignments.
580 public class CompoundAssign : Assign
582 // This is just a hack implemented for arrays only
583 public sealed class TargetExpression : Expression
585 Expression child;
586 public TargetExpression (Expression child)
588 this.child = child;
589 this.loc = child.Location;
592 public override Expression CreateExpressionTree (ResolveContext ec)
594 throw new NotSupportedException ("ET");
597 protected override Expression DoResolve (ResolveContext ec)
599 type = child.Type;
600 eclass = ExprClass.Value;
601 return this;
604 public override void Emit (EmitContext ec)
606 child.Emit (ec);
610 // Used for underlying binary operator
611 readonly Binary.Operator op;
612 Expression right;
613 Expression left;
615 public CompoundAssign (Binary.Operator op, Expression target, Expression source)
616 : base (target, source, target.Location)
618 right = source;
619 this.op = op;
622 public CompoundAssign (Binary.Operator op, Expression target, Expression source, Expression left)
623 : this (op, target, source)
625 this.left = left;
628 protected override Expression DoResolve (ResolveContext ec)
630 right = right.Resolve (ec);
631 if (right == null)
632 return null;
634 MemberAccess ma = target as MemberAccess;
635 using (ec.Set (ResolveContext.Options.CompoundAssignmentScope)) {
636 target = target.Resolve (ec);
639 if (target == null)
640 return null;
642 if (target is MethodGroupExpr){
643 ec.Report.Error (1656, loc,
644 "Cannot assign to `{0}' because it is a `{1}'",
645 ((MethodGroupExpr)target).Name, target.ExprClassName);
646 return null;
649 if (target is EventExpr)
650 return new EventAddOrRemove (target, op, right, loc).Resolve (ec);
653 // Only now we can decouple the original source/target
654 // into a tree, to guarantee that we do not have side
655 // effects.
657 if (left == null)
658 left = new TargetExpression (target);
660 source = new Binary (op, left, right, true, loc);
662 if (target is DynamicMemberBinder) {
663 Arguments targs = ((DynamicMemberBinder) target).Arguments;
664 source = source.Resolve (ec);
666 Arguments args = new Arguments (2);
667 args.AddRange (targs);
668 args.Add (new Argument (source));
669 source = new DynamicMemberBinder (ma.Name, args, loc).ResolveLValue (ec, right);
671 // Handles possible event addition/subtraction
672 if (op == Binary.Operator.Addition || op == Binary.Operator.Subtraction) {
673 args = new Arguments (2);
674 args.AddRange (targs);
675 args.Add (new Argument (right));
676 string method_prefix = op == Binary.Operator.Addition ?
677 Event.AEventAccessor.AddPrefix : Event.AEventAccessor.RemovePrefix;
679 var invoke = DynamicInvocation.CreateSpecialNameInvoke (
680 new MemberAccess (right, method_prefix + ma.Name, loc), args, loc).Resolve (ec);
682 args = new Arguments (1);
683 args.AddRange (targs);
684 source = new DynamicEventCompoundAssign (ma.Name, args,
685 (ExpressionStatement) source, (ExpressionStatement) invoke, loc).Resolve (ec);
688 return source;
691 return base.DoResolve (ec);
694 protected override Expression ResolveConversions (ResolveContext ec)
696 Type target_type = target.Type;
699 // 1. the return type is implicitly convertible to the type of target
701 if (Convert.ImplicitConversionExists (ec, source, target_type)) {
702 source = Convert.ImplicitConversion (ec, source, target_type, loc);
703 return this;
707 // Otherwise, if the selected operator is a predefined operator
709 Binary b = source as Binary;
710 if (b != null) {
712 // 2a. the operator is a shift operator
714 // 2b. the return type is explicitly convertible to the type of x, and
715 // y is implicitly convertible to the type of x
717 if ((b.Oper & Binary.Operator.ShiftMask) != 0 ||
718 Convert.ImplicitConversionExists (ec, right, target_type)) {
719 source = Convert.ExplicitConversion (ec, source, target_type, loc);
720 return this;
724 if (TypeManager.IsDynamicType (source.Type)) {
725 Arguments arg = new Arguments (1);
726 arg.Add (new Argument (source));
727 return new SimpleAssign (target, new DynamicConversion (target_type, CSharpBinderFlags.ConvertExplicit, arg, loc), loc).Resolve (ec);
730 right.Error_ValueCannotBeConverted (ec, loc, target_type, false);
731 return null;
734 protected override void CloneTo (CloneContext clonectx, Expression t)
736 CompoundAssign ctarget = (CompoundAssign) t;
738 ctarget.right = ctarget.source = source.Clone (clonectx);
739 ctarget.target = target.Clone (clonectx);