Ooops.
[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                         this.target = target;
136                         this.source = this.real_source = source;
137                         this.loc = l;
138                 }
139
140                 protected Assign (Assign embedded, Location l)
141                         : this (embedded.target, embedded.source, l)
142                 {
143                         this.is_embedded = true;
144                 }
145
146                 public Expression Target {
147                         get {
148                                 return target;
149                         }
150
151                         set {
152                                 target = value;
153                         }
154                 }
155
156                 public Expression Source {
157                         get {
158                                 return source;
159                         }
160
161                         set {
162                                 source = value;
163                         }
164                 }
165
166                 public static void error70 (EventInfo ei, Location l)
167                 {
168                         Report.Error (70, l, "The event '" + ei.Name +
169                                       "' can only appear on the left-side of a += or -= (except when" +
170                                       " used from within the type '" + ei.DeclaringType + "')");
171                 }
172
173                 bool IsLateIndexSet(Expression target, EmitContext ec)
174                 {
175                         if (target is Invocation) 
176                         {
177                                 Invocation i = (Invocation) target;
178                                 if (i.Expr is MethodGroupExpr) 
179                                 {
180                                         MethodGroupExpr mg = (MethodGroupExpr) i.Expr;
181                                         if (mg.Name == "LateIndexSet")
182                                                 return true;
183                                 }
184                         }
185                         return false;
186                 }
187
188                 //
189                 // Will return either `this' or an instance of `New'.
190                 //
191                 public override Expression DoResolve (EmitContext ec)
192                 {
193                         // Create an embedded assignment if our source is an assignment.
194                         if (source is Assign)
195                                 source = embedded = new Assign ((Assign) source, loc);
196
197                         real_source = source = source.Resolve (ec);
198                         if (source == null)
199                                 return null;
200                         
201                         if (source is EnumConstant) 
202                                 source = ((EnumConstant)source).WidenToCompilerConstant();
203
204                         //
205                         // This is used in an embedded assignment.
206                         // As an example, consider the statement "A = X = Y = Z".
207                         //
208                         if (is_embedded && !(source is Constant)) {
209                                 // If this is the innermost assignment (the "Y = Z" in our example),
210                                 // create a new temporary local, otherwise inherit that variable
211                                 // from our child (the "X = (Y = Z)" inherits the local from the
212                                 // "Y = Z" assignment).
213                                 if (embedded == null)
214                                         real_temp = temp = new LocalTemporary (ec, source.Type);
215                                 else
216                                         temp = embedded.temp;
217
218                                 // Set the source to the new temporary variable.
219                                 // This means that the following target.ResolveLValue () will tell
220                                 // the target to read it's source value from that variable.
221                                 source = temp;
222                         }
223
224                         // If we have an embedded assignment, use the embedded assignment's temporary
225                         // local variable as source.
226                         if (embedded != null)
227                                 source = (embedded.temp != null) ? embedded.temp : embedded.source;
228                         //To support 'Mid' Assignment Statement
229                         if(target is Invocation) {
230
231                                 Invocation i = (Invocation) target;
232                                 Expression mid_expr;
233                                 mid_expr = i.Expr;
234
235                                 if (mid_expr is SimpleName) {
236                                         SimpleName sn = mid_expr as SimpleName;
237                                         string s = sn.Name;
238                                
239                                         if (s == "Mid" || s == "Mid$") {
240                                         //It is Mid statement. Construct the function and 
241                                         //call corresponding Microsoft.VisualBasic.CompilerServices function 
242                                                 Expression etmp;
243                                                 ArrayList arglist = new ArrayList();
244         
245                                                 Argument arg4;
246                                                 Expression e = null;
247                                                 eclass = ExprClass.Value;
248         
249                                                 arglist  = i.Arguments;
250         
251                                                 etmp = Mono.MonoBASIC.Parser.DecomposeQI("Microsoft.VisualBasic.CompilerServices.StringType.MidStmtStr", loc);
252                                                 //Get fourth argument and add it to argument list
253         
254                                                 arg4 = new Argument (source, Argument.AType.Expression);
255                                                 arglist.Add (arg4);
256                                                 e = (Expression) new Invocation (etmp, arglist, loc);
257                                                 e = e.Resolve(ec);
258                                                 return e;
259                                         }
260                                 }
261         
262                         }
263                         target = target.ResolveLValue (ec, source);
264
265                         if (target == null)
266                                 return null;
267
268                         Type target_type = target.Type;
269                         Type source_type = real_source.Type;
270
271                         // If we're an embedded assignment, our parent will reuse our source as its
272                         // source, it won't read from our target.
273                         if (is_embedded)
274                                 type = source_type;
275                         else
276                                 type = target_type;
277                         eclass = ExprClass.Value;
278
279                         //
280                         // If we are doing a property assignment, then
281                         // set the `value' field on the property, and Resolve
282                         // it.
283                         //
284                         if (target is PropertyExpr){
285                                 PropertyExpr property_assign = (PropertyExpr) target;
286
287                                 if (source_type != target_type){
288                                         source = ConvertImplicitRequired (ec, source, target_type, loc);
289                                         if (source == null)
290                                                 return null;
291                                 }
292
293                                 //
294                                 // FIXME: Maybe handle this in the LValueResolve
295                                 //
296                                 if (!property_assign.VerifyAssignable ())
297                                         return null;
298
299                                 return this;
300                         }
301
302                         if (target is IndexerAccess) {
303                                 return this;
304                         }
305
306                         if (target is EventExpr) {
307                                 EventInfo ei = ((EventExpr) target).EventInfo;
308
309                                 Expression ml = MemberLookup (
310                                         ec, ec.ContainerType, ei.Name,
311                                         MemberTypes.Event, AllBindingFlags | BindingFlags.DeclaredOnly, loc);
312
313                                 if (ml == null) {
314                                         //
315                                         // If this is the case, then the Event does not belong 
316                                         // to this Type and so, according to the spec
317                                         // is allowed to only appear on the left hand of
318                                         // the += and -= operators
319                                         //
320                                         // Note that target will not appear as an EventExpr
321                                         // in the case it is being referenced within the same type container;
322                                         // it will appear as a FieldExpr in that case.
323                                         //
324                                         
325                                         if (!(source is Binary)) {
326                                                 error70 (ei, loc);
327                                                 return null;
328                                         } else {
329                                                 Binary tmp = ((Binary) source);
330                                                 if (tmp.Oper != Binary.Operator.Addition &&
331                                                     tmp.Oper != Binary.Operator.Subtraction) {
332                                                         error70 (ei, loc);
333                                                         return null;
334                                                 }
335                                         }
336                                 }
337                         }
338                         
339                         if (source is New || target_type == TypeManager.object_type) {
340                                 if (source_type == TypeManager.object_type) {
341                                         Expression etmp = Mono.MonoBASIC.Parser.DecomposeQI (
342                                                                 "System.Runtime.CompilerServices.RuntimeHelpers.GetObjectValue",
343                                                                  Location.Null);
344                                         ArrayList args = new ArrayList ();
345                                         args.Add (new Argument (source, Argument.AType.Expression));
346                                         Expression e = new Invocation (etmp, args, loc);
347                                         source = e.Resolve (ec);
348                                         return this;
349                                 }
350                                 if (target_type.IsValueType) {
351                                         New n = (New) source;
352                                         n.ValueTypeVariable = target;
353                                         return n;
354                                 }
355                         }
356
357                         if (target.eclass != ExprClass.Variable && target.eclass != ExprClass.EventAccess){
358                                 Report.Error (30074, loc,
359                                               "Left hand of an assignment must be a variable, " +
360                                               "a property or an indexer");
361                                 return null;
362                         }
363
364                         if ((source.eclass == ExprClass.Type) && (source is TypeExpr)) {
365                                 source.Error118 ("variable or value");
366                                 return null;
367                         } else if (source is MethodGroupExpr){
368                                 ((MethodGroupExpr) source).ReportUsageError ();
369                                 return null;
370                         }
371
372                         if (target_type == source_type)
373                                 return this;
374                         
375                         //
376                         // If this assignemnt/operator was part of a compound binary
377                         // operator, then we allow an explicit conversion, as detailed
378                         // in the spec. 
379                         //
380
381                         if (this is CompoundAssign){
382                                 CompoundAssign a = (CompoundAssign) this;
383                                 
384                                 Binary b = source as Binary;
385                                 if (b != null && b.IsBuiltinOperator){
386                                         //
387                                         // 1. if the source is explicitly convertible to the
388                                         //    target_type
389                                         //
390                                         
391                                         source = ConvertExplicit (ec, source, target_type, false, loc);
392                                         if (source == null){
393                                                 Error_CannotConvertImplicit (loc, source_type, target_type);
394                                                 return null;
395                                         }
396                                 
397                                         //
398                                         // 2. and the original right side is implicitly convertible to
399                                         // the type of target_type.
400                                         //
401                                         source = ConvertImplicit(ec, source, target_type, loc);
402                                         if (source != null)
403                                                 return this;
404
405                                         Error_CannotConvertImplicit (loc, a.original_source.Type, target_type);
406                                         return null;
407                                 }
408                         }
409
410                         if (IsLateIndexSet(target, ec)) 
411                         {
412                                 // then we must rewrite the whole expression, since 
413                                 // THIS assign is no longer valid/needed
414                                 Invocation i = (Invocation) target;
415                                 return i;
416                         }
417
418                         source = ConvertImplicitRequired (ec, source, target_type, loc);
419                         if (source == null)
420                                 return null;
421
422                         // If we're an embedded assignment, we need to create a new temporary variable
423                         // for the converted value.  Our parent will use this new variable as its source.
424                         // The same applies when we have an embedded assignment - in this case, we need
425                         // to convert our embedded assignment's temporary local variable to the correct
426                         // type and store it in a new temporary local.
427                         if (is_embedded || embedded != null) {
428                                 type = target_type;
429                                 temp = new LocalTemporary (ec, type);
430                                 must_free_temp = true;
431                         }
432                         
433                         return this;
434                 }
435
436                 Expression EmitEmbedded (EmitContext ec)
437                 {
438                         // Emit an embedded assignment.
439                         
440                         if (real_temp != null) {
441                                 // If we're the innermost assignment, `real_source' is the right-hand
442                                 // expression which gets assigned to all the variables left of it.
443                                 // Emit this expression and store its result in real_temp.
444                                 real_source.Emit (ec);
445                                 real_temp.Store (ec);
446                         }
447
448                         if (embedded != null)
449                                 embedded.EmitEmbedded (ec);
450
451                         // This happens when we've done a type conversion, in this case source will be
452                         // the expression which does the type conversion from real_temp.
453                         // So emit it and store the result in temp; this is the var which will be read
454                         // by our parent.
455                         if (temp != real_temp) {
456                                 source.Emit (ec);
457                                 temp.Store (ec);
458                         }
459
460                         Expression temp_source = (temp != null) ? temp : source;
461                         ((IAssignMethod) target).EmitAssign (ec, temp_source);
462                         return temp_source;
463                 }
464
465                 void ReleaseEmbedded (EmitContext ec)
466                 {
467                         if (embedded != null)
468                                 embedded.ReleaseEmbedded (ec);
469
470                         if (real_temp != null)
471                                 real_temp.Release (ec);
472
473                         if (must_free_temp)
474                                 temp.Release (ec);
475                 }
476
477                 void Emit (EmitContext ec, bool is_statement)
478                 {
479                         if (target is EventExpr) {
480                                 ((EventExpr) target).EmitAddOrRemove (ec, source);
481                                 return;
482                         }
483
484                         //
485                         // FIXME! We need a way to "probe" if the process can
486                         // just use `dup' to propagate the result
487                         // 
488                         IAssignMethod am = (IAssignMethod) target;
489
490                         if (this is CompoundAssign){
491                                 am.CacheTemporaries (ec);
492                         }
493
494                         Expression temp_source;
495                         if (embedded != null) {
496                                 temp_source = embedded.EmitEmbedded (ec);
497
498                                 if (temp != null) {
499                                         source.Emit (ec);
500                                         temp.Store (ec);
501                                         temp_source = temp;
502                                 }
503                         } else
504                                 temp_source = source;
505
506                         if (is_statement)
507                                 am.EmitAssign (ec, temp_source);
508                         else {
509                                 LocalTemporary tempo;
510
511                                 tempo = new LocalTemporary (ec, source.Type);
512
513                                 temp_source.Emit (ec);
514                                 tempo.Store (ec);
515                                 am.EmitAssign (ec, tempo);
516                                 tempo.Emit (ec);
517                                 tempo.Release (ec);
518                         }
519
520                         if (embedded != null) {
521                                 if (temp != null)
522                                         temp.Release (ec);
523                                 embedded.ReleaseEmbedded (ec);
524                         }
525                 }
526                 
527                 public override void Emit (EmitContext ec)
528                 {
529                         Emit (ec, false);
530                 }
531
532                 public override void EmitStatement (EmitContext ec)
533                 {
534                         Emit (ec, true);
535                 }
536         }
537
538         
539         //
540         // This class is used for compound assignments.  
541         //
542         class CompoundAssign : Assign {
543                 Binary.Operator op;
544                 public Expression original_source;
545                 
546                 public CompoundAssign (Binary.Operator op, Expression target, Expression source, Location l)
547                         : base (target, source, l)
548                 {
549                         original_source = source;
550                         this.op = op;
551                 }
552
553                 public Expression ResolveSource (EmitContext ec)
554                 {
555                         return original_source.Resolve (ec);
556                 }
557
558                 public override Expression DoResolve (EmitContext ec)
559                 {
560                         target = target.ResolveLValue (ec, source);
561                         if (target == null)
562                                 return null;
563
564                         original_source = original_source.Resolve (ec);
565                         if (original_source == null)
566                                 return null;
567
568                         //
569                         // Only now we can decouple the original source/target
570                         // into a tree, to guarantee that we do not have side
571                         // effects.
572                         //
573                         source = new Binary (op, target, original_source, loc);
574                         return base.DoResolve (ec);
575                 }
576         }
577 }