2009-11-24 Jb Evain <jbevain@novell.com>
[mcs.git] / mcs / const.cs
blobdbf4b4273c4544439ed58f38b9fa181bfaff2c30
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.
12 namespace Mono.CSharp {
14 using System;
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Collections;
19 public interface IConstant
21 void CheckObsoleteness (Location loc);
22 bool ResolveValue ();
23 Constant CreateConstantReference (ResolveContext rc, Location loc);
26 public class Const : FieldBase, IConstant {
27 protected Constant value;
28 bool in_transit;
29 bool resolved;
30 bool define_called;
32 public const int AllowedModifiers =
33 Modifiers.NEW |
34 Modifiers.PUBLIC |
35 Modifiers.PROTECTED |
36 Modifiers.INTERNAL |
37 Modifiers.PRIVATE;
39 public Const (DeclSpace parent, FullNamedExpression type, string name,
40 Expression expr, int mod_flags, Attributes attrs, Location loc)
41 : base (parent, type, mod_flags, AllowedModifiers,
42 new MemberName (name, loc), attrs)
44 initializer = expr;
45 ModFlags |= Modifiers.STATIC;
48 protected override bool CheckBase ()
50 // Constant.Define can be called when the parent type hasn't yet been populated
51 // and it's base types need not have been populated. So, we defer this check
52 // to the second time Define () is called on this member.
53 if (Parent.PartialContainer.BaseCache == null)
54 return true;
55 return base.CheckBase ();
58 /// <summary>
59 /// Defines the constant in the @parent
60 /// </summary>
61 public override bool Define ()
63 // Because constant define can be called from other class
64 if (define_called) {
65 CheckBase ();
66 return FieldBuilder != null;
69 define_called = true;
71 if (!base.Define ())
72 return false;
74 Type ttype = MemberType;
75 if (!IsConstantTypeValid (ttype)) {
76 Error_InvalidConstantType (ttype, Location, Report);
79 // If the constant is private then we don't need any field the
80 // value is already inlined and cannot be referenced
81 //if ((ModFlags & Modifiers.PRIVATE) != 0 && RootContext.Optimize)
82 // return true;
84 FieldAttributes field_attr = FieldAttributes.Static | Modifiers.FieldAttr (ModFlags);
85 // Decimals cannot be emitted into the constant blob. So, convert to 'readonly'.
86 if (ttype == TypeManager.decimal_type) {
87 field_attr |= FieldAttributes.InitOnly;
88 } else {
89 field_attr |= FieldAttributes.Literal;
92 FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType, field_attr);
93 TypeManager.RegisterConstant (FieldBuilder, this);
94 Parent.MemberCache.AddMember (FieldBuilder, this);
96 if ((field_attr & FieldAttributes.InitOnly) != 0)
97 Parent.PartialContainer.RegisterFieldForInitialization (this,
98 new FieldInitializer (FieldBuilder, initializer, this));
100 return true;
103 public static bool IsConstantTypeValid (Type t)
105 if (TypeManager.IsBuiltinOrEnum (t))
106 return true;
108 if (TypeManager.IsGenericParameter (t) || t.IsPointer)
109 return false;
111 return TypeManager.IsReferenceType (t);
114 /// <summary>
115 /// Emits the field value by evaluating the expression
116 /// </summary>
117 public override void Emit ()
119 if (!ResolveValue ())
120 return;
122 if (FieldBuilder == null)
123 return;
125 if (value.Type == TypeManager.decimal_type) {
126 FieldBuilder.SetCustomAttribute (CreateDecimalConstantAttribute (value));
127 } else{
128 FieldBuilder.SetConstant (value.GetTypedValue ());
131 base.Emit ();
134 public static CustomAttributeBuilder CreateDecimalConstantAttribute (Constant c)
136 PredefinedAttribute pa = PredefinedAttributes.Get.DecimalConstant;
137 if (pa.Constructor == null &&
138 !pa.ResolveConstructor (c.Location, TypeManager.byte_type, TypeManager.byte_type,
139 TypeManager.uint32_type, TypeManager.uint32_type, TypeManager.uint32_type))
140 return null;
142 Decimal d = (Decimal) c.GetValue ();
143 int [] bits = Decimal.GetBits (d);
144 object [] args = new object [] {
145 (byte) (bits [3] >> 16),
146 (byte) (bits [3] >> 31),
147 (uint) bits [2], (uint) bits [1], (uint) bits [0]
150 return new CustomAttributeBuilder (pa.Constructor, args);
153 public static void Error_ExpressionMustBeConstant (Location loc, string e_name, Report Report)
155 Report.Error (133, loc, "The expression being assigned to `{0}' must be constant", e_name);
158 public static void Error_CyclicDeclaration (MemberCore mc, Report Report)
160 Report.Error (110, mc.Location, "The evaluation of the constant value for `{0}' involves a circular definition",
161 mc.GetSignatureForError ());
164 public static void Error_ConstantCanBeInitializedWithNullOnly (Type type, Location loc, string name, Report Report)
166 Report.Error (134, loc, "A constant `{0}' of reference type `{1}' can only be initialized with null",
167 name, TypeManager.CSharpName (type));
170 public static void Error_InvalidConstantType (Type t, Location loc, Report Report)
172 if (TypeManager.IsGenericParameter (t)) {
173 Report.Error (1959, loc,
174 "Type parameter `{0}' cannot be declared const", TypeManager.CSharpName (t));
175 } else {
176 Report.Error (283, loc,
177 "The type `{0}' cannot be declared const", TypeManager.CSharpName (t));
181 #region IConstant Members
183 public bool ResolveValue ()
185 if (resolved)
186 return value != null;
188 ResolveContext.Options opt = ResolveContext.Options.ConstantScope;
189 if (this is EnumMember)
190 opt |= ResolveContext.Options.EnumScope;
191 ResolveContext rc = new ResolveContext (this, opt);
193 SetMemberIsUsed ();
194 if (in_transit) {
195 Error_CyclicDeclaration (this, Report);
196 // Suppress cyclic errors
197 value = New.Constantify (MemberType).Resolve (rc);
198 resolved = true;
199 return false;
202 in_transit = true;
204 value = DoResolveValue (rc);
206 in_transit = false;
207 resolved = true;
208 return value != null;
211 protected virtual Constant DoResolveValue (ResolveContext ec)
213 Constant value = initializer.ResolveAsConstant (ec, this);
214 if (value == null)
215 return null;
217 Constant c = value.ConvertImplicitly (ec, MemberType);
218 if (c == null) {
219 if (TypeManager.IsReferenceType (MemberType))
220 Error_ConstantCanBeInitializedWithNullOnly (MemberType, Location, GetSignatureForError (), Report);
221 else
222 value.Error_ValueCannotBeConverted (ec, Location, MemberType, false);
225 return c;
228 public virtual Constant CreateConstantReference (ResolveContext rc, Location loc)
230 if (value == null)
231 return null;
233 return Constant.CreateConstant (rc, value.Type, value.GetValue(), loc);
236 #endregion
239 public class ExternalConstant : IConstant
241 FieldInfo fi;
242 object value;
243 CompilerContext ctx;
245 public ExternalConstant (FieldInfo fi, CompilerContext ctx)
247 this.fi = fi;
248 this.ctx = ctx;
251 private ExternalConstant (FieldInfo fi, object value, CompilerContext ctx)
252 : this (fi, ctx)
254 this.value = value;
258 // Decimal constants cannot be encoded in the constant blob, and thus are marked
259 // as IsInitOnly ('readonly' in C# parlance). We get its value from the
260 // DecimalConstantAttribute metadata.
262 public static IConstant CreateDecimal (FieldInfo fi, ResolveContext rc)
264 if (fi is FieldBuilder)
265 return null;
267 PredefinedAttribute pa = PredefinedAttributes.Get.DecimalConstant;
268 if (!pa.IsDefined)
269 return null;
271 object[] attrs = fi.GetCustomAttributes (pa.Type, false);
272 if (attrs.Length != 1)
273 return null;
275 IConstant ic = new ExternalConstant (fi,
276 ((System.Runtime.CompilerServices.DecimalConstantAttribute) attrs [0]).Value,
277 rc.Compiler);
279 return ic;
282 #region IConstant Members
284 public void CheckObsoleteness (Location loc)
286 ObsoleteAttribute oa = AttributeTester.GetMemberObsoleteAttribute (fi);
287 if (oa == null) {
288 return;
291 AttributeTester.Report_ObsoleteMessage (oa, TypeManager.GetFullNameSignature (fi), loc, ctx.Report);
294 public bool ResolveValue ()
296 if (value != null)
297 return true;
299 value = fi.GetValue (fi);
300 return true;
303 public Constant CreateConstantReference (ResolveContext rc, Location loc)
305 return Constant.CreateConstant (rc, TypeManager.TypeToCoreType (fi.FieldType), value, loc);
308 #endregion