2 // enum.cs: Enum handling.
4 // Author: Miguel de Icaza (miguel@gnu.org)
5 // Ravi Pratap (ravi@ximian.com)
7 // Licensed under the terms of the GNU GPL
9 // (C) 2001 Ximian, Inc (http://www.ximian.com)
13 using System.Collections;
14 using System.Reflection;
15 using System.Reflection.Emit;
16 using System.Globalization;
18 namespace Mono.CSharp {
20 class EnumMember: MemberCore {
21 static string[] attribute_targets = new string [] { "field" };
24 public FieldBuilder builder;
25 internal readonly Expression Type;
27 public EnumMember (Enum parent_enum, Expression expr, string name,
28 Location loc, Attributes attrs):
29 base (null, new MemberName (name), attrs, loc)
31 this.parent_enum = parent_enum;
32 this.ModFlags = parent_enum.ModFlags;
36 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder cb)
38 if (a.Type == TypeManager.marshal_as_attr_type) {
39 UnmanagedMarshal marshal = a.GetMarshal (this);
40 if (marshal != null) {
41 builder.SetMarshal (marshal);
46 if (a.Type.IsSubclassOf (TypeManager.security_attr_type)) {
47 a.Error_InvalidSecurityParent ();
51 builder.SetCustomAttribute (cb);
54 public override AttributeTargets AttributeTargets {
56 return AttributeTargets.Field;
60 public void DefineMember (TypeBuilder tb)
62 FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static
63 | FieldAttributes.Literal;
65 builder = tb.DefineField (Name, tb, attr);
68 public override bool Define ()
70 throw new NotImplementedException ();
73 public void Emit (EmitContext ec)
75 if (OptAttributes != null)
76 OptAttributes.Emit (ec, this);
81 public override string GetSignatureForError()
83 return String.Concat (parent_enum.GetSignatureForError (), '.', base.GetSignatureForError ());
86 public override string[] ValidAttributeTargets {
88 return attribute_targets;
92 protected override bool VerifyClsCompliance(DeclSpace ds)
94 // Because parent is TypeContainer and we have only DeclSpace parent.
95 // Parameter replacing is required
96 return base.VerifyClsCompliance (parent_enum);
99 // There is no base type
100 protected override void VerifyObsoleteAttribute()
104 public override string DocCommentHeader {
110 /// Enumeration container
112 public class Enum : DeclSpace {
113 public ArrayList ordered_enums;
115 public Expression BaseType;
117 public Type UnderlyingType;
119 Hashtable member_to_location;
122 // This is for members that have been defined
124 Hashtable member_to_value;
127 // This is used to mark members we're currently defining
129 Hashtable in_transit;
131 ArrayList field_builders;
133 public const int AllowedModifiers =
136 Modifiers.PROTECTED |
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)
144 this.BaseType = type;
145 ModFlags = Modifiers.Check (AllowedModifiers, mod_flags,
146 IsTopLevel ? Modifiers.INTERNAL : Modifiers.PRIVATE, l);
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 ();
156 /// Adds @name to the enumeration space, with @expr
157 /// being its definition.
159 public void AddEnumMember (string name, Expression expr, Location loc, Attributes opt_attrs, string documentation)
161 if (name == "value__") {
162 Report.Error (76, loc, "An item in an enumeration cannot have an identifier `value__'");
166 EnumMember em = new EnumMember (this, expr, name, loc, opt_attrs);
167 em.DocComment = documentation;
168 if (!AddToContainer (em, name))
172 // TODO: can be almost deleted
173 ordered_enums.Add (name);
174 member_to_location.Add (name, loc);
177 public override TypeBuilder DefineType ()
179 if (TypeBuilder != null)
182 ec = new EmitContext (this, this, Location, null, null, ModFlags, false);
183 ec.InEnumContext = true;
185 if (!(BaseType is TypeLookupExpression)) {
186 Report.Error (1008, Location,
187 "Type byte, sbyte, short, ushort, int, uint, long or ulong expected");
191 TypeExpr ute = ResolveBaseTypeExpr (BaseType, false, Location);
192 UnderlyingType = ute.Type;
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");
208 if (TypeManager.NamespaceClash (Name, Location))
211 ModuleBuilder builder = CodeGen.Module.Builder;
213 TypeBuilder = builder.DefineType (Name, TypeAttr, TypeManager.enum_type);
215 TypeBuilder builder = Parent.TypeBuilder;
217 TypeBuilder = builder.DefineNestedType (
218 Basename, TypeAttr, TypeManager.enum_type);
221 ec.ContainerType = TypeBuilder;
224 // Call MapToInternalType for corlib
226 TypeBuilder.DefineField ("value__", UnderlyingType,
227 FieldAttributes.Public | FieldAttributes.SpecialName
228 | FieldAttributes.RTSpecialName);
230 TypeManager.AddEnumType (Name, TypeBuilder, this);
235 bool IsValidEnumConstant (Expression e)
237 if (!(e is Constant))
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 ||
249 object GetNextDefaultValue (object default_value)
251 if (UnderlyingType == TypeManager.int32_type) {
252 int i = (int) default_value;
254 if (i < System.Int32.MaxValue)
258 } else if (UnderlyingType == TypeManager.uint32_type) {
259 uint i = (uint) default_value;
261 if (i < System.UInt32.MaxValue)
265 } else if (UnderlyingType == TypeManager.int64_type) {
266 long i = (long) default_value;
268 if (i < System.Int64.MaxValue)
272 } else if (UnderlyingType == TypeManager.uint64_type) {
273 ulong i = (ulong) default_value;
275 if (i < System.UInt64.MaxValue)
279 } else if (UnderlyingType == TypeManager.short_type) {
280 short i = (short) default_value;
282 if (i < System.Int16.MaxValue)
286 } else if (UnderlyingType == TypeManager.ushort_type) {
287 ushort i = (ushort) default_value;
289 if (i < System.UInt16.MaxValue)
293 } else if (UnderlyingType == TypeManager.byte_type) {
294 byte i = (byte) default_value;
296 if (i < System.Byte.MaxValue)
300 } else if (UnderlyingType == TypeManager.sbyte_type) {
301 sbyte i = (sbyte) default_value;
303 if (i < System.SByte.MaxValue)
312 void Error_ConstantValueCannotBeConverted (object val, Location loc)
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));
320 /// Determines if a standard implicit conversion exists from
321 /// expr_type to target_type
323 public static bool ImplicitConversionExists (Type expr_type, Type target_type)
325 expr_type = TypeManager.TypeToCoreType (expr_type);
327 if (expr_type == TypeManager.void_type)
330 if (expr_type == target_type)
333 // First numeric conversions
335 if (expr_type == TypeManager.sbyte_type){
337 // From sbyte to short, int, long, float, double.
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))
347 } else if (expr_type == TypeManager.byte_type){
349 // From byte to short, ushort, int, uint, long, ulong, float, double
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))
362 } else if (expr_type == TypeManager.short_type){
364 // From short to int, long, float, double
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))
373 } else if (expr_type == TypeManager.ushort_type){
375 // From ushort to int, uint, long, ulong, float, double
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))
386 } else if (expr_type == TypeManager.int32_type){
388 // From int to long, float, double
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))
396 } else if (expr_type == TypeManager.uint32_type){
398 // From uint to long, ulong, float, double
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))
407 } else if ((expr_type == TypeManager.uint64_type) ||
408 (expr_type == TypeManager.int64_type)) {
410 // From long/ulong to float, double
412 if ((target_type == TypeManager.double_type) ||
413 (target_type == TypeManager.float_type) ||
414 (target_type == TypeManager.decimal_type))
417 } else if (expr_type == TypeManager.char_type){
419 // From char to ushort, int, uint, long, ulong, float, double
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))
431 } else if (expr_type == TypeManager.float_type){
435 if (target_type == TypeManager.double_type)
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
446 public object LookupEnumValue (string name, Location loc)
449 Report.Error (-1, loc, "Enum.LookupEnumValue () called too soon");
451 object default_value = null;
454 default_value = member_to_value [name];
456 if (default_value != null)
457 return default_value;
460 // This may happen if we're calling a method in System.Enum, for instance
463 if (!defined_names.Contains (name))
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.");
473 // So if the above doesn't happen, we have a member that is undefined
474 // We now proceed to define it
476 Expression val = this [name];
480 int idx = ordered_enums.IndexOf (name);
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)
496 default_value = GetNextDefaultValue (default_value);
500 in_transit.Add (name, true);
501 val = val.Resolve (EmitContext);
502 in_transit.Remove (name);
507 if (!IsValidEnumConstant (val)) {
508 Report.Error (1008, loc,
509 "Type byte, sbyte, short, ushort, int, uint, long or ulong expected");
514 default_value = c.GetValue ();
516 if (default_value == null) {
517 Error_ConstantValueCannotBeConverted (c, loc);
521 if (val is EnumConstant){
522 Type etype = TypeManager.EnumToUnderlying (c.Type);
524 if (!ImplicitConversionExists (etype, UnderlyingType)){
525 Convert.Error_CannotImplicitConversion (
526 loc, c.Type, UnderlyingType);
532 EnumMember em = (EnumMember) defined_names [name];
533 em.DefineMember (TypeBuilder);
536 default_value = TypeManager.ChangeType (default_value, UnderlyingType, out fail);
538 Error_ConstantValueCannotBeConverted (c, loc);
542 em.builder.SetConstant (default_value);
543 field_builders.Add (em.builder);
544 member_to_value [name] = default_value;
546 if (!TypeManager.RegisterFieldValue (em.builder, default_value))
549 return default_value;
552 public override bool Define ()
555 // If there was an error during DefineEnum, return
557 if (TypeBuilder == null)
561 throw new InternalErrorException ("Enum.Define () called too soon");
563 object default_value = 0;
566 foreach (string name in ordered_enums) {
568 // Have we already been defined, thanks to some cross-referencing ?
570 if (member_to_value.Contains (name))
573 Location loc = (Mono.CSharp.Location) member_to_location [name];
575 if (this [name] != null) {
576 default_value = LookupEnumValue (name, loc);
578 if (default_value == null)
581 EnumMember em = (EnumMember) defined_names [name];
583 em.DefineMember (TypeBuilder);
584 FieldBuilder fb = em.builder;
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));
593 default_value = TypeManager.ChangeType (default_value, UnderlyingType, out fail);
595 Error_ConstantValueCannotBeConverted (default_value, loc);
599 fb.SetConstant (default_value);
600 field_builders.Add (fb);
601 member_to_value [name] = default_value;
603 if (!TypeManager.RegisterFieldValue (fb, default_value))
607 default_value = GetNextDefaultValue (default_value);
613 public override void Emit ()
615 if (OptAttributes != null) {
616 OptAttributes.Emit (ec, this);
619 foreach (EnumMember em in defined_names.Values) {
626 void VerifyClsName ()
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]);
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 ());
643 protected override bool VerifyClsCompliance (DeclSpace ds)
645 if (!base.VerifyClsCompliance (ds))
650 if (!AttributeTester.IsClsCompliant (UnderlyingType)) {
651 Report.Error (3009, Location, "`{0}': base type `{1}' is not CLS-compliant", GetSignatureForError (), TypeManager.CSharpName (UnderlyingType));
660 public override MemberList FindMembers (MemberTypes mt, BindingFlags bf,
661 MemberFilter filter, object criteria)
663 ArrayList members = new ArrayList ();
665 if ((mt & MemberTypes.Field) != 0) {
666 if (criteria is string && member_to_value [criteria] == null) {
667 LookupEnumValue ((string) criteria, Location.Null);
670 foreach (FieldBuilder fb in field_builders)
671 if (filter (fb, criteria) == true)
675 return new MemberList (members);
678 public override MemberCache MemberCache {
684 public ArrayList ValueNames {
686 return ordered_enums;
691 public Expression this [string name] {
693 return ((EnumMember) defined_names [name]).Type;
697 public override AttributeTargets AttributeTargets {
699 return AttributeTargets.Enum;
703 protected override TypeAttributes TypeAttr {
705 return Modifiers.TypeAttr (ModFlags, IsTopLevel) |
706 TypeAttributes.Class | TypeAttributes.Sealed |
712 protected override void VerifyObsoleteAttribute()
714 // UnderlyingType is never obsolete
718 // Generates xml doc comments (if any), and if required,
719 // handle warning report.
721 internal override void GenerateDocComment (DeclSpace ds)
723 DocUtil.GenerateEnumDocComment (this, ds);
727 // Represents header string for documentation comment.
729 public override string DocCommentHeader {