Skip to content

fix[next]: resolve closure variable references by value - #2865

Draft
havogt wants to merge 4 commits into
GridTools:mainfrom
havogt:closure-var-fixes-minimal
Draft

fix[next]: resolve closure variable references by value#2865
havogt wants to merge 4 commits into
GridTools:mainfrom
havogt:closure-var-fixes-minimal

Conversation

@havogt

@havogt havogt commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

References to builtins and field operators are recognized by their source-level name in the later frontend passes, so a builtin referenced through a module (gtx.where) or under an alias (my_where = where), an operator referenced through a module (helpers.helper) or under an alias (helper as h2), and a scalar module attribute (np.pi) all failed, most of them with internal errors late in the pipeline.

Extend ClosureVarFolding to resolve such references by value: scalar module attributes are folded into constants, builtins are rewritten to their builtin name, and module-prefixed operators to a name derived from the operator's definition, registered as additional closure variables. The program lowering registers each callable under every name it is referenced by, which makes aliases work without rewriting and retires the misnamed-function lint.

References to builtins and field operators are recognized by their source-level
name in the later frontend passes, so a builtin referenced through a module
(`gtx.where`) or under an alias (`my_where = where`), an operator referenced
through a module (`helpers.helper`) or under an alias (`helper as h2`), and a
scalar module attribute (`np.pi`) all failed, most of them with internal errors
late in the pipeline.

Extend `ClosureVarFolding` to resolve such references by value: scalar module
attributes are folded into constants, builtins are rewritten to their builtin
name, and module-prefixed operators to a name derived from the operator's
definition, registered as additional closure variables. The program lowering
registers each callable under every name it is referenced by, which makes aliases
work without rewriting and retires the misnamed-function lint.

`typing.Final` scalars and program-level module-prefixed calls are left as they
are.

Claude-Session: https://claude.ai/code/session_01Sr9xdMdZgG4wjLLAKbweYb
…irst

Direct references to a field operator were rewritten to the synthesized,
definition-derived name as well, which changed every call site's name in the
FOAST and IR. Only references reached through a module get a synthesized name.

The shadowing check ran after the closure-variable lookup, so a name already in
the closure variables was accepted even when a local of the same name existed.
On Python 3.12.3 `inspect.getclosurevars` still collects attribute names from
`co_names`, which is how `gtx.where` put `where` there in CI. Check for a
shadowing local or parameter first.

Claude-Session: https://claude.ai/code/session_01Sr9xdMdZgG4wjLLAKbweYb
…ames

Review follow-ups: a FOAST-level test that direct and aliased operator
references are left alone and that module-prefixed references resolve as
intended; the two FrozenNamespace/enum integration tests that the rewrite of
test_closure_vars.py had dropped are restored.

The synthesized name for a module-prefixed operator is now the definition name
plus a hash of its source file and qualified name: the previous scheme mapped
`.` and `_` to the same character, so distinct modules could collide, and it
embedded `__module__`, which differs between running and importing a script.
The builtin lookup is a linear identity scan instead of an id-keyed dict.

Claude-Session: https://claude.ai/code/session_01Sr9xdMdZgG4wjLLAKbweYb
…t the file path

Hashing `co_filename` made the symbol for a module-prefixed operator depend on
where the checkout lives, and the symbol reaches the IR and the on-disk cache
keys. Hash `__module__.__qualname__` instead; a test pins that a definition
moved to another path keeps its name.

Claude-Session: https://claude.ai/code/session_01Sr9xdMdZgG4wjLLAKbweYb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant