Exemplo n.º 1
0
        ////////////////////////////////////////////////////////////////////////////////
        // Determine best method for overload resolution. Returns null if no best
        // method, in which case two tying methods are returned for error reporting.
        private CandidateFunctionMember FindBestMethod(
            List <CandidateFunctionMember> list,
            CType pTypeThrough,
            ArgInfos args,
            out CandidateFunctionMember methAmbig1,
            out CandidateFunctionMember methAmbig2)
        {
            Debug.Assert(list.Any());
            Debug.Assert(list.First().mpwi != null);
            Debug.Assert(list.Count > 0);

            // select the best method:

            /*
             * Effectively, we pick the best item from a set using a non-transitive ranking function
             * So, pick the first item (candidate) and compare against next (contender), if there is
             *  no next, goto phase 2
             * If first is better, move to next contender, if none proceed to phase 2
             * If second is better, make the contender the candidate and make the item following
             *  contender into the new contender, if there is none, goto phase 2
             * If neither, make contender+1 into candidate and contender+2 into contender, if possible,
             *  otherwise, if contender was last, return null, otherwise if new candidate is last,
             *  goto phase 2
             * Phase 2: compare all items before candidate to candidate
             *  If candidate always better, return it, otherwise return null
             *
             */
            // Record two method that are ambiguous for error reporting.
            CandidateFunctionMember ambig1 = null;
            CandidateFunctionMember ambig2 = null;
            bool ambiguous = false;
            CandidateFunctionMember candidate = list[0];

            for (int i = 1; i < list.Count; i++)
            {
                CandidateFunctionMember contender = list[i];
                Debug.Assert(candidate != contender);

                switch (WhichMethodIsBetter(candidate, contender, pTypeThrough, args))
                {
                case BetterType.Left:
                    ambiguous = false;      // (meaning m1 is better...)
                    break;

                case BetterType.Right:
                    ambiguous = false;
                    candidate = contender;
                    break;

                default:

                    // in case of tie we don't want to bother with the contender who tied...
                    ambig1 = candidate;
                    ambig2 = contender;

                    i++;
                    if (i < list.Count)
                    {
                        contender = list[i];
                        candidate = contender;
                    }
                    else
                    {
                        ambiguous = true;
                    }
                    break;
                }
            }

            if (!ambiguous)
            {
                // Now, compare the candidate with items previous to it...
                foreach (CandidateFunctionMember contender in list)
                {
                    if (contender == candidate)
                    {
                        // We hit our winner, so its good enough...
                        methAmbig1 = null;
                        methAmbig2 = null;
                        return(candidate);
                    }

                    switch (WhichMethodIsBetter(contender, candidate, pTypeThrough, args))
                    {
                    case BetterType.Right:

                        // meaning m2 is better
                        continue;

                    case BetterType.Same:
                    case BetterType.Neither:
                        ambig1 = candidate;
                        ambig2 = contender;
                        break;
                    }

                    break;
                }
            }

            // an ambiguous call. Return two of the ambiguous set.
            if (ambig1 != null & ambig2 != null)
            {
                methAmbig1 = ambig1;
                methAmbig2 = ambig2;
            }
            else
            {
                // For some reason, we have an ambiguity but never had a tie.
                // This can easily happen in a circular graph of candidate methods.
                methAmbig1 = list.First();
                methAmbig2 = list.Skip(1).First();
            }

            return(null);
        }
