未验证 提交 a1fcf0a7 编写于 作者: J Julien Couvreur 提交者: GitHub

Async-streams: disposal should continue without jump within a finally (#46188)

上级 804ccfb3
......@@ -23,13 +23,14 @@ internal sealed class AsyncIteratorMethodToStateMachineRewriter : AsyncMethodToS
private readonly AsyncIteratorInfo _asyncIteratorInfo;
/// <summary>
/// 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 an extracted `finally`, we will use the label preceding the extracted `finally`.
/// Inside a `finally`, we will use the label terminating the `finally` (to avoid restrictions with leave opcode).
/// Inside a `finally`, we'll have no/null label (disposal continues without a jump).
/// </summary>
private LabelSymbol _enclosingFinallyEntryOrFinallyExitOrExitLabel;
private ArrayBuilder<LabelSymbol> _previousDisposalLabels;
private LabelSymbol _currentDisposalLabel;
/// <summary>
/// We use _exprReturnLabel for normal end of method (ie. no more values) and `yield break;`.
......@@ -62,7 +63,7 @@ internal sealed class AsyncIteratorMethodToStateMachineRewriter : AsyncMethodToS
Debug.Assert(asyncIteratorInfo != null);
_asyncIteratorInfo = asyncIteratorInfo;
_enclosingFinallyEntryOrFinallyExitOrExitLabel = _exprReturnLabel;
_currentDisposalLabel = _exprReturnLabel;
_exprReturnLabelTrue = F.GenerateLabel("yieldReturn");
}
......@@ -152,24 +153,25 @@ protected override BoundStatement GenerateSetExceptionCall(LocalSymbol exception
return F.Block(builder.ToImmutableAndFree());
}
private BoundStatement GenerateJumpToCurrentFinallyOrExit()
private BoundStatement GenerateJumpToCurrentDisposalLabel()
{
Debug.Assert((object)_enclosingFinallyEntryOrFinallyExitOrExitLabel != null);
Debug.Assert(_currentDisposalLabel is object);
return F.If(
// if (disposeMode)
F.InstanceField(_asyncIteratorInfo.DisposeModeField),
// goto finallyOrExitLabel;
thenClause: F.Goto(_enclosingFinallyEntryOrFinallyExitOrExitLabel));
// goto currentDisposalLabel;
thenClause: F.Goto(_currentDisposalLabel));
}
private BoundStatement AppendJumpToCurrentFinallyOrExit(BoundStatement node)
private BoundStatement AppendJumpToCurrentDisposalLabel(BoundStatement node)
{
Debug.Assert(_currentDisposalLabel is object);
// Append:
// if (disposeMode) goto _enclosingFinallyOrExitLabel;
// if (disposeMode) goto currentDisposalLabel;
return F.Block(
node,
GenerateJumpToCurrentFinallyOrExit());
GenerateJumpToCurrentDisposalLabel());
}
protected override BoundBinaryOperator ShouldEnterFinallyBlock()
......@@ -205,9 +207,10 @@ protected override BoundStatement VisitBody(BoundStatement body)
var rewrittenBody = (BoundStatement)Visit(body);
Debug.Assert(_exprReturnLabel.Equals(_currentDisposalLabel));
return F.Block(
F.Label(resumeLabel), // initialStateResumeLabel:
GenerateJumpToCurrentFinallyOrExit(), // if (disposeMode) goto _exprReturnLabel;
GenerateJumpToCurrentDisposalLabel(), // if (disposeMode) goto _exprReturnLabel;
GenerateSetBothStates(StateMachineStates.NotStartedStateMachine), // this.state = cachedState = -1;
rewrittenBody);
}
......@@ -221,7 +224,7 @@ public override BoundNode VisitYieldReturnStatement(BoundYieldReturnStatement no
// <next_state_label>: ;
// <hidden sequence point>
// this.state = cachedState = NotStartedStateMachine;
// if (disposeMode) goto _enclosingFinallyOrExitLabel;
// if (disposeMode) goto currentDisposalLabel;
// Note: at label _exprReturnLabelTrue we have:
// _promiseOfValueOrEnd.SetResult(true);
......@@ -255,9 +258,10 @@ public override BoundNode VisitYieldReturnStatement(BoundYieldReturnStatement no
// this.state = cachedState = NotStartedStateMachine
GenerateSetBothStates(StateMachineStates.NotStartedStateMachine));
Debug.Assert(_currentDisposalLabel is object); // no yield return allowed inside a finally
blockBuilder.Add(
// if (disposeMode) goto _enclosingFinallyOrExitLabel;
GenerateJumpToCurrentFinallyOrExit());
// if (disposeMode) goto currentDisposalLabel;
GenerateJumpToCurrentDisposalLabel());
blockBuilder.Add(
F.HiddenSequencePoint());
......@@ -271,13 +275,14 @@ public override BoundNode VisitYieldBreakStatement(BoundYieldBreakStatement node
// Produce:
// disposeMode = true;
// goto _enclosingFinallyOrExitLabel;
// goto currentDisposalLabel;
Debug.Assert(_currentDisposalLabel is object); // no yield break allowed inside a finally
return F.Block(
// disposeMode = true;
SetDisposeMode(true),
// goto _enclosingFinallyOrExitLabel;
F.Goto(_enclosingFinallyEntryOrFinallyExitOrExitLabel));
// goto currentDisposalLabel;
F.Goto(_currentDisposalLabel));
}
private BoundExpressionStatement SetDisposeMode(bool value)
......@@ -292,60 +297,46 @@ private BoundExpressionStatement SetDisposeMode(bool value)
/// to restore execution from a given state, without executing other code to get there).
///
/// From there, we don't want normal code flow:
/// - from `yield return`, we'll jump to the enclosing `finally` (or method exit)
/// - after finishing a `finally`, we'll jump to the next enclosing `finally` (or method exit)
/// - from `yield return` within a try, we'll jump to its `finally` if it has one (or method exit)
/// - after finishing a `finally` within a `finally`, we'll continue
/// - after finishing a `finally` within a `try`, jump to the its `finally` if it has one (or method exit)
///
/// Some `finally` clauses may have already been rewritten and extracted to a plain block (<see cref="AsyncExceptionHandlerRewriter"/>).
/// In those cases, we saved the finally-entry label in <see cref="BoundTryStatement.FinallyLabelOpt"/>.
/// </summary>
public override BoundNode VisitTryStatement(BoundTryStatement node)
{
_previousDisposalLabels ??= new ArrayBuilder<LabelSymbol>();
_previousDisposalLabels.Push(_enclosingFinallyEntryOrFinallyExitOrExitLabel);
var finallyExit = F.GenerateLabel("finallyExit");
_previousDisposalLabels.Push(finallyExit);
if (node.FinallyBlockOpt != null)
var savedDisposalLabel = _currentDisposalLabel;
if (node.FinallyBlockOpt is object)
{
var finallyEntry = F.GenerateLabel("finallyEntry");
_enclosingFinallyEntryOrFinallyExitOrExitLabel = finallyEntry;
_currentDisposalLabel = finallyEntry;
// Add finallyEntry label:
// try
// {
// ...
// finallyEntry:
// Add finallyExit label:
// finally
// {
// ...
// finallyExit:
// }
node = node.Update(
tryBlock: F.Block(node.TryBlock, F.Label(finallyEntry)),
node.CatchBlocks, F.Block(node.FinallyBlockOpt, F.Label(finallyExit)), node.FinallyLabelOpt, node.PreferFaultHandler);
node.CatchBlocks, node.FinallyBlockOpt, node.FinallyLabelOpt, node.PreferFaultHandler);
}
else if ((object)node.FinallyLabelOpt != null)
else if (node.FinallyLabelOpt is object)
{
_enclosingFinallyEntryOrFinallyExitOrExitLabel = node.FinallyLabelOpt;
_currentDisposalLabel = node.FinallyLabelOpt;
}
var result = (BoundStatement)base.VisitTryStatement(node);
// While inside the try and catch blocks, we'll use the current finallyEntry label for disposal
// As soon as we close the try and catch blocks (ie. possibly enter the finally block), we'll use the finallyExit label for disposal (restored/popped from the stack by CloseTryCatchBlocks)
Debug.Assert(_enclosingFinallyEntryOrFinallyExitOrExitLabel == finallyExit);
_currentDisposalLabel = savedDisposalLabel;
// When exiting the try statement, we restore the previous disposal label.
_enclosingFinallyEntryOrFinallyExitOrExitLabel = _previousDisposalLabels.Pop();
if (node.FinallyBlockOpt != null)
if (node.FinallyBlockOpt != null && _currentDisposalLabel is object)
{
// Append:
// if (disposeMode) /* jump to parent's finally or exit */
result = AppendJumpToCurrentFinallyOrExit(result);
// if (disposeMode) goto currentDisposalLabel;
result = AppendJumpToCurrentDisposalLabel(result);
}
// Note: we add this jump to extracted `finally` blocks as well, using `VisitExtractedFinallyBlock` below
......@@ -353,9 +344,14 @@ public override BoundNode VisitTryStatement(BoundTryStatement node)
return result;
}
protected override void CloseTryCatchBlocks()
protected override BoundBlock VisitFinally(BoundBlock finallyBlock)
{
_enclosingFinallyEntryOrFinallyExitOrExitLabel = _previousDisposalLabels.Pop();
// within a finally, continuing disposal doesn't require any jump
var savedDisposalLabel = _currentDisposalLabel;
_currentDisposalLabel = null;
var result = base.VisitFinally(finallyBlock);
_currentDisposalLabel = savedDisposalLabel;
return result;
}
/// <summary>
......@@ -364,10 +360,17 @@ protected override void CloseTryCatchBlocks()
/// </summary>
public override BoundNode VisitExtractedFinallyBlock(BoundExtractedFinallyBlock extractedFinally)
{
// Remove the wrapping and append:
// if (disposeMode) goto enclosingFinallyOrExitLabel;
// Remove the wrapping and optionally append:
// if (disposeMode) goto currentDisposalLabel;
return AppendJumpToCurrentFinallyOrExit((BoundStatement)VisitBlock(extractedFinally.FinallyBlock));
BoundStatement result = VisitFinally(extractedFinally.FinallyBlock);
if (_currentDisposalLabel is object)
{
result = AppendJumpToCurrentDisposalLabel(result);
}
return result;
}
#endregion Visitors
......
......@@ -32,13 +32,6 @@ internal abstract class MethodToStateMachineRewriter : MethodToClassRewriter
/// </summary>
protected abstract BoundStatement GenerateReturn(bool finished);
/// <summary>
/// Signal the transition from `try`/`catch` blocks to the `finally` block.
/// </summary>
protected virtual void CloseTryCatchBlocks()
{
}
protected readonly SyntheticBoundNodeFactory F;
/// <summary>
......@@ -851,12 +844,11 @@ public override BoundNode VisitTryStatement(BoundTryStatement node)
ImmutableArray<BoundCatchBlock> catchBlocks = this.VisitList(node.CatchBlocks);
CloseTryCatchBlocks();
BoundBlock finallyBlockOpt = node.FinallyBlockOpt == null ? null : F.Block(
F.HiddenSequencePoint(),
F.If(
condition: ShouldEnterFinallyBlock(),
thenClause: (BoundBlock)this.Visit(node.FinallyBlockOpt)
thenClause: VisitFinally(node.FinallyBlockOpt)
),
F.HiddenSequencePoint());
......@@ -873,6 +865,11 @@ public override BoundNode VisitTryStatement(BoundTryStatement node)
return result;
}
protected virtual BoundBlock VisitFinally(BoundBlock finallyBlock)
{
return (BoundBlock)this.Visit(finallyBlock);
}
protected virtual BoundBinaryOperator ShouldEnterFinallyBlock()
{
return F.IntLessThan(F.Local(cachedState), F.Literal(StateMachineStates.FirstUnusedState));
......
......@@ -169,7 +169,7 @@ public static async Task Main()
var v = CompileAndVerify(comp, expectedOutput: "hello world");
v.VerifyIL("C.<GetSplits>d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @"
{
// Code size 250 (0xfa)
// Code size 240 (0xf0)
.maxstack 3
.locals init (int V_0,
System.Exception V_1)
......@@ -186,7 +186,7 @@ .maxstack 3
IL_0010: ldarg.0
IL_0011: ldfld ""bool C.<GetSplits>d__1.<>w__disposeMode""
IL_0016: brfalse.s IL_001d
IL_0018: leave IL_00d7
IL_0018: leave IL_00cd
IL_001d: ldarg.0
IL_001e: ldc.i4.m1
IL_001f: dup
......@@ -197,13 +197,13 @@ .maxstack 3
{
IL_0027: nop
IL_0028: nop
IL_0029: leave.s IL_00a0
IL_0029: leave.s IL_0096
}
finally
{
IL_002b: ldloc.0
IL_002c: ldc.i4.m1
IL_002d: bne.un.s IL_009f
IL_002d: bne.un.s IL_0095
IL_002f: nop
IL_0030: ldarg.0
IL_0031: ldarg.0
......@@ -243,58 +243,54 @@ .maxstack 3
IL_0081: endfinally
}
IL_0082: ldarg.0
IL_0083: ldfld ""bool C.<GetSplits>d__1.<>w__disposeMode""
IL_0088: brfalse.s IL_008c
IL_008a: br.s IL_009f
IL_008c: ldarg.0
IL_008d: ldnull
IL_008e: stfld ""object C.<GetSplits>d__1.<>s__1""
IL_0093: ldstr ""world""
IL_0098: call ""void System.Console.Write(string)""
IL_009d: nop
IL_009e: nop
IL_009f: endfinally
}
IL_0083: ldnull
IL_0084: stfld ""object C.<GetSplits>d__1.<>s__1""
IL_0089: ldstr ""world""
IL_008e: call ""void System.Console.Write(string)""
IL_0093: nop
IL_0094: nop
IL_0095: endfinally
}
IL_0096: ldarg.0
IL_0097: ldfld ""bool C.<GetSplits>d__1.<>w__disposeMode""
IL_009c: brfalse.s IL_00a0
IL_009e: leave.s IL_00cd
IL_00a0: ldarg.0
IL_00a1: ldfld ""bool C.<GetSplits>d__1.<>w__disposeMode""
IL_00a6: brfalse.s IL_00aa
IL_00a8: leave.s IL_00d7
IL_00aa: ldarg.0
IL_00ab: ldc.i4.1
IL_00ac: stfld ""bool C.<GetSplits>d__1.<>w__disposeMode""
IL_00b1: leave.s IL_00d7
IL_00a1: ldc.i4.1
IL_00a2: stfld ""bool C.<GetSplits>d__1.<>w__disposeMode""
IL_00a7: leave.s IL_00cd
}
catch System.Exception
{
IL_00b3: stloc.1
IL_00b4: ldarg.0
IL_00b5: ldc.i4.s -2
IL_00b7: stfld ""int C.<GetSplits>d__1.<>1__state""
IL_00bc: ldarg.0
IL_00bd: ldflda ""System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore<bool> C.<GetSplits>d__1.<>v__promiseOfValueOrEnd""
IL_00c2: ldloc.1
IL_00c3: call ""void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore<bool>.SetException(System.Exception)""
IL_00c8: nop
IL_00c9: ldarg.0
IL_00ca: ldflda ""System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.<GetSplits>d__1.<>t__builder""
IL_00cf: call ""void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()""
IL_00d4: nop
IL_00d5: leave.s IL_00f9
IL_00a9: stloc.1
IL_00aa: ldarg.0
IL_00ab: ldc.i4.s -2
IL_00ad: stfld ""int C.<GetSplits>d__1.<>1__state""
IL_00b2: ldarg.0
IL_00b3: ldflda ""System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore<bool> C.<GetSplits>d__1.<>v__promiseOfValueOrEnd""
IL_00b8: ldloc.1
IL_00b9: call ""void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore<bool>.SetException(System.Exception)""
IL_00be: nop
IL_00bf: ldarg.0
IL_00c0: ldflda ""System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.<GetSplits>d__1.<>t__builder""
IL_00c5: call ""void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()""
IL_00ca: nop
IL_00cb: leave.s IL_00ef
}
IL_00d7: ldarg.0
IL_00d8: ldc.i4.s -2
IL_00da: stfld ""int C.<GetSplits>d__1.<>1__state""
IL_00df: ldarg.0
IL_00e0: ldflda ""System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore<bool> C.<GetSplits>d__1.<>v__promiseOfValueOrEnd""
IL_00e5: ldc.i4.0
IL_00e6: call ""void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore<bool>.SetResult(bool)""
IL_00eb: nop
IL_00ec: ldarg.0
IL_00ed: ldflda ""System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.<GetSplits>d__1.<>t__builder""
IL_00f2: call ""void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()""
IL_00f7: nop
IL_00f8: ret
IL_00f9: ret
IL_00cd: ldarg.0
IL_00ce: ldc.i4.s -2
IL_00d0: stfld ""int C.<GetSplits>d__1.<>1__state""
IL_00d5: ldarg.0
IL_00d6: ldflda ""System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore<bool> C.<GetSplits>d__1.<>v__promiseOfValueOrEnd""
IL_00db: ldc.i4.0
IL_00dc: call ""void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore<bool>.SetResult(bool)""
IL_00e1: nop
IL_00e2: ldarg.0
IL_00e3: ldflda ""System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.<GetSplits>d__1.<>t__builder""
IL_00e8: call ""void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()""
IL_00ed: nop
IL_00ee: ret
IL_00ef: ret
}");
}
......@@ -340,7 +336,7 @@ public static async Task Main()
var v = CompileAndVerify(comp, expectedOutput: "hello world!");
v.VerifyIL("C.<GetSplits>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @"
{
// Code size 194 (0xc2)
// Code size 181 (0xb5)
.maxstack 3
.locals init (int V_0,
System.Exception V_1)
......@@ -356,97 +352,93 @@ .maxstack 3
IL_000e: br.s IL_0010
IL_0010: ldarg.0
IL_0011: ldfld ""bool C.<GetSplits>d__0.<>w__disposeMode""
IL_0016: brfalse.s IL_001d
IL_0018: leave IL_009f
IL_001d: ldarg.0
IL_001e: ldc.i4.m1
IL_001f: dup
IL_0020: stloc.0
IL_0021: stfld ""int C.<GetSplits>d__0.<>1__state""
IL_0026: nop
IL_0016: brfalse.s IL_001a
IL_0018: leave.s IL_0092
IL_001a: ldarg.0
IL_001b: ldc.i4.m1
IL_001c: dup
IL_001d: stloc.0
IL_001e: stfld ""int C.<GetSplits>d__0.<>1__state""
IL_0023: nop
.try
{
IL_0027: nop
IL_0028: nop
IL_0029: leave.s IL_0068
IL_0024: nop
IL_0025: nop
IL_0026: leave.s IL_005b
}
finally
{
IL_002b: ldloc.0
IL_002c: ldc.i4.m1
IL_002d: bne.un.s IL_0067
IL_002f: nop
IL_0028: ldloc.0
IL_0029: ldc.i4.m1
IL_002a: bne.un.s IL_005a
IL_002c: nop
.try
{
IL_0030: nop
IL_0031: ldstr ""hello ""
IL_0036: call ""void System.Console.Write(string)""
IL_003b: nop
IL_003c: nop
IL_003d: leave.s IL_0051
IL_002d: nop
IL_002e: ldstr ""hello ""
IL_0033: call ""void System.Console.Write(string)""
IL_0038: nop
IL_0039: nop
IL_003a: leave.s IL_004e
}
finally
{
IL_003f: ldloc.0
IL_0040: ldc.i4.m1
IL_0041: bne.un.s IL_0050
IL_0043: nop
IL_0044: ldstr ""world""
IL_0049: call ""void System.Console.Write(string)""
IL_004e: nop
IL_004f: nop
IL_0050: endfinally
IL_003c: ldloc.0
IL_003d: ldc.i4.m1
IL_003e: bne.un.s IL_004d
IL_0040: nop
IL_0041: ldstr ""world""
IL_0046: call ""void System.Console.Write(string)""
IL_004b: nop
IL_004c: nop
IL_004d: endfinally
}
IL_0051: ldarg.0
IL_0052: ldfld ""bool C.<GetSplits>d__0.<>w__disposeMode""
IL_0057: brfalse.s IL_005b
IL_0059: br.s IL_0067
IL_005b: ldstr ""!""
IL_0060: call ""void System.Console.Write(string)""
IL_0065: nop
IL_0066: nop
IL_0067: endfinally
}
IL_0068: ldarg.0
IL_0069: ldfld ""bool C.<GetSplits>d__0.<>w__disposeMode""
IL_006e: brfalse.s IL_0072
IL_0070: leave.s IL_009f
IL_0072: ldarg.0
IL_0073: ldc.i4.1
IL_0074: stfld ""bool C.<GetSplits>d__0.<>w__disposeMode""
IL_0079: leave.s IL_009f
IL_004e: ldstr ""!""
IL_0053: call ""void System.Console.Write(string)""
IL_0058: nop
IL_0059: nop
IL_005a: endfinally
}
IL_005b: ldarg.0
IL_005c: ldfld ""bool C.<GetSplits>d__0.<>w__disposeMode""
IL_0061: brfalse.s IL_0065
IL_0063: leave.s IL_0092
IL_0065: ldarg.0
IL_0066: ldc.i4.1
IL_0067: stfld ""bool C.<GetSplits>d__0.<>w__disposeMode""
IL_006c: leave.s IL_0092
}
catch System.Exception
{
IL_007b: stloc.1
IL_007c: ldarg.0
IL_007d: ldc.i4.s -2
IL_007f: stfld ""int C.<GetSplits>d__0.<>1__state""
IL_006e: stloc.1
IL_006f: ldarg.0
IL_0070: ldc.i4.s -2
IL_0072: stfld ""int C.<GetSplits>d__0.<>1__state""
IL_0077: ldarg.0
IL_0078: ldflda ""System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore<bool> C.<GetSplits>d__0.<>v__promiseOfValueOrEnd""
IL_007d: ldloc.1
IL_007e: call ""void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore<bool>.SetException(System.Exception)""
IL_0083: nop
IL_0084: ldarg.0
IL_0085: ldflda ""System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore<bool> C.<GetSplits>d__0.<>v__promiseOfValueOrEnd""
IL_008a: ldloc.1
IL_008b: call ""void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore<bool>.SetException(System.Exception)""
IL_0090: nop
IL_0091: ldarg.0
IL_0092: ldflda ""System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.<GetSplits>d__0.<>t__builder""
IL_0097: call ""void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()""
IL_009c: nop
IL_009d: leave.s IL_00c1
IL_0085: ldflda ""System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.<GetSplits>d__0.<>t__builder""
IL_008a: call ""void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()""
IL_008f: nop
IL_0090: leave.s IL_00b4
}
IL_009f: ldarg.0
IL_00a0: ldc.i4.s -2
IL_00a2: stfld ""int C.<GetSplits>d__0.<>1__state""
IL_0092: ldarg.0
IL_0093: ldc.i4.s -2
IL_0095: stfld ""int C.<GetSplits>d__0.<>1__state""
IL_009a: ldarg.0
IL_009b: ldflda ""System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore<bool> C.<GetSplits>d__0.<>v__promiseOfValueOrEnd""
IL_00a0: ldc.i4.0
IL_00a1: call ""void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore<bool>.SetResult(bool)""
IL_00a6: nop
IL_00a7: ldarg.0
IL_00a8: ldflda ""System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore<bool> C.<GetSplits>d__0.<>v__promiseOfValueOrEnd""
IL_00ad: ldc.i4.0
IL_00ae: call ""void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore<bool>.SetResult(bool)""
IL_00b3: nop
IL_00b4: ldarg.0
IL_00b5: ldflda ""System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.<GetSplits>d__0.<>t__builder""
IL_00ba: call ""void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()""
IL_00bf: nop
IL_00c0: ret
IL_00c1: ret
IL_00a8: ldflda ""System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.<GetSplits>d__0.<>t__builder""
IL_00ad: call ""void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()""
IL_00b2: nop
IL_00b3: ret
IL_00b4: ret
}");
}
......@@ -7841,5 +7833,152 @@ public partial class C3
Diagnostic(ErrorCode.ERR_PartialMethodWithNonVoidReturnMustHaveAccessMods, "M2").WithArguments("C3.M2(System.Threading.CancellationToken)").WithLocation(41, 68)
);
}
[Fact]
[WorkItem(43936, "https://github.com/dotnet/roslyn/issues/43936")]
public void TryFinallyNestedInsideFinally()
{
var comp = CreateCompilationWithAsyncIterator(@"
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class C
{
public static async IAsyncEnumerable<int> M()
{
try
{
await Task.Yield();
yield return 1;
throw null;
}
finally
{
Console.Write(""BEFORE "");
try
{
Console.Write(""INSIDE "");
}
finally
{
Console.Write(""INSIDE2 "");
}
Console.Write(""AFTER "");
}
throw null;
}
public static async Task Main()
{
await foreach (var i in C.M())
{
break;
}
}
}", options: TestOptions.DebugExe);
var v = CompileAndVerify(comp, expectedOutput: "BEFORE INSIDE INSIDE2 AFTER");
v.VerifyDiagnostics();
}
[Fact]
[WorkItem(43936, "https://github.com/dotnet/roslyn/issues/43936")]
public void TryFinallyNestedInsideFinally_WithAwaitInFinally()
{
var comp = CreateCompilationWithAsyncIterator(@"
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class C
{
public static async IAsyncEnumerable<int> M()
{
try
{
yield return 1;
throw null;
}
finally
{
await Task.Yield();
Console.Write(""BEFORE "");
try
{
Console.Write(""INSIDE "");
}
finally
{
Console.Write(""INSIDE2 "");
}
Console.Write(""AFTER "");
}
throw null;
}
public static async Task Main()
{
await foreach (var i in C.M())
{
break;
}
}
}", options: TestOptions.DebugExe);
var v = CompileAndVerify(comp, expectedOutput: "BEFORE INSIDE INSIDE2 AFTER");
v.VerifyDiagnostics();
}
[Fact]
[WorkItem(43936, "https://github.com/dotnet/roslyn/issues/43936")]
public void TryFinallyNestedInsideFinally_WithAwaitInNestedFinally()
{
var comp = CreateCompilationWithAsyncIterator(@"
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class C
{
public static async IAsyncEnumerable<int> M()
{
try
{
yield return 1;
throw null;
}
finally
{
Console.Write(""BEFORE "");
try
{
Console.Write(""INSIDE "");
}
finally
{
Console.Write(""INSIDE2 "");
await Task.Yield();
}
Console.Write(""AFTER "");
}
throw null;
}
public static async Task Main()
{
await foreach (var i in C.M())
{
break;
}
}
}", options: TestOptions.DebugExe);
var v = CompileAndVerify(comp, expectedOutput: "BEFORE INSIDE INSIDE2 AFTER");
v.VerifyDiagnostics();
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册