Merge
[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
14 #if STATIC
15 using IKVM.Reflection;
16 #else
17 using System.Reflection;
18 #endif
19
20 namespace Mono.CSharp {
21
22         public class Const : FieldBase
23         {
24                 const Modifiers AllowedModifiers =
25                         Modifiers.NEW |
26                         Modifiers.PUBLIC |
27                         Modifiers.PROTECTED |
28                         Modifiers.INTERNAL |
29                         Modifiers.PRIVATE;
30
31                 public Const (TypeDefinition parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, Attributes attrs)
32                         : base (parent, type, mod_flags, AllowedModifiers, name, attrs)
33                 {
34                         ModFlags |= Modifiers.STATIC;
35                 }
36
37                 /// <summary>
38                 ///   Defines the constant in the @parent
39                 /// </summary>
40                 public override bool Define ()
41                 {
42                         if (!base.Define ())
43                                 return false;
44
45                         if (!member_type.IsConstantCompatible) {
46                                 Error_InvalidConstantType (member_type, Location, Report);
47                         }
48
49                         FieldAttributes field_attr = FieldAttributes.Static | ModifiersExtensions.FieldAttr (ModFlags);
50                         // Decimals cannot be emitted into the constant blob.  So, convert to 'readonly'.
51                         if (member_type.BuiltinType == BuiltinTypeSpec.Type.Decimal) {
52                                 field_attr |= FieldAttributes.InitOnly;
53                         } else {
54                                 field_attr |= FieldAttributes.Literal;
55                         }
56
57                         FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType.GetMetaInfo (), field_attr);
58                         spec = new ConstSpec (Parent.Definition, this, MemberType, FieldBuilder, ModFlags, initializer);
59
60                         Parent.MemberCache.AddMember (spec);
61
62                         if ((field_attr & FieldAttributes.InitOnly) != 0)
63                                 Parent.PartialContainer.RegisterFieldForInitialization (this,
64                                         new FieldInitializer (this, initializer, Location));
65
66                         if (declarators != null) {
67                                 var t = new TypeExpression (MemberType, TypeExpression.Location);
68                                 foreach (var d in declarators) {
69                                         var c = new Const (Parent, t, ModFlags & ~Modifiers.STATIC, new MemberName (d.Name.Value, d.Name.Location), OptAttributes);
70                                         c.initializer = d.Initializer;
71                                         ((ConstInitializer) c.initializer).Name = d.Name.Value;
72                                         c.Define ();
73                                         Parent.PartialContainer.Members.Add (c);
74                                 }
75                         }
76
77                         return true;
78                 }
79
80                 public void DefineValue ()
81                 {
82                         var rc = new ResolveContext (this);
83                         ((ConstSpec) spec).GetConstant (rc);
84                 }
85
86                 /// <summary>
87                 ///  Emits the field value by evaluating the expression
88                 /// </summary>
89                 public override void Emit ()
90                 {
91                         var c = ((ConstSpec) spec).Value as Constant;
92                         if (c.Type.BuiltinType == BuiltinTypeSpec.Type.Decimal) {
93                                 Module.PredefinedAttributes.DecimalConstant.EmitAttribute (FieldBuilder, (decimal) c.GetValue (), c.Location);
94                         } else {
95                                 FieldBuilder.SetConstant (c.GetValue ());
96                         }
97
98                         base.Emit ();
99                 }
100
101                 public static void Error_InvalidConstantType (TypeSpec t, Location loc, Report Report)
102                 {
103                         if (t.IsGenericParameter) {
104                                 Report.Error (1959, loc,
105                                         "Type parameter `{0}' cannot be declared const", t.GetSignatureForError ());
106                         } else {
107                                 Report.Error (283, loc,
108                                         "The type `{0}' cannot be declared const", t.GetSignatureForError ());
109                         }
110                 }
111
112                 public override void Accept (StructuralVisitor visitor)
113                 {
114                         visitor.Visit (this);
115                 }
116         }
117
118         public class ConstSpec : FieldSpec
119         {
120                 Expression value;
121
122                 public ConstSpec (TypeSpec declaringType, IMemberDefinition definition, TypeSpec memberType, FieldInfo fi, Modifiers mod, Expression value)
123                         : base (declaringType, definition, memberType, fi, mod)
124                 {
125                         if (value == null)
126                                 throw new ArgumentNullException ("value");
127
128                         this.value = value;
129                 }
130
131                 //
132                 // This expresion is guarantee to be a constant at emit phase only
133                 //
134                 public Expression Value {
135                         get {
136                                 return value;
137                         }
138                 }
139
140                 //
141                 // For compiled constants we have to resolve the value as there could be constant dependecies. This
142                 // is needed for imported constants too to get the right context type
143                 //
144                 public Constant GetConstant (ResolveContext rc)
145                 {
146                         if (value.eclass != ExprClass.Value)
147                                 value = value.Resolve (rc);
148
149                         return (Constant) value;
150                 }
151         }
152
153         public class ConstInitializer : ShimExpression
154         {
155                 bool in_transit;
156                 readonly FieldBase field;
157
158                 public ConstInitializer (FieldBase field, Expression value, Location loc)
159                         : base (value)
160                 {
161                         this.loc = loc;
162                         this.field = field;
163                 }
164
165                 public string Name { get; set; }
166
167                 protected override Expression DoResolve (ResolveContext unused)
168                 {
169                         if (type != null)
170                                 return expr;
171
172                         var opt = ResolveContext.Options.ConstantScope;
173                         if (field is EnumMember)
174                                 opt |= ResolveContext.Options.EnumScope;
175
176                         //
177                         // Use a context in which the constant was declared and
178                         // not the one in which is referenced
179                         //
180                         var rc = new ResolveContext (field, opt);
181                         expr = DoResolveInitializer (rc);
182                         type = expr.Type;
183
184                         return expr;
185                 }
186
187                 protected virtual Expression DoResolveInitializer (ResolveContext rc)
188                 {
189                         if (in_transit) {
190                                 field.Compiler.Report.Error (110, expr.Location,
191                                         "The evaluation of the constant value for `{0}' involves a circular definition",
192                                         GetSignatureForError ());
193
194                                 expr = null;
195                         } else {
196                                 in_transit = true;
197                                 expr = expr.Resolve (rc);
198                         }
199
200                         in_transit = false;
201
202                         if (expr != null) {
203                                 Constant c = expr as Constant;
204                                 if (c != null)
205                                         c = field.ConvertInitializer (rc, c);
206
207                                 if (c == null) {
208                                         if (TypeSpec.IsReferenceType (field.MemberType))
209                                                 Error_ConstantCanBeInitializedWithNullOnly (rc, field.MemberType, expr.Location, GetSignatureForError ());
210                                         else if (!(expr is Constant))
211                                                 Error_ExpressionMustBeConstant (rc, expr.Location, GetSignatureForError ());
212                                         else
213                                                 expr.Error_ValueCannotBeConverted (rc, field.MemberType, false);
214                                 }
215
216                                 expr = c;
217                         }
218
219                         if (expr == null) {
220                                 expr = New.Constantify (field.MemberType, Location);
221                                 if (expr == null)
222                                         expr = Constant.CreateConstantFromValue (field.MemberType, null, Location);
223                                 expr = expr.Resolve (rc);
224                         }
225
226                         return expr;
227                 }
228
229                 public override string GetSignatureForError ()
230                 {
231                         if (Name == null)
232                                 return field.GetSignatureForError ();
233
234                         return field.Parent.GetSignatureForError () + "." + Name;
235                 }
236
237                 public override object Accept (StructuralVisitor visitor)
238                 {
239                         return visitor.Visit (this);
240                 }
241         }
242 }