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