Clean up predefined members caching
[mono.git] / mcs / mcs / membercache.cs
index b5a6027d86fcc8d352e1eb6b0d90b0751a94f6bf..6f6970320e6df0e49c4e114e20e9e0b281363c82 100644 (file)
 //
 
 using System;
-using System.Text;
 using System.Collections.Generic;
-using System.Globalization;
-using System.Reflection.Emit;
-using System.Reflection;
-using System.Linq;
 
 namespace Mono.CSharp {
 
@@ -43,11 +38,12 @@ namespace Mono.CSharp {
                ArrayType = 1 << 19,
                PointerType = 1 << 20,
                InternalCompilerType = 1 << 21,
+               MissingType = 1 << 22,
+               Void = 1 << 23,
 
                NestedMask = Class | Struct | Delegate | Enum | Interface,
                GenericMask = Method | Class | Struct | Delegate | Interface,
-               MaskType = Constructor | Event | Field | Method | Property | Indexer | Operator | Destructor | NestedMask,
-               All = MaskType
+               MaskType = Constructor | Event | Field | Method | Property | Indexer | Operator | Destructor | NestedMask
        }
 
        [Flags]
@@ -61,7 +57,10 @@ namespace Mono.CSharp {
                // Exclude static
                InstanceOnly = 1 << 2,
 
-               NoAccessors = 1 << 3
+               NoAccessors = 1 << 3,
+
+               // Member has to be override
+               OverrideOnly = 1 << 4
        }
 
        public struct MemberFilter : IEquatable<MemberSpec>
@@ -70,8 +69,7 @@ namespace Mono.CSharp {
                public readonly MemberKind Kind;
                public readonly AParametersCollection Parameters;
                public readonly TypeSpec MemberType;
-
-               int arity; // -1 to ignore the check
+               public readonly int Arity; // -1 to ignore the check
 
                private MemberFilter (string name, MemberKind kind)
                {
@@ -79,7 +77,7 @@ namespace Mono.CSharp {
                        Kind = kind;
                        Parameters = null;
                        MemberType = null;
-                       arity = -1;
+                       Arity = -1;
                }
 
                public MemberFilter (MethodSpec m)
@@ -88,7 +86,7 @@ namespace Mono.CSharp {
                        Kind = MemberKind.Method;
                        Parameters = m.Parameters;
                        MemberType = m.ReturnType;
-                       arity = m.Arity;
+                       Arity = m.Arity;
                }
 
                public MemberFilter (string name, int arity, MemberKind kind, AParametersCollection param, TypeSpec type)
@@ -97,12 +95,12 @@ namespace Mono.CSharp {
                        Kind = kind;
                        Parameters = param;
                        MemberType = type;
-                       this.arity = arity;
+                       this.Arity = arity;
                }
 
                public static MemberFilter Constructor (AParametersCollection param)
                {
-                       return new MemberFilter (System.Reflection.ConstructorInfo.ConstructorName, 0, MemberKind.Constructor, param, null);
+                       return new MemberFilter (Mono.CSharp.Constructor.ConstructorName, 0, MemberKind.Constructor, param, null);
                }
 
                public static MemberFilter Property (string name, TypeSpec type)
@@ -130,7 +128,7 @@ namespace Mono.CSharp {
                                return false;
 
                        // Check arity when not disabled
-                       if (arity >= 0 && arity != other.Arity)
+                       if (Arity >= 0 && Arity != other.Arity)
                                return false;
 
                        if (Parameters != null) {
@@ -170,6 +168,7 @@ namespace Mono.CSharp {
        //
        public class MemberCache
        {
+               [Flags]
                enum StateFlags
                {
                        HasConversionOperator = 1 << 1,
@@ -209,6 +208,40 @@ namespace Mono.CSharp {
                {
                }
 
+               //
+               // For cases where we need to union cache members
+               //
+               public void AddBaseType (TypeSpec baseType)
+               {
+                       var cache = baseType.MemberCache;
+
+                       IList<MemberSpec> list;
+                       foreach (var entry in cache.member_hash) {
+                               if (!member_hash.TryGetValue (entry.Key, out list)) {
+                                       if (entry.Value.Count == 1) {
+                                               list = entry.Value;
+                                       } else {
+                                               list = new List<MemberSpec> (entry.Value);
+                                       }
+
+                                       member_hash.Add (entry.Key, list);
+                                       continue;
+                               }
+
+                               foreach (var ce in entry.Value) {
+                                       if (list.Contains (ce))
+                                               continue;
+
+                                       if (list is MemberSpec[]) {
+                                               list = new List<MemberSpec> () { list [0] };
+                                               member_hash[entry.Key] = list;
+                                       }
+
+                                       list.Add (ce);
+                               }
+                       }
+               }
+
                //
                // Member-cache does not contain base members but it does
                // contain all base interface members, so the Lookup code
@@ -232,9 +265,6 @@ namespace Mono.CSharp {
                                }
 
                                foreach (var ce in entry.Value) {
-                                       if (ce.DeclaringType != iface)
-                                               break;
-
                                        if (list.Contains (ce))
                                                continue;
 
@@ -242,6 +272,12 @@ namespace Mono.CSharp {
                                                member_hash[entry.Key] = list;
                                }
                        }
+
+                       // Add also all base interfaces
+                       if (iface.Interfaces != null) {
+                               foreach (var base_iface in iface.Interfaces)
+                                       AddInterface (base_iface);
+                       }
                }
 
                public void AddMember (InterfaceMemberBase imb, string exlicitName, MemberSpec ms)
@@ -249,7 +285,7 @@ namespace Mono.CSharp {
                        // Explicit names cannot be looked-up but can be used for
                        // collision checking (no name mangling needed)
                        if (imb.IsExplicitImpl)
-                               AddMember (exlicitName, ms);
+                               AddMember (exlicitName, ms, false);
                        else
                                AddMember (ms);
                }
@@ -259,23 +295,31 @@ namespace Mono.CSharp {
                //
                public void AddMember (MemberSpec ms)
                {
-                       AddMember (GetLookupName (ms), ms);
+                       AddMember (GetLookupName (ms), ms, false);
                }
 
-               void AddMember (string name, MemberSpec member)
+               void AddMember (string name, MemberSpec member, bool removeHiddenMembers)
                {
                        if (member.Kind == MemberKind.Operator) {
                                var dt = member.DeclaringType;
-                               if (dt == TypeManager.string_type || dt == TypeManager.delegate_type || dt == TypeManager.multicast_delegate_type) {
+                               switch (dt.BuildinType) {
+                               case BuildinTypeSpec.Type.String:
+                               case BuildinTypeSpec.Type.Delegate:
+                               case BuildinTypeSpec.Type.MulticastDelegate:
                                        // Some core types have user operators but they cannot be used as normal
                                        // user operators as they are predefined and therefore having different
                                        // rules (e.g. binary operators) by not setting the flag we hide them for
                                        // user conversions
                                        // TODO: Should I do this for all core types ?
-                               } else if (name == Operator.GetMetadataName (Operator.OpType.Implicit) || name == Operator.GetMetadataName (Operator.OpType.Explicit)) {
-                                       state |= StateFlags.HasConversionOperator;
-                               } else {
-                                       state |= StateFlags.HasUserOperator;
+                                       break;
+                               default:
+                                       if (name == Operator.GetMetadataName (Operator.OpType.Implicit) || name == Operator.GetMetadataName (Operator.OpType.Explicit)) {
+                                               state |= StateFlags.HasConversionOperator;
+                                       } else {
+                                               state |= StateFlags.HasUserOperator;
+                                       }
+
+                                       break;
                                }
                        }
 
@@ -285,11 +329,11 @@ namespace Mono.CSharp {
                                return;
                        }
 
-                       if (member.DeclaringType.IsInterface) {
+                       if (removeHiddenMembers && member.DeclaringType.IsInterface) {
                                if (AddInterfaceMember (member, ref list))
                                        member_hash[name] = list;
                        } else {
-                               if (list is MemberSpec[]) {
+                               if (list.Count == 1) {
                                        list = new List<MemberSpec> () { list[0] };
                                        member_hash[name] = list;
                                }
@@ -298,6 +342,11 @@ namespace Mono.CSharp {
                        }
                }
 
+               public void AddMemberImported (MemberSpec ms)
+               {
+                       AddMember (GetLookupName (ms), ms, true);
+               }
+
                //
                // Ignores any base interface member which can be hidden
                // by this interface
@@ -324,8 +373,8 @@ namespace Mono.CSharp {
                                                continue;
                                }
 
-                               if (member.DeclaringType.ImplementsInterface (entry.DeclaringType)) {
-                                       if (existing is MemberSpec[]) {
+                               if (member.DeclaringType.ImplementsInterface (entry.DeclaringType, false)) {
+                                       if (existing.Count == 1) {
                                                existing = new MemberSpec[] { member };
                                                return true;
                                        }
@@ -335,11 +384,11 @@ namespace Mono.CSharp {
                                }
 
                                if ((entry.DeclaringType == member.DeclaringType && entry.IsAccessor == member.IsAccessor) ||
-                                       entry.DeclaringType.ImplementsInterface (member.DeclaringType))
+                                       entry.DeclaringType.ImplementsInterface (member.DeclaringType, false))
                                        return false;
                        }
 
-                       if (existing is MemberSpec[]) {
+                       if (existing.Count == 1) {
                                existing = new List<MemberSpec> () { existing[0], member };
                                return true;
                        }
@@ -363,6 +412,9 @@ namespace Mono.CSharp {
                                                if ((restrictions & BindingRestriction.NoAccessors) != 0 && entry.IsAccessor)
                                                        continue;
 
+                                               if ((restrictions & BindingRestriction.OverrideOnly) != 0 && (entry.Modifiers & Modifiers.OVERRIDE) == 0)
+                                                       continue;
+
                                                if (!filter.Equals (entry))
                                                        continue;
 
@@ -446,7 +498,7 @@ namespace Mono.CSharp {
                //
                // Looks for extension methods with defined name and extension type
                //
-               public List<MethodSpec> FindExtensionMethods (TypeSpec invocationType, TypeSpec extensionType, string name, int arity)
+               public List<MethodSpec> FindExtensionMethods (TypeContainer invocationType, TypeSpec extensionType, string name, int arity)
                {
                        IList<MemberSpec> entries;
                        if (!member_hash.TryGetValue (name, out entries))
@@ -461,12 +513,10 @@ namespace Mono.CSharp {
                                if (!ms.IsExtensionMethod)
                                        continue;
 
-                               if (!ms.IsAccessible (invocationType))
+                               if (!ms.IsAccessible (invocationType.CurrentType))
                                        continue;
 
-                               // TODO: CodeGen.Assembly.Builder
-                               if ((ms.DeclaringType.Modifiers & Modifiers.INTERNAL) != 0 &&
-                                       !TypeManager.IsThisOrFriendAssembly (CodeGen.Assembly.Builder, ms.Assembly))
+                               if ((ms.DeclaringType.Modifiers & Modifiers.INTERNAL) != 0 && !ms.DeclaringType.MemberDefinition.IsInternalAsPublic (invocationType.DeclaringAssembly))
                                        continue;
 
                                if (candidates == null)
@@ -485,9 +535,14 @@ namespace Mono.CSharp {
                {
                        bestCandidate = null;
                        var container = member.Parent.PartialContainer.Definition;
-                       if (!container.IsInterface)
+                       if (!container.IsInterface) {
                                container = container.BaseType;
 
+                               // It can happen for a user definition of System.Object
+                               if (container == null)
+                                       return null;
+                       }
+
                        string name = GetLookupName (member);
                        IList<MemberSpec> applicable;
                        var member_param = member is IParametersMember ? ((IParametersMember) member).Parameters : null;
@@ -503,43 +558,38 @@ namespace Mono.CSharp {
                                                if ((entry.Modifiers & Modifiers.PRIVATE) != 0)
                                                        continue;
 
-                                               if ((entry.Modifiers & Modifiers.AccessibilityMask) == Modifiers.INTERNAL) {
-                                                       if (!TypeManager.IsThisOrFriendAssembly (member.Assembly, entry.Assembly))
-                                                               continue;
-                                               }
+                                               if ((entry.Modifiers & Modifiers.AccessibilityMask) == Modifiers.INTERNAL &&
+                                                       !entry.DeclaringType.MemberDefinition.IsInternalAsPublic (member.Module.DeclaringAssembly))
+                                                       continue;
 
                                                //
-                                               // Is the member of the correct type ?
-                                               // Destructors are ignored as they cannot be overridden by user
+                                               // Isn't the member of same kind ?
+                                               //
                                                if ((entry.Kind & ~MemberKind.Destructor & mkind & MemberKind.MaskType) == 0) {
-                                                       if ((entry.Kind & MemberKind.Destructor) == 0 && (member_param == null || !(entry is IParametersMember))) {
-                                                               bestCandidate = entry;
-                                                               return null;
-                                                       }
+                                                       // Destructors are ignored as they cannot be overridden by user
+                                                       if ((entry.Kind & MemberKind.Destructor) != 0)
+                                                               continue;
 
-                                                       continue;
-                                               }
+                                                       // A method with different arity does not hide base member
+                                                       if (mkind != MemberKind.Method && member.MemberName.Arity != entry.Arity)
+                                                               continue;
 
-                                               if (entry.Kind != mkind) {
-                                                       if (bestCandidate == null)
-                                                               bestCandidate = entry;
+                                                       bestCandidate = entry;
+                                                       return null;
+                                               }
 
+                                               //
+                                               // Same kind of different arity is valid
+                                               //
+                                               if (member.MemberName.Arity != entry.Arity) {
                                                        continue;
                                                }
 
-                                               if (member_param != null) {
-                                                       // Check arity match
-                                                       int arity = member.MemberName.Arity;
-                                                       if (arity != entry.Arity)
-                                                               continue;
-
-                                                       var pm = entry as IParametersMember;
-                                                       if (pm == null)
-                                                               continue;
-
+                                               if ((entry.Kind & mkind & (MemberKind.Method | MemberKind.Indexer)) != 0) {
                                                        if (entry.IsAccessor != member is AbstractPropertyEventMethod)
                                                                continue;
 
+                                                       var pm = entry as IParametersMember;
                                                        if (!TypeSpecComparer.Override.IsEqual (pm.Parameters, member_param))
                                                                continue;
                                                }
@@ -613,6 +663,10 @@ namespace Mono.CSharp {
                                return MemberKind.Interface;
                        if (member is EventProperty)
                                return MemberKind.Event;
+                       if (member is Delegate)
+                               return MemberKind.Delegate;
+                       if (member is Enum)
+                               return MemberKind.Enum;
 
                        throw new NotImplementedException (member.GetType ().ToString ());
                }
@@ -769,9 +823,9 @@ namespace Mono.CSharp {
 
                        if (ms.Kind == MemberKind.Constructor) {
                                if (ms.IsStatic)
-                                       return ConstructorInfo.TypeConstructorName;
+                                       return Constructor.TypeConstructorName;
 
-                               return ConstructorInfo.ConstructorName;
+                               return Constructor.ConstructorName;
                        }
 
                        return ms.Name;
@@ -783,7 +837,7 @@ namespace Mono.CSharp {
                                return IndexerNameAlias;
 
                        if (mc is Constructor)
-                               return ConstructorInfo.ConstructorName;
+                               return Constructor.ConstructorName;
 
                        return mc.MemberName.Name;
                }
@@ -803,29 +857,37 @@ namespace Mono.CSharp {
                                         (mc.state & StateFlags.HasUserOperator) != 0) {
 
                                        if (mc.member_hash.TryGetValue (Operator.GetMetadataName (op), out applicable)) {
-                                               int match_count = 0;
-                                               for (int i = 0; i < applicable.Count; ++i) {
-                                                       if (applicable[i].Kind == MemberKind.Operator) {
-                                                               ++match_count;
-                                                               continue;
+                                               int i;
+                                               for (i = 0; i < applicable.Count; ++i) {
+                                                       if (applicable[i].Kind != MemberKind.Operator) {
+                                                               break;
                                                        }
+                                               }
 
-                                                       // Handles very rare case where a method exists with same name as operator (op_xxxx)
-                                                       if (found == null) {
-                                                               found = new List<MemberSpec> ();
-                                                               found.Add (applicable [i]);
-                                                       } else {
-                                                               var prev = found as List<MemberSpec>;
-                                                               if (prev == null) {
-                                                                       prev = new List<MemberSpec> (found.Count + 1);
-                                                                       prev.AddRange (found);
+                                               //
+                                               // Handles very rare case where a method with same name as operator (op_xxxx) exists
+                                               // and we have to resize the applicable list
+                                               //
+                                               if (i != applicable.Count) {
+                                                       for (i = 0; i < applicable.Count; ++i) {
+                                                               if (applicable[i].Kind != MemberKind.Operator) {
+                                                                       continue;
                                                                }
 
-                                                               prev.Add (applicable[i]);
-                                                       }
-                                               }
+                                                               if (found == null) {
+                                                                       found = new List<MemberSpec> ();
+                                                                       found.Add (applicable[i]);
+                                                               } else {
+                                                                       var prev = found as List<MemberSpec>;
+                                                                       if (prev == null) {
+                                                                               prev = new List<MemberSpec> (found.Count + 1);
+                                                                               prev.AddRange (found);
+                                                                       }
 
-                                               if (match_count > 0 && match_count == applicable.Count) {
+                                                                       prev.Add (applicable[i]);
+                                                               }
+                                                       }
+                                               } else {
                                                        if (found == null) {
                                                                found = applicable;
                                                        } else {
@@ -885,7 +947,7 @@ namespace Mono.CSharp {
                public void InflateMembers (MemberCache cacheToInflate, TypeSpec inflatedType, TypeParameterInflator inflator)
                {
                        var inflated_member_hash = cacheToInflate.member_hash;
-                       Dictionary<MethodSpec, MethodSpec> accessor_relation = null;
+                       Dictionary<MemberSpec, MethodSpec> accessor_relation = null;
                        List<MemberSpec> accessor_members = null;
 
                        // Copy member specific flags when all members were added
@@ -921,10 +983,10 @@ namespace Mono.CSharp {
 
                                        if (member.DeclaringType != inflatedType) {
                                                //
-                                               // Don't inflate non generic interface members
+                                               // Don't inflate top-level non-generic interface members
                                                // merged into generic interface
                                                //
-                                               if (!member.DeclaringType.IsGeneric) {
+                                               if (!member.DeclaringType.IsGeneric && !member.DeclaringType.IsNested) {
                                                        inflated_members [i] = member;
                                                        continue;
                                                }
@@ -961,8 +1023,8 @@ namespace Mono.CSharp {
 
                                        if (member.IsAccessor) {
                                                if (accessor_relation == null)
-                                                       accessor_relation = new Dictionary<MethodSpec, MethodSpec> ();
-                                               accessor_relation.Add ((MethodSpec) member, (MethodSpec) inflated);
+                                                       accessor_relation = new Dictionary<MemberSpec, MethodSpec> ();
+                                               accessor_relation.Add (member, (MethodSpec) inflated);
                                        }
                                }
                        }
@@ -986,6 +1048,53 @@ namespace Mono.CSharp {
                        }
                }
 
+               //
+               // Removes hidden base members of an interface. For compiled interfaces we cannot
+               // do name filtering during Add (as we do for import) because we need all base
+               // names to be valid during type definition.
+               // Add replaces hidden base member with current one which means any name collision
+               // (CS0108) of non-first name would be unnoticed because the name was replaced
+               // with the one from compiled type
+               //
+               public void RemoveHiddenMembers (TypeSpec container)
+               {
+                       foreach (var entry in member_hash) {
+                               var values = entry.Value;
+
+                               int container_members_start_at = 0;
+                               while (values[container_members_start_at].DeclaringType != container && ++container_members_start_at < entry.Value.Count);
+
+                               if (container_members_start_at == 0 || container_members_start_at == values.Count)
+                                       continue;
+
+                               for (int i = 0; i < container_members_start_at; ++i) {
+                                       var member = values[i];
+
+                                       if (!container.ImplementsInterface (member.DeclaringType, false))
+                                               continue;
+
+                                       var member_param = member is IParametersMember ? ((IParametersMember) member).Parameters : ParametersCompiled.EmptyReadOnlyParameters;
+
+                                       for (int ii = container_members_start_at; ii < values.Count; ++ii) {
+                                               var container_entry = values[ii];
+
+                                               if (container_entry.Arity != member.Arity)
+                                                       continue;
+
+                                               if (container_entry is IParametersMember) {
+                                                       if (!TypeSpecComparer.Override.IsEqual (((IParametersMember) container_entry).Parameters, member_param))
+                                                               continue;
+                                               }
+
+                                               values.RemoveAt (i);
+                                               --container_members_start_at;
+                                               --ii;
+                                               --i;
+                                       }
+                               }
+                       }
+               }
+
                //
                // Checks all appropriate container members for CLS compliance
                //
@@ -1014,7 +1123,7 @@ namespace Mono.CSharp {
                                        if ((name_entry.Kind & MemberKind.MaskType) == 0)
                                                continue;
 
-                                       if (name_entry.MemberDefinition.IsNotCLSCompliant ())
+                                       if (name_entry.MemberDefinition.CLSAttributeValue == false)
                                            continue;
 
                                        IParametersMember p_a = name_entry as IParametersMember;