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