Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ internal sealed class AsyncIteratorMethodToStateMachineRewriter : AsyncMethodToS
/// Where should we jump to to continue the execution of disposal path.
///
/// Initially, this is the method's return value label (<see cref="AsyncMethodToStateMachineRewriter._exprReturnLabel"/>).
/// Inside a `try` or `catch` with a `finally`, we'll use the label directly preceding the `finally`.
/// Inside a `try` or `catch` with a `finally`, we'll use the label directly following the `finally`.
/// Inside a `try` or `catch` with an extracted `finally`, we will use the label preceding the extracted `finally`.
/// Inside a `finally`, we'll have no/null label (disposal continues without a jump).
/// </summary>
Expand Down Expand Up @@ -351,7 +351,8 @@ private BoundExpressionStatement SetDisposeMode(bool value)

/// <summary>
/// An async-iterator state machine has a flag indicating "dispose mode".
/// We enter dispose mode by calling DisposeAsync() when the state machine is paused on a `yield return`.
/// We enter dispose mode by calling DisposeAsync() when the state machine is paused on a `yield return`,
/// or by reaching a `yield break`.
/// DisposeAsync() will resume execution of the state machine from that state (using existing dispatch mechanism
/// to restore execution from a given state, without executing other code to get there).
///
Expand All @@ -366,21 +367,11 @@ private BoundExpressionStatement SetDisposeMode(bool value)
public override BoundNode VisitTryStatement(BoundTryStatement node)
{
var savedDisposalLabel = _currentDisposalLabel;
LabelSymbol afterFinally = null;
if (node.FinallyBlockOpt is object)
{
var finallyEntry = F.GenerateLabel("finallyEntry");
_currentDisposalLabel = finallyEntry;

// Add finallyEntry label:
// try
// {
// ...
// finallyEntry:
// }

node = node.Update(
tryBlock: F.Block(node.TryBlock, F.Label(finallyEntry)),
node.CatchBlocks, node.FinallyBlockOpt, node.FinallyLabelOpt, node.PreferFaultHandler);
afterFinally = F.GenerateLabel("afterFinally");
_currentDisposalLabel = afterFinally;
}
else if (node.FinallyLabelOpt is object)
{
Expand All @@ -389,6 +380,14 @@ public override BoundNode VisitTryStatement(BoundTryStatement node)

var result = (BoundStatement)base.VisitTryStatement(node);

if (afterFinally != null)
{
// Append a label immediately after the try-catch-finally statement,
// which disposal within `try`/`catch` blocks jumps to in order to pass control flow to the `finally` block implicitly:
// tryEnd:
result = F.Block(result, F.Label(afterFinally));
}

_currentDisposalLabel = savedDisposalLabel;

if (node.FinallyBlockOpt != null && _currentDisposalLabel is object)
Expand Down
Loading