Exemplo n.º 1
0
        public override BoundNode VisitBinaryOperator(BoundBinaryOperator node)
        {
            BoundSpillSequenceBuilder builder = null;
            var             right             = VisitExpression(ref builder, node.Right);
            BoundExpression left;

            if (builder == null)
            {
                left = VisitExpression(ref builder, node.Left);
            }
            else
            {
                var leftBuilder = new BoundSpillSequenceBuilder();
                left = VisitExpression(ref leftBuilder, node.Left);
                left = Spill(leftBuilder, left);
                if (node.OperatorKind == BinaryOperatorKind.LogicalBoolOr || node.OperatorKind == BinaryOperatorKind.LogicalBoolAnd)
                {
                    leftBuilder.AddStatement(_F.If(
                                                 node.OperatorKind == BinaryOperatorKind.LogicalBoolAnd ? left : _F.Not(left),
                                                 UpdateStatement(builder, _F.Assignment(left, right), substituteTemps: false)));

                    return(UpdateExpression(leftBuilder, left));
                }
                else
                {
                    // if the right-hand-side has await, spill the left
                    leftBuilder.Include(builder);
                    builder = leftBuilder;
                }
            }

            return(UpdateExpression(builder, node.Update(node.OperatorKind, left, right, node.ConstantValue, node.MethodOpt, node.ResultKind, node.Type)));
        }
Exemplo n.º 2
0
        public override BoundNode VisitCall(BoundCall node)
        {
            BoundSpillSequenceBuilder builder = null;
            var arguments = this.VisitExpressionList(ref builder, node.Arguments, node.ArgumentRefKindsOpt);

            BoundExpression receiver = null;

            if (builder == null)
            {
                receiver = VisitExpression(ref builder, node.ReceiverOpt);
            }
            else if (!node.Method.IsStatic)
            {
                // spill the receiver if there were await expressions in the arguments
                var receiverBuilder = new BoundSpillSequenceBuilder();

                receiver = node.ReceiverOpt;
                RefKind refKind = ReceiverSpillRefKind(receiver);

                receiver = Spill(receiverBuilder, VisitExpression(ref receiverBuilder, receiver), refKind: refKind);
                receiverBuilder.Include(builder);
                builder = receiverBuilder;
            }

            return(UpdateExpression(builder, node.Update(receiver, node.Method, arguments)));
        }
Exemplo n.º 3
0
        public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
        {
            BoundSpillSequenceBuilder builder = null;
            var             right             = VisitExpression(ref builder, node.Right);
            BoundExpression left;

            if (builder == null || node.Left.Kind == BoundKind.Local)
            {
                left = VisitExpression(ref builder, node.Left);
            }
            else
            {
                // if the right-hand-side has await, spill the left
                var leftBuilder = new BoundSpillSequenceBuilder();
                left = VisitExpression(ref leftBuilder, node.Left);
                if (left.Kind != BoundKind.Local)
                {
                    left = Spill(leftBuilder, left, RefKind.Ref);
                }

                leftBuilder.Include(builder);
                builder = leftBuilder;
            }

            return(UpdateExpression(builder, node.Update(left, right, node.RefKind, node.Type)));
        }
Exemplo n.º 4
0
        private void OnAssignment(BinaryExpression node)
        {
            BoundSpillSequenceBuilder builder = null;
            var        right = VisitExpression(ref builder, node.Right);
            Expression left;

            if (builder == null || node.Left.Entity.EntityType == EntityType.Local)
            {
                left = VisitExpression(ref builder, node.Left);
            }
            else
            {
                // if the right-hand-side has await, spill the left
                var leftBuilder = new BoundSpillSequenceBuilder();
                left = VisitExpression(ref leftBuilder, node.Left);
                if (left.Entity.EntityType == EntityType.Local)
                {
                    left = Spill(leftBuilder, left, true);
                }

                leftBuilder.Include(builder);
                builder = leftBuilder;
            }
            node.Left  = left;
            node.Right = right;
            ReplaceCurrentNode(UpdateExpression(builder, node));
        }
