//
//
-//
-// 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;
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 : FieldBase, IConstant {
+ protected Constant value;
+ bool in_transit;
+ bool resolved;
+ bool define_called;
public const int AllowedModifiers =
Modifiers.NEW |
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)
- {
- 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)
+ if (Parent.PartialContainer.BaseCache == null)
return true;
return base.CheckBase ();
}
/// </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;
- 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;
}
+ // 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);
+
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 {
+ } else {
field_attr |= FieldAttributes.Literal;
}
FieldBuilder = Parent.TypeBuilder.DefineField (Name, MemberType, field_attr);
-
TypeManager.RegisterConstant (FieldBuilder, this);
+ if (ttype == TypeManager.decimal_type)
+ Parent.PartialContainer.RegisterFieldForInitialization (this,
+ new FieldInitializer (FieldBuilder, initializer, Parent));
+
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;
+
+ return true;
+ }
- // 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 (!ResolveValue ())
+ return;
- if (!Convert.ImplicitStandardConversionExists (Convert.ConstantEC, expr, type)){
- Convert.Error_CannotImplicitConversion (loc, expr.Type, type);
- return null;
- }
+ if (FieldBuilder == null)
+ 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)
{
- if (resolved) {
- value = ConstantValue;
- return true;
- }
+ 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)
+ return value != null;
+ 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);
+ resolved = true;
return false;
}
in_transit = true;
- int errors = Report.Errors;
+ // TODO: IResolveContext here
+ EmitContext ec = new EmitContext (
+ this, Parent, Location, null, MemberType, ModFlags);
+ ec.InEnumContext = this is EnumMember;
+ value = DoResolveValue (ec);
+ in_transit = false;
+ resolved = true;
+ return value != null;
+ }
- //
- // We might have cleared Expr ourselves in a recursive definition
- //
- if (Expr == null){
- value = null;
- return false;
+ 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 (!MemberType.IsValueType && MemberType != TypeManager.string_type && !value.IsDefaultValue)
+ Error_ConstantCanBeInitializedWithNullOnly (Location, GetSignatureForError ());
+ else
+ value.Error_ValueCannotBeConverted (null, Location, MemberType, false);
}
- Expr = Expr.Resolve (const_ec);
+ return c;
+ }
- in_transit = false;
+ public virtual Constant CreateConstantReference (Location loc)
+ {
+ if (value == null)
+ return null;
- if (Expr == null) {
- if (errors == Report.Errors)
- Report.Error (150, Location, "A constant value is expected");
- value = null;
- return false;
- }
+ return Constant.CreateConstant (value.Type, value.GetValue(), loc);
+ }
- 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;
- }
+ #endregion
+ }
- if (MemberType != real_expr.Type) {
- ce = ChangeType (Location, ce, MemberType);
- if (ce == null){
- value = null;
- return false;
- }
- Expr = ce;
- }
+ public class ExternalConstant : IConstant
+ {
+ FieldInfo fi;
+ object value;
- 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");
- }
- }
+ public ExternalConstant (FieldInfo fi)
+ {
+ this.fi = fi;
+ }
- 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);
+ 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)
+ {
+ ObsoleteAttribute oa = AttributeTester.GetMemberObsoleteAttribute (fi);
+ if (oa == null) {
+ return;
}
- if (!TypeManager.RegisterFieldValue (FieldBuilder, ConstantValue))
- throw new Exception ("Cannot register const value");
+ AttributeTester.Report_ObsoleteMessage (oa, TypeManager.GetFullNameSignature (fi), loc);
+ }
- value = ConstantValue;
- resolved = true;
+ public bool ResolveValue ()
+ {
+ if (value != null)
+ return true;
+
+ value = fi.GetValue (fi);
return true;
}
-
-
- /// <summary>
- /// Emits the field value by evaluating the expression
- /// </summary>
- public override void Emit ()
+
+ public Constant CreateConstantReference (Location loc)
{
- object value;
- LookupConstantValue (out value);
- base.Emit ();
+ return Constant.CreateConstant (fi.FieldType, value, loc);
}
- }
-}
+ #endregion
+ }
+}