closure_migration

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Closure migration

闭包迁移

Migrate callers before deleting parametric overloads. Prefer value-taking APIs with unified closures. Pair with
mojo-syntax
(and
mojo-gpu-fundamentals
for GPU launch code).
请先迁移调用方,再删除参数化重载。优先采用带统一闭包的传值API。可搭配
mojo-syntax
(GPU启动代码搭配
mojo-gpu-fundamentals
)使用。

Forbidden

严格禁止

Hard ban — do not under any circumstance add
@__parameter
/
@parameter
to a nested closure to persist, introduce, or paper over a legacy closure. Not as a migration bridge, not to satisfy a still-capturing API, not to “borrow imm”, not behind a thin
*_value
wrapper. That is forbidden.
Do notDo instead
@__parameter
/
@parameter
on nested defs
Unified
def … {imm}:
/
{mut x, imm}:
/ named captures
@__parameter
body +
*_value
forwarder
Make the body unified; pass it as a value
@__parameter
“imm borrow” helper
File-scope / normal function with an imm parameter
@__copy_capture
on a nested closure
Capture list (
{imm}
,
{mut x, imm}
, …)
Keep
@__parameter
because an API is still
capturing[_]
/ comptime
fn
Migrate or widen that API to value-taking; do not paper over it
If a callee still only accepts a comptime
capturing[_]
function parameter, use a nested
def … capturing -> T
without
@__parameter
when that is what the type requires, or change that API (or leave the call site unmigrated). Never put
@__parameter
on the caller.
For a still-
capturing
epilogue next to unified launch closures: capture an imm parameter (e.g.
residual_buf: DeviceBuffer[…]
) directly. Do not leave a live
var
LayoutTensor/pointer of that origin in the same scope — that mut+imm aliases once unified children are formed.
绝对禁止——任何情况下都不得在嵌套闭包中添加
@__parameter
/
@parameter
来保留、引入或掩盖旧版闭包问题。无论作为迁移过渡、适配仍需捕获的API、实现“不可变借用”,还是通过轻量
*_value
包装器,均不允许。
禁止操作替代方案
在嵌套def上使用
@__parameter
/
@parameter
使用统一闭包
def … {imm}:
/
{mut x, imm}:
或命名捕获列表
@__parameter
函数体 +
*_value
转发器
将函数体改为统一闭包格式,以值传递方式传入
@__parameter
“不可变借用”辅助函数
使用文件级/普通函数,接收不可变参数
在嵌套闭包上使用
@__copy_capture
使用捕获列表(
{imm}
{mut x, imm}
等)
因API仍为
capturing[_]
/ 编译期
fn
而保留
@__parameter
将该API迁移或扩展为传值类型;不得掩盖问题
如果被调用方仍仅接受编译期
capturing[_]
函数参数,当类型要求时,可使用不带
@__parameter
的嵌套
def … capturing -> T
,或者修改该API(或暂不迁移调用站点)。绝对不要在调用方添加
@__parameter
对于统一启动闭包旁仍为
capturing
类型的收尾代码:直接捕获不可变参数(例如
residual_buf: DeviceBuffer[…]
)。切勿在同一作用域中保留该源对象的可变
var
LayoutTensor/指针——形成统一子闭包后会产生可变+不可变别名冲突。

Target shapes

目标格式

