/// <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);
        }
Example #2
0
        /// <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);
        }