Await for array access expression
[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 isCompound);
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, type);
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 isCompound)
240                 {
241                         if (isCompound)
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, type);
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, type);
277                         } else {
278                                 ec.Emit (OpCodes.Ldloca, builder, type);
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 Expression Target {
298                         get { return target; }
299                 }
300
301                 public Expression Source {
302                         get {
303                                 return source;
304                         }
305                 }
306
307                 public override bool ContainsEmitWithAwait ()
308                 {
309                         return target.ContainsEmitWithAwait () || source.ContainsEmitWithAwait ();
310                 }
311
312                 public override Expression CreateExpressionTree (ResolveContext ec)
313                 {
314                         ec.Report.Error (832, loc, "An expression tree cannot contain an assignment operator");
315                         return null;
316                 }
317
318                 protected override Expression DoResolve (ResolveContext ec)
319                 {
320                         bool ok = true;
321                         source = source.Resolve (ec);
322                                                 
323                         if (source == null) {
324                                 ok = false;
325                                 source = EmptyExpression.Null;
326                         }
327
328                         target = target.ResolveLValue (ec, source);
329
330                         if (target == null || !ok)
331                                 return null;
332
333                         TypeSpec target_type = target.Type;
334                         TypeSpec source_type = source.Type;
335
336                         eclass = ExprClass.Value;
337                         type = target_type;
338
339                         if (!(target is IAssignMethod)) {
340                                 Error_ValueAssignment (ec, loc);
341                                 return null;
342                         }
343
344                         if (target_type != source_type) {
345                                 Expression resolved = ResolveConversions (ec);
346
347                                 if (resolved != this)
348                                         return resolved;
349                         }
350
351                         return this;
352                 }
353
354 #if NET_4_0
355                 public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx)
356                 {
357                         var tassign = target as IDynamicAssign;
358                         if (tassign == null)
359                                 throw new InternalErrorException (target.GetType () + " does not support dynamic assignment");
360
361                         var target_object = tassign.MakeAssignExpression (ctx, source);
362
363                         //
364                         // Some hacking is needed as DLR does not support void type and requires
365                         // always have object convertible return type to support caching and chaining
366                         //
367                         // We do this by introducing an explicit block which returns RHS value when
368                         // available or null
369                         //
370                         if (target_object.NodeType == System.Linq.Expressions.ExpressionType.Block)
371                                 return target_object;
372
373                         System.Linq.Expressions.UnaryExpression source_object;
374                         if (ctx.HasSet (BuilderContext.Options.CheckedScope)) {
375                                 source_object = System.Linq.Expressions.Expression.ConvertChecked (source.MakeExpression (ctx), target_object.Type);
376                         } else {
377                                 source_object = System.Linq.Expressions.Expression.Convert (source.MakeExpression (ctx), target_object.Type);
378                         }
379
380                         return System.Linq.Expressions.Expression.Assign (target_object, source_object);
381                 }
382 #endif
383                 protected virtual Expression ResolveConversions (ResolveContext ec)
384                 {
385                         source = Convert.ImplicitConversionRequired (ec, source, target.Type, source.Location);
386                         if (source == null)
387                                 return null;
388
389                         return this;
390                 }
391
392                 void Emit (EmitContext ec, bool is_statement)
393                 {
394                         IAssignMethod t = (IAssignMethod) target;
395                         t.EmitAssign (ec, source, !is_statement, this is CompoundAssign);
396                 }
397
398                 public override void Emit (EmitContext ec)
399                 {
400                         Emit (ec, false);
401                 }
402
403                 public override void EmitStatement (EmitContext ec)
404                 {
405                         Emit (ec, true);
406                 }
407
408                 protected override void CloneTo (CloneContext clonectx, Expression t)
409                 {
410                         Assign _target = (Assign) t;
411
412                         _target.target = target.Clone (clonectx);
413                         _target.source = source.Clone (clonectx);
414                 }
415         }
416
417         public class SimpleAssign : Assign
418         {
419                 public SimpleAssign (Expression target, Expression source)
420                         : this (target, source, target.Location)
421                 {
422                 }
423
424                 public SimpleAssign (Expression target, Expression source, Location loc)
425                         : base (target, source, loc)
426                 {
427                 }
428
429                 bool CheckEqualAssign (Expression t)
430                 {
431                         if (source is Assign) {
432                                 Assign a = (Assign) source;
433                                 if (t.Equals (a.Target))
434                                         return true;
435                                 return a is SimpleAssign && ((SimpleAssign) a).CheckEqualAssign (t);
436                         }
437                         return t.Equals (source);
438                 }
439
440                 protected override Expression DoResolve (ResolveContext ec)
441                 {
442                         Expression e = base.DoResolve (ec);
443                         if (e == null || e != this)
444                                 return e;
445
446                         if (CheckEqualAssign (target))
447                                 ec.Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?");
448
449                         return this;
450                 }
451         }
452
453         public class RuntimeExplicitAssign : Assign
454         {
455                 public RuntimeExplicitAssign (Expression target, Expression source)
456                         : base (target, source, target.Location)
457                 {
458                 }
459
460                 protected override Expression ResolveConversions (ResolveContext ec)
461                 {
462                         source = EmptyCast.Create (source, target.Type);
463                         return this;
464                 }
465         }
466
467         //
468         // Compiler generated assign
469         //
470         class CompilerAssign : Assign
471         {
472                 public CompilerAssign (Expression target, Expression source, Location loc)
473                         : base (target, source, loc)
474                 {
475                 }
476
477                 public void UpdateSource (Expression source)
478                 {
479                         base.source = source;
480                 }
481         }
482
483         //
484         // Implements fields and events class initializers
485         //
486         public class FieldInitializer : Assign
487         {
488                 //
489                 // Field initializers are tricky for partial classes. They have to
490                 // share same constructor (block) for expression trees resolve but
491                 // they have they own resolve scope
492                 //
493                 sealed class FieldInitializerContext : ResolveContext
494                 {
495                         ExplicitBlock ctor_block;
496
497                         public FieldInitializerContext (IMemberContext mc, ResolveContext constructorContext)
498                                 : base (mc, Options.FieldInitializerScope | Options.ConstructorScope)
499                         {
500                                 this.ctor_block = constructorContext.CurrentBlock.Explicit;
501                         }
502
503                         public override ExplicitBlock ConstructorBlock {
504                                 get {
505                                         return ctor_block;
506                                 }
507                         }
508                 }
509
510                 //
511                 // Keep resolved value because field initializers have their own rules
512                 //
513                 ExpressionStatement resolved;
514                 IMemberContext mc;
515
516                 public FieldInitializer (FieldSpec spec, Expression expression, IMemberContext mc)
517                         : base (new FieldExpr (spec, expression.Location), expression, expression.Location)
518                 {
519                         this.mc = mc;
520                         if (!spec.IsStatic)
521                                 ((FieldExpr)target).InstanceExpression = new CompilerGeneratedThis (mc.CurrentType, expression.Location);
522                 }
523
524                 protected override Expression DoResolve (ResolveContext ec)
525                 {
526                         // Field initializer can be resolved (fail) many times
527                         if (source == null)
528                                 return null;
529
530                         if (resolved == null) {
531                                 var ctx = new FieldInitializerContext (mc, ec);
532                                 resolved = base.DoResolve (ctx) as ExpressionStatement;
533                         }
534
535                         return resolved;
536                 }
537
538                 public override void EmitStatement (EmitContext ec)
539                 {
540                         if (resolved == null)
541                                 return;
542                         
543                         if (resolved != this)
544                                 resolved.EmitStatement (ec);
545                         else
546                                 base.EmitStatement (ec);
547                 }
548                 
549                 public bool IsDefaultInitializer {
550                         get {
551                                 Constant c = source as Constant;
552                                 if (c == null)
553                                         return false;
554                                 
555                                 FieldExpr fe = (FieldExpr)target;
556                                 return c.IsDefaultInitializer (fe.Type);
557                         }
558                 }
559
560                 public override bool IsSideEffectFree {
561                         get {
562                                 return source.IsSideEffectFree;
563                         }
564                 }
565         }
566
567         //
568         // This class is used for compound assignments.
569         //
570         public class CompoundAssign : Assign
571         {
572                 // This is just a hack implemented for arrays only
573                 public sealed class TargetExpression : Expression
574                 {
575                         readonly Expression child;
576
577                         public TargetExpression (Expression child)
578                         {
579                                 this.child = child;
580                                 this.loc = child.Location;
581                         }
582
583                         public override bool ContainsEmitWithAwait ()
584                         {
585                                 return child.ContainsEmitWithAwait ();
586                         }
587
588                         public override Expression CreateExpressionTree (ResolveContext ec)
589                         {
590                                 throw new NotSupportedException ("ET");
591                         }
592
593                         protected override Expression DoResolve (ResolveContext ec)
594                         {
595                                 type = child.Type;
596                                 eclass = ExprClass.Value;
597                                 return this;
598                         }
599
600                         public override void Emit (EmitContext ec)
601                         {
602                                 child.Emit (ec);
603                         }
604
605                         public override Expression EmitToField (EmitContext ec)
606                         {
607                                 return child.EmitToField (ec);
608                         }
609                 }
610
611                 // Used for underlying binary operator
612                 readonly Binary.Operator op;
613                 Expression right;
614                 Expression left;
615
616                 public CompoundAssign (Binary.Operator op, Expression target, Expression source, Location loc)
617                         : base (target, source, loc)
618                 {
619                         right = source;
620                         this.op = op;
621                 }
622
623                 public CompoundAssign (Binary.Operator op, Expression target, Expression source, Expression left, Location loc)
624                         : this (op, target, source, loc)
625                 {
626                         this.left = left;
627                 }
628
629                 protected override Expression DoResolve (ResolveContext ec)
630                 {
631                         right = right.Resolve (ec);
632                         if (right == null)
633                                 return null;
634
635                         MemberAccess ma = target as MemberAccess;
636                         using (ec.Set (ResolveContext.Options.CompoundAssignmentScope)) {
637                                 target = target.Resolve (ec);
638                         }
639                         
640                         if (target == null)
641                                 return null;
642
643                         if (target is MethodGroupExpr){
644                                 ec.Report.Error (1656, loc,
645                                         "Cannot assign to `{0}' because it is a `{1}'",
646                                         ((MethodGroupExpr)target).Name, target.ExprClassName);
647                                 return null;
648                         }
649
650                         var event_expr = target as EventExpr;
651                         if (event_expr != null) {
652                                 source = Convert.ImplicitConversionRequired (ec, right, target.Type, loc);
653                                 if (source == null)
654                                         return null;
655
656                                 Expression rside;
657                                 if (op == Binary.Operator.Addition)
658                                         rside = EmptyExpression.EventAddition;
659                                 else if (op == Binary.Operator.Subtraction)
660                                         rside = EmptyExpression.EventSubtraction;
661                                 else
662                                         rside = null;
663
664                                 target = target.ResolveLValue (ec, rside);
665                                 if (target == null)
666                                         return null;
667
668                                 eclass = ExprClass.Value;
669                                 type = event_expr.Operator.ReturnType;
670                                 return this;
671                         }
672
673                         //
674                         // Only now we can decouple the original source/target
675                         // into a tree, to guarantee that we do not have side
676                         // effects.
677                         //
678                         if (left == null)
679                                 left = new TargetExpression (target);
680
681                         source = new Binary (op, left, right, true, loc);
682
683                         if (target is DynamicMemberAssignable) {
684                                 Arguments targs = ((DynamicMemberAssignable) target).Arguments;
685                                 source = source.Resolve (ec);
686
687                                 Arguments args = new Arguments (targs.Count + 1);
688                                 args.AddRange (targs);
689                                 args.Add (new Argument (source));
690
691                                 var binder_flags = CSharpBinderFlags.ValueFromCompoundAssignment;
692
693                                 //
694                                 // Compound assignment does target conversion using additional method
695                                 // call, set checked context as the binary operation can overflow
696                                 //
697                                 if (ec.HasSet (ResolveContext.Options.CheckedScope))
698                                         binder_flags |= CSharpBinderFlags.CheckedContext;
699
700                                 if (target is DynamicMemberBinder) {
701                                         source = new DynamicMemberBinder (ma.Name, binder_flags, args, loc).Resolve (ec);
702
703                                         // Handles possible event addition/subtraction
704                                         if (op == Binary.Operator.Addition || op == Binary.Operator.Subtraction) {
705                                                 args = new Arguments (targs.Count + 1);
706                                                 args.AddRange (targs);
707                                                 args.Add (new Argument (right));
708                                                 string method_prefix = op == Binary.Operator.Addition ?
709                                                         Event.AEventAccessor.AddPrefix : Event.AEventAccessor.RemovePrefix;
710
711                                                 var invoke = DynamicInvocation.CreateSpecialNameInvoke (
712                                                         new MemberAccess (right, method_prefix + ma.Name, loc), args, loc).Resolve (ec);
713
714                                                 args = new Arguments (targs.Count);
715                                                 args.AddRange (targs);
716                                                 source = new DynamicEventCompoundAssign (ma.Name, args,
717                                                         (ExpressionStatement) source, (ExpressionStatement) invoke, loc).Resolve (ec);
718                                         }
719                                 } else {
720                                         source = new DynamicIndexBinder (binder_flags, args, loc).Resolve (ec);
721                                 }
722
723                                 return source;
724                         }
725
726                         return base.DoResolve (ec);
727                 }
728
729                 protected override Expression ResolveConversions (ResolveContext ec)
730                 {
731                         //
732                         // LAMESPEC: Under dynamic context no target conversion is happening
733                         // This allows more natual dynamic behaviour but breaks compatibility
734                         // with static binding
735                         //
736                         if (target is RuntimeValueExpression)
737                                 return this;
738
739                         TypeSpec target_type = target.Type;
740
741                         //
742                         // 1. the return type is implicitly convertible to the type of target
743                         //
744                         if (Convert.ImplicitConversionExists (ec, source, target_type)) {
745                                 source = Convert.ImplicitConversion (ec, source, target_type, loc);
746                                 return this;
747                         }
748
749                         //
750                         // Otherwise, if the selected operator is a predefined operator
751                         //
752                         Binary b = source as Binary;
753                         if (b == null && source is ReducedExpression)
754                                 b = ((ReducedExpression) source).OriginalExpression as Binary;
755
756                         if (b != null) {
757                                 //
758                                 // 2a. the operator is a shift operator
759                                 //
760                                 // 2b. the return type is explicitly convertible to the type of x, and
761                                 // y is implicitly convertible to the type of x
762                                 //
763                                 if ((b.Oper & Binary.Operator.ShiftMask) != 0 ||
764                                         Convert.ImplicitConversionExists (ec, right, target_type)) {
765                                         source = Convert.ExplicitConversion (ec, source, target_type, loc);
766                                         return this;
767                                 }
768                         }
769
770                         if (source.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
771                                 Arguments arg = new Arguments (1);
772                                 arg.Add (new Argument (source));
773                                 return new SimpleAssign (target, new DynamicConversion (target_type, CSharpBinderFlags.ConvertExplicit, arg, loc), loc).Resolve (ec);
774                         }
775
776                         right.Error_ValueCannotBeConverted (ec, loc, target_type, false);
777                         return null;
778                 }
779
780                 protected override void CloneTo (CloneContext clonectx, Expression t)
781                 {
782                         CompoundAssign ctarget = (CompoundAssign) t;
783
784                         ctarget.right = ctarget.source = source.Clone (clonectx);
785                         ctarget.target = target.Clone (clonectx);
786                 }
787         }
788 }