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