diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index 971adeea4bcae..91734f420c4db 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -263,7 +263,7 @@ private BoundForEachStatement BindForEachPartsWorker(BindingDiagnosticBag diagno var placeholder = new BoundAwaitableValuePlaceholder(expr, builder.MoveNextInfo?.Method.ReturnType ?? CreateErrorType()); awaitInfo = BindAwaitInfo(placeholder, expr, diagnostics, ref hasErrors); - if (!hasErrors && awaitInfo.GetResult?.ReturnType.SpecialType != SpecialType.System_Boolean) + if (!hasErrors && (awaitInfo.GetResult ?? awaitInfo.RuntimeAsyncAwaitMethod)?.ReturnType.SpecialType != SpecialType.System_Boolean) { diagnostics.Add(ErrorCode.ERR_BadGetAsyncEnumerator, expr.Location, getEnumeratorMethod.ReturnTypeWithAnnotations, getEnumeratorMethod); hasErrors = true; diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundAwaitableInfo.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundAwaitableInfo.cs index f2e0914bf17da..cec0bdf783cfd 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundAwaitableInfo.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundAwaitableInfo.cs @@ -34,5 +34,7 @@ private partial void Validate() break; } } + + Debug.Assert(GetAwaiter is not null || RuntimeAsyncAwaitMethod is not null || IsDynamic || HasErrors); } } diff --git a/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs b/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs index 5b8abab5452d7..7e62d676d7f0a 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/CodeGenerator.cs @@ -321,7 +321,10 @@ private void HandleReturn() { _builder.MarkLabel(s_returnLabel); - Debug.Assert(_method.ReturnsVoid == (_returnTemp == null)); + Debug.Assert(_method.ReturnsVoid == (_returnTemp == null) + || (_method.IsAsync + && _module.Compilation.IsRuntimeAsyncEnabledIn(_method) + && ((InternalSpecialType)_method.ReturnType.ExtendedSpecialType) is InternalSpecialType.System_Threading_Tasks_Task or InternalSpecialType.System_Threading_Tasks_ValueTask)); if (_emitPdbSequencePoints && !_method.IsIterator && !_method.IsAsync) { diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncExceptionHandlerRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncExceptionHandlerRewriter.cs index 3f0fb684273ae..924e66fde3953 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncExceptionHandlerRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncExceptionHandlerRewriter.cs @@ -28,6 +28,7 @@ internal sealed class AsyncExceptionHandlerRewriter : BoundTreeRewriterWithStack private AwaitCatchFrame _currentAwaitCatchFrame; private AwaitFinallyFrame _currentAwaitFinallyFrame = new AwaitFinallyFrame(); private bool _inCatchWithoutAwaits; + private bool _needsFinalThrow; private AsyncExceptionHandlerRewriter( MethodSymbol containingMethod, @@ -129,9 +130,45 @@ public static BoundStatement Rewrite( var rewriter = new AsyncExceptionHandlerRewriter(containingSymbol, containingType, factory, analysis); var loweredStatement = (BoundStatement)rewriter.Visit(statement); + loweredStatement = rewriter.FinalizeMethodBody(loweredStatement); + return loweredStatement; } + private BoundStatement FinalizeMethodBody(BoundStatement loweredStatement) + { + if (loweredStatement == null) + { + return null; + } + + // When we add a `switch (pendingBranch)` to the end of the try block, + // this can result in a method body that cannot be proven to terminate. + // While we can technically prove it by doing a full data flow analysis, + // this is effectively the halting problem, and the runtime will not do + // this analysis. The resulting IL will be technically invalid, and if it's + // not wrapped in another state machine (a la the compiler async rewriter), + // the runtime will refuse to load it. For runtime async, where we are effectively + // emitting the result of this rewriter directly, we need to ensure that + // we always emit a throw at the end of the try block when the switch is present. + // This ensures that the method can be proven to terminate, and the runtime will + // accept it. This throw will never be reached, and we could potentially do a + // more sophisticated analysis to determine if it is needed by pushing control + // flow analysis through the bound nodes, see https://github.com/dotnet/roslyn/pull/78970. + // This is risky, however, and for now we are taking the conservative approach + // of always emitting the throw. + BoundStatement result = loweredStatement; + if (_needsFinalThrow) + { + result = _F.Block( + loweredStatement, + _F.Throw(_F.Null(_F.SpecialType(SpecialType.System_Object))) + ); + } + + return result; + } + public override BoundNode VisitTryStatement(BoundTryStatement node) { var tryStatementSyntax = node.Syntax; @@ -354,6 +391,7 @@ private BoundStatement UnpendBranches( cases.Add(caseStatement); } + _needsFinalThrow = true; return _F.Switch(_F.Local(pendingBranchVar), cases.ToImmutableAndFree()); } @@ -402,22 +440,37 @@ public override BoundNode VisitReturnStatement(BoundReturnStatement node) private BoundStatement UnpendException(LocalSymbol pendingExceptionLocal) { + // If this is runtime async, we don't need to create a second local for the exception, + // as the pendingExceptionLocal will not be hoisted to a state machine by a future rewrite. + if (_F.Compilation.IsRuntimeAsyncEnabledIn(_F.CurrentFunction)) + { + // pendingExceptionLocal is already an object + // so we can just use it directly + return checkAndThrow(pendingExceptionLocal); + } + // create a temp. // pendingExceptionLocal will certainly be captured, no need to access it over and over. LocalSymbol obj = _F.SynthesizedLocal(_F.SpecialType(SpecialType.System_Object)); var objInit = _F.Assignment(_F.Local(obj), _F.Local(pendingExceptionLocal)); // throw pendingExceptionLocal; - BoundStatement rethrow = Rethrow(obj); - return _F.Block( ImmutableArray.Create(obj), objInit, - _F.If( - _F.ObjectNotEqual( - _F.Local(obj), - _F.Null(obj.Type)), - rethrow)); + checkAndThrow(obj)); + + BoundStatement checkAndThrow(LocalSymbol obj) + { + BoundStatement rethrow = Rethrow(obj); + + BoundStatement checkAndThrow = _F.If( + _F.ObjectNotEqual( + _F.Local(obj), + _F.Null(obj.Type)), + rethrow); + return checkAndThrow; + } } private BoundStatement Rethrow(LocalSymbol obj) @@ -706,14 +759,24 @@ public override BoundNode VisitLambda(BoundLambda node) { var oldContainingSymbol = _F.CurrentFunction; var oldAwaitFinallyFrame = _currentAwaitFinallyFrame; + var oldNeedsFinalThrow = _needsFinalThrow; _F.CurrentFunction = node.Symbol; _currentAwaitFinallyFrame = new AwaitFinallyFrame(); + _needsFinalThrow = false; - var result = base.VisitLambda(node); + var result = (BoundLambda)base.VisitLambda(node); + result = result.Update( + result.UnboundLambda, + result.Symbol, + (BoundBlock)FinalizeMethodBody(result.Body), + node.Diagnostics, + node.Binder, + node.Type); _F.CurrentFunction = oldContainingSymbol; _currentAwaitFinallyFrame = oldAwaitFinallyFrame; + _needsFinalThrow = oldNeedsFinalThrow; return result; } @@ -722,14 +785,18 @@ public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatemen { var oldContainingSymbol = _F.CurrentFunction; var oldAwaitFinallyFrame = _currentAwaitFinallyFrame; + var oldNeedsFinalThrow = _needsFinalThrow; _F.CurrentFunction = node.Symbol; _currentAwaitFinallyFrame = new AwaitFinallyFrame(); + _needsFinalThrow = false; - var result = base.VisitLocalFunctionStatement(node); + var result = (BoundLocalFunctionStatement)base.VisitLocalFunctionStatement(node); + result = result.Update(node.Symbol, (BoundBlock)FinalizeMethodBody(result.Body), (BoundBlock)FinalizeMethodBody(result.ExpressionBody)); _F.CurrentFunction = oldContainingSymbol; _currentAwaitFinallyFrame = oldAwaitFinallyFrame; + _needsFinalThrow = oldNeedsFinalThrow; return result; } diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/RuntimeAsyncRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/RuntimeAsyncRewriter.cs index 96f226b001662..d3d1b807241ae 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/RuntimeAsyncRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/RuntimeAsyncRewriter.cs @@ -22,20 +22,17 @@ public static BoundStatement Rewrite( return node; } - // PROTOTYPE: try/finally rewriting // PROTOTYPE: struct lifting - var rewriter = new RuntimeAsyncRewriter(compilationState.Compilation, new SyntheticBoundNodeFactory(method, node.Syntax, compilationState, diagnostics)); + var rewriter = new RuntimeAsyncRewriter(new SyntheticBoundNodeFactory(method, node.Syntax, compilationState, diagnostics)); var result = (BoundStatement)rewriter.Visit(node); return SpillSequenceSpiller.Rewrite(result, method, compilationState, diagnostics); } - private readonly CSharpCompilation _compilation; private readonly SyntheticBoundNodeFactory _factory; private readonly Dictionary _placeholderMap; - private RuntimeAsyncRewriter(CSharpCompilation compilation, SyntheticBoundNodeFactory factory) + private RuntimeAsyncRewriter(SyntheticBoundNodeFactory factory) { - _compilation = compilation; _factory = factory; _placeholderMap = []; } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs index fc099b36f20b7..0dc679d32a1e5 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs @@ -223,14 +223,14 @@ private BoundStatement RewriteForEachEnumerator( var disposalFinallyBlock = GetDisposalFinallyBlock(forEachSyntax, enumeratorInfo, enumeratorType, boundEnumeratorVar, out var hasAsyncDisposal); if (isAsync) { - Debug.Assert(awaitableInfo is { GetResult: { } }); + Debug.Assert(awaitableInfo is { GetResult: not null } or { RuntimeAsyncAwaitMethod: not null }); // We need to be sure that when the disposal isn't async we reserve an unused state machine state number for it, // so that await foreach always produces 2 state machine states: one for MoveNextAsync and the other for DisposeAsync. // Otherwise, EnC wouldn't be able to map states when the disposal changes from having async dispose to not, or vice versa. var debugInfo = new BoundAwaitExpressionDebugInfo(s_moveNextAsyncAwaitId, ReservedStateMachineCount: (byte)(hasAsyncDisposal ? 0 : 1)); - rewrittenCondition = RewriteAwaitExpression(forEachSyntax, rewrittenCondition, awaitableInfo, awaitableInfo.GetResult.ReturnType, debugInfo, used: true); + rewrittenCondition = RewriteAwaitExpression(forEachSyntax, rewrittenCondition, awaitableInfo, (awaitableInfo.GetResult ?? awaitableInfo.RuntimeAsyncAwaitMethod)!.ReturnType, debugInfo, used: true); } BoundStatement whileLoop = RewriteWhileStatement( diff --git a/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs b/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs index 66fae04bf79cc..3afc4b6638c33 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs @@ -704,15 +704,24 @@ public override BoundNode VisitYieldReturnStatement(BoundYieldReturnStatement no return UpdateStatement(builder, node.Update(expression)); } +#nullable enable public override BoundNode VisitCatchBlock(BoundCatchBlock node) { - BoundExpression exceptionSourceOpt = (BoundExpression)this.Visit(node.ExceptionSourceOpt); + BoundExpression? exceptionSourceOpt = (BoundExpression?)this.Visit(node.ExceptionSourceOpt); var locals = node.Locals; var exceptionFilterPrologueOpt = node.ExceptionFilterPrologueOpt; - Debug.Assert(exceptionFilterPrologueOpt is null); // it is introduced by this pass - BoundSpillSequenceBuilder builder = null; + if (exceptionFilterPrologueOpt is not null) + { + exceptionFilterPrologueOpt = (BoundStatementList?)VisitStatementList(exceptionFilterPrologueOpt); + } + BoundSpillSequenceBuilder? builder = null; + var exceptionFilterOpt = VisitExpression(ref builder, node.ExceptionFilterOpt); + Debug.Assert(exceptionFilterPrologueOpt is null || builder is null, + "You are exercising SpillSequenceSpiller in a new fashion, causing a spill in an exception filter after LocalRewriting is complete. This is not someting " + + "that this builder supports today, so please update this rewrite to include the statements from exceptionFilterPrologueOpt with the appropriate " + + "syntax node and tracking."); if (builder is { }) { Debug.Assert(builder.Value is null); @@ -721,9 +730,10 @@ public override BoundNode VisitCatchBlock(BoundCatchBlock node) } BoundBlock body = (BoundBlock)this.Visit(node.Body); - TypeSymbol exceptionTypeOpt = this.VisitType(node.ExceptionTypeOpt); + TypeSymbol? exceptionTypeOpt = this.VisitType(node.ExceptionTypeOpt); return node.Update(locals, exceptionSourceOpt, exceptionTypeOpt, exceptionFilterPrologueOpt, exceptionFilterOpt, body, node.IsSynthesizedAsyncCatchAll); } +#nullable disable #if DEBUG public override BoundNode DefaultVisit(BoundNode node) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs index 3078e2a20345e..db0d38c794395 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs @@ -143,6 +143,14 @@ public static void Main() }"; var expected = @""; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.FailsPEVerify); + verifier.VerifyDiagnostics( + // (3,1): hidden CS8019: Unnecessary using directive. + // using System.Diagnostics; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System.Diagnostics;").WithLocation(3, 1) + ); } [WorkItem(14878, "https://github.com/dotnet/roslyn/issues/14878")] @@ -570,6 +578,16 @@ .locals init (int V_0, IL_0332: ret } "); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [G]: Unexpected type on the stack. { Offset = 0x104, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); } [Fact, WorkItem(855080, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/855080")] @@ -577,9 +595,6 @@ public void GenericCatchVariableInAsyncMethod() { var source = @" using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; namespace ConsoleApplication1 { @@ -612,6 +627,16 @@ public static async Task Goo() where T : Exception } "; CompileAndVerify(source, expectedOutput: "3"); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("3", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Bar]: Unexpected type on the stack. { Offset = 0xc, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [Goo]: Unexpected type on the stack. { Offset = 0x15, Found = value 'T', Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); } [Fact] @@ -651,6 +676,22 @@ public static void Main() -1 "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify( + comp, + expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [G]: Unexpected type on the stack. { Offset = 0x13, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (7,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) + ); } [Fact] @@ -689,6 +730,19 @@ public static void Main() exception "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [H]: Unexpected type on the stack. { Offset = 0xa, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (7,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) + ); } [Fact] @@ -839,9 +893,56 @@ .locals init (int V_0, IL_00d1: ret } "); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x29, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (7,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) + ); + + verifier.VerifyIL("Test.G()", """ + { + // Code size 42 (0x2a) + .maxstack 3 + .locals init (object V_0) + IL_0000: ldnull + IL_0001: stloc.0 + .try + { + IL_0002: leave.s IL_0007 + } + catch object + { + IL_0004: stloc.0 + IL_0005: leave.s IL_0007 + } + IL_0007: call "System.Threading.Tasks.Task Test.F()" + IL_000c: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0011: ldloc.0 + IL_0012: brfalse.s IL_0029 + IL_0014: ldloc.0 + IL_0015: isinst "System.Exception" + IL_001a: dup + IL_001b: brtrue.s IL_001f + IL_001d: ldloc.0 + IL_001e: throw + IL_001f: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0024: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0029: ret + } + """); } - [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.TestExecutionNeedsWindowsTypes)] + [Fact] public void AsyncInFinally002() { var source = @" @@ -893,12 +994,71 @@ public static void Main() } } }"; - var expected = @"FOne or more errors occurred. -"; + var expected = ExecutionConditionUtil.IsWindowsDesktop + ? @"FOne or more errors occurred." + : @"FOne or more errors occurred. (hello)"; + CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0xb, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (7,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) + ); + + verifier.VerifyIL("Test.G()", """ + { + // Code size 59 (0x3b) + .maxstack 2 + .locals init (int V_0, //x + object V_1, + int V_2) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: ldnull + IL_0003: stloc.1 + .try + { + IL_0004: ldstr "hello" + IL_0009: newobj "System.Exception..ctor(string)" + IL_000e: throw + } + catch object + { + IL_000f: stloc.1 + IL_0010: leave.s IL_0012 + } + IL_0012: ldloc.0 + IL_0013: call "System.Threading.Tasks.Task Test.F()" + IL_0018: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001d: stloc.2 + IL_001e: ldloc.2 + IL_001f: add + IL_0020: stloc.0 + IL_0021: ldloc.1 + IL_0022: brfalse.s IL_0039 + IL_0024: ldloc.1 + IL_0025: isinst "System.Exception" + IL_002a: dup + IL_002b: brtrue.s IL_002f + IL_002d: ldloc.1 + IL_002e: throw + IL_002f: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0034: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0039: ldnull + IL_003a: throw + } + """); } - [ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Fact] public void AsyncInFinally003() { var source = @" @@ -954,7 +1114,10 @@ public static void Main() }, module.GetFieldNames("Test.d__1")); }); - v.VerifyPdb("Test.G", @" + // Native PDBs require desktop + if (ExecutionConditionUtil.IsWindowsDesktop) + { + v.VerifyPdb("Test.G", @" @@ -982,6 +1145,7 @@ public static void Main() "); + } v.VerifyIL("Test.d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @"{ @@ -1168,7 +1332,8 @@ .locals init (int V_0, IL_0194: ldarg.0 IL_0195: ldnull IL_0196: stfld ""object Test.d__1.<>s__2"" - IL_019b: leave.s IL_01b7 + IL_019b: ldnull + IL_019c: throw } catch System.Exception { @@ -1193,6 +1358,78 @@ .locals init (int V_0, IL_01cb: nop IL_01cc: ret }", sequencePoints: "Test+d__1.MoveNext"); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x48, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (7,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) + ); + + verifier.VerifyIL("Test.G()", """ + { + // Code size 75 (0x4b) + .maxstack 2 + .locals init (int V_0, //x + object V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: ldnull + IL_0003: stloc.1 + IL_0004: ldc.i4.0 + IL_0005: stloc.2 + .try + { + IL_0006: call "System.Threading.Tasks.Task Test.F()" + IL_000b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0010: stloc.0 + IL_0011: ldloc.0 + IL_0012: stloc.3 + IL_0013: ldc.i4.1 + IL_0014: stloc.2 + IL_0015: leave.s IL_001a + } + catch object + { + IL_0017: stloc.1 + IL_0018: leave.s IL_001a + } + IL_001a: ldloc.0 + IL_001b: call "System.Threading.Tasks.Task Test.F()" + IL_0020: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: stloc.s V_4 + IL_0027: ldloc.s V_4 + IL_0029: add + IL_002a: stloc.0 + IL_002b: ldloc.1 + IL_002c: brfalse.s IL_0043 + IL_002e: ldloc.1 + IL_002f: isinst "System.Exception" + IL_0034: dup + IL_0035: brtrue.s IL_0039 + IL_0037: ldloc.1 + IL_0038: throw + IL_0039: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_003e: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0043: ldloc.2 + IL_0044: ldc.i4.1 + IL_0045: bne.un.s IL_0049 + IL_0047: ldloc.3 + IL_0048: ret + IL_0049: ldnull + IL_004a: throw + } + """); } [Fact] @@ -1241,6 +1478,971 @@ public static void Main() 2 "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x3e, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (7,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) + ); + + verifier.VerifyIL("Test.G()", """ + { + // Code size 63 (0x3f) + .maxstack 2 + .locals init (int V_0, //x + object V_1, + int V_2) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldnull + IL_0003: stloc.1 + .try + { + IL_0004: newobj "System.Exception..ctor()" + IL_0009: throw + } + catch object + { + IL_000a: stloc.1 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: call "System.Threading.Tasks.Task Test.F()" + IL_0013: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0018: stloc.2 + IL_0019: ldloc.2 + IL_001a: add + IL_001b: stloc.0 + IL_001c: ldloc.1 + IL_001d: brfalse.s IL_0034 + IL_001f: ldloc.1 + IL_0020: isinst "System.Exception" + IL_0025: dup + IL_0026: brtrue.s IL_002a + IL_0028: ldloc.1 + IL_0029: throw + IL_002a: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_002f: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0034: leave.s IL_003b + } + catch object + { + IL_0036: pop + IL_0037: ldloc.0 + IL_0038: stloc.2 + IL_0039: leave.s IL_003d + } + IL_003b: ldnull + IL_003c: throw + IL_003d: ldloc.2 + IL_003e: ret + } + """); + } + + [Fact] + public void AsyncInFinally005() + { + var source = @" +using System; +using System.Threading.Tasks; +class Test +{ + static async Task F() + { + return 2; + } + static async Task G() + { + int x = 0; + try + { + x = await F(); + throw new Exception(x.ToString()); + } + finally + { + x += await F(); + } + } + public static void Main() + { + System.Globalization.CultureInfo saveUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture; + System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.InvariantCulture; + Task t2 = G(); + try + { + t2.Wait(1000 * 60); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + System.Threading.Thread.CurrentThread.CurrentUICulture = saveUICulture; + } + } +}"; + var expected = "One or more errors occurred."; + if (!ExecutionConditionUtil.IsDesktop) + { + expected += " (2)"; + } + var verifier = CompileAndVerify(source, expectedOutput: expected); + verifier.VerifyIL("Test.d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 353 (0x161) + .maxstack 3 + .locals init (int V_0, + int V_1, + int V_2, + System.Runtime.CompilerServices.TaskAwaiter V_3, + object V_4, + System.Exception V_5) + IL_0000: ldarg.0 + IL_0001: ldfld "int Test.d__1.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0026 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq IL_00e9 + IL_0011: ldarg.0 + IL_0012: ldc.i4.0 + IL_0013: stfld "int Test.d__1.5__2" + IL_0018: ldarg.0 + IL_0019: ldnull + IL_001a: stfld "object Test.d__1.<>7__wrap2" + IL_001f: ldarg.0 + IL_0020: ldc.i4.0 + IL_0021: stfld "int Test.d__1.<>7__wrap3" + IL_0026: nop + .try + { + IL_0027: ldloc.0 + IL_0028: brfalse.s IL_0061 + IL_002a: call "System.Threading.Tasks.Task Test.F()" + IL_002f: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_0034: stloc.3 + IL_0035: ldloca.s V_3 + IL_0037: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_003c: brtrue.s IL_007d + IL_003e: ldarg.0 + IL_003f: ldc.i4.0 + IL_0040: dup + IL_0041: stloc.0 + IL_0042: stfld "int Test.d__1.<>1__state" + IL_0047: ldarg.0 + IL_0048: ldloc.3 + IL_0049: stfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_004e: ldarg.0 + IL_004f: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Test.d__1.<>t__builder" + IL_0054: ldloca.s V_3 + IL_0056: ldarg.0 + IL_0057: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, Test.d__1>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Test.d__1)" + IL_005c: leave IL_0160 + IL_0061: ldarg.0 + IL_0062: ldfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_0067: stloc.3 + IL_0068: ldarg.0 + IL_0069: ldflda "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_006e: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_0074: ldarg.0 + IL_0075: ldc.i4.m1 + IL_0076: dup + IL_0077: stloc.0 + IL_0078: stfld "int Test.d__1.<>1__state" + IL_007d: ldloca.s V_3 + IL_007f: call "int System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0084: stloc.2 + IL_0085: ldarg.0 + IL_0086: ldloc.2 + IL_0087: stfld "int Test.d__1.5__2" + IL_008c: ldarg.0 + IL_008d: ldflda "int Test.d__1.5__2" + IL_0092: call "string int.ToString()" + IL_0097: newobj "System.Exception..ctor(string)" + IL_009c: throw + } + catch object + { + IL_009d: stloc.s V_4 + IL_009f: ldarg.0 + IL_00a0: ldloc.s V_4 + IL_00a2: stfld "object Test.d__1.<>7__wrap2" + IL_00a7: leave.s IL_00a9 + } + IL_00a9: ldarg.0 + IL_00aa: ldarg.0 + IL_00ab: ldfld "int Test.d__1.5__2" + IL_00b0: stfld "int Test.d__1.<>7__wrap4" + IL_00b5: call "System.Threading.Tasks.Task Test.F()" + IL_00ba: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_00bf: stloc.3 + IL_00c0: ldloca.s V_3 + IL_00c2: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_00c7: brtrue.s IL_0105 + IL_00c9: ldarg.0 + IL_00ca: ldc.i4.1 + IL_00cb: dup + IL_00cc: stloc.0 + IL_00cd: stfld "int Test.d__1.<>1__state" + IL_00d2: ldarg.0 + IL_00d3: ldloc.3 + IL_00d4: stfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_00d9: ldarg.0 + IL_00da: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Test.d__1.<>t__builder" + IL_00df: ldloca.s V_3 + IL_00e1: ldarg.0 + IL_00e2: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, Test.d__1>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Test.d__1)" + IL_00e7: leave.s IL_0160 + IL_00e9: ldarg.0 + IL_00ea: ldfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_00ef: stloc.3 + IL_00f0: ldarg.0 + IL_00f1: ldflda "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_00f6: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_00fc: ldarg.0 + IL_00fd: ldc.i4.m1 + IL_00fe: dup + IL_00ff: stloc.0 + IL_0100: stfld "int Test.d__1.<>1__state" + IL_0105: ldloca.s V_3 + IL_0107: call "int System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_010c: stloc.2 + IL_010d: ldarg.0 + IL_010e: ldarg.0 + IL_010f: ldfld "int Test.d__1.<>7__wrap4" + IL_0114: ldloc.2 + IL_0115: add + IL_0116: stfld "int Test.d__1.5__2" + IL_011b: ldarg.0 + IL_011c: ldfld "object Test.d__1.<>7__wrap2" + IL_0121: stloc.s V_4 + IL_0123: ldloc.s V_4 + IL_0125: brfalse.s IL_013e + IL_0127: ldloc.s V_4 + IL_0129: isinst "System.Exception" + IL_012e: dup + IL_012f: brtrue.s IL_0134 + IL_0131: ldloc.s V_4 + IL_0133: throw + IL_0134: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0139: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_013e: ldarg.0 + IL_013f: ldnull + IL_0140: stfld "object Test.d__1.<>7__wrap2" + IL_0145: ldnull + IL_0146: throw + } + catch System.Exception + { + IL_0147: stloc.s V_5 + IL_0149: ldarg.0 + IL_014a: ldc.i4.s -2 + IL_014c: stfld "int Test.d__1.<>1__state" + IL_0151: ldarg.0 + IL_0152: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Test.d__1.<>t__builder" + IL_0157: ldloc.s V_5 + IL_0159: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_015e: leave.s IL_0160 + } + IL_0160: ret + } + """); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (6,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(6, 28) + ); + verifier.VerifyIL("Test.G()", """ + { + // Code size 78 (0x4e) + .maxstack 2 + .locals init (int V_0, //x + object V_1, + int V_2) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: ldnull + IL_0003: stloc.1 + .try + { + IL_0004: call "System.Threading.Tasks.Task Test.F()" + IL_0009: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000e: stloc.0 + IL_000f: ldloca.s V_0 + IL_0011: constrained. "int" + IL_0017: callvirt "string object.ToString()" + IL_001c: newobj "System.Exception..ctor(string)" + IL_0021: throw + } + catch object + { + IL_0022: stloc.1 + IL_0023: leave.s IL_0025 + } + IL_0025: ldloc.0 + IL_0026: call "System.Threading.Tasks.Task Test.F()" + IL_002b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0030: stloc.2 + IL_0031: ldloc.2 + IL_0032: add + IL_0033: stloc.0 + IL_0034: ldloc.1 + IL_0035: brfalse.s IL_004c + IL_0037: ldloc.1 + IL_0038: isinst "System.Exception" + IL_003d: dup + IL_003e: brtrue.s IL_0042 + IL_0040: ldloc.1 + IL_0041: throw + IL_0042: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0047: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_004c: ldnull + IL_004d: throw + } + """); + } + + [Fact] + public void AsyncInFinally006_AsyncVoid_01() + { + var source = """ + using System; + using System.Threading; + using System.Threading.Tasks; + class Test + { + static async Task F() + { + return 2; + } + static async void G(SemaphoreSlim semaphore) + { + int x = 0; + try + { + x = await F(); + } + finally + { + x += await F(); + Console.WriteLine(x); + semaphore.Release(); + } + } + public static void Main() + { + System.Globalization.CultureInfo saveUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture; + System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.InvariantCulture; + try + { + var semaphore = new SemaphoreSlim(0, 1); + G(semaphore); + semaphore.Wait(1000 * 60); + } + finally + { + System.Threading.Thread.CurrentThread.CurrentUICulture = saveUICulture; + } + } + } + """; + var expected = "4"; + var verifier = CompileAndVerify(source, expectedOutput: expected); + verifier.VerifyIL("Test.d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 377 (0x179) + .maxstack 3 + .locals init (int V_0, + int V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2, + object V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int Test.d__1.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0026 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq IL_00db + IL_0011: ldarg.0 + IL_0012: ldc.i4.0 + IL_0013: stfld "int Test.d__1.5__2" + IL_0018: ldarg.0 + IL_0019: ldnull + IL_001a: stfld "object Test.d__1.<>7__wrap2" + IL_001f: ldarg.0 + IL_0020: ldc.i4.0 + IL_0021: stfld "int Test.d__1.<>7__wrap3" + IL_0026: nop + .try + { + IL_0027: ldloc.0 + IL_0028: brfalse.s IL_0061 + IL_002a: call "System.Threading.Tasks.Task Test.F()" + IL_002f: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_0034: stloc.2 + IL_0035: ldloca.s V_2 + IL_0037: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_003c: brtrue.s IL_007d + IL_003e: ldarg.0 + IL_003f: ldc.i4.0 + IL_0040: dup + IL_0041: stloc.0 + IL_0042: stfld "int Test.d__1.<>1__state" + IL_0047: ldarg.0 + IL_0048: ldloc.2 + IL_0049: stfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_004e: ldarg.0 + IL_004f: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_0054: ldloca.s V_2 + IL_0056: ldarg.0 + IL_0057: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.AwaitUnsafeOnCompleted, Test.d__1>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Test.d__1)" + IL_005c: leave IL_0178 + IL_0061: ldarg.0 + IL_0062: ldfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_0067: stloc.2 + IL_0068: ldarg.0 + IL_0069: ldflda "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_006e: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_0074: ldarg.0 + IL_0075: ldc.i4.m1 + IL_0076: dup + IL_0077: stloc.0 + IL_0078: stfld "int Test.d__1.<>1__state" + IL_007d: ldloca.s V_2 + IL_007f: call "int System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0084: stloc.1 + IL_0085: ldarg.0 + IL_0086: ldloc.1 + IL_0087: stfld "int Test.d__1.5__2" + IL_008c: leave.s IL_0098 + } + catch object + { + IL_008e: stloc.3 + IL_008f: ldarg.0 + IL_0090: ldloc.3 + IL_0091: stfld "object Test.d__1.<>7__wrap2" + IL_0096: leave.s IL_0098 + } + IL_0098: ldarg.0 + IL_0099: ldarg.0 + IL_009a: ldfld "int Test.d__1.5__2" + IL_009f: stfld "int Test.d__1.<>7__wrap4" + IL_00a4: call "System.Threading.Tasks.Task Test.F()" + IL_00a9: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_00ae: stloc.2 + IL_00af: ldloca.s V_2 + IL_00b1: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_00b6: brtrue.s IL_00f7 + IL_00b8: ldarg.0 + IL_00b9: ldc.i4.1 + IL_00ba: dup + IL_00bb: stloc.0 + IL_00bc: stfld "int Test.d__1.<>1__state" + IL_00c1: ldarg.0 + IL_00c2: ldloc.2 + IL_00c3: stfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_00c8: ldarg.0 + IL_00c9: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_00ce: ldloca.s V_2 + IL_00d0: ldarg.0 + IL_00d1: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.AwaitUnsafeOnCompleted, Test.d__1>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Test.d__1)" + IL_00d6: leave IL_0178 + IL_00db: ldarg.0 + IL_00dc: ldfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_00e1: stloc.2 + IL_00e2: ldarg.0 + IL_00e3: ldflda "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_00e8: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_00ee: ldarg.0 + IL_00ef: ldc.i4.m1 + IL_00f0: dup + IL_00f1: stloc.0 + IL_00f2: stfld "int Test.d__1.<>1__state" + IL_00f7: ldloca.s V_2 + IL_00f9: call "int System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_00fe: stloc.1 + IL_00ff: ldarg.0 + IL_0100: ldarg.0 + IL_0101: ldfld "int Test.d__1.<>7__wrap4" + IL_0106: ldloc.1 + IL_0107: add + IL_0108: stfld "int Test.d__1.5__2" + IL_010d: ldarg.0 + IL_010e: ldfld "int Test.d__1.5__2" + IL_0113: call "void System.Console.WriteLine(int)" + IL_0118: ldarg.0 + IL_0119: ldfld "System.Threading.SemaphoreSlim Test.d__1.semaphore" + IL_011e: callvirt "int System.Threading.SemaphoreSlim.Release()" + IL_0123: pop + IL_0124: ldarg.0 + IL_0125: ldfld "object Test.d__1.<>7__wrap2" + IL_012a: stloc.3 + IL_012b: ldloc.3 + IL_012c: brfalse.s IL_0143 + IL_012e: ldloc.3 + IL_012f: isinst "System.Exception" + IL_0134: dup + IL_0135: brtrue.s IL_0139 + IL_0137: ldloc.3 + IL_0138: throw + IL_0139: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_013e: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0143: ldarg.0 + IL_0144: ldnull + IL_0145: stfld "object Test.d__1.<>7__wrap2" + IL_014a: leave.s IL_0165 + } + catch System.Exception + { + IL_014c: stloc.s V_4 + IL_014e: ldarg.0 + IL_014f: ldc.i4.s -2 + IL_0151: stfld "int Test.d__1.<>1__state" + IL_0156: ldarg.0 + IL_0157: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_015c: ldloc.s V_4 + IL_015e: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetException(System.Exception)" + IL_0163: leave.s IL_0178 + } + IL_0165: ldarg.0 + IL_0166: ldc.i4.s -2 + IL_0168: stfld "int Test.d__1.<>1__state" + IL_016d: ldarg.0 + IL_016e: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_0173: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetResult()" + IL_0178: ret + } + """); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (6,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(6, 28) + ); + verifier.VerifyIL("Test.G(System.Threading.SemaphoreSlim)", """ + { + // Code size 43 (0x2b) + .maxstack 2 + .locals init (Test.d__1 V_0) + IL_0000: ldloca.s V_0 + IL_0002: call "System.Runtime.CompilerServices.AsyncVoidMethodBuilder System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Create()" + IL_0007: stfld "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_000c: ldloca.s V_0 + IL_000e: ldarg.0 + IL_000f: stfld "System.Threading.SemaphoreSlim Test.d__1.semaphore" + IL_0014: ldloca.s V_0 + IL_0016: ldc.i4.m1 + IL_0017: stfld "int Test.d__1.<>1__state" + IL_001c: ldloca.s V_0 + IL_001e: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_0023: ldloca.s V_0 + IL_0025: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Startd__1>(ref Test.d__1)" + IL_002a: ret + } + """); + } + + [Fact] + public void AsyncInFinally006_AsyncVoid_02() + { + var source = """ + using System; + using System.Threading; + using System.Threading.Tasks; + class Test + { + static async Task F() + { + return 2; + } + static async void G(SemaphoreSlim semaphore) + { + int x = 0; + try + { + x = await F(); + } + finally + { + x += await F(); + } + + Console.WriteLine(x); + semaphore.Release(); + } + public static void Main() + { + System.Globalization.CultureInfo saveUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture; + System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.InvariantCulture; + try + { + var semaphore = new SemaphoreSlim(0, 1); + G(semaphore); + semaphore.Wait(1000 * 60); + } + finally + { + System.Threading.Thread.CurrentThread.CurrentUICulture = saveUICulture; + } + } + } + """; + var expected = "4"; + var verifier = CompileAndVerify(source, expectedOutput: expected); + verifier.VerifyIL("Test.d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 377 (0x179) + .maxstack 3 + .locals init (int V_0, + int V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2, + object V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int Test.d__1.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0026 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq IL_00db + IL_0011: ldarg.0 + IL_0012: ldc.i4.0 + IL_0013: stfld "int Test.d__1.5__2" + IL_0018: ldarg.0 + IL_0019: ldnull + IL_001a: stfld "object Test.d__1.<>7__wrap2" + IL_001f: ldarg.0 + IL_0020: ldc.i4.0 + IL_0021: stfld "int Test.d__1.<>7__wrap3" + IL_0026: nop + .try + { + IL_0027: ldloc.0 + IL_0028: brfalse.s IL_0061 + IL_002a: call "System.Threading.Tasks.Task Test.F()" + IL_002f: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_0034: stloc.2 + IL_0035: ldloca.s V_2 + IL_0037: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_003c: brtrue.s IL_007d + IL_003e: ldarg.0 + IL_003f: ldc.i4.0 + IL_0040: dup + IL_0041: stloc.0 + IL_0042: stfld "int Test.d__1.<>1__state" + IL_0047: ldarg.0 + IL_0048: ldloc.2 + IL_0049: stfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_004e: ldarg.0 + IL_004f: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_0054: ldloca.s V_2 + IL_0056: ldarg.0 + IL_0057: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.AwaitUnsafeOnCompleted, Test.d__1>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Test.d__1)" + IL_005c: leave IL_0178 + IL_0061: ldarg.0 + IL_0062: ldfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_0067: stloc.2 + IL_0068: ldarg.0 + IL_0069: ldflda "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_006e: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_0074: ldarg.0 + IL_0075: ldc.i4.m1 + IL_0076: dup + IL_0077: stloc.0 + IL_0078: stfld "int Test.d__1.<>1__state" + IL_007d: ldloca.s V_2 + IL_007f: call "int System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0084: stloc.1 + IL_0085: ldarg.0 + IL_0086: ldloc.1 + IL_0087: stfld "int Test.d__1.5__2" + IL_008c: leave.s IL_0098 + } + catch object + { + IL_008e: stloc.3 + IL_008f: ldarg.0 + IL_0090: ldloc.3 + IL_0091: stfld "object Test.d__1.<>7__wrap2" + IL_0096: leave.s IL_0098 + } + IL_0098: ldarg.0 + IL_0099: ldarg.0 + IL_009a: ldfld "int Test.d__1.5__2" + IL_009f: stfld "int Test.d__1.<>7__wrap4" + IL_00a4: call "System.Threading.Tasks.Task Test.F()" + IL_00a9: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_00ae: stloc.2 + IL_00af: ldloca.s V_2 + IL_00b1: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_00b6: brtrue.s IL_00f7 + IL_00b8: ldarg.0 + IL_00b9: ldc.i4.1 + IL_00ba: dup + IL_00bb: stloc.0 + IL_00bc: stfld "int Test.d__1.<>1__state" + IL_00c1: ldarg.0 + IL_00c2: ldloc.2 + IL_00c3: stfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_00c8: ldarg.0 + IL_00c9: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_00ce: ldloca.s V_2 + IL_00d0: ldarg.0 + IL_00d1: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.AwaitUnsafeOnCompleted, Test.d__1>(ref System.Runtime.CompilerServices.TaskAwaiter, ref Test.d__1)" + IL_00d6: leave IL_0178 + IL_00db: ldarg.0 + IL_00dc: ldfld "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_00e1: stloc.2 + IL_00e2: ldarg.0 + IL_00e3: ldflda "System.Runtime.CompilerServices.TaskAwaiter Test.d__1.<>u__1" + IL_00e8: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_00ee: ldarg.0 + IL_00ef: ldc.i4.m1 + IL_00f0: dup + IL_00f1: stloc.0 + IL_00f2: stfld "int Test.d__1.<>1__state" + IL_00f7: ldloca.s V_2 + IL_00f9: call "int System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_00fe: stloc.1 + IL_00ff: ldarg.0 + IL_0100: ldarg.0 + IL_0101: ldfld "int Test.d__1.<>7__wrap4" + IL_0106: ldloc.1 + IL_0107: add + IL_0108: stfld "int Test.d__1.5__2" + IL_010d: ldarg.0 + IL_010e: ldfld "object Test.d__1.<>7__wrap2" + IL_0113: stloc.3 + IL_0114: ldloc.3 + IL_0115: brfalse.s IL_012c + IL_0117: ldloc.3 + IL_0118: isinst "System.Exception" + IL_011d: dup + IL_011e: brtrue.s IL_0122 + IL_0120: ldloc.3 + IL_0121: throw + IL_0122: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0127: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_012c: ldarg.0 + IL_012d: ldnull + IL_012e: stfld "object Test.d__1.<>7__wrap2" + IL_0133: ldarg.0 + IL_0134: ldfld "int Test.d__1.5__2" + IL_0139: call "void System.Console.WriteLine(int)" + IL_013e: ldarg.0 + IL_013f: ldfld "System.Threading.SemaphoreSlim Test.d__1.semaphore" + IL_0144: callvirt "int System.Threading.SemaphoreSlim.Release()" + IL_0149: pop + IL_014a: leave.s IL_0165 + } + catch System.Exception + { + IL_014c: stloc.s V_4 + IL_014e: ldarg.0 + IL_014f: ldc.i4.s -2 + IL_0151: stfld "int Test.d__1.<>1__state" + IL_0156: ldarg.0 + IL_0157: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_015c: ldloc.s V_4 + IL_015e: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetException(System.Exception)" + IL_0163: leave.s IL_0178 + } + IL_0165: ldarg.0 + IL_0166: ldc.i4.s -2 + IL_0168: stfld "int Test.d__1.<>1__state" + IL_016d: ldarg.0 + IL_016e: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_0173: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetResult()" + IL_0178: ret + } + """); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (6,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(6, 28) + ); + verifier.VerifyIL("Test.G(System.Threading.SemaphoreSlim)", """ + { + // Code size 43 (0x2b) + .maxstack 2 + .locals init (Test.d__1 V_0) + IL_0000: ldloca.s V_0 + IL_0002: call "System.Runtime.CompilerServices.AsyncVoidMethodBuilder System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Create()" + IL_0007: stfld "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_000c: ldloca.s V_0 + IL_000e: ldarg.0 + IL_000f: stfld "System.Threading.SemaphoreSlim Test.d__1.semaphore" + IL_0014: ldloca.s V_0 + IL_0016: ldc.i4.m1 + IL_0017: stfld "int Test.d__1.<>1__state" + IL_001c: ldloca.s V_0 + IL_001e: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.d__1.<>t__builder" + IL_0023: ldloca.s V_0 + IL_0025: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Startd__1>(ref Test.d__1)" + IL_002a: ret + } + """); + } + + [Fact] + public void AsyncInFinallyWithGotos() + { + var source = """ + using System; + using System.Threading.Tasks; + class Test + { + static async Task F() + { + return 2; + } + static async Task G() + { + int x = 0; + bool loop = true; + goto afterLabel; + label: + loop = false; + afterLabel: + try + { + x = await F(); + } + finally + { + x += await F(); + } + if (loop) + { + goto label; + } + return x; + } + public static void Main() + { + Task t2 = G(); + t2.Wait(1000 * 60); + Console.WriteLine(t2.Result); + } + } + """; + + var expected = "4"; + CompileAndVerify(source, expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x45, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (5,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(5, 28) + ); + verifier.VerifyIL("Test.G()", """ + { + // Code size 70 (0x46) + .maxstack 2 + .locals init (int V_0, //x + bool V_1, //loop + object V_2, + int V_3) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: ldc.i4.1 + IL_0003: stloc.1 + IL_0004: br.s IL_0008 + IL_0006: ldc.i4.0 + IL_0007: stloc.1 + IL_0008: ldnull + IL_0009: stloc.2 + .try + { + IL_000a: call "System.Threading.Tasks.Task Test.F()" + IL_000f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0014: stloc.0 + IL_0015: leave.s IL_001a + } + catch object + { + IL_0017: stloc.2 + IL_0018: leave.s IL_001a + } + IL_001a: ldloc.0 + IL_001b: call "System.Threading.Tasks.Task Test.F()" + IL_0020: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: stloc.3 + IL_0026: ldloc.3 + IL_0027: add + IL_0028: stloc.0 + IL_0029: ldloc.2 + IL_002a: brfalse.s IL_0041 + IL_002c: ldloc.2 + IL_002d: isinst "System.Exception" + IL_0032: dup + IL_0033: brtrue.s IL_0037 + IL_0035: ldloc.2 + IL_0036: throw + IL_0037: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_003c: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0041: ldloc.1 + IL_0042: brtrue.s IL_0006 + IL_0044: ldloc.0 + IL_0045: ret + } + """); } [Fact] @@ -1302,6 +2504,24 @@ public static void Main() }"; var expected = @"15"; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0xc1, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (23,17): warning CS0162: Unreachable code detected + // System.Console.WriteLine("FAIL"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(23, 17), + // (42,9): warning CS0162: Unreachable code detected + // System.Console.WriteLine("FAIL"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(42, 9) + ); } [Fact] @@ -1372,6 +2592,27 @@ public static void Main() var expected = @"hello 15"; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0xc7, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (25,21): warning CS0162: Unreachable code detected + // System.Console.WriteLine("FAIL"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(25, 21), + // (32,17): warning CS0162: Unreachable code detected + // System.Console.WriteLine("FAIL"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(32, 17), + // (46,13): warning CS0162: Unreachable code detected + // System.Console.WriteLine("FAIL"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(46, 13) + ); } [Fact] @@ -1443,6 +2684,27 @@ public static void Main() var expected = @"bye 15"; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x96, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (25,21): warning CS0162: Unreachable code detected + // System.Console.WriteLine("FAIL"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(25, 21), + // (32,17): warning CS0162: Unreachable code detected + // System.Console.WriteLine("FAIL"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(32, 17), + // (47,13): warning CS0162: Unreachable code detected + // System.Console.WriteLine("FAIL"); + Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(47, 13) + ); } [Fact] @@ -1585,6 +2847,59 @@ .locals init (int V_0, IL_00a9: ret } "); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify( + comp, + expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x1f, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics( + // (7,28): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task F() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) + ); + + verifier.VerifyIL("Test.G()", """ + { + // Code size 32 (0x20) + .maxstack 2 + .locals init (int V_0, //x + int V_1) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: ldc.i4.0 + IL_0003: stloc.1 + .try + { + IL_0004: ldloc.0 + IL_0005: ldloc.0 + IL_0006: div + IL_0007: stloc.0 + IL_0008: leave.s IL_000f + } + catch object + { + IL_000a: pop + IL_000b: ldc.i4.1 + IL_000c: stloc.1 + IL_000d: leave.s IL_000f + } + IL_000f: ldloc.1 + IL_0010: ldc.i4.1 + IL_0011: bne.un.s IL_001e + IL_0013: call "System.Threading.Tasks.Task Test.F()" + IL_0018: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001d: stloc.0 + IL_001e: ldloc.0 + IL_001f: ret + } + """); } [Fact] @@ -1649,6 +2964,17 @@ Attempted to divide by zero. 2 "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x5f, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); } [WorkItem(74, "https://github.com/dotnet/roslyn/issues/1334")] @@ -1718,6 +3044,17 @@ Attempted to divide by zero. 2 "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x58, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); } [WorkItem(74, "https://github.com/dotnet/roslyn/issues/1334")] @@ -1792,6 +3129,17 @@ Attempted to divide by zero. 4 "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x9d, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); } [Fact] @@ -1846,6 +3194,130 @@ public static void Main() 2 "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0xa4, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Test.G", """ + { + // Code size 165 (0xa5) + .maxstack 2 + .locals init (int V_0, //x + int V_1, + System.Exception V_2, //ex + object V_3, + int V_4, + object V_5, + System.Exception V_6) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: ldc.i4.0 + IL_0003: stloc.1 + .try + { + IL_0004: ldc.i4.0 + IL_0005: stloc.s V_4 + .try + { + IL_0007: ldloc.0 + IL_0008: ldloc.0 + IL_0009: div + IL_000a: stloc.0 + IL_000b: leave.s IL_002d + } + filter + { + IL_000d: isinst "object" + IL_0012: dup + IL_0013: brtrue.s IL_0019 + IL_0015: pop + IL_0016: ldc.i4.0 + IL_0017: br.s IL_0025 + IL_0019: stloc.s V_5 + IL_001b: ldloc.s V_5 + IL_001d: stloc.3 + IL_001e: ldloc.0 + IL_001f: ldc.i4.0 + IL_0020: cgt.un + IL_0022: ldc.i4.0 + IL_0023: cgt.un + IL_0025: endfilter + } // end filter + { // handler + IL_0027: pop + IL_0028: ldc.i4.1 + IL_0029: stloc.s V_4 + IL_002b: leave.s IL_002d + } + IL_002d: ldloc.s V_4 + IL_002f: ldc.i4.1 + IL_0030: bne.un.s IL_0052 + IL_0032: call "System.Threading.Tasks.Task Test.F()" + IL_0037: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003c: stloc.0 + IL_003d: ldloc.3 + IL_003e: isinst "System.Exception" + IL_0043: dup + IL_0044: brtrue.s IL_0048 + IL_0046: ldloc.3 + IL_0047: throw + IL_0048: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0052: leave.s IL_0089 + } + filter + { + IL_0054: isinst "System.Exception" + IL_0059: dup + IL_005a: brtrue.s IL_0060 + IL_005c: pop + IL_005d: ldc.i4.0 + IL_005e: br.s IL_0082 + IL_0060: stloc.s V_6 + IL_0062: ldloc.s V_6 + IL_0064: castclass "System.Exception" + IL_0069: stloc.2 + IL_006a: ldloc.0 + IL_006b: brtrue.s IL_007e + IL_006d: ldstr "hello" + IL_0072: newobj "System.Exception..ctor(string)" + IL_0077: dup + IL_0078: stloc.2 + IL_0079: ldnull + IL_007a: cgt.un + IL_007c: br.s IL_007f + IL_007e: ldc.i4.0 + IL_007f: ldc.i4.0 + IL_0080: cgt.un + IL_0082: endfilter + } // end filter + { // handler + IL_0084: pop + IL_0085: ldc.i4.1 + IL_0086: stloc.1 + IL_0087: leave.s IL_0089 + } + IL_0089: ldloc.1 + IL_008a: ldc.i4.1 + IL_008b: bne.un.s IL_00a3 + IL_008d: call "System.Threading.Tasks.Task Test.F()" + IL_0092: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0097: stloc.0 + IL_0098: ldloc.2 + IL_0099: callvirt "string System.Exception.Message.get" + IL_009e: call "void System.Console.WriteLine(string)" + IL_00a3: ldloc.0 + IL_00a4: ret + } + """); } [Fact] @@ -1902,6 +3374,17 @@ public static void Main() 2 "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0xcf, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); } [Fact] @@ -1983,6 +3466,17 @@ public static void Main() 42 "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [G]: Unexpected type on the stack. { Offset = 0x16e, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); } [Fact] @@ -2067,6 +3561,17 @@ public static void Main() 42 "; CompileAndVerify(source, expectedOutput: expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [b__0]: Unexpected type on the stack. { Offset = 0x1a9, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); } [Fact] @@ -2103,6 +3608,18 @@ static void Main() 1 "; CompileAndVerify(source, expected); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify( + comp, + expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Return value missing on the stack. { Offset = 0x3d } + """ + }); + verifier.VerifyDiagnostics(); } [Fact, WorkItem(67091, "https://github.com/dotnet/roslyn/issues/67091")] @@ -2169,6 +3686,20 @@ async Task M2(string caller, Exception ex) CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: expectedOutput).VerifyDiagnostics(); CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: expectedOutput).VerifyDiagnostics(); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x2a } + [M1]: Return value missing on the stack. { Offset = 0x6d } + [M1]: Return value missing on the stack. { Offset = 0xaa } + [M1]: Return value missing on the stack. { Offset = 0x7f } + [M2]: Return value missing on the stack. { Offset = 0x3f } + """ + }); + verifier.VerifyDiagnostics(); } [Fact, WorkItem(67091, "https://github.com/dotnet/roslyn/issues/67091")] @@ -2266,6 +3797,22 @@ async Task M2(string caller, Exception ex) CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: expectedOutput).VerifyDiagnostics(); CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: expectedOutput).VerifyDiagnostics(); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), + verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x7a } + [M1]: Return value missing on the stack. { Offset = 0x6d } + [M1]: Return value missing on the stack. { Offset = 0xf8 } + [M1]: Return value missing on the stack. { Offset = 0x159 } + [M1]: Return value missing on the stack. { Offset = 0x11c } + [M1]: Return value missing on the stack. { Offset = 0x7f } + [M2]: Return value missing on the stack. { Offset = 0x3f } + """ + }); + verifier.VerifyDiagnostics(); } [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/70483")] @@ -2334,6 +3881,24 @@ async Task M2(string caller, Exception ex) CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: expectedOutput).VerifyDiagnostics(); CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: expectedOutput).VerifyDiagnostics(); + + var ilVerifyMessage = awaitInTry2 + ? """ + [Main]: Return value missing on the stack. { Offset = 0x2a } + [M1]: Return value missing on the stack. { Offset = 0x6b } + [M1]: Return value missing on the stack. { Offset = 0xc1 } + [M1]: Return value missing on the stack. { Offset = 0x7d } + [M2]: Return value missing on the stack. { Offset = 0x3f } + """ + : """ + [Main]: Return value missing on the stack. { Offset = 0x2a } + [M1]: Return value missing on the stack. { Offset = 0x88 } + [M2]: Return value missing on the stack. { Offset = 0x3f } + """; + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with { ILVerifyMessage = ilVerifyMessage }); + verifier.VerifyDiagnostics(); } [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/71569")] @@ -2414,6 +3979,34 @@ class Exception2 : Exception { } targetFramework: TargetFramework.Mscorlib46).VerifyDiagnostics(); CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: expectedOutput, targetFramework: TargetFramework.Mscorlib46).VerifyDiagnostics(); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var ilVerifyMessage = (await1, await2) switch + { + (true, true) => """ + [
$]: Return value missing on the stack. { Offset = 0x3b } + [<
$>g__Test1|0_0]: Return value missing on the stack. { Offset = 0x67 } + [<
$>g__Test2|0_1]: Return value missing on the stack. { Offset = 0x59 } + """, + (false, true) => """ + [
$]: Return value missing on the stack. { Offset = 0x3b } + [<
$>g__Test1|0_0]: Return value missing on the stack. { Offset = 0x26 } + [<
$>g__Test2|0_1]: Return value missing on the stack. { Offset = 0x59 } + """, + (true, false) => """ + [
$]: Return value missing on the stack. { Offset = 0x3b } + [<
$>g__Test1|0_0]: Return value missing on the stack. { Offset = 0x67 } + [<
$>g__Test2|0_1]: Return value missing on the stack. { Offset = 0x24 } + """, + (false, false) => """ + [
$]: Return value missing on the stack. { Offset = 0x3b } + [<
$>g__Test1|0_0]: Return value missing on the stack. { Offset = 0x26 } + [<
$>g__Test2|0_1]: Return value missing on the stack. { Offset = 0x24 } + """ + }; + + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with { ILVerifyMessage = ilVerifyMessage }); + verifier.VerifyDiagnostics(); } [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/71569")] @@ -2471,6 +4064,11 @@ class Exception3 : Exception { } CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: expectedOutput).VerifyDiagnostics(); CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: expectedOutput).VerifyDiagnostics(); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails); + // PROTOTYPE: ILVerify is crashing with IndexOutOfRangeException when attempting to get messages. + verifier.VerifyDiagnostics(); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/71569")] @@ -2542,6 +4140,245 @@ public async IAsyncEnumerator GetAsyncEnumerator(CancellationToken ct) CompileAndVerify(CreateCompilationWithTasksExtensions(sources, options: TestOptions.DebugExe), expectedOutput: expectedOutput).VerifyDiagnostics(); CompileAndVerify(CreateCompilationWithTasksExtensions(sources, options: TestOptions.ReleaseExe), expectedOutput: expectedOutput).VerifyDiagnostics(); + + var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Skipped); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Program.<
$>g__Run|0_0()", getIL(statement)); + + static string getIL(string statement) + { + if (statement.Contains("using")) + { + return """ + { + // Code size 84 (0x54) + .maxstack 2 + .locals init (int V_0, + C V_1, //c + object V_2) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: newobj "Exception1..ctor()" + IL_0007: throw + } + catch Exception1 + { + IL_0008: pop + IL_0009: ldc.i4.1 + IL_000a: stloc.0 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: ldc.i4.1 + IL_000f: bne.un.s IL_0052 + IL_0011: newobj "C..ctor()" + IL_0016: stloc.1 + IL_0017: ldnull + IL_0018: stloc.2 + .try + { + .try + { + IL_0019: newobj "Exception2..ctor()" + IL_001e: throw + } + catch Exception2 + { + IL_001f: pop + .try + { + IL_0020: newobj "Exception3..ctor()" + IL_0025: throw + } + catch Exception3 + { + IL_0026: pop + IL_0027: rethrow + } + } + } + catch object + { + IL_0029: stloc.2 + IL_002a: leave.s IL_002c + } + IL_002c: ldloc.1 + IL_002d: brfalse.s IL_003a + IL_002f: ldloc.1 + IL_0030: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0035: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_003a: ldloc.2 + IL_003b: brfalse.s IL_0052 + IL_003d: ldloc.2 + IL_003e: isinst "System.Exception" + IL_0043: dup + IL_0044: brtrue.s IL_0048 + IL_0046: ldloc.2 + IL_0047: throw + IL_0048: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0052: ldnull + IL_0053: throw + } + """; + } + else if (statement.Contains("foreach")) + { + return """ + { + // Code size 123 (0x7b) + .maxstack 2 + .locals init (int V_0, + System.Collections.Generic.IAsyncEnumerator V_1, + System.Threading.CancellationToken V_2, + object V_3) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: newobj "Exception1..ctor()" + IL_0007: throw + } + catch Exception1 + { + IL_0008: pop + IL_0009: ldc.i4.1 + IL_000a: stloc.0 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: ldc.i4.1 + IL_000f: bne.un.s IL_0079 + IL_0011: newobj "C..ctor()" + IL_0016: ldloca.s V_2 + IL_0018: initobj "System.Threading.CancellationToken" + IL_001e: ldloc.2 + IL_001f: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0024: stloc.1 + IL_0025: ldnull + IL_0026: stloc.3 + .try + { + IL_0027: br.s IL_0030 + IL_0029: ldloc.1 + IL_002a: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_002f: pop + IL_0030: ldloc.1 + IL_0031: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0036: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_003b: brtrue.s IL_0029 + IL_003d: leave.s IL_0042 + } + catch object + { + IL_003f: stloc.3 + IL_0040: leave.s IL_0042 + } + IL_0042: ldloc.1 + IL_0043: brfalse.s IL_0050 + IL_0045: ldloc.1 + IL_0046: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_004b: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0050: ldloc.3 + IL_0051: brfalse.s IL_0068 + IL_0053: ldloc.3 + IL_0054: isinst "System.Exception" + IL_0059: dup + IL_005a: brtrue.s IL_005e + IL_005c: ldloc.3 + IL_005d: throw + IL_005e: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0063: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0068: nop + .try + { + IL_0069: newobj "Exception2..ctor()" + IL_006e: throw + } + catch Exception2 + { + IL_006f: pop + .try + { + IL_0070: newobj "Exception3..ctor()" + IL_0075: throw + } + catch Exception3 + { + IL_0076: pop + IL_0077: rethrow + } + } + IL_0079: ldnull + IL_007a: throw + } + """; + } + else + { + return """ + { + // Code size 69 (0x45) + .maxstack 2 + .locals init (int V_0, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1, + System.Runtime.CompilerServices.YieldAwaitable V_2) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: newobj "Exception1..ctor()" + IL_0007: throw + } + catch Exception1 + { + IL_0008: pop + IL_0009: ldc.i4.1 + IL_000a: stloc.0 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: ldc.i4.1 + IL_000f: bne.un.s IL_0045 + IL_0011: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0016: stloc.2 + IL_0017: ldloca.s V_2 + IL_0019: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_001e: stloc.1 + IL_001f: ldloca.s V_1 + IL_0021: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0026: brtrue.s IL_002e + IL_0028: ldloc.1 + IL_0029: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_002e: ldloca.s V_1 + IL_0030: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + .try + { + IL_0035: newobj "Exception2..ctor()" + IL_003a: throw + } + catch Exception2 + { + IL_003b: pop + .try + { + IL_003c: newobj "Exception3..ctor()" + IL_0041: throw + } + catch Exception3 + { + IL_0042: pop + IL_0043: rethrow + } + } + } + """; + } + } } } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs index f0782c2d9d877..5478059d72df6 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs @@ -498,7 +498,7 @@ public async System.Threading.Tasks.Task GetTemperatureAsync() CompileAndVerify(comp); } - [ConditionalFact(typeof(DesktopOnly))] + [Fact] [WorkItem(30566, "https://github.com/dotnet/roslyn/issues/30566")] public void YieldReturnAwait1() { @@ -527,7 +527,7 @@ public static async Task Main(string[] args) 8"); } - [ConditionalFact(typeof(DesktopOnly))] + [Fact] [WorkItem(30566, "https://github.com/dotnet/roslyn/issues/30566")] public void YieldReturnAwait2() { @@ -2618,7 +2618,7 @@ static async System.Threading.Tasks.Task Main() CompileAndVerify(comp, expectedOutput: "Stream1:1 Finally Stream2:1 Stream2:2 Finally Done"); } - [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Fact] public void AsyncIteratorWithAwaitCompletedAndYield() { string source = @" @@ -3187,7 +3187,7 @@ .locals init (int V_0, } } - [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Fact] public void AsyncIteratorWithAwaitCompletedAndYield_WithEnumeratorCancellation() { string source = @" @@ -3452,7 +3452,7 @@ .locals init (int V_0, }", sequencePoints: "C+d__0.MoveNext", source: source); } - [ConditionalTheory(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Theory] [InlineData("[EnumeratorCancellation] ", "")] [InlineData("", "[EnumeratorCancellation] ")] public void AsyncIteratorWithAwaitCompletedAndYield_WithEnumeratorCancellation_ExtendedPartialMethod(string definitionAttributes, string implementationAttributes) @@ -3720,7 +3720,7 @@ .locals init (int V_0, }", sequencePoints: "C+d__0.MoveNext", source: source); } - [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Fact] public void AsyncIteratorWithAwaitCompletedAndYield_WithEnumeratorCancellation_LocalFunction() { string source = @" @@ -3989,7 +3989,7 @@ .locals init (int V_0, }", sequencePoints: "C+<g__local|0_0>d.MoveNext", source: source); } - [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Fact] public void AsyncIteratorWithAwaitCompletedAndYield_WithEnumeratorCancellation_NoUsage() { string source = @" @@ -4457,7 +4457,7 @@ static async System.Threading.Tasks.Task Main() CompileAndVerify(comp, expectedOutput: "0 1 2 3 Done"); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "1 2 END DISPOSAL DONE")] [InlineData(10, "1 2 END DISPOSAL DONE")] @@ -4659,7 +4659,7 @@ static async System.Threading.Tasks.Task Main() CompileAndVerify(comp, expectedOutput: "0 1 2 3 4 Done"); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "0 DISPOSAL DONE")] [InlineData(2, "0 1 DISPOSAL Finally DONE")] [InlineData(3, "0 1 Finally 2 DISPOSAL DONE")] @@ -4694,7 +4694,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(2, "1 Break Throw Caught Finally END DISPOSAL DONE")] public void TryFinally_DisposeIAsyncEnumeratorMethod(int iterations, string expectedOutput) { @@ -4742,7 +4742,7 @@ public async System.Collections.Generic.IAsyncEnumerator GetAsyncEnumerator CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(2, "1 Break Throw Caught Finally END DISPOSAL DONE")] public void TryFinally_YieldBreakInDisposeMode(int iterations, string expectedOutput) { @@ -4780,10 +4780,219 @@ public static async System.Collections.Generic.IAsyncEnumerable M() }"; var comp = CreateCompilationWithAsyncIterator(new[] { Run(iterations), source }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: expectedOutput); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, + verify: Verification.FailsILVerify with + { + ILVerifyMessage = "[MoveNext]: Leave into try block. { Offset = 0x11a }" + }); + + verifier.VerifyIL("C.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", """ + { + // Code size 414 (0x19e) + .maxstack 3 + .locals init (int V_0, + System.Runtime.CompilerServices.TaskAwaiter V_1, + C.d__0 V_2, + System.Exception V_3) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -4 + IL_000a: sub + IL_000b: switch ( + IL_0026, + IL_0028, + IL_002c, + IL_002c, + IL_002a) + IL_0024: br.s IL_002c + IL_0026: br.s IL_0059 + IL_0028: br.s IL_002c + IL_002a: br.s IL_00aa + IL_002c: ldarg.0 + IL_002d: ldfld "bool C.d__0.<>w__disposeMode" + IL_0032: brfalse.s IL_0039 + IL_0034: leave IL_0167 + IL_0039: ldarg.0 + IL_003a: ldc.i4.m1 + IL_003b: dup + IL_003c: stloc.0 + IL_003d: stfld "int C.d__0.<>1__state" + IL_0042: nop + IL_0043: ldarg.0 + IL_0044: ldc.i4.1 + IL_0045: stfld "int C.d__0.<>2__current" + IL_004a: ldarg.0 + IL_004b: ldc.i4.s -4 + IL_004d: dup + IL_004e: stloc.0 + IL_004f: stfld "int C.d__0.<>1__state" + IL_0054: leave IL_0190 + IL_0059: ldarg.0 + IL_005a: ldc.i4.m1 + IL_005b: dup + IL_005c: stloc.0 + IL_005d: stfld "int C.d__0.<>1__state" + IL_0062: ldarg.0 + IL_0063: ldfld "bool C.d__0.<>w__disposeMode" + IL_0068: brfalse.s IL_006f + IL_006a: leave IL_0167 + IL_006f: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.CompletedTask.get" + IL_0074: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_0079: stloc.1 + IL_007a: ldloca.s V_1 + IL_007c: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_0081: brtrue.s IL_00c6 + IL_0083: ldarg.0 + IL_0084: ldc.i4.0 + IL_0085: dup + IL_0086: stloc.0 + IL_0087: stfld "int C.d__0.<>1__state" + IL_008c: ldarg.0 + IL_008d: ldloc.1 + IL_008e: stfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_0093: ldarg.0 + IL_0094: stloc.2 + IL_0095: ldarg.0 + IL_0096: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_009b: ldloca.s V_1 + IL_009d: ldloca.s V_2 + IL_009f: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__0)" + IL_00a4: nop + IL_00a5: leave IL_019d + IL_00aa: ldarg.0 + IL_00ab: ldfld "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_00b0: stloc.1 + IL_00b1: ldarg.0 + IL_00b2: ldflda "System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1" + IL_00b7: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_00bd: ldarg.0 + IL_00be: ldc.i4.m1 + IL_00bf: dup + IL_00c0: stloc.0 + IL_00c1: stfld "int C.d__0.<>1__state" + IL_00c6: ldloca.s V_1 + IL_00c8: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_00cd: nop + .try + { + .try + { + IL_00ce: nop + .try + { + IL_00cf: nop + IL_00d0: ldstr "Break " + IL_00d5: call "void System.Console.Write(string)" + IL_00da: nop + IL_00db: ldarg.0 + IL_00dc: ldc.i4.1 + IL_00dd: stfld "bool C.d__0.<>w__disposeMode" + IL_00e2: br.s IL_00e4 + IL_00e4: leave.s IL_00f9 + } + finally + { + IL_00e6: ldloc.0 + IL_00e7: ldc.i4.m1 + IL_00e8: bne.un.s IL_00f8 + IL_00ea: nop + IL_00eb: ldstr "Throw " + IL_00f0: call "void System.Console.Write(string)" + IL_00f5: nop + IL_00f6: ldnull + IL_00f7: throw + IL_00f8: endfinally + } + IL_00f9: ldarg.0 + IL_00fa: ldfld "bool C.d__0.<>w__disposeMode" + IL_00ff: brfalse.s IL_0103 + IL_0101: br.s IL_0104 + IL_0103: nop + IL_0104: leave.s IL_011c + } + catch object + { + IL_0106: pop + IL_0107: nop + IL_0108: ldstr "Caught " + IL_010d: call "void System.Console.Write(string)" + IL_0112: nop + IL_0113: ldarg.0 + IL_0114: ldc.i4.1 + IL_0115: stfld "bool C.d__0.<>w__disposeMode" + IL_011a: leave.s IL_0104 + } + IL_011c: leave.s IL_0130 + } + finally + { + IL_011e: ldloc.0 + IL_011f: ldc.i4.m1 + IL_0120: bne.un.s IL_012f + IL_0122: nop + IL_0123: ldstr "Finally " + IL_0128: call "void System.Console.Write(string)" + IL_012d: nop + IL_012e: nop + IL_012f: endfinally + } + IL_0130: ldarg.0 + IL_0131: ldfld "bool C.d__0.<>w__disposeMode" + IL_0136: brfalse.s IL_013a + IL_0138: leave.s IL_0167 + IL_013a: leave.s IL_0167 + } + catch System.Exception + { + IL_013c: stloc.3 + IL_013d: ldarg.0 + IL_013e: ldc.i4.s -2 + IL_0140: stfld "int C.d__0.<>1__state" + IL_0145: ldarg.0 + IL_0146: ldc.i4.0 + IL_0147: stfld "int C.d__0.<>2__current" + IL_014c: ldarg.0 + IL_014d: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_0152: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_0157: nop + IL_0158: ldarg.0 + IL_0159: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_015e: ldloc.3 + IL_015f: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetException(System.Exception)" + IL_0164: nop + IL_0165: leave.s IL_019d + } + IL_0167: ldarg.0 + IL_0168: ldc.i4.s -2 + IL_016a: stfld "int C.d__0.<>1__state" + IL_016f: ldarg.0 + IL_0170: ldc.i4.0 + IL_0171: stfld "int C.d__0.<>2__current" + IL_0176: ldarg.0 + IL_0177: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder C.d__0.<>t__builder" + IL_017c: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_0181: nop + IL_0182: ldarg.0 + IL_0183: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_0188: ldc.i4.0 + IL_0189: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_018e: nop + IL_018f: ret + IL_0190: ldarg.0 + IL_0191: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore C.d__0.<>v__promiseOfValueOrEnd" + IL_0196: ldc.i4.1 + IL_0197: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_019c: nop + IL_019d: ret + } + """); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 2 DISPOSAL Dispose Finally Throw Dispose CAUGHT2 DONE")] [InlineData(3, "1 2 Try Dispose Finally Throw Dispose CAUGHT DISPOSAL DONE")] @@ -4829,7 +5038,7 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "Try 1 DISPOSAL Finally Item1 Item2 Throw CAUGHT2 DONE")] [InlineData(2, "Try 1 2 DISPOSAL Finally Item1 Item2 Throw CAUGHT2 DONE")] [InlineData(3, "Try 1 2 Finally Item1 Item2 Throw CAUGHT DISPOSAL DONE")] @@ -4878,7 +5087,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M2() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(2, "1 Throw Caught Throw2 Dispose CAUGHT DISPOSAL DONE")] public void TryFinally_AwaitUsingInCatch(int iterations, string expectedOutput) { @@ -4922,7 +5131,7 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "Try Item1 Item2 Throw1 Finally Item1 Item2 Throw2 CAUGHT DISPOSAL DONE")] public void TryFinally_AwaitForeachInCatch(int iterations, string expectedOutput) { @@ -4976,7 +5185,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M2() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 Throw Caught Finally END DISPOSAL DONE")] public void TryFinally_YieldBreakInCatch(int iterations, string expectedOutput) @@ -5020,7 +5229,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 Throw Caught Finally END DISPOSAL DONE")] public void TryFinally_YieldBreakInCatch_WithAwaits(int iterations, string expectedOutput) @@ -5066,7 +5275,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL Finally2 DONE")] [InlineData(2, "1 Throw Caught Break Finally Finally2 END DISPOSAL DONE")] public void TryFinally_YieldBreakInCatch_Nested(int iterations, string expectedOutput) @@ -5119,7 +5328,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "1 DISPOSAL Finally DONE")] [InlineData(2, "1 Break Finally END DISPOSAL DONE")] @@ -5152,7 +5361,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "1 DISPOSAL Finally DONE")] [InlineData(2, "1 2 DISPOSAL Finally DONE")] @@ -5183,7 +5392,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "1 DISPOSAL Finally1 Finally2 Finally5 Finally6 DONE")] [InlineData(2, "1 2 DISPOSAL Finally1 Finally2 Finally5 Finally6 DONE")] @@ -5248,7 +5457,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "1 DISPOSAL Finally DONE")] [InlineData(2, "1 Throw Finally CAUGHT DISPOSAL DONE")] @@ -5282,7 +5491,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "1 DISPOSAL Finally DONE")] [InlineData(2, "1 2 DISPOSAL Finally DONE")] @@ -5313,7 +5522,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL Finally DONE")] [InlineData(10, "1 Throw Finally CAUGHT DISPOSAL DONE")] public void TryFinally_WithYieldsOnly_WithThrow(int iterations, string expectedOutput) @@ -5345,7 +5554,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 Try Finally 2 DISPOSAL DONE")] [InlineData(3, "1 Try Finally 2 END DISPOSAL DONE")] @@ -5380,7 +5589,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 Throw Finally CAUGHT DISPOSAL DONE")] public void TryFinally_WithAwaitsOnly_WithThrow(int iterations, string expectedOutput) @@ -5414,7 +5623,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 Throw1 Throw2 Finally CAUGHT DISPOSAL DONE")] public void TryFinally_WithAwaitsOnly_WithSlowThrowInAwait(int iterations, string expectedOutput) @@ -5453,7 +5662,7 @@ static async System.Threading.Tasks.Task SlowThrowAsync() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 Throw Finally CAUGHT DISPOSAL DONE")] public void TryFinally_WithAwaitsOnly_WithFastThrowInAwait(int iterations, string expectedOutput) @@ -5493,7 +5702,7 @@ static async System.Threading.Tasks.Task FastThrowAsync() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 Throw Finally1 Caught")] [InlineData(2, "1 2 Throw Finally2 Finally1 Caught")] [InlineData(3, "1 2 Finally2 3 Throw Finally3 Finally1 Caught")] @@ -5571,7 +5780,7 @@ static async System.Threading.Tasks.Task Main() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "100 1 Throw Finally1 Caught")] [InlineData(2, "100 1 Throw Finally1 Caught")] [InlineData(3, "100 1 2 Throw Finally2 Finally1 Caught")] @@ -5664,7 +5873,7 @@ static async System.Threading.Tasks.Task Main() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 Try Caught1 Caught2 After END DISPOSAL DONE")] public void TryFinally_AwaitAndCatch(int iterations, string expectedOutput) @@ -5699,7 +5908,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 Throw Caught END DISPOSAL DONE")] public void TryFinally_AwaitInCatch(int iterations, string expectedOutput) @@ -5729,7 +5938,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 Throw Caught END DISPOSAL DONE")] public void TryFinally_AwaitAndYieldBreakInCatch(int iterations, string expectedOutput) @@ -5760,7 +5969,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "1 DISPOSAL Finally DONE")] [InlineData(2, "1 Try 2 DISPOSAL Finally DONE")] @@ -6145,7 +6354,7 @@ static async System.Collections.Generic.IAsyncEnumerable M() ); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "0 DISPOSAL Finally1 DONE")] [InlineData(2, "0 Finally1 Again 2 DISPOSAL Finally3 DONE")] @@ -6185,7 +6394,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "1 DISPOSAL Finally CAUGHT2 DONE")] [InlineData(2, "1 Finally CAUGHT DISPOSAL DONE")] @@ -6219,7 +6428,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "1 DISPOSAL Finally1 Finally2 CAUGHT2 DONE")] [InlineData(2, "1 Finally1 Finally2 CAUGHT DISPOSAL DONE")] @@ -6260,7 +6469,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "1 DISPOSAL DONE")] [InlineData(2, "1 Try1 Try2 Caught Finally1 Finally2 END DISPOSAL DONE")] @@ -6299,7 +6508,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(1, "Try1 1 DISPOSAL Finally1 Finally2 DONE")] [InlineData(2, "Try1 1 Throw Finally1 Finally2 CAUGHT DISPOSAL DONE")] [InlineData(10, "Try1 1 Throw Finally1 Finally2 CAUGHT DISPOSAL DONE")] @@ -6335,7 +6544,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() CompileAndVerify(comp, expectedOutput: expectedOutput); } - [ConditionalTheory(typeof(WindowsDesktopOnly))] + [Theory] [InlineData(0, "DISPOSAL DONE")] [InlineData(1, "Try1 1 DISPOSAL Finally1 Finally2 DONE")] [InlineData(2, "Try1 1 Try2 Finally1 Finally2 END DISPOSAL DONE")] diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs index caaf685ab21be..b2ce91134ce5b 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs @@ -260,7 +260,7 @@ public static async Task M(int x, int y, int z) }); } - [ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Fact] public void SynthesizedVariables1() { var source = @@ -334,7 +334,9 @@ public async Task M(IDisposable disposable) }, module.GetFieldNames("C.d__3")); }); - vd.VerifyPdb("C.M", @" + if (ExecutionConditionUtil.IsWindows) + { + vd.VerifyPdb("C.M", @" @@ -374,6 +376,7 @@ public async Task M(IDisposable disposable) ", options: PdbValidationOptions.ExcludeDocuments); + } } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs index 999bf6667331d..c8d82cdbedbf2 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs @@ -2417,7 +2417,7 @@ static void Main() CompileAndVerify(source, expected); } - [ConditionalFact(typeof(DesktopOnly))] + [Fact] public void SpillArglist() { var source = @" @@ -2474,12 +2474,12 @@ public static void Main() Console.WriteLine(Result); } }"; - var expected = @" + var expected = ExecutionConditionUtil.IsDesktop ? @" 1 2 0 -"; - CompileAndVerify(source, expectedOutput: expected); +" : null; + CompileAndVerify(source, targetFramework: TargetFramework.NetFramework, expectedOutput: expected, verify: Verification.FailsILVerify); } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs index 7c86bdebe12b0..72e76b38a4489 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs @@ -46,16 +46,42 @@ private static CSharpCompilation CreateCompilation(string source, IEnumerable(T obj); public delegate void Action(T1 arg1, T2 arg2); + public static class Activator + { + public static T CreateInstance() => throw null!; + } + public class AggregateException : Exception + { + public AggregateException() {} + public AggregateException(string message) : base(message) {} + } + public class ArgumentException : Exception + { + public ArgumentException() : base("") {} + public ArgumentException(string message) : base(message) {} + public ArgumentException(string message, string paramName) : base(message) {} + } public class ArgumentNullException : Exception { + public ArgumentNullException() : base("") {} public ArgumentNullException(string message) : base(message) {} } + public class ArgumentOutOfRangeException : Exception + { + public ArgumentOutOfRangeException() : base("") {} + public ArgumentOutOfRangeException(string message) : base(message) {} + public ArgumentOutOfRangeException(string message, string paramName) : base(message) {} + } + public class Array + { + public int Length => throw null!; + } public class Attribute {} [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] public sealed class AsyncMethodBuilderAttribute : Attribute @@ -83,27 +109,86 @@ public enum AttributeTargets Event = 0x200, } public struct Boolean {} + public struct Byte {} public static class Console { public static void Write(object i) {} public static void Write(int i) {} - public static void WriteLine(string s) {} + public static void WriteLine(bool b) {} public static void WriteLine(int i) {} + public static void WriteLine(string s) {} + } + public struct Decimal + { + public Decimal(int value) {} + public Decimal(int lo, int mid, int hi, bool isNegative, byte scale) {} + public string ToString(IFormatProvider provider) => null!; + public static implicit operator decimal(int value) => default; + public static decimal operator +(decimal left, decimal right) => default; } public class Delegate {} + public class DivideByZeroException : Exception + { + public DivideByZeroException() {} + public DivideByZeroException(string message) : base(message) {} + } public class Enum {} public class Exception { public Exception() {} public Exception(string message) {} public virtual string Message { get; } + public virtual Type GetType() => null!; } + public class FlagsAttribute : Attribute {} + public class FormattableString : IFormattable {} public delegate TResult Func(); public delegate TResult Func(T arg); - public struct Int32 {} + public delegate TResult Func(T1 arg1, T2 arg2); + public interface IConvertible + { + } + public interface IDisposable + { + void Dispose(); + } + public interface IFormattable + { + } + public interface IFormatProvider + { + object GetFormat(Type formatType); + } + public struct Int16 {} + public struct Int32 : IConvertible {} public struct IntPtr {} + public class InvalidOperationException : Exception + { + public InvalidOperationException() {} + public InvalidOperationException(string message) : base(message) {} + } + public interface IEquatable + { + bool Equals(T other); + } public class MulticastDelegate {} - public struct Nullable(T t) where T : struct {} + public class NotImplementedException : Exception + { + public NotImplementedException() {} + public NotImplementedException(string message) : base(message) {} + } + public class NotSupportedException : Exception + { + public NotSupportedException() : base("") {} + } + public struct Nullable where T : struct + { + public T GetValueOrDefault() => throw null!; + public bool HasValue => throw null!; + public Nullable() {} + public Nullable(T value) {} + public T Value => throw null!; + } public class NullReferenceException : Exception { public NullReferenceException(string message) : base(message) {} @@ -111,31 +196,194 @@ public NullReferenceException(string message) : base(message) {} public class Object { public virtual string ToString() => null!; + public virtual int GetHashCode() => 0; + public virtual bool Equals(object obj) => false; + public static bool ReferenceEquals(object objA, object objB) => false; + } + public class ObsoleteAttribute : Attribute + { + public ObsoleteAttribute() {} + public ObsoleteAttribute(string message) {} + public bool IsError { get; set; } + } + public class OperationCanceledException : Exception + { + public OperationCanceledException() {} + public OperationCanceledException(string message) : base(message) {} + } + public class OverflowException : Exception + { + public OverflowException() {} + public OverflowException(string message) : base(message) {} + } + public class ParamArrayAttribute {} + public struct RuntimeTypeHandle {} + public struct SByte {} + public class String + { + public static string Concat(string str0, string str1) => null!; + public static string Concat(string str0, string str1, string str2) => null!; + public static string Concat(string str0, string str1, string str2, string str3) => null!; + public static string Concat(params string[] arr) => null!; + public static string Format(string format, params object[] args) => null!; + } + public class Type + { + public string Name => null!; + public static Type GetTypeFromHandle(RuntimeTypeHandle handle) => null!; + } + public class Tuple + { + public static Tuple Create(T1 item1, T2 item2) => null!; + } + public class Tuple + { + public Tuple(T1 item1, T2 item2) {} + public T1 Item1 { get; } + public T2 Item2 { get; } + } + public struct UInt32 {} + public struct ValueTuple + { + public ValueTuple(T1 item1, T2 item2) {} + public T1 Item1; + public T2 Item2; + } + public struct ValueTuple + { + public ValueTuple(T1 item1, T2 item2, T3 item3) {} + public T1 Item1; + public T2 Item2; + public T3 Item3; } - public class String {} - public class Type {} public class ValueType {} public class Void {} + public static class Environment + { + public static void FailFast(string message) {} + } + namespace Collections + { + namespace Generic + { + public interface IEnumerable + { + System.Collections.Generic.IEnumerator GetEnumerator(); + } + public interface IEnumerator + { + T Current { get; } + bool MoveNext(); + void Reset(); + } + public class List : IEnumerable, Collections.IEnumerable + { + public List() {} + public void Add(T item) {} + public IEnumerator GetEnumerator() => default!; + Collections.IEnumerator Collections.IEnumerable.GetEnumerator() => default!; + } + } + + public interface IEnumerable + { + System.Collections.IEnumerator GetEnumerator(); + } + public interface IEnumerator + { + object Current { get; } + bool MoveNext(); + void Reset(); + } + } + namespace Diagnostics + { + public class Debug + { + public static void Assert(bool condition) {} + public static void Fail(string message) {} + } + } + namespace Globalization + { + public class CultureInfo : IFormatProvider + { + public static CultureInfo InvariantCulture => null!; + public object GetFormat(Type formatType) => null!; + } + } + namespace Linq + { + public static class Enumerable + { + public static T First(Collections.Generic.IEnumerable source) => default!; + public static Collections.Generic.IEnumerable Zip(this Collections.Generic.IEnumerable first, Collections.Generic.IEnumerable second, Func resultSelector) => default!; + } + } + namespace Text + { + public class StringBuilder + { + public StringBuilder Append(string value) => this; + public override string ToString() => ""; + } + } namespace Threading { public class AutoResetEvent : EventWaitHandle { public AutoResetEvent(bool initialState) {} } + public struct CancellationToken + { + public static CancellationToken None => default; + public bool Equals(CancellationToken other) => false; + } + public class CancellationTokenSource + { + public CancellationToken Token => default; + public void Cancel() {} + public void Dispose() {} + public static CancellationTokenSource CreateLinkedTokenSource(CancellationToken token1, CancellationToken token2) => null!; + } public class EventWaitHandle { public bool Set() => default; public bool WaitOne() => default; public bool WaitOne(int millisecondsTimeout) => default; } + public class ExecutionContext + { + public static ExecutionContext Capture() => null!; + public static void Run(ExecutionContext executionContext, ContextCallback callback, object state) {} + } + public delegate void ContextCallback(object state); public static class Interlocked { public static int Increment(ref int location) => default; + public static T CompareExchange(ref T location1, T value, T comparand) where T : class => default!; + } + public class SynchronizationContext + { + public static SynchronizationContext Current => null!; + public virtual Type GetType() => null!; + public virtual void Post(SendOrPostCallback d, object state) {} + } + public delegate void SendOrPostCallback(object state); + public class SemaphoreSlim + { + public SemaphoreSlim(int initialCount, int maxCount) {} + public int Release() => default; + public void Wait() {} + public bool Wait(int millisecondsTimeout) => default; } - public static class Thread + public class Thread { public static void Sleep(int millisecondsTimeout) {} + public static Thread CurrentThread => new Thread(); + public System.Globalization.CultureInfo CurrentUICulture { get; set; } = null!; + public int ManagedThreadId => 0; } namespace Tasks { @@ -153,6 +401,7 @@ public void Wait() {} public static YieldAwaitable Yield() => default; public static Task FromResult(TResult result) => default; public Task ContinueWith(Action continuationAction) => default; + public static Task FromException(Exception exception) => null!; } public class Task { @@ -167,13 +416,18 @@ public void Wait() {} [AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder))] public struct ValueTask { + public ValueTask(Task task) {} + public ValueTask(Sources.IValueTaskSource source, short token) {} public ValueTaskAwaiter GetAwaiter() => default; public static ValueTask FromResult(TResult result) => default; + public static ValueTask CompletedTask => default; } [AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder<>))] public struct ValueTask { public ValueTask(TResult result) {} + public ValueTask(Task task) {} + public ValueTask(Sources.IValueTaskSource source, short token) {} public ValueTaskAwaiter GetAwaiter() => default; public TResult Result => default; } @@ -185,6 +439,17 @@ public TaskFactory() {} public Task StartNew(Func function) => default; public ValueTask StartNew(Func function) => default; public ValueTask StartNew(Func> function) => default; + public Task StartNew(Action action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler) => default; + } + public class TaskScheduler + { + public static TaskScheduler Current => null!; + public static TaskScheduler Default => null!; + } + public enum TaskCreationOptions + { + None = 0, + DenyChildAttach = 8 } public class TaskCompletionSource { @@ -202,155 +467,237 @@ public void SetResult(TResult result) {} public void SetCanceled() {} public void SetException(Exception exception) {} } + namespace Sources + { + public interface IValueTaskSource + { + ValueTaskSourceStatus GetStatus(short token); + void OnCompleted(Action continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags); + void GetResult(short token); + } + public interface IValueTaskSource + { + ValueTaskSourceStatus GetStatus(short token); + void OnCompleted(Action continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags); + TResult GetResult(short token); + } + public enum ValueTaskSourceStatus + { + Pending = 0, + Succeeded = 1, + Faulted = 2, + Canceled = 3 + } + [Flags] + public enum ValueTaskSourceOnCompletedFlags + { + None = 0, + UseSchedulingContext = 1, + FlowExecutionContext = 2 + } + } } } - - namespace Runtime.CompilerServices + namespace Runtime { - public class AsyncMethodBuilderAttribute : Attribute + namespace InteropServices { - public AsyncMethodBuilderAttribute(Type builderType) {} - } - public struct AsyncTaskMethodBuilder - { - public static AsyncTaskMethodBuilder Create() => default; - public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine {} - public void SetStateMachine(IAsyncStateMachine stateMachine) {} - public void SetException(Exception exception) {} - public void SetResult() {} - public Threading.Tasks.Task Task => default; - public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) - where TAwaiter : INotifyCompletion - where TStateMachine : IAsyncStateMachine {} - public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) - where TAwaiter : ICriticalNotifyCompletion - where TStateMachine : IAsyncStateMachine {} - } - public struct AsyncTaskMethodBuilder - { - public static AsyncTaskMethodBuilder Create() => default; - public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine {} - public void SetStateMachine(IAsyncStateMachine stateMachine) {} - public void SetException(Exception exception) {} - public void SetResult(T result) {} - public Threading.Tasks.Task Task => default; - public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) - where TAwaiter : INotifyCompletion - where TStateMachine : IAsyncStateMachine {} - public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) - where TAwaiter : ICriticalNotifyCompletion - where TStateMachine : IAsyncStateMachine {} - } - public struct AsyncValueTaskMethodBuilder - { - public static AsyncValueTaskMethodBuilder Create() => default; - public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine {} - public void SetStateMachine(IAsyncStateMachine stateMachine) {} - public void SetException(Exception exception) {} - public void SetResult() {} - public Threading.Tasks.ValueTask Task => default; - public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) - where TAwaiter : INotifyCompletion - where TStateMachine : IAsyncStateMachine {} - public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) - where TAwaiter : ICriticalNotifyCompletion - where TStateMachine : IAsyncStateMachine {} - } - public struct AsyncValueTaskMethodBuilder - { - public static AsyncValueTaskMethodBuilder Create() => default; - public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine {} - public void SetStateMachine(IAsyncStateMachine stateMachine) {} - public void SetException(Exception exception) {} - public void SetResult(T result) {} - public Threading.Tasks.ValueTask Task => default; - public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) - where TAwaiter : INotifyCompletion - where TStateMachine : IAsyncStateMachine {} - public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) - where TAwaiter : ICriticalNotifyCompletion - where TStateMachine : IAsyncStateMachine {} - } - public struct AsyncVoidMethodBuilder - { - public static AsyncVoidMethodBuilder Create() => default; - public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine {} - public void SetStateMachine(IAsyncStateMachine stateMachine) {} - public void SetException(Exception exception) {} - public void SetResult() {} - public Threading.Tasks.Task Task => default; - public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) - where TAwaiter : INotifyCompletion - where TStateMachine : IAsyncStateMachine {} - public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) - where TAwaiter : ICriticalNotifyCompletion - where TStateMachine : IAsyncStateMachine {} - } - public class ExtensionAttribute : Attribute {} - public interface IAsyncStateMachine - { - void MoveNext(); - void SetStateMachine(IAsyncStateMachine stateMachine); - } - public interface INotifyCompletion - { - void OnCompleted(Action continuation); - } - public interface ICriticalNotifyCompletion : INotifyCompletion - { - void UnsafeOnCompleted(Action continuation); - } - public static class RuntimeFeature - { - public const string NumericIntPtr = nameof(NumericIntPtr); - } - public struct TaskAwaiter : ICriticalNotifyCompletion - { - public void OnCompleted(Action continuation) {} - public void UnsafeOnCompleted(Action continuation) {} - public bool IsCompleted => false; - public void GetResult() {} - } - public struct TaskAwaiter : ICriticalNotifyCompletion - { - public void OnCompleted(Action continuation) {} - public void UnsafeOnCompleted(Action continuation) {} - public bool IsCompleted => false; - public TResult GetResult() => default; - } - public struct ValueTaskAwaiter : ICriticalNotifyCompletion - { - public void OnCompleted(Action continuation) {} - public void UnsafeOnCompleted(Action continuation) {} - public bool IsCompleted => false; - public void GetResult() {} + public sealed class StructLayoutAttribute : Attribute + { + public StructLayoutAttribute(LayoutKind layoutKind) {} + public LayoutKind Value { get; } + } + + public enum LayoutKind + { + Sequential = 0, + Explicit = 2, + Auto = 3 + } } - public struct ValueTaskAwaiter : ICriticalNotifyCompletion + namespace ExceptionServices { - public void OnCompleted(Action continuation) {} - public void UnsafeOnCompleted(Action continuation) {} - public bool IsCompleted => false; - public TResult GetResult() => default; + public sealed class ExceptionDispatchInfo + { + public static ExceptionDispatchInfo Capture(Exception source) => null!; + public Exception SourceException => null!; + public void Throw() {} + } } - public struct YieldAwaitable + namespace CompilerServices { - public YieldAwaiter GetAwaiter() => default; - public struct YieldAwaiter : ICriticalNotifyCompletion + public class AsyncMethodBuilderAttribute : Attribute + { + public AsyncMethodBuilderAttribute(Type builderType) {} + } + public class StateMachineAttribute : Attribute + { + public StateMachineAttribute(Type stateMachineType) {} + public Type StateMachineType { get; } + } + public class MethodImplAttribute : Attribute + { + public MethodImplAttribute(MethodImplOptions methodImplOptions) {} + public MethodImplOptions Value { get; } + } + public enum MethodImplOptions + { + Unmanaged = 4, + NoInlining = 8, + AggressiveInlining = 256 + } + public struct AsyncTaskMethodBuilder + { + public static AsyncTaskMethodBuilder Create() => default; + public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine {} + public void SetStateMachine(IAsyncStateMachine stateMachine) {} + public void SetException(Exception exception) {} + public void SetResult() {} + public Threading.Tasks.Task Task => default; + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine {} + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine {} + } + public struct AsyncTaskMethodBuilder + { + public static AsyncTaskMethodBuilder Create() => default; + public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine {} + public void SetStateMachine(IAsyncStateMachine stateMachine) {} + public void SetException(Exception exception) {} + public void SetResult(T result) {} + public Threading.Tasks.Task Task => default; + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine {} + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine {} + } + public struct AsyncValueTaskMethodBuilder + { + public static AsyncValueTaskMethodBuilder Create() => default; + public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine {} + public void SetStateMachine(IAsyncStateMachine stateMachine) {} + public void SetException(Exception exception) {} + public void SetResult() {} + public Threading.Tasks.ValueTask Task => default; + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine {} + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine {} + } + public struct AsyncValueTaskMethodBuilder + { + public static AsyncValueTaskMethodBuilder Create() => default; + public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine {} + public void SetStateMachine(IAsyncStateMachine stateMachine) {} + public void SetException(Exception exception) {} + public void SetResult(T result) {} + public Threading.Tasks.ValueTask Task => default; + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine {} + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine {} + } + public struct AsyncVoidMethodBuilder + { + public static AsyncVoidMethodBuilder Create() => default; + public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine {} + public void SetStateMachine(IAsyncStateMachine stateMachine) {} + public void SetException(Exception exception) {} + public void SetResult() {} + public Threading.Tasks.Task Task => default; + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine {} + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine {} + } + public class ExtensionAttribute : Attribute {} + public interface IAsyncStateMachine + { + void MoveNext(); + void SetStateMachine(IAsyncStateMachine stateMachine); + } + public interface INotifyCompletion + { + void OnCompleted(Action continuation); + } + public interface ICriticalNotifyCompletion : INotifyCompletion + { + void UnsafeOnCompleted(Action continuation); + } + public static class RuntimeFeature { + public const string NumericIntPtr = nameof(NumericIntPtr); + } + public struct TaskAwaiter : ICriticalNotifyCompletion + { + public void OnCompleted(Action continuation) {} + public void UnsafeOnCompleted(Action continuation) {} + public bool IsCompleted => false; + public void GetResult() {} + } + public struct TaskAwaiter : ICriticalNotifyCompletion + { + public void OnCompleted(Action continuation) {} public void UnsafeOnCompleted(Action continuation) {} + public bool IsCompleted => false; + public TResult GetResult() => default; + } + public class TupleElementNamesAttribute : Attribute + { + public TupleElementNamesAttribute(string[] transformNames) {} + public string[] TransformNames { get; } + } + public struct ValueTaskAwaiter : ICriticalNotifyCompletion + { public void OnCompleted(Action continuation) {} + public void UnsafeOnCompleted(Action continuation) {} public bool IsCompleted => false; public void GetResult() {} } + public struct ValueTaskAwaiter : ICriticalNotifyCompletion + { + public void OnCompleted(Action continuation) {} + public void UnsafeOnCompleted(Action continuation) {} + public bool IsCompleted => false; + public TResult GetResult() => default; + } + public struct YieldAwaitable + { + public YieldAwaiter GetAwaiter() => default; + public struct YieldAwaiter : ICriticalNotifyCompletion + { + public void UnsafeOnCompleted(Action continuation) {} + public void OnCompleted(Action continuation) {} + public bool IsCompleted => false; + public void GetResult() {} + } + } } } } """; - private static CSharpCompilation CreateRuntimeAsyncCompilation(CSharpTestSource source, IEnumerable references = null, CSharpCompilationOptions options = null, CSharpParseOptions parseOptions = null, string runtimeAsyncAwaitHelpers = RuntimeAsyncAwaitHelpers) + internal static CSharpCompilation CreateRuntimeAsyncCompilation(CSharpTestSource source, IEnumerable references = null, CSharpCompilationOptions options = null, CSharpParseOptions parseOptions = null, string runtimeAsyncAwaitHelpers = RuntimeAsyncAwaitHelpers) { // PROTOTYPE: Remove this helper and just use .NET 10 when we can - var corlib = CreateEmptyCompilation([RuntimeAsyncCoreLib, runtimeAsyncAwaitHelpers]); + var corlib = CreateEmptyCompilation([ + RuntimeAsyncCoreLib, + runtimeAsyncAwaitHelpers, + AsyncStreamsTypes, + TestSources.Index, + TestSources.Range + ]); var compilation = CreateEmptyCompilation(source, references: [.. references ?? [], corlib.EmitToImageReference()], options: options, parseOptions: parseOptions ?? WithRuntimeAsync(TestOptions.RegularPreview)); return compilation; diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs index 52fe5242f2174..3f03040351c3a 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs @@ -173,13 +173,180 @@ public async ValueTask DisposeAsync() } } "; - var comp_checked = CreateCompilationWithTasksExtensions(new[] { source.Replace("REPLACE", "checked"), s_IAsyncEnumerable }, options: TestOptions.DebugExe); + var checkedSource = source.Replace("REPLACE", "checked"); + var comp_checked = CreateCompilationWithTasksExtensions(new[] { checkedSource, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp_checked.VerifyDiagnostics(); CompileAndVerify(comp_checked, expectedOutput: "overflow"); - var comp_unchecked = CreateCompilationWithTasksExtensions(new[] { source.Replace("REPLACE", "unchecked"), s_IAsyncEnumerable }, options: TestOptions.DebugExe); + var uncheckedSource = source.Replace("REPLACE", "unchecked"); + var comp_unchecked = CreateCompilationWithTasksExtensions(new[] { uncheckedSource, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp_unchecked.VerifyDiagnostics(); CompileAndVerify(comp_unchecked, expectedOutput: "0xFFFFFFFF"); + + var runtimeAsyncCompChecked = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(checkedSource); + var verifierChecked = CompileAndVerify(runtimeAsyncCompChecked, expectedOutput: CodeGenAsyncTests.ExpectedOutput("overflow", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x85 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x31, Found = Int32, Expected = value 'System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x24 } + """ + }); + verifierChecked.VerifyIL("C.Main()", """ + { + // Code size 134 (0x86) + .maxstack 5 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + int V_3) //i + .try + { + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_003e + IL_0018: ldloc.0 + IL_0019: callvirt "uint System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: conv.ovf.i4.un + IL_001f: stloc.3 + IL_0020: ldstr "0x{0:X8}" + IL_0025: ldc.i4.1 + IL_0026: newarr "object" + IL_002b: dup + IL_002c: ldc.i4.0 + IL_002d: ldloc.3 + IL_002e: box "int" + IL_0033: stelem.ref + IL_0034: call "string string.Format(string, params object[])" + IL_0039: call "void System.Console.Write(object)" + IL_003e: ldloc.0 + IL_003f: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0044: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0049: brtrue.s IL_0018 + IL_004b: leave.s IL_0050 + } + catch object + { + IL_004d: stloc.2 + IL_004e: leave.s IL_0050 + } + IL_0050: ldloc.0 + IL_0051: brfalse.s IL_005e + IL_0053: ldloc.0 + IL_0054: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0059: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005e: ldloc.2 + IL_005f: brfalse.s IL_0076 + IL_0061: ldloc.2 + IL_0062: isinst "System.Exception" + IL_0067: dup + IL_0068: brtrue.s IL_006c + IL_006a: ldloc.2 + IL_006b: throw + IL_006c: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0071: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0076: leave.s IL_0085 + } + catch System.OverflowException + { + IL_0078: pop + IL_0079: ldstr "overflow" + IL_007e: call "void System.Console.Write(object)" + IL_0083: leave.s IL_0085 + } + IL_0085: ret + } + """); + + var runtimeAsyncCompUnchecked = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(uncheckedSource); + var verifierUnchecked = CompileAndVerify(runtimeAsyncCompUnchecked, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0xFFFFFFFF", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x84 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x31, Found = Int32, Expected = value 'System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x24 } + """ + }); + verifierUnchecked.VerifyIL("C.Main()", """ + { + // Code size 133 (0x85) + .maxstack 5 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + int V_3) //i + .try + { + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_003d + IL_0018: ldloc.0 + IL_0019: callvirt "uint System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldstr "0x{0:X8}" + IL_0024: ldc.i4.1 + IL_0025: newarr "object" + IL_002a: dup + IL_002b: ldc.i4.0 + IL_002c: ldloc.3 + IL_002d: box "int" + IL_0032: stelem.ref + IL_0033: call "string string.Format(string, params object[])" + IL_0038: call "void System.Console.Write(object)" + IL_003d: ldloc.0 + IL_003e: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0043: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0048: brtrue.s IL_0018 + IL_004a: leave.s IL_004f + } + catch object + { + IL_004c: stloc.2 + IL_004d: leave.s IL_004f + } + IL_004f: ldloc.0 + IL_0050: brfalse.s IL_005d + IL_0052: ldloc.0 + IL_0053: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0058: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005d: ldloc.2 + IL_005e: brfalse.s IL_0075 + IL_0060: ldloc.2 + IL_0061: isinst "System.Exception" + IL_0066: dup + IL_0067: brtrue.s IL_006b + IL_0069: ldloc.2 + IL_006a: throw + IL_006b: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0070: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0075: leave.s IL_0084 + } + catch System.OverflowException + { + IL_0077: pop + IL_0078: ldstr "overflow" + IL_007d: call "void System.Console.Write(object)" + IL_0082: leave.s IL_0084 + } + IL_0084: ret + } + """); } [Fact] @@ -816,6 +983,39 @@ public int Current var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync 1"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync 1", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x2b } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x43, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 44 (0x2c) + .maxstack 2 + .locals init (C.Enumerator V_0, + System.Threading.CancellationToken V_1) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: call "C.Enumerator C.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: br.s IL_001d + IL_0016: ldloc.0 + IL_0017: callvirt "int C.Enumerator.Current.get" + IL_001c: pop + IL_001d: ldloc.0 + IL_001e: ldc.i4.1 + IL_001f: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync(int)" + IL_0024: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0029: brtrue.s IL_0016 + IL_002b: ret + } + """); } [Fact] @@ -851,6 +1051,40 @@ public int Current var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync 0"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync 0", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x30 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x45, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 49 (0x31) + .maxstack 2 + .locals init (C.Enumerator V_0, + System.Threading.CancellationToken V_1) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: call "C.Enumerator C.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: br.s IL_001d + IL_0016: ldloc.0 + IL_0017: callvirt "int C.Enumerator.Current.get" + IL_001c: pop + IL_001d: ldloc.0 + IL_001e: ldc.i4.0 + IL_001f: newarr "int" + IL_0024: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync(params int[])" + IL_0029: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002e: brtrue.s IL_0016 + IL_0030: ret + } + """); } [Fact] @@ -1038,8 +1272,80 @@ class Element var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); + string expectedOutput = "NextAsync(0) Current(1) Convert(1) Got(1) NextAsync(1) Current(2) Convert(2) Got(2) NextAsync(2) Current(3) Convert(3) Got(3) NextAsync(3) Dispose(4)"; CompileAndVerify(comp, - expectedOutput: "NextAsync(0) Current(1) Convert(1) Got(1) NextAsync(1) Current(2) Convert(2) Got(2) NextAsync(2) Current(3) Convert(3) Got(3) NextAsync(3) Dispose(4)"); + expectedOutput: expectedOutput); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x75 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x44, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x47 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 118 (0x76) + .maxstack 5 + .locals init (C.AsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + Element V_3) //i + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: call "C.AsyncEnumerator C.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_003d + IL_0018: ldloc.0 + IL_0019: callvirt "int C.AsyncEnumerator.Current.get" + IL_001e: call "Element Element.op_Explicit(int)" + IL_0023: stloc.3 + IL_0024: ldstr "Got({0}) " + IL_0029: ldc.i4.1 + IL_002a: newarr "object" + IL_002f: dup + IL_0030: ldc.i4.0 + IL_0031: ldloc.3 + IL_0032: stelem.ref + IL_0033: call "string string.Format(string, params object[])" + IL_0038: call "void System.Console.Write(object)" + IL_003d: ldloc.0 + IL_003e: callvirt "System.Threading.Tasks.Task C.AsyncEnumerator.MoveNextAsync()" + IL_0043: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0048: brtrue.s IL_0018 + IL_004a: leave.s IL_004f + } + catch object + { + IL_004c: stloc.2 + IL_004d: leave.s IL_004f + } + IL_004f: ldloc.0 + IL_0050: brfalse.s IL_005d + IL_0052: ldloc.0 + IL_0053: callvirt "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_0058: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005d: ldloc.2 + IL_005e: brfalse.s IL_0075 + IL_0060: ldloc.2 + IL_0061: isinst "System.Exception" + IL_0066: dup + IL_0067: brtrue.s IL_006b + IL_0069: ldloc.2 + IL_006a: throw + IL_006b: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0070: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0075: ret + } + """); } [Fact] @@ -1082,6 +1388,92 @@ public async ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "Got(1) Got(2) Captured(1)"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("Got(1) Got(2) Captured(1)", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xa1 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x21, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x24 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 162 (0xa2) + .maxstack 5 + .locals init (System.Action V_0, //f + C.AsyncEnumerator V_1, + System.Threading.CancellationToken V_2, + object V_3, + C.<>c__DisplayClass0_0 V_4) //CS$<>8__locals0 + IL_0000: ldnull + IL_0001: stloc.0 + IL_0002: newobj "C..ctor()" + IL_0007: ldloca.s V_2 + IL_0009: initobj "System.Threading.CancellationToken" + IL_000f: ldloc.2 + IL_0010: call "C.AsyncEnumerator C.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0015: stloc.1 + IL_0016: ldnull + IL_0017: stloc.3 + .try + { + IL_0018: br.s IL_0063 + IL_001a: newobj "C.<>c__DisplayClass0_0..ctor()" + IL_001f: stloc.s V_4 + IL_0021: ldloc.s V_4 + IL_0023: ldloc.1 + IL_0024: callvirt "int C.AsyncEnumerator.Current.get" + IL_0029: stfld "int C.<>c__DisplayClass0_0.i" + IL_002e: ldstr "Got({0}) " + IL_0033: ldc.i4.1 + IL_0034: newarr "object" + IL_0039: dup + IL_003a: ldc.i4.0 + IL_003b: ldloc.s V_4 + IL_003d: ldfld "int C.<>c__DisplayClass0_0.i" + IL_0042: box "int" + IL_0047: stelem.ref + IL_0048: call "string string.Format(string, params object[])" + IL_004d: call "void System.Console.Write(object)" + IL_0052: ldloc.0 + IL_0053: brtrue.s IL_0063 + IL_0055: ldloc.s V_4 + IL_0057: ldftn "void C.<>c__DisplayClass0_0.
b__0()" + IL_005d: newobj "System.Action..ctor(object, System.IntPtr)" + IL_0062: stloc.0 + IL_0063: ldloc.1 + IL_0064: callvirt "System.Threading.Tasks.Task C.AsyncEnumerator.MoveNextAsync()" + IL_0069: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_006e: brtrue.s IL_001a + IL_0070: leave.s IL_0075 + } + catch object + { + IL_0072: stloc.3 + IL_0073: leave.s IL_0075 + } + IL_0075: ldloc.1 + IL_0076: brfalse.s IL_0083 + IL_0078: ldloc.1 + IL_0079: callvirt "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_007e: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0083: ldloc.3 + IL_0084: brfalse.s IL_009b + IL_0086: ldloc.3 + IL_0087: isinst "System.Exception" + IL_008c: dup + IL_008d: brtrue.s IL_0091 + IL_008f: ldloc.3 + IL_0090: throw + IL_0091: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0096: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_009b: ldloc.0 + IL_009c: callvirt "void System.Action.Invoke()" + IL_00a1: ret + } + """); } [Fact] @@ -1138,7 +1530,80 @@ public async ValueTask DisposeAsync() }"; var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "NextAsync(1) Current(1) Got(1) NextAsync(2) Current(2) Got(2) NextAsync(3) Current(3) Got(3) NextAsync(4) Dispose(4)"); + var expectedOutput = "NextAsync(1) Current(1) Got(1) NextAsync(2) Current(2) Got(2) NextAsync(3) Current(3) Got(3) NextAsync(4) Dispose(4)"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x7a } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x44, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x47 } + """ + }); + verifier.VerifyIL("Program.Main()", """ + { + // Code size 123 (0x7b) + .maxstack 5 + .locals init (C.AsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + IntContainer V_3) //i + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: call "C.AsyncEnumerator C.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0042 + IL_0018: ldloc.0 + IL_0019: callvirt "IntContainer C.AsyncEnumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldstr "Got({0}) " + IL_0024: ldc.i4.1 + IL_0025: newarr "object" + IL_002a: dup + IL_002b: ldc.i4.0 + IL_002c: ldloc.3 + IL_002d: callvirt "int IntContainer.Value.get" + IL_0032: box "int" + IL_0037: stelem.ref + IL_0038: call "string string.Format(string, params object[])" + IL_003d: call "void System.Console.Write(object)" + IL_0042: ldloc.0 + IL_0043: callvirt "System.Threading.Tasks.Task C.AsyncEnumerator.MoveNextAsync()" + IL_0048: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_004d: brtrue.s IL_0018 + IL_004f: leave.s IL_0054 + } + catch object + { + IL_0051: stloc.2 + IL_0052: leave.s IL_0054 + } + IL_0054: ldloc.0 + IL_0055: brfalse.s IL_0062 + IL_0057: ldloc.0 + IL_0058: callvirt "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_005d: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0062: ldloc.2 + IL_0063: brfalse.s IL_007a + IL_0065: ldloc.2 + IL_0066: isinst "System.Exception" + IL_006b: dup + IL_006c: brtrue.s IL_0070 + IL_006e: ldloc.2 + IL_006f: throw + IL_0070: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0075: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_007a: ret + } + """); } [Fact] @@ -1179,6 +1644,77 @@ public ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "exception"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("exception", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x67 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 104 (0x68) + .maxstack 2 + .locals init (C.AsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2) + .try + { + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: call "C.AsyncEnumerator C.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0021 + IL_0018: ldloc.0 + IL_0019: callvirt "int C.AsyncEnumerator.Current.get" + IL_001e: pop + IL_001f: ldnull + IL_0020: throw + IL_0021: ldloc.0 + IL_0022: callvirt "System.Threading.Tasks.Task C.AsyncEnumerator.MoveNextAsync()" + IL_0027: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002c: brtrue.s IL_0018 + IL_002e: leave.s IL_0033 + } + catch object + { + IL_0030: stloc.2 + IL_0031: leave.s IL_0033 + } + IL_0033: ldloc.0 + IL_0034: brfalse.s IL_0041 + IL_0036: ldloc.0 + IL_0037: callvirt "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_003c: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0041: ldloc.2 + IL_0042: brfalse.s IL_0059 + IL_0044: ldloc.2 + IL_0045: isinst "System.Exception" + IL_004a: dup + IL_004b: brtrue.s IL_004f + IL_004d: ldloc.2 + IL_004e: throw + IL_004f: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0054: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0059: ldnull + IL_005a: throw + } + catch System.ArgumentException + { + IL_005b: callvirt "string System.Exception.Message.get" + IL_0060: call "void System.Console.Write(object)" + IL_0065: leave.s IL_0067 + } + IL_0067: ret + } + """); } [Fact] @@ -1222,6 +1758,78 @@ public async ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "dispose exception"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("dispose exception", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x67 } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 104 (0x68) + .maxstack 2 + .locals init (C.AsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2) + .try + { + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: call "C.AsyncEnumerator C.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0021 + IL_0018: ldloc.0 + IL_0019: callvirt "int C.AsyncEnumerator.Current.get" + IL_001e: pop + IL_001f: ldnull + IL_0020: throw + IL_0021: ldloc.0 + IL_0022: callvirt "System.Threading.Tasks.Task C.AsyncEnumerator.MoveNextAsync()" + IL_0027: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002c: brtrue.s IL_0018 + IL_002e: leave.s IL_0033 + } + catch object + { + IL_0030: stloc.2 + IL_0031: leave.s IL_0033 + } + IL_0033: ldloc.0 + IL_0034: brfalse.s IL_0041 + IL_0036: ldloc.0 + IL_0037: callvirt "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_003c: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0041: ldloc.2 + IL_0042: brfalse.s IL_0059 + IL_0044: ldloc.2 + IL_0045: isinst "System.Exception" + IL_004a: dup + IL_004b: brtrue.s IL_004f + IL_004d: ldloc.2 + IL_004e: throw + IL_004f: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0054: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0059: ldnull + IL_005a: throw + } + catch System.ArgumentException + { + IL_005b: callvirt "string System.Exception.Message.get" + IL_0060: call "void System.Console.Write(object)" + IL_0065: leave.s IL_0067 + } + IL_0067: ret + } + """); } [Fact] @@ -1269,6 +1877,75 @@ public async ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "wait dispose exception"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("wait dispose exception", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5e } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 95 (0x5f) + .maxstack 2 + .locals init (C.AsyncEnumerator V_0, + object V_1) + .try + { + IL_0000: newobj "C..ctor()" + IL_0005: call "C.AsyncEnumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0018 + IL_000f: ldloc.0 + IL_0010: callvirt "int C.AsyncEnumerator.Current.get" + IL_0015: pop + IL_0016: ldnull + IL_0017: throw + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.AsyncEnumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000f + IL_0025: leave.s IL_002a + } + catch object + { + IL_0027: stloc.1 + IL_0028: leave.s IL_002a + } + IL_002a: ldloc.0 + IL_002b: brfalse.s IL_0038 + IL_002d: ldloc.0 + IL_002e: callvirt "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_0033: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0038: ldloc.1 + IL_0039: brfalse.s IL_0050 + IL_003b: ldloc.1 + IL_003c: isinst "System.Exception" + IL_0041: dup + IL_0042: brtrue.s IL_0046 + IL_0044: ldloc.1 + IL_0045: throw + IL_0046: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004b: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0050: ldnull + IL_0051: throw + } + catch System.ArgumentException + { + IL_0052: callvirt "string System.Exception.Message.get" + IL_0057: call "void System.Console.Write(object)" + IL_005c: leave.s IL_005e + } + IL_005e: ret + } + """); } [Fact] @@ -1312,6 +1989,74 @@ public ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "wait exception"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("wait exception", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5e } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 95 (0x5f) + .maxstack 2 + .locals init (C.AsyncEnumerator V_0, + object V_1) + .try + { + IL_0000: newobj "C..ctor()" + IL_0005: call "C.AsyncEnumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0018 + IL_000f: ldloc.0 + IL_0010: callvirt "int C.AsyncEnumerator.Current.get" + IL_0015: pop + IL_0016: ldnull + IL_0017: throw + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.AsyncEnumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000f + IL_0025: leave.s IL_002a + } + catch object + { + IL_0027: stloc.1 + IL_0028: leave.s IL_002a + } + IL_002a: ldloc.0 + IL_002b: brfalse.s IL_0038 + IL_002d: ldloc.0 + IL_002e: callvirt "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_0033: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0038: ldloc.1 + IL_0039: brfalse.s IL_0050 + IL_003b: ldloc.1 + IL_003c: isinst "System.Exception" + IL_0041: dup + IL_0042: brtrue.s IL_0046 + IL_0044: ldloc.1 + IL_0045: throw + IL_0046: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004b: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0050: ldnull + IL_0051: throw + } + catch System.ArgumentException + { + IL_0052: callvirt "string System.Exception.Message.get" + IL_0057: call "void System.Console.Write(object)" + IL_005c: leave.s IL_005e + } + IL_005e: ret + } + """); } [Fact] @@ -1594,6 +2339,34 @@ public async System.Threading.Tasks.Task MoveNextAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x21 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 34 (0x22) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: br.s IL_0014 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: pop + IL_0014: ldloc.0 + IL_0015: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001f: brtrue.s IL_000d + IL_0021: ret + } + """); } [Fact] @@ -1762,9 +2535,10 @@ public Task MoveNextAsync() public ref int Current => ref _array[_index]; } } - """ + AsyncStreamsTypes; + """; - var comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12); + CSharpTestSource sources = [source, AsyncStreamsTypes]; + var comp = CreateCompilationWithTasksExtensions(sources, parseOptions: TestOptions.Regular12); comp.VerifyDiagnostics( // (17,41): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater. // await foreach (ref readonly var i in new C()) @@ -1772,11 +2546,69 @@ public Task MoveNextAsync() var expectedOutput = "123"; - comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13); + comp = CreateCompilationWithTasksExtensions(sources, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular13); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); - comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + comp = CreateCompilationWithTasksExtensions(sources, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5b } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 92 (0x5c) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2) + IL_0000: call "System.Collections.Generic.IAsyncEnumerable C.F()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0023 + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: call "void System.Console.Write(int)" + IL_0023: ldloc.0 + IL_0024: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0029: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_002e: brtrue.s IL_0018 + IL_0030: leave.s IL_0035 + } + catch object + { + IL_0032: stloc.2 + IL_0033: leave.s IL_0035 + } + IL_0035: ldloc.0 + IL_0036: brfalse.s IL_0043 + IL_0038: ldloc.0 + IL_0039: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_003e: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0043: ldloc.2 + IL_0044: brfalse.s IL_005b + IL_0046: ldloc.2 + IL_0047: isinst "System.Exception" + IL_004c: dup + IL_004d: brtrue.s IL_0051 + IL_004f: ldloc.2 + IL_0050: throw + IL_0051: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0056: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_005b: ret + } + """); } [Fact] @@ -2059,6 +2891,71 @@ public S(int i) var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "1 2 Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("1 2 Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x74 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x21, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x24 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 117 (0x75) + .maxstack 2 + .locals init (C.Enumerator V_0, + object V_1, + int V_2) //s + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0032 + IL_000f: ldloc.0 + IL_0010: callvirt "int C.Enumerator.Current.get" + IL_0015: stloc.2 + IL_0016: ldloca.s V_2 + IL_0018: constrained. "int" + IL_001e: callvirt "string object.ToString()" + IL_0023: ldstr " " + IL_0028: call "string string.Concat(string, string)" + IL_002d: call "void System.Console.Write(object)" + IL_0032: ldloc.0 + IL_0033: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0038: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003d: brtrue.s IL_000f + IL_003f: leave.s IL_0044 + } + catch object + { + IL_0041: stloc.1 + IL_0042: leave.s IL_0044 + } + IL_0044: ldloc.0 + IL_0045: brfalse.s IL_0052 + IL_0047: ldloc.0 + IL_0048: callvirt "System.Threading.Tasks.ValueTask C.Enumerator.DisposeAsync()" + IL_004d: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0052: ldloc.1 + IL_0053: brfalse.s IL_006a + IL_0055: ldloc.1 + IL_0056: isinst "System.Exception" + IL_005b: dup + IL_005c: brtrue.s IL_0060 + IL_005e: ldloc.1 + IL_005f: throw + IL_0060: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0065: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_006a: ldstr "Done" + IL_006f: call "void System.Console.Write(object)" + IL_0074: ret + } + """); } [Theory] @@ -2232,6 +3129,64 @@ public async Task MoveNextAsync() """ + s_IAsyncEnumerable; var comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "1 2 Done").VerifyDiagnostics(); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("1 2 Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x72 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x3b, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 115 (0x73) + .maxstack 5 + .locals init (C.Enumerator V_0, + C.Enumerable V_1, + int V_2, //x + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_3, + System.Runtime.CompilerServices.YieldAwaitable V_4) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "C.Enumerable" + IL_0009: call "C.Enumerator C.Enumerable.GetAsyncEnumerator()" + IL_000e: stloc.0 + IL_000f: br.s IL_005b + IL_0011: ldloc.0 + IL_0012: callvirt "int C.Enumerator.Current.get" + IL_0017: stloc.2 + IL_0018: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_001d: stloc.s V_4 + IL_001f: ldloca.s V_4 + IL_0021: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0026: stloc.3 + IL_0027: ldloca.s V_3 + IL_0029: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_002e: brtrue.s IL_0036 + IL_0030: ldloc.3 + IL_0031: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_0036: ldloca.s V_3 + IL_0038: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_003d: ldstr "{0} " + IL_0042: ldc.i4.1 + IL_0043: newarr "object" + IL_0048: dup + IL_0049: ldc.i4.0 + IL_004a: ldloc.2 + IL_004b: box "int" + IL_0050: stelem.ref + IL_0051: call "string string.Format(string, params object[])" + IL_0056: call "void System.Console.Write(object)" + IL_005b: ldloc.0 + IL_005c: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0061: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0066: brtrue.s IL_0011 + IL_0068: ldstr "Done" + IL_006d: call "void System.Console.Write(object)" + IL_0072: ret + } + """); } [Fact] @@ -2276,9 +3231,81 @@ public async Task MoveNextAsync() } } } - """ + AsyncStreamsTypes; - var comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + """; + var comp = CreateCompilationWithTasksExtensions([source, AsyncStreamsTypes], options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "2 4 -1 Done").VerifyDiagnostics(); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("2 4 -1 Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x7f } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x3b, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 128 (0x80) + .maxstack 5 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + int V_3) //i + IL_0000: call "System.Collections.Generic.IAsyncEnumerable C.M()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_003d + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldstr "{0} " + IL_0024: ldc.i4.1 + IL_0025: newarr "object" + IL_002a: dup + IL_002b: ldc.i4.0 + IL_002c: ldloc.3 + IL_002d: box "int" + IL_0032: stelem.ref + IL_0033: call "string string.Format(string, params object[])" + IL_0038: call "void System.Console.Write(object)" + IL_003d: ldloc.0 + IL_003e: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0043: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0048: brtrue.s IL_0018 + IL_004a: leave.s IL_004f + } + catch object + { + IL_004c: stloc.2 + IL_004d: leave.s IL_004f + } + IL_004f: ldloc.0 + IL_0050: brfalse.s IL_005d + IL_0052: ldloc.0 + IL_0053: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0058: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005d: ldloc.2 + IL_005e: brfalse.s IL_0075 + IL_0060: ldloc.2 + IL_0061: isinst "System.Exception" + IL_0066: dup + IL_0067: brtrue.s IL_006b + IL_0069: ldloc.2 + IL_006a: throw + IL_006b: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0070: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0075: ldstr "Done" + IL_007a: call "void System.Console.Write(object)" + IL_007f: ret + } + """); } [Fact] @@ -2384,6 +3411,72 @@ public S(int i) comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x74 } + [get_Current]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0xb } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x3b, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x24 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 117 (0x75) + .maxstack 2 + .locals init (C.Enumerator V_0, + object V_1, + S V_2) //s + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0032 + IL_000f: ldloc.0 + IL_0010: callvirt "S C.Enumerator.Current.get" + IL_0015: stloc.2 + IL_0016: ldloca.s V_2 + IL_0018: constrained. "S" + IL_001e: callvirt "string object.ToString()" + IL_0023: ldstr " " + IL_0028: call "string string.Concat(string, string)" + IL_002d: call "void System.Console.Write(object)" + IL_0032: ldloc.0 + IL_0033: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0038: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003d: brtrue.s IL_000f + IL_003f: leave.s IL_0044 + } + catch object + { + IL_0041: stloc.1 + IL_0042: leave.s IL_0044 + } + IL_0044: ldloc.0 + IL_0045: brfalse.s IL_0052 + IL_0047: ldloc.0 + IL_0048: callvirt "System.Threading.Tasks.ValueTask C.Enumerator.DisposeAsync()" + IL_004d: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0052: ldloc.1 + IL_0053: brfalse.s IL_006a + IL_0055: ldloc.1 + IL_0056: isinst "System.Exception" + IL_005b: dup + IL_005c: brtrue.s IL_0060 + IL_005e: ldloc.1 + IL_005f: throw + IL_0060: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0065: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_006a: ldstr "Done" + IL_006f: call "void System.Console.Write(object)" + IL_0074: ret + } + """); } [Fact] @@ -2437,7 +3530,7 @@ public S(int i) } public override string ToString() => i.ToString(); } - """ + AsyncStreamsTypes; + """; var expectedDiagnostics = new[] { @@ -2446,15 +3539,85 @@ public S(int i) Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "var").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(16, 24) }; - CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); + CSharpTestSource sources = [source, AsyncStreamsTypes]; + CreateCompilationWithTasksExtensions(sources, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); var expectedOutput = "M:1 M:2 M:Done MainDone"; - var comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular13, options: TestOptions.ReleaseExe); + var comp = CreateCompilationWithTasksExtensions(sources, parseOptions: TestOptions.Regular13, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); - comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + comp = CreateCompilationWithTasksExtensions(sources, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x76 } + [get_Current]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0xb } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x3b, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x24 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 119 (0x77) + .maxstack 3 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + string V_3) //s + IL_0000: call "System.Collections.Generic.IAsyncEnumerable C.M()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0034 + IL_0018: ldloc.0 + IL_0019: callvirt "string System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldstr "M:" + IL_0024: ldloc.3 + IL_0025: ldstr " " + IL_002a: call "string string.Concat(string, string, string)" + IL_002f: call "void System.Console.Write(object)" + IL_0034: ldloc.0 + IL_0035: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_003a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_003f: brtrue.s IL_0018 + IL_0041: leave.s IL_0046 + } + catch object + { + IL_0043: stloc.2 + IL_0044: leave.s IL_0046 + } + IL_0046: ldloc.0 + IL_0047: brfalse.s IL_0054 + IL_0049: ldloc.0 + IL_004a: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_004f: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0054: ldloc.2 + IL_0055: brfalse.s IL_006c + IL_0057: ldloc.2 + IL_0058: isinst "System.Exception" + IL_005d: dup + IL_005e: brtrue.s IL_0062 + IL_0060: ldloc.2 + IL_0061: throw + IL_0062: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0067: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_006c: ldstr "MainDone" + IL_0071: call "void System.Console.Write(object)" + IL_0076: ret + } + """); } [Fact] @@ -2561,6 +3724,38 @@ public override string ToString() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "1 2 3 Done", verify: Verification.Fails); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("1 2 3 Done", isRuntimeAsync: true), verify: Verification.Fails); + verifier.VerifyIL("C.Main()", """ + { + // Code size 77 (0x4d) + .maxstack 2 + .locals init (C.Enumerator V_0, + S V_1) //s + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: br.s IL_0035 + IL_000d: ldloc.0 + IL_000e: callvirt "ref S C.Enumerator.Current.get" + IL_0013: ldobj "S" + IL_0018: stloc.1 + IL_0019: ldloca.s V_1 + IL_001b: constrained. "S" + IL_0021: callvirt "string object.ToString()" + IL_0026: ldstr " " + IL_002b: call "string string.Concat(string, string)" + IL_0030: call "void System.Console.Write(object)" + IL_0035: ldloc.0 + IL_0036: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_003b: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0040: brtrue.s IL_000d + IL_0042: ldstr "Done" + IL_0047: call "void System.Console.Write(object)" + IL_004c: ret + } + """); } [Fact] @@ -2616,6 +3811,55 @@ public S(int i) comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x59 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x4f, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 90 (0x5a) + .maxstack 5 + .locals init (C.Enumerator V_0, + S& V_1) //s + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: br.s IL_0042 + IL_000d: ldloc.0 + IL_000e: callvirt "ref S C.Enumerator.Current.get" + IL_0013: stloc.1 + IL_0014: ldstr "{0} " + IL_0019: ldc.i4.1 + IL_001a: newarr "object" + IL_001f: dup + IL_0020: ldc.i4.0 + IL_0021: ldloc.1 + IL_0022: ldobj "S" + IL_0027: box "S" + IL_002c: stelem.ref + IL_002d: call "string string.Format(string, params object[])" + IL_0032: call "void System.Console.Write(object)" + IL_0037: ldloc.1 + IL_0038: ldflda "int S.F" + IL_003d: dup + IL_003e: ldind.i4 + IL_003f: ldc.i4.1 + IL_0040: add + IL_0041: stind.i4 + IL_0042: ldloc.0 + IL_0043: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0048: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_004d: brtrue.s IL_000d + IL_004f: ldstr "Done" + IL_0054: call "void System.Console.Write(object)" + IL_0059: ret + } + """); } [Fact] @@ -2666,20 +3910,88 @@ public S(int i) } public override string ToString() => F.ToString(); } - """ + AsyncStreamsTypes; + """; - CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + CSharpTestSource sources = [source, AsyncStreamsTypes]; + CreateCompilationWithTasksExtensions(sources, parseOptions: TestOptions.Regular12).VerifyDiagnostics( // (16,32): error CS9202: Feature 'ref and unsafe in async and iterator methods' is not available in C# 12.0. Please use language version 13.0 or greater. // await foreach (ref var s in new C()) Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "s").WithArguments("ref and unsafe in async and iterator methods", "13.0").WithLocation(16, 32)); var expectedOutput = "M:2 M:4 M:Done MainDone"; - var comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular13, options: TestOptions.ReleaseExe); + var comp = CreateCompilationWithTasksExtensions(sources, parseOptions: TestOptions.Regular13, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); - comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + comp = CreateCompilationWithTasksExtensions(sources, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x76 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x4f, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 119 (0x77) + .maxstack 3 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + string V_3) //s + IL_0000: call "System.Collections.Generic.IAsyncEnumerable C.M()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0034 + IL_0018: ldloc.0 + IL_0019: callvirt "string System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldstr "M:" + IL_0024: ldloc.3 + IL_0025: ldstr " " + IL_002a: call "string string.Concat(string, string, string)" + IL_002f: call "void System.Console.Write(object)" + IL_0034: ldloc.0 + IL_0035: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_003a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_003f: brtrue.s IL_0018 + IL_0041: leave.s IL_0046 + } + catch object + { + IL_0043: stloc.2 + IL_0044: leave.s IL_0046 + } + IL_0046: ldloc.0 + IL_0047: brfalse.s IL_0054 + IL_0049: ldloc.0 + IL_004a: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_004f: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0054: ldloc.2 + IL_0055: brfalse.s IL_006c + IL_0057: ldloc.2 + IL_0058: isinst "System.Exception" + IL_005d: dup + IL_005e: brtrue.s IL_0062 + IL_0060: ldloc.2 + IL_0061: throw + IL_0062: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0067: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_006c: ldstr "MainDone" + IL_0071: call "void System.Console.Write(object)" + IL_0076: ret + } + """); } [Fact] @@ -2911,8 +4223,75 @@ public async ValueTask DisposeAsync() }"; var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, - expectedOutput: "NextAsync(0) Current(0) Got(1) NextAsync(1) Current(1) Got(2) NextAsync(2) Current(2) Got(3) NextAsync(3) Current(3) Got(4) NextAsync(4) DisposeAsync Done"); + var expectedOutput = "NextAsync(0) Current(0) Got(1) NextAsync(1) Current(1) Got(2) NextAsync(2) Current(2) Got(3) NextAsync(3) Current(3) Got(4) NextAsync(4) DisposeAsync Done"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x76 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x45, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 119 (0x77) + .maxstack 5 + .locals init (C.AsyncEnumerator V_0, + object V_1, + int V_2) //i + IL_0000: newobj "C..ctor()" + IL_0005: call "C.AsyncEnumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0035 + IL_000f: ldloca.s V_0 + IL_0011: call "int C.AsyncEnumerator.Current.get" + IL_0016: stloc.2 + IL_0017: ldstr "Got({0}) " + IL_001c: ldc.i4.1 + IL_001d: newarr "object" + IL_0022: dup + IL_0023: ldc.i4.0 + IL_0024: ldloc.2 + IL_0025: box "int" + IL_002a: stelem.ref + IL_002b: call "string string.Format(string, params object[])" + IL_0030: call "void System.Console.Write(object)" + IL_0035: ldloca.s V_0 + IL_0037: call "System.Threading.Tasks.Task C.AsyncEnumerator.MoveNextAsync()" + IL_003c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0041: brtrue.s IL_000f + IL_0043: leave.s IL_0048 + } + catch object + { + IL_0045: stloc.1 + IL_0046: leave.s IL_0048 + } + IL_0048: ldloca.s V_0 + IL_004a: call "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_004f: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0054: ldloc.1 + IL_0055: brfalse.s IL_006c + IL_0057: ldloc.1 + IL_0058: isinst "System.Exception" + IL_005d: dup + IL_005e: brtrue.s IL_0062 + IL_0060: ldloc.1 + IL_0061: throw + IL_0062: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0067: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_006c: ldstr "Done" + IL_0071: call "void System.Console.Write(object)" + IL_0076: ret + } + """); } [Fact] @@ -2962,7 +4341,8 @@ public async ValueTask DisposeAsync() }"; var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4) Done"); + var expectedOutput = "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4) Done"; + CompileAndVerify(comp, expectedOutput: expectedOutput); var tree = comp.SyntaxTrees.Single(); var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); @@ -2976,6 +4356,79 @@ public async ValueTask DisposeAsync() var boundNode = (BoundForEachStatement)memberModel.GetUpperBoundNode(foreachSyntax); ForEachEnumeratorInfo internalInfo = boundNode.EnumeratorInfoOpt; Assert.True(internalInfo.NeedsDisposal); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x7f } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x44, Found = Int32, Expected = value 'System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x47 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 128 (0x80) + .maxstack 5 + .locals init (C.AsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + int V_3) //i + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: call "C.AsyncEnumerator C.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_003d + IL_0018: ldloc.0 + IL_0019: callvirt "int C.AsyncEnumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldstr "Got({0}) " + IL_0024: ldc.i4.1 + IL_0025: newarr "object" + IL_002a: dup + IL_002b: ldc.i4.0 + IL_002c: ldloc.3 + IL_002d: box "int" + IL_0032: stelem.ref + IL_0033: call "string string.Format(string, params object[])" + IL_0038: call "void System.Console.Write(object)" + IL_003d: ldloc.0 + IL_003e: callvirt "System.Threading.Tasks.ValueTask C.AsyncEnumerator.MoveNextAsync()" + IL_0043: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0048: brtrue.s IL_0018 + IL_004a: leave.s IL_004f + } + catch object + { + IL_004c: stloc.2 + IL_004d: leave.s IL_004f + } + IL_004f: ldloc.0 + IL_0050: brfalse.s IL_005d + IL_0052: ldloc.0 + IL_0053: callvirt "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_0058: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005d: ldloc.2 + IL_005e: brfalse.s IL_0075 + IL_0060: ldloc.2 + IL_0061: isinst "System.Exception" + IL_0066: dup + IL_0067: brtrue.s IL_006b + IL_0069: ldloc.2 + IL_006a: throw + IL_006b: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0070: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0075: ldstr "Done" + IL_007a: call "void System.Console.Write(object)" + IL_007f: ret + } + """); } [Fact] @@ -3036,6 +4489,84 @@ public void OnCompleted(System.Action continuation) { } Assert.Equal("C.Awaitable C.AsyncEnumerator.MoveNextAsync()", info.MoveNextMethod.ToTestDisplayString()); Assert.Equal("System.Int32", info.ElementType.ToTestDisplayString()); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("Item(1) Dispose Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x8d } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 142 (0x8e) + .maxstack 5 + .locals init (C.AsyncEnumerator V_0, + object V_1, + int V_2, //i + C.Awaiter V_3) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.AsyncEnumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0036 + IL_000f: ldloc.0 + IL_0010: callvirt "int C.AsyncEnumerator.Current.get" + IL_0015: stloc.2 + IL_0016: ldstr "Item({0}) " + IL_001b: ldc.i4.1 + IL_001c: newarr "object" + IL_0021: dup + IL_0022: ldc.i4.0 + IL_0023: ldloc.2 + IL_0024: box "int" + IL_0029: stelem.ref + IL_002a: call "string string.Format(string, params object[])" + IL_002f: call "void System.Console.Write(object)" + IL_0034: br.s IL_0058 + IL_0036: ldloc.0 + IL_0037: callvirt "C.Awaitable C.AsyncEnumerator.MoveNextAsync()" + IL_003c: callvirt "C.Awaiter C.Awaitable.GetAwaiter()" + IL_0041: stloc.3 + IL_0042: ldloc.3 + IL_0043: callvirt "bool C.Awaiter.IsCompleted.get" + IL_0048: brtrue.s IL_0050 + IL_004a: ldloc.3 + IL_004b: call "void System.Runtime.CompilerServices.AsyncHelpers.AwaitAwaiter(C.Awaiter)" + IL_0050: ldloc.3 + IL_0051: callvirt "bool C.Awaiter.GetResult()" + IL_0056: brtrue.s IL_000f + IL_0058: leave.s IL_005d + } + catch object + { + IL_005a: stloc.1 + IL_005b: leave.s IL_005d + } + IL_005d: ldloc.0 + IL_005e: brfalse.s IL_006b + IL_0060: ldloc.0 + IL_0061: callvirt "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_0066: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_006b: ldloc.1 + IL_006c: brfalse.s IL_0083 + IL_006e: ldloc.1 + IL_006f: isinst "System.Exception" + IL_0074: dup + IL_0075: brtrue.s IL_0079 + IL_0077: ldloc.1 + IL_0078: throw + IL_0079: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_007e: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0083: ldstr "Done" + IL_0088: call "void System.Console.Write(object)" + IL_008d: ret + } + """); } [Fact] @@ -3296,11 +4827,79 @@ public async ValueTask DisposeAsync() ForEachEnumeratorInfo internalInfo = boundNode.EnumeratorInfoOpt; Assert.True(internalInfo.NeedsDisposal); + string expectedOutput = "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4)"; CompileAndVerify(comp, - expectedOutput: "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4)"); + expectedOutput: expectedOutput); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x6c } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x44, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x51 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 109 (0x6d) + .maxstack 5 + .locals init (C.Enumerator V_0, + object V_1, + int V_2) //i + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0034 + IL_000f: ldloc.0 + IL_0010: callvirt "int C.Enumerator.Current.get" + IL_0015: stloc.2 + IL_0016: ldstr "Got({0}) " + IL_001b: ldc.i4.1 + IL_001c: newarr "object" + IL_0021: dup + IL_0022: ldc.i4.0 + IL_0023: ldloc.2 + IL_0024: box "int" + IL_0029: stelem.ref + IL_002a: call "string string.Format(string, params object[])" + IL_002f: call "void System.Console.Write(object)" + IL_0034: ldloc.0 + IL_0035: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_003a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003f: brtrue.s IL_000f + IL_0041: leave.s IL_0046 + } + catch object + { + IL_0043: stloc.1 + IL_0044: leave.s IL_0046 + } + IL_0046: ldloc.0 + IL_0047: brfalse.s IL_0054 + IL_0049: ldloc.0 + IL_004a: callvirt "System.Threading.Tasks.ValueTask C.Enumerator.DisposeAsync()" + IL_004f: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0054: ldloc.1 + IL_0055: brfalse.s IL_006c + IL_0057: ldloc.1 + IL_0058: isinst "System.Exception" + IL_005d: dup + IL_005e: brtrue.s IL_0062 + IL_0060: ldloc.1 + IL_0061: throw + IL_0062: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0067: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_006c: ret + } + """); } - [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Fact] public void TestWithPattern_WithUnsealed_WithIAsyncDisposable() { string source = @" @@ -3550,7 +5149,75 @@ public async ValueTask DisposeAsync() ForEachEnumeratorInfo internalInfo = boundNode.EnumeratorInfoOpt; Assert.True(internalInfo.NeedsDisposal); - CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4)"); + var expectedOutput = "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4)"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x6c } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x44, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x51 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 109 (0x6d) + .maxstack 5 + .locals init (C.Enumerator V_0, + object V_1, + int V_2) //i + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0034 + IL_000f: ldloc.0 + IL_0010: callvirt "int C.Enumerator.Current.get" + IL_0015: stloc.2 + IL_0016: ldstr "Got({0}) " + IL_001b: ldc.i4.1 + IL_001c: newarr "object" + IL_0021: dup + IL_0022: ldc.i4.0 + IL_0023: ldloc.2 + IL_0024: box "int" + IL_0029: stelem.ref + IL_002a: call "string string.Format(string, params object[])" + IL_002f: call "void System.Console.Write(object)" + IL_0034: ldloc.0 + IL_0035: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_003a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003f: brtrue.s IL_000f + IL_0041: leave.s IL_0046 + } + catch object + { + IL_0043: stloc.1 + IL_0044: leave.s IL_0046 + } + IL_0046: ldloc.0 + IL_0047: brfalse.s IL_0054 + IL_0049: ldloc.0 + IL_004a: callvirt "System.Threading.Tasks.ValueTask C.Enumerator.DisposeAsync()" + IL_004f: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0054: ldloc.1 + IL_0055: brfalse.s IL_006c + IL_0057: ldloc.1 + IL_0058: isinst "System.Exception" + IL_005d: dup + IL_005e: brtrue.s IL_0062 + IL_0060: ldloc.1 + IL_0061: throw + IL_0062: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0067: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_006c: ret + } + """); } [Fact] @@ -3708,7 +5375,8 @@ async ValueTask IAsyncDisposable.DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4)"); + var expectedOutput = "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4)"; + CompileAndVerify(comp, expectedOutput: expectedOutput); var tree = comp.SyntaxTrees.First(); var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); @@ -3730,6 +5398,77 @@ async ValueTask IAsyncDisposable.DisposeAsync() var boundNode = (BoundForEachStatement)memberModel.GetUpperBoundNode(foreachSyntax); ForEachEnumeratorInfo internalInfo = boundNode.EnumeratorInfoOpt; Assert.True(internalInfo.NeedsDisposal); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x75 } + [System.Collections.Generic.IAsyncEnumerator.MoveNextAsync]: Unexpected type on the stack. { Offset = 0x44, Found = Int32, Expected = value 'System.Threading.Tasks.ValueTask`1' } + [System.IAsyncDisposable.DisposeAsync]: Return value missing on the stack. { Offset = 0x51 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 118 (0x76) + .maxstack 5 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + int V_3) //i + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_003d + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldstr "Got({0}) " + IL_0024: ldc.i4.1 + IL_0025: newarr "object" + IL_002a: dup + IL_002b: ldc.i4.0 + IL_002c: ldloc.3 + IL_002d: box "int" + IL_0032: stelem.ref + IL_0033: call "string string.Format(string, params object[])" + IL_0038: call "void System.Console.Write(object)" + IL_003d: ldloc.0 + IL_003e: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0043: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0048: brtrue.s IL_0018 + IL_004a: leave.s IL_004f + } + catch object + { + IL_004c: stloc.2 + IL_004d: leave.s IL_004f + } + IL_004f: ldloc.0 + IL_0050: brfalse.s IL_005d + IL_0052: ldloc.0 + IL_0053: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0058: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005d: ldloc.2 + IL_005e: brfalse.s IL_0075 + IL_0060: ldloc.2 + IL_0061: isinst "System.Exception" + IL_0066: dup + IL_0067: brtrue.s IL_006b + IL_0069: ldloc.2 + IL_006a: throw + IL_006b: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0070: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0075: ret + } + """); } [Fact] @@ -3781,7 +5520,8 @@ public async ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4)"); + var expectedOutput = "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4)"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); // The thing to notice here is that the call to GetAsyncEnumerator is a constrained call (we're not boxing to `IAsyncEnumerable`) verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" @@ -4000,6 +5740,81 @@ .locals init (int V_0, IL_01ee: nop IL_01ef: ret }"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var runtimeAsyncVerifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x81 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x44, Found = Int32, Expected = value 'System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x51 } + """ + }); + runtimeAsyncVerifier.VerifyIL("C.Main()", """ + { + // Code size 130 (0x82) + .maxstack 5 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + C V_1, + System.Threading.CancellationToken V_2, + object V_3, + int V_4) //i + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "C" + IL_0009: ldloca.s V_2 + IL_000b: initobj "System.Threading.CancellationToken" + IL_0011: ldloc.2 + IL_0012: constrained. "C" + IL_0018: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_001d: stloc.0 + IL_001e: ldnull + IL_001f: stloc.3 + .try + { + IL_0020: br.s IL_0049 + IL_0022: ldloc.0 + IL_0023: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_0028: stloc.s V_4 + IL_002a: ldstr "Got({0}) " + IL_002f: ldc.i4.1 + IL_0030: newarr "object" + IL_0035: dup + IL_0036: ldc.i4.0 + IL_0037: ldloc.s V_4 + IL_0039: box "int" + IL_003e: stelem.ref + IL_003f: call "string string.Format(string, params object[])" + IL_0044: call "void System.Console.Write(object)" + IL_0049: ldloc.0 + IL_004a: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_004f: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0054: brtrue.s IL_0022 + IL_0056: leave.s IL_005b + } + catch object + { + IL_0058: stloc.3 + IL_0059: leave.s IL_005b + } + IL_005b: ldloc.0 + IL_005c: brfalse.s IL_0069 + IL_005e: ldloc.0 + IL_005f: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0064: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0069: ldloc.3 + IL_006a: brfalse.s IL_0081 + IL_006c: ldloc.3 + IL_006d: isinst "System.Exception" + IL_0072: dup + IL_0073: brtrue.s IL_0077 + IL_0075: ldloc.3 + IL_0076: throw + IL_0077: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_007c: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0081: ret + } + """); } [Fact] @@ -4052,6 +5867,78 @@ public ValueTask DisposeAsync() comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4) Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4) Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x7f } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x44, Found = Int32, Expected = value 'System.Threading.Tasks.ValueTask`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 128 (0x80) + .maxstack 5 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + int V_3) //i + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_003d + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldstr "Got({0}) " + IL_0024: ldc.i4.1 + IL_0025: newarr "object" + IL_002a: dup + IL_002b: ldc.i4.0 + IL_002c: ldloc.3 + IL_002d: box "int" + IL_0032: stelem.ref + IL_0033: call "string string.Format(string, params object[])" + IL_0038: call "void System.Console.Write(object)" + IL_003d: ldloc.0 + IL_003e: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0043: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0048: brtrue.s IL_0018 + IL_004a: leave.s IL_004f + } + catch object + { + IL_004c: stloc.2 + IL_004d: leave.s IL_004f + } + IL_004f: ldloc.0 + IL_0050: brfalse.s IL_005d + IL_0052: ldloc.0 + IL_0053: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0058: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005d: ldloc.2 + IL_005e: brfalse.s IL_0075 + IL_0060: ldloc.2 + IL_0061: isinst "System.Exception" + IL_0066: dup + IL_0067: brtrue.s IL_006b + IL_0069: ldloc.2 + IL_006a: throw + IL_006b: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0070: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0075: ldstr "Done" + IL_007a: call "void System.Console.Write(object)" + IL_007f: ret + } + """); } [Fact] @@ -4105,8 +5992,105 @@ public async ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); + string expectedOutput = "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Continue(2) NextAsync(2) Current(3) Continue(3) NextAsync(3) Current(4) Break Dispose(4) Done"; CompileAndVerify(comp, - expectedOutput: "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Continue(2) NextAsync(2) Current(3) Continue(3) NextAsync(3) Current(4) Break Dispose(4) Done"); + expectedOutput: expectedOutput); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xb7 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x45, Found = Int32, Expected = value 'System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x51 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 184 (0xb8) + .maxstack 5 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + int V_3) //i + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0075 + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldloc.3 + IL_0020: ldc.i4.2 + IL_0021: beq.s IL_0027 + IL_0023: ldloc.3 + IL_0024: ldc.i4.3 + IL_0025: bne.un.s IL_0047 + IL_0027: ldstr "Continue({0}) " + IL_002c: ldc.i4.1 + IL_002d: newarr "object" + IL_0032: dup + IL_0033: ldc.i4.0 + IL_0034: ldloc.3 + IL_0035: box "int" + IL_003a: stelem.ref + IL_003b: call "string string.Format(string, params object[])" + IL_0040: call "void System.Console.Write(object)" + IL_0045: br.s IL_0075 + IL_0047: ldloc.3 + IL_0048: ldc.i4.4 + IL_0049: bne.un.s IL_0057 + IL_004b: ldstr "Break " + IL_0050: call "void System.Console.Write(object)" + IL_0055: br.s IL_0082 + IL_0057: ldstr "Got({0}) " + IL_005c: ldc.i4.1 + IL_005d: newarr "object" + IL_0062: dup + IL_0063: ldc.i4.0 + IL_0064: ldloc.3 + IL_0065: box "int" + IL_006a: stelem.ref + IL_006b: call "string string.Format(string, params object[])" + IL_0070: call "void System.Console.Write(object)" + IL_0075: ldloc.0 + IL_0076: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_007b: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0080: brtrue.s IL_0018 + IL_0082: leave.s IL_0087 + } + catch object + { + IL_0084: stloc.2 + IL_0085: leave.s IL_0087 + } + IL_0087: ldloc.0 + IL_0088: brfalse.s IL_0095 + IL_008a: ldloc.0 + IL_008b: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0090: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0095: ldloc.2 + IL_0096: brfalse.s IL_00ad + IL_0098: ldloc.2 + IL_0099: isinst "System.Exception" + IL_009e: dup + IL_009f: brtrue.s IL_00a3 + IL_00a1: ldloc.2 + IL_00a2: throw + IL_00a3: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_00a8: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_00ad: ldstr "Done" + IL_00b2: call "void System.Console.Write(object)" + IL_00b7: ret + } + """); } [Fact] @@ -4161,8 +6145,115 @@ public async ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); + string expectedOutput = "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Continue(2) NextAsync(2) Current(3) Continue(3) NextAsync(3) Current(4) Goto Dispose(4) Done"; CompileAndVerify(comp, - expectedOutput: "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Continue(2) NextAsync(2) Current(3) Continue(3) NextAsync(3) Current(4) Goto Dispose(4) Done"); + expectedOutput: expectedOutput); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xc7 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x45, Found = Int32, Expected = value 'System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x51 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 200 (0xc8) + .maxstack 5 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + int V_3, + int V_4) //i + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + IL_0016: ldc.i4.0 + IL_0017: stloc.3 + .try + { + IL_0018: br.s IL_007d + IL_001a: ldloc.0 + IL_001b: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_0020: stloc.s V_4 + IL_0022: ldloc.s V_4 + IL_0024: ldc.i4.2 + IL_0025: beq.s IL_002c + IL_0027: ldloc.s V_4 + IL_0029: ldc.i4.3 + IL_002a: bne.un.s IL_004d + IL_002c: ldstr "Continue({0}) " + IL_0031: ldc.i4.1 + IL_0032: newarr "object" + IL_0037: dup + IL_0038: ldc.i4.0 + IL_0039: ldloc.s V_4 + IL_003b: box "int" + IL_0040: stelem.ref + IL_0041: call "string string.Format(string, params object[])" + IL_0046: call "void System.Console.Write(object)" + IL_004b: br.s IL_007d + IL_004d: ldloc.s V_4 + IL_004f: ldc.i4.4 + IL_0050: bne.un.s IL_005e + IL_0052: ldstr "Goto " + IL_0057: call "void System.Console.Write(object)" + IL_005c: br.s IL_008c + IL_005e: ldstr "Got({0}) " + IL_0063: ldc.i4.1 + IL_0064: newarr "object" + IL_0069: dup + IL_006a: ldc.i4.0 + IL_006b: ldloc.s V_4 + IL_006d: box "int" + IL_0072: stelem.ref + IL_0073: call "string string.Format(string, params object[])" + IL_0078: call "void System.Console.Write(object)" + IL_007d: ldloc.0 + IL_007e: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0083: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0088: brtrue.s IL_001a + IL_008a: leave.s IL_0093 + IL_008c: ldc.i4.1 + IL_008d: stloc.3 + IL_008e: leave.s IL_0093 + } + catch object + { + IL_0090: stloc.2 + IL_0091: leave.s IL_0093 + } + IL_0093: ldloc.0 + IL_0094: brfalse.s IL_00a1 + IL_0096: ldloc.0 + IL_0097: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_009c: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_00a1: ldloc.2 + IL_00a2: brfalse.s IL_00b9 + IL_00a4: ldloc.2 + IL_00a5: isinst "System.Exception" + IL_00aa: dup + IL_00ab: brtrue.s IL_00af + IL_00ad: ldloc.2 + IL_00ae: throw + IL_00af: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_00b4: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_00b9: ldloc.3 + IL_00ba: ldc.i4.1 + IL_00bb: pop + IL_00bc: pop + IL_00bd: ldstr "Done" + IL_00c2: call "void System.Console.Write(object)" + IL_00c7: ret + } + """); } [Fact] @@ -4216,8 +6307,82 @@ public async ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); + string expectedOutput = "NextAsync(0) Current(0) Got(1) NextAsync(1) Current(1) Got(2) NextAsync(2) Current(2) Got(3) NextAsync(3) Current(3) Got(4) NextAsync(4) Dispose(4) Done"; CompileAndVerify(comp, - expectedOutput: "NextAsync(0) Current(0) Got(1) NextAsync(1) Current(1) Got(2) NextAsync(2) Current(2) Got(3) NextAsync(3) Current(3) Got(4) NextAsync(4) Dispose(4) Done"); + expectedOutput: expectedOutput); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x7f } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x45, Found = Int32, Expected = value 'System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x47 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 128 (0x80) + .maxstack 5 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + int V_3) //i + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_003d + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldstr "Got({0}) " + IL_0024: ldc.i4.1 + IL_0025: newarr "object" + IL_002a: dup + IL_002b: ldc.i4.0 + IL_002c: ldloc.3 + IL_002d: box "int" + IL_0032: stelem.ref + IL_0033: call "string string.Format(string, params object[])" + IL_0038: call "void System.Console.Write(object)" + IL_003d: ldloc.0 + IL_003e: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0043: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0048: brtrue.s IL_0018 + IL_004a: leave.s IL_004f + } + catch object + { + IL_004c: stloc.2 + IL_004d: leave.s IL_004f + } + IL_004f: ldloc.0 + IL_0050: brfalse.s IL_005d + IL_0052: ldloc.0 + IL_0053: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0058: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005d: ldloc.2 + IL_005e: brfalse.s IL_0075 + IL_0060: ldloc.2 + IL_0061: isinst "System.Exception" + IL_0066: dup + IL_0067: brtrue.s IL_006b + IL_0069: ldloc.2 + IL_006a: throw + IL_006b: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0070: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0075: ldstr "Done" + IL_007a: call "void System.Console.Write(object)" + IL_007f: ret + } + """); } [Fact, WorkItem(27651, "https://github.com/dotnet/roslyn/issues/27651")] @@ -4307,6 +6472,78 @@ internal struct AsyncEnumerator : IAsyncEnumerator var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "Success"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("Success", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x64 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 101 (0x65) + .maxstack 2 + .locals init (C V_0, //c + System.Collections.Generic.IAsyncEnumerator V_1, + System.Threading.CancellationToken V_2, + object V_3) + IL_0000: ldnull + IL_0001: stloc.0 + .try + { + IL_0002: ldloc.0 + IL_0003: ldloca.s V_2 + IL_0005: initobj "System.Threading.CancellationToken" + IL_000b: ldloc.2 + IL_000c: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0011: stloc.1 + IL_0012: ldnull + IL_0013: stloc.3 + .try + { + IL_0014: br.s IL_001d + IL_0016: ldloc.1 + IL_0017: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001c: pop + IL_001d: ldloc.1 + IL_001e: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0023: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0028: brtrue.s IL_0016 + IL_002a: leave.s IL_002f + } + catch object + { + IL_002c: stloc.3 + IL_002d: leave.s IL_002f + } + IL_002f: ldloc.1 + IL_0030: brfalse.s IL_003d + IL_0032: ldloc.1 + IL_0033: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0038: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_003d: ldloc.3 + IL_003e: brfalse.s IL_0055 + IL_0040: ldloc.3 + IL_0041: isinst "System.Exception" + IL_0046: dup + IL_0047: brtrue.s IL_004b + IL_0049: ldloc.3 + IL_004a: throw + IL_004b: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0050: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0055: leave.s IL_0064 + } + catch System.NullReferenceException + { + IL_0057: pop + IL_0058: ldstr "Success" + IL_005d: call "void System.Console.Write(object)" + IL_0062: leave.s IL_0064 + } + IL_0064: ret + } + """); } [Fact] @@ -4367,6 +6604,100 @@ public async ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "Try NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4) Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var expectedOutput = "Try NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Current(3) Got(3) NextAsync(3) Dispose(4) Done"; + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x98 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x44, Found = Int32, Expected = value 'System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x51 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 153 (0x99) + .maxstack 5 + .locals init (int V_0, + System.Collections.Generic.IAsyncEnumerator V_1, + System.Threading.CancellationToken V_2, + object V_3, + int V_4) //i + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldstr "Try " + IL_0007: call "void System.Console.Write(object)" + IL_000c: ldnull + IL_000d: throw + } + catch System.NullReferenceException + { + IL_000e: pop + IL_000f: ldc.i4.1 + IL_0010: stloc.0 + IL_0011: leave.s IL_0013 + } + IL_0013: ldloc.0 + IL_0014: ldc.i4.1 + IL_0015: bne.un.s IL_008e + IL_0017: newobj "C..ctor()" + IL_001c: ldloca.s V_2 + IL_001e: initobj "System.Threading.CancellationToken" + IL_0024: ldloc.2 + IL_0025: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_002a: stloc.1 + IL_002b: ldnull + IL_002c: stloc.3 + .try + { + IL_002d: br.s IL_0056 + IL_002f: ldloc.1 + IL_0030: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_0035: stloc.s V_4 + IL_0037: ldstr "Got({0}) " + IL_003c: ldc.i4.1 + IL_003d: newarr "object" + IL_0042: dup + IL_0043: ldc.i4.0 + IL_0044: ldloc.s V_4 + IL_0046: box "int" + IL_004b: stelem.ref + IL_004c: call "string string.Format(string, params object[])" + IL_0051: call "void System.Console.Write(object)" + IL_0056: ldloc.1 + IL_0057: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_005c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0061: brtrue.s IL_002f + IL_0063: leave.s IL_0068 + } + catch object + { + IL_0065: stloc.3 + IL_0066: leave.s IL_0068 + } + IL_0068: ldloc.1 + IL_0069: brfalse.s IL_0076 + IL_006b: ldloc.1 + IL_006c: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0071: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0076: ldloc.3 + IL_0077: brfalse.s IL_008e + IL_0079: ldloc.3 + IL_007a: isinst "System.Exception" + IL_007f: dup + IL_0080: brtrue.s IL_0084 + IL_0082: ldloc.3 + IL_0083: throw + IL_0084: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0089: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_008e: ldstr "Done" + IL_0093: call "void System.Console.Write(object)" + IL_0098: ret + } + """); } /// Covered in greater details by @@ -4478,6 +6809,80 @@ class Element var boundNode = (BoundForEachStatement)memberModel.GetUpperBoundNode(foreachSyntax); ForEachEnumeratorInfo internalInfo = boundNode.EnumeratorInfoOpt; Assert.True(internalInfo.NeedsDisposal); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var expectedOutput = "NextAsync(0) Current(1) Convert(1) Got(1) NextAsync(1) Current(2) Convert(2) Got(2) NextAsync(2) Dispose(3) Done"; + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x7f } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x44, Found = Int32, Expected = value 'System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x51 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 128 (0x80) + .maxstack 5 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + Element V_3) //i + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_003d + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: call "Element Element.op_Implicit(int)" + IL_0023: stloc.3 + IL_0024: ldstr "Got({0}) " + IL_0029: ldc.i4.1 + IL_002a: newarr "object" + IL_002f: dup + IL_0030: ldc.i4.0 + IL_0031: ldloc.3 + IL_0032: stelem.ref + IL_0033: call "string string.Format(string, params object[])" + IL_0038: call "void System.Console.Write(object)" + IL_003d: ldloc.0 + IL_003e: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0043: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0048: brtrue.s IL_0018 + IL_004a: leave.s IL_004f + } + catch object + { + IL_004c: stloc.2 + IL_004d: leave.s IL_004f + } + IL_004f: ldloc.0 + IL_0050: brfalse.s IL_005d + IL_0052: ldloc.0 + IL_0053: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0058: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005d: ldloc.2 + IL_005e: brfalse.s IL_0075 + IL_0060: ldloc.2 + IL_0061: isinst "System.Exception" + IL_0066: dup + IL_0067: brtrue.s IL_006b + IL_0069: ldloc.2 + IL_006a: throw + IL_006b: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0070: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0075: ldstr "Done" + IL_007a: call "void System.Console.Write(object)" + IL_007f: ret + } + """); } [Fact] @@ -4529,7 +6934,8 @@ public async ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Dispose(3)"); + var expectedOutput = "NextAsync(0) Current(1) Got(1) NextAsync(1) Current(2) Got(2) NextAsync(2) Dispose(3)"; + CompileAndVerify(comp, expectedOutput: expectedOutput); var tree = comp.SyntaxTrees.Single(); var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); @@ -4551,6 +6957,88 @@ public async ValueTask DisposeAsync() var boundNode = (BoundForEachStatement)memberModel.GetUpperBoundNode(foreachSyntax); ForEachEnumeratorInfo internalInfo = boundNode.EnumeratorInfoOpt; Assert.True(internalInfo.NeedsDisposal); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x97 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x44, Found = Int32, Expected = value 'System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x51 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 152 (0x98) + .maxstack 5 + .locals init (C? V_0, //c + C V_1, + System.Collections.Generic.IAsyncEnumerator V_2, + System.Threading.CancellationToken V_3, + object V_4, + int V_5) //i + IL_0000: ldloca.s V_0 + IL_0002: ldloca.s V_1 + IL_0004: initobj "C" + IL_000a: ldloc.1 + IL_000b: call "C?..ctor(C)" + IL_0010: ldloca.s V_0 + IL_0012: call "C C?.Value.get" + IL_0017: stloc.1 + IL_0018: ldloca.s V_1 + IL_001a: ldloca.s V_3 + IL_001c: initobj "System.Threading.CancellationToken" + IL_0022: ldloc.3 + IL_0023: constrained. "C" + IL_0029: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_002e: stloc.2 + IL_002f: ldnull + IL_0030: stloc.s V_4 + .try + { + IL_0032: br.s IL_005b + IL_0034: ldloc.2 + IL_0035: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_003a: stloc.s V_5 + IL_003c: ldstr "Got({0}) " + IL_0041: ldc.i4.1 + IL_0042: newarr "object" + IL_0047: dup + IL_0048: ldc.i4.0 + IL_0049: ldloc.s V_5 + IL_004b: box "int" + IL_0050: stelem.ref + IL_0051: call "string string.Format(string, params object[])" + IL_0056: call "void System.Console.Write(object)" + IL_005b: ldloc.2 + IL_005c: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0061: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0066: brtrue.s IL_0034 + IL_0068: leave.s IL_006e + } + catch object + { + IL_006a: stloc.s V_4 + IL_006c: leave.s IL_006e + } + IL_006e: ldloc.2 + IL_006f: brfalse.s IL_007c + IL_0071: ldloc.2 + IL_0072: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0077: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_007c: ldloc.s V_4 + IL_007e: brfalse.s IL_0097 + IL_0080: ldloc.s V_4 + IL_0082: isinst "System.Exception" + IL_0087: dup + IL_0088: brtrue.s IL_008d + IL_008a: ldloc.s V_4 + IL_008c: throw + IL_008d: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0092: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0097: ret + } + """); } [Fact] @@ -4585,6 +7073,85 @@ IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(System.Threading. var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "Success"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("Success", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x88 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 137 (0x89) + .maxstack 2 + .locals init (C? V_0, //c + System.Collections.Generic.IAsyncEnumerator V_1, + C V_2, + System.Threading.CancellationToken V_3, + object V_4) + IL_0000: ldloca.s V_0 + IL_0002: initobj "C?" + .try + { + IL_0008: ldloca.s V_0 + IL_000a: call "C C?.Value.get" + IL_000f: stloc.2 + IL_0010: ldloca.s V_2 + IL_0012: ldloca.s V_3 + IL_0014: initobj "System.Threading.CancellationToken" + IL_001a: ldloc.3 + IL_001b: constrained. "C" + IL_0021: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0026: stloc.1 + IL_0027: ldnull + IL_0028: stloc.s V_4 + .try + { + IL_002a: br.s IL_003d + IL_002c: ldloc.1 + IL_002d: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_0032: pop + IL_0033: ldstr "UNREACHABLE" + IL_0038: call "void System.Console.Write(object)" + IL_003d: ldloc.1 + IL_003e: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0043: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0048: brtrue.s IL_002c + IL_004a: leave.s IL_0050 + } + catch object + { + IL_004c: stloc.s V_4 + IL_004e: leave.s IL_0050 + } + IL_0050: ldloc.1 + IL_0051: brfalse.s IL_005e + IL_0053: ldloc.1 + IL_0054: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0059: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005e: ldloc.s V_4 + IL_0060: brfalse.s IL_0079 + IL_0062: ldloc.s V_4 + IL_0064: isinst "System.Exception" + IL_0069: dup + IL_006a: brtrue.s IL_006f + IL_006c: ldloc.s V_4 + IL_006e: throw + IL_006f: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0074: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0079: leave.s IL_0088 + } + catch System.InvalidOperationException + { + IL_007b: pop + IL_007c: ldstr "Success" + IL_0081: call "void System.Console.Write(object)" + IL_0086: leave.s IL_0088 + } + IL_0088: ret + } + """); } [Fact] @@ -4639,7 +7206,8 @@ public static class Extensions var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Deconstruct(1) Got(1,-1) NextAsync(1) Current(2) Deconstruct(2) Got(2,-2) NextAsync(2) Dispose(3) Done"); + var expectedOutput = "NextAsync(0) Current(1) Deconstruct(1) Got(1,-1) NextAsync(1) Current(2) Deconstruct(2) Got(2,-2) NextAsync(2) Dispose(3) Done"; + CompileAndVerify(comp, expectedOutput: expectedOutput); var tree = comp.SyntaxTrees.Single(); var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); @@ -4661,6 +7229,92 @@ public static class Extensions var boundNode = (BoundForEachStatement)memberModel.GetUpperBoundNode(foreachSyntax); ForEachEnumeratorInfo internalInfo = boundNode.EnumeratorInfoOpt; Assert.True(internalInfo.NeedsDisposal); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x93 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x44, Found = Int32, Expected = value 'System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x47 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 148 (0x94) + .maxstack 5 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + string V_3, //i + int V_4, //j + string V_5, + int V_6) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0051 + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: ldloca.s V_5 + IL_0020: ldloca.s V_6 + IL_0022: call "void Extensions.Deconstruct(int, out string, out int)" + IL_0027: ldloc.s V_5 + IL_0029: stloc.3 + IL_002a: ldloc.s V_6 + IL_002c: stloc.s V_4 + IL_002e: ldstr "Got({0},{1}) " + IL_0033: ldc.i4.2 + IL_0034: newarr "object" + IL_0039: dup + IL_003a: ldc.i4.0 + IL_003b: ldloc.3 + IL_003c: stelem.ref + IL_003d: dup + IL_003e: ldc.i4.1 + IL_003f: ldloc.s V_4 + IL_0041: box "int" + IL_0046: stelem.ref + IL_0047: call "string string.Format(string, params object[])" + IL_004c: call "void System.Console.Write(object)" + IL_0051: ldloc.0 + IL_0052: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0057: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005c: brtrue.s IL_0018 + IL_005e: leave.s IL_0063 + } + catch object + { + IL_0060: stloc.2 + IL_0061: leave.s IL_0063 + } + IL_0063: ldloc.0 + IL_0064: brfalse.s IL_0071 + IL_0066: ldloc.0 + IL_0067: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_006c: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0071: ldloc.2 + IL_0072: brfalse.s IL_0089 + IL_0074: ldloc.2 + IL_0075: isinst "System.Exception" + IL_007a: dup + IL_007b: brtrue.s IL_007f + IL_007d: ldloc.2 + IL_007e: throw + IL_007f: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0084: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0089: ldstr "Done" + IL_008e: call "void System.Console.Write(object)" + IL_0093: ret + } + """); } [Fact] @@ -4738,7 +7392,8 @@ public async ValueTask DisposeAsync() }"; var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got(1,-1) NextAsync(1) Current(2) Got(2,-2) NextAsync(2) Dispose(3) Done"); + var expectedOutput = "NextAsync(0) Current(1) Got(1,-1) NextAsync(1) Current(2) Got(2,-2) NextAsync(2) Dispose(3) Done"; + CompileAndVerify(comp, expectedOutput: expectedOutput); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); @@ -4755,6 +7410,88 @@ public async ValueTask DisposeAsync() Assert.Equal("(System.String, System.Int32)", info.ElementType.ToTestDisplayString()); Assert.Equal(ConversionKind.Identity, info.ElementConversion.Kind); Assert.Equal(ConversionKind.Identity, info.CurrentConversion.Kind); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x91 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x44, Found = Int32, Expected = value 'System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x47 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 146 (0x92) + .maxstack 5 + .locals init (System.Collections.Generic.IAsyncEnumerator> V_0, + System.Threading.CancellationToken V_1, + object V_2, + string V_3, //i + int V_4) //j + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator> System.Collections.Generic.IAsyncEnumerable>.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_004f + IL_0018: ldloc.0 + IL_0019: callvirt "System.ValueTuple System.Collections.Generic.IAsyncEnumerator>.Current.get" + IL_001e: dup + IL_001f: ldfld "string System.ValueTuple.Item1" + IL_0024: stloc.3 + IL_0025: ldfld "int System.ValueTuple.Item2" + IL_002a: stloc.s V_4 + IL_002c: ldstr "Got({0},{1}) " + IL_0031: ldc.i4.2 + IL_0032: newarr "object" + IL_0037: dup + IL_0038: ldc.i4.0 + IL_0039: ldloc.3 + IL_003a: stelem.ref + IL_003b: dup + IL_003c: ldc.i4.1 + IL_003d: ldloc.s V_4 + IL_003f: box "int" + IL_0044: stelem.ref + IL_0045: call "string string.Format(string, params object[])" + IL_004a: call "void System.Console.Write(object)" + IL_004f: ldloc.0 + IL_0050: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator>.MoveNextAsync()" + IL_0055: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005a: brtrue.s IL_0018 + IL_005c: leave.s IL_0061 + } + catch object + { + IL_005e: stloc.2 + IL_005f: leave.s IL_0061 + } + IL_0061: ldloc.0 + IL_0062: brfalse.s IL_006f + IL_0064: ldloc.0 + IL_0065: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_006a: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_006f: ldloc.2 + IL_0070: brfalse.s IL_0087 + IL_0072: ldloc.2 + IL_0073: isinst "System.Exception" + IL_0078: dup + IL_0079: brtrue.s IL_007d + IL_007b: ldloc.2 + IL_007c: throw + IL_007d: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0082: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0087: ldstr "Done" + IL_008c: call "void System.Console.Write(object)" + IL_0091: ret + } + """); } [Fact] @@ -4818,7 +7555,93 @@ public static class Extensions var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got(1,-1) NextAsync(1) Current(2) Got(2,-2) NextAsync(2) Dispose(3) Done"); + string expectedOutput = "NextAsync(0) Current(1) Got(1,-1) NextAsync(1) Current(2) Got(2,-2) NextAsync(2) Dispose(3) Done"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x95 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x44, Found = Int32, Expected = value 'System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x51 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 150 (0x96) + .maxstack 5 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, //e + System.Threading.CancellationToken V_1, + object V_2, + int V_3, //i + int V_4, //j + int V_5, + int V_6) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0056 + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: ldloca.s V_5 + IL_0020: ldloca.s V_6 + IL_0022: call "void Extensions.Deconstruct(int, out int, out int)" + IL_0027: ldloc.s V_5 + IL_0029: stloc.3 + IL_002a: ldloc.s V_6 + IL_002c: stloc.s V_4 + IL_002e: ldstr "Got({0},{1}) " + IL_0033: ldc.i4.2 + IL_0034: newarr "object" + IL_0039: dup + IL_003a: ldc.i4.0 + IL_003b: ldloc.3 + IL_003c: box "int" + IL_0041: stelem.ref + IL_0042: dup + IL_0043: ldc.i4.1 + IL_0044: ldloc.s V_4 + IL_0046: box "int" + IL_004b: stelem.ref + IL_004c: call "string string.Format(string, params object[])" + IL_0051: call "void System.Console.Write(object)" + IL_0056: ldloc.0 + IL_0057: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_005c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0061: brtrue.s IL_0018 + IL_0063: leave.s IL_0068 + } + catch object + { + IL_0065: stloc.2 + IL_0066: leave.s IL_0068 + } + IL_0068: ldloc.0 + IL_0069: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_006e: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0073: ldloc.2 + IL_0074: brfalse.s IL_008b + IL_0076: ldloc.2 + IL_0077: isinst "System.Exception" + IL_007c: dup + IL_007d: brtrue.s IL_0081 + IL_007f: ldloc.2 + IL_0080: throw + IL_0081: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0086: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_008b: ldstr "Done" + IL_0090: call "void System.Console.Write(object)" + IL_0095: ret + } + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/30257")] @@ -5042,7 +7865,8 @@ static async System.Threading.Tasks.Task Main() var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got NextAsync(1) Current(2) Got NextAsync(2) Current(3) Got NextAsync(3) Dispose(4)"); + var expectedOutput = "NextAsync(0) Current(1) Got NextAsync(1) Current(2) Got NextAsync(2) Current(3) Got NextAsync(3) Dispose(4)"; + CompileAndVerify(comp, expectedOutput: expectedOutput); var tree = comp.SyntaxTrees.Single(); var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); @@ -5064,9 +7888,71 @@ static async System.Threading.Tasks.Task Main() var boundNode = (BoundForEachStatement)memberModel.GetUpperBoundNode(foreachSyntax); ForEachEnumeratorInfo internalInfo = boundNode.EnumeratorInfoOpt; Assert.True(internalInfo.NeedsDisposal); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x61 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x44, Found = Int32, Expected = value 'System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x47 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 98 (0x62) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2) + IL_0000: newobj "Collection..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0029 + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: pop + IL_001f: ldstr "Got " + IL_0024: call "void System.Console.Write(object)" + IL_0029: ldloc.0 + IL_002a: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_002f: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0034: brtrue.s IL_0018 + IL_0036: leave.s IL_003b + } + catch object + { + IL_0038: stloc.2 + IL_0039: leave.s IL_003b + } + IL_003b: ldloc.0 + IL_003c: brfalse.s IL_0049 + IL_003e: ldloc.0 + IL_003f: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0044: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0049: ldloc.2 + IL_004a: brfalse.s IL_0061 + IL_004c: ldloc.2 + IL_004d: isinst "System.Exception" + IL_0052: dup + IL_0053: brtrue.s IL_0057 + IL_0055: ldloc.2 + IL_0056: throw + IL_0057: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_005c: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0061: ret + } + """); } - [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Fact] public void TestWithInterfaceImplementingPattern() { string source = @" @@ -5123,7 +8009,8 @@ static async System.Threading.Tasks.Task Main() var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got NextAsync(1) Current(2) Got NextAsync(2) Current(3) Got NextAsync(3)"); + string expectedOutput = "NextAsync(0) Current(1) Got NextAsync(1) Current(2) Got NextAsync(2) Current(3) Got NextAsync(3)"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); var tree = comp.SyntaxTrees.Single(); var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); @@ -5287,9 +8174,43 @@ .locals init (int V_0, IL_0119: nop IL_011a: ret }", sequencePoints: "C+
d__0.MoveNext", source: source); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x44, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [Main]: Return value missing on the stack. { Offset = 0x34 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 53 (0x35) + .maxstack 2 + .locals init (IMyAsyncEnumerator V_0, + System.Threading.CancellationToken V_1) + IL_0000: newobj "Collection..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "IMyAsyncEnumerator ICollection.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: br.s IL_0027 + IL_0016: ldloc.0 + IL_0017: callvirt "int IMyAsyncEnumerator.Current.get" + IL_001c: pop + IL_001d: ldstr "Got " + IL_0022: call "void System.Console.Write(object)" + IL_0027: ldloc.0 + IL_0028: callvirt "System.Threading.Tasks.Task IMyAsyncEnumerator.MoveNextAsync()" + IL_002d: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0032: brtrue.s IL_0016 + IL_0034: ret + } + """); } - [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Fact] public void TestWithInterfaceImplementingPattern_ChildImplementsDisposeAsync() { string source = @" @@ -5349,7 +8270,8 @@ static async System.Threading.Tasks.Task Main() var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "NextAsync(0) Current(1) Got NextAsync(1) Current(2) Got NextAsync(2) Current(3) Got NextAsync(3)"); + var expectedOutput = "NextAsync(0) Current(1) Got NextAsync(1) Current(2) Got NextAsync(2) Current(3) Got NextAsync(3)"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); var tree = comp.SyntaxTrees.Single(); var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); @@ -5357,6 +8279,40 @@ static async System.Threading.Tasks.Task Main() var info = model.GetForEachStatementInfo(foreachSyntax); Assert.Null(info.DisposeMethod); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier2 = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x44, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [Main]: Return value missing on the stack. { Offset = 0x34 } + """ + }); + verifier2.VerifyIL("C.Main()", """ + { + // Code size 53 (0x35) + .maxstack 2 + .locals init (IMyAsyncEnumerator V_0, + System.Threading.CancellationToken V_1) + IL_0000: newobj "Collection..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "IMyAsyncEnumerator ICollection.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: br.s IL_0027 + IL_0016: ldloc.0 + IL_0017: callvirt "int IMyAsyncEnumerator.Current.get" + IL_001c: pop + IL_001d: ldstr "Got " + IL_0022: call "void System.Console.Write(object)" + IL_0027: ldloc.0 + IL_0028: callvirt "System.Threading.Tasks.Task IMyAsyncEnumerator.MoveNextAsync()" + IL_002d: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0032: brtrue.s IL_0016 + IL_0034: ret + } + """); } [Fact] @@ -5623,6 +8579,36 @@ public int Current var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x27 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 40 (0x28) + .maxstack 2 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: ldc.i4.0 + IL_0006: newarr "int" + IL_000b: call "C.Enumerator C.GetAsyncEnumerator(params int[])" + IL_0010: stloc.0 + IL_0011: br.s IL_001a + IL_0013: ldloc.0 + IL_0014: callvirt "int C.Enumerator.Current.get" + IL_0019: pop + IL_001a: ldloc.0 + IL_001b: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0020: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: brtrue.s IL_0013 + IL_0027: ret + } + """); } [Fact] @@ -5719,6 +8705,64 @@ public async Task DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync DisposeAsync Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync DisposeAsync Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x58 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 89 (0x59) + .maxstack 2 + .locals init (C.Enumerator V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0016 + IL_000f: ldloc.0 + IL_0010: callvirt "int C.Enumerator.Current.get" + IL_0015: pop + IL_0016: ldloc.0 + IL_0017: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: brtrue.s IL_000f + IL_0023: leave.s IL_0028 + } + catch object + { + IL_0025: stloc.1 + IL_0026: leave.s IL_0028 + } + IL_0028: ldloc.0 + IL_0029: brfalse.s IL_0036 + IL_002b: ldloc.0 + IL_002c: callvirt "System.Threading.Tasks.Task C.Enumerator.DisposeAsync()" + IL_0031: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0036: ldloc.1 + IL_0037: brfalse.s IL_004e + IL_0039: ldloc.1 + IL_003a: isinst "System.Exception" + IL_003f: dup + IL_0040: brtrue.s IL_0044 + IL_0042: ldloc.1 + IL_0043: throw + IL_0044: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0049: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_004e: ldstr "Done" + IL_0053: call "void System.Console.Write(object)" + IL_0058: ret + } + """); } [Fact] @@ -5807,6 +8851,36 @@ public static class Extension var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x2b } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 44 (0x2c) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: br.s IL_0014 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: pop + IL_0014: ldloc.0 + IL_0015: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001f: brtrue.s IL_000d + IL_0021: ldstr "Done" + IL_0026: call "void System.Console.Write(object)" + IL_002b: ret + } + """); } [Fact] @@ -5855,6 +8929,36 @@ public static class Extension2 var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x2b } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 44 (0x2c) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: br.s IL_0014 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: pop + IL_0014: ldloc.0 + IL_0015: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001f: brtrue.s IL_000d + IL_0021: ldstr "Done" + IL_0026: call "void System.Console.Write(object)" + IL_002b: ret + } + """); } [Fact] @@ -5899,6 +9003,64 @@ public async ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync DisposeAsync Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync DisposeAsync Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x58 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 89 (0x59) + .maxstack 2 + .locals init (C.Enumerator V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0016 + IL_000f: ldloc.0 + IL_0010: callvirt "int C.Enumerator.Current.get" + IL_0015: pop + IL_0016: ldloc.0 + IL_0017: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: brtrue.s IL_000f + IL_0023: leave.s IL_0028 + } + catch object + { + IL_0025: stloc.1 + IL_0026: leave.s IL_0028 + } + IL_0028: ldloc.0 + IL_0029: brfalse.s IL_0036 + IL_002b: ldloc.0 + IL_002c: callvirt "System.Threading.Tasks.ValueTask C.Enumerator.DisposeAsync()" + IL_0031: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0036: ldloc.1 + IL_0037: brfalse.s IL_004e + IL_0039: ldloc.1 + IL_003a: isinst "System.Exception" + IL_003f: dup + IL_0040: brtrue.s IL_0044 + IL_0042: ldloc.1 + IL_0043: throw + IL_0044: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0049: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_004e: ldstr "Done" + IL_0053: call "void System.Console.Write(object)" + IL_0058: ret + } + """); } [Fact] @@ -6033,6 +9195,73 @@ public void OnCompleted(System.Action continuation) { } var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync DisposeAsync Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync DisposeAsync Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x6e } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 111 (0x6f) + .maxstack 2 + .locals init (C.Enumerator V_0, + object V_1, + Awaiter V_2) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0016 + IL_000f: ldloc.0 + IL_0010: callvirt "int C.Enumerator.Current.get" + IL_0015: pop + IL_0016: ldloc.0 + IL_0017: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: brtrue.s IL_000f + IL_0023: leave.s IL_0028 + } + catch object + { + IL_0025: stloc.1 + IL_0026: leave.s IL_0028 + } + IL_0028: ldloc.0 + IL_0029: brfalse.s IL_004c + IL_002b: ldloc.0 + IL_002c: callvirt "Awaitable C.Enumerator.DisposeAsync()" + IL_0031: callvirt "Awaiter Awaitable.GetAwaiter()" + IL_0036: stloc.2 + IL_0037: ldloc.2 + IL_0038: callvirt "bool Awaiter.IsCompleted.get" + IL_003d: brtrue.s IL_0045 + IL_003f: ldloc.2 + IL_0040: call "void System.Runtime.CompilerServices.AsyncHelpers.AwaitAwaiter(Awaiter)" + IL_0045: ldloc.2 + IL_0046: callvirt "bool Awaiter.GetResult()" + IL_004b: pop + IL_004c: ldloc.1 + IL_004d: brfalse.s IL_0064 + IL_004f: ldloc.1 + IL_0050: isinst "System.Exception" + IL_0055: dup + IL_0056: brtrue.s IL_005a + IL_0058: ldloc.1 + IL_0059: throw + IL_005a: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_005f: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0064: ldstr "Done" + IL_0069: call "void System.Console.Write(object)" + IL_006e: ret + } + """); } [Fact] @@ -6077,6 +9306,64 @@ public async Task DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync DisposeAsync Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync DisposeAsync Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x58 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 89 (0x59) + .maxstack 2 + .locals init (C.Enumerator V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0016 + IL_000f: ldloc.0 + IL_0010: callvirt "int C.Enumerator.Current.get" + IL_0015: pop + IL_0016: ldloc.0 + IL_0017: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: brtrue.s IL_000f + IL_0023: leave.s IL_0028 + } + catch object + { + IL_0025: stloc.1 + IL_0026: leave.s IL_0028 + } + IL_0028: ldloc.0 + IL_0029: brfalse.s IL_0036 + IL_002b: ldloc.0 + IL_002c: callvirt "System.Threading.Tasks.Task C.Enumerator.DisposeAsync()" + IL_0031: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0036: ldloc.1 + IL_0037: brfalse.s IL_004e + IL_0039: ldloc.1 + IL_003a: isinst "System.Exception" + IL_003f: dup + IL_0040: brtrue.s IL_0044 + IL_0042: ldloc.1 + IL_0043: throw + IL_0044: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0049: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_004e: ldstr "Done" + IL_0053: call "void System.Console.Write(object)" + IL_0058: ret + } + """); } [Fact] @@ -6123,6 +9410,65 @@ public async Task DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync DisposeAsync Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync DisposeAsync Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x59 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 90 (0x5a) + .maxstack 2 + .locals init (C.Enumerator V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0016 + IL_000f: ldloc.0 + IL_0010: callvirt "int C.Enumerator.Current.get" + IL_0015: pop + IL_0016: ldloc.0 + IL_0017: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: brtrue.s IL_000f + IL_0023: leave.s IL_0028 + } + catch object + { + IL_0025: stloc.1 + IL_0026: leave.s IL_0028 + } + IL_0028: ldloc.0 + IL_0029: brfalse.s IL_0037 + IL_002b: ldloc.0 + IL_002c: callvirt "System.Threading.Tasks.Task C.Enumerator.DisposeAsync()" + IL_0031: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0036: pop + IL_0037: ldloc.1 + IL_0038: brfalse.s IL_004f + IL_003a: ldloc.1 + IL_003b: isinst "System.Exception" + IL_0040: dup + IL_0041: brtrue.s IL_0045 + IL_0043: ldloc.1 + IL_0044: throw + IL_0045: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004a: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_004f: ldstr "Done" + IL_0054: call "void System.Console.Write(object)" + IL_0059: ret + } + """); } [Fact] @@ -6168,6 +9514,66 @@ public async Task DisposeAsync(int i = 1) var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "MoveNextAsync DisposeAsync 1 Done"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("MoveNextAsync DisposeAsync 1 Done", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5a } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Unexpected type on the stack. { Offset = 0x43, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 91 (0x5b) + .maxstack 2 + .locals init (C.Enumerator V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0016 + IL_000f: ldloc.0 + IL_0010: callvirt "int C.Enumerator.Current.get" + IL_0015: pop + IL_0016: ldloc.0 + IL_0017: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: brtrue.s IL_000f + IL_0023: leave.s IL_0028 + } + catch object + { + IL_0025: stloc.1 + IL_0026: leave.s IL_0028 + } + IL_0028: ldloc.0 + IL_0029: brfalse.s IL_0038 + IL_002b: ldloc.0 + IL_002c: ldc.i4.1 + IL_002d: callvirt "System.Threading.Tasks.Task C.Enumerator.DisposeAsync(int)" + IL_0032: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0037: pop + IL_0038: ldloc.1 + IL_0039: brfalse.s IL_0050 + IL_003b: ldloc.1 + IL_003c: isinst "System.Exception" + IL_0041: dup + IL_0042: brtrue.s IL_0046 + IL_0044: ldloc.1 + IL_0045: throw + IL_0046: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004b: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0050: ldstr "Done" + IL_0055: call "void System.Console.Write(object)" + IL_005a: ret + } + """); } [Fact] @@ -6222,6 +9628,80 @@ static async Task Main() }"; var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "StructAwaitable1StructAwaitable2"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source, options: TestOptions.ReleaseExe); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("StructAwaitable1StructAwaitable2", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x86 } + """ + }); + verifier.VerifyIL("Program.Main()", """ + { + // Code size 135 (0x87) + .maxstack 2 + .locals init (Enumerable.Enumerator V_0, + object V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2, + System.Runtime.CompilerServices.TaskAwaiter V_3) + IL_0000: newobj "Enumerable..ctor()" + IL_0005: call "Enumerable.Enumerator Enumerable.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_0016 + IL_000f: ldloc.0 + IL_0010: callvirt "object Enumerable.Enumerator.Current.get" + IL_0015: pop + IL_0016: ldloc.0 + IL_0017: callvirt "StructAwaitable1 Enumerable.Enumerator.MoveNextAsync()" + IL_001c: box "StructAwaitable1" + IL_0021: call "System.Runtime.CompilerServices.TaskAwaiter Extensions.GetAwaiter(I1)" + IL_0026: stloc.2 + IL_0027: ldloca.s V_2 + IL_0029: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_002e: brtrue.s IL_0036 + IL_0030: ldloc.2 + IL_0031: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter>(System.Runtime.CompilerServices.TaskAwaiter)" + IL_0036: ldloca.s V_2 + IL_0038: call "bool System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_003d: brtrue.s IL_000f + IL_003f: leave.s IL_0044 + } + catch object + { + IL_0041: stloc.1 + IL_0042: leave.s IL_0044 + } + IL_0044: ldloc.0 + IL_0045: brfalse.s IL_006e + IL_0047: ldloc.0 + IL_0048: callvirt "StructAwaitable2 Enumerable.Enumerator.DisposeAsync()" + IL_004d: box "StructAwaitable2" + IL_0052: call "System.Runtime.CompilerServices.TaskAwaiter Extensions.GetAwaiter(I2)" + IL_0057: stloc.3 + IL_0058: ldloca.s V_3 + IL_005a: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_005f: brtrue.s IL_0067 + IL_0061: ldloc.3 + IL_0062: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.TaskAwaiter)" + IL_0067: ldloca.s V_3 + IL_0069: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_006e: ldloc.1 + IL_006f: brfalse.s IL_0086 + IL_0071: ldloc.1 + IL_0072: isinst "System.Exception" + IL_0077: dup + IL_0078: brtrue.s IL_007c + IL_007a: ldloc.1 + IL_007b: throw + IL_007c: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0081: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0086: ret + } + """); } [Fact] @@ -6322,6 +9802,72 @@ public static async Task Main() var comp = CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x72 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 115 (0x73) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + C? V_1, + C V_2, + System.Threading.CancellationToken V_3, + object V_4) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "C?" + IL_0009: call "C C?.Value.get" + IL_000e: stloc.2 + IL_000f: ldloca.s V_2 + IL_0011: ldloca.s V_3 + IL_0013: initobj "System.Threading.CancellationToken" + IL_0019: ldloc.3 + IL_001a: constrained. "C" + IL_0020: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0025: stloc.0 + IL_0026: ldnull + IL_0027: stloc.s V_4 + .try + { + IL_0029: br.s IL_0036 + IL_002b: ldloc.0 + IL_002c: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_0031: call "void System.Console.Write(int)" + IL_0036: ldloc.0 + IL_0037: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_003c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0041: brtrue.s IL_002b + IL_0043: leave.s IL_0049 + } + catch object + { + IL_0045: stloc.s V_4 + IL_0047: leave.s IL_0049 + } + IL_0049: ldloc.0 + IL_004a: brfalse.s IL_0057 + IL_004c: ldloc.0 + IL_004d: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0052: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0057: ldloc.s V_4 + IL_0059: brfalse.s IL_0072 + IL_005b: ldloc.s V_4 + IL_005d: isinst "System.Exception" + IL_0062: dup + IL_0063: brtrue.s IL_0068 + IL_0065: ldloc.s V_4 + IL_0067: throw + IL_0068: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_006d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0072: ret + } + """); } [Fact] @@ -6346,6 +9892,67 @@ public static async Task Main() var comp = CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 95 (0x5f) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + C? V_1, + C V_2, + object V_3) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "C?" + IL_0009: call "C C?.Value.get" + IL_000e: stloc.2 + IL_000f: ldloca.s V_2 + IL_0011: call "System.Collections.Generic.IAsyncEnumerator C.GetAsyncEnumerator()" + IL_0016: stloc.0 + IL_0017: ldnull + IL_0018: stloc.3 + .try + { + IL_0019: br.s IL_0026 + IL_001b: ldloc.0 + IL_001c: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_0021: call "void System.Console.Write(int)" + IL_0026: ldloc.0 + IL_0027: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_002c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0031: brtrue.s IL_001b + IL_0033: leave.s IL_0038 + } + catch object + { + IL_0035: stloc.3 + IL_0036: leave.s IL_0038 + } + IL_0038: ldloc.0 + IL_0039: brfalse.s IL_0046 + IL_003b: ldloc.0 + IL_003c: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0041: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0046: ldloc.3 + IL_0047: brfalse.s IL_005e + IL_0049: ldloc.3 + IL_004a: isinst "System.Exception" + IL_004f: dup + IL_0050: brtrue.s IL_0054 + IL_0052: ldloc.3 + IL_0053: throw + IL_0054: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0059: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_005e: ret + } + """); } [Fact] @@ -6446,6 +10053,33 @@ public static class Extensions Assert.Equal("System.Int32", info.ElementType.ToTestDisplayString()); Assert.Equal(ConversionKind.Identity, info.ElementConversion.Kind); Assert.Equal(ConversionKind.Identity, info.CurrentConversion.Kind); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -6476,6 +10110,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(object)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -6506,6 +10167,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x21 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 34 (0x22) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: ldnull + IL_0001: call "C.Enumerator Extensions.GetAsyncEnumerator(object)" + IL_0006: stloc.0 + IL_0007: br.s IL_0014 + IL_0009: ldloc.0 + IL_000a: callvirt "int C.Enumerator.Current.get" + IL_000f: call "void System.Console.Write(int)" + IL_0014: ldloc.0 + IL_0015: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001f: brtrue.s IL_0009 + IL_0021: ret + } + """); } [Fact] @@ -6536,6 +10224,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x27 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 40 (0x28) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0019 + IL_000d: ldloca.s V_0 + IL_000f: call "readonly int C.Enumerator.Current.get" + IL_0014: call "void System.Console.Write(int)" + IL_0019: ldloca.s V_0 + IL_001b: call "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0020: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: brtrue.s IL_000d + IL_0027: ret + } + """); } [Fact] @@ -6754,6 +10469,35 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xb } + [
g__Inner|0_0]: Return value missing on the stack. { Offset = 0x26 } + """ + }); + verifier.VerifyIL("C.
g__Inner|0_0(T)", """ + { + // Code size 39 (0x27) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: ldarg.0 + IL_0001: box "T" + IL_0006: call "C.Enumerator Extensions.GetAsyncEnumerator(object)" + IL_000b: stloc.0 + IL_000c: br.s IL_0019 + IL_000e: ldloc.0 + IL_000f: callvirt "int C.Enumerator.Current.get" + IL_0014: call "void System.Console.Write(int)" + IL_0019: ldloc.0 + IL_001a: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001f: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0024: brtrue.s IL_000e + IL_0026: ret + } + """); } [Fact] @@ -6789,6 +10533,35 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xb } + [
g__Inner|0_0]: Return value missing on the stack. { Offset = 0x26 } + """ + }); + verifier.VerifyIL("C.
g__Inner|0_0(T)", """ + { + // Code size 39 (0x27) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: ldarg.0 + IL_0001: box "T" + IL_0006: call "C.Enumerator Extensions.GetAsyncEnumerator(System.IConvertible)" + IL_000b: stloc.0 + IL_000c: br.s IL_0019 + IL_000e: ldloc.0 + IL_000f: callvirt "int C.Enumerator.Current.get" + IL_0014: call "void System.Console.Write(int)" + IL_0019: ldloc.0 + IL_001a: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001f: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0024: brtrue.s IL_000e + IL_0026: ret + } + """); } [Fact] @@ -6820,6 +10593,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: ldstr " " + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(object)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -6852,6 +10652,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: ldstr " " + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(string)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -6951,6 +10778,37 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 47 (0x2f) + .maxstack 1 + .locals init (C.Enumerator V_0, + C V_1) + IL_0000: ldloca.s V_1 + IL_0002: initobj "C" + IL_0008: ldloc.1 + IL_0009: box "C" + IL_000e: call "C.Enumerator Extensions.GetAsyncEnumerator(object)" + IL_0013: stloc.0 + IL_0014: br.s IL_0021 + IL_0016: ldloc.0 + IL_0017: callvirt "int C.Enumerator.Current.get" + IL_001c: call "void System.Console.Write(int)" + IL_0021: ldloc.0 + IL_0022: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0027: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002c: brtrue.s IL_0016 + IL_002e: ret + } + """); } [Fact] @@ -6982,6 +10840,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(I)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -7012,6 +10897,41 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x3f } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 64 (0x40) + .maxstack 2 + .locals init (C.Enumerator V_0) + IL_0000: ldsfld "System.Func C.<>c.<>9__0_0" + IL_0005: dup + IL_0006: brtrue.s IL_001f + IL_0008: pop + IL_0009: ldsfld "C.<>c C.<>c.<>9" + IL_000e: ldftn "int C.<>c.
b__0_0()" + IL_0014: newobj "System.Func..ctor(object, System.IntPtr)" + IL_0019: dup + IL_001a: stsfld "System.Func C.<>c.<>9__0_0" + IL_001f: call "C.Enumerator Extensions.GetAsyncEnumerator(System.Func)" + IL_0024: stloc.0 + IL_0025: br.s IL_0032 + IL_0027: ldloc.0 + IL_0028: callvirt "int C.Enumerator.Current.get" + IL_002d: call "void System.Console.Write(int)" + IL_0032: ldloc.0 + IL_0033: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0038: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003d: brtrue.s IL_0027 + IL_003f: ret + } + """); } [Fact] @@ -7043,6 +10963,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x21 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 34 (0x22) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: ldc.i4.0 + IL_0001: call "C.Enumerator Extensions.GetAsyncEnumerator(E)" + IL_0006: stloc.0 + IL_0007: br.s IL_0014 + IL_0009: ldloc.0 + IL_000a: callvirt "int C.Enumerator.Current.get" + IL_000f: call "void System.Console.Write(int)" + IL_0014: ldloc.0 + IL_0015: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001f: brtrue.s IL_0009 + IL_0021: ret + } + """); } [Fact] @@ -7073,6 +11020,36 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x29 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 42 (0x2a) + .maxstack 1 + .locals init (C.Enumerator V_0, + int? V_1) + IL_0000: ldloca.s V_1 + IL_0002: initobj "int?" + IL_0008: ldloc.1 + IL_0009: call "C.Enumerator Extensions.GetAsyncEnumerator(int?)" + IL_000e: stloc.0 + IL_000f: br.s IL_001c + IL_0011: ldloc.0 + IL_0012: callvirt "int C.Enumerator.Current.get" + IL_0017: call "void System.Console.Write(int)" + IL_001c: ldloc.0 + IL_001d: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0022: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0027: brtrue.s IL_0011 + IL_0029: ret + } + """); } [Fact] @@ -7103,6 +11080,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x21 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 34 (0x22) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: ldnull + IL_0001: call "C.Enumerator Extensions.GetAsyncEnumerator(object)" + IL_0006: stloc.0 + IL_0007: br.s IL_0014 + IL_0009: ldloc.0 + IL_000a: callvirt "int C.Enumerator.Current.get" + IL_000f: call "void System.Console.Write(int)" + IL_0014: ldloc.0 + IL_0015: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001f: brtrue.s IL_0009 + IL_0021: ret + } + """); } [Fact] @@ -7133,6 +11137,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "object..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(object)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -7169,6 +11200,64 @@ public static async IAsyncEnumerator GetAsyncEnumerator(this Range range) parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 95 (0x5f) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + object V_1) + IL_0000: ldc.i4.1 + IL_0001: call "System.Index System.Index.op_Implicit(int)" + IL_0006: ldc.i4.4 + IL_0007: call "System.Index System.Index.op_Implicit(int)" + IL_000c: newobj "System.Range..ctor(System.Index, System.Index)" + IL_0011: call "System.Collections.Generic.IAsyncEnumerator Extensions.GetAsyncEnumerator(System.Range)" + IL_0016: stloc.0 + IL_0017: ldnull + IL_0018: stloc.1 + .try + { + IL_0019: br.s IL_0026 + IL_001b: ldloc.0 + IL_001c: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_0021: call "void System.Console.Write(int)" + IL_0026: ldloc.0 + IL_0027: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_002c: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0031: brtrue.s IL_001b + IL_0033: leave.s IL_0038 + } + catch object + { + IL_0035: stloc.1 + IL_0036: leave.s IL_0038 + } + IL_0038: ldloc.0 + IL_0039: brfalse.s IL_0046 + IL_003b: ldloc.0 + IL_003c: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0041: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0046: ldloc.1 + IL_0047: brfalse.s IL_005e + IL_0049: ldloc.1 + IL_004a: isinst "System.Exception" + IL_004f: dup + IL_0050: brtrue.s IL_0054 + IL_0052: ldloc.1 + IL_0053: throw + IL_0054: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0059: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_005e: ret + } + """); } [Fact] @@ -7201,6 +11290,63 @@ public static async IAsyncEnumerator GetAsyncEnumerator(this (T first, T s var comp = CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x55 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 86 (0x56) + .maxstack 3 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + object V_1) + IL_0000: ldc.i4.1 + IL_0001: ldc.i4.2 + IL_0002: ldc.i4.3 + IL_0003: newobj "System.ValueTuple..ctor(int, int, int)" + IL_0008: call "System.Collections.Generic.IAsyncEnumerator Extensions.GetAsyncEnumerator(System.ValueTuple)" + IL_000d: stloc.0 + IL_000e: ldnull + IL_000f: stloc.1 + .try + { + IL_0010: br.s IL_001d + IL_0012: ldloc.0 + IL_0013: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_0018: call "void System.Console.Write(int)" + IL_001d: ldloc.0 + IL_001e: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0023: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0028: brtrue.s IL_0012 + IL_002a: leave.s IL_002f + } + catch object + { + IL_002c: stloc.1 + IL_002d: leave.s IL_002f + } + IL_002f: ldloc.0 + IL_0030: brfalse.s IL_003d + IL_0032: ldloc.0 + IL_0033: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0038: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_003d: ldloc.1 + IL_003e: brfalse.s IL_0055 + IL_0040: ldloc.1 + IL_0041: isinst "System.Exception" + IL_0046: dup + IL_0047: brtrue.s IL_004b + IL_0049: ldloc.1 + IL_004a: throw + IL_004b: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0050: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0055: ret + } + """); } [Fact] @@ -7236,9 +11382,126 @@ public static class Extensions }"; var comp = CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: @"1.1 + string expectedOutput = @"1.1 2.2 -3.3"); +3.3"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xd5 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 214 (0xd6) + .maxstack 9 + .locals init (System.Collections.Generic.IAsyncEnumerator> V_0, + System.ValueTuple> V_1, + object V_2, + int V_3, //a + decimal V_4, //b + decimal V_5) + IL_0000: ldloca.s V_1 + IL_0002: ldc.i4.3 + IL_0003: newarr "int" + IL_0008: dup + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.1 + IL_000b: stelem.i4 + IL_000c: dup + IL_000d: ldc.i4.1 + IL_000e: ldc.i4.2 + IL_000f: stelem.i4 + IL_0010: dup + IL_0011: ldc.i4.2 + IL_0012: ldc.i4.3 + IL_0013: stelem.i4 + IL_0014: newobj "System.Collections.Generic.List..ctor()" + IL_0019: dup + IL_001a: ldc.i4.1 + IL_001b: ldc.i4.0 + IL_001c: ldc.i4.0 + IL_001d: ldc.i4.0 + IL_001e: ldc.i4.1 + IL_001f: newobj "decimal..ctor(int, int, int, bool, byte)" + IL_0024: callvirt "void System.Collections.Generic.List.Add(decimal)" + IL_0029: dup + IL_002a: ldc.i4.2 + IL_002b: ldc.i4.0 + IL_002c: ldc.i4.0 + IL_002d: ldc.i4.0 + IL_002e: ldc.i4.1 + IL_002f: newobj "decimal..ctor(int, int, int, bool, byte)" + IL_0034: callvirt "void System.Collections.Generic.List.Add(decimal)" + IL_0039: dup + IL_003a: ldc.i4.3 + IL_003b: ldc.i4.0 + IL_003c: ldc.i4.0 + IL_003d: ldc.i4.0 + IL_003e: ldc.i4.1 + IL_003f: newobj "decimal..ctor(int, int, int, bool, byte)" + IL_0044: callvirt "void System.Collections.Generic.List.Add(decimal)" + IL_0049: call "System.ValueTuple>..ctor(int[], System.Collections.Generic.List)" + IL_004e: ldloc.1 + IL_004f: ldfld "int[] System.ValueTuple>.Item1" + IL_0054: ldloc.1 + IL_0055: ldfld "System.Collections.Generic.List System.ValueTuple>.Item2" + IL_005a: newobj "System.ValueTuple, System.Collections.Generic.IEnumerable>..ctor(System.Collections.Generic.IEnumerable, System.Collections.Generic.IEnumerable)" + IL_005f: call "System.Collections.Generic.IAsyncEnumerator> Extensions.GetAsyncEnumerator(System.ValueTuple, System.Collections.Generic.IEnumerable>)" + IL_0064: stloc.0 + IL_0065: ldnull + IL_0066: stloc.2 + .try + { + IL_0067: br.s IL_009d + IL_0069: ldloc.0 + IL_006a: callvirt "System.ValueTuple System.Collections.Generic.IAsyncEnumerator>.Current.get" + IL_006f: dup + IL_0070: ldfld "int System.ValueTuple.Item1" + IL_0075: stloc.3 + IL_0076: ldfld "decimal System.ValueTuple.Item2" + IL_007b: stloc.s V_4 + IL_007d: ldloc.3 + IL_007e: call "decimal decimal.op_Implicit(int)" + IL_0083: ldloc.s V_4 + IL_0085: call "decimal decimal.op_Addition(decimal, decimal)" + IL_008a: stloc.s V_5 + IL_008c: ldloca.s V_5 + IL_008e: call "System.Globalization.CultureInfo System.Globalization.CultureInfo.InvariantCulture.get" + IL_0093: call "string decimal.ToString(System.IFormatProvider)" + IL_0098: call "void System.Console.WriteLine(string)" + IL_009d: ldloc.0 + IL_009e: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator>.MoveNextAsync()" + IL_00a3: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_00a8: brtrue.s IL_0069 + IL_00aa: leave.s IL_00af + } + catch object + { + IL_00ac: stloc.2 + IL_00ad: leave.s IL_00af + } + IL_00af: ldloc.0 + IL_00b0: brfalse.s IL_00bd + IL_00b2: ldloc.0 + IL_00b3: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_00b8: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_00bd: ldloc.2 + IL_00be: brfalse.s IL_00d5 + IL_00c0: ldloc.2 + IL_00c1: isinst "System.Exception" + IL_00c6: dup + IL_00c7: brtrue.s IL_00cb + IL_00c9: ldloc.2 + IL_00ca: throw + IL_00cb: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_00d0: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_00d5: ret + } + """); } [Fact] @@ -7352,6 +11615,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator1 V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator1 C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator1.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator1.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -7439,6 +11729,64 @@ public static class Extensions var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5b } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 92 (0x5c) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0023 + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: call "void System.Console.Write(int)" + IL_0023: ldloc.0 + IL_0024: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_0029: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_002e: brtrue.s IL_0018 + IL_0030: leave.s IL_0035 + } + catch object + { + IL_0032: stloc.2 + IL_0033: leave.s IL_0035 + } + IL_0035: ldloc.0 + IL_0036: brfalse.s IL_0043 + IL_0038: ldloc.0 + IL_0039: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_003e: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0043: ldloc.2 + IL_0044: brfalse.s IL_005b + IL_0046: ldloc.2 + IL_0047: isinst "System.Exception" + IL_004c: dup + IL_004d: brtrue.s IL_0051 + IL_004f: ldloc.2 + IL_0050: throw + IL_0051: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0056: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_005b: ret + } + """); } [Fact] @@ -7629,6 +11977,33 @@ public static class Extensions2 var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions2.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -7749,6 +12124,33 @@ public static class Extensions2 var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions2.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -7825,6 +12227,33 @@ public static class Extensions2 var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions1.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -7896,6 +12325,34 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "23"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("23", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x26 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 39 (0x27) + .maxstack 2 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: ldc.i4.1 + IL_0006: call "C.Enumerator Extensions.GetAsyncEnumerator(C, int)" + IL_000b: stloc.0 + IL_000c: br.s IL_0019 + IL_000e: ldloc.0 + IL_000f: callvirt "int C.Enumerator.Current.get" + IL_0014: call "void System.Console.Write(int)" + IL_0019: ldloc.0 + IL_001a: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001f: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0024: brtrue.s IL_000e + IL_0026: ret + } + """); } [Fact] @@ -7963,6 +12420,35 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x2b } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 44 (0x2c) + .maxstack 2 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: ldc.i4.0 + IL_0006: newarr "int" + IL_000b: call "C.Enumerator Extensions.GetAsyncEnumerator(C, params int[])" + IL_0010: stloc.0 + IL_0011: br.s IL_001e + IL_0013: ldloc.0 + IL_0014: callvirt "int C.Enumerator.Current.get" + IL_0019: call "void System.Console.Write(int)" + IL_001e: ldloc.0 + IL_001f: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0024: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0029: brtrue.s IL_0013 + IL_002b: ret + } + """); } [Fact] @@ -8099,6 +12585,36 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion)); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x2b } + """ + }); + verifier.VerifyIL("C.Main()", $$""" + { + // Code size 44 (0x2c) + .maxstack 2 + .locals init (C.Enumerator V_0, + C V_1) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "C" + IL_0009: call "C.Enumerator Extensions.GetAsyncEnumerator({{modifier}} C)" + IL_000e: stloc.0 + IL_000f: br.s IL_001d + IL_0011: ldloca.s V_0 + IL_0013: call "readonly int C.Enumerator.Current.get" + IL_0018: call "void System.Console.Write(int)" + IL_001d: ldloca.s V_0 + IL_001f: call "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0024: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0029: brtrue.s IL_0011 + IL_002b: ret + } + """); } [Theory] @@ -8132,6 +12648,36 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion)); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x2c } + """ + }); + verifier.VerifyIL("C.Main()", $$""" + { + // Code size 45 (0x2d) + .maxstack 1 + .locals init (C V_0, //c + C.Enumerator V_1) + IL_0000: ldloca.s V_0 + IL_0002: initobj "C" + IL_0008: ldloca.s V_0 + IL_000a: call "C.Enumerator Extensions.GetAsyncEnumerator({{modifier}} C)" + IL_000f: stloc.1 + IL_0010: br.s IL_001e + IL_0012: ldloca.s V_1 + IL_0014: call "readonly int C.Enumerator.Current.get" + IL_0019: call "void System.Console.Write(int)" + IL_001e: ldloca.s V_1 + IL_0020: call "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0025: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002a: brtrue.s IL_0012 + IL_002c: ret + } + """); } [Fact] @@ -8195,6 +12741,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -8225,6 +12798,33 @@ internal static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -8374,6 +12974,33 @@ internal static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -8407,6 +13034,33 @@ internal static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -8440,6 +13094,58 @@ struct Enumerator : IAsyncDisposable var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: @"123Disposed"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123Disposed", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x52 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 83 (0x53) + .maxstack 2 + .locals init (Enumerator V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: call "Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_001b + IL_000f: ldloca.s V_0 + IL_0011: call "readonly int Enumerator.Current.get" + IL_0016: call "void System.Console.Write(int)" + IL_001b: ldloca.s V_0 + IL_001d: call "System.Threading.Tasks.Task Enumerator.MoveNextAsync()" + IL_0022: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0027: brtrue.s IL_000f + IL_0029: leave.s IL_002e + } + catch object + { + IL_002b: stloc.1 + IL_002c: leave.s IL_002e + } + IL_002e: ldloca.s V_0 + IL_0030: call "System.Threading.Tasks.ValueTask Enumerator.DisposeAsync()" + IL_0035: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_003a: ldloc.1 + IL_003b: brfalse.s IL_0052 + IL_003d: ldloc.1 + IL_003e: isinst "System.Exception" + IL_0043: dup + IL_0044: brtrue.s IL_0048 + IL_0046: ldloc.1 + IL_0047: throw + IL_0048: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0052: ret + } + """); } [Fact] @@ -8474,6 +13180,59 @@ struct Enumerator : IAsyncDisposable var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: @"123Disposed"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123Disposed", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x58 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 89 (0x59) + .maxstack 2 + .locals init (Enumerator V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: call "Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_001b + IL_000f: ldloca.s V_0 + IL_0011: call "readonly int Enumerator.Current.get" + IL_0016: call "void System.Console.Write(int)" + IL_001b: ldloca.s V_0 + IL_001d: call "System.Threading.Tasks.Task Enumerator.MoveNextAsync()" + IL_0022: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0027: brtrue.s IL_000f + IL_0029: leave.s IL_002e + } + catch object + { + IL_002b: stloc.1 + IL_002c: leave.s IL_002e + } + IL_002e: ldloca.s V_0 + IL_0030: constrained. "Enumerator" + IL_0036: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_003b: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0040: ldloc.1 + IL_0041: brfalse.s IL_0058 + IL_0043: ldloc.1 + IL_0044: isinst "System.Exception" + IL_0049: dup + IL_004a: brtrue.s IL_004e + IL_004c: ldloc.1 + IL_004d: throw + IL_004e: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0053: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0058: ret + } + """); } [Fact] @@ -8507,6 +13266,58 @@ struct Enumerator var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: @"123Disposed"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123Disposed", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x52 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 83 (0x53) + .maxstack 2 + .locals init (Enumerator V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: call "Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_001b + IL_000f: ldloca.s V_0 + IL_0011: call "readonly int Enumerator.Current.get" + IL_0016: call "void System.Console.Write(int)" + IL_001b: ldloca.s V_0 + IL_001d: call "System.Threading.Tasks.Task Enumerator.MoveNextAsync()" + IL_0022: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0027: brtrue.s IL_000f + IL_0029: leave.s IL_002e + } + catch object + { + IL_002b: stloc.1 + IL_002c: leave.s IL_002e + } + IL_002e: ldloca.s V_0 + IL_0030: call "System.Threading.Tasks.ValueTask Enumerator.DisposeAsync()" + IL_0035: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_003a: ldloc.1 + IL_003b: brfalse.s IL_0052 + IL_003d: ldloc.1 + IL_003e: isinst "System.Exception" + IL_0043: dup + IL_0044: brtrue.s IL_0048 + IL_0046: ldloc.1 + IL_0047: throw + IL_0048: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0052: ret + } + """); } [Fact] @@ -8537,6 +13348,33 @@ public static class Extensions var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.ValueTask C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -8586,6 +13424,60 @@ public static class Extensions Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "foreach").WithArguments("C.Enumerator.Current").WithLocation(8, 15) ); CompileAndVerify(comp, expectedOutput: "123Disposed"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123Disposed", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x52 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 83 (0x53) + .maxstack 2 + .locals init (C.Enumerator V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_001a + IL_000f: ldloc.0 + IL_0010: callvirt "int C.Enumerator.Current.get" + IL_0015: call "void System.Console.Write(int)" + IL_001a: ldloc.0 + IL_001b: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0020: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: brtrue.s IL_000f + IL_0027: leave.s IL_002c + } + catch object + { + IL_0029: stloc.1 + IL_002a: leave.s IL_002c + } + IL_002c: ldloc.0 + IL_002d: brfalse.s IL_003a + IL_002f: ldloc.0 + IL_0030: callvirt "System.Threading.Tasks.Task C.Enumerator.DisposeAsync()" + IL_0035: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003a: ldloc.1 + IL_003b: brfalse.s IL_0052 + IL_003d: ldloc.1 + IL_003e: isinst "System.Exception" + IL_0043: dup + IL_0044: brtrue.s IL_0048 + IL_0046: ldloc.1 + IL_0047: throw + IL_0048: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0052: ret + } + """); } [Fact] @@ -8620,6 +13512,33 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator N.Extensions.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -8738,6 +13657,33 @@ public sealed class Enumerator var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("N1.N2.N3.C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (N1.N2.N3.C.Enumerator V_0) + IL_0000: newobj "N1.N2.N3.C..ctor()" + IL_0005: call "N1.N2.N3.C.Enumerator N1.N2.Extensions.GetAsyncEnumerator(N1.N2.N3.C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int N1.N2.N3.C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task N1.N2.N3.C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -8790,6 +13736,33 @@ public static class Extensions // using N3; Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using N3;").WithLocation(5, 1)); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("N1.C.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (N1.C.Enumerator V_0) + IL_0000: newobj "N1.C..ctor()" + IL_0005: call "N1.C.Enumerator N2.Extensions.GetAsyncEnumerator(N1.C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int N1.C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task N1.C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -8932,6 +13905,33 @@ public sealed class Enumerator var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("Program.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Program.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -8967,6 +13967,33 @@ public sealed class Enumerator var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x25 } + """ + }); + verifier.VerifyIL("Program.Inner.Main()", """ + { + // Code size 38 (0x26) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.Enumerator Program.GetAsyncEnumerator(C)" + IL_000a: stloc.0 + IL_000b: br.s IL_0018 + IL_000d: ldloc.0 + IL_000e: callvirt "int C.Enumerator.Current.get" + IL_0013: call "void System.Console.Write(int)" + IL_0018: ldloc.0 + IL_0019: callvirt "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_001e: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: brtrue.s IL_000d + IL_0025: ret + } + """); } [Fact] @@ -9037,6 +14064,46 @@ public static class Extensions var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "123123"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("123123", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x58 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 89 (0x59) + .maxstack 1 + .locals init (C.Enumerator V_0) + IL_0000: newobj "C..ctor()" + IL_0005: call "ref C.Enumerator Extensions.GetAsyncEnumerator(C)" + IL_000a: ldobj "C.Enumerator" + IL_000f: stloc.0 + IL_0010: br.s IL_001e + IL_0012: ldloca.s V_0 + IL_0014: call "readonly int C.Enumerator.Current.get" + IL_0019: call "void System.Console.Write(int)" + IL_001e: ldloca.s V_0 + IL_0020: call "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0025: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002a: brtrue.s IL_0012 + IL_002c: newobj "C..ctor()" + IL_0031: call "ref C.Enumerator Extensions.GetAsyncEnumerator(C)" + IL_0036: ldobj "C.Enumerator" + IL_003b: stloc.0 + IL_003c: br.s IL_004a + IL_003e: ldloca.s V_0 + IL_0040: call "readonly int C.Enumerator.Current.get" + IL_0045: call "void System.Console.Write(int)" + IL_004a: ldloca.s V_0 + IL_004c: call "System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()" + IL_0051: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0056: brtrue.s IL_003e + IL_0058: ret + } + """); } [Theory, WorkItem(59955, "https://github.com/dotnet/roslyn/issues/59955")] @@ -9092,6 +14159,67 @@ public async ValueTask DisposeAsync() { comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "RAN"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("RAN", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5b } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = value 'System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 92 (0x5c) + .maxstack 2 + .locals init (AsyncEnumerator V_0, + AsyncEnumerable V_1, + System.Threading.CancellationToken V_2, + object V_3) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "AsyncEnumerable" + IL_0009: ldloca.s V_2 + IL_000b: initobj "System.Threading.CancellationToken" + IL_0011: ldloc.2 + IL_0012: call "AsyncEnumerator AsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0017: stloc.0 + IL_0018: ldnull + IL_0019: stloc.3 + .try + { + IL_001a: br.s IL_0024 + IL_001c: ldloca.s V_0 + IL_001e: call "int AsyncEnumerator.Current.get" + IL_0023: pop + IL_0024: ldloca.s V_0 + IL_0026: call "System.Threading.Tasks.ValueTask AsyncEnumerator.MoveNextAsync()" + IL_002b: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0030: brtrue.s IL_001c + IL_0032: leave.s IL_0037 + } + catch object + { + IL_0034: stloc.3 + IL_0035: leave.s IL_0037 + } + IL_0037: ldloca.s V_0 + IL_0039: call "System.Threading.Tasks.ValueTask AsyncEnumerator.DisposeAsync()" + IL_003e: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0043: ldloc.3 + IL_0044: brfalse.s IL_005b + IL_0046: ldloc.3 + IL_0047: isinst "System.Exception" + IL_004c: dup + IL_004d: brtrue.s IL_0051 + IL_004f: ldloc.3 + IL_0050: throw + IL_0051: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0056: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_005b: ret + } + """); } else { @@ -9168,6 +14296,67 @@ public async ValueTask DisposeAsync() { comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "RAN"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("RAN", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5b } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = value 'System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 92 (0x5c) + .maxstack 2 + .locals init (AsyncEnumerator V_0, + AsyncEnumerable V_1, + System.Threading.CancellationToken V_2, + object V_3) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "AsyncEnumerable" + IL_0009: ldloca.s V_2 + IL_000b: initobj "System.Threading.CancellationToken" + IL_0011: ldloc.2 + IL_0012: call "AsyncEnumerator AsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0017: stloc.0 + IL_0018: ldnull + IL_0019: stloc.3 + .try + { + IL_001a: br.s IL_0024 + IL_001c: ldloca.s V_0 + IL_001e: call "int AsyncEnumerator.Current.get" + IL_0023: pop + IL_0024: ldloca.s V_0 + IL_0026: call "System.Threading.Tasks.ValueTask AsyncEnumerator.MoveNextAsync()" + IL_002b: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0030: brtrue.s IL_001c + IL_0032: leave.s IL_0037 + } + catch object + { + IL_0034: stloc.3 + IL_0035: leave.s IL_0037 + } + IL_0037: ldloca.s V_0 + IL_0039: call "System.Threading.Tasks.ValueTask AsyncEnumerator.DisposeAsync()" + IL_003e: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0043: ldloc.3 + IL_0044: brfalse.s IL_005b + IL_0046: ldloc.3 + IL_0047: isinst "System.Exception" + IL_004c: dup + IL_004d: brtrue.s IL_0051 + IL_004f: ldloc.3 + IL_0050: throw + IL_0051: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0056: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_005b: ret + } + """); } else { @@ -9246,6 +14435,70 @@ async ValueTask IAsyncDisposable.DisposeAsync() { comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "RAN"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("RAN", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x61 } + [System.Collections.Generic.IAsyncEnumerator.MoveNextAsync]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = value 'System.Threading.Tasks.ValueTask`1' } + [System.IAsyncDisposable.DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 98 (0x62) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + AsyncEnumerable V_1, + System.Threading.CancellationToken V_2, + object V_3) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "AsyncEnumerable" + IL_0009: ldloca.s V_2 + IL_000b: initobj "System.Threading.CancellationToken" + IL_0011: ldloc.2 + IL_0012: constrained. "AsyncEnumerable" + IL_0018: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_001d: stloc.0 + IL_001e: ldnull + IL_001f: stloc.3 + .try + { + IL_0020: br.s IL_0029 + IL_0022: ldloc.0 + IL_0023: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_0028: pop + IL_0029: ldloc.0 + IL_002a: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_002f: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0034: brtrue.s IL_0022 + IL_0036: leave.s IL_003b + } + catch object + { + IL_0038: stloc.3 + IL_0039: leave.s IL_003b + } + IL_003b: ldloc.0 + IL_003c: brfalse.s IL_0049 + IL_003e: ldloc.0 + IL_003f: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0044: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0049: ldloc.3 + IL_004a: brfalse.s IL_0061 + IL_004c: ldloc.3 + IL_004d: isinst "System.Exception" + IL_0052: dup + IL_0053: brtrue.s IL_0057 + IL_0055: ldloc.3 + IL_0056: throw + IL_0057: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_005c: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0061: ret + } + """); } else { @@ -9378,6 +14631,67 @@ public async ValueTask DisposeAsync() { comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "RAN"); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("RAN", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5b } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x25, Found = Int32, Expected = value 'System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 92 (0x5c) + .maxstack 2 + .locals init (AsyncEnumerator V_0, + AsyncEnumerable V_1, + System.Threading.CancellationToken V_2, + object V_3) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "AsyncEnumerable" + IL_0009: ldloca.s V_2 + IL_000b: initobj "System.Threading.CancellationToken" + IL_0011: ldloc.2 + IL_0012: call "AsyncEnumerator AsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0017: stloc.0 + IL_0018: ldnull + IL_0019: stloc.3 + .try + { + IL_001a: br.s IL_0024 + IL_001c: ldloca.s V_0 + IL_001e: call "System.ValueTuple AsyncEnumerator.Current.get" + IL_0023: pop + IL_0024: ldloca.s V_0 + IL_0026: call "System.Threading.Tasks.ValueTask AsyncEnumerator.MoveNextAsync()" + IL_002b: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0030: brtrue.s IL_001c + IL_0032: leave.s IL_0037 + } + catch object + { + IL_0034: stloc.3 + IL_0035: leave.s IL_0037 + } + IL_0037: ldloca.s V_0 + IL_0039: call "System.Threading.Tasks.ValueTask AsyncEnumerator.DisposeAsync()" + IL_003e: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0043: ldloc.3 + IL_0044: brfalse.s IL_005b + IL_0046: ldloc.3 + IL_0047: isinst "System.Exception" + IL_004c: dup + IL_004d: brtrue.s IL_0051 + IL_004f: ldloc.3 + IL_0050: throw + IL_0051: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0056: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_005b: ret + } + """); } else { @@ -9479,9 +14793,77 @@ static async Task Test() } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + var expectedOutput = ExecutionConditionUtil.IsMonoOrCoreClr ? "D" : null; CompileAndVerify(comp, - expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "D" : null, + expectedOutput: expectedOutput, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics(); + + var runtimeAsyncComp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(src); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xa } + [Test]: Return value missing on the stack. { Offset = 0x7f } + """ + }); + verifier.VerifyIL("C.Test()", """ + { + // Code size 128 (0x80) + .maxstack 2 + .locals init (TEnumerator V_0, + TEnumerable V_1, + System.Threading.CancellationToken V_2, + object V_3) + IL_0000: ldloca.s V_1 + IL_0002: dup + IL_0003: initobj "TEnumerable" + IL_0009: ldloca.s V_2 + IL_000b: initobj "System.Threading.CancellationToken" + IL_0011: ldloc.2 + IL_0012: constrained. "TEnumerable" + IL_0018: callvirt "TEnumerator IGetEnumerator.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_001d: stloc.0 + IL_001e: ldnull + IL_001f: stloc.3 + .try + { + IL_0020: br.s IL_0034 + IL_0022: ldloca.s V_0 + IL_0024: constrained. "TEnumerator" + IL_002a: callvirt "int ICustomEnumerator.Current.get" + IL_002f: call "void System.Console.Write(int)" + IL_0034: ldloca.s V_0 + IL_0036: constrained. "TEnumerator" + IL_003c: callvirt "System.Threading.Tasks.ValueTask ICustomEnumerator.MoveNextAsync()" + IL_0041: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0046: brtrue.s IL_0022 + IL_0048: leave.s IL_004d + } + catch object + { + IL_004a: stloc.3 + IL_004b: leave.s IL_004d + } + IL_004d: ldloc.0 + IL_004e: box "TEnumerator" + IL_0053: brfalse.s IL_0067 + IL_0055: ldloca.s V_0 + IL_0057: constrained. "TEnumerator" + IL_005d: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0062: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0067: ldloc.3 + IL_0068: brfalse.s IL_007f + IL_006a: ldloc.3 + IL_006b: isinst "System.Exception" + IL_0070: dup + IL_0071: brtrue.s IL_0075 + IL_0073: ldloc.3 + IL_0074: throw + IL_0075: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_007a: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_007f: ret + } + """); } } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs index 39bf18e7fc8e2..e3920e372dfa0 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs @@ -212,7 +212,60 @@ public async ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "C body DisposeAsync1 DisposeAsync2 end"); + string expectedOutput = "C body DisposeAsync1 DisposeAsync2 end"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x29 } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + [
b__1_0]: Return value missing on the stack. { Offset = 0x47 } + """ + }); + verifier.VerifyIL("C.<>c.
b__1_0()", """ + { + // Code size 72 (0x48) + .maxstack 2 + .locals init (C V_0, //y + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: ldstr "body " + IL_000d: call "void System.Console.Write(object)" + IL_0012: leave.s IL_0017 + } + catch object + { + IL_0014: stloc.1 + IL_0015: leave.s IL_0017 + } + IL_0017: ldloc.0 + IL_0018: brfalse.s IL_0025 + IL_001a: ldloc.0 + IL_001b: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0020: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0025: ldloc.1 + IL_0026: brfalse.s IL_003d + IL_0028: ldloc.1 + IL_0029: isinst "System.Exception" + IL_002e: dup + IL_002f: brtrue.s IL_0033 + IL_0031: ldloc.1 + IL_0032: throw + IL_0033: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0038: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_003d: ldstr "end" + IL_0042: call "void System.Console.Write(object)" + IL_0047: ret + } + """); } [Fact] @@ -400,7 +453,79 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "try using dispose_start dispose_end end"); + string expectedOutput = "try using dispose_start dispose_end end"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x62 } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 99 (0x63) + .maxstack 2 + .locals init (int V_0, + C V_1, //x + object V_2) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldstr "try " + IL_0007: call "void System.Console.Write(object)" + IL_000c: newobj "System.ArgumentNullException..ctor()" + IL_0011: throw + } + catch System.ArgumentNullException + { + IL_0012: pop + IL_0013: ldc.i4.1 + IL_0014: stloc.0 + IL_0015: leave.s IL_0017 + } + IL_0017: ldloc.0 + IL_0018: ldc.i4.1 + IL_0019: bne.un.s IL_0062 + IL_001b: newobj "C..ctor()" + IL_0020: stloc.1 + IL_0021: ldnull + IL_0022: stloc.2 + .try + { + IL_0023: ldstr "using " + IL_0028: call "void System.Console.Write(object)" + IL_002d: leave.s IL_0032 + } + catch object + { + IL_002f: stloc.2 + IL_0030: leave.s IL_0032 + } + IL_0032: ldloc.1 + IL_0033: brfalse.s IL_0040 + IL_0035: ldloc.1 + IL_0036: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_003b: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0040: ldloc.2 + IL_0041: brfalse.s IL_0058 + IL_0043: ldloc.2 + IL_0044: isinst "System.Exception" + IL_0049: dup + IL_004a: brtrue.s IL_004e + IL_004c: ldloc.2 + IL_004d: throw + IL_004e: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0053: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0058: ldstr "end" + IL_005d: call "void System.Console.Write(object)" + IL_0062: ret + } + """); } [Fact] @@ -513,9 +638,84 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() } } "; + string expectedOutput = "using dispose_start dispose_end return"; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "using dispose_start dispose_end return"); + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x67, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 104 (0x68) + .maxstack 2 + .locals init (object V_0, + C V_1, //x + object V_2) + IL_0000: ldnull + IL_0001: stloc.0 + .try + { + IL_0002: leave.s IL_0007 + } + catch object + { + IL_0004: stloc.0 + IL_0005: leave.s IL_0007 + } + IL_0007: newobj "C..ctor()" + IL_000c: stloc.1 + IL_000d: ldnull + IL_000e: stloc.2 + .try + { + IL_000f: ldstr "using " + IL_0014: call "void System.Console.Write(object)" + IL_0019: leave.s IL_001e + } + catch object + { + IL_001b: stloc.2 + IL_001c: leave.s IL_001e + } + IL_001e: ldloc.1 + IL_001f: brfalse.s IL_002c + IL_0021: ldloc.1 + IL_0022: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0027: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_002c: ldloc.2 + IL_002d: brfalse.s IL_0044 + IL_002f: ldloc.2 + IL_0030: isinst "System.Exception" + IL_0035: dup + IL_0036: brtrue.s IL_003a + IL_0038: ldloc.2 + IL_0039: throw + IL_003a: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_003f: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0044: ldloc.0 + IL_0045: brfalse.s IL_005c + IL_0047: ldloc.0 + IL_0048: isinst "System.Exception" + IL_004d: dup + IL_004e: brtrue.s IL_0052 + IL_0050: ldloc.0 + IL_0051: throw + IL_0052: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0057: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_005c: ldstr "return" + IL_0061: call "void System.Console.Write(object)" + IL_0066: ldc.i4.1 + IL_0067: ret + } + """); } [Fact] @@ -551,7 +751,74 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "using caught message"); + string expectedOutput = "using caught message"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x61 } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x3c } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 98 (0x62) + .maxstack 2 + .locals init (C V_0, //x + object V_1, + System.Exception V_2) //e + .try + { + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: ldstr "using " + IL_000d: call "void System.Console.Write(object)" + IL_0012: leave.s IL_0017 + } + catch object + { + IL_0014: stloc.1 + IL_0015: leave.s IL_0017 + } + IL_0017: ldloc.0 + IL_0018: brfalse.s IL_0025 + IL_001a: ldloc.0 + IL_001b: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0020: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0025: ldloc.1 + IL_0026: brfalse.s IL_003d + IL_0028: ldloc.1 + IL_0029: isinst "System.Exception" + IL_002e: dup + IL_002f: brtrue.s IL_0033 + IL_0031: ldloc.1 + IL_0032: throw + IL_0033: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0038: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_003d: leave.s IL_0057 + } + catch System.Exception + { + IL_003f: stloc.2 + IL_0040: ldstr "caught " + IL_0045: ldloc.2 + IL_0046: callvirt "string System.Exception.Message.get" + IL_004b: call "string string.Concat(string, string)" + IL_0050: call "void System.Console.Write(object)" + IL_0055: leave.s IL_0061 + } + IL_0057: ldstr "SKIPPED" + IL_005c: call "void System.Console.Write(object)" + IL_0061: ret + } + """); } [Fact] @@ -578,7 +845,65 @@ static async Task Main() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "before after"); + string expectedOutput = "before after"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x58, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 89 (0x59) + .maxstack 2 + .locals init (object V_0, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1, + System.Runtime.CompilerServices.YieldAwaitable V_2) + IL_0000: ldnull + IL_0001: stloc.0 + .try + { + IL_0002: leave.s IL_0007 + } + catch object + { + IL_0004: stloc.0 + IL_0005: leave.s IL_0007 + } + IL_0007: ldstr "before " + IL_000c: call "void System.Console.Write(object)" + IL_0011: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0016: stloc.2 + IL_0017: ldloca.s V_2 + IL_0019: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_001e: stloc.1 + IL_001f: ldloca.s V_1 + IL_0021: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0026: brtrue.s IL_002e + IL_0028: ldloc.1 + IL_0029: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_002e: ldloca.s V_1 + IL_0030: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_0035: ldstr "after" + IL_003a: call "void System.Console.Write(object)" + IL_003f: ldloc.0 + IL_0040: brfalse.s IL_0057 + IL_0042: ldloc.0 + IL_0043: isinst "System.Exception" + IL_0048: dup + IL_0049: brtrue.s IL_004d + IL_004b: ldloc.0 + IL_004c: throw + IL_004d: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0052: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0057: ldc.i4.1 + IL_0058: ret + } + """); } [Fact] @@ -908,7 +1233,70 @@ public void Dispose() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); + string expectedOutput = "body DisposeAsync"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x48, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 75 (0x4b) + .maxstack 2 + .locals init (C V_0, //x + object V_1, + int V_2, + int V_3) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + IL_0008: ldc.i4.0 + IL_0009: stloc.2 + .try + { + IL_000a: ldstr "body " + IL_000f: call "void System.Console.Write(object)" + IL_0014: ldc.i4.1 + IL_0015: stloc.3 + IL_0016: ldc.i4.1 + IL_0017: stloc.2 + IL_0018: leave.s IL_001d + } + catch object + { + IL_001a: stloc.1 + IL_001b: leave.s IL_001d + } + IL_001d: ldloc.0 + IL_001e: brfalse.s IL_002b + IL_0020: ldloc.0 + IL_0021: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0026: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_002b: ldloc.1 + IL_002c: brfalse.s IL_0043 + IL_002e: ldloc.1 + IL_002f: isinst "System.Exception" + IL_0034: dup + IL_0035: brtrue.s IL_0039 + IL_0037: ldloc.1 + IL_0038: throw + IL_0039: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_003e: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0043: ldloc.2 + IL_0044: ldc.i4.1 + IL_0045: bne.un.s IL_0049 + IL_0047: ldloc.3 + IL_0048: ret + IL_0049: ldnull + IL_004a: throw + } + """); } [Fact] @@ -1065,7 +1453,24 @@ System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe, references: new[] { CSharpRef }); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "body DisposeAsync end"); + var expectedOutput = "body DisposeAsync end"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + // PROTOTYPE: Test dynamic + /*comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x29 } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // TODO: Add expected IL here after running test + } + """);*/ } [Fact] @@ -1092,7 +1497,24 @@ public System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe, references: new[] { CSharpRef }); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "body DisposeAsync end"); + string expectedOutput = "body DisposeAsync end"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + // PROTOTYPE: Test dynamic + /*comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x29 } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // TODO: Add expected IL here after running test + } + """);*/ } [Fact] @@ -1118,7 +1540,8 @@ System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); + string expectedOutput = "body DisposeAsync"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { // Code size 306 (0x132) @@ -1243,7 +1666,8 @@ .locals init (int V_0, IL_00fa: ldarg.0 IL_00fb: ldnull IL_00fc: stfld ""C C.
d__0.<>s__1"" - IL_0101: leave.s IL_011d + IL_0101: ldnull + IL_0102: throw } catch System.Exception { @@ -1267,6 +1691,64 @@ .locals init (int V_0, IL_0130: nop IL_0131: ret }"); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x45 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 72 (0x48) + .maxstack 2 + .locals init (C V_0, + object V_1, + int V_2) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + IL_0008: ldc.i4.0 + IL_0009: stloc.2 + .try + { + IL_000a: ldstr "body " + IL_000f: call "void System.Console.Write(object)" + IL_0014: ldc.i4.1 + IL_0015: stloc.2 + IL_0016: leave.s IL_001b + } + catch object + { + IL_0018: stloc.1 + IL_0019: leave.s IL_001b + } + IL_001b: ldloc.0 + IL_001c: brfalse.s IL_0029 + IL_001e: ldloc.0 + IL_001f: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0024: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0029: ldloc.1 + IL_002a: brfalse.s IL_0041 + IL_002c: ldloc.1 + IL_002d: isinst "System.Exception" + IL_0032: dup + IL_0033: brtrue.s IL_0037 + IL_0035: ldloc.1 + IL_0036: throw + IL_0037: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_003c: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0041: ldloc.2 + IL_0042: ldc.i4.1 + IL_0043: bne.un.s IL_0046 + IL_0045: ret + IL_0046: ldnull + IL_0047: throw + } + """); } [Fact] @@ -1292,7 +1774,8 @@ public System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); + string expectedOutput = "body DisposeAsync"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { // Code size 306 (0x132) @@ -1417,7 +1900,8 @@ .locals init (int V_0, IL_00fa: ldarg.0 IL_00fb: ldnull IL_00fc: stfld ""C C.
d__0.<>s__1"" - IL_0101: leave.s IL_011d + IL_0101: ldnull + IL_0102: throw } catch System.Exception { @@ -1442,6 +1926,64 @@ .locals init (int V_0, IL_0131: ret } "); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x45 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 72 (0x48) + .maxstack 2 + .locals init (C V_0, + object V_1, + int V_2) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + IL_0008: ldc.i4.0 + IL_0009: stloc.2 + .try + { + IL_000a: ldstr "body " + IL_000f: call "void System.Console.Write(object)" + IL_0014: ldc.i4.1 + IL_0015: stloc.2 + IL_0016: leave.s IL_001b + } + catch object + { + IL_0018: stloc.1 + IL_0019: leave.s IL_001b + } + IL_001b: ldloc.0 + IL_001c: brfalse.s IL_0029 + IL_001e: ldloc.0 + IL_001f: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0024: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0029: ldloc.1 + IL_002a: brfalse.s IL_0041 + IL_002c: ldloc.1 + IL_002d: isinst "System.Exception" + IL_0032: dup + IL_0033: brtrue.s IL_0037 + IL_0035: ldloc.1 + IL_0036: throw + IL_0037: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_003c: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0041: ldloc.2 + IL_0042: ldc.i4.1 + IL_0043: bne.un.s IL_0046 + IL_0045: ret + IL_0046: ldnull + IL_0047: throw + } + """); } [Fact] @@ -1467,7 +2009,9 @@ public System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); + string expectedOutput = "body DisposeAsync"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); + verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { // Code size 306 (0x132) @@ -1592,7 +2136,8 @@ .locals init (int V_0, IL_00fa: ldarg.0 IL_00fb: ldnull IL_00fc: stfld ""C C.
d__0.<>s__1"" - IL_0101: leave.s IL_011d + IL_0101: ldnull + IL_0102: throw } catch System.Exception { @@ -1617,6 +2162,64 @@ .locals init (int V_0, IL_0131: ret } "); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x45 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 72 (0x48) + .maxstack 2 + .locals init (C V_0, + object V_1, + int V_2) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + IL_0008: ldc.i4.0 + IL_0009: stloc.2 + .try + { + IL_000a: ldstr "body " + IL_000f: call "void System.Console.Write(object)" + IL_0014: ldc.i4.1 + IL_0015: stloc.2 + IL_0016: leave.s IL_001b + } + catch object + { + IL_0018: stloc.1 + IL_0019: leave.s IL_001b + } + IL_001b: ldloc.0 + IL_001c: brfalse.s IL_0029 + IL_001e: ldloc.0 + IL_001f: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0024: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0029: ldloc.1 + IL_002a: brfalse.s IL_0041 + IL_002c: ldloc.1 + IL_002d: isinst "System.Exception" + IL_0032: dup + IL_0033: brtrue.s IL_0037 + IL_0035: ldloc.1 + IL_0036: throw + IL_0037: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_003c: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0041: ldloc.2 + IL_0042: ldc.i4.1 + IL_0043: bne.un.s IL_0046 + IL_0045: ret + IL_0046: ldnull + IL_0047: throw + } + """); } [Fact] @@ -1637,7 +2240,26 @@ public static async System.Threading.Tasks.Task Main() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "body"); + string expectedOutput = "body"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xa } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 11 (0xb) + .maxstack 1 + IL_0000: ldstr "body" + IL_0005: call "void System.Console.Write(object)" + IL_000a: ret + } + """); } [Fact] @@ -1686,7 +2308,24 @@ public System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe, references: new[] { CSharpRef }); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); + string expectedOutput = "body DisposeAsync"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + // PROTOTYPE: Test runtime async with dynamic + /*comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x29 } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // TODO: Add expected IL here after running test + } + """);*/ } [Fact] @@ -1712,7 +2351,8 @@ System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); + string expectedOutput = "body DisposeAsync"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); verifier.VerifyIL("S.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { // Code size 298 (0x12a) @@ -1832,7 +2472,8 @@ .locals init (int V_0, IL_00f2: ldarg.0 IL_00f3: ldnull IL_00f4: stfld ""object S.
d__0.<>s__2"" - IL_00f9: leave.s IL_0115 + IL_00f9: ldnull + IL_00fa: throw } catch System.Exception { @@ -1856,6 +2497,63 @@ .locals init (int V_0, IL_0128: nop IL_0129: ret }"); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x4b } + """ + }); + verifier.VerifyIL("S.Main()", """ + { + // Code size 78 (0x4e) + .maxstack 2 + .locals init (S V_0, + object V_1, + int V_2) + IL_0000: ldloca.s V_0 + IL_0002: initobj "S" + IL_0008: ldnull + IL_0009: stloc.1 + IL_000a: ldc.i4.0 + IL_000b: stloc.2 + .try + { + IL_000c: ldstr "body " + IL_0011: call "void System.Console.Write(object)" + IL_0016: ldc.i4.1 + IL_0017: stloc.2 + IL_0018: leave.s IL_001d + } + catch object + { + IL_001a: stloc.1 + IL_001b: leave.s IL_001d + } + IL_001d: ldloca.s V_0 + IL_001f: constrained. "S" + IL_0025: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_002a: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_002f: ldloc.1 + IL_0030: brfalse.s IL_0047 + IL_0032: ldloc.1 + IL_0033: isinst "System.Exception" + IL_0038: dup + IL_0039: brtrue.s IL_003d + IL_003b: ldloc.1 + IL_003c: throw + IL_003d: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0042: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0047: ldloc.2 + IL_0048: ldc.i4.1 + IL_0049: bne.un.s IL_004c + IL_004b: ret + IL_004c: ldnull + IL_004d: throw + } + """); } [Fact] @@ -1881,7 +2579,8 @@ public System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); + string expectedOutput = "body DisposeAsync"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); verifier.VerifyIL("S.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { // Code size 292 (0x124) @@ -2000,7 +2699,8 @@ .locals init (int V_0, IL_00ec: ldarg.0 IL_00ed: ldnull IL_00ee: stfld ""object S.
d__0.<>s__2"" - IL_00f3: leave.s IL_010f + IL_00f3: ldnull + IL_00f4: throw } catch System.Exception { @@ -2024,6 +2724,62 @@ .locals init (int V_0, IL_0122: nop IL_0123: ret }"); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x45 } + """ + }); + verifier.VerifyIL("S.Main()", """ + { + // Code size 72 (0x48) + .maxstack 2 + .locals init (S V_0, + object V_1, + int V_2) + IL_0000: ldloca.s V_0 + IL_0002: initobj "S" + IL_0008: ldnull + IL_0009: stloc.1 + IL_000a: ldc.i4.0 + IL_000b: stloc.2 + .try + { + IL_000c: ldstr "body " + IL_0011: call "void System.Console.Write(object)" + IL_0016: ldc.i4.1 + IL_0017: stloc.2 + IL_0018: leave.s IL_001d + } + catch object + { + IL_001a: stloc.1 + IL_001b: leave.s IL_001d + } + IL_001d: ldloca.s V_0 + IL_001f: call "System.Threading.Tasks.ValueTask S.DisposeAsync()" + IL_0024: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0029: ldloc.1 + IL_002a: brfalse.s IL_0041 + IL_002c: ldloc.1 + IL_002d: isinst "System.Exception" + IL_0032: dup + IL_0033: brtrue.s IL_0037 + IL_0035: ldloc.1 + IL_0036: throw + IL_0037: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_003c: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0041: ldloc.2 + IL_0042: ldc.i4.1 + IL_0043: bne.un.s IL_0046 + IL_0045: ret + IL_0046: ldnull + IL_0047: throw + } + """); } [Fact] @@ -2060,6 +2816,60 @@ ValueTask IAsyncDisposable.DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "True"); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("True", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x4f } + """ + }); + verifier.VerifyIL("S.Main()", """ + { + // Code size 80 (0x50) + .maxstack 2 + .locals init (S V_0, //s + S V_1, + object V_2) + IL_0000: ldloca.s V_0 + IL_0002: newobj "C..ctor()" + IL_0007: call "S..ctor(C)" + IL_000c: ldloc.0 + IL_000d: stloc.1 + IL_000e: ldnull + IL_000f: stloc.2 + .try + { + IL_0010: leave.s IL_0015 + } + catch object + { + IL_0012: stloc.2 + IL_0013: leave.s IL_0015 + } + IL_0015: ldloca.s V_1 + IL_0017: constrained. "S" + IL_001d: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0022: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0027: ldloc.2 + IL_0028: brfalse.s IL_003f + IL_002a: ldloc.2 + IL_002b: isinst "System.Exception" + IL_0030: dup + IL_0031: brtrue.s IL_0035 + IL_0033: ldloc.2 + IL_0034: throw + IL_0035: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_003a: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_003f: ldloc.0 + IL_0040: ldfld "C S._c" + IL_0045: ldfld "bool C._disposed" + IL_004a: call "void System.Console.WriteLine(bool)" + IL_004f: ret + } + """); } [Fact] @@ -2087,6 +2897,74 @@ public System.Threading.Tasks.ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "body DisposeAsync"); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("body DisposeAsync", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x63 } + """ + }); + verifier.VerifyIL("S.Main", """ + { + // Code size 102 (0x66) + .maxstack 2 + .locals init (S V_0, + S? V_1, + object V_2, + int V_3) + IL_0000: ldloca.s V_0 + IL_0002: initobj "S" + IL_0008: ldloc.0 + IL_0009: newobj "S?..ctor(S)" + IL_000e: stloc.1 + IL_000f: ldnull + IL_0010: stloc.2 + IL_0011: ldc.i4.0 + IL_0012: stloc.3 + .try + { + IL_0013: ldstr "body " + IL_0018: call "void System.Console.Write(object)" + IL_001d: ldc.i4.1 + IL_001e: stloc.3 + IL_001f: leave.s IL_0024 + } + catch object + { + IL_0021: stloc.2 + IL_0022: leave.s IL_0024 + } + IL_0024: ldloca.s V_1 + IL_0026: call "bool S?.HasValue.get" + IL_002b: brfalse.s IL_0047 + IL_002d: ldloca.s V_1 + IL_002f: call "S S?.GetValueOrDefault()" + IL_0034: stloc.0 + IL_0035: ldloca.s V_0 + IL_0037: constrained. "S" + IL_003d: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_0042: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0047: ldloc.2 + IL_0048: brfalse.s IL_005f + IL_004a: ldloc.2 + IL_004b: isinst "System.Exception" + IL_0050: dup + IL_0051: brtrue.s IL_0055 + IL_0053: ldloc.2 + IL_0054: throw + IL_0055: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_005a: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_005f: ldloc.3 + IL_0060: ldc.i4.1 + IL_0061: bne.un.s IL_0064 + IL_0063: ret + IL_0064: ldnull + IL_0065: throw + } + """); } [Fact] @@ -2114,6 +2992,73 @@ public System.Threading.Tasks.ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "body"); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("body", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x5e } + """ + }); + verifier.VerifyIL("S.Main", """ + { + // Code size 97 (0x61) + .maxstack 2 + .locals init (S? V_0, + object V_1, + int V_2, + S V_3) + IL_0000: ldloca.s V_0 + IL_0002: initobj "S?" + IL_0008: ldloc.0 + IL_0009: stloc.0 + IL_000a: ldnull + IL_000b: stloc.1 + IL_000c: ldc.i4.0 + IL_000d: stloc.2 + .try + { + IL_000e: ldstr "body" + IL_0013: call "void System.Console.Write(object)" + IL_0018: ldc.i4.1 + IL_0019: stloc.2 + IL_001a: leave.s IL_001f + } + catch object + { + IL_001c: stloc.1 + IL_001d: leave.s IL_001f + } + IL_001f: ldloca.s V_0 + IL_0021: call "bool S?.HasValue.get" + IL_0026: brfalse.s IL_0042 + IL_0028: ldloca.s V_0 + IL_002a: call "S S?.GetValueOrDefault()" + IL_002f: stloc.3 + IL_0030: ldloca.s V_3 + IL_0032: constrained. "S" + IL_0038: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_003d: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0042: ldloc.1 + IL_0043: brfalse.s IL_005a + IL_0045: ldloc.1 + IL_0046: isinst "System.Exception" + IL_004b: dup + IL_004c: brtrue.s IL_0050 + IL_004e: ldloc.1 + IL_004f: throw + IL_0050: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0055: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_005a: ldloc.2 + IL_005b: ldc.i4.1 + IL_005c: bne.un.s IL_005f + IL_005e: ret + IL_005f: ldnull + IL_0060: throw + } + """); } [Fact] @@ -2185,7 +3130,108 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "ctor1 ctor2 body dispose2_start dispose2_end dispose1_start dispose1_end"); + string expectedOutput = "ctor1 ctor2 body dispose2_start dispose2_end dispose1_start dispose1_end"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x8c } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x6a } + """ + }); + verifier.VerifyIL("S.Main()", """ + { + // Code size 143 (0x8f) + .maxstack 2 + .locals init (S V_0, //s1 + S V_1, //s2 + object V_2, + int V_3, + object V_4, + int V_5) + IL_0000: ldc.i4.1 + IL_0001: newobj "S..ctor(int)" + IL_0006: stloc.0 + IL_0007: ldnull + IL_0008: stloc.2 + IL_0009: ldc.i4.0 + IL_000a: stloc.3 + .try + { + IL_000b: ldc.i4.2 + IL_000c: newobj "S..ctor(int)" + IL_0011: stloc.1 + IL_0012: ldnull + IL_0013: stloc.s V_4 + IL_0015: ldc.i4.0 + IL_0016: stloc.s V_5 + .try + { + IL_0018: ldstr "body " + IL_001d: call "void System.Console.Write(object)" + IL_0022: ldc.i4.1 + IL_0023: stloc.s V_5 + IL_0025: leave.s IL_002b + } + catch object + { + IL_0027: stloc.s V_4 + IL_0029: leave.s IL_002b + } + IL_002b: ldloc.1 + IL_002c: brfalse.s IL_0039 + IL_002e: ldloc.1 + IL_002f: callvirt "System.Threading.Tasks.ValueTask S.DisposeAsync()" + IL_0034: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0039: ldloc.s V_4 + IL_003b: brfalse.s IL_0054 + IL_003d: ldloc.s V_4 + IL_003f: isinst "System.Exception" + IL_0044: dup + IL_0045: brtrue.s IL_004a + IL_0047: ldloc.s V_4 + IL_0049: throw + IL_004a: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004f: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0054: ldloc.s V_5 + IL_0056: ldc.i4.1 + IL_0057: beq.s IL_005b + IL_0059: leave.s IL_0062 + IL_005b: ldc.i4.1 + IL_005c: stloc.3 + IL_005d: leave.s IL_0062 + } + catch object + { + IL_005f: stloc.2 + IL_0060: leave.s IL_0062 + } + IL_0062: ldloc.0 + IL_0063: brfalse.s IL_0070 + IL_0065: ldloc.0 + IL_0066: callvirt "System.Threading.Tasks.ValueTask S.DisposeAsync()" + IL_006b: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0070: ldloc.2 + IL_0071: brfalse.s IL_0088 + IL_0073: ldloc.2 + IL_0074: isinst "System.Exception" + IL_0079: dup + IL_007a: brtrue.s IL_007e + IL_007c: ldloc.2 + IL_007d: throw + IL_007e: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0083: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0088: ldloc.3 + IL_0089: ldc.i4.1 + IL_008a: bne.un.s IL_008d + IL_008c: ret + IL_008d: ldnull + IL_008e: throw + } + """); } [Fact] @@ -2224,7 +3270,100 @@ public System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "ctor1 ctor2 body dispose2 dispose1 caught"); + string expectedOutput = "ctor1 ctor2 body dispose2 dispose1 caught"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x85 } + """ + }); + verifier.VerifyIL("S.Main()", """ + { + // Code size 134 (0x86) + .maxstack 2 + .locals init (S V_0, //s1 + S V_1, //s2 + object V_2, + object V_3) + .try + { + IL_0000: ldc.i4.1 + IL_0001: newobj "S..ctor(int)" + IL_0006: stloc.0 + IL_0007: ldnull + IL_0008: stloc.2 + .try + { + IL_0009: ldc.i4.2 + IL_000a: newobj "S..ctor(int)" + IL_000f: stloc.1 + IL_0010: ldnull + IL_0011: stloc.3 + .try + { + IL_0012: ldstr "body " + IL_0017: call "void System.Console.Write(object)" + IL_001c: newobj "System.Exception..ctor()" + IL_0021: throw + } + catch object + { + IL_0022: stloc.3 + IL_0023: leave.s IL_0025 + } + IL_0025: ldloc.1 + IL_0026: brfalse.s IL_0033 + IL_0028: ldloc.1 + IL_0029: callvirt "System.Threading.Tasks.ValueTask S.DisposeAsync()" + IL_002e: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0033: ldloc.3 + IL_0034: brfalse.s IL_004b + IL_0036: ldloc.3 + IL_0037: isinst "System.Exception" + IL_003c: dup + IL_003d: brtrue.s IL_0041 + IL_003f: ldloc.3 + IL_0040: throw + IL_0041: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0046: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_004b: leave.s IL_0050 + } + catch object + { + IL_004d: stloc.2 + IL_004e: leave.s IL_0050 + } + IL_0050: ldloc.0 + IL_0051: brfalse.s IL_005e + IL_0053: ldloc.0 + IL_0054: callvirt "System.Threading.Tasks.ValueTask S.DisposeAsync()" + IL_0059: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005e: ldloc.2 + IL_005f: brfalse.s IL_0076 + IL_0061: ldloc.2 + IL_0062: isinst "System.Exception" + IL_0067: dup + IL_0068: brtrue.s IL_006c + IL_006a: ldloc.2 + IL_006b: throw + IL_006c: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0071: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0076: leave.s IL_0085 + } + catch System.Exception + { + IL_0078: pop + IL_0079: ldstr "caught" + IL_007e: call "void System.Console.Write(object)" + IL_0083: leave.s IL_0085 + } + IL_0085: ret + } + """); } [Fact] @@ -2269,7 +3408,99 @@ public System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "ctor1 ctor2 dispose1 caught"); + string expectedOutput = "ctor1 ctor2 dispose1 caught"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x81 } + """ + }); + verifier.VerifyIL("S.Main()", """ + { + // Code size 130 (0x82) + .maxstack 2 + .locals init (S V_0, //s1 + S V_1, //s2 + object V_2, + object V_3) + .try + { + IL_0000: ldc.i4.1 + IL_0001: newobj "S..ctor(int)" + IL_0006: stloc.0 + IL_0007: ldnull + IL_0008: stloc.2 + .try + { + IL_0009: ldc.i4.2 + IL_000a: newobj "S..ctor(int)" + IL_000f: stloc.1 + IL_0010: ldnull + IL_0011: stloc.3 + .try + { + IL_0012: ldstr "SKIPPED" + IL_0017: call "void System.Console.Write(object)" + IL_001c: leave.s IL_0021 + } + catch object + { + IL_001e: stloc.3 + IL_001f: leave.s IL_0021 + } + IL_0021: ldloc.1 + IL_0022: brfalse.s IL_002f + IL_0024: ldloc.1 + IL_0025: callvirt "System.Threading.Tasks.ValueTask S.DisposeAsync()" + IL_002a: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_002f: ldloc.3 + IL_0030: brfalse.s IL_0047 + IL_0032: ldloc.3 + IL_0033: isinst "System.Exception" + IL_0038: dup + IL_0039: brtrue.s IL_003d + IL_003b: ldloc.3 + IL_003c: throw + IL_003d: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0042: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0047: leave.s IL_004c + } + catch object + { + IL_0049: stloc.2 + IL_004a: leave.s IL_004c + } + IL_004c: ldloc.0 + IL_004d: brfalse.s IL_005a + IL_004f: ldloc.0 + IL_0050: callvirt "System.Threading.Tasks.ValueTask S.DisposeAsync()" + IL_0055: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005a: ldloc.2 + IL_005b: brfalse.s IL_0072 + IL_005d: ldloc.2 + IL_005e: isinst "System.Exception" + IL_0063: dup + IL_0064: brtrue.s IL_0068 + IL_0066: ldloc.2 + IL_0067: throw + IL_0068: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_006d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0072: leave.s IL_0081 + } + catch System.Exception + { + IL_0074: pop + IL_0075: ldstr "caught" + IL_007a: call "void System.Console.Write(object)" + IL_007f: leave.s IL_0081 + } + IL_0081: ret + } + """); } [Fact] @@ -2397,9 +3628,60 @@ public System.Threading.Tasks.ValueTask DisposeAsync(params string[] s) => throw null; } "; + var expectedOutput = "dispose"; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "dispose"); + CompileAndVerify(comp, expectedOutput: expectedOutput); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x35, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main", """ + { + // Code size 54 (0x36) + .maxstack 2 + .locals init (C V_0, //x + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: leave.s IL_000d + } + catch object + { + IL_000a: stloc.1 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: brfalse.s IL_001c + IL_0010: ldloc.0 + IL_0011: ldc.i4.0 + IL_0012: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync(int)" + IL_0017: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_001c: ldloc.1 + IL_001d: brfalse.s IL_0034 + IL_001f: ldloc.1 + IL_0020: isinst "System.Exception" + IL_0025: dup + IL_0026: brtrue.s IL_002a + IL_0028: ldloc.1 + IL_0029: throw + IL_002a: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_002f: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0034: ldc.i4.1 + IL_0035: ret + } + """); } [Fact] @@ -2458,9 +3740,63 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() } } "; + var expectedOutput = "using dispose_start dispose_end return"; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "using dispose_start dispose_end return"); + CompileAndVerify(comp, expectedOutput: expectedOutput); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x48, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + """ + }); + verifier.VerifyIL("C.Main", """ + { + // Code size 73 (0x49) + .maxstack 2 + .locals init (C V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: ldstr "using " + IL_000d: call "void System.Console.Write(object)" + IL_0012: leave.s IL_0017 + } + catch object + { + IL_0014: stloc.1 + IL_0015: leave.s IL_0017 + } + IL_0017: ldloc.0 + IL_0018: brfalse.s IL_0025 + IL_001a: ldloc.0 + IL_001b: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0020: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0025: ldloc.1 + IL_0026: brfalse.s IL_003d + IL_0028: ldloc.1 + IL_0029: isinst "System.Exception" + IL_002e: dup + IL_002f: brtrue.s IL_0033 + IL_0031: ldloc.1 + IL_0032: throw + IL_0033: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0038: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_003d: ldstr "return" + IL_0042: call "void System.Console.Write(object)" + IL_0047: ldc.i4.1 + IL_0048: ret + } + """); } [Fact] @@ -2491,6 +3827,59 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "using dispose_start dispose_end return"); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("using dispose_start dispose_end return", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x48, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + """ + }); + verifier.VerifyIL("C.Main", """ + { + // Code size 73 (0x49) + .maxstack 2 + .locals init (C V_0, //x + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: ldstr "using " + IL_000d: call "void System.Console.Write(object)" + IL_0012: leave.s IL_0017 + } + catch object + { + IL_0014: stloc.1 + IL_0015: leave.s IL_0017 + } + IL_0017: ldloc.0 + IL_0018: brfalse.s IL_0025 + IL_001a: ldloc.0 + IL_001b: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0020: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0025: ldloc.1 + IL_0026: brfalse.s IL_003d + IL_0028: ldloc.1 + IL_0029: isinst "System.Exception" + IL_002e: dup + IL_002f: brtrue.s IL_0033 + IL_0031: ldloc.1 + IL_0032: throw + IL_0033: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0038: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_003d: ldstr "return" + IL_0042: call "void System.Console.Write(object)" + IL_0047: ldc.i4.1 + IL_0048: ret + } + """); } [Fact] @@ -2523,7 +3912,60 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "using dispose_start dispose_end return"); + string expectedOutput = "using dispose_start dispose_end return"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x48, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 73 (0x49) + .maxstack 2 + .locals init (C V_0, //x + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: ldstr "using " + IL_000d: call "void System.Console.Write(object)" + IL_0012: leave.s IL_0017 + } + catch object + { + IL_0014: stloc.1 + IL_0015: leave.s IL_0017 + } + IL_0017: ldloc.0 + IL_0018: brfalse.s IL_0025 + IL_001a: ldloc.0 + IL_001b: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0020: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0025: ldloc.1 + IL_0026: brfalse.s IL_003d + IL_0028: ldloc.1 + IL_0029: isinst "System.Exception" + IL_002e: dup + IL_002f: brtrue.s IL_0033 + IL_0031: ldloc.1 + IL_0032: throw + IL_0033: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0038: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_003d: ldstr "return" + IL_0042: call "void System.Console.Write(object)" + IL_0047: ldc.i4.1 + IL_0048: ret + } + """); } [Fact] @@ -2553,7 +3995,61 @@ public async System.Threading.Tasks.ValueTask DisposeAsync(int i = 0) "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "using dispose_start dispose_end return"); + string expectedOutput = "using dispose_start dispose_end return"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x49, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 74 (0x4a) + .maxstack 2 + .locals init (C V_0, //x + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: ldstr "using " + IL_000d: call "void System.Console.Write(object)" + IL_0012: leave.s IL_0017 + } + catch object + { + IL_0014: stloc.1 + IL_0015: leave.s IL_0017 + } + IL_0017: ldloc.0 + IL_0018: brfalse.s IL_0026 + IL_001a: ldloc.0 + IL_001b: ldc.i4.0 + IL_001c: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync(int)" + IL_0021: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0026: ldloc.1 + IL_0027: brfalse.s IL_003e + IL_0029: ldloc.1 + IL_002a: isinst "System.Exception" + IL_002f: dup + IL_0030: brtrue.s IL_0034 + IL_0032: ldloc.1 + IL_0033: throw + IL_0034: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0039: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_003e: ldstr "return" + IL_0043: call "void System.Console.Write(object)" + IL_0048: ldc.i4.1 + IL_0049: ret + } + """); } [Fact] @@ -2583,7 +4079,62 @@ public async System.Threading.Tasks.ValueTask DisposeAsync(params int[] x) "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "using dispose_start dispose_end(0) return"); + string expectedOutput = "using dispose_start dispose_end(0) return"; + CompileAndVerify(comp, expectedOutput: expectedOutput); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x4e, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x4e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 79 (0x4f) + .maxstack 2 + .locals init (C V_0, //x + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: ldstr "using " + IL_000d: call "void System.Console.Write(object)" + IL_0012: leave.s IL_0017 + } + catch object + { + IL_0014: stloc.1 + IL_0015: leave.s IL_0017 + } + IL_0017: ldloc.0 + IL_0018: brfalse.s IL_002b + IL_001a: ldloc.0 + IL_001b: ldc.i4.0 + IL_001c: newarr "int" + IL_0021: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync(params int[])" + IL_0026: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_002b: ldloc.1 + IL_002c: brfalse.s IL_0043 + IL_002e: ldloc.1 + IL_002f: isinst "System.Exception" + IL_0034: dup + IL_0035: brtrue.s IL_0039 + IL_0037: ldloc.1 + IL_0038: throw + IL_0039: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_003e: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0043: ldstr "return" + IL_0048: call "void System.Console.Write(object)" + IL_004d: ldc.i4.1 + IL_004e: ret + } + """); } [Fact] @@ -2670,7 +4221,7 @@ private System.Threading.Tasks.ValueTask DisposeAsync() Diagnostic(ErrorCode.ERR_NoConvToIAsyncDisp, "var x = new C()").WithArguments("C").WithLocation(6, 22)); } - [ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)] + [Fact] [WorkItem(32316, "https://github.com/dotnet/roslyn/issues/32316")] public void TestPatternBasedDisposal_InstanceMethod_UsingDeclaration() { @@ -2696,7 +4247,8 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "using dispose_start dispose_end return"); + string expectedOutput = "using dispose_start dispose_end return"; + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); // Sequence point highlights `await using ...` verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" @@ -2866,6 +4418,58 @@ .locals init (int V_0, IL_012e: ret } ", sequencePoints: "C+
d__0.MoveNext", source: source); + + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x48, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 73 (0x49) + .maxstack 2 + .locals init (C V_0, //x + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: ldstr "using " + IL_000d: call "void System.Console.Write(object)" + IL_0012: leave.s IL_0017 + } + catch object + { + IL_0014: stloc.1 + IL_0015: leave.s IL_0017 + } + IL_0017: ldloc.0 + IL_0018: brfalse.s IL_0025 + IL_001a: ldloc.0 + IL_001b: callvirt "System.Threading.Tasks.ValueTask C.DisposeAsync()" + IL_0020: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0025: ldloc.1 + IL_0026: brfalse.s IL_003d + IL_0028: ldloc.1 + IL_0029: isinst "System.Exception" + IL_002e: dup + IL_002f: brtrue.s IL_0033 + IL_0031: ldloc.1 + IL_0032: throw + IL_0033: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0038: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_003d: ldstr "return" + IL_0042: call "void System.Console.Write(object)" + IL_0047: ldc.i4.1 + IL_0048: ret + } + """); } [Fact] @@ -2903,9 +4507,67 @@ public class Awaiter : System.Runtime.CompilerServices.INotifyCompletion public void OnCompleted(System.Action continuation) { } } "; + var expectedOutput = "using dispose_start dispose_end return"; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "using dispose_start dispose_end return"); + CompileAndVerify(comp, expectedOutput: expectedOutput); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with { ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x5e, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ }); + verifier.VerifyIL("C.Main", """ + { + // Code size 95 (0x5f) + .maxstack 2 + .locals init (C V_0, //x + object V_1, + Awaiter V_2) + IL_0000: ldloca.s V_0 + IL_0002: initobj "C" + IL_0008: ldnull + IL_0009: stloc.1 + .try + { + IL_000a: ldstr "using " + IL_000f: call "void System.Console.Write(object)" + IL_0014: leave.s IL_0019 + } + catch object + { + IL_0016: stloc.1 + IL_0017: leave.s IL_0019 + } + IL_0019: ldloca.s V_0 + IL_001b: call "Awaitable C.DisposeAsync()" + IL_0020: callvirt "Awaiter Awaitable.GetAwaiter()" + IL_0025: stloc.2 + IL_0026: ldloc.2 + IL_0027: callvirt "bool Awaiter.IsCompleted.get" + IL_002c: brtrue.s IL_0034 + IL_002e: ldloc.2 + IL_002f: call "void System.Runtime.CompilerServices.AsyncHelpers.AwaitAwaiter(Awaiter)" + IL_0034: ldloc.2 + IL_0035: callvirt "bool Awaiter.GetResult()" + IL_003a: pop + IL_003b: ldloc.1 + IL_003c: brfalse.s IL_0053 + IL_003e: ldloc.1 + IL_003f: isinst "System.Exception" + IL_0044: dup + IL_0045: brtrue.s IL_0049 + IL_0047: ldloc.1 + IL_0048: throw + IL_0049: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004e: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0053: ldstr "return" + IL_0058: call "void System.Console.Write(object)" + IL_005d: ldc.i4.1 + IL_005e: ret + } + """); } [Fact] @@ -2932,9 +4594,61 @@ public async System.Threading.Tasks.Task DisposeAsync() } } "; + var expectedOutput = "using dispose_start dispose_end return"; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "using dispose_start dispose_end return"); + CompileAndVerify(comp, expectedOutput: expectedOutput); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x48, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x38 } + """ + }); + verifier.VerifyIL("C.Main", """ + { + // Code size 73 (0x49) + .maxstack 2 + .locals init (C V_0, //x + object V_1) + IL_0000: ldloca.s V_0 + IL_0002: initobj "C" + IL_0008: ldnull + IL_0009: stloc.1 + .try + { + IL_000a: ldstr "using " + IL_000f: call "void System.Console.Write(object)" + IL_0014: leave.s IL_0019 + } + catch object + { + IL_0016: stloc.1 + IL_0017: leave.s IL_0019 + } + IL_0019: ldloca.s V_0 + IL_001b: call "System.Threading.Tasks.Task C.DisposeAsync()" + IL_0020: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: ldloc.1 + IL_0026: brfalse.s IL_003d + IL_0028: ldloc.1 + IL_0029: isinst "System.Exception" + IL_002e: dup + IL_002f: brtrue.s IL_0033 + IL_0031: ldloc.1 + IL_0032: throw + IL_0033: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0038: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_003d: ldstr "return" + IL_0042: call "void System.Console.Write(object)" + IL_0047: ldc.i4.1 + IL_0048: ret + } + """); } [Fact] @@ -2963,9 +4677,59 @@ public async System.Threading.Tasks.Task DisposeAsync() } "; // it's okay to await `Task` even if we don't care about the result + var expectedOutput = "using dispose_start dispose_end return"; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "using dispose_start dispose_end return"); + CompileAndVerify(comp, expectedOutput: expectedOutput); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with { ILVerifyMessage = """ + [Main]: Unexpected type on the stack. { Offset = 0x49, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + [DisposeAsync]: Unexpected type on the stack. { Offset = 0x39, Found = Int32, Expected = ref 'System.Threading.Tasks.Task`1' } + """ }); + verifier.VerifyIL("C.Main", """ + { + // Code size 74 (0x4a) + .maxstack 2 + .locals init (C V_0, //x + object V_1) + IL_0000: ldloca.s V_0 + IL_0002: initobj "C" + IL_0008: ldnull + IL_0009: stloc.1 + .try + { + IL_000a: ldstr "using " + IL_000f: call "void System.Console.Write(object)" + IL_0014: leave.s IL_0019 + } + catch object + { + IL_0016: stloc.1 + IL_0017: leave.s IL_0019 + } + IL_0019: ldloca.s V_0 + IL_001b: call "System.Threading.Tasks.Task C.DisposeAsync()" + IL_0020: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: pop + IL_0026: ldloc.1 + IL_0027: brfalse.s IL_003e + IL_0029: ldloc.1 + IL_002a: isinst "System.Exception" + IL_002f: dup + IL_0030: brtrue.s IL_0034 + IL_0032: ldloc.1 + IL_0033: throw + IL_0034: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0039: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_003e: ldstr "return" + IL_0043: call "void System.Console.Write(object)" + IL_0048: ldc.i4.1 + IL_0049: ret + } + """); } [Fact] @@ -3034,6 +4798,63 @@ static async Task Main() }"; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "StructAwaitable"); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("StructAwaitable", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x4f } + """ + }); + verifier.VerifyIL("Program.Main", """ + { + // Code size 80 (0x50) + .maxstack 2 + .locals init (Disposable V_0, + object V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2) + IL_0000: newobj "Disposable..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: leave.s IL_000d + } + catch object + { + IL_000a: stloc.1 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: brfalse.s IL_0037 + IL_0010: ldloc.0 + IL_0011: callvirt "StructAwaitable Disposable.DisposeAsync()" + IL_0016: box "StructAwaitable" + IL_001b: call "System.Runtime.CompilerServices.TaskAwaiter Extensions.GetAwaiter(object)" + IL_0020: stloc.2 + IL_0021: ldloca.s V_2 + IL_0023: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_0028: brtrue.s IL_0030 + IL_002a: ldloc.2 + IL_002b: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.TaskAwaiter)" + IL_0030: ldloca.s V_2 + IL_0032: call "void System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0037: ldloc.1 + IL_0038: brfalse.s IL_004f + IL_003a: ldloc.1 + IL_003b: isinst "System.Exception" + IL_0040: dup + IL_0041: brtrue.s IL_0045 + IL_0043: ldloc.1 + IL_0044: throw + IL_0045: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_004a: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_004f: ret + } + """); } [Fact, WorkItem(45111, "https://github.com/dotnet/roslyn/issues/45111")] @@ -3057,6 +4878,54 @@ public Task DisposeAsync() Assert.Equal(TypeKind.Error, comp.GetWellKnownType(WellKnownType.System_IAsyncDisposable).TypeKind); comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "DISPOSED"); + + // Runtime async verification (Note: This test doesn't require IAsyncDisposableDefinition since the interface is missing) + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.MakeTypeMissing(WellKnownType.System_IAsyncDisposable); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("DISPOSED", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [
$]: Return value missing on the stack. { Offset = 0x33 } + """ + }); + verifier.VerifyIL("", """ + { + // Code size 52 (0x34) + .maxstack 2 + .locals init (C V_0, + object V_1) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldnull + IL_0007: stloc.1 + .try + { + IL_0008: leave.s IL_000d + } + catch object + { + IL_000a: stloc.1 + IL_000b: leave.s IL_000d + } + IL_000d: ldloc.0 + IL_000e: brfalse.s IL_001b + IL_0010: ldloc.0 + IL_0011: callvirt "System.Threading.Tasks.Task C.DisposeAsync()" + IL_0016: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001b: ldloc.1 + IL_001c: brfalse.s IL_0033 + IL_001e: ldloc.1 + IL_001f: isinst "System.Exception" + IL_0024: dup + IL_0025: brtrue.s IL_0029 + IL_0027: ldloc.1 + IL_0028: throw + IL_0029: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_002e: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0033: ret + } + """); } [Fact, WorkItem(45111, "https://github.com/dotnet/roslyn/issues/45111")] @@ -3565,6 +5434,40 @@ public static ValueTask DisposeAsync(this IEnumerable objects) """; var comp = CreateCompilationWithTasksExtensions([source, IAsyncDisposableDefinition]); CompileAndVerify(comp, expectedOutput: "DISPOSED").VerifyDiagnostics(); + + // Runtime async verification + comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("DISPOSED", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [
$]: Return value missing on the stack. { Offset = 0x3b } + [System.IAsyncDisposable.DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("Class1.System.IAsyncDisposable.DisposeAsync", """ + { + // Code size 47 (0x2f) + .maxstack 1 + .locals init (System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_0, + System.Runtime.CompilerServices.YieldAwaitable V_1) + IL_0000: ldstr "DISPOSED" + IL_0005: call "void System.Console.Write(object)" + IL_000a: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_000f: stloc.1 + IL_0010: ldloca.s V_1 + IL_0012: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0017: stloc.0 + IL_0018: ldloca.s V_0 + IL_001a: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_001f: brtrue.s IL_0027 + IL_0021: ldloc.0 + IL_0022: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_0027: ldloca.s V_0 + IL_0029: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_002e: ret + } + """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73691")] diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs index 374d2e0ff27ba..b5a75239170f5 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs @@ -3334,7 +3334,7 @@ static async Task F() diff1.VerifyIL("C.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" { - // Code size 678 (0x2a6) + // Code size 650 (0x28a) .maxstack 3 .locals init (int V_0, System.Runtime.CompilerServices.TaskAwaiter V_1, @@ -3382,7 +3382,7 @@ .locals init (int V_0, IL_0053: ldloca.s V_2 IL_0055: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__0)"" IL_005a: nop - IL_005b: leave IL_02a5 + IL_005b: leave IL_0289 IL_0060: ldarg.0 IL_0061: ldfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1"" IL_0066: stloc.1 @@ -3468,7 +3468,7 @@ .locals init (int V_0, IL_0116: ldloca.s V_2 IL_0118: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref C.d__0)"" IL_011d: nop - IL_011e: leave IL_02a5 + IL_011e: leave IL_0289 IL_0123: ldarg.0 IL_0124: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.d__0.<>u__2"" IL_0129: stloc.s V_4 @@ -3553,7 +3553,7 @@ .locals init (int V_0, IL_01dc: ldloca.s V_2 IL_01de: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref C.d__0)"" IL_01e3: nop - IL_01e4: leave IL_02a5 + IL_01e4: leave IL_0289 IL_01e9: ldarg.0 IL_01ea: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.d__0.<>u__2"" IL_01ef: stloc.s V_8 @@ -3591,7 +3591,7 @@ .locals init (int V_0, IL_023d: ldc.i4.1 IL_023e: beq.s IL_0242 IL_0240: br.s IL_0244 - IL_0242: leave.s IL_0283 + IL_0242: leave.s IL_0275 IL_0244: ldarg.0 IL_0245: ldnull IL_0246: stfld ""object C.d__0.<>s__3"" @@ -3601,7 +3601,8 @@ .locals init (int V_0, IL_0252: ldarg.0 IL_0253: ldnull IL_0254: stfld ""System.IAsyncDisposable C.d__0.5__2"" - IL_0259: leave.s IL_0283 + IL_0259: ldnull + IL_025a: throw } catch System.Exception { @@ -3610,32 +3611,20 @@ .locals init (int V_0, IL_025e: ldc.i4.s -2 IL_0260: stfld ""int C.d__0.<>1__state"" IL_0265: ldarg.0 - IL_0266: ldnull - IL_0267: stfld ""System.IAsyncDisposable C.d__0.5__1"" - IL_026c: ldarg.0 - IL_026d: ldnull - IL_026e: stfld ""System.IAsyncDisposable C.d__0.5__2"" - IL_0273: ldarg.0 - IL_0274: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder"" - IL_0279: ldloc.s V_6 - IL_027b: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" - IL_0280: nop - IL_0281: leave.s IL_02a5 + IL_0266: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder"" + IL_026b: ldloc.s V_6 + IL_026d: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" + IL_0272: nop + IL_0273: leave.s IL_0289 } - IL_0283: ldarg.0 - IL_0284: ldc.i4.s -2 - IL_0286: stfld ""int C.d__0.<>1__state"" - IL_028b: ldarg.0 - IL_028c: ldnull - IL_028d: stfld ""System.IAsyncDisposable C.d__0.5__1"" - IL_0292: ldarg.0 - IL_0293: ldnull - IL_0294: stfld ""System.IAsyncDisposable C.d__0.5__2"" - IL_0299: ldarg.0 - IL_029a: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder"" - IL_029f: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" - IL_02a4: nop - IL_02a5: ret + IL_0275: ldarg.0 + IL_0276: ldc.i4.s -2 + IL_0278: stfld ""int C.d__0.<>1__state"" + IL_027d: ldarg.0 + IL_027e: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder"" + IL_0283: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" + IL_0288: nop + IL_0289: ret }"); } diff --git a/src/Compilers/CSharp/Test/Emit2/PDB/PDBAsyncTests.cs b/src/Compilers/CSharp/Test/Emit2/PDB/PDBAsyncTests.cs index e60ce9795253e..7e958adc673f7 100644 --- a/src/Compilers/CSharp/Test/Emit2/PDB/PDBAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/PDB/PDBAsyncTests.cs @@ -1878,7 +1878,7 @@ .locals init (int V_0, - +