/// <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); } } }
/// <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); } } }
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)); }
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))); }
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); } } } }
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); } } } }
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)); } } }