ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Roc 语言数字类型后缀解析:以 `0.F` 为例剖析 `undeclared_type` 编译错误的完整处理链路

Roc 语言数字类型后缀解析:以 `0.F` 为例剖析 `undeclared_type` 编译错误的完整处理链路 Roc 语言数字类型后缀解析以0.F为例剖析undeclared_type编译错误的完整处理链路【免费下载链接】rocA fast, friendly, functional language.项目地址: https://gitcode.com/GitHub_Trending/ro/roc本篇技术指南围绕 Roc 编译器GitHub 项目ro/roc快照测试test/snapshots/number_suffix_undeclared_type.md展开深入剖析带类型后缀的数字字面量引用了作用域中不存在的类型这一典型编译错误场景。读者将掌握 Roc 数字类型后缀如0.I64、3.14.Dec的词法、语法、规范化canonicalization三层处理机制理解undeclared_type诊断如何被触发、传播并最终成为类型为Error的运行时错误表达式以及如何借助快照测试工具验证与更新这类行为。快照文件结构与数字后缀测试定位test/snapshots/README.md明确说明快照测试通过捕获 Roc 代码在每个编译阶段的输出来验证编译器行为覆盖tokenization、parsing、canonicalization、type checking完整流水线用于在编译器行为发生意外变化时检测回归。每个快照文件包含期望输出本主题文件正是其中一枚普通快照typeexpr聚焦数字后缀引用的类型不在作用域内时各阶段的表现。本快照的核心观察对象是数字字面量的类型后缀语法。Roc 允许在数字字面量后直接跟.再接一个类型名例如0.I64、123.U8、3.14.Dec该后缀决定了数字字面量的解释类型。而本文分析的是反例当后缀引用的类型F在当前作用域中根本不存在时编译器如何在各个阶段降级处理。快照文件逐段解读META测试类型与描述descriptionNumber with type suffix that is not in scope typeexprtypeexpr表示该快照针对一个表达式级别的源码片段做编译验证description用一句话概括测试意图——带类型后缀的数字但该类型不在作用域内。SOURCE最小触发用例0.F仅一行、三个字符就足以触发整个错误处理链路。0是整数字面量.紧跟其后无空格F是后缀类型标识符。从词法上看0与F之间没有空格且以点号衔接被切分为Int与NoSpaceDotUpperIdent两个 token。EXPECTED 与 PROBLEMS期望结果与诊断EXPECTED NIL PROBLEMS NIL这两个区段均为NIL含义需要结合test/snapshots/README.md的说明理解普通快照的PROBLEMS区段包含每个reporting.Report的规范化 S-表达式序列化见src/reporting/report_sexpr.zigNIL表示编译过程没有产生任何报告diagnostic。也就是说这个用例在编译阶段不产生面向用户的可报告诊断错误信息被静默地记录为表达式本身。这里有一个值得注意的细节0.F这类后缀类型未声明的写法在规范化阶段被转换成运行时错误表达式见下文 CANONICALIZE 区段但它没有像类型不匹配那样生成独立的PROBLEMS报告。这与test/snapshots/number_suffix_custom_type.md123.Foo因from_numeral类型不兼容产生 Type Mismatch 报告形成鲜明对比后者在PROBLEMS区段有完整报告而前者是纯降级路径。TOKENS词法输出Int,NoSpaceDotUpperIdent, EndOfFile,词法层把源码切成两个有效 token 加一个结束标记Int整数字面量0NoSpaceDotUpperIdent紧贴数字的.F——NoSpace强调点号前不能有空格UpperIdent表示以大写字母开头的标识符EndOfFile文件结束。PARSE语法树输出(e-typed-int (raw 0) (type F))解析器将0.F构建为带类型的整数字面量表达式e-typed-intraw 0是原始数字文本type F是后缀类型标识符。对应到源码src/parse/Parser.zig在处理Inttoken 后检查下一个 token 是否为NoSpaceDotUpperIdent若是则将类型标识符解析出来构造AST.Expr.typed_int节点该结构体包含number_tok、type_ident、literal、region四个字段见 AST.zig 中 typed_int 定义。语法阶段不验证类型是否存在它只负责把语法形状正确记录下来。FORMATTED格式化输出NO CHANGE格式化器认为0.F已经是规范写法无需任何改动。这说明NO CHANGE是格式化输出的原样保留标记也印证了该语法在格式层面完全合法。CANONICALIZE规范化输出(e-runtime-error (tag undeclared_type))这是本快照最核心的语义降级点。规范化canonicalization阶段发现后缀类型F不在作用域内于是把整个表达式替换为运行时错误表达式e-runtime-error错误标签为undeclared_type。其底层实现位于 Can.zigresolveNumericSuffixTargetCan.zig#L6504负责把后缀类型标识符解析为目标先做作用域类型绑定查找若查不到再回退检查是否为编译器内置数值类型builtinNumKindFromTypeIdentCan.zig#L6476 覆盖U8/I8/U16/I16/U32/I32/U64/I64/U128/I128/F32/F64/Dec两者都失败则返回null调用方在.typed_int分支中拿到null后直接调用pushMalformed注入Diagnostic{ .undeclared_type .{ .name type_ident, .region region } }Can.zig#L11185-L11197随后continue跳过正常路径使该表达式在 CIR 中成为错误节点。typed_frac分支Can.zig#L11216-L11228对小数后缀走完全相同逻辑。Diagnostic.undeclared_type的定义见 Diagnostic.zig携带name未声明的类型标识符与region源码区域两个字段它会被写入 AST/CIR 节点存储最终以diag_undeclared_type标记的节点形态保留见 NodeStore.zig。TYPES类型推导输出(expr (type Error))类型检查阶段对上述错误表达式统一赋予Error类型。这是 Roc 的类型系统处理编译期已判死表达式的标准方式——错误表达式携带Error类型参与后续类型统一时不会产生二次诊断也不会引发级联的类型不匹配噪声。源码级原理数字后缀的三阶段生命周期结合源码0.F的完整生命周期可归纳为三个阶段任何后缀数字字面量无论类型是否在作用域内都走同一骨架词法/语法阶段ParserInttoken 后紧跟NoSpaceDotUpperIdent时Parser.zig 把后缀类型记录进AST.Expr.typed_int。此处顺带处理两类历史兼容语法形如123u64的旧式紧凑后缀NumericLiteral.deprecatedSuffixFromSource识别typeIdentFromDeprecatedSuffix映射为现代类型名见 Parser.zig#L793-L796以及0.U64这类现代点式后缀。旧式后缀还会触发deprecated_number_suffix诊断Parser.zig#L806-L810提示迁移到点式写法。规范化阶段CanonicalizationresolveNumericSuffixTarget以先作用域、后内置类型的顺序解析后缀类型命中作用域内类型记录后缀目标recordNumericSuffixTarget并构建e_typed_int/e_typed_num_from_numeral等 CIR 表达式未命中任何类型注入undeclared_type诊断并降级为e_runtime_error即本快照展示的路径。类型检查阶段Type Checker错误表达式统一获得Error类型避免后续级联报错。测试佐证int_test.zig 中的同族用例src/canonicalize/test/int_test.zig提供了与本快照同族的单元测试可交叉印证上述机制canonicalize builtin typed integer suffix without caller setupint_test.zig#L64-L740.I64正常规范化为e_typed_int值为0类型名为I64——内置类型后缀无需额外设置即可解析canonicalize builtin typed fractional suffix without caller setupint_test.zig#L76-L863.14.Dec规范化为e_typed_frac值为3_140_000_000_000_000_000定点表示类型名为Dectyped numeric suffix still uses ordinary scope lookupint_test.zig#L88-L107123.UnknownType与快照0.F完全同构断言诊断列表中确实存在名为UnknownType的undeclared_type诊断——这正是快照文件中CANONICALIZE区段(e-runtime-error (tag undeclared_type))的单元测试对应物typed numeric suffix uses local shadow of builtin numeric typeint_test.zig#L109-L137在局部定义U64 : {}后写123.U64后缀目标解析为.local本地类型优先于内置类型并产生builtin_type_shadowed_warning警告——证明后缀解析遵循普通作用域查找规则用户类型可以遮蔽内置数值类型。对比视角声明类型存在时的行为差异将本快照与test/snapshots/number_suffix_custom_type.md对比可以更精确地界定undeclared_type路径的边界场景后缀类型状态规范化结果诊断报告0.F本快照完全未声明e-runtime-error (tag undeclared_type)无PROBLEMSNIL123.Foocustom_type 快照已声明但from_numeral签名不兼容保留e_typed_int并进入类型检查Type Mismatch 完整报告后者要求自定义类型Foo提供签名形如Numeral - Try(Foo, [InvalidNumeral(Str)])的from_numeral方法见 number_suffix_custom_type.md 中的定义与 PROBLEMS 区段 的期望类型提示才能作为数字后缀使用而0.F因为类型根本不存在连检查from_numeral签名的资格都没有直接在规范化阶段降级。两条路径共同勾勒出 Roc 数字后缀的类型解析策略后缀类型必须先能在作用域中解析到内置类型或自定义类型解析不到即判死为undeclared_type运行时错误解析得到但方法签名不合规则留给类型检查阶段产出面向用户的诊断。如何复现与验证快照工具的使用test/snapshots/README.md提供了复现与维护本快照的完整命令使用 Zig 构建系统# 生成/刷新所有快照 zig build run-snapshot-tool # 仅处理指定快照文件 zig build run-snapshot-tool -- test/snapshots/number_suffix_undeclared_type.md # 当期望行为PROBLEMS 等确实改变时以当前输出更新期望值 zig build run-snapshot-tool -- test/snapshots/number_suffix_undeclared_type.md --update-expected快照工具的入口位于 src/snapshot_tool/main.zig其中META的type字段决定编译与捕获方式expr类型按表达式编译并逐一填充TOKENS/PARSE/FORMATTED/CANONICALIZE/TYPES等区段。若你修改了数字后缀解析逻辑应先运行--update-expected审视新输出是否符合预期再决定是否保留该变更——这正是快照测试作为回归探测器的日常用法。小结本快照在编译器测试体系中的角色test/snapshots/number_suffix_undeclared_type.md用最小的源码样本0.F钉住了 Roc 编译器处理数字后缀类型未声明时的整条降级链路语法层接受IntNoSpaceDotUpperIdent合法构成e-typed-int表达式规范化层判死resolveNumericSuffixTarget解析失败后注入undeclared_type诊断表达式降级为e-runtime-error类型层收敛错误表达式统一为Error类型不产生级联报告诊断语义该场景不生成面向用户的PROBLEMS报告与类型不匹配等场景的报告化处理形成刻意区分详见 test/snapshots/README.md 关于语义诊断与渲染输出的说明。对编译器开发者而言这一快照既是回归防线也是理解错误如何在不产生诊断报告的前提下安全降级的样板对 Roc 语言使用者而言它提醒我们数字类型后缀必须指向作用域内可见的类型内置数值类型或实现了from_numeral的自定义类型否则字面量会被静默降级为Error值进而影响包含它的整个表达式的类型推导。【免费下载链接】rocA fast, friendly, functional language.项目地址: https://gitcode.com/GitHub_Trending/ro/roc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表