New test.
[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 Value { get; }
24         }
25
26         public class Const : FieldMember, IConstant {
27                 Constant value;
28                 bool in_transit;
29                 bool define_called;
30
31                 public const int AllowedModifiers =
32                         Modifiers.NEW |
33                         Modifiers.PUBLIC |
34                         Modifiers.PROTECTED |
35                         Modifiers.INTERNAL |
36                         Modifiers.PRIVATE;
37
38                 public Const (DeclSpace parent, Expression constant_type, string name,
39                               Expression expr, int mod_flags, Attributes attrs, Location loc)
40                         : base (parent, constant_type, mod_flags, AllowedModifiers,
41                                 new MemberName (name, loc), attrs)
42                 {
43                         initializer = expr;
44                         ModFlags |= Modifiers.STATIC;
45                 }
46
47                 protected override bool CheckBase ()
48                 {
49                         // Constant.Define can be called when the parent type hasn't yet been populated
50                         // and it's base types need not have been populated.  So, we defer this check
51                         // to the second time Define () is called on this member.
52                         if (Parent.PartialContainer.BaseCache == null)
53                                 return true;
54                         return base.CheckBase ();
55                 }
56
57                 /// <summary>
58                 ///   Defines the constant in the @parent
59                 /// </summary>
60                 public override bool Define ()
61                 {
62                         // Because constant define can be called from other class
63                         if (define_called) {
64                                 CheckBase ();
65                                 return FieldBuilder != null;
66                         }
67
68                         define_called = true;
69
70                         if (!base.Define ())
71                                 return false;
72
73                         Type ttype = MemberType;
74                         if (!IsConstantTypeValid (ttype)) {
75                                 Error_InvalidConstantType (ttype, Location);
76                                 return false;
77                         }
78
79                         while (ttype.IsArray)
80                             ttype = TypeManager.GetElementType (ttype);
81
82                         FieldAttributes field_attr = FieldAttributes.Static | Modifiers.FieldAttr (ModFlags);
83                         // Decimals cannot be emitted into the constant blob.  So, convert to 'readonly'.
84                         if (ttype == TypeManager.decimal_type) {
85                                 field_attr |= FieldAttributes.InitOnly;
86                                 Parent.PartialContainer.RegisterFieldForInitialization (this);
87                         } else {
88                                 field_attr |= FieldAttributes.Literal;
89                         }
90
91                         FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType, field_attr);
92
93                         TypeManager.RegisterConstant (FieldBuilder, this);
94
95                         return true;
96                 }
97
98                 public static bool IsConstantTypeValid (Type t)
99                 {
100                         if (TypeManager.IsBuiltinOrEnum (t))
101                                 return true;
102
103                         if (t.IsPointer || t.IsValueType)
104                                 return false;
105                         
106                         if (TypeManager.IsGenericParameter (t))
107                                 return false;
108
109                         return true;
110                 }
111
112                 /// <summary>
113                 ///  Emits the field value by evaluating the expression
114                 /// </summary>
115                 public override void Emit ()
116                 {
117                         if (!ResolveValue ())
118                                 return;
119
120                         if (value.Type == TypeManager.decimal_type) {
121                                 Decimal d = ((DecimalConstant)value).Value;
122                                 int[] bits = Decimal.GetBits (d);
123                                 object[] args = new object[] { (byte)(bits [3] >> 16), (byte)(bits [3] >> 31), (uint)bits [2], (uint)bits [1], (uint)bits [0] };
124                                 CustomAttributeBuilder cab = new CustomAttributeBuilder (TypeManager.decimal_constant_attribute_ctor, args);
125                                 FieldBuilder.SetCustomAttribute (cab);
126                         }
127                         else{
128                                 FieldBuilder.SetConstant (value.GetTypedValue ());
129                         }
130
131                         base.Emit ();
132                 }
133
134                 public static void Error_ExpressionMustBeConstant (Location loc, string e_name)
135                 {
136                         Report.Error (133, loc, "The expression being assigned to `{0}' must be constant", e_name);
137                 }
138
139                 public static void Error_CyclicDeclaration (MemberCore mc)
140                 {
141                         Report.Error (110, mc.Location, "The evaluation of the constant value for `{0}' involves a circular definition",
142                                 mc.GetSignatureForError ());
143                 }
144
145                 public static void Error_ConstantCanBeInitializedWithNullOnly (Location loc, string name)
146                 {
147                         Report.Error (134, loc, "`{0}': the constant of reference type other than string can only be initialized with null",
148                                 name);
149                 }
150
151                 public static void Error_InvalidConstantType (Type t, Location loc)
152                 {
153                         Report.Error (283, loc, "The type `{0}' cannot be declared const", TypeManager.CSharpName (t));
154                 }
155
156                 #region IConstant Members
157
158                 public bool ResolveValue ()
159                 {
160                         if (value != null)
161                                 return true;
162
163                         SetMemberIsUsed ();
164                         if (in_transit) {
165                                 Error_CyclicDeclaration (this);
166                                 // Suppress cyclic errors
167                                 value = New.Constantify (MemberType);
168                                 return false;
169                         }
170
171                         in_transit = true;
172                         // TODO: IResolveContext here
173                         EmitContext ec = new EmitContext (this, Parent, Location, null, MemberType, ModFlags);
174                         value = initializer.ResolveAsConstant (ec, this);
175                         in_transit = false;
176
177                         if (value == null)
178                                 return false;
179
180                         value = value.ImplicitConversionRequired (MemberType, Location);
181                         if (value == null)
182                                 return false;
183
184                         if (!MemberType.IsValueType && MemberType != TypeManager.string_type && !value.IsDefaultValue) {
185                                 Error_ConstantCanBeInitializedWithNullOnly (Location, GetSignatureForError ());
186                                 return false;
187                         }
188
189                         return true;
190                 }
191
192                 public Constant Value {
193                         get {
194                                 return value;
195                         }
196                 }
197
198                 #endregion
199         }
200
201         public class ExternalConstant : IConstant
202         {
203                 FieldInfo fi;
204                 Constant value;
205
206                 public ExternalConstant (FieldInfo fi)
207                 {
208                         this.fi = fi;
209                 }
210
211                 private ExternalConstant (FieldInfo fi, Constant value):
212                         this (fi)
213                 {
214                         this.value = value;
215                 }
216
217                 //
218                 // Decimal constants cannot be encoded in the constant blob, and thus are marked
219                 // as IsInitOnly ('readonly' in C# parlance).  We get its value from the 
220                 // DecimalConstantAttribute metadata.
221                 //
222                 public static IConstant CreateDecimal (FieldInfo fi)
223                 {
224                         if (fi is FieldBuilder)
225                                 return null;
226                         
227                         object[] attrs = fi.GetCustomAttributes (TypeManager.decimal_constant_attribute_type, false);
228                         if (attrs.Length != 1)
229                                 return null;
230
231                         IConstant ic = new ExternalConstant (fi,
232                                 new DecimalConstant (((System.Runtime.CompilerServices.DecimalConstantAttribute) attrs [0]).Value, Location.Null));
233
234                         return ic;
235                 }
236
237                 #region IConstant Members
238
239                 public void CheckObsoleteness (Location loc)
240                 {
241                         ObsoleteAttribute oa = AttributeTester.GetMemberObsoleteAttribute (fi);
242                         if (oa == null) {
243                                 return;
244                         }
245
246                         AttributeTester.Report_ObsoleteMessage (oa, TypeManager.GetFullNameSignature (fi), loc);
247                 }
248
249                 public bool ResolveValue ()
250                 {
251                         if (value != null)
252                                 return true;
253
254                         if (fi.DeclaringType.IsEnum) {
255                                 value = Expression.Constantify (fi.GetValue (fi), TypeManager.EnumToUnderlying (fi.FieldType));
256                                 value = new EnumConstant (value, fi.DeclaringType);
257                                 return true;
258                         }
259
260                         value = Expression.Constantify (fi.GetValue (fi), fi.FieldType);
261                         return true;
262                 }
263
264                 public Constant Value {
265                         get {
266                                 return value;
267                         }
268                 }
269
270                 #endregion
271         }
272
273 }