private bool TryRewriteLocal(LocalSymbol local, out LocalSymbol newLocal) { if (VariablesCaptured.Contains(local)) { // no longer a local symbol newLocal = null; return(false); } if (localMap.TryGetValue(local, out newLocal)) { return(true); } var newType = VisitType(local.Type); if (newType == local.Type) { newLocal = local; } else { newLocal = new SynthesizedLocal(CurrentMethod, newType, local.Name); localMap.Add(local, newLocal); } return(true); }
public override BoundNode VisitSequence(BoundSequence node) { // Spilled local temps do not appear here in a sequence expression, because any temps in a // sequence expression that need to be spilled would have been moved up to the // statement level by the AwaitLiftingRewriter. if (!node.Locals.IsDefaultOrEmpty) { foreach (var local in node.Locals) { Debug.Assert(!VariablesCaptured.Contains(local) || proxies.ContainsKey(local)); } } return(base.VisitSequence(node)); }
public override BoundNode VisitLocal(BoundLocal node) { CapturedSymbolReplacement proxy; if (proxies.TryGetValue(node.LocalSymbol, out proxy)) { return(proxy.Replacement(node.Syntax, frameType => FramePointer(node.Syntax, frameType))); } Debug.Assert(!VariablesCaptured.Contains(node.LocalSymbol)); LocalSymbol replacementLocal; if (this.localMap.TryGetValue(node.LocalSymbol, out replacementLocal)) { return(new BoundLocal(node.Syntax, replacementLocal, node.ConstantValueOpt, replacementLocal.Type, node.HasErrors)); } return(base.VisitLocal(node)); }
/// <summary> /// Translate a statement that declares a given set of locals. Also allocates and frees hoisted temps as /// required for the translation. /// </summary> /// <param name="locals">The set of locals declared in the original version of this statement</param> /// <param name="wrapped">A delegate to return the translation of the body of this statement</param> private BoundNode PossibleIteratorScope(ImmutableArray <LocalSymbol> locals, Func <BoundStatement> wrapped) { if (locals.IsDefaultOrEmpty) { return(wrapped()); } var proxyFields = ArrayBuilder <SynthesizedFieldSymbolBase> .GetInstance(); foreach (var local in locals) { if (!VariablesCaptured.Contains(local)) { continue; } CapturedSymbolReplacement proxy; if (proxies.TryGetValue(local, out proxy)) { // All of the user-declared variables have pre-allocated proxies var field = proxy.HoistedField; Debug.Assert((object)field != null); Debug.Assert(local.DeclarationKind != LocalDeclarationKind.CompilerGenerated); // temps have lazily allocated proxies if (local.DeclarationKind != LocalDeclarationKind.CompilerGenerated) { proxyFields.Add(field); } } else { if (local.RefKind == RefKind.None) { SynthesizedFieldSymbolBase field = MakeHoistedTemp(local, TypeMap.SubstituteType(local.Type)); proxy = new CapturedToFrameSymbolReplacement(field); proxies.Add(local, proxy); } // ref temporary variables have proxies that are allocated on demand // See VisitAssignmentOperator. } } var translatedStatement = wrapped(); // produce code to free (e.g. mark as available for reuse) and clear (e.g. set to null) the proxies for any temps for these locals var clearTemps = ArrayBuilder <BoundAssignmentOperator> .GetInstance(); foreach (var local in locals) { ArrayBuilder <SynthesizedFieldSymbolBase> frees; if (freeTempsMap.TryGetValue(local, out frees)) { Debug.Assert(local.DeclarationKind == LocalDeclarationKind.CompilerGenerated); // only temps are managed this way freeTempsMap.Remove(local); foreach (var field in frees) { if (MightContainReferences(field.Type)) { clearTemps.Add(F.AssignmentExpression(F.Field(F.This(), field), F.NullOrDefault(field.Type))); } FreeTemp(field); } frees.Free(); } } if (clearTemps.Count != 0) { translatedStatement = F.Block( translatedStatement, F.Block(clearTemps.Select(e => F.ExpressionStatement(e)).AsImmutable <BoundStatement>()) ); } clearTemps.Free(); // wrap the node in an iterator scope for debugging if (proxyFields.Count != 0) { translatedStatement = new BoundIteratorScope(F.Syntax, proxyFields.ToImmutable(), translatedStatement); } proxyFields.Free(); return(translatedStatement); }
public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) { BoundExpression originalLeft = node.Left; if (originalLeft.Kind != BoundKind.Local) { return(base.VisitAssignmentOperator(node)); } var leftLocal = (BoundLocal)originalLeft; BoundExpression originalRight = node.Right; if (leftLocal.LocalSymbol.RefKind != RefKind.None && node.RefKind != RefKind.None && VariablesCaptured.Contains(leftLocal.LocalSymbol)) { Debug.Assert(!proxies.ContainsKey(leftLocal.LocalSymbol)); // we need to create the proxy at this time Debug.Assert(!IsStackAlloc(originalRight)); //spilling ref local variables throw ExceptionUtilities.Unreachable; } if (VariablesCaptured.Contains(leftLocal.LocalSymbol) && !proxies.ContainsKey(leftLocal.LocalSymbol)) { Debug.Assert(leftLocal.LocalSymbol.DeclarationKind == LocalDeclarationKind.CompilerGenerated); //spilling temp variables throw ExceptionUtilities.Unreachable; } BoundExpression rewrittenLeft = (BoundExpression)this.Visit(leftLocal); BoundExpression rewrittenRight = (BoundExpression)this.Visit(originalRight); TypeSymbol rewrittenType = VisitType(node.Type); // Check if we're assigning the result of stackalloc to a hoisted local. // If we are, we need to store the result in a temp local and then assign // the value of the local to the field corresponding to the hoisted local. // If the receiver of the field is on the stack when the stackalloc happens, // popping it will free the memory (?) or otherwise cause verification issues. // DevDiv Bugs 59454 if (rewrittenLeft.Kind != BoundKind.Local && IsStackAlloc(originalRight)) { // From ILGENREC::genAssign: // DevDiv Bugs 59454: Handle hoisted local initialized with a stackalloc // NOTE: Need to check for cast of stackalloc on RHS. // If LHS isLocal, then genAddr is a noop so regular case works fine. SyntheticBoundNodeFactory factory = new SyntheticBoundNodeFactory(this.CurrentMethod, rewrittenLeft.Syntax, this.CompilationState, this.Diagnostics); BoundAssignmentOperator tempAssignment; BoundLocal tempLocal = factory.StoreToTemp(rewrittenRight, out tempAssignment); Debug.Assert(node.RefKind == RefKind.None); BoundAssignmentOperator rewrittenAssignment = node.Update(rewrittenLeft, tempLocal, node.RefKind, rewrittenType); return(new BoundSequence( node.Syntax, ImmutableArray.Create <LocalSymbol>(tempLocal.LocalSymbol), ImmutableArray.Create <BoundExpression>(tempAssignment), rewrittenAssignment, rewrittenType)); } return(node.Update(rewrittenLeft, rewrittenRight, node.RefKind, rewrittenType)); }