Exemplo n.º 2
0
        private BetterType WhichMethodIsBetterTieBreaker(
            CandidateFunctionMember node1,
            CandidateFunctionMember node2,
            CType pTypeThrough,
            ArgInfos args)
        {
            MethPropWithInst mpwi1 = node1.mpwi;
            MethPropWithInst mpwi2 = node2.mpwi;

            // Same signatures. If they have different lifting numbers, the smaller number wins.
            // Otherwise, if one is generic and the other isn't then the non-generic wins.
            // Otherwise, if one is expanded and the other isn't then the non-expanded wins.
            // Otherwise, if one has fewer modopts than the other then it wins.
            if (node1.ctypeLift != node2.ctypeLift)
            {
                return(node1.ctypeLift < node2.ctypeLift ? BetterType.Left : BetterType.Right);
            }

            // Non-generic wins.
            if (mpwi1.TypeArgs.Count != 0)
            {
                if (mpwi2.TypeArgs.Count == 0)
                {
                    return(BetterType.Right);
                }
            }
            else if (mpwi2.TypeArgs.Count != 0)
            {
                return(BetterType.Left);
            }

            // Non-expanded wins
            if (node1.fExpanded)
            {
                if (!node2.fExpanded)
                {
                    return(BetterType.Right);
                }
            }
            else if (node2.fExpanded)
            {
                return(BetterType.Left);
            }

            // See if one's parameter types (un-instantiated) are more specific.
            BetterType nT = GetGlobalSymbols().CompareTypes(
                RearrangeNamedArguments(mpwi1.MethProp().Params, mpwi1, pTypeThrough, args),
                RearrangeNamedArguments(mpwi2.MethProp().Params, mpwi2, pTypeThrough, args));

            if (nT == BetterType.Left || nT == BetterType.Right)
            {
                return(nT);
            }

            // Fewer modopts wins.
            if (mpwi1.MethProp().modOptCount != mpwi2.MethProp().modOptCount)
            {
                return(mpwi1.MethProp().modOptCount < mpwi2.MethProp().modOptCount ? BetterType.Left : BetterType.Right);
            }

            // Bona-fide tie.
            return(BetterType.Neither);
        }
Exemplo n.º 3
0
        ////////////////////////////////////////////////////////////////////////////////
        // Determine which method is better for the purposes of overload resolution.
        // Better means: as least as good in all params, and better in at least one param.
        // Better w/r to a param means is an ordering, from best down:
        // 1) same type as argument
        // 2) implicit conversion from argument to formal type
        // Because of user defined conversion opers this relation is not transitive.
        //
        // If there is a tie because of identical signatures, the tie may be broken by the
        // following rules:
        // 1) If one is generic and the other isn't, the non-generic wins.
        // 2) Otherwise if one is expanded (params) and the other isn't, the non-expanded wins.
        // 3) Otherwise if one has more specific parameter types (at the declaration) it wins:
        //    This occurs if at least on parameter type is more specific and no parameter type is
        //    less specific.
        //* A type parameter is less specific than a non-type parameter.
        //* A constructed type is more specific than another constructed type if at least
        //      one type argument is more specific and no type argument is less specific than
        //      the corresponding type args in the other.
        // 4) Otherwise if one has more modopts than the other does, the smaller number of modopts wins.
        //
        // Returns Left if m1 is better, Right if m2 is better, or Neither/Same

        private BetterType WhichMethodIsBetter(
            CandidateFunctionMember node1,
            CandidateFunctionMember node2,
            CType pTypeThrough,
            ArgInfos args)
        {
            MethPropWithInst mpwi1 = node1.mpwi;
            MethPropWithInst mpwi2 = node2.mpwi;

            // Substitutions should have already been done on these!
            TypeArray pta1 = RearrangeNamedArguments(node1.@params, mpwi1, pTypeThrough, args);
            TypeArray pta2 = RearrangeNamedArguments(node2.@params, mpwi2, pTypeThrough, args);

            // If the parameter types for both candidate methods are identical,
            // use the tie breaking rules.

            if (pta1 == pta2)
            {
                return(WhichMethodIsBetterTieBreaker(node1, node2, pTypeThrough, args));
            }

            //  Otherwise, do a parameter-by-parameter comparison:
            //
            // Given an argument list A with a set of argument expressions {E1, ... En} and
            // two applicable function members Mp and Mq with parameter types {P1,... Pn} and
            // {Q1, ... Qn}, Mp is defined to be a better function member than Mq if:
            //* for each argument the implicit conversion from Ex to Qx is not better than
            //   the implicit conversion from Ex to Px.
            //* for at least one argument, the conversion from Ex to Px is better than the
            //   conversion from Ex to Qx.

            BetterType betterMethod = BetterType.Neither;
            int        carg         = args.carg;

            for (int i = 0; i < carg; i++)
            {
                Expr  arg = args.fHasExprs ? args.prgexpr[i] : null;
                CType p1  = pta1[i];
                CType p2  = pta2[i];

                // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                // RUNTIME BINDER ONLY CHANGE
                // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                //
                // We need to consider conversions from the actual runtime type
                // since we could have private interfaces that we are converting

                CType argType = arg?.RuntimeObjectActualType ?? args.types[i];

                // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                // END RUNTIME BINDER ONLY CHANGE
                // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                BetterType betterConversion = WhichConversionIsBetter(argType, p1, p2);

                if (betterMethod == BetterType.Right)
                {
                    if (betterConversion == BetterType.Left)
                    {
                        betterMethod = BetterType.Neither;
                        break;
                    }
                }
                else if (betterMethod == BetterType.Left)
                {
                    if (betterConversion == BetterType.Right)
                    {
                        betterMethod = BetterType.Neither;
                        break;
                    }
                }
                else
                {
                    Debug.Assert(betterMethod == BetterType.Neither);
                    if (betterConversion == BetterType.Right || betterConversion == BetterType.Left)
                    {
                        betterMethod = betterConversion;
                    }
                }
            }

            // We may have different sizes if we had optional parameters. If thats the case,
            // the one with fewer parameters wins (ie less optional parameters) unless it is
            // expanded. If so, the one with more parameters wins (ie option beats expanded).
            if (pta1.Count != pta2.Count && betterMethod == BetterType.Neither)
            {
                if (node1.fExpanded)
                {
                    if (!node2.fExpanded)
                    {
                        return(BetterType.Right);
                    }
                }
                else if (node2.fExpanded)
                {
                    return(BetterType.Left);
                }

                // Here, if both methods needed to use optionals to fill in the signatures,
                // then we are ambiguous. Otherwise, take the one that didn't need any
                // optionals.

                if (pta1.Count == carg)
                {
                    return(BetterType.Left);
                }

                if (pta2.Count == carg)
                {
                    return(BetterType.Right);
                }

                return(BetterType.Neither);
            }

            return(betterMethod);
        }
