尧图精选

Roc 编译器 mono 快照测试解析:为什么顶级常量永远不会被 lambda 捕获

🕒 发布时间:2026/9/18 14:01:37 📁 来源:尧图网络
Roc 编译器 mono 快照测试解析为什么顶级常量永远不会被 lambda 捕获【免费下载链接】rocA fast, friendly, functional language.项目地址: https://gitcode.com/GitHub_Trending/ro/roc导读本文以 Roc 仓库中的 mono 快照测试 test/snapshots/mono_pure_lambda.md 为线索逐区块拆解这份“纯 lambda”编译测试文件说明它如何验证一个关键编译器行为顶级top-level常量不会被 lambda 捕获因此不会进入闭包的捕获集合。读完本文你将掌握 Roc 快照测试文件的区块结构与阅读方法理解e-closure/e-lookup-local等规范 IRcanonical IR形态的区别并能对照 src/snapshot_tool/main.zig 中的 mono 测试执行与校验流程把这份快照当作调试编译器行为时的可复现样本。一、快照文件整体结构与 mono 测试的定位Roc 仓库的 test/snapshots/ 目录存放大量编译快照测试。每份快照是一个 Markdown 文件用#标题将一次编译过程切成多个“观察点”从源码到词法、语法、规范 IR、类型推断逐层记录结果。mono_pure_lambda.md属于mono类型测试在 src/snapshot_tool/main.zig 的NodeType枚举中mono与file、package、platform、app、snippet、reporting一样属于“完整模块”级测试见 src/snapshot_tool/main.zig 的注释“Snippet, mono, and reporting tests are full modules”。整份文件共十个区块区块内容META测试元信息description一句话概括测试意图typemono标记测试类型SOURCE被测的原始 Roc 源码MONO单态化monomorphize后的类型化表示FORMATTED格式化结果NO CHANGE表示格式化前后无差异EXPECTED期望输出NIL表示无PROBLEMS编译诊断问题列表NIL表示无TOKENS词法分析产出的 token 流PARSE语法分析产出的 ASTs-expression 形式CANONICALIZE规范 IRcanon IR即去糖、消解后的中间表示TYPES类型推断结果inferred types执行快照测试时工具会按固定顺序输出区块对 mono 测试顺序是META, SOURCE, MONO, FORMATTED之后紧跟其余区块见 src/snapshot_tool/main.zig。二、被测源码一行常量加一个纯 lambdaSOURCE区块给出了三段极简的顶层声明one 1 add_one |x| x one result add_one(5)one 1顶级常量位于模块顶层type-mod并非任何函数体内的局部变量add_one |x| x oneRoc 的 lambda 语法|x|是参数列表函数体引用x自身参数与one外部名字result add_one(5)对 lambda 应用整数5。这份测试的主题即description所述“top-level constants are never captured by lambdas”顶级常量永远不会被 lambda 捕获。one虽然被 lambda 的函数体引用但它属于模块作用域而不是某个外层函数调用帧中的局部变量因此编译器在规范 IR 中处理它的方式与处理“局部变量捕获”有本质区别——这正是后文CANONICALIZE对比的重点。三、MONO 输出单态化后的类型标注视图one : Dec one 1 add_one |x| x one result : Dec result add_one(5)MONO是 mono 测试特有的产物它给出“单态化monomorphized后的类型字符串”。在 src/snapshot_tool/main.zig 的注释中明确写着“Get the defaulted (monomorphized) type string for an expression”。可以看到one : Dec整数字面量1默认解析为Dec十进制小数类型所以顶级常量one被标注为Decadd_one未附加类型标注说明单态化后add_one仍是多态的详见第六节类型推断result : Decadd_one(5)的应用结果单态化为Dec。值得强调的是MONO输出并非随意打印工具会对它做一次完整校验validateMonoOutput会把 MONO 文本当作“无 header 的 type module”重新解析、规范化并类型检查见 src/snapshot_tool/main.zig。也就是说这份带标注的代码必须本身是合法、可类型检查的 Roc 代码测试才算通过。四、词法与语法证据TOKENS 与 PARSETOKENS区块用逗号分隔的 token 名记录了词法流LowerIdent,OpAssign,Int, LowerIdent,OpAssign,OpBar,LowerIdent,OpBar,LowerIdent,OpPlus,LowerIdent, LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,Int,CloseRound, EndOfFile,三行分别对应三条声明LowerIdent(one) OpAssign() Int(1)LowerIdent(add_one) OpAssign() OpBar(|) LowerIdent(x) OpBar(|) LowerIdent(x) OpPlus() LowerIdent(one)——注意 lambda 的|x|由两个OpBar包裹参数名函数体是x oneLowerIdent(result) OpAssign() LowerIdent(add_one) NoSpaceOpenRound(5) CloseRound——NoSpaceOpenRound表示调用括号紧跟被调用者无空格Roc 的调用语法。PARSE区块给出了等价的 AST(file (type-mod) (statements (s-decl (p-ident (raw one)) (e-int (raw 1))) (s-decl (p-ident (raw add_one)) (e-lambda (args (p-ident (raw x))) (e-binop (op ) (e-ident (raw x)) (e-ident (raw one))))) (s-decl (p-ident (raw result)) (e-apply (e-ident (raw add_one)) (e-int (raw 5))))))关键结构一目了然文件根节点带type-mod类型模块三条s-decl声明中add_one对应e-lambda参数x、函数体是e-binop 连接e-ident x与e-ident oneresult对应e-apply调用add_one实参为整数5。语法层面此时并不区分“捕获”与“普通引用”该区分发生在下一阶段的规范 IR 中。五、核心主题CANONICALIZE 证明顶级常量不进捕获集规范 IRCANONICALIZE区块是这份快照真正想“说话”的地方(can-ir (d-let (p-assign (ident one)) (e-num (value 1))) (d-let (p-assign (ident add_one)) (e-lambda (args (p-assign (ident x))) (e-dispatch-call (method plus) (constraint-fn-var 222) (receiver (e-lookup-local (p-assign (ident x)))) (args (e-lookup-local (p-assign (ident one))))))) (d-let (p-assign (ident result)) (e-call (constraint-fn-var 234) (e-lookup-local (p-assign (ident add_one))) (e-num (value 5)))))注意三点每条顶层声明变成d-letlet 绑定one的值是e-num (value 1)add_one的 lambda 体把x one编译成e-dispatch-call (method plus)x是 receiverone是参数二者都以e-lookup-local方式按名查表关键差异这里的 lambda 节点没有任何e-closure包装也没有captures列表。one作为顶级常量在规范 IR 中被当作可直接按名查找e-lookup-local的全局定义而不是需要随闭包对象携带的捕获值。把它与同目录下的“局部变量捕获”快照对比差异立刻显现。在 test/snapshots/mono_closure_single_capture.md 中外层函数参数x被内层 lambda 引用其规范 IR 出现(s-let (p-assign (ident add_x)) (e-closure (captures (capture (ident x))) (e-lambda ...)))内层 lambda 被显式包进e-closure并列出捕获集合(capture (ident x))。多变量版本 test/snapshots/mono_closure_multiple_captures.md 同样出现(capture (ident a)) (capture (ident b))。由此可以得出这条编译器行为链函数参数、函数体内 let 绑定的局部变量被内层 lambda 引用时编译器会构建e-closure并记录捕获列表而模块顶层声明的常量被引用时只生成e-lookup-local查找永不进入任何闭包的捕获集合。从源码结构看这与 Roc 的模块语义一致——顶级常量是全局可寻址的定义其生命周期覆盖整个模块无需闭包为其保留环境快照。这既是本快照description的验证目标也是理解 Roc 闭包内存布局捕获集大小即闭包负载的起点。六、TYPES类型推断与约束求解TYPES区块给出推断类型(inferred-types (defs (patt (type Dec)) (patt (type a - a where [a.plus : a, Dec - a])) (patt (type Dec))) (expressions (expr (type Dec)) (expr (type a - a where [a.plus : a, Dec - a])) (expr (type Dec))))one : Dec字面量1默认单态化为Decadd_one : a - a where [a.plus : a, Dec - a]由于one是Decx one中的即plus方法要求“a加上一个Dec仍得到a”于是add_one保持多态a - a但携带约束a.plus : a, Dec - a。也就是说a在“存在与Dec相加能力”的数字类型范围内自由result : Dec以5Dec实例化add_one后得到Dec。对照 test/snapshots/mono_arithmetic.md 可以看到同样模式sum 1 2推断为Dec规范 IR 中是e-dispatch-call (method plus)作用于两个e-num。而涉及闭包的 test/snapshots/mono_closure_single_capture.md 里类型约束还会出现from_numeral数字字面量转换约束本测试因one已显式为Dec而省去了该约束的复杂度。七、FORMATTED / EXPECTED / PROBLEMS格式化与诊断基线FORMATTED输出NO CHANGE源码本身已符合 Roc 格式化规范格式化前后无任何改动。快照测试会把源码交给格式化器任何非规范书写如多余空格都会导致此区块出现改写后的代码从而暴露格式化回归EXPECTED输出NIL该测试没有独立的“期望输出”mono 测试的断言对象主要是MONO/CANONICALIZE/TYPES等中间产物PROBLEMS输出NIL编译全程未产生任何诊断问题无类型错误、无解析错误与第六节推断结果吻合。八、背后的测试基础设施这份快照不是孤立的文本它在 src/snapshot_tool/main.zig 中被真实执行类型识别# MONO\n~~~roc\n是区块标题常量src/snapshot_tool/main.zig遇到即标记为 mono 节点执行路径mono 测试走“完整模块”路径先对SOURCE做解析、规范化和类型检查再生成MONO区块并对MONO文本调用validateMonoOutput重新校验src/snapshot_tool/main.zig确保单态化输出本身是合法可编译的 Roc单态化取型通过“Get the defaulted (monomorphized) type string”的接口从活动符号表中取出每个定义/表达式的单态化类型字符串src/snapshot_tool/main.zig已知限制源码中以 TODO 注明“mono 测试暂未运行常量折叠constant folding待 zig-16 分支的 ComptimeEvaluator 就绪后补上”src/snapshot_tool/main.zig这说明当前MONO输出反映的是未做编译期求值的真实单态化视图。九、如何利用这份快照做进一步验证读者可以沿两条路径继续深入横向对比同一测试家族把本文件与 test/snapshots/mono_closure_single_capture.md、test/snapshots/mono_closure_multiple_captures.md、test/snapshots/mono_nested_closures.md 并排阅读可以系统观察“捕获数量从 0 → 1 → 多”时e-closure/captures的增长形态从而理解 Roc 闭包表示与内存布局运行快照测试在构建好编译器的环境下执行快照测试工具参见仓库根目录 README.md 与构建文档 BUILDING_FROM_SOURCE.md对本文件做增删捕获变量的改动后重新生成快照即可亲手验证“顶级常量不进入捕获集”这一边界行为——注意仓库为只读实验应在本地副本中进行。总结mono_pure_lambda.md用十一个区块、二十余行文本完整记录了一次“纯 lambda”编译过程SOURCE定义顶级常量one与引用它的 lambdaMONO给出单态化类型标注TOKENS/PARSE呈现词法与语法证据CANONICALIZE以“无e-closure、仅e-lookup-local”的形态证明顶级常量不被捕获TYPES展示约束求解结果。作为typemono快照家族的一员它与 test/snapshots/mono_closure_single_capture.md 等文件互为镜像共同界定了 Roc 闭包捕获系统的语义边界捕获只针对局部环境全局常量永远按名解析。【免费下载链接】rocA fast, friendly, functional language.项目地址: https://gitcode.com/GitHub_Trending/ro/roc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →