The following snippets are accepted by the F# compiler but throw System.InvalidProgramException at load, before any user code runs. Follow-up to #8083 (general case fixed by #20302).
1. localloc in an exception handler
open Microsoft.FSharp.NativeInterop
try () with _ -> NativePtr.stackalloc<int> 1 |> ignore
2. localloc while this is uninitialized (chained base constructor)
open Microsoft.FSharp.NativeInterop
type A(p: nativeptr<int>) = class end
type B() = inherit A(NativePtr.stackalloc<int> 1)
B() |> ignore
Suggest rejecting them in PostInferenceChecks (dsyme's suggestion on #8083) — a compile-time error instead of an InvalidProgramException at JIT time.
Details
Fixed for the general case by #20302: IlxGen spills the pending stack, emits localloc at an empty stack, then reloads the pending values under the result pointer.
Case 1 (handler). The CoreCLR importer rejects localloc when block->hasHndIndex() (dotnet/runtime, jit/importer.cpp) — that is, inside a catch, finally, or filter handler, not a try body. This is why the for .. in seq shape in fsharp/fslang-suggestions#1469 works. eenv.withinSEH does not yet separate a handler from a try body, so the diagnostic needs a new flag.
Case 2 (uninitialized this). ECMA forbids spilling an uninitialized this, so EmitLocallocCode falls back to a plain emit when uninitializedThisOnStackCount > 0. The code is a use-after-free regardless: the stack memory dies when the constructor returns. A real fix, instead of a diagnostic, would hoist the base-constructor arguments into locals before this is pushed.
The following snippets are accepted by the F# compiler but throw
System.InvalidProgramExceptionat load, before any user code runs. Follow-up to #8083 (general case fixed by #20302).1.
locallocin an exception handler2.
locallocwhilethisis uninitialized (chained base constructor)Suggest rejecting them in
PostInferenceChecks(dsyme's suggestion on #8083) — a compile-time error instead of anInvalidProgramExceptionat JIT time.Details
Fixed for the general case by #20302:
IlxGenspills the pending stack, emitslocallocat an empty stack, then reloads the pending values under the result pointer.Case 1 (handler). The CoreCLR importer rejects
locallocwhenblock->hasHndIndex()(dotnet/runtime,jit/importer.cpp) — that is, inside acatch,finally, orfilterhandler, not atrybody. This is why thefor .. in seqshape in fsharp/fslang-suggestions#1469 works.eenv.withinSEHdoes not yet separate a handler from atrybody, so the diagnostic needs a new flag.Case 2 (uninitialized
this). ECMA forbids spilling an uninitializedthis, soEmitLocallocCodefalls back to a plain emit whenuninitializedThisOnStackCount > 0. The code is a use-after-free regardless: the stack memory dies when the constructor returns. A real fix, instead of a diagnostic, would hoist the base-constructor arguments into locals beforethisis pushed.