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