diff --git a/eng/Versions.props b/eng/Versions.props index 402c63052d6..14961511ea5 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -151,8 +151,8 @@ 2.16.5 1.0.31 4.3.0-1.22220.8 - - 5.0.0-preview.7.20364.11 + + 10.0.0 5.0.0-preview.7.20364.11 17.14.1 13.0.3 diff --git a/src/Compiler/AbstractIL/il.fs b/src/Compiler/AbstractIL/il.fs index 5d7848f246e..fe67991f80a 100644 --- a/src/Compiler/AbstractIL/il.fs +++ b/src/Compiler/AbstractIL/il.fs @@ -2113,6 +2113,11 @@ type ILMethodDef x.ImplAttributes &&& MethodImplAttributes.AggressiveInlining <> enum 0 member x.IsMustRun = x.ImplAttributes &&& MethodImplAttributes.NoOptimization <> enum 0 + + // Async is defined as 0x2000 or 8192 + // https://github.com/dotnet/runtime/blob/main/docs/design/specs/runtime-async.md + member x.IsAsync = + x.ImplAttributes &&& enum (0x2000) <> enum 0 member x.WithSpecialName = x.With(attributes = (x.Attributes ||| MethodAttributes.SpecialName)) @@ -2170,6 +2175,9 @@ type ILMethodDef |> conditionalAdd condition MethodImplAttributes.AggressiveInlining) ) + member x.WithAsync(condition) = + x.With(implAttributes = (x.ImplAttributes |> conditionalAdd condition (enum 0x2000))) + member x.WithRuntime(condition) = x.With(implAttributes = (x.ImplAttributes |> conditionalAdd condition MethodImplAttributes.Runtime)) diff --git a/src/Compiler/AbstractIL/il.fsi b/src/Compiler/AbstractIL/il.fsi index 3d6f88bb6ca..4a927a61e95 100644 --- a/src/Compiler/AbstractIL/il.fsi +++ b/src/Compiler/AbstractIL/il.fsi @@ -1157,6 +1157,9 @@ type ILMethodDef = /// SafeHandle finalizer must be run. member IsMustRun: bool + /// https://github.com/dotnet/runtime/blob/main/docs/design/specs/runtime-async.md + member IsAsync: bool + /// Functional update of the value member internal With: ?name: string * @@ -1200,6 +1203,8 @@ type ILMethodDef = member internal WithAggressiveInlining: bool -> ILMethodDef + member internal WithAsync: bool -> ILMethodDef + member internal WithRuntime: bool -> ILMethodDef /// Tables of methods. Logically equivalent to a list of methods but diff --git a/src/Compiler/AbstractIL/ilprint.fs b/src/Compiler/AbstractIL/ilprint.fs index 12e421f6829..abb3bd7a6f9 100644 --- a/src/Compiler/AbstractIL/ilprint.fs +++ b/src/Compiler/AbstractIL/ilprint.fs @@ -610,6 +610,9 @@ let goutput_mbody is_entrypoint env os (md: ILMethodDef) = output_string os "native " elif md.ImplAttributes &&& MethodImplAttributes.IL <> enum 0 then output_string os "cil " + if md.IsAsync then + output_string os "async " + else output_string os "runtime " @@ -728,6 +731,7 @@ let goutput_mdef env os (md: ILMethodDef) = if md.IsAggressiveInline then output_string os "aggressiveinlining " + (goutput_mbody is_entrypoint menv) os md output_string os "\n" diff --git a/src/Compiler/CodeGen/IlxGen.fs b/src/Compiler/CodeGen/IlxGen.fs index 553e6ac9aa1..49028db0c29 100644 --- a/src/Compiler/CodeGen/IlxGen.fs +++ b/src/Compiler/CodeGen/IlxGen.fs @@ -9148,7 +9148,8 @@ and ComputeMethodImplAttribs cenv (_v: Val) attrs = let hasSynchronizedImplFlag = (implflags &&& 0x20) <> 0x0 let hasNoInliningImplFlag = (implflags &&& 0x08) <> 0x0 let hasAggressiveInliningImplFlag = (implflags &&& 0x0100) <> 0x0 - hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningImplFlag, hasAggressiveInliningImplFlag, attrs + let hasAsyncImplFlag = (implflags &&& 0x2000) <> 0x0 + hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningImplFlag, hasAggressiveInliningImplFlag, hasAsyncImplFlag, attrs and GenMethodForBinding cenv @@ -9332,7 +9333,7 @@ and GenMethodForBinding | _ -> [], None // check if the hasPreserveSigNamedArg and hasSynchronizedImplFlag implementation flags have been specified - let hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningFlag, hasAggressiveInliningImplFlag, attrs = + let hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningFlag, hasAggressiveInliningImplFlag, hasAsyncImplFlag, attrs = ComputeMethodImplAttribs cenv v attrs let securityAttributes, attrs = @@ -9607,6 +9608,7 @@ and GenMethodForBinding .WithSynchronized(hasSynchronizedImplFlag) .WithNoInlining(hasNoInliningFlag) .WithAggressiveInlining(hasAggressiveInliningImplFlag) + .WithAsync(hasAsyncImplFlag) .With(isEntryPoint = isExplicitEntryPoint, securityDecls = secDecls) let mdef = @@ -10673,7 +10675,7 @@ and GenAbstractBinding cenv eenv tref (vref: ValRef) = let memberInfo = Option.get vref.MemberInfo let attribs = vref.Attribs - let hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningFlag, hasAggressiveInliningImplFlag, attribs = + let hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningFlag, hasAggressiveInliningImplFlag, hasAsyncImplFlag, attribs = ComputeMethodImplAttribs cenv vref.Deref attribs if memberInfo.MemberFlags.IsDispatchSlot && not memberInfo.IsImplemented then @@ -10727,6 +10729,7 @@ and GenAbstractBinding cenv eenv tref (vref: ValRef) = .WithSynchronized(hasSynchronizedImplFlag) .WithNoInlining(hasNoInliningFlag) .WithAggressiveInlining(hasAggressiveInliningImplFlag) + .WithAsync(hasAsyncImplFlag) match memberInfo.MemberFlags.MemberKind with | SynMemberKind.ClassConstructor diff --git a/temp_csharp/Program.cs b/temp_csharp/Program.cs new file mode 100644 index 00000000000..78d65cf450e --- /dev/null +++ b/temp_csharp/Program.cs @@ -0,0 +1,11 @@ +using System.Runtime.CompilerServices; + +public class Test +{ + public async Task AsyncMethod() + { + await Task.Delay(1000); + + return 42; + } +} diff --git a/temp_csharp/copilot-instructions.md b/temp_csharp/copilot-instructions.md new file mode 100644 index 00000000000..5bd9dd536d2 --- /dev/null +++ b/temp_csharp/copilot-instructions.md @@ -0,0 +1,28 @@ +# GitHub Copilot Instructions for csharp_asyncruntime.csproj + +This project uses the new async/await patterns as specified in the .NET runtime design document: + +https://github.com/dotnet/runtime/blob/main/docs/design/specs/runtime-async.md + +To use the feature, a number of flags need to be enabled: + +- The project must target net10.0 (note: running on net10.0 is not sufficient, it must compile against net10.0 to see the value) +- true must be set in the project file +- $(Features);runtime-async=on must be set in the project file +- When running, the environment variable DOTNET_RuntimeAsync=1 must be set + +This project has a special target called "Decompile" that will decompile the emitted IL back to C# code so you can see the emitted IL patterns. + +The output will end up in `artifacts\bin\csharp_asyncruntime\Debug\net10.0\csharp_asyncruntime.il`. + +Notably, you will see the new async IL patterns as specified in the design document. + +- `AsyncMethod() cil managed async` indicates that the method is async. +- Async methods also do not have matching return type conventions as sync methods. For sync methods, the stack should contain a value convertible to the stated return type before the ret instruction. For async methods, the stack should be empty in the case of Task or ValueTask, or the type argument in the case of Task or ValueTask. + +Use this project as a reference for how the IL should look when emitting async methods from C# code. + +### Relevant commands + +`dotnet build` - builds the project +`dotnet run` - runs the project diff --git a/temp_csharp/csharp_asyncruntime.csproj b/temp_csharp/csharp_asyncruntime.csproj new file mode 100644 index 00000000000..4e64b9c18f8 --- /dev/null +++ b/temp_csharp/csharp_asyncruntime.csproj @@ -0,0 +1,24 @@ + + + + net10.0 + csharp_asyncruntime + enable + enable + true + $(Features);runtime-async=on + $(NoWarn);SYSLIB5007 + + + + + C:\Users\jimmy\.nuget\packages\runtime.win-x64.microsoft.netcore.ildasm\10.0.0-rc.2.25502.107\runtime.win-x64.microsoft.netcore.ildasm.10.0.0-rc.2.25502.107\runtimes\win-x64\native\ildasm.exe + + + + + + + + + diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/MethodImplAttribute/MethodImplAttribute.Async.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/MethodImplAttribute/MethodImplAttribute.Async.fs new file mode 100644 index 00000000000..8d130eab93d --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/MethodImplAttribute/MethodImplAttribute.Async.fs @@ -0,0 +1,7 @@ +// Regression test for DevDiv:212424 +// "NoInlining attribute not emitted into IL" +module M +open System.Threading.Tasks +open System.Runtime.CompilerServices +[] +let getUnit (f : unit -> Task) = AsyncHelpers.Await(f()) \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/MethodImplAttribute/MethodImplAttribute.Async.fs.il.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/MethodImplAttribute/MethodImplAttribute.Async.fs.il.bsl new file mode 100644 index 00000000000..54f1c3e5d66 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/MethodImplAttribute/MethodImplAttribute.Async.fs.il.bsl @@ -0,0 +1,67 @@ + + + + + +.assembly extern runtime { } +.assembly extern FSharp.Core { } +.assembly assembly +{ + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.FSharpInterfaceDataVersionAttribute::.ctor(int32, + int32, + int32) = ( 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 ) + + + + + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module assembly.exe + +.imagebase {value} +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 +.corflags 0x00000001 + + + + + +.class public abstract auto ansi sealed M + extends [runtime]System.Object +{ + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) + .method public static void getUnit(class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2> f) cil managed async + { + + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldnull + IL_0002: callvirt instance !1 class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>::Invoke(!0) + IL_0007: call !!0 [runtime]System.Runtime.CompilerServices.AsyncHelpers::Await(class [runtime]System.Threading.Tasks.Task`1) + IL_000c: pop + IL_000d: ret + } + +} + +.class private abstract auto ansi sealed ''.$M + extends [runtime]System.Object +{ + .method public static void main@() cil managed + { + .entrypoint + + .maxstack 8 + IL_0000: ret + } + +} + + + + + + diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/MethodImplAttribute/MethodImplAttribute.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/MethodImplAttribute/MethodImplAttribute.fs index adbf38b92d6..29df42c2801 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/MethodImplAttribute/MethodImplAttribute.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/MethodImplAttribute/MethodImplAttribute.fs @@ -60,6 +60,15 @@ module MethodImplAttribute = compilation |> getCompilation |> verifyCompilation + + + // SOURCE=MethodImplAttribute.Async.fs SCFLAGS="-a -g --optimize-" COMPILE_ONLY=1 POSTCMD="..\\CompareIL.cmd MethodImplAttribute.Async.dll" # MethodImplAttribute.Async.fs + [] + let ``Async_fs`` compilation = + compilation + |> getCompilation + |> verifyCompilation + // SOURCE=MethodImplAttribute.NoOptimization.fs SCFLAGS="-a -g --optimize-" COMPILE_ONLY=1 POSTCMD="..\\CompareIL.cmd MethodImplAttribute.NoOptimization.dll" # MethodImplAttribute.NoOptimization.fs [] diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl index e2aaca46ac3..076b1de364c 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.bsl @@ -754,6 +754,7 @@ FSharp.Compiler.AbstractIL.IL+ILMemberAccess: System.String ToString() FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean HasSecurity FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean IsAbstract FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean IsAggressiveInline +FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean IsAsync FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean IsCheckAccessOnOverride FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean IsClassInitializer FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean IsConstructor @@ -779,6 +780,7 @@ FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean IsZeroInit FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean get_HasSecurity() FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean get_IsAbstract() FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean get_IsAggressiveInline() +FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean get_IsAsync() FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean get_IsCheckAccessOnOverride() FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean get_IsClassInitializer() FSharp.Compiler.AbstractIL.IL+ILMethodDef: Boolean get_IsConstructor()