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