2009-12-17 Marek Safar <marek.safar@gmail.com>
[mono.git] / mcs / mcs / field.cs
1 //
2 // field.cs: All field handlers
3 //
4 // Authors: Miguel de Icaza (miguel@gnu.org)
5 //          Martin Baulig (martin@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, 2002, 2003 Ximian, Inc (http://www.ximian.com)
11 // Copyright 2004-2008 Novell, Inc
12 //
13
14 using System;
15 using System.Collections.Generic;
16 using System.Reflection;
17 using System.Reflection.Emit;
18 using System.Runtime.InteropServices;
19
20 namespace Mono.CSharp
21 {
22         //
23         // Abstract class for all fields
24         //
25         abstract public class FieldBase : MemberBase
26         {
27                 public FieldBuilder FieldBuilder;
28                 public Status status;
29                 protected Expression initializer;
30
31                 [Flags]
32                 public enum Status : byte {
33                         HAS_OFFSET = 4          // Used by FieldMember.
34                 }
35
36                 static readonly string[] attribute_targets = new string [] { "field" };
37
38                 protected FieldBase (DeclSpace parent, FullNamedExpression type, Modifiers mod,
39                                      Modifiers allowed_mod, MemberName name, Attributes attrs)
40                         : base (parent, null, type, mod, allowed_mod | Modifiers.ABSTRACT, Modifiers.PRIVATE,
41                                 name, attrs)
42                 {
43                         if ((mod & Modifiers.ABSTRACT) != 0)
44                                 Report.Error (681, Location, "The modifier 'abstract' is not valid on fields. Try using a property instead");
45                 }
46
47                 public override AttributeTargets AttributeTargets {
48                         get {
49                                 return AttributeTargets.Field;
50                         }
51                 }
52
53                 public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder cb, PredefinedAttributes pa)
54                 {
55                         if (a.Type == pa.FieldOffset) {
56                                 status |= Status.HAS_OFFSET;
57
58                                 if (!Parent.PartialContainer.HasExplicitLayout) {
59                                         Report.Error (636, Location, "The FieldOffset attribute can only be placed on members of types marked with the StructLayout(LayoutKind.Explicit)");
60                                         return;
61                                 }
62
63                                 if ((ModFlags & Modifiers.STATIC) != 0 || this is Const) {
64                                         Report.Error (637, Location, "The FieldOffset attribute is not allowed on static or const fields");
65                                         return;
66                                 }
67                         }
68
69                         if (a.Type == pa.FixedBuffer) {
70                                 Report.Error (1716, Location, "Do not use 'System.Runtime.CompilerServices.FixedBuffer' attribute. Use the 'fixed' field modifier instead");
71                                 return;
72                         }
73
74 #if false
75                         if (a.Type == pa.MarshalAs) {
76                                 UnmanagedMarshal marshal = a.GetMarshal (this);
77                                 if (marshal != null) {
78                                         FieldBuilder.SetMarshal (marshal);
79                                 }
80                                 return;
81                         }
82 #endif
83                         if ((a.HasSecurityAttribute)) {
84                                 a.Error_InvalidSecurityParent ();
85                                 return;
86                         }
87
88                         if (a.Type == pa.Dynamic) {
89                                 a.Error_MisusedDynamicAttribute ();
90                                 return;
91                         }
92
93                         FieldBuilder.SetCustomAttribute (cb);
94                 }
95
96                 protected override bool CheckBase ()
97                 {
98                         if (!base.CheckBase ())
99                                 return false;
100  
101                         MemberInfo conflict_symbol = Parent.PartialContainer.FindBaseMemberWithSameName (Name, false);
102                         if (conflict_symbol == null) {
103                                 if ((ModFlags & Modifiers.NEW) != 0) {
104                                         Report.Warning (109, 4, Location, "The member `{0}' does not hide an inherited member. The new keyword is not required", GetSignatureForError ());
105                                 }
106                                 return true;
107                         }
108  
109                         if ((ModFlags & (Modifiers.NEW | Modifiers.OVERRIDE | Modifiers.BACKING_FIELD)) == 0) {
110                                 Report.SymbolRelatedToPreviousError (conflict_symbol);
111                                 Report.Warning (108, 2, Location, "`{0}' hides inherited member `{1}'. Use the new keyword if hiding was intended",
112                                         GetSignatureForError (), TypeManager.GetFullNameSignature (conflict_symbol));
113                         }
114  
115                         return true;
116                 }
117
118                 protected override void DoMemberTypeDependentChecks ()
119                 {
120                         base.DoMemberTypeDependentChecks ();
121
122                         if (TypeManager.IsGenericParameter (MemberType))
123                                 return;
124
125                         if (MemberType.IsSealed && MemberType.IsAbstract) {
126                                 Error_VariableOfStaticClass (Location, GetSignatureForError (), MemberType, Report);
127                         }
128
129                         CheckBase ();
130                         IsTypePermitted ();
131                 }
132
133                 //
134                 //   Represents header string for documentation comment.
135                 //
136                 public override string DocCommentHeader {
137                         get { return "F:"; }
138                 }
139
140                 public override void Emit ()
141                 {
142                         if (TypeManager.IsDynamicType (member_type)) {
143                                 PredefinedAttributes.Get.Dynamic.EmitAttribute (FieldBuilder);
144                         } else {
145                                 var trans_flags = TypeManager.HasDynamicTypeUsed (member_type);
146                                 if (trans_flags != null) {
147                                         var pa = PredefinedAttributes.Get.DynamicTransform;
148                                         if (pa.Constructor != null || pa.ResolveConstructor (Location, TypeManager.bool_type.MakeArrayType ())) {
149                                                 FieldBuilder.SetCustomAttribute (new CustomAttributeBuilder (pa.Constructor, new object[] { trans_flags }));
150                                         }
151                                 }
152                         }
153
154                         if ((ModFlags & Modifiers.COMPILER_GENERATED) != 0 && !Parent.IsCompilerGenerated)
155                                 PredefinedAttributes.Get.CompilerGenerated.EmitAttribute (FieldBuilder);
156
157                         if (OptAttributes != null) {
158                                 OptAttributes.Emit ();
159                         }
160
161                         if (((status & Status.HAS_OFFSET) == 0) && (ModFlags & (Modifiers.STATIC | Modifiers.BACKING_FIELD)) == 0 && Parent.PartialContainer.HasExplicitLayout) {
162                                 Report.Error (625, Location, "`{0}': Instance field types marked with StructLayout(LayoutKind.Explicit) must have a FieldOffset attribute", GetSignatureForError ());
163                         }
164
165                         base.Emit ();
166                 }
167
168                 public static void Error_VariableOfStaticClass (Location loc, string variable_name, Type static_class, Report Report)
169                 {
170                         Report.SymbolRelatedToPreviousError (static_class);
171                         Report.Error (723, loc, "`{0}': cannot declare variables of static types",
172                                 variable_name);
173                 }
174
175                 public Expression Initializer {
176                         set {
177                                 if (value != null) {
178                                         this.initializer = value;
179                                 }
180                         }
181                 }
182
183                 protected virtual bool IsFieldClsCompliant {
184                         get {
185                                 if (FieldBuilder == null)
186                                         return true;
187
188                                 return AttributeTester.IsClsCompliant (FieldBuilder.FieldType);
189                         }
190                 }
191
192                 public override string[] ValidAttributeTargets 
193                 {
194                         get {
195                                 return attribute_targets;
196                         }
197                 }
198
199                 protected override bool VerifyClsCompliance ()
200                 {
201                         if (!base.VerifyClsCompliance ())
202                                 return false;
203
204                         if (!IsFieldClsCompliant) {
205                                 Report.Warning (3003, 1, Location, "Type of `{0}' is not CLS-compliant",
206                                         GetSignatureForError ());
207                         }
208                         return true;
209                 }
210
211                 public void SetAssigned ()
212                 {
213                         caching_flags |= Flags.IsAssigned;
214                 }
215         }
216
217         interface IFixedBuffer
218         {
219                 FieldInfo Element { get; }
220                 Type ElementType { get; }
221         }
222
223         public class FixedFieldExternal: IFixedBuffer
224         {
225                 FieldInfo element_field;
226
227                 public FixedFieldExternal (FieldInfo fi)
228                 {
229                         element_field = fi.FieldType.GetField (FixedField.FixedElementName);
230                 }
231
232                 #region IFixedField Members
233
234                 public FieldInfo Element {
235                         get {
236                                 return element_field;
237                         }
238                 }
239
240                 public Type ElementType {
241                         get {
242                                 return element_field.FieldType;
243                         }
244                 }
245
246                 #endregion
247         }
248
249         /// <summary>
250         /// Fixed buffer implementation
251         /// </summary>
252         public class FixedField : FieldBase, IFixedBuffer
253         {
254                 public const string FixedElementName = "FixedElementField";
255                 static int GlobalCounter = 0;
256                 static object[] ctor_args = new object[] { (short)LayoutKind.Sequential };
257                 static FieldInfo[] fi;
258
259                 TypeBuilder fixed_buffer_type;
260                 FieldBuilder element;
261                 Expression size_expr;
262
263                 const Modifiers AllowedModifiers =
264                         Modifiers.NEW |
265                         Modifiers.PUBLIC |
266                         Modifiers.PROTECTED |
267                         Modifiers.INTERNAL |
268                         Modifiers.PRIVATE |
269                         Modifiers.UNSAFE;
270
271                 public FixedField (DeclSpace parent, FullNamedExpression type, Modifiers mod, string name,
272                         Expression size_expr, Attributes attrs, Location loc):
273                         base (parent, type, mod, AllowedModifiers, new MemberName (name, loc), attrs)
274                 {
275                         if (RootContext.Version < LanguageVersion.ISO_2)
276                                 Report.FeatureIsNotAvailable (loc, "fixed size buffers");
277
278                         this.size_expr = size_expr;
279                 }
280
281                 public override bool Define()
282                 {
283                         if (!base.Define ())
284                                 return false;
285
286                         if (!TypeManager.IsPrimitiveType (MemberType)) {
287                                 Report.Error (1663, Location, "`{0}': Fixed size buffers type must be one of the following: bool, byte, short, int, long, char, sbyte, ushort, uint, ulong, float or double",
288                                         GetSignatureForError ());
289                         }                       
290                         
291                         // Create nested fixed buffer container
292                         string name = String.Format ("<{0}>__FixedBuffer{1}", Name, GlobalCounter++);
293                         fixed_buffer_type = Parent.TypeBuilder.DefineNestedType (name, Parent.Module.DefaultCharSetType |
294                                 TypeAttributes.NestedPublic | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit, TypeManager.value_type);
295                         
296                         element = fixed_buffer_type.DefineField (FixedElementName, MemberType, FieldAttributes.Public);
297                         RootContext.RegisterCompilerGeneratedType (fixed_buffer_type);
298                         
299                         FieldBuilder = Parent.TypeBuilder.DefineField (Name, fixed_buffer_type, ModifiersExtensions.FieldAttr (ModFlags));
300                         Parent.MemberCache.AddMember (FieldBuilder, this);
301                         TypeManager.RegisterFieldBase (FieldBuilder, this);
302
303                         return true;
304                 }
305
306                 protected override void DoMemberTypeIndependentChecks ()
307                 {
308                         base.DoMemberTypeIndependentChecks ();
309
310                         if (!IsUnsafe)
311                                 Expression.UnsafeError (Report, Location);
312
313                         if (Parent.PartialContainer.Kind != Kind.Struct) {
314                                 Report.Error (1642, Location, "`{0}': Fixed size buffer fields may only be members of structs",
315                                         GetSignatureForError ());
316                         }
317                 }
318
319                 public override void Emit()
320                 {
321                         ResolveContext rc = new ResolveContext (this);
322                         Constant c = size_expr.ResolveAsConstant (rc, this);
323                         if (c == null)
324                                 return;
325                         
326                         IntConstant buffer_size_const = c.ImplicitConversionRequired (rc, TypeManager.int32_type, Location) as IntConstant;
327                         if (buffer_size_const == null)
328                                 return;
329
330                         int buffer_size = buffer_size_const.Value;
331
332                         if (buffer_size <= 0) {
333                                 Report.Error (1665, Location, "`{0}': Fixed size buffers must have a length greater than zero", GetSignatureForError ());
334                                 return;
335                         }
336
337                         int type_size = Expression.GetTypeSize (MemberType);
338
339                         if (buffer_size > int.MaxValue / type_size) {
340                                 Report.Error (1664, Location, "Fixed size buffer `{0}' of length `{1}' and type `{2}' exceeded 2^31 limit",
341                                         GetSignatureForError (), buffer_size.ToString (), TypeManager.CSharpName (MemberType));
342                                 return;
343                         }
344
345                         buffer_size *= type_size;
346                         EmitFieldSize (buffer_size);
347
348                         PredefinedAttributes.Get.UnsafeValueType.EmitAttribute (fixed_buffer_type);
349
350                         base.Emit ();
351                 }
352
353                 void EmitFieldSize (int buffer_size)
354                 {
355                         CustomAttributeBuilder cab;
356                         PredefinedAttribute pa;
357
358                         pa = PredefinedAttributes.Get.StructLayout;
359                         if (pa.Constructor == null &&
360                                 !pa.ResolveConstructor (Location, TypeManager.short_type))
361                                         return;
362
363                         // TODO: It's not cleared
364                         if (fi == null)
365                                 fi = new FieldInfo[] { pa.Type.GetField ("Size") };
366
367                         object[] fi_val = new object[] { buffer_size };
368                         cab = new CustomAttributeBuilder (pa.Constructor,
369                                 ctor_args, fi, fi_val);
370                         fixed_buffer_type.SetCustomAttribute (cab);
371                         
372                         //
373                         // Don't emit FixedBufferAttribute attribute for private types
374                         //
375                         if ((ModFlags & Modifiers.PRIVATE) != 0)
376                                 return;
377
378                         pa = PredefinedAttributes.Get.FixedBuffer;
379                         if (pa.Constructor == null &&
380                                 !pa.ResolveConstructor (Location, TypeManager.type_type, TypeManager.int32_type))
381                                 return;
382
383                         cab = new CustomAttributeBuilder (pa.Constructor, new object[] { MemberType, buffer_size });
384                         FieldBuilder.SetCustomAttribute (cab);
385                 }
386
387                 protected override bool IsFieldClsCompliant {
388                         get {
389                                 return false;
390                         }
391                 }
392
393                 public void SetCharSet (TypeAttributes ta)
394                 {
395                         TypeAttributes cta = fixed_buffer_type.Attributes;
396                         if ((cta & TypeAttributes.UnicodeClass) != (ta & TypeAttributes.UnicodeClass))
397                                 SetTypeBuilderCharSet ((cta & ~TypeAttributes.AutoClass) | TypeAttributes.UnicodeClass);
398                         else if ((cta & TypeAttributes.AutoClass) != (ta & TypeAttributes.AutoClass))
399                                 SetTypeBuilderCharSet ((cta & ~TypeAttributes.UnicodeClass) | TypeAttributes.AutoClass);
400                         else if (cta == 0 && ta != 0)
401                                 SetTypeBuilderCharSet (cta & ~(TypeAttributes.UnicodeClass | TypeAttributes.AutoClass));
402                 }
403
404                 void SetTypeBuilderCharSet (TypeAttributes ta)
405                 {
406                         MethodInfo mi = typeof (TypeBuilder).GetMethod ("SetCharSet", BindingFlags.Instance | BindingFlags.NonPublic);
407                         if (mi == null) {
408                                 Report.RuntimeMissingSupport (Location, "TypeBuilder::SetCharSet");
409                         } else {
410                                 mi.Invoke (fixed_buffer_type, new object [] { ta });
411                         }
412                 }
413
414                 #region IFixedField Members
415
416                 public FieldInfo Element {
417                         get {
418                                 return element;
419                         }
420                 }
421
422                 public Type ElementType {
423                         get {
424                                 return MemberType;
425                         }
426                 }
427
428                 #endregion
429         }
430
431         //
432         // The Field class is used to represents class/struct fields during parsing.
433         //
434         public class Field : FieldBase {
435                 // <summary>
436                 //   Modifiers allowed in a class declaration
437                 // </summary>
438                 const Modifiers AllowedModifiers =
439                         Modifiers.NEW |
440                         Modifiers.PUBLIC |
441                         Modifiers.PROTECTED |
442                         Modifiers.INTERNAL |
443                         Modifiers.PRIVATE |
444                         Modifiers.STATIC |
445                         Modifiers.VOLATILE |
446                         Modifiers.UNSAFE |
447                         Modifiers.READONLY;
448
449                 public Field (DeclSpace parent, FullNamedExpression type, Modifiers mod, MemberName name,
450                               Attributes attrs)
451                         : base (parent, type, mod, AllowedModifiers, name, attrs)
452                 {
453                 }
454
455                 bool CanBeVolatile ()
456                 {
457                         if (TypeManager.IsReferenceType (MemberType))
458                                 return true;
459
460                         if (MemberType == TypeManager.bool_type || MemberType == TypeManager.char_type ||
461                                 MemberType == TypeManager.sbyte_type || MemberType == TypeManager.byte_type ||
462                                 MemberType == TypeManager.short_type || MemberType == TypeManager.ushort_type ||
463                                 MemberType == TypeManager.int32_type || MemberType == TypeManager.uint32_type ||
464                                 MemberType == TypeManager.float_type ||
465                                 MemberType == TypeManager.intptr_type || MemberType == TypeManager.uintptr_type)
466                                 return true;
467
468                         if (TypeManager.IsEnumType (MemberType))
469                                 return true;
470
471                         return false;
472                 }
473
474                 bool CheckStructLayout (Type type, bool isStatic)
475                 {
476                         if (TypeManager.IsBuiltinType (type))
477                                 return true;
478
479                         if (isStatic) {
480                                 if (!TypeManager.IsValueType (type) || TypeManager.IsEqual (type, Parent.TypeBuilder))
481                                         return true;
482                         }
483
484                         if (!TypeManager.IsEqual (TypeManager.DropGenericTypeArguments (type), Parent.TypeBuilder)) {
485                                 if (!TypeManager.IsGenericType (type))
486                                         return true;
487
488                                 foreach (Type t in TypeManager.GetTypeArguments (type)) {
489                                         if (!CheckStructLayout (t, false))
490                                                 return false;
491                                 }
492                                 return true;
493                         }
494                         
495                         Report.Error (523, Location,
496                                 "Struct member `{0}' of type `{1}' causes a cycle in the struct layout",
497                                 GetSignatureForError (), TypeManager.CSharpName (MemberType));
498                         return false;
499                 }
500
501                 public override bool Define ()
502                 {
503                         if (!base.Define ())
504                                 return false;
505
506                         try {
507                                 Type[] required_modifier = null;
508                                 if ((ModFlags & Modifiers.VOLATILE) != 0) {
509                                         if (TypeManager.isvolatile_type == null)
510                                                 TypeManager.isvolatile_type = TypeManager.CoreLookupType (Compiler,
511                                                         "System.Runtime.CompilerServices", "IsVolatile", Kind.Class, true);
512
513                                         if (TypeManager.isvolatile_type != null)
514                                                 required_modifier = new Type [] { TypeManager.isvolatile_type };
515                                 }
516
517                                 FieldBuilder = Parent.TypeBuilder.DefineField (
518                                         Name, MemberType, required_modifier, null, ModifiersExtensions.FieldAttr (ModFlags));
519
520                                 // Don't cache inaccessible fields
521                                 if ((ModFlags & Modifiers.BACKING_FIELD) == 0) {
522                                         Parent.MemberCache.AddMember (FieldBuilder, this);
523                                 }
524
525                                 TypeManager.RegisterFieldBase (FieldBuilder, this);
526                         }
527                         catch (ArgumentException) {
528                                 Report.RuntimeMissingSupport (Location, "`void' or `void*' field type");
529                                 return false;
530                         }
531
532                         if (initializer != null) {
533                                 ((TypeContainer) Parent).RegisterFieldForInitialization (this,
534                                         new FieldInitializer (FieldBuilder, initializer, this));
535                         } else {
536                                 if (Parent.PartialContainer.Kind == Kind.Struct)
537                                         CheckStructLayout (member_type, (ModFlags & Modifiers.STATIC) != 0);
538                         }
539
540                         return true;
541                 }
542
543                 protected override void DoMemberTypeDependentChecks ()
544                 {
545                         base.DoMemberTypeDependentChecks ();
546
547                         if ((ModFlags & Modifiers.VOLATILE) != 0) {
548                                 if (!CanBeVolatile ()) {
549                                         Report.Error (677, Location, "`{0}': A volatile field cannot be of the type `{1}'",
550                                                 GetSignatureForError (), TypeManager.CSharpName (MemberType));
551                                 }
552
553                                 if ((ModFlags & Modifiers.READONLY) != 0) {
554                                         Report.Error (678, Location, "`{0}': A field cannot be both volatile and readonly",
555                                                 GetSignatureForError ());
556                                 }
557                         }
558                 }
559
560                 protected override bool VerifyClsCompliance ()
561                 {
562                         if (!base.VerifyClsCompliance ())
563                                 return false;
564
565                         if ((ModFlags & Modifiers.VOLATILE) != 0) {
566                                 Report.Warning (3026, 1, Location, "CLS-compliant field `{0}' cannot be volatile", GetSignatureForError ());
567                         }
568
569                         return true;
570                 }
571         }
572 }