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