2010-04-29 Marek Safar <marek.safar@gmail.com>
[mono.git] / mcs / mcs / const.cs
index 882e5968d8b9e346c6d337202dffe32e0404dad6..6b502c04f2b2986eb189a973afd4b72362c157ae 100644 (file)
@@ -9,27 +9,17 @@
 // Copyright 2003-2008 Novell, Inc.
 //
 
-namespace Mono.CSharp {
+using System;
+using System.Reflection;
+using System.Reflection.Emit;
 
-       using System;
-       using System.Reflection;
-       using System.Reflection.Emit;
-       using System.Collections;
+namespace Mono.CSharp {
 
-       public interface IConstant
+       public class Const : FieldBase
        {
-               void CheckObsoleteness (Location loc);
-               bool ResolveValue ();
-               Constant CreateConstantReference (Location loc);
-       }
-
-       public class Const : FieldBase, IConstant {
-               protected Constant value;
-               bool in_transit;
-               bool resolved;
-               bool define_called;
+               Constant value;
 
-               public const int AllowedModifiers =
+               public const Modifiers AllowedModifiers =
                        Modifiers.NEW |
                        Modifiers.PUBLIC |
                        Modifiers.PROTECTED |
@@ -37,22 +27,14 @@ namespace Mono.CSharp {
                        Modifiers.PRIVATE;
 
                public Const (DeclSpace parent, FullNamedExpression type, string name,
-                             Expression expr, int mod_flags, Attributes attrs, Location loc)
+                             Expression expr, Modifiers mod_flags, Attributes attrs, Location loc)
                        : base (parent, type, mod_flags, AllowedModifiers,
                                new MemberName (name, loc), attrs)
                {
-                       initializer = expr;
-                       ModFlags |= Modifiers.STATIC;
-               }
+                       if (expr != null)
+                               initializer = new ConstInitializer (this, expr);
 
-               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.PartialContainer.BaseCache == null)
-                               return true;
-                       return base.CheckBase ();
+                       ModFlags |= Modifiers.STATIC;
                }
 
                /// <summary>
@@ -60,28 +42,15 @@ namespace Mono.CSharp {
                /// </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;
 
-                       Type ttype = MemberType;
-                       if (!IsConstantTypeValid (ttype)) {
-                               Error_InvalidConstantType (ttype, Location);
+                       TypeSpec ttype = MemberType;
+                       if (!ttype.IsConstantCompatible) {
+                               Error_InvalidConstantType (ttype, Location, Report);
                        }
 
-                       // 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;
-
-                       FieldAttributes field_attr = FieldAttributes.Static | Modifiers.FieldAttr (ModFlags);
+                       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;
@@ -89,26 +58,24 @@ namespace Mono.CSharp {
                                field_attr |= FieldAttributes.Literal;
                        }
 
-                       FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType, field_attr);
-                       TypeManager.RegisterConstant (FieldBuilder, this);
-                       Parent.MemberCache.AddMember (FieldBuilder, this);
+                       FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType.GetMetaInfo (), field_attr);
+                       spec = new ConstSpec (Parent.Definition, this, MemberType, FieldBuilder, ModFlags, initializer);
+
+                       Parent.MemberCache.AddMember (spec);
 
                        if ((field_attr & FieldAttributes.InitOnly) != 0)
                                Parent.PartialContainer.RegisterFieldForInitialization (this,
-                                       new FieldInitializer (FieldBuilder, initializer, this));
+                                       new FieldInitializer (this, initializer, this));
 
                        return true;
                }
 
-               public static bool IsConstantTypeValid (Type t)
+               public Constant DefineValue ()
                {
-                       if (TypeManager.IsBuiltinOrEnum (t))
-                               return true;
-
-                       if (TypeManager.IsGenericParameter (t) || t.IsPointer)
-                               return false;
+                       if (value == null)
+                               value = initializer.Resolve (new ResolveContext (this)) as Constant;
 
-                       return TypeManager.IsReferenceType (t);
+                       return value;
                }
 
                /// <summary>
