2009-09-30 Marek Safar <marek.safar@gmail.com>
[mono.git] / mcs / mcs / assign.cs
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
10 //
11 // Copyright 2001, 2002, 2003 Ximian, Inc.
12 // Copyright 2004-2008 Novell, Inc
13 //
14 using System;
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Collections;
18
19 namespace Mono.CSharp {
20
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 {
36                 //
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
40                 //
41                 void Emit (EmitContext ec, bool leave_copy);
42
43                 //
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.
50                 //
51                 void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load);
52
53                 /*
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.
56
57                 For compound assignments it gets complicated.
58
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:
62
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)
71
72
73                 When prepare_for_load is true, EmitAssign emits a `token' on the stack that
74                 Emit will use for its state.
75
76                 Let's take FieldExpr as an example. assume we are emitting f ().y += 1;
77
78                 Here is the call tree again. This time, each call is annotated with the IL
79                 it produces:
80
81                 this.EmitAssign (ec, source, false, true)
82                         call f
83                         dup
84
85                         Binary.Emit ()
86                                 this.Emit (ec, false);
87                                 ldfld y
88                                 end this.Emit (ec, false);
89
90                                 IntConstant.Emit ()
91                                 ldc.i4.1
92                                 end IntConstant.Emit
93
94                                 add
95                         end Binary.Emit ()
96
97                         stfld
98                 end this.EmitAssign (ec, source, false, true)
99
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
103
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
107
108                 this.EmitAssign (ec, source, true, true)
109                         call f
110                         dup
111
112                         Binary.Emit ()
113                                 this.Emit (ec, false);
114                                 ldfld y
115                                 end this.Emit (ec, false);
116
117                                 IntConstant.Emit ()
118                                 ldc.i4.1
119                                 end IntConstant.Emit
120
121                                 add
122                         end Binary.Emit ()
123
124                         dup
125                         stloc temp
126                         stfld
127                         ldloc temp
128                 end this.EmitAssign (ec, source, true, true)
129
130                 And with it true in Emit
131
132                 this.EmitAssign (ec, source, false, true)
133                         call f
134                         dup
135
136                         Binary.Emit ()
137                                 this.Emit (ec, true);
138                                 ldfld y
139                                 dup
140                                 stloc temp
141                                 end this.Emit (ec, true);
142
143                                 IntConstant.Emit ()
144                                 ldc.i4.1
145                                 end IntConstant.Emit
146
147                                 add
148                         end Binary.Emit ()
149
150                         stfld
151                         ldloc temp
152                 end this.EmitAssign (ec, source, false, true)
153
154                 Note that these two examples are what happens for ++x and x++, respectively.
155                 */
156         }
157
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.
165         ///
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.
169         ///
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.
172         ///
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.
178         ///
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;
185
186                 public LocalTemporary (Type t) : this (t, false) {}
187
188                 public LocalTemporary (Type t, bool is_address)
189                 {
190                         type = t;
191                         eclass = ExprClass.Value;
192                         this.is_address = is_address;
193                 }
194
195                 public LocalTemporary (LocalBuilder b, Type t)
196                 {
197                         type = t;
198                         eclass = ExprClass.Value;
199                         loc = Location.Null;
200                         builder = b;
201                 }
202
203                 public void Release (EmitContext ec)
204                 {
205                         ec.FreeTemporaryLocal (builder, type);
206                         builder = null;
207                 }
208
209                 public override Expression CreateExpressionTree (ResolveContext ec)
210                 {
211                         Arguments args = new Arguments (1);
212                         args.Add (new Argument (this));
213                         return CreateExpressionFactoryCall (ec, "Constant", args);
214                 }
215
216                 public override Expression DoResolve (ResolveContext ec)
217                 {
218                         return this;
219                 }
220
221                 public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
222                 {
223                         return this;
224                 }
225
226                 public override void Emit (EmitContext ec)
227                 {
228                         ILGenerator ig = ec.ig;
229
230                         if (builder == null)
231                                 throw new InternalErrorException ("Emit without Store, or after Release");
232
233                         ig.Emit (OpCodes.Ldloc, builder);
234                         // we need to copy from the pointer
235                         if (is_address)
236                                 LoadFromPtr (ig, type);
237                 }
238
239                 #region IAssignMethod Members
240
241                 public void Emit (EmitContext ec, bool leave_copy)
242                 {
243                         Emit (ec);
244
245                         if (leave_copy)
246                                 Emit (ec);
247                 }
248
249                 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
250                 {
251                         if (prepare_for_load)
252                                 throw new NotImplementedException ();
253
254                         source.Emit (ec);
255
256                         Store (ec);
257
258                         if (leave_copy)
259                                 Emit (ec);
260                 }
261
262                 #endregion
263
264                 public LocalBuilder Builder {
265                         get { return builder; }
266                 }
267
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)
272                 {
273                         ILGenerator ig = ec.ig;
274                         if (builder == null)
275                                 builder = ec.GetTemporaryLocal (is_address ? TypeManager.GetReferenceType (type): type);
276
277                         ig.Emit (OpCodes.Stloc, builder);
278                 }
279
280                 public void AddressOf (EmitContext ec, AddressOp mode)
281                 {
282                         if (builder == null)
283                                 builder = ec.GetTemporaryLocal (is_address ? TypeManager.GetReferenceType (type): type);
284
285                         // if is_address, than this is just the address anyways,
286                         // so we just return this.
287                         ILGenerator ig = ec.ig;
288
289                         if (is_address)
290                                 ig.Emit (OpCodes.Ldloc, builder);
291                         else
292                                 ig.Emit (OpCodes.Ldloca, builder);
293                 }
294
295                 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
296                 {
297                         type = storey.MutateType (type);
298                 }
299
300                 public bool PointsToAddress {
301                         get {
302                                 return is_address;
303                         }
304                 }
305         }
306
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;
313
314                 protected Assign (Expression target, Expression source, Location loc)
315                 {
316                         this.target = target;
317                         this.source = source;
318                         this.loc = loc;
319                 }
320                 
321                 public override Expression CreateExpressionTree (ResolveContext ec)
322                 {
323                         ec.Report.Error (832, loc, "An expression tree cannot contain an assignment operator");
324                         return null;
325                 }
326
327                 public Expression Target {
328                         get { return target; }
329                 }
330
331                 public Expression Source {
332                         get { return source; }
333                 }
334
335                 public override Expression DoResolve (ResolveContext ec)
336                 {
337                         bool ok = true;
338                         source = source.Resolve (ec);
339                                                 
340                         if (source == null) {
341                                 ok = false;
342                                 source = EmptyExpression.Null;
343                         }
344
345                         target = target.ResolveLValue (ec, source);
346
347                         if (target == null || !ok)
348                                 return null;
349
350                         Type target_type = target.Type;
351                         Type source_type = source.Type;
352
353                         eclass = ExprClass.Value;
354                         type = target_type;
355
356                         if (!(target is IAssignMethod)) {
357                                 Error_ValueAssignment (ec, loc);
358                                 return null;
359                         }
360
361                         if ((RootContext.Version == LanguageVersion.ISO_1) &&
362                                    (source is MethodGroupExpr)){
363                                 ((MethodGroupExpr) source).ReportUsageError (ec);
364                                 return null;
365                         }
366
367                         if (!TypeManager.IsEqual (target_type, source_type)) {
368                                 Expression resolved = ResolveConversions (ec);
369
370                                 if (resolved != this)
371                                         return resolved;
372                         }
373
374                         return this;
375                 }
376
377                 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
378                 {
379                         source.MutateHoistedGenericType (storey);
380                         target.MutateHoistedGenericType (storey);
381                         type = storey.MutateType (type);                        
382                 }
383
384                 protected virtual Expression ResolveConversions (ResolveContext ec)
385                 {
386                         source = Convert.ImplicitConversionRequired (ec, source, target.Type, loc);
387                         if (source == null)
388                                 return null;
389
390                         return this;
391                 }
392
393                 void Emit (EmitContext ec, bool is_statement)
394                 {
395                         IAssignMethod t = (IAssignMethod) target;
396                         t.EmitAssign (ec, source, !is_statement, this is CompoundAssign);
397                 }
398
399                 public override void Emit (EmitContext ec)
400                 {
401                         Emit (ec, false);
402                 }
403
404                 public override void EmitStatement (EmitContext ec)
405                 {
406                         Emit (ec, true);
407                 }
408
409                 protected override void CloneTo (CloneContext clonectx, Expression t)
410                 {
411                         Assign _target = (Assign) t;
412
413                         _target.target = target.Clone (clonectx);
414                         _target.source = source.Clone (clonectx);
415                 }
416         }
417
418         class SimpleAssign : Assign {
419                 public SimpleAssign (Expression target, Expression source)
420                         : this (target, source, target.Location)
421                 {
422                 }
423
424                 public SimpleAssign (Expression target, Expression source, Location loc)
425                         : base (target, source, loc)
426                 {
427                 }
428
429                 bool CheckEqualAssign (Expression t)
430                 {
431                         if (source is Assign) {
432                                 Assign a = (Assign) source;
433                                 if (t.Equals (a.Target))
434                                         return true;
435                                 return a is SimpleAssign && ((SimpleAssign) a).CheckEqualAssign (t);
436                         }
437                         return t.Equals (source);
438                 }
439
440                 public override Expression DoResolve (ResolveContext ec)
441                 {
442                         Expression e = base.DoResolve (ec);
443                         if (e == null || e != this)
444                                 return e;
445
446                         if (CheckEqualAssign (target))
447                                 ec.Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?");
448
449                         return this;
450                 }
451         }
452
453         // This class implements fields and events class initializers
454         public class FieldInitializer : Assign
455         {
456                 //
457                 // Keep resolved value because field initializers have their own rules
458                 //
459                 ExpressionStatement resolved;
460                 IMemberContext rc;
461
462                 public FieldInitializer (FieldBuilder field, Expression expression, IMemberContext rc)
463                         : base (new FieldExpr (field, expression.Location), expression, expression.Location)
464                 {
465                         this.rc = rc;
466                         if (!field.IsStatic)
467                                 ((FieldExpr)target).InstanceExpression = CompilerGeneratedThis.Instance;
468                 }
469
470                 public override Expression DoResolve (ResolveContext ec)
471                 {
472                         // Field initializer can be resolved (fail) many times
473                         if (source == null)
474                                 return null;
475
476                         if (resolved == null) {
477                                 //
478                                 // Field initializers are tricky for partial classes. They have to
479                                 // share same costructor (block) but they have they own resolve scope.
480                                 //
481
482                                 IMemberContext old = ec.MemberContext;
483                                 ec.MemberContext = rc;
484
485                                 using (ec.Set (ResolveContext.Options.FieldInitializerScope)) {
486                                         resolved = base.DoResolve (ec) as ExpressionStatement;
487                                 }
488
489                                 ec.MemberContext = old;
490                         }
491
492                         return resolved;
493                 }
494
495                 public override void EmitStatement (EmitContext ec)
496                 {
497                         if (resolved == null)
498                                 return;
499                         
500                         if (resolved != this)
501                                 resolved.EmitStatement (ec);
502                         else
503                                 base.EmitStatement (ec);
504                 }
505                 
506                 public bool IsComplexInitializer {
507                         get { return !(source is Constant); }
508                 }
509
510                 public bool IsDefaultInitializer {
511                         get {
512                                 Constant c = source as Constant;
513                                 if (c == null)
514                                         return false;
515                                 
516                                 FieldExpr fe = (FieldExpr)target;
517                                 return c.IsDefaultInitializer (fe.Type);
518                         }
519                 }
520         }
521
522         class EventAddOrRemove : ExpressionStatement {
523                 EventExpr target;
524                 Binary.Operator op;
525                 Expression source;
526
527                 public EventAddOrRemove (Expression target, Binary.Operator op, Expression source, Location loc)
528                 {
529                         this.target = target as EventExpr;
530                         this.op = op;
531                         this.source = source;
532                         this.loc = loc;
533                 }
534
535                 public override Expression CreateExpressionTree (ResolveContext ec)
536                 {
537                         return new SimpleAssign (target, source).CreateExpressionTree (ec);
538                 }
539
540                 public override Expression DoResolve (ResolveContext ec)
541                 {
542                         if (op != Binary.Operator.Addition && op != Binary.Operator.Subtraction)
543                                 target.Error_AssignmentEventOnly (ec);
544
545                         source = source.Resolve (ec);
546                         if (source == null)
547                                 return null;
548
549                         source = Convert.ImplicitConversionRequired (ec, source, target.Type, loc);
550                         if (source == null)
551                                 return null;
552
553                         eclass = ExprClass.Value;
554                         type = TypeManager.void_type;
555                         return this;
556                 }
557
558                 public override void Emit (EmitContext ec)
559                 {
560                         if (RootContext.EvalMode)
561                                 EmitStatement (ec);
562                         else
563                                 throw new InternalErrorException ("don't know what to emit");                           
564                 }
565
566                 public override void EmitStatement (EmitContext ec)
567                 {
568                         target.EmitAddOrRemove (ec, op == Binary.Operator.Addition, source);
569                 }
570         }
571
572         //
573         // This class is used for compound assignments.
574         //
575         public class CompoundAssign : Assign
576         {
577                 // This is just a hack implemented for arrays only
578                 public sealed class TargetExpression : Expression
579                 {
580                         Expression child;
581                         public TargetExpression (Expression child)
582                         {
583                                 this.child = child;
584                                 this.loc = child.Location;
585                         }
586
587                         public override Expression CreateExpressionTree (ResolveContext ec)
588                         {
589                                 throw new NotSupportedException ("ET");
590                         }
591
592                         public override Expression DoResolve (ResolveContext ec)
593                         {
594                                 type = child.Type;
595                                 eclass = ExprClass.Value;
596                                 return this;
597                         }
598
599                         public override void Emit (EmitContext ec)
600                         {
601                                 child.Emit (ec);
602                         }
603                 }
604
605                 // Used for underlying binary operator
606                 readonly Binary.Operator op;
607                 Expression right;
608                 Expression left;
609
610                 public CompoundAssign (Binary.Operator op, Expression target, Expression source)
611                         : base (target, source, target.Location)
612                 {
613                         right = source;
614                         this.op = op;
615                 }
616
617                 public CompoundAssign (Binary.Operator op, Expression target, Expression source, Expression left)
618                         : this (op, target, source)
619                 {
620                         this.left = left;
621                 }
622
623                 public override Expression DoResolve (ResolveContext ec)
624                 {
625                         right = right.Resolve (ec);
626                         if (right == null)
627                                 return null;
628
629                         MemberAccess ma = target as MemberAccess;
630                         using (ec.Set (ResolveContext.Options.CompoundAssignmentScope)) {
631                                 target = target.Resolve (ec);
632                         }
633                         
634                         if (target == null)
635                                 return null;
636
637                         if (target is MethodGroupExpr){
638                                 ec.Report.Error (1656, loc,
639                                         "Cannot assign to `{0}' because it is a `{1}'",
640                                         ((MethodGroupExpr)target).Name, target.ExprClassName);
641                                 return null;
642                         }
643
644                         if (target is EventExpr)
645                                 return new EventAddOrRemove (target, op, right, loc).DoResolve (ec);
646
647                         //
648                         // Only now we can decouple the original source/target
649                         // into a tree, to guarantee that we do not have side
650                         // effects.
651                         //
652                         if (left == null)
653                                 left = new TargetExpression (target);
654
655                         source = new Binary (op, left, right, true);
656
657                         // TODO: TargetExpression breaks MemberAccess composition
658                         if (target is DynamicMemberBinder) {
659                                 Arguments targs = ((DynamicMemberBinder) target).Arguments;
660                                 source = source.Resolve (ec);
661
662                                 Arguments args = new Arguments (2);
663                                 args.AddRange (targs);
664                                 args.Add (new Argument (source));
665                                 source = new DynamicMemberBinder (true, ma.Name, args, loc).Resolve (ec);
666
667                                 // Handles possible event addition/subtraction
668                                 if (op == Binary.Operator.Addition || op == Binary.Operator.Subtraction) {
669                                         args = new Arguments (2);
670                                         args.AddRange (targs);
671                                         args.Add (new Argument (right));
672                                         string method_prefix = op == Binary.Operator.Addition ?
673                                                 Event.AEventAccessor.AddPrefix : Event.AEventAccessor.RemovePrefix;
674
675                                         Expression invoke = new DynamicInvocation (
676                                                 new MemberAccess (right, method_prefix + ma.Name, loc), args, loc).Resolve (ec);
677
678                                         args = new Arguments (1);
679                                         args.AddRange (targs);
680                                         source = new DynamicEventCompoundAssign (ma.Name, args,
681                                                 (ExpressionStatement) source, (ExpressionStatement) invoke, loc).Resolve (ec);
682                                 }
683
684                                 return source;
685                         }
686
687                         return base.DoResolve (ec);
688                 }
689
690 #if NET_4_0
691                 public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx)
692                 {
693                         var target_object = target.MakeExpression (ctx);
694                         var source_object = System.Linq.Expressions.Expression.Convert (source.MakeExpression (ctx), target_object.Type);
695                         return System.Linq.Expressions.Expression.Assign (target_object, source_object);
696                 }
697 #endif
698
699                 protected override Expression ResolveConversions (ResolveContext ec)
700                 {
701                         Type target_type = target.Type;
702
703                         //
704                         // 1. the return type is implicitly convertible to the type of target
705                         //
706                         if (Convert.ImplicitConversionExists (ec, source, target_type)) {
707                                 source = Convert.ImplicitConversion (ec, source, target_type, loc);
708                                 return this;
709                         }
710
711                         //
712                         // Otherwise, if the selected operator is a predefined operator
713                         //
714                         Binary b = source as Binary;
715                         if (b != null) {
716                                 //
717                                 // 2a. the operator is a shift operator
718                                 //
719                                 // 2b. the return type is explicitly convertible to the type of x, and
720                                 // y is implicitly convertible to the type of x
721                                 //
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;
726                                 }
727                         }
728
729                         right.Error_ValueCannotBeConverted (ec, loc, target_type, false);
730                         return null;
731                 }
732
733                 protected override void CloneTo (CloneContext clonectx, Expression t)
734                 {
735                         CompoundAssign ctarget = (CompoundAssign) t;
736
737                         ctarget.right = ctarget.source = source.Clone (clonectx);
738                         ctarget.target = target.Clone (clonectx);
739                 }
740         }
741 }