From ebfe93ea754ab763220a56a827bf62a5d96a1f65 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 8 Sep 2025 15:50:24 +0000
Subject: [PATCH 1/6] Initial plan
From 63fc0901b3874c53b57c71934ffdd775e8663425 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 8 Sep 2025 16:21:29 +0000
Subject: [PATCH 2/6] Add tracking for pinned locals in TailCallChecks (WIP)
Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
---
.../_pkrvm7jw40e0xgp_2025-09-08_16_18_58.trx | 1416 +++++++++++++++++
.../_pkrvm7jw40e0xgp_2025-09-08_16_19_44.trx | 45 +
.../_pkrvm7jw40e0xgp_2025-09-08_16_19_56.trx | 45 +
.../_pkrvm7jw40e0xgp_2025-09-08_16_20_38.trx | 856 ++++++++++
src/Compiler/Checking/TailCallChecks.fs | 19 +-
.../EmittedIL/TailCalls.fs | 27 +
6 files changed, 2407 insertions(+), 1 deletion(-)
create mode 100644 TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_18_58.trx
create mode 100644 TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_19_44.trx
create mode 100644 TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_19_56.trx
create mode 100644 TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_20_38.trx
diff --git a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_18_58.trx b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_18_58.trx
new file mode 100644
index 00000000000..a66fbd8b862
--- /dev/null
+++ b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_18_58.trx
@@ -0,0 +1,1416 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [xUnit.net 00:00:11.22] EmittedIL.Tail Calls.TailCall 06 - No tail call with pinned locals [FAIL]
+
+
+
+
\ No newline at end of file
diff --git a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_19_44.trx b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_19_44.trx
new file mode 100644
index 00000000000..9989a2dcf28
--- /dev/null
+++ b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_19_44.trx
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No test matches the given testcase filter `FullyQualifiedName~'TailCall 06'` in /home/runner/work/fsharp/fsharp/artifacts/bin/FSharp.Compiler.ComponentTests/Release/net10.0/FSharp.Compiler.ComponentTests.dll
+
+
+
+
\ No newline at end of file
diff --git a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_19_56.trx b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_19_56.trx
new file mode 100644
index 00000000000..dbe71b9e14e
--- /dev/null
+++ b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_19_56.trx
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No test matches the given testcase filter `FullyQualifiedName~TailCall06` in /home/runner/work/fsharp/fsharp/artifacts/bin/FSharp.Compiler.ComponentTests/Release/net10.0/FSharp.Compiler.ComponentTests.dll
+
+
+
+
\ No newline at end of file
diff --git a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_20_38.trx b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_20_38.trx
new file mode 100644
index 00000000000..22494d7fa2f
--- /dev/null
+++ b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_20_38.trx
@@ -0,0 +1,856 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [xUnit.net 00:00:14.96] EmittedIL.Tail Calls.TailCall 06 - No tail call with pinned locals [FAIL]
+
+
+
+
\ No newline at end of file
diff --git a/src/Compiler/Checking/TailCallChecks.fs b/src/Compiler/Checking/TailCallChecks.fs
index eba18dbeb4d..d6d7205d9ec 100644
--- a/src/Compiler/Checking/TailCallChecks.fs
+++ b/src/Compiler/Checking/TailCallChecks.fs
@@ -78,6 +78,9 @@ type cenv =
/// Values in module that have been marked []
mustTailCall: Zset
+
+ /// Indicates whether the current method has pinned locals that would prevent tail calls
+ hasPinnedLocals: bool
}
override x.ToString() = ""
@@ -202,6 +205,7 @@ let CheckForNonTailRecCall (cenv: cenv) expr (tailCall: TailCall) =
&& not (IsValRefIsDllImport cenv.g vref)
&& not isCCall
&& not hasByrefArg
+ && not cenv.hasPinnedLocals
noTailCallBlockers // blockers that will prevent the IL level from emitting a tail instruction
else
@@ -730,11 +734,23 @@ and CheckBinding cenv alwaysCheckNoReraise ctxt (TBind(v, bindRhs, _) as bind) :
| Some info -> info
| _ -> ValReprInfo.emptyValData
+ // Check if this binding introduces a pinned local
+ let cenv =
+ if v.IsFixed then
+ { cenv with hasPinnedLocals = true }
+ else
+ cenv
+
CheckLambdas isTop (Some v) cenv v.ShouldInline valReprInfo tailCall alwaysCheckNoReraise bindRhs v.Range v.Type ctxt
and CheckBindings cenv binds =
+ let mutable currentCenv = cenv
for bind in binds do
- CheckBinding cenv false PermitByRefExpr.Yes bind
+ let (TBind(v, _, _)) = bind
+ // Update the environment if this binding is fixed
+ if v.IsFixed then
+ currentCenv <- { currentCenv with hasPinnedLocals = true }
+ CheckBinding currentCenv false PermitByRefExpr.Yes bind
let CheckModuleBinding cenv (isRec: bool) (TBind _ as bind) =
@@ -871,6 +887,7 @@ let CheckImplFile (g: TcGlobals, amap, reportErrors, implFileContents) =
stackGuard = StackGuard(PostInferenceChecksStackGuardDepth, "CheckImplFile")
amap = amap
mustTailCall = Zset.empty valOrder
+ hasPinnedLocals = false
}
CheckDefnInModule cenv implFileContents
diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TailCalls.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TailCalls.fs
index 034ab15f37a..4151a5281e4 100644
--- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TailCalls.fs
+++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TailCalls.fs
@@ -235,3 +235,30 @@ let run() = let mutable x = 0 in foo(&x,&x,5)
"""
]
+ []
+ let ``TailCall 06 - No tail call with pinned locals``() =
+ FSharp """
+module TailCall06
+let foo(x:int, y) = printfn "%d" x
+let run() =
+ use ptr = fixed [| 1; 2; 3 |]
+ foo(42, 5)
+ """
+ |> compileWithTailCalls
+ |> shouldSucceed
+ |> verifyIL [
+ """
+ .method public static void run() cil managed
+ {
+
+ .maxstack 6
+ .locals init (native int V_0,
+ int32[] V_1,
+ int32& pinned V_2)
+ IL_0040: ldc.i4.s 42
+ IL_0042: ldc.i4.5
+ IL_0043: call void TailCall06::foo(int32,
+ !!0)
+ """
+ ]
+
From ce1a97f29e6b4de911a0088002a2a56b5563fbf7 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 8 Sep 2025 16:32:23 +0000
Subject: [PATCH 3/6] Add pinned locals tracking to IlxGen (still WIP - tail
calls not blocked yet)
Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
---
.../_pkrvm7jw40e0xgp_2025-09-08_16_30_03.trx | 45 +
.../_pkrvm7jw40e0xgp_2025-09-08_16_30_18.trx | 45 +
.../_pkrvm7jw40e0xgp_2025-09-08_16_30_37.trx | 852 ++++++++++++++++++
.../_pkrvm7jw40e0xgp_2025-09-08_16_31_54.trx | 832 +++++++++++++++++
src/Compiler/CodeGen/IlxGen.fs | 12 +-
.../EmittedIL/TailCalls.fs | 9 +-
6 files changed, 1787 insertions(+), 8 deletions(-)
create mode 100644 TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_03.trx
create mode 100644 TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_18.trx
create mode 100644 TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_37.trx
create mode 100644 TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_31_54.trx
diff --git a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_03.trx b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_03.trx
new file mode 100644
index 00000000000..ca22e81e3d8
--- /dev/null
+++ b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_03.trx
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No test matches the given testcase filter `FullyQualifiedName~'pinned locals'` in /home/runner/work/fsharp/fsharp/artifacts/bin/FSharp.Compiler.ComponentTests/Release/net10.0/FSharp.Compiler.ComponentTests.dll
+
+
+
+
\ No newline at end of file
diff --git a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_18.trx b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_18.trx
new file mode 100644
index 00000000000..59db8a4ba64
--- /dev/null
+++ b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_18.trx
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No test matches the given testcase filter `FullyQualifiedName~'TailCall.*06'` in /home/runner/work/fsharp/fsharp/artifacts/bin/FSharp.Compiler.ComponentTests/Release/net10.0/FSharp.Compiler.ComponentTests.dll
+
+
+
+
\ No newline at end of file
diff --git a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_37.trx b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_37.trx
new file mode 100644
index 00000000000..12ead895859
--- /dev/null
+++ b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_37.trx
@@ -0,0 +1,852 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [xUnit.net 00:00:09.47] EmittedIL.Tail Calls.TailCall 06 - No tail call with pinned locals [FAIL]
+
+
+
+
\ No newline at end of file
diff --git a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_31_54.trx b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_31_54.trx
new file mode 100644
index 00000000000..8513a84ca60
--- /dev/null
+++ b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_31_54.trx
@@ -0,0 +1,832 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [xUnit.net 00:00:09.70] EmittedIL.Tail Calls.TailCall 06 - No tail call with pinned locals [FAIL]
+
+
+
+
\ No newline at end of file
diff --git a/src/Compiler/CodeGen/IlxGen.fs b/src/Compiler/CodeGen/IlxGen.fs
index 08020ebf21b..480cce0a7d5 100644
--- a/src/Compiler/CodeGen/IlxGen.fs
+++ b/src/Compiler/CodeGen/IlxGen.fs
@@ -1250,6 +1250,9 @@ and IlxGenEnv =
/// Indicates that the .locals init flag should be set on a method and all its nested methods and lambdas
initLocals: bool
+ /// Indicates whether the current method has pinned locals that would prevent tail calls
+ hasPinnedLocals: bool
+
/// Delay code gen for files.
delayCodeGen: bool
@@ -4371,6 +4374,7 @@ and GenApp (cenv: cenv) cgbuf eenv (f, fty, tyargs, curriedArgs, m) sequel =
isDllImport,
isSelfInit,
makesNoCriticalTailcalls,
+ eenv.hasPinnedLocals,
sequel
)
else
@@ -4482,11 +4486,13 @@ and CanTailcall
isDllImport,
isSelfInit,
makesNoCriticalTailcalls,
+ hasPinnedLocals,
sequel
) =
// Can't tailcall with a struct object arg since it involves a byref
// Can't tailcall with a .NET 2.0 generic constrained call since it involves a byref
+ // Can't tailcall when there are pinned locals since the stack frame must remain alive
if
not hasStructObjArg
&& Option.isNone ccallInfo
@@ -4495,6 +4501,7 @@ and CanTailcall
&& not isDllImport
&& not isSelfInit
&& not makesNoCriticalTailcalls
+ && not hasPinnedLocals
&&
// We can tailcall even if we need to generate "unit", as long as we're about to throw the value away anyway as par of the return.
@@ -4693,7 +4700,7 @@ and GenIndirectCall cenv cgbuf eenv (funcTy, tyargs, curriedArgs, m) sequel =
check ilxClosureApps
let isTailCall =
- CanTailcall(false, None, eenv.withinSEH, hasByrefArg, false, false, false, false, sequel)
+ CanTailcall(false, None, eenv.withinSEH, hasByrefArg, false, false, false, false, eenv.hasPinnedLocals, sequel)
CountCallFuncInstructions()
@@ -5431,6 +5438,7 @@ and GenILCall
isDllImport,
false,
makesNoCriticalTailcalls,
+ eenv.hasPinnedLocals,
sequel
)
@@ -9864,6 +9872,7 @@ and AllocLocal cenv cgbuf eenv compgen (v, ty, isFixed) (scopeMarks: Mark * Mark
realloc,
{ eenv with
liveLocals = IntMap.add j () eenv.liveLocals
+ hasPinnedLocals = eenv.hasPinnedLocals || isFixed
}
/// Decide storage for local value and if necessary allocate an ILLocal for it
@@ -12020,6 +12029,7 @@ let GetEmptyIlxGenEnv (g: TcGlobals) ccu =
withinSEH = false
isInLoop = false
initLocals = true
+ hasPinnedLocals = false
imports = None
delayCodeGen = true
delayedFileGenReverse = []
diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TailCalls.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TailCalls.fs
index 4151a5281e4..47db7bf43b6 100644
--- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TailCalls.fs
+++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TailCalls.fs
@@ -248,17 +248,12 @@ let run() =
|> shouldSucceed
|> verifyIL [
"""
- .method public static void run() cil managed
- {
-
- .maxstack 6
- .locals init (native int V_0,
- int32[] V_1,
- int32& pinned V_2)
IL_0040: ldc.i4.s 42
IL_0042: ldc.i4.5
IL_0043: call void TailCall06::foo(int32,
!!0)
+ IL_0048: nop
+ IL_0049: ldc.i4.0
"""
]
From ff7379c563af97f45d343c796e83f02cdfddcf2f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 8 Sep 2025 16:42:07 +0000
Subject: [PATCH 4/6] Implement fix to prevent tail calls when pinned locals
are present
Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
---
.../_pkrvm7jw40e0xgp_2025-09-08_16_36_48.trx | 45 +++++++++++++++++++
src/Compiler/CodeGen/IlxGen.fs | 18 ++++----
.../EmittedIL/TailCalls.fs | 2 -
3 files changed, 54 insertions(+), 11 deletions(-)
create mode 100644 TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_36_48.trx
diff --git a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_36_48.trx b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_36_48.trx
new file mode 100644
index 00000000000..2df244fe411
--- /dev/null
+++ b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_36_48.trx
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No test matches the given testcase filter `DisplayName~'TailCall 06'` in /home/runner/work/fsharp/fsharp/artifacts/bin/FSharp.Compiler.ComponentTests/Release/net10.0/FSharp.Compiler.ComponentTests.dll
+
+
+
+
\ No newline at end of file
diff --git a/src/Compiler/CodeGen/IlxGen.fs b/src/Compiler/CodeGen/IlxGen.fs
index 480cce0a7d5..0ecb4142d03 100644
--- a/src/Compiler/CodeGen/IlxGen.fs
+++ b/src/Compiler/CodeGen/IlxGen.fs
@@ -1250,9 +1250,6 @@ and IlxGenEnv =
/// Indicates that the .locals init flag should be set on a method and all its nested methods and lambdas
initLocals: bool
- /// Indicates whether the current method has pinned locals that would prevent tail calls
- hasPinnedLocals: bool
-
/// Delay code gen for files.
delayCodeGen: bool
@@ -2692,6 +2689,10 @@ type CodeGenBuffer(m: range, mgbuf: AssemblyBuilder, methodName, alreadyUsedArgs
j, true
| None -> cgbuf.AllocLocal(ranges, ty, isFixed, canBeReallocd), false
+ /// Check if any locals have been allocated as pinned/fixed
+ member _.HasPinnedLocals() =
+ locals |> Seq.exists (fun (_, _, isFixed, _) -> isFixed)
+
member _.Close() =
let instrs = codebuf.ToArray()
@@ -4374,7 +4375,7 @@ and GenApp (cenv: cenv) cgbuf eenv (f, fty, tyargs, curriedArgs, m) sequel =
isDllImport,
isSelfInit,
makesNoCriticalTailcalls,
- eenv.hasPinnedLocals,
+ cgbuf,
sequel
)
else
@@ -4486,13 +4487,14 @@ and CanTailcall
isDllImport,
isSelfInit,
makesNoCriticalTailcalls,
- hasPinnedLocals,
+ cgbuf: CodeGenBuffer,
sequel
) =
// Can't tailcall with a struct object arg since it involves a byref
// Can't tailcall with a .NET 2.0 generic constrained call since it involves a byref
// Can't tailcall when there are pinned locals since the stack frame must remain alive
+ let hasPinnedLocals = cgbuf.HasPinnedLocals()
if
not hasStructObjArg
&& Option.isNone ccallInfo
@@ -4700,7 +4702,7 @@ and GenIndirectCall cenv cgbuf eenv (funcTy, tyargs, curriedArgs, m) sequel =
check ilxClosureApps
let isTailCall =
- CanTailcall(false, None, eenv.withinSEH, hasByrefArg, false, false, false, false, eenv.hasPinnedLocals, sequel)
+ CanTailcall(false, None, eenv.withinSEH, hasByrefArg, false, false, false, false, cgbuf, sequel)
CountCallFuncInstructions()
@@ -5438,7 +5440,7 @@ and GenILCall
isDllImport,
false,
makesNoCriticalTailcalls,
- eenv.hasPinnedLocals,
+ cgbuf,
sequel
)
@@ -9872,7 +9874,6 @@ and AllocLocal cenv cgbuf eenv compgen (v, ty, isFixed) (scopeMarks: Mark * Mark
realloc,
{ eenv with
liveLocals = IntMap.add j () eenv.liveLocals
- hasPinnedLocals = eenv.hasPinnedLocals || isFixed
}
/// Decide storage for local value and if necessary allocate an ILLocal for it
@@ -12029,7 +12030,6 @@ let GetEmptyIlxGenEnv (g: TcGlobals) ccu =
withinSEH = false
isInLoop = false
initLocals = true
- hasPinnedLocals = false
imports = None
delayCodeGen = true
delayedFileGenReverse = []
diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TailCalls.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TailCalls.fs
index 47db7bf43b6..021f87dcede 100644
--- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TailCalls.fs
+++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TailCalls.fs
@@ -252,8 +252,6 @@ let run() =
IL_0042: ldc.i4.5
IL_0043: call void TailCall06::foo(int32,
!!0)
- IL_0048: nop
- IL_0049: ldc.i4.0
"""
]
From 3c000d9f639f814d9343d128b791e78f3ecb4a43 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 17 Sep 2025 17:49:29 +0000
Subject: [PATCH 5/6] Fix PR review comments: remove mutable, gitignore .trx
files, add recursive test, add release notes, format code
Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
---
.gitignore | 1 +
.../_pkrvm7jw40e0xgp_2025-09-08_16_18_58.trx | 1416 -----------------
.../_pkrvm7jw40e0xgp_2025-09-08_16_19_44.trx | 45 -
.../_pkrvm7jw40e0xgp_2025-09-08_16_19_56.trx | 45 -
.../_pkrvm7jw40e0xgp_2025-09-08_16_20_38.trx | 856 ----------
.../_pkrvm7jw40e0xgp_2025-09-08_16_30_03.trx | 45 -
.../_pkrvm7jw40e0xgp_2025-09-08_16_30_18.trx | 45 -
.../_pkrvm7jw40e0xgp_2025-09-08_16_30_37.trx | 852 ----------
.../_pkrvm7jw40e0xgp_2025-09-08_16_31_54.trx | 832 ----------
.../_pkrvm7jw40e0xgp_2025-09-08_16_36_48.trx | 45 -
.../.FSharp.Compiler.Service/9.0.300.md | 1 +
src/Compiler/Checking/TailCallChecks.fs | 9 +-
src/Compiler/CodeGen/IlxGen.fs | 1 +
.../EmittedIL/TailCalls.fs | 21 +
14 files changed, 30 insertions(+), 4184 deletions(-)
delete mode 100644 TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_18_58.trx
delete mode 100644 TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_19_44.trx
delete mode 100644 TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_19_56.trx
delete mode 100644 TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_20_38.trx
delete mode 100644 TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_03.trx
delete mode 100644 TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_18.trx
delete mode 100644 TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_37.trx
delete mode 100644 TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_31_54.trx
delete mode 100644 TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_36_48.trx
diff --git a/.gitignore b/.gitignore
index 49032888dca..822b0ef242d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -142,6 +142,7 @@ positive.exe
# Test result files
tests/**/TestResults/*.trx
+TestResults/*.trx
# Standard output/error files in root directory
StandardOutput.txt
diff --git a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_18_58.trx b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_18_58.trx
deleted file mode 100644
index a66fbd8b862..00000000000
--- a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_18_58.trx
+++ /dev/null
@@ -1,1416 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- [xUnit.net 00:00:11.22] EmittedIL.Tail Calls.TailCall 06 - No tail call with pinned locals [FAIL]
-
-
-
-
\ No newline at end of file
diff --git a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_19_44.trx b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_19_44.trx
deleted file mode 100644
index 9989a2dcf28..00000000000
--- a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_19_44.trx
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- No test matches the given testcase filter `FullyQualifiedName~'TailCall 06'` in /home/runner/work/fsharp/fsharp/artifacts/bin/FSharp.Compiler.ComponentTests/Release/net10.0/FSharp.Compiler.ComponentTests.dll
-
-
-
-
\ No newline at end of file
diff --git a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_19_56.trx b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_19_56.trx
deleted file mode 100644
index dbe71b9e14e..00000000000
--- a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_19_56.trx
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- No test matches the given testcase filter `FullyQualifiedName~TailCall06` in /home/runner/work/fsharp/fsharp/artifacts/bin/FSharp.Compiler.ComponentTests/Release/net10.0/FSharp.Compiler.ComponentTests.dll
-
-
-
-
\ No newline at end of file
diff --git a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_20_38.trx b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_20_38.trx
deleted file mode 100644
index 22494d7fa2f..00000000000
--- a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_20_38.trx
+++ /dev/null
@@ -1,856 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- [xUnit.net 00:00:14.96] EmittedIL.Tail Calls.TailCall 06 - No tail call with pinned locals [FAIL]
-
-
-
-
\ No newline at end of file
diff --git a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_03.trx b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_03.trx
deleted file mode 100644
index ca22e81e3d8..00000000000
--- a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_03.trx
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- No test matches the given testcase filter `FullyQualifiedName~'pinned locals'` in /home/runner/work/fsharp/fsharp/artifacts/bin/FSharp.Compiler.ComponentTests/Release/net10.0/FSharp.Compiler.ComponentTests.dll
-
-
-
-
\ No newline at end of file
diff --git a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_18.trx b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_18.trx
deleted file mode 100644
index 59db8a4ba64..00000000000
--- a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_18.trx
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- No test matches the given testcase filter `FullyQualifiedName~'TailCall.*06'` in /home/runner/work/fsharp/fsharp/artifacts/bin/FSharp.Compiler.ComponentTests/Release/net10.0/FSharp.Compiler.ComponentTests.dll
-
-
-
-
\ No newline at end of file
diff --git a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_37.trx b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_37.trx
deleted file mode 100644
index 12ead895859..00000000000
--- a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_30_37.trx
+++ /dev/null
@@ -1,852 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- [xUnit.net 00:00:09.47] EmittedIL.Tail Calls.TailCall 06 - No tail call with pinned locals [FAIL]
-
-
-
-
\ No newline at end of file
diff --git a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_31_54.trx b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_31_54.trx
deleted file mode 100644
index 8513a84ca60..00000000000
--- a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_31_54.trx
+++ /dev/null
@@ -1,832 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- [xUnit.net 00:00:09.70] EmittedIL.Tail Calls.TailCall 06 - No tail call with pinned locals [FAIL]
-
-
-
-
\ No newline at end of file
diff --git a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_36_48.trx b/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_36_48.trx
deleted file mode 100644
index 2df244fe411..00000000000
--- a/TestResults/_pkrvm7jw40e0xgp_2025-09-08_16_36_48.trx
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- No test matches the given testcase filter `DisplayName~'TailCall 06'` in /home/runner/work/fsharp/fsharp/artifacts/bin/FSharp.Compiler.ComponentTests/Release/net10.0/FSharp.Compiler.ComponentTests.dll
-
-
-
-
\ No newline at end of file
diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md
index 7f5ab7289d2..569646fc0ce 100644
--- a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md
+++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md
@@ -1,5 +1,6 @@
### Fixed
* Fix missing TailCall warning in TOp.IntegerForLoop ([PR #18399](https://github.com/dotnet/fsharp/pull/18399))
+* Fix F# compiler to prevent tail call emission when pinned locals are present ([PR #XXXX](https://github.com/dotnet/fsharp/pull/XXXX))
* Fix classification of `nameof` in `nameof<'T>`, `match … with nameof ident -> …`. ([Issue #10026](https://github.com/dotnet/fsharp/issues/10026), [PR #18300](https://github.com/dotnet/fsharp/pull/18300))
* Fix Realsig+ generates nested closures with incorrect Generic ([Issue #17797](https://github.com/dotnet/fsharp/issues/17797), [PR #17877](https://github.com/dotnet/fsharp/pull/17877))
* Fix optimizer internal error for records with static fields ([Issue #18165](https://github.com/dotnet/fsharp/issues/18165), [PR #18280](https://github.com/dotnet/fsharp/pull/18280))
diff --git a/src/Compiler/Checking/TailCallChecks.fs b/src/Compiler/Checking/TailCallChecks.fs
index d6d7205d9ec..bb4bebbcf98 100644
--- a/src/Compiler/Checking/TailCallChecks.fs
+++ b/src/Compiler/Checking/TailCallChecks.fs
@@ -744,12 +744,15 @@ and CheckBinding cenv alwaysCheckNoReraise ctxt (TBind(v, bindRhs, _) as bind) :
CheckLambdas isTop (Some v) cenv v.ShouldInline valReprInfo tailCall alwaysCheckNoReraise bindRhs v.Range v.Type ctxt
and CheckBindings cenv binds =
- let mutable currentCenv = cenv
for bind in binds do
let (TBind(v, _, _)) = bind
// Update the environment if this binding is fixed
- if v.IsFixed then
- currentCenv <- { currentCenv with hasPinnedLocals = true }
+ let currentCenv =
+ if v.IsFixed then
+ { cenv with hasPinnedLocals = true }
+ else
+ cenv
+
CheckBinding currentCenv false PermitByRefExpr.Yes bind
let CheckModuleBinding cenv (isRec: bool) (TBind _ as bind) =
diff --git a/src/Compiler/CodeGen/IlxGen.fs b/src/Compiler/CodeGen/IlxGen.fs
index 0ecb4142d03..32b04b8c90f 100644
--- a/src/Compiler/CodeGen/IlxGen.fs
+++ b/src/Compiler/CodeGen/IlxGen.fs
@@ -4495,6 +4495,7 @@ and CanTailcall
// Can't tailcall with a .NET 2.0 generic constrained call since it involves a byref
// Can't tailcall when there are pinned locals since the stack frame must remain alive
let hasPinnedLocals = cgbuf.HasPinnedLocals()
+
if
not hasStructObjArg
&& Option.isNone ccallInfo
diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TailCalls.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TailCalls.fs
index 021f87dcede..0ab38dcc41e 100644
--- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TailCalls.fs
+++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TailCalls.fs
@@ -255,3 +255,24 @@ let run() =
"""
]
+ []
+ let ``TailCall 07 - No tail call with pinned locals in recursive function``() =
+ FSharp """
+module TailCall07
+let rec countdown n =
+ use ptr = fixed [| n |]
+ if n <= 0 then
+ 0
+ else
+ countdown (n - 1)
+ """
+ |> compileWithTailCalls
+ |> shouldSucceed
+ |> verifyIL [
+ """
+.locals init (native int V_0,
+ int32[] V_1,
+ int32& pinned V_2)
+ """
+ ]
+
From b4edc34c578a0dc432af791c8e251a9d77d45167 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 18 Sep 2025 08:35:25 +0000
Subject: [PATCH 6/6] Move release notes from 9.0.300 to 10.0.100 as requested
Co-authored-by: T-Gro <46543583+T-Gro@users.noreply.github.com>
---
docs/release-notes/.FSharp.Compiler.Service/10.0.100.md | 1 +
docs/release-notes/.FSharp.Compiler.Service/9.0.300.md | 1 -
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md
index 06b39cc019d..208c34fcc3b 100644
--- a/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md
+++ b/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md
@@ -7,6 +7,7 @@
### Fixed
+* Fix F# compiler to prevent tail call emission when pinned locals are present ([PR #XXXX](https://github.com/dotnet/fsharp/pull/XXXX))
* Fix SignatureHash to include constant values in hash computation ([Issue #18758](https://github.com/dotnet/fsharp/issues/18758))
* Fix parsing errors using anonymous records and units of measures ([PR #18543](https://github.com/dotnet/fsharp/pull/18543))
* Fix parsing errors using anonymous records and code quotations ([PR #18603](https://github.com/dotnet/fsharp/pull/18603))
diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md
index 569646fc0ce..7f5ab7289d2 100644
--- a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md
+++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md
@@ -1,6 +1,5 @@
### Fixed
* Fix missing TailCall warning in TOp.IntegerForLoop ([PR #18399](https://github.com/dotnet/fsharp/pull/18399))
-* Fix F# compiler to prevent tail call emission when pinned locals are present ([PR #XXXX](https://github.com/dotnet/fsharp/pull/XXXX))
* Fix classification of `nameof` in `nameof<'T>`, `match … with nameof ident -> …`. ([Issue #10026](https://github.com/dotnet/fsharp/issues/10026), [PR #18300](https://github.com/dotnet/fsharp/pull/18300))
* Fix Realsig+ generates nested closures with incorrect Generic ([Issue #17797](https://github.com/dotnet/fsharp/issues/17797), [PR #17877](https://github.com/dotnet/fsharp/pull/17877))
* Fix optimizer internal error for records with static fields ([Issue #18165](https://github.com/dotnet/fsharp/issues/18165), [PR #18280](https://github.com/dotnet/fsharp/pull/18280))