Skip to content

Diagnostic for NativePtr.stackalloc in positions where localloc can never be valid IL #20295

Description

@T-Gro

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status
    New

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions