2 // membercache.cs: A container for all member lookups
4 // Author: Miguel de Icaza (miguel@gnu.org)
5 // Marek Safar (marek.safar@gmail.com)
7 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 // Copyright 2001 Ximian, Inc (http://www.ximian.com)
10 // Copyright 2004-2010 Novell, Inc
11 // Copyright 2011 Xamarin Inc
16 using System
.Collections
.Generic
;
18 namespace Mono
.CSharp
{
21 public enum MemberKind
37 TypeParameter
= 1 << 16,
40 PointerType
= 1 << 20,
41 InternalCompilerType
= 1 << 21,
42 MissingType
= 1 << 22,
46 NestedMask
= Class
| Struct
| Delegate
| Enum
| Interface
,
47 GenericMask
= Method
| Class
| Struct
| Delegate
| Interface
,
48 MaskType
= Constructor
| Event
| Field
| Method
| Property
| Indexer
| Operator
| Destructor
| NestedMask
52 public enum BindingRestriction
56 // Inspect only queried type members
57 DeclaredOnly
= 1 << 1,
60 InstanceOnly
= 1 << 2,
64 // Member has to be override
68 public struct MemberFilter
: IEquatable
<MemberSpec
>
70 public readonly string Name
;
71 public readonly MemberKind Kind
;
72 public readonly AParametersCollection Parameters
;
73 public readonly TypeSpec MemberType
;
74 public readonly int Arity
; // -1 to ignore the check
76 private MemberFilter (string name
, MemberKind kind
)
85 public MemberFilter (MethodSpec m
)
88 Kind
= MemberKind
.Method
;
89 Parameters
= m
.Parameters
;
90 MemberType
= m
.ReturnType
;
94 public MemberFilter (string name
, int arity
, MemberKind kind
, AParametersCollection param
, TypeSpec type
)
103 public static MemberFilter
Constructor (AParametersCollection param
)
105 return new MemberFilter (Mono
.CSharp
.Constructor
.ConstructorName
, 0, MemberKind
.Constructor
, param
, null);
108 public static MemberFilter
Property (string name
, TypeSpec type
)
110 return new MemberFilter (name
, 0, MemberKind
.Property
, null, type
);
113 public static MemberFilter
Field (string name
, TypeSpec type
)
115 return new MemberFilter (name
, 0, MemberKind
.Field
, null, type
);
118 public static MemberFilter
Method (string name
, int arity
, AParametersCollection param
, TypeSpec type
)
120 return new MemberFilter (name
, arity
, MemberKind
.Method
, param
, type
);
123 #region IEquatable<MemberSpec> Members
125 public bool Equals (MemberSpec other
)
127 // Is the member of the correct type ?
128 // TODO: Isn't this redundant ?
129 if ((other
.Kind
& Kind
& MemberKind
.MaskType
) == 0)
132 // Check arity when not disabled
133 if (Arity
>= 0 && Arity
!= other
.Arity
)
136 if (Parameters
!= null) {
137 if (other
is IParametersMember
) {
138 var other_param
= ((IParametersMember
) other
).Parameters
;
139 if (!TypeSpecComparer
.Override
.IsEqual (Parameters
, other_param
))
146 if (MemberType
!= null) {
147 if (other
is IInterfaceMemberSpec
) {
148 var other_type
= ((IInterfaceMemberSpec
) other
).MemberType
;
149 if (!TypeSpecComparer
.Override
.IsEqual (other_type
, MemberType
))
163 // The MemberCache is the main members container used by compiler. It contains
164 // all members imported or defined during compilation using on demand filling
165 // process. Inflated containers are also using MemberCache to make inflated
166 // members look like normal definition.
168 // All of the methods are performance and memory sensitive as the MemberCache
169 // is the underlying engine of all member based operations.
171 public class MemberCache
176 HasConversionOperator
= 1 << 1,
177 HasUserOperator
= 1 << 2
180 readonly Dictionary
<string, IList
<MemberSpec
>> member_hash
;
181 Dictionary
<string, MemberSpec
[]> locase_members
;
182 IList
<MethodSpec
> missing_abstract
;
183 StateFlags state
; // TODO: Move to TypeSpec or ITypeDefinition
185 public static readonly string IndexerNameAlias
= "<this>";
187 public static readonly MemberCache Empty
= new MemberCache (0);
189 public MemberCache ()
194 public MemberCache (int capacity
)
196 member_hash
= new Dictionary
<string, IList
<MemberSpec
>> (capacity
);
199 public MemberCache (MemberCache cache
)
200 : this (cache
.member_hash
.Count
)
202 this.state
= cache
.state
;
206 // Creates a new MemberCache for the given `container'.
208 public MemberCache (TypeContainer container
)
209 : this () // TODO: Optimize the size
214 // For cases where we need to union cache members
216 public void AddBaseType (TypeSpec baseType
)
218 var cache
= baseType
.MemberCache
;
220 IList
<MemberSpec
> list
;
221 foreach (var entry
in cache
.member_hash
) {
222 if (!member_hash
.TryGetValue (entry
.Key
, out list
)) {
223 if (entry
.Value
.Count
== 1) {
226 list
= new List
<MemberSpec
> (entry
.Value
);
229 member_hash
.Add (entry
.Key
, list
);
233 foreach (var ce
in entry
.Value
) {
234 if (list
.Contains (ce
))
237 if (list
is MemberSpec
[]) {
238 list
= new List
<MemberSpec
> () { list [0] }
;
239 member_hash
[entry
.Key
] = list
;
248 // Member-cache does not contain base members but it does
249 // contain all base interface members, so the Lookup code
250 // can use simple inheritance rules.
252 public void AddInterface (TypeSpec iface
)
254 var cache
= iface
.MemberCache
;
256 IList
<MemberSpec
> list
;
257 foreach (var entry
in cache
.member_hash
) {
258 if (!member_hash
.TryGetValue (entry
.Key
, out list
)) {
259 if (entry
.Value
.Count
== 1) {
262 list
= new List
<MemberSpec
> (entry
.Value
);
265 member_hash
.Add (entry
.Key
, list
);
269 foreach (var ce
in entry
.Value
) {
270 if (list
.Contains (ce
))
273 if (AddInterfaceMember (ce
, ref list
))
274 member_hash
[entry
.Key
] = list
;
278 // Add also all base interfaces
279 if (iface
.Interfaces
!= null) {
280 foreach (var base_iface
in iface
.Interfaces
)
281 AddInterface (base_iface
);
285 public void AddMember (InterfaceMemberBase imb
, string exlicitName
, MemberSpec ms
)
287 // Explicit names cannot be looked-up but can be used for
288 // collision checking (no name mangling needed)
289 if (imb
.IsExplicitImpl
)
290 AddMember (exlicitName
, ms
, false);
296 // Add non-explicit member to member cache
298 public void AddMember (MemberSpec ms
)
300 AddMember (GetLookupName (ms
), ms
, false);
303 void AddMember (string name
, MemberSpec member
, bool removeHiddenMembers
)
305 if (member
.Kind
== MemberKind
.Operator
) {
306 var dt
= member
.DeclaringType
;
309 // Some core types have user operators but they cannot be used like normal
310 // user operators as they are predefined and therefore having different
311 // rules (e.g. binary operators) by not setting the flag we hide them for
314 if (!BuiltinTypeSpec
.IsPrimitiveType (dt
)) {
315 switch (dt
.BuiltinType
) {
316 case BuiltinTypeSpec
.Type
.String
:
317 case BuiltinTypeSpec
.Type
.Delegate
:
318 case BuiltinTypeSpec
.Type
.MulticastDelegate
:
321 if (name
== Operator
.GetMetadataName (Operator
.OpType
.Implicit
) || name
== Operator
.GetMetadataName (Operator
.OpType
.Explicit
)) {
322 state
|= StateFlags
.HasConversionOperator
;
324 state
|= StateFlags
.HasUserOperator
;
332 IList
<MemberSpec
> list
;
333 if (!member_hash
.TryGetValue (name
, out list
)) {
334 member_hash
.Add (name
, new MemberSpec
[] { member }
);
338 if (removeHiddenMembers
&& member
.DeclaringType
.IsInterface
) {
339 if (AddInterfaceMember (member
, ref list
))
340 member_hash
[name
] = list
;
342 if (list
.Count
== 1) {
343 list
= new List
<MemberSpec
> () { list[0] }
;
344 member_hash
[name
] = list
;
351 public void AddMemberImported (MemberSpec ms
)
353 AddMember (GetLookupName (ms
), ms
, true);
357 // Ignores any base interface member which can be hidden
360 static bool AddInterfaceMember (MemberSpec member
, ref IList
<MemberSpec
> existing
)
362 var member_param
= member
is IParametersMember
? ((IParametersMember
) member
).Parameters
: ParametersCompiled
.EmptyReadOnlyParameters
;
365 // interface IA : IB { int Prop { set; } }
366 // interface IB { bool Prop { get; } }
368 // IB.Prop is never accessible from IA interface
370 for (int i
= 0; i
< existing
.Count
; ++i
) {
371 var entry
= existing
[i
];
373 if (entry
.Arity
!= member
.Arity
)
376 if (entry
is IParametersMember
) {
377 var entry_param
= ((IParametersMember
) entry
).Parameters
;
378 if (!TypeSpecComparer
.Override
.IsEqual (entry_param
, member_param
))
382 if (member
.DeclaringType
.ImplementsInterface (entry
.DeclaringType
, false)) {
383 if (existing
.Count
== 1) {
384 existing
= new MemberSpec
[] { member }
;
388 existing
.RemoveAt (i
--);
392 if ((entry
.DeclaringType
== member
.DeclaringType
&& entry
.IsAccessor
== member
.IsAccessor
) ||
393 entry
.DeclaringType
.ImplementsInterface (member
.DeclaringType
, false))
397 if (existing
.Count
== 1) {
398 existing
= new List
<MemberSpec
> () { existing[0], member }
;
402 existing
.Add (member
);
406 public static MemberSpec
FindMember (TypeSpec container
, MemberFilter filter
, BindingRestriction restrictions
)
409 IList
<MemberSpec
> applicable
;
410 if (container
.MemberCache
.member_hash
.TryGetValue (filter
.Name
, out applicable
)) {
411 // Start from the end because interface members are in reverse order
412 for (int i
= applicable
.Count
- 1; i
>= 0; i
--) {
413 var entry
= applicable
[i
];
415 if ((restrictions
& BindingRestriction
.InstanceOnly
) != 0 && entry
.IsStatic
)
418 if ((restrictions
& BindingRestriction
.NoAccessors
) != 0 && entry
.IsAccessor
)
421 if ((restrictions
& BindingRestriction
.OverrideOnly
) != 0 && (entry
.Modifiers
& Modifiers
.OVERRIDE
) == 0)
424 if (!filter
.Equals (entry
))
427 if ((restrictions
& BindingRestriction
.DeclaredOnly
) != 0 && container
.IsInterface
&& entry
.DeclaringType
!= container
)
434 if ((restrictions
& BindingRestriction
.DeclaredOnly
) != 0)
437 container
= container
.BaseType
;
438 } while (container
!= null);
444 // A special method to work with member lookup only. It returns a list of all members named @name
445 // starting from @container. It's very performance sensitive
447 public static IList
<MemberSpec
> FindMembers (TypeSpec container
, string name
, bool declaredOnly
)
449 IList
<MemberSpec
> applicable
;
452 if (container
.MemberCache
.member_hash
.TryGetValue (name
, out applicable
) || declaredOnly
)
455 container
= container
.BaseType
;
456 } while (container
!= null);
462 // Finds the nested type in container
464 public static TypeSpec
FindNestedType (TypeSpec container
, string name
, int arity
)
466 IList
<MemberSpec
> applicable
;
467 TypeSpec best_match
= null;
469 // TODO: Don't know how to handle this yet
470 // When resolving base type of nested type, parent type must have
471 // base type resolved to scan full hierarchy correctly
472 // Similarly MemberCacheTypes will inflate BaseType and Interfaces
473 // based on type definition
474 var tc
= container
.MemberDefinition
as TypeContainer
;
476 tc
.DefineContainer ();
478 if (container
.MemberCacheTypes
.member_hash
.TryGetValue (name
, out applicable
)) {
479 for (int i
= applicable
.Count
- 1; i
>= 0; i
--) {
480 var entry
= applicable
[i
];
481 if ((entry
.Kind
& MemberKind
.NestedMask
) == 0)
484 var ts
= (TypeSpec
) entry
;
485 if (arity
== ts
.Arity
)
489 if (best_match
== null) {
491 } else if (System
.Math
.Abs (ts
.Arity
+ arity
) < System
.Math
.Abs (ts
.Arity
+ arity
)) {
498 container
= container
.BaseType
;
499 } while (container
!= null);
505 // Looks for extension methods with defined name and extension type
507 public List
<MethodSpec
> FindExtensionMethods (IMemberContext invocationContext
, TypeSpec extensionType
, string name
, int arity
)
509 IList
<MemberSpec
> entries
;
510 if (!member_hash
.TryGetValue (name
, out entries
))
513 List
<MethodSpec
> candidates
= null;
514 foreach (var entry
in entries
) {
515 if (entry
.Kind
!= MemberKind
.Method
|| (arity
> 0 && entry
.Arity
!= arity
))
518 var ms
= (MethodSpec
) entry
;
519 if (!ms
.IsExtensionMethod
)
522 if (!ms
.IsAccessible (invocationContext
))
526 // Extension methods cannot be nested hence checking parent is enough
528 if ((ms
.DeclaringType
.Modifiers
& Modifiers
.INTERNAL
) != 0 && !ms
.DeclaringType
.MemberDefinition
.IsInternalAsPublic (invocationContext
.Module
.DeclaringAssembly
))
531 if (candidates
== null)
532 candidates
= new List
<MethodSpec
> ();
540 // Returns base members of @member member if no exact match is found @bestCandidate returns
543 public static MemberSpec
FindBaseMember (MemberCore member
, out MemberSpec bestCandidate
, ref bool overrides
)
545 bestCandidate
= null;
546 var container
= member
.Parent
.PartialContainer
.Definition
;
547 if (!container
.IsInterface
) {
548 container
= container
.BaseType
;
550 // It can happen for a user definition of System.Object
551 if (container
== null)
555 string name
= GetLookupName (member
);
556 var member_param
= member
is IParametersMember
? ((IParametersMember
) member
).Parameters
: null;
558 var mkind
= GetMemberCoreKind (member
);
559 bool member_with_accessors
= mkind
== MemberKind
.Indexer
|| mkind
== MemberKind
.Property
;
561 IList
<MemberSpec
> applicable
;
562 MemberSpec ambig_candidate
= null;
565 if (container
.MemberCache
.member_hash
.TryGetValue (name
, out applicable
)) {
566 for (int i
= 0; i
< applicable
.Count
; ++i
) {
567 var entry
= applicable
[i
];
569 if ((entry
.Modifiers
& Modifiers
.PRIVATE
) != 0)
572 if ((entry
.Modifiers
& Modifiers
.AccessibilityMask
) == Modifiers
.INTERNAL
&&
573 !entry
.DeclaringType
.MemberDefinition
.IsInternalAsPublic (member
.Module
.DeclaringAssembly
))
577 // Isn't the member of same kind ?
579 if ((entry
.Kind
& ~MemberKind
.Destructor
& mkind
& MemberKind
.MaskType
) == 0) {
580 // Destructors are ignored as they cannot be overridden by user
581 if ((entry
.Kind
& MemberKind
.Destructor
) != 0)
584 // A method with different arity does not hide base member
585 if (mkind
!= MemberKind
.Method
&& member
.MemberName
.Arity
!= entry
.Arity
)
588 bestCandidate
= entry
;
593 // Same kind of different arity is valid
595 if (member
.MemberName
.Arity
!= entry
.Arity
) {
599 if ((entry
.Kind
& mkind
& (MemberKind
.Method
| MemberKind
.Indexer
)) != 0) {
600 if (entry
.IsAccessor
!= member
is AbstractPropertyEventMethod
)
603 var pm
= entry
as IParametersMember
;
604 if (!TypeSpecComparer
.Override
.IsEqual (pm
.Parameters
, member_param
))
609 // Skip override for member with accessors. It may not fully implement the base member
610 // but keep flag we found an implementation in case the base member is abstract
612 if (member_with_accessors
&& ((entry
.Modifiers
& (Modifiers
.OVERRIDE
| Modifiers
.SEALED
)) == Modifiers
.OVERRIDE
)) {
614 // Set candidate to override implementation to flag we found an implementation
621 // For members with parameters we can encounter an ambiguous candidates (they match exactly)
622 // because generic type parameters could be inflated into same types
624 if (ambig_candidate
== null && (entry
.Kind
& mkind
& (MemberKind
.Method
| MemberKind
.Indexer
)) != 0) {
625 bestCandidate
= null;
626 ambig_candidate
= entry
;
630 bestCandidate
= ambig_candidate
;
635 if (container
.IsInterface
|| ambig_candidate
!= null)
638 container
= container
.BaseType
;
639 } while (container
!= null);
641 return ambig_candidate
;
645 // Returns inflated version of MemberSpec, it works similarly to
646 // SRE TypeBuilder.GetMethod
648 public static T GetMember
<T
> (TypeSpec container
, T spec
) where T
: MemberSpec
650 IList
<MemberSpec
> applicable
;
651 if (container
.MemberCache
.member_hash
.TryGetValue (GetLookupName (spec
), out applicable
)) {
652 for (int i
= applicable
.Count
- 1; i
>= 0; i
--) {
653 var entry
= applicable
[i
];
654 if (entry
.MemberDefinition
== spec
.MemberDefinition
)
659 throw new InternalErrorException ("Missing member `{0}' on inflated type `{1}'",
660 spec
.GetSignatureForError (), container
.GetSignatureForError ());
663 static MemberKind
GetMemberCoreKind (MemberCore member
)
665 if (member
is FieldBase
)
666 return MemberKind
.Field
;
667 if (member
is Indexer
)
668 return MemberKind
.Indexer
;
670 return MemberKind
.Class
;
671 if (member
is Struct
)
672 return MemberKind
.Struct
;
673 if (member
is Destructor
)
674 return MemberKind
.Destructor
;
675 if (member
is Method
)
676 return MemberKind
.Method
;
677 if (member
is Property
)
678 return MemberKind
.Property
;
679 if (member
is EventField
)
680 return MemberKind
.Event
;
681 if (member
is Interface
)
682 return MemberKind
.Interface
;
683 if (member
is EventProperty
)
684 return MemberKind
.Event
;
685 if (member
is Delegate
)
686 return MemberKind
.Delegate
;
688 return MemberKind
.Enum
;
690 throw new NotImplementedException (member
.GetType ().ToString ());
693 public static List
<FieldSpec
> GetAllFieldsForDefiniteAssignment (TypeSpec container
)
695 List
<FieldSpec
> fields
= null;
696 foreach (var entry
in container
.MemberCache
.member_hash
) {
697 foreach (var name_entry
in entry
.Value
) {
698 if (name_entry
.Kind
!= MemberKind
.Field
)
701 if ((name_entry
.Modifiers
& Modifiers
.STATIC
) != 0)
705 // Fixed size buffers are not subject to definite assignment checking
707 if (name_entry
is FixedFieldSpec
|| name_entry
is ConstSpec
)
710 var fs
= (FieldSpec
) name_entry
;
713 // LAMESPEC: Very bizzare hack, definitive assignment is not done
714 // for imported non-public reference fields except array. No idea what the
715 // actual csc rule is
717 if (!fs
.IsPublic
&& container
.MemberDefinition
.IsImported
&& (!fs
.MemberType
.IsArray
&& TypeSpec
.IsReferenceType (fs
.MemberType
)))
721 fields
= new List
<FieldSpec
> ();
728 return fields
?? new List
<FieldSpec
> (0);
731 public static IList
<MemberSpec
> GetCompletitionMembers (IMemberContext ctx
, TypeSpec container
, string name
)
733 var matches
= new List
<MemberSpec
> ();
734 foreach (var entry
in container
.MemberCache
.member_hash
) {
735 foreach (var name_entry
in entry
.Value
) {
736 if (name_entry
.IsAccessor
)
739 if ((name_entry
.Kind
& (MemberKind
.Constructor
| MemberKind
.Destructor
| MemberKind
.Operator
)) != 0)
742 if (!name_entry
.IsAccessible (ctx
))
745 if (name
== null || name_entry
.Name
.StartsWith (name
)) {
746 matches
.Add (name_entry
);
755 // Returns members of @iface only, base members are ignored
757 public static List
<MethodSpec
> GetInterfaceMethods (TypeSpec iface
)
760 // MemberCache flatten interfaces, therefore in cases like this one
762 // interface IA : IB {}
763 // interface IB { void Foo () }
765 // we would return Foo inside IA which is not expected in this case
767 var methods
= new List
<MethodSpec
> ();
768 foreach (var entry
in iface
.MemberCache
.member_hash
.Values
) {
769 foreach (var name_entry
in entry
) {
770 if (iface
== name_entry
.DeclaringType
) {
771 if (name_entry
.Kind
== MemberKind
.Method
) {
772 methods
.Add ((MethodSpec
) name_entry
);
782 // Returns all not implememted abstract members inside abstract type
783 // NOTE: Returned list is shared and must not be modified
785 public static IList
<MethodSpec
> GetNotImplementedAbstractMethods (TypeSpec type
)
787 if (type
.MemberCache
.missing_abstract
!= null)
788 return type
.MemberCache
.missing_abstract
;
790 var abstract_methods
= new List
<MethodSpec
> ();
791 List
<TypeSpec
> hierarchy
= null;
794 // Stage 1: top-to-bottom scan for abstract members
796 var abstract_type
= type
;
798 foreach (var entry
in abstract_type
.MemberCache
.member_hash
) {
799 foreach (var name_entry
in entry
.Value
) {
800 if ((name_entry
.Modifiers
& Modifiers
.ABSTRACT
) == 0)
803 if (name_entry
.Kind
!= MemberKind
.Method
)
806 abstract_methods
.Add ((MethodSpec
) name_entry
);
810 var base_type
= abstract_type
.BaseType
;
811 if (!base_type
.IsAbstract
)
814 if (hierarchy
== null)
815 hierarchy
= new List
<TypeSpec
> ();
817 hierarchy
.Add (abstract_type
);
818 abstract_type
= base_type
;
821 int not_implemented_count
= abstract_methods
.Count
;
822 if (not_implemented_count
== 0 || hierarchy
== null) {
823 type
.MemberCache
.missing_abstract
= abstract_methods
;
824 return type
.MemberCache
.missing_abstract
;
828 // Stage 2: Remove already implemented methods
830 foreach (var type_up
in hierarchy
) {
831 var members
= type_up
.MemberCache
.member_hash
;
832 if (members
.Count
== 0)
835 for (int i
= 0; i
< abstract_methods
.Count
; ++i
) {
836 var candidate
= abstract_methods
[i
];
837 if (candidate
== null)
840 IList
<MemberSpec
> applicable
;
841 if (!members
.TryGetValue (candidate
.Name
, out applicable
))
844 var filter
= new MemberFilter (candidate
);
845 foreach (var item
in applicable
) {
846 if ((item
.Modifiers
& (Modifiers
.OVERRIDE
| Modifiers
.VIRTUAL
)) == 0)
849 if (filter
.Equals (item
)) {
850 --not_implemented_count
;
851 abstract_methods
[i
] = null;
858 if (not_implemented_count
== abstract_methods
.Count
) {
859 type
.MemberCache
.missing_abstract
= abstract_methods
;
860 return type
.MemberCache
.missing_abstract
;
863 var not_implemented
= new MethodSpec
[not_implemented_count
];
865 foreach (var m
in abstract_methods
) {
869 not_implemented
[counter
++] = m
;
872 type
.MemberCache
.missing_abstract
= not_implemented
;
873 return type
.MemberCache
.missing_abstract
;
876 static string GetLookupName (MemberSpec ms
)
878 if (ms
.Kind
== MemberKind
.Indexer
)
879 return IndexerNameAlias
;
881 if (ms
.Kind
== MemberKind
.Constructor
) {
883 return Constructor
.TypeConstructorName
;
885 return Constructor
.ConstructorName
;
891 static string GetLookupName (MemberCore mc
)
894 return IndexerNameAlias
;
896 if (mc
is Constructor
)
897 return mc
.IsStatic
? Constructor
.TypeConstructorName
: Constructor
.ConstructorName
;
899 return mc
.MemberName
.Name
;
903 // Returns all operators declared on container and its base types (until declaredOnly is used)
905 public static IList
<MemberSpec
> GetUserOperator (TypeSpec container
, Operator
.OpType op
, bool declaredOnly
)
907 IList
<MemberSpec
> found
= null;
909 IList
<MemberSpec
> applicable
;
911 var mc
= container
.MemberCache
;
913 if (((op
== Operator
.OpType
.Implicit
|| op
== Operator
.OpType
.Explicit
) && (mc
.state
& StateFlags
.HasConversionOperator
) != 0) ||
914 (mc
.state
& StateFlags
.HasUserOperator
) != 0) {
916 if (mc
.member_hash
.TryGetValue (Operator
.GetMetadataName (op
), out applicable
)) {
918 for (i
= 0; i
< applicable
.Count
; ++i
) {
919 if (applicable
[i
].Kind
!= MemberKind
.Operator
) {
925 // Handles very rare case where a method with same name as operator (op_xxxx) exists
926 // and we have to resize the applicable list
928 if (i
!= applicable
.Count
) {
929 for (i
= 0; i
< applicable
.Count
; ++i
) {
930 if (applicable
[i
].Kind
!= MemberKind
.Operator
) {
935 found
= new List
<MemberSpec
> ();
936 found
.Add (applicable
[i
]);
938 var prev
= found
as List
<MemberSpec
>;
940 prev
= new List
<MemberSpec
> (found
.Count
+ 1);
941 prev
.AddRange (found
);
944 prev
.Add (applicable
[i
]);
951 var merged
= found
as List
<MemberSpec
>;
952 if (merged
== null) {
953 merged
= new List
<MemberSpec
> (found
.Count
+ applicable
.Count
);
954 merged
.AddRange (found
);
958 merged
.AddRange (applicable
);
964 // BaseType call can be expensive
968 container
= container
.BaseType
;
969 } while (container
!= null);
975 // Inflates all member cache nested types
977 public void InflateTypes (MemberCache inflated_cache
, TypeParameterInflator inflator
)
979 foreach (var item
in member_hash
) {
980 IList
<MemberSpec
> inflated_members
= null;
981 for (int i
= 0; i
< item
.Value
.Count
; ++i
) {
982 var member
= item
.Value
[i
];
984 // FIXME: When inflating members refering nested types before they are inflated
988 if ((member
.Kind
& MemberKind
.NestedMask
) != 0 &&
989 (member
.Modifiers
& Modifiers
.COMPILER_GENERATED
) == 0) {
990 if (inflated_members
== null) {
991 inflated_members
= new MemberSpec
[item
.Value
.Count
];
992 inflated_cache
.member_hash
.Add (item
.Key
, inflated_members
);
995 inflated_members
[i
] = member
.InflateMember (inflator
);
1002 // Inflates all open type members, requires InflateTypes to be called before
1004 public void InflateMembers (MemberCache cacheToInflate
, TypeSpec inflatedType
, TypeParameterInflator inflator
)
1006 var inflated_member_hash
= cacheToInflate
.member_hash
;
1007 Dictionary
<MemberSpec
, MethodSpec
> accessor_relation
= null;
1008 List
<MemberSpec
> accessor_members
= null;
1010 // Copy member specific flags when all members were added
1011 cacheToInflate
.state
= state
;
1013 foreach (var item
in member_hash
) {
1014 var members
= item
.Value
;
1015 IList
<MemberSpec
> inflated_members
= null;
1016 for (int i
= 0; i
< members
.Count
; ++i
) {
1017 var member
= members
[i
];
1020 // All nested types have been inflated earlier except for
1021 // compiler types which are created later and could miss InflateTypes
1023 if ((member
.Kind
& MemberKind
.NestedMask
) != 0 &&
1024 (member
.Modifiers
& Modifiers
.COMPILER_GENERATED
) == 0) {
1025 if (inflated_members
== null)
1026 inflated_members
= inflated_member_hash
[item
.Key
];
1032 // Clone the container first
1034 if (inflated_members
== null) {
1035 inflated_members
= new MemberSpec
[item
.Value
.Count
];
1036 inflated_member_hash
.Add (item
.Key
, inflated_members
);
1039 var local_inflator
= inflator
;
1041 if (member
.DeclaringType
!= inflatedType
) {
1043 // Don't inflate top-level non-generic interface members
1044 // merged into generic interface
1046 if (!member
.DeclaringType
.IsGeneric
&& !member
.DeclaringType
.IsNested
) {
1047 inflated_members
[i
] = member
;
1052 // Needed when inflating flatten interfaces. It inflates
1053 // container type only, type parameters are already done
1055 // Handles cases like:
1057 // interface I<T> {}
1058 // interface I<U, V> : I<U> {}
1060 // class C: I<int, bool> {}
1062 var inflated_parent
= inflator
.Inflate (member
.DeclaringType
);
1063 if (inflated_parent
!= inflator
.TypeInstance
)
1064 local_inflator
= new TypeParameterInflator (inflator
, inflated_parent
);
1068 // Inflate every member, its parent is now different
1070 var inflated
= member
.InflateMember (local_inflator
);
1071 inflated_members
[i
] = inflated
;
1073 if (member
is PropertySpec
|| member
is EventSpec
) {
1074 if (accessor_members
== null)
1075 accessor_members
= new List
<MemberSpec
> ();
1077 accessor_members
.Add (inflated
);
1081 if (member
.IsAccessor
) {
1082 if (accessor_relation
== null)
1083 accessor_relation
= new Dictionary
<MemberSpec
, MethodSpec
> ();
1084 accessor_relation
.Add (member
, (MethodSpec
) inflated
);
1089 if (accessor_members
!= null) {
1090 foreach (var member
in accessor_members
) {
1091 var prop
= member
as PropertySpec
;
1093 if (prop
.Get
!= null)
1094 prop
.Get
= accessor_relation
[prop
.Get
];
1095 if (prop
.Set
!= null)
1096 prop
.Set
= accessor_relation
[prop
.Set
];
1101 var ev
= (EventSpec
) member
;
1102 ev
.AccessorAdd
= accessor_relation
[ev
.AccessorAdd
];
1103 ev
.AccessorRemove
= accessor_relation
[ev
.AccessorRemove
];
1109 // Removes hidden base members of an interface. For compiled interfaces we cannot
1110 // do name filtering during Add (as we do for import) because we need all base
1111 // names to be valid during type definition.
1112 // Add replaces hidden base member with current one which means any name collision
1113 // (CS0108) of non-first name would be unnoticed because the name was replaced
1114 // with the one from compiled type
1116 public void RemoveHiddenMembers (TypeSpec container
)
1118 foreach (var entry
in member_hash
) {
1119 var values
= entry
.Value
;
1121 int container_members_start_at
= 0;
1122 while (values
[container_members_start_at
].DeclaringType
!= container
&& ++container_members_start_at
< entry
.Value
.Count
);
1124 if (container_members_start_at
== 0 || container_members_start_at
== values
.Count
)
1127 for (int i
= 0; i
< container_members_start_at
; ++i
) {
1128 var member
= values
[i
];
1130 if (!container
.ImplementsInterface (member
.DeclaringType
, false))
1133 var member_param
= member
is IParametersMember
? ((IParametersMember
) member
).Parameters
: ParametersCompiled
.EmptyReadOnlyParameters
;
1135 for (int ii
= container_members_start_at
; ii
< values
.Count
; ++ii
) {
1136 var container_entry
= values
[ii
];
1138 if (container_entry
.Arity
!= member
.Arity
)
1141 if (container_entry
is IParametersMember
) {
1142 if (!TypeSpecComparer
.Override
.IsEqual (((IParametersMember
) container_entry
).Parameters
, member_param
))
1146 values
.RemoveAt (i
);
1147 --container_members_start_at
;
1156 // Checks all appropriate container members for CLS compliance
1158 public void VerifyClsCompliance (TypeSpec container
, Report report
)
1160 if (locase_members
!= null)
1163 if (container
.BaseType
== null) {
1164 locase_members
= new Dictionary
<string, MemberSpec
[]> (member_hash
.Count
); // StringComparer.OrdinalIgnoreCase);
1166 container
.BaseType
.MemberCache
.VerifyClsCompliance (container
.BaseType
, report
);
1167 locase_members
= new Dictionary
<string, MemberSpec
[]> (container
.BaseType
.MemberCache
.locase_members
); //, StringComparer.OrdinalIgnoreCase);
1170 var is_imported_type
= container
.MemberDefinition
.IsImported
;
1171 foreach (var entry
in container
.MemberCache
.member_hash
) {
1172 for (int i
= 0; i
< entry
.Value
.Count
; ++i
) {
1173 var name_entry
= entry
.Value
[i
];
1174 if ((name_entry
.Modifiers
& (Modifiers
.PUBLIC
| Modifiers
.PROTECTED
)) == 0)
1177 if ((name_entry
.Modifiers
& (Modifiers
.OVERRIDE
| Modifiers
.COMPILER_GENERATED
)) != 0)
1180 if ((name_entry
.Kind
& MemberKind
.MaskType
) == 0)
1183 if (name_entry
.MemberDefinition
.CLSAttributeValue
== false)
1186 IParametersMember p_a
= null;
1187 if (!is_imported_type
) {
1188 p_a
= name_entry
as IParametersMember
;
1189 if (p_a
!= null && !name_entry
.IsAccessor
) {
1190 var p_a_pd
= p_a
.Parameters
;
1192 // Check differing overloads in @container
1194 for (int ii
= i
+ 1; ii
< entry
.Value
.Count
; ++ii
) {
1195 var checked_entry
= entry
.Value
[ii
];
1196 IParametersMember p_b
= checked_entry
as IParametersMember
;
1200 if (p_a_pd
.Count
!= p_b
.Parameters
.Count
)
1203 if (checked_entry
.IsAccessor
)
1206 var res
= ParametersCompiled
.IsSameClsSignature (p_a
.Parameters
, p_b
.Parameters
);
1208 ReportOverloadedMethodClsDifference (name_entry
, checked_entry
, res
, report
);
1214 if (i
> 0 || name_entry
.Kind
== MemberKind
.Constructor
|| name_entry
.Kind
== MemberKind
.Indexer
)
1217 var name_entry_locase
= name_entry
.Name
.ToLowerInvariant ();
1220 if (!locase_members
.TryGetValue (name_entry_locase
, out found
)) {
1221 found
= new MemberSpec
[] { name_entry }
;
1222 locase_members
.Add (name_entry_locase
, found
);
1224 bool same_names_only
= true;
1225 foreach (var f
in found
) {
1226 if (f
.Name
== name_entry
.Name
) {
1228 IParametersMember p_b
= f
as IParametersMember
;
1232 if (p_a
.Parameters
.Count
!= p_b
.Parameters
.Count
)
1238 var res
= ParametersCompiled
.IsSameClsSignature (p_a
.Parameters
, p_b
.Parameters
);
1240 ReportOverloadedMethodClsDifference (f
, name_entry
, res
, report
);
1247 same_names_only
= false;
1248 if (!is_imported_type
) {
1249 var last
= GetLaterDefinedMember (f
, name_entry
);
1250 if (last
== f
.MemberDefinition
) {
1251 report
.SymbolRelatedToPreviousError (name_entry
);
1253 report
.SymbolRelatedToPreviousError (f
);
1256 report
.Warning (3005, 1, last
.Location
,
1257 "Identifier `{0}' differing only in case is not CLS-compliant", last
.GetSignatureForError ());
1261 if (!same_names_only
) {
1262 Array
.Resize (ref found
, found
.Length
+ 1);
1263 found
[found
.Length
- 1] = name_entry
;
1264 locase_members
[name_entry_locase
] = found
;
1272 // Local report helper to issue correctly ordered members stored in hashtable
1274 static MemberCore
GetLaterDefinedMember (MemberSpec a
, MemberSpec b
)
1276 var mc_a
= a
.MemberDefinition
as MemberCore
;
1277 var mc_b
= b
.MemberDefinition
as MemberCore
;
1284 if (a
.DeclaringType
.MemberDefinition
!= b
.DeclaringType
.MemberDefinition
)
1287 if (mc_a
.Location
.File
!= mc_a
.Location
.File
)
1290 return mc_b
.Location
.Row
> mc_a
.Location
.Row
? mc_b
: mc_a
;
1293 static void ReportOverloadedMethodClsDifference (MemberSpec a
, MemberSpec b
, int res
, Report report
)
1295 var last
= GetLaterDefinedMember (a
, b
);
1296 if (last
== a
.MemberDefinition
) {
1297 report
.SymbolRelatedToPreviousError (b
);
1299 report
.SymbolRelatedToPreviousError (a
);
1302 if ((res
& 1) != 0) {
1303 report
.Warning (3006, 1, last
.Location
,
1304 "Overloaded method `{0}' differing only in ref or out, or in array rank, is not CLS-compliant",
1305 last
.GetSignatureForError ());
1308 if ((res
& 2) != 0) {
1309 report
.Warning (3007, 1, last
.Location
,
1310 "Overloaded method `{0}' differing only by unnamed array types is not CLS-compliant",
1311 last
.GetSignatureForError ());
1315 public bool CheckExistingMembersOverloads (MemberCore member
, AParametersCollection parameters
)
1317 var name
= GetLookupName (member
);
1318 var imb
= member
as InterfaceMemberBase
;
1319 if (imb
!= null && imb
.IsExplicitImpl
) {
1320 name
= imb
.GetFullName (name
);
1323 return CheckExistingMembersOverloads (member
, name
, parameters
);
1326 public bool CheckExistingMembersOverloads (MemberCore member
, string name
, AParametersCollection parameters
)
1328 IList
<MemberSpec
> entries
;
1329 if (!member_hash
.TryGetValue (name
, out entries
))
1332 var Report
= member
.Compiler
.Report
;
1334 int method_param_count
= parameters
.Count
;
1335 for (int i
= entries
.Count
- 1; i
>= 0; --i
) {
1336 var ce
= entries
[i
];
1337 var pm
= ce
as IParametersMember
;
1338 var pd
= pm
== null ? ParametersCompiled
.EmptyReadOnlyParameters
: pm
.Parameters
;
1339 if (pd
.Count
!= method_param_count
)
1342 if (ce
.Arity
!= member
.MemberName
.Arity
)
1345 // Ignore merged interface members
1346 if (member
.Parent
.PartialContainer
!= ce
.DeclaringType
.MemberDefinition
)
1349 var p_types
= pd
.Types
;
1350 if (method_param_count
> 0) {
1351 int ii
= method_param_count
- 1;
1352 TypeSpec type_a
, type_b
;
1354 type_a
= parameters
.Types
[ii
];
1355 type_b
= p_types
[ii
];
1357 if ((pd
.FixedParameters
[ii
].ModFlags
& Parameter
.Modifier
.ISBYREF
) !=
1358 (parameters
.FixedParameters
[ii
].ModFlags
& Parameter
.Modifier
.ISBYREF
))
1361 } while (TypeSpecComparer
.Override
.IsEqual (type_a
, type_b
) && ii
-- != 0);
1367 // Operators can differ in return type only
1369 if (member
is Operator
&& ce
.Kind
== MemberKind
.Operator
&& ((MethodSpec
) ce
).ReturnType
!= ((Operator
) member
).ReturnType
)
1373 // Report difference in parameter modifiers only
1375 if (pd
!= null && member
is MethodCore
) {
1376 ii
= method_param_count
;
1377 while (ii
-- != 0 && parameters
.FixedParameters
[ii
].ModFlags
== pd
.FixedParameters
[ii
].ModFlags
&&
1378 parameters
.ExtensionMethodType
== pd
.ExtensionMethodType
) ;
1381 var mc
= ce
as MethodSpec
;
1382 member
.Compiler
.Report
.SymbolRelatedToPreviousError (ce
);
1383 if ((member
.ModFlags
& Modifiers
.PARTIAL
) != 0 && (mc
.Modifiers
& Modifiers
.PARTIAL
) != 0) {
1384 if (parameters
.HasParams
|| pd
.HasParams
) {
1385 Report
.Error (758, member
.Location
,
1386 "A partial method declaration and partial method implementation cannot differ on use of `params' modifier");
1388 Report
.Error (755, member
.Location
,
1389 "A partial method declaration and partial method implementation must be both an extension method or neither");
1391 } else if (member
is Constructor
) {
1392 Report
.Error (851, member
.Location
,
1393 "Overloaded contructor `{0}' cannot differ on use of parameter modifiers only",
1394 member
.GetSignatureForError ());
1396 Report
.Error (663, member
.Location
,
1397 "Overloaded method `{0}' cannot differ on use of parameter modifiers only",
1398 member
.GetSignatureForError ());
1405 if ((ce
.Kind
& MemberKind
.Method
) != 0) {
1406 Method method_a
= member
as Method
;
1407 Method method_b
= ce
.MemberDefinition
as Method
;
1408 if (method_a
!= null && method_b
!= null && (method_a
.ModFlags
& method_b
.ModFlags
& Modifiers
.PARTIAL
) != 0) {
1409 const Modifiers partial_modifiers
= Modifiers
.STATIC
| Modifiers
.UNSAFE
;
1410 if (method_a
.IsPartialDefinition
== method_b
.IsPartialImplementation
) {
1411 if ((method_a
.ModFlags
& partial_modifiers
) == (method_b
.ModFlags
& partial_modifiers
) ||
1412 method_a
.Parent
.IsUnsafe
&& method_b
.Parent
.IsUnsafe
) {
1413 if (method_a
.IsPartialImplementation
) {
1414 method_a
.SetPartialDefinition (method_b
);
1415 if (entries
.Count
== 1)
1416 member_hash
.Remove (name
);
1418 entries
.RemoveAt (i
);
1420 method_b
.SetPartialDefinition (method_a
);
1421 method_a
.caching_flags
|= MemberCore
.Flags
.PartialDefinitionExists
;
1426 if (method_a
.IsStatic
!= method_b
.IsStatic
) {
1427 Report
.SymbolRelatedToPreviousError (ce
);
1428 Report
.Error (763, member
.Location
,
1429 "A partial method declaration and partial method implementation must be both `static' or neither");
1432 Report
.SymbolRelatedToPreviousError (ce
);
1433 Report
.Error (764, member
.Location
,
1434 "A partial method declaration and partial method implementation must be both `unsafe' or neither");
1438 Report
.SymbolRelatedToPreviousError (ce
);
1439 if (method_a
.IsPartialDefinition
) {
1440 Report
.Error (756, member
.Location
, "A partial method `{0}' declaration is already defined",
1441 member
.GetSignatureForError ());
1444 Report
.Error (757, member
.Location
, "A partial method `{0}' implementation is already defined",
1445 member
.GetSignatureForError ());
1449 Report
.SymbolRelatedToPreviousError (ce
);
1451 bool is_reserved_a
= member
is AbstractPropertyEventMethod
|| member
is Operator
;
1452 bool is_reserved_b
= ((MethodSpec
) ce
).IsReservedMethod
;
1454 if (is_reserved_a
|| is_reserved_b
) {
1455 Report
.Error (82, member
.Location
, "A member `{0}' is already reserved",
1457 ce
.GetSignatureForError () :
1458 member
.GetSignatureForError ());
1462 Report
.SymbolRelatedToPreviousError (ce
);
1465 if (member
is Operator
&& ce
.Kind
== MemberKind
.Operator
) {
1466 Report
.Error (557, member
.Location
, "Duplicate user-defined conversion in type `{0}'",
1467 member
.Parent
.GetSignatureForError ());
1471 Report
.Error (111, member
.Location
,
1472 "A member `{0}' is already defined. Rename this member or use different parameter types",
1473 member
.GetSignatureForError ());