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