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
;
19 namespace Mono
.CSharp
{
21 class EnumMember
: MemberCore
{
22 static string[] attribute_targets
= new string [] { "field" }
;
25 public FieldBuilder builder
;
26 internal readonly Expression Type
;
28 public EnumMember (Enum parent_enum
, Expression expr
, string name
,
29 Location loc
, Attributes attrs
):
30 base (null, new MemberName (name
), attrs
, loc
)
32 this.parent_enum
= parent_enum
;
33 this.ModFlags
= parent_enum
.ModFlags
;
37 public override void ApplyAttributeBuilder(Attribute a
, CustomAttributeBuilder cb
)
39 if (a
.Type
== TypeManager
.marshal_as_attr_type
) {
40 UnmanagedMarshal marshal
= a
.GetMarshal (this);
41 if (marshal
!= null) {
42 builder
.SetMarshal (marshal
);
47 if (a
.Type
.IsSubclassOf (TypeManager
.security_attr_type
)) {
48 a
.Error_InvalidSecurityParent ();
52 builder
.SetCustomAttribute (cb
);
55 public override AttributeTargets AttributeTargets
{
57 return AttributeTargets
.Field
;
61 public void DefineMember (TypeBuilder tb
)
63 FieldAttributes attr
= FieldAttributes
.Public
| FieldAttributes
.Static
64 | FieldAttributes
.Literal
;
66 builder
= tb
.DefineField (Name
, tb
, attr
);
69 public override bool Define ()
71 throw new NotImplementedException ();
74 public void Emit (EmitContext ec
)
76 if (OptAttributes
!= null)
77 OptAttributes
.Emit (ec
, this);
82 public override string GetSignatureForError()
84 return String
.Concat (parent_enum
.GetSignatureForError (), '.', base.GetSignatureForError ());
87 public override string[] ValidAttributeTargets
{
89 return attribute_targets
;
93 protected override bool VerifyClsCompliance(DeclSpace ds
)
95 // Because parent is TypeContainer and we have only DeclSpace parent.
96 // Parameter replacing is required
97 return base.VerifyClsCompliance (parent_enum
);
100 // There is no base type
101 protected override void VerifyObsoleteAttribute()
105 public override string DocCommentHeader
{
111 /// Enumeration container
113 public class Enum
: DeclSpace
{
114 public ArrayList ordered_enums
;
116 public Expression BaseType
;
118 public Type UnderlyingType
;
120 Hashtable member_to_location
;
123 // This is for members that have been defined
125 Hashtable member_to_value
;
128 // This is used to mark members we're currently defining
130 Hashtable in_transit
;
132 ArrayList field_builders
;
134 public const int AllowedModifiers
=
137 Modifiers
.PROTECTED
|
141 public Enum (NamespaceEntry ns
, TypeContainer parent
, Expression type
,
142 int mod_flags
, MemberName name
, Attributes attrs
, Location l
)
143 : base (ns
, parent
, name
, attrs
, l
)
145 this.BaseType
= type
;
146 ModFlags
= Modifiers
.Check (AllowedModifiers
, mod_flags
,
147 IsTopLevel
? Modifiers
.INTERNAL
: Modifiers
.PRIVATE
, l
);
149 ordered_enums
= new ArrayList ();
150 member_to_location
= new Hashtable ();
151 member_to_value
= new Hashtable ();
152 in_transit
= new Hashtable ();
153 field_builders
= new ArrayList ();
157 /// Adds @name to the enumeration space, with @expr
158 /// being its definition.
160 public void AddEnumMember (string name
, Expression expr
, Location loc
, Attributes opt_attrs
, string documentation
)
162 if (name
== "value__") {
163 Report
.Error (76, loc
, "An item in an enumeration can't have an identifier `value__'");
167 EnumMember em
= new EnumMember (this, expr
, name
, loc
, opt_attrs
);
168 em
.DocComment
= documentation
;
169 if (!AddToContainer (em
, name
, ""))
173 // TODO: can be almost deleted
174 ordered_enums
.Add (name
);
175 member_to_location
.Add (name
, loc
);
179 // This is used by corlib compilation: we map from our
180 // type to a type that is consumable by the DefineField
182 Type
MapToInternalType (Type t
)
184 if (t
== TypeManager
.int32_type
)
186 if (t
== TypeManager
.int64_type
)
187 return typeof (long);
188 if (t
== TypeManager
.uint32_type
)
189 return typeof (uint);
190 if (t
== TypeManager
.uint64_type
)
191 return typeof (ulong);
192 if (t
== TypeManager
.float_type
)
193 return typeof (float);
194 if (t
== TypeManager
.double_type
)
195 return typeof (double);
196 if (t
== TypeManager
.byte_type
)
197 return typeof (byte);
198 if (t
== TypeManager
.sbyte_type
)
199 return typeof (sbyte);
200 if (t
== TypeManager
.char_type
)
201 return typeof (char);
202 if (t
== TypeManager
.short_type
)
203 return typeof (short);
204 if (t
== TypeManager
.ushort_type
)
205 return typeof (ushort);
207 throw new Exception ();
210 public override TypeBuilder
DefineType ()
212 if (TypeBuilder
!= null)
215 TypeAttributes attr
= Modifiers
.TypeAttr (ModFlags
, IsTopLevel
);
217 ec
= new EmitContext (this, this, Location
, null, null, ModFlags
, false);
219 attr
|= TypeAttributes
.Class
| TypeAttributes
.Sealed
;
221 if (!(BaseType
is TypeLookupExpression
)) {
222 Report
.Error (1008, Location
,
223 "Type byte, sbyte, short, ushort, int, uint, " +
224 "long, or ulong expected (got: `{0}')", BaseType
);
228 TypeExpr ute
= ResolveBaseTypeExpr (BaseType
, false, Location
);
232 UnderlyingType
= ute
.Type
;
234 if (UnderlyingType
!= TypeManager
.int32_type
&&
235 UnderlyingType
!= TypeManager
.uint32_type
&&
236 UnderlyingType
!= TypeManager
.int64_type
&&
237 UnderlyingType
!= TypeManager
.uint64_type
&&
238 UnderlyingType
!= TypeManager
.short_type
&&
239 UnderlyingType
!= TypeManager
.ushort_type
&&
240 UnderlyingType
!= TypeManager
.byte_type
&&
241 UnderlyingType
!= TypeManager
.sbyte_type
) {
242 Report
.Error (1008, Location
,
243 "Type byte, sbyte, short, ushort, int, uint, " +
244 "long, or ulong expected (got: " +
245 TypeManager
.CSharpName (UnderlyingType
) + ")");
250 if (TypeManager
.NamespaceClash (Name
, Location
))
253 ModuleBuilder builder
= CodeGen
.Module
.Builder
;
255 TypeBuilder
= builder
.DefineType (Name
, attr
, TypeManager
.enum_type
);
257 TypeBuilder builder
= Parent
.TypeBuilder
;
259 TypeBuilder
= builder
.DefineNestedType (
260 Basename
, attr
, TypeManager
.enum_type
);
264 // Call MapToInternalType for corlib
266 TypeBuilder
.DefineField ("value__", UnderlyingType
,
267 FieldAttributes
.Public
| FieldAttributes
.SpecialName
268 | FieldAttributes
.RTSpecialName
);
270 TypeManager
.AddEnumType (Name
, TypeBuilder
, this);
275 bool IsValidEnumConstant (Expression e
)
277 if (!(e
is Constant
))
280 if (e
is IntConstant
|| e
is UIntConstant
|| e
is LongConstant
||
281 e
is ByteConstant
|| e
is SByteConstant
|| e
is ShortConstant
||
282 e
is UShortConstant
|| e
is ULongConstant
|| e
is EnumConstant
||
289 object GetNextDefaultValue (object default_value
)
291 if (UnderlyingType
== TypeManager
.int32_type
) {
292 int i
= (int) default_value
;
294 if (i
< System
.Int32
.MaxValue
)
298 } else if (UnderlyingType
== TypeManager
.uint32_type
) {
299 uint i
= (uint) default_value
;
301 if (i
< System
.UInt32
.MaxValue
)
305 } else if (UnderlyingType
== TypeManager
.int64_type
) {
306 long i
= (long) default_value
;
308 if (i
< System
.Int64
.MaxValue
)
312 } else if (UnderlyingType
== TypeManager
.uint64_type
) {
313 ulong i
= (ulong) default_value
;
315 if (i
< System
.UInt64
.MaxValue
)
319 } else if (UnderlyingType
== TypeManager
.short_type
) {
320 short i
= (short) default_value
;
322 if (i
< System
.Int16
.MaxValue
)
326 } else if (UnderlyingType
== TypeManager
.ushort_type
) {
327 ushort i
= (ushort) default_value
;
329 if (i
< System
.UInt16
.MaxValue
)
333 } else if (UnderlyingType
== TypeManager
.byte_type
) {
334 byte i
= (byte) default_value
;
336 if (i
< System
.Byte
.MaxValue
)
340 } else if (UnderlyingType
== TypeManager
.sbyte_type
) {
341 sbyte i
= (sbyte) default_value
;
343 if (i
< System
.SByte
.MaxValue
)
352 void Error_ConstantValueCannotBeConverted (object val
, Location loc
)
355 Report
.Error (31, loc
, "Constant value '" + ((Constant
) val
).AsString () +
356 "' cannot be converted" +
357 " to a " + TypeManager
.CSharpName (UnderlyingType
));
359 Report
.Error (31, loc
, "Constant value '" + val
+
360 "' cannot be converted" +
361 " to a " + TypeManager
.CSharpName (UnderlyingType
));
366 /// Determines if a standard implicit conversion exists from
367 /// expr_type to target_type
369 public static bool ImplicitConversionExists (Type expr_type
, Type target_type
)
371 expr_type
= TypeManager
.TypeToCoreType (expr_type
);
373 if (expr_type
== TypeManager
.void_type
)
376 if (expr_type
== target_type
)
379 // First numeric conversions
381 if (expr_type
== TypeManager
.sbyte_type
){
383 // From sbyte to short, int, long, float, double.
385 if ((target_type
== TypeManager
.int32_type
) ||
386 (target_type
== TypeManager
.int64_type
) ||
387 (target_type
== TypeManager
.double_type
) ||
388 (target_type
== TypeManager
.float_type
) ||
389 (target_type
== TypeManager
.short_type
) ||
390 (target_type
== TypeManager
.decimal_type
))
393 } else if (expr_type
== TypeManager
.byte_type
){
395 // From byte to short, ushort, int, uint, long, ulong, float, double
397 if ((target_type
== TypeManager
.short_type
) ||
398 (target_type
== TypeManager
.ushort_type
) ||
399 (target_type
== TypeManager
.int32_type
) ||
400 (target_type
== TypeManager
.uint32_type
) ||
401 (target_type
== TypeManager
.uint64_type
) ||
402 (target_type
== TypeManager
.int64_type
) ||
403 (target_type
== TypeManager
.float_type
) ||
404 (target_type
== TypeManager
.double_type
) ||
405 (target_type
== TypeManager
.decimal_type
))
408 } else if (expr_type
== TypeManager
.short_type
){
410 // From short to int, long, float, double
412 if ((target_type
== TypeManager
.int32_type
) ||
413 (target_type
== TypeManager
.int64_type
) ||
414 (target_type
== TypeManager
.double_type
) ||
415 (target_type
== TypeManager
.float_type
) ||
416 (target_type
== TypeManager
.decimal_type
))
419 } else if (expr_type
== TypeManager
.ushort_type
){
421 // From ushort to int, uint, long, ulong, float, double
423 if ((target_type
== TypeManager
.uint32_type
) ||
424 (target_type
== TypeManager
.uint64_type
) ||
425 (target_type
== TypeManager
.int32_type
) ||
426 (target_type
== TypeManager
.int64_type
) ||
427 (target_type
== TypeManager
.double_type
) ||
428 (target_type
== TypeManager
.float_type
) ||
429 (target_type
== TypeManager
.decimal_type
))
432 } else if (expr_type
== TypeManager
.int32_type
){
434 // From int to long, float, double
436 if ((target_type
== TypeManager
.int64_type
) ||
437 (target_type
== TypeManager
.double_type
) ||
438 (target_type
== TypeManager
.float_type
) ||
439 (target_type
== TypeManager
.decimal_type
))
442 } else if (expr_type
== TypeManager
.uint32_type
){
444 // From uint to long, ulong, float, double
446 if ((target_type
== TypeManager
.int64_type
) ||
447 (target_type
== TypeManager
.uint64_type
) ||
448 (target_type
== TypeManager
.double_type
) ||
449 (target_type
== TypeManager
.float_type
) ||
450 (target_type
== TypeManager
.decimal_type
))
453 } else if ((expr_type
== TypeManager
.uint64_type
) ||
454 (expr_type
== TypeManager
.int64_type
)) {
456 // From long/ulong to float, double
458 if ((target_type
== TypeManager
.double_type
) ||
459 (target_type
== TypeManager
.float_type
) ||
460 (target_type
== TypeManager
.decimal_type
))
463 } else if (expr_type
== TypeManager
.char_type
){
465 // From char to ushort, int, uint, long, ulong, float, double
467 if ((target_type
== TypeManager
.ushort_type
) ||
468 (target_type
== TypeManager
.int32_type
) ||
469 (target_type
== TypeManager
.uint32_type
) ||
470 (target_type
== TypeManager
.uint64_type
) ||
471 (target_type
== TypeManager
.int64_type
) ||
472 (target_type
== TypeManager
.float_type
) ||
473 (target_type
== TypeManager
.double_type
) ||
474 (target_type
== TypeManager
.decimal_type
))
477 } else if (expr_type
== TypeManager
.float_type
){
481 if (target_type
== TypeManager
.double_type
)
489 // Horrible, horrible. But there is no other way we can pass the EmitContext
490 // to the recursive definition triggered by the evaluation of a forward
493 static EmitContext current_ec
= null;
496 /// This is used to lookup the value of an enum member. If the member is undefined,
497 /// it attempts to define it and return its value
499 public object LookupEnumValue (EmitContext ec
, string name
, Location loc
)
502 object default_value
= null;
505 default_value
= member_to_value
[name
];
507 if (default_value
!= null)
508 return default_value
;
511 // This may happen if we're calling a method in System.Enum, for instance
514 if (!defined_names
.Contains (name
))
517 if (in_transit
.Contains (name
)) {
518 Report
.Error (110, loc
, "The evaluation of the constant value for `" +
519 Name
+ "." + name
+ "' involves a circular definition.");
524 // So if the above doesn't happen, we have a member that is undefined
525 // We now proceed to define it
527 Expression val
= this [name
];
531 int idx
= ordered_enums
.IndexOf (name
);
536 for (int i
= 0; i
< idx
; ++i
) {
537 string n
= (string) ordered_enums
[i
];
538 Location m_loc
= (Mono
.CSharp
.Location
)
539 member_to_location
[n
];
540 in_transit
.Add (name
, true);
542 EmitContext old_ec
= current_ec
;
545 default_value
= LookupEnumValue (ec
, n
, m_loc
);
549 in_transit
.Remove (name
);
550 if (default_value
== null)
554 default_value
= GetNextDefaultValue (default_value
);
558 bool old
= ec
.InEnumContext
;
559 ec
.InEnumContext
= true;
560 in_transit
.Add (name
, true);
562 EmitContext old_ec
= current_ec
;
564 val
= val
.Resolve (ec
);
567 in_transit
.Remove (name
);
568 ec
.InEnumContext
= old
;
573 if (!IsValidEnumConstant (val
)) {
576 "Type byte, sbyte, short, ushort, int, uint, long, or " +
577 "ulong expected (have: " + val
+ ")");
582 default_value
= c
.GetValue ();
584 if (default_value
== null) {
585 Error_ConstantValueCannotBeConverted (c
, loc
);
589 if (val
is EnumConstant
){
590 Type etype
= TypeManager
.EnumToUnderlying (c
.Type
);
592 if (!ImplicitConversionExists (etype
, UnderlyingType
)){
593 Convert
.Error_CannotImplicitConversion (
594 loc
, c
.Type
, UnderlyingType
);
600 EnumMember em
= (EnumMember
) defined_names
[name
];
601 em
.DefineMember (TypeBuilder
);
604 default_value
= TypeManager
.ChangeType (default_value
, UnderlyingType
, out fail
);
606 Error_ConstantValueCannotBeConverted (c
, loc
);
610 em
.builder
.SetConstant (default_value
);
611 field_builders
.Add (em
.builder
);
612 member_to_value
[name
] = default_value
;
614 if (!TypeManager
.RegisterFieldValue (em
.builder
, default_value
))
617 return default_value
;
620 public override bool DefineMembers (TypeContainer parent
)
625 public override bool Define ()
628 // If there was an error during DefineEnum, return
630 if (TypeBuilder
== null)
633 ec
= new EmitContext (this, this, Location
, null, UnderlyingType
, ModFlags
, false);
635 object default_value
= 0;
638 foreach (string name
in ordered_enums
) {
640 // Have we already been defined, thanks to some cross-referencing ?
642 if (member_to_value
.Contains (name
))
645 Location loc
= (Mono
.CSharp
.Location
) member_to_location
[name
];
647 if (this [name
] != null) {
648 default_value
= LookupEnumValue (ec
, name
, loc
);
650 if (default_value
== null)
653 if (name
== "value__"){
654 Report
.Error (76, loc
, "The name `value__' is reserved for enumerations");
658 EnumMember em
= (EnumMember
) defined_names
[name
];
660 em
.DefineMember (TypeBuilder
);
661 FieldBuilder fb
= em
.builder
;
663 if (default_value
== null) {
664 Report
.Error (543, loc
, "Enumerator value for '" + name
+ "' is too large to " +
670 default_value
= TypeManager
.ChangeType (default_value
, UnderlyingType
, out fail
);
672 Error_ConstantValueCannotBeConverted (default_value
, loc
);
676 fb
.SetConstant (default_value
);
677 field_builders
.Add (fb
);
678 member_to_value
[name
] = default_value
;
680 if (!TypeManager
.RegisterFieldValue (fb
, default_value
))
684 default_value
= GetNextDefaultValue (default_value
);
690 public override void Emit ()
692 if (OptAttributes
!= null) {
693 OptAttributes
.Emit (ec
, this);
696 foreach (EnumMember em
in defined_names
.Values
) {
703 void VerifyClsName ()
705 Hashtable ht
= new Hashtable ();
706 foreach (string name
in ordered_enums
) {
707 string locase
= name
.ToLower (System
.Globalization
.CultureInfo
.InvariantCulture
);
708 if (!ht
.Contains (locase
)) {
709 ht
.Add (locase
, defined_names
[name
]);
713 MemberCore conflict
= (MemberCore
)ht
[locase
];
714 Report
.SymbolRelatedToPreviousError (conflict
);
715 conflict
= GetDefinition (name
);
716 Report
.Error (3005, conflict
.Location
, "Identifier '{0}' differing only in case is not CLS-compliant", conflict
.GetSignatureForError ());
720 protected override bool VerifyClsCompliance (DeclSpace ds
)
722 if (!base.VerifyClsCompliance (ds
))
727 if (!AttributeTester
.IsClsCompliant (UnderlyingType
)) {
728 Report
.Error (3009, Location
, "'{0}': base type '{1}' is not CLS-compliant", GetSignatureForError (), TypeManager
.CSharpName (UnderlyingType
));
735 /// Returns full enum name.
737 string GetEnumeratorName (string valueName
)
739 return String
.Concat (Name
, ".", valueName
);
745 public override MemberList
FindMembers (MemberTypes mt
, BindingFlags bf
,
746 MemberFilter filter
, object criteria
)
748 ArrayList members
= new ArrayList ();
750 if ((mt
& MemberTypes
.Field
) != 0) {
751 if (criteria
is string){
752 if (member_to_value
[criteria
] == null && current_ec
!= null){
753 LookupEnumValue (current_ec
, (string) criteria
, Location
.Null
);
757 foreach (FieldBuilder fb
in field_builders
)
758 if (filter (fb
, criteria
) == true)
762 return new MemberList (members
);
765 public override MemberCache MemberCache
{
771 public ArrayList ValueNames
{
773 return ordered_enums
;
778 public Expression
this [string name
] {
780 return ((EnumMember
) defined_names
[name
]).Type
;
784 public override AttributeTargets AttributeTargets
{
786 return AttributeTargets
.Enum
;
790 protected override void VerifyObsoleteAttribute()
792 // UnderlyingType is never obsolete
796 // Generates xml doc comments (if any), and if required,
797 // handle warning report.
799 internal override void GenerateDocComment (DeclSpace ds
)
801 DocUtil
.GenerateEnumDocComment (this, ds
);
805 // Represents header string for documentation comment.
807 public override string DocCommentHeader
{