Exemple #1
0
        private bool fixIndependentTypeArguments(out bool any)
        {
            // •	All unfixed type variables Xi which do not depend on (§7.5.2.5) any Xj are fixed (§7.5.2.10).
            any = false;
            var needToFix = new bool[_genericParameters.Length];

            for (int i = 0; i < needToFix.Length; i++)
            {
                needToFix[i] = !_fixed[i];
            }

            for (int p = 0; p < _parameters.Length; p++)
            {
                foreach (var idx in getContainedIn(ParserUtil.GetDelegateReturnType(_parameters[p].ParameterType)))
                {
                    needToFix[idx] = false;
                }
            }

            for (int i = 0; i < needToFix.Length; i++)
            {
                if (!needToFix[i])
                {
                    continue;
                }
                if (!fix(i))
                {
                    return(false);
                }
                any = true;
            }

            return(true);
        }
Exemple #2
0
        public int Better(CandidateInfo <T> other)
        {
            // §7.5.3.2 Better function member
            // First check whether all the applicable parameter types are equivalent, in which case the tie-breaking rules apply
            if (EquivalentSpecifiedParameterTypes(other))
            {
                // • If MP is a non-generic method and MQ is a generic method, then MP is better than MQ.
                var mgen = Member.IsGenericMethod();
                if (mgen != other.Member.IsGenericMethod())
                {
                    return(mgen ? 1 : -1);
                }

                // • If MP is applicable in its normal form and MQ has a params array and is applicable only in its expanded form, then MP is better than MQ.
                if (IsExpandedForm != other.IsExpandedForm)
                {
                    return(IsExpandedForm ? 1 : -1);
                }

                // • If MP has more declared parameters than MQ, then MP is better than MQ. This can occur if both methods have params arrays and are applicable only in their expanded forms.
                if (OriginalNumberOfParameters != other.OriginalNumberOfParameters)
                {
                    return(OriginalNumberOfParameters < other.OriginalNumberOfParameters ? 1 : -1);
                }

                // • If all parameters of MP have a corresponding argument whereas default arguments need to be substituted for at least one optional parameter in MQ then MP is better than MQ.
                var any1 = Parameters.Any(p => p.ArgumentIndex == -1);
                var any2 = other.Parameters.Any(p => p.ArgumentIndex == -1);
                if (any1 != any2)
                {
                    return(any1 ? 1 : -1);
                }

                // • If MP has more specific parameter types than MQ, then MP is better than MQ.
                int isMoreSpecific = ParserUtil.Better(Parameters.Select(p => p.UninstantiatedParameterType), other.Parameters.Select(p => p.UninstantiatedParameterType), ParserUtil.MoreSpecific);
                if (isMoreSpecific != 0)
                {
                    return(isMoreSpecific);
                }

                // • If one member is a non-lifted operator and the other is a lifted operator, the non-lifted one is better.
                if (IsLiftedOperator != other.IsLiftedOperator)
                {
                    return(IsLiftedOperator ? 1 : -1);
                }

                // • Otherwise, neither function member is better.
                return(0);
            }

            return(ParserUtil.Better(ParametersInSpecifiedOrder(), other.ParametersInSpecifiedOrder(), (p1, p2) =>
            {
                var c1 = Conversion.Implicit(p1.Argument, p1.ParameterType);
                var c2 = Conversion.Implicit(p2.Argument, p2.ParameterType);

                // Given an implicit conversion C1 that converts from an expression E to a type T1, and an implicit conversion C2 that
                // converts from an expression E to a type T2, C1 is a better conversion than C2 if at least one of the following holds:
                // • E has a type S and an identity conversion exists from S to T1 but not from S to T2
                if (c1 is IdentityConversion != c2 is IdentityConversion)
                {
                    return c1 is IdentityConversion ? -1 : 1;
                }

                // • E is not an anonymous function and T1 is a better conversion target than T2 (§7.5.3.5)
                var isAnonymousFunction = p1.Argument is ResolveContextExpression && ((ResolveContextExpression)p1.Argument).WasAnonymousFunction;
                if (!isAnonymousFunction)
                {
                    return betterConversionTarget(p1.ParameterType, p2.ParameterType);
                }

                // • E is an anonymous function, T1 is either a delegate type D1 or an expression tree type Expression<D1>, T2 is either a delegate type D2 or an expression tree type Expression<D2> and one of the following holds:
                //     o D1 is a better conversion target than D2
                //     o D1 and D2 have identical parameter lists, and one of the following holds:
                //          • D1 has a return type Y1, and D2 has a return type Y2, an inferred return type X exists for E in the context of that parameter list (§7.5.2.12), and the conversion from X to Y1 is better than the conversion from X to Y2
                //          • D1 has a return type Y, and D2 is void returning
                else
                {
                    var del1 = ParserUtil.GetDelegateType(p1.ParameterType);
                    var del2 = ParserUtil.GetDelegateType(p2.ParameterType);
                    if (del1 == null || del2 == null)
                    {
                        throw new InternalErrorException("Internal error 23087");
                    }
                    var del1param = ParserUtil.GetDelegateParameterTypes(p1.ParameterType);
                    var del2param = ParserUtil.GetDelegateParameterTypes(p2.ParameterType);
                    if (del1param.SequenceEqual(del2param))
                    {
                        var ret1 = ParserUtil.GetDelegateReturnType(p1.ParameterType);
                        var ret2 = ParserUtil.GetDelegateReturnType(p2.ParameterType);
                        if ((ret1 == typeof(void)) != (ret2 == typeof(void)))
                        {
                            return (ret1 == typeof(void)) ? 1 : -1;
                        }
                        var realRet = ParserUtil.GetDelegateReturnType(((ResolveContextExpression)p1.Argument).ExpressionType);
                        var rc1 = Conversion.Implicit(realRet, ret1);
                        var rc2 = Conversion.Implicit(realRet, ret2);
                        if (rc1 is IdentityConversion != rc2 is IdentityConversion)
                        {
                            return rc1 is IdentityConversion ? -1 : 1;
                        }
                        return betterConversionTarget(ret1, ret2);
                    }
                    return betterConversionTarget(del1, del2);
                }
            }));
        }
