// Does a member lookup in a single type, without considering inheritance. private LookupResult MemberLookupWithoutInheritance(TypeSymbol type, string name, int arity, bool invoked) { LookupResult result = new LookupResult(); IEnumerable <Symbol> members = type.GetMembers(name); foreach (Symbol member in members) { LookupResult resultOfThisMember; // Do we need to exclude override members, or is that done later by overload resolution. It seems like // not excluding them here can't lead to problems, because we will always find the overridden method as well. SymbolKind memberKind = member.Kind; DiagnosticInfo diagInfo; if (WrongArity(member, arity, out diagInfo)) { resultOfThisMember = LookupResult.WrongArity(member, diagInfo); } else if (invoked && !IsInvocable(member)) { resultOfThisMember = LookupResult.Bad(member, new CSDiagnosticInfo(ErrorCode.ERR_NonInvocableMemberCalled, member.GetFullName())); } else if (!IsMemberAccessible(member)) { resultOfThisMember = LookupResult.Inaccessible(member); } else { resultOfThisMember = LookupResult.Good(member); } result = MergeLookupsInSameScope(result, resultOfThisMember); } return(result); }
// Looks up a member of given name and arity in a particular type. private LookupResult MemberLookup(TypeSymbol type, string name, int arity, bool invoked) { LookupResult result; switch (type.TypeKind) { case TypeKind.RefType: return(MemberLookup(((RefTypeSymbol)type).ReferencedType, name, arity, invoked)); case TypeKind.TypeParameter: result = MemberLookupInTypeParameter(type, name, arity, invoked); break; case TypeKind.Interface: result = MemberLookupInInterface(type, name, arity, invoked); break; case TypeKind.Class: case TypeKind.Struct: case TypeKind.Enum: case TypeKind.Delegate: case TypeKind.ArrayType: result = MemberLookupInClass(type, name, arity, invoked); break; case TypeKind.Error: case TypeKind.PointerType: return(LookupResult.Empty); case TypeKind.Unknown: default: Debug.Fail("Unknown type kind"); return(LookupResult.Empty); } // TODO: Diagnose ambiguity problems here, and conflicts between non-method and method? Or is that // done in the caller? return(result); #if SLOW // A member lookup is the process whereby the meaning of a name in the context of // a type is determined. A member lookup can occur as part of evaluating a // simple-name or a member-access in an expression. If the // simple-name or member-access occurs as the simple-expression of an // invocation-expression, the member is said to be invoked. // If a member is a method or event, or if it is a constant, field or property // of a delegate type, then the member is said to be invocable. // Member lookup considers not only the name of a member but also the number of // type parameters the member has and whether the member is accessible. For the // purposes of member lookup, generic methods and nested generic types have the // number of type parameters indicated in their respective declarations and all // other members have zero type parameters. // A member lookup of a name N with K type parameters in a type T is processed as follows: // First, a set of accessible members named N is determined. // If T is a type parameter, then the set is the union of the sets of accessible // members named N in each of the types specified as a primary constraint or secondary // constraint for T, along with the set of accessible members named N in object. // Otherwise, the set consists of all accessible members named N in T, // including inherited members and the accessible members named N in object. If T is // a constructed type, the set of members is obtained by substituting type arguments // as described in §10.3.2. Members that include an override modifier are excluded // from the set. var results = new HashSet <Symbol>(); var inaccessible = new HashSet <Symbol>(); var notInvocable = new HashSet <Symbol>(); var hidden1 = new HashSet <Symbol>(); var hidden2 = new HashSet <Symbol>(); var types = new HashSet <TypeSymbol>(type.TypeAndAllBaseTypes()); types.Add(System_Object); foreach (TypeSymbol t in types) { results.UnionWith( from s in t.GetMembers(name) where !s.IsOverride select s); } inaccessible.UnionWith(from s in results where !IsMemberAccessible(s) select s); results.ExceptWith(inaccessible); var badArity = new HashSet <Symbol>(); // Next, if K is zero, all nested types whose declarations include type parameters are removed. // If K is not zero, all members with a different number of type parameters are removed. // Note that when K is zero, methods having type parameters are not removed, since the // type inference process might be able to infer the type arguments. if (arity == 0) { badArity.UnionWith(from s in results where s.IsNestedType() && ((NamedTypeSymbol)s).Arity != 0 select s); } else { badArity.UnionWith(from s in results where s is NamedTypeSymbol && ((NamedTypeSymbol)s).Arity != arity select s); badArity.UnionWith(from s in results where s is MethodSymbol && ((MethodSymbol)s).TypeParameters.Count != arity select s); } results.ExceptWith(badArity); // Next, if the member is invoked, all non-invocable members are removed from the set. if (invoked) { notInvocable.UnionWith(from s in results where !IsInvocable(s) select s); } results.ExceptWith(notInvocable); // Next, members that are hidden by other members are removed from the set. // For every member S.M in the set, where S is the type in which the member M is declared, // the following rules are applied: foreach (var member in results) { var declaringType = member.ContainingType; // If M is a constant, field, property, event, or enumeration member, // then all members declared in a base type of S are removed from the set. if (member is FieldSymbol || member is PropertySymbol /* UNDONE || member is EventSymbol */) { foreach (var baseType in declaringType.AllBaseTypeDefinitions()) { hidden1.UnionWith( from s in results where s.ContainingType.OriginalDefinition == baseType select s); } } else if (member is NamedTypeSymbol) { // If M is a type declaration, then all non-types declared in a base type of S // are removed from the set, and all type declarations with the same number of // type parameters as M declared in a base type of S are removed from the set. foreach (var baseType in declaringType.AllBaseTypeDefinitions()) { hidden1.UnionWith( from s in results where s.ContainingType.OriginalDefinition == baseType where !(s is NamedTypeSymbol) || ((NamedTypeSymbol)s).Arity == ((NamedTypeSymbol)member).Arity select s); } } else if (member is MethodSymbol) { // If M is a method, then all non-method members declared in a base type of S // are removed from the set. foreach (var baseType in declaringType.AllBaseTypeDefinitions()) { hidden1.UnionWith( from m in results where m.ContainingType.OriginalDefinition == baseType && !(m is MethodSymbol) select m); } } } results.ExceptWith(hidden1); // Next, interface members that are hidden by class members are removed from the set. // This step only has an effect if T is a type parameter and T has both an effective base // class other than object and a non-empty effective interface set. For every // member S.M in the set, where S is the type in which the member M is declared, the // following rules are applied if S is a class declaration other than object: foreach (var member in results) { var declaringType = member.ContainingType; if (!declaringType.IsClassType() || declaringType == System_Object) { continue; } // If M is a constant, field, property, event, enumeration member, or type declaration, // then all members declared in an interface declaration are removed from the set. if (member is FieldSymbol || member is PropertySymbol /* UNDONE || member is EventSymbol */ || member is NamedTypeSymbol) { hidden2.UnionWith( from m in results where m.ContainingType.IsInterfaceType() select m); } else if (member is MethodSymbol) { // If M is a method, then all non-method members declared in an interface declaration // are removed from the set, and all methods with the same signature as M declared // in an interface declaration are removed from the set. hidden2.UnionWith( from m in results where m.ContainingType.IsInterfaceType() && (!(m is MethodSymbol) || HaveSameSignature((MethodSymbol)m, (MethodSymbol)member)) select m); } } results.ExceptWith(hidden2); hidden1.UnionWith(hidden2); // Finally, having removed hidden members, the result of the lookup is determined: // If the set consists of a single member that is not a method, then this member is the result of the lookup. // Otherwise, if the set contains only methods, then this group of methods is the result of the lookup. // Otherwise, the lookup is ambiguous, and a compile-time error occurs. // For member lookups in types other than type parameters and interfaces, and member lookups in interfaces // that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct // base interface), the effect of the lookup rules is simply that derived members hide base members with the // same name or signature. Such single-inheritance lookups are never ambiguous. The ambiguities that can possibly // arise from member lookups in multiple-inheritance interfaces are described in §13.2.5. // UNDONE: Make this match what the original compiler does. if (results.Count == 0 || (results.Count > 1 && !results.All(m => m is MethodSymbol))) { if (inaccessible.Count != 0) { return(LookupResult.Inaccessible(inaccessible.First())); } else if (badArity.Count != 0) { return(LookupResult.WrongArity(badArity.First(), null)); } else { return(LookupResult.Bad((DiagnosticInfo)null)); } } else { return(LookupResult.ForSymbols(results)); } #endif }