2008-05-01 Rodrigo Kumpera <rkumpera@novell.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 {
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 (EmitContext ec)
210                 {
211                         throw new NotSupportedException ("ET");
212                 }
213
214                 public override Expression DoResolve (EmitContext ec)
215                 {
216                         return this;
217                 }
218
219                 public override void Emit (EmitContext ec)
220                 {
221                         ILGenerator ig = ec.ig;
222
223                         if (builder == null)
224                                 throw new InternalErrorException ("Emit without Store, or after Release");
225
226                         ig.Emit (OpCodes.Ldloc, builder);
227                         // we need to copy from the pointer
228                         if (is_address)
229                                 LoadFromPtr (ig, type);
230                 }
231
232                 // NB: if you have `is_address' on the stack there must
233                 // be a managed pointer. Otherwise, it is the type from
234                 // the ctor.
235                 public void Store (EmitContext ec)
236                 {
237                         ILGenerator ig = ec.ig;
238                         if (builder == null)
239                                 builder = ec.GetTemporaryLocal (is_address ? TypeManager.GetReferenceType (type): type);
240
241                         ig.Emit (OpCodes.Stloc, builder);
242                 }
243
244                 public void AddressOf (EmitContext ec, AddressOp mode)
245                 {
246                         if (builder == null)
247                                 builder = ec.GetTemporaryLocal (is_address ? TypeManager.GetReferenceType (type): type);
248
249                         // if is_address, than this is just the address anyways,
250                         // so we just return this.
251                         ILGenerator ig = ec.ig;
252
253                         if (is_address)
254                                 ig.Emit (OpCodes.Ldloc, builder);
255                         else
256                                 ig.Emit (OpCodes.Ldloca, builder);
257                 }
258
259                 public bool PointsToAddress {
260                         get {
261                                 return is_address;
262                         }
263                 }
264         }
265
266         /// <summary>
267         ///   The Assign node takes care of assigning the value of source into
268         ///   the expression represented by target.
269         /// </summary>
270         public class Assign : ExpressionStatement {
271                 protected Expression target, source;
272
273                 protected Assign (Expression target, Expression source, Location loc)
274                 {
275                         this.target = target;
276                         this.source = source;
277                         this.loc = loc;
278                 }
279                 
280                 public override Expression CreateExpressionTree (EmitContext ec)
281                 {
282                         Report.Error (832, loc, "An expression tree cannot contain an assignment operator");
283                         return null;
284                 }
285
286                 public Expression Target {
287                         get { return target; }
288                 }
289
290                 public Expression Source {
291                         get { return source; }
292                 }
293
294                 public override Expression DoResolve (EmitContext ec)
295                 {
296                         bool ok = true;
297                         source = source.Resolve (ec);
298                                                 
299                         if (source == null) {
300                                 ok = false;
301                                 source = EmptyExpression.Null;
302                         }
303
304                         target = target.ResolveLValue (ec, source, Location);
305
306                         if (target == null || !ok)
307                                 return null;
308
309                         Type target_type = target.Type;
310                         Type source_type = source.Type;
311
312                         eclass = ExprClass.Value;
313                         type = target_type;
314
315                         if (!(target is IAssignMethod)) {
316                                 Error_ValueAssignment (loc);
317                                 return null;
318                         }
319
320                         if ((source.eclass == ExprClass.Type) && (source is TypeExpr)) {
321                                 source.Error_UnexpectedKind (ec.DeclContainer, "variable or value", loc);
322                                 return null;
323                         } else if ((RootContext.Version == LanguageVersion.ISO_1) &&
324                                    (source is MethodGroupExpr)){
325                                 ((MethodGroupExpr) source).ReportUsageError ();
326                                 return null;
327                         }
328
329                         if (TypeManager.IsEqual (target_type, source_type)) {
330                                 if (target.eclass == ExprClass.Variable) {
331                                         New n = source as New;
332                                         if (n == null)
333                                                 return this;
334
335                                         if (n.HasInitializer) {
336                                                 n.SetTargetVariable (target);
337                                         } else if (target_type.IsValueType) {
338                                                 n.SetTargetVariable (target);
339                                                 return n;
340                                         }
341                                 }
342
343                                 return this;
344                         }
345
346                         return ResolveConversions (ec);
347                 }
348
349                 protected virtual Expression ResolveConversions (EmitContext ec)
350                 {
351                         source = Convert.ImplicitConversionRequired (ec, source, target.Type, loc);
352                         if (source == null)
353                                 return null;
354
355                         return this;
356                 }
357
358                 void Emit (EmitContext ec, bool is_statement)
359                 {
360                         ((IAssignMethod) target).EmitAssign (ec, source, !is_statement, this is CompoundAssign);
361                 }
362
363                 public override void Emit (EmitContext ec)
364                 {
365                         Emit (ec, false);
366                 }
367
368                 public override void EmitStatement (EmitContext ec)
369                 {
370                         Emit (ec, true);
371                 }
372
373                 protected override void CloneTo (CloneContext clonectx, Expression t)
374                 {
375                         Assign _target = (Assign) t;
376
377                         _target.target = target.Clone (clonectx);
378                         _target.source = source.Clone (clonectx);
379                 }
380         }
381
382         class SimpleAssign : Assign {
383                 public SimpleAssign (Expression target, Expression source)
384                         : this (target, source, target.Location)
385                 {
386                 }
387
388                 public SimpleAssign (Expression target, Expression source, Location loc)
389                         : base (target, source, loc)
390                 {
391                 }
392
393                 bool CheckEqualAssign (Expression t)
394                 {
395                         if (source is Assign) {
396                                 Assign a = (Assign) source;
397                                 if (t.Equals (a.Target))
398                                         return true;
399                                 return a is SimpleAssign && ((SimpleAssign) a).CheckEqualAssign (t);
400                         }
401                         return t.Equals (source);
402                 }
403
404                 public override Expression DoResolve (EmitContext ec)
405                 {
406                         Expression e = base.DoResolve (ec);
407                         if (e == null || e != this)
408                                 return e;
409
410                         if (CheckEqualAssign (target))
411                                 Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?");
412
413                         return this;
414                 }
415         }
416
417         // This class implements fields and events class initializers
418         public class FieldInitializer : Assign
419         {
420                 //
421                 // Keep resolved value because field initializers have their own rules
422                 //
423                 ExpressionStatement resolved;
424
425                 public FieldInitializer (FieldBuilder field, Expression expression)
426                         : base (new FieldExpr (field, expression.Location, true), expression, expression.Location)
427                 {
428                         if (!field.IsStatic)
429                                 ((FieldExpr)target).InstanceExpression = CompilerGeneratedThis.Instance;
430                 }
431
432                 public override Expression DoResolve (EmitContext ec)
433                 {
434                         // Field initializer can be resolved (fail) many times
435                         if (source == null)
436                                 return null;
437
438                         if (resolved == null)
439                                 resolved = base.DoResolve (ec) as ExpressionStatement;
440
441                         return resolved;
442                 }
443
444                 public override void EmitStatement (EmitContext ec)
445                 {
446                         if (resolved == null)
447                                 return;
448                         
449                         if (resolved != this)
450                                 resolved.EmitStatement (ec);
451                         else
452                                 base.EmitStatement (ec);
453                 }
454                 
455                 public bool IsComplexInitializer {
456                         get { return !(source is Constant); }
457                 }
458
459                 public bool IsDefaultInitializer {
460                         get {
461                                 Constant c = source as Constant;
462                                 if (c == null)
463                                         return false;
464                                 
465                                 FieldExpr fe = (FieldExpr)target;
466                                 return c.IsDefaultInitializer (fe.Type);
467                         }
468                 }
469         }
470
471         class EventAddOrRemove : ExpressionStatement {
472                 EventExpr target;
473                 Binary.Operator op;
474                 Expression source;
475
476                 public EventAddOrRemove (Expression target, Binary.Operator op, Expression source, Location loc)
477                 {
478                         this.target = target as EventExpr;
479                         this.op = op;
480                         this.source = source;
481                         this.loc = loc;
482                 }
483
484                 public override Expression CreateExpressionTree (EmitContext ec)
485                 {
486                         return new SimpleAssign (target, source).CreateExpressionTree (ec);
487                 }
488
489                 public override Expression DoResolve (EmitContext ec)
490                 {
491                         eclass = ExprClass.Value;
492                         type = target.Type;
493
494                         source = source.Resolve (ec);
495                         if (source == null)
496                                 return null;
497
498                         Type stype = source.Type;
499                         if (source.eclass == ExprClass.MethodGroup || stype == TypeManager.anonymous_method_type) {
500                                 if (RootContext.Version != LanguageVersion.ISO_1) {
501                                         source = Convert.ImplicitConversionRequired (ec, source, type, loc);
502                                         if (source == null)
503                                                 return null;
504                                         stype = source.Type;
505                                 }
506                         } else if (!TypeManager.Equals (type, stype) && !(source is NullLiteral))
507                                 return null;
508
509                         return this;
510                 }
511
512                 public override void Emit (EmitContext ec)
513                 {
514                         throw new InternalErrorException ("don't know what to emit");
515                 }
516
517                 public override void EmitStatement (EmitContext ec)
518                 {
519                         target.EmitAddOrRemove (ec, op == Binary.Operator.Addition, source);
520                 }
521         }
522
523         //
524         // This class is used for compound assignments.
525         //
526         class CompoundAssign : Assign {
527                 Binary.Operator op;
528                 Expression original_source;
529
530                 public CompoundAssign (Binary.Operator op, Expression target, Expression source)
531                         : base (target, source, target.Location)
532                 {
533                         original_source = source;
534                         this.op = op;
535                 }
536
537                 // !!! What a stupid name
538                 public class Helper : Expression {
539                         Expression child;
540                         public Helper (Expression child)
541                         {
542                                 this.child = child;
543                                 this.loc = child.Location;
544                         }
545
546                         public override Expression CreateExpressionTree (EmitContext ec)
547                         {
548                                 throw new NotSupportedException ("ET");
549                         }
550
551                         public override Expression DoResolve (EmitContext ec)
552                         {
553                                 child = child.Resolve (ec);
554                                 if (child == null)
555                                         return null;
556                                 type = child.Type;
557                                 eclass = ExprClass.Value;
558                                 return this;
559                         }
560
561                         public override void Emit (EmitContext ec)
562                         {
563                                 child.Emit (ec);
564                         }
565                 }
566
567                 public override Expression DoResolve (EmitContext ec)
568                 {
569                         original_source = original_source.Resolve (ec);
570                         if (original_source == null)
571                                 return null;
572
573                         using (ec.Set (EmitContext.Flags.InCompoundAssignment)) {
574                                 target = target.Resolve (ec);
575                         }
576                         
577                         if (target == null)
578                                 return null;
579
580                         if (target is MethodGroupExpr){
581                                 Error_CannotAssign (((MethodGroupExpr)target).Name, target.ExprClassName);
582                                 return null;
583                         }
584
585                         if (target is EventExpr)
586                                 return new EventAddOrRemove (target, op, original_source, loc).DoResolve (ec);
587
588                         //
589                         // Only now we can decouple the original source/target
590                         // into a tree, to guarantee that we do not have side
591                         // effects.
592                         //
593                         source = new Binary (op, new Helper (target), original_source, true);
594                         return base.DoResolve (ec);
595                 }
596
597                 protected override Expression ResolveConversions (EmitContext ec)
598                 {
599                         // source might have changed to BinaryDelegate
600                         Type target_type = target.Type;
601                         Type source_type = source.Type;
602                         Binary b = source as Binary;
603                         if (b == null)
604                                 return base.ResolveConversions (ec);
605
606                         // FIXME: Restrict only to predefined operators
607
608                         //
609                         // 1. if the source is explicitly convertible to the
610                         //    target_type
611                         //
612                         source = Convert.ExplicitConversion (ec, source, target_type, loc);
613                         if (source == null){
614                                 original_source.Error_ValueCannotBeConverted (ec, loc, target_type, true);
615                                 return null;
616                         }
617
618                         //
619                         // 2. and the original right side is implicitly convertible to
620                         // the type of target
621                         //
622                         if (Convert.ImplicitConversionExists (ec, original_source, target_type))
623                                 return this;
624
625                         //
626                         // In the spec 2.4 they added: or if type of the target is int
627                         // and the operator is a shift operator...
628                         //
629                         if (source_type == TypeManager.int32_type && (b.Oper & Binary.Operator.ShiftMask) != 0)
630                                 return this;
631
632                         original_source.Error_ValueCannotBeConverted (ec, loc, target_type, false);
633                         return null;
634                 }
635
636                 protected override void CloneTo (CloneContext clonectx, Expression t)
637                 {
638                         CompoundAssign target = (CompoundAssign) t;
639
640                         target.original_source = original_source.Clone (clonectx);
641                 }
642         }
643 }