Exemplo n.º 5
0
        public override void OnBinaryExpression(BinaryExpression node)
        {
            if (node.Operator == BinaryOperatorType.Assign)
            {
                OnAssignment(node);
                return;
            }
            if (node.Operator == BinaryOperatorType.TypeTest)
            {
                OnIsaOperator(node);
                return;
            }

            BoundSpillSequenceBuilder builder = null;
            var        right = VisitExpression(ref builder, node.Right);
            Expression left;

            if (builder == null)
            {
                left = VisitExpression(ref builder, node.Left);
            }
            else
            {
                var leftBuilder = new BoundSpillSequenceBuilder();
                left = VisitExpression(ref leftBuilder, node.Left);
                left = Spill(leftBuilder, left);
                if (node.Operator == BinaryOperatorType.Or || node.Operator == BinaryOperatorType.And)
                {
                    var tmp = _F.DeclareTempLocal(_currentMethod, node.ExpressionType);
                    tmp.Local["SynthesizedKind"] = AWAIT_SPILL_MARKER;
                    leftBuilder.AddLocal(tmp);
                    leftBuilder.AddStatement(new ExpressionStatement(_F.CreateAssignment(_F.CreateLocalReference(tmp), left)));
                    var trueBlock = new Block();
                    trueBlock.Add(UpdateExpression(builder, _F.CreateAssignment(_F.CreateLocalReference(tmp), right)));
                    leftBuilder.AddStatement(
                        new IfStatement(left.LexicalInfo,
                                        node.Operator == BinaryOperatorType.And ?
                                        _F.CreateLocalReference(tmp) :
                                        (Expression)_F.CreateNotExpression(_F.CreateLocalReference(tmp)),
                                        trueBlock,
                                        null));

                    ReplaceCurrentNode(UpdateExpression(leftBuilder, _F.CreateLocalReference(tmp)));
                    return;
                }
                // if the right-hand-side has await, spill the left
                leftBuilder.Include(builder);
                builder = leftBuilder;
            }

            node.Left  = left;
            node.Right = right;
            ReplaceCurrentNode(UpdateExpression(builder, node));
        }
Exemplo n.º 6
0
        public override void OnMethodInvocationExpression(MethodInvocationExpression node)
        {
            BoundSpillSequenceBuilder builder = null;
            var entity = node.Target.Entity;

            if (entity.EntityType == EntityType.BuiltinFunction)
            {
                if (entity == BuiltinFunction.Eval)
                {
                    OnEval(node);
                }
                else if (entity == BuiltinFunction.Switch)
                {
                    throw new ArgumentException("Should be unreachable: Await spiller on switch");
                }
                else
                {
                    base.OnMethodInvocationExpression(node);
                }
                return;
            }

            var method = (IMethod)entity;
            var refs   = method.GetParameters().Select(p => p.IsByRef).ToList();

            node.Arguments = VisitExpressionList(ref builder, node.Arguments, refs);

            if (builder == null)
            {
                node.Target = VisitExpression(ref builder, node.Target);
            }
            else if (!method.IsStatic)
            {
                // spill the receiver if there were await expressions in the arguments
                var targetBuilder = new BoundSpillSequenceBuilder();

                var target = node.Target;
                var isRef  = TargetSpillRefKind(target);

                node.Target = Spill(targetBuilder, VisitExpression(ref targetBuilder, target), isRef);
                targetBuilder.Include(builder);
                builder = targetBuilder;
            }

            ReplaceCurrentNode(UpdateExpression(builder, node));
        }
Exemplo n.º 7
0
        public override BoundNode VisitArrayCreation(BoundArrayCreation node)
        {
            BoundSpillSequenceBuilder builder = null;
            var             init = (BoundArrayInitialization)VisitExpression(ref builder, node.InitializerOpt);
            BoundExpression bound;

            if (builder == null)
            {
                bound = VisitExpression(ref builder, node.Size);
            }
            else
            {
                // spill bounds expressions if initializers contain await
                var boundsBuilder = new BoundSpillSequenceBuilder();
                bound = VisitExpression(ref boundsBuilder, node.Size);
                boundsBuilder.Include(builder);
                builder = boundsBuilder;
            }

            return(UpdateExpression(builder, node.Update(bound, init, node.Type)));
        }