Exemplo n.º 4
0
        ////////////////////////////////////////////////////////////////////////////////
        // Determine best method for overload resolution. Returns null if no best 
        // method, in which case two tying methods are returned for error reporting.

        protected CandidateFunctionMember FindBestMethod(
            List<CandidateFunctionMember> list,
            CType pTypeThrough,
            ArgInfos args,
            out CandidateFunctionMember methAmbig1,
            out CandidateFunctionMember methAmbig2)
        {
            Debug.Assert(list.Any());
            Debug.Assert(list.First().mpwi != null);
            Debug.Assert(list.Count > 0);

            // select the best method:
            /*
            Effectively, we pick the best item from a set using a non-transitive ranking function
            So, pick the first item (candidate) and compare against next (contender), if there is
                no next, goto phase 2
            If first is better, move to next contender, if none proceed to phase 2
            If second is better, make the contender the candidate and make the item following
                contender into the new contender, if there is none, goto phase 2
            If neither, make contender+1 into candidate and contender+2 into contender, if possible,
                otherwise, if contender was last, return null, otherwise if new candidate is last,
                goto phase 2
            Phase 2: compare all items before candidate to candidate
                If candidate always better, return it, otherwise return null

        */
            // Record two method that are ambiguous for error reporting.
            CandidateFunctionMember ambig1 = null;
            CandidateFunctionMember ambig2 = null;
            bool ambiguous = false;
            CandidateFunctionMember candidate = list[0];
            for (int i = 1; i < list.Count; i++)
            {
                CandidateFunctionMember contender = list[i];
                Debug.Assert(candidate != contender);

                BetterType result = WhichMethodIsBetter(candidate, contender, pTypeThrough, args);
                if (result == BetterType.Left)
                {
                    ambiguous = false;
                    continue;  // (meaning m1 is better...)
                }
                else if (result == BetterType.Right)
                {
                    ambiguous = false;
                    candidate = contender;
                }
                else
                {
                    // in case of tie we don't want to bother with the contender who tied...
                    ambig1 = candidate;
                    ambig2 = contender;

                    i++;
                    if (i < list.Count)
                    {
                        contender = list[i];
                        candidate = contender;
                    }
                    else
                    {
                        ambiguous = true;
                    }
                }
            }
            if (ambiguous)
                goto AMBIG;

            // Now, compare the candidate with items previous to it...
            foreach (CandidateFunctionMember contender in list)
            {
                if (contender == candidate)
                {
                    // We hit our winner, so its good enough...
                    methAmbig1 = null;
                    methAmbig2 = null;
                    return candidate;
                }
                BetterType result = WhichMethodIsBetter(contender, candidate, pTypeThrough, args);
                if (result == BetterType.Right)
                { // meaning m2 is better
                    continue;
                }
                else if (result == BetterType.Same || result == BetterType.Neither)
                {
                    ambig1 = candidate;
                    ambig2 = contender;
                }
                break;
            }

        AMBIG:
            // an ambig call. Return two of the ambiguous set.
            if (ambig1 != null && ambig2 != null)
            {
                methAmbig1 = ambig1;
                methAmbig2 = ambig2;
            }
            else
            {
                // For some reason, we have an ambiguity but never had a tie.
                // This can easily happen in a circular graph of candidate methods.
                methAmbig1 = list.First();
                methAmbig2 = list.Skip(1).First();
            }

            return null;
        }
