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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ namespace Microsoft.CodeAnalysis.CSharp
/// <summary>
/// Structure containing all semantic information about an await expression.
/// </summary>
// https://github.com/dotnet/roslyn/issues/79818: Add runtime async info
public readonly struct AwaitExpressionInfo : IEquatable<AwaitExpressionInfo>
{
public IMethodSymbol? GetAwaiterMethod { get; }
Expand All @@ -21,11 +20,30 @@ namespace Microsoft.CodeAnalysis.CSharp

public bool IsDynamic { get; }

internal AwaitExpressionInfo(IMethodSymbol getAwaiter, IPropertySymbol isCompleted, IMethodSymbol getResult, bool isDynamic)
/// <summary>
/// When runtime async is enabled for this await expression, this represents either:
/// <list type="bullet">
/// <item>
/// A call to <c>System.Runtime.CompilerServices.AsyncHelpers.Await</c>, if this is a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a "call", it's a method symbol

/// supported task type. In such cases, <see cref="GetAwaiterMethod" />,
/// <see cref="IsCompletedProperty" />, and <see cref="GetResultMethod" /> will be
/// <see langword="null" />.
/// </item>
/// <item>
/// A call to <c>System.Runtime.CompilerServices.AsyncHelpers.AwaitAwaiter|UnsafeAwaitAwaiter</c>.
/// In these cases, the other properties may be non-<see langword="null" /> if the
Copy link
Member

@jcouv jcouv Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume you mean that GetAwaiterMethod/IsCompletedProperty/GetResultMethod may be set in runtime-async scenario. Looking at the logic in GetAwaitableExpressionInfo, we don't set those in runtime-async scenario (since we won't need them). Why not make this behavior official in the public API? The behavior we have seems better than the behavior this comment describes. #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the logic in GetAwaitableExpressionInfo, we don't set those in runtime-async scenario (since we won't need them).

We do set them in this case, because we will need them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, I misread. We don't need the other members for System.Runtime.CompilerServices.AsyncHelpers.Await case, but we do need them for the AwaitAwiter/UnsafeAwaitAwaiter cases. Thanks

/// the rest of the await expression is successfully bound.
/// </item>
/// </list>
/// </summary>
public IMethodSymbol? RuntimeAwaitMethod { get; }

internal AwaitExpressionInfo(IMethodSymbol? getAwaiter, IPropertySymbol? isCompleted, IMethodSymbol? getResult, IMethodSymbol? runtimeAwaitMethod, bool isDynamic)
{
GetAwaiterMethod = getAwaiter;
IsCompletedProperty = isCompleted;
GetResultMethod = getResult;
RuntimeAwaitMethod = runtimeAwaitMethod;
Copy link
Member

@jcouv jcouv Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider asserting that we either set runtimeAwaitMethod or the members related to non-runtime-async #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a thing that can be asserted.

IsDynamic = isDynamic;
}

Expand All @@ -39,12 +57,13 @@ public bool Equals(AwaitExpressionInfo other)
return object.Equals(this.GetAwaiterMethod, other.GetAwaiterMethod)
&& object.Equals(this.IsCompletedProperty, other.IsCompletedProperty)
&& object.Equals(this.GetResultMethod, other.GetResultMethod)
&& object.Equals(this.RuntimeAwaitMethod, other.RuntimeAwaitMethod)
&& IsDynamic == other.IsDynamic;
}

public override int GetHashCode()
{
return Hash.Combine(GetAwaiterMethod, Hash.Combine(IsCompletedProperty, Hash.Combine(GetResultMethod, IsDynamic.GetHashCode())));
return Hash.Combine(GetAwaiterMethod, Hash.Combine(IsCompletedProperty, Hash.Combine(GetResultMethod, Hash.Combine(RuntimeAwaitMethod, IsDynamic.GetHashCode()))));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,7 @@ public override AwaitExpressionInfo GetAwaitExpressionInfo(AwaitExpressionSyntax
getAwaiter: (IMethodSymbol)awaitableInfo.GetAwaiter?.ExpressionSymbol.GetPublicSymbol(),
isCompleted: awaitableInfo.IsCompleted.GetPublicSymbol(),
getResult: awaitableInfo.GetResult.GetPublicSymbol(),
runtimeAwaitMethod: awaitableInfo.RuntimeAsyncAwaitCall?.Method.GetPublicSymbol(),
isDynamic: awaitableInfo.IsDynamic);
}

