2007-11-16 Marek Safar <marek.safar@gmail.com>
[mono.git] / mcs / mcs / const.cs
index 39fe478eb9224584d4f4133c40446b99cf49df78..de881fbc2c102a34c30ca7c382605c243e649050 100644 (file)
@@ -20,13 +20,14 @@ namespace Mono.CSharp {
        {
                void CheckObsoleteness (Location loc);
                bool ResolveValue ();
-               Constant Value { get; }
+               Constant CreateConstantReference (Location loc);
        }
 
-       public class Const : FieldMember, IConstant {
-               Expression Expr;
-               Constant value;
+       public class Const : FieldBase, IConstant {
+               protected Constant value;
                bool in_transit;
+               bool resolved;
+               bool define_called;
 
                public const int AllowedModifiers =
                        Modifiers.NEW |
@@ -35,12 +36,12 @@ 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, loc), expr, attrs)
+                               new MemberName (name, loc), attrs)
                {
-                       Expr = expr;
+                       initializer = expr;
                        ModFlags |= Modifiers.STATIC;
                }
 
@@ -49,7 +50,7 @@ namespace Mono.CSharp {
                        // 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)
+                       if (Parent.PartialContainer.BaseCache == null)
                                return true;
                        return base.CheckBase ();
                }
@@ -59,34 +60,64 @@ namespace Mono.CSharp {
                /// </summary>
                public override bool Define ()
                {
-                       // Make Define () idempotent, but ensure that the error check happens.
-                       if (FieldBuilder != null)
-                               return base.CheckBase ();
+                       // Because constant define can be called from other class
+                       if (define_called) {
+                               CheckBase ();
+                               return FieldBuilder != null;
+                       }
+
+                       define_called = true;
 
                        if (!base.Define ())
                                return false;
 
                        Type ttype = MemberType;
+                       if (!IsConstantTypeValid (ttype)) {
+                               Error_InvalidConstantType (ttype, Location);
+                               return false;
+                       }
+
+                       // If the constant is private then we don't need any field the
+                       // value is already inlined and cannot be referenced
+                       //if ((ModFlags & Modifiers.PRIVATE) != 0 && RootContext.Optimize)
+                       //      return true;
+
                        while (ttype.IsArray)
-                           ttype = TypeManager.GetElementType (ttype);
+                               ttype = TypeManager.GetElementType (ttype);
 
                        FieldAttributes field_attr = FieldAttributes.Static | Modifiers.FieldAttr (ModFlags);
                        // Decimals cannot be emitted into the constant blob.  So, convert to 'readonly'.
                        if (ttype == TypeManager.decimal_type) {
                                field_attr |= FieldAttributes.InitOnly;
-                               Parent.RegisterFieldForInitialization (this);
-                       }
-                       else {
+                       } else {
                                field_attr |= FieldAttributes.Literal;
                        }
 
                        FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType, field_attr);
-
                        TypeManager.RegisterConstant (FieldBuilder, this);
+                       Parent.MemberCache.AddMember (FieldBuilder, this);
+
+                       if (ttype == TypeManager.decimal_type)
+                               Parent.PartialContainer.RegisterFieldForInitialization (this,
+                                       new FieldInitializer (FieldBuilder, initializer));
+
+                       return true;
+               }
+
+               public static bool IsConstantTypeValid (Type t)
+               {
+                       if (TypeManager.IsBuiltinOrEnum (t))
+                               return true;
+
+                       if (t.IsPointer || t.IsValueType)
+                               return false;
+                       
+                       if (TypeManager.IsGenericParameter (t))
+                               return false;
 
                        return true;
                }
-               
+
                /// <summary>
                ///  Emits the field value by evaluating the expression
                /// </summary>
@@ -95,6 +126,9 @@ namespace Mono.CSharp {
                        if (!ResolveValue ())
                                return;
 
+                       if (FieldBuilder == null)
+                               return;
+
                        if (value.Type == TypeManager.decimal_type) {
                                Decimal d = ((DecimalConstant)value).Value;
                                int[] bits = Decimal.GetBits (d);
@@ -120,54 +154,109 @@ namespace Mono.CSharp {
                                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 (value != null)
-                               return true;
+                       if (resolved)
+                               return value != null;
 
                        SetMemberIsUsed ();
                        if (in_transit) {
                                Error_CyclicDeclaration (this);
                                // Suppress cyclic errors
                                value = New.Constantify (MemberType);
+                               resolved = true;
                                return false;
                        }
 
                        in_transit = true;
-                       EmitContext ec = new EmitContext (Parent, Location, null, MemberType, ModFlags);
-                       value = Expr.ResolveAsConstant (ec, this);
-                       if (value == null)
-                               return false;
+                       // TODO: IResolveContext here
+                       EmitContext ec = new EmitContext (
+                               this, Parent, Location, null, MemberType, ModFlags);
+                       ec.InEnumContext = this is EnumMember;
+                       ec.IsAnonymousMethodAllowed = false;
+                       value = DoResolveValue (ec);
+                       in_transit = false;
+                       resolved = true;
+                       return value != null;
+               }
 
-                       value = value.ToType (MemberType, Location);
+               protected virtual Constant DoResolveValue (EmitContext ec)
+               {
+                       Constant value = initializer.ResolveAsConstant (ec, this);
                        if (value == null)
-                               return false;
+                               return null;
+
+                       Constant c = value.ConvertImplicitly (MemberType);
+                       if (c == null) {
+                               if (!MemberType.IsValueType && MemberType != TypeManager.string_type && !value.IsDefaultValue)
+                                       Error_ConstantCanBeInitializedWithNullOnly (Location, GetSignatureForError ());
+                               else
+                                       value.Error_ValueCannotBeConverted (null, Location, MemberType, false);
+                       }
 
-                       in_transit = false;
-                       return true;
+                       return c;
                }
 
-               public Constant Value {
-                       get {
-                               return value;
-                       }
+               public virtual Constant CreateConstantReference (Location loc)
+               {
+                       if (value == null)
+                               return null;
+
+                       return Constant.CreateConstant (value.Type, value.GetValue(), loc);
                }
 
                #endregion
        }
 
-       public class ExternalConstant: IConstant
+       public class ExternalConstant : IConstant
        {
                FieldInfo fi;
-               Constant value;
+               object value;
 
                public ExternalConstant (FieldInfo fi)
                {
                        this.fi = fi;
                }
 
+               private ExternalConstant (FieldInfo fi, object value):
+                       this (fi)
+               {
+                       this.value = value;
+               }
+
+               //
+               // 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)
@@ -185,20 +274,13 @@ namespace Mono.CSharp {
                        if (value != null)
                                return true;
 
-                       if (fi.DeclaringType.IsEnum) {
-                               value = Expression.Constantify (fi.GetValue (fi), TypeManager.EnumToUnderlying (fi.FieldType));
-                               value = new EnumConstant (value, fi.DeclaringType);
-                               return true;
-                       }
-
-                       value = Expression.Constantify (fi.GetValue (fi), fi.FieldType);
+                       value = fi.GetValue (fi);
                        return true;
                }
 
-               public Constant Value {
-                       get {
-                               return value;
-                       }
+               public Constant CreateConstantReference (Location loc)
+               {
+                       return Constant.CreateConstant (fi.FieldType, value, loc);
                }
 
                #endregion