2002-04-13 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mcs / mcs / 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 string Basename;
223                 
224                 /// <summary>
225                 ///   defined_names is used for toplevel objects
226                 /// </summary>
227                 protected Hashtable defined_names;
228
229                 TypeContainer parent;           
230
231                 public DeclSpace (TypeContainer parent, string name, Location l)
232                         : base (name, l)
233                 {
234                         Basename = name.Substring (1 + name.LastIndexOf ('.'));
235                         defined_names = new Hashtable ();
236                         this.parent = parent;
237                 }
238
239                 /// <summary>
240                 ///   Returns a status code based purely on the name
241                 ///   of the member being added
242                 /// </summary>
243                 protected AdditionResult IsValid (string name)
244                 {
245                         if (name == Basename)
246                                 return AdditionResult.EnclosingClash;
247
248                         if (defined_names.Contains (name))
249                                 return AdditionResult.NameExists;
250
251                         return AdditionResult.Success;
252                 }
253
254                 /// <summary>
255                 ///   Introduce @name into this declaration space and
256                 ///   associates it with the object @o.  Note that for
257                 ///   methods this will just point to the first method. o
258                 /// </summary>
259                 protected void DefineName (string name, object o)
260                 {
261                         defined_names.Add (name, o);
262                 }
263
264                 /// <summary>
265                 ///   Returns the object associated with a given name in the declaration
266                 ///   space.  This is the inverse operation of `DefineName'
267                 /// </summary>
268                 public object GetDefinition (string name)
269                 {
270                         return defined_names [name];
271                 }
272                 
273                 bool in_transit = false;
274                 
275                 /// <summary>
276                 ///   This function is used to catch recursive definitions
277                 ///   in declarations.
278                 /// </summary>
279                 public bool InTransit {
280                         get {
281                                 return in_transit;
282                         }
283
284                         set {
285                                 in_transit = value;
286                         }
287                 }
288
289                 public TypeContainer Parent {
290                         get {
291                                 return parent;
292                         }
293                 }
294
295                 // 
296                 // root_types contains all the types.  All TopLevel types
297                 // hence have a parent that points to `root_types', that is
298                 // why there is a non-obvious test down here.
299                 //
300                 public bool IsTopLevel {
301                         get {
302                                 if (parent != null){
303                                         if (parent.parent == null)
304                                                 return true;
305                                 }
306                                 return false;
307                         }
308                 }
309
310                 public virtual void CloseType ()
311                 {
312                         if (!Created){
313                                 try {
314                                         TypeBuilder.CreateType ();
315                                 } catch {
316                                         //
317                                         // The try/catch is needed because
318                                         // nested enumerations fail to load when they
319                                         // are defined.
320                                         //
321                                         // Even if this is the right order (enumerations
322                                         // declared after types).
323                                         //
324                                         // Note that this still creates the type and
325                                         // it is possible to save it
326                                 }
327                                 Created = true;
328                         }
329                 }
330
331                 /// <remarks>
332                 ///  Should be overriten by the appropriate declaration space
333                 /// <remarks>
334                 public abstract TypeBuilder DefineType ();
335                 
336                 //
337                 // Whether this is an `unsafe context'
338                 //
339                 public bool UnsafeContext {
340                         get {
341                                 if ((ModFlags & Modifiers.UNSAFE) != 0)
342                                         return true;
343                                 if (parent != null)
344                                         return parent.UnsafeContext;
345                                 return false;
346                         }
347                 }
348
349                 public static string MakeFQN (string nsn, string name)
350                 {
351                         string prefix = (nsn == "" ? "" : nsn + ".");
352
353                         return prefix + name;
354                 }
355
356                 Type LookupInterfaceOrClass (string ns, string name, out bool error)
357                 {
358                         DeclSpace parent;
359                         Type t;
360
361                         error = false;
362                         name = MakeFQN (ns, name);
363                         
364                         t  = TypeManager.LookupType (name);
365                         if (t != null)
366                                 return t;
367
368                         parent = (DeclSpace) RootContext.Tree.Decls [name];
369                         if (parent == null)
370                                 return null;
371                         
372                         t = parent.DefineType ();
373                         if (t == null){
374                                 Report.Error (146, "Class definition is circular: `"+name+"'");
375                                 error = true;
376                                 return null;
377                         }
378                         return t;
379                 }
380                 
381                 /// <summary>
382                 ///   GetType is used to resolve type names at the DeclSpace level.
383                 ///   Use this to lookup class/struct bases, interface bases or 
384                 ///   delegate type references
385                 /// </summary>
386                 ///
387                 /// <remarks>
388                 ///   Contrast this to LookupType which is used inside method bodies to 
389                 ///   lookup types that have already been defined.  GetType is used
390                 ///   during the tree resolution process and potentially define
391                 ///   recursively the type
392                 /// </remarks>
393                 public Type FindType (string name)
394                 {
395                         Type t;
396                         bool error;
397
398                         //
399                         // For the case the type we are looking for is nested within this one
400                         // or is in any base class
401                         //
402                         DeclSpace containing_ds = this;
403
404                         while (containing_ds != null){
405                                 Type current_type = containing_ds.TypeBuilder;
406
407                                 while (current_type != null) {
408                                         string pre = current_type.FullName;
409                                         
410                                         t = LookupInterfaceOrClass (pre, name, out error);
411                                         if (error)
412                                                 return null;
413                                 
414                                         if (t != null) 
415                                                 return t;
416
417                                         current_type = current_type.BaseType;
418                                 }
419                                 containing_ds = containing_ds.Parent;
420                         }
421                         
422                         //
423                         // Attempt to lookup the class on our namespace and all it's implicit parents
424                         //
425                         for (string ns = Namespace.Name; ns != null; ns = RootContext.ImplicitParent (ns)) {
426
427                                 t = LookupInterfaceOrClass (ns, name, out error);
428                                 if (error)
429                                         return null;
430                                 
431                                 if (t != null) 
432                                         return t;
433                         }
434                         
435                         //
436                         // Attempt to do a direct unqualified lookup
437                         //
438                         t = LookupInterfaceOrClass ("", name, out error);
439                         if (error)
440                                 return null;
441                         
442                         if (t != null)
443                                 return t;
444                         
445                         //
446                         // Attempt to lookup the class on any of the `using'
447                         // namespaces
448                         //
449
450                         for (Namespace ns = Namespace; ns != null; ns = ns.Parent){
451
452                                 t = LookupInterfaceOrClass (ns.Name, name, out error);
453                                 if (error)
454                                         return null;
455
456                                 if (t != null)
457                                         return t;
458
459                                 //
460                                 // Now check the using clause list
461                                 //
462                                 ArrayList using_list = ns.UsingTable;
463                                 
464                                 if (using_list == null)
465                                         continue;
466
467                                 foreach (string n in using_list){
468                                         t = LookupInterfaceOrClass (n, name, out error);
469                                         if (error)
470                                                 return null;
471
472                                         if (t != null)
473                                                 return t;
474                                 }
475                                 
476                         }
477
478                         Report.Error (246, Location, "Can not find type `"+name+"'");
479                         return null;
480                 }
481         }
482 }