**** Merged from MCS ****
[mono.git] / mcs / gmcs / 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 : FieldBase {
28                 public Expression Expr;
29                 EmitContext const_ec;
30
31                 bool resolved = false;
32                 object ConstantValue = null;
33                 Type type;
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 (Expression constant_type, string name, Expression expr,
45                               int mod_flags, Attributes attrs, Location loc)
46                         : base (constant_type, mod_flags, AllowedModifiers,
47                                 new MemberName (name), null, attrs, loc)
48                 {
49                         Expr = expr;
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                 public override void ApplyAttributeBuilder (object builder, Attribute a, CustomAttributeBuilder cb)
72                 {
73                         throw new NotImplementedException ("Can't apply attributes to constants ???");
74                 }
75
76                 /// <summary>
77                 ///   Defines the constant in the @parent
78                 /// </summary>
79                 public override bool Define (TypeContainer parent)
80                 {
81                         type = parent.ResolveType (Type, false, Location);
82
83                         if (type == null)
84                                 return false;
85
86                         const_ec = new EmitContext (parent, Location, null, type, ModFlags);
87                         
88                         Type ttype = type;
89                         while (ttype.IsArray)
90                             ttype = TypeManager.GetElementType (ttype);
91                         
92                         if (!TypeManager.IsBuiltinType (ttype) &&
93                             (!ttype.IsSubclassOf (TypeManager.enum_type))) {
94                                 Report.Error (
95                                         -3, Location,
96                                         "Constant type is not valid (only system types are allowed)");
97                                 return false;
98                         }
99
100                         Type ptype = parent.TypeBuilder.BaseType;
101
102                         if (ptype != null) {
103                                 MemberList list = TypeContainer.FindMembers (
104                                         ptype, MemberTypes.Field, BindingFlags.Public,
105                                         System.Type.FilterName, Name);
106                                 
107                                 if (list.Count == 0)
108                                         if ((ModFlags & Modifiers.NEW) != 0)
109                                                 WarningNotHiding (parent);
110
111                         } else if ((ModFlags & Modifiers.NEW) != 0)
112                                 WarningNotHiding (parent);
113
114                         FieldBuilder = parent.TypeBuilder.DefineField (Name, type, FieldAttr);
115
116                         TypeManager.RegisterConstant (FieldBuilder, this);
117
118                         return true;
119                 }
120
121                 //
122                 // Changes the type of the constant expression `expr' to the Type `type'
123                 // Returns null on failure.
124                 //
125                 public static Constant ChangeType (Location loc, Constant expr, Type type)
126                 {
127                         bool fail;
128
129                         // from the null type to any reference-type.
130                         if (expr is NullLiteral && !type.IsValueType && !TypeManager.IsEnumType (type))
131                                 return NullLiteral.Null;
132
133                         if (!Convert.ImplicitStandardConversionExists (expr, type)){
134                                 Convert.Error_CannotImplicitConversion (loc, expr.Type, type);
135                                 return null;
136                         }
137                         
138                         object constant_value = TypeManager.ChangeType (expr.GetValue (), type, out fail);
139                         if (fail){
140                                 Convert.Error_CannotImplicitConversion (loc, expr.Type, type);
141                                 
142                                 //
143                                 // We should always catch the error before this is ever
144                                 // reached, by calling Convert.ImplicitStandardConversionExists
145                                 //
146                                 throw new Exception (
147                                                      String.Format ("LookupConstantValue: This should never be reached {0} {1}", expr.Type, type));
148                         }
149
150                         Constant retval;
151                         if (type == TypeManager.int32_type)
152                                 retval = new IntConstant ((int) constant_value);
153                         else if (type == TypeManager.uint32_type)
154                                 retval = new UIntConstant ((uint) constant_value);
155                         else if (type == TypeManager.int64_type)
156                                 retval = new LongConstant ((long) constant_value);
157                         else if (type == TypeManager.uint64_type)
158                                 retval = new ULongConstant ((ulong) constant_value);
159                         else if (type == TypeManager.float_type)
160                                 retval = new FloatConstant ((float) constant_value);
161                         else if (type == TypeManager.double_type)
162                                 retval = new DoubleConstant ((double) constant_value);
163                         else if (type == TypeManager.string_type)
164                                 retval = new StringConstant ((string) constant_value);
165                         else if (type == TypeManager.short_type)
166                                 retval = new ShortConstant ((short) constant_value);
167                         else if (type == TypeManager.ushort_type)
168                                 retval = new UShortConstant ((ushort) constant_value);
169                         else if (type == TypeManager.sbyte_type)
170                                 retval = new SByteConstant ((sbyte) constant_value);
171                         else if (type == TypeManager.byte_type)
172                                 retval = new ByteConstant ((byte) constant_value);
173                         else if (type == TypeManager.char_type)
174                                 retval = new CharConstant ((char) constant_value);
175                         else if (type == TypeManager.bool_type)
176                                 retval = new BoolConstant ((bool) constant_value);
177                         else
178                                 throw new Exception ("LookupConstantValue: Unhandled constant type: " + type);
179                         
180                         return retval;
181                 }
182                 
183                 /// <summary>
184                 ///  Looks up the value of a constant field. Defines it if it hasn't
185                 ///  already been. Similar to LookupEnumValue in spirit.
186                 /// </summary>
187                 public bool LookupConstantValue (out object value)
188                 {
189                         if (resolved) {
190                                 value = ConstantValue;
191                                 return true;
192                         }
193
194                         if (in_transit) {
195                                 Report.Error (110, Location,
196                                               "The evaluation of the constant value for `" +
197                                               Name + "' involves a circular definition.");
198                                 value = null;
199                                 return false;
200                         }
201
202                         in_transit = true;
203                         int errors = Report.Errors;
204
205                         //
206                         // We might have cleared Expr ourselves in a recursive definition
207                         //
208                         if (Expr == null){
209                                 value = null;
210                                 return false;
211                         }
212                         
213                         Expr = Expr.Resolve (const_ec);
214
215                         in_transit = false;
216
217                         if (Expr == null) {
218                                 if (errors == Report.Errors)
219                                         Report.Error (150, Location, "A constant value is expected");
220                                 value = null;
221                                 return false;
222                         }
223
224                         Constant ce = Expr as Constant;
225                         if (ce == null){
226                                 UnCheckedExpr un_expr = Expr as UnCheckedExpr;
227                                 CheckedExpr ch_expr = Expr as CheckedExpr;
228
229                                 if ((un_expr != null) && (un_expr.Expr is Constant))
230                                         Expr = un_expr.Expr;
231                                 else if ((ch_expr != null) && (ch_expr.Expr is Constant))
232                                         Expr = ch_expr.Expr;
233                                 else if (Expr is ArrayCreation) {
234                                         ArrayCreation ac = (ArrayCreation) Expr;
235
236                                         Expr = ac.TurnIntoConstant ();
237                                         if (Expr == null){
238                                                 Report.Error (150, Location, "A constant value is expected");
239                                                 value = null;
240                                                 return false;
241                                         }
242                                 } else {
243                                         if (errors == Report.Errors)
244                                                 Report.Error (150, Location, "A constant value is expected");
245                                         value = null;
246                                         return false;
247                                 }
248                         }
249
250                         if (type != ce.Type) {
251                                 ce = ChangeType (Location, ce, type);
252                                 if (ce == null){
253                                         value = null;
254                                         return false;
255                                 }
256                                 Expr = ce;
257                         }
258                         ConstantValue = ce.GetValue ();
259
260                         if (type.IsEnum){
261                                 //
262                                 // This sadly does not work for our user-defined enumerations types ;-(
263                                 //
264                                 try {
265                                         ConstantValue = System.Enum.ToObject (
266                                                 type, ConstantValue);
267                                 } catch (ArgumentException){
268                                         Report.Error (
269                                                 -16, Location,
270                                                 ".NET SDK 1.0 does not permit to create the constant "+
271                                                 " field from a user-defined enumeration");
272                                 }
273                         }
274
275                         FieldBuilder.SetConstant (ConstantValue);
276
277                         if (!TypeManager.RegisterFieldValue (FieldBuilder, ConstantValue))
278                                 throw new Exception ("Cannot register const value");
279
280                         value = ConstantValue;
281                         resolved = true;
282                         return true;
283                 }
284                 
285                 
286                 /// <summary>
287                 ///  Emits the field value by evaluating the expression
288                 /// </summary>
289                 public override void Emit (TypeContainer parent)
290                 {
291                         object value;
292                         LookupConstantValue (out value);
293                         base.Emit (parent);
294                 }
295         }
296 }
297
298