@@ -116,12 +83,6 @@ namespace Mono.CSharp {
                /// </summary>
                public override void Emit ()
                {
-                       if (!ResolveValue ())
-                               return;
-
-                       if (FieldBuilder == null)
-                               return;
-
                        if (value.Type == TypeManager.decimal_type) {
                                FieldBuilder.SetCustomAttribute (CreateDecimalConstantAttribute (value));
                        } else{
@@ -150,26 +111,9 @@ namespace Mono.CSharp {
                        return new CustomAttributeBuilder (pa.Constructor, args);
                }
 
-               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 (Type type, Location loc, string name)
+               public static void Error_InvalidConstantType (TypeSpec t, Location loc, Report Report)
                {
-                       Report.Error (134, loc, "A constant `{0}' of reference type `{1}' can only be initialized with null",
-                               name, TypeManager.CSharpName (type));
-               }
-
-               public static void Error_InvalidConstantType (Type t, Location loc)
-               {
-                       if (TypeManager.IsGenericParameter (t)) {
+                       if (t.IsGenericParameter) {
                                Report.Error (1959, loc,
                                        "Type parameter `{0}' cannot be declared const", TypeManager.CSharpName (t));
                        } else {
@@ -177,136 +121,102 @@ namespace Mono.CSharp {
                                        "The type `{0}' cannot be declared const", TypeManager.CSharpName (t));
                        }
                }
+       }
 
-               #region IConstant Members
+       public class ConstSpec : FieldSpec
+       {
+               Expression value;
 
-               public bool ResolveValue ()
+               public ConstSpec (TypeSpec declaringType, IMemberDefinition definition, TypeSpec memberType, FieldInfo fi, Modifiers mod, Expression value)
+                       : base (declaringType, definition, memberType, fi, mod)
                {
-                       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;
-                       // TODO: IResolveContext here
-                       EmitContext ec = new EmitContext (
-                               this, Parent, null, MemberType);
-
-                       EmitContext.Options opt = EmitContext.Options.ConstantScope;
-                       if (this is EnumMember)
-                               opt |= EmitContext.Options.EnumScope;
-
-                       using (ec.Set (opt)) {
-                               value = DoResolveValue (ec);
-                       }
-
-                       in_transit = false;
-                       resolved = true;
-                       return value != null;
+                       this.value = value;
                }
 
-               protected virtual Constant DoResolveValue (EmitContext ec)
-               {
-                       Constant value = initializer.ResolveAsConstant (ec, this);
-                       if (value == null)
-                               return null;
-
-                       Constant c = value.ConvertImplicitly (MemberType);
-                       if (c == null) {
-                               if (TypeManager.IsReferenceType (MemberType))
-                                       Error_ConstantCanBeInitializedWithNullOnly (MemberType, Location, GetSignatureForError ());
-                               else
-                                       value.Error_ValueCannotBeConverted (ec, Location, MemberType, false);
+               public Expression Value {
+                       get {
+                               return value;
+                       }
+                       private set {
+                               this.value = value;
                        }
-
-                       return c;
-               }
-
-               public virtual Constant CreateConstantReference (Location loc)
-               {
-                       if (value == null)
-                               return null;
-
-                       return Constant.CreateConstant (value.Type, value.GetValue(), loc);
                }
-
-               #endregion
        }
 
-       public class ExternalConstant : IConstant
+       class ConstInitializer : ShimExpression
        {
-               FieldInfo fi;
-               object value;
+               bool in_transit;
+               protected readonly FieldBase field;
 
-               public ExternalConstant (FieldInfo fi)
+               public ConstInitializer (FieldBase field, Expression value)
+                       : base (value)
                {
-                       this.fi = fi;
-               }
+                       if (value != null)
+                               this.loc = value.Location;
 
-               private ExternalConstant (FieldInfo fi, object value):
-                       this (fi)
-               {
-                       this.value = value;
+                       this.field = field;
                }
 
-               //
-               // 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)
+               protected override Expression DoResolve (ResolveContext unused)
                {
-                       if (fi is FieldBuilder)
-                               return null;
+                       if (type != null)
+                               return expr;
 
-                       PredefinedAttribute pa = PredefinedAttributes.Get.DecimalConstant;
-                       if (!pa.IsDefined)
-                               return null;
+                       var opt = ResolveContext.Options.ConstantScope;
+                       if (field is EnumMember)
+                               opt |= ResolveContext.Options.EnumScope;
 
-                       object[] attrs = fi.GetCustomAttributes (pa.Type, false);
-                       if (attrs.Length != 1)
-                               return null;
+                       //
+                       // Use a context in which the constant was declared and
+                       // not the one in which is referenced
+                       //
+                       var rc = new ResolveContext (field, opt);
+                       expr = DoResolveInitializer (rc);
+                       type = expr.Type;
 
-                       IConstant ic = new ExternalConstant (fi,
-                               ((System.Runtime.CompilerServices.DecimalConstantAttribute) attrs [0]).Value);
-
-                       return ic;
+                       return expr;
                }
 
-               #region IConstant Members
-
-               public void CheckObsoleteness (Location loc)
+               protected virtual Expression DoResolveInitializer (ResolveContext rc)
                {
-                       ObsoleteAttribute oa = AttributeTester.GetMemberObsoleteAttribute (fi);
-                       if (oa == null) {
-                               return;
+                       if (in_transit) {
+                               field.Compiler.Report.Error (110, field.Location,
+                                       "The evaluation of the constant value for `{0}' involves a circular definition",
+                                       field.GetSignatureForError ());
+
+                               expr = null;
+                       } else {
+                               in_transit = true;
+                               expr = expr.Resolve (rc);
                        }
 
-                       AttributeTester.Report_ObsoleteMessage (oa, TypeManager.GetFullNameSignature (fi), loc);
-               }
+                       in_transit = false;
 
-               public bool ResolveValue ()
-               {
-                       if (value != null)
-                               return true;
+                       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 = c;
+                       }
 
-                       value = fi.GetValue (fi);
-                       return true;
-               }
+                       if (expr == null) {
+                               expr = New.Constantify (field.MemberType);
+                               if (expr == null)
+                                       expr = Constant.CreateConstantFromValue (field.MemberType, null, Location);
+                               expr = expr.Resolve (rc);
+                       }
 
-               public Constant CreateConstantReference (Location loc)
-               {
-                       return Constant.CreateConstant (TypeManager.TypeToCoreType (fi.FieldType), value, loc);
+                       return expr;
                }
-
-               #endregion
        }
-
 }