*** Merged revisions from mcs: 51156, 51258, 51349
[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.Parent, 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 (Name, 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 (ec, this);
317                         }
318
319                         foreach (EnumMember em in defined_names.Values) {
320                                 if (!em.Emit (ec))
321                                         return;
322                         }
323
324                         base.Emit ();
325                 }
326
327                 //
328                 // IMemberFinder
329                 //
330                 public override MemberList FindMembers (MemberTypes mt, BindingFlags bf,
331                         MemberFilter filter, object criteria)
332                 {
333                         if ((mt & MemberTypes.Field) == 0)
334                                 return no_list;
335
336                         EnumMember em = defined_names [criteria] as EnumMember;
337                         if (em == null)
338                                 return no_list;
339
340                         FieldBuilder[] fb = new FieldBuilder[] { em.builder };
341                         return new MemberList (fb);
342                 }
343
344                 void VerifyClsName ()
345                 {
346                         HybridDictionary dict = new HybridDictionary (defined_names.Count, true);
347                         foreach (EnumMember em in defined_names.Values) {
348                                 if (!em.IsClsComplianceRequired (this))
349                                         continue;
350
351                                 try {
352                                         dict.Add (em.Name, em);
353                                 }
354                                 catch (ArgumentException) {
355                                         Report.SymbolRelatedToPreviousError ((MemberCore)dict [em.Name]);
356                                         Report.Warning (3005, em.Location, "Identifier `{0}' differing only in case is not CLS-compliant", em.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 (!AttributeTester.IsClsCompliant (UnderlyingType)) {
369                                 Report.Error (3009, Location, "`{0}': base type `{1}' is not CLS-compliant", GetSignatureForError (), TypeManager.CSharpName (UnderlyingType));
370                         }
371
372                         return true;
373                 }
374         
375
376                 public override MemberCache MemberCache {
377                         get {
378                                 return null;
379                         }
380                 }
381
382                 public override AttributeTargets AttributeTargets {
383                         get {
384                                 return AttributeTargets.Enum;
385                         }
386                 }
387
388                 protected override TypeAttributes TypeAttr {
389                         get {
390                                 return Modifiers.TypeAttr (ModFlags, IsTopLevel) |
391                                 TypeAttributes.Class | TypeAttributes.Sealed |
392                                 base.TypeAttr;
393                         }
394                 }
395
396                 //
397                 // Generates xml doc comments (if any), and if required,
398                 // handle warning report.
399                 //
400                 internal override void GenerateDocComment (DeclSpace ds)
401                 {
402                         base.GenerateDocComment (ds);
403
404                         foreach (EnumMember em in defined_names.Values) {
405                                 em.GenerateDocComment (this);
406                         }
407                 }
408
409                 //
410                 //   Represents header string for documentation comment.
411                 //
412                 public override string DocCommentHeader {
413                         get { return "T:"; }
414                 }
415         }
416 }