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