2010-03-12 Jb Evain <jbevain@novell.com>
[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 // Copyright 2001-2003 Ximian, Inc.
9 // Copyright 2003-2008 Novell, Inc.
10 //
11
12 using System;
13 using System.Reflection;
14 using System.Reflection.Emit;
15
16 namespace Mono.CSharp {
17
18         public class Const : FieldBase
19         {
20                 bool define_called;
21
22                 public const Modifiers AllowedModifiers =
23                         Modifiers.NEW |
24                         Modifiers.PUBLIC |
25                         Modifiers.PROTECTED |
26                         Modifiers.INTERNAL |
27                         Modifiers.PRIVATE;
28
29                 public Const (DeclSpace parent, FullNamedExpression type, string name,
30                               Expression expr, Modifiers mod_flags, Attributes attrs, Location loc)
31                         : base (parent, type, mod_flags, AllowedModifiers,
32                                 new MemberName (name, loc), attrs)
33                 {
34                         if (expr != null)
35                                 initializer = new ConstInitializer (this, expr);
36
37                         ModFlags |= Modifiers.STATIC;
38                 }
39
40                 protected override bool CheckBase ()
41                 {
42                         // Constant.Define can be called when the parent type hasn't yet been populated
43                         // and it's base types need not have been populated.  So, we defer this check
44                         // to the second time Define () is called on this member.
45                         if (Parent.PartialContainer.BaseCache == null)
46                                 return true;
47                         return base.CheckBase ();
48                 }
49
50                 /// <summary>
51                 ///   Defines the constant in the @parent
52                 /// </summary>
53                 public override bool Define ()
54                 {
55                         // Because constant define can be called from other class
56                         if (define_called) {
57                                 CheckBase ();
58                                 return FieldBuilder != null;
59                         }
60
61                         define_called = true;
62
63                         if (!base.Define ())
64                                 return false;
65
66                         Type ttype = MemberType;
67                         if (!IsConstantTypeValid (ttype)) {
68                                 Error_InvalidConstantType (ttype, Location, Report);
69                         }
70
71                         FieldAttributes field_attr = FieldAttributes.Static | ModifiersExtensions.FieldAttr (ModFlags);
72                         // Decimals cannot be emitted into the constant blob.  So, convert to 'readonly'.
73                         if (ttype == TypeManager.decimal_type) {
74                                 field_attr |= FieldAttributes.InitOnly;
75                         } else {
76                                 field_attr |= FieldAttributes.Literal;
77                         }
78
79                         FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType, field_attr);
80                         spec = new ConstSpec (this, FieldBuilder, ModFlags, initializer);
81
82                         TypeManager.RegisterConstant (FieldBuilder, (ConstSpec) spec);
83                         Parent.MemberCache.AddMember (FieldBuilder, spec);
84
85                         if ((field_attr & FieldAttributes.InitOnly) != 0)
86                                 Parent.PartialContainer.RegisterFieldForInitialization (this,
87                                         new FieldInitializer (this, initializer, this));
88
89                         return true;
90                 }
91
92                 public static bool IsConstantTypeValid (Type t)
93                 {
94                         if (TypeManager.IsBuiltinOrEnum (t))
95                                 return true;
96
97                         if (TypeManager.IsGenericParameter (t) || t.IsPointer)
98                                 return false;
99
100                         return TypeManager.IsReferenceType (t);
101                 }
102
103                 /// <summary>
104                 ///  Emits the field value by evaluating the expression
105                 /// </summary>
106                 public override void Emit ()
107                 {
108                         var value = initializer.Resolve (new ResolveContext (this)) as Constant;
109                         if (value == null || FieldBuilder == null)
110                                 return;
111
112                         if (value.Type == TypeManager.decimal_type) {
113                                 FieldBuilder.SetCustomAttribute (CreateDecimalConstantAttribute (value));
114                         } else{
115                                 FieldBuilder.SetConstant (value.GetTypedValue ());
116                         }
117
118                         base.Emit ();
119                 }
120
121                 public static CustomAttributeBuilder CreateDecimalConstantAttribute (Constant c)
122                 {
123                         PredefinedAttribute pa = PredefinedAttributes.Get.DecimalConstant;
124                         if (pa.Constructor == null &&
125                                 !pa.ResolveConstructor (c.Location, TypeManager.byte_type, TypeManager.byte_type,
126                                         TypeManager.uint32_type, TypeManager.uint32_type, TypeManager.uint32_type))
127                                 return null;
128
129                         Decimal d = (Decimal) c.GetValue ();
130                         int [] bits = Decimal.GetBits (d);
131                         object [] args = new object [] { 
132                                 (byte) (bits [3] >> 16),
133                                 (byte) (bits [3] >> 31),
134                                 (uint) bits [2], (uint) bits [1], (uint) bits [0]
135                         };
136
137                         return new CustomAttributeBuilder (pa.Constructor, args);
138                 }
139
140                 public static void Error_InvalidConstantType (Type t, Location loc, Report Report)
141                 {
142                         if (TypeManager.IsGenericParameter (t)) {
143                                 Report.Error (1959, loc,
144                                         "Type parameter `{0}' cannot be declared const", TypeManager.CSharpName (t));
145                         } else {
146                                 Report.Error (283, loc,
147                                         "The type `{0}' cannot be declared const", TypeManager.CSharpName (t));
148                         }
149                 }
150         }
151
152         public class ConstSpec : FieldSpec
153         {
154                 Expression value;
155
156                 public ConstSpec (IMemberDefinition definition, FieldInfo fi, Modifiers mod, Expression value)
157                         : base (definition, fi, mod)
158                 {
159                         this.value = value;
160                 }
161
162                 public Expression Value {
163                         get {
164                                 return value;
165                         }
166                         set {
167                                 this.value = value;
168                         }
169                 }
170         }
171
172         class ConstInitializer : ShimExpression
173         {
174                 bool in_transit;
175                 protected readonly FieldBase field;
176
177                 public ConstInitializer (FieldBase field, Expression value)
178                         : base (value)
179                 {
180                         if (value != null)
181                                 this.loc = value.Location;
182
183                         this.field = field;
184                 }
185
186                 protected override Expression DoResolve (ResolveContext unused)
187                 {
188                         if (type != null)
189                                 return expr;
190
191                         var opt = ResolveContext.Options.ConstantScope;
192                         if (field is EnumMember)
193                                 opt |= ResolveContext.Options.EnumScope;
194
195                         //
196                         // Use a context in which the constant was declared and
197                         // not the one in which is referenced
198                         //
199                         var rc = new ResolveContext (field, opt);
200                         expr = DoResolveInitializer (rc);
201                         type = expr.Type;
202
203                         return expr;
204                 }
205
206                 protected virtual Expression DoResolveInitializer (ResolveContext rc)
207                 {
208                         if (in_transit) {
209                                 field.Compiler.Report.Error (110, field.Location,
210                                         "The evaluation of the constant value for `{0}' involves a circular definition",
211                                         field.GetSignatureForError ());
212
213                                 expr = null;
214                         } else {
215                                 in_transit = true;
216                                 expr = expr.Resolve (rc);
217                         }
218
219                         in_transit = false;
220
221                         if (expr != null) {
222                                 Constant c = expr as Constant;
223                                 if (c != null)
224                                         c = field.ConvertInitializer (rc, c);
225
226                                 if (c == null) {
227                                         if (TypeManager.IsReferenceType (field.MemberType))
228                                                 Error_ConstantCanBeInitializedWithNullOnly (rc, field.MemberType, loc, field.GetSignatureForError ());
229                                         else if (!(expr is Constant))
230                                                 Error_ExpressionMustBeConstant (rc, field.Location, field.GetSignatureForError ());
231                                         else
232                                                 expr.Error_ValueCannotBeConverted (rc, loc, field.MemberType, false);
233                                 }
234
235                                 expr = c;
236                         }
237
238                         if (expr == null) {
239                                 expr = New.Constantify (field.MemberType);
240                                 if (expr == null)
241                                         expr = Constant.CreateConstantFromValue (field.MemberType, null, Location);
242                                 expr = expr.Resolve (rc);
243                         }
244
245
246                         return expr;
247                 }
248         }
249 }