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.AwaitSpill, syntax: _F.Syntax); leftBuilder.AddLocal(tmp, _F.Diagnostics); 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), substituteTemps: false))); 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, left, right, node.ConstantValue, node.MethodOpt, node.ResultKind, node.Type))); }
private BoundStatement UpdateStatement(BoundSpillSequenceBuilder builder, BoundStatement statement) { if (builder == null) { Debug.Assert(statement != null); return(statement); } Debug.Assert(builder.Value == null); if (statement != null) { builder.AddStatement(statement); } var result = _F.Block(builder.GetLocals(), builder.GetStatements()); builder.Free(); return(result); }
private static Statement UpdateStatement(BoundSpillSequenceBuilder builder, Statement statement) { if (builder == null) { // statement doesn't contain any await Debug.Assert(statement != null); return(statement); } Debug.Assert(builder.Value == null); if (statement != null) { builder.AddStatement(statement); } var result = new Block(builder.GetStatements().ToArray()); return(result); }
private BoundStatement UpdateStatement(BoundSpillSequenceBuilder builder, BoundStatement statement, bool substituteTemps) { if (builder == null) { // statement doesn't contain any await Debug.Assert(!substituteTemps || _tempSubstitution.Count == 0); Debug.Assert(statement != null); return(statement); } Debug.Assert(builder.Value == null); if (statement != null) { builder.AddStatement(statement); } var substituterOpt = (substituteTemps && _tempSubstitution.Count > 0) ? new LocalSubstituter(_tempSubstitution, RecursionDepth) : null; var result = _F.Block(builder.GetLocals(), builder.GetStatements(substituterOpt)); builder.Free(); return(result); }
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)); } } }