2005-09-25 Marek Sieradzki <marek.sieradzki@gmail.com>
[mono.git] / mcs / mbas / assign.cs
1
2 // assign.cs: Assignments.
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Martin Baulig (martin@gnome.org)
7 //   Manjula GHM (mmanjula@novell.com)
8 //   Satya Sudha K (ksathyasudha@novell.com)
9 //
10 // (C) 2001, 2002 Ximian, Inc.
11 //
12 // This program is free software; you can redistribute it and/or
13 // modify it under the terms of the GNU General Public License
14 // as published by the Free Software Foundation; either version 2
15 // of the License, or (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25
26 using System;
27 using System.Collections;
28 using System.Reflection;
29 using System.Reflection.Emit;
30
31 namespace Mono.MonoBASIC {
32
33         /// <summary>
34         ///   This interface is implemented by expressions that can be assigned to.
35         /// </summary>
36         /// <remarks>
37         ///   This interface is implemented by Expressions whose values can not
38         ///   store the result on the top of the stack.
39         ///
40         ///   Expressions implementing this (Properties, Indexers and Arrays) would
41         ///   perform an assignment of the Expression "source" into its final
42         ///   location.
43         ///
44         ///   No values on the top of the stack are expected to be left by
45         ///   invoking this method.
46         /// </remarks>
47         public interface IAssignMethod {
48                 //
49                 // This method will emit the code for the actual assignment
50                 //
51                 void EmitAssign (EmitContext ec, Expression source);
52
53                 //
54                 // This method is invoked before any code generation takes
55                 // place, and it is a mechanism to inform that the expression
56                 // will be invoked more than once, and that the method should
57                 // use temporary values to avoid having side effects
58                 //
59                 // Example: a [ g () ] ++
60                 //
61                 void CacheTemporaries (EmitContext ec);
62         }
63
64         /// <summary>
65         ///   An Expression to hold a temporary value.
66         /// </summary>
67         /// <remarks>
68         ///   The LocalTemporary class is used to hold temporary values of a given
69         ///   type to "simulate" the expression semantics on property and indexer
70         ///   access whose return values are void.
71         ///
72         ///   The local temporary is used to alter the normal flow of code generation
73         ///   basically it creates a local variable, and its emit instruction generates
74         ///   code to access this value, return its address or save its value.
75         /// </remarks>
76         public class LocalTemporary : Expression, IMemoryLocation {
77                 LocalBuilder builder;
78                 
79                 public LocalTemporary (EmitContext ec, Type t)
80                 {
81                         type = t;
82                         eclass = ExprClass.Value;
83                         loc = Location.Null;
84                         builder = ec.GetTemporaryStorage (t);
85                 }
86
87                 public void Release (EmitContext ec)
88                 {
89                         ec.FreeTemporaryStorage (builder);
90                         builder = null;
91                 }
92                 
93                 public LocalTemporary (LocalBuilder b, Type t)
94                 {
95                         type = t;
96                         eclass = ExprClass.Value;
97                         loc = Location.Null;
98                         builder = b;
99                 }
100                 
101                 public override Expression DoResolve (EmitContext ec)
102                 {
103                         return this;
104                 }
105
106                 public override void Emit (EmitContext ec)
107                 {
108                         ec.ig.Emit (OpCodes.Ldloc, builder); 
109                 }
110
111                 public void Store (EmitContext ec)
112                 {
113                         ec.ig.Emit (OpCodes.Stloc, builder);
114                 }
115
116                 public void AddressOf (EmitContext ec, AddressOp mode)
117                 {
118                         ec.ig.Emit (OpCodes.Ldloca, builder);
119                 }
120         }
121
122         /// <summary>
123         ///   The Assign node takes care of assigning the value of source into
124         ///   the expression represented by target. 
125         /// </summary>
126         public class Assign : ExpressionStatement {
127                 protected Expression target, source, real_source;
128                 protected LocalTemporary temp = null, real_temp = null;
129                 protected Assign embedded = null;
130                 protected bool is_embedded = false;
131                 protected bool must_free_temp = false;
132
133                 public Assign (Expression target, Expression source, Location l)
134                 {
135                         source = Parser.SetValueRequiredFlag (source);
136                         target = Parser.SetLeftHandFlag (target);
137                         if (target is MemberAccess)
138                                 ((MemberAccess) target).IsLeftHand = true;
139                         this.target = target;
140                         this.source = this.real_source = source;
141                         this.loc = l;
142                 }
143
144                 protected Assign (Assign embedded, Location l)
145                         : this (embedded.target, embedded.source, l)
146                 {
147                         this.is_embedded = true;
148                 }
149
150                 public Expression Target {
151                         get {
152                                 return target;
153                         }
154
155                         set {
156                                 target = value;
157                         }
158                 }
159
160                 public Expression Source {
161                         get {
162                                 return source;
163                         }
164
165                         set {
166                                 source = value;
167                         }
168                 }
169
170                 public static void error70 (EventInfo ei, Location l)
171                 {
172                         Report.Error (70, l, "The event '" + ei.Name +
173                                       "' can only appear on the left-side of a += or -= (except when" +
174                                       " used from within the type '" + ei.DeclaringType + "')");
175                 }
176
177                 bool IsLateIndexSet(Expression target, EmitContext ec)
178                 {
179                         if (target is Invocation) 
180                         {
181                                 Invocation i = (Invocation) target;
182                                 if (i.Expr is MethodGroupExpr) 
183                                 {
184                                         MethodGroupExpr mg = (MethodGroupExpr) i.Expr;
185                                         if (mg.Name == "LateIndexSet")
186                                                 return true;
187                                 }
188                         }
189                         return false;
190                 }
191
192                 //
193                 // Will return either `this' or an instance of `New'.
194                 //
195                 public override Expression DoResolve (EmitContext ec)
196                 {
197                         // Create an embedded assignment if our source is an assignment.
198                         if (source is Assign)
199                                 source = embedded = new Assign ((Assign) source, loc);
200
201                         real_source = source = source.Resolve (ec);
202                         if (source == null)
203                                 return null;
204                         
205                         if (source is EnumConstant) 
206                                 source = ((EnumConstant)source).WidenToCompilerConstant();
207
208                         //
209                         // This is used in an embedded assignment.
210                         // As an example, consider the statement "A = X = Y = Z".
211                         //
212                         if (is_embedded && !(source is Constant)) {
213                                 // If this is the innermost assignment (the "Y = Z" in our example),
214                                 // create a new temporary local, otherwise inherit that variable
215                                 // from our child (the "X = (Y = Z)" inherits the local from the
216                                 // "Y = Z" assignment).
217                                 if (embedded == null)
218                                         real_temp = temp = new LocalTemporary (ec, source.Type);
219                                 else
220                                         temp = embedded.temp;
221
222                                 // Set the source to the new temporary variable.
223                                 // This means that the following target.ResolveLValue () will tell
224                                 // the target to read it's source value from that variable.
225                                 source = temp;
226                         }
227
228                         // If we have an embedded assignment, use the embedded assignment's temporary
229                         // local variable as source.
230                         if (embedded != null)
231                                 source = (embedded.temp != null) ? embedded.temp : embedded.source;
232                         //To support 'Mid' Assignment Statement
233                         if (target is Invocation) {
234
235                                 Invocation i = (Invocation) target;
236                                 Expression mid_expr;
237                                 mid_expr = i.Expr;
238
239                                 if (mid_expr is SimpleName) {
240                                         SimpleName sn = mid_expr as SimpleName;
241                                         string s = sn.Name;
242                                
243                                         if (s == "Mid" || s == "Mid$") {
244                                         //It is Mid statement. Construct the function and 
245                                         //call corresponding Microsoft.VisualBasic.CompilerServices function 
246                                                 Expression etmp;
247                                                 ArrayList arglist = new ArrayList();
248         
249                                                 Argument arg3,arg4;
250                                                 Expression e = null;
251                                                 eclass = ExprClass.Value;
252         
253                                                 arglist  = i.Arguments;
254                 
255                                                 // If maximum Insert Length value is omitted    
256                                                 if(arglist.Count == 2) {
257                                                         string val = null;
258                                                         val = source.ToString();
259                                                         int maxInsertLength = val.Length;       
260                                                         arg3 = new Argument (new IntLiteral(maxInsertLength), Argument.AType.Expression);
261                                                         arglist.Add (arg3);
262                                                 }
263
264                                                 etmp = Mono.MonoBASIC.Parser.DecomposeQI("Microsoft.VisualBasic.CompilerServices.StringType.MidStmtStr", loc);
265                                                 //Get fourth argument and add it to argument list
266         
267                                                 arg4 = new Argument (source, Argument.AType.Expression);
268                                                 arglist.Add (arg4);
269                                                 e = (Expression) new Invocation (etmp, arglist, loc);
270                                                 e = e.Resolve(ec);
271                                                 return e;
272                                         }
273                                 }
274                         }
275
276                         Expression tmpTarget = target.ResolveLValue (ec, source);
277                         if (tmpTarget == null) {
278                                 // Case of LateBinding.
279                                 // Get the appropriate arguments, add the source as the last argument
280                                 Expression lateBindingExpr = null;
281                                 ArrayList arguments = null;
282                                 if (target is Invocation) {
283                                         lateBindingExpr = ((Invocation) target).Expr;
284                                         arguments = ((Invocation) target).Arguments;
285                                 }
286                                 if (target is MemberAccess) {
287                                         lateBindingExpr = target;
288                                 }
289                                 if (arguments == null)
290                                         arguments = new ArrayList ();
291
292                                 arguments.Add (new Argument (source, Argument.AType.Expression));
293
294                                 if (lateBindingExpr != null) {
295                                         Expression etmp = lateBindingExpr;
296                                         Type exprType = lateBindingExpr.Type;
297                                         // Get the target of the invocation/memberAccess
298                                         if (exprType == null) {
299                                                 if (etmp is Invocation)
300                                                         etmp = ((Invocation) etmp).Expr;
301                                                 if (etmp is MemberAccess)
302                                                         etmp = ((MemberAccess) etmp).Expr;
303                                                 exprType = etmp.Type;
304                                         }
305
306                                         if (exprType == TypeManager.object_type) {
307                                                 StatementSequence tmp = new StatementSequence (ec.CurrentBlock, loc, lateBindingExpr, 
308                                                                                         arguments, false, true);
309                                                 if (!tmp.ResolveArguments (ec))
310                                                         return null;
311                                                 tmp.GenerateLateBindingStatements ();
312                                                 return tmp.Resolve (ec);
313                                         }
314                                 }
315                                 return null;
316                         }
317
318                         target = tmpTarget;
319
320                         Type target_type = target.Type;
321                         Type source_type = real_source.Type;
322                         // If we're an embedded assignment, our parent will reuse our source as its
323                         // source, it won't read from our target.
324                         if (is_embedded)
325                                 type = source_type;
326                         else
327                                 type = target_type;
328                         eclass = ExprClass.Value;
329
330                         //
331                         // If we are doing a property assignment, then
332                         // set the `value' field on the property, and Resolve
333                         // it.
334                         //
335                         if (target is PropertyGroupExpr)
336                                 return target;
337
338                         if (target is IndexerAccess) {
339                                 return this;
340                         }
341
342                         if (target is EventExpr) {
343                                 EventInfo ei = ((EventExpr) target).EventInfo;
344
345                                 Expression ml = MemberLookup (
346                                         ec, ec.ContainerType, ei.Name,
347                                         MemberTypes.Event, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
348
349                                 if (ml == null) {
350                                         //
351                                         // If this is the case, then the Event does not belong 
352                                         // to this Type and so, according to the spec
353                                         // is allowed to only appear on the left hand of
354                                         // the += and -= operators
355                                         //
356                                         // Note that target will not appear as an EventExpr
357                                         // in the case it is being referenced within the same type container;
358                                         // it will appear as a FieldExpr in that case.
359                                         //
360                                         
361                                         if (!(source is Binary)) {
362                                                 error70 (ei, loc);
363                                                 return null;
364                                         } else {
365                                                 Binary tmp = ((Binary) source);
366                                                 if (tmp.Oper != Binary.Operator.Addition &&
367                                                     tmp.Oper != Binary.Operator.Subtraction) {
368                                                         error70 (ei, loc);
369                                                         return null;
370                                                 }
371                                         }
372                                 }
373                         }
374                         
375                         if (source is New || target_type == TypeManager.object_type) {
376                                 if (source_type == TypeManager.object_type) {
377                                         Expression etmp = Mono.MonoBASIC.Parser.DecomposeQI (
378                                                                 "System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue",
379                                                                  Location.Null);
380                                         ArrayList args = new ArrayList ();
381                                         args.Add (new Argument (source, Argument.AType.Expression));
382                                         Expression e = new Invocation (etmp, args, loc);
383                                         source = e.Resolve (ec);
384                                         return this;
385                                 }
386                                 if (target_type.IsValueType) {
387                                         if (source_type != target_type) {
388                                                 source = ConvertImplicitRequired (ec, source, target_type, loc);
389                                                 if (source == null)
390                                                         return null;
391                                         }
392                                         New n = (New) source;
393                                         n.ValueTypeVariable = target;
394                                         return n;
395                                 }
396                         }
397
398                         if (target.eclass != ExprClass.Variable && target.eclass != ExprClass.EventAccess){
399                                 Report.Error (30074, loc,
400                                               "Left hand of an assignment must be a variable, " +
401                                               "a property or an indexer");
402                                 return null;
403                         }
404
405                         if ((source.eclass == ExprClass.Type) && (source is TypeExpr)) {
406                                 source.Error118 ("variable or value");
407                                 return null;
408                         } else if (source is MethodGroupExpr){
409                                 ((MethodGroupExpr) source).ReportUsageError ();
410                                 return null;
411                         }
412
413                         if (target_type == source_type)
414                                 return this;
415                         
416                         //
417                         // If this assignemnt/operator was part of a compound binary
418                         // operator, then we allow an explicit conversion, as detailed
419                         // in the spec. 
420                         //
421
422                         if (this is CompoundAssign){
423                                 CompoundAssign a = (CompoundAssign) this;
424                                 
425                                 Binary b = source as Binary;
426                                 if (b != null && b.IsBuiltinOperator){
427                                         //
428                                         // 1. if the source is explicitly convertible to the
429                                         //    target_type
430                                         //
431                                         
432                                         source = ConvertExplicit (ec, source, target_type, false, loc);
433                                         if (source == null){
434                                                 Error_CannotConvertImplicit (loc, source_type, target_type);
435                                                 return null;
436                                         }
437                                 
438                                         //
439                                         // 2. and the original right side is implicitly convertible to
440                                         // the type of target_type.
441                                         //
442                                         source = ConvertImplicit(ec, source, target_type, loc);
443                                         if (source != null)
444                                                 return this;
445
446                                         Error_CannotConvertImplicit (loc, a.original_source.Type, target_type);
447                                         return null;
448                                 }
449                         }
450
451                         if (IsLateIndexSet(target, ec)) 
452                         {
453                                 // then we must rewrite the whole expression, since 
454                                 // THIS assign is no longer valid/needed
455                                 Invocation i = (Invocation) target;
456                                 return i;
457                         }
458                         source = ConvertImplicitRequired (ec, source, target_type, loc);
459                         if (source == null)
460                                 return null;
461
462                         // If we're an embedded assignment, we need to create a new temporary variable
463                         // for the converted value.  Our parent will use this new variable as its source.
464                         // The same applies when we have an embedded assignment - in this case, we need
465                         // to convert our embedded assignment's temporary local variable to the correct
466                         // type and store it in a new temporary local.
467                         if (is_embedded || embedded != null) {
468                                 type = target_type;
469                                 temp = new LocalTemporary (ec, type);
470                                 must_free_temp = true;
471                         }
472                         
473                         return this;
474                 }
475
476                 Expression EmitEmbedded (EmitContext ec)
477                 {
478                         // Emit an embedded assignment.
479                         
480                         if (real_temp != null) {
481                                 // If we're the innermost assignment, `real_source' is the right-hand
482                                 // expression which gets assigned to all the variables left of it.
483                                 // Emit this expression and store its result in real_temp.
484                                 real_source.Emit (ec);
485                                 real_temp.Store (ec);
486                         }
487
488                         if (embedded != null)
489                                 embedded.EmitEmbedded (ec);
490
491                         // This happens when we've done a type conversion, in this case source will be
492                         // the expression which does the type conversion from real_temp.
493                         // So emit it and store the result in temp; this is the var which will be read
494                         // by our parent.
495                         if (temp != real_temp) {
496                                 source.Emit (ec);
497                                 temp.Store (ec);
498                         }
499
500                         Expression temp_source = (temp != null) ? temp : source;
501                         ((IAssignMethod) target).EmitAssign (ec, temp_source);
502                         return temp_source;
503                 }
504
505                 void ReleaseEmbedded (EmitContext ec)
506                 {
507                         if (embedded != null)
508                                 embedded.ReleaseEmbedded (ec);
509
510                         if (real_temp != null)
511                                 real_temp.Release (ec);
512
513                         if (must_free_temp)
514                                 temp.Release (ec);
515                 }
516
517                 void Emit (EmitContext ec, bool is_statement)
518                 {
519                         if (target is EventExpr) {
520                                 ((EventExpr) target).EmitAddOrRemove (ec, source);
521                                 return;
522                         }
523
524                         //
525                         // FIXME! We need a way to "probe" if the process can
526                         // just use `dup' to propagate the result
527                         // 
528                         IAssignMethod am = (IAssignMethod) target;
529
530                         if (this is CompoundAssign){
531                                 am.CacheTemporaries (ec);
532                         }
533
534                         Expression temp_source;
535                         if (embedded != null) {
536                                 temp_source = embedded.EmitEmbedded (ec);
537
538                                 if (temp != null) {
539                                         source.Emit (ec);
540                                         temp.Store (ec);
541                                         temp_source = temp;
542                                 }
543                         } else
544                                 temp_source = source;
545
546                         if (is_statement)
547                                 am.EmitAssign (ec, temp_source);
548                         else {
549                                 LocalTemporary tempo;
550
551                                 tempo = new LocalTemporary (ec, source.Type);
552
553                                 temp_source.Emit (ec);
554                                 tempo.Store (ec);
555                                 am.EmitAssign (ec, tempo);
556                                 tempo.Emit (ec);
557                                 tempo.Release (ec);
558                         }
559
560                         if (embedded != null) {
561                                 if (temp != null)
562                                         temp.Release (ec);
563                                 embedded.ReleaseEmbedded (ec);
564                         }
565                 }
566                 
567                 public override void Emit (EmitContext ec)
568                 {
569                         Emit (ec, false);
570                 }
571
572                 public override void EmitStatement (EmitContext ec)
573                 {
574                         Emit (ec, true);
575                 }
576         }
577
578         
579         //
580         // This class is used for compound assignments.  
581         //
582         class CompoundAssign : Assign {
583                 Binary.Operator op;
584                 public Expression original_source;
585                 
586                 public CompoundAssign (Binary.Operator op, Expression target, Expression source, Location l)
587                         : base (target, source, l)
588                 {
589                         original_source = source;
590                         this.op = op;
591                 }
592
593                 public Expression ResolveSource (EmitContext ec)
594                 {
595                         return original_source.Resolve (ec);
596                 }
597
598                 public override Expression DoResolve (EmitContext ec)
599                 {
600                         target = target.ResolveLValue (ec, source);
601                         if (target == null)
602                                 return null;
603
604                         original_source = original_source.Resolve (ec);
605                         if (original_source == null)
606                                 return null;
607
608                         //
609                         // Only now we can decouple the original source/target
610                         // into a tree, to guarantee that we do not have side
611                         // effects.
612                         //
613                         source = new Binary (op, target, original_source, loc);
614                         return base.DoResolve (ec);
615                 }
616         }
617 }