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