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