public override BoundNode VisitConditionalOperator(BoundConditionalOperator node) { BoundSpillSequenceBuilder conditionBuilder = null; var condition = VisitExpression(ref conditionBuilder, node.Condition); BoundSpillSequenceBuilder consequenceBuilder = null; var consequence = VisitExpression(ref consequenceBuilder, node.Consequence); BoundSpillSequenceBuilder alternativeBuilder = null; var alternative = VisitExpression(ref alternativeBuilder, node.Alternative); if (consequenceBuilder == null && alternativeBuilder == null) { return(UpdateExpression(conditionBuilder, node.Update(node.IsRef, condition, consequence, alternative, node.ConstantValueOpt, node.Type))); } if (conditionBuilder == null) { conditionBuilder = new BoundSpillSequenceBuilder(); } if (consequenceBuilder == null) { consequenceBuilder = new BoundSpillSequenceBuilder(); } if (alternativeBuilder == null) { alternativeBuilder = new BoundSpillSequenceBuilder(); } if (node.Type.IsVoidType()) { conditionBuilder.AddStatement( _F.If(condition, UpdateStatement(consequenceBuilder, _F.ExpressionStatement(consequence)), UpdateStatement(alternativeBuilder, _F.ExpressionStatement(alternative)))); return(conditionBuilder.Update(_F.Default(node.Type))); } else { var tmp = _F.SynthesizedLocal(node.Type, kind: SynthesizedLocalKind.Spill, syntax: _F.Syntax); conditionBuilder.AddLocal(tmp); conditionBuilder.AddStatement( _F.If(condition, UpdateStatement(consequenceBuilder, _F.Assignment(_F.Local(tmp), consequence)), UpdateStatement(alternativeBuilder, _F.Assignment(_F.Local(tmp), alternative)))); return(conditionBuilder.Update(_F.Local(tmp))); } }
public override BoundNode VisitConditionalOperator(BoundConditionalOperator node) { BoundSpillSequenceBuilder conditionBuilder = null; var condition = VisitExpression(ref conditionBuilder, node.Condition); BoundSpillSequenceBuilder consequenceBuilder = null; var consequence = VisitExpression(ref consequenceBuilder, node.Consequence); BoundSpillSequenceBuilder alternativeBuilder = null; var alternative = VisitExpression(ref alternativeBuilder, node.Alternative); if (consequenceBuilder == null && alternativeBuilder == null) { return(UpdateExpression(conditionBuilder, node.Update(condition, consequence, alternative, node.ConstantValueOpt, node.Type))); } if (conditionBuilder == null) { conditionBuilder = new BoundSpillSequenceBuilder(); } if (consequenceBuilder == null) { consequenceBuilder = new BoundSpillSequenceBuilder(); } if (alternativeBuilder == null) { alternativeBuilder = new BoundSpillSequenceBuilder(); } if (node.Type.SpecialType == SpecialType.System_Void) { conditionBuilder.AddStatement( F.If(condition, UpdateStatement(consequenceBuilder, F.ExpressionStatement(consequence), substituteTemps: false), UpdateStatement(alternativeBuilder, F.ExpressionStatement(alternative), substituteTemps: false))); return(conditionBuilder.Update(F.Default(node.Type))); } else { var tmp = F.SynthesizedLocal(node.Type, kind: SynthesizedLocalKind.AwaitSpill); conditionBuilder.AddLocal(tmp, F.Diagnostics); conditionBuilder.AddStatement( F.If(condition, UpdateStatement(consequenceBuilder, F.Assignment(F.Local(tmp), consequence), substituteTemps: false), UpdateStatement(alternativeBuilder, F.Assignment(F.Local(tmp), alternative), substituteTemps: false))); return(conditionBuilder.Update(F.Local(tmp))); } }
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); value = Spill(builder, value); return(builder.Update(value)); }
private static Expression UpdateExpression(BoundSpillSequenceBuilder builder, Expression expression) { if (builder == null) { return(expression); } Debug.Assert(builder.Value == null); if (!builder.HasLocals && !builder.HasStatements) { return(expression); } return(builder.Update(expression)); }
public override BoundNode VisitConversion(BoundConversion node) { if (node.ConversionKind == ConversionKind.AnonymousFunction && node.Type.IsExpressionTree()) { // Expression trees do not contain any code that requires spilling. return(node); } BoundSpillSequenceBuilder builder = null; var operand = VisitExpression(ref builder, node.Operand); return(UpdateExpression( builder, node.UpdateOperand(operand))); }
private BoundExpression VisitExpression(ref BoundSpillSequenceBuilder builder, BoundExpression expression) { // wrap the node in a spill sequence to mark the fact that it must be moved up the tree. // The caller will handle this node type if the result is discarded. if (expression != null && expression.Kind == BoundKind.AwaitExpression) { // we force the await expression to be assigned to a temp variable var awaitExpression = (BoundAwaitExpression)expression; awaitExpression = awaitExpression.Update( VisitExpression(ref builder, awaitExpression.Expression), awaitExpression.GetAwaiter, awaitExpression.IsCompleted, awaitExpression.GetResult, awaitExpression.Type); BoundAssignmentOperator assignToTemp; var replacement = F.StoreToTemp(awaitExpression, out assignToTemp, kind: SynthesizedLocalKind.AwaitSpill); if (builder == null) { builder = new BoundSpillSequenceBuilder(); } builder.AddLocal(replacement.LocalSymbol, F.Diagnostics); F.Syntax = awaitExpression.Syntax; builder.AddStatement(F.ExpressionStatement(assignToTemp)); return(replacement); } var e = (BoundExpression)this.Visit(expression); if (e == null || e.Kind != SpillSequenceBuilder) { return(e); } var newBuilder = (BoundSpillSequenceBuilder)e; if (builder == null) { builder = newBuilder.Update(null); } else { builder.Include(newBuilder); } return(newBuilder.Value); }
public override BoundNode VisitConversion(BoundConversion node) { BoundSpillSequenceBuilder builder = null; var operand = VisitExpression(ref builder, node.Operand); return(UpdateExpression( builder, node.Update( operand, node.Conversion, isBaseConversion: node.IsBaseConversion, @checked: node.Checked, explicitCastInCode: node.ExplicitCastInCode, constantValueOpt: node.ConstantValueOpt, type: node.Type))); }
/// <summary> /// If an expression node that declares synthesized short-lived locals (currently only sequence) contains /// a spill sequence (from an await or switch expression), these locals become long-lived since their /// values may be read by code that follows. We promote these variables to long-lived of kind /// <see cref="SynthesizedLocalKind.Spill"/>. /// </summary> private void PromoteAndAddLocals(BoundSpillSequenceBuilder builder, ImmutableArray <LocalSymbol> locals) { foreach (var local in locals) { if (local.SynthesizedKind.IsLongLived()) { builder.AddLocal(local); } else { LocalSymbol longLived = local.WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind.Spill, _F.Syntax); _tempSubstitution.Add(local, longLived); builder.AddLocal(longLived); } } }
/// <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); } } }
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)); }
private Expression VisitExpression(ref BoundSpillSequenceBuilder builder, Expression expression) { // wrap the node in a spill sequence to mark the fact that it must be moved up the tree. // The caller will handle this node type if the result is discarded. if (expression != null && expression.NodeType == NodeType.AwaitExpression) { // we force the await expression to be assigned to a temp variable var awaitExpression = (AwaitExpression)expression; awaitExpression.BaseExpression = VisitExpression(ref builder, awaitExpression.BaseExpression); var local = _F.DeclareTempLocal(_currentMethod, awaitExpression.ExpressionType); var replacement = _F.CreateAssignment( awaitExpression.LexicalInfo, _F.CreateLocalReference(local), awaitExpression); if (builder == null) { builder = new BoundSpillSequenceBuilder(); } builder.AddLocal(local); builder.AddStatement(new ExpressionStatement(replacement)); return(_F.CreateLocalReference(local)); } var e = Visit(expression); if (e == null || e.NodeType != SpillSequenceBuilder) { return(e); } var newBuilder = (BoundSpillSequenceBuilder)e; if (builder == null) { builder = newBuilder.Update(null); } else { builder.Include(newBuilder); } return(newBuilder.Value); }
/// <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); } } }
private ExpressionPairCollection VisitExpressionPairList( ref BoundSpillSequenceBuilder builder, ExpressionPairCollection args, bool forceSpill = false, bool sideEffectsOnly = false) { var args1 = new ExpressionCollection(); args1.AddRange(args.Select(p => p.First)); var args2 = new ExpressionCollection(); args2.AddRange(args.Select(p => p.Second)); args1 = VisitExpressionList(ref builder, args1, null, forceSpill, sideEffectsOnly); args2 = VisitExpressionList(ref builder, args2, null, forceSpill, sideEffectsOnly); args.Clear(); args.AddRange(args1.Zip(args2, (l, r) => new ExpressionPair(l.LexicalInfo, l, r))); return(args); }
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); }
public override BoundNode VisitConversion(BoundConversion node) { BoundSpillSequenceBuilder builder = null; var operand = VisitExpression(ref builder, node.Operand); return(UpdateExpression( builder, node.Update( operand, node.ConversionKind, node.ResultKind, isBaseConversion: node.IsBaseConversion, symbolOpt: node.SymbolOpt, @checked: node.Checked, explicitCastInCode: node.ExplicitCastInCode, isExtensionMethod: node.IsExtensionMethod, isArrayIndex: node.IsArrayIndex, constantValueOpt: node.ConstantValueOpt, type: node.Type))); }
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))); }
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))); }
private SliceCollection VisitSliceCollection( ref BoundSpillSequenceBuilder builder, SliceCollection args) { foreach (var arg in args) { if (arg.Begin != null) { VisitExpression(ref builder, arg.Begin); } if (arg.End != null) { VisitExpression(ref builder, arg.End); } if (arg.Step != null) { VisitExpression(ref builder, arg.Step); } } return(args); }
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 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); }
public override BoundNode VisitExpressionStatement(BoundExpressionStatement node) { EnterStatement(node); BoundSpillSequenceBuilder builder = null; BoundExpression expr; if (node.Expression.Kind == BoundKind.AwaitExpression) { // await expression with result discarded var awaitExpression = (BoundAwaitExpression)node.Expression; var expression = VisitExpression(ref builder, awaitExpression.Expression); expr = awaitExpression.Update(expression, awaitExpression.GetAwaiter, awaitExpression.IsCompleted, awaitExpression.GetResult, awaitExpression.Type); } else { expr = VisitExpression(ref builder, node.Expression); } Debug.Assert(expr != null); Debug.Assert(builder == null || builder.Value == null); return(UpdateStatement(builder, node.Update(expr), substituteTemps: true)); }
public override BoundNode VisitSequence(BoundSequence node) { BoundSpillSequenceBuilder valueBuilder = null; var value = VisitExpression(ref valueBuilder, node.Value); BoundSpillSequenceBuilder builder = null; var sideEffects = VisitExpressionList(ref builder, node.SideEffects, forceSpill: valueBuilder != null, sideEffectsOnly: true); if (builder == null && valueBuilder == null) { return(node.Update(node.Locals, sideEffects, value, node.Type)); } if (builder == null) { builder = new BoundSpillSequenceBuilder(); } PromoteAndAddLocals(builder, node.Locals); builder.AddExpressions(sideEffects); builder.Include(valueBuilder); return(builder.Update(value)); }
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))); }
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))); }
private ImmutableArray <BoundExpression> VisitExpressionList( ref BoundSpillSequenceBuilder builder, ImmutableArray <BoundExpression> args, ImmutableArray <RefKind> refKinds = default(ImmutableArray <RefKind>), bool forceSpill = false, bool sideEffectsOnly = false) { Debug.Assert(!sideEffectsOnly || refKinds.IsDefault); Debug.Assert(refKinds.IsDefault || refKinds.Length == args.Length); if (args.Length == 0) { return(args); } var newList = VisitList(args); Debug.Assert(newList.Length == args.Length); int lastSpill; if (forceSpill) { lastSpill = newList.Length; } else { lastSpill = -1; for (int i = newList.Length - 1; i >= 0; i--) { if (newList[i].Kind == SpillSequenceBuilderKind) { lastSpill = i; break; } } } if (lastSpill == -1) { return(newList); } if (builder == null) { builder = new BoundSpillSequenceBuilder(); } var result = ArrayBuilder <BoundExpression> .GetInstance(newList.Length); // everything up until the last spill must be spilled entirely for (int i = 0; i < lastSpill; i++) { var refKind = refKinds.IsDefault ? RefKind.None : refKinds[i]; var replacement = Spill(builder, newList[i], refKind, sideEffectsOnly); Debug.Assert(sideEffectsOnly || replacement != null); if (!sideEffectsOnly) { result.Add(replacement); } } // the value of the last spill and everything that follows is not spilled if (lastSpill < newList.Length) { var lastSpillNode = (BoundSpillSequenceBuilder)newList[lastSpill]; builder.Include(lastSpillNode); result.Add(lastSpillNode.Value); for (int i = lastSpill + 1; i < newList.Length; i++) { result.Add(newList[i]); } } return(result.ToImmutableAndFree()); }
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 ImmutableArray <BoundExpression> VisitExpressionList( ref BoundSpillSequenceBuilder builder, ImmutableArray <BoundExpression> args, ImmutableArray <RefKind> refKinds = default(ImmutableArray <RefKind>), bool forceSpill = false, bool sideEffectsOnly = false) { var newList = VisitList(args); Debug.Assert(newList.Length == args.Length); int lastSpill; if (forceSpill) { lastSpill = newList.Length - 1; } else { lastSpill = -1; for (int i = newList.Length - 1; i >= 0; i--) { if (newList[i].Kind == SpillSequenceBuilder) { lastSpill = i; break; } } } if (lastSpill == -1) { return(newList); } if (builder == null) { builder = new BoundSpillSequenceBuilder(); } var result = ArrayBuilder <BoundExpression> .GetInstance(); for (int i = 0; i <= lastSpill; i++) { var refKind = (!refKinds.IsDefaultOrEmpty && refKinds.Length > i && refKinds[i] != RefKind.None) ? RefKind.Ref : RefKind.None; var replacement = Spill(builder, newList[i], refKind, sideEffectsOnly); Debug.Assert(sideEffectsOnly || replacement != null); if (!sideEffectsOnly) { result.Add(replacement); } } for (int i = lastSpill + 1; i < newList.Length; i++) { result.Add(newList[i]); } return(result.ToImmutableAndFree()); }
public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalAccess node) { var receiverRefKind = ReceiverSpillRefKind(node.Receiver); BoundSpillSequenceBuilder receiverBuilder = null; var receiver = VisitExpression(ref receiverBuilder, node.Receiver); BoundSpillSequenceBuilder whenNotNullBuilder = null; var whenNotNull = VisitExpression(ref whenNotNullBuilder, node.WhenNotNull); BoundSpillSequenceBuilder whenNullBuilder = null; var whenNullOpt = VisitExpression(ref whenNullBuilder, node.WhenNullOpt); if (whenNotNullBuilder == null && whenNullBuilder == null) { return(UpdateExpression(receiverBuilder, node.Update(receiver, node.HasValueMethodOpt, whenNotNull, whenNullOpt, node.Id, node.Type))); } if (receiverBuilder == null) { receiverBuilder = new BoundSpillSequenceBuilder(); } if (whenNotNullBuilder == null) { whenNotNullBuilder = new BoundSpillSequenceBuilder(); } if (whenNullBuilder == null) { whenNullBuilder = new BoundSpillSequenceBuilder(); } BoundExpression condition; if (receiver.Type.IsReferenceType || receiver.Type.IsValueType || receiverRefKind == RefKind.None) { // spill to a clone receiver = Spill(receiverBuilder, receiver, RefKind.None); var hasValueOpt = node.HasValueMethodOpt; if (hasValueOpt == null) { condition = _F.ObjectNotEqual( _F.Convert(_F.SpecialType(SpecialType.System_Object), receiver), _F.Null(_F.SpecialType(SpecialType.System_Object))); } else { condition = _F.Call(receiver, hasValueOpt); } } else { Debug.Assert(node.HasValueMethodOpt == null); receiver = Spill(receiverBuilder, receiver, RefKind.Ref); var clone = _F.SynthesizedLocal(receiver.Type, _F.Syntax, refKind: RefKind.None, kind: SynthesizedLocalKind.Spill); receiverBuilder.AddLocal(clone); // (object)default(T) != null var isNotClass = _F.ObjectNotEqual( _F.Convert(_F.SpecialType(SpecialType.System_Object), _F.Default(receiver.Type)), _F.Null(_F.SpecialType(SpecialType.System_Object))); // isNotCalss || {clone = receiver; (object)clone != null} condition = _F.LogicalOr( isNotClass, _F.MakeSequence( _F.AssignmentExpression(_F.Local(clone), receiver), _F.ObjectNotEqual( _F.Convert(_F.SpecialType(SpecialType.System_Object), _F.Local(clone)), _F.Null(_F.SpecialType(SpecialType.System_Object)))) ); receiver = _F.ComplexConditionalReceiver(receiver, _F.Local(clone)); } if (node.Type.SpecialType == SpecialType.System_Void) { var whenNotNullStatement = UpdateStatement(whenNotNullBuilder, _F.ExpressionStatement(whenNotNull)); whenNotNullStatement = ConditionalReceiverReplacer.Replace(whenNotNullStatement, receiver, node.Id, RecursionDepth); Debug.Assert(whenNullOpt == null || !LocalRewriter.ReadIsSideeffecting(whenNullOpt)); receiverBuilder.AddStatement(_F.If(condition, whenNotNullStatement)); return(receiverBuilder.Update(_F.Default(node.Type))); } else { var tmp = _F.SynthesizedLocal(node.Type, kind: SynthesizedLocalKind.Spill, syntax: _F.Syntax); var whenNotNullStatement = UpdateStatement(whenNotNullBuilder, _F.Assignment(_F.Local(tmp), whenNotNull)); whenNotNullStatement = ConditionalReceiverReplacer.Replace(whenNotNullStatement, receiver, node.Id, RecursionDepth); whenNullOpt = whenNullOpt ?? _F.Default(node.Type); receiverBuilder.AddLocal(tmp); receiverBuilder.AddStatement( _F.If(condition, whenNotNullStatement, UpdateStatement(whenNullBuilder, _F.Assignment(_F.Local(tmp), whenNullOpt)))); return(receiverBuilder.Update(_F.Local(tmp))); } }
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); } } } }