Exemplo n.º 8
0
        public override BoundNode VisitPointerElementAccess(BoundPointerElementAccess node)
        {
            BoundSpillSequenceBuilder builder = null;
            var             index             = VisitExpression(ref builder, node.Index);
            BoundExpression expression;

            if (builder == null)
            {
                expression = VisitExpression(ref builder, node.Expression);
            }
            else
            {
                var expressionBuilder = new BoundSpillSequenceBuilder();
                expression = VisitExpression(ref expressionBuilder, node.Expression);
                expression = Spill(expressionBuilder, expression);
                expressionBuilder.Include(builder);
                builder = expressionBuilder;
            }

            return(UpdateExpression(builder, node.Update(expression, index, node.Checked, node.Type)));
        }
Exemplo n.º 9
0
        private BoundExpression VisitExpression(ref BoundSpillSequenceBuilder builder, BoundExpression expression)
        {
            var e = (BoundExpression)this.Visit(expression);

            if (e == null || e.Kind != SpillSequenceBuilderKind)
            {
                return(e);
            }

            var newBuilder = (BoundSpillSequenceBuilder)e;

            if (builder == null)
            {
                builder = newBuilder.Update(null);
            }
            else
            {
                builder.Include(newBuilder);
            }

            return(newBuilder.Value);
        }
Exemplo n.º 10
0
        public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node)
        {
            BoundSpillSequenceBuilder builder = null;
            var right = VisitExpression(ref builder, node.Right);

            BoundExpression left = node.Left;

            if (builder == null)
            {
                left = VisitExpression(ref builder, left);
            }
            else
            {
                // if the right-hand-side has await, spill the left
                var leftBuilder = new BoundSpillSequenceBuilder();

                switch (left.Kind)
                {
                case BoundKind.Local:
                case BoundKind.Parameter:
                    // locals and parameters are directly assignable, LHS is not on the stack so nothing to spill
                    break;

                case BoundKind.FieldAccess:
                    var field = (BoundFieldAccess)left;
                    // static fields are directly assignable, LHS is not on the stack, nothing to spill
                    if (field.FieldSymbol.IsStatic)
                    {
                        break;
                    }

                    // instance fields are directly assignable, but receiver is pushed, so need to spill that.
                    var receiver = VisitExpression(ref leftBuilder, field.ReceiverOpt);
                    receiver = Spill(builder, receiver, field.FieldSymbol.ContainingType.IsValueType ? RefKind.Ref : RefKind.None);
                    left     = field.Update(receiver, field.FieldSymbol, field.ConstantValueOpt, field.ResultKind, field.Type);
                    break;

                case BoundKind.ArrayAccess:
                    var arrayAccess = (BoundArrayAccess)left;
                    // array and indices are pushed on stack so need to spill that
                    var expression = VisitExpression(ref leftBuilder, arrayAccess.Expression);
                    expression = Spill(builder, expression, RefKind.None);
                    var index = this.VisitExpression(ref builder, arrayAccess.Index);
                    left = arrayAccess.Update(expression, index, arrayAccess.Type);
                    break;

                default:
                    // must be something indirectly assignable, just visit and spill as an ordinary Ref  (not a RefReadOnly!!)
                    //
                    // NOTE: in some cases this will result in spiller producing an error.
                    //       For example if the LHS is a ref-returning method like
                    //
                    //       obj.RefReturning(a, b, c) = await Something();
                    //
                    //       the spiller would eventually have to spill the evaluation result of "refReturning" call as an ordinary Ref,
                    //       which it can't.
                    left = Spill(leftBuilder, VisitExpression(ref leftBuilder, left), RefKind.Ref);
                    break;
                }

                leftBuilder.Include(builder);
                builder = leftBuilder;
            }

            return(UpdateExpression(builder, node.Update(left, right, node.IsRef, node.Type)));
        }
