tile-ai / tile-ai/tilelang

[BUG] Reusing an alloc_var name as a loop target causes an out-of-scope error

Open
#3,231 0 comments 0 reactions 0 assignees View on GitHub
bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.