2007-01-10 Chris Toshok <toshok@ximian.com>
[mono.git] / mcs / mcs / const.cs
index 1c15f85ecaa87a95376b2a3753923b132a7a94c5..b947e4d68f5485ee6552e1e4abb33d32d71d8459 100644 (file)
@@ -9,15 +9,6 @@
 //
 //
 
-//
-// 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
-//
-
 namespace Mono.CSharp {
 
        using System;
@@ -25,14 +16,17 @@ namespace Mono.CSharp {
        using System.Reflection.Emit;
        using System.Collections;
 
-       public class Const : FieldMember {
-               public Expression Expr;
-               EmitContext const_ec;
-
-               bool resolved = false;
-               object ConstantValue = null;
+       public interface IConstant
+       {
+               void CheckObsoleteness (Location loc);
+               bool ResolveValue ();
+               Constant CreateConstantReference (Location loc);
+       }
 
-               bool in_transit = false;
+       public class Const : FieldMember, IConstant {
+               Constant value;
+               bool in_transit;
+               bool define_called;
 
                public const int AllowedModifiers =
                        Modifiers.NEW |
@@ -41,54 +35,56 @@ namespace Mono.CSharp {
                        Modifiers.INTERNAL |
                        Modifiers.PRIVATE;
 
-               public Const (TypeContainer parent, Expression constant_type, string name,
+               public Const (DeclSpace 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)
+                               new MemberName (name, loc), attrs)
                {
-                       Expr = expr;
+                       initializer = expr;
                        ModFlags |= Modifiers.STATIC;
                }
 
-#if DEBUG
-               void dump_tree (Type t)
+               protected override bool CheckBase ()
                {
-                       Console.WriteLine ("Dumping hierarchy");
-                       while (t != null){
-                               Console.WriteLine ("   " + t.FullName + " " +
-                                       (t.GetType ().IsEnum ? "yes" : "no"));
-                               t = t.BaseType;
-                       }
+                       // 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.PartialContainer.BaseCache == null)
+                               return true;
+                       return base.CheckBase ();
                }
-#endif
 
                /// <summary>
                ///   Defines the constant in the @parent
                /// </summary>
                public override bool Define ()
                {
+                       // Because constant define can be called from other class
+                       if (define_called) {
+                               CheckBase ();
+                               return FieldBuilder != null;
+                       }
+
+                       define_called = true;
+
                        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)");
+                       if (!IsConstantTypeValid (ttype)) {
+                               Error_InvalidConstantType (ttype, Location);
                                return false;
                        }
 
+                       while (ttype.IsArray)
+                           ttype = TypeManager.GetElementType (ttype);
+
                        FieldAttributes field_attr = FieldAttributes.Static | Modifiers.FieldAttr (ModFlags);
-                       // I don't know why but they emit decimal constant as InitOnly
+                       // Decimals cannot be emitted into the constant blob.  So, convert to 'readonly'.
                        if (ttype == TypeManager.decimal_type) {
                                field_attr |= FieldAttributes.InitOnly;
-                       }
-                       else {
+                               Parent.PartialContainer.RegisterFieldForInitialization (this);
+                       else {
                                field_attr |= FieldAttributes.Literal;
                        }
 
@@ -99,207 +95,174 @@ namespace Mono.CSharp {
                        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 static bool IsConstantTypeValid (Type t)
                {
-                       if (type == TypeManager.object_type)
-                               return expr;
+                       if (TypeManager.IsBuiltinOrEnum (t))
+                               return true;
 
-                       bool fail;
+                       if (t.IsPointer || t.IsValueType)
+                               return false;
+                       
+                       if (TypeManager.IsGenericParameter (t))
+                               return false;
 
-                       // from the null type to any reference-type.
-                       if (expr.Type == TypeManager.null_type && !type.IsValueType && !TypeManager.IsEnumType (type))
-                               return NullLiteral.Null;
+                       return true;
+               }
 
-                       if (!Convert.ImplicitStandardConversionExists (Convert.ConstantEC, expr, type)){
-                               Convert.Error_CannotImplicitConversion (loc, expr.Type, type);
-                               return null;
-                       }
+               /// <summary>
+               ///  Emits the field value by evaluating the expression
+               /// </summary>
+               public override void Emit ()
+               {
+                       if (!ResolveValue ())
+                               return;
 
-                       // 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);
+                       if (value.Type == TypeManager.decimal_type) {
+                               Decimal d = ((DecimalConstant)value).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);
                        }
-                       
-                       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));
+                       else{
+                               FieldBuilder.SetConstant (value.GetTypedValue ());
                        }
 
-                       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;
+                       base.Emit ();
                }
-               
-               /// <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_ExpressionMustBeConstant (Location loc, string e_name)
+               {
+                       Report.Error (133, loc, "The expression being assigned to `{0}' must be constant", e_name);
+               }
+
+               public static void Error_CyclicDeclaration (MemberCore mc)
+               {
+                       Report.Error (110, mc.Location, "The evaluation of the constant value for `{0}' involves a circular definition",
+                               mc.GetSignatureForError ());
+               }
+
+               public static void Error_ConstantCanBeInitializedWithNullOnly (Location loc, string name)
+               {
+                       Report.Error (134, loc, "`{0}': the constant of reference type other than string can only be initialized with null",
+                               name);
+               }
+
+               public static void Error_InvalidConstantType (Type t, Location loc)
+               {
+                       Report.Error (283, loc, "The type `{0}' cannot be declared const", TypeManager.CSharpName (t));
+               }
+
+               #region IConstant Members
+
+               public bool ResolveValue ()
                {
-                       if (resolved) {
-                               value = ConstantValue;
+                       if (value != null)
                                return true;
-                       }
 
+                       SetMemberIsUsed ();
                        if (in_transit) {
-                               Report.Error (110, Location,
-                                             "The evaluation of the constant value for `" +
-                                             Name + "' involves a circular definition.");
-                               value = null;
+                               Error_CyclicDeclaration (this);
+                               // Suppress cyclic errors
+                               value = New.Constantify (MemberType);
                                return false;
                        }
 
                        in_transit = true;
-                       int errors = Report.Errors;
+                       // TODO: IResolveContext here
+                       EmitContext ec = new EmitContext (this, Parent, Location, null, MemberType, ModFlags);
+                       value = initializer.ResolveAsConstant (ec, this);
+                       in_transit = false;
 
-                       //
-                       // We might have cleared Expr ourselves in a recursive definition
-                       //
-                       if (Expr == null){
-                               value = null;
+                       if (value == null)
                                return false;
-                       }
 
-                       Expr = Expr.Resolve (const_ec);
-
-                       in_transit = false;
+                       value = value.ImplicitConversionRequired (MemberType, Location);
+                       if (value == null)
+                               return false;
 
-                       if (Expr == null) {
-                               if (errors == Report.Errors)
-                                       Report.Error (150, Location, "A constant value is expected");
-                               value = null;
+                       if (!MemberType.IsValueType && MemberType != TypeManager.string_type && !value.IsDefaultValue) {
+                               Error_ConstantCanBeInitializedWithNullOnly (Location, GetSignatureForError ());
                                return false;
                        }
 
-                       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;
-                       }
+                       return true;
+               }
 
-                       if (MemberType != real_expr.Type) {
-                               ce = ChangeType (Location, ce, MemberType);
-                               if (ce == null){
-                                       value = null;
-                                       return false;
-                               }
-                               Expr = ce;
-                       }
-                       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");
-                               }
-                       }
+               public Constant CreateConstantReference (Location loc)
+               {
+                       if (value == null)
+                               return null;
 
-                       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);
-                       }
-                       else{
-                               FieldBuilder.SetConstant (ConstantValue);
-                       }
+                       return Constant.CreateConstant (value.Type, value.GetValue(), loc);
+               }
 