Exemplo n.º 11
0
        private BoundExpression Spill(
            BoundSpillSequenceBuilder builder,
            BoundExpression expression,
            RefKind refKind      = RefKind.None,
            bool sideEffectsOnly = false)
        {
            Debug.Assert(builder != null);

            while (true)
            {
                switch (expression.Kind)
                {
                case BoundKind.ArrayInitialization:
                    Debug.Assert(refKind == RefKind.None);
                    Debug.Assert(!sideEffectsOnly);
                    var arrayInitialization = (BoundArrayInitialization)expression;
                    var newInitializers     = VisitExpressionList(ref builder, arrayInitialization.Initializers, forceSpill: true);
                    return(arrayInitialization.Update(newInitializers));

                case BoundKind.ArgListOperator:
                    Debug.Assert(refKind == RefKind.None);
                    Debug.Assert(!sideEffectsOnly);
                    var argumentList = (BoundArgListOperator)expression;
                    var newArgs      = VisitExpressionList(ref builder, argumentList.Arguments, argumentList.ArgumentRefKindsOpt, forceSpill: true);
                    return(argumentList.Update(newArgs, argumentList.ArgumentRefKindsOpt, argumentList.Type));

                case SpillSequenceBuilderKind:
                    var sequenceBuilder = (BoundSpillSequenceBuilder)expression;
                    builder.Include(sequenceBuilder);
                    expression = sequenceBuilder.Value;
                    continue;

                case BoundKind.Sequence:
                    // neither the side-effects nor the value of the sequence contains await
                    // (otherwise it would be converted to a SpillSequenceBuilder).
                    if (refKind != RefKind.None)
                    {
                        return(expression);
                    }

                    goto default;

                case BoundKind.ThisReference:
                case BoundKind.BaseReference:
                    if (refKind != RefKind.None || expression.Type.IsReferenceType)
                    {
                        return(expression);
                    }

                    goto default;

                case BoundKind.Parameter:
                    if (refKind != RefKind.None)
                    {
                        return(expression);
                    }

                    goto default;

                case BoundKind.Local:
                    var local = (BoundLocal)expression;
                    if (local.LocalSymbol.SynthesizedKind == SynthesizedLocalKind.Spill || refKind != RefKind.None)
                    {
                        return(local);
                    }

                    goto default;

                case BoundKind.FieldAccess:
                    var field       = (BoundFieldAccess)expression;
                    var fieldSymbol = field.FieldSymbol;
                    if (fieldSymbol.IsStatic)
                    {
                        // no need to spill static fields if used as locations or if readonly
                        if (refKind != RefKind.None || fieldSymbol.IsLet)
                        {
                            return(field);
                        }
                        goto default;
                    }

                    if (refKind == RefKind.None)
                    {
                        goto default;
                    }

                    var receiver = Spill(builder, field.ReceiverOpt, fieldSymbol.ContainingType.IsValueType ? refKind : RefKind.None);
                    return(field.Update(receiver, fieldSymbol, field.ConstantValueOpt, field.ResultKind, field.Type));

                case BoundKind.Literal:
                case BoundKind.TypeExpression:
                    return(expression);

                case BoundKind.ConditionalReceiver:
                    // we will rewrite this as a part of rewriting whole LoweredConditionalAccess
                    // later, if needed
                    return(expression);

                default:
                    if (expression.Type.SpecialType == SpecialType.System_Void || sideEffectsOnly)
                    {
                        builder.AddStatement(_F.ExpressionStatement(expression));
                        return(null);
                    }
                    else
                    {
                        BoundAssignmentOperator assignToTemp;

                        var replacement = _F.StoreToTemp(
                            expression,
                            out assignToTemp,
                            refKind: refKind,
                            kind: SynthesizedLocalKind.Spill,
                            syntaxOpt: _F.Syntax);

                        builder.AddLocal(replacement.LocalSymbol);
                        builder.AddStatement(_F.ExpressionStatement(assignToTemp));
                        return(replacement);
                    }
                }
            }
        }
