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