-                       if (!TypeManager.RegisterFieldValue (FieldBuilder, ConstantValue))
-                               throw new Exception ("Cannot register const value");
+               #endregion
+       }
 
-                       value = ConstantValue;
-                       resolved = true;
-                       return true;
+       public class ExternalConstant : IConstant
+       {
+               FieldInfo fi;
+               object value;
+
+               public ExternalConstant (FieldInfo fi)
+               {
+                       this.fi = fi;
                }
-               
-               
-               /// <summary>
-               ///  Emits the field value by evaluating the expression
-               /// </summary>
-               public override void Emit ()
+
+               private ExternalConstant (FieldInfo fi, object value):
+                       this (fi)
                {
-                       object value;
-                       LookupConstantValue (out value);
+                       this.value = value;
+               }
 
-                       if (OptAttributes != null) {
-                               OptAttributes.Emit (const_ec, this);
+               //
+               // Decimal constants cannot be encoded in the constant blob, and thus are marked
+               // as IsInitOnly ('readonly' in C# parlance).  We get its value from the 
+               // DecimalConstantAttribute metadata.
+               //
+               public static IConstant CreateDecimal (FieldInfo fi)
+               {
+                       if (fi is FieldBuilder)
+                               return null;
+                       
+                       object[] attrs = fi.GetCustomAttributes (TypeManager.decimal_constant_attribute_type, false);
+                       if (attrs.Length != 1)
+                               return null;
+
+                       IConstant ic = new ExternalConstant (fi,
+                               ((System.Runtime.CompilerServices.DecimalConstantAttribute) attrs [0]).Value);
+
+                       return ic;
+               }
+
+               #region IConstant Members
+
+               public void CheckObsoleteness (Location loc)
+               {
+                       ObsoleteAttribute oa = AttributeTester.GetMemberObsoleteAttribute (fi);
+                       if (oa == null) {
+                               return;
                        }
 
-                       base.Emit ();
+                       AttributeTester.Report_ObsoleteMessage (oa, TypeManager.GetFullNameSignature (fi), loc);
+               }
+
+               public bool ResolveValue ()
+               {
+                       if (value != null)
+                               return true;
+
+                       value = fi.GetValue (fi);
+                       return true;
                }
-       }
-}
 
+               public Constant CreateConstantReference (Location loc)
+               {
+                       return Constant.CreateConstant (fi.FieldType, value, loc);
+               }
 
+               #endregion
+       }
+
+}