2005-05-31 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / mcs / enum.cs
old mode 100755 (executable)
new mode 100644 (file)
index 003506f..f2ed451
@@ -13,22 +13,110 @@ using System;
 using System.Collections;
 using System.Reflection;
 using System.Reflection.Emit;
+using System.Globalization;
 
 namespace Mono.CSharp {
 
+       class EnumMember: MemberCore {
+               static string[] attribute_targets = new string [] { "field" };
+
+               Enum parent_enum;
+               public FieldBuilder builder;
+               internal readonly Expression Type;
+
+               public EnumMember (Enum parent_enum, Expression expr, string name,
+                                  Location loc, Attributes attrs):
+                       base (null, new MemberName (name), attrs, loc)
+               {
+                       this.parent_enum = parent_enum;
+                       this.ModFlags = parent_enum.ModFlags;
+                       this.Type = expr;
+               }
+
+               public override void ApplyAttributeBuilder (Attribute a, CustomAttributeBuilder cb)
+               {
+                       if (a.Type == TypeManager.marshal_as_attr_type) {
+                               UnmanagedMarshal marshal = a.GetMarshal (this);
+                               if (marshal != null) {
+                                       builder.SetMarshal (marshal);
+                               }
+                               return;
+                       }
+
+                       if (a.Type.IsSubclassOf (TypeManager.security_attr_type)) {
+                               a.Error_InvalidSecurityParent ();
+                               return;
+                       }
+
+                       builder.SetCustomAttribute (cb);
+               }
+
+               public override AttributeTargets AttributeTargets {
+                       get {
+                               return AttributeTargets.Field;
+                       }
+               }
+
+               public void DefineMember (TypeBuilder tb)
+               {
+                       FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static
+                               | FieldAttributes.Literal;
+                       
+                       builder = tb.DefineField (Name, tb, attr);
+               }
+
+               public override bool Define ()
+               {
+                       throw new NotImplementedException ();
+               }
+
+               public void Emit (EmitContext ec)
+               {
+                       if (OptAttributes != null)
+                               OptAttributes.Emit (ec, this); 
+
+                       Emit ();
+               }
+
+               public override string GetSignatureForError()
+               {
+                       return String.Concat (parent_enum.GetSignatureForError (), '.', base.GetSignatureForError ());
+               }
+
+               public override string[] ValidAttributeTargets {
+                       get {
+                               return attribute_targets;
+                       }
+               }
+
+               protected override bool VerifyClsCompliance(DeclSpace ds)
+               {
+                       // Because parent is TypeContainer and we have only DeclSpace parent.
+                       // Parameter replacing is required
+                       return base.VerifyClsCompliance (parent_enum);
+               }
+
+               // There is no base type
+               protected override void VerifyObsoleteAttribute()
+               {
+               }
+
+               public override string DocCommentHeader {
+                       get { return "F:"; }
+               }
+       }
+
        /// <summary>
        ///   Enumeration container
        /// </summary>
        public class Enum : DeclSpace {
-               ArrayList ordered_enums;
+               public ArrayList ordered_enums;
                
                public Expression BaseType;
-               public Attributes  OptAttributes;
                
                public Type UnderlyingType;
 
                Hashtable member_to_location;
-               Hashtable member_to_attributes;
 
                //
                // This is for members that have been defined
@@ -49,13 +137,13 @@ namespace Mono.CSharp {
                        Modifiers.INTERNAL |
                        Modifiers.PRIVATE;
 
-               public Enum (TypeContainer parent, Expression type, int mod_flags, string name, Attributes attrs, Location l)
-                       : base (parent, name, l)
+               public Enum (NamespaceEntry ns, TypeContainer parent, Expression type,
+                            int mod_flags, MemberName name, Attributes attrs, Location l)
+                       : base (ns, parent, name, attrs, l)
                {
                        this.BaseType = type;
                        ModFlags = Modifiers.Check (AllowedModifiers, mod_flags,
                                                    IsTopLevel ? Modifiers.INTERNAL : Modifiers.PRIVATE, l);
-                       OptAttributes = attrs;
 
                        ordered_enums = new ArrayList ();
                        member_to_location = new Hashtable ();
@@ -68,55 +156,22 @@ namespace Mono.CSharp {
                ///   Adds @name to the enumeration space, with @expr
                ///   being its definition.  
                /// </summary>
-               public AdditionResult AddEnumMember (string name, Expression expr, Location loc,
-                                                    Attributes opt_attrs)
+               public void AddEnumMember (string name, Expression expr, Location loc, Attributes opt_attrs, string documentation)
                {
-                       if (defined_names.Contains (name))
-                               return AdditionResult.NameExists;
+                       if (name == "value__") {
+                               Report.Error (76, loc, "An item in an enumeration can't have an identifier `value__'");
+                               return;
+                       }
+
+                       EnumMember em = new EnumMember (this, expr, name, loc, opt_attrs);
+                       em.DocComment = documentation;
+                       if (!AddToContainer (em, name))
+                               return;
 
-                       DefineName (name, expr);
 
+                       // TODO: can be almost deleted
                        ordered_enums.Add (name);
                        member_to_location.Add (name, loc);
-
-                       if (member_to_attributes == null)
-                               member_to_attributes = new Hashtable ();
-
-                       member_to_attributes.Add (name, opt_attrs);
-                       
-                       return AdditionResult.Success;
-               }
-
-               //
-               // This is used by corlib compilation: we map from our
-               // type to a type that is consumable by the DefineField
-               //
-               Type MapToInternalType (Type t)
-               {
-                       if (t == TypeManager.int32_type)
-                               return typeof (int);
-                       if (t == TypeManager.int64_type)
-                               return typeof (long);
-                       if (t == TypeManager.uint32_type)
-                               return typeof (uint);
-                       if (t == TypeManager.uint64_type)
-                               return typeof (ulong);
-                       if (t == TypeManager.float_type)
-                               return typeof (float);
-                       if (t == TypeManager.double_type)
-                               return typeof (double);
-                       if (t == TypeManager.byte_type)
-                               return typeof (byte);
-                       if (t == TypeManager.sbyte_type)
-                               return typeof (sbyte);
-                       if (t == TypeManager.char_type)
-                               return typeof (char);
-                       if (t == TypeManager.short_type)
-                               return typeof (short);
-                       if (t == TypeManager.ushort_type)
-                               return typeof (ushort);
-
-                       throw new Exception ();
                }
                
                public override TypeBuilder DefineType ()
@@ -124,11 +179,18 @@ namespace Mono.CSharp {
                        if (TypeBuilder != null)
                                return TypeBuilder;
 
-                       TypeAttributes attr = Modifiers.TypeAttr (ModFlags, IsTopLevel);
+                       ec = new EmitContext (this, this, Location, null, null, ModFlags, false);
+                       ec.InEnumContext = true;
 
-                       attr |= TypeAttributes.Class | TypeAttributes.Sealed;
+                       if (!(BaseType is TypeLookupExpression)) {
+                               Report.Error (1008, Location,
+                                             "Type byte, sbyte, short, ushort, int, uint, " +
+                                             "long, or ulong expected (got: `{0}')", BaseType);
+                               return null;
+                       }
 
-                       UnderlyingType = ResolveType (BaseType, false, Location);
+                       TypeExpr ute = ResolveBaseTypeExpr (BaseType, false, Location);
+                       UnderlyingType = ute.Type;
 
                        if (UnderlyingType != TypeManager.int32_type &&
                            UnderlyingType != TypeManager.uint32_type &&
@@ -146,16 +208,21 @@ namespace Mono.CSharp {
                        }
 
                        if (IsTopLevel) {
-                               ModuleBuilder builder = CodeGen.ModuleBuilder;
+                               if (TypeManager.NamespaceClash (Name, Location))
+                                       return null;
+                               
+                               ModuleBuilder builder = CodeGen.Module.Builder;
 
-                               TypeBuilder = builder.DefineType (Name, attr, TypeManager.enum_type);
+                               TypeBuilder = builder.DefineType (Name, TypeAttr, TypeManager.enum_type);
                        } else {
                                TypeBuilder builder = Parent.TypeBuilder;
 
                                TypeBuilder = builder.DefineNestedType (
-                                       Basename, attr, TypeManager.enum_type);
+                                       Basename, TypeAttr, TypeManager.enum_type);
                        }
 
+                       ec.ContainerType = TypeBuilder;
+
                        //
                        // Call MapToInternalType for corlib
                        //
@@ -175,7 +242,8 @@ namespace Mono.CSharp {
 
                        if (e is IntConstant || e is UIntConstant || e is LongConstant ||
                            e is ByteConstant || e is SByteConstant || e is ShortConstant ||
-                           e is UShortConstant || e is ULongConstant || e is EnumConstant)
+                           e is UShortConstant || e is ULongConstant || e is EnumConstant ||
+                           e is CharConstant)
                                return true;
                        else
                                return false;
@@ -257,12 +325,138 @@ namespace Mono.CSharp {
                        return;
                }
 
+               /// <summary>
+               ///  Determines if a standard implicit conversion exists from
+               ///  expr_type to target_type
+               /// </summary>
+               public static bool ImplicitConversionExists (Type expr_type, Type target_type)
+               {
+                       expr_type = TypeManager.TypeToCoreType (expr_type);
+
+                       if (expr_type == TypeManager.void_type)
+                               return false;
+                       
+                       if (expr_type == target_type)
+                               return true;
+
+                       // First numeric conversions 
+
+                       if (expr_type == TypeManager.sbyte_type){
+                               //
+                               // From sbyte to short, int, long, float, double.
+                               //
+                               if ((target_type == TypeManager.int32_type) || 
+                                   (target_type == TypeManager.int64_type) ||
+                                   (target_type == TypeManager.double_type) ||
+                                   (target_type == TypeManager.float_type)  ||
+                                   (target_type == TypeManager.short_type) ||
+                                   (target_type == TypeManager.decimal_type))
+                                       return true;
+                               
+                       } else if (expr_type == TypeManager.byte_type){
+                               //
+                               // From byte to short, ushort, int, uint, long, ulong, float, double
+                               // 
+                               if ((target_type == TypeManager.short_type) ||
+                                   (target_type == TypeManager.ushort_type) ||
+                                   (target_type == TypeManager.int32_type) ||
+                                   (target_type == TypeManager.uint32_type) ||
+                                   (target_type == TypeManager.uint64_type) ||
+                                   (target_type == TypeManager.int64_type) ||
+                                   (target_type == TypeManager.float_type) ||
+                                   (target_type == TypeManager.double_type) ||
+                                   (target_type == TypeManager.decimal_type))
+                                       return true;
+       
+                       } else if (expr_type == TypeManager.short_type){
+                               //
+                               // From short to int, long, float, double
+                               // 
+                               if ((target_type == TypeManager.int32_type) ||
+                                   (target_type == TypeManager.int64_type) ||
+                                   (target_type == TypeManager.double_type) ||
+                                   (target_type == TypeManager.float_type) ||
+                                   (target_type == TypeManager.decimal_type))
+                                       return true;
+                                       
+                       } else if (expr_type == TypeManager.ushort_type){
+                               //
+                               // From ushort to int, uint, long, ulong, float, double
+                               //
+                               if ((target_type == TypeManager.uint32_type) ||
+                                   (target_type == TypeManager.uint64_type) ||
+                                   (target_type == TypeManager.int32_type) ||
+                                   (target_type == TypeManager.int64_type) ||
+                                   (target_type == TypeManager.double_type) ||
+                                   (target_type == TypeManager.float_type) ||
+                                   (target_type == TypeManager.decimal_type))
+                                       return true;
+                                   
+                       } else if (expr_type == TypeManager.int32_type){
+                               //
+                               // From int to long, float, double
+                               //
+                               if ((target_type == TypeManager.int64_type) ||
+                                   (target_type == TypeManager.double_type) ||
+                                   (target_type == TypeManager.float_type) ||
+                                   (target_type == TypeManager.decimal_type))
+                                       return true;
+                                       
+                       } else if (expr_type == TypeManager.uint32_type){
+                               //
+                               // From uint to long, ulong, float, double
+                               //
+                               if ((target_type == TypeManager.int64_type) ||
+                                   (target_type == TypeManager.uint64_type) ||
+                                   (target_type == TypeManager.double_type) ||
+                                   (target_type == TypeManager.float_type) ||
+                                   (target_type == TypeManager.decimal_type))
+                                       return true;
+                                       
+                       } else if ((expr_type == TypeManager.uint64_type) ||
+                                  (expr_type == TypeManager.int64_type)) {
+                               //
+                               // From long/ulong to float, double
+                               //
+                               if ((target_type == TypeManager.double_type) ||
+                                   (target_type == TypeManager.float_type) ||
+                                   (target_type == TypeManager.decimal_type))
+                                       return true;
+                                   
+                       } else if (expr_type == TypeManager.char_type){
+                               //
+                               // From char to ushort, int, uint, long, ulong, float, double
+                               // 
+                               if ((target_type == TypeManager.ushort_type) ||
+                                   (target_type == TypeManager.int32_type) ||
+                                   (target_type == TypeManager.uint32_type) ||
+                                   (target_type == TypeManager.uint64_type) ||
+                                   (target_type == TypeManager.int64_type) ||
+                                   (target_type == TypeManager.float_type) ||
+                                   (target_type == TypeManager.double_type) ||
+                                   (target_type == TypeManager.decimal_type))
+                                       return true;
+
+                       } else if (expr_type == TypeManager.float_type){
+                               //
+                               // float to double
+                               //
+                               if (target_type == TypeManager.double_type)
+                                       return true;
+                       }       
+                       
+                       return false;
+               }
+
                /// <summary>
                ///  This is used to lookup the value of an enum member. If the member is undefined,
                ///  it attempts to define it and return its value
                /// </summary>
-               public object LookupEnumValue (EmitContext ec, string name, Location loc)
+               public object LookupEnumValue (string name, Location loc)
                {
+                       if (ec == null)
+                               Report.Error (-1, loc, "Enum.LookupEnumValue () called too soon");
+                       
                        object default_value = null;
                        Constant c = null;
 
@@ -302,7 +496,7 @@ namespace Mono.CSharp {
                                                Location m_loc = (Mono.CSharp.Location)
                                                        member_to_location [n];
                                                in_transit.Add (name, true);
-                                               default_value = LookupEnumValue (ec, n, m_loc);
+                                               default_value = LookupEnumValue (n, m_loc);
                                                in_transit.Remove (name);
                                                if (default_value == null)
                                                        return null;
@@ -312,58 +506,57 @@ namespace Mono.CSharp {
                                }
                                
                        } else {
-                               bool old = ec.InEnumContext;
-                               ec.InEnumContext = true;
                                in_transit.Add (name, true);
-                               val = val.Resolve (ec);
+                               val = val.Resolve (EmitContext);
                                in_transit.Remove (name);
-                               ec.InEnumContext = old;
-                               
+
                                if (val == null)
                                        return null;
 
-                               if (IsValidEnumConstant (val)) {
-                                       c = (Constant) val;
-                                       default_value = c.GetValue ();
-                                       
-                                       if (default_value == null) {
-                                               Error_ConstantValueCannotBeConverted (c, loc);
-                                               return null;
-                                       }
-                                       
-                               } else {
+                               if (!IsValidEnumConstant (val)) {
                                        Report.Error (
                                                1008, loc,
                                                "Type byte, sbyte, short, ushort, int, uint, long, or " +
                                                "ulong expected (have: " + val + ")");
                                        return null;
                                }
+
+                               c = (Constant) val;
+                               default_value = c.GetValue ();
+
+                               if (default_value == null) {
+                                       Error_ConstantValueCannotBeConverted (c, loc);
+                                       return null;
+                               }
+
+                               if (val is EnumConstant){
+                                       Type etype = TypeManager.EnumToUnderlying (c.Type);
+                                       
+                                       if (!ImplicitConversionExists (etype, UnderlyingType)){
+                                               Convert.Error_CannotImplicitConversion (
+                                                       loc, c.Type, UnderlyingType);
+                                               return null;
+                                       }
+                               }
                        }
 
-                       FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static
-                                       | FieldAttributes.Literal;
-                       
-                       FieldBuilder fb = TypeBuilder.DefineField (name, UnderlyingType, attr);
+                       EnumMember em = (EnumMember) defined_names [name];
+                       em.DefineMember (TypeBuilder);
 
-                       try {
-                               default_value = TypeManager.ChangeType (default_value, UnderlyingType);
-                       } catch {
+                       bool fail;
+                       default_value = TypeManager.ChangeType (default_value, UnderlyingType, out fail);
+                       if (fail){
                                Error_ConstantValueCannotBeConverted (c, loc);
                                return null;
                        }
 
-                       fb.SetConstant (default_value);
-                       field_builders.Add (fb);
+                       em.builder.SetConstant (default_value);
+                       field_builders.Add (em.builder);
                        member_to_value [name] = default_value;
 
-                       if (!TypeManager.RegisterFieldValue (fb, default_value))
+                       if (!TypeManager.RegisterFieldValue (em.builder, default_value))
                                return null;
 
-                       //
-                       // Now apply attributes
-                       //
-                       Attribute.ApplyAttributes (ec, fb, fb, (Attributes) member_to_attributes [name], loc); 
-                       
                        return default_value;
                }
 
@@ -372,23 +565,20 @@ namespace Mono.CSharp {
                        return true;
                }
                
-               public override bool Define (TypeContainer parent)
+               public override bool Define ()
                {
                        //
                        // If there was an error during DefineEnum, return
                        //
                        if (TypeBuilder == null)
                                return false;
-                       
-                       EmitContext ec = new EmitContext (parent, this, Location, null,
-                                                         UnderlyingType, ModFlags, false);
+
+                       if (ec == null)
+                               throw new InternalErrorException ("Enum.Define () called too soon");
                        
                        object default_value = 0;
                        
-                       FieldAttributes attr = FieldAttributes.Public | FieldAttributes.Static
-                                            | FieldAttributes.Literal;
-
-                       
+               
                        foreach (string name in ordered_enums) {
                                //
                                // Have we already been defined, thanks to some cross-referencing ?
@@ -399,24 +589,30 @@ namespace Mono.CSharp {
                                Location loc = (Mono.CSharp.Location) member_to_location [name];
 
                                if (this [name] != null) {
-                                       default_value = LookupEnumValue (ec, name, loc);
+                                       default_value = LookupEnumValue (name, loc);
 
                                        if (default_value == null)
                                                return true;
-
                                } else {
-                                       FieldBuilder fb = TypeBuilder.DefineField (
-                                               name, UnderlyingType, attr);
+                                       if (name == "value__"){
+                                               Report.Error (76, loc, "The name `value__' is reserved for enumerations");
+                                               return false;
+                                       }
+
+                                       EnumMember em = (EnumMember) defined_names [name];
+
+                                       em.DefineMember (TypeBuilder);
+                                       FieldBuilder fb = em.builder;
                                        
                                        if (default_value == null) {
                                           Report.Error (543, loc, "Enumerator value for '" + name + "' is too large to " +
                                                              "fit in its type");
                                                return false;
                                        }
-                                       
-                                       try {
-                                               default_value = TypeManager.ChangeType (default_value, UnderlyingType);
-                                       } catch {
+
+                                       bool fail;
+                                       default_value = TypeManager.ChangeType (default_value, UnderlyingType, out fail);
+                                       if (fail){
                                                Error_ConstantValueCannotBeConverted (default_value, loc);
                                                return false;
                                        }
@@ -427,17 +623,54 @@ namespace Mono.CSharp {
                                        
                                        if (!TypeManager.RegisterFieldValue (fb, default_value))
                                                return false;
-
-                                       //
-                                       // Apply attributes on the enum member
-                                       //
-                                       Attribute.ApplyAttributes (ec, fb, fb, (Attributes) member_to_attributes [name], loc);
                                }
 
                                default_value = GetNextDefaultValue (default_value);
                        }
-                       
-                       Attribute.ApplyAttributes (ec, TypeBuilder, this, OptAttributes, Location);
+
+                       return true;
+               }
+
+               public override void Emit ()
+               {
+                       if (OptAttributes != null) {
+                               OptAttributes.Emit (ec, this);
+                       }
+
+                       foreach (EnumMember em in defined_names.Values) {
+                               em.Emit (ec);
+                       }
+
+                       base.Emit ();
+               }
+               
+               void VerifyClsName ()
+               {
+                       Hashtable ht = new Hashtable ();
+                       foreach (string name in ordered_enums) {
+                               string locase = name.ToLower (System.Globalization.CultureInfo.InvariantCulture);
+                               if (!ht.Contains (locase)) {
+                                       ht.Add (locase, defined_names [name]);
+                                       continue;
+                               }
+                               MemberCore conflict = (MemberCore)ht [locase];
+                               Report.SymbolRelatedToPreviousError (conflict);
+                               conflict = GetDefinition (name);
+                               Report.Error (3005, conflict.Location, "Identifier '{0}' differing only in case is not CLS-compliant", conflict.GetSignatureForError ());
+                       }
+               }
+
+               protected override bool VerifyClsCompliance (DeclSpace ds)
+               {
+                       if (!base.VerifyClsCompliance (ds))
+                               return false;
+
+                       VerifyClsName ();
+
+                       if (!AttributeTester.IsClsCompliant (UnderlyingType)) {
+                               Report.Error (3009, Location, "'{0}': base type '{1}' is not CLS-compliant", GetSignatureForError (), TypeManager.CSharpName (UnderlyingType));
+                       }
 
                        return true;
                }
@@ -451,6 +684,10 @@ namespace Mono.CSharp {
                        ArrayList members = new ArrayList ();
 
                        if ((mt & MemberTypes.Field) != 0) {
+                               if (criteria is string && member_to_value [criteria] == null) {
+                                       LookupEnumValue ((string) criteria, Location.Null);
+                               }
+                               
                                foreach (FieldBuilder fb in field_builders)
                                        if (filter (fb, criteria) == true)
                                                members.Add (fb);
@@ -474,8 +711,44 @@ namespace Mono.CSharp {
                // indexer
                public Expression this [string name] {
                        get {
-                               return (Expression) defined_names [name];
+                               return ((EnumMember) defined_names [name]).Type;
                        }
                }
+
+               public override AttributeTargets AttributeTargets {
+                       get {
+                               return AttributeTargets.Enum;
+                       }
+               }
+
+               protected override TypeAttributes TypeAttr {
+                       get {
+                               return Modifiers.TypeAttr (ModFlags, IsTopLevel) |
+                               TypeAttributes.Class | TypeAttributes.Sealed |
+                               base.TypeAttr;
+                       }
+               }
+
+
+               protected override void VerifyObsoleteAttribute()
+               {
+                       // UnderlyingType is never obsolete
+               }
+
+               //
+               // Generates xml doc comments (if any), and if required,
+               // handle warning report.
+               //
+               internal override void GenerateDocComment (DeclSpace ds)
+               {
+                       DocUtil.GenerateEnumDocComment (this, ds);
+               }
+
+               //
+               //   Represents header string for documentation comment.
+               //
+               public override string DocCommentHeader {
+                       get { return "T:"; }
+               }
        }
 }