/// <summary> /// Removes unused declared variables from the specified block. /// </summary> /// <param name="block">The block to remove unused declared variables from.</param> /// <returns>The result of removing unused variables.</returns> /// <example> /// <code>{ int x, int y; x + 1; }</code> /// becomes /// <code>{ int x; x + 1; }</code> /// </example> private static BlockExpression RemoveUnusedVariables(BlockExpression block) { var variables = block.Variables; var n = variables.Count; if (n > 0) { // REVIEW: This does a lot of repeated scanning of the same child nodes. // We could consider storing these results for later reuse. var finder = new FreeVariableFinder(); finder.Visit(block.Expressions); var freeVariables = finder.FreeVariables; var remainingVariables = default(List <ParameterExpression>); for (var i = 0; i < n; i++) { var variable = variables[i]; if (!freeVariables.Contains(variable)) { if (remainingVariables == null) { remainingVariables = new List <ParameterExpression>(n); for (var j = 0; j < i; j++) { remainingVariables.Add(variables[j]); } } } else { remainingVariables?.Add(variable); } } if (remainingVariables != null) { return(block.Update(remainingVariables, block.Expressions)); } } return(block); }
/// <summary> /// Tries to beta-reduce the specified <paramref name="expression"/> if it's semantically sound to do so. This /// includes preserving the order and plurality of side-effects of the arguments passed to the lambda expression. /// </summary> /// <param name="expression">The expression to beta-reduce.</param> /// <param name="result">The result of beta-reducing the specified <paramref name="expression"/>, if semantically sound to do so.</param> /// <returns><c>true</c> if a beta reduction was performed; otherwise, <c>false</c>.</returns> public bool TryReduce(InvocationExpression expression, out Expression result) { var lambda = (LambdaExpression)expression.Expression; var parameters = lambda.Parameters; var arguments = expression.Arguments; var n = arguments.Count; if (n == 0) { result = lambda.Body; return(true); } var findRestrictions = new FindInliningRestrictions(); var bindings = new Dictionary <ParameterExpression, Binding>(n); var bindingOrder = new List <ParameterExpression>(n); for (var i = 0; i < n; i++) { var parameter = parameters[i]; var argument = arguments[i]; findRestrictions.Visit(argument); if (findRestrictions.Unsafe) { break; } var fvs = new FreeVariableFinder(); fvs.Visit(argument); var binding = new Binding { Argument = argument, FreeVariables = fvs.FreeVariables, IsConstant = _provider.HasConstantValue(argument), IsPure = _provider.IsPure(argument), CanRepeat = CanInlineMany(argument), }; bindings.Add(parameter, binding); if (!binding.IsPure) { bindingOrder.Add(parameter); } } // // If we found any lval in an expression we'd be inlining, we can't proceed because we risk reordering // reads and writes. For example: // // (x => a + x)(a = 1) // // would evaluate as: // // a = 1; // (x => a + x)(a); // // and produces 2. However, if we inline the side-effect of assignment, we end up with: // // a + (a = 1) // // which will read the original value of a for the left operand. // // CONSIDER: The analysis could be made more precise to pick up on the storage locations being written // to and the reads being performed by the lambda body. // if (!findRestrictions.Unsafe) { var impl = new Impl(this, bindings, bindingOrder); var tmp = impl.Visit(lambda.Body); impl.EnsureAllBound(); if (impl.Status == Status.AllBound) { result = tmp; return(true); } } result = null; return(false); }