Repository navigation
Raise when a block body reads a tensor from the enclosing scope - #1889
Merged
polvalente merged 2 commits intoOct 22, 2026
Merged
Conversation
A block body is compiled as a function of the block's arguments, so a tensor it reads from the enclosing scope cannot be resolved there. The Evaluator returned an unevaluated expression, EXLA failed to find the parameter, and the gradient of such a tensor came out as zeros. The block parameters shared the caller's context, so nothing caught this. Trace the body under a context of its own, as while does, so an operation that mixes a parameter with an outer tensor raises when the block is built. A body that returns an outer tensor unchanged is checked separately. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Contributor
Author
|
I don't see a downside to this change, but this is messing with conventions I'm not super familiar with so there might be a reason to not do this. |
blasphemetheus
marked this pull request as ready for review
October 20, 2026 16:26
polvalente
approved these changes
Oct 22, 2026
polvalente
left a comment
Member
There was a problem hiding this comment.
I think this is an important fix!
For Nx.block we need to keep in mind that the default implementation might never run, so it shouldn't rely on closures for anything. If a runtime value from outside its scope is needed, it must be passed as an argument
This was referenced Oct 24, 2026
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.
Makes
Nx.blockraise when the function given to it reads a tensor from the enclosing scope, instead of producing wrong results.A block body is traced once and then compiled as a function of the block's arguments. A tensor the body reads from outside, such as a weight from the surrounding
defn, is not one of those arguments, so no compiler can resolve it. Today nothing catches this at trace time, and the failure shows up later in different ways:For example, with a block whose body computes
Nx.sin(Nx.dot(x, w))while onlyxis passed as an argument, the gradient with respect towis zeros where the plain function gives nonzero values.The cause is that block parameters were created with the caller's context, so an operation mixing a parameter with an outer tensor looked like ordinary code.
whileavoids this by giving its body a context of its own. The fix does the same for blocks, so the existing "expressions come from different contexts" check fires when the block is built. A body that returns an outer tensor unchanged does not go through that check, so it is checked separately. Outside a trace the arguments have no context, and the check is skipped there aswhiledoes.No block that exists today reads tensors from the enclosing scope. The
Nx.LinAlgblocks,Take,TopK, the FFT blocks and the custom-call blocks all pass their tensors as arguments and close over options only.Three tests cover an operation that mixes a parameter with an outer tensor, a body that returns an outer tensor, and the same mistake inside
grad. All three fail on main.Found while reviewing #1865, where a checkpoint body that reads a weight from the enclosing scope hits exactly this. This draft is based on main at
b532ba1e.🤖 Generated with Claude Code