Exemple #1
0
 /// <summary>
 ///     Adds a local name to this name resolver.</summary>
 /// <param name="name">
 ///     Specifies the identifier.</param>
 /// <param name="resolveToExpression">
 ///     The expression to which the specified identifier should resolve.</param>
 public void AddLocalName(string name, Expression resolveToExpression)
 {
     if (_localNames.ContainsKey(name))
     {
         throw new InvalidOperationException("The variable “{0}” cannot be redeclared because it is already used (perhaps in a parent scope) to denote something else.".Fmt(name));
     }
     _localNames[name] = new ResolveContextExpression(resolveToExpression);
 }
Exemple #2
0
 /// <summary>
 ///     Adds a local name to this name resolver.</summary>
 /// <param name="name">
 ///     Specifies the identifier.</param>
 /// <param name="resolveToExpression">
 ///     The expression to which the specified identifier should resolve.</param>
 public void AddLocalName(string name, Expression resolveToExpression)
 {
     if (_localNames.ContainsKey(name))
         throw new InvalidOperationException("The variable “{0}” cannot be redeclared because it is already used (perhaps in a parent scope) to denote something else.".Fmt(name));
     _localNames[name] = new ResolveContextExpression(resolveToExpression);
 }
Exemple #3
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);
                }
            }
        }