8a2884023300f0e50657440dc14f89829759a0d2
[mono.git] / mcs / mcs / 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 // Copyright 2011 Xamarin Inc
13 //
14
15 using System;
16
17 #if STATIC
18 using MetaType = IKVM.Reflection.Type;
19 using IKVM.Reflection;
20 #else
21 using MetaType = System.Type;
22 using System.Reflection;
23 #endif
24
25 namespace Mono.CSharp
26 {
27
28         public class EnumMember : Const
29         {
30 #if !STATIC
31                 class MemberTypeDelegator : TypeDelegator
32                 {
33                         Type underlyingType;
34
35                         public MemberTypeDelegator (Type delegatingType, Type underlyingType)
36                                 : base (delegatingType)
37                         {
38                                 this.underlyingType = underlyingType;
39                         }
40
41                         public override Type GetEnumUnderlyingType ()
42                         {
43                                 return underlyingType;
44                         }
45                 }
46 #endif
47
48                 class EnumTypeExpr : TypeExpr
49                 {
50                         public override TypeSpec ResolveAsType (IMemberContext ec, bool allowUnboundTypeArguments)
51                         {
52                                 type = ec.CurrentType;
53                                 eclass = ExprClass.Type;
54                                 return type;
55                         }
56                 }
57
58                 public EnumMember (Enum parent, MemberName name, Attributes attrs)
59                         : base (parent, new EnumTypeExpr (), Modifiers.PUBLIC, name, attrs)
60                 {
61                 }
62
63                 static bool IsValidEnumType (TypeSpec t)
64                 {
65                         switch (t.BuiltinType) {
66                         case BuiltinTypeSpec.Type.Int:
67                         case BuiltinTypeSpec.Type.UInt:
68                         case BuiltinTypeSpec.Type.Long:
69                         case BuiltinTypeSpec.Type.Byte:
70                         case BuiltinTypeSpec.Type.SByte:
71                         case BuiltinTypeSpec.Type.Short:
72                         case BuiltinTypeSpec.Type.UShort:
73                         case BuiltinTypeSpec.Type.ULong:
74                         case BuiltinTypeSpec.Type.Char:
75                                 return true;
76                         default:
77                                 return t.IsEnum;
78                         }
79                 }
80
81                 public override Constant ConvertInitializer (ResolveContext rc, Constant expr)
82                 {
83                         if (expr is EnumConstant)
84                                 expr = ((EnumConstant) expr).Child;
85
86                         var en = (Enum)Parent;
87                         var underlying = en.UnderlyingType;
88                         if (expr != null) {
89                                 expr = expr.ImplicitConversionRequired (rc, underlying);
90                                 if (expr != null && !IsValidEnumType (expr.Type)) {
91                                         en.Error_UnderlyingType (Location);
92                                         expr = null;
93                                 }
94                         }
95
96                         if (expr == null)
97                                 expr = New.Constantify (underlying, Location);
98
99                         return new EnumConstant (expr, MemberType);
100                 }
101
102                 public override bool Define ()
103                 {
104                         if (!ResolveMemberType ())
105                                 return false;
106
107                         MetaType ftype = MemberType.GetMetaInfo ();
108 #if !STATIC
109                         //
110                         // Workaround for .net SRE limitation which cannot define field of unbaked enum type
111                         // which is how all enums are declared
112                         //
113                         ftype = new MemberTypeDelegator (ftype, ((Enum)Parent).UnderlyingType.GetMetaInfo ());
114 #endif
115
116                         const FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal;
117                         FieldBuilder = Parent.TypeBuilder.DefineField (Name, ftype, attr);
118                         spec = new ConstSpec (Parent.Definition, this, MemberType, FieldBuilder, ModFlags, initializer);
119
120                         Parent.MemberCache.AddMember (spec);
121                         return true;
122                 }
123                 
124                 public override void Accept (StructuralVisitor visitor)
125                 {
126                         visitor.Visit (this);
127                 }
128
129         }
130
131         /// <summary>
132         ///   Enumeration container
133         /// </summary>
134         public class Enum : TypeDefinition
135         {
136                 //
137                 // Implicit enum member initializer, used when no constant value is provided
138                 //
139                 sealed class ImplicitInitializer : Expression
140                 {
141                         readonly EnumMember prev;
142                         readonly EnumMember current;
143
144                         public ImplicitInitializer (EnumMember current, EnumMember prev)
145                         {
146                                 this.current = current;
147                                 this.prev = prev;
148                         }
149
150                         public override bool ContainsEmitWithAwait ()
151                         {
152                                 return false;
153                         }
154
155                         public override Expression CreateExpressionTree (ResolveContext ec)
156                         {
157                                 throw new NotSupportedException ("Missing Resolve call");
158                         }
159
160                         protected override Expression DoResolve (ResolveContext rc)
161                         {
162                                 // We are the first member
163                                 if (prev == null) {
164                                         return New.Constantify (current.Parent.Definition, Location);
165                                 }
166
167                                 var c = ((ConstSpec) prev.Spec).GetConstant (rc) as EnumConstant;
168                                 try {
169                                         return c.Increment ();
170                                 } catch (OverflowException) {
171                                         rc.Report.Error (543, current.Location,
172                                                 "The enumerator value `{0}' is outside the range of enumerator underlying type `{1}'",
173                                                 current.GetSignatureForError (), ((Enum) current.Parent).UnderlyingType.GetSignatureForError ());
174
175                                         return New.Constantify (current.Parent.Definition, current.Location);
176                                 }
177                         }
178
179                         public override void Emit (EmitContext ec)
180                         {
181                                 throw new NotSupportedException ("Missing Resolve call");
182                         }
183                 }
184
185                 public static readonly string UnderlyingValueField = "value__";
186
187                 const Modifiers AllowedModifiers =
188                         Modifiers.NEW |
189                         Modifiers.PUBLIC |
190                         Modifiers.PROTECTED |
191                         Modifiers.INTERNAL |
192                         Modifiers.PRIVATE;
193
194                 readonly FullNamedExpression underlying_type_expr;
195
196                 public Enum (TypeContainer parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, Attributes attrs)
197                         : base (parent, name, attrs, MemberKind.Enum)
198                 {
199                         underlying_type_expr = type;
200                         var accmods = IsTopLevel ? Modifiers.INTERNAL : Modifiers.PRIVATE;
201                         ModFlags = ModifiersExtensions.Check (AllowedModifiers, mod_flags, accmods, Location, Report);
202                         spec = new EnumSpec (null, this, null, null, ModFlags);
203                 }
204
205                 #region Properties
206
207                 public override AttributeTargets AttributeTargets {
208                         get {
209                                 return AttributeTargets.Enum;
210                         }
211                 }
212
213                 public FullNamedExpression BaseTypeExpression {
214                         get {
215                                 return underlying_type_expr;
216                         }
217                 }
218
219                 protected override TypeAttributes TypeAttr {
220                         get {
221                                 return base.TypeAttr | TypeAttributes.Class | TypeAttributes.Sealed;
222                         }
223                 }
224
225                 public TypeSpec UnderlyingType {
226                         get {
227                                 return ((EnumSpec) spec).UnderlyingType;
228                         }
229                 }
230
231                 #endregion
232
233                 public override void Accept (StructuralVisitor visitor)
234                 {
235                         visitor.Visit (this);
236                 }
237
238                 public void AddEnumMember (EnumMember em)
239                 {
240                         if (em.Name == UnderlyingValueField) {
241                                 Report.Error (76, em.Location, "An item in an enumeration cannot have an identifier `{0}'",
242                                         UnderlyingValueField);
243                                 return;
244                         }
245
246                         AddMember (em);
247                 }
248
249                 public void Error_UnderlyingType (Location loc)
250                 {
251                         Report.Error (1008, loc,
252                                 "Type byte, sbyte, short, ushort, int, uint, long or ulong expected");
253                 }
254
255                 protected override void DoDefineContainer ()
256                 {
257                         TypeSpec ut;
258                         if (underlying_type_expr != null) {
259                                 ut = underlying_type_expr.ResolveAsType (this);
260                                 if (!EnumSpec.IsValidUnderlyingType (ut)) {
261                                         Error_UnderlyingType (underlying_type_expr.Location);
262                                         ut = null;
263                                 }
264                         } else {
265                                 ut = null;
266                         }
267
268                         if (ut == null)
269                                 ut = Compiler.BuiltinTypes.Int;
270
271                         ((EnumSpec) spec).UnderlyingType = ut;
272
273                         TypeBuilder.DefineField (UnderlyingValueField, UnderlyingType.GetMetaInfo (),
274                                 FieldAttributes.Public | FieldAttributes.SpecialName | FieldAttributes.RTSpecialName);
275
276                         DefineBaseTypes ();
277                 }
278
279                 protected override bool DoDefineMembers ()
280                 {
281                         for (int i = 0; i < Members.Count; ++i) {
282                                 EnumMember em = (EnumMember) Members[i];
283                                 if (em.Initializer == null) {
284                                         em.Initializer = new ImplicitInitializer (em, i == 0 ? null : (EnumMember) Members[i - 1]);
285                                 }
286
287                                 em.Define ();
288                         }
289
290                         return true;
291                 }
292
293                 public override bool IsUnmanagedType ()
294                 {
295                         return true;
296                 }
297
298                 protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class)
299                 {
300                         base_type = Compiler.BuiltinTypes.Enum;
301                         base_class = null;
302                         return null;
303                 }
304
305                 protected override bool VerifyClsCompliance ()
306                 {
307                         if (!base.VerifyClsCompliance ())
308                                 return false;
309
310                         switch (UnderlyingType.BuiltinType) {
311                         case BuiltinTypeSpec.Type.UInt:
312                         case BuiltinTypeSpec.Type.ULong:
313                         case BuiltinTypeSpec.Type.UShort:
314                                 Report.Warning (3009, 1, Location, "`{0}': base type `{1}' is not CLS-compliant",
315                                         GetSignatureForError (), UnderlyingType.GetSignatureForError ());
316                                 break;
317                         }
318
319                         return true;
320                 }       
321         }
322
323         class EnumSpec : TypeSpec
324         {
325                 TypeSpec underlying;
326
327                 public EnumSpec (TypeSpec declaringType, ITypeDefinition definition, TypeSpec underlyingType, MetaType info, Modifiers modifiers)
328                         : base (MemberKind.Enum, declaringType, definition, info, modifiers | Modifiers.SEALED)
329                 {
330                         this.underlying = underlyingType;
331                 }
332
333                 public TypeSpec UnderlyingType {
334                         get {
335                                 return underlying;
336                         }
337                         set {
338                                 if (underlying != null)
339                                         throw new InternalErrorException ("UnderlyingType reset");
340
341                                 underlying = value;
342                         }
343                 }
344
345                 public static TypeSpec GetUnderlyingType (TypeSpec t)
346                 {
347                         return ((EnumSpec) t.GetDefinition ()).UnderlyingType;
348                 }
349
350                 public static bool IsValidUnderlyingType (TypeSpec type)
351                 {
352                         switch (type.BuiltinType) {
353                         case BuiltinTypeSpec.Type.Int:
354                         case BuiltinTypeSpec.Type.UInt:
355                         case BuiltinTypeSpec.Type.Long:
356                         case BuiltinTypeSpec.Type.Byte:
357                         case BuiltinTypeSpec.Type.SByte:
358                         case BuiltinTypeSpec.Type.Short:
359                         case BuiltinTypeSpec.Type.UShort:
360                         case BuiltinTypeSpec.Type.ULong:
361                                 return true;
362                         }
363
364                         return false;
365                 }
366         }
367 }