fsharp icon indicating copy to clipboard operation
fsharp copied to clipboard

Prevent tailcalls when locals are pinned or address-taken; add regression coverage

Open Copilot opened this issue 1 month ago • 3 comments

Tail calls were still emitted when pinning locals (e.g., fixed &value or &&value), leading to corrupted results at runtime.

  • Codegen safety: Track address-taken locals in IlxGen and suppress tailcall emission when any local is pinned or its address is taken.
  • Regression tests: Added TailCall 08/09 component tests covering pinned locals and the && operator to ensure correct output.

Example:

[<MethodImpl(MethodImplOptions.NoInlining)>]
let foo() =
    let mutable value = 42
    use ptr = fixed &value  // or: bar &&value
    bar ptr                 // should print 42 without tailcall
Original prompt

This section details on the original issue you should resolve

<issue_title>Tail calls with pinned locals</issue_title> <issue_description>Opening another issue, since the original is closed and I haven't gotten a response there.

The original problem with pinned arrays seems to be fixed, the tail call is gone when using the newest SDK. However, there are still problems when pinning a local variable:

Repro steps

open Microsoft.FSharp.NativeInterop
open System.Runtime.CompilerServices

module Program =

    [<MethodImpl(MethodImplOptions.NoInlining)>]
    let bar (pValue: nativeptr<int>) : unit =
        let value = NativePtr.read pValue
        printfn "value = %A" value

    [<MethodImpl(MethodImplOptions.NoInlining)>]
    let foo() =
        let mutable value = 42
        use ptr = fixed &value
        bar ptr

    [<EntryPoint>]
    let main argv =
        foo()
        0

The same also happens when using the taking the address directly with the && operator:

    [<MethodImpl(MethodImplOptions.NoInlining)>]
    let foo() =
        let mutable value = 42
        bar &&value

Expected behavior

Should print 42.

Actual behavior

Tail call gets emitted and prints garbage:

 IL_0003: ldloca.s     'value'
 IL_0005: stloc.1      // V_1
 IL_0006: ldloca.s     'value'
 IL_0008: conv.i
 IL_0009: tail.
 IL_000b: call         void Program/Program::bar(int32*)
 IL_0010: ret

Known workarounds

Wrap the bar call in a try finally block.

Related information

  • .NET SDK 10.0.101</issue_description>

<agent_instructions>Locate the changes done at https://github.com/dotnet/fsharp/pull/18893 . Add the newly reported test cases to it.

Execute them (not just type check, but real tests that do compile as well run in the test framework).

Include the test case with the && operator - I do not see it as mandatory change though, this operator should not be used in my opinion.

Prepare changes needed to Ilxgen for preventing a tail call emission. Keep a TODO list and list all attempts you tried, make sure to push that TODO list as part of you work.

If you encounter any behavior, write down HYPOTHESIS.md with what you think is happening - there can be more of those. For each, add necessary instrumentation in the code and track verified/rejected in the HYPOTHESIS.md file.</agent_instructions>

Comments on the Issue (you are @copilot in this section)

Custom agent used: F# agent Generic agent for F# coding following the coding guidelines of F# from MsLearn

  • Fixes dotnet/fsharp#19147

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot avatar Dec 11 '25 12:12 Copilot

/run test-baseline EmittedIL

T-Gro avatar Dec 11 '25 16:12 T-Gro

:white_check_mark: No release notes required

github-actions[bot] avatar Dec 11 '25 16:12 github-actions[bot]

🔧 CLI Command Report

  • Command: /run test-baseline
  • Outcome: success

✅ Patch applied: - Files changed: 2 - Lines changed: 240

github-actions[bot] avatar Dec 11 '25 16:12 github-actions[bot]

/run test-baseline EmittedIL

T-Gro avatar Dec 15 '25 15:12 T-Gro

🔧 CLI Command Report

  • Command: /run test-baseline
  • Outcome: success

✅ Patch applied: - Files changed: 2 - Lines changed: 64

github-actions[bot] avatar Dec 15 '25 16:12 github-actions[bot]