2010-01-14 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                         if (RootContext.Version < LanguageVersion.ISO_2)
295                                 Report.FeatureIsNotAvailable (loc, "fixed size buffers");
296
297                         initializer = new ConstInitializer (this, size_expr);
298                 }
299
300                 public override Constant ConvertInitializer (ResolveContext rc, Constant expr)
301                 {
302                         return expr.ImplicitConversionRequired (rc, TypeManager.int32_type, Location);
303                 }
304
305                 public override bool Define ()
306                 {
307                         if (!base.Define ())
308                                 return false;
309
310                         if (!TypeManager.IsPrimitiveType (MemberType)) {
311                                 Report.Error (1663, Location,
312                                         "`{0}': Fixed size buffers type must be one of the following: bool, byte, short, int, long, char, sbyte, ushort, uint, ulong, float or double",
313                                         GetSignatureForError ());
314                         }                       
315                         
316                         // Create nested fixed buffer container
317                         string name = String.Format ("<{0}>__FixedBuffer{1}", Name, GlobalCounter++);
318                         fixed_buffer_type = Parent.TypeBuilder.DefineNestedType (name, Parent.Module.DefaultCharSetType |
319                                 TypeAttributes.NestedPublic | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit, TypeManager.value_type);
320                         
321                         var element = fixed_buffer_type.DefineField (FixedElementName, MemberType, FieldAttributes.Public);
322                         RootContext.RegisterCompilerGeneratedType (fixed_buffer_type);
323                         
324                         FieldBuilder = Parent.TypeBuilder.DefineField (Name, fixed_buffer_type, ModifiersExtensions.FieldAttr (ModFlags));
325                         spec = new FixedFieldSpec (this, FieldBuilder, element, ModFlags);
326
327                         Parent.MemberCache.AddMember (FieldBuilder, this);
328                         TypeManager.RegisterFieldBase (FieldBuilder, this);
329
330                         return true;
331                 }
332
333                 protected override void DoMemberTypeIndependentChecks ()
334                 {
335                         base.DoMemberTypeIndependentChecks ();
336
337                         if (!IsUnsafe)
338                                 Expression.UnsafeError (Report, Location);
339
340                         if (Parent.PartialContainer.Kind != Kind.Struct) {
341                                 Report.Error (1642, Location, "`{0}': Fixed size buffer fields may only be members of structs",
342                                         GetSignatureForError ());
343                         }
344                 }
345
346                 public override void Emit()
347                 {
348                         ResolveContext rc = new ResolveContext (this);
349                         IntConstant buffer_size_const = initializer.Resolve (rc) as IntConstant;
350                         if (buffer_size_const == null)
351                                 return;
352
353                         int buffer_size = buffer_size_const.Value;
354
355                         if (buffer_size <= 0) {
356                                 Report.Error (1665, Location, "`{0}': Fixed size buffers must have a length greater than zero", GetSignatureForError ());
357                                 return;
358                         }
359
360                         int type_size = Expression.GetTypeSize (MemberType);
361
362                         if (buffer_size > int.MaxValue / type_size) {
363                                 Report.Error (1664, Location, "Fixed size buffer `{0}' of length `{1}' and type `{2}' exceeded 2^31 limit",
364                                         GetSignatureForError (), buffer_size.ToString (), TypeManager.CSharpName (MemberType));
365                                 return;
366                         }
367
368                         buffer_size *= type_size;
369                         EmitFieldSize (buffer_size);
370
371                         PredefinedAttributes.Get.UnsafeValueType.EmitAttribute (fixed_buffer_type);
372
373                         base.Emit ();
374                 }
375
376                 void EmitFieldSize (int buffer_size)
377                 {
378                         CustomAttributeBuilder cab;
379                         PredefinedAttribute pa;
380
381                         pa = PredefinedAttributes.Get.StructLayout;
382                         if (pa.Constructor == null &&
383                                 !pa.ResolveConstructor (Location, TypeManager.short_type))
384                                         return;
385
386                         // TODO: It's not cleared
387                         if (fi == null)
388                                 fi = new FieldInfo[] { pa.Type.GetField ("Size") };
389
390                         object[] fi_val = new object[] { buffer_size };
391                         cab = new CustomAttributeBuilder (pa.Constructor,
392                                 ctor_args, fi, fi_val);
393                         fixed_buffer_type.SetCustomAttribute (cab);
394                         
395                         //
396                         // Don't emit FixedBufferAttribute attribute for private types
397                         //
398                         if ((ModFlags & Modifiers.PRIVATE) != 0)
399                                 return;
400
401                         pa = PredefinedAttributes.Get.FixedBuffer;
402                         if (pa.Constructor == null &&
403                                 !pa.ResolveConstructor (Location, TypeManager.type_type, TypeManager.int32_type))
404                                 return;
405
406                         cab = new CustomAttributeBuilder (pa.Constructor, new object[] { MemberType, buffer_size });
407                         FieldBuilder.SetCustomAttribute (cab);
408                 }
409
410                 protected override bool IsFieldClsCompliant {
411                         get {
412                                 return false;
413                         }
414                 }
415
416                 public void SetCharSet (TypeAttributes ta)
417                 {
418                         TypeAttributes cta = fixed_buffer_type.Attributes;
419                         if ((cta & TypeAttributes.UnicodeClass) != (ta & TypeAttributes.UnicodeClass))
420                                 SetTypeBuilderCharSet ((cta & ~TypeAttributes.AutoClass) | TypeAttributes.UnicodeClass);
421                         else if ((cta & TypeAttributes.AutoClass) != (ta & TypeAttributes.AutoClass))
422                                 SetTypeBuilderCharSet ((cta & ~TypeAttributes.UnicodeClass) | TypeAttributes.AutoClass);
423                         else if (cta == 0 && ta != 0)
424                                 SetTypeBuilderCharSet (cta & ~(TypeAttributes.UnicodeClass | TypeAttributes.AutoClass));
425                 }
426
427                 void SetTypeBuilderCharSet (TypeAttributes ta)
428                 {
429                         MethodInfo mi = typeof (TypeBuilder).GetMethod ("SetCharSet", BindingFlags.Instance | BindingFlags.NonPublic);
430                         if (mi == null) {
431                                 Report.RuntimeMissingSupport (Location, "TypeBuilder::SetCharSet");
432                         } else {
433                                 mi.Invoke (fixed_buffer_type, new object [] { ta });
434                         }
435                 }
436         }
437
438         class FixedFieldSpec : FieldSpec
439         {
440                 readonly FieldInfo element;
441
442                 public FixedFieldSpec (IMemberDefinition definition, FieldInfo info, FieldInfo element, Modifiers modifiers)
443                          : base (definition, info, modifiers)
444                 {
445                         this.element = element;
446                 }
447
448                 public FieldInfo Element {
449                         get {
450                                 return element;
451                         }
452                 }
453
454                 public Type ElementType {
455                         get {
456                                 return element.FieldType;
457                         }
458                 }
459         }
460
461         //
462         // The Field class is used to represents class/struct fields during parsing.
463         //
464         public class Field : FieldBase {
465                 // <summary>
466                 //   Modifiers allowed in a class declaration
467                 // </summary>
468                 const Modifiers AllowedModifiers =
469                         Modifiers.NEW |
470                         Modifiers.PUBLIC |
471                         Modifiers.PROTECTED |
472                         Modifiers.INTERNAL |
473                         Modifiers.PRIVATE |
474                         Modifiers.STATIC |
475                         Modifiers.VOLATILE |
476                         Modifiers.UNSAFE |
477                         Modifiers.READONLY;
478
479                 public Field (DeclSpace parent, FullNamedExpression type, Modifiers mod, MemberName name,
480                               Attributes attrs)
481                         : base (parent, type, mod, AllowedModifiers, name, attrs)
482                 {
483                 }
484
485                 bool CanBeVolatile ()
486                 {
487                         if (TypeManager.IsReferenceType (MemberType))
488                                 return true;
489
490                         if (MemberType == TypeManager.bool_type || MemberType == TypeManager.char_type ||
491                                 MemberType == TypeManager.sbyte_type || MemberType == TypeManager.byte_type ||
492                                 MemberType == TypeManager.short_type || MemberType == TypeManager.ushort_type ||
493                                 MemberType == TypeManager.int32_type || MemberType == TypeManager.uint32_type ||
494                                 MemberType == TypeManager.float_type ||
495                                 MemberType == TypeManager.intptr_type || MemberType == TypeManager.uintptr_type)
496                                 return true;
497
498                         if (TypeManager.IsEnumType (MemberType))
499                                 return true;
500
501                         return false;
502                 }
503
504                 bool CheckStructLayout (Type type, bool isStatic)
505                 {
506                         if (TypeManager.IsBuiltinType (type))
507                                 return true;
508
509                         if (isStatic) {
510                                 if (!TypeManager.IsValueType (type) || TypeManager.IsEqual (type, Parent.TypeBuilder))
511                                         return true;
512                         }
513
514                         if (!TypeManager.IsEqual (TypeManager.DropGenericTypeArguments (type), Parent.TypeBuilder)) {
515                                 if (!TypeManager.IsGenericType (type))
516                                         return true;
517
518                                 foreach (Type t in TypeManager.GetTypeArguments (type)) {
519                                         if (!CheckStructLayout (t, false))
520                                                 return false;
521                                 }
522                                 return true;
523                         }
524                         
525                         Report.Error (523, Location,
526                                 "Struct member `{0}' of type `{1}' causes a cycle in the struct layout",
527                                 GetSignatureForError (), TypeManager.CSharpName (MemberType));
528                         return false;
529                 }
530
531                 public override bool Define ()
532                 {
533                         if (!base.Define ())
534                                 return false;
535
536                         try {
537                                 Type[] required_modifier = null;
538                                 if ((ModFlags & Modifiers.VOLATILE) != 0) {
539                                         if (TypeManager.isvolatile_type == null)
540                                                 TypeManager.isvolatile_type = TypeManager.CoreLookupType (Compiler,
541                                                         "System.Runtime.CompilerServices", "IsVolatile", Kind.Class, true);
542
543                                         if (TypeManager.isvolatile_type != null)
544                                                 required_modifier = new Type [] { TypeManager.isvolatile_type };
545                                 }
546
547                                 FieldBuilder = Parent.TypeBuilder.DefineField (
548                                         Name, MemberType, required_modifier, null, ModifiersExtensions.FieldAttr (ModFlags));
549
550                                 spec = new FieldSpec (this, FieldBuilder, ModFlags);
551
552                                 // Don't cache inaccessible fields
553                                 if ((ModFlags & Modifiers.BACKING_FIELD) == 0) {
554                                         Parent.MemberCache.AddMember (FieldBuilder, this);
555                                 }
556
557                                 TypeManager.RegisterFieldBase (FieldBuilder, this);
558                         }
559                         catch (ArgumentException) {
560                                 Report.RuntimeMissingSupport (Location, "`void' or `void*' field type");
561                                 return false;
562                         }
563
564                         if (initializer != null) {
565                                 ((TypeContainer) Parent).RegisterFieldForInitialization (this,
566                                         new FieldInitializer (this, initializer, this));
567                         } else {
568                                 if (Parent.PartialContainer.Kind == Kind.Struct)
569                                         CheckStructLayout (member_type, (ModFlags & Modifiers.STATIC) != 0);
570                         }
571
572                         return true;
573                 }
574
575                 protected override void DoMemberTypeDependentChecks ()
576                 {
577                         base.DoMemberTypeDependentChecks ();
578
579                         if ((ModFlags & Modifiers.VOLATILE) != 0) {
580                                 if (!CanBeVolatile ()) {
581                                         Report.Error (677, Location, "`{0}': A volatile field cannot be of the type `{1}'",
582                                                 GetSignatureForError (), TypeManager.CSharpName (MemberType));
583                                 }
584
585                                 if ((ModFlags & Modifiers.READONLY) != 0) {
586                                         Report.Error (678, Location, "`{0}': A field cannot be both volatile and readonly",
587                                                 GetSignatureForError ());
588                                 }
589                         }
590                 }
591
592                 protected override bool VerifyClsCompliance ()
593                 {
594                         if (!base.VerifyClsCompliance ())
595                                 return false;
596
597                         if ((ModFlags & Modifiers.VOLATILE) != 0) {
598                                 Report.Warning (3026, 1, Location, "CLS-compliant field `{0}' cannot be volatile", GetSignatureForError ());
599                         }
600
601                         return true;
602                 }
603         }
604 }