Exemplo n.º 1
0
        // 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
        }