Exemplo n.º 12
0
        private BoundExpression Spill(
            BoundSpillSequenceBuilder builder,
            BoundExpression expression,
            RefKind refKind      = RefKind.None,
            bool sideEffectsOnly = false)
        {
            Debug.Assert(builder != null);

            while (true)
            {
                switch (expression.Kind)
                {
                case BoundKind.ArrayInitialization:
                    Debug.Assert(refKind == RefKind.None);
                    Debug.Assert(!sideEffectsOnly);
                    var arrayInitialization = (BoundArrayInitialization)expression;
                    var newInitializers     = VisitExpressionList(ref builder, arrayInitialization.Initializers, forceSpill: true);
                    return(arrayInitialization.Update(newInitializers));

                case BoundKind.ArgListOperator:
                    Debug.Assert(refKind == RefKind.None);
                    Debug.Assert(!sideEffectsOnly);
                    var argumentList = (BoundArgListOperator)expression;
                    var newArgs      = VisitExpressionList(ref builder, argumentList.Arguments, argumentList.ArgumentRefKindsOpt, forceSpill: true);
                    return(argumentList.Update(newArgs, argumentList.ArgumentRefKindsOpt, argumentList.Type));

                case SpillSequenceBuilder:
                    var sequenceBuilder = (BoundSpillSequenceBuilder)expression;
                    builder.Include(sequenceBuilder);
                    expression = sequenceBuilder.Value;
                    continue;

                case BoundKind.Sequence:
                    // We don't need promote short-lived variables defined by the sequence to long-lived,
                    // since neither the side-effects nor the value of the sequence contains await
                    // (otherwise it would be converted to a SpillSequenceBuilder).
                    var sequence = (BoundSequence)expression;
                    builder.AddLocals(sequence.Locals);
                    builder.AddExpressions(sequence.SideEffects);
                    expression = sequence.Value;
                    continue;

                case BoundKind.ThisReference:
                case BoundKind.BaseReference:
                    if (refKind != RefKind.None || expression.Type.IsReferenceType)
                    {
                        return(expression);
                    }

                    goto default;

                case BoundKind.Parameter:
                    if (refKind != RefKind.None)
                    {
                        return(expression);
                    }

                    goto default;

                case BoundKind.Local:
                    var local = (BoundLocal)expression;
                    if (local.LocalSymbol.SynthesizedKind == SynthesizedLocalKind.AwaitSpill || refKind != RefKind.None)
                    {
                        return(local);
                    }

                    goto default;

                case BoundKind.FieldAccess:
                    var field = (BoundFieldAccess)expression;
                    if (field.FieldSymbol.IsReadOnly)
                    {
                        if (field.FieldSymbol.IsStatic)
                        {
                            return(field);
                        }
                        if (field.FieldSymbol.ContainingType.IsValueType)
                        {
                            goto default;
                        }
                        // save the receiver; can get the field later.
                        var receiver = Spill(builder, field.ReceiverOpt, (refKind != RefKind.None && field.FieldSymbol.Type.IsReferenceType) ? refKind : RefKind.None, sideEffectsOnly);
                        return(field.Update(receiver, field.FieldSymbol, field.ConstantValueOpt, field.ResultKind, field.Type));
                    }
                    goto default;

                case BoundKind.Call:
                    var call = (BoundCall)expression;
                    if (refKind != RefKind.None)
                    {
                        Debug.Assert(call.Method.RefKind != RefKind.None);
                        _F.Diagnostics.Add(ErrorCode.ERR_RefReturningCallAndAwait, _F.Syntax.Location, call.Method);
                        refKind = RefKind.None;     // Switch the RefKind to avoid asserting later in the pipeline
                    }
                    goto default;

                case BoundKind.Literal:
                case BoundKind.TypeExpression:
                    return(expression);

                case BoundKind.ConditionalReceiver:
                    // we will rewrite this as a part of rewriting whole LoweredConditionalAccess
                    // later, if needed
                    return(expression);

                default:
                    if (expression.Type.SpecialType == SpecialType.System_Void || sideEffectsOnly)
                    {
                        builder.AddStatement(_F.ExpressionStatement(expression));
                        return(null);
                    }
                    else
                    {
                        BoundAssignmentOperator assignToTemp;
                        Debug.Assert(_F.Syntax.IsKind(SyntaxKind.AwaitExpression));

                        var replacement = _F.StoreToTemp(
                            expression,
                            out assignToTemp,
                            refKind: refKind,
                            kind: SynthesizedLocalKind.AwaitSpill,
                            syntaxOpt: _F.Syntax);

                        builder.AddLocal(replacement.LocalSymbol, _F.Diagnostics);
                        builder.AddStatement(_F.ExpressionStatement(assignToTemp));
                        return(replacement);
                    }
                }
            }
        }
