Skip to content

Conversation

Copilot
Copy link
Contributor

@Copilot Copilot AI commented Sep 8, 2025

Problem

The F# compiler was emitting .tail. prefixes for method calls within functions that contain pinned locals (created using the fixed keyword). This could lead to runtime crashes and memory corruption because tail calls can cause stack unwinding that invalidates pinned memory references before they are unpinned.

Consider this example:

open Microsoft.FSharp.NativeInterop

let rec tailCallWithPinnedLocal x =
    if x <= 0 then 
        0 
    else 
        let mutable thing = x
        use ptr = fixed &thing  // Creates a pinned local
        tailCallWithPinnedLocal (x - 1)  // This was emitting .tail. prefix

The generated IL would include tail. before the recursive call, which is unsafe when pinned locals are present.

Solution

This PR implements a fix that suppresses tail call emission when any method contains pinned local variables. The implementation:

  1. Detection: Checks for pinned locals by examining ILLocal.IsPinned in the method body
  2. Suppression: Creates a modified compilation environment with emitTailcalls = false when pinned locals are detected
  3. Comprehensive Coverage: Handles both IL emission paths:
    • emitILMethodBody in ilreflect.fs (Reflection.Emit path)
    • GenILMethodBody in ilwrite.fs (direct IL writing path)

Changes

  • src/Compiler/AbstractIL/ilreflect.fs: Modified emitILMethodBody to check for pinned locals and suppress tail calls
  • src/Compiler/AbstractIL/ilwrite.fs: Modified GenILMethodBody to check for pinned locals and suppress tail calls
  • tests/: Added regression test to verify tail calls are suppressed with pinned locals but preserved without them
  • docs/release-notes/: Added release note documenting the fix

Validation

The fix ensures that:

  • Methods with pinned locals never emit .tail. prefixes, preventing memory safety issues
  • Methods without pinned locals continue to emit tail calls normally for performance
  • Both IL emission code paths are protected against this issue

This is a conservative approach that prioritizes memory safety over potential performance optimizations in the specific case of methods containing pinned locals.


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@Copilot Copilot AI changed the title [WIP] F# Compiler Tailcall Suppression Fix for Pinned Locals Fix tail call emission for methods containing pinned locals to prevent runtime crashes Sep 8, 2025
@Copilot Copilot AI requested a review from T-Gro September 8, 2025 16:42
Copilot finished work on behalf of T-Gro September 8, 2025 16:42
@T-Gro
Copy link
Member

T-Gro commented Sep 19, 2025

Resolved better at #18893

@T-Gro T-Gro closed this Sep 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

2 participants