2005-07-13 Rafael Teixeira <rafaelteixeirabr@hotmail.com>
[mono.git] / mcs / mbas / block.cs
1 //
2 // block.cs: Block representation for the IL tree.
3 //
4 // Author:
5 //   Rafael Teixeira (rafaelteixeirabr@hotmail.com)
6 //   Miguel de Icaza (miguel@ximian.com)
7 //   Martin Baulig (martin@gnome.org)
8 //       Anirban Bhattacharjee (banirban@novell.com)
9 //   Manjula GHM (mmanjula@novell.com)
10 //   Satya Sudha K (ksathyasudha@novell.com)
11 //
12 // (C) 2001, 2002, 2003, 2004, 2005 Ximian, Inc.
13 //
14
15 using System;
16 using System.Text;
17 using System.Reflection;
18 using System.Reflection.Emit;
19 using System.Diagnostics;
20
21 namespace Mono.MonoBASIC {
22
23         using System.Collections;
24
25         /// <summary>
26         ///   Block represents a VB.NET block.
27         /// </summary>
28         ///
29         /// <remarks>
30         ///   This class is used in a number of places: either to represent
31         ///   explicit blocks that the programmer places or implicit blocks.
32         ///
33         ///   Implicit blocks are used as labels or to introduce variable
34         ///   declarations.
35         /// </remarks>
36         public class Block : Statement {
37                 public readonly Block     Parent;
38                 public readonly bool      Implicit;
39                 public readonly Location  StartLocation;
40                 public Location    EndLocation;
41
42                 //
43                 // The statements in this block
44                 //
45                 public ArrayList statements;
46
47                 //
48                 // An array of Blocks.  We keep track of children just
49                 // to generate the local variable declarations.
50                 //
51                 // Statements and child statements are handled through the
52                 // statements.
53                 //
54                 ArrayList children;
55                 
56                 //
57                 // Labels.  (label, block) pairs.
58                 //
59                 CaseInsensitiveHashtable labels;
60
61                 //
62                 // Keeps track of (name, type) pairs
63                 //
64                 CaseInsensitiveHashtable variables;
65
66                 //
67                 // Keeps track of constants
68                 CaseInsensitiveHashtable constants;
69
70                 //
71                 // Maps variable names to ILGenerator.LocalBuilders
72                 //
73                 //CaseInsensitiveHashtable local_builders;
74
75                 // to hold names of variables required for late binding
76                 public const string lateBindingArgs = "1_LBArgs";
77                 public const string lateBindingArgNames = "1_LBArgsNames";
78                 public const string lateBindingCopyBack = "1_LBCopyBack";
79
80                 bool isLateBindingRequired = false;
81
82                 bool used = false;
83
84                 static int id;
85
86                 int this_id;
87                 
88                 public Block (Block parent)
89                         : this (parent, false, Location.Null, Location.Null)
90                 { }
91
92                 public Block (Block parent, bool implicit_block)
93                         : this (parent, implicit_block, Location.Null, Location.Null)
94                 { }
95
96                 public Block (Block parent, bool implicit_block, Parameters parameters)
97                         : this (parent, implicit_block, parameters, Location.Null, Location.Null)
98                 { }
99
100                 public Block (Block parent, Location start, Location end)
101                         : this (parent, false, start, end)
102                 { }
103
104                 public Block (Block parent, Parameters parameters, Location start, Location end)
105                         : this (parent, false, parameters, start, end)
106                 { }
107
108                 public Block (Block parent, bool implicit_block, Location start, Location end)
109                         : this (parent, implicit_block, Parameters.EmptyReadOnlyParameters,
110                                 start, end)
111                 { }
112
113                 public Block (Block parent, bool implicit_block, Parameters parameters,
114                               Location start, Location end)
115                 {
116                         if (parent != null)
117                                 parent.AddChild (this);
118                         else {
119                                 // Top block
120                                 // Add variables that may be required for late binding
121                                 variables = new CaseInsensitiveHashtable ();
122                                 ArrayList rank_specifier = new ArrayList ();
123                                 ArrayList element = new ArrayList ();
124                                 element.Add (new EmptyExpression ());
125                                 rank_specifier.Add (element);
126                                 Expression e = Mono.MonoBASIC.Parser.DecomposeQI ("System.Object[]", start);
127                                 AddVariable (e, Block.lateBindingArgs, null, start);
128                                 e = Mono.MonoBASIC.Parser.DecomposeQI ("System.String[]", start);
129                                 AddVariable (e, Block.lateBindingArgNames, null, start);
130                                 e = Mono.MonoBASIC.Parser.DecomposeQI ("System.Boolean[]", start);
131                                 AddVariable (e, Block.lateBindingCopyBack, null, start);
132                         }
133                         
134                         this.Parent = parent;
135                         this.Implicit = implicit_block;
136                         this.parameters = parameters;
137                         this.StartLocation = start;
138                         this.EndLocation = end;
139                         this.loc = start;
140                         this_id = id++;
141                         statements = new ArrayList ();
142                 }
143
144                 public bool IsLateBindingRequired {
145                         get {
146                                 return isLateBindingRequired;
147                         }
148                         set {
149                                 isLateBindingRequired = value;
150                         }
151                 }
152
153                 public int ID {
154                         get {
155                                 return this_id;
156                         }
157                 }
158
159                 void AddChild (Block b)
160                 {
161                         if (children == null)
162                                 children = new ArrayList ();
163                         
164                         children.Add (b);
165                 }
166
167                 public void SetEndLocation (Location loc)
168                 {
169                         EndLocation = loc;
170                 }
171
172                 /// <summary>
173                 ///   Adds a label to the current block. 
174                 /// </summary>
175                 ///
176                 /// <returns>
177                 ///   false if the name already exists in this block. true
178                 ///   otherwise.
179                 /// </returns>
180                 ///
181 /**
182                 public bool AddLabel (string name, LabeledStatement target)
183                 {
184                         if (labels == null)
185                                 labels = new CaseInsensitiveHashtable ();
186                         if (labels.Contains (name))
187                                 return false;
188                         
189                         labels.Add (name, target);
190                         return true;
191                 }
192 **/
193
194
195                  public bool AddLabel (string name, LabeledStatement target, Location loc)
196                 {
197 /**
198                         if (switch_block != null)
199                                 return switch_block.AddLabel (name, target, loc);
200 **/
201                         Block cur = this;
202                         while (cur != null) {
203
204                                 if (cur.DoLookupLabel (name) != null) {
205                                         Report.Error (
206                                                 140, loc, "The label '" + name +"' is a duplicate");
207                                         return false;
208                                 }
209
210                                 if (!Implicit)
211                                         break;
212
213                                 cur = cur.Parent;
214                         }
215
216                          while (cur != null) {
217                                 if (cur.DoLookupLabel (name) != null) {
218                                         Report.Error (
219                                                 158, loc,
220                                                 "The label '"+ name +"' shadows another label " +
221                                                 "by the same name in a containing scope.");
222                                         return false;
223                                 }
224
225                                 if (children != null) {
226                                         foreach (Block b in children) {
227                                                 LabeledStatement s = b.DoLookupLabel (name);
228                                                 if (s == null)
229                                                         continue;
230                                                 Report.Error (
231                                                         158, s.Location,
232                                                         "The label '"+ name +"' shadows another " +
233                                                         "label by the same name in a " +
234                                                         "containing scope.");
235                                                 return false;
236                                         }
237                                 }
238                                 cur = cur.Parent;
239                         }
240                          if (labels == null)
241                                 labels = new CaseInsensitiveHashtable ();
242                         if (labels.Contains (name))
243                                 return false;
244
245                         labels.Add (name, target);
246                         return true;
247
248                 }
249                 
250                 public LabeledStatement LookupLabel (string name)
251                 {
252                         LabeledStatement s = DoLookupLabel (name);
253                         if (s != null) 
254                                 return s;
255                         
256                         if (children == null)
257                                 return null;
258
259                         foreach (Block child in children) {
260                                 if (!child.Implicit)
261                                         continue;
262
263                                 s = child.LookupLabel (name);
264                                 if (s != null) 
265                                         return s;
266                         }
267
268                         return null;
269                 }
270
271                 public LabeledStatement DoLookupLabel (string name)
272                 {
273                         if (labels != null){
274                                 if (labels.Contains (name))
275                                         return ((LabeledStatement) labels [name]);
276                         }
277 /**
278                         if (Parent != null)
279                                 return Parent.LookupLabel (name);
280 **/
281                         return null;
282                 }
283
284                 VariableInfo this_variable = null;
285
286                 // <summary>
287                 //   Returns the "this" instance variable of this block.
288                 //   See AddThisVariable() for more information.
289                 // </summary>
290                 public VariableInfo ThisVariable {
291                         get {
292                                 if (this_variable != null)
293                                         return this_variable;
294                                 else if (Parent != null)
295                                         return Parent.ThisVariable;
296                                 else
297                                         return null;
298                         }
299                 }
300
301                 Hashtable child_variable_names;
302
303                 // <summary>
304                 //   Marks a variable with name @name as being used in a child block.
305                 //   If a variable name has been used in a child block, it's illegal to
306                 //   declare a variable with the same name in the current block.
307                 // </summary>
308                 public void AddChildVariableName (string name)
309                 {
310                         if (child_variable_names == null)
311                                 child_variable_names = new CaseInsensitiveHashtable ();
312
313                         if (!child_variable_names.Contains (name))
314                                 child_variable_names.Add (name, true);
315                 }
316
317                 // <summary>
318                 //   Marks all variables from block @block and all its children as being
319                 //   used in a child block.
320                 // </summary>
321                 public void AddChildVariableNames (Block block)
322                 {
323                         if (block.Variables != null) {
324                                 foreach (string name in block.Variables.Keys)
325                                         AddChildVariableName (name);
326                         }
327
328                         foreach (Block child in block.children) {
329                                 if (child.Variables != null) {
330                                         foreach (string name in child.Variables.Keys)
331                                                 AddChildVariableName (name);
332                                 }
333                         }
334                 }
335
336                 // <summary>
337                 //   Checks whether a variable name has already been used in a child block.
338                 // </summary>
339                 public bool IsVariableNameUsedInChildBlock (string name)
340                 {
341                         if (child_variable_names == null)
342                                 return false;
343
344                         return child_variable_names.Contains (name);
345                 }
346
347                 // <summary>
348                 //   This is used by non-static 'struct' constructors which do not have an
349                 //   initializer - in this case, the constructor must initialize all of the
350                 //   struct's fields.  To do this, we add a "this" variable and use the flow
351                 //   analysis code to ensure that it's been fully initialized before control
352                 //   leaves the constructor.
353                 // </summary>
354                 public VariableInfo AddThisVariable (TypeContainer tc, Location l)
355                 {
356                         if (this_variable != null)
357                                 return this_variable;
358
359                         this_variable = new VariableInfo (tc, ID, l);
360
361                         if (variables == null)
362                                 variables = new CaseInsensitiveHashtable ();
363                         variables.Add ("this", this_variable);
364
365                         return this_variable;
366                 }
367
368                 public VariableInfo AddVariable (EmitContext ec, Expression type, string name, Location l)
369                 {
370                         if (!variables_initialized)
371                                 throw new InvalidOperationException();
372                                 
373                         VariableInfo vi = AddVariable(type, name, null, loc);
374
375                         int priorCount = count_variables;
376                         DeclSpace ds = ec.DeclSpace;
377
378                         if (!vi.Resolve (ds)) {
379                                 vi.Number = -1;
380                         } else {
381                                 vi.Number = ++count_variables;
382                                 if (vi.StructInfo != null)
383                                         count_variables += vi.StructInfo.Count;
384                         }
385                         if (priorCount < count_variables)
386                                 ec.CurrentBranching.CurrentUsageVector.AddExtraLocals(count_variables - priorCount);
387                                 
388                         return vi;
389                 }
390                 
391                 public VariableInfo AddVariable (Expression type, string name, Parameters pars, Location l)
392                 {
393                         if (variables == null)
394                                 variables = new CaseInsensitiveHashtable ();
395
396                         VariableInfo vi = GetVariableInfo (name);
397                         if (vi != null) {
398                                 if (vi.Block != ID)
399                                         Report.Error (30616, l, "A local variable named '" + name + "' " +
400                                                       "cannot be declared in this scope since it would " +
401                                                       "give a different meaning to '" + name + "', which " +
402                                                       "is already used in a 'parent or current' scope to " +
403                                                       "denote something else");
404                                 else
405                                         Report.Error (30290, l, "A local variable '" + name + "' is already " +
406                                                       "defined in this scope");
407                                 return null;
408                         }
409
410                         if (IsVariableNameUsedInChildBlock (name)) {
411                                 Report.Error (136, l, "A local variable named '" + name + "' " +
412                                               "cannot be declared in this scope since it would " +
413                                               "give a different meaning to '" + name + "', which " +
414                                               "is already used in a 'child' scope to denote something " +
415                                               "else");
416                                 return null;
417                         }
418
419                         if (pars != null) {
420                                 int idx = 0;
421                                 Parameter p = pars.GetParameterByName (name, out idx);
422                                 if (p != null) {
423                                         Report.Error (30616, l, "A local variable named '" + name + "' " +
424                                                       "cannot be declared in this scope since it would " +
425                                                       "give a different meaning to '" + name + "', which " +
426                                                       "is already used in a 'parent or current' scope to " +
427                                                       "denote something else");
428                                         return null;
429                                 }
430                         }
431                         
432                         vi = new VariableInfo (type, name, ID, l);
433
434                         variables.Add (name, vi);
435
436                         return vi;
437                 }
438
439                 public bool AddConstant (Expression type, string name, Expression value, Parameters pars, Location l)
440                 {
441                         if (AddVariable (type, name, pars, l) == null)
442                                 return false;
443                         
444                         if (constants == null)
445                                 constants = new CaseInsensitiveHashtable ();
446
447                         constants.Add (name, value);
448                         return true;
449                 }
450
451                 public Hashtable Variables {
452                         get {
453                                 return variables;
454                         }
455                 }
456
457                 public VariableInfo GetVariableInfo (string name)
458                 {
459                         if (variables != null) {
460                                 object temp;
461                                 temp = variables [name];
462
463                                 if (temp != null){
464                                         return (VariableInfo) temp;
465                                 }
466                         }
467
468                         if (Parent != null)
469                                 return Parent.GetVariableInfo (name);
470
471                         return null;
472                 }
473                 
474                 public Expression GetVariableType (string name)
475                 {
476                         VariableInfo vi = GetVariableInfo (name);
477
478                         if (vi != null)
479                                 return vi.Type;
480
481                         return null;
482                 }
483
484                 public Expression GetConstantExpression (string name)
485                 {
486                         if (constants != null) {
487                                 object temp;
488                                 temp = constants [name];
489                                 
490                                 if (temp != null)
491                                         return (Expression) temp;
492                         }
493                         
494                         if (Parent != null)
495                                 return Parent.GetConstantExpression (name);
496
497                         return null;
498                 }
499                 
500                 /// <summary>
501                 ///   True if the variable named @name has been defined
502                 ///   in this block
503                 /// </summary>
504                 public bool IsVariableDefined (string name)
505                 {
506                         // Console.WriteLine ("Looking up {0} in {1}", name, ID);
507                         if (variables != null) {
508                                 if (variables.Contains (name))
509                                         return true;
510                         }
511                         
512                         if (Parent != null)
513                                 return Parent.IsVariableDefined (name);
514
515                         return false;
516                 }
517
518                 /// <summary>
519                 ///   True if the variable named @name is a constant
520                 ///  </summary>
521                 public bool IsConstant (string name)
522                 {
523                         Expression e = null;
524                         
525                         e = GetConstantExpression (name);
526                         
527                         return e != null;
528                 }
529                 
530                 /// <summary>
531                 ///   Use to fetch the statement associated with this label
532                 /// </summary>
533                 public Statement this [string name] {
534                         get {
535                                 return (Statement) labels [name];
536                         }
537                 }
538
539                 Parameters parameters = null;
540                 public Parameters Parameters {
541                         get {
542                                 if (Parent != null)
543                                         return Parent.Parameters;
544
545                                 return parameters;
546                         }
547                 }
548
549                 /// <returns>
550                 ///   A list of labels that were not used within this block
551                 /// </returns>
552                 public string [] GetUnreferenced ()
553                 {
554                         // FIXME: Implement me
555                         return null;
556                 }
557
558                 public void AddStatement (Statement s)
559                 {
560                         statements.Add (s);
561                         used = true;
562                 }
563
564                 public bool Used {
565                         get {
566                                 return used;
567                         }
568                 }
569
570                 public void Use ()
571                 {
572                         used = true;
573                 }
574
575                 bool variables_initialized = false;
576                 int count_variables = 0, first_variable = 0;
577
578                 void UpdateVariableInfo (EmitContext ec)
579                 {
580                         DeclSpace ds = ec.DeclSpace;
581
582                         first_variable = 0;
583
584                         if (Parent != null)
585                                 first_variable += Parent.CountVariables;
586
587                         count_variables = first_variable;
588                         if (variables != null) {
589                                 foreach (VariableInfo vi in variables.Values) {
590                                         if (!vi.Resolve (ds)) {
591                                                 vi.Number = -1;
592                                                 continue;
593                                         }
594
595                                         vi.Number = ++count_variables;
596
597                                         if (vi.StructInfo != null)
598                                                 count_variables += vi.StructInfo.Count;
599                                 }
600                         }
601
602                         variables_initialized = true;
603                 }
604
605                 //
606                 // <returns>
607                 //   The number of local variables in this block
608                 // </returns>
609                 public int CountVariables
610                 {
611                         get {
612                                 if (!variables_initialized)
613                                         throw new Exception ();
614
615                                 return count_variables;
616                         }
617                 }
618
619                 /// <summary>
620                 ///   Emits the variable declarations and labels.
621                 /// </summary>
622                 /// <remarks>
623                 ///   tc: is our typecontainer (to resolve type references)
624                 ///   ig: is the code generator:
625                 ///   toplevel: the toplevel block.  This is used for checking 
626                 ///             that no two labels with the same name are used.
627                 /// </remarks>
628                 public void EmitMeta (EmitContext ec, Block toplevel)
629                 {
630                         //DeclSpace ds = ec.DeclSpace;
631                         ILGenerator ig = ec.ig;
632
633                         if (!variables_initialized)
634                                 UpdateVariableInfo (ec);
635
636                         //
637                         // Process this block variables
638                         //
639                         if (variables != null){
640                                 //local_builders = new CaseInsensitiveHashtable ();
641                                 
642                                 foreach (DictionaryEntry de in variables){
643                                         string name = (string) de.Key;
644                                         /*
645                                         if (!isLateBindingRequired) {
646                                                 if (name.Equals (Block.lateBindingArgs) || 
647                                                     name.Equals (Block.lateBindingArgNames) ||
648                                                     name.Equals (Block.lateBindingCopyBack))
649                                                         continue;
650                                         }
651                                         */
652                                         VariableInfo vi = (VariableInfo) de.Value;
653
654                                         if (vi.VariableType == null)
655                                                 continue;
656
657                                         vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
658
659                                         if (CodeGen.SymbolWriter != null)
660                                                 vi.LocalBuilder.SetLocalSymInfo (name);
661
662                                         if (constants == null)
663                                                 continue;
664
665                                         Expression cv = (Expression) constants [name];
666                                         if (cv == null)
667                                                 continue;
668
669                                         Expression e = cv.Resolve (ec);
670                                         if (e == null)
671                                                 continue;
672
673                                         if (!(e is Constant)){
674                                                 Report.Error (133, vi.Location,
675                                                               "The expression being assigned to '" +
676                                                               name + "' must be constant (" + e + ")");
677                                                 continue;
678                                         }
679
680                                         constants.Remove (name);
681                                         constants.Add (name, e);
682                                 }
683                         }
684
685                         //
686                         // Now, handle the children
687                         //
688                         if (children != null){
689                                 foreach (Block b in children)
690                                         b.EmitMeta (ec, toplevel);
691                         }
692                 }
693
694                 public void UsageWarning ()
695                 {
696                         string name;
697                         
698                         if (variables != null){
699                                 foreach (DictionaryEntry de in variables){
700                                         VariableInfo vi = (VariableInfo) de.Value;
701                                         
702                                         if (vi.Used)
703                                                 continue;
704                                         
705                                         name = (string) de.Key;
706                                                 
707                                         if (vi.Assigned){
708                                                 Report.Warning (
709                                                         219, vi.Location, "The variable '" + name +
710                                                         "' is assigned but its value is never used");
711                                         } else {
712                                                 if (!(name.Equals(lateBindingArgs)||name.Equals(lateBindingArgNames)||name.Equals(lateBindingCopyBack)))
713                                                 Report.Warning (
714                                                         168, vi.Location, "The variable '" +
715                                                         name +"' is declared but never used");
716                                         } 
717                                 }
718                         }
719
720                         if (children != null)
721                                 foreach (Block b in children)
722                                         b.UsageWarning ();
723                 }
724
725                 bool has_ret = false;
726
727                 public override bool Resolve (EmitContext ec)
728                 {
729                         Block prev_block = ec.CurrentBlock;
730                         bool ok = true;
731
732                         ec.CurrentBlock = this;
733
734                         if (!variables_initialized)
735                                 UpdateVariableInfo (ec);
736                                 
737                         ec.StartFlowBranching (this);
738
739                         Report.Debug (1, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
740
741                         ArrayList new_statements = new ArrayList ();
742                         bool unreachable = false, warning_shown = false;
743
744                         foreach (Statement s in statements){
745
746                                 if (unreachable && !(s is LabeledStatement)) {
747                                         if (!warning_shown && !(s is EmptyStatement)) {
748                                                 warning_shown = true;
749                                                 Warning_DeadCodeFound (s.loc);
750                                         }
751                                         continue;
752                                 }
753
754                                 if (s.Resolve (ec) == false) {
755                                         ok = false;
756                                         continue;
757                                 }
758
759                                 if (s is LabeledStatement)
760                                         unreachable = false;
761                                 else
762                                         unreachable = ! ec.CurrentBranching.IsReachable ();
763
764                                 new_statements.Add (s);
765                         }
766
767                         statements = new_statements;
768
769                         Report.Debug (1, "RESOLVE BLOCK DONE", StartLocation, ec.CurrentBranching);
770
771                         FlowReturns returns = ec.EndFlowBranching ();
772                         ec.CurrentBlock = prev_block;
773
774                         // If we're a non-static 'struct' constructor which doesn't have an
775                         // initializer, then we must initialize all of the struct's fields.
776                         if ((this_variable != null) && (returns != FlowReturns.EXCEPTION) &&
777                             !this_variable.IsAssigned (ec, loc))
778                                 ok = false;
779
780                         if ((labels != null) && (RootContext.WarningLevel >= 2)) {
781                                 foreach (LabeledStatement label in labels.Values)
782                                         if (!label.HasBeenReferenced)
783                                                 Report.Warning (164, label.Location,
784                                                                 "This label has not been referenced");
785                         }
786
787                         if ((returns == FlowReturns.ALWAYS) ||
788                             (returns == FlowReturns.EXCEPTION) ||
789                             (returns == FlowReturns.UNREACHABLE))
790                                 has_ret = true;
791
792                         return ok;
793                 }
794                 
795                 protected override bool DoEmit (EmitContext ec)
796                 {
797                         Block prev_block = ec.CurrentBlock;
798
799                         ec.CurrentBlock = this;
800
801                         ec.Mark (StartLocation);
802                         foreach (Statement s in statements)
803                                 s.Emit (ec);
804                                 
805                         ec.Mark (EndLocation); 
806                         
807                         ec.CurrentBlock = prev_block;
808                         return has_ret;
809                 }
810                 
811                 public override string ToString ()
812                 {
813                         return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
814                 }
815
816         } // class Block
817
818 } // namespace Mono.MonoBASIC