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