2004-08-07 Anirban Bhattacharjee <banirban@novell.com>
[mono.git] / mcs / mbas / enum.cs
1 //
2 // enum.cs: Enum handling.
3 //
4 // Author: Miguel de Icaza (miguel@gnu.org)
5 //         Ravi Pratap     (ravi@ximian.com)
6 //         Anirban Bhattacharjee (banirban@novell.com)
7 //
8 // Licensed under the terms of the GNU GPL
9 //
10 // (C) 2001 Ximian, Inc (http://www.ximian.com)
11 //
12
13 using System;
14 using System.Collections;
15 using System.Reflection;
16 using System.Reflection.Emit;
17
18 namespace Mono.MonoBASIC {
19
20         /// <summary>
21         ///   Enumeration container
22         /// </summary>
23         public class Enum : DeclSpace {
24                 ArrayList ordered_enums;
25                 
26                 public Expression BaseType;
27                 public Attributes  OptAttributes;
28                 
29                 public Type UnderlyingType;
30
31                 Hashtable member_to_location;
32                 Hashtable member_to_attributes;
33
34                 //
35                 // This is for members that have been defined
36                 //
37                 Hashtable member_to_value;
38
39                 //
40                 // This is used to mark members we're currently defining
41                 //
42                 Hashtable in_transit;
43                 
44                 ArrayList field_builders;
45                 
46                 public const int AllowedModifiers =
47                         Modifiers.NEW |
48                         Modifiers.PUBLIC |
49                         Modifiers.PROTECTED |
50                         Modifiers.INTERNAL |
51                         Modifiers.PRIVATE;
52
53                 public Enum (TypeContainer parent, Expression type, int mod_flags, string name, Attributes attrs, Location l)
54                         : base (parent, name, l)
55                 {
56                         this.BaseType = type;
57                         ModFlags = Modifiers.Check (AllowedModifiers, mod_flags,
58                                                     IsTopLevel ? Modifiers.INTERNAL : Modifiers.PUBLIC, l);
59                         OptAttributes = attrs;
60
61                         ordered_enums = new ArrayList ();
62                         member_to_location = new Hashtable ();
63                         member_to_value = new Hashtable ();
64                         in_transit = new Hashtable ();
65                         field_builders = new ArrayList ();
66                 }
67
68                 /// <summary>
69                 ///   Adds @name to the enumeration space, with @expr
70                 ///   being its definition.  
71                 /// </summary>
72                 public AdditionResult AddEnumMember (string name, Expression expr, Location loc,
73                                                      Attributes opt_attrs)
74                 {
75                         if (defined_names.Contains (name))
76                                 return AdditionResult.NameExists;
77
78                         DefineName (name, expr);
79
80                         ordered_enums.Add (name);
81                         member_to_location.Add (name, loc);
82
83                         if (member_to_attributes == null)
84                                 member_to_attributes = new Hashtable ();
85
86                         member_to_attributes.Add (name, opt_attrs);
87                         
88                         return AdditionResult.Success;
89                 }
90
91                 //
92                 // This is used by corlib compilation: we map from our
93                 // type to a type that is consumable by the DefineField
94                 //
95                 Type MapToInternalType (Type t)
96                 {
97                         if (t == TypeManager.int32_type)
98                                 return typeof (int);
99                         if (t == TypeManager.int64_type)
100                                 return typeof (long);
101                         if (t == TypeManager.uint32_type)
102                                 return typeof (uint);
103                         if (t == TypeManager.uint64_type)
104                                 return typeof (ulong);
105                         if (t == TypeManager.float_type)
106                                 return typeof (float);
107                         if (t == TypeManager.double_type)
108                                 return typeof (double);
109                         if (t == TypeManager.byte_type)
110                                 return typeof (byte);
111                         if (t == TypeManager.sbyte_type)
112                                 return typeof (sbyte);
113                         if (t == TypeManager.char_type)
114                                 return typeof (char);
115                         if (t == TypeManager.short_type)
116                                 return typeof (short);
117                         if (t == TypeManager.ushort_type)
118                                 return typeof (ushort);
119
120                         throw new Exception ();
121                 }
122                 
123                 public override TypeBuilder DefineType ()
124                 {
125                         if (TypeBuilder != null)
126                                 return TypeBuilder;
127
128                         TypeAttributes attr = Modifiers.TypeAttr (ModFlags, IsTopLevel);
129
130                         attr |= TypeAttributes.Class | TypeAttributes.Sealed;
131
132                         UnderlyingType = ResolveType (BaseType, false, Location);
133
134                         if (UnderlyingType != TypeManager.int32_type &&
135                             UnderlyingType != TypeManager.uint32_type &&
136                             UnderlyingType != TypeManager.int64_type &&
137                             UnderlyingType != TypeManager.uint64_type &&
138                             UnderlyingType != TypeManager.short_type &&
139                             UnderlyingType != TypeManager.ushort_type &&
140                             UnderlyingType != TypeManager.byte_type  &&
141                             UnderlyingType != TypeManager.sbyte_type) {
142                                 Report.Error (30650, Location,
143                                               "Type byte, sbyte, short, ushort, int, uint, " +
144                                               "long, or ulong expected (got: " +
145                                               TypeManager.MonoBASIC_Name (UnderlyingType) + ")");
146                                 return null;
147                         }
148
149                         if (IsTopLevel) {
150                                 ModuleBuilder builder = CodeGen.ModuleBuilder;
151
152                                 TypeBuilder = builder.DefineType (Name, attr, TypeManager.enum_type);
153                         } else {
154                                 TypeBuilder builder = Parent.TypeBuilder;
155
156                                 TypeBuilder = builder.DefineNestedType (
157                                         Basename, attr, TypeManager.enum_type);
158                         }
159
160                         //
161                         // Call MapToInternalType for corlib
162                         //
163                         TypeBuilder.DefineField ("value__", UnderlyingType,
164                                                  FieldAttributes.Public | FieldAttributes.SpecialName
165                                                  | FieldAttributes.RTSpecialName);
166
167                         TypeManager.AddEnumType (Name, TypeBuilder, this);
168
169                         return TypeBuilder;
170                 }
171
172             bool IsValidEnumConstant (Expression e)
173                 {
174                         if (!(e is Constant))
175                                 return false;
176
177                         if (e is IntConstant || e is UIntConstant || e is LongConstant ||
178                             e is ByteConstant || e is SByteConstant || e is ShortConstant ||
179                             e is UShortConstant || e is ULongConstant || e is EnumConstant)
180                                 return true;
181                         else
182                                 return false;
183                 }
184
185                 object GetNextDefaultValue (object default_value)
186                 {
187                         if (UnderlyingType == TypeManager.int32_type) {
188                                 int i = (int) default_value;
189                                 
190                                 if (i < System.Int32.MaxValue)
191                                         return ++i;
192                                 else
193                                         return null;
194                         } else if (UnderlyingType == TypeManager.uint32_type) {
195                                 uint i = (uint) default_value;
196
197                                 if (i < System.UInt32.MaxValue)
198                                         return ++i;
199                                 else
200                                         return null;
201                         } else if (UnderlyingType == TypeManager.int64_type) {
202                                 long i = (long) default_value;
203
204                                 if (i < System.Int64.MaxValue)
205                                         return ++i;
206                                 else
207                                         return null;
208                         } else if (UnderlyingType == TypeManager.uint64_type) {
209                                 ulong i = (ulong) default_value;
210
211                                 if (i < System.UInt64.MaxValue)
212                                         return ++i;
213                                 else
214                                         return null;
215                         } else if (UnderlyingType == TypeManager.short_type) {
216                                 short i = (short) default_value;
217
218                                 if (i < System.Int16.MaxValue)
219                                         return ++i;
220                                 else
221                                         return null;
222                         } else if (UnderlyingType == TypeManager.ushort_type) {
223                                 ushort i = (ushort) default_value;
224
225                                 if (i < System.UInt16.MaxValue)
226                                         return ++i;
227                                 else
228                                         return null;
229                         } else if (UnderlyingType == TypeManager.byte_type) {
230                                 byte i = (byte) default_value;
231
232                                 if (i < System.Byte.MaxValue)
233                                         return ++i;
234                                 else
235                                         return null;
236                         } else if (UnderlyingType == TypeManager.sbyte_type) {
237                                 sbyte i = (sbyte) default_value;
238
239                                 if (i < System.SByte.MaxValue)
240                                         return ++i;
241                                 else
242                                         return null;
243                         }
244
245                         return null;
246                 }
247
248                 void Error_ConstantValueCannotBeConverted (object val, Location loc)
249                 {
250                         if (val is Constant)
251                                 Report.Error (30439, loc, "Constant value '" + ((Constant) val).AsString () +
252                                               "' cannot be converted" +
253                                               " to a " + TypeManager.MonoBASIC_Name (UnderlyingType));
254                         else 
255                                 Report.Error (30439, loc, "Constant value '" + val +
256                                               "' cannot be converted" +
257                                               " to a " + TypeManager.MonoBASIC_Name (UnderlyingType));
258                         return;
259                 }
260
261                 /// <summary>
262                 ///  Determines if a standard implicit conversion exists from
263                 ///  expr_type to target_type
264                 /// </summary>
265                 public static bool ImplicitConversionExists (Type expr_type, Type target_type)
266                 {
267                         expr_type = TypeManager.TypeToCoreType (expr_type);
268
269                         if (expr_type == TypeManager.void_type)
270                                 return false;
271                         
272                         if (expr_type == target_type)
273                                 return true;
274
275                         // First numeric conversions 
276
277                         if (expr_type == TypeManager.sbyte_type){
278                                 //
279                                 // From sbyte to short, int, long, float, double.
280                                 //
281                                 if ((target_type == TypeManager.int32_type) || 
282                                     (target_type == TypeManager.int64_type) ||
283                                     (target_type == TypeManager.double_type) ||
284                                     (target_type == TypeManager.float_type)  ||
285                                     (target_type == TypeManager.short_type) ||
286                                     (target_type == TypeManager.decimal_type))
287                                         return true;
288                                 
289                         } else if (expr_type == TypeManager.byte_type){
290                                 //
291                                 // From byte to short, ushort, int, uint, long, ulong, float, double
292                                 // 
293                                 if ((target_type == TypeManager.short_type) ||
294                                     (target_type == TypeManager.ushort_type) ||
295                                     (target_type == TypeManager.int32_type) ||
296                                     (target_type == TypeManager.uint32_type) ||
297                                     (target_type == TypeManager.uint64_type) ||
298                                     (target_type == TypeManager.int64_type) ||
299                                     (target_type == TypeManager.float_type) ||
300                                     (target_type == TypeManager.double_type) ||
301                                     (target_type == TypeManager.decimal_type))
302                                         return true;
303         
304                         } else if (expr_type == TypeManager.short_type){
305                                 //
306                                 // From short to int, long, float, double
307                                 // 
308                                 if ((target_type == TypeManager.int32_type) ||
309                                     (target_type == TypeManager.int64_type) ||
310                                     (target_type == TypeManager.double_type) ||
311                                     (target_type == TypeManager.float_type) ||
312                                     (target_type == TypeManager.decimal_type))
313                                         return true;
314                                         
315                         } else if (expr_type == TypeManager.ushort_type){
316                                 //
317                                 // From ushort to int, uint, long, ulong, float, double
318                                 //
319                                 if ((target_type == TypeManager.uint32_type) ||
320                                     (target_type == TypeManager.uint64_type) ||
321                                     (target_type == TypeManager.int32_type) ||
322                                     (target_type == TypeManager.int64_type) ||
323                                     (target_type == TypeManager.double_type) ||
324                                     (target_type == TypeManager.float_type) ||
325                                     (target_type == TypeManager.decimal_type))
326                                         return true;
327                                     
328                         } else if (expr_type == TypeManager.int32_type){
329                                 //
330                                 // From int to long, float, double
331                                 //
332                                 if ((target_type == TypeManager.int64_type) ||
333                                     (target_type == TypeManager.double_type) ||
334                                     (target_type == TypeManager.float_type) ||
335                                     (target_type == TypeManager.decimal_type))
336                                         return true;
337                                         
338                         } else if (expr_type == TypeManager.uint32_type){
339                                 //
340                                 // From uint to long, ulong, float, double
341                                 //
342                                 if ((target_type == TypeManager.int64_type) ||
343                                     (target_type == TypeManager.uint64_type) ||
344                                     (target_type == TypeManager.double_type) ||
345                                     (target_type == TypeManager.float_type) ||
346                                     (target_type == TypeManager.decimal_type))
347                                         return true;
348                                         
349                         } else if ((expr_type == TypeManager.uint64_type) ||
350                                    (expr_type == TypeManager.int64_type)) {
351                                 //
352                                 // From long/ulong to float, double
353                                 //
354                                 if ((target_type == TypeManager.double_type) ||
355                                     (target_type == TypeManager.float_type) ||
356                                     (target_type == TypeManager.decimal_type))
357                                         return true;
358                                     
359                         } else if (expr_type == TypeManager.char_type){
360                                 //
361                                 // From char to ushort, int, uint, long, ulong, float, double
362                                 // 
363                                 if ((target_type == TypeManager.ushort_type) ||
364                                     (target_type == TypeManager.int32_type) ||
365                                     (target_type == TypeManager.uint32_type) ||
366                                     (target_type == TypeManager.uint64_type) ||
367                                     (target_type == TypeManager.int64_type) ||
368                                     (target_type == TypeManager.float_type) ||
369                                     (target_type == TypeManager.double_type) ||
370                                     (target_type == TypeManager.decimal_type))
371                                         return true;
372
373                         } else if (expr_type == TypeManager.float_type){
374                                 //
375                                 // float to double
376                                 //
377                                 if (target_type == TypeManager.double_type)
378                                         return true;
379                         }       
380                         
381                         return false;
382                 }
383
384                 /// <summary>
385                 ///  This is used to lookup the value of an enum member. If the member is undefined,
386                 ///  it attempts to define it and return its value
387                 /// </summary>
388                 public object LookupEnumValue (EmitContext ec, string name, Location loc)
389                 {
390                         object default_value = null;
391                         Constant c = null;
392                         TypeContainer parent = ec.TypeContainer;
393
394                         // first check whether the requested name is there
395                         // in the member list of enum
396                         if (!(ordered_enums.Contains(name)))
397                                 Report.Error (30456, loc,
398                                         name + " is not found in member list of enum " + this.Name);
399
400                         default_value = member_to_value [name];
401                         
402                         if (default_value != null)
403                                 return default_value;
404
405                         //
406                         // This may happen if we're calling a method in System.Enum, for instance
407                         // Enum.IsDefined().
408                         //
409                         if (!defined_names.Contains (name))
410                                 return null;
411
412                         if (in_transit.Contains (name)) {
413                                 Report.Error (110, loc, "The evaluation of the constant value for `" +
414                                               Name + "." + name + "' involves a circular definition.");
415                                 return null;
416                         }
417
418                         //
419                         // So if the above doesn't happen, we have a member that is undefined
420                         // We now proceed to define it 
421                         //
422                         Expression val = this [name];
423                         int idx = ordered_enums.IndexOf (name);
424
425                         if (val == null) {
426                                 
427                                 //int idx = ordered_enums.IndexOf (name);
428
429                                 if (idx == 0)
430                                         default_value = 0;
431                                 else {
432                                         for (int i = 0; i < idx; ++i) {
433                                                 string n = (string) ordered_enums [i];
434                                                 Location m_loc = (Mono.MonoBASIC.Location)
435                                                         member_to_location [n];
436                                                 in_transit.Add (name, true);
437                                                 default_value = LookupEnumValue (ec, n, m_loc);
438                                                 in_transit.Remove (name);
439                                                 if (default_value == null)
440                                                         return null;
441                                         }
442                                         
443                                         default_value = GetNextDefaultValue (default_value);
444                                 }
445                                 
446                         } else {
447                                 // check for any cyclic dependency
448                                 if (val is Mono.MonoBASIC.SimpleName) {
449                                         int var_idx = ordered_enums.IndexOf (val.ToString());
450
451                                         if (idx <= var_idx)
452                                                 Report.Error(30500, loc,
453                                                         "The evaluation of the constant value for `" +
454                                                         Name + "." + name + "' involves a circular definition");
455                                         else
456                                                 default_value = member_to_value [val.ToString()];
457                                 }
458                                 else {
459                                         bool old = ec.InEnumContext;
460                                         ec.InEnumContext = true;
461                                         in_transit.Add (name, true);
462                                         val = val.Resolve (ec);
463                                         in_transit.Remove (name);
464                                         ec.InEnumContext = old;
465
466                                         if (val == null)
467                                                 return null;
468
469                                         if (!IsValidEnumConstant (val)) {
470                                                 Report.Error (
471                                                         30650, loc,
472                                                         "Type byte, sbyte, short, ushort, int, uint, long, or " +
473                                                         "ulong expected (have: " + val + ")");
474                                                 return null;
475                                         }
476
477                                         c = (Constant) val;
478                                 
479                                         default_value = c.GetValue ();
480                                 }
481
482                                 if (default_value == null) {
483                                         Error_ConstantValueCannotBeConverted (c, loc);
484                                         return null;
485                                 }
486
487                                 if (val is EnumConstant) {
488                                         Type etype = TypeManager.EnumToUnderlying (c.Type);
489                                         
490                                         if ( (!ImplicitConversionExists (etype, UnderlyingType)) &&
491                                                 (!Expression.RuntimeConversionExists (ec, val, UnderlyingType)) ){
492                                                 Expression.Error_CannotConvertImplicit (
493                                                         loc, c.Type, UnderlyingType);
494                                                 return null;
495                                         }
496                                 }
497                         }
498
499                         FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static
500                                         | FieldAttributes.Literal;
501                         
502                         FieldBuilder fb = TypeBuilder.DefineField (name, TypeBuilder, attr);
503
504                         try {
505                                 default_value = TypeManager.ChangeType (default_value, UnderlyingType);
506                         } catch {
507                                 Error_ConstantValueCannotBeConverted (c, loc);
508                                 return null;
509                         }
510
511                         fb.SetConstant (default_value);
512                         field_builders.Add (fb);
513                         member_to_value [name] = default_value;
514
515                         if (!TypeManager.RegisterFieldValue (fb, default_value))
516                                 return null;
517
518                         //
519                         // Now apply attributes
520                         //
521                         Attribute.ApplyAttributes (ec, fb, fb, (Attributes) member_to_attributes [name], loc); 
522                         
523                         return default_value;
524                 }
525
526                 public override bool DefineMembers (TypeContainer parent)
527                 {
528                         return true;
529                 }
530                 
531                 public override bool Define (TypeContainer parent)
532                 {
533                         //
534                         // If there was an error during DefineEnum, return
535                         //
536                         if (TypeBuilder == null)
537                                 return false;
538                         
539                         EmitContext ec = new EmitContext (parent, this, Location, null,
540                                                           UnderlyingType, ModFlags, false);
541                         
542                         object default_value = 0;
543                         
544                         foreach (string name in ordered_enums) {
545                                 if (member_to_value.Contains (name))
546                                         continue;
547                                 
548                                 Location loc = (Mono.MonoBASIC.Location) member_to_location [name];
549
550                                 if (this [name] != null) {
551                                         default_value = LookupEnumValue (ec, name, loc);
552
553                                         if (default_value == null)
554                                                 return true;
555                                 } else {
556                                         FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static
557                                                 | FieldAttributes.Literal;
558                         
559                                         FieldBuilder fb = TypeBuilder.DefineField (name, TypeBuilder, attr);
560
561                                         if (default_value == null) {
562                                                 Report.Error (30439, loc, "Enumerator value for '" + name + "' is too large to " +
563                                                         "fit in its type");
564                                                 return false;
565                                         }
566
567                                         try     {
568                                                 default_value = TypeManager.ChangeType (default_value, UnderlyingType);
569                                         } catch {
570                                                 Error_ConstantValueCannotBeConverted (default_value, loc);
571                                                 return false;
572                                         }
573
574                                         fb.SetConstant (default_value);
575                                         field_builders.Add (fb);
576                                         member_to_value [name] = default_value;
577
578                                         if (!TypeManager.RegisterFieldValue (fb, default_value))
579                                                 return false;
580
581                                         //
582                                         // Now apply attributes
583                                         //
584                                         Attribute.ApplyAttributes (ec, fb, fb, (Attributes) member_to_attributes [name], loc); 
585                                 }
586
587                                 default_value = GetNextDefaultValue (default_value);
588                         }
589                         
590                         Attribute.ApplyAttributes (ec, TypeBuilder, this, OptAttributes, Location);
591                         return true;
592                 }
593                 
594                 //
595                 // IMemberFinder
596                 //
597                 public override MemberList FindMembers (MemberTypes mt, BindingFlags bf,
598                                                         MemberFilter filter, object criteria)
599                 {
600                         ArrayList members = new ArrayList ();
601
602                         if ((mt & MemberTypes.Field) != 0) {
603                                 foreach (FieldBuilder fb in field_builders)
604                                         if (filter (fb, criteria) == true)
605                                                 members.Add (fb);
606                         }
607
608                         return new MemberList (members);
609                 }
610
611                 public override MemberCache MemberCache {
612                         get {
613                                 return null;
614                         }
615                 }
616
617                 public ArrayList ValueNames {
618                         get {
619                                 return ordered_enums;
620                         }
621                 }
622
623                 // indexer
624                 public Expression this [string name] {
625                         get {
626                                 return (Expression) defined_names [name];
627                         }
628                 }
629         }
630 }