Conversation
❗ Release notes requiredYou can open this PR in browser to add release notes: open in github.dev
|
…c; add Language preview release notes
The features dictionary lost ImplicitDIMCoverage, MethodOverloadsCache,
ErrorOnMissingSignatureAttribute, DirectDelegateConstruction,
AccessProtectedBaseFieldFromClosure and RecordSpreads entries, causing
54 CI test failures ('Unable to find feature' internal errors and
preview features not enabled).
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Another thing to think through is inlining. Currently there are no checks at all for use of suspending Currently it is up to the "expert" user to not misuse This is still a sketch, but it successfully compiles |
We could have a notion of PostIlxGen checks. |
Roslyn-async2-inspired edge cases for the runtime-async intrinsic, driven through
the test-only runtimeTask CE (treated as a hypothetical library):
* execution fixture (RuntimeAsync/RuntimeAsyncEdgeCases.fs): locals/loops across
suspension, non-ref struct across suspension, ValueTask operand, exception
propagation, IAsyncDisposable with genuinely-async DisposeAsync.
* facts (RuntimeAsyncEdgeCaseTests.fs): Await overload selection per operand type,
no compiler state machine (direct + CE), the C1 forbidden `tail.` prefix, and a
parametrized set of currently-undiagnosed contract-forbidden patterns
(await-in-finally/catch, ref-struct- and byref-across-suspension).
Every asserted IL substring and runtime symptom was captured empirically on the
pinned net11 preview; the forbidden patterns match docs/runtime-async.md.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 55dd72c8-46d3-4959-9677-c52d41779596
The sequence-points baseline (and ildasm) cannot render MethodImplOptions.Async (0x2000), so the lifted __runtimeAsync body shows up as a plain outer@<line> closure. Factor the metadata flag check into assertAsyncFlagOnLiftedClosureOnly and chain it onto the sequence-points fact so the exact program that emits the .bsl also proves the async marker lands only on the lifted closure, never on the user's outer/helper methods. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 55dd72c8-46d3-4959-9677-c52d41779596
|
Looks like the runtime feature is quickly evolving, see #19056 (comment) |
|
I wonder how to support the other allowed return types. __runtimeAsyncReturn<'T> : 'T -> Task<'T>
__runtimeAsyncReturnValueTask<'T> : 'T -> ValueTask<'T>
__runtimeAsyncReturnUnit : unit -> Task
__runtimeAsyncReturnValueTaskUnit : unit -> ValueTaskand it quickly becomes a whole zoo. Do we need the non-generic versions at all? Only for potential C# interop, I guess. The upside is that the current type check is all we need to keep it correct, without any extra handling. The other alternative it to have a unconstrained |
This comment has been minimized.
This comment has been minimized.
|
Unfortunatelly to make async iterators like See: |
|
AI thoughts on runtime async state machines implementation: |
Record the eight task and async members already declared in async.fsi and tasks.fsi at b004333. This keeps the generated Debug baseline synchronized before adding the sequence proposal APIs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Reuse sequence lowering for yield state while keeping runtime awaits inside generated reference-type MoveNextAsync and DisposeAsync methods. Normalize explicit recipes independently of optimization and keep ordinary input sequences synchronous. Demonstrate library-defined awaits, synchronous and asynchronous enumeration, cleanup, and explicit cancellation policy. Keep measured continuation costs and incomplete builder-generated source ranges visible as proposal limitations. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
Implement enumerator interfaces directly and reuse the first instance without sharing later acquisitions. Preserve runtime source-call/await fusion without crossing potentially throwing prefixes. Keep cold async-cleanup exception state out of every continuation and use direct configured-awaitable intrinsics. Retain the direct ValueTask<bool> producer contract and cleanup semantics. Add exception-order, aliasing, acquisition and failure regressions; update affected API and IL expectations. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
🤖🕵️ Rechecked at Legend: checked = verified fixed; unchecked = partially fixed.
Remaining details and examplesImports and test harness omitted.
__runtimeAsyncReturn (
try raise outer
with _ ->
AsyncHelpers.Await gate
try raise inner
with _ -> reraise ())Callback construction — executes twice instead of once. let mutable constructed = 0
let inline twice ([<InlineIfLambda>] f: unit -> int) =
__runtimeAsyncReturn (f () + f ())
let run () =
twice (constructed <- constructed + 1; fun () -> 21)Calling Preview gate — this compiles and emits runtime-async code under F# 9. // --langversion:9 --optimize-
let f x = x |> __runtimeAsyncReturn
Nested ordinary __runtimeAsyncReturn (
seq {
let value = AsyncHelpers.Await gate
yield value
yield value + 1
})With an incomplete gate, enumeration throws Imported pin lifetime — rejected locally, accepted across assemblies. // Library.dll
let inline comparePin (array: byte[]) ([<InlineIfLambda>] action) =
use before = fixed array
action ()
use after = fixed array
struct (NativePtr.toNativeInt before, NativePtr.toNativeInt after)
// Consumer.dll
__runtimeAsyncReturn (comparePin data (fun () -> AsyncHelpers.Await gate))After suspension and compacting GC, the addresses differ: the pin did not survive. The witness never dereferences a stale address. Debug baseline: original intrinsic omissions fixed; eight other entries remain missing. Performance: bounded optimized task/seq A/B measured +6.6% optimizer time; not a general performance claim. Cross-assembly inlining: the new test passes, but consumer IL still contains the inlined body. It does not verify the claimed restriction. |
|
🔍 Tooling Safety Check — Affects-Bootstrap, Affects-Build-Infra, Affects-Compiler-Output, Affects-Test-Tooling
|
|
Handling the very edgy case of pinned locals is problematic: // Library.dll
let inline comparePin (array: byte[]) ([<InlineIfLambda>] action) =
use before = fixed array
action ()
use after = fixed array
struct (NativePtr.toNativeInt before, NativePtr.toNativeInt after)
// Consumer.dll
__runtimeAsyncReturn (comparePin data (fun () -> AsyncHelpers.Await gate))We could preserve the This is downstream from the design decision to not make the concept of inline runtime-async code fragment explicit. The implicit assumption is that any inline function may contain runtime suspensions (AsyncHelpers awaits). |
Add preview F# compiler support for .NET runtime-async methods. Compiler-recognized
__runtimeAsyncReturnintrinsics markTask/ValueTaskmethods and lambdas withMethodImplOptions.Async, whileAsyncHelpers.Await*calls become runtime suspension points.The optimizer preserves and specializes inline suspension fragments, rewrites suspending exception handlers and finally compensations, and reports unsupported byref or suspension patterns. The feature is gated by
langversion:previewand target-runtime metadata support; FSharp.Core exposes the intrinsics only fornet10.0, with builders remaining application-defined.Consider an inline "sync" CE builder. applying
__runtimeAsyncReturnto the inlined code in its Run method compiles the computation expression into a single runtime async method:Resumption is handled by the runtime, calling
AsyncHelpers.AwaitinBindis all that is needed to make the resulting CE async:runtime spec :
Runtime-async specification
interesting docs and links:
To do:
--optimize-(debug configuration)IAsyncEnumerable