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 @@ -1756,18 +1756,22 @@ internal override System.Reflection.MethodImplAttributes ImplementationAttribute
result |= (System.Reflection.MethodImplAttributes.Runtime | System.Reflection.MethodImplAttributes.InternalCall);
}

if (this.IsAsync && this.DeclaringCompilation.IsRuntimeAsyncEnabledIn(this))
{
// https://github.com/dotnet/roslyn/issues/79792: Use real value from MethodImplAttributes when available
// When a method is emitted using runtime async, we add MethodImplAttributes.Async to indicate to the
// runtime to generate the state machine
result |= (System.Reflection.MethodImplAttributes)0x2000;
}
AddAsyncImplAttributeIfNeeded(ref result);

return result;
}
}

protected void AddAsyncImplAttributeIfNeeded(ref System.Reflection.MethodImplAttributes result)
{
if (this.IsAsync && this.DeclaringCompilation.IsRuntimeAsyncEnabledIn(this))
{
// When a method is emitted using runtime async, we add MethodImplAttributes.Async to indicate to the
// runtime to generate the state machine
result |= System.Reflection.MethodImplAttributes.Async;
}
}

internal override int TryGetOverloadResolutionPriority()
=> GetEarlyDecodedWellKnownAttributeData()?.OverloadResolutionPriority ?? 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,12 @@ public override string Name

internal override System.Reflection.MethodImplAttributes ImplementationAttributes
{
get { return default(System.Reflection.MethodImplAttributes); }
get
{
var attributes = default(System.Reflection.MethodImplAttributes);
AddAsyncImplAttributeIfNeeded(ref attributes);
return attributes;
}
}

public override ImmutableArray<TypeParameterSymbol> TypeParameters
Expand Down
23 changes: 10 additions & 13 deletions src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen
[CompilerTrait(CompilerFeature.Async)]
public class CodeGenAsyncTests : EmitMetadataTestBase
{
// https://github.com/dotnet/roslyn/issues/79792: Use the real value when possible
private const MethodImplAttributes MethodImplOptionsAsync = (MethodImplAttributes)0x2000;

internal static string ExpectedOutput(string output, bool isRuntimeAsync = false)
{
return ExecutionConditionUtil.IsMonoOrCoreClr
Expand Down Expand Up @@ -332,7 +329,7 @@ void verify(ModuleSymbol module)
{
var test = module.ContainingAssembly.GetTypeByMetadataName("Test");
var f = test.GetMethod("F");
Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes);
Assert.Equal(MethodImplAttributes.Async, f.ImplementationAttributes);
AssertEx.Equal(["<>c"], test.GetTypeMembers().SelectAsArray(t => t.Name));
}
}
Expand Down Expand Up @@ -396,7 +393,7 @@ void verify(ModuleSymbol module)
{
var test = module.ContainingAssembly.GetTypeByMetadataName("Test");
var f = test.GetMethod("F");
Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes);
Assert.Equal(MethodImplAttributes.Async, f.ImplementationAttributes);
Assert.Empty(test.GetTypeMembers());
}
}
Expand Down Expand Up @@ -459,7 +456,7 @@ void verify(ModuleSymbol module)
{
var test = module.ContainingAssembly.GetTypeByMetadataName("Test");
var f = test.GetMethod("F");
Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes);
Assert.Equal(MethodImplAttributes.Async, f.ImplementationAttributes);
AssertEx.Equal(["<>c"], test.GetTypeMembers().SelectAsArray(t => t.Name));
}
}
Expand Down Expand Up @@ -512,7 +509,7 @@ void verify(ModuleSymbol module)
{
var test = module.ContainingAssembly.GetTypeByMetadataName("Test");
var f = test.GetMethod("F");
Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes);
Assert.Equal(MethodImplAttributes.Async, f.ImplementationAttributes);
AssertEx.Empty(test.GetTypeMembers());
}
}
Expand Down Expand Up @@ -632,7 +629,7 @@ void verify(ModuleSymbol module)
{
var test = module.ContainingAssembly.GetTypeByMetadataName("Test");
var f = test.GetMethod("F");
Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes);
Assert.Equal(MethodImplAttributes.Async, f.ImplementationAttributes);
if (!useValueTask && useGeneric)
{
AssertEx.Equal(["<>c"], test.GetTypeMembers().SelectAsArray(t => t.Name));
Expand Down Expand Up @@ -767,7 +764,7 @@ void verify(ModuleSymbol module)
{
var test = module.ContainingAssembly.GetTypeByMetadataName("Test");
var f = test.GetMethod("F");
Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes);
Assert.Equal(MethodImplAttributes.Async, f.ImplementationAttributes);
AssertEx.Empty(test.GetTypeMembers());
}
}
Expand Down Expand Up @@ -887,7 +884,7 @@ void verify(ModuleSymbol module)
{
var test = module.ContainingAssembly.GetTypeByMetadataName("Test");
var f = test.GetMethod("F");
Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes);
Assert.Equal(MethodImplAttributes.Async, f.ImplementationAttributes);
AssertEx.Empty(test.GetTypeMembers());
}
}
Expand Down Expand Up @@ -1019,7 +1016,7 @@ void verify(ModuleSymbol module)
{
var test = module.ContainingAssembly.GetTypeByMetadataName("Test");
var f = test.GetMethod("F");
Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes);
Assert.Equal(MethodImplAttributes.Async, f.ImplementationAttributes);
AssertEx.Empty(test.GetTypeMembers());
}
}
Expand Down Expand Up @@ -1130,7 +1127,7 @@ void verify(ModuleSymbol module)
{
var test = module.ContainingAssembly.GetTypeByMetadataName("Test");
var f = test.GetMethod("F");
Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes);
Assert.Equal(MethodImplAttributes.Async, f.ImplementationAttributes);
AssertEx.Empty(test.GetTypeMembers());
}
}
Expand Down Expand Up @@ -1238,7 +1235,7 @@ void verify(ModuleSymbol module)
{
var test = module.ContainingAssembly.GetTypeByMetadataName("Test");
var f = test.GetMethod("F");
Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes);
Assert.Equal(MethodImplAttributes.Async, f.ImplementationAttributes);
AssertEx.Empty(test.GetTypeMembers());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1104,7 +1104,7 @@ public void LocalDeclarationStatement_17()
CompileAndVerify(comp, expectedOutput: "Hi!");
}

