You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When a function is tail-call optimised, nested lambdas/function references inside its body were gaining unnecessary default parameters from the outer TCO scope. For example, fun () -> g() inside let rec update _arg = run g would compile to:
The _arg: Any=_arg default is wrong β _arrow1 doesn't reference _arg at all.
Root cause
transformFunction computes tcArgs from ctx.TailCallOpportunity and appends them unconditionally to every nested function body. The capture is correct for lambdas that do close over a mutated TCO variable (Python captures by reference, so default-value binding is needed). However, it was applied even when the variable is never referenced.
Fix
Before appending a TCO arg as a captured default, check isIdentUsed name body on the Fable body. Variables not referenced in the nested lambda are excluded. Variables that are referenced continue to be captured correctly (the existing TCO tests for this β "State of internally mutated tail called function parameters is preserved properly" β still pass).
// Only capture TCO variables actually referenced in the function body.ifnot(isIdentUsed name body)then None
elsematch name with...
Test plan
Added a new test test Passing a function reference in a TCO context does not capture unneeded outer args to tests/Python/TestTailCall.fs.
Existing Python TCO tests verify that lambdas using TCO variables still capture them correctly.
Original error: Validation Failed: {"resource":"Label","code":"unprocessable","field":"data","message":"Could not resolve to a node with the global id of 'PR_kwDOAvIOw87MBD9X'."}
From 2fdac3ca16968094160f670d2178f4f24c91de9e Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Fri, 20 Mar 2026 01:40:32 +0000
Subject: [PATCH] [Python] Fix TCO context adding unnecessary default params to
nested lambdas (fix #3877)
When a function is tail-call optimised, all nested functions/lambdas
created within its body previously inherited all TCO variables as default
parameters (e.g. `_arg: Any=_arg`), even when the nested function doesn't
reference those variables at all.
Root cause: `transformFunction` computes `tcArgs` from the outer
`TailCallOpportunity` in context and appends them unconditionally to every
nested function. The capture is needed for closures that *do* reference a
mutated TCO variable (Python captures by reference, so defaults bind the
current value). But it was applied indiscriminately.
Fix: before appending a TCO arg as a default, check whether the function
body actually references that variable using `isIdentUsed`. Variables not
referenced in the body are excluded from the captured defaults.
This eliminates spurious parameters like `_arg: Any=_arg` on lambdas like
`fun () -> g()` that don't use the outer TCO variable, and keeps the
capture for lambdas like `fun () -> i` that do.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
src/Fable.Cli/CHANGELOG.md | 4 ++++
src/Fable.Compiler/CHANGELOG.md | 4 ++++
.../Python/Fable2Python.Transforms.fs | 6 ++++++
tests/Python/TestTailCall.fs | 14 ++++++++++++++
4 files changed, 28 insertions(+)
diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md
index 0277cae..bc4c657 100644
--- a/src/Fable.Cli/CHANGELOG.md+++ b/src/Fable.Cli/CHANGELOG.md@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
+### Fixed++* [Python] Fix function references passed as arguments in
... (truncated)
π€ Repo Assist β automated AI assistant
Summary
Fixes #3877.
When a function is tail-call optimised, nested lambdas/function references inside its body were gaining unnecessary default parameters from the outer TCO scope. For example,
fun () -> g()insidelet rec update _arg = run gwould compile to:The
_arg: Any=_argdefault is wrong β_arrow1doesn't reference_argat all.Root cause
transformFunctioncomputestcArgsfromctx.TailCallOpportunityand appends them unconditionally to every nested function body. The capture is correct for lambdas that do close over a mutated TCO variable (Python captures by reference, so default-value binding is needed). However, it was applied even when the variable is never referenced.Fix
Before appending a TCO arg as a captured default, check
isIdentUsed name bodyon the Fable body. Variables not referenced in the nested lambda are excluded. Variables that are referenced continue to be captured correctly (the existing TCO tests for this β "State of internally mutated tail called function parameters is preserved properly" β still pass).Test plan
test Passing a function reference in a TCO context does not capture unneeded outer argstotests/Python/TestTailCall.fs.π€ Generated with Repo Assist
Note
This was originally intended as a pull request, but PR creation failed. The changes have been pushed to the branch
repo-assist/fix-python-tco-unnecessary-closure-capture-f92ee42d72632d3d.Original error: Validation Failed: {"resource":"Label","code":"unprocessable","field":"data","message":"Could not resolve to a node with the global id of 'PR_kwDOAvIOw87MBD9X'."}
To create the pull request manually:
gh pr create --title "[Repo Assist] [Python] Fix TCO context adding unnecessary default params to nested lambdas" --base main --head repo-assist/fix-python-tco-unnecessary-closure-capture-f92ee42d72632d3d --repo fable-compiler/FableShow patch preview (106 of 106 lines)