Expand Down
1 change: 1 addition & 0 deletions src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
abstract Microsoft.CodeAnalysis.CSharp.InterceptableLocation.Equals(Microsoft.CodeAnalysis.CSharp.InterceptableLocation? other) -> bool
Microsoft.CodeAnalysis.CSharp.AwaitExpressionInfo.RuntimeAwaitMethod.get -> Microsoft.CodeAnalysis.IMethodSymbol?
Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp14 = 1400 -> Microsoft.CodeAnalysis.CSharp.LanguageVersion
Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax.AddAttributeLists(params Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax![]! items) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionBlockDeclarationSyntax!
Expand Down
62 changes: 31 additions & 31 deletions src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public static void Main()
var expected = @"";
CompileAndVerify(source, expectedOutput: expected);

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.FailsPEVerify);
verifier.VerifyDiagnostics(
// (3,1): hidden CS8019: Unnecessary using directive.
Expand Down Expand Up @@ -579,7 +579,7 @@ .locals init (int V_0,
}
");

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true),
verify: Verification.Fails with
{
Expand Down Expand Up @@ -628,7 +628,7 @@ public static async Task<T> Goo<T>() where T : Exception
";
CompileAndVerify(source, expectedOutput: "3");

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("3", isRuntimeAsync: true), verify: Verification.Fails with
{
ILVerifyMessage = """
Expand Down Expand Up @@ -677,7 +677,7 @@ public static void Main()
";
CompileAndVerify(source, expectedOutput: expected);

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(
comp,
expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true),
Expand Down Expand Up @@ -731,7 +731,7 @@ public static void Main()
";
CompileAndVerify(source, expectedOutput: expected);

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with
{
ILVerifyMessage = """
Expand Down Expand Up @@ -894,7 +894,7 @@ .locals init (int V_0,
}
");

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true),
verify: Verification.Fails with
{
Expand Down Expand Up @@ -1000,7 +1000,7 @@ public static void Main()

CompileAndVerify(source, expectedOutput: expected);

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with
{
ILVerifyMessage = """
Expand Down Expand Up @@ -1359,7 +1359,7 @@ .locals init (int V_0,
IL_01cc: ret
}", sequencePoints: "Test+<G>d__1.MoveNext");

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with
{
ILVerifyMessage = """
Expand Down Expand Up @@ -1479,7 +1479,7 @@ public static void Main()
";
CompileAndVerify(source, expectedOutput: expected);

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true),
verify: Verification.Fails with
{
Expand Down Expand Up @@ -1765,7 +1765,7 @@ .locals init (int V_0,
}
""");

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with
{
ILVerifyMessage = """
Expand Down Expand Up @@ -2043,7 +2043,7 @@ .locals init (int V_0,
}
""");

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with
{
ILVerifyMessage = """
Expand Down Expand Up @@ -2296,7 +2296,7 @@ .locals init (int V_0,
}
""");

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with
{
ILVerifyMessage = """
Expand Down Expand Up @@ -2377,7 +2377,7 @@ public static void Main()
var expected = "4";
CompileAndVerify(source, expected);

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true), verify: Verification.Fails with
{
ILVerifyMessage = """
Expand Down Expand Up @@ -2504,7 +2504,7 @@ public static void Main()
var expected = @"15";
CompileAndVerify(source, expectedOutput: expected);

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true),
verify: Verification.Fails with
{
Expand Down Expand Up @@ -2592,7 +2592,7 @@ public static void Main()
15";
CompileAndVerify(source, expectedOutput: expected);

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true),
verify: Verification.Fails with
{
Expand Down Expand Up @@ -2684,7 +2684,7 @@ public static void Main()
15";
CompileAndVerify(source, expectedOutput: expected);

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true),
verify: Verification.Fails with
{
Expand Down Expand Up @@ -2847,7 +2847,7 @@ .locals init (int V_0,
}
");

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(
comp,
expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true),
Expand Down Expand Up @@ -2964,7 +2964,7 @@ Attempted to divide by zero.
";
CompileAndVerify(source, expectedOutput: expected);

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true),
verify: Verification.Fails with
{
Expand Down Expand Up @@ -3044,7 +3044,7 @@ Attempted to divide by zero.
";
CompileAndVerify(source, expectedOutput: expected);

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true),
verify: Verification.Fails with
{
Expand Down Expand Up @@ -3129,7 +3129,7 @@ Attempted to divide by zero.
";
CompileAndVerify(source, expectedOutput: expected);

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true),
verify: Verification.Fails with
{
Expand Down Expand Up @@ -3194,7 +3194,7 @@ public static void Main()
";
CompileAndVerify(source, expectedOutput: expected);

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true),
verify: Verification.Fails with
{
Expand Down Expand Up @@ -3374,7 +3374,7 @@ public static void Main()
";
CompileAndVerify(source, expectedOutput: expected);

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true),
verify: Verification.Fails with
{
Expand Down Expand Up @@ -3466,7 +3466,7 @@ public static void Main()
";
CompileAndVerify(source, expectedOutput: expected);

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true),
verify: Verification.Fails with
{
Expand Down Expand Up @@ -3561,7 +3561,7 @@ public static void Main()
";
CompileAndVerify(source, expectedOutput: expected);

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true),
verify: Verification.Fails with
{
Expand Down Expand Up @@ -3608,7 +3608,7 @@ static void Main()
";
CompileAndVerify(source, expected);

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(
comp,
expectedOutput: CodeGenAsyncTests.ExpectedOutput(expected, isRuntimeAsync: true),
Expand Down Expand Up @@ -3686,7 +3686,7 @@ 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 comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true),
verify: Verification.Fails with
{
Expand Down Expand Up @@ -3797,7 +3797,7 @@ 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 comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true),
verify: Verification.Fails with
{
Expand Down Expand Up @@ -3895,7 +3895,7 @@ async Task M2(string caller, Exception ex)
[M2]: Return value missing on the stack. { Offset = 0x3f }
""";

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with { ILVerifyMessage = ilVerifyMessage });
verifier.VerifyDiagnostics();
}
Expand Down Expand Up @@ -3979,7 +3979,7 @@ class Exception2 : Exception { }
CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: expectedOutput,
targetFramework: TargetFramework.Mscorlib46).VerifyDiagnostics();

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
var ilVerifyMessage = (await1, await2) switch
{
(true, true) => """
Expand Down Expand Up @@ -4064,7 +4064,7 @@ 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 comp = 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 = 0x1d }"
Expand Down Expand Up @@ -4779,7 +4779,7 @@ public async IAsyncEnumerator<int> 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 comp = CreateRuntimeAsyncCompilation(source);
var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Skipped);
verifier.VerifyDiagnostics();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ public static void Main()
";
CompileAndVerify(source, expectedOutput: expected, references: new[] { CSharpRef });

var comp = CodeGenAsyncTests.CreateRuntimeAsyncCompilation(source);
var comp = CreateRuntimeAsyncCompilation(source);
comp.VerifyEmitDiagnostics(
// (9,16): error CS9328: Method 'Test.F(dynamic)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'.
// return await t;
Expand Down
Loading