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()
106 /// Enumeration container
108 public class Enum
: DeclSpace
{
109 ArrayList ordered_enums
;
111 public Expression BaseType
;
113 public Type UnderlyingType
;
115 Hashtable member_to_location
;
118 // This is for members that have been defined
120 Hashtable member_to_value
;
123 // This is used to mark members we're currently defining
125 Hashtable in_transit
;
127 ArrayList field_builders
;
129 public const int AllowedModifiers
=
132 Modifiers
.PROTECTED
|
136 public Enum (NamespaceEntry ns
, TypeContainer parent
, Expression type
,
137 int mod_flags
, MemberName name
, Attributes attrs
, Location l
)
138 : base (ns
, parent
, name
, attrs
, l
)
140 this.BaseType
= type
;
141 ModFlags
= Modifiers
.Check (AllowedModifiers
, mod_flags
,
142 IsTopLevel
? Modifiers
.INTERNAL
: Modifiers
.PRIVATE
, l
);
144 ordered_enums
= new ArrayList ();
145 member_to_location
= new Hashtable ();
146 member_to_value
= new Hashtable ();
147 in_transit
= new Hashtable ();
148 field_builders
= new ArrayList ();
152 /// Adds @name to the enumeration space, with @expr
153 /// being its definition.
155 public void AddEnumMember (string name
, Expression expr
, Location loc
, Attributes opt_attrs
)
157 if (name
== "value__") {
158 Report
.Error (76, loc
, "An item in an enumeration can't have an identifier `value__'");
162 EnumMember em
= new EnumMember (this, expr
, name
, loc
, opt_attrs
);
163 if (!AddToContainer (em
, false, name
, ""))
167 // TODO: can be almost deleted
168 ordered_enums
.Add (name
);
169 member_to_location
.Add (name
, loc
);
173 // This is used by corlib compilation: we map from our
174 // type to a type that is consumable by the DefineField
176 Type
MapToInternalType (Type t
)
178 if (t
== TypeManager
.int32_type
)
180 if (t
== TypeManager
.int64_type
)
181 return typeof (long);
182 if (t
== TypeManager
.uint32_type
)
183 return typeof (uint);
184 if (t
== TypeManager
.uint64_type
)
185 return typeof (ulong);
186 if (t
== TypeManager
.float_type
)
187 return typeof (float);
188 if (t
== TypeManager
.double_type
)
189 return typeof (double);
190 if (t
== TypeManager
.byte_type
)
191 return typeof (byte);
192 if (t
== TypeManager
.sbyte_type
)
193 return typeof (sbyte);
194 if (t
== TypeManager
.char_type
)
195 return typeof (char);
196 if (t
== TypeManager
.short_type
)
197 return typeof (short);
198 if (t
== TypeManager
.ushort_type
)
199 return typeof (ushort);
201 throw new Exception ();
204 public override TypeBuilder
DefineType ()
206 if (TypeBuilder
!= null)
209 TypeAttributes attr
= Modifiers
.TypeAttr (ModFlags
, IsTopLevel
);
211 attr
|= TypeAttributes
.Class
| TypeAttributes
.Sealed
;
213 if (!(BaseType
is TypeLookupExpression
)) {
214 Report
.Error (1008, Location
,
215 "Type byte, sbyte, short, ushort, int, uint, " +
216 "long, or ulong expected (got: `{0}')", BaseType
);
220 UnderlyingType
= ResolveType (BaseType
, false, Location
);
222 if (UnderlyingType
!= TypeManager
.int32_type
&&
223 UnderlyingType
!= TypeManager
.uint32_type
&&
224 UnderlyingType
!= TypeManager
.int64_type
&&
225 UnderlyingType
!= TypeManager
.uint64_type
&&
226 UnderlyingType
!= TypeManager
.short_type
&&
227 UnderlyingType
!= TypeManager
.ushort_type
&&
228 UnderlyingType
!= TypeManager
.byte_type
&&
229 UnderlyingType
!= TypeManager
.sbyte_type
) {
230 Report
.Error (1008, Location
,
231 "Type byte, sbyte, short, ushort, int, uint, " +
232 "long, or ulong expected (got: " +
233 TypeManager
.CSharpName (UnderlyingType
) + ")");
238 if (TypeManager
.NamespaceClash (Name
, Location
))
241 ModuleBuilder builder
= CodeGen
.Module
.Builder
;
243 TypeBuilder
= builder
.DefineType (Name
, attr
, TypeManager
.enum_type
);
245 TypeBuilder builder
= Parent
.TypeBuilder
;
247 TypeBuilder
= builder
.DefineNestedType (
248 Basename
, attr
, TypeManager
.enum_type
);
252 // Call MapToInternalType for corlib
254 TypeBuilder
.DefineField ("value__", UnderlyingType
,
255 FieldAttributes
.Public
| FieldAttributes
.SpecialName
256 | FieldAttributes
.RTSpecialName
);
258 TypeManager
.AddEnumType (Name
, TypeBuilder
, this);
263 bool IsValidEnumConstant (Expression e
)
265 if (!(e
is Constant
))
268 if (e
is IntConstant
|| e
is UIntConstant
|| e
is LongConstant
||
269 e
is ByteConstant
|| e
is SByteConstant
|| e
is ShortConstant
||
270 e
is UShortConstant
|| e
is ULongConstant
|| e
is EnumConstant
||
277 object GetNextDefaultValue (object default_value
)
279 if (UnderlyingType
== TypeManager
.int32_type
) {
280 int i
= (int) default_value
;
282 if (i
< System
.Int32
.MaxValue
)
286 } else if (UnderlyingType
== TypeManager
.uint32_type
) {
287 uint i
= (uint) default_value
;
289 if (i
< System
.UInt32
.MaxValue
)
293 } else if (UnderlyingType
== TypeManager
.int64_type
) {
294 long i
= (long) default_value
;
296 if (i
< System
.Int64
.MaxValue
)
300 } else if (UnderlyingType
== TypeManager
.uint64_type
) {
301 ulong i
= (ulong) default_value
;
303 if (i
< System
.UInt64
.MaxValue
)
307 } else if (UnderlyingType
== TypeManager
.short_type
) {
308 short i
= (short) default_value
;
310 if (i
< System
.Int16
.MaxValue
)
314 } else if (UnderlyingType
== TypeManager
.ushort_type
) {
315 ushort i
= (ushort) default_value
;
317 if (i
< System
.UInt16
.MaxValue
)
321 } else if (UnderlyingType
== TypeManager
.byte_type
) {
322 byte i
= (byte) default_value
;
324 if (i
< System
.Byte
.MaxValue
)
328 } else if (UnderlyingType
== TypeManager
.sbyte_type
) {
329 sbyte i
= (sbyte) default_value
;
331 if (i
< System
.SByte
.MaxValue
)
340 void Error_ConstantValueCannotBeConverted (object val
, Location loc
)
343 Report
.Error (31, loc
, "Constant value '" + ((Constant
) val
).AsString () +
344 "' cannot be converted" +
345 " to a " + TypeManager
.CSharpName (UnderlyingType
));
347 Report
.Error (31, loc
, "Constant value '" + val
+
348 "' cannot be converted" +
349 " to a " + TypeManager
.CSharpName (UnderlyingType
));
354 /// Determines if a standard implicit conversion exists from
355 /// expr_type to target_type
357 public static bool ImplicitConversionExists (Type expr_type
, Type target_type
)
359 expr_type
= TypeManager
.TypeToCoreType (expr_type
);
361 if (expr_type
== TypeManager
.void_type
)
364 if (expr_type
== target_type
)
367 // First numeric conversions
369 if (expr_type
== TypeManager
.sbyte_type
){
371 // From sbyte to short, int, long, float, double.
373 if ((target_type
== TypeManager
.int32_type
) ||
374 (target_type
== TypeManager
.int64_type
) ||
375 (target_type
== TypeManager
.double_type
) ||
376 (target_type
== TypeManager
.float_type
) ||
377 (target_type
== TypeManager
.short_type
) ||
378 (target_type
== TypeManager
.decimal_type
))
381 } else if (expr_type
== TypeManager
.byte_type
){
383 // From byte to short, ushort, int, uint, long, ulong, float, double
385 if ((target_type
== TypeManager
.short_type
) ||
386 (target_type
== TypeManager
.ushort_type
) ||
387 (target_type
== TypeManager
.int32_type
) ||
388 (target_type
== TypeManager
.uint32_type
) ||
389 (target_type
== TypeManager
.uint64_type
) ||
390 (target_type
== TypeManager
.int64_type
) ||
391 (target_type
== TypeManager
.float_type
) ||
392 (target_type
== TypeManager
.double_type
) ||
393 (target_type
== TypeManager
.decimal_type
))
396 } else if (expr_type
== TypeManager
.short_type
){
398 // From short to int, long, float, double
400 if ((target_type
== TypeManager
.int32_type
) ||
401 (target_type
== TypeManager
.int64_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
.ushort_type
){
409 // From ushort to int, uint, long, ulong, float, double
411 if ((target_type
== TypeManager
.uint32_type
) ||
412 (target_type
== TypeManager
.uint64_type
) ||
413 (target_type
== TypeManager
.int32_type
) ||
414 (target_type
== TypeManager
.int64_type
) ||
415 (target_type
== TypeManager
.double_type
) ||
416 (target_type
== TypeManager
.float_type
) ||
417 (target_type
== TypeManager
.decimal_type
))
420 } else if (expr_type
== TypeManager
.int32_type
){
422 // From int to long, float, double
424 if ((target_type
== TypeManager
.int64_type
) ||
425 (target_type
== TypeManager
.double_type
) ||
426 (target_type
== TypeManager
.float_type
) ||
427 (target_type
== TypeManager
.decimal_type
))
430 } else if (expr_type
== TypeManager
.uint32_type
){
432 // From uint to long, ulong, float, double
434 if ((target_type
== TypeManager
.int64_type
) ||
435 (target_type
== TypeManager
.uint64_type
) ||
436 (target_type
== TypeManager
.double_type
) ||
437 (target_type
== TypeManager
.float_type
) ||
438 (target_type
== TypeManager
.decimal_type
))
441 } else if ((expr_type
== TypeManager
.uint64_type
) ||
442 (expr_type
== TypeManager
.int64_type
)) {
444 // From long/ulong to float, double
446 if ((target_type
== TypeManager
.double_type
) ||
447 (target_type
== TypeManager
.float_type
) ||
448 (target_type
== TypeManager
.decimal_type
))
451 } else if (expr_type
== TypeManager
.char_type
){
453 // From char to ushort, int, uint, long, ulong, float, double
455 if ((target_type
== TypeManager
.ushort_type
) ||
456 (target_type
== TypeManager
.int32_type
) ||
457 (target_type
== TypeManager
.uint32_type
) ||
458 (target_type
== TypeManager
.uint64_type
) ||
459 (target_type
== TypeManager
.int64_type
) ||
460 (target_type
== TypeManager
.float_type
) ||
461 (target_type
== TypeManager
.double_type
) ||
462 (target_type
== TypeManager
.decimal_type
))
465 } else if (expr_type
== TypeManager
.float_type
){
469 if (target_type
== TypeManager
.double_type
)
477 // Horrible, horrible. But there is no other way we can pass the EmitContext
478 // to the recursive definition triggered by the evaluation of a forward
481 static EmitContext current_ec
= null;
484 /// This is used to lookup the value of an enum member. If the member is undefined,
485 /// it attempts to define it and return its value
487 public object LookupEnumValue (EmitContext ec
, string name
, Location loc
)
490 object default_value
= null;
493 default_value
= member_to_value
[name
];
495 if (default_value
!= null)
496 return default_value
;
499 // This may happen if we're calling a method in System.Enum, for instance
502 if (!defined_names
.Contains (name
))
505 if (in_transit
.Contains (name
)) {
506 Report
.Error (110, loc
, "The evaluation of the constant value for `" +
507 Name
+ "." + name
+ "' involves a circular definition.");
512 // So if the above doesn't happen, we have a member that is undefined
513 // We now proceed to define it
515 Expression val
= this [name
];
519 int idx
= ordered_enums
.IndexOf (name
);
524 for (int i
= 0; i
< idx
; ++i
) {
525 string n
= (string) ordered_enums
[i
];
526 Location m_loc
= (Mono
.CSharp
.Location
)
527 member_to_location
[n
];
528 in_transit
.Add (name
, true);
530 EmitContext old_ec
= current_ec
;
533 default_value
= LookupEnumValue (ec
, n
, m_loc
);
537 in_transit
.Remove (name
);
538 if (default_value
== null)
542 default_value
= GetNextDefaultValue (default_value
);
546 bool old
= ec
.InEnumContext
;
547 ec
.InEnumContext
= true;
548 in_transit
.Add (name
, true);
550 EmitContext old_ec
= current_ec
;
552 val
= val
.Resolve (ec
);
555 in_transit
.Remove (name
);
556 ec
.InEnumContext
= old
;
561 if (!IsValidEnumConstant (val
)) {
564 "Type byte, sbyte, short, ushort, int, uint, long, or " +
565 "ulong expected (have: " + val
+ ")");
570 default_value
= c
.GetValue ();
572 if (default_value
== null) {
573 Error_ConstantValueCannotBeConverted (c
, loc
);
577 if (val
is EnumConstant
){
578 Type etype
= TypeManager
.EnumToUnderlying (c
.Type
);
580 if (!ImplicitConversionExists (etype
, UnderlyingType
)){
581 Convert
.Error_CannotImplicitConversion (
582 loc
, c
.Type
, UnderlyingType
);
588 EnumMember em
= (EnumMember
) defined_names
[name
];
589 em
.DefineMember (TypeBuilder
);
592 default_value
= TypeManager
.ChangeType (default_value
, UnderlyingType
, out fail
);
594 Error_ConstantValueCannotBeConverted (c
, loc
);
598 em
.builder
.SetConstant (default_value
);
599 field_builders
.Add (em
.builder
);
600 member_to_value
[name
] = default_value
;
602 if (!TypeManager
.RegisterFieldValue (em
.builder
, default_value
))
605 return default_value
;
608 public override bool DefineMembers (TypeContainer parent
)
613 public override bool Define ()
616 // If there was an error during DefineEnum, return
618 if (TypeBuilder
== null)
621 EmitContext ec
= new EmitContext (this, this, Location
, null,
622 UnderlyingType
, ModFlags
, false);
625 object default_value
= 0;
628 foreach (string name
in ordered_enums
) {
630 // Have we already been defined, thanks to some cross-referencing ?
632 if (member_to_value
.Contains (name
))
635 Location loc
= (Mono
.CSharp
.Location
) member_to_location
[name
];
637 if (this [name
] != null) {
638 default_value
= LookupEnumValue (ec
, name
, loc
);
640 if (default_value
== null)
643 if (name
== "value__"){
644 Report
.Error (76, loc
, "The name `value__' is reserved for enumerations");
648 EnumMember em
= (EnumMember
) defined_names
[name
];
650 em
.DefineMember (TypeBuilder
);
651 FieldBuilder fb
= em
.builder
;
653 if (default_value
== null) {
654 Report
.Error (543, loc
, "Enumerator value for '" + name
+ "' is too large to " +
660 default_value
= TypeManager
.ChangeType (default_value
, UnderlyingType
, out fail
);
662 Error_ConstantValueCannotBeConverted (default_value
, loc
);
666 fb
.SetConstant (default_value
);
667 field_builders
.Add (fb
);
668 member_to_value
[name
] = default_value
;
670 if (!TypeManager
.RegisterFieldValue (fb
, default_value
))
674 default_value
= GetNextDefaultValue (default_value
);
680 public override void Emit ()
682 EmitContext ec
= new EmitContext (
683 Parent
, this, Location
, null, null, ModFlags
, false);
685 if (OptAttributes
!= null) {
686 OptAttributes
.Emit (ec
, this);
689 foreach (EnumMember em
in defined_names
.Values
) {
696 void VerifyClsName ()
698 Hashtable ht
= new Hashtable ();
699 foreach (string name
in ordered_enums
) {
700 string locase
= name
.ToLower (System
.Globalization
.CultureInfo
.InvariantCulture
);
701 if (!ht
.Contains (locase
)) {
702 ht
.Add (locase
, defined_names
[name
]);
706 MemberCore conflict
= (MemberCore
)ht
[locase
];
707 Report
.SymbolRelatedToPreviousError (conflict
);
708 conflict
= GetDefinition (name
);
709 Report
.Error (3005, conflict
.Location
, "Identifier '{0}' differing only in case is not CLS-compliant", conflict
.GetSignatureForError ());
713 protected override bool VerifyClsCompliance (DeclSpace ds
)
715 if (!base.VerifyClsCompliance (ds
))
720 if (!AttributeTester
.IsClsCompliant (UnderlyingType
)) {
721 Report
.Error (3009, Location
, "'{0}': base type '{1}' is not CLS-compliant", GetSignatureForError (), TypeManager
.CSharpName (UnderlyingType
));
728 /// Returns full enum name.
730 string GetEnumeratorName (string valueName
)
732 return String
.Concat (Name
, ".", valueName
);
738 public override MemberList
FindMembers (MemberTypes mt
, BindingFlags bf
,
739 MemberFilter filter
, object criteria
)
741 ArrayList members
= new ArrayList ();
743 if ((mt
& MemberTypes
.Field
) != 0) {
744 if (criteria
is string){
745 if (member_to_value
[criteria
] == null && current_ec
!= null){
746 LookupEnumValue (current_ec
, (string) criteria
, Location
.Null
);
750 foreach (FieldBuilder fb
in field_builders
)
751 if (filter (fb
, criteria
) == true)
755 return new MemberList (members
);
758 public override MemberCache MemberCache
{
764 public ArrayList ValueNames
{
766 return ordered_enums
;
771 public Expression
this [string name
] {
773 return ((EnumMember
) defined_names
[name
]).Type
;
777 public override AttributeTargets AttributeTargets
{
779 return AttributeTargets
.Enum
;
783 protected override void VerifyObsoleteAttribute()
785 // UnderlyingType is never obsolete