Example #1
0
 /// <summary>
 /// If an expression node that declares synthesized short-lived locals (currently only sequence) contains an await, these locals become long-lived since their
 /// values may be read by code that follows the await. We promote these variables to long-lived of kind <see cref="SynthesizedLocalKind.AwaitSpill"/>.
 /// </summary>
 private void PromoteAndAddLocals(BoundSpillSequenceBuilder builder, ImmutableArray <LocalSymbol> locals)
 {
     foreach (var local in locals)
     {
         if (local.SynthesizedKind.IsLongLived())
         {
             builder.AddLocal(local, _F.Diagnostics);
         }
         else
         {
             Debug.Assert(_F.Syntax.IsKind(SyntaxKind.AwaitExpression));
             LocalSymbol longLived = local.WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind.AwaitSpill, _F.Syntax);
             _tempSubstitution.Add(local, longLived);
             builder.AddLocal(longLived, _F.Diagnostics);
         }
     }
 }
Example #2
0
        /// <summary>
        /// If an expression node that declares synthesized short-lived locals (currently only sequence) contains an await, these locals become long-lived since their
        /// values may be read by code that follows the await. We promote these variables to long-lived of kind <see cref="SynthesizedLocalKind.AwaitSpill"/>.
        /// </summary>
        private void PromoteAndAddLocals(BoundSpillSequenceBuilder builder, ImmutableArray <LocalSymbol> locals)
        {
            foreach (var local in locals)
            {
                if (local.SynthesizedLocalKind.IsLongLived())
                {
                    builder.AddLocal(local, F.Diagnostics);
                }
                else
                {
                    SynthesizedLocal shortLived = (SynthesizedLocal)local;
                    SynthesizedLocal longLived  = shortLived.WithSynthesizedLocalKind(SynthesizedLocalKind.AwaitSpill);
                    tempSubstitution.Add(shortLived, longLived);

                    builder.AddLocal(longLived, F.Diagnostics);
                }
            }
        }
Example #3
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));
        }
Example #4
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)
                {
                    var tmp = _F.SynthesizedLocal(node.Type, kind: SynthesizedLocalKind.Spill, syntax: _F.Syntax);
                    leftBuilder.AddLocal(tmp);
                    leftBuilder.AddStatement(_F.Assignment(_F.Local(tmp), left));
                    leftBuilder.AddStatement(_F.If(
                                                 node.OperatorKind == BinaryOperatorKind.LogicalBoolAnd ? _F.Local(tmp) : _F.Not(_F.Local(tmp)),
                                                 UpdateStatement(builder, _F.Assignment(_F.Local(tmp), right))));

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

            return(UpdateExpression(builder, node.Update(node.OperatorKind, node.ConstantValue, node.MethodOpt, node.ResultKind, left, right, node.Type)));
        }
Example #5
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);
                    }
                }
            }
        }
Example #6
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);
                    }
                }
            }
        }
Example #7
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));
                }
            }
        }