2005-08-02 Marek Safar <marek.safar@seznam.cz>
[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 //         Marek Safar     (marek.safar@seznam.cz)
7 //
8 // Licensed under the terms of the GNU GPL
9 //
10 // (C) 2001 Ximian, Inc (http://www.ximian.com)
11 //
12
13 using System;
14 using System.Collections;
15 using System.Collections.Specialized;
16 using System.Reflection;
17 using System.Reflection.Emit;
18 using System.Globalization;
19
20 namespace Mono.CSharp {
21
22         class EnumMember: MemberCore {
23                 static string[] attribute_targets = new string [] { "field" };
24
25                 Enum parent_enum;
26                 public FieldBuilder builder;
27                 internal readonly Expression Type;
28
29                 public EnumMember (Enum parent_enum, Expression expr, string name,
30                                    Location loc, Attributes attrs):
31                         base (null, new MemberName (name), attrs, loc)
32                 {
33                         this.parent_enum = parent_enum;
34                         this.ModFlags = parent_enum.ModFlags;
35                         this.Type = expr;
36                 }
37
38                 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder cb)
39                 {
40                         if (a.Type == TypeManager.marshal_as_attr_type) {
41                                 UnmanagedMarshal marshal = a.GetMarshal (this);
42                                 if (marshal != null) {
43                                         builder.SetMarshal (marshal);
44                                 }
45                                 return;
46                         }
47
48                         if (a.Type.IsSubclassOf (TypeManager.security_attr_type)) {
49                                 a.Error_InvalidSecurityParent ();
50                                 return;
51                         }
52
53                         builder.SetCustomAttribute (cb);
54                 }
55
56                 public override AttributeTargets AttributeTargets {
57                         get {
58                                 return AttributeTargets.Field;
59                         }
60                 }
61
62                 public void DefineMember (TypeBuilder tb)
63                 {
64                         FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static
65                                 | FieldAttributes.Literal;
66                         
67                         builder = tb.DefineField (Name, tb, attr);
68                 }
69
70                 public override bool Define ()
71                 {
72                         throw new NotImplementedException ();
73                 }
74
75                 public void Emit (EmitContext ec)
76                 {
77                         if (OptAttributes != null)
78                                 OptAttributes.Emit (ec, this); 
79
80                         Emit ();
81                 }
82
83                 public override string GetSignatureForError()
84                 {
85                         return String.Concat (parent_enum.GetSignatureForError (), '.', base.GetSignatureForError ());
86                 }
87
88                 public override string[] ValidAttributeTargets {
89                         get {
90                                 return attribute_targets;
91                         }
92                 }
93
94                 protected override bool VerifyClsCompliance(DeclSpace ds)
95                 {
96                         // Because parent is TypeContainer and we have only DeclSpace parent.
97                         // Parameter replacing is required
98                         return base.VerifyClsCompliance (parent_enum);
99                 }
100
101                 // There is no base type
102                 protected override void VerifyObsoleteAttribute()
103                 {
104                 }
105
106                 public override string DocCommentHeader {
107                         get { return "F:"; }
108                 }
109         }
110
111         /// <summary>
112         ///   Enumeration container
113         /// </summary>
114         public class Enum : DeclSpace {
115                 public ArrayList ordered_enums;
116                 
117                 public Expression BaseType;
118                 
119                 public Type UnderlyingType;
120
121                 Hashtable member_to_location;
122
123                 //
124                 // This is for members that have been defined
125                 //
126                 Hashtable member_to_value;
127
128                 //
129                 // This is used to mark members we're currently defining
130                 //
131                 Hashtable in_transit;
132                 
133                 ArrayList field_builders;
134                 
135                 public const int AllowedModifiers =
136                         Modifiers.NEW |
137                         Modifiers.PUBLIC |
138                         Modifiers.PROTECTED |
139                         Modifiers.INTERNAL |
140                         Modifiers.PRIVATE;
141
142                 public Enum (NamespaceEntry ns, TypeContainer parent, Expression type,
143                              int mod_flags, MemberName name, Attributes attrs, Location l)
144                         : base (ns, parent, name, attrs, l)
145                 {
146                         this.BaseType = type;
147                         ModFlags = Modifiers.Check (AllowedModifiers, mod_flags,
148                                                     IsTopLevel ? Modifiers.INTERNAL : Modifiers.PRIVATE, l);
149
150                         ordered_enums = new ArrayList ();
151                         member_to_location = new Hashtable ();
152                         member_to_value = new Hashtable ();
153                         in_transit = new Hashtable ();
154                         field_builders = new ArrayList ();
155                 }
156
157                 /// <summary>
158                 ///   Adds @name to the enumeration space, with @expr
159                 ///   being its definition.  
160                 /// </summary>
161                 public void AddEnumMember (string name, Expression expr, Location loc, Attributes opt_attrs, string documentation)
162                 {
163                         if (name == "value__") {
164                                 Report.Error (76, loc, "An item in an enumeration cannot have an identifier `value__'");
165                                 return;
166                         }
167
168                         EnumMember em = new EnumMember (this, expr, name, loc, opt_attrs);
169                         em.DocComment = documentation;
170                         if (!AddToContainer (em, name))
171                                 return;
172
173
174                         // TODO: can be almost deleted
175                         ordered_enums.Add (name);
176                         member_to_location.Add (name, loc);
177                 }
178                 
179                 public override TypeBuilder DefineType ()
180                 {
181                         if (TypeBuilder != null)
182                                 return TypeBuilder;
183
184                         ec = new EmitContext (this, this, Location, null, null, ModFlags, false);
185                         ec.InEnumContext = true;
186
187                         if (!(BaseType is TypeLookupExpression)) {
188                                 Report.Error (1008, Location,
189                                         "Type byte, sbyte, short, ushort, int, uint, long or ulong expected");
190                                 return null;
191                         }
192
193                         TypeExpr ute = ResolveBaseTypeExpr (BaseType, false, Location);
194                         UnderlyingType = ute.Type;
195
196                         if (UnderlyingType != TypeManager.int32_type &&
197                             UnderlyingType != TypeManager.uint32_type &&
198                             UnderlyingType != TypeManager.int64_type &&
199                             UnderlyingType != TypeManager.uint64_type &&
200                             UnderlyingType != TypeManager.short_type &&
201                             UnderlyingType != TypeManager.ushort_type &&
202                             UnderlyingType != TypeManager.byte_type  &&
203                             UnderlyingType != TypeManager.sbyte_type) {
204                                 Report.Error (1008, Location,
205                                         "Type byte, sbyte, short, ushort, int, uint, long or ulong expected");
206                                 return null;
207                         }
208
209                         if (IsTopLevel) {
210                                 if (TypeManager.NamespaceClash (Name, Location))
211                                         return null;
212                                 
213                                 ModuleBuilder builder = CodeGen.Module.Builder;
214
215                                 TypeBuilder = builder.DefineType (Name, TypeAttr, TypeManager.enum_type);
216                         } else {
217                                 TypeBuilder builder = Parent.TypeBuilder;
218
219                                 TypeBuilder = builder.DefineNestedType (
220                                         Basename, TypeAttr, TypeManager.enum_type);
221                         }
222
223                         ec.ContainerType = TypeBuilder;
224
225                         //
226                         // Call MapToInternalType for corlib
227                         //
228                         TypeBuilder.DefineField ("value__", UnderlyingType,
229                                                  FieldAttributes.Public | FieldAttributes.SpecialName
230                                                  | FieldAttributes.RTSpecialName);
231
232                         TypeManager.AddUserType (Name, this);
233
234                         return TypeBuilder;
235                 }
236
237                 bool IsValidEnumConstant (Expression e)
238                 {
239                         if (!(e is Constant))
240                                 return false;
241
242                         if (e is IntConstant || e is UIntConstant || e is LongConstant ||
243                             e is ByteConstant || e is SByteConstant || e is ShortConstant ||
244                             e is UShortConstant || e is ULongConstant || e is EnumConstant ||
245                             e is CharConstant)
246                                 return true;
247                         else
248                                 return false;
249                 }
250
251                 object GetNextDefaultValue (object default_value)
252                 {
253                         if (UnderlyingType == TypeManager.int32_type) {
254                                 int i = (int) default_value;
255                                 
256                                 if (i < System.Int32.MaxValue)
257                                         return ++i;
258                                 else
259                                         return null;
260                         } else if (UnderlyingType == TypeManager.uint32_type) {
261                                 uint i = (uint) default_value;
262
263                                 if (i < System.UInt32.MaxValue)
264                                         return ++i;
265                                 else
266                                         return null;
267                         } else if (UnderlyingType == TypeManager.int64_type) {
268                                 long i = (long) default_value;
269
270                                 if (i < System.Int64.MaxValue)
271                                         return ++i;
272                                 else
273                                         return null;
274                         } else if (UnderlyingType == TypeManager.uint64_type) {
275                                 ulong i = (ulong) default_value;
276
277                                 if (i < System.UInt64.MaxValue)
278                                         return ++i;
279                                 else
280                                         return null;
281                         } else if (UnderlyingType == TypeManager.short_type) {
282                                 short i = (short) default_value;
283
284                                 if (i < System.Int16.MaxValue)
285                                         return ++i;
286                                 else
287                                         return null;
288                         } else if (UnderlyingType == TypeManager.ushort_type) {
289                                 ushort i = (ushort) default_value;
290
291                                 if (i < System.UInt16.MaxValue)
292                                         return ++i;
293                                 else
294                                         return null;
295                         } else if (UnderlyingType == TypeManager.byte_type) {
296                                 byte i = (byte) default_value;
297
298                                 if (i < System.Byte.MaxValue)
299                                         return ++i;
300                                 else
301                                         return null;
302                         } else if (UnderlyingType == TypeManager.sbyte_type) {
303                                 sbyte i = (sbyte) default_value;
304
305                                 if (i < System.SByte.MaxValue)
306                                         return ++i;
307                                 else
308                                         return null;
309                         }
310
311                         return null;
312                 }
313
314                 /// <summary>
315                 ///  Determines if a standard implicit conversion exists from
316                 ///  expr_type to target_type
317                 /// </summary>
318                 public static bool ImplicitConversionExists (Type expr_type, Type target_type)
319                 {
320                         expr_type = TypeManager.TypeToCoreType (expr_type);
321
322                         if (expr_type == TypeManager.void_type)
323                                 return false;
324                         
325                         if (expr_type == target_type)
326                                 return true;
327
328                         // First numeric conversions 
329
330                         if (expr_type == TypeManager.sbyte_type){
331                                 //
332                                 // From sbyte to short, int, long, float, double.
333                                 //
334                                 if ((target_type == TypeManager.int32_type) || 
335                                     (target_type == TypeManager.int64_type) ||
336                                     (target_type == TypeManager.double_type) ||
337                                     (target_type == TypeManager.float_type)  ||
338                                     (target_type == TypeManager.short_type) ||
339                                     (target_type == TypeManager.decimal_type))
340                                         return true;
341                                 
342                         } else if (expr_type == TypeManager.byte_type){
343                                 //
344                                 // From byte to short, ushort, int, uint, long, ulong, float, double
345                                 // 
346                                 if ((target_type == TypeManager.short_type) ||
347                                     (target_type == TypeManager.ushort_type) ||
348                                     (target_type == TypeManager.int32_type) ||
349                                     (target_type == TypeManager.uint32_type) ||
350                                     (target_type == TypeManager.uint64_type) ||
351                                     (target_type == TypeManager.int64_type) ||
352                                     (target_type == TypeManager.float_type) ||
353                                     (target_type == TypeManager.double_type) ||
354                                     (target_type == TypeManager.decimal_type))
355                                         return true;
356         
357                         } else if (expr_type == TypeManager.short_type){
358                                 //
359                                 // From short to int, long, float, double
360                                 // 
361                                 if ((target_type == TypeManager.int32_type) ||
362                                     (target_type == TypeManager.int64_type) ||
363                                     (target_type == TypeManager.double_type) ||
364                                     (target_type == TypeManager.float_type) ||
365                                     (target_type == TypeManager.decimal_type))
366                                         return true;
367                                         
368                         } else if (expr_type == TypeManager.ushort_type){
369                                 //
370                                 // From ushort to int, uint, long, ulong, float, double
371                                 //
372                                 if ((target_type == TypeManager.uint32_type) ||
373                                     (target_type == TypeManager.uint64_type) ||
374                                     (target_type == TypeManager.int32_type) ||
375                                     (target_type == TypeManager.int64_type) ||
376                                     (target_type == TypeManager.double_type) ||
377                                     (target_type == TypeManager.float_type) ||
378                                     (target_type == TypeManager.decimal_type))
379                                         return true;
380                                     
381                         } else if (expr_type == TypeManager.int32_type){
382                                 //
383                                 // From int to long, float, double
384                                 //
385                                 if ((target_type == TypeManager.int64_type) ||
386                                     (target_type == TypeManager.double_type) ||
387                                     (target_type == TypeManager.float_type) ||
388                                     (target_type == TypeManager.decimal_type))
389                                         return true;
390                                         
391                         } else if (expr_type == TypeManager.uint32_type){
392                                 //
393                                 // From uint to long, ulong, float, double
394                                 //
395                                 if ((target_type == TypeManager.int64_type) ||
396                                     (target_type == TypeManager.uint64_type) ||
397                                     (target_type == TypeManager.double_type) ||
398                                     (target_type == TypeManager.float_type) ||
399                                     (target_type == TypeManager.decimal_type))
400                                         return true;
401                                         
402                         } else if ((expr_type == TypeManager.uint64_type) ||
403                                    (expr_type == TypeManager.int64_type)) {
404                                 //
405                                 // From long/ulong to float, double
406                                 //
407                                 if ((target_type == TypeManager.double_type) ||
408                                     (target_type == TypeManager.float_type) ||
409                                     (target_type == TypeManager.decimal_type))
410                                         return true;
411                                     
412                         } else if (expr_type == TypeManager.char_type){
413                                 //
414                                 // From char to ushort, int, uint, long, ulong, float, double
415                                 // 
416                                 if ((target_type == TypeManager.ushort_type) ||
417                                     (target_type == TypeManager.int32_type) ||
418                                     (target_type == TypeManager.uint32_type) ||
419                                     (target_type == TypeManager.uint64_type) ||
420                                     (target_type == TypeManager.int64_type) ||
421                                     (target_type == TypeManager.float_type) ||
422                                     (target_type == TypeManager.double_type) ||
423                                     (target_type == TypeManager.decimal_type))
424                                         return true;
425
426                         } else if (expr_type == TypeManager.float_type){
427                                 //
428                                 // float to double
429                                 //
430                                 if (target_type == TypeManager.double_type)
431                                         return true;
432                         }       
433                         
434                         return false;
435                 }
436
437                 /// <summary>
438                 ///  This is used to lookup the value of an enum member. If the member is undefined,
439                 ///  it attempts to define it and return its value
440                 /// </summary>
441                 public object LookupEnumValue (string name, Location loc)
442                 {
443                         if (ec == null)
444                                 Report.Error (-1, loc, "Enum.LookupEnumValue () called too soon");
445                         
446                         object default_value = null;
447                         Constant c = null;
448
449                         default_value = member_to_value [name];
450
451                         if (default_value != null)
452                                 return default_value;
453
454                         //
455                         // This may happen if we're calling a method in System.Enum, for instance
456                         // Enum.IsDefined().
457                         //
458                         if (!defined_names.Contains (name))
459                                 return null;
460
461                         if (in_transit.Contains (name)) {
462                                 Report.Error (110, loc, "The evaluation of the constant value for `" +
463                                               Name + "." + name + "' involves a circular definition.");
464                                 return null;
465                         }
466
467                         //
468                         // So if the above doesn't happen, we have a member that is undefined
469                         // We now proceed to define it 
470                         //
471                         Expression val = this [name];
472
473                         if (val == null) {
474                                 
475                                 int idx = ordered_enums.IndexOf (name);
476
477                                 if (idx == 0)
478                                         default_value = 0;
479                                 else {
480                                         for (int i = 0; i < idx; ++i) {
481                                                 string n = (string) ordered_enums [i];
482                                                 Location m_loc = (Mono.CSharp.Location)
483                                                         member_to_location [n];
484                                                 in_transit.Add (name, true);
485                                                 default_value = LookupEnumValue (n, m_loc);
486                                                 in_transit.Remove (name);
487                                                 if (default_value == null)
488                                                         return null;
489                                         }
490                                         
491                                         default_value = GetNextDefaultValue (default_value);
492                                 }
493                                 
494                         } else {
495                                 in_transit.Add (name, true);
496                                 val = val.Resolve (EmitContext);
497                                 in_transit.Remove (name);
498
499                                 if (val == null)
500                                         return null;
501
502                                 if (!IsValidEnumConstant (val)) {
503                                         Report.Error (1008, loc,
504                                                 "Type byte, sbyte, short, ushort, int, uint, long or ulong expected");
505                                         return null;
506                                 }
507
508                                 c = (Constant) val;
509                                 default_value = c.GetValue ();
510
511                                 if (default_value == null) {
512                                         c.Error_ConstantValueCannotBeConverted (loc, UnderlyingType);
513                                         return null;
514                                 }
515
516                                 if (val is EnumConstant){
517                                         Type etype = TypeManager.EnumToUnderlying (c.Type);
518                                         
519                                         if (!ImplicitConversionExists (etype, UnderlyingType)){
520                                                 Convert.Error_CannotImplicitConversion (
521                                                         loc, c.Type, UnderlyingType);
522                                                 return null;
523                                         }
524                                 }
525                         }
526
527                         EnumMember em = (EnumMember) defined_names [name];
528                         em.DefineMember (TypeBuilder);
529
530                         bool fail;
531                         default_value = TypeManager.ChangeType (default_value, UnderlyingType, out fail);
532                         if (fail){
533                                 c.Error_ConstantValueCannotBeConverted (loc, UnderlyingType);
534                                 return null;
535                         }
536
537                         em.builder.SetConstant (default_value);
538                         field_builders.Add (em.builder);
539                         member_to_value [name] = default_value;
540
541                         if (!TypeManager.RegisterFieldValue (em.builder, default_value))
542                                 return null;
543
544                         return default_value;
545                 }
546                 
547                 public override bool Define ()
548                 {
549                         //
550                         // If there was an error during DefineEnum, return
551                         //
552                         if (TypeBuilder == null)
553                                 return false;
554
555                         if (ec == null)
556                                 throw new InternalErrorException ("Enum.Define () called too soon");
557                         
558                         object default_value = 0;
559                         
560                 
561                         foreach (string name in ordered_enums) {
562                                 //
563                                 // Have we already been defined, thanks to some cross-referencing ?
564                                 // 
565                                 if (member_to_value.Contains (name))
566                                         continue;
567                                 
568                                 Location loc = (Mono.CSharp.Location) member_to_location [name];
569
570                                 if (this [name] != null) {
571                                         default_value = LookupEnumValue (name, loc);
572
573                                         if (default_value == null)
574                                                 return true;
575                                 } else {
576                                         EnumMember em = (EnumMember) defined_names [name];
577
578                                         em.DefineMember (TypeBuilder);
579                                         FieldBuilder fb = em.builder;
580                                         
581                                         if (default_value == null) {
582                                            Report.Error (543, loc, "The enumerator value `{0}.{1}' is too large to fit in its type `{2}'",
583                                                    GetSignatureForError (), name, TypeManager.CSharpName (this.UnderlyingType));
584                                                 return false;
585                                         }
586
587                                         bool fail;
588                                         default_value = TypeManager.ChangeType (default_value, UnderlyingType, out fail);
589                                         if (fail){
590                                                 //TODO: really interested if can be reached
591                                                 throw new NotImplementedException ();
592                                         }
593
594                                         fb.SetConstant (default_value);
595                                         field_builders.Add (fb);
596                                         member_to_value [name] = default_value;
597                                         
598                                         if (!TypeManager.RegisterFieldValue (fb, default_value))
599                                                 return false;
600                                 }
601
602                                 default_value = GetNextDefaultValue (default_value);
603                         }
604
605                         return true;
606                 }
607
608                 public override void Emit ()
609                 {
610                         if (OptAttributes != null) {
611                                 OptAttributes.Emit (ec, this);
612                         }
613
614                         foreach (EnumMember em in defined_names.Values) {
615                                 em.Emit (ec);
616                         }
617
618                         base.Emit ();
619                 }
620                 
621                 void VerifyClsName ()
622                 {
623                         HybridDictionary dict = new HybridDictionary (defined_names.Count, true);
624                         foreach (EnumMember em in defined_names.Values) {
625                                 if (!em.IsClsCompliaceRequired (this))
626                                         continue;
627
628                                 try {
629                                         dict.Add (em.Name, em);
630                                 }
631                                 catch (ArgumentException) {
632                                         Report.SymbolRelatedToPreviousError (em);
633                                         MemberCore col = (MemberCore)dict [em.Name];
634                                         Report.Error (3005, col.Location, "Identifier `{0}' differing only in case is not CLS-compliant", col.GetSignatureForError ());
635                                 }
636                         }
637                 }
638
639                 protected override bool VerifyClsCompliance (DeclSpace ds)
640                 {
641                         if (!base.VerifyClsCompliance (ds))
642                                 return false;
643
644                         VerifyClsName ();
645
646                         if (UnderlyingType == TypeManager.uint32_type ||
647                                 UnderlyingType == TypeManager.uint64_type ||
648                                 UnderlyingType == TypeManager.ushort_type) {
649                                 Report.Error (3009, Location, "`{0}': base type `{1}' is not CLS-compliant", GetSignatureForError (), TypeManager.CSharpName (UnderlyingType));
650                         }
651
652                         return true;
653                 }
654                 
655                 //
656                 // IMemberFinder
657                 //
658                 public override MemberList FindMembers (MemberTypes mt, BindingFlags bf,
659                                                         MemberFilter filter, object criteria)
660                 {
661                         ArrayList members = new ArrayList ();
662
663                         if ((mt & MemberTypes.Field) != 0) {
664                                 if (criteria is string && member_to_value [criteria] == null) {
665                                         LookupEnumValue ((string) criteria, Location.Null);
666                                 }
667                                 
668                                 foreach (FieldBuilder fb in field_builders)
669                                         if (filter (fb, criteria) == true)
670                                                 members.Add (fb);
671                         }
672
673                         return new MemberList (members);
674                 }
675
676                 public override MemberCache MemberCache {
677                         get {
678                                 return null;
679                         }
680                 }
681
682                 public ArrayList ValueNames {
683                         get {
684                                 return ordered_enums;
685                         }
686                 }
687
688                 // indexer
689                 public Expression this [string name] {
690                         get {
691                                 return ((EnumMember) defined_names [name]).Type;
692                         }
693                 }
694
695                 public override AttributeTargets AttributeTargets {
696                         get {
697                                 return AttributeTargets.Enum;
698                         }
699                 }
700
701                 protected override TypeAttributes TypeAttr {
702                         get {
703                                 return Modifiers.TypeAttr (ModFlags, IsTopLevel) |
704                                 TypeAttributes.Class | TypeAttributes.Sealed |
705                                 base.TypeAttr;
706                         }
707                 }
708
709
710                 protected override void VerifyObsoleteAttribute()
711                 {
712                         // UnderlyingType is never obsolete
713                 }
714
715                 //
716                 // Generates xml doc comments (if any), and if required,
717                 // handle warning report.
718                 //
719                 internal override void GenerateDocComment (DeclSpace ds)
720                 {
721                         DocUtil.GenerateEnumDocComment (this, ds);
722                 }
723
724                 //
725                 //   Represents header string for documentation comment.
726                 //
727                 public override string DocCommentHeader {
728                         get { return "T:"; }
729                 }
730         }
731 }