2010-06-15 Marek Safar <marek.safar@gmail.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                 Constant value;
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                 /// <summary>
41                 ///   Defines the constant in the @parent
42                 /// </summary>
43                 public override bool Define ()
44                 {
45                         if (!base.Define ())
46                                 return false;
47
48                         TypeSpec ttype = MemberType;
49                         if (!ttype.IsConstantCompatible) {
50                                 Error_InvalidConstantType (ttype, Location, Report);
51                         }
52
53                         FieldAttributes field_attr = FieldAttributes.Static | ModifiersExtensions.FieldAttr (ModFlags);
54                         // Decimals cannot be emitted into the constant blob.  So, convert to 'readonly'.
55                         if (ttype == TypeManager.decimal_type) {
56                                 field_attr |= FieldAttributes.InitOnly;
57                         } else {
58                                 field_attr |= FieldAttributes.Literal;
59                         }
60
61                         FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType.GetMetaInfo (), field_attr);
62                         spec = new ConstSpec (Parent.Definition, this, MemberType, FieldBuilder, ModFlags, initializer);
63
64                         Parent.MemberCache.AddMember (spec);
65
66                         if ((field_attr & FieldAttributes.InitOnly) != 0)
67                                 Parent.PartialContainer.RegisterFieldForInitialization (this,
68                                         new FieldInitializer (this, initializer, this));
69
70                         return true;
71                 }
72
73                 public Constant DefineValue ()
74                 {
75                         if (value == null)
76                                 value = initializer.Resolve (new ResolveContext (this)) as Constant;
77
78                         return value;
79                 }
80
81                 /// <summary>
82                 ///  Emits the field value by evaluating the expression
83                 /// </summary>
84                 public override void Emit ()
85                 {
86                         if (value.Type == TypeManager.decimal_type) {
87                                 FieldBuilder.SetCustomAttribute (CreateDecimalConstantAttribute (value));
88                         } else{
89                                 FieldBuilder.SetConstant (value.GetTypedValue ());
90                         }
91
92                         base.Emit ();
93                 }
94
95                 public static CustomAttributeBuilder CreateDecimalConstantAttribute (Constant c)
96                 {
97                         PredefinedAttribute pa = PredefinedAttributes.Get.DecimalConstant;
98                         if (pa.Constructor == null &&
99                                 !pa.ResolveConstructor (c.Location, TypeManager.byte_type, TypeManager.byte_type,
100                                         TypeManager.uint32_type, TypeManager.uint32_type, TypeManager.uint32_type))
101                                 return null;
102
103                         Decimal d = (Decimal) c.GetValue ();
104                         int [] bits = Decimal.GetBits (d);
105                         object [] args = new object [] { 
106                                 (byte) (bits [3] >> 16),
107                                 (byte) (bits [3] >> 31),
108                                 (uint) bits [2], (uint) bits [1], (uint) bits [0]
109                         };
110
111                         return new CustomAttributeBuilder (pa.Constructor, args);
112                 }
113
114                 public static void Error_InvalidConstantType (TypeSpec t, Location loc, Report Report)
115                 {
116                         if (t.IsGenericParameter) {
117                                 Report.Error (1959, loc,
118                                         "Type parameter `{0}' cannot be declared const", TypeManager.CSharpName (t));
119                         } else {
120                                 Report.Error (283, loc,
121                                         "The type `{0}' cannot be declared const", TypeManager.CSharpName (t));
122                         }
123                 }
124         }
125
126         public class ConstSpec : FieldSpec
127         {
128                 Expression value;
129
130                 public ConstSpec (TypeSpec declaringType, IMemberDefinition definition, TypeSpec memberType, FieldInfo fi, Modifiers mod, Expression value)
131                         : base (declaringType, definition, memberType, fi, mod)
132                 {
133                         this.value = value;
134                 }
135
136                 public Expression Value {
137                         get {
138                                 return value;
139                         }
140                         private set {
141                                 this.value = value;
142                         }
143                 }
144         }
145
146         class ConstInitializer : ShimExpression
147         {
148                 bool in_transit;
149                 protected readonly FieldBase field;
150
151                 public ConstInitializer (FieldBase field, Expression value)
152                         : base (value)
153                 {
154                         if (value != null)
155                                 this.loc = value.Location;
156
157                         this.field = field;
158                 }
159
160                 protected override Expression DoResolve (ResolveContext unused)
161                 {
162                         if (type != null)
163                                 return expr;
164
165                         var opt = ResolveContext.Options.ConstantScope;
166                         if (field is EnumMember)
167                                 opt |= ResolveContext.Options.EnumScope;
168
169                         //
170                         // Use a context in which the constant was declared and
171                         // not the one in which is referenced
172                         //
173                         var rc = new ResolveContext (field, opt);
174                         expr = DoResolveInitializer (rc);
175                         type = expr.Type;
176
177                         return expr;
178                 }
179
180                 protected virtual Expression DoResolveInitializer (ResolveContext rc)
181                 {
182                         if (in_transit) {
183                                 field.Compiler.Report.Error (110, field.Location,
184                                         "The evaluation of the constant value for `{0}' involves a circular definition",
185                                         field.GetSignatureForError ());
186
187                                 expr = null;
188                         } else {
189                                 in_transit = true;
190                                 expr = expr.Resolve (rc);
191                         }
192
193                         in_transit = false;
194
195                         if (expr != null) {
196                                 Constant c = expr as Constant;
197                                 if (c != null)
198                                         c = field.ConvertInitializer (rc, c);
199
200                                 if (c == null) {
201                                         if (TypeManager.IsReferenceType (field.MemberType))
202                                                 Error_ConstantCanBeInitializedWithNullOnly (rc, field.MemberType, loc, field.GetSignatureForError ());
203                                         else if (!(expr is Constant))
204                                                 Error_ExpressionMustBeConstant (rc, field.Location, field.GetSignatureForError ());
205                                         else
206                                                 expr.Error_ValueCannotBeConverted (rc, loc, field.MemberType, false);
207                                 }
208
209                                 expr = c;
210                         }
211
212                         if (expr == null) {
213                                 expr = New.Constantify (field.MemberType);
214                                 if (expr == null)
215                                         expr = Constant.CreateConstantFromValue (field.MemberType, null, Location);
216                                 expr = expr.Resolve (rc);
217                         }
218
219                         return expr;
220                 }
221         }
222 }