2002-06-15 Rafael Teixeira <rafaelteixeirabr@hotmail.com>
[mono.git] / mcs / mbas / decl.cs
1 //
2 // decl.cs: Declaration base class for structs, classes, enums and interfaces.
3 //
4 // Author: Miguel de Icaza (miguel@gnu.org)
5 //
6 // Licensed under the terms of the GNU GPL
7 //
8 // (C) 2001 Ximian, Inc (http://www.ximian.com)
9 //
10 // TODO: Move the method verification stuff from the class.cs and interface.cs here
11 //
12
13 using System;
14 using System.Collections;
15 using System.Reflection.Emit;
16 using System.Reflection;
17
18 namespace Mono.CSharp {
19
20         /// <summary>
21         ///   Base representation for members.  This is only used to keep track
22         ///   of Name, Location and Modifier flags.
23         /// </summary>
24         public abstract class MemberCore {
25                 /// <summary>
26                 ///   Public name
27                 /// </summary>
28                 public string Name;
29
30                 /// <summary>
31                 ///   Modifier flags that the user specified in the source code
32                 /// </summary>
33                 public int ModFlags;
34
35                 /// <summary>
36                 ///   Location where this declaration happens
37                 /// </summary>
38                 public readonly Location Location;
39
40                 public MemberCore (string name, Location loc)
41                 {
42                         Name = name;
43                         Location = loc;
44                 }
45
46                 protected void WarningNotHiding (TypeContainer parent)
47                 {
48                         Report.Warning (
49                                 109, Location,
50                                 "The member `" + parent.Name + "." + Name + "' does not hide an " +
51                                 "inherited member.  The keyword new is not required");
52                                                            
53                 }
54
55                 static string MethodBaseName (MethodBase mb)
56                 {
57                         return "`" + mb.ReflectedType.Name + "." + mb.Name + "'";
58                 }
59
60                 void Error_CannotChangeAccessModifiers (TypeContainer parent, MethodInfo parent_method)
61                 {
62                         //
63                         // FIXME: report the old/new permissions?
64                         //
65                         Report.Error (
66                                 507, "`" + parent_method + "." + Name +
67                                 ": can't change the access modifiers from `" +
68                                 parent_method.DeclaringType.Name + "." + parent_method.Name + "'");
69                 }
70                 
71                 //
72                 // Performs various checks on the MethodInfo `mb' regarding the modifier flags
73                 // that have been defined.
74                 //
75                 // `name' is the user visible name for reporting errors (this is used to
76                 // provide the right name regarding method names and properties)
77                 //
78                 protected bool CheckMethodAgainstBase (TypeContainer parent,
79                                                        MethodAttributes my_attrs, MethodInfo mb)
80                 {
81                         bool ok = true;
82                         
83                         if ((ModFlags & Modifiers.OVERRIDE) != 0){
84                                 if (!(mb.IsAbstract || mb.IsVirtual)){
85                                         Report.Error (
86                                                 506, Location, parent.MakeName (Name) +
87                                                 ": cannot override inherited member " +
88                                                 MethodBaseName (mb) + " because it is not " +
89                                                 "virtual, abstract or override");
90                                         ok = false;
91                                 }
92                                 
93                                 // Now we check that the overriden method is not final
94                                 
95                                 if (mb.IsFinal) {
96                                         Report.Error (239, Location, parent.MakeName (Name) + " : cannot " +
97                                                       "override inherited member " + MethodBaseName (mb) +
98                                                       " because it is sealed.");
99                                         ok = false;
100                                 }
101
102                                 //
103                                 // Check that the permissions are not being changed
104                                 //
105                                 MethodAttributes thisp = my_attrs & MethodAttributes.MemberAccessMask;
106                                 MethodAttributes parentp = mb.Attributes & MethodAttributes.MemberAccessMask;
107
108                                 if (thisp != parentp){
109                                         Error_CannotChangeAccessModifiers (parent, mb);
110                                         ok = false;
111                                 }
112                         }
113
114                         if (mb.IsVirtual || mb.IsAbstract){
115                                 if ((ModFlags & (Modifiers.NEW | Modifiers.OVERRIDE)) == 0){
116                                         if (Name != "Finalize" && (RootContext.WarningLevel >= 2)){
117                                                 Report.Warning (
118                                                         114, Location, parent.MakeName (Name) + 
119                                                         " hides inherited member " + MethodBaseName (mb) +
120                                                         ".  To make the current member override that " +
121                                                         "implementation, add the override keyword, " +
122                                                         "otherwise use the new keyword");
123                                         }
124                                 }
125                         }
126
127                         return ok;
128                 }
129
130                 public abstract bool Define (TypeContainer parent);
131
132                 // 
133                 // Whehter is it ok to use an unsafe pointer in this type container
134                 //
135                 public bool UnsafeOK (DeclSpace parent)
136                 {
137                         //
138                         // First check if this MemberCore modifier flags has unsafe set
139                         //
140                         if ((ModFlags & Modifiers.UNSAFE) != 0)
141                                 return true;
142
143                         if (parent.UnsafeContext)
144                                 return true;
145
146                         Expression.UnsafeError (Location);
147                         return false;
148                 }
149         }
150
151         //
152         // FIXME: This is temporary outside DeclSpace, because I have to fix a bug
153         // in MCS that makes it fail the lookup for the enum
154         //
155
156                 /// <summary>
157                 ///   The result value from adding an declaration into
158                 ///   a struct or a class
159                 /// </summary>
160                 public enum AdditionResult {
161                         /// <summary>
162                         /// The declaration has been successfully
163                         /// added to the declation space.
164                         /// </summary>
165                         Success,
166
167                         /// <summary>
168                         ///   The symbol has already been defined.
169                         /// </summary>
170                         NameExists,
171
172                         /// <summary>
173                         ///   Returned if the declation being added to the
174                         ///   name space clashes with its container name.
175                         ///
176                         ///   The only exceptions for this are constructors
177                         ///   and static constructors
178                         /// </summary>
179                         EnclosingClash,
180
181                         /// <summary>
182                         ///   Returned if a constructor was created (because syntactically
183                         ///   it looked like a constructor) but was not (because the name
184                         ///   of the method is not the same as the container class
185                         /// </summary>
186                         NotAConstructor,
187
188                         /// <summary>
189                         ///   This is only used by static constructors to emit the
190                         ///   error 111, but this error for other things really
191                         ///   happens at another level for other functions.
192                         /// </summary>
193                         MethodExists
194                 }
195
196         /// <summary>
197         ///   Base class for structs, classes, enumerations and interfaces.  
198         /// </summary>
199         /// <remarks>
200         ///   They all create new declaration spaces.  This
201         ///   provides the common foundation for managing those name
202         ///   spaces.
203         /// </remarks>
204         public abstract class DeclSpace : MemberCore {
205                 /// <summary>
206                 ///   this points to the actual definition that is being
207                 ///   created with System.Reflection.Emit
208                 /// </summary>
209                 public TypeBuilder TypeBuilder;
210
211                 /// <summary>
212                 ///   This variable tracks whether we have Closed the type
213                 /// </summary>
214                 public bool Created = false;
215                 
216                 //
217                 // This is the namespace in which this typecontainer
218                 // was declared.  We use this to resolve names.
219                 //
220                 public Namespace Namespace;
221
222                 public Hashtable Cache = new Hashtable ();
223                 
224                 public string Basename;
225                 
226                 /// <summary>
227                 ///   defined_names is used for toplevel objects
228                 /// </summary>
229                 protected Hashtable defined_names;
230
231                 TypeContainer parent;           
232
233                 public DeclSpace (TypeContainer parent, string name, Location l)
234                         : base (name, l)
235                 {
236                         Basename = name.Substring (1 + name.LastIndexOf ('.'));
237                         defined_names = new Hashtable ();
238                         this.parent = parent;
239                 }
240
241                 /// <summary>
242                 ///   Returns a status code based purely on the name
243                 ///   of the member being added
244                 /// </summary>
245                 protected AdditionResult IsValid (string name)
246                 {
247                         if (name == Basename)
248                                 return AdditionResult.EnclosingClash;
249
250                         if (defined_names.Contains (name))
251                                 return AdditionResult.NameExists;
252
253                         return AdditionResult.Success;
254                 }
255
256                 /// <summary>
257                 ///   Introduce @name into this declaration space and
258                 ///   associates it with the object @o.  Note that for
259                 ///   methods this will just point to the first method. o
260                 /// </summary>
261                 protected void DefineName (string name, object o)
262                 {
263                         defined_names.Add (name, o);
264                 }
265
266                 /// <summary>
267                 ///   Returns the object associated with a given name in the declaration
268                 ///   space.  This is the inverse operation of `DefineName'
269                 /// </summary>
270                 public object GetDefinition (string name)
271                 {
272                         return defined_names [name];
273                 }
274                 
275                 bool in_transit = false;
276                 
277                 /// <summary>
278                 ///   This function is used to catch recursive definitions
279                 ///   in declarations.
280                 /// </summary>
281                 public bool InTransit {
282                         get {
283                                 return in_transit;
284                         }
285
286                         set {
287                                 in_transit = value;
288                         }
289                 }
290
291                 public TypeContainer Parent {
292                         get {
293                                 return parent;
294                         }
295                 }
296
297                 // 
298                 // root_types contains all the types.  All TopLevel types
299                 // hence have a parent that points to `root_types', that is
300                 // why there is a non-obvious test down here.
301                 //
302                 public bool IsTopLevel {
303                         get {
304                                 if (parent != null){
305                                         if (parent.parent == null)
306                                                 return true;
307                                 }
308                                 return false;
309                         }
310                 }
311
312                 public virtual void CloseType ()
313                 {
314                         if (!Created){
315                                 try {
316                                         TypeBuilder.CreateType ();
317                                 } catch {
318                                         //
319                                         // The try/catch is needed because
320                                         // nested enumerations fail to load when they
321                                         // are defined.
322                                         //
323                                         // Even if this is the right order (enumerations
324                                         // declared after types).
325                                         //
326                                         // Note that this still creates the type and
327                                         // it is possible to save it
328                                 }
329                                 Created = true;
330                         }
331                 }
332
333                 /// <remarks>
334                 ///  Should be overriten by the appropriate declaration space
335                 /// <remarks>
336                 public abstract TypeBuilder DefineType ();
337                 
338                 //
339                 // Whether this is an `unsafe context'
340                 //
341                 public bool UnsafeContext {
342                         get {
343                                 if ((ModFlags & Modifiers.UNSAFE) != 0)
344                                         return true;
345                                 if (parent != null)
346                                         return parent.UnsafeContext;
347                                 return false;
348                         }
349                 }
350
351                 public static string MakeFQN (string nsn, string name)
352                 {
353                         string prefix = (nsn == "" ? "" : nsn + ".");
354
355                         return prefix + name;
356                 }
357
358                 Type LookupInterfaceOrClass (string ns, string name, out bool error)
359                 {
360                         DeclSpace parent;
361                         Type t;
362
363                         error = false;
364                         name = MakeFQN (ns, name);
365                         
366                         t  = TypeManager.LookupType (name);
367                         if (t != null)
368                                 return t;
369
370                         parent = (DeclSpace) RootContext.Tree.Decls [name];
371                         if (parent == null)
372                                 return null;
373                         
374                         t = parent.DefineType ();
375                         if (t == null){
376                                 Report.Error (146, "Class definition is circular: `"+name+"'");
377                                 error = true;
378                                 return null;
379                         }
380                         return t;
381                 }
382                 
383                 /// <summary>
384                 ///   GetType is used to resolve type names at the DeclSpace level.
385                 ///   Use this to lookup class/struct bases, interface bases or 
386                 ///   delegate type references
387                 /// </summary>
388                 ///
389                 /// <remarks>
390                 ///   Contrast this to LookupType which is used inside method bodies to 
391                 ///   lookup types that have already been defined.  GetType is used
392                 ///   during the tree resolution process and potentially define
393                 ///   recursively the type
394                 /// </remarks>
395                 public Type FindType (string name)
396                 {
397                         Type t;
398                         bool error;
399
400                         //
401                         // For the case the type we are looking for is nested within this one
402                         // or is in any base class
403                         //
404                         DeclSpace containing_ds = this;
405
406                         while (containing_ds != null){
407                                 Type current_type = containing_ds.TypeBuilder;
408
409                                 while (current_type != null) {
410                                         string pre = current_type.FullName;
411                                         
412                                         t = LookupInterfaceOrClass (pre, name, out error);
413                                         if (error)
414                                                 return null;
415                                 
416                                         if (t != null) 
417                                                 return t;
418
419                                         current_type = current_type.BaseType;
420                                 }
421                                 containing_ds = containing_ds.Parent;
422                         }
423                         
424                         //
425                         // Attempt to lookup the class on our namespace and all it's implicit parents
426                         //
427                         for (string ns = Namespace.Name; ns != null; ns = RootContext.ImplicitParent (ns)) {
428
429                                 t = LookupInterfaceOrClass (ns, name, out error);
430                                 if (error)
431                                         return null;
432                                 
433                                 if (t != null) 
434                                         return t;
435                         }
436                         
437                         //
438                         // Attempt to do a direct unqualified lookup
439                         //
440                         t = LookupInterfaceOrClass ("", name, out error);
441                         if (error)
442                                 return null;
443                         
444                         if (t != null)
445                                 return t;
446                         
447                         //
448                         // Attempt to lookup the class on any of the `using'
449                         // namespaces
450                         //
451
452                         for (Namespace ns = Namespace; ns != null; ns = ns.Parent){
453
454                                 t = LookupInterfaceOrClass (ns.Name, name, out error);
455                                 if (error)
456                                         return null;
457
458                                 if (t != null)
459                                         return t;
460
461                                 //
462                                 // Now check the using clause list
463                                 //
464                                 ArrayList using_list = ns.UsingTable;
465                                 
466                                 if (using_list == null)
467                                         continue;
468
469                                 foreach (string n in using_list){
470                                         t = LookupInterfaceOrClass (n, name, out error);
471                                         if (error)
472                                                 return null;
473
474                                         if (t != null)
475                                                 return t;
476                                 }
477                                 
478                         }
479
480                         Report.Error (246, Location, "Can not find type `"+name+"'");
481                         return null;
482                 }
483         }
484 }