// 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>
/// </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;
}
}
}
-
-