Exemplo n.º 5
0
        protected BetterType WhichMethodIsBetterTieBreaker(
            CandidateFunctionMember node1,
            CandidateFunctionMember node2,
            CType pTypeThrough,
            ArgInfos args)
        {
            MethPropWithInst mpwi1 = node1.mpwi;
            MethPropWithInst mpwi2 = node2.mpwi;

            // Same signatures. If they have different lifting numbers, the smaller number wins.
            // Otherwise, if one is generic and the other isn't then the non-generic wins.
            // Otherwise, if one is expanded and the other isn't then the non-expanded wins.
            // Otherwise, if one has fewer modopts than the other then it wins.
            if (node1.ctypeLift != node2.ctypeLift)
            {
                return node1.ctypeLift < node2.ctypeLift ? BetterType.Left : BetterType.Right;
            }

            // Non-generic wins.
            if (mpwi1.TypeArgs.size != 0)
            {
                if (mpwi2.TypeArgs.size == 0)
                {
                    return BetterType.Right;
                }
            }
            else if (mpwi2.TypeArgs.size != 0)
            {
                return BetterType.Left;
            }

            // Non-expanded wins
            if (node1.fExpanded)
            {
                if (!node2.fExpanded)
                {
                    return BetterType.Right;
                }
            }
            else if (node2.fExpanded)
            {
                return BetterType.Left;
            }

            // See if one's parameter types (un-instantiated) are more specific.
            BetterType nT = GetGlobalSymbols().CompareTypes(
               RearrangeNamedArguments(mpwi1.MethProp().Params, mpwi1, pTypeThrough, args),
               RearrangeNamedArguments(mpwi2.MethProp().Params, mpwi2, pTypeThrough, args));
            if (nT == BetterType.Left || nT == BetterType.Right)
            {
                return nT;
            }

            // Fewer modopts wins.
            if (mpwi1.MethProp().modOptCount != mpwi2.MethProp().modOptCount)
            {
                return mpwi1.MethProp().modOptCount < mpwi2.MethProp().modOptCount ? BetterType.Left : BetterType.Right;
            }

            // Bona-fide tie.
            return BetterType.Neither;
        }
