* enum.cs: bug fixing - setting one enum member to another
[mono.git] / mcs / mbas / enum.cs
1 //
2 // enum.cs: Enum handling.
3 //
4 // Author: Miguel de Icaza (miguel@gnu.org)
5 //         Ravi Pratap     (ravi@ximian.com)
6 //
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.MonoBASIC {
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.PUBLIC, 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.sbyte_type) {
141                                 Report.Error (30650, Location,
142                                               "Type byte, sbyte, short, ushort, int, uint, " +
143                                               "long, or ulong expected (got: " +
144                                               TypeManager.MonoBASIC_Name (UnderlyingType) + ")");
145                                 return null;
146                         }
147
148                         if (IsTopLevel) {
149                                 ModuleBuilder builder = CodeGen.ModuleBuilder;
150
151                                 TypeBuilder = builder.DefineType (Name, attr, TypeManager.enum_type);
152                         } else {
153                                 TypeBuilder builder = Parent.TypeBuilder;
154
155                                 TypeBuilder = builder.DefineNestedType (
156                                         Basename, attr, TypeManager.enum_type);
157                         }
158
159                         //
160                         // Call MapToInternalType for corlib
161                         //
162                         TypeBuilder.DefineField ("value__", UnderlyingType,
163                                                  FieldAttributes.Public | FieldAttributes.SpecialName
164                                                  | FieldAttributes.RTSpecialName);
165
166                         TypeManager.AddEnumType (Name, TypeBuilder, this);
167
168                         return TypeBuilder;
169                 }
170
171             bool IsValidEnumConstant (Expression e)
172                 {
173                         if (!(e is Constant))
174                                 return false;
175
176                         if (e is IntConstant || e is UIntConstant || e is LongConstant ||
177                             e is ByteConstant || e is SByteConstant || e is ShortConstant ||
178                             e is UShortConstant || e is ULongConstant || e is EnumConstant)
179                                 return true;
180                         else
181                                 return false;
182                 }
183
184                 object GetNextDefaultValue (object default_value)
185                 {
186                         if (UnderlyingType == TypeManager.int32_type) {
187                                 int i = (int) default_value;
188                                 
189                                 if (i < System.Int32.MaxValue)
190                                         return ++i;
191                                 else
192                                         return null;
193                         } else if (UnderlyingType == TypeManager.uint32_type) {
194                                 uint i = (uint) default_value;
195
196                                 if (i < System.UInt32.MaxValue)
197                                         return ++i;
198                                 else
199                                         return null;
200                         } else if (UnderlyingType == TypeManager.int64_type) {
201                                 long i = (long) default_value;
202
203                                 if (i < System.Int64.MaxValue)
204                                         return ++i;
205                                 else
206                                         return null;
207                         } else if (UnderlyingType == TypeManager.uint64_type) {
208                                 ulong i = (ulong) default_value;
209
210                                 if (i < System.UInt64.MaxValue)
211                                         return ++i;
212                                 else
213                                         return null;
214                         } else if (UnderlyingType == TypeManager.short_type) {
215                                 short i = (short) default_value;
216
217                                 if (i < System.Int16.MaxValue)
218                                         return ++i;
219                                 else
220                                         return null;
221                         } else if (UnderlyingType == TypeManager.ushort_type) {
222                                 ushort i = (ushort) default_value;
223
224                                 if (i < System.UInt16.MaxValue)
225                                         return ++i;
226                                 else
227                                         return null;
228                         } else if (UnderlyingType == TypeManager.byte_type) {
229                                 byte i = (byte) default_value;
230
231                                 if (i < System.Byte.MaxValue)
232                                         return ++i;
233                                 else
234                                         return null;
235                         } else if (UnderlyingType == TypeManager.sbyte_type) {
236                                 sbyte i = (sbyte) default_value;
237
238                                 if (i < System.SByte.MaxValue)
239                                         return ++i;
240                                 else
241                                         return null;
242                         }
243
244                         return null;
245                 }
246
247                 void Error_ConstantValueCannotBeConverted (object val, Location loc)
248                 {
249                         if (val is Constant)
250                                 Report.Error (30439, loc, "Constant value '" + ((Constant) val).AsString () +
251                                               "' cannot be converted" +
252                                               " to a " + TypeManager.MonoBASIC_Name (UnderlyingType));
253                         else 
254                                 Report.Error (30439, loc, "Constant value '" + val +
255                                               "' cannot be converted" +
256                                               " to a " + TypeManager.MonoBASIC_Name (UnderlyingType));
257                         return;
258                 }
259
260                 /// <summary>
261                 ///  Determines if a standard implicit conversion exists from
262                 ///  expr_type to target_type
263                 /// </summary>
264                 public static bool ImplicitConversionExists (Type expr_type, Type target_type)
265                 {
266                         expr_type = TypeManager.TypeToCoreType (expr_type);
267
268                         if (expr_type == TypeManager.void_type)
269                                 return false;
270                         
271                         if (expr_type == target_type)
272                                 return true;
273
274                         // First numeric conversions 
275
276                         if (expr_type == TypeManager.sbyte_type){
277                                 //
278                                 // From sbyte to short, int, long, float, double.
279                                 //
280                                 if ((target_type == TypeManager.int32_type) || 
281                                     (target_type == TypeManager.int64_type) ||
282                                     (target_type == TypeManager.double_type) ||
283                                     (target_type == TypeManager.float_type)  ||
284                                     (target_type == TypeManager.short_type) ||
285                                     (target_type == TypeManager.decimal_type))
286                                         return true;
287                                 
288                         } else if (expr_type == TypeManager.byte_type){
289                                 //
290                                 // From byte to short, ushort, int, uint, long, ulong, float, double
291                                 // 
292                                 if ((target_type == TypeManager.short_type) ||
293                                     (target_type == TypeManager.ushort_type) ||
294                                     (target_type == TypeManager.int32_type) ||
295                                     (target_type == TypeManager.uint32_type) ||
296                                     (target_type == TypeManager.uint64_type) ||
297                                     (target_type == TypeManager.int64_type) ||
298                                     (target_type == TypeManager.float_type) ||
299                                     (target_type == TypeManager.double_type) ||
300                                     (target_type == TypeManager.decimal_type))
301                                         return true;
302         
303                         } else if (expr_type == TypeManager.short_type){
304                                 //
305                                 // From short to int, long, float, double
306                                 // 
307                                 if ((target_type == TypeManager.int32_type) ||
308                                     (target_type == TypeManager.int64_type) ||
309                                     (target_type == TypeManager.double_type) ||
310                                     (target_type == TypeManager.float_type) ||
311                                     (target_type == TypeManager.decimal_type))
312                                         return true;
313                                         
314                         } else if (expr_type == TypeManager.ushort_type){
315                                 //
316                                 // From ushort to int, uint, long, ulong, float, double
317                                 //
318                                 if ((target_type == TypeManager.uint32_type) ||
319                                     (target_type == TypeManager.uint64_type) ||
320                                     (target_type == TypeManager.int32_type) ||
321                                     (target_type == TypeManager.int64_type) ||
322                                     (target_type == TypeManager.double_type) ||
323                                     (target_type == TypeManager.float_type) ||
324                                     (target_type == TypeManager.decimal_type))
325                                         return true;
326                                     
327                         } else if (expr_type == TypeManager.int32_type){
328                                 //
329                                 // From int to long, float, double
330                                 //
331                                 if ((target_type == TypeManager.int64_type) ||
332                                     (target_type == TypeManager.double_type) ||
333                                     (target_type == TypeManager.float_type) ||
334                                     (target_type == TypeManager.decimal_type))
335                                         return true;
336                                         
337                         } else if (expr_type == TypeManager.uint32_type){
338                                 //
339                                 // From uint to long, ulong, float, double
340                                 //
341                                 if ((target_type == TypeManager.int64_type) ||
342                                     (target_type == TypeManager.uint64_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.uint64_type) ||
349                                    (expr_type == TypeManager.int64_type)) {
350                                 //
351                                 // From long/ulong to float, double
352                                 //
353                                 if ((target_type == TypeManager.double_type) ||
354                                     (target_type == TypeManager.float_type) ||
355                                     (target_type == TypeManager.decimal_type))
356                                         return true;
357                                     
358                         } else if (expr_type == TypeManager.char_type){
359                                 //
360                                 // From char to ushort, int, uint, long, ulong, float, double
361                                 // 
362                                 if ((target_type == TypeManager.ushort_type) ||
363                                     (target_type == TypeManager.int32_type) ||
364                                     (target_type == TypeManager.uint32_type) ||
365                                     (target_type == TypeManager.uint64_type) ||
366                                     (target_type == TypeManager.int64_type) ||
367                                     (target_type == TypeManager.float_type) ||
368                                     (target_type == TypeManager.double_type) ||
369                                     (target_type == TypeManager.decimal_type))
370                                         return true;
371
372                         } else if (expr_type == TypeManager.float_type){
373                                 //
374                                 // float to double
375                                 //
376                                 if (target_type == TypeManager.double_type)
377                                         return true;
378                         }       
379                         
380                         return false;
381                 }
382
383                 /// <summary>
384                 ///  This is used to lookup the value of an enum member. If the member is undefined,
385                 ///  it attempts to define it and return its value
386                 /// </summary>
387                 public object LookupEnumValue (EmitContext ec, string name, Location loc)
388                 {
389                         object default_value = null;
390                         Constant c = null;
391                         TypeContainer parent = ec.TypeContainer;
392
393                         // first check whether the requested name is there
394                         // in the member list of enum
395                         if (!(ordered_enums.Contains(name)))
396                                 Report.Error (30456, loc,
397                                         name + " is not found in member list of enum " + this.Name);
398
399                         default_value = member_to_value [name];
400                         
401                         if (default_value != null)
402                                 return default_value;
403
404                         //
405                         // This may happen if we're calling a method in System.Enum, for instance
406                         // Enum.IsDefined().
407                         //
408                         if (!defined_names.Contains (name))
409                                 return null;
410
411                         if (in_transit.Contains (name)) {
412                                 Report.Error (110, loc, "The evaluation of the constant value for `" +
413                                               Name + "." + name + "' involves a circular definition.");
414                                 return null;
415                         }
416
417                         //
418                         // So if the above doesn't happen, we have a member that is undefined
419                         // We now proceed to define it 
420                         //
421                         Expression val = this [name];
422                         int idx = ordered_enums.IndexOf (name);
423
424                         if (val == null) {
425                                 
426                                 //int idx = ordered_enums.IndexOf (name);
427
428                                 if (idx == 0)
429                                         default_value = 0;
430                                 else {
431                                         for (int i = 0; i < idx; ++i) {
432                                                 string n = (string) ordered_enums [i];
433                                                 Location m_loc = (Mono.MonoBASIC.Location)
434                                                         member_to_location [n];
435                                                 in_transit.Add (name, true);
436                                                 default_value = LookupEnumValue (ec, n, m_loc);
437                                                 in_transit.Remove (name);
438                                                 if (default_value == null)
439                                                         return null;
440                                         }
441                                         
442                                         default_value = GetNextDefaultValue (default_value);
443                                 }
444                                 
445                         } else {
446                                 // check for any cyclic dependency
447                                 if (val is Mono.MonoBASIC.SimpleName) {
448                                         int var_idx = ordered_enums.IndexOf (val.ToString());
449
450                                         if (idx <= var_idx)
451                                                 Report.Error(30500, loc,
452                                                         "The evaluation of the constant value for `" +
453                                                         Name + "." + name + "' involves a circular definition");
454                                         else
455                                                 default_value = member_to_value [val.ToString()];
456                                 }
457                                 else {
458                                         bool old = ec.InEnumContext;
459                                         ec.InEnumContext = true;
460                                         in_transit.Add (name, true);
461                                         val = val.Resolve (ec);
462                                         in_transit.Remove (name);
463                                         ec.InEnumContext = old;
464
465                                         if (val == null)
466                                                 return null;
467
468                                         if (!IsValidEnumConstant (val)) {
469                                                 Report.Error (
470                                                         30650, loc,
471                                                         "Type byte, sbyte, short, ushort, int, uint, long, or " +
472                                                         "ulong expected (have: " + val + ")");
473                                                 return null;
474                                         }
475
476                                         c = (Constant) val;
477                                 
478                                         default_value = c.GetValue ();
479                                 }
480
481                                 if (default_value == null) {
482                                         Error_ConstantValueCannotBeConverted (c, loc);
483                                         return null;
484                                 }
485
486                                 if (val is EnumConstant) {
487                                         Type etype = TypeManager.EnumToUnderlying (c.Type);
488                                         
489                                         if ( (!ImplicitConversionExists (etype, UnderlyingType)) &&
490                                                 (!Expression.RuntimeConversionExists (ec, val, UnderlyingType)) ){
491                                                 Expression.Error_CannotConvertImplicit (
492                                                         loc, c.Type, UnderlyingType);
493                                                 return null;
494                                         }
495                                 }
496                         }
497
498                         /*FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static
499                                         | FieldAttributes.Literal;
500                         */
501                         FieldBuilder fb = TypeBuilder.DefineField (name, UnderlyingType, 
502                                                                         /*attr*/ Modifiers.FieldAttr (ModFlags));
503
504                         try {
505                                 default_value = TypeManager.ChangeType (default_value, UnderlyingType);
506                         } catch {
507                                 Error_ConstantValueCannotBeConverted (c, loc);
508                                 return null;
509                         }
510
511                         fb.SetConstant (default_value);
512                         field_builders.Add (fb);
513                         member_to_value [name] = default_value;
514
515                         if (!TypeManager.RegisterFieldValue (fb, default_value))
516                                 return null;
517
518                         //
519                         // Now apply attributes
520                         //
521                         Attribute.ApplyAttributes (ec, fb, fb, (Attributes) member_to_attributes [name], loc); 
522                         
523                         return default_value;
524                 }
525
526                 public override bool DefineMembers (TypeContainer parent)
527                 {
528                         return true;
529                 }
530                 
531                 public override bool Define (TypeContainer parent)
532                 {
533                         //
534                         // If there was an error during DefineEnum, return
535                         //
536                         if (TypeBuilder == null)
537                                 return false;
538                         
539                         EmitContext ec = new EmitContext (parent, this, Location, null,
540                                                           UnderlyingType, ModFlags, false);
541                         
542                         object default_value = 0;
543                         
544                         /*FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static
545                                              | FieldAttributes.Literal;
546                         */
547                         
548                         foreach (string name in ordered_enums) {
549                                 //
550                                 // Have we already been defined, thanks to some cross-referencing ?
551                                 // 
552
553                                 if (member_to_value.Contains (name))
554                                         continue;
555                                 
556                                 Location loc = (Mono.MonoBASIC.Location) member_to_location [name];
557
558                                 if (this [name] != null) {
559                                         default_value = LookupEnumValue (ec, name, loc);
560
561                                         if (default_value == null)
562                                                 return true;
563
564                                 } else {
565                                         FieldBuilder fb = TypeBuilder.DefineField (
566                                                 name, UnderlyingType, 
567                                                         /*attr*/ Modifiers.FieldAttr (ModFlags));
568                                         
569                                         if (default_value == null) {
570                                            Report.Error (30439, loc, "Enumerator value for '" + name + "' is too large to " +
571                                                               "fit in its type");
572                                                 return false;
573                                         }
574                                         
575                                         try {
576                                                 default_value = TypeManager.ChangeType (default_value, UnderlyingType);
577                                         } catch {
578                                                 Error_ConstantValueCannotBeConverted (default_value, loc);
579                                                 return false;
580                                         }
581
582                                         fb.SetConstant (default_value);
583                                         field_builders.Add (fb);
584                                         member_to_value [name] = default_value;
585
586                                         if (!TypeManager.RegisterFieldValue (fb, default_value))
587                                                 return false;
588
589                                         //
590                                         // Apply attributes on the enum member
591                                         //
592                                         Attribute.ApplyAttributes (ec, fb, fb, (Attributes) member_to_attributes [name], loc);
593                                 }
594
595                                 default_value = GetNextDefaultValue (default_value);
596                         }
597                         
598                         Attribute.ApplyAttributes (ec, TypeBuilder, this, OptAttributes, Location);
599
600                         return true;
601                 }
602                 
603                 //
604                 // IMemberFinder
605                 //
606                 public override MemberList FindMembers (MemberTypes mt, BindingFlags bf,
607                                                         MemberFilter filter, object criteria)
608                 {
609                         ArrayList members = new ArrayList ();
610
611                         if ((mt & MemberTypes.Field) != 0) {
612                                 foreach (FieldBuilder fb in field_builders)
613                                         if (filter (fb, criteria) == true)
614                                                 members.Add (fb);
615                         }
616
617                         return new MemberList (members);
618                 }
619
620                 public override MemberCache MemberCache {
621                         get {
622                                 return null;
623                         }
624                 }
625
626                 public ArrayList ValueNames {
627                         get {
628                                 return ordered_enums;
629                         }
630                 }
631
632                 // indexer
633                 public Expression this [string name] {
634                         get {
635                                 return (Expression) defined_names [name];
636                         }
637                 }
638         }
639 }