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