[Fact]
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80313")]
public void LocalDeclarationStatement_18()
{
var text = @"
Expand Down Expand Up @@ -1133,6 +1133,50 @@ public void LocalDeclarationStatement_18()

comp = CreateCompilation(text, options: TestOptions.DebugExe);
comp.VerifyEmitDiagnostics();

comp = CreateRuntimeAsyncCompilation(text);
// https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output
var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with
{
ILVerifyMessage = "[<Main>$]: Return value missing on the stack. { Offset = 0x2f }"
}, sourceSymbolValidator: validator);
verifier.VerifyIL("<top-level-statements-entry-point>", """
{
// Code size 48 (0x30)
.maxstack 1
.locals init (int V_0, //c
System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1,
System.Runtime.CompilerServices.YieldAwaitable V_2)
IL_0000: ldc.i4.s -100
IL_0002: stloc.0
IL_0003: ldloca.s V_0
IL_0005: ldind.i4
IL_0006: call "void System.Console.Write(int)"
IL_000b: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()"
IL_0010: stloc.2
IL_0011: ldloca.s V_2
IL_0013: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()"
IL_0018: stloc.1
IL_0019: ldloca.s V_1
IL_001b: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get"
IL_0020: brtrue.s IL_0028
IL_0022: ldloc.1
IL_0023: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter<System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter>(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)"
IL_0028: ldloca.s V_1
IL_002a: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()"
IL_002f: ret
}
""");

static void validator(ModuleSymbol module)
{
var program = module.GlobalNamespace.GetTypeMember("Program");
Assert.NotNull(program);

var main = program.GetMethod("<Main>$");
Assert.NotNull(main);
Assert.Equal(MethodImplAttributes.Async, main.ImplementationAttributes & MethodImplAttributes.Async);
}
}

[Fact]
Expand Down Expand Up @@ -7825,7 +7869,7 @@ public void Return_03()
}
}

