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