2005-12-27 Atsushi Enomoto <atsushi@ximian.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                                 if (i.Expr is DecoratedIdentifier)
238                                         mid_expr = ((DecoratedIdentifier) i.Expr).Id;
239                                 else
240                                         mid_expr = i.Expr;
241                                         
242
243                                 if (mid_expr is SimpleName) {
244                                         SimpleName sn = mid_expr as SimpleName;
245                                         string s = sn.Name;
246                                
247                                         if (s == "Mid" || s == "Mid$") {
248                                         //It is Mid statement. Construct the function and 
249                                         //call corresponding Microsoft.VisualBasic.CompilerServices function 
250                                                 Expression etmp;
251                                                 ArrayList arglist = new ArrayList();
252         
253                                                 Argument arg3,arg4;
254                                                 Expression e = null;
255                                                 eclass = ExprClass.Value;
256         
257                                                 arglist  = i.Arguments;
258                 
259                                                 // If maximum Insert Length value is omitted    
260                                                 if(arglist.Count == 2) {
261                                                         string val = null;
262                                                         val = source.ToString();
263                                                         int maxInsertLength = val.Length;       
264                                                         arg3 = new Argument (new IntLiteral(maxInsertLength), Argument.AType.Expression);
265                                                         arglist.Add (arg3);
266                                                 }
267
268                                                 etmp = Mono.MonoBASIC.Parser.DecomposeQI("Microsoft.VisualBasic.CompilerServices.StringType.MidStmtStr", loc);
269                                                 //Get fourth argument and add it to argument list
270         
271                                                 arg4 = new Argument (source, Argument.AType.Expression);
272                                                 arglist.Add (arg4);
273                                                 e = (Expression) new Invocation (etmp, arglist, loc);
274                                                 e = e.Resolve(ec);
275                                                 return e;
276                                         }
277                                 }
278                         }
279
280                         Expression tmpTarget = target.ResolveLValue (ec, source);
281                         if (tmpTarget == null) {
282                                 // Case of LateBinding.
283                                 // Get the appropriate arguments, add the source as the last argument
284                                 Expression lateBindingExpr = null;
285                                 ArrayList arguments = null;
286                                 if (target is Invocation) {
287                                         lateBindingExpr = ((Invocation) target).Expr;
288                                         arguments = ((Invocation) target).Arguments;
289                                 }
290                                 if (target is MemberAccess) {
291                                         lateBindingExpr = target;
292                                 }
293                                 if (arguments == null)
294                                         arguments = new ArrayList ();
295
296                                 arguments.Add (new Argument (source, Argument.AType.Expression));
297
298                                 if (lateBindingExpr != null) {
299                                         Expression etmp = lateBindingExpr;
300                                         Type exprType = lateBindingExpr.Type;
301                                         // Get the target of the invocation/memberAccess
302                                         if (exprType == null) {
303                                                 if (etmp is Invocation)
304                                                         etmp = ((Invocation) etmp).Expr;
305                                                 if (etmp is MemberAccess)
306                                                         etmp = ((MemberAccess) etmp).Expr;
307                                                 exprType = etmp.Type;
308                                         }
309
310                                         if (exprType == TypeManager.object_type) {
311                                                 StatementSequence tmp = new StatementSequence (ec.CurrentBlock, loc, lateBindingExpr, 
312                                                                                         arguments, false, true);
313                                                 if (!tmp.ResolveArguments (ec))
314                                                         return null;
315                                                 tmp.GenerateLateBindingStatements ();
316                                                 return tmp.Resolve (ec);
317                                         }
318                                 }
319                                 return null;
320                         }
321
322                         target = tmpTarget;
323
324                         Type target_type = target.Type;
325                         Type source_type = real_source.Type;
326                         // If we're an embedded assignment, our parent will reuse our source as its
327                         // source, it won't read from our target.
328                         if (is_embedded)
329                                 type = source_type;
330                         else
331                                 type = target_type;
332                         eclass = ExprClass.Value;
333
334                         //
335                         // If we are doing a property assignment, then
336                         // set the `value' field on the property, and Resolve
337                         // it.
338                         //
339                         if (target is PropertyGroupExpr)
340                                 return target;
341
342                         if (target is IndexerAccess) {
343                                 return this;
344                         }
345
346                         if (target is EventExpr) {
347                                 EventInfo ei = ((EventExpr) target).EventInfo;
348
349                                 Expression ml = MemberLookup (
350                                         ec, ec.ContainerType, ei.Name,
351                                         MemberTypes.Event, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
352
353                                 if (ml == null) {
354                                         //
355                                         // If this is the case, then the Event does not belong 
356                                         // to this Type and so, according to the spec
357                                         // is allowed to only appear on the left hand of
358                                         // the += and -= operators
359                                         //
360                                         // Note that target will not appear as an EventExpr
361                                         // in the case it is being referenced within the same type container;
362                                         // it will appear as a FieldExpr in that case.
363                                         //
364                                         
365                                         if (!(source is Binary)) {
366                                                 error70 (ei, loc);
367                                                 return null;
368                                         } else {
369                                                 Binary tmp = ((Binary) source);
370                                                 if (tmp.Oper != Binary.Operator.Addition &&
371                                                     tmp.Oper != Binary.Operator.Subtraction) {
372                                                         error70 (ei, loc);
373                                                         return null;
374                                                 }
375                                         }
376                                 }
377                         }
378                         
379                         if (source is New || target_type == TypeManager.object_type) {
380                                 if (source_type == TypeManager.object_type) {
381                                         Expression etmp = Mono.MonoBASIC.Parser.DecomposeQI (
382                                                                 "System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue",
383                                                                  Location.Null);
384                                         ArrayList args = new ArrayList ();
385                                         args.Add (new Argument (source, Argument.AType.Expression));
386                                         Expression e = new Invocation (etmp, args, loc);
387                                         source = e.Resolve (ec);
388                                         return this;
389                                 }
390                                 if (target_type.IsValueType) {
391                                         if (source_type != target_type) {
392                                                 source = ConvertImplicitRequired (ec, source, target_type, loc);
393                                                 if (source == null)
394                                                         return null;
395                                         }
396                                         New n = (New) source;
397                                         n.ValueTypeVariable = target;
398                                         return n;
399                                 }
400                         }
401
402                         if (target.eclass != ExprClass.Variable && target.eclass != ExprClass.EventAccess){
403                                 Report.Error (30074, loc,
404                                               "Left hand of an assignment must be a variable, " +
405                                               "a property or an indexer");
406                                 return null;
407                         }
408
409                         if ((source.eclass == ExprClass.Type) && (source is TypeExpr)) {
410                                 source.Error118 ("variable or value");
411                                 return null;
412                         } else if (source is MethodGroupExpr){
413                                 ((MethodGroupExpr) source).ReportUsageError ();
414                                 return null;
415                         }
416
417                         if (target_type == source_type)
418                                 return this;
419                         
420                         //
421                         // If this assignemnt/operator was part of a compound binary
422                         // operator, then we allow an explicit conversion, as detailed
423                         // in the spec. 
424                         //
425
426                         if (this is CompoundAssign){
427                                 CompoundAssign a = (CompoundAssign) this;
428                                 
429                                 Binary b = source as Binary;
430                                 if (b != null && b.IsBuiltinOperator){
431                                         //
432                                         // 1. if the source is explicitly convertible to the
433                                         //    target_type
434                                         //
435                                         
436                                         source = ConvertExplicit (ec, source, target_type, false, loc);
437                                         if (source == null){
438                                                 Error_CannotConvertImplicit (loc, source_type, target_type);
439                                                 return null;
440                                         }
441                                 
442                                         //
443                                         // 2. and the original right side is implicitly convertible to
444                                         // the type of target_type.
445                                         //
446                                         source = ConvertImplicit(ec, source, target_type, loc);
447                                         if (source != null)
448                                                 return this;
449
450                                         Error_CannotConvertImplicit (loc, a.original_source.Type, target_type);
451                                         return null;
452                                 }
453                         }
454
455                         if (IsLateIndexSet(target, ec)) 
456                         {
457                                 // then we must rewrite the whole expression, since 
458                                 // THIS assign is no longer valid/needed
459                                 Invocation i = (Invocation) target;
460                                 return i;
461                         }
462                         source = ConvertImplicitRequired (ec, source, target_type, loc);
463                         if (source == null)
464                                 return null;
465
466                         // If we're an embedded assignment, we need to create a new temporary variable
467                         // for the converted value.  Our parent will use this new variable as its source.
468                         // The same applies when we have an embedded assignment - in this case, we need
469                         // to convert our embedded assignment's temporary local variable to the correct
470                         // type and store it in a new temporary local.
471                         if (is_embedded || embedded != null) {
472                                 type = target_type;
473                                 temp = new LocalTemporary (ec, type);
474                                 must_free_temp = true;
475                         }
476                         
477                         return this;
478                 }
479
480                 Expression EmitEmbedded (EmitContext ec)
481                 {
482                         // Emit an embedded assignment.
483                         
484                         if (real_temp != null) {
485                                 // If we're the innermost assignment, `real_source' is the right-hand
486                                 // expression which gets assigned to all the variables left of it.
487                                 // Emit this expression and store its result in real_temp.
488                                 real_source.Emit (ec);
489                                 real_temp.Store (ec);
490                         }
491
492                         if (embedded != null)
493                                 embedded.EmitEmbedded (ec);
494
495                         // This happens when we've done a type conversion, in this case source will be
496                         // the expression which does the type conversion from real_temp.
497                         // So emit it and store the result in temp; this is the var which will be read
498                         // by our parent.
499                         if (temp != real_temp) {
500                                 source.Emit (ec);
501                                 temp.Store (ec);
502                         }
503
504                         Expression temp_source = (temp != null) ? temp : source;
505                         ((IAssignMethod) target).EmitAssign (ec, temp_source);
506                         return temp_source;
507                 }
508
509                 void ReleaseEmbedded (EmitContext ec)
510                 {
511                         if (embedded != null)
512                                 embedded.ReleaseEmbedded (ec);
513
514                         if (real_temp != null)
515                                 real_temp.Release (ec);
516
517                         if (must_free_temp)
518                                 temp.Release (ec);
519                 }
520
521                 void Emit (EmitContext ec, bool is_statement)
522                 {
523                         if (target is EventExpr) {
524                                 ((EventExpr) target).EmitAddOrRemove (ec, source);
525                                 return;
526                         }
527
528                         //
529                         // FIXME! We need a way to "probe" if the process can
530                         // just use `dup' to propagate the result
531                         // 
532                         IAssignMethod am = (IAssignMethod) target;
533
534                         if (this is CompoundAssign){
535                                 am.CacheTemporaries (ec);
536                         }
537
538                         Expression temp_source;
539                         if (embedded != null) {
540                                 temp_source = embedded.EmitEmbedded (ec);
541
542                                 if (temp != null) {
543                                         source.Emit (ec);
544                                         temp.Store (ec);
545                                         temp_source = temp;
546                                 }
547                         } else
548                                 temp_source = source;
549
550                         if (is_statement)
551                                 am.EmitAssign (ec, temp_source);
552                         else {
553                                 LocalTemporary tempo;
554
555                                 tempo = new LocalTemporary (ec, source.Type);
556
557                                 temp_source.Emit (ec);
558                                 tempo.Store (ec);
559                                 am.EmitAssign (ec, tempo);
560                                 tempo.Emit (ec);
561                                 tempo.Release (ec);
562                         }
563
564                         if (embedded != null) {
565                                 if (temp != null)
566                                         temp.Release (ec);
567                                 embedded.ReleaseEmbedded (ec);
568                         }
569                 }
570                 
571                 public override void Emit (EmitContext ec)
572                 {
573                         Emit (ec, false);
574                 }
575
576                 public override void EmitStatement (EmitContext ec)
577                 {
578                         Emit (ec, true);
579                 }
580         }
581
582         
583         //
584         // This class is used for compound assignments.  
585         //
586         class CompoundAssign : Assign {
587                 Binary.Operator op;
588                 public Expression original_source;
589                 
590                 public CompoundAssign (Binary.Operator op, Expression target, Expression source, Location l)
591                         : base (target, source, l)
592                 {
593                         original_source = source;
594                         this.op = op;
595                 }
596
597                 public Expression ResolveSource (EmitContext ec)
598                 {
599                         return original_source.Resolve (ec);
600                 }
601
602                 public override Expression DoResolve (EmitContext ec)
603                 {
604                         target = target.ResolveLValue (ec, source);
605                         if (target == null)
606                                 return null;
607
608                         original_source = original_source.Resolve (ec);
609                         if (original_source == null)
610                                 return null;
611
612                         //
613                         // Only now we can decouple the original source/target
614                         // into a tree, to guarantee that we do not have side
615                         // effects.
616                         //
617                         source = new Binary (op, target, original_source, loc);
618                         return base.DoResolve (ec);
619                 }
620         }
621 }