2010-06-17 Marek Safar <marek.safar@gmail.com>
[mono.git] / mcs / mcs / const.cs
index 4fbd98552b08e6084d91b76920f2dde14c5d0521..6b502c04f2b2986eb189a973afd4b72362c157ae 100644 (file)
@@ -5,71 +5,36 @@
 //   Miguel de Icaza (miguel@ximian.com)
 //   Marek Safar (marek.safar@seznam.cz)
 //
-// (C) 2001 Ximian, Inc.
-//
+// Copyright 2001-2003 Ximian, Inc.
+// Copyright 2003-2008 Novell, Inc.
 //
 
-//
-// This is needed because the following situation arises:
-//
-//     The FieldBuilder is declared with the real type for an enumeration
-//
-//     When we attempt to set the value for the constant, the FieldBuilder.SetConstant
-//     function aborts because it requires its argument to be of the same type
-//
+using System;
+using System.Reflection;
+using System.Reflection.Emit;
 
 namespace Mono.CSharp {
 
-       using System;
-       using System.Reflection;
-       using System.Reflection.Emit;
-       using System.Collections;
-
-       public class Const : FieldMember {
-               public Expression Expr;
-               EmitContext const_ec;
+       public class Const : FieldBase
+       {
+               Constant value;
 
-               bool resolved = false;
-               object ConstantValue = null;
-
-               bool in_transit = false;
-
-               public const int AllowedModifiers =
+               public const Modifiers AllowedModifiers =
                        Modifiers.NEW |
                        Modifiers.PUBLIC |
                        Modifiers.PROTECTED |
                        Modifiers.INTERNAL |
                        Modifiers.PRIVATE;
 
-               public Const (TypeContainer parent, Expression constant_type, string name,
-                             Expression expr, int mod_flags, Attributes attrs, Location loc)
-                       : base (parent, constant_type, mod_flags, AllowedModifiers,
-                               new MemberName (name), null, attrs, loc)
+               public Const (DeclSpace parent, FullNamedExpression type, string name,
+                             Expression expr, Modifiers mod_flags, Attributes attrs, Location loc)
+                       : base (parent, type, mod_flags, AllowedModifiers,
+                               new MemberName (name, loc), attrs)
                {
-                       Expr = expr;
-                       ModFlags |= Modifiers.STATIC;
-               }
+                       if (expr != null)
+                               initializer = new ConstInitializer (this, expr);
 
-#if DEBUG
-               void dump_tree (Type t)
-               {
-                       Console.WriteLine ("Dumping hierarchy");
-                       while (t != null){
-                               Console.WriteLine ("   " + t.FullName + " " +
-                                       (t.GetType ().IsEnum ? "yes" : "no"));
-                               t = t.BaseType;
-                       }
-               }
-#endif
-
-               protected override bool CheckBase ()
-               {
-                       // Constant.Define can be called when the parent type hasn't yet been populated
-                       // and it's base types need not have been populated.  So, we defer this check
-                       // to the second time Define () is called on this member.
-                       if (Parent.BaseCache == null)
-                               return true;
-                       return base.CheckBase ();
+                       ModFlags |= Modifiers.STATIC;
                }
 
                /// <summary>
@@ -77,240 +42,181 @@ namespace Mono.CSharp {
                /// </summary>
                public override bool Define ()
                {
-                       // Make Define () idempotent, but ensure that the error check happens.
-                       if (FieldBuilder != null)
-                               return base.CheckBase ();
-
                        if (!base.Define ())
                                return false;
 
-                       const_ec = new EmitContext (Parent, Location, null, MemberType, ModFlags);
-
-                       Type ttype = MemberType;
-                       while (ttype.IsArray)
-                           ttype = TypeManager.GetElementType (ttype);
-                       
-                       if (!TypeManager.IsBuiltinType(ttype) && (!ttype.IsSubclassOf(TypeManager.enum_type)) && !(Expr is NullLiteral)){
-                               Report.Error (
-                                       -3, Location,
-                                       "Constant type is not valid (only system types are allowed)");
-                               return false;
+                       TypeSpec ttype = MemberType;
+                       if (!ttype.IsConstantCompatible) {
+                               Error_InvalidConstantType (ttype, Location, Report);
                        }
 
-                       FieldAttributes field_attr = FieldAttributes.Static | Modifiers.FieldAttr (ModFlags);
-                       // I don't know why but they emit decimal constant as InitOnly
+                       FieldAttributes field_attr = FieldAttributes.Static | ModifiersExtensions.FieldAttr (ModFlags);
+                       // Decimals cannot be emitted into the constant blob.  So, convert to 'readonly'.
                        if (ttype == TypeManager.decimal_type) {
                                field_attr |= FieldAttributes.InitOnly;
-                       }
-                       else {
+                       } else {
                                field_attr |= FieldAttributes.Literal;
                        }
 
-                       FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType, field_attr);
+                       FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType.GetMetaInfo (), field_attr);
+                       spec = new ConstSpec (Parent.Definition, this, MemberType, FieldBuilder, ModFlags, initializer);
+
+                       Parent.MemberCache.AddMember (spec);
 
-                       TypeManager.RegisterConstant (FieldBuilder, this);
+                       if ((field_attr & FieldAttributes.InitOnly) != 0)
+                               Parent.PartialContainer.RegisterFieldForInitialization (this,
+                                       new FieldInitializer (this, initializer, this));
 
                        return true;
                }
 
-               //
-               // Changes the type of the constant expression `expr' to the Type `type'
-               // Returns null on failure.
-               //
-               public static Constant ChangeType (Location loc, Constant expr, Type type)
+               public Constant DefineValue ()
                {
-                       if (type == TypeManager.object_type)
-                               return expr;
+                       if (value == null)
+                               value = initializer.Resolve (new ResolveContext (this)) as Constant;
 
-                       bool fail;
+                       return value;
+               }
 
-                       // from the null type to any reference-type.
-                       if (expr.Type == TypeManager.null_type && !type.IsValueType && !TypeManager.IsEnumType (type))
-                               return NullLiteral.Null;
+               /// <summary>
+               ///  Emits the field value by evaluating the expression
+               /// </summary>
+               public override void Emit ()
+               {
+                       if (value.Type == TypeManager.decimal_type) {
+                               FieldBuilder.SetCustomAttribute (CreateDecimalConstantAttribute (value));
+                       } else{
+                               FieldBuilder.SetConstant (value.GetTypedValue ());
+                       }
+
+                       base.Emit ();
+               }
 
-                       if (!Convert.ImplicitStandardConversionExists (Convert.ConstantEC, expr, type)){
-                               Convert.Error_CannotImplicitConversion (loc, expr.Type, type);
+               public static CustomAttributeBuilder CreateDecimalConstantAttribute (Constant c)
+               {
+                       PredefinedAttribute pa = PredefinedAttributes.Get.DecimalConstant;
+                       if (pa.Constructor == null &&
+                               !pa.ResolveConstructor (c.Location, TypeManager.byte_type, TypeManager.byte_type,
+                                       TypeManager.uint32_type, TypeManager.uint32_type, TypeManager.uint32_type))
                                return null;
-                       }
 
-                       // Special-case: The 0 literal can be converted to an enum value,
-                       // and ImplicitStandardConversionExists will return true in that case.
-                       if (expr.Type == TypeManager.int32_type && TypeManager.IsEnumType (type)){
-                               if (expr is IntLiteral && ((IntLiteral) expr).Value == 0)
-                                       return new EnumConstant (expr, type);
-                       }
-                       
-                       object constant_value = TypeManager.ChangeType (expr.GetValue (), type, out fail);
-                       if (fail){
-                               Convert.Error_CannotImplicitConversion (loc, expr.Type, type);
-                               
-                               //
-                               // We should always catch the error before this is ever
-                               // reached, by calling Convert.ImplicitStandardConversionExists
-                               //
-                               throw new Exception (
-                                                    String.Format ("LookupConstantValue: This should never be reached {0} {1}", expr.Type, type));
-                       }
+                       Decimal d = (Decimal) c.GetValue ();
+                       int [] bits = Decimal.GetBits (d);
+                       object [] args = new object [] { 
+                               (byte) (bits [3] >> 16),
+                               (byte) (bits [3] >> 31),
+                               (uint) bits [2], (uint) bits [1], (uint) bits [0]
+                       };
 
-                       Constant retval;
-                       if (type == TypeManager.int32_type)
-                               retval = new IntConstant ((int) constant_value);
-                       else if (type == TypeManager.uint32_type)
-                               retval = new UIntConstant ((uint) constant_value);
-                       else if (type == TypeManager.int64_type)
-                               retval = new LongConstant ((long) constant_value);
-                       else if (type == TypeManager.uint64_type)
-                               retval = new ULongConstant ((ulong) constant_value);
-                       else if (type == TypeManager.float_type)
-                               retval = new FloatConstant ((float) constant_value);
-                       else if (type == TypeManager.double_type)
-                               retval = new DoubleConstant ((double) constant_value);
-                       else if (type == TypeManager.string_type)
-                               retval = new StringConstant ((string) constant_value);
-                       else if (type == TypeManager.short_type)
-                               retval = new ShortConstant ((short) constant_value);
-                       else if (type == TypeManager.ushort_type)
-                               retval = new UShortConstant ((ushort) constant_value);
-                       else if (type == TypeManager.sbyte_type)
-                               retval = new SByteConstant ((sbyte) constant_value);
-                       else if (type == TypeManager.byte_type)
-                               retval = new ByteConstant ((byte) constant_value);
-                       else if (type == TypeManager.char_type)
-                               retval = new CharConstant ((char) constant_value);
-                       else if (type == TypeManager.bool_type)
-                               retval = new BoolConstant ((bool) constant_value);
-                       else if (type == TypeManager.decimal_type)
-                               retval = new DecimalConstant ((decimal) constant_value);
-                       else
-                               throw new Exception ("LookupConstantValue: Unhandled constant type: " + type);
-                       
-                       return retval;
+                       return new CustomAttributeBuilder (pa.Constructor, args);
                }
-               
-               /// <summary>
-               ///  Looks up the value of a constant field. Defines it if it hasn't
-               ///  already been. Similar to LookupEnumValue in spirit.
-               /// </summary>
-               public bool LookupConstantValue (out object value)
+
+               public static void Error_InvalidConstantType (TypeSpec t, Location loc, Report Report)
                {
-                       if (resolved) {
-                               value = ConstantValue;
-                               return true;
+                       if (t.IsGenericParameter) {
+                               Report.Error (1959, loc,
+                                       "Type parameter `{0}' cannot be declared const", TypeManager.CSharpName (t));
+                       } else {
+                               Report.Error (283, loc,
+                                       "The type `{0}' cannot be declared const", TypeManager.CSharpName (t));
                        }
+               }
+       }
 
-                       if (in_transit) {
-                               Report.Error (110, Location,
-                                             "The evaluation of the constant value for `" +
-                                             Name + "' involves a circular definition.");
-                               value = null;
-                               return false;
+       public class ConstSpec : FieldSpec
+       {
+               Expression value;
+
+               public ConstSpec (TypeSpec declaringType, IMemberDefinition definition, TypeSpec memberType, FieldInfo fi, Modifiers mod, Expression value)
+                       : base (declaringType, definition, memberType, fi, mod)
+               {
+                       this.value = value;
+               }
+
+               public Expression Value {
+                       get {
+                               return value;
+                       }
+                       private set {
+                               this.value = value;
                        }
+               }
+       }
 
-                       in_transit = true;
-                       int errors = Report.Errors;
+       class ConstInitializer : ShimExpression
+       {
+               bool in_transit;
+               protected readonly FieldBase field;
+
+               public ConstInitializer (FieldBase field, Expression value)
+                       : base (value)
+               {
+                       if (value != null)
+                               this.loc = value.Location;
+
+                       this.field = field;
+               }
+
+               protected override Expression DoResolve (ResolveContext unused)
+               {
+                       if (type != null)
+                               return expr;
+
+                       var opt = ResolveContext.Options.ConstantScope;
+                       if (field is EnumMember)
+                               opt |= ResolveContext.Options.EnumScope;
 
                        //
-                       // We might have cleared Expr ourselves in a recursive definition
+                       // Use a context in which the constant was declared and
+                       // not the one in which is referenced
                        //
-                       if (Expr == null){
-                               value = null;
-                               return false;
-                       }
+                       var rc = new ResolveContext (field, opt);
+                       expr = DoResolveInitializer (rc);
+                       type = expr.Type;
 
-                       Expr = Expr.Resolve (const_ec);
+                       return expr;
+               }
 
-                       in_transit = false;
+               protected virtual Expression DoResolveInitializer (ResolveContext rc)
+               {
+                       if (in_transit) {
+                               field.Compiler.Report.Error (110, field.Location,
+                                       "The evaluation of the constant value for `{0}' involves a circular definition",
+                                       field.GetSignatureForError ());
 
-                       if (Expr == null) {
-                               if (errors == Report.Errors)
-                                       Report.Error (150, Location, "A constant value is expected");
-                               value = null;
-                               return false;
+                               expr = null;
+                       } else {
+                               in_transit = true;
+                               expr = expr.Resolve (rc);
                        }
 
-                       Expression real_expr = Expr;
-
-                       Constant ce = Expr as Constant;
-                       if (ce == null){
-                               UnCheckedExpr un_expr = Expr as UnCheckedExpr;
-                               CheckedExpr ch_expr = Expr as CheckedExpr;
-                               EmptyCast ec_expr = Expr as EmptyCast;
-
-                               if ((un_expr != null) && (un_expr.Expr is Constant))
-                                       Expr = un_expr.Expr;
-                               else if ((ch_expr != null) && (ch_expr.Expr is Constant))
-                                       Expr = ch_expr.Expr;
-                               else if ((ec_expr != null) && (ec_expr.Child is Constant))
-                                       Expr = ec_expr.Child;
-                               else if (Expr is ArrayCreation){
-                                       Report.Error (133, Location, "Arrays can not be constant");
-                               } else {
-                                       if (errors == Report.Errors)
-                                               Report.Error (150, Location, "A constant value is expected");
-                                       value = null;
-                                       return false;
-                               }
-
-                               ce = Expr as Constant;
-                       }
+                       in_transit = false;
 
-                       if (MemberType != real_expr.Type) {
-                               ce = ChangeType (Location, ce, MemberType);
-                               if (ce == null){
-                                       value = null;
-                                       return false;
+                       if (expr != null) {
+                               Constant c = expr as Constant;
+                               if (c != null)
+                                       c = field.ConvertInitializer (rc, c);
+
+                               if (c == null) {
+                                       if (TypeManager.IsReferenceType (field.MemberType))
+                                               Error_ConstantCanBeInitializedWithNullOnly (rc, field.MemberType, loc, field.GetSignatureForError ());
+                                       else if (!(expr is Constant))
+                                               Error_ExpressionMustBeConstant (rc, field.Location, field.GetSignatureForError ());
+                                       else
+                                               expr.Error_ValueCannotBeConverted (rc, loc, field.MemberType, false);
                                }
-                               Expr = ce;
-                       }
 
-                       if (ce != null)
-                               ConstantValue = ce.GetValue ();
-
-                       if (MemberType.IsEnum){
-                               //
-                               // This sadly does not work for our user-defined enumerations types ;-(
-                               //
-                               try {
-                                       ConstantValue = System.Enum.ToObject (
-                                               MemberType, ConstantValue);
-                               } catch (ArgumentException){
-                                       Report.Error (
-                                               -16, Location,
-                                               ".NET SDK 1.0 does not permit to create the constant "+
-                                               " field from a user-defined enumeration");
-                               }
+                               expr = c;
                        }
 
-                       if (ce is DecimalConstant) {
-                               Decimal d = ((DecimalConstant)ce).Value;
-                               int[] bits = Decimal.GetBits (d);
-                               object[] args = new object[] { (byte)(bits [3] >> 16), (byte)(bits [3] >> 31), (uint)bits [2], (uint)bits [1], (uint)bits [0] };
-                               CustomAttributeBuilder cab = new CustomAttributeBuilder (TypeManager.decimal_constant_attribute_ctor, args);
-                               FieldBuilder.SetCustomAttribute (cab);
+                       if (expr == null) {
+                               expr = New.Constantify (field.MemberType);
+                               if (expr == null)
+                                       expr = Constant.CreateConstantFromValue (field.MemberType, null, Location);
+                               expr = expr.Resolve (rc);
                        }
-                       else{
-                               FieldBuilder.SetConstant (ConstantValue);
-                       }
-
-                       if (!TypeManager.RegisterFieldValue (FieldBuilder, ConstantValue))
-                               throw new Exception ("Cannot register const value");
 
-                       value = ConstantValue;
-                       resolved = true;
-                       return true;
-               }
-               
-               
-               /// <summary>
-               ///  Emits the field value by evaluating the expression
-               /// </summary>
-               public override void Emit ()
-               {
-                       object value;
-                       LookupConstantValue (out value);
-                       base.Emit ();
+                       return expr;
                }
        }
 }
-
-