New test + update.
[mono.git] / mcs / mcs / const.cs
1 //
2 // const.cs: Constant declarations.
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Marek Safar (marek.safar@seznam.cz)
7 //
8 // (C) 2001 Ximian, Inc.
9 //
10 //
11
12 namespace Mono.CSharp {
13
14         using System;
15         using System.Reflection;
16         using System.Reflection.Emit;
17         using System.Collections;
18
19         public interface IConstant
20         {
21                 void CheckObsoleteness (Location loc);
22                 bool ResolveValue ();
23                 Constant CreateConstantReference (Location loc);
24         }
25
26         public class Const : FieldBase, IConstant {
27                 protected Constant value;
28                 bool in_transit;
29                 bool resolved;
30                 bool define_called;
31
32                 public const int AllowedModifiers =
33                         Modifiers.NEW |
34                         Modifiers.PUBLIC |
35                         Modifiers.PROTECTED |
36                         Modifiers.INTERNAL |
37                         Modifiers.PRIVATE;
38
39                 public Const (DeclSpace parent, Expression constant_type, string name,
40                               Expression expr, int mod_flags, Attributes attrs, Location loc)
41                         : base (parent, constant_type, mod_flags, AllowedModifiers,
42                                 new MemberName (name, loc), attrs)
43                 {
44                         initializer = expr;
45                         ModFlags |= Modifiers.STATIC;
46                 }
47
48                 protected override bool CheckBase ()
49                 {
50                         // Constant.Define can be called when the parent type hasn't yet been populated
51                         // and it's base types need not have been populated.  So, we defer this check
52                         // to the second time Define () is called on this member.
53                         if (Parent.PartialContainer.BaseCache == null)
54                                 return true;
55                         return base.CheckBase ();
56                 }
57
58                 /// <summary>
59                 ///   Defines the constant in the @parent
60                 /// </summary>
61                 public override bool Define ()
62                 {
63                         // Because constant define can be called from other class
64                         if (define_called) {
65                                 CheckBase ();
66                                 return FieldBuilder != null;
67                         }
68
69                         define_called = true;
70
71                         if (!base.Define ())
72                                 return false;
73
74                         Type ttype = MemberType;
75                         if (!IsConstantTypeValid (ttype)) {
76                                 Error_InvalidConstantType (ttype, Location);
77                                 return false;
78                         }
79
80                         // If the constant is private then we don't need any field the
81                         // value is already inlined and cannot be referenced
82                         //if ((ModFlags & Modifiers.PRIVATE) != 0 && RootContext.Optimize)
83                         //      return true;
84
85                         while (ttype.IsArray)
86                                 ttype = TypeManager.GetElementType (ttype);
87
88                         FieldAttributes field_attr = FieldAttributes.Static | Modifiers.FieldAttr (ModFlags);
89                         // Decimals cannot be emitted into the constant blob.  So, convert to 'readonly'.
90                         if (ttype == TypeManager.decimal_type) {
91                                 field_attr |= FieldAttributes.InitOnly;
92                         } else {
93                                 field_attr |= FieldAttributes.Literal;
94                         }
95
96                         FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType, field_attr);
97                         TypeManager.RegisterConstant (FieldBuilder, this);
98                         Parent.MemberCache.AddMember (FieldBuilder, this);
99
100                         if (ttype == TypeManager.decimal_type)
101                                 Parent.PartialContainer.RegisterFieldForInitialization (this,
102                                         new FieldInitializer (FieldBuilder, initializer));
103
104                         return true;
105                 }
106
107                 public static bool IsConstantTypeValid (Type t)
108                 {
109                         if (TypeManager.IsBuiltinOrEnum (t))
110                                 return true;
111
112                         if (t.IsPointer || t.IsValueType)
113                                 return false;
114                         
115                         if (TypeManager.IsGenericParameter (t))
116                                 return false;
117
118                         return true;
119                 }
120
121                 /// <summary>
122                 ///  Emits the field value by evaluating the expression
123                 /// </summary>
124                 public override void Emit ()
125                 {
126                         if (!ResolveValue ())
127                                 return;
128
129                         if (FieldBuilder == null)
130                                 return;
131
132                         if (value.Type == TypeManager.decimal_type) {
133                                 EmitDecimalConstant ();
134                         } else{
135                                 FieldBuilder.SetConstant (value.GetTypedValue ());
136                         }
137
138                         base.Emit ();
139                 }
140
141                 void EmitDecimalConstant ()
142                 {
143                         ConstructorInfo ctor = TypeManager.decimal_constant_attribute_ctor;
144                         if (ctor == null) {
145                                 if (TypeManager.decimal_constant_attribute_type == null) {
146                                         TypeManager.decimal_constant_attribute_type = TypeManager.CoreLookupType (
147                                                 "System.Runtime.CompilerServices", "DecimalConstantAttribute", Kind.Class, true);
148
149                                         if (TypeManager.decimal_constant_attribute_type == null)
150                                                 return;
151                                 }
152
153                                 ctor = TypeManager.GetPredefinedConstructor (TypeManager.decimal_constant_attribute_type, Location,
154                                         TypeManager.byte_type, TypeManager.byte_type,
155                                         TypeManager.uint32_type, TypeManager.uint32_type, TypeManager.uint32_type);
156
157                                 if (ctor == null)
158                                         return;
159
160                                 TypeManager.decimal_constant_attribute_ctor = ctor;
161                         }
162
163                         Decimal d = ((DecimalConstant) value).Value;
164                         int [] bits = Decimal.GetBits (d);
165                         object [] args = new object [] { 
166                                 (byte) (bits [3] >> 16),
167                                 (byte) (bits [3] >> 31),
168                                 (uint) bits [2], (uint) bits [1], (uint) bits [0]
169                         };
170
171                         CustomAttributeBuilder cab = new CustomAttributeBuilder (ctor, args);
172                         FieldBuilder.SetCustomAttribute (cab);
173                 }
174
175                 public static void Error_ExpressionMustBeConstant (Location loc, string e_name)
176                 {
177                         Report.Error (133, loc, "The expression being assigned to `{0}' must be constant", e_name);
178                 }
179
180                 public static void Error_CyclicDeclaration (MemberCore mc)
181                 {
182                         Report.Error (110, mc.Location, "The evaluation of the constant value for `{0}' involves a circular definition",
183                                 mc.GetSignatureForError ());
184                 }
185
186                 public static void Error_ConstantCanBeInitializedWithNullOnly (Location loc, string name)
187                 {
188                         Report.Error (134, loc, "`{0}': the constant of reference type other than string can only be initialized with null",
189                                 name);
190                 }
191
192                 public static void Error_InvalidConstantType (Type t, Location loc)
193                 {
194                         Report.Error (283, loc, "The type `{0}' cannot be declared const", TypeManager.CSharpName (t));
195                 }
196
197                 #region IConstant Members
198
199                 public bool ResolveValue ()
200                 {
201                         if (resolved)
202                                 return value != null;
203
204                         SetMemberIsUsed ();
205                         if (in_transit) {
206                                 Error_CyclicDeclaration (this);
207                                 // Suppress cyclic errors
208                                 value = New.Constantify (MemberType);
209                                 resolved = true;
210                                 return false;
211                         }
212
213                         in_transit = true;
214                         // TODO: IResolveContext here
215                         EmitContext ec = new EmitContext (
216                                 this, Parent, Location, null, MemberType, ModFlags);
217                         ec.InEnumContext = this is EnumMember;
218                         ec.IsAnonymousMethodAllowed = false;
219                         value = DoResolveValue (ec);
220                         in_transit = false;
221                         resolved = true;
222                         return value != null;
223                 }
224
225                 protected virtual Constant DoResolveValue (EmitContext ec)
226                 {
227                         Constant value = initializer.ResolveAsConstant (ec, this);
228                         if (value == null)
229                                 return null;
230
231                         Constant c = value.ConvertImplicitly (MemberType);
232                         if (c == null) {
233                                 if (!MemberType.IsValueType && MemberType != TypeManager.string_type && !value.IsDefaultValue)
234                                         Error_ConstantCanBeInitializedWithNullOnly (Location, GetSignatureForError ());
235                                 else
236                                         value.Error_ValueCannotBeConverted (null, Location, MemberType, false);
237                         }
238
239                         return c;
240                 }
241
242                 public virtual Constant CreateConstantReference (Location loc)
243                 {
244                         if (value == null)
245                                 return null;
246
247                         return Constant.CreateConstant (value.Type, value.GetValue(), loc);
248                 }
249
250                 #endregion
251         }
252
253         public class ExternalConstant : IConstant
254         {
255                 FieldInfo fi;
256                 object value;
257
258                 public ExternalConstant (FieldInfo fi)
259                 {
260                         this.fi = fi;
261                 }
262
263                 private ExternalConstant (FieldInfo fi, object value):
264                         this (fi)
265                 {
266                         this.value = value;
267                 }
268
269                 //
270                 // Decimal constants cannot be encoded in the constant blob, and thus are marked
271                 // as IsInitOnly ('readonly' in C# parlance).  We get its value from the 
272                 // DecimalConstantAttribute metadata.
273                 //
274                 public static IConstant CreateDecimal (FieldInfo fi)
275                 {
276                         if (fi is FieldBuilder)
277                                 return null;
278
279                         if (TypeManager.decimal_constant_attribute_type == null)
280                                 return null;
281
282                         object[] attrs = fi.GetCustomAttributes (TypeManager.decimal_constant_attribute_type, false);
283                         if (attrs.Length != 1)
284                                 return null;
285
286                         IConstant ic = new ExternalConstant (fi,
287                                 ((System.Runtime.CompilerServices.DecimalConstantAttribute) attrs [0]).Value);
288
289                         return ic;
290                 }
291
292                 #region IConstant Members
293
294                 public void CheckObsoleteness (Location loc)
295                 {
296                         ObsoleteAttribute oa = AttributeTester.GetMemberObsoleteAttribute (fi);
297                         if (oa == null) {
298                                 return;
299                         }
300
301                         AttributeTester.Report_ObsoleteMessage (oa, TypeManager.GetFullNameSignature (fi), loc);
302                 }
303
304                 public bool ResolveValue ()
305                 {
306                         if (value != null)
307                                 return true;
308
309                         value = fi.GetValue (fi);
310                         return true;
311                 }
312
313                 public Constant CreateConstantReference (Location loc)
314                 {
315                         return Constant.CreateConstant (fi.FieldType, value, loc);
316                 }
317
318                 #endregion
319         }
320
321 }