public override BoundNode VisitSpillSequence(BoundSpillSequence node) { var builder = new BoundSpillSequenceBuilder(); // Ensure later errors (e.g. in async rewriting) are associated with the correct node. _F.Syntax = node.Syntax; builder.AddStatements(VisitList(node.SideEffects)); builder.AddLocals(node.Locals); var value = VisitExpression(ref builder, node.Value); return(builder.Update(value)); }
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); } } } }