2002-08-12 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                 //
355                 // Whether this is an `unsafe context'
356                 //
357                 public bool UnsafeContext {
358                         get {
359                                 if ((ModFlags & Modifiers.UNSAFE) != 0)
360                                         return true;
361                                 if (parent != null)
362                                         return parent.UnsafeContext;
363                                 return false;
364                         }
365                 }
366
367                 public static string MakeFQN (string nsn, string name)
368                 {
369                         string prefix = (nsn == "" ? "" : nsn + ".");
370
371                         return prefix + name;
372                 }
373
374                 EmitContext type_resolve_ec;
375                 EmitContext GetTypeResolveEmitContext (TypeContainer parent, Location loc)
376                 {
377                         type_resolve_ec = new EmitContext (parent, this, loc, null, null, ModFlags, false);
378                         type_resolve_ec.ResolvingTypeTree = true;
379
380                         return type_resolve_ec;
381                 }
382
383                 // <summary>
384                 //    Looks up the type, as parsed into the expression `e' 
385                 // </summary>
386                 public Type ResolveType (Expression e, bool silent, Location loc)
387                 {
388                         if (type_resolve_ec == null)
389                                 type_resolve_ec = GetTypeResolveEmitContext (parent, loc);
390                         type_resolve_ec.loc = loc;
391                         Expression d = e.Resolve (type_resolve_ec, ResolveFlags.Type);
392                         if (d == null || d.eclass != ExprClass.Type){
393                                 if (!silent){
394                                         Report.Error (246, loc, "Cannot find type `"+ e.ToString () +"'");
395                                 }
396                                 return null;
397                         }
398
399                         return d.Type;
400                 }
401
402                 // <summary>
403                 //    Resolves the expression `e' for a type, and will recursively define
404                 //    types. 
405                 // </summary>
406                 public Expression ResolveTypeExpr (Expression e, bool silent, Location loc)
407                 {
408                         if (type_resolve_ec == null)
409                                 type_resolve_ec = GetTypeResolveEmitContext (parent, loc);
410
411                         Expression d = e.Resolve (type_resolve_ec, ResolveFlags.Type);
412                         if (d == null || d.eclass != ExprClass.Type){
413                                 if (!silent){
414                                         Report.Error (246, loc, "Cannot find type `"+ e +"'");
415                                 }
416                                 return null;
417                         }
418
419                         return d;
420                 }
421                 
422                 Type LookupInterfaceOrClass (string ns, string name, out bool error)
423                 {
424                         DeclSpace parent;
425                         Type t;
426
427                         error = false;
428                         name = MakeFQN (ns, name);
429                         
430                         t  = TypeManager.LookupType (name);
431                         if (t != null)
432                                 return t;
433
434                         parent = (DeclSpace) RootContext.Tree.Decls [name];
435                         if (parent == null)
436                                 return null;
437                         
438                         t = parent.DefineType ();
439                         if (t == null){
440                                 Report.Error (146, "Class definition is circular: `"+name+"'");
441                                 error = true;
442                                 return null;
443                         }
444                         return t;
445                 }
446                 
447                 /// <summary>
448                 ///   GetType is used to resolve type names at the DeclSpace level.
449                 ///   Use this to lookup class/struct bases, interface bases or 
450                 ///   delegate type references
451                 /// </summary>
452                 ///
453                 /// <remarks>
454                 ///   Contrast this to LookupType which is used inside method bodies to 
455                 ///   lookup types that have already been defined.  GetType is used
456                 ///   during the tree resolution process and potentially define
457                 ///   recursively the type
458                 /// </remarks>
459                 public Type FindType (string name)
460                 {
461                         Type t;
462                         bool error;
463
464                         //
465                         // For the case the type we are looking for is nested within this one
466                         // or is in any base class
467                         //
468                         DeclSpace containing_ds = this;
469
470                         while (containing_ds != null){
471                                 Type current_type = containing_ds.TypeBuilder;
472
473                                 while (current_type != null) {
474                                         string pre = current_type.FullName;
475                                         
476                                         t = LookupInterfaceOrClass (pre, name, out error);
477                                         if (error)
478                                                 return null;
479                                 
480                                         if (t != null) 
481                                                 return t;
482
483                                         current_type = current_type.BaseType;
484                                 }
485                                 containing_ds = containing_ds.Parent;
486                         }
487                         
488                         //
489                         // Attempt to lookup the class on our namespace and all it's implicit parents
490                         //
491                         for (string ns = Namespace.Name; ns != null; ns = RootContext.ImplicitParent (ns)) {
492
493                                 t = LookupInterfaceOrClass (ns, name, out error);
494                                 if (error)
495                                         return null;
496                                 
497                                 if (t != null) 
498                                         return t;
499                         }
500                         
501                         //
502                         // Attempt to do a direct unqualified lookup
503                         //
504                         t = LookupInterfaceOrClass ("", name, out error);
505                         if (error)
506                                 return null;
507                         
508                         if (t != null)
509                                 return t;
510                         
511                         //
512                         // Attempt to lookup the class on any of the `using'
513                         // namespaces
514                         //
515
516                         for (Namespace ns = Namespace; ns != null; ns = ns.Parent){
517
518                                 t = LookupInterfaceOrClass (ns.Name, name, out error);
519                                 if (error)
520                                         return null;
521
522                                 if (t != null)
523                                         return t;
524
525                                 //
526                                 // Now check the using clause list
527                                 //
528                                 ArrayList using_list = ns.UsingTable;
529                                 
530                                 if (using_list == null)
531                                         continue;
532
533                                 foreach (string n in using_list){
534                                         t = LookupInterfaceOrClass (n, name, out error);
535                                         if (error)
536                                                 return null;
537
538                                         if (t != null)
539                                                 return t;
540                                 }
541                                 
542                         }
543
544                         //Report.Error (246, Location, "Can not find type `"+name+"'");
545                         return null;
546                 }
547         }
548 }