Exemple #3
0
        public static CandidateInfo <T> ResolveOverloads <T>(List <Tuple <T, ParameterInfo[]> > overloads, IEnumerable <ArgumentInfo> arguments, NameResolver resolver) where T : MemberInfo
        {
            var candidates = overloads.SelectMany(ov => ParserUtil.EvaluateArgumentList(ov.Item1, ov.Item2, arguments, resolver)).ToList();

            // Type inference
            for (int i = 0; i < candidates.Count; i++)
            {
                if (candidates[i].Member is MethodInfo && ((MethodInfo)(MemberInfo)candidates[i].Member).IsGenericMethodDefinition)
                {
                    candidates[i] = TypeInferer.TypeInference(candidates[i], resolver);
                }
            }

            // Remove nulls (entries where type inference failed) and entries that are not applicable (§7.5.3.1 Applicable function member)
            candidates = candidates.Where(c => c != null && c.Parameters.All(p => p.Mode == ArgumentMode.In
                ? Conversion.Implicit(p.Argument, p.ParameterType) != null
                : p.ParameterType == p.Argument.ExpressionType)).ToList();

            if (candidates.Count == 0)
            {
                return(null);
            }
            if (candidates.Count == 1)
            {
                return(candidates[0]);
            }

            // We have more than one candidate, so need to find the “best” one
            bool[] cannot = new bool[candidates.Count];
            for (int i = 0; i < cannot.Length; i++)
            {
                for (int j = i + 1; j < cannot.Length; j++)
                {
                    int compare = candidates[i].Better(candidates[j]);
                    if (compare != 1) // j is not better
                    {
                        cannot[j] = true;
                    }
                    if (compare != -1) // i is not better
                    {
                        cannot[i] = true;
                    }
                }
            }

            CandidateInfo <T> candidate = null;

            for (int i = 0; i < cannot.Length; i++)
            {
                if (!cannot[i])
                {
                    if (candidate == null)
                    {
                        candidate = candidates[i];
                    }
                    else
                    {
                        // There is more than one applicable candidate — method call is ambiguous
                        return(null);
                    }
                }
            }

            // Either candidate == null, in which case no candidate was better than all others, or this is the successful candidate
            return(candidate);
        }
