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