proto[next]: ambient values (clean reimplementation) - #72
Open
havogt wants to merge 1 commit into
Open
Conversation
Declared as annotations in a container and bound to plain ContextVars at program-execution time, so a value is reached by bare name inside an operator and never appears in a signature. A declaration becomes a synthesised program parameter when the program is defined, so it travels the ordinary path from there; the two forms differ only in whether that parameter is listed as static. Reimplemented against tmp/ambient_spec.md rather than ported, so it carries none of the residue of the designs it went through.
havogt
added a commit
to GridTools/gt4py_knowledge
that referenced
this pull request
Aug 6, 2026
…, open the Declaration question Repoints at the reimplementation (havogt/gt4py#72) and drops the content-hash bullet, which is no longer part of that prototype. Corrects a wrong claim: gt4py's existing static-argument substitution only visits the program body, so an ambient 'Static[T]' was specialised per value but never folded — the recompile cost without the benefit. Notes that the variant-count check cannot catch this, since it measures specialisation rather than folding. Records why a Declaration owning its ContextVar may be better than the raw variable: validation of bind keys, type-checking of values, and no unscoped '.set()'.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Prototype, not for upstream. Supersedes #71, which reached this design through several redesigns and carries their residue. This was written from a specification rather than ported, so the two are directly comparable:
What it does
Declarations are annotations bound to plain
contextvars.ContextVars.Grid.dx(class access) is the variable, thebind=key;grid.dx(instance access) is its value, so embedded execution sees an ordinary scalar. A filled container binds everything it carries — a grid is one thing semantically and each program picks the parts it reads. Connectivities bind by the same rule with aFieldOffsetas the declaration.A declaration becomes a synthesised program parameter when the program is defined, so from there it is an ordinary argument. The two forms differ only in whether that parameter is listed in
static_params:Extern→ one compiled program for all values,Static→ one per distinct value.One correction carried over from #71
#71 claimed
Staticneeded no new machinery because gt4py's existing static-argument fold would bake the value in. That was wrong.StaticArgsubstitution only visitsitir_program.body, and an ambient reference lives inside a lowered operator'sFunctionDefinition. Verified on both branches by grepping the generated C++:So
Staticwas paying the recompile cost without the benefit. This branch extends the substitution into the function definitions, filtering the symbol map by each definition's own parameters so a shadowing local cannot be clobbered.Worth noting the variant-count test could not catch this — it measures specialisation, not folding.
Deliberately out of scope
No content hashing:
freeze(),frozen_content_hash()and thehash_offset_provider_items_by_idrewrite from #71 are dropped, andcommon.pyis untouched. That is an orthogonal improvement to a function whose own docstring callsid()-keying unsafe, and it should be proposed on its own.Also out: ambient fields, connectivities declared inside containers, and
a(mesh.V2E)in an operator.FieldOffsetstays a module-level declaration and the offset provider is still assembled and passed as today.Design decisions worth review
Base.dxandDerived.dxwould be different variables and binding the base would leave the derived unbound. Use composition.id()is unusable — it would miss the cache every run. A digest ofmodule.qualname.attris used, and containers that share both (a factory called twice) are rejected withname=as an escape hatch.ts.NamespaceTypewas added instead of the throwaway-class hack in proto[next]: ambient binding of the offset provider #71; keeping the class inambient.pywould have madetype_translationimportambient.Known rough edges
Grid.dx(class access) is a rawContextVar, so it owns nothing:bindcannot validate keys or type-check values, andGrid.dx.set(0.5)escapes all scoping. Making it aDeclarationthat owns the variable would fix all three; noted in the proposal as an open question._get_closure_vars_recursivelyrather than miscompiled — better than silent shadowing, but not the clean support intended.Grid.dxinside an operator body, andgrid.dxas an argument in a program body, fail with internal errors rather thanDSLErrors.Tests
tests/next_tests/unit_tests/test_ambient.py(backend-free mechanics) andtests/next_tests/integration_tests/feature_tests/ffront_tests/test_ambient_values.py(backend matrix). Verified independently of the implementation: 331 passed, 4 skipped across both plustest_arg_call_interface.py, CPU backends;pre-commit runclean.