Skip to content

Diagnose or hoist NativePtr.stackalloc where localloc is invalid IL - #20491

Open
T-Gro wants to merge 8 commits into
mainfrom
fix/issue-20295
Open

Diagnose or hoist NativePtr.stackalloc where localloc is invalid IL#20491
T-Gro wants to merge 8 commits into
mainfrom
fix/issue-20295

Conversation

@T-Gro

@T-Gro T-Gro commented Sep 8, 2026

Copy link
Copy Markdown
Member

Fixes #20295

  • NativePtr.stackalloc inside the with handler, filter or finally block of a try (directly or via an inlinable lambda) now reports error FS3916 instead of emitting a localloc that the runtime rejects with InvalidProgramException at method load.
  • NativePtr.stackalloc passed as a chained base-constructor argument no longer produces an assembly that throws InvalidProgramException; the base-constructor arguments are hoisted into locals before the uninitialized this is pushed.

Copilot and others added 7 commits September 8, 2026 11:41
NativePtr.stackalloc emits the 'localloc' IL instruction, which the JIT rejects inside an exception-handling region, causing InvalidProgramException at method load. Detect this during PostInferenceChecks and emit compile-time error FS3916 when stackalloc is applied inside a 'with' handler or 'finally' block. A withinHandler env flag is set on the handler/finally bodies and reset at closure and method boundaries so stackalloc in a lambda or object-expression method defined in a handler stays legal. Part of issue #20295 (Case 1).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…FS3916)

An immediately-applied lambda in a 'try' handler is inlined into the handler's IL region by the optimizer, so its 'localloc' still lands inside the exception-handling region and throws InvalidProgramException under --optimize+. The CheckLambdas 'withinHandler = false' reset was based on the false premise that every lambda becomes a separate method; remove it so lambda bodies inside a handler are conservatively checked. Genuine method boundaries (object-expression/interface methods) keep their reset via CheckMethod and remain legal (verified by compileExeAndRun). Part of issue #20295 (Case 1).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… Case 2)

NativePtr.stackalloc used as a chained base-constructor argument emitted 'localloc' while an uninitialized 'this' was pending on the evaluation stack, which the JIT rejects (InvalidProgramException at method load). The uninitialized 'this' cannot be spilled to a local, so the existing spill mechanism could not clear the stack.

When a base/self-ctor argument may emit 'localloc', evaluate the arguments into locals first (at a clean stack), then push 'this' and reload them. Left-to-right evaluation order is preserved and ordinary base ctors emit byte-identical IL. The fix is applied in both GenApp and GenILCall.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…0295)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds a Fixed release note for the Case-2 base-constructor argument hoist. Verified xlf/surface-area baselines are in sync and formatting is clean; ran full regression (NativeInterop 17/17, Byref 138/138, Language RegressionTests 29/29, SurfaceAreaTest green).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…20295 Case 1)

The syntactic PostInferenceChecks detection ran before inlining and closure
conversion, so it could not tell an escaping closure (whose 'localloc' lands in
its own method and is legal) from an inlined/immediately-applied lambda or a
'let inline' wrapper (whose 'localloc' lands in the handler region and is
illegal). This produced both a false positive (rejecting legal escaping
closures) and a false negative ('let inline' wrapper compiled and threw
InvalidProgramException at load).

Detect 'localloc' emission inside a catch/filter/finally/fault region at codegen
instead, via a new eenv.withinExnHandler flag set on the handler bodies in
GenTryWith/GenTryFinally and reset at method/closure boundaries. By that point
inlining and closure conversion have run, so the true exception-handling region
is known: escaping closures stay legal, inlined localloc is rejected. The try
body keeps 'localloc' legal per ECMA-335. Removes the now-unused withinHandler
plumbing and the nativeptr_stackalloc_vref intrinsic.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…20295)

The prior commit's test edit stripped the [<Fact>] let header, orphaning the
try-body assertion inside the escaping-closure test and breaking the
ComponentTests build with FS0020. Restore it as its own Fact.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

❗ Release notes required

You can open this PR in browser to add release notes: open in github.dev


✅ Found changes and release notes in following paths:

Warning

No PR link found in some release notes, please consider adding it.

Change path Release notes path Description
`src/Compiler` docs/release-notes/.FSharp.Compiler.Service/11.0.100.md No current pull request URL (#20491) found, please consider adding it

@github-actions github-actions Bot added the AI-Tooling-Check-Bypassed Tooling check: non-fork PR, not diff-analyzed label Sep 8, 2026
@T-Gro T-Gro added AI-Auto-Resolve-CI Opt-in: LabelOps triages CI failures on this PR every 3h AI-Auto-Resolve-Conflicts Opt-in: LabelOps merges main into this PR and resolves conflicts every 3h labels Sep 9, 2026
@T-Gro
T-Gro requested a review from abonie September 9, 2026 13:04
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Commit pushed: f0587a9

Generated by LabelOps — PR Maintenance · gpt56 2.5M

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

🤖 LabelOps — CI fix.

Fixed the net472 component-test failure in commit 2ab00dc43: the escaping-closure regression test now explicitly constructs System.Action<unit>, avoiding the ambiguous System.Action overload resolution reported by CI. The focused syntax check passed; the full repository build could not reach this test because bootstrap fslex tooling is unavailable in the workspace.

Merge-tree inspection found no conflicts.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • southcentralus0.in.applicationinsights.azure.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "southcentralus0.in.applicationinsights.azure.com"

See Network Configuration for more information.

Generated by LabelOps — PR Maintenance · gpt56 2.5M ·

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI-Auto-Resolve-CI Opt-in: LabelOps triages CI failures on this PR every 3h AI-Auto-Resolve-Conflicts Opt-in: LabelOps merges main into this PR and resolves conflicts every 3h AI-Tooling-Check-Bypassed Tooling check: non-fork PR, not diff-analyzed

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

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

1 participant