closure_migration
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseClosure migration
闭包迁移
Migrate callers before deleting parametric overloads. Prefer value-taking
APIs with unified closures. Pair with (and
for GPU launch code).
mojo-syntaxmojo-gpu-fundamentals请先迁移调用方,再删除参数化重载。优先采用带统一闭包的传值API。可搭配(GPU启动代码搭配)使用。
mojo-syntaxmojo-gpu-fundamentalsForbidden
严格禁止
Hard ban — do not under any circumstance add /
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 wrapper. That is forbidden.
@__parameter@parameter*_value| Do not | Do instead |
|---|---|
| Unified |
| Make the body unified; pass it as a value |
| File-scope / normal function with an imm parameter |
| Capture list ( |
Keep | Migrate or widen that API to value-taking; do not paper over it |
If a callee still only accepts a comptime function parameter,
use a nested without when that is
what the type requires, or change that API (or leave the call site
unmigrated). Never put on the caller.
capturing[_]def … capturing -> T@__parameter@__parameterFor a still- epilogue next to unified launch closures: capture an
imm parameter (e.g. ) directly. Do not
leave a live LayoutTensor/pointer of that origin in the same scope —
that mut+imm aliases once unified children are formed.
capturingresidual_buf: DeviceBuffer[…]var绝对禁止——任何情况下都不得在嵌套闭包中添加 / 来保留、引入或掩盖旧版闭包问题。无论作为迁移过渡、适配仍需捕获的API、实现“不可变借用”,还是通过轻量包装器,均不允许。
@__parameter@parameter*_value| 禁止操作 | 替代方案 |
|---|---|
在嵌套def上使用 | 使用统一闭包 |
| 将函数体改为统一闭包格式,以值传递方式传入 |
| 使用文件级/普通函数,接收不可变参数 |
在嵌套闭包上使用 | 使用捕获列表( |
因API仍为 | 将该API迁移或扩展为传值类型;不得掩盖问题 |
如果被调用方仍仅接受编译期函数参数,当类型要求时,可使用不带的嵌套,或者修改该API(或暂不迁移调用站点)。绝对不要在调用方添加。
capturing[_]@__parameterdef … capturing -> T@__parameter对于统一启动闭包旁仍为类型的收尾代码:直接捕获不可变参数(例如)。切勿在同一作用域中保留该源对象的可变 LayoutTensor/指针——形成统一子闭包后会产生可变+不可变别名冲突。
capturingresidual_buf: DeviceBuffer[…]varTarget shapes
目标格式
| Legacy | Preferred |
|---|---|
| |
Comptime param | |
Nested | Unified |
@__parametercapturing thinFuncType@__parameter| 旧版格式 | 推荐格式 |
|---|---|
| |
编译期参数 | |
嵌套 | 统一闭包格式 |
带的嵌套def类型为,无法转换为统一的。仅重写调用方式不够——需修改闭包的声明方式(且绝不能重新添加)。
@__parametercapturing thinFuncType@__parameterChecklist
检查清单
- Inventory: and
rg 'api_name\[' --glob '*.mojo'rg '@__parameter|@parameter' --glob '*.mojo' - Rewrite calls: →
api[fn](a, b)api(a, fn, b) - On every nested closure in scope: drop /
@__parameter/@parameter; add a capture list@__copy_capture - If a callee still needs a comptime capturing param → migrate that API first
(or leave the call site unmigrated); do not keep on the caller
@__parameter - Delete parametric overloads only after callers typecheck
- Update skills/docs that still teach the legacy path
- Typecheck: (filters Metal noise)
mojo build --emit llvm <file> -o /tmp/x.ll - Self-check: → zero on nested closures you own
rg '@__parameter|@parameter' --glob '<touched>.mojo'
- 排查:使用和
rg 'api_name\[' --glob '*.mojo'命令扫描代码rg '@__parameter|@parameter' --glob '*.mojo' - 重写调用:→
api[fn](a, b)api(a, fn, b) - 对作用域内所有嵌套闭包:移除/
@__parameter/@parameter;添加捕获列表@__copy_capture - 如果被调用方仍需编译期捕获参数→先迁移该API(或暂不迁移调用站点);不得在调用方保留
@__parameter - 仅在所有调用方通过类型检查后,再删除参数化重载
- 更新仍在教授旧版用法的技能文档
- 类型检查:(过滤Metal相关日志)
mojo build --emit llvm <file> -o /tmp/x.ll - 自我检查:→ 你负责的嵌套闭包中该结果应为0
rg '@__parameter|@parameter' --glob '<touched>.mojo'
Capture choice
捕获方式选择
| Default / symptom | Choice |
|---|---|
| Read-only use of outer state | |
Mutates some outer state; also reads | |
| Mutates several outer names | |
| Needs ownership / move | |
| Named precision only | |
| No free runtime captures | |
| Add |
| That name needs |
| Capture-all |
| Buffer captured |
| |
| |
| Capture list on an |
| Closure struct would hold fields that alias the same origin, one mut and one imm — see Aliasing below |
| |
Do not use capture-all when the closure also mentions register-passable
outer values (, indices, lengths). Mix an explicit name with a
trailing default (). At most one bare convention
( / / ) may appear as the default for unlisted captures.
{mut}Intmutimm{mut tt_in, imm}immmutvar{imm}- writes an output (/
TileTensorwhere.unsafe_ptr()is required), ormut=True - calls a method that internally does then
self._buf.unsafe_ptr()—unsafe_mut_cast[True]()is the usual case. The method may be declaredCacheBustingBuffer.offset_ptr(imm); the captured field still fails to match (selfargument #0). Mut-capture every such buffer (lit.call).{mut cb_a, mut cb_b, mut cb_c, mut cb_a_scales, mut cb_b_scales, imm}
Do not “fix” this with extra (the method already has
that) or by restoring . Only skip when that would create
an aliasing pair with another captured field of the same origin (see below).
unsafe_mut_cast@__parametermut| 默认场景/报错信息 | 选择方案 |
|---|---|
| 仅读取外部状态 | |
修改部分外部状态;同时读取 | |
| 修改多个外部变量 | |
| 需要所有权/转移 | |
| 仅需精确命名捕获 | |
| 无需运行时捕获 | |
| 添加 |
捕获变量出现 | 该变量需要 |
| 全捕获 |
| 缓冲区被 |
| |
| |
| 带 |
| 闭包结构体将包含指向同一源的别名字段,一个可变一个不可变——见下文别名处理 |
| 尽可能用 |
当闭包同时引用寄存器可传递的外部值(、索引、长度等)时,不要使用全捕获。应将显式名称与默认结合使用(例如)。最多只能有一个无修饰的捕获约定( / / )作为未列出变量的默认规则。
Int{mut}mutimm{mut tt_in, imm}immmutvar{imm}- 写入输出(/ 需要
TileTensor的mut=True),或者.unsafe_ptr() - 调用内部执行然后
self._buf.unsafe_ptr()的方法——常见案例为unsafe_mut_cast[True]()。该方法可能被声明为CacheBustingBuffer.offset_ptr(不可变);但捕获的字段仍会匹配失败(self参数#0错误)。需对所有此类缓冲区进行可变捕获(例如lit.call)。{mut cb_a, mut cb_b, mut cb_c, mut cb_a_scales, mut cb_b_scales, imm}
不要通过额外的(方法已包含该逻辑)或恢复来“修复”此问题。仅当与同一源的其他捕获字段产生别名对时,才可以跳过捕获(见下文)。
unsafe_mut_cast@__parametermutAliasing
别名处理
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:
- Make the mutable use immutable if the API allows (read-only
,
DeviceBufferviews, dropas_immut()). Prefer this also when an outer{mut …}stays mutable while a nested unified closure imm-captures a view ofvar buf: call a normal function (file-scope or otherwise non-capturing) that takesbufas an imm parameter, and form the launch there.buf: DeviceBuffer[…]takes an immenqueue_memset, so memset alone does not requireDeviceBuffer.{mut buf, …} - Explicitly mark the mutated/backing buffer in the closure capture list:
When a closure captures a helper/lambda that mutates a buffer (), a default
origin_of(buf)would create an aliasing immutable capture of{imm}. Addingbuf(e.g.mut buf) unifies the capture convention to mutable.{mut buf, imm} - Pass the mutable origin as a closure argument (in the parameter list) so it is not a captured field. When the value-taking API's
mut buf: …is fixed, use a thinFuncTypeadapter that only forwards{mut buf, imm}into the all-imm body. Prefer widening the API when practical.buf - Do not “fix” aliasing with (or similar). That erases the lifetime tracker and is highly discouraged.
.as_unsafe_any_origin() - Do not “fix” anything with .
@__parameter
当多个捕获/嵌套值引用同一源且其中一个为可变时,闭包结构体将包含别名字段。优先按以下顺序处理:
- 将可变用法改为不可变(如果API允许):使用只读、
DeviceBuffer视图,移除as_immut()。当外部{mut …}保持可变,而嵌套统一闭包不可变捕获var buf的视图时,也优先采用此方案:调用一个普通函数(文件级或非捕获函数),接收buf作为不可变参数,并在其中创建启动逻辑。buf: DeviceBuffer[…]接收不可变enqueue_memset,因此仅memset操作无需DeviceBuffer。{mut buf, …} - 在闭包捕获列表中显式标记被修改/作为基础的缓冲区:当闭包捕获一个会修改缓冲区()的辅助函数/ lambda时,默认
origin_of(buf)会创建{imm}的不可变别名捕获。添加buf(例如mut buf)可将捕获约定统一为可变。{mut buf, imm} - 将可变源作为闭包参数传入(参数列表中声明),使其不成为捕获字段。当传值API的
mut buf: …固定时,可使用轻量FuncType适配器,仅将{mut buf, imm}转发至全不可变的函数体。实际可行时优先扩展API。buf - 不要使用(或类似方法)“修复”别名问题。这会擦除生命周期追踪器,强烈不推荐。
.as_unsafe_any_origin() - 不要使用“修复”任何问题。
@__parameter
Safety rules
安全规则
- Key bulk edits off the value-argument name, not every def with that name
- Do not bulk-replace capture lists (destroys etc.)
{mut count} - inside
name[i] =is a local, not an outerwith … as namecapturemut - Zero /
@__parameteron nested closures in migrated code@parameter
- 批量编辑时以值参数名称为依据,而非所有同名def
- 不要批量替换捕获列表(会破坏等自定义设置)
{mut count} - 内部的
with … as name操作针对的是局部变量,而非外部name[i] =捕获mut - 迁移后的代码中,嵌套闭包的/
@__parameter数量必须为0@parameter
More detail
更多细节
Full step-by-step process, error catalog, and verification:
process.md.
完整分步流程、错误目录及验证方法:
process.md