Repository navigation
Description
π€ 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() inside let rec update _arg = run g would compile to:
def _arrow1(__unit: None=None, _arg: Any=_arg) -> None:
g()
run(_arrow1)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.
if not (isIdentUsed name body) then None
else
match name with
...Test plan
- Added a new test
test Passing a function reference in a TCO context does not capture unneeded outer argstotests/Python/TestTailCall.fs. - Existing Python TCO tests verify that lambdas using TCO variables still capture them correctly.
- CI Python test suite validates output.
π€ Generated with Repo Assist
Generated by Repo Assist Β· β·
To install this agentic workflow, run
gh aw add githubnext/agentics/workflows/repo-assist.md@346204513ecfa08b81566450d7d599556807389f
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)
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)
Activity
dbrattli commentedon Mar 22, 2026
Fixed by #4426