Exemplo n.º 13
0
        private Expression Spill(
            BoundSpillSequenceBuilder builder,
            Expression expression,
            bool isRef           = false,
            bool sideEffectsOnly = false)
        {
            Debug.Assert(builder != null);

            while (true)
            {
                switch (expression.NodeType)
                {
                case NodeType.ListLiteralExpression:
                case NodeType.ArrayLiteralExpression:
                    Debug.Assert(!isRef);
                    Debug.Assert(!sideEffectsOnly);
                    var arrayInitialization = (ListLiteralExpression)expression;
                    var newInitializers     = VisitExpressionList(ref builder, arrayInitialization.Items, forceSpill: true);
                    arrayInitialization.Items = newInitializers;
                    return(arrayInitialization);

                case NodeType.HashLiteralExpression:
                    Debug.Assert(!isRef);
                    Debug.Assert(!sideEffectsOnly);
                    var hashInitialization  = (HashLiteralExpression)expression;
                    var newInitializerPairs = VisitExpressionPairList(ref builder, hashInitialization.Items, forceSpill: true);
                    hashInitialization.Items = newInitializerPairs;
                    return(hashInitialization);

                case SpillSequenceBuilder:
                    var sequenceBuilder = (BoundSpillSequenceBuilder)expression;
                    builder.Include(sequenceBuilder);
                    expression = sequenceBuilder.Value;
                    continue;

                case NodeType.SelfLiteralExpression:
                case NodeType.SuperLiteralExpression:
                    if (isRef || !expression.ExpressionType.IsValueType)
                    {
                        return(expression);
                    }
                    goto default;

                case NodeType.ParameterDeclaration:
                    if (isRef)
                    {
                        return(expression);
                    }
                    goto default;

                case NodeType.ReferenceExpression:
                    var local = expression.Entity as InternalLocal;
                    if (local != null)
                    {
                        if (local.Local["SynthesizedKind"] == AWAIT_SPILL_MARKER || isRef)
                        {
                            return(expression);
                        }
                    }
                    goto default;

                case NodeType.MemberReferenceExpression:
                    if (expression.Entity.EntityType == EntityType.Field)
                    {
                        var field = (IField)expression.Entity;
                        if (field.IsInitOnly)
                        {
                            if (field.IsStatic)
                            {
                                return(expression);
                            }
                            if (!field.DeclaringType.IsValueType)
                            {
                                // save the receiver; can get the field later.
                                var target = Spill(builder,
                                                   ((MemberReferenceExpression)expression).Target,
                                                   isRef && !field.Type.IsValueType,
                                                   sideEffectsOnly);
                                return(_F.CreateMemberReference(target, field));
                            }
                        }
                    }
                    goto default;

                case NodeType.MethodInvocationExpression:
                    var mie = (MethodInvocationExpression)expression;
                    if (expression.Entity == BuiltinFunction.Eval)
                    {
                        builder.AddExpressions(mie.Arguments.Where(a => a != mie.Arguments.Last));
                        expression = mie.Arguments.Last;
                        continue;
                    }
                    if (isRef)
                    {
                        Debug.Assert(mie.ExpressionType.IsPointer);
                        CompilerContext.Current.Errors.Add(CompilerErrorFactory.UnsafeReturnInAsync(mie));
                    }
                    goto default;

                case NodeType.BoolLiteralExpression:
                case NodeType.CharLiteralExpression:
                case NodeType.DoubleLiteralExpression:
                case NodeType.IntegerLiteralExpression:
                case NodeType.NullLiteralExpression:
                case NodeType.RELiteralExpression:
                case NodeType.StringLiteralExpression:
                case NodeType.TypeofExpression:
                    return(expression);

                default:
                    if (expression.ExpressionType == _tss.VoidType || sideEffectsOnly)
                    {
                        builder.AddStatement(new ExpressionStatement(expression));
                        return(null);
                    }
                    var replacement = _F.DeclareTempLocal(_currentMethod, expression.ExpressionType);

                    var assignToTemp = _F.CreateAssignment(
                        _F.CreateLocalReference(replacement),
                        expression);

                    builder.AddLocal(replacement);
                    builder.AddStatement(new ExpressionStatement(assignToTemp));
                    return(_F.CreateLocalReference(replacement));
                }
            }
        }