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