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