2005-07-14 Sureshkumar T <tsureshkumar@novell.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 cannot 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, long or ulong expected");
188                                 return null;
189                         }
190
191                         TypeExpr ute = ResolveBaseTypeExpr (BaseType, false, Location);
192                         UnderlyingType = ute.Type;
193
194                         if (UnderlyingType != TypeManager.int32_type &&
195                             UnderlyingType != TypeManager.uint32_type &&
196                             UnderlyingType != TypeManager.int64_type &&
197                             UnderlyingType != TypeManager.uint64_type &&
198                             UnderlyingType != TypeManager.short_type &&
199                             UnderlyingType != TypeManager.ushort_type &&
200                             UnderlyingType != TypeManager.byte_type  &&
201                             UnderlyingType != TypeManager.sbyte_type) {
202                                 Report.Error (1008, Location,
203                                         "Type byte, sbyte, short, ushort, int, uint, long or ulong expected");
204                                 return null;
205                         }
206
207                         if (IsTopLevel) {
208                                 if (TypeManager.NamespaceClash (Name, Location))
209                                         return null;
210                                 
211                                 ModuleBuilder builder = CodeGen.Module.Builder;
212
213                                 TypeBuilder = builder.DefineType (Name, TypeAttr, TypeManager.enum_type);
214                         } else {
215                                 TypeBuilder builder = Parent.TypeBuilder;
216
217                                 TypeBuilder = builder.DefineNestedType (
218                                         Basename, TypeAttr, TypeManager.enum_type);
219                         }
220
221                         ec.ContainerType = TypeBuilder;
222
223                         //
224                         // Call MapToInternalType for corlib
225                         //
226                         TypeBuilder.DefineField ("value__", UnderlyingType,
227                                                  FieldAttributes.Public | FieldAttributes.SpecialName
228                                                  | FieldAttributes.RTSpecialName);
229
230                         TypeManager.AddEnumType (Name, TypeBuilder, this);
231
232                         return TypeBuilder;
233                 }
234
235                 bool IsValidEnumConstant (Expression e)
236                 {
237                         if (!(e is Constant))
238                                 return false;
239
240                         if (e is IntConstant || e is UIntConstant || e is LongConstant ||
241                             e is ByteConstant || e is SByteConstant || e is ShortConstant ||
242                             e is UShortConstant || e is ULongConstant || e is EnumConstant ||
243                             e is CharConstant)
244                                 return true;
245                         else
246                                 return false;
247                 }
248
249                 object GetNextDefaultValue (object default_value)
250                 {
251                         if (UnderlyingType == TypeManager.int32_type) {
252                                 int i = (int) default_value;
253                                 
254                                 if (i < System.Int32.MaxValue)
255                                         return ++i;
256                                 else
257                                         return null;
258                         } else if (UnderlyingType == TypeManager.uint32_type) {
259                                 uint i = (uint) default_value;
260
261                                 if (i < System.UInt32.MaxValue)
262                                         return ++i;
263                                 else
264                                         return null;
265                         } else if (UnderlyingType == TypeManager.int64_type) {
266                                 long i = (long) default_value;
267
268                                 if (i < System.Int64.MaxValue)
269                                         return ++i;
270                                 else
271                                         return null;
272                         } else if (UnderlyingType == TypeManager.uint64_type) {
273                                 ulong i = (ulong) default_value;
274
275                                 if (i < System.UInt64.MaxValue)
276                                         return ++i;
277                                 else
278                                         return null;
279                         } else if (UnderlyingType == TypeManager.short_type) {
280                                 short i = (short) default_value;
281
282                                 if (i < System.Int16.MaxValue)
283                                         return ++i;
284                                 else
285                                         return null;
286                         } else if (UnderlyingType == TypeManager.ushort_type) {
287                                 ushort i = (ushort) default_value;
288
289                                 if (i < System.UInt16.MaxValue)
290                                         return ++i;
291                                 else
292                                         return null;
293                         } else if (UnderlyingType == TypeManager.byte_type) {
294                                 byte i = (byte) default_value;
295
296                                 if (i < System.Byte.MaxValue)
297                                         return ++i;
298                                 else
299                                         return null;
300                         } else if (UnderlyingType == TypeManager.sbyte_type) {
301                                 sbyte i = (sbyte) default_value;
302
303                                 if (i < System.SByte.MaxValue)
304                                         return ++i;
305                                 else
306                                         return null;
307                         }
308
309                         return null;
310                 }
311
312                 void Error_ConstantValueCannotBeConverted (object val, Location loc)
313                 {
314                         Report.Error (31, loc, "Constant value `{0}' cannot be converted to a `{1}'",
315                                 val is Constant ? ((Constant) val).AsString () : val,
316                                 TypeManager.CSharpName (UnderlyingType));
317                 }
318
319                 /// <summary>
320                 ///  Determines if a standard implicit conversion exists from
321                 ///  expr_type to target_type
322                 /// </summary>
323                 public static bool ImplicitConversionExists (Type expr_type, Type target_type)
324                 {
325                         expr_type = TypeManager.TypeToCoreType (expr_type);
326
327                         if (expr_type == TypeManager.void_type)
328                                 return false;
329                         
330                         if (expr_type == target_type)
331                                 return true;
332
333                         // First numeric conversions 
334
335                         if (expr_type == TypeManager.sbyte_type){
336                                 //
337                                 // From sbyte to short, int, long, float, double.
338                                 //
339                                 if ((target_type == TypeManager.int32_type) || 
340                                     (target_type == TypeManager.int64_type) ||
341                                     (target_type == TypeManager.double_type) ||
342                                     (target_type == TypeManager.float_type)  ||
343                                     (target_type == TypeManager.short_type) ||
344                                     (target_type == TypeManager.decimal_type))
345                                         return true;
346                                 
347                         } else if (expr_type == TypeManager.byte_type){
348                                 //
349                                 // From byte to short, ushort, int, uint, long, ulong, float, double
350                                 // 
351                                 if ((target_type == TypeManager.short_type) ||
352                                     (target_type == TypeManager.ushort_type) ||
353                                     (target_type == TypeManager.int32_type) ||
354                                     (target_type == TypeManager.uint32_type) ||
355                                     (target_type == TypeManager.uint64_type) ||
356                                     (target_type == TypeManager.int64_type) ||
357                                     (target_type == TypeManager.float_type) ||
358                                     (target_type == TypeManager.double_type) ||
359                                     (target_type == TypeManager.decimal_type))
360                                         return true;
361         
362                         } else if (expr_type == TypeManager.short_type){
363                                 //
364                                 // From short to int, long, float, double
365                                 // 
366                                 if ((target_type == TypeManager.int32_type) ||
367                                     (target_type == TypeManager.int64_type) ||
368                                     (target_type == TypeManager.double_type) ||
369                                     (target_type == TypeManager.float_type) ||
370                                     (target_type == TypeManager.decimal_type))
371                                         return true;
372                                         
373                         } else if (expr_type == TypeManager.ushort_type){
374                                 //
375                                 // From ushort to int, uint, long, ulong, float, double
376                                 //
377                                 if ((target_type == TypeManager.uint32_type) ||
378                                     (target_type == TypeManager.uint64_type) ||
379                                     (target_type == TypeManager.int32_type) ||
380                                     (target_type == TypeManager.int64_type) ||
381                                     (target_type == TypeManager.double_type) ||
382                                     (target_type == TypeManager.float_type) ||
383                                     (target_type == TypeManager.decimal_type))
384                                         return true;
385                                     
386                         } else if (expr_type == TypeManager.int32_type){
387                                 //
388                                 // From int to long, float, double
389                                 //
390                                 if ((target_type == TypeManager.int64_type) ||
391                                     (target_type == TypeManager.double_type) ||
392                                     (target_type == TypeManager.float_type) ||
393                                     (target_type == TypeManager.decimal_type))
394                                         return true;
395                                         
396                         } else if (expr_type == TypeManager.uint32_type){
397                                 //
398                                 // From uint to long, ulong, float, double
399                                 //
400                                 if ((target_type == TypeManager.int64_type) ||
401                                     (target_type == TypeManager.uint64_type) ||
402                                     (target_type == TypeManager.double_type) ||
403                                     (target_type == TypeManager.float_type) ||
404                                     (target_type == TypeManager.decimal_type))
405                                         return true;
406                                         
407                         } else if ((expr_type == TypeManager.uint64_type) ||
408                                    (expr_type == TypeManager.int64_type)) {
409                                 //
410                                 // From long/ulong to float, double
411                                 //
412                                 if ((target_type == TypeManager.double_type) ||
413                                     (target_type == TypeManager.float_type) ||
414                                     (target_type == TypeManager.decimal_type))
415                                         return true;
416                                     
417                         } else if (expr_type == TypeManager.char_type){
418                                 //
419                                 // From char to ushort, int, uint, long, ulong, float, double
420                                 // 
421                                 if ((target_type == TypeManager.ushort_type) ||
422                                     (target_type == TypeManager.int32_type) ||
423                                     (target_type == TypeManager.uint32_type) ||
424                                     (target_type == TypeManager.uint64_type) ||
425                                     (target_type == TypeManager.int64_type) ||
426                                     (target_type == TypeManager.float_type) ||
427                                     (target_type == TypeManager.double_type) ||
428                                     (target_type == TypeManager.decimal_type))
429                                         return true;
430
431                         } else if (expr_type == TypeManager.float_type){
432                                 //
433                                 // float to double
434                                 //
435                                 if (target_type == TypeManager.double_type)
436                                         return true;
437                         }       
438                         
439                         return false;
440                 }
441
442                 /// <summary>
443                 ///  This is used to lookup the value of an enum member. If the member is undefined,
444                 ///  it attempts to define it and return its value
445                 /// </summary>
446                 public object LookupEnumValue (string name, Location loc)
447                 {
448                         if (ec == null)
449                                 Report.Error (-1, loc, "Enum.LookupEnumValue () called too soon");
450                         
451                         object default_value = null;
452                         Constant c = null;
453
454                         default_value = member_to_value [name];
455
456                         if (default_value != null)
457                                 return default_value;
458
459                         //
460                         // This may happen if we're calling a method in System.Enum, for instance
461                         // Enum.IsDefined().
462                         //
463                         if (!defined_names.Contains (name))
464                                 return null;
465
466                         if (in_transit.Contains (name)) {
467                                 Report.Error (110, loc, "The evaluation of the constant value for `" +
468                                               Name + "." + name + "' involves a circular definition.");
469                                 return null;
470                         }
471
472                         //
473                         // So if the above doesn't happen, we have a member that is undefined
474                         // We now proceed to define it 
475                         //
476                         Expression val = this [name];
477
478                         if (val == null) {
479                                 
480                                 int idx = ordered_enums.IndexOf (name);
481
482                                 if (idx == 0)
483                                         default_value = 0;
484                                 else {
485                                         for (int i = 0; i < idx; ++i) {
486                                                 string n = (string) ordered_enums [i];
487                                                 Location m_loc = (Mono.CSharp.Location)
488                                                         member_to_location [n];
489                                                 in_transit.Add (name, true);
490                                                 default_value = LookupEnumValue (n, m_loc);
491                                                 in_transit.Remove (name);
492                                                 if (default_value == null)
493                                                         return null;
494                                         }
495                                         
496                                         default_value = GetNextDefaultValue (default_value);
497                                 }
498                                 
499                         } else {
500                                 in_transit.Add (name, true);
501                                 val = val.Resolve (EmitContext);
502                                 in_transit.Remove (name);
503
504                                 if (val == null)
505                                         return null;
506
507                                 if (!IsValidEnumConstant (val)) {
508                                         Report.Error (1008, loc,
509                                                 "Type byte, sbyte, short, ushort, int, uint, long or ulong expected");
510                                         return null;
511                                 }
512
513                                 c = (Constant) val;
514                                 default_value = c.GetValue ();
515
516                                 if (default_value == null) {
517                                         Error_ConstantValueCannotBeConverted (c, loc);
518                                         return null;
519                                 }
520
521                                 if (val is EnumConstant){
522                                         Type etype = TypeManager.EnumToUnderlying (c.Type);
523                                         
524                                         if (!ImplicitConversionExists (etype, UnderlyingType)){
525                                                 Convert.Error_CannotImplicitConversion (
526                                                         loc, c.Type, UnderlyingType);
527                                                 return null;
528                                         }
529                                 }
530                         }
531
532                         EnumMember em = (EnumMember) defined_names [name];
533                         em.DefineMember (TypeBuilder);
534
535                         bool fail;
536                         default_value = TypeManager.ChangeType (default_value, UnderlyingType, out fail);
537                         if (fail){
538                                 Error_ConstantValueCannotBeConverted (c, loc);
539                                 return null;
540                         }
541
542                         em.builder.SetConstant (default_value);
543                         field_builders.Add (em.builder);
544                         member_to_value [name] = default_value;
545
546                         if (!TypeManager.RegisterFieldValue (em.builder, default_value))
547                                 return null;
548
549                         return default_value;
550                 }
551                 
552                 public override bool Define ()
553                 {
554                         //
555                         // If there was an error during DefineEnum, return
556                         //
557                         if (TypeBuilder == null)
558                                 return false;
559
560                         if (ec == null)
561                                 throw new InternalErrorException ("Enum.Define () called too soon");
562                         
563                         object default_value = 0;
564                         
565                 
566                         foreach (string name in ordered_enums) {
567                                 //
568                                 // Have we already been defined, thanks to some cross-referencing ?
569                                 // 
570                                 if (member_to_value.Contains (name))
571                                         continue;
572                                 
573                                 Location loc = (Mono.CSharp.Location) member_to_location [name];
574
575                                 if (this [name] != null) {
576                                         default_value = LookupEnumValue (name, loc);
577
578                                         if (default_value == null)
579                                                 return true;
580                                 } else {
581                                         EnumMember em = (EnumMember) defined_names [name];
582
583                                         em.DefineMember (TypeBuilder);
584                                         FieldBuilder fb = em.builder;
585                                         
586                                         if (default_value == null) {
587                                            Report.Error (543, loc, "The enumerator value `{0}.{1}' is too large to fit in its type `{2}'",
588                                                    GetSignatureForError (), name, TypeManager.CSharpName (this.UnderlyingType));
589                                                 return false;
590                                         }
591
592                                         bool fail;
593                                         default_value = TypeManager.ChangeType (default_value, UnderlyingType, out fail);
594                                         if (fail){
595                                                 Error_ConstantValueCannotBeConverted (default_value, loc);
596                                                 return false;
597                                         }
598
599                                         fb.SetConstant (default_value);
600                                         field_builders.Add (fb);
601                                         member_to_value [name] = default_value;
602                                         
603                                         if (!TypeManager.RegisterFieldValue (fb, default_value))
604                                                 return false;
605                                 }
606
607                                 default_value = GetNextDefaultValue (default_value);
608                         }
609
610                         return true;
611                 }
612
613                 public override void Emit ()
614                 {
615                         if (OptAttributes != null) {
616                                 OptAttributes.Emit (ec, this);
617                         }
618
619                         foreach (EnumMember em in defined_names.Values) {
620                                 em.Emit (ec);
621                         }
622
623                         base.Emit ();
624                 }
625                 
626                 void VerifyClsName ()
627                 {
628                         Hashtable ht = new Hashtable ();
629                         foreach (string name in ordered_enums) {
630                                 string locase = name.ToLower (System.Globalization.CultureInfo.InvariantCulture);
631                                 if (!ht.Contains (locase)) {
632                                         ht.Add (locase, defined_names [name]);
633                                         continue;
634                                 }
635  
636                                 MemberCore conflict = (MemberCore)ht [locase];
637                                 Report.SymbolRelatedToPreviousError (conflict);
638                                 conflict = GetDefinition (name);
639                                 Report.Error (3005, conflict.Location, "Identifier `{0}' differing only in case is not CLS-compliant", conflict.GetSignatureForError ());
640                         }
641                 }
642
643                 protected override bool VerifyClsCompliance (DeclSpace ds)
644                 {
645                         if (!base.VerifyClsCompliance (ds))
646                                 return false;
647
648                         VerifyClsName ();
649
650                         if (!AttributeTester.IsClsCompliant (UnderlyingType)) {
651                                 Report.Error (3009, Location, "`{0}': base type `{1}' is not CLS-compliant", GetSignatureForError (), TypeManager.CSharpName (UnderlyingType));
652                         }
653
654                         return true;
655                 }
656                 
657                 //
658                 // IMemberFinder
659                 //
660                 public override MemberList FindMembers (MemberTypes mt, BindingFlags bf,
661                                                         MemberFilter filter, object criteria)
662                 {
663                         ArrayList members = new ArrayList ();
664
665                         if ((mt & MemberTypes.Field) != 0) {
666                                 if (criteria is string && member_to_value [criteria] == null) {
667                                         LookupEnumValue ((string) criteria, Location.Null);
668                                 }
669                                 
670                                 foreach (FieldBuilder fb in field_builders)
671                                         if (filter (fb, criteria) == true)
672                                                 members.Add (fb);
673                         }
674
675                         return new MemberList (members);
676                 }
677
678                 public override MemberCache MemberCache {
679                         get {
680                                 return null;
681                         }
682                 }
683
684                 public ArrayList ValueNames {
685                         get {
686                                 return ordered_enums;
687                         }
688                 }
689
690                 // indexer
691                 public Expression this [string name] {
692                         get {
693                                 return ((EnumMember) defined_names [name]).Type;
694                         }
695                 }
696
697                 public override AttributeTargets AttributeTargets {
698                         get {
699                                 return AttributeTargets.Enum;
700                         }
701                 }
702
703                 protected override TypeAttributes TypeAttr {
704                         get {
705                                 return Modifiers.TypeAttr (ModFlags, IsTopLevel) |
706                                 TypeAttributes.Class | TypeAttributes.Sealed |
707                                 base.TypeAttr;
708                         }
709                 }
710
711
712                 protected override void VerifyObsoleteAttribute()
713                 {
714                         // UnderlyingType is never obsolete
715                 }
716
717                 //
718                 // Generates xml doc comments (if any), and if required,
719                 // handle warning report.
720                 //
721                 internal override void GenerateDocComment (DeclSpace ds)
722                 {
723                         DocUtil.GenerateEnumDocComment (this, ds);
724                 }
725
726                 //
727                 //   Represents header string for documentation comment.
728                 //
729                 public override string DocCommentHeader {
730                         get { return "T:"; }
731                 }
732         }
733 }