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 can't 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, " +
188 "long, or ulong expected (got: `{0}')", BaseType);
192 TypeExpr ute = ResolveBaseTypeExpr (BaseType, false, Location);
193 UnderlyingType = ute.Type;
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) + ")");
211 if (TypeManager.NamespaceClash (Name, Location))
214 ModuleBuilder builder = CodeGen.Module.Builder;
216 TypeBuilder = builder.DefineType (Name, TypeAttr, TypeManager.enum_type);
218 TypeBuilder builder = Parent.TypeBuilder;
220 TypeBuilder = builder.DefineNestedType (
221 Basename, TypeAttr, TypeManager.enum_type);
224 ec.ContainerType = TypeBuilder;
227 // Call MapToInternalType for corlib
229 TypeBuilder.DefineField ("value__", UnderlyingType,
230 FieldAttributes.Public | FieldAttributes.SpecialName
231 | FieldAttributes.RTSpecialName);
233 TypeManager.AddEnumType (Name, TypeBuilder, this);
238 bool IsValidEnumConstant (Expression e)
240 if (!(e is Constant))
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 ||
252 object GetNextDefaultValue (object default_value)
254 if (UnderlyingType == TypeManager.int32_type) {
255 int i = (int) default_value;
257 if (i < System.Int32.MaxValue)
261 } else if (UnderlyingType == TypeManager.uint32_type) {
262 uint i = (uint) default_value;
264 if (i < System.UInt32.MaxValue)
268 } else if (UnderlyingType == TypeManager.int64_type) {
269 long i = (long) default_value;
271 if (i < System.Int64.MaxValue)
275 } else if (UnderlyingType == TypeManager.uint64_type) {
276 ulong i = (ulong) default_value;
278 if (i < System.UInt64.MaxValue)
282 } else if (UnderlyingType == TypeManager.short_type) {
283 short i = (short) default_value;
285 if (i < System.Int16.MaxValue)
289 } else if (UnderlyingType == TypeManager.ushort_type) {
290 ushort i = (ushort) default_value;
292 if (i < System.UInt16.MaxValue)
296 } else if (UnderlyingType == TypeManager.byte_type) {
297 byte i = (byte) default_value;
299 if (i < System.Byte.MaxValue)
303 } else if (UnderlyingType == TypeManager.sbyte_type) {
304 sbyte i = (sbyte) default_value;
306 if (i < System.SByte.MaxValue)
315 void Error_ConstantValueCannotBeConverted (object val, Location loc)
318 Report.Error (31, loc, "Constant value '" + ((Constant) val).AsString () +
319 "' cannot be converted" +
320 " to a " + TypeManager.CSharpName (UnderlyingType));
322 Report.Error (31, loc, "Constant value '" + val +
323 "' cannot be converted" +
324 " to a " + TypeManager.CSharpName (UnderlyingType));
329 /// Determines if a standard implicit conversion exists from
330 /// expr_type to target_type
332 public static bool ImplicitConversionExists (Type expr_type, Type target_type)
334 expr_type = TypeManager.TypeToCoreType (expr_type);
336 if (expr_type == TypeManager.void_type)
339 if (expr_type == target_type)
342 // First numeric conversions
344 if (expr_type == TypeManager.sbyte_type){
346 // From sbyte to short, int, long, float, double.
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))
356 } else if (expr_type == TypeManager.byte_type){
358 // From byte to short, ushort, int, uint, long, ulong, float, double
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))
371 } else if (expr_type == TypeManager.short_type){
373 // From short to int, long, float, double
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))
382 } else if (expr_type == TypeManager.ushort_type){
384 // From ushort to int, uint, long, ulong, float, double
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))
395 } else if (expr_type == TypeManager.int32_type){
397 // From int to long, float, double
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))
405 } else if (expr_type == TypeManager.uint32_type){
407 // From uint to long, ulong, float, double
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))
416 } else if ((expr_type == TypeManager.uint64_type) ||
417 (expr_type == TypeManager.int64_type)) {
419 // From long/ulong to float, double
421 if ((target_type == TypeManager.double_type) ||
422 (target_type == TypeManager.float_type) ||
423 (target_type == TypeManager.decimal_type))
426 } else if (expr_type == TypeManager.char_type){
428 // From char to ushort, int, uint, long, ulong, float, double
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))
440 } else if (expr_type == TypeManager.float_type){
444 if (target_type == TypeManager.double_type)
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
455 public object LookupEnumValue (string name, Location loc)
458 Report.Error (-1, loc, "Enum.LookupEnumValue () called too soon");
460 object default_value = null;
463 default_value = member_to_value [name];
465 if (default_value != null)
466 return default_value;
469 // This may happen if we're calling a method in System.Enum, for instance
472 if (!defined_names.Contains (name))
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.");
482 // So if the above doesn't happen, we have a member that is undefined
483 // We now proceed to define it
485 Expression val = this [name];
489 int idx = ordered_enums.IndexOf (name);
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)
505 default_value = GetNextDefaultValue (default_value);
509 in_transit.Add (name, true);
510 val = val.Resolve (EmitContext);
511 in_transit.Remove (name);
516 if (!IsValidEnumConstant (val)) {
519 "Type byte, sbyte, short, ushort, int, uint, long, or " +
520 "ulong expected (have: " + val + ")");
525 default_value = c.GetValue ();
527 if (default_value == null) {
528 Error_ConstantValueCannotBeConverted (c, loc);
532 if (val is EnumConstant){
533 Type etype = TypeManager.EnumToUnderlying (c.Type);
535 if (!ImplicitConversionExists (etype, UnderlyingType)){
536 Convert.Error_CannotImplicitConversion (
537 loc, c.Type, UnderlyingType);
543 EnumMember em = (EnumMember) defined_names [name];
544 em.DefineMember (TypeBuilder);
547 default_value = TypeManager.ChangeType (default_value, UnderlyingType, out fail);
549 Error_ConstantValueCannotBeConverted (c, loc);
553 em.builder.SetConstant (default_value);
554 field_builders.Add (em.builder);
555 member_to_value [name] = default_value;
557 if (!TypeManager.RegisterFieldValue (em.builder, default_value))
560 return default_value;
563 public override bool DefineMembers (TypeContainer parent)
568 public override bool Define ()
571 // If there was an error during DefineEnum, return
573 if (TypeBuilder == null)
577 throw new InternalErrorException ("Enum.Define () called too soon");
579 object default_value = 0;
582 foreach (string name in ordered_enums) {
584 // Have we already been defined, thanks to some cross-referencing ?
586 if (member_to_value.Contains (name))
589 Location loc = (Mono.CSharp.Location) member_to_location [name];
591 if (this [name] != null) {
592 default_value = LookupEnumValue (name, loc);
594 if (default_value == null)
597 if (name == "value__"){
598 Report.Error (76, loc, "The name `value__' is reserved for enumerations");
602 EnumMember em = (EnumMember) defined_names [name];
604 em.DefineMember (TypeBuilder);
605 FieldBuilder fb = em.builder;
607 if (default_value == null) {
608 Report.Error (543, loc, "Enumerator value for '" + name + "' is too large to " +
614 default_value = TypeManager.ChangeType (default_value, UnderlyingType, out fail);
616 Error_ConstantValueCannotBeConverted (default_value, loc);
620 fb.SetConstant (default_value);
621 field_builders.Add (fb);
622 member_to_value [name] = default_value;
624 if (!TypeManager.RegisterFieldValue (fb, default_value))
628 default_value = GetNextDefaultValue (default_value);
634 public override void Emit ()
636 if (OptAttributes != null) {
637 OptAttributes.Emit (ec, this);
640 foreach (EnumMember em in defined_names.Values) {
647 void VerifyClsName ()
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]);
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 ());
664 protected override bool VerifyClsCompliance (DeclSpace ds)
666 if (!base.VerifyClsCompliance (ds))
671 if (!AttributeTester.IsClsCompliant (UnderlyingType)) {
672 Report.Error (3009, Location, "'{0}': base type '{1}' is not CLS-compliant", GetSignatureForError (), TypeManager.CSharpName (UnderlyingType));
681 public override MemberList FindMembers (MemberTypes mt, BindingFlags bf,
682 MemberFilter filter, object criteria)
684 ArrayList members = new ArrayList ();
686 if ((mt & MemberTypes.Field) != 0) {
687 if (criteria is string && member_to_value [criteria] == null) {
688 LookupEnumValue ((string) criteria, Location.Null);
691 foreach (FieldBuilder fb in field_builders)
692 if (filter (fb, criteria) == true)
696 return new MemberList (members);
699 public override MemberCache MemberCache {
705 public ArrayList ValueNames {
707 return ordered_enums;
712 public Expression this [string name] {
714 return ((EnumMember) defined_names [name]).Type;
718 public override AttributeTargets AttributeTargets {
720 return AttributeTargets.Enum;
724 protected override TypeAttributes TypeAttr {
726 return Modifiers.TypeAttr (ModFlags, IsTopLevel) |
727 TypeAttributes.Class | TypeAttributes.Sealed |
733 protected override void VerifyObsoleteAttribute()
735 // UnderlyingType is never obsolete
739 // Generates xml doc comments (if any), and if required,
740 // handle warning report.
742 internal override void GenerateDocComment (DeclSpace ds)
744 DocUtil.GenerateEnumDocComment (this, ds);
748 // Represents header string for documentation comment.
750 public override string DocCommentHeader {