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