LegacyPreferred
api[fn](a, b)
api(a, fn, b)
Comptime param
fn: def(...) raises capturing[_] -> None
FuncType: def(...) raises -> None
+ runtime
ref func: FuncType
(or
func: FuncType
)
Nested
@__parameter
/
@parameter
def
Unified
def ...(…) raises {imm}:
/
{mut buf, imm}:
/ named captures
@__parameter
nested defs type as
capturing thin
and do not convert to a unified
FuncType
. Call rewrite alone is not enough — change how the closure is declared (and never re-add
@__parameter
).
旧版格式推荐格式
api[fn](a, b)
api(a, fn, b)
编译期参数
fn: def(...) raises capturing[_] -> None
FuncType: def(...) raises -> None
+ 运行时
ref func: FuncType
(或
func: FuncType
嵌套
@__parameter
/
@parameter
定义的def
统一闭包格式
def ...(…) raises {imm}:
/
{mut buf, imm}:
或命名捕获列表
@__parameter
的嵌套def类型为
capturing thin
,无法转换为统一的
FuncType
。仅重写调用方式不够——需修改闭包的声明方式(且绝不能重新添加
@__parameter
)。

Checklist

检查清单

  1. Inventory:
    rg 'api_name\[' --glob '*.mojo'
    and
    rg '@__parameter|@parameter' --glob '*.mojo'
  2. Rewrite calls:
    api[fn](a, b)
    api(a, fn, b)
  3. On every nested closure in scope: drop
    @__parameter
    /
    @parameter
    /
    @__copy_capture
    ; add a capture list
  4. If a callee still needs a comptime capturing param → migrate that API first (or leave the call site unmigrated); do not keep
    @__parameter
    on the caller
  5. Delete parametric overloads only after callers typecheck
  6. Update skills/docs that still teach the legacy path
  7. Typecheck:
    mojo build --emit llvm <file> -o /tmp/x.ll
    (filters Metal noise)
  8. Self-check:
    rg '@__parameter|@parameter' --glob '<touched>.mojo'
    → zero on nested closures you own
  1. 排查:使用
    rg 'api_name\[' --glob '*.mojo'
    rg '@__parameter|@parameter' --glob '*.mojo'
    命令扫描代码
  2. 重写调用:
    api[fn](a, b)
    api(a, fn, b)
  3. 对作用域内所有嵌套闭包:移除
    @__parameter
    /
    @parameter
    /
    @__copy_capture
    ;添加捕获列表
  4. 如果被调用方仍需编译期捕获参数→先迁移该API(或暂不迁移调用站点);不得在调用方保留
    @__parameter
  5. 仅在所有调用方通过类型检查后,再删除参数化重载
  6. 更新仍在教授旧版用法的技能文档
  7. 类型检查:
    mojo build --emit llvm <file> -o /tmp/x.ll
    (过滤Metal相关日志)
  8. 自我检查:
    rg '@__parameter|@parameter' --glob '<touched>.mojo'
    → 你负责的嵌套闭包中该结果应为0

Capture choice

捕获方式选择

Default / symptomChoice
Read-only use of outer state
{imm}
(capture-all) — not if the body calls
offset_ptr
or builds a mut
TileTensor
(see freeze note below)
Mutates some outer state; also reads
Int
/ other register-passable values
{mut buf, imm}
not capture-all
{mut}
Mutates several outer names
{mut a, mut b, imm}
Needs ownership / move
{var}
or named
var x
Named precision only
{mut count}
,
{imm buf, imm shape}
No free runtime captures
{}
Could not infer capture convention
Add
{imm}
/
{mut name, imm}
/ named list
expression must be mutable in assignment
on a capture
That name needs
mut
register passible value … can not be captured by 'mut'
Capture-all
{mut}
pulled in an
Int
(etc.) — use
{mut buf, imm}
.mut … is 'False' but … is 'True'
on
.unsafe_ptr()
/
TileTensor
Buffer captured
{imm}
{mut out_buf, imm}
; do not paper over with
unsafe_mut_cast
'lit.call' op callee expected call argument #0
on
offset_ptr
/ similar
{imm}
froze the buffer used as
self
{mut cb_a, mut cb_b, …, imm}
cannot bind an RValue to a reference
on
bench_func
kernel_launch
nested inside
bench_func
with
@__copy_capture
— define
kernel_launch
at outer function scope and remove
@__copy_capture
from
bench_func
expected ':' in function definition
at
raises {…}
Capture list on an
@__parameter
def — strip
@__parameter
and keep the list
aliasing values passed immutably…mutably
/ note names
origin_of(buf)
Closure struct would hold fields that alias the same origin, one mut and one imm — see Aliasing below
cannot capture … not copyable
/ not a parameter reference
{imm}
if possible; else
{var}
/ named
var x
; never
@__parameter
Do not use capture-all
{mut}
when the closure also mentions register-passable outer values (
Int
, indices, lengths). Mix an explicit
mut
name with a trailing default
imm
(
{mut tt_in, imm}
). At most one bare convention (
imm
/
mut
/
var
) may appear as the default for unlisted captures.
{imm}
freezes captured buffers. That is wrong whenever the body:
  • writes an output (
    TileTensor
    /
    .unsafe_ptr()
    where
    mut=True
    is required), or
  • calls a method that internally does
    self._buf.unsafe_ptr()
    then
    unsafe_mut_cast[True]()
    CacheBustingBuffer.offset_ptr
    is the usual case. The method may be declared
    self
    (imm); the captured field still fails to match (
    lit.call
    argument #0). Mut-capture every such buffer (
    {mut cb_a, mut cb_b, mut cb_c, mut cb_a_scales, mut cb_b_scales, imm}
    ).
Do not “fix” this with extra
unsafe_mut_cast
(the method already has that) or by restoring
@__parameter
. Only skip
mut
when that would create an aliasing pair with another captured field of the same origin (see below).
默认场景/报错信息选择方案
仅读取外部状态
{imm}
(捕获所有)——如果函数体调用
offset_ptr
或创建可变
TileTensor
则不适用(见下文冻结说明)
修改部分外部状态;同时读取
Int
或其他寄存器可传递值
{mut buf, imm}
——不要使用全捕获
{mut}
修改多个外部变量
{mut a, mut b, imm}
需要所有权/转移
{var}
或命名
var x
仅需精确命名捕获
{mut count}
{imm buf, imm shape}
无需运行时捕获
{}
Could not infer capture convention
(无法推断捕获约定)
添加
{imm}
/
{mut name, imm}
或命名列表
捕获变量出现
expression must be mutable in assignment
(赋值时表达式必须可变)
该变量需要
mut
捕获
register passible value … can not be captured by 'mut'
(寄存器可传递值无法被
mut
捕获)
全捕获
{mut}
包含了
Int
等类型——改用
{mut buf, imm}
.unsafe_ptr()
/
TileTensor
出现
.mut … is 'False' but … is 'True'
缓冲区被
{imm}
捕获——改用
{mut out_buf, imm}
不要
unsafe_mut_cast
掩盖问题
offset_ptr
或类似函数出现
'lit.call' op callee expected call argument #0
{imm}
冻结了作为
self
的缓冲区——改用
{mut cb_a, mut cb_b, …, imm}
bench_func
出现
cannot bind an RValue to a reference
(无法将右值绑定到引用)
kernel_launch
嵌套在带
@__copy_capture
bench_func
中——将
kernel_launch
定义在外部函数作用域,并移除
bench_func
@__copy_capture
raises {…}
处出现
expected ':' in function definition
(函数定义中缺少冒号)
@__parameter
的def添加了捕获列表——移除
@__parameter
并保留捕获列表
aliasing values passed immutably…mutably
/ 提示
origin_of(buf)
闭包结构体将包含指向同一源的别名字段,一个可变一个不可变——见下文别名处理
cannot capture … not copyable
/ 不是参数引用
尽可能用
{imm}
;否则用
{var}
或命名
var x
;绝不要用
@__parameter
当闭包同时引用寄存器可传递的外部值(
Int
、索引、长度等)时,不要使用全捕获
{mut}
。应将显式
mut
名称与默认
imm
结合使用(例如
{mut tt_in, imm}
)。最多只能有一个无修饰的捕获约定(
imm
/
mut
/
var
)作为未列出变量的默认规则。
{imm}
冻结捕获的缓冲区。当函数体出现以下情况时,该方式不适用:
  • 写入输出(
    TileTensor
    / 需要
    mut=True
    .unsafe_ptr()
    ),或者
  • 调用内部执行
    self._buf.unsafe_ptr()
    然后
    unsafe_mut_cast[True]()
    的方法——常见案例为
    CacheBustingBuffer.offset_ptr
    。该方法可能被声明为
    self
    (不可变);但捕获的字段仍会匹配失败(
    lit.call
    参数#0错误)。需对所有此类缓冲区进行可变捕获(例如
    {mut cb_a, mut cb_b, mut cb_c, mut cb_a_scales, mut cb_b_scales, imm}
    )。
不要通过额外的
unsafe_mut_cast
(方法已包含该逻辑)或恢复
@__parameter
来“修复”此问题。仅当与同一源的其他捕获字段产生别名对时,才可以跳过
mut
捕获(见下文)。

Aliasing

别名处理

When several captured / nested values reference the same origin and one of them is mutable, the closure struct would contain aliasing fields. Prefer, in order:
  1. Make the mutable use immutable if the API allows (read-only
    DeviceBuffer
    ,
    as_immut()
    views, drop
    {mut …}
    ). Prefer this also when an outer
    var buf
    stays mutable while a nested unified closure imm-captures a view of
    buf
    : call a normal function (file-scope or otherwise non-capturing) that takes
    buf: DeviceBuffer[…]
    as an imm parameter, and form the launch there.
    enqueue_memset
    takes an imm
    DeviceBuffer
    , so memset alone does not require
    {mut buf, …}
    .
  2. Explicitly mark the mutated/backing buffer in the closure capture list: When a closure captures a helper/lambda that mutates a buffer (
    origin_of(buf)
    ), a default
    {imm}
    would create an aliasing immutable capture of
    buf
    . Adding
    mut buf
    (e.g.
    {mut buf, imm}
    ) unifies the capture convention to mutable.
  3. Pass the mutable origin as a closure argument (
    mut buf: …
    in the parameter list) so it is not a captured field. When the value-taking API's
    FuncType
    is fixed, use a thin
    {mut buf, imm}
    adapter that only forwards
    buf
    into the all-imm body. Prefer widening the API when practical.
  4. Do not “fix” aliasing with
    .as_unsafe_any_origin()
    (or similar). That erases the lifetime tracker and is highly discouraged.
  5. Do not “fix” anything with
    @__parameter
    .
当多个捕获/嵌套值引用同一源且其中一个为可变时,闭包结构体将包含别名字段。优先按以下顺序处理:
  1. 将可变用法改为不可变(如果API允许):使用只读
    DeviceBuffer
    as_immut()
    视图,移除
    {mut …}
    。当外部
    var buf
    保持可变,而嵌套统一闭包不可变捕获
    buf
    的视图时,也优先采用此方案:调用一个普通函数(文件级或非捕获函数),接收
    buf: DeviceBuffer[…]
    作为不可变参数,并在其中创建启动逻辑。
    enqueue_memset
    接收不可变
    DeviceBuffer
    ,因此仅memset操作无需
    {mut buf, …}
  2. 在闭包捕获列表中显式标记被修改/作为基础的缓冲区:当闭包捕获一个会修改缓冲区(
    origin_of(buf)
    )的辅助函数/ lambda时,默认
    {imm}
    会创建
    buf
    的不可变别名捕获。添加
    mut buf
    (例如
    {mut buf, imm}
    )可将捕获约定统一为可变。
  3. 将可变源作为闭包参数传入(参数列表中声明
    mut buf: …
    ),使其不成为捕获字段。当传值API的
    FuncType
    固定时,可使用轻量
    {mut buf, imm}
    适配器,仅将
    buf
    转发至全不可变的函数体。实际可行时优先扩展API。
  4. 不要使用
    .as_unsafe_any_origin()
    (或类似方法)“修复”别名问题。这会擦除生命周期追踪器,强烈不推荐。
  5. 不要使用
    @__parameter
    “修复”任何问题。

Safety rules

安全规则

  • Key bulk edits off the value-argument name, not every def with that name
  • Do not bulk-replace capture lists (destroys
    {mut count}
    etc.)
  • name[i] =
    inside
    with … as name
    is a local, not an outer
    mut
    capture
  • Zero
    @__parameter
    /
    @parameter
    on nested closures in migrated code
  • 批量编辑时以值参数名称为依据,而非所有同名def
  • 不要批量替换捕获列表(会破坏
    {mut count}
    等自定义设置)
  • with … as name
    内部的
    name[i] =
    操作针对的是局部变量,而非外部
    mut
    捕获
  • 迁移后的代码中,嵌套闭包的
    @__parameter
    /
    @parameter
    数量必须为0

More detail

更多细节

Full step-by-step process, error catalog, and verification: process.md.
完整分步流程、错误目录及验证方法: process.md