2 // enum.cs: Enum handling.
4 // Author: Miguel de Icaza (miguel@gnu.org)
5 // Ravi Pratap (ravi@ximian.com)
6 // Marek Safar (marek.safar@seznam.cz)
8 // Licensed under the terms of the GNU GPL
10 // (C) 2001 Ximian, Inc (http://www.ximian.com)
14 using System.Collections;
15 using System.Collections.Specialized;
16 using System.Reflection;
17 using System.Reflection.Emit;
18 using System.Globalization;
20 namespace Mono.CSharp {
22 class EnumMember: MemberCore {
23 static string[] attribute_targets = new string [] { "field" };
26 public FieldBuilder builder;
27 internal readonly Expression Type;
29 public EnumMember (Enum parent_enum, Expression expr, string name,
30 Location loc, Attributes attrs):
31 base (null, new MemberName (name), attrs, loc)
33 this.parent_enum = parent_enum;
34 this.ModFlags = parent_enum.ModFlags;
38 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder cb)
40 if (a.Type == TypeManager.marshal_as_attr_type) {
41 UnmanagedMarshal marshal = a.GetMarshal (this);
42 if (marshal != null) {
43 builder.SetMarshal (marshal);
48 if (a.Type.IsSubclassOf (TypeManager.security_attr_type)) {
49 a.Error_InvalidSecurityParent ();
53 builder.SetCustomAttribute (cb);
56 public override AttributeTargets AttributeTargets {
58 return AttributeTargets.Field;
62 public void DefineMember (TypeBuilder tb)
64 FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static
65 | FieldAttributes.Literal;
67 builder = tb.DefineField (Name, tb, attr);
70 public override bool Define ()
72 throw new NotImplementedException ();
75 public void Emit (EmitContext ec)
77 if (OptAttributes != null)
78 OptAttributes.Emit (ec, this);
83 public override string GetSignatureForError()
85 return String.Concat (parent_enum.GetSignatureForError (), '.', base.GetSignatureForError ());
88 public override string[] ValidAttributeTargets {
90 return attribute_targets;
94 protected override bool VerifyClsCompliance(DeclSpace ds)
96 // Because parent is TypeContainer and we have only DeclSpace parent.
97 // Parameter replacing is required
98 return base.VerifyClsCompliance (parent_enum);
101 // There is no base type
102 protected override void VerifyObsoleteAttribute()
106 public override string DocCommentHeader {
112 /// Enumeration container
114 public class Enum : DeclSpace {
115 public ArrayList ordered_enums;
117 public Expression BaseType;
119 public Type UnderlyingType;
121 Hashtable member_to_location;
124 // This is for members that have been defined
126 Hashtable member_to_value;
129 // This is used to mark members we're currently defining
131 Hashtable in_transit;
133 ArrayList field_builders;
135 public const int AllowedModifiers =
138 Modifiers.PROTECTED |
142 public Enum (NamespaceEntry ns, TypeContainer parent, Expression type,
143 int mod_flags, MemberName name, Attributes attrs, Location l)
144 : base (ns, parent, name, attrs, l)
146 this.BaseType = type;
147 ModFlags = Modifiers.Check (AllowedModifiers, mod_flags,
148 IsTopLevel ? Modifiers.INTERNAL : Modifiers.PRIVATE, l);
150 ordered_enums = new ArrayList ();
151 member_to_location = new Hashtable ();
152 member_to_value = new Hashtable ();
153 in_transit = new Hashtable ();
154 field_builders = new ArrayList ();
158 /// Adds @name to the enumeration space, with @expr
159 /// being its definition.
161 public void AddEnumMember (string name, Expression expr, Location loc, Attributes opt_attrs, string documentation)
163 if (name == "value__") {
164 Report.Error (76, loc, "An item in an enumeration cannot have an identifier `value__'");
168 EnumMember em = new EnumMember (this, expr, name, loc, opt_attrs);
169 em.DocComment = documentation;
170 if (!AddToContainer (em, name))
174 // TODO: can be almost deleted
175 ordered_enums.Add (name);
176 member_to_location.Add (name, loc);
179 public override TypeBuilder DefineType ()
181 if (TypeBuilder != null)
184 ec = new EmitContext (this, this, Location, null, null, ModFlags, false);
185 ec.InEnumContext = true;
187 if (!(BaseType is TypeLookupExpression)) {
188 Report.Error (1008, Location,
189 "Type byte, sbyte, short, ushort, int, uint, long or ulong expected");
193 TypeExpr ute = ResolveBaseTypeExpr (BaseType, false, Location);
194 UnderlyingType = ute.Type;
196 if (UnderlyingType != TypeManager.int32_type &&
197 UnderlyingType != TypeManager.uint32_type &&
198 UnderlyingType != TypeManager.int64_type &&
199 UnderlyingType != TypeManager.uint64_type &&
200 UnderlyingType != TypeManager.short_type &&
201 UnderlyingType != TypeManager.ushort_type &&
202 UnderlyingType != TypeManager.byte_type &&
203 UnderlyingType != TypeManager.sbyte_type) {
204 Report.Error (1008, Location,
205 "Type byte, sbyte, short, ushort, int, uint, long or ulong expected");
210 if (TypeManager.NamespaceClash (Name, Location))
213 ModuleBuilder builder = CodeGen.Module.Builder;
215 TypeBuilder = builder.DefineType (Name, TypeAttr, TypeManager.enum_type);
217 TypeBuilder builder = Parent.TypeBuilder;
219 TypeBuilder = builder.DefineNestedType (
220 Basename, TypeAttr, TypeManager.enum_type);
223 ec.ContainerType = TypeBuilder;
226 // Call MapToInternalType for corlib
228 TypeBuilder.DefineField ("value__", UnderlyingType,
229 FieldAttributes.Public | FieldAttributes.SpecialName
230 | FieldAttributes.RTSpecialName);
232 TypeManager.AddUserType (Name, this);
237 bool IsValidEnumConstant (Expression e)
239 if (!(e is Constant))
242 if (e is IntConstant || e is UIntConstant || e is LongConstant ||
243 e is ByteConstant || e is SByteConstant || e is ShortConstant ||
244 e is UShortConstant || e is ULongConstant || e is EnumConstant ||
251 object GetNextDefaultValue (object default_value)
253 if (UnderlyingType == TypeManager.int32_type) {
254 int i = (int) default_value;
256 if (i < System.Int32.MaxValue)
260 } else if (UnderlyingType == TypeManager.uint32_type) {
261 uint i = (uint) default_value;
263 if (i < System.UInt32.MaxValue)
267 } else if (UnderlyingType == TypeManager.int64_type) {
268 long i = (long) default_value;
270 if (i < System.Int64.MaxValue)
274 } else if (UnderlyingType == TypeManager.uint64_type) {
275 ulong i = (ulong) default_value;
277 if (i < System.UInt64.MaxValue)
281 } else if (UnderlyingType == TypeManager.short_type) {
282 short i = (short) default_value;
284 if (i < System.Int16.MaxValue)
288 } else if (UnderlyingType == TypeManager.ushort_type) {
289 ushort i = (ushort) default_value;
291 if (i < System.UInt16.MaxValue)
295 } else if (UnderlyingType == TypeManager.byte_type) {
296 byte i = (byte) default_value;
298 if (i < System.Byte.MaxValue)
302 } else if (UnderlyingType == TypeManager.sbyte_type) {
303 sbyte i = (sbyte) default_value;
305 if (i < System.SByte.MaxValue)
315 /// Determines if a standard implicit conversion exists from
316 /// expr_type to target_type
318 public static bool ImplicitConversionExists (Type expr_type, Type target_type)
320 expr_type = TypeManager.TypeToCoreType (expr_type);
322 if (expr_type == TypeManager.void_type)
325 if (expr_type == target_type)
328 // First numeric conversions
330 if (expr_type == TypeManager.sbyte_type){
332 // From sbyte to short, int, long, float, double.
334 if ((target_type == TypeManager.int32_type) ||
335 (target_type == TypeManager.int64_type) ||
336 (target_type == TypeManager.double_type) ||
337 (target_type == TypeManager.float_type) ||
338 (target_type == TypeManager.short_type) ||
339 (target_type == TypeManager.decimal_type))
342 } else if (expr_type == TypeManager.byte_type){
344 // From byte to short, ushort, int, uint, long, ulong, float, double
346 if ((target_type == TypeManager.short_type) ||
347 (target_type == TypeManager.ushort_type) ||
348 (target_type == TypeManager.int32_type) ||
349 (target_type == TypeManager.uint32_type) ||
350 (target_type == TypeManager.uint64_type) ||
351 (target_type == TypeManager.int64_type) ||
352 (target_type == TypeManager.float_type) ||
353 (target_type == TypeManager.double_type) ||
354 (target_type == TypeManager.decimal_type))
357 } else if (expr_type == TypeManager.short_type){
359 // From short to int, long, float, double
361 if ((target_type == TypeManager.int32_type) ||
362 (target_type == TypeManager.int64_type) ||
363 (target_type == TypeManager.double_type) ||
364 (target_type == TypeManager.float_type) ||
365 (target_type == TypeManager.decimal_type))
368 } else if (expr_type == TypeManager.ushort_type){
370 // From ushort to int, uint, long, ulong, float, double
372 if ((target_type == TypeManager.uint32_type) ||
373 (target_type == TypeManager.uint64_type) ||
374 (target_type == TypeManager.int32_type) ||
375 (target_type == TypeManager.int64_type) ||
376 (target_type == TypeManager.double_type) ||
377 (target_type == TypeManager.float_type) ||
378 (target_type == TypeManager.decimal_type))
381 } else if (expr_type == TypeManager.int32_type){
383 // From int to long, float, double
385 if ((target_type == TypeManager.int64_type) ||
386 (target_type == TypeManager.double_type) ||
387 (target_type == TypeManager.float_type) ||
388 (target_type == TypeManager.decimal_type))
391 } else if (expr_type == TypeManager.uint32_type){
393 // From uint to long, ulong, float, double
395 if ((target_type == TypeManager.int64_type) ||
396 (target_type == TypeManager.uint64_type) ||
397 (target_type == TypeManager.double_type) ||
398 (target_type == TypeManager.float_type) ||
399 (target_type == TypeManager.decimal_type))
402 } else if ((expr_type == TypeManager.uint64_type) ||
403 (expr_type == TypeManager.int64_type)) {
405 // From long/ulong to float, double
407 if ((target_type == TypeManager.double_type) ||
408 (target_type == TypeManager.float_type) ||
409 (target_type == TypeManager.decimal_type))
412 } else if (expr_type == TypeManager.char_type){
414 // From char to ushort, int, uint, long, ulong, float, double
416 if ((target_type == TypeManager.ushort_type) ||
417 (target_type == TypeManager.int32_type) ||
418 (target_type == TypeManager.uint32_type) ||
419 (target_type == TypeManager.uint64_type) ||
420 (target_type == TypeManager.int64_type) ||
421 (target_type == TypeManager.float_type) ||
422 (target_type == TypeManager.double_type) ||
423 (target_type == TypeManager.decimal_type))
426 } else if (expr_type == TypeManager.float_type){
430 if (target_type == TypeManager.double_type)
438 /// This is used to lookup the value of an enum member. If the member is undefined,
439 /// it attempts to define it and return its value
441 public object LookupEnumValue (string name, Location loc)
444 Report.Error (-1, loc, "Enum.LookupEnumValue () called too soon");
446 object default_value = null;
449 default_value = member_to_value [name];
451 if (default_value != null)
452 return default_value;
455 // This may happen if we're calling a method in System.Enum, for instance
458 if (!defined_names.Contains (name))
461 if (in_transit.Contains (name)) {
462 Report.Error (110, loc, "The evaluation of the constant value for `" +
463 Name + "." + name + "' involves a circular definition.");
468 // So if the above doesn't happen, we have a member that is undefined
469 // We now proceed to define it
471 Expression val = this [name];
475 int idx = ordered_enums.IndexOf (name);
480 for (int i = 0; i < idx; ++i) {
481 string n = (string) ordered_enums [i];
482 Location m_loc = (Mono.CSharp.Location)
483 member_to_location [n];
484 in_transit.Add (name, true);
485 default_value = LookupEnumValue (n, m_loc);
486 in_transit.Remove (name);
487 if (default_value == null)
491 default_value = GetNextDefaultValue (default_value);
495 in_transit.Add (name, true);
496 val = val.Resolve (EmitContext);
497 in_transit.Remove (name);
502 if (!IsValidEnumConstant (val)) {
503 Report.Error (1008, loc,
504 "Type byte, sbyte, short, ushort, int, uint, long or ulong expected");
509 default_value = c.GetValue ();
511 if (default_value == null) {
512 c.Error_ConstantValueCannotBeConverted (loc, UnderlyingType);
516 if (val is EnumConstant){
517 Type etype = TypeManager.EnumToUnderlying (c.Type);
519 if (!ImplicitConversionExists (etype, UnderlyingType)){
520 Convert.Error_CannotImplicitConversion (
521 loc, c.Type, UnderlyingType);
527 EnumMember em = (EnumMember) defined_names [name];
528 em.DefineMember (TypeBuilder);
531 default_value = TypeManager.ChangeType (default_value, UnderlyingType, out fail);
533 c.Error_ConstantValueCannotBeConverted (loc, UnderlyingType);
537 em.builder.SetConstant (default_value);
538 field_builders.Add (em.builder);
539 member_to_value [name] = default_value;
541 if (!TypeManager.RegisterFieldValue (em.builder, default_value))
544 return default_value;
547 public override bool Define ()
550 // If there was an error during DefineEnum, return
552 if (TypeBuilder == null)
556 throw new InternalErrorException ("Enum.Define () called too soon");
558 object default_value = 0;
561 foreach (string name in ordered_enums) {
563 // Have we already been defined, thanks to some cross-referencing ?
565 if (member_to_value.Contains (name))
568 Location loc = (Mono.CSharp.Location) member_to_location [name];
570 if (this [name] != null) {
571 default_value = LookupEnumValue (name, loc);
573 if (default_value == null)
576 EnumMember em = (EnumMember) defined_names [name];
578 em.DefineMember (TypeBuilder);
579 FieldBuilder fb = em.builder;
581 if (default_value == null) {
582 Report.Error (543, loc, "The enumerator value `{0}.{1}' is too large to fit in its type `{2}'",
583 GetSignatureForError (), name, TypeManager.CSharpName (this.UnderlyingType));
588 default_value = TypeManager.ChangeType (default_value, UnderlyingType, out fail);
590 //TODO: really interested if can be reached
591 throw new NotImplementedException ();
594 fb.SetConstant (default_value);
595 field_builders.Add (fb);
596 member_to_value [name] = default_value;
598 if (!TypeManager.RegisterFieldValue (fb, default_value))
602 default_value = GetNextDefaultValue (default_value);
608 public override void Emit ()
610 if (OptAttributes != null) {
611 OptAttributes.Emit (ec, this);
614 foreach (EnumMember em in defined_names.Values) {
621 void VerifyClsName ()
623 HybridDictionary dict = new HybridDictionary (defined_names.Count, true);
624 foreach (EnumMember em in defined_names.Values) {
625 if (!em.IsClsCompliaceRequired (this))
629 dict.Add (em.Name, em);
631 catch (ArgumentException) {
632 Report.SymbolRelatedToPreviousError (em);
633 MemberCore col = (MemberCore)dict [em.Name];
634 Report.Error (3005, col.Location, "Identifier `{0}' differing only in case is not CLS-compliant", col.GetSignatureForError ());
639 protected override bool VerifyClsCompliance (DeclSpace ds)
641 if (!base.VerifyClsCompliance (ds))
646 if (UnderlyingType == TypeManager.uint32_type ||
647 UnderlyingType == TypeManager.uint64_type ||
648 UnderlyingType == TypeManager.ushort_type) {
649 Report.Error (3009, Location, "`{0}': base type `{1}' is not CLS-compliant", GetSignatureForError (), TypeManager.CSharpName (UnderlyingType));
658 public override MemberList FindMembers (MemberTypes mt, BindingFlags bf,
659 MemberFilter filter, object criteria)
661 ArrayList members = new ArrayList ();
663 if ((mt & MemberTypes.Field) != 0) {
664 if (criteria is string && member_to_value [criteria] == null) {
665 LookupEnumValue ((string) criteria, Location.Null);
668 foreach (FieldBuilder fb in field_builders)
669 if (filter (fb, criteria) == true)
673 return new MemberList (members);
676 public override MemberCache MemberCache {
682 public ArrayList ValueNames {
684 return ordered_enums;
689 public Expression this [string name] {
691 return ((EnumMember) defined_names [name]).Type;
695 public override AttributeTargets AttributeTargets {
697 return AttributeTargets.Enum;
701 protected override TypeAttributes TypeAttr {
703 return Modifiers.TypeAttr (ModFlags, IsTopLevel) |
704 TypeAttributes.Class | TypeAttributes.Sealed |
710 protected override void VerifyObsoleteAttribute()
712 // UnderlyingType is never obsolete
716 // Generates xml doc comments (if any), and if required,
717 // handle warning report.
719 internal override void GenerateDocComment (DeclSpace ds)
721 DocUtil.GenerateEnumDocComment (this, ds);
725 // Represents header string for documentation comment.
727 public override string DocCommentHeader {