2004-03-16 Martin Baulig <martin@ximian.com>
[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                 object ConstantValue = null;
32                 Type type;
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 (Expression constant_type, string name, Expression expr, int mod_flags,
44                               Attributes attrs, Location loc)
45                         : base (constant_type, mod_flags, AllowedModifiers, name, null, attrs, loc)
46                 {
47                         Expr = expr;
48                 }
49
50                 public FieldAttributes FieldAttr {
51                         get {
52                                 return FieldAttributes.Literal | FieldAttributes.Static |
53                                         Modifiers.FieldAttr (ModFlags) ;
54                         }
55                 }
56
57 #if DEBUG
58                 void dump_tree (Type t)
59                 {
60                         Console.WriteLine ("Dumping hierarchy");
61                         while (t != null){
62                                 Console.WriteLine ("   " + t.FullName + " " +
63                                         (t.GetType ().IsEnum ? "yes" : "no"));
64                                 t = t.BaseType;
65                         }
66                 }
67 #endif
68
69                 /// <summary>
70                 ///   Defines the constant in the @parent
71                 /// </summary>
72                 public override bool Define (TypeContainer parent)
73                 {
74                         type = parent.ResolveType (Type, false, Location);
75
76                         if (type == null)
77                                 return false;
78
79                         const_ec = new EmitContext (parent, Location, null, type, ModFlags);
80                         
81                         if (!TypeManager.IsBuiltinType (type) &&
82                             (!type.IsSubclassOf (TypeManager.enum_type))) {
83                                 Report.Error (
84                                         -3, Location,
85                                         "Constant type is not valid (only system types are allowed)");
86                                 return false;
87                         }
88
89                         Type ptype = parent.TypeBuilder.BaseType;
90
91                         if (ptype != null) {
92                                 MemberList list = TypeContainer.FindMembers (
93                                         ptype, MemberTypes.Field, BindingFlags.Public,
94                                         System.Type.FilterName, Name);
95                                 
96                                 if (list.Count == 0)
97                                         if ((ModFlags & Modifiers.NEW) != 0)
98                                                 WarningNotHiding (parent);
99
100                         } else if ((ModFlags & Modifiers.NEW) != 0)
101                                 WarningNotHiding (parent);
102
103                         FieldBuilder = parent.TypeBuilder.DefineField (Name, type, FieldAttr);
104
105                         TypeManager.RegisterConstant (FieldBuilder, this);
106
107                         return true;
108                 }
109
110                 //
111                 // Changes the type of the constant expression `expr' to the Type `type'
112                 // Returns null on failure.
113                 //
114                 public static Constant ChangeType (Location loc, Constant expr, Type type)
115                 {
116                         bool fail;
117
118                         // from the null type to any reference-type.
119                         if (expr is NullLiteral && !type.IsValueType && !TypeManager.IsEnumType (type))
120                                 return NullLiteral.Null;
121
122                         if (!Convert.ImplicitStandardConversionExists (expr, type)){
123                                 Convert.Error_CannotImplicitConversion (loc, expr.Type, type);
124                                 return null;
125                         }
126                         
127                         object constant_value = TypeManager.ChangeType (expr.GetValue (), type, out fail);
128                         if (fail){
129                                 Convert.Error_CannotImplicitConversion (loc, expr.Type, type);
130                                 
131                                 //
132                                 // We should always catch the error before this is ever
133                                 // reached, by calling Convert.ImplicitStandardConversionExists
134                                 //
135                                 throw new Exception (
136                                                      String.Format ("LookupConstantValue: This should never be reached {0} {1}", expr.Type, type));
137                         }
138
139                         Constant retval;
140                         if (type == TypeManager.int32_type)
141                                 retval = new IntConstant ((int) constant_value);
142                         else if (type == TypeManager.uint32_type)
143                                 retval = new UIntConstant ((uint) constant_value);
144                         else if (type == TypeManager.int64_type)
145                                 retval = new LongConstant ((long) constant_value);
146                         else if (type == TypeManager.uint64_type)
147                                 retval = new ULongConstant ((ulong) constant_value);
148                         else if (type == TypeManager.float_type)
149                                 retval = new FloatConstant ((float) constant_value);
150                         else if (type == TypeManager.double_type)
151                                 retval = new DoubleConstant ((double) constant_value);
152                         else if (type == TypeManager.string_type)
153                                 retval = new StringConstant ((string) constant_value);
154                         else if (type == TypeManager.short_type)
155                                 retval = new ShortConstant ((short) constant_value);
156                         else if (type == TypeManager.ushort_type)
157                                 retval = new UShortConstant ((ushort) constant_value);
158                         else if (type == TypeManager.sbyte_type)
159                                 retval = new SByteConstant ((sbyte) constant_value);
160                         else if (type == TypeManager.byte_type)
161                                 retval = new ByteConstant ((byte) constant_value);
162                         else if (type == TypeManager.char_type)
163                                 retval = new CharConstant ((char) constant_value);
164                         else if (type == TypeManager.bool_type)
165                                 retval = new BoolConstant ((bool) constant_value);
166                         else
167                                 throw new Exception ("LookupConstantValue: Unhandled constant type: " + type);
168                         
169                         return retval;
170                 }
171                 
172                 /// <summary>
173                 ///  Looks up the value of a constant field. Defines it if it hasn't
174                 ///  already been. Similar to LookupEnumValue in spirit.
175                 /// </summary>
176                 public object LookupConstantValue ()
177                 {
178                         if (ConstantValue != null)
179                                 return ConstantValue;
180
181                         if (in_transit) {
182                                 Report.Error (110, Location,
183                                               "The evaluation of the constant value for `" +
184                                               Name + "' involves a circular definition.");
185                                 return null;
186                         }
187
188                         in_transit = true;
189                         int errors = Report.Errors;
190
191                         //
192                         // We might have cleared Expr ourselves in a recursive definition
193                         //
194                         if (Expr == null)
195                                 return null;
196                         
197                         Expr = Expr.Resolve (const_ec);
198
199                         in_transit = false;
200
201                         if (Expr == null) {
202                                 if (errors == Report.Errors)
203                                         Report.Error (150, Location, "A constant value is expected");
204                                 return null;
205                         }
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
212                                 if ((un_expr != null) && (un_expr.Expr is Constant))
213                                         Expr = un_expr.Expr;
214                                 else if ((ch_expr != null) && (ch_expr.Expr is Constant))
215                                         Expr = ch_expr.Expr;
216                                 else {
217                                         if (errors == Report.Errors)
218                                                 Report.Error (150, Location, "A constant value is expected");
219                                         return null;
220                                 }
221                         }
222
223                         if (type != ce.Type) {
224                                 ce = ChangeType (Location, ce, type);
225                                 if (ce == null)
226                                         return null;
227                                 Expr = ce;
228                         }
229                         ConstantValue = ce.GetValue ();
230
231                         if (type.IsEnum){
232                                 //
233                                 // This sadly does not work for our user-defined enumerations types ;-(
234                                 //
235                                 try {
236                                         ConstantValue = System.Enum.ToObject (
237                                                 type, ConstantValue);
238                                 } catch (ArgumentException){
239                                         Report.Error (
240                                                 -16, Location,
241                                                 ".NET SDK 1.0 does not permit to create the constant "+
242                                                 " field from a user-defined enumeration");
243                                 }
244                         }
245
246                         FieldBuilder.SetConstant (ConstantValue);
247
248                         if (!TypeManager.RegisterFieldValue (FieldBuilder, ConstantValue))
249                                 return null;
250
251                         return ConstantValue;
252                 }
253                 
254                 
255                 /// <summary>
256                 ///  Emits the field value by evaluating the expression
257                 /// </summary>
258                 public void Emit (TypeContainer parent)
259                 {
260                         LookupConstantValue ();
261                         
262                         return;
263                 }
264         }
265 }
266
267