Exemplo n.º 6
0
        ////////////////////////////////////////////////////////////////////////////////
        // Determine which method is better for the purposes of overload resolution.
        // Better means: as least as good in all params, and better in at least one param.
        // Better w/r to a param means is an ordering, from best down:
        // 1) same type as argument
        // 2) implicit conversion from argument to formal type
        // Because of user defined conversion opers this relation is not transitive.
        //
        // If there is a tie because of identical signatures, the tie may be broken by the
        // following rules:
        // 1) If one is generic and the other isn't, the non-generic wins.
        // 2) Otherwise if one is expanded (params) and the other isn't, the non-expanded wins.
        // 3) Otherwise if one has more specific parameter types (at the declaration) it wins:
        //    This occurs if at least on parameter type is more specific and no parameter type is
        //    less specific.
        //* A type parameter is less specific than a non-type parameter.
        //* A constructed type is more specific than another constructed type if at least
        //      one type argument is more specific and no type argument is less specific than
        //      the corresponding type args in the other.
        // 4) Otherwise if one has more modopts than the other does, the smaller number of modopts wins.
        //
        // Returns Left if m1 is better, Right if m2 is better, or Neither/Same

        protected BetterType WhichMethodIsBetter(
            CandidateFunctionMember node1,
            CandidateFunctionMember node2,
            CType pTypeThrough,
            ArgInfos args)
        {
            MethPropWithInst mpwi1 = node1.mpwi;
            MethPropWithInst mpwi2 = node2.mpwi;

            // Substitutions should have already been done on these!
            TypeArray pta1 = RearrangeNamedArguments(node1.@params, mpwi1, pTypeThrough, args);
            TypeArray pta2 = RearrangeNamedArguments(node2.@params, mpwi2, pTypeThrough, args);

            // If the parameter types for both candidate methods are identical,
            // use the tie breaking rules.

            if (pta1 == pta2)
            {
                return WhichMethodIsBetterTieBreaker(node1, node2, pTypeThrough, args);
            }

            //  Otherwise, do a parameter-by-parameter comparison:
            //
            // Given an argument list A with a set of argument expressions {E1, ... En} and
            // two applicable function members Mp and Mq with parameter types {P1,... Pn} and
            // {Q1, ... Qn}, Mp is defined to be a better function member than Mq if:
            //* for each argument the implicit conversion from Ex to Qx is not better than
            //   the implicit conversion from Ex to Px.
            //* for at least one argument, the conversion from Ex to Px is better than the
            //   conversion from Ex to Qx.

            BetterType betterMethod = BetterType.Neither;
            CType type1 = pTypeThrough != null ? pTypeThrough : mpwi1.GetType();
            CType type2 = pTypeThrough != null ? pTypeThrough : mpwi2.GetType();
            MethodOrPropertySymbol methProp1 = GroupToArgsBinder.FindMostDerivedMethod(GetSymbolLoader(), mpwi1.MethProp(), type1);
            MethodOrPropertySymbol methProp2 = GroupToArgsBinder.FindMostDerivedMethod(GetSymbolLoader(), mpwi2.MethProp(), type2);
            List<Name> names1 = methProp1.ParameterNames;
            List<Name> names2 = methProp2.ParameterNames;

            for (int i = 0; i < args.carg; i++)
            {
                EXPR arg = args.fHasExprs ? args.prgexpr[i] : null;
                CType argType = args.types.Item(i);
                CType p1 = pta1.Item(i);
                CType p2 = pta2.Item(i);

                // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                // RUNTIME BINDER ONLY CHANGE
                // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                //
                // We need to consider conversions from the actual runtime type
                // since we could have private interfaces that we are converting

                if (arg.RuntimeObjectActualType != null)
                {
                    argType = arg.RuntimeObjectActualType;
                }

                // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                // END RUNTIME BINDER ONLY CHANGE
                // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                BetterType betterConversion = WhichConversionIsBetter(arg, argType, p1, p2);

                if (betterMethod == BetterType.Right && betterConversion == BetterType.Left)
                {
                    betterMethod = BetterType.Neither;
                    break;
                }
                else if (betterMethod == BetterType.Left && betterConversion == BetterType.Right)
                {
                    betterMethod = BetterType.Neither;
                    break;
                }
                else if (betterMethod == BetterType.Neither)
                {
                    if (betterConversion == BetterType.Right || betterConversion == BetterType.Left)
                    {
                        betterMethod = betterConversion;
                    }
                }
            }

            // We may have different sizes if we had optional parameters. If thats the case,
            // the one with fewer parameters wins (ie less optional parameters) unless it is
            // expanded. If so, the one with more parameters wins (ie option beats expanded).
            if (pta1.size != pta2.size && betterMethod == BetterType.Neither)
            {
                if (node1.fExpanded && !node2.fExpanded)
                {
                    return BetterType.Right;
                }
                else if (node2.fExpanded && !node1.fExpanded)
                {
                    return BetterType.Left;
                }

                // Here, if both methods needed to use optionals to fill in the signatures,
                // then we are ambiguous. Otherwise, take the one that didn't need any 
                // optionals.

                if (pta1.size == args.carg)
                {
                    return BetterType.Left;
                }
                else if (pta2.size == args.carg)
                {
                    return BetterType.Right;
                }
                return BetterType.Neither;
            }

            return betterMethod;
        }