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