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