/// <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); }
/// <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); }
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); } } }