37d6e1eb93d24ff996e9a9762e35a5d72f00c88e
[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 // Licensed under the terms of the GNU GPL
9 //
10 // (C) 2001 Ximian, Inc (http://www.ximian.com)
11 //
12
13 using System;
14 using System.Collections;
15 using System.Collections.Specialized;
16 using System.Reflection;
17 using System.Reflection.Emit;
18 using System.Globalization;
19
20 namespace Mono.CSharp {
21
22         public class EnumMember : MemberCore, IConstant {
23                 static string[] attribute_targets = new string [] { "field" };
24
25                 public FieldBuilder builder;
26
27                 readonly Enum parent_enum;
28                 readonly Expression ValueExpr;
29                 readonly EnumMember prev_member;
30
31                 Constant value;
32                 bool in_transit;
33
34                 public EnumMember (Enum parent_enum, EnumMember prev_member, Expression expr,
35                                 MemberName name, Attributes attrs):
36                         base (parent_enum.Parent, name, attrs)
37                 {
38                         this.parent_enum = parent_enum;
39                         this.ModFlags = parent_enum.ModFlags;
40                         this.ValueExpr = expr;
41                         this.prev_member = prev_member;
42                 }
43
44                 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder cb)
45                 {
46                         if (a.Type == TypeManager.marshal_as_attr_type) {
47                                 UnmanagedMarshal marshal = a.GetMarshal (this);
48                                 if (marshal != null) {
49                                         builder.SetMarshal (marshal);
50                                 }
51                                 return;
52                         }
53
54                         if (a.Type.IsSubclassOf (TypeManager.security_attr_type)) {
55                                 a.Error_InvalidSecurityParent ();
56                                 return;
57                         }
58
59                         builder.SetCustomAttribute (cb);
60                 }
61
62                 public override AttributeTargets AttributeTargets {
63                         get {
64                                 return AttributeTargets.Field;
65                         }
66                 }
67
68                 bool IsValidEnumType (Type t)
69                 {
70                         return (t == TypeManager.int32_type || t == TypeManager.uint32_type || t == TypeManager.int64_type ||
71                                 t == TypeManager.byte_type || t == TypeManager.sbyte_type || t == TypeManager.short_type ||
72                                 t == TypeManager.ushort_type || t == TypeManager.uint64_type || t == TypeManager.char_type ||
73                                 t.IsEnum);
74                 }
75         
76                 public override bool Define ()
77                 {
78                         const FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal;
79                         TypeBuilder tb = parent_enum.TypeBuilder;
80                         builder = tb.DefineField (Name, tb, attr);
81
82                         TypeManager.RegisterConstant (builder, this);
83                         return true;
84                 }
85
86                 // Because parent is TypeContainer and we have DeclSpace only
87                 public override void CheckObsoleteness (Location loc)
88                 {
89                         parent_enum.CheckObsoleteness (loc);
90
91                         ObsoleteAttribute oa = GetObsoleteAttribute ();
92                         if (oa == null) {
93                                 return;
94                         }
95
96                         AttributeTester.Report_ObsoleteMessage (oa, GetSignatureForError (), loc);
97                 }
98
99                 public bool ResolveValue ()
100                 {
101                         if (value != null)
102                                 return true;
103
104                         if (in_transit) {
105                                 // suppress cyclic errors
106                                 value = new EnumConstant (New.Constantify (parent_enum.UnderlyingType), parent_enum.TypeBuilder);
107                                 Const.Error_CyclicDeclaration (this);
108                                 return false;
109                         }
110
111                         if (ValueExpr != null) {
112                                 in_transit = true;
113                                 Constant c = ValueExpr.ResolveAsConstant (parent_enum.EmitContext, this);
114                                 in_transit = false;
115
116                                 if (c == null)
117                                         return false;
118
119                                 if (c is EnumConstant)
120                                         c = ((EnumConstant)c).Child;
121                                         
122                                 c = c.ToType (parent_enum.UnderlyingType, Location);
123                                 if (c == null)
124                                         return false;
125
126                                 if (!IsValidEnumType (c.Type)) {
127                                         Report.Error (1008, Location, "Type byte, sbyte, short, ushort, int, uint, long or ulong expected");
128                                         return false;
129                                 }
130
131                                 in_transit = false;
132                                 value = new EnumConstant (c, parent_enum.TypeBuilder);
133                                 return true;
134                         }
135
136                         if (prev_member == null) {
137                                 value = new EnumConstant (New.Constantify (parent_enum.UnderlyingType), parent_enum.TypeBuilder);
138                                 return true;
139                         }
140
141                         if (!prev_member.ResolveValue ())
142                                 return false;
143
144                         in_transit = true;
145
146                         try {
147                                 value = prev_member.value.Increment ();
148                         }
149                         catch (OverflowException) {
150                                 Report.Error (543, Location, "The enumerator value `{0}' is too large to fit in its type `{1}'",
151                                         GetSignatureForError (), TypeManager.CSharpName (parent_enum.UnderlyingType));
152                                 return false;
153                         }
154                         in_transit = false;
155
156                         return true;
157                 }
158
159                 public bool Emit (EmitContext ec)
160                 {
161                         if (OptAttributes != null)
162                                 OptAttributes.Emit (ec, this); 
163
164                         if (!ResolveValue ())
165                                 return false;
166
167                         builder.SetConstant (value.GetValue ());
168                         Emit ();
169                         return true;
170                 }
171
172                 public override string GetSignatureForError()
173                 {
174                         return String.Concat (parent_enum.GetSignatureForError (), '.', Name);
175                 }
176
177                 public override string[] ValidAttributeTargets {
178                         get {
179                                 return attribute_targets;
180                         }
181                 }
182
183                 protected override bool VerifyClsCompliance(DeclSpace ds)
184                 {
185                         // Because parent is TypeContainer and we have only DeclSpace parent.
186                         // Parameter replacing is required
187                         return base.VerifyClsCompliance (parent_enum);
188                 }
189
190                 public override string DocCommentHeader {
191                         get { return "F:"; }
192                 }
193
194                 #region IConstant Members
195
196                 public Constant Value {
197                         get {
198                                 return value;
199                         }
200                 }
201
202                 #endregion
203         }
204
205         /// <summary>
206         ///   Enumeration container
207         /// </summary>
208         public class Enum : DeclSpace {
209                 Expression BaseType;
210                 public Type UnderlyingType;
211
212                 static MemberList no_list = new MemberList (new object[0]);
213                 
214                 public const int AllowedModifiers =
215                         Modifiers.NEW |
216                         Modifiers.PUBLIC |
217                         Modifiers.PROTECTED |
218                         Modifiers.INTERNAL |
219                         Modifiers.PRIVATE;
220
221                 public Enum (NamespaceEntry ns, TypeContainer parent, Expression type,
222                              int mod_flags, MemberName name, Attributes attrs)
223                         : base (ns, parent, name, attrs)
224                 {
225                         this.BaseType = type;
226                         ModFlags = Modifiers.Check (AllowedModifiers, mod_flags,
227                                                     IsTopLevel ? Modifiers.INTERNAL : Modifiers.PRIVATE, name.Location);
228                 }
229
230                 public void AddEnumMember (EnumMember em)
231                 {
232                         if (em.Name == "value__") {
233                                 Report.Error (76, em.Location, "An item in an enumeration cannot have an identifier `value__'");
234                                 return;
235                         }
236
237                         if (!AddToContainer (em, em.Name))
238                                 return;
239                 }
240                 
241                 public override TypeBuilder DefineType ()
242                 {
243                         if (TypeBuilder != null)
244                                 return TypeBuilder;
245
246                         ec = new EmitContext (this, this, Location, null, null, ModFlags, false);
247                         ec.InEnumContext = true;
248
249                         if (!(BaseType is TypeLookupExpression)) {
250                                 Report.Error (1008, Location,
251                                         "Type byte, sbyte, short, ushort, int, uint, long or ulong expected");
252                                 return null;
253                         }
254
255                         TypeExpr ute = ResolveBaseTypeExpr (BaseType, false, Location);
256                         UnderlyingType = ute.Type;
257
258                         if (UnderlyingType != TypeManager.int32_type &&
259                             UnderlyingType != TypeManager.uint32_type &&
260                             UnderlyingType != TypeManager.int64_type &&
261                             UnderlyingType != TypeManager.uint64_type &&
262                             UnderlyingType != TypeManager.short_type &&
263                             UnderlyingType != TypeManager.ushort_type &&
264                             UnderlyingType != TypeManager.byte_type  &&
265                             UnderlyingType != TypeManager.sbyte_type) {
266                                 Report.Error (1008, Location,
267                                         "Type byte, sbyte, short, ushort, int, uint, long or ulong expected");
268                                 return null;
269                         }
270
271                         if (IsTopLevel) {
272                                 if (TypeManager.NamespaceClash (Name, Location))
273                                         return null;
274                                 
275                                 ModuleBuilder builder = CodeGen.Module.Builder;
276
277                                 TypeBuilder = builder.DefineType (Name, TypeAttr, TypeManager.enum_type);
278                         } else {
279                                 TypeBuilder builder = Parent.TypeBuilder;
280
281                                 TypeBuilder = builder.DefineNestedType (
282                                         Basename, TypeAttr, TypeManager.enum_type);
283                         }
284
285                         ec.ContainerType = TypeBuilder;
286
287                         //
288                         // Call MapToInternalType for corlib
289                         //
290                         TypeBuilder.DefineField ("value__", UnderlyingType,
291                                                  FieldAttributes.Public | FieldAttributes.SpecialName
292                                                  | FieldAttributes.RTSpecialName);
293
294                         TypeManager.AddUserType (this);
295
296                         foreach (EnumMember em in defined_names.Values) {
297                                 if (!em.Define ())
298                                         return null;
299                         }
300
301                         return TypeBuilder;
302                 }
303                 
304                 public override bool Define ()
305                 {
306                         return true;
307                 }
308
309                 public override void Emit ()
310                 {
311                         if (OptAttributes != null) {
312                                 OptAttributes.Emit (ec, this);
313                         }
314
315                         foreach (EnumMember em in defined_names.Values) {
316                                 if (!em.Emit (ec))
317                                         return;
318                         }
319
320                         base.Emit ();
321                 }
322
323                 //
324                 // IMemberFinder
325                 //
326                 public override MemberList FindMembers (MemberTypes mt, BindingFlags bf,
327                         MemberFilter filter, object criteria)
328                 {
329                         if ((mt & MemberTypes.Field) == 0)
330                                 return no_list;
331
332                         EnumMember em = defined_names [criteria] as EnumMember;
333                         if (em == null)
334                                 return no_list;
335
336                         FieldBuilder[] fb = new FieldBuilder[] { em.builder };
337                         return new MemberList (fb);
338                 }
339
340                 void VerifyClsName ()
341                 {
342                         HybridDictionary dict = new HybridDictionary (defined_names.Count, true);
343                         foreach (EnumMember em in defined_names.Values) {
344                                 if (!em.IsClsComplianceRequired (this))
345                                         continue;
346
347                                 try {
348                                         dict.Add (em.Name, em);
349                                 }
350                                 catch (ArgumentException) {
351                                         Report.SymbolRelatedToPreviousError (em);
352                                         MemberCore col = (MemberCore)dict [em.Name];
353                                         Report.Error (3005, col.Location, "Identifier `{0}' differing only in case is not CLS-compliant", col.GetSignatureForError ());
354                                 }
355                         }
356                 }
357
358                 protected override bool VerifyClsCompliance (DeclSpace ds)
359                 {
360                         if (!base.VerifyClsCompliance (ds))
361                                 return false;
362
363                         VerifyClsName ();
364
365                         if (UnderlyingType == TypeManager.uint32_type ||
366                                 UnderlyingType == TypeManager.uint64_type ||
367                                 UnderlyingType == TypeManager.ushort_type) {
368                                 Report.Error (3009, Location, "`{0}': base type `{1}' is not CLS-compliant", GetSignatureForError (), TypeManager.CSharpName (UnderlyingType));
369                         }
370
371                         return true;
372                 }
373         
374
375                 public override MemberCache MemberCache {
376                         get {
377                                 return null;
378                         }
379                 }
380
381                 public override AttributeTargets AttributeTargets {
382                         get {
383                                 return AttributeTargets.Enum;
384                         }
385                 }
386
387                 protected override TypeAttributes TypeAttr {
388                         get {
389                                 return Modifiers.TypeAttr (ModFlags, IsTopLevel) |
390                                 TypeAttributes.Class | TypeAttributes.Sealed |
391                                 base.TypeAttr;
392                         }
393                 }
394
395                 //
396                 // Generates xml doc comments (if any), and if required,
397                 // handle warning report.
398                 //
399                 internal override void GenerateDocComment (DeclSpace ds)
400                 {
401                         base.GenerateDocComment (ds);
402
403                         foreach (EnumMember em in defined_names.Values) {
404                                 em.GenerateDocComment (this);
405                         }
406                 }
407
408                 //
409                 //   Represents header string for documentation comment.
410                 //
411                 public override string DocCommentHeader {
412                         get { return "T:"; }
413                 }
414         }
415 }