diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs
index a78d7999a783f..c53c5b486ca1f 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs
@@ -39,7 +39,7 @@ private BoundAwaitExpression BindAwait(BoundExpression expression, SyntaxNode no
// non-void return type T, the await-expression is classified as a value of type T.
TypeSymbol awaitExpressionType = info.GetResult?.ReturnType ?? (hasErrors ? CreateErrorType() : Compilation.DynamicType);
- return new BoundAwaitExpression(node, expression, info, awaitExpressionType, hasErrors);
+ return new BoundAwaitExpression(node, expression, info, debugInfo: default, awaitExpressionType, hasErrors);
}
internal void ReportBadAwaitDiagnostics(SyntaxNodeOrToken nodeOrToken, BindingDiagnosticBag diagnostics, ref bool hasErrors)
diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundAwaitExpressionDebugInfo.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundAwaitExpressionDebugInfo.cs
new file mode 100644
index 0000000000000..3eb3412dec8fd
--- /dev/null
+++ b/src/Compilers/CSharp/Portable/BoundTree/BoundAwaitExpressionDebugInfo.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.CodeAnalysis.CodeGen;
+
+namespace Microsoft.CodeAnalysis.CSharp;
+
+///
+/// Debug info associated with to support EnC.
+///
+/// The id of the generated await.
+///
+/// The number of async state machine states to reserve.
+///
+/// Any time multiple s might be associated with the same syntax node
+/// we need to make sure that the same number of state machine states gets allocated for the node,
+/// regardless of the actual number of s that get emitted.
+///
+/// To do so one or more of the emitted s may
+/// reserve additional dummy state machine states so that the total number of states
+/// (one for each plus total reserved states) is constant
+/// regardless of semantics of the syntax node.
+///
+/// E.g. `await foreach` produces at least one and at most two s:
+/// one for MoveNextAsync and the other for DisposeAsync.
+///
+/// If the enumerator is async-disposable it produces two s with
+/// set to 0.
+///
+/// If the enumerator is not async-disposable it produces a single with
+/// set to 1.
+///
+/// The states are only reserved in DEBUG builds.
+///
+internal readonly record struct BoundAwaitExpressionDebugInfo(AwaitDebugId AwaitId, byte ReservedStateMachineCount);
diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
index 6ed9b23bca448..b240da9267c7d 100644
--- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
+++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
@@ -690,6 +690,7 @@
+
diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs
index 15f6b47398925..b344d2914ce5a 100644
--- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs
+++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs
@@ -2136,7 +2136,7 @@ public BoundAwaitableInfo Update(BoundAwaitableValuePlaceholder? awaitableInstan
internal sealed partial class BoundAwaitExpression : BoundExpression
{
- public BoundAwaitExpression(SyntaxNode syntax, BoundExpression expression, BoundAwaitableInfo awaitableInfo, TypeSymbol type, bool hasErrors = false)
+ public BoundAwaitExpression(SyntaxNode syntax, BoundExpression expression, BoundAwaitableInfo awaitableInfo, BoundAwaitExpressionDebugInfo debugInfo, TypeSymbol type, bool hasErrors = false)
: base(BoundKind.AwaitExpression, syntax, type, hasErrors || expression.HasErrors() || awaitableInfo.HasErrors())
{
@@ -2146,20 +2146,22 @@ public BoundAwaitExpression(SyntaxNode syntax, BoundExpression expression, Bound
this.Expression = expression;
this.AwaitableInfo = awaitableInfo;
+ this.DebugInfo = debugInfo;
}
public new TypeSymbol Type => base.Type!;
public BoundExpression Expression { get; }
public BoundAwaitableInfo AwaitableInfo { get; }
+ public BoundAwaitExpressionDebugInfo DebugInfo { get; }
[DebuggerStepThrough]
public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitAwaitExpression(this);
- public BoundAwaitExpression Update(BoundExpression expression, BoundAwaitableInfo awaitableInfo, TypeSymbol type)
+ public BoundAwaitExpression Update(BoundExpression expression, BoundAwaitableInfo awaitableInfo, BoundAwaitExpressionDebugInfo debugInfo, TypeSymbol type)
{
- if (expression != this.Expression || awaitableInfo != this.AwaitableInfo || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything))
+ if (expression != this.Expression || awaitableInfo != this.AwaitableInfo || debugInfo != this.DebugInfo || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything))
{
- var result = new BoundAwaitExpression(this.Syntax, expression, awaitableInfo, type, this.HasErrors);
+ var result = new BoundAwaitExpression(this.Syntax, expression, awaitableInfo, debugInfo, type, this.HasErrors);
result.CopyAttributes(this);
return result;
}
@@ -10938,7 +10940,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor
BoundExpression expression = (BoundExpression)this.Visit(node.Expression);
BoundAwaitableInfo awaitableInfo = (BoundAwaitableInfo)this.Visit(node.AwaitableInfo);
TypeSymbol? type = this.VisitType(node.Type);
- return node.Update(expression, awaitableInfo, type);
+ return node.Update(expression, awaitableInfo, node.DebugInfo, type);
}
public override BoundNode? VisitTypeOfOperator(BoundTypeOfOperator node)
{
@@ -12746,12 +12748,12 @@ public NullabilityRewriter(ImmutableDictionary.GetInstance();
diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodToStateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodToStateMachineRewriter.cs
index dec45c13ff114..67f3255bb682e 100644
--- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodToStateMachineRewriter.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodToStateMachineRewriter.cs
@@ -380,7 +380,7 @@ private BoundBlock VisitAwaitExpression(BoundAwaitExpression node, BoundExpressi
// if(!($awaiterTemp.IsCompleted)) { ... }
F.If(
condition: F.Not(GenerateGetIsCompleted(awaiterTemp, isCompletedMethod)),
- thenClause: GenerateAwaitForIncompleteTask(awaiterTemp)));
+ thenClause: GenerateAwaitForIncompleteTask(awaiterTemp, node.DebugInfo)));
BoundExpression getResultCall = MakeCallMaybeDynamic(
F.Local(awaiterTemp),
getResult,
@@ -450,9 +450,10 @@ private BoundExpression GenerateGetIsCompleted(LocalSymbol awaiterTemp, MethodSy
return F.Call(F.Local(awaiterTemp), getIsCompletedMethod);
}
- private BoundBlock GenerateAwaitForIncompleteTask(LocalSymbol awaiterTemp)
+ private BoundBlock GenerateAwaitForIncompleteTask(LocalSymbol awaiterTemp, BoundAwaitExpressionDebugInfo debugInfo)
{
- AddResumableState(awaiterTemp.GetDeclaratorSyntax(), out StateMachineState stateNumber, out GeneratedLabelSymbol resumeLabel);
+ var awaitSyntax = awaiterTemp.GetDeclaratorSyntax();
+ AddResumableState(awaitSyntax, debugInfo.AwaitId, out StateMachineState stateNumber, out GeneratedLabelSymbol resumeLabel);
TypeSymbol awaiterFieldType = awaiterTemp.Type.IsVerifierReference()
? F.SpecialType(SpecialType.System_Object)
@@ -485,6 +486,15 @@ private BoundBlock GenerateAwaitForIncompleteTask(LocalSymbol awaiterTemp)
blockBuilder.Add(
GenerateReturn(false));
+ if (F.Compilation.Options.EnableEditAndContinue)
+ {
+ for (int i = 0; i < debugInfo.ReservedStateMachineCount; i++)
+ {
+ AddResumableState(awaitSyntax, new AwaitDebugId((byte)(debugInfo.AwaitId.RelativeStateOrdinal + 1 + i)), out _, out var dummyResumeLabel);
+ blockBuilder.Add(F.Label(dummyResumeLabel));
+ }
+ }
+
blockBuilder.Add(
F.Label(resumeLabel));
diff --git a/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorMethodToStateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorMethodToStateMachineRewriter.cs
index 5f75666a8248d..1cfae5948d2b1 100644
--- a/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorMethodToStateMachineRewriter.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorMethodToStateMachineRewriter.cs
@@ -333,7 +333,7 @@ public override BoundNode VisitYieldReturnStatement(BoundYieldReturnStatement no
// : ;
//
// this.state = finalizeState;
- AddResumableState(node.Syntax, out StateMachineState stateNumber, out GeneratedLabelSymbol resumeLabel);
+ AddResumableState(node.Syntax, awaitId: default, out StateMachineState stateNumber, out GeneratedLabelSymbol resumeLabel);
_currentFinallyFrame.AddState(stateNumber);
var rewrittenExpression = (BoundExpression)Visit(node.Expression);
@@ -464,12 +464,12 @@ private IteratorFinallyFrame PushFrame(BoundTryStatement statement)
{
var syntax = statement.Syntax;
- if (slotAllocatorOpt?.TryGetPreviousStateMachineState(syntax, out var finalizeState) != true)
+ if (slotAllocatorOpt?.TryGetPreviousStateMachineState(syntax, awaitId: default, out var finalizeState) != true)
{
finalizeState = _nextFinalizeState--;
}
- AddStateDebugInfo(syntax, finalizeState);
+ AddStateDebugInfo(syntax, awaitId: default, finalizeState);
var finallyMethod = MakeSynthesizedFinally(finalizeState);
var newFrame = new IteratorFinallyFrame(_currentFinallyFrame, finalizeState, finallyMethod, _yieldsInTryAnalysis.Labels(statement));
diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Await.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Await.cs
index 8b19828417d34..c791821e87fc7 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Await.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Await.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Immutable;
+using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
namespace Microsoft.CodeAnalysis.CSharp
@@ -19,9 +20,9 @@ public BoundExpression VisitAwaitExpression(BoundAwaitExpression node, bool used
return RewriteAwaitExpression((BoundExpression)base.VisitAwaitExpression(node)!, used);
}
- private BoundExpression RewriteAwaitExpression(SyntaxNode syntax, BoundExpression rewrittenExpression, BoundAwaitableInfo awaitableInfo, TypeSymbol type, bool used)
+ private BoundExpression RewriteAwaitExpression(SyntaxNode syntax, BoundExpression rewrittenExpression, BoundAwaitableInfo awaitableInfo, TypeSymbol type, BoundAwaitExpressionDebugInfo debugInfo, bool used)
{
- return RewriteAwaitExpression(new BoundAwaitExpression(syntax, rewrittenExpression, awaitableInfo, type) { WasCompilerGenerated = true }, used);
+ return RewriteAwaitExpression(new BoundAwaitExpression(syntax, rewrittenExpression, awaitableInfo, debugInfo, type) { WasCompilerGenerated = true }, used);
}
///
diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs
index 6a6203f3c642c..1d5f4a7509e5d 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs
@@ -6,6 +6,7 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
@@ -14,6 +15,9 @@ namespace Microsoft.CodeAnalysis.CSharp
{
internal partial class LocalRewriter
{
+ private static readonly AwaitDebugId s_moveNextAsyncAwaitId = new AwaitDebugId(RelativeStateOrdinal: 0);
+ private static readonly AwaitDebugId s_disposeAsyncAwaitId = new AwaitDebugId(RelativeStateOrdinal: 1);
+
///
/// This is the entry point for foreach-loop lowering. It delegates to
/// RewriteEnumeratorForEachStatement
@@ -208,14 +212,22 @@ private BoundStatement RewriteForEachEnumerator(
var rewrittenBodyBlock = CreateBlockDeclaringIterationVariables(iterationVariables, iterationVarDecl, rewrittenBody, forEachSyntax);
BoundExpression rewrittenCondition = SynthesizeCall(
- methodArgumentInfo: enumeratorInfo.MoveNextInfo,
- syntax: forEachSyntax,
- receiver: boundEnumeratorVar,
- allowExtensionAndOptionalParameters: isAsync);
+ methodArgumentInfo: enumeratorInfo.MoveNextInfo,
+ syntax: forEachSyntax,
+ receiver: boundEnumeratorVar,
+ allowExtensionAndOptionalParameters: isAsync);
+
+ var disposalFinallyBlock = GetDisposalFinallyBlock(forEachSyntax, enumeratorInfo, enumeratorType, boundEnumeratorVar, out var hasAsyncDisposal);
if (isAsync)
{
Debug.Assert(awaitableInfo is { GetResult: { } });
- rewrittenCondition = RewriteAwaitExpression(forEachSyntax, rewrittenCondition, awaitableInfo, awaitableInfo.GetResult.ReturnType, used: true);
+
+ // 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);
}
BoundStatement whileLoop = RewriteWhileStatement(
@@ -228,9 +240,22 @@ private BoundStatement RewriteForEachEnumerator(
BoundStatement result;
- if (enumeratorInfo.NeedsDisposal)
+ if (disposalFinallyBlock != null)
{
- BoundStatement tryFinally = WrapWithTryFinallyDispose(forEachSyntax, enumeratorInfo, enumeratorType, boundEnumeratorVar, whileLoop);
+ // try {
+ // while (e.MoveNext()) {
+ // V v = (V)(T)e.Current; -OR- (D1 d1, ...) = (V)(T)e.Current;
+ // /* loop body */
+ // }
+ // }
+ // finally {
+ // /* dispose of e */
+ // }
+ BoundStatement tryFinally = new BoundTryStatement(
+ forEachSyntax,
+ tryBlock: new BoundBlock(forEachSyntax, locals: ImmutableArray.Empty, statements: ImmutableArray.Create(whileLoop)),
+ catchBlocks: ImmutableArray.Empty,
+ finallyBlockOpt: disposalFinallyBlock);
// E e = ((C)(x)).GetEnumerator();
// try {
@@ -275,14 +300,20 @@ private bool TryGetDisposeMethod(SyntaxNode forEachSyntax, ForEachEnumeratorInfo
/// - interface-based disposal (the enumerator type converts to IDisposable/IAsyncDisposable)
/// - we need to do a runtime check for IDisposable
///
- private BoundStatement WrapWithTryFinallyDispose(
+ /// Finally block, or null if none should be emitted.
+ private BoundBlock? GetDisposalFinallyBlock(
CSharpSyntaxNode forEachSyntax,
ForEachEnumeratorInfo enumeratorInfo,
TypeSymbol enumeratorType,
BoundLocal boundEnumeratorVar,
- BoundStatement rewrittenBody)
+ out bool hasAsyncDisposal)
{
- Debug.Assert(enumeratorInfo.NeedsDisposal);
+ hasAsyncDisposal = false;
+
+ if (!enumeratorInfo.NeedsDisposal)
+ {
+ return null;
+ }
NamedTypeSymbol? idisposableTypeSymbol = null;
bool isImplicit = false;
@@ -295,7 +326,7 @@ private BoundStatement WrapWithTryFinallyDispose(
// This is a temporary workaround for https://github.com/dotnet/roslyn/issues/39948
if (disposeMethod is null)
{
- return rewrittenBody;
+ return null;
}
idisposableTypeSymbol = disposeMethod.ContainingType;
@@ -313,7 +344,6 @@ private BoundStatement WrapWithTryFinallyDispose(
containingType: _factory.CurrentType,
location: enumeratorInfo.Location);
- BoundBlock finallyBlockOpt;
if (isImplicit || !(enumeratorInfo.PatternDisposeInfo is null))
{
Conversion receiverConversion = enumeratorType.IsStructType() ?
@@ -344,6 +374,7 @@ private BoundStatement WrapWithTryFinallyDispose(
// await /* disposeCall */
disposeCallStatement = WrapWithAwait(forEachSyntax, disposeCall, disposeAwaitableInfoOpt);
_sawAwaitInExceptionHandler = true;
+ hasAsyncDisposal = true;
}
else
{
@@ -373,7 +404,7 @@ private BoundStatement WrapWithTryFinallyDispose(
hasErrors: false);
}
- finallyBlockOpt = new BoundBlock(forEachSyntax,
+ return new BoundBlock(forEachSyntax,
locals: ImmutableArray.Empty,
statements: ImmutableArray.Create(alwaysOrMaybeDisposeStmt));
}
@@ -429,27 +460,10 @@ private BoundStatement WrapWithTryFinallyDispose(
// IDisposable d = e as IDisposable;
// if (d != null) d.Dispose();
- finallyBlockOpt = new BoundBlock(forEachSyntax,
+ return new BoundBlock(forEachSyntax,
locals: ImmutableArray.Create(disposableVar),
statements: ImmutableArray.Create(disposableVarDecl, ifStmt));
}
-
- // try {
- // while (e.MoveNext()) {
- // V v = (V)(T)e.Current; -OR- (D1 d1, ...) = (V)(T)e.Current;
- // /* loop body */
- // }
- // }
- // finally {
- // /* dispose of e */
- // }
- BoundStatement tryFinally = new BoundTryStatement(forEachSyntax,
- tryBlock: new BoundBlock(forEachSyntax,
- locals: ImmutableArray.Empty,
- statements: ImmutableArray.Create(rewrittenBody)),
- catchBlocks: ImmutableArray.Empty,
- finallyBlockOpt: finallyBlockOpt);
- return tryFinally;
}
///
@@ -459,7 +473,8 @@ private BoundStatement WrapWithTryFinallyDispose(
private BoundStatement WrapWithAwait(SyntaxNode forEachSyntax, BoundExpression disposeCall, BoundAwaitableInfo disposeAwaitableInfoOpt)
{
TypeSymbol awaitExpressionType = disposeAwaitableInfoOpt.GetResult?.ReturnType ?? _compilation.DynamicType;
- var awaitExpr = RewriteAwaitExpression(forEachSyntax, disposeCall, disposeAwaitableInfoOpt, awaitExpressionType, used: false);
+ var debugInfo = new BoundAwaitExpressionDebugInfo(s_disposeAsyncAwaitId, ReservedStateMachineCount: 0);
+ var awaitExpr = RewriteAwaitExpression(forEachSyntax, disposeCall, disposeAwaitableInfoOpt, awaitExpressionType, debugInfo, used: false);
return new BoundExpressionStatement(forEachSyntax, awaitExpr);
}
diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UsingStatement.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UsingStatement.cs
index eb1915e4adda0..904ded1a899ca 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UsingStatement.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UsingStatement.cs
@@ -478,7 +478,7 @@ private BoundExpression GenerateDisposeCall(
_sawAwaitInExceptionHandler = true;
TypeSymbol awaitExpressionType = awaitOpt.GetResult?.ReturnType ?? _compilation.DynamicType;
- disposeCall = RewriteAwaitExpression(resourceSyntax, disposeCall, awaitOpt, awaitExpressionType, false);
+ disposeCall = RewriteAwaitExpression(resourceSyntax, disposeCall, awaitOpt, awaitExpressionType, debugInfo: default, used: false);
}
}
diff --git a/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs b/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs
index dfe215468f343..9bfb3a387e2f3 100644
--- a/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs
@@ -722,7 +722,7 @@ public override BoundNode VisitAwaitExpression(BoundAwaitExpression node)
// the spilling will occur in the enclosing node.
BoundSpillSequenceBuilder builder = null;
var expr = VisitExpression(ref builder, node.Expression);
- return UpdateExpression(builder, node.Update(expr, node.AwaitableInfo, node.Type));
+ return UpdateExpression(builder, node.Update(expr, node.AwaitableInfo, node.DebugInfo, node.Type));
}
public override BoundNode VisitSpillSequence(BoundSpillSequence node)
diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs
index 464b9a6ed9e8c..9e8d545d5e069 100644
--- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs
@@ -206,22 +206,22 @@ protected override BoundExpression FramePointer(SyntaxNode syntax, NamedTypeSymb
return result;
}
#nullable enable
- protected void AddResumableState(SyntaxNode awaitOrYieldReturnSyntax, out StateMachineState state, out GeneratedLabelSymbol resumeLabel)
- => AddResumableState(_resumableStateAllocator, awaitOrYieldReturnSyntax, out state, out resumeLabel);
+ protected void AddResumableState(SyntaxNode awaitOrYieldReturnSyntax, AwaitDebugId awaitId, out StateMachineState state, out GeneratedLabelSymbol resumeLabel)
+ => AddResumableState(_resumableStateAllocator, awaitOrYieldReturnSyntax, awaitId, out state, out resumeLabel);
- protected void AddResumableState(ResumableStateMachineStateAllocator allocator, SyntaxNode awaitOrYieldReturnSyntax, out StateMachineState stateNumber, out GeneratedLabelSymbol resumeLabel)
+ protected void AddResumableState(ResumableStateMachineStateAllocator allocator, SyntaxNode awaitOrYieldReturnSyntax, AwaitDebugId awaitId, out StateMachineState stateNumber, out GeneratedLabelSymbol resumeLabel)
{
- stateNumber = allocator.AllocateState(awaitOrYieldReturnSyntax);
- AddStateDebugInfo(awaitOrYieldReturnSyntax, stateNumber);
+ stateNumber = allocator.AllocateState(awaitOrYieldReturnSyntax, awaitId);
+ AddStateDebugInfo(awaitOrYieldReturnSyntax, awaitId, stateNumber);
AddState(stateNumber, out resumeLabel);
}
- protected void AddStateDebugInfo(SyntaxNode node, StateMachineState state)
+ protected void AddStateDebugInfo(SyntaxNode node, AwaitDebugId awaitId, StateMachineState state)
{
Debug.Assert(SyntaxBindingUtilities.BindsToResumableStateMachineState(node) || SyntaxBindingUtilities.BindsToTryStatement(node), $"Unexpected syntax: {node.Kind()}");
int syntaxOffset = CurrentMethod.CalculateLocalSyntaxOffset(node.SpanStart, node.SyntaxTree);
- _stateDebugInfoBuilder.Add(new StateMachineStateDebugInfo(syntaxOffset, state));
+ _stateDebugInfoBuilder.Add(new StateMachineStateDebugInfo(syntaxOffset, awaitId, state));
}
protected void AddState(StateMachineState stateNumber, out GeneratedLabelSymbol resumeLabel)
diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/ResumableStateMachineStateAllocator.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/ResumableStateMachineStateAllocator.cs
index ce443a1429a01..685dc8e1b6fc4 100644
--- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/ResumableStateMachineStateAllocator.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/ResumableStateMachineStateAllocator.cs
@@ -43,13 +43,13 @@ public ResumableStateMachineStateAllocator(VariableSlotAllocator? slotAllocator,
_nextState = slotAllocator?.GetFirstUnusedStateMachineState(increasing) ?? firstState;
}
- public StateMachineState AllocateState(SyntaxNode awaitOrYieldReturnSyntax)
+ public StateMachineState AllocateState(SyntaxNode awaitOrYieldReturnSyntax, AwaitDebugId awaitId)
{
Debug.Assert(SyntaxBindingUtilities.BindsToResumableStateMachineState(awaitOrYieldReturnSyntax));
int direction = _increasing ? +1 : -1;
- if (_slotAllocator?.TryGetPreviousStateMachineState(awaitOrYieldReturnSyntax, out var state) == true)
+ if (_slotAllocator?.TryGetPreviousStateMachineState(awaitOrYieldReturnSyntax, awaitId, out var state) == true)
{
#if DEBUG
// two states of the new state machine should not match the same state of the previous machine:
diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs
index aa7f9f5f2557b..778817985654a 100644
--- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs
@@ -1619,12 +1619,15 @@ async System.Collections.Generic.IAsyncEnumerator M()
}";
var expected = new[]
{
- // (4,60): error CS8652: The feature 'async streams' is not available in C# 7.3. Please use language version 8.0 or greater.
+ // 1.cs(2,2): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater.
+ // #nullable disable
+ Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "nullable").WithArguments("nullable reference types", "8.0").WithLocation(2, 2),
+ // 0.cs(4,60): error CS8370: Feature 'async streams' is not available in C# 7.3. Please use language version 8.0 or greater.
// async System.Collections.Generic.IAsyncEnumerator M()
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "M").WithArguments("async streams", "8.0").WithLocation(4, 60),
- // (34,2): error CS8652: The feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater.
+ // 1.cs(21,2): error CS8370: Feature 'nullable reference types' is not available in C# 7.3. Please use language version 8.0 or greater.
// #nullable disable
- Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "nullable").WithArguments("nullable reference types", "8.0").WithLocation(34, 2)
+ Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "nullable").WithArguments("nullable reference types", "8.0").WithLocation(21, 2)
};
var comp = CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, parseOptions: TestOptions.Regular7_3);
comp.VerifyDiagnostics(expected);
diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs
index 7403c1d4816fd..9f35f02b9440e 100644
--- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs
@@ -335,48 +335,45 @@ public async Task M(IDisposable disposable)
});
vd.VerifyPdb("C.M", @"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-");
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+", options: PdbValidationOptions.ExcludeDocuments);
}
[Fact]
diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs
index 4acb278d5d927..5c7354071d03d 100644
--- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs
@@ -2513,7 +2513,7 @@ public class DerivedEnumerator : Enumerator, System.IAsyncDisposable
verifier.VerifyIL("C.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @"
{
- // Code size 262 (0x106)
+ // Code size 273 (0x111)
.maxstack 3
.locals init (int V_0,
System.Threading.CancellationToken V_1,
@@ -2528,118 +2528,124 @@ .locals init (int V_0,
{
// sequence point:
IL_0007: ldloc.0
- IL_0008: brfalse.s IL_000c
- IL_000a: br.s IL_0011
- IL_000c: br IL_009a
+ IL_0008: brfalse.s IL_0012
+ IL_000a: br.s IL_000c
+ IL_000c: ldloc.0
+ IL_000d: ldc.i4.1
+ IL_000e: beq.s IL_0017
+ IL_0010: br.s IL_001c
+ IL_0012: br IL_00a5
+ IL_0017: br IL_00a5
// sequence point: {
- IL_0011: nop
+ IL_001c: nop
// sequence point: await foreach
- IL_0012: nop
+ IL_001d: nop
// sequence point: new C()
- IL_0013: ldarg.0
- IL_0014: newobj ""C..ctor()""
- IL_0019: ldloca.s V_1
- IL_001b: initobj ""System.Threading.CancellationToken""
- IL_0021: ldloc.1
- IL_0022: call ""C.Enumerator C.GetAsyncEnumerator(System.Threading.CancellationToken)""
- IL_0027: stfld ""C.Enumerator C.d__0.<>s__1""
+ IL_001e: ldarg.0
+ IL_001f: newobj ""C..ctor()""
+ IL_0024: ldloca.s V_1
+ IL_0026: initobj ""System.Threading.CancellationToken""
+ IL_002c: ldloc.1
+ IL_002d: call ""C.Enumerator C.GetAsyncEnumerator(System.Threading.CancellationToken)""
+ IL_0032: stfld ""C.Enumerator C.d__0.<>s__1""
// sequence point:
- IL_002c: br.s IL_005c
+ IL_0037: br.s IL_0067
// sequence point: var i
- IL_002e: ldarg.0
- IL_002f: ldarg.0
- IL_0030: ldfld ""C.Enumerator C.d__0.<>s__1""
- IL_0035: callvirt ""int C.Enumerator.Current.get""
- IL_003a: stfld ""int C.d__0.5__2""
+ IL_0039: ldarg.0
+ IL_003a: ldarg.0
+ IL_003b: ldfld ""C.Enumerator C.d__0.<>s__1""
+ IL_0040: callvirt ""int C.Enumerator.Current.get""
+ IL_0045: stfld ""int C.d__0.5__2""
// sequence point: {
- IL_003f: nop
+ IL_004a: nop
// sequence point: Write($""Got({i}) "");
- IL_0040: ldstr ""Got({0}) ""
- IL_0045: ldarg.0
- IL_0046: ldfld ""int C.d__0.5__2""
- IL_004b: box ""int""
- IL_0050: call ""string string.Format(string, object)""
- IL_0055: call ""void System.Console.Write(string)""
- IL_005a: nop
+ IL_004b: ldstr ""Got({0}) ""
+ IL_0050: ldarg.0
+ IL_0051: ldfld ""int C.d__0.5__2""
+ IL_0056: box ""int""
+ IL_005b: call ""string string.Format(string, object)""
+ IL_0060: call ""void System.Console.Write(string)""
+ IL_0065: nop
// sequence point: }
- IL_005b: nop
+ IL_0066: nop
// sequence point: in
- IL_005c: ldarg.0
- IL_005d: ldfld ""C.Enumerator C.d__0.<>s__1""
- IL_0062: callvirt ""System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()""
- IL_0067: callvirt ""System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()""
- IL_006c: stloc.2
+ IL_0067: ldarg.0
+ IL_0068: ldfld ""C.Enumerator C.d__0.<>s__1""
+ IL_006d: callvirt ""System.Threading.Tasks.Task C.Enumerator.MoveNextAsync()""
+ IL_0072: callvirt ""System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()""
+ IL_0077: stloc.2
// sequence point:
- IL_006d: ldloca.s V_2
- IL_006f: call ""bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get""
- IL_0074: brtrue.s IL_00b6
- IL_0076: ldarg.0
- IL_0077: ldc.i4.0
- IL_0078: dup
- IL_0079: stloc.0
- IL_007a: stfld ""int C.d__0.<>1__state""
+ IL_0078: ldloca.s V_2
+ IL_007a: call ""bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get""
+ IL_007f: brtrue.s IL_00c1
+ IL_0081: ldarg.0
+ IL_0082: ldc.i4.0
+ IL_0083: dup
+ IL_0084: stloc.0
+ IL_0085: stfld ""int C.d__0.<>1__state""
// async: yield
- IL_007f: ldarg.0
- IL_0080: ldloc.2
- IL_0081: stfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1""
- IL_0086: ldarg.0
- IL_0087: stloc.3
- IL_0088: ldarg.0
- IL_0089: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder""
- IL_008e: ldloca.s V_2
- IL_0090: ldloca.s V_3
- IL_0092: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, C.d__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__0)""
- IL_0097: nop
- IL_0098: leave.s IL_0105
+ IL_008a: ldarg.0
+ IL_008b: ldloc.2
+ IL_008c: stfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1""
+ IL_0091: ldarg.0
+ IL_0092: stloc.3
+ IL_0093: ldarg.0
+ IL_0094: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder""
+ IL_0099: ldloca.s V_2
+ IL_009b: ldloca.s V_3
+ IL_009d: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, C.d__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__0)""
+ IL_00a2: nop
+ IL_00a3: leave.s IL_0110
// async: resume
- IL_009a: ldarg.0
- IL_009b: ldfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1""
- IL_00a0: stloc.2
- IL_00a1: ldarg.0
- IL_00a2: ldflda ""System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1""
- IL_00a7: initobj ""System.Runtime.CompilerServices.TaskAwaiter""
- IL_00ad: ldarg.0
- IL_00ae: ldc.i4.m1
- IL_00af: dup
- IL_00b0: stloc.0
- IL_00b1: stfld ""int C.d__0.<>1__state""
- IL_00b6: ldarg.0
- IL_00b7: ldloca.s V_2
- IL_00b9: call ""bool System.Runtime.CompilerServices.TaskAwaiter.GetResult()""
- IL_00be: stfld ""bool C.d__0.<>s__3""
- IL_00c3: ldarg.0
- IL_00c4: ldfld ""bool C.d__0.<>s__3""
- IL_00c9: brtrue IL_002e
+ IL_00a5: ldarg.0
+ IL_00a6: ldfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1""
+ IL_00ab: stloc.2
+ IL_00ac: ldarg.0
+ IL_00ad: ldflda ""System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1""
+ IL_00b2: initobj ""System.Runtime.CompilerServices.TaskAwaiter""
+ IL_00b8: ldarg.0
+ IL_00b9: ldc.i4.m1
+ IL_00ba: dup
+ IL_00bb: stloc.0
+ IL_00bc: stfld ""int C.d__0.<>1__state""
+ IL_00c1: ldarg.0
+ IL_00c2: ldloca.s V_2
+ IL_00c4: call ""bool System.Runtime.CompilerServices.TaskAwaiter.GetResult()""
+ IL_00c9: stfld ""bool C.d__0.<>s__3""
IL_00ce: ldarg.0
- IL_00cf: ldnull
- IL_00d0: stfld ""C.Enumerator C.d__0.<>s__1""
- IL_00d5: leave.s IL_00f1
+ IL_00cf: ldfld ""bool C.d__0.<>s__3""
+ IL_00d4: brtrue IL_0039
+ IL_00d9: ldarg.0
+ IL_00da: ldnull
+ IL_00db: stfld ""C.Enumerator C.d__0.<>s__1""
+ IL_00e0: leave.s IL_00fc
}
catch System.Exception
{
// async: catch handler, sequence point:
- IL_00d7: stloc.s V_4
- IL_00d9: ldarg.0
- IL_00da: ldc.i4.s -2
- IL_00dc: stfld ""int C.d__0.<>1__state""
- IL_00e1: ldarg.0
- IL_00e2: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder""
- IL_00e7: ldloc.s V_4
- IL_00e9: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)""
- IL_00ee: nop
- IL_00ef: leave.s IL_0105
+ IL_00e2: stloc.s V_4
+ IL_00e4: ldarg.0
+ IL_00e5: ldc.i4.s -2
+ IL_00e7: stfld ""int C.d__0.<>1__state""
+ IL_00ec: ldarg.0
+ IL_00ed: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder""
+ IL_00f2: ldloc.s V_4
+ IL_00f4: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)""
+ IL_00f9: nop
+ IL_00fa: leave.s IL_0110
}
// sequence point: }
- IL_00f1: ldarg.0
- IL_00f2: ldc.i4.s -2
- IL_00f4: stfld ""int C.d__0.<>1__state""
+ IL_00fc: ldarg.0
+ IL_00fd: ldc.i4.s -2
+ IL_00ff: stfld ""int C.d__0.<>1__state""
// sequence point:
- IL_00f9: ldarg.0
- IL_00fa: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder""
- IL_00ff: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()""
- IL_0104: nop
- IL_0105: ret
-}", sequencePoints: "C+d__0.MoveNext", source: source + s_IAsyncEnumerable);
+ IL_0104: ldarg.0
+ IL_0105: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder""
+ IL_010a: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()""
+ IL_010f: nop
+ IL_0110: ret
+}
+", sequencePoints: "C+d__0.MoveNext", source: source + s_IAsyncEnumerable);
}
[Fact]
@@ -4219,7 +4225,7 @@ static async System.Threading.Tasks.Task Main()
verifier.VerifyIL("C.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @"
{
- // Code size 272 (0x110)
+ // Code size 283 (0x11b)
.maxstack 3
.locals init (int V_0,
System.Threading.CancellationToken V_1,
@@ -4234,124 +4240,129 @@ .locals init (int V_0,
{
// sequence point:
IL_0007: ldloc.0
- IL_0008: brfalse.s IL_000c
- IL_000a: br.s IL_0011
- IL_000c: br IL_0096
+ IL_0008: brfalse.s IL_0012
+ IL_000a: br.s IL_000c
+ IL_000c: ldloc.0
+ IL_000d: ldc.i4.1
+ IL_000e: beq.s IL_0017
+ IL_0010: br.s IL_001c
+ IL_0012: br IL_00a1
+ IL_0017: br IL_00a1
// sequence point: {
- IL_0011: nop
+ IL_001c: nop
// sequence point: ICollection c = new Collection();
- IL_0012: ldarg.0
- IL_0013: newobj ""Collection..ctor()""
- IL_0018: stfld ""ICollection C.d__0.5__1""
+ IL_001d: ldarg.0
+ IL_001e: newobj ""Collection..ctor()""
+ IL_0023: stfld ""ICollection C.d__0.5__1""
// sequence point: await foreach
- IL_001d: nop
+ IL_0028: nop
// sequence point: c
- IL_001e: ldarg.0
- IL_001f: ldarg.0
- IL_0020: ldfld ""ICollection C.d__0.5__1""
- IL_0025: ldloca.s V_1
- IL_0027: initobj ""System.Threading.CancellationToken""
- IL_002d: ldloc.1
- IL_002e: callvirt ""IMyAsyncEnumerator ICollection.GetAsyncEnumerator(System.Threading.CancellationToken)""
- IL_0033: stfld ""IMyAsyncEnumerator C.d__0.<>s__2""
+ IL_0029: ldarg.0
+ IL_002a: ldarg.0
+ IL_002b: ldfld ""ICollection C.d__0.5__1""
+ IL_0030: ldloca.s V_1
+ IL_0032: initobj ""System.Threading.CancellationToken""
+ IL_0038: ldloc.1
+ IL_0039: callvirt ""IMyAsyncEnumerator ICollection.GetAsyncEnumerator(System.Threading.CancellationToken)""
+ IL_003e: stfld ""IMyAsyncEnumerator C.d__0.<>s__2""
// sequence point:
- IL_0038: br.s IL_0058
+ IL_0043: br.s IL_0063
// sequence point: var i
- IL_003a: ldarg.0
- IL_003b: ldarg.0
- IL_003c: ldfld ""IMyAsyncEnumerator C.d__0.<>s__2""
- IL_0041: callvirt ""int IMyAsyncEnumerator.Current.get""
- IL_0046: stfld ""int C.d__0.5__3""
+ IL_0045: ldarg.0
+ IL_0046: ldarg.0
+ IL_0047: ldfld ""IMyAsyncEnumerator C.d__0.<>s__2""
+ IL_004c: callvirt ""int IMyAsyncEnumerator.Current.get""
+ IL_0051: stfld ""int C.d__0.5__3""
// sequence point: {
- IL_004b: nop
- // sequence point: Write($""Got "");
- IL_004c: ldstr ""Got ""
- IL_0051: call ""void System.Console.Write(string)""
IL_0056: nop
+ // sequence point: Write($""Got "");
+ IL_0057: ldstr ""Got ""
+ IL_005c: call ""void System.Console.Write(string)""
+ IL_0061: nop
// sequence point: }
- IL_0057: nop
+ IL_0062: nop
// sequence point: in
- IL_0058: ldarg.0
- IL_0059: ldfld ""IMyAsyncEnumerator C.d__0.<>s__2""
- IL_005e: callvirt ""System.Threading.Tasks.Task IMyAsyncEnumerator.MoveNextAsync()""
- IL_0063: callvirt ""System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()""
- IL_0068: stloc.2
+ IL_0063: ldarg.0
+ IL_0064: ldfld ""IMyAsyncEnumerator C.d__0.<>s__2""
+ IL_0069: callvirt ""System.Threading.Tasks.Task IMyAsyncEnumerator.MoveNextAsync()""
+ IL_006e: callvirt ""System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()""
+ IL_0073: stloc.2
// sequence point:
- IL_0069: ldloca.s V_2
- IL_006b: call ""bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get""
- IL_0070: brtrue.s IL_00b2
- IL_0072: ldarg.0
- IL_0073: ldc.i4.0
- IL_0074: dup
- IL_0075: stloc.0
- IL_0076: stfld ""int C.d__0.<>1__state""
+ IL_0074: ldloca.s V_2
+ IL_0076: call ""bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get""
+ IL_007b: brtrue.s IL_00bd
+ IL_007d: ldarg.0
+ IL_007e: ldc.i4.0
+ IL_007f: dup
+ IL_0080: stloc.0
+ IL_0081: stfld ""int C.d__0.<>1__state""
// async: yield
- IL_007b: ldarg.0
- IL_007c: ldloc.2
- IL_007d: stfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1""
- IL_0082: ldarg.0
- IL_0083: stloc.3
- IL_0084: ldarg.0
- IL_0085: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder""
- IL_008a: ldloca.s V_2
- IL_008c: ldloca.s V_3
- IL_008e: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, C.d__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__0)""
- IL_0093: nop
- IL_0094: leave.s IL_010f
+ IL_0086: ldarg.0
+ IL_0087: ldloc.2
+ IL_0088: stfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1""
+ IL_008d: ldarg.0
+ IL_008e: stloc.3
+ IL_008f: ldarg.0
+ IL_0090: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder""
+ IL_0095: ldloca.s V_2
+ IL_0097: ldloca.s V_3
+ IL_0099: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, C.d__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__0)""
+ IL_009e: nop
+ IL_009f: leave.s IL_011a
// async: resume
- IL_0096: ldarg.0
- IL_0097: ldfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1""
- IL_009c: stloc.2
- IL_009d: ldarg.0
- IL_009e: ldflda ""System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1""
- IL_00a3: initobj ""System.Runtime.CompilerServices.TaskAwaiter""
- IL_00a9: ldarg.0
- IL_00aa: ldc.i4.m1
- IL_00ab: dup
- IL_00ac: stloc.0
- IL_00ad: stfld ""int C.d__0.<>1__state""
- IL_00b2: ldarg.0
- IL_00b3: ldloca.s V_2
- IL_00b5: call ""bool System.Runtime.CompilerServices.TaskAwaiter.GetResult()""
- IL_00ba: stfld ""bool C.d__0.<>s__4""
- IL_00bf: ldarg.0
- IL_00c0: ldfld ""bool C.d__0.<>s__4""
- IL_00c5: brtrue IL_003a
+ IL_00a1: ldarg.0
+ IL_00a2: ldfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1""
+ IL_00a7: stloc.2
+ IL_00a8: ldarg.0
+ IL_00a9: ldflda ""System.Runtime.CompilerServices.TaskAwaiter C.d__0.<>u__1""
+ IL_00ae: initobj ""System.Runtime.CompilerServices.TaskAwaiter""
+ IL_00b4: ldarg.0
+ IL_00b5: ldc.i4.m1
+ IL_00b6: dup
+ IL_00b7: stloc.0
+ IL_00b8: stfld ""int C.d__0.<>1__state""
+ IL_00bd: ldarg.0
+ IL_00be: ldloca.s V_2
+ IL_00c0: call ""bool System.Runtime.CompilerServices.TaskAwaiter.GetResult()""
+ IL_00c5: stfld ""bool C.d__0.<>s__4""
IL_00ca: ldarg.0
- IL_00cb: ldnull
- IL_00cc: stfld ""IMyAsyncEnumerator C.d__0.<>s__2""
- IL_00d1: leave.s IL_00f4
+ IL_00cb: ldfld ""bool C.d__0.<>s__4""
+ IL_00d0: brtrue IL_0045
+ IL_00d5: ldarg.0
+ IL_00d6: ldnull
+ IL_00d7: stfld ""IMyAsyncEnumerator C.d__0.<>s__2""
+ IL_00dc: leave.s IL_00ff
}
catch System.Exception
{
// async: catch handler, sequence point:
- IL_00d3: stloc.s V_4
- IL_00d5: ldarg.0
- IL_00d6: ldc.i4.s -2
- IL_00d8: stfld ""int C.d__0.<>1__state""
- IL_00dd: ldarg.0
- IL_00de: ldnull
- IL_00df: stfld ""ICollection C.d__0.5__1""
- IL_00e4: ldarg.0
- IL_00e5: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder""
- IL_00ea: ldloc.s V_4
- IL_00ec: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)""
- IL_00f1: nop
- IL_00f2: leave.s IL_010f
+ IL_00de: stloc.s V_4
+ IL_00e0: ldarg.0
+ IL_00e1: ldc.i4.s -2
+ IL_00e3: stfld ""int C.d__0.<>1__state""
+ IL_00e8: ldarg.0
+ IL_00e9: ldnull
+ IL_00ea: stfld ""ICollection C.d__0.5__1""
+ IL_00ef: ldarg.0
+ IL_00f0: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder""
+ IL_00f5: ldloc.s V_4
+ IL_00f7: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)""
+ IL_00fc: nop
+ IL_00fd: leave.s IL_011a
}
// sequence point: }
- IL_00f4: ldarg.0
- IL_00f5: ldc.i4.s -2
- IL_00f7: stfld ""int C.d__0.<>1__state""
+ IL_00ff: ldarg.0
+ IL_0100: ldc.i4.s -2
+ IL_0102: stfld ""int C.d__0.<>1__state""
// sequence point:
- IL_00fc: ldarg.0
- IL_00fd: ldnull
- IL_00fe: stfld ""ICollection C.d__0.5__1""
- IL_0103: ldarg.0
- IL_0104: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder""
- IL_0109: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()""
- IL_010e: nop
- IL_010f: ret
+ IL_0107: ldarg.0
+ IL_0108: ldnull
+ IL_0109: stfld ""ICollection C.d__0.5__1""
+ IL_010e: ldarg.0
+ IL_010f: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder""
+ IL_0114: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()""
+ IL_0119: nop
+ IL_011a: ret
}", sequencePoints: "C+d__0.MoveNext", source: source);
}
diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs
index f90b37fa014a8..007e10002de4a 100644
--- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs
@@ -6417,6 +6417,897 @@ .locals init (int V_0,
}");
}
+ [Fact]
+ [WorkItem("https://github.com/dotnet/roslyn/issues/69805")]
+ public void UpdateAwaitForEach_AsyncDisposableEnumerator()
+ {
+ var source0 = MarkedSource(@"
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+class C
+{
+ async Task F()
+ {
+ await foreach (var x in Iterator())
+ {
+ Body(1);
+ }
+
+ End();
+ }
+
+ IAsyncEnumerable Iterator() => null;
+ static void Body(int x) {}
+ static void End() { }
+}
+");
+ var source1 = MarkedSource(@"
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+class C
+{
+ async Task F()
+ {
+ await foreach (var x in Iterator())
+ {
+ Body(2);
+ }
+
+ End();
+ }
+
+ IAsyncEnumerable Iterator() => null;
+ static void Body(int x) { }
+ static void End() { }
+}");
+ var asyncStreamsTree = Parse(
+ AsyncStreamsTypes, options: (CSharpParseOptions)source0.Tree.Options, filename: "AsyncStreams.cs");
+
+ var compilation0 = CreateCompilationWithTasksExtensions(new[] { source0.Tree, asyncStreamsTree }, options: ComSafeDebugDll);
+ var compilation1 = compilation0.WithSource([source1.Tree, asyncStreamsTree]);
+
+ var v0 = CompileAndVerify(compilation0);
+ v0.VerifyDiagnostics();
+ var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData);
+
+ var f0 = compilation0.GetMember("C.F");
+ var f1 = compilation1.GetMember("C.F");
+
+ var generation0 = EmitBaseline.CreateInitialBaseline(md0, v0.CreateSymReader().GetEncMethodDebugInfo);
+
+ v0.VerifyPdb("C.F", @"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+", options: PdbValidationOptions.ExcludeDocuments);
+
+ v0.VerifyMethodBody("C.d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @"
+{
+ // Code size 477 (0x1dd)
+ .maxstack 3
+ .locals init (int V_0,
+ System.Threading.CancellationToken V_1,
+ System.Runtime.CompilerServices.ValueTaskAwaiter V_2,
+ System.Threading.Tasks.ValueTask V_3,
+ C.d__0 V_4,
+ object V_5,
+ System.Runtime.CompilerServices.ValueTaskAwaiter V_6,
+ System.Threading.Tasks.ValueTask V_7,
+ System.Exception V_8)
+ // sequence point:
+ IL_0000: ldarg.0
+ IL_0001: ldfld ""int C.d__0.<>1__state""
+ IL_0006: stloc.0
+ .try
+ {
+ // sequence point:
+ IL_0007: ldloc.0
+ IL_0008: brfalse.s IL_0012
+ IL_000a: br.s IL_000c
+ IL_000c: ldloc.0
+ IL_000d: ldc.i4.1
+ IL_000e: beq.s IL_0014
+ IL_0010: br.s IL_0019
+ IL_0012: br.s IL_0048
+ IL_0014: br IL_0143
+ // sequence point: {
+ IL_0019: nop
+ // sequence point: await foreach
+ IL_001a: nop
+ // sequence point: Iterator()
+ IL_001b: ldarg.0
+ IL_001c: ldarg.0
+ IL_001d: ldfld ""C C.d__0.<>4__this""
+ IL_0022: call ""System.Collections.Generic.IAsyncEnumerable C.Iterator()""
+ IL_0027: ldloca.s V_1
+ IL_0029: initobj ""System.Threading.CancellationToken""
+ IL_002f: ldloc.1
+ IL_0030: callvirt ""System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)""
+ IL_0035: stfld ""System.Collections.Generic.IAsyncEnumerator C.d__0.<>s__1""
+ // sequence point:
+ IL_003a: ldarg.0
+ IL_003b: ldnull
+ IL_003c: stfld ""object C.d__0.<>s__2""
+ IL_0041: ldarg.0
+ IL_0042: ldc.i4.0
+ IL_0043: stfld ""int C.d__0.<>s__3""
+ // sequence point:
+ IL_0048: nop
+ .try
+ {
+ // sequence point:
+ IL_0049: ldloc.0
+ IL_004a: brfalse.s IL_004e
+ IL_004c: br.s IL_0050
+ IL_004e: br.s IL_00b1
+ // sequence point:
+ IL_0050: br.s IL_006c
+ // sequence point: var x
+ IL_0052: ldarg.0
+ IL_0053: ldarg.0
+ IL_0054: ldfld ""System.Collections.Generic.IAsyncEnumerator C.d__0.<>s__1""
+ IL_0059: callvirt ""int System.Collections.Generic.IAsyncEnumerator.Current.get""
+ IL_005e: stfld ""int C.d__0.5__4""
+ // sequence point: {
+ IL_0063: nop
+ // sequence point: Body(1);
+ IL_0064: ldc.i4.1
+ IL_0065: call ""void C.Body(int)""
+ IL_006a: nop
+ // sequence point: }
+ IL_006b: nop
+ // sequence point: in
+ IL_006c: ldarg.0
+ IL_006d: ldfld ""System.Collections.Generic.IAsyncEnumerator C.d__0.<>s__1""
+ IL_0072: callvirt ""System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()""
+ IL_0077: stloc.3
+ IL_0078: ldloca.s V_3
+ IL_007a: call ""System.Runtime.CompilerServices.ValueTaskAwaiter System.Threading.Tasks.ValueTask.GetAwaiter()""
+ IL_007f: stloc.2
+ // sequence point:
+ IL_0080: ldloca.s V_2
+ IL_0082: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter.IsCompleted.get""
+ IL_0087: brtrue.s IL_00cd
+ IL_0089: ldarg.0
+ IL_008a: ldc.i4.0
+ IL_008b: dup
+ IL_008c: stloc.0
+ IL_008d: stfld ""int C.d__0.<>1__state""
+ // async: yield
+ IL_0092: ldarg.0
+ IL_0093: ldloc.2
+ IL_0094: stfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.d__0.<>u__1""
+ IL_0099: ldarg.0
+ IL_009a: stloc.s V_4
+ IL_009c: ldarg.0
+ IL_009d: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__0.<>t__builder""
+ IL_00a2: ldloca.s V_2
+ IL_00a4: ldloca.s V_4
+ IL_00a6: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, C.d__0>(ref System.Runtime.CompilerServices.ValueTaskAwaiter, ref C.d__0)""
+ IL_00ab: nop
+ IL_00ac: leave IL_01dc
+ // async: resume
+ IL_00b1: ldarg.0
+ IL_00b2: ldfld ""System.Runtime.CompilerServices.ValueTaskAwaiter C.d__0.<>u__1""
+ IL_00b7: stloc.2
+ IL_00b8: ldarg.0
+ IL_00b9: ldflda ""System.Runtime.CompilerServices.ValueTaskAwaiter C.d__0.<>u__1""
+ IL_00be: initobj ""System.Runtime.CompilerServices.ValueTaskAwaiter""
+ IL_00c4: ldarg.0
+ IL_00c5: ldc.i4.m1
+ IL_00c6: dup
+ IL_00c7: stloc.0
+ IL_00c8: stfld ""int C.d__0.<>1__state""
+ IL_00cd: ldarg.0
+ IL_00ce: ldloca.s V_2
+ IL_00d0: call ""bool System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()""
+ IL_00d5: stfld ""bool C.d__0.<>s__5""
+ IL_00da: ldarg.0
+ IL_00db: ldfld ""bool C.d__0.<>s__5""
+ IL_00e0: brtrue IL_0052
+ // sequence point:
+ IL_00e5: leave.s IL_00f3
+ }
+ catch object
+ {
+ // sequence point:
+ IL_00e7: stloc.s V_5
+ IL_00e9: ldarg.0
+ IL_00ea: ldloc.s V_5
+ IL_00ec: stfld ""object C.