2008-01-08 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 //
8 // (C) 2001, 2002, 2003 Ximian, Inc.
9 // (C) 2004 Novell, Inc
10 //
11 using System;
12 using System.Reflection;
13 using System.Reflection.Emit;
14
15 namespace Mono.CSharp {
16
17         /// <summary>
18         ///   This interface is implemented by expressions that can be assigned to.
19         /// </summary>
20         /// <remarks>
21         ///   This interface is implemented by Expressions whose values can not
22         ///   store the result on the top of the stack.
23         ///
24         ///   Expressions implementing this (Properties, Indexers and Arrays) would
25         ///   perform an assignment of the Expression "source" into its final
26         ///   location.
27         ///
28         ///   No values on the top of the stack are expected to be left by
29         ///   invoking this method.
30         /// </remarks>
31         public interface IAssignMethod {
32                 //
33                 // This is an extra version of Emit. If leave_copy is `true'
34                 // A copy of the expression will be left on the stack at the
35                 // end of the code generated for EmitAssign
36                 //
37                 void Emit (EmitContext ec, bool leave_copy);
38
39                 //
40                 // This method does the assignment
41                 // `source' will be stored into the location specified by `this'
42                 // if `leave_copy' is true, a copy of `source' will be left on the stack
43                 // if `prepare_for_load' is true, when `source' is emitted, there will
44                 // be data on the stack that it can use to compuatate its value. This is
45                 // for expressions like a [f ()] ++, where you can't call `f ()' twice.
46                 //
47                 void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load);
48
49                 /*
50                 For simple assignments, this interface is very simple, EmitAssign is called with source
51                 as the source expression and leave_copy and prepare_for_load false.
52
53                 For compound assignments it gets complicated.
54
55                 EmitAssign will be called as before, however, prepare_for_load will be
56                 true. The @source expression will contain an expression
57                 which calls Emit. So, the calls look like:
58
59                 this.EmitAssign (ec, source, false, true) ->
60                         source.Emit (ec); ->
61                                 [...] ->
62                                         this.Emit (ec, false); ->
63                                         end this.Emit (ec, false); ->
64                                 end [...]
65                         end source.Emit (ec);
66                 end this.EmitAssign (ec, source, false, true)
67
68
69                 When prepare_for_load is true, EmitAssign emits a `token' on the stack that
70                 Emit will use for its state.
71
72                 Let's take FieldExpr as an example. assume we are emitting f ().y += 1;
73
74                 Here is the call tree again. This time, each call is annotated with the IL
75                 it produces:
76
77                 this.EmitAssign (ec, source, false, true)
78                         call f
79                         dup
80
81                         Binary.Emit ()
82                                 this.Emit (ec, false);
83                                 ldfld y
84                                 end this.Emit (ec, false);
85
86                                 IntConstant.Emit ()
87                                 ldc.i4.1
88                                 end IntConstant.Emit
89
90                                 add
91                         end Binary.Emit ()
92
93                         stfld
94                 end this.EmitAssign (ec, source, false, true)
95
96                 Observe two things:
97                         1) EmitAssign left a token on the stack. It was the result of f ().
98                         2) This token was used by Emit
99
100                 leave_copy (in both EmitAssign and Emit) tells the compiler to leave a copy
101                 of the expression at that point in evaluation. This is used for pre/post inc/dec
102                 and for a = x += y. Let's do the above example with leave_copy true in EmitAssign
103
104                 this.EmitAssign (ec, source, true, true)
105                         call f
106                         dup
107
108                         Binary.Emit ()
109                                 this.Emit (ec, false);
110                                 ldfld y
111                                 end this.Emit (ec, false);
112
113                                 IntConstant.Emit ()
114                                 ldc.i4.1
115                                 end IntConstant.Emit
116
117                                 add
118                         end Binary.Emit ()
119
120                         dup
121                         stloc temp
122                         stfld
123                         ldloc temp
124                 end this.EmitAssign (ec, source, true, true)
125
126                 And with it true in Emit
127
128                 this.EmitAssign (ec, source, false, true)
129                         call f
130                         dup
131
132                         Binary.Emit ()
133                                 this.Emit (ec, true);
134                                 ldfld y
135                                 dup
136                                 stloc temp
137                                 end this.Emit (ec, true);
138
139                                 IntConstant.Emit ()
140                                 ldc.i4.1
141                                 end IntConstant.Emit
142
143                                 add
144                         end Binary.Emit ()
145
146                         stfld
147                         ldloc temp
148                 end this.EmitAssign (ec, source, false, true)
149
150                 Note that these two examples are what happens for ++x and x++, respectively.
151                 */
152         }
153
154         /// <summary>
155         ///   An Expression to hold a temporary value.
156         /// </summary>
157         /// <remarks>
158         ///   The LocalTemporary class is used to hold temporary values of a given
159         ///   type to "simulate" the expression semantics on property and indexer
160         ///   access whose return values are void.
161         ///
162         ///   The local temporary is used to alter the normal flow of code generation
163         ///   basically it creates a local variable, and its emit instruction generates
164         ///   code to access this value, return its address or save its value.
165         ///
166         ///   If `is_address' is true, then the value that we store is the address to the
167         ///   real value, and not the value itself.
168         ///
169         ///   This is needed for a value type, because otherwise you just end up making a
170         ///   copy of the value on the stack and modifying it. You really need a pointer
171         ///   to the origional value so that you can modify it in that location. This
172         ///   Does not happen with a class because a class is a pointer -- so you always
173         ///   get the indirection.
174         ///
175         ///   The `is_address' stuff is really just a hack. We need to come up with a better
176         ///   way to handle it.
177         /// </remarks>
178         public class LocalTemporary : Expression, IMemoryLocation {
179                 LocalBuilder builder;
180                 bool is_address;
181
182                 public LocalTemporary (Type t) : this (t, false) {}
183
184                 public LocalTemporary (Type t, bool is_address)
185                 {
186                         type = t;
187                         eclass = ExprClass.Value;
188                         this.is_address = is_address;
189                 }
190
191                 public LocalTemporary (LocalBuilder b, Type t)
192                 {
193                         type = t;
194                         eclass = ExprClass.Value;
195                         loc = Location.Null;
196                         builder = b;
197                 }
198
199                 public void Release (EmitContext ec)
200                 {
201                         ec.FreeTemporaryLocal (builder, type);
202                         builder = null;
203                 }
204
205                 public override Expression DoResolve (EmitContext ec)
206                 {
207                         return this;
208                 }
209
210                 public override void Emit (EmitContext ec)
211                 {
212                         ILGenerator ig = ec.ig;
213
214                         if (builder == null)
215                                 throw new InternalErrorException ("Emit without Store, or after Release");
216
217                         ig.Emit (OpCodes.Ldloc, builder);
218                         // we need to copy from the pointer
219                         if (is_address)
220                                 LoadFromPtr (ig, type);
221                 }
222
223                 // NB: if you have `is_address' on the stack there must
224                 // be a managed pointer. Otherwise, it is the type from
225                 // the ctor.
226                 public void Store (EmitContext ec)
227                 {
228                         ILGenerator ig = ec.ig;
229                         if (builder == null)
230                                 builder = ec.GetTemporaryLocal (is_address ? TypeManager.GetReferenceType (type): type);
231
232                         ig.Emit (OpCodes.Stloc, builder);
233                 }
234
235                 public void AddressOf (EmitContext ec, AddressOp mode)
236                 {
237                         if (builder == null)
238                                 builder = ec.GetTemporaryLocal (is_address ? TypeManager.GetReferenceType (type): type);
239
240                         // if is_address, than this is just the address anyways,
241                         // so we just return this.
242                         ILGenerator ig = ec.ig;
243
244                         if (is_address)
245                                 ig.Emit (OpCodes.Ldloc, builder);
246                         else
247                                 ig.Emit (OpCodes.Ldloca, builder);
248                 }
249
250                 public bool PointsToAddress {
251                         get {
252                                 return is_address;
253                         }
254                 }
255         }
256
257         /// <summary>
258         ///   The Assign node takes care of assigning the value of source into
259         ///   the expression represented by target.
260         /// </summary>
261         public class Assign : ExpressionStatement {
262                 protected Expression target, source, real_source;
263                 protected LocalTemporary temp = null, real_temp = null;
264                 protected Assign embedded = null;
265                 protected bool is_embedded = false;
266                 protected bool must_free_temp = false;
267
268                 public Assign (Expression target, Expression source)
269                         : this (target, source, target.Location)
270                 {
271                 }
272
273                 public Assign (Expression target, Expression source, Location l)
274                 {
275                         this.target = target;
276                         this.source = this.real_source = source;
277                         this.loc = l;
278                 }
279
280                 protected Assign (Assign embedded, Location l)
281                         : this (embedded.target, embedded.source, l)
282                 {
283                         this.is_embedded = true;
284                 }
285
286                 protected virtual Assign GetEmbeddedAssign (Location loc)
287                 {
288                         return new Assign (this, loc);
289                 }
290
291                 public Expression Target {
292                         get {
293                                 return target;
294                         }
295
296                         set {
297                                 target = value;
298                         }
299                 }
300
301                 public Expression Source {
302                         get {
303                                 return source;
304                         }
305
306                         set {
307                                 source = value;
308                         }
309                 }
310
311                 public static void error70 (EventInfo ei, Location l)
312                 {
313                         Report.Error (70, l, "The event `" + TypeManager.CSharpSignature (ei) +
314                                       "' can only appear on the left hand side of += or -= (except when" +
315                                       " used from within the type `" + ei.DeclaringType + "')");
316                 }
317
318                 //
319                 // Will return either `this' or an instance of `New'.
320                 //
321                 public override Expression DoResolve (EmitContext ec)
322                 {
323                         // Create an embedded assignment if our source is an assignment.
324                         if (source is Assign)
325                                 source = embedded = ((Assign) source).GetEmbeddedAssign (loc);
326
327                         real_source = source = source.Resolve (ec);
328                                                 
329                         if (source == null) {
330                                 // Ensure that we don't propagate the error as spurious "uninitialized variable" errors.
331                                 target = target.ResolveLValue (ec, EmptyExpression.Null, Location);
332                                 return null;
333                         }
334
335                         //
336                         // This is used in an embedded assignment.
337                         // As an example, consider the statement "A = X = Y = Z".
338                         //
339                         if (is_embedded && !(source is Constant)) {
340                                 // If this is the innermost assignment (the "Y = Z" in our example),
341                                 // create a new temporary local, otherwise inherit that variable
342                                 // from our child (the "X = (Y = Z)" inherits the local from the
343                                 // "Y = Z" assignment).
344
345                                 if (embedded == null) {
346                                         if (this is CompoundAssign)
347                                                 real_temp = temp = new LocalTemporary (target.Type);
348                                         else
349                                                 real_temp = temp = new LocalTemporary (source.Type);
350                                 } else
351                                         temp = embedded.temp;
352
353                                 // Set the source to the new temporary variable.
354                                 // This means that the following target.ResolveLValue () will tell
355                                 // the target to read it's source value from that variable.
356                                 source = temp;
357                         }
358
359                         // If we have an embedded assignment, use the embedded assignment's temporary
360                         // local variable as source.
361                         if (embedded != null)
362                                 source = (embedded.temp != null) ? embedded.temp : embedded.source;
363                         
364                         target = target.ResolveLValue (ec, source, Location);
365
366                         if (target == null)
367                                 return null;
368                         
369                         bool same_assignment = (embedded != null) ? embedded.Target.Equals(target) : source.Equals (target);
370                         if (same_assignment) {
371                                 Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?");
372                         }
373
374                         Type target_type = target.Type;
375                         Type source_type = real_source.Type;
376
377                         // If we're an embedded assignment, our parent will reuse our source as its
378                         // source, it won't read from our target.
379                         if (is_embedded)
380                                 type = source_type;
381                         else
382                                 type = target_type;
383                         eclass = ExprClass.Value;
384
385                         if (target is EventExpr) {
386                                 EventInfo ei = ((EventExpr) target).EventInfo;
387
388                                 Expression ml = MemberLookup (
389                                         ec.ContainerType, ec.ContainerType, ei.Name,
390                                         MemberTypes.Event, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
391
392                                 if (ml == null) {
393                                         //
394                                         // If this is the case, then the Event does not belong
395                                         // to this Type and so, according to the spec
396                                         // is allowed to only appear on the left hand of
397                                         // the += and -= operators
398                                         //
399                                         // Note that target will not appear as an EventExpr
400                                         // in the case it is being referenced within the same type container;
401                                         // it will appear as a FieldExpr in that case.
402                                         //
403
404                                         if (!(source is BinaryDelegate)) {
405                                                 error70 (ei, loc);
406                                                 return null;
407                                         }
408                                 }
409                         }
410
411                         if (!(target is IAssignMethod) && (target.eclass != ExprClass.EventAccess)) {
412                                 Error_ValueAssignment (loc);
413                                 return null;
414                         }
415
416                         if ((source.eclass == ExprClass.Type) && (source is TypeExpr)) {
417                                 source.Error_UnexpectedKind (ec.DeclContainer, "variable or value", loc);
418                                 return null;
419                         } else if ((RootContext.Version == LanguageVersion.ISO_1) &&
420                                    (source is MethodGroupExpr)){
421                                 ((MethodGroupExpr) source).ReportUsageError ();
422                                 return null;
423
424                         }
425
426                         if (target_type == source_type) {
427                                 if (target.eclass == ExprClass.Variable) {
428                                         New n = source as New;
429                                         if (n == null)
430                                                 return this;
431
432                                         if (n.HasInitializer) {
433                                                 n.SetTargetVariable (target);
434                                         } else if (target_type.IsValueType) {
435                                                 n.SetTargetVariable (target);
436                                                 return n;
437                                         }
438                                 }
439
440                                 return this;
441                         }
442
443                         //
444                         // If this assignment/operator was part of a compound binary
445                         // operator, then we allow an explicit conversion, as detailed
446                         // in the spec.
447                         //
448
449                         if (this is CompoundAssign){
450                                 CompoundAssign a = (CompoundAssign) this;
451
452                                 Binary b = source as Binary;
453                                 if (b != null){
454                                         //
455                                         // 1. if the source is explicitly convertible to the
456                                         //    target_type
457                                         //
458
459                                         source = Convert.ExplicitConversion (ec, source, target_type, loc);
460                                         if (source == null){
461                                                 a.original_source.Error_ValueCannotBeConverted (ec, loc, target_type, true);
462                                                 return null;
463                                         }
464
465                                         //
466                                         // 2. and the original right side is implicitly convertible to
467                                         // the type of target
468                                         //
469                                         if (Convert.ImplicitConversionExists (ec, a.original_source, target_type))
470                                                 return this;
471
472                                         //
473                                         // In the spec 2.4 they added: or if type of the target is int
474                                         // and the operator is a shift operator...
475                                         //
476                                         if (source_type == TypeManager.int32_type &&
477                                             (b.Oper == Binary.Operator.LeftShift || b.Oper == Binary.Operator.RightShift))
478                                                 return this;
479
480                                         a.original_source.Error_ValueCannotBeConverted (ec, loc, target_type, false);
481                                         return null;
482                                 }
483                         }
484
485                         if (source.eclass == ExprClass.MethodGroup && !TypeManager.IsDelegateType (target_type)) {
486                                 Report.Error (428, source.Location, "Cannot convert method group `{0}' to non-delegate type `{1}'. Did you intend to invoke the method?",
487                                         ((MethodGroupExpr)source).Name, target.GetSignatureForError ());
488                                 return null;
489                         }
490
491                         source = Convert.ImplicitConversionRequired (ec, source, target_type, loc);
492                         if (source == null)
493                                 return null;
494
495                         // If we're an embedded assignment, we need to create a new temporary variable
496                         // for the converted value.  Our parent will use this new variable as its source.
497                         // The same applies when we have an embedded assignment - in this case, we need
498                         // to convert our embedded assignment's temporary local variable to the correct
499                         // type and store it in a new temporary local.
500                         if (is_embedded || embedded != null) {
501                                 type = target_type;
502                                 temp = new LocalTemporary (type);
503                                 must_free_temp = true;
504                         }
505
506                         return this;
507                 }
508
509                 Expression EmitEmbedded (EmitContext ec)
510                 {
511                         // Emit an embedded assignment.
512
513                         if (real_temp != null) {
514                                 // If we're the innermost assignment, `real_source' is the right-hand
515                                 // expression which gets assigned to all the variables left of it.
516                                 // Emit this expression and store its result in real_temp.
517                                 real_source.Emit (ec);
518                                 real_temp.Store (ec);
519                         }
520
521                         if (embedded != null)
522                                 embedded.EmitEmbedded (ec);
523
524                         // This happens when we've done a type conversion, in this case source will be
525                         // the expression which does the type conversion from real_temp.
526                         // So emit it and store the result in temp; this is the var which will be read
527                         // by our parent.
528                         if (temp != real_temp) {
529                                 source.Emit (ec);
530                                 temp.Store (ec);
531                         }
532
533                         Expression temp_source = (temp != null) ? temp : source;
534                         ((IAssignMethod) target).EmitAssign (ec, temp_source, false, false);
535                         return temp_source;
536                 }
537
538                 void ReleaseEmbedded (EmitContext ec)
539                 {
540                         if (embedded != null)
541                                 embedded.ReleaseEmbedded (ec);
542
543                         if (real_temp != null)
544                                 real_temp.Release (ec);
545
546                         if (must_free_temp)
547                                 temp.Release (ec);
548                 }
549
550                 void Emit (EmitContext ec, bool is_statement)
551                 {
552                         if (target is EventExpr) {
553                                 ((EventExpr) target).EmitAddOrRemove (ec, source);
554                                 return;
555                         }
556
557                         IAssignMethod am = (IAssignMethod) target;
558
559                         Expression temp_source;
560                         if (embedded != null) {
561                                 temp_source = embedded.EmitEmbedded (ec);
562
563                                 if (temp != null) {
564                                         source.Emit (ec);
565                                         temp.Store (ec);
566                                         temp_source = temp;
567                                 }
568                         } else
569                                 temp_source = source;
570
571                         am.EmitAssign (ec, temp_source, !is_statement, this is CompoundAssign);
572
573                         if (embedded != null) {
574                                 if (temp != null)
575                                         temp.Release (ec);
576                                 embedded.ReleaseEmbedded (ec);
577                         }
578                 }
579
580                 public override void Emit (EmitContext ec)
581                 {
582                         Emit (ec, false);
583                 }
584
585                 public override void EmitStatement (EmitContext ec)
586                 {
587                         Emit (ec, true);
588                 }
589
590                 protected override void CloneTo (CloneContext clonectx, Expression t)
591                 {
592                         Assign _target = (Assign) t;
593
594                         _target.target = target.Clone (clonectx);
595                         _target.source = source.Clone (clonectx);
596                 }
597         }
598
599
600         // This class implements fields and events class initializers
601         public class FieldInitializer : Assign
602         {
603                 //
604                 // Keep resolved value because field initializers have their own rules
605                 //
606                 ExpressionStatement resolved;
607
608                 public FieldInitializer (FieldBuilder field, Expression expression)
609                         : base (new FieldExpr (field, expression.Location, true), expression)
610                 {
611                         if (!field.IsStatic)
612                                 ((FieldExpr)target).InstanceExpression = CompilerGeneratedThis.Instance;
613                 }
614
615                 public override Expression DoResolve (EmitContext ec)
616                 {
617                         // Field initializer can be resolved (fail) many times
618                         if (Source == null)
619                                 return null;
620
621                         if (resolved == null)
622                                 resolved = base.DoResolve (ec) as ExpressionStatement;
623
624                         return resolved;
625                 }
626
627                 public override void EmitStatement (EmitContext ec)
628                 {
629                         if (resolved == null)
630                                 return;
631                         
632                         if (resolved != this)
633                                 resolved.EmitStatement (ec);
634                         else
635                                 base.EmitStatement (ec);
636                 }
637                 
638                 public bool IsComplexInitializer {
639                         get {
640                                 if (embedded != null)
641                                         return true;
642
643                                 return !(source is Constant);
644                         }
645                 }
646
647                 public bool IsDefaultInitializer {
648                         get {
649                                 Constant c = source as Constant;
650                                 if (c == null)
651                                         return false;
652                                 
653                                 FieldExpr fe = (FieldExpr)target;
654                                 return c.IsDefaultInitializer (fe.Type);
655                         }
656                 }
657         }
658
659
660         //
661         // This class is used for compound assignments.
662         //
663         class CompoundAssign : Assign {
664                 Binary.Operator op;
665                 public Expression original_source;
666
667                 public CompoundAssign (Binary.Operator op, Expression target, Expression source)
668                         : base (target, source, target.Location)
669                 {
670                         original_source = source;
671                         this.op = op;
672                 }
673
674                 protected CompoundAssign (CompoundAssign embedded, Location l)
675                         : this (embedded.op, embedded.target, embedded.source)
676                 {
677                         this.is_embedded = true;
678                 }
679
680                 protected override Assign GetEmbeddedAssign (Location loc)
681                 {
682                         return new CompoundAssign (this, loc);
683                 }
684
685                 public override Expression DoResolve (EmitContext ec)
686                 {
687                         original_source = original_source.Resolve (ec);
688                         if (original_source == null)
689                                 return null;
690
691                         using (ec.Set (EmitContext.Flags.InCompoundAssignment)) {
692                                 target = target.Resolve (ec);
693                         }
694                         
695                         if (target == null)
696                                 return null;
697
698                         if (target is MethodGroupExpr){
699                                 Error_CannotAssign (((MethodGroupExpr)target).Name, target.ExprClassName);
700                                 return null;
701                         }
702                         //
703                         // Only now we can decouple the original source/target
704                         // into a tree, to guarantee that we do not have side
705                         // effects.
706                         //
707                         source = new Binary (op, target, original_source, true);
708                         return base.DoResolve (ec);
709                 }
710
711                 protected override void CloneTo (CloneContext clonectx, Expression t)
712                 {
713                         CompoundAssign target = (CompoundAssign) t;
714
715                         target.original_source = original_source.Clone (clonectx);
716                 }
717         }
718 }