2002-08-16 Martin Baulig <martin@gnome.org>
[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.MakeName (Name) + " does not hide an " +
51                                 "inherited member.  The keyword new is not required");
52                                                            
53                 }
54
55                 void Error_CannotChangeAccessModifiers (TypeContainer parent, MethodInfo parent_method,
56                                                         string name)
57                 {
58                         //
59                         // FIXME: report the old/new permissions?
60                         //
61                         Report.Error (
62                                 507, Location, parent.MakeName (Name) +
63                                 ": can't change the access modifiers when overriding inherited " +
64                                 "member `" + name + "'");
65                 }
66                 
67                 //
68                 // Performs various checks on the MethodInfo `mb' regarding the modifier flags
69                 // that have been defined.
70                 //
71                 // `name' is the user visible name for reporting errors (this is used to
72                 // provide the right name regarding method names and properties)
73                 //
74                 protected bool CheckMethodAgainstBase (TypeContainer parent, MethodAttributes my_attrs,
75                                                        MethodInfo mb, string name)
76                 {
77                         bool ok = true;
78                         
79                         if ((ModFlags & Modifiers.OVERRIDE) != 0){
80                                 if (!(mb.IsAbstract || mb.IsVirtual)){
81                                         Report.Error (
82                                                 506, Location, parent.MakeName (Name) +
83                                                 ": cannot override inherited member `" +
84                                                 name + "' because it is not " +
85                                                 "virtual, abstract or override");
86                                         ok = false;
87                                 }
88                                 
89                                 // Now we check that the overriden method is not final
90                                 
91                                 if (mb.IsFinal) {
92                                         Report.Error (239, Location, parent.MakeName (Name) + " : cannot " +
93                                                       "override inherited member `" + name +
94                                                       "' because it is sealed.");
95                                         ok = false;
96                                 }
97
98                                 //
99                                 // Check that the permissions are not being changed
100                                 //
101                                 MethodAttributes thisp = my_attrs & MethodAttributes.MemberAccessMask;
102                                 MethodAttributes parentp = mb.Attributes & MethodAttributes.MemberAccessMask;
103
104                                 if (thisp != parentp){
105                                         Error_CannotChangeAccessModifiers (parent, mb, name);
106                                         ok = false;
107                                 }
108                         }
109
110                         if (mb.IsVirtual || mb.IsAbstract){
111                                 if ((ModFlags & (Modifiers.NEW | Modifiers.OVERRIDE)) == 0){
112                                         if (Name != "Finalize" && (RootContext.WarningLevel >= 2)){
113                                                 Report.Warning (
114                                                         114, Location, parent.MakeName (Name) + 
115                                                         " hides inherited member `" + name +
116                                                         "'.  To make the current member override that " +
117                                                         "implementation, add the override keyword, " +
118                                                         "otherwise use the new keyword");
119                                         }
120                                 }
121                         } else {
122                                 if ((ModFlags & (Modifiers.NEW | Modifiers.OVERRIDE)) == 0){
123                                         if (Name != "Finalize" && (RootContext.WarningLevel >= 1)){
124                                                 Report.Warning (
125                                                         108, Location, "The keyword new is required on " +
126                                                         parent.MakeName (Name) + " because it hides " +
127                                                         "inherited member `" + name + "'");
128                                         }
129                                 }
130                         }
131
132                         return ok;
133                 }
134
135                 public abstract bool Define (TypeContainer parent);
136
137                 // 
138                 // Whehter is it ok to use an unsafe pointer in this type container
139                 //
140                 public bool UnsafeOK (DeclSpace parent)
141                 {
142                         //
143                         // First check if this MemberCore modifier flags has unsafe set
144                         //
145                         if ((ModFlags & Modifiers.UNSAFE) != 0)
146                                 return true;
147
148                         if (parent.UnsafeContext)
149                                 return true;
150
151                         Expression.UnsafeError (Location);
152                         return false;
153                 }
154         }
155
156         //
157         // FIXME: This is temporary outside DeclSpace, because I have to fix a bug
158         // in MCS that makes it fail the lookup for the enum
159         //
160
161                 /// <summary>
162                 ///   The result value from adding an declaration into
163                 ///   a struct or a class
164                 /// </summary>
165                 public enum AdditionResult {
166                         /// <summary>
167                         /// The declaration has been successfully
168                         /// added to the declation space.
169                         /// </summary>
170                         Success,
171
172                         /// <summary>
173                         ///   The symbol has already been defined.
174                         /// </summary>
175                         NameExists,
176
177                         /// <summary>
178                         ///   Returned if the declation being added to the
179                         ///   name space clashes with its container name.
180                         ///
181                         ///   The only exceptions for this are constructors
182                         ///   and static constructors
183                         /// </summary>
184                         EnclosingClash,
185
186                         /// <summary>
187                         ///   Returned if a constructor was created (because syntactically
188                         ///   it looked like a constructor) but was not (because the name
189                         ///   of the method is not the same as the container class
190                         /// </summary>
191                         NotAConstructor,
192
193                         /// <summary>
194                         ///   This is only used by static constructors to emit the
195                         ///   error 111, but this error for other things really
196                         ///   happens at another level for other functions.
197                         /// </summary>
198                         MethodExists
199                 }
200
201         /// <summary>
202         ///   Base class for structs, classes, enumerations and interfaces.  
203         /// </summary>
204         /// <remarks>
205         ///   They all create new declaration spaces.  This
206         ///   provides the common foundation for managing those name
207         ///   spaces.
208         /// </remarks>
209         public abstract class DeclSpace : MemberCore {
210                 /// <summary>
211                 ///   this points to the actual definition that is being
212                 ///   created with System.Reflection.Emit
213                 /// </summary>
214                 public TypeBuilder TypeBuilder;
215
216                 /// <summary>
217                 ///   This variable tracks whether we have Closed the type
218                 /// </summary>
219                 public bool Created = false;
220                 
221                 //
222                 // This is the namespace in which this typecontainer
223                 // was declared.  We use this to resolve names.
224                 //
225                 public Namespace Namespace;
226
227                 public Hashtable Cache = new Hashtable ();
228                 
229                 public string Basename;
230                 
231                 /// <summary>
232                 ///   defined_names is used for toplevel objects
233                 /// </summary>
234                 protected Hashtable defined_names;
235
236                 TypeContainer parent;           
237
238                 public DeclSpace (TypeContainer parent, string name, Location l)
239                         : base (name, l)
240                 {
241                         Basename = name.Substring (1 + name.LastIndexOf ('.'));
242                         defined_names = new Hashtable ();
243                         this.parent = parent;
244                 }
245
246                 /// <summary>
247                 ///   Returns a status code based purely on the name
248                 ///   of the member being added
249                 /// </summary>
250                 protected AdditionResult IsValid (string name)
251                 {
252                         if (name == Basename)
253                                 return AdditionResult.EnclosingClash;
254
255                         if (defined_names.Contains (name))
256                                 return AdditionResult.NameExists;
257
258                         return AdditionResult.Success;
259                 }
260
261                 /// <summary>
262                 ///   Introduce @name into this declaration space and
263                 ///   associates it with the object @o.  Note that for
264                 ///   methods this will just point to the first method. o
265                 /// </summary>
266                 protected void DefineName (string name, object o)
267                 {
268                         defined_names.Add (name, o);
269                 }
270
271                 /// <summary>
272                 ///   Returns the object associated with a given name in the declaration
273                 ///   space.  This is the inverse operation of `DefineName'
274                 /// </summary>
275                 public object GetDefinition (string name)
276                 {
277                         return defined_names [name];
278                 }
279                 
280                 bool in_transit = false;
281                 
282                 /// <summary>
283                 ///   This function is used to catch recursive definitions
284                 ///   in declarations.
285                 /// </summary>
286                 public bool InTransit {
287                         get {
288                                 return in_transit;
289                         }
290
291                         set {
292                                 in_transit = value;
293                         }
294                 }
295
296                 public TypeContainer Parent {
297                         get {
298                                 return parent;
299                         }
300                 }
301
302                 /// <summary>
303                 ///   Looks up the alias for the name
304                 /// </summary>
305                 public string LookupAlias (string name)
306                 {
307                         if (Namespace != null)
308                                 return Namespace.LookupAlias (name);
309                         else
310                                 return null;
311                 }
312                 
313                 // 
314                 // root_types contains all the types.  All TopLevel types
315                 // hence have a parent that points to `root_types', that is
316                 // why there is a non-obvious test down here.
317                 //
318                 public bool IsTopLevel {
319                         get {
320                                 if (parent != null){
321                                         if (parent.parent == null)
322                                                 return true;
323                                 }
324                                 return false;
325                         }
326                 }
327
328                 public virtual void CloseType ()
329                 {
330                         if (!Created){
331                                 try {
332                                         TypeBuilder.CreateType ();
333                                 } catch {
334                                         //
335                                         // The try/catch is needed because
336                                         // nested enumerations fail to load when they
337                                         // are defined.
338                                         //
339                                         // Even if this is the right order (enumerations
340                                         // declared after types).
341                                         //
342                                         // Note that this still creates the type and
343                                         // it is possible to save it
344                                 }
345                                 Created = true;
346                         }
347                 }
348
349                 /// <remarks>
350                 ///  Should be overriten by the appropriate declaration space
351                 /// <remarks>
352                 public abstract TypeBuilder DefineType ();
353                 
354                 /// <summary>
355                 ///   Define all members, but don't apply any attributes or do anything which may
356                 ///   access not-yet-defined classes.  This method also creates the MemberCache.
357                 /// </summary>
358                 public abstract bool DefineMembers (TypeContainer parent);
359
360                 //
361                 // Whether this is an `unsafe context'
362                 //
363                 public bool UnsafeContext {
364                         get {
365                                 if ((ModFlags & Modifiers.UNSAFE) != 0)
366                                         return true;
367                                 if (parent != null)
368                                         return parent.UnsafeContext;
369                                 return false;
370                         }
371                 }
372
373                 public static string MakeFQN (string nsn, string name)
374                 {
375                         string prefix = (nsn == "" ? "" : nsn + ".");
376
377                         return prefix + name;
378                 }
379
380                 EmitContext type_resolve_ec;
381                 EmitContext GetTypeResolveEmitContext (TypeContainer parent, Location loc)
382                 {
383                         type_resolve_ec = new EmitContext (parent, this, loc, null, null, ModFlags, false);
384                         type_resolve_ec.ResolvingTypeTree = true;
385
386                         return type_resolve_ec;
387                 }
388
389                 // <summary>
390                 //    Looks up the type, as parsed into the expression `e' 
391                 // </summary>
392                 public Type ResolveType (Expression e, bool silent, Location loc)
393                 {
394                         if (type_resolve_ec == null)
395                                 type_resolve_ec = GetTypeResolveEmitContext (parent, loc);
396                         type_resolve_ec.loc = loc;
397                         Expression d = e.Resolve (type_resolve_ec, ResolveFlags.Type);
398                         if (d == null || d.eclass != ExprClass.Type){
399                                 if (!silent){
400                                         Report.Error (246, loc, "Cannot find type `"+ e.ToString () +"'");
401                                 }
402                                 return null;
403                         }
404
405                         return d.Type;
406                 }
407
408                 // <summary>
409                 //    Resolves the expression `e' for a type, and will recursively define
410                 //    types. 
411                 // </summary>
412                 public Expression ResolveTypeExpr (Expression e, bool silent, Location loc)
413                 {
414                         if (type_resolve_ec == null)
415                                 type_resolve_ec = GetTypeResolveEmitContext (parent, loc);
416
417                         Expression d = e.Resolve (type_resolve_ec, ResolveFlags.Type);
418                         if (d == null || d.eclass != ExprClass.Type){
419                                 if (!silent){
420                                         Report.Error (246, loc, "Cannot find type `"+ e +"'");
421                                 }
422                                 return null;
423                         }
424
425                         return d;
426                 }
427                 
428                 Type LookupInterfaceOrClass (string ns, string name, out bool error)
429                 {
430                         DeclSpace parent;
431                         Type t;
432
433                         error = false;
434                         name = MakeFQN (ns, name);
435                         
436                         t  = TypeManager.LookupType (name);
437                         if (t != null)
438                                 return t;
439
440                         parent = (DeclSpace) RootContext.Tree.Decls [name];
441                         if (parent == null)
442                                 return null;
443                         
444                         t = parent.DefineType ();
445                         if (t == null){
446                                 Report.Error (146, "Class definition is circular: `"+name+"'");
447                                 error = true;
448                                 return null;
449                         }
450                         return t;
451                 }
452                 
453                 /// <summary>
454                 ///   GetType is used to resolve type names at the DeclSpace level.
455                 ///   Use this to lookup class/struct bases, interface bases or 
456                 ///   delegate type references
457                 /// </summary>
458                 ///
459                 /// <remarks>
460                 ///   Contrast this to LookupType which is used inside method bodies to 
461                 ///   lookup types that have already been defined.  GetType is used
462                 ///   during the tree resolution process and potentially define
463                 ///   recursively the type
464                 /// </remarks>
465                 public Type FindType (string name)
466                 {
467                         Type t;
468                         bool error;
469
470                         //
471                         // For the case the type we are looking for is nested within this one
472                         // or is in any base class
473                         //
474                         DeclSpace containing_ds = this;
475
476                         while (containing_ds != null){
477                                 Type current_type = containing_ds.TypeBuilder;
478
479                                 while (current_type != null) {
480                                         string pre = current_type.FullName;
481                                         
482                                         t = LookupInterfaceOrClass (pre, name, out error);
483                                         if (error)
484                                                 return null;
485                                 
486                                         if (t != null) 
487                                                 return t;
488
489                                         current_type = current_type.BaseType;
490                                 }
491                                 containing_ds = containing_ds.Parent;
492                         }
493                         
494                         //
495                         // Attempt to lookup the class on our namespace and all it's implicit parents
496                         //
497                         for (string ns = Namespace.Name; ns != null; ns = RootContext.ImplicitParent (ns)) {
498
499                                 t = LookupInterfaceOrClass (ns, name, out error);
500                                 if (error)
501                                         return null;
502                                 
503                                 if (t != null) 
504                                         return t;
505                         }
506                         
507                         //
508                         // Attempt to do a direct unqualified lookup
509                         //
510                         t = LookupInterfaceOrClass ("", name, out error);
511                         if (error)
512                                 return null;
513                         
514                         if (t != null)
515                                 return t;
516                         
517                         //
518                         // Attempt to lookup the class on any of the `using'
519                         // namespaces
520                         //
521
522                         for (Namespace ns = Namespace; ns != null; ns = ns.Parent){
523
524                                 t = LookupInterfaceOrClass (ns.Name, name, out error);
525                                 if (error)
526                                         return null;
527
528                                 if (t != null)
529                                         return t;
530
531                                 //
532                                 // Now check the using clause list
533                                 //
534                                 ArrayList using_list = ns.UsingTable;
535                                 
536                                 if (using_list == null)
537                                         continue;
538
539                                 foreach (string n in using_list){
540                                         t = LookupInterfaceOrClass (n, name, out error);
541                                         if (error)
542                                                 return null;
543
544                                         if (t != null)
545                                                 return t;
546                                 }
547                                 
548                         }
549
550                         //Report.Error (246, Location, "Can not find type `"+name+"'");
551                         return null;
552                 }
553
554                 /// <remarks>
555                 ///   This function is broken and not what you're looking for.  It should only
556                 ///   be used while the type is still being created since it doesn't use the cache
557                 ///   and relies on the filter doing the member name check.
558                 /// </remarks>
559                 internal abstract MemberList FindMembers (MemberTypes mt, BindingFlags bf,
560                                                           MemberFilter filter, object criteria);
561
562                 /// <remarks>
563                 ///   If we have a MemberCache, return it.  This property may return null if the
564                 ///   class doesn't have a member cache or while it's still being created.
565                 /// </remarks>
566                 public abstract MemberCache MemberCache {
567                         get;
568                 }
569         }
570 }