[BUG] Reusing an alloc_var name as a loop target causes an out-of-scope error
- Dominant language
- Python
- Stars
- 7.4k
- Forks
- 745
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 104
Description
### Required prerequisites
- [x] I have read the documentation .
- [x] I have searched the [Issue Tracker](https://github.com/tile-ai/tilelang/issues) that this hasn't already been reported. (comment there if it has.)
### What version of TileLang are you using?
0.1.14 and 0.1.14+cuda.gitd178b4c9
### System information
Reproduced in:
- Linux, Python 3.13, TileLang 0.1.14, PyTorch 2.7.1+cu128, RTX 5090.
- Windows, Python 3.13, TileLang source build at d178b4c9, PyTorch 2.11.0+cu130, RTX 5060 Laptop.
The failure occurs during frontend IR construction, before CUDA compilation.
### Problem description
Reusing a previous T.alloc_var name as the induction variable of a subsequent loop raises an out-of-scope error.
The second loop introduces a new induction variable, but the frontend treats its binding as an assignment to the previous mutable scalar. Renaming only the second loop target avoids the error.
### Reproducible example code
```python
import tilelang.language as T
@T.prim_func
def kernel(A: T.Tensor((2,), "int32")):
with T.Kernel(1, threads=1):
for j in T.serial(2):
d = T.alloc_var("int32")
d = j
A[j] = d
for d in T.Parallel(2):
A[d] = d
Control case: replace the second loop with:
for k in T.Parallel(2):
A[k] = k
```
### Traceback
```pytb
Relevant excerpt:
File ".../repro_loop_rebind.py", line 17, in kernel
A[d] = d
File ".../tilelang/language/eager/builder.py", in rval
raise RuntimeError(...)
RuntimeError: Immutable variable `d` is used outside its defining region!
The error dump shows the second loop incorrectly storing its induction variable into the old buffer:
d[0] = v
```
### Expected behavior
The second loop should bind d to its own induction variable, without writing to the previous alloc_var. The kernel should produce [0, 1].
Actual reads of expired bindings should remain rejected.
### Additional context
In Builder.bind(), an existing local.var binding takes the mutable-assignment path when the incoming value is a PrimExpr. Loop targets currently use this same path.
Distinguishing loop-target binding from ordinary assignment avoids the unintended buffer store while preserving mutable-scalar updates and scope validation.
Contributor guide
Research direction
Start in tilelang/language/eager/builder.py at Builder.bind(), then run the provided reproducer to observe how the second loop target is handled. Done means the kernel produces [0, 1] without the out-of-scope error, while reads of expired bindings remain rejected.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100