5c513a00fb3e0664a00e8bb13c57d55a5b3241a1
[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 //
13 // This is needed because the following situation arises:
14 //
15 //     The FieldBuilder is declared with the real type for an enumeration
16 //
17 //     When we attempt to set the value for the constant, the FieldBuilder.SetConstant
18 //     function aborts because it requires its argument to be of the same type
19 //
20
21 namespace Mono.CSharp {
22
23         using System;
24         using System.Reflection;
25         using System.Reflection.Emit;
26         using System.Collections;
27
28         public class Const : FieldMember {
29                 public Expression Expr;
30                 EmitContext const_ec;
31
32                 bool resolved = false;
33                 object ConstantValue = null;
34
35                 bool in_transit = false;
36
37                 public const int AllowedModifiers =
38                         Modifiers.NEW |
39                         Modifiers.PUBLIC |
40                         Modifiers.PROTECTED |
41                         Modifiers.INTERNAL |
42                         Modifiers.PRIVATE;
43
44                 public Const (TypeContainer parent, Expression constant_type, string name,
45                               Expression expr, int mod_flags, Attributes attrs, Location loc)
46                         : base (parent, constant_type, mod_flags, AllowedModifiers,
47                                 new MemberName (name), expr, attrs, loc)
48                 {
49                         Expr = expr;
50                         ModFlags |= Modifiers.STATIC;
51                 }
52
53 #if DEBUG
54                 void dump_tree (Type t)
55                 {
56                         Console.WriteLine ("Dumping hierarchy");
57                         while (t != null){
58                                 Console.WriteLine ("   " + t.FullName + " " +
59                                         (t.GetType ().IsEnum ? "yes" : "no"));
60                                 t = t.BaseType;
61                         }
62                 }
63 #endif
64
65                 protected override bool CheckBase ()
66                 {
67                         // Constant.Define can be called when the parent type hasn't yet been populated
68                         // and it's base types need not have been populated.  So, we defer this check
69                         // to the second time Define () is called on this member.
70                         if (Parent.BaseCache == null)
71                                 return true;
72                         return base.CheckBase ();
73                 }
74
75                 /// <summary>
76                 ///   Defines the constant in the @parent
77                 /// </summary>
78                 public override bool Define ()
79                 {
80                         // Make Define () idempotent, but ensure that the error check happens.
81                         if (FieldBuilder != null)
82                                 return base.CheckBase ();
83
84                         if (!base.Define ())
85                                 return false;
86
87                         const_ec = new EmitContext (Parent, Location, null, MemberType, ModFlags);
88
89                         Type ttype = MemberType;
90                         while (ttype.IsArray)
91                             ttype = TypeManager.GetElementType (ttype);
92                         
93                         if (!TypeManager.IsBuiltinType(ttype) && (!ttype.IsSubclassOf(TypeManager.enum_type)) && !(Expr is NullLiteral)){
94                                 Report.Error (
95                                         -3, Location,
96                                         "Constant type is not valid (only system types are allowed)");
97                                 return false;
98                         }
99
100                         FieldAttributes field_attr = FieldAttributes.Static | Modifiers.FieldAttr (ModFlags);
101                         // Decimals cannot be emitted into the constant blob.  So, convert to 'readonly'.
102                         if (ttype == TypeManager.decimal_type) {
103                                 field_attr |= FieldAttributes.InitOnly;
104                                 Parent.RegisterFieldForInitialization (this);
105                         }
106                         else {
107                                 field_attr |= FieldAttributes.Literal;
108                         }
109
110                         FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType, field_attr);
111
112                         TypeManager.RegisterConstant (FieldBuilder, this);
113
114                         return true;
115                 }
116
117                 //
118                 // Changes the type of the constant expression `expr' to the Type `type'
119                 // Returns null on failure.
120                 //
121                 public static Constant ChangeType (Location loc, Constant expr, Type type)
122                 {
123                         if (type == TypeManager.object_type)
124                                 return expr;
125
126                         bool fail;
127
128                         // from the null type to any reference-type.
129                         if (expr.Type == TypeManager.null_type && !type.IsValueType && !TypeManager.IsEnumType (type))
130                                 return NullLiteral.Null;
131
132                         if (!Convert.ImplicitStandardConversionExists (Convert.ConstantEC, expr, type)){
133                                 Convert.Error_CannotImplicitConversion (loc, expr.Type, type);
134                                 return null;
135                         }
136
137                         // Special-case: The 0 literal can be converted to an enum value,
138                         // and ImplicitStandardConversionExists will return true in that case.
139                         if (expr.Type == TypeManager.int32_type && TypeManager.IsEnumType (type)){
140                                 if (expr is IntLiteral && ((IntLiteral) expr).Value == 0)
141                                         return new EnumConstant (expr, type);
142                         }
143                         
144                         object constant_value = TypeManager.ChangeType (expr.GetValue (), type, out fail);
145                         if (fail){
146                                 Convert.Error_CannotImplicitConversion (loc, expr.Type, type);
147                                 
148                                 //
149                                 // We should always catch the error before this is ever
150                                 // reached, by calling Convert.ImplicitStandardConversionExists
151                                 //
152                                 throw new Exception (
153                                                      String.Format ("LookupConstantValue: This should never be reached {0} {1}", expr.Type, type));
154                         }
155
156                         Constant retval;
157                         if (type == TypeManager.int32_type)
158                                 retval = new IntConstant ((int) constant_value);
159                         else if (type == TypeManager.uint32_type)
160                                 retval = new UIntConstant ((uint) constant_value);
161                         else if (type == TypeManager.int64_type)
162                                 retval = new LongConstant ((long) constant_value);
163                         else if (type == TypeManager.uint64_type)
164                                 retval = new ULongConstant ((ulong) constant_value);
165                         else if (type == TypeManager.float_type)
166                                 retval = new FloatConstant ((float) constant_value);
167                         else if (type == TypeManager.double_type)
168                                 retval = new DoubleConstant ((double) constant_value);
169                         else if (type == TypeManager.string_type)
170                                 retval = new StringConstant ((string) constant_value);
171                         else if (type == TypeManager.short_type)
172                                 retval = new ShortConstant ((short) constant_value);
173                         else if (type == TypeManager.ushort_type)
174                                 retval = new UShortConstant ((ushort) constant_value);
175                         else if (type == TypeManager.sbyte_type)
176                                 retval = new SByteConstant ((sbyte) constant_value);
177                         else if (type == TypeManager.byte_type)
178                                 retval = new ByteConstant ((byte) constant_value);
179                         else if (type == TypeManager.char_type)
180                                 retval = new CharConstant ((char) constant_value);
181                         else if (type == TypeManager.bool_type)
182                                 retval = new BoolConstant ((bool) constant_value);
183                         else if (type == TypeManager.decimal_type)
184                                 retval = new DecimalConstant ((decimal) constant_value);
185                         else
186                                 throw new Exception ("LookupConstantValue: Unhandled constant type: " + type);
187                         
188                         return retval;
189                 }
190                 
191                 /// <summary>
192                 ///  Looks up the value of a constant field. Defines it if it hasn't
193                 ///  already been. Similar to LookupEnumValue in spirit.
194                 /// </summary>
195                 public bool LookupConstantValue (out object value)
196                 {
197                         if (resolved) {
198                                 value = ConstantValue;
199                                 return true;
200                         }
201
202                         if (in_transit) {
203                                 Report.Error (110, Location,
204                                               "The evaluation of the constant value for `" +
205                                               Name + "' involves a circular definition.");
206                                 value = null;
207                                 return false;
208                         }
209
210                         in_transit = true;
211                         int errors = Report.Errors;
212
213                         //
214                         // We might have cleared Expr ourselves in a recursive definition
215                         //
216                         if (Expr == null){
217                                 value = null;
218                                 return false;
219                         }
220
221                         Expr = Expr.Resolve (const_ec);
222
223                         in_transit = false;
224
225                         if (Expr == null) {
226                                 if (errors == Report.Errors)
227                                         Report.Error (150, Location, "A constant value is expected");
228                                 value = null;
229                                 return false;
230                         }
231
232                         Expression real_expr = Expr;
233
234                         Constant ce = Expr as Constant;
235                         if (ce == null){
236                                 UnCheckedExpr un_expr = Expr as UnCheckedExpr;
237                                 CheckedExpr ch_expr = Expr as CheckedExpr;
238                                 EmptyCast ec_expr = Expr as EmptyCast;
239
240                                 if ((un_expr != null) && (un_expr.Expr is Constant))
241                                         Expr = un_expr.Expr;
242                                 else if ((ch_expr != null) && (ch_expr.Expr is Constant))
243                                         Expr = ch_expr.Expr;
244                                 else if ((ec_expr != null) && (ec_expr.Child is Constant))
245                                         Expr = ec_expr.Child;
246                                 else if (Expr is ArrayCreation){
247                                         Report.Error (133, Location, "Arrays can not be constant");
248                                 } else {
249                                         if (errors == Report.Errors)
250                                                 Report.Error (150, Location, "A constant value is expected");
251                                         value = null;
252                                         return false;
253                                 }
254
255                                 ce = Expr as Constant;
256                         }
257
258                         if (MemberType != real_expr.Type) {
259                                 ce = ChangeType (Location, ce, MemberType);
260                                 if (ce == null){
261                                         value = null;
262                                         return false;
263                                 }
264                                 Expr = ce;
265                         }
266
267                         if (ce != null)
268                                 ConstantValue = ce.GetValue ();
269
270                         if (MemberType.IsEnum){
271                                 //
272                                 // This sadly does not work for our user-defined enumerations types ;-(
273                                 //
274                                 try {
275                                         ConstantValue = System.Enum.ToObject (
276                                                 MemberType, ConstantValue);
277                                 } catch (ArgumentException){
278                                         Report.Error (
279                                                 -16, Location,
280                                                 ".NET SDK 1.0 does not permit to create the constant "+
281                                                 " field from a user-defined enumeration");
282                                 }
283                         }
284
285                         if (ce is DecimalConstant) {
286                                 Decimal d = ((DecimalConstant)ce).Value;
287                                 int[] bits = Decimal.GetBits (d);
288                                 object[] args = new object[] { (byte)(bits [3] >> 16), (byte)(bits [3] >> 31), (uint)bits [2], (uint)bits [1], (uint)bits [0] };
289                                 CustomAttributeBuilder cab = new CustomAttributeBuilder (TypeManager.decimal_constant_attribute_ctor, args);
290                                 FieldBuilder.SetCustomAttribute (cab);
291                         }
292                         else{
293                                 FieldBuilder.SetConstant (ConstantValue);
294                         }
295
296                         if (!TypeManager.RegisterFieldValue (FieldBuilder, ConstantValue))
297                                 throw new Exception ("Cannot register const value");
298
299                         value = ConstantValue;
300                         resolved = true;
301                         return true;
302                 }
303                 
304                 
305                 /// <summary>
306                 ///  Emits the field value by evaluating the expression
307                 /// </summary>
308                 public override void Emit ()
309                 {
310                         object value;
311                         LookupConstantValue (out value);
312                         base.Emit ();
313                 }
314         }
315 }
316
317