d8f5415fe500a691b9c52b3f49c3be36e86fbb49
[mono.git] / enum.cs
1 //
2 // enum.cs: Enum handling.
3 //
4 // Author: Miguel de Icaza (miguel@gnu.org)
5 //         Ravi Pratap     (ravi@ximian.com)
6 //         Marek Safar     (marek.safar@seznam.cz)
7 //
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 //
10 // Copyright 2001 Ximian, Inc (http://www.ximian.com)
11 // Copyright 2003-2003 Novell, Inc (http://www.novell.com)
12 //
13
14 using System;
15 using System.Collections.Generic;
16 using System.Reflection;
17 using System.Reflection.Emit;
18 using System.Globalization;
19
20 namespace Mono.CSharp {
21
22         public class EnumMember : Const
23         {
24                 class EnumTypeExpr : TypeExpr
25                 {
26                         protected override TypeExpr DoResolveAsTypeStep (IMemberContext ec)
27                         {
28                                 type = ec.CurrentType;
29                                 return this;
30                         }
31
32                         public override TypeExpr ResolveAsTypeTerminal (IMemberContext ec, bool silent)
33                         {
34                                 return DoResolveAsTypeStep (ec);
35                         }
36                 }
37
38                 public EnumMember (Enum parent, MemberName name, Attributes attrs)
39                         : base (parent, new EnumTypeExpr (), Modifiers.PUBLIC, name, attrs)
40                 {
41                 }
42
43                 static bool IsValidEnumType (TypeSpec t)
44                 {
45                         return (t == TypeManager.int32_type || t == TypeManager.uint32_type || t == TypeManager.int64_type ||
46                                 t == TypeManager.byte_type || t == TypeManager.sbyte_type || t == TypeManager.short_type ||
47                                 t == TypeManager.ushort_type || t == TypeManager.uint64_type || t == TypeManager.char_type ||
48                                 TypeManager.IsEnumType (t));
49                 }
50
51                 public override Constant ConvertInitializer (ResolveContext rc, Constant expr)
52                 {
53                         if (expr is EnumConstant)
54                                 expr = ((EnumConstant) expr).Child;
55
56                         var underlying = ((Enum) Parent).UnderlyingType;
57                         if (expr != null) {
58                                 expr = expr.ImplicitConversionRequired (rc, underlying, Location);
59                                 if (expr != null && !IsValidEnumType (expr.Type)) {
60                                         Enum.Error_1008 (Location, Report);
61                                         expr = null;
62                                 }
63                         }
64
65                         if (expr == null)
66                                 expr = New.Constantify (underlying);
67
68                         return new EnumConstant (expr, MemberType).Resolve (rc);
69                 }
70
71                 public override bool Define ()
72                 {
73                         if (!ResolveMemberType ())
74                                 return false;
75
76                         const FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal;
77                         FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType.GetMetaInfo (), attr);
78                         spec = new ConstSpec (Parent.Definition, this, MemberType, FieldBuilder, ModFlags, initializer);
79
80                         Parent.MemberCache.AddMember (spec);
81                         return true;
82                 }
83         }
84
85         /// <summary>
86         ///   Enumeration container
87         /// </summary>
88         public class Enum : TypeContainer
89         {
90                 //
91                 // Implicit enum member initializer, used when no constant value is provided
92                 //
93                 class ImplicitInitializer : Expression
94                 {
95                         readonly EnumMember prev;
96                         readonly EnumMember current;
97
98                         public ImplicitInitializer (EnumMember current, EnumMember prev)
99                         {
100                                 this.current = current;
101                                 this.prev = prev;
102                         }
103
104                         public override Expression CreateExpressionTree (ResolveContext ec)
105                         {
106                                 throw new NotSupportedException ("Missing Resolve call");
107                         }
108
109                         protected override Expression DoResolve (ResolveContext rc)
110                         {
111                                 // We are the first member
112                                 if (prev == null) {
113                                         return New.Constantify (current.Parent.Definition).Resolve (rc);
114                                 }
115
116                                 var c = ((ConstSpec) prev.Spec).GetConstant (rc) as EnumConstant;
117                                 try {
118                                         return c.Increment ().Resolve (rc);
119                                 } catch (OverflowException) {
120                                         rc.Report.Error (543, current.Location,
121                                                 "The enumerator value `{0}' is outside the range of enumerator underlying type `{1}'",
122                                                 current.GetSignatureForError (), ((Enum) current.Parent).UnderlyingType.GetSignatureForError ());
123
124                                         return New.Constantify (current.Parent.Definition).Resolve (rc);
125                                 }
126                         }
127
128                         public override void Emit (EmitContext ec)
129                         {
130                                 throw new NotSupportedException ("Missing Resolve call");
131                         }
132                 }
133
134                 public static readonly string UnderlyingValueField = "value__";
135
136                 const Modifiers AllowedModifiers =
137                         Modifiers.NEW |
138                         Modifiers.PUBLIC |
139                         Modifiers.PROTECTED |
140                         Modifiers.INTERNAL |
141                         Modifiers.PRIVATE;
142
143                 public Enum (NamespaceEntry ns, DeclSpace parent, TypeExpression type,
144                              Modifiers mod_flags, MemberName name, Attributes attrs)
145                         : base (ns, parent, name, attrs, MemberKind.Enum)
146                 {
147                         base_type_expr = type;
148                         var accmods = IsTopLevel ? Modifiers.INTERNAL : Modifiers.PRIVATE;
149                         ModFlags = ModifiersExtensions.Check (AllowedModifiers, mod_flags, accmods, Location, Report);
150                         spec = new EnumSpec (null, this, null, null, ModFlags);
151                 }
152
153                 #region Properties
154
155                 public override AttributeTargets AttributeTargets {
156                         get {
157                                 return AttributeTargets.Enum;
158                         }
159                 }
160
161                 public TypeExpr BaseTypeExpression {
162                         get {
163                                 return base_type_expr;
164                         }
165                 }
166
167                 protected override TypeAttributes TypeAttr {
168                         get {
169                                 return ModifiersExtensions.TypeAttr (ModFlags, IsTopLevel) |
170                                         TypeAttributes.Class | TypeAttributes.Sealed | base.TypeAttr;
171                         }
172                 }
173
174                 public TypeSpec UnderlyingType {
175                         get {
176                                 return ((EnumSpec) spec).UnderlyingType;
177                         }
178                 }
179
180                 #endregion
181
182                 public void AddEnumMember (EnumMember em)
183                 {
184                         if (em.Name == UnderlyingValueField) {
185                                 Report.Error (76, em.Location, "An item in an enumeration cannot have an identifier `{0}'",
186                                         UnderlyingValueField);
187                                 return;
188                         }
189
190                         AddConstant (em);
191                 }
192
193                 public static void Error_1008 (Location loc, Report Report)
194                 {
195                         Report.Error (1008, loc,
196                                 "Type byte, sbyte, short, ushort, int, uint, long or ulong expected");
197                 }
198
199                 protected override bool DefineNestedTypes ()
200                 {
201                         ((EnumSpec) spec).UnderlyingType = base_type_expr == null ? TypeManager.int32_type : base_type_expr.Type;
202
203                         TypeBuilder.DefineField (UnderlyingValueField, UnderlyingType.GetMetaInfo (),
204                                 FieldAttributes.Public | FieldAttributes.SpecialName | FieldAttributes.RTSpecialName);
205
206                         if (!RootContext.StdLib)
207                                 RootContext.hack_corlib_enums.Add (this);
208
209                         return true;
210                 }
211
212                 protected override bool DoDefineMembers ()
213                 {
214                         if (constants != null) {
215                                 for (int i = 0; i < constants.Count; ++i) {
216                                         EnumMember em = (EnumMember) constants [i];
217                                         if (em.Initializer == null) {
218                                                 em.Initializer = new ImplicitInitializer (em, i == 0 ? null : (EnumMember) constants[i - 1]);
219                                         }
220
221                                         em.Define ();
222                                 }
223                         }
224
225                         return true;
226                 }
227
228                 public override bool IsUnmanagedType ()
229                 {
230                         return true;
231                 }
232
233                 protected override TypeExpr[] ResolveBaseTypes (out TypeExpr base_class)
234                 {
235                         base_type = TypeManager.enum_type;
236                         base_class = base_type_expr;
237                         return null;
238                 }
239
240                 protected override bool VerifyClsCompliance ()
241                 {
242                         if (!base.VerifyClsCompliance ())
243                                 return false;
244
245                         if (UnderlyingType == TypeManager.uint32_type ||
246                                 UnderlyingType == TypeManager.uint64_type ||
247                                 UnderlyingType == TypeManager.ushort_type) {
248                                 Report.Warning (3009, 1, Location, "`{0}': base type `{1}' is not CLS-compliant", GetSignatureForError (), TypeManager.CSharpName (UnderlyingType));
249                         }
250
251                         return true;
252                 }       
253         }
254
255         class EnumSpec : TypeSpec
256         {
257                 TypeSpec underlying;
258
259                 public EnumSpec (TypeSpec declaringType, ITypeDefinition definition, TypeSpec underlyingType, Type info, Modifiers modifiers)
260                         : base (MemberKind.Enum, declaringType, definition, info, modifiers | Modifiers.SEALED)
261                 {
262                         this.underlying = underlyingType;
263                 }
264
265                 public TypeSpec UnderlyingType {
266                         get {
267                                 return underlying;
268                         }
269                         set {
270                                 if (underlying != null)
271                                         throw new InternalErrorException ("UnderlyingType reset");
272
273                                 underlying = value;
274                         }
275                 }
276
277                 public static TypeSpec GetUnderlyingType (TypeSpec t)
278                 {
279                         return ((EnumSpec) t.GetDefinition ()).UnderlyingType;
280                 }
281         }
282 }