public override BoundNode VisitTryStatement(BoundTryStatement node) { var tryStatementSyntax = node.Syntax; Debug.Assert(tryStatementSyntax.IsKind(SyntaxKind.TryStatement)); BoundStatement finalizedRegion; BoundBlock rewrittenFinally; var finallyContainsAwaits = _analysis.FinallyContainsAwaits(node); if (!finallyContainsAwaits) { finalizedRegion = RewriteFinalizedRegion(node); rewrittenFinally = (BoundBlock)this.Visit(node.FinallyBlockOpt); if (rewrittenFinally == null) { return(finalizedRegion); } var asTry = finalizedRegion as BoundTryStatement; if (asTry != null) { // since finalized region is a try we can just attach finally to it Debug.Assert(asTry.FinallyBlockOpt == null); return(asTry.Update(asTry.TryBlock, asTry.CatchBlocks, rewrittenFinally, asTry.PreferFaultHandler)); } else { // wrap finalizedRegion into a Try with a finally. return(_F.Try((BoundBlock)finalizedRegion, ImmutableArray <BoundCatchBlock> .Empty, rewrittenFinally)); } } // rewrite finalized region (try and catches) in the current frame var frame = PushFrame(node); finalizedRegion = RewriteFinalizedRegion(node); rewrittenFinally = (BoundBlock)this.VisitBlock(node.FinallyBlockOpt); PopFrame(); var exceptionType = _F.SpecialType(SpecialType.System_Object); var pendingExceptionLocal = new SynthesizedLocal(_F.CurrentMethod, exceptionType, SynthesizedLocalKind.TryAwaitPendingException, tryStatementSyntax); var finallyLabel = _F.GenerateLabel("finallyLabel"); var pendingBranchVar = new SynthesizedLocal(_F.CurrentMethod, _F.SpecialType(SpecialType.System_Int32), SynthesizedLocalKind.TryAwaitPendingBranch, tryStatementSyntax); var catchAll = _F.Catch(_F.Local(pendingExceptionLocal), _F.Block()); var catchAndPendException = _F.Try( _F.Block( finalizedRegion, _F.HiddenSequencePoint(), _F.Goto(finallyLabel), PendBranches(frame, pendingBranchVar, finallyLabel)), ImmutableArray.Create(catchAll)); var syntheticFinally = _F.Block( _F.HiddenSequencePoint(), _F.Label(finallyLabel), rewrittenFinally, _F.HiddenSequencePoint(), UnpendException(pendingExceptionLocal), UnpendBranches( frame, pendingBranchVar, pendingExceptionLocal)); var locals = ArrayBuilder <LocalSymbol> .GetInstance(); var statements = ArrayBuilder <BoundStatement> .GetInstance(); statements.Add(_F.HiddenSequencePoint()); locals.Add(pendingExceptionLocal); statements.Add(_F.Assignment(_F.Local(pendingExceptionLocal), _F.Default(pendingExceptionLocal.Type))); locals.Add(pendingBranchVar); statements.Add(_F.Assignment(_F.Local(pendingBranchVar), _F.Default(pendingBranchVar.Type))); LocalSymbol returnLocal = frame.returnValue; if (returnLocal != null) { locals.Add(returnLocal); } statements.Add(catchAndPendException); statements.Add(syntheticFinally); var completeTry = _F.Block( locals.ToImmutableAndFree(), statements.ToImmutableAndFree()); return(completeTry); }
private BoundStatement UnpendBranches( AwaitFinallyFrame frame, SynthesizedLocal pendingBranchVar, SynthesizedLocal pendingException) { var parent = frame.ParentOpt; // handle proxy labels if have any var proxiedLabels = frame.proxiedLabels; var proxyLabels = frame.proxyLabels; // skip 0 - it means we took no explicit branches int i = 1; var cases = ArrayBuilder <BoundSwitchSection> .GetInstance(); if (proxiedLabels != null) { for (int cnt = proxiedLabels.Count; i <= cnt; i++) { var target = proxiedLabels[i - 1]; var parentProxy = parent.ProxyLabelIfNeeded(target); var caseStatement = _F.SwitchSection(i, _F.Goto(parentProxy)); cases.Add(caseStatement); } } if (frame.returnProxyLabel != null) { BoundLocal pendingValue = null; if (frame.returnValue != null) { pendingValue = _F.Local(frame.returnValue); } SynthesizedLocal returnValue; BoundStatement unpendReturn; var returnLabel = parent.ProxyReturnIfNeeded(_F.CurrentMethod, pendingValue, out returnValue); if (returnLabel == null) { unpendReturn = new BoundReturnStatement(_F.Syntax, pendingValue); } else { if (pendingValue == null) { unpendReturn = _F.Goto(returnLabel); } else { unpendReturn = _F.Block( _F.Assignment( _F.Local(returnValue), pendingValue), _F.Goto(returnLabel)); } } var caseStatement = _F.SwitchSection(i, unpendReturn); cases.Add(caseStatement); } return(_F.Switch(_F.Local(pendingBranchVar), cases.ToImmutableAndFree())); }
internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular( SourceEventSymbol eventSymbol, bool isAddMethod, CSharpCompilation compilation, BindingDiagnosticBag diagnostics ) { CSharpSyntaxNode syntax = eventSymbol.CSharpSyntaxNode; TypeSymbol delegateType = eventSymbol.Type; MethodSymbol accessor = isAddMethod ? eventSymbol.AddMethod : eventSymbol.RemoveMethod; ParameterSymbol thisParameter = accessor.ThisParameter; TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); SpecialMember updateMethodId = isAddMethod ? SpecialMember.System_Delegate__Combine : SpecialMember.System_Delegate__Remove; MethodSymbol updateMethod = (MethodSymbol)compilation.GetSpecialTypeMember( updateMethodId ); BoundStatement @return = new BoundReturnStatement( syntax, refKind: RefKind.None, expressionOpt: null ) { WasCompilerGenerated = true }; if (updateMethod == null) { MemberDescriptor memberDescriptor = SpecialMembers.GetDescriptor(updateMethodId); diagnostics.Add( new CSDiagnostic( new CSDiagnosticInfo( ErrorCode.ERR_MissingPredefinedMember, memberDescriptor.DeclaringTypeMetadataName, memberDescriptor.Name ), syntax.Location ) ); return(BoundBlock.SynthesizedNoLocals(syntax, @return)); } Binder.ReportUseSite(updateMethod, diagnostics, syntax); BoundThisReference fieldReceiver = eventSymbol.IsStatic ? null : new BoundThisReference(syntax, thisParameter.Type) { WasCompilerGenerated = true }; BoundFieldAccess boundBackingField = new BoundFieldAccess( syntax, receiver: fieldReceiver, fieldSymbol: eventSymbol.AssociatedField, constantValueOpt: null ) { WasCompilerGenerated = true }; BoundParameter boundParameter = new BoundParameter( syntax, parameterSymbol: accessor.Parameters[0] ) { WasCompilerGenerated = true }; BoundExpression delegateUpdate; MethodSymbol compareExchangeMethod = (MethodSymbol)compilation.GetWellKnownTypeMember( WellKnownMember.System_Threading_Interlocked__CompareExchange_T ); if ((object)compareExchangeMethod == null) { // (DelegateType)Delegate.Combine(_event, value) delegateUpdate = BoundConversion.SynthesizedNonUserDefined( syntax, operand: BoundCall.Synthesized( syntax, receiverOpt: null, method: updateMethod, arguments: ImmutableArray.Create <BoundExpression>( boundBackingField, boundParameter ) ), conversion: Conversion.ExplicitReference, type: delegateType ); // _event = (DelegateType)Delegate.Combine(_event, value); BoundStatement eventUpdate = new BoundExpressionStatement( syntax, expression: new BoundAssignmentOperator( syntax, left: boundBackingField, right: delegateUpdate, type: delegateType ) { WasCompilerGenerated = true } ) { WasCompilerGenerated = true }; return(BoundBlock.SynthesizedNoLocals( syntax, statements: ImmutableArray.Create <BoundStatement>(eventUpdate, @return) )); } compareExchangeMethod = compareExchangeMethod.Construct( ImmutableArray.Create <TypeSymbol>(delegateType) ); Binder.ReportUseSite(compareExchangeMethod, diagnostics, syntax); GeneratedLabelSymbol loopLabel = new GeneratedLabelSymbol("loop"); const int numTemps = 3; LocalSymbol[] tmps = new LocalSymbol[numTemps]; BoundLocal[] boundTmps = new BoundLocal[numTemps]; for (int i = 0; i < numTemps; i++) { tmps[i] = new SynthesizedLocal( accessor, TypeWithAnnotations.Create(delegateType), SynthesizedLocalKind.LoweringTemp ); boundTmps[i] = new BoundLocal(syntax, tmps[i], null, delegateType) { WasCompilerGenerated = true }; } // tmp0 = _event; BoundStatement tmp0Init = new BoundExpressionStatement( syntax, expression: new BoundAssignmentOperator( syntax, left: boundTmps[0], right: boundBackingField, type: delegateType ) { WasCompilerGenerated = true } ) { WasCompilerGenerated = true }; // LOOP: BoundStatement loopStart = new BoundLabelStatement(syntax, label: loopLabel) { WasCompilerGenerated = true }; // tmp1 = tmp0; BoundStatement tmp1Update = new BoundExpressionStatement( syntax, expression: new BoundAssignmentOperator( syntax, left: boundTmps[1], right: boundTmps[0], type: delegateType ) { WasCompilerGenerated = true } ) { WasCompilerGenerated = true }; // (DelegateType)Delegate.Combine(tmp1, value) delegateUpdate = BoundConversion.SynthesizedNonUserDefined( syntax, operand: BoundCall.Synthesized( syntax, receiverOpt: null, method: updateMethod, arguments: ImmutableArray.Create <BoundExpression>(boundTmps[1], boundParameter) ), conversion: Conversion.ExplicitReference, type: delegateType ); // tmp2 = (DelegateType)Delegate.Combine(tmp1, value); BoundStatement tmp2Update = new BoundExpressionStatement( syntax, expression: new BoundAssignmentOperator( syntax, left: boundTmps[2], right: delegateUpdate, type: delegateType ) { WasCompilerGenerated = true } ) { WasCompilerGenerated = true }; // Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1) BoundExpression compareExchange = BoundCall.Synthesized( syntax, receiverOpt: null, method: compareExchangeMethod, arguments: ImmutableArray.Create <BoundExpression>( boundBackingField, boundTmps[2], boundTmps[1] ) ); // tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); BoundStatement tmp0Update = new BoundExpressionStatement( syntax, expression: new BoundAssignmentOperator( syntax, left: boundTmps[0], right: compareExchange, type: delegateType ) { WasCompilerGenerated = true } ) { WasCompilerGenerated = true }; // tmp0 == tmp1 // i.e. exit when they are equal, jump to start otherwise BoundExpression loopExitCondition = new BoundBinaryOperator( syntax, operatorKind: BinaryOperatorKind.ObjectEqual, left: boundTmps[0], right: boundTmps[1], constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType ) { WasCompilerGenerated = true }; // branchfalse (tmp0 == tmp1) LOOP BoundStatement loopEnd = new BoundConditionalGoto( syntax, condition: loopExitCondition, jumpIfTrue: false, label: loopLabel ) { WasCompilerGenerated = true }; return(new BoundBlock( syntax, locals: tmps.AsImmutable(), statements: ImmutableArray.Create <BoundStatement>( tmp0Init, loopStart, tmp1Update, tmp2Update, tmp0Update, loopEnd, @return ) ) { WasCompilerGenerated = true }); }
private DecisionTree AddByValue(DecisionTree.ByType byType, BoundConstantPattern value, DecisionMaker makeDecision) { if (byType.Default != null) { try { return(AddByValue(byType.Default, value, makeDecision)); } finally { if (byType.Default.MatchIsComplete) { // This code may be unreachable due to https://github.com/dotnet/roslyn/issues/16878 byType.MatchIsComplete = true; } } } if (value.ConstantValue == ConstantValue.Null) { // This should not occur, as the caller will have invoked AddByNull instead. throw ExceptionUtilities.Unreachable; } if ((object)value.Value.Type == null) { return(null); } foreach (var kvp in byType.TypeAndDecision) { var matchedType = kvp.Key; var decision = kvp.Value; // See if the test is already subsumed switch (ExpressionOfTypeMatchesPatternType(value.Value.Type, matchedType, ref _useSiteDiagnostics)) { case true: if (decision.MatchIsComplete) { return(null); } continue; case false: case null: continue; } } DecisionTree forType = null; // This new type test should logically be last. However it might be the same type as the one that is already // last. In that case we can produce better code by piggy-backing our new case on to the last decision. // Also, the last one might be a non-overlapping type, in which case we can piggy-back onto the second-last // type test. for (int i = byType.TypeAndDecision.Count - 1; i >= 0; i--) { var kvp = byType.TypeAndDecision[i]; var matchedType = kvp.Key; var decision = kvp.Value; if (matchedType.TupleUnderlyingTypeOrSelf() == value.Value.Type.TupleUnderlyingTypeOrSelf()) { forType = decision; break; } else if (ExpressionOfTypeMatchesPatternType(value.Value.Type, matchedType, ref _useSiteDiagnostics) != false) { // because there is overlap, we cannot reuse some earlier entry break; } } // if we did not piggy-back, then create a new decision tree node for the type. if (forType == null) { var type = value.Value.Type; var localSymbol = new SynthesizedLocal(_enclosingSymbol as MethodSymbol, type, SynthesizedLocalKind.PatternMatching, Syntax, false, RefKind.None); var narrowedExpression = new BoundLocal(Syntax, localSymbol, null, type); forType = new DecisionTree.ByValue(narrowedExpression, value.Value.Type.TupleUnderlyingTypeOrSelf(), localSymbol); byType.TypeAndDecision.Add(new KeyValuePair <TypeSymbol, DecisionTree>(value.Value.Type, forType)); } return(AddByValue(forType, value, makeDecision)); }
private BoundExpression HoistRefInitialization( SynthesizedLocal local, BoundAssignmentOperator node ) { Debug.Assert(local.SynthesizedKind == SynthesizedLocalKind.Spill); Debug.Assert(local.SyntaxOpt != null); Debug.Assert(this.OriginalMethod.IsAsync); var right = (BoundExpression)Visit(node.Right); var sideEffects = ArrayBuilder <BoundExpression> .GetInstance(); bool needsSacrificialEvaluation = false; var hoistedFields = ArrayBuilder <StateMachineFieldSymbol> .GetInstance(); AwaitExpressionSyntax awaitSyntaxOpt; int syntaxOffset; if (F.Compilation.Options.OptimizationLevel == OptimizationLevel.Debug) { awaitSyntaxOpt = (AwaitExpressionSyntax)local.GetDeclaratorSyntax(); syntaxOffset = OriginalMethod.CalculateLocalSyntaxOffset( LambdaUtilities.GetDeclaratorPosition(awaitSyntaxOpt), awaitSyntaxOpt.SyntaxTree ); } else { // These are only used to calculate debug id for ref-spilled variables, // no need to do so in release build. awaitSyntaxOpt = null; syntaxOffset = -1; } var replacement = HoistExpression( right, awaitSyntaxOpt, syntaxOffset, local.RefKind, sideEffects, hoistedFields, ref needsSacrificialEvaluation ); proxies.Add( local, new CapturedToExpressionSymbolReplacement( replacement, hoistedFields.ToImmutableAndFree(), isReusable: true ) ); if (needsSacrificialEvaluation) { var type = TypeMap.SubstituteType(local.Type).Type; var sacrificialTemp = F.SynthesizedLocal(type, refKind: RefKind.Ref); Debug.Assert( TypeSymbol.Equals(type, replacement.Type, TypeCompareKind.ConsiderEverything2) ); return(F.Sequence( ImmutableArray.Create(sacrificialTemp), sideEffects.ToImmutableAndFree(), F.AssignmentExpression(F.Local(sacrificialTemp), replacement, isRef: true) )); } if (sideEffects.Count == 0) { sideEffects.Free(); return(null); } var last = sideEffects.Last(); sideEffects.RemoveLast(); return(F.Sequence( ImmutableArray <LocalSymbol> .Empty, sideEffects.ToImmutableAndFree(), last )); }
/// <summary> /// Generate a thread-safe accessor for a regular field-like event. /// /// DelegateType tmp0 = _event; //backing field /// DelegateType tmp1; /// DelegateType tmp2; /// do { /// tmp1 = tmp0; /// tmp2 = (DelegateType)Delegate.Combine(tmp1, value); //Remove for -= /// tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); /// } while ((object)tmp0 != (object)tmp1); /// </summary> internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular(SourceEventSymbol eventSymbol, bool isAddMethod, CSharpCompilation compilation, DiagnosticBag diagnostics) { CSharpSyntaxNode syntax = eventSymbol.CSharpSyntaxNode; TypeSymbol delegateType = eventSymbol.Type; MethodSymbol accessor = isAddMethod ? eventSymbol.AddMethod : eventSymbol.RemoveMethod; ParameterSymbol thisParameter = accessor.ThisParameter; TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); MethodSymbol updateMethod = (MethodSymbol)compilation.GetSpecialTypeMember(isAddMethod ? SpecialMember.System_Delegate__Combine : SpecialMember.System_Delegate__Remove); MethodSymbol compareExchangeMethod = GetConstructedCompareExchangeMethod(delegateType, compilation, accessor.Locations[0], diagnostics); if ((object)compareExchangeMethod == null) { return(new BoundBlock(syntax, locals: ImmutableArray <LocalSymbol> .Empty, statements: ImmutableArray.Create <BoundStatement>( new BoundReturnStatement(syntax, expressionOpt: null) { WasCompilerGenerated = true })) { WasCompilerGenerated = true }); } GeneratedLabelSymbol loopLabel = new GeneratedLabelSymbol("loop"); const int numTemps = 3; LocalSymbol[] tmps = new LocalSymbol[numTemps]; BoundLocal[] boundTmps = new BoundLocal[numTemps]; for (int i = 0; i < numTemps; i++) { tmps[i] = new SynthesizedLocal(accessor, delegateType, SynthesizedLocalKind.LoweringTemp); boundTmps[i] = new BoundLocal(syntax, tmps[i], null, delegateType); } BoundThisReference fieldReceiver = eventSymbol.IsStatic ? null : new BoundThisReference(syntax, thisParameter.Type) { WasCompilerGenerated = true }; BoundFieldAccess boundBackingField = new BoundFieldAccess(syntax, receiver: fieldReceiver, fieldSymbol: eventSymbol.AssociatedField, constantValueOpt: null) { WasCompilerGenerated = true }; BoundParameter boundParameter = new BoundParameter(syntax, parameterSymbol: accessor.Parameters[0]) { WasCompilerGenerated = true }; // tmp0 = _event; BoundStatement tmp0Init = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[0], right: boundBackingField, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // LOOP: BoundStatement loopStart = new BoundLabelStatement(syntax, label: loopLabel) { WasCompilerGenerated = true }; // tmp1 = tmp0; BoundStatement tmp1Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[1], right: boundTmps[0], type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // (DelegateType)Delegate.Combine(tmp1, value) BoundExpression delegateUpdate = BoundConversion.SynthesizedNonUserDefined(syntax, operand: BoundCall.Synthesized(syntax, receiverOpt: null, method: updateMethod, arguments: ImmutableArray.Create <BoundExpression>(boundTmps[1], boundParameter)), kind: ConversionKind.ExplicitReference, type: delegateType); // tmp2 = (DelegateType)Delegate.Combine(tmp1, value); BoundStatement tmp2Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[2], right: delegateUpdate, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1) BoundExpression compareExchange = BoundCall.Synthesized(syntax, receiverOpt: null, method: compareExchangeMethod, arguments: ImmutableArray.Create <BoundExpression>(boundBackingField, boundTmps[2], boundTmps[1])); // tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); BoundStatement tmp0Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[0], right: compareExchange, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // tmp0 == tmp1 // i.e. exit when they are equal, jump to start otherwise BoundExpression loopExitCondition = new BoundBinaryOperator(syntax, operatorKind: BinaryOperatorKind.ObjectEqual, left: boundTmps[0], right: boundTmps[1], constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType) { WasCompilerGenerated = true }; // branchfalse (tmp0 == tmp1) LOOP BoundStatement loopEnd = new BoundConditionalGoto(syntax, condition: loopExitCondition, jumpIfTrue: false, label: loopLabel) { WasCompilerGenerated = true }; BoundStatement @return = new BoundReturnStatement(syntax, expressionOpt: null) { WasCompilerGenerated = true }; return(new BoundBlock(syntax, locals: tmps.AsImmutable(), statements: ImmutableArray.Create <BoundStatement>( tmp0Init, loopStart, tmp1Update, tmp2Update, tmp0Update, loopEnd, @return)) { WasCompilerGenerated = true }); }
public override BoundNode VisitTryStatement(BoundTryStatement node) { var tryStatementSyntax = node.Syntax; // If you add a syntax kind to the assertion below, please also ensure // that the scenario has been tested with Edit-and-Continue. Debug.Assert( tryStatementSyntax.IsKind(SyntaxKind.TryStatement) || tryStatementSyntax.IsKind(SyntaxKind.UsingStatement) || tryStatementSyntax.IsKind(SyntaxKind.ForEachStatement) || tryStatementSyntax.IsKind(SyntaxKind.ForEachVariableStatement)); BoundStatement finalizedRegion; BoundBlock rewrittenFinally; var finallyContainsAwaits = _analysis.FinallyContainsAwaits(node); if (!finallyContainsAwaits) { finalizedRegion = RewriteFinalizedRegion(node); rewrittenFinally = (BoundBlock)this.Visit(node.FinallyBlockOpt); if (rewrittenFinally == null) { return(finalizedRegion); } var asTry = finalizedRegion as BoundTryStatement; if (asTry != null) { // since finalized region is a try we can just attach finally to it Debug.Assert(asTry.FinallyBlockOpt == null); return(asTry.Update(asTry.TryBlock, asTry.CatchBlocks, rewrittenFinally, asTry.FinallyLabelOpt, asTry.PreferFaultHandler)); } else { // wrap finalizedRegion into a Try with a finally. return(_F.Try((BoundBlock)finalizedRegion, ImmutableArray <BoundCatchBlock> .Empty, rewrittenFinally)); } } // rewrite finalized region (try and catches) in the current frame var frame = PushFrame(node); finalizedRegion = RewriteFinalizedRegion(node); rewrittenFinally = (BoundBlock)this.VisitBlock(node.FinallyBlockOpt); PopFrame(); var exceptionType = _F.SpecialType(SpecialType.System_Object); var pendingExceptionLocal = new SynthesizedLocal(_F.CurrentFunction, TypeSymbolWithAnnotations.Create(exceptionType), SynthesizedLocalKind.TryAwaitPendingException, tryStatementSyntax); var finallyLabel = _F.GenerateLabel("finallyLabel"); var pendingBranchVar = new SynthesizedLocal(_F.CurrentFunction, TypeSymbolWithAnnotations.Create(_F.SpecialType(SpecialType.System_Int32)), SynthesizedLocalKind.TryAwaitPendingBranch, tryStatementSyntax); var catchAll = _F.Catch(_F.Local(pendingExceptionLocal), _F.Block()); var catchAndPendException = _F.Try( _F.Block( finalizedRegion, _F.HiddenSequencePoint(), _F.Goto(finallyLabel), PendBranches(frame, pendingBranchVar, finallyLabel)), ImmutableArray.Create(catchAll), finallyLabel: finallyLabel); BoundBlock syntheticFinallyBlock = _F.Block( _F.HiddenSequencePoint(), _F.Label(finallyLabel), rewrittenFinally, _F.HiddenSequencePoint(), UnpendException(pendingExceptionLocal), UnpendBranches( frame, pendingBranchVar, pendingExceptionLocal)); BoundStatement syntheticFinally = syntheticFinallyBlock; if (_F.CurrentFunction.IsAsync && _F.CurrentFunction.IsIterator) { // We wrap this block so that it can be processed as a finally block by async-iterator rewriting syntheticFinally = _F.ExtractedFinallyBlock(syntheticFinallyBlock); } var locals = ArrayBuilder <LocalSymbol> .GetInstance(); var statements = ArrayBuilder <BoundStatement> .GetInstance(); statements.Add(_F.HiddenSequencePoint()); locals.Add(pendingExceptionLocal); statements.Add(_F.Assignment(_F.Local(pendingExceptionLocal), _F.Default(pendingExceptionLocal.Type.TypeSymbol))); locals.Add(pendingBranchVar); statements.Add(_F.Assignment(_F.Local(pendingBranchVar), _F.Default(pendingBranchVar.Type.TypeSymbol))); LocalSymbol returnLocal = frame.returnValue; if (returnLocal != null) { locals.Add(returnLocal); } statements.Add(catchAndPendException); statements.Add(syntheticFinally); var completeTry = _F.Block( locals.ToImmutableAndFree(), statements.ToImmutableAndFree()); return(completeTry); }