[Fact]
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80313")]
public void Return_04()
{
var text = @"
Expand Down Expand Up @@ -7893,6 +7937,51 @@ public void Return_04()
</methods>
</symbols>", options: PdbValidationOptions.SkipConversionValidation);
}

comp = CreateRuntimeAsyncCompilation(text);
// https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output
var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with
{
ILVerifyMessage = "[<Main>$]: Unexpected type on the stack. { Offset = 0x43, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1<int32>' }"
}, sourceSymbolValidator: validator);

verifier.VerifyIL("<top-level-statements-entry-point>", """
{
// Code size 68 (0x44)
.maxstack 3
IL_0000: ldstr "hello "
IL_0005: call "void System.Console.Write(string)"
IL_000a: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get"
IL_000f: ldsfld "System.Func<int> Program.<>c.<>9__0_0"
IL_0014: dup
IL_0015: brtrue.s IL_002e
IL_0017: pop
IL_0018: ldsfld "Program.<>c Program.<>c.<>9"
IL_001d: ldftn "int Program.<>c.<<Main>$>b__0_0()"
IL_0023: newobj "System.Func<int>..ctor(object, System.IntPtr)"
IL_0028: dup
IL_0029: stsfld "System.Func<int> Program.<>c.<>9__0_0"
IL_002e: callvirt "System.Threading.Tasks.Task<int> System.Threading.Tasks.TaskFactory.StartNew<int>(System.Func<int>)"
IL_0033: call "int System.Runtime.CompilerServices.AsyncHelpers.Await<int>(System.Threading.Tasks.Task<int>)"
IL_0038: pop
IL_0039: ldarg.0
IL_003a: ldc.i4.0
IL_003b: ldelem.ref
IL_003c: call "void System.Console.Write(string)"
IL_0041: ldc.i4.s 11
IL_0043: ret
}
""");

static void validator(ModuleSymbol module)
{
var program = module.GlobalNamespace.GetTypeMember("Program");
Assert.NotNull(program);

var main = program.GetMethod("<Main>$");
Assert.NotNull(main);
Assert.Equal(MethodImplAttributes.Async, main.ImplementationAttributes & MethodImplAttributes.Async);
}
}

[Fact]
Expand Down
26 changes: 26 additions & 0 deletions src/Compilers/Core/Portable/MethodImplExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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 System.Reflection;
using System.Runtime.CompilerServices;

namespace Microsoft.CodeAnalysis;

internal static class MethodImplAttributeExtensions
{
extension(MethodImplAttributes)
{
// https://github.com/dotnet/roslyn/issues/79792: Use the real value when possible
public static MethodImplAttributes Async => (MethodImplAttributes)0x2000;
}
}

internal static class MethodImplOptionsExtensions
{
extension(MethodImplOptions)
{
// https://github.com/dotnet/roslyn/issues/79792: Use the real value when possible
public static MethodImplOptions Async => (MethodImplOptions)0x2000;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -427,17 +427,15 @@ internal static void DecodeMethodImplAttribute<T, TAttributeSyntaxNode, TAttribu
options = options & ~(MethodImplOptions)3;
}

// https://github.com/dotnet/roslyn/issues/79792: Use the real value when possible
const MethodImplOptions MethodImplOptionsAsync = (MethodImplOptions)0x2000;
if ((options & MethodImplOptionsAsync) != 0)
if ((options & MethodImplOptions.Async) != 0)
{
// Error if [MethodImpl(MethodImplOptions.Async)] is used directly on a method
// We give an exception to the AsyncHelpers special type, as it manually implements the pattern as part of the
// runtime's async support
if ((InternalSpecialType)appliedToSymbol.ExtendedSpecialType != InternalSpecialType.System_Runtime_CompilerServices_AsyncHelpers)
{
arguments.Diagnostics.Add(messageProvider.CreateDiagnostic(messageProvider.ERR_MethodImplAttributeAsyncCannotBeUsed, arguments.AttributeSyntaxOpt.Location));
options &= ~MethodImplOptionsAsync;
options &= ~MethodImplOptions.Async;
}
}
}
Expand Down