Exemple #4
0
        private bool infer()
        {
            // First of all, make sure that all the lambda expressions have the right number of parameters,
            // and that we don’t have lambdas where there is no delegate or expression-tree type
            for (int i = 0; i < _arguments.Length; i++)
            {
                var lambda = _arguments[i] as ResolveContextLambda;
                if (lambda == null)
                {
                    continue;
                }
                var dlgParam = ParserUtil.GetDelegateParameterTypes(_parameters[i].ParameterType);
                if (dlgParam == null || dlgParam.Count() != lambda.Lambda.Parameters.Count)
                {
                    return(false);
                }
            }

            // §7.5.2.1 The first phase
            for (int p = 0; p < _arguments.Length; p++)
            {
                // If Ei is an anonymous function, an explicit parameter type inference (§7.5.2.7) is made from Ei to Ti.
                // This applies only if the anonymous function is explicitly-typed, in which case it will already have been resolved to an expression tree
                var expr = _arguments[p] as ResolveContextExpression;
                if (expr != null && expr.Expression is LambdaExpression)
                {
                    if (!explicitParameterTypeInference((LambdaExpression)expr.Expression, _parameters[p].ParameterType))
                    {
                        return(false);
                    }
                }
                else if (_arguments[p] is ResolveContextLambda)
                {
                    // Ignore implicitly-typed lambdas for now
                }
                // Otherwise, if Ei has a type U and xi is a value parameter then a lower-bound inference is made from U to Ti.
                else if (_parameters[p].Mode == ArgumentMode.In)
                {
                    if (!lowerBoundInference(_arguments[p].ExpressionType, _parameters[p].ParameterType))
                    {
                        return(false);
                    }
                }
                else
                {
                    if (!exactInference(_arguments[p].ExpressionType, _parameters[p].ParameterType))
                    {
                        return(false);
                    }
                }
            }

            // §7.5.2.2 The second phase
            while (true)
            {
                // If no unfixed type parameters exist then type inference succeeds.
                if (_fixed.All(b => b))
                {
                    // Change all the implicitly-typed lambda expressions to explicit ones
                    for (int p = 0; p < _parameters.Length; p++)
                    {
                        var lambda = _arguments[p] as ResolveContextLambda;
                        if (lambda == null)
                        {
                            continue;
                        }
                        var dlgParameterTypes = ParserUtil.GetDelegateParameterTypes(_parameters[p].ParameterType);
                        var linqExpr          = lambda.Lambda.ToLinqExpression(_resolver, dlgParameterTypes.Select(dlgp => substituteFixed(dlgp)).ToArray(), false);
                        _arguments[p] = new ResolveContextExpression(linqExpr, wasAnonymousFunction: true);
                    }
                    return(true);
                }

                // If there exists one or more arguments Ei with corresponding parameter type Ti such that the output type of Ei with type Ti
                // contains at least one unfixed type parameter Xj, and none of the input types of Ei with type Ti contains any unfixed type
                // parameter Xj, then an output type inference is made from all such Ei to Ti.
                // (Plain English: infer the return types of all the lambda expressions where all the input types to the lambda are fixed.)
                // (While we’re at it, we will also collection information about which parameter types are still dependent on each other.)
                var isDependent = new bool[_genericParameters.Length];
                for (int p = 0; p < _parameters.Length; p++)
                {
                    // Is it a lambda?
                    var arg = _arguments[p] as ResolveContextLambda;
                    if (arg != null)
                    {
                        var dlgParameterTypes = ParserUtil.GetDelegateParameterTypes(_parameters[p].ParameterType);
                        if (dlgParameterTypes == null)
                        {
                            throw new InvalidOperationException("Cannot apply lambda expression argument “{0}” to a parameter that is not of a delegate or expression-tree type.".Fmt(arg.Lambda.ToString()));
                        }
                        var dlgReturnType     = ParserUtil.GetDelegateReturnType(_parameters[p].ParameterType);
                        var occurInReturnType = getContainedIn(dlgReturnType).ToArray();

                        // Any unfixed parameters ⇒ not interested
                        if (dlgParameterTypes.SelectMany(t => getContainedIn(t)).Any(i => !_fixed[i]))
                        {
                            // An unfixed type parameter occurs in the delegate parameter types, so the
                            // type parameters that occur in the delegate return type depend on it
                            foreach (var inRet in occurInReturnType)
                            {
                                isDependent[inRet] = true;
                            }
                            continue;
                        }

                        // Generate the lambda expression so it becomes explicitly typed and determine its return type
                        var linqExpr      = arg.Lambda.ToLinqExpression(_resolver, dlgParameterTypes.Select(dlgp => substituteFixed(dlgp)).ToArray(), false);
                        var argReturnType = ((LambdaExpression)linqExpr).ReturnType;
                        _arguments[p] = new ResolveContextExpression(linqExpr, wasAnonymousFunction: true);
                        if (!lowerBoundInference(argReturnType, dlgReturnType))
                        {
                            return(false);
                        }
                    }
                    else
                    {
                        // Is it a method group?
                        var mg = _arguments[p] as ResolveContextMethodGroup;
                        if (mg != null)
                        {
                            throw new NotImplementedException();
                        }
                    }
                }

                // Whether or not the previous step actually made an inference, we must now fix at least one type parameter, as follows:

                // If there exists one or more type parameters Xi such that Xi is unfixed, and Xi has a non-empty set of bounds, and Xi
                // does not depend on any Xj then each such Xi is fixed. If any fixing operation fails then type inference fails.
                bool any = false;
                for (int i = 0; i < _genericParameters.Length; i++)
                {
                    if (!_fixed[i] && _bounds[i] != null && !isDependent[i])
                    {
                        if (!fix(i))
                        {
                            return(false);
                        }
                        any = true;
                    }
                }

                if (!any)
                {
                    // Otherwise, if there exists one or more type parameters Xi such that Xi is unfixed, and Xi has a non-empty set of bounds,
                    // and there is at least one type parameter Xj that depends on Xi then each such Xi is fixed. If any fixing operation fails
                    // then type inference fails.
                    for (int i = 0; i < _genericParameters.Length; i++)
                    {
                        if (!_fixed[i] && _bounds[i] != null)
                        {
                            if (!fix(i))
                            {
                                return(false);
                            }
                        }
                        any = true;
                    }
                }

                if (!any)
                {
                    // Otherwise, we are unable to make progress and there are unfixed parameters. Type inference fails.
                    return(false);
                }
            }
        }