rustc 错误 E0093 深度解析:`[rustc_intrinsic]` 声明未识别内建函数时的诊断与源码机制
E0093(unrecognized intrinsic function)是 rustc 在类型检查阶段对 #[rustc_intrinsic] 属性函数做的名字校验报错:当函数名不在编译器已知的内建函数(intrinsic)列表中时,rustc 会拒绝编译并指出具体的名字。本篇以 rust 仓库中 E0093 错误文档 为核心,完整继承其报错示例与排查建议,并结合 rustc_hir_analysis 中的诊断定义与检查实现源码,讲清该错误的触发路径、合法内建函数名的来源,以及新增内建函数时必须同步修改的位置,帮助开发者准确定位拼写错误、理解编译器对 intrinsic 的两道校验关卡。
什么是 E0093,何时会触发
E0093 的含义非常直接:声明了一个未识别的内建函数(An unknown intrinsic function was declared)。触发前提是代码使用了内部特性 #[rustc_intrinsic] 来声明内建函数,但函数名与编译器内置的内建函数清单对不上。错误信息格式为:
error: unrecognized intrinsic function: `foo`
按照错误文档给出的最小复现代码,需要开启 #![feature(intrinsics)] 并允许内部特性 #![allow(internal_features)],然后声明一个名字不存在的 intrinsic:
#![feature(intrinsics)]
#![allow(internal_features)]
#[rustc_intrinsic]
unsafe fn foo(); // error: unrecognized intrinsic function: `foo`
fn main() {
unsafe {
foo();
}
}
这段代码中的 foo 并不是 rustc 认识的内建函数名,于是编译失败。文档给出的首要排查建议是:检查函数名是否拼写错误。所有可用的内建函数名都可以在 Rust 源码的 library/core/src/intrinsics 模块(即 core::intrinsics)中查找到——这也是本文后续深入源码的依据入口。
#[rustc_intrinsic] 属性本身属于编译器内部属性,其解析逻辑位于 rustc_attr_parsing 的内部属性定义,这也是为什么必须显式 #![allow(internal_features)] 才能使用该属性的原因。
诊断定义:错误信息从哪里来
E0093 的诊断结构体定义在 rustc_hir_analysis 的 diagnostics.rs:
#[derive(Diagnostic)]
#[diag("unrecognized intrinsic function: `{$name}`", code = E0093)]
#[help("if you're adding an intrinsic, be sure to update `check_intrinsic_type`")]
pub(crate) struct UnrecognizedIntrinsicFunction {
#[primary_span]
#[label("unrecognized intrinsic")]
pub span: Span,
pub name: Symbol,
}
两个细节值得注意:
- 错误主标签(label)会落在 intrinsic 声明处,提示
unrecognized intrinsic,方便定位到出错的函数项; - 诊断自带一条 help 文案:“如果你正在新增一个 intrinsic,务必更新
check_intrinsic_type”。这句话直接指向了下一节要讲的核心检查函数,也是 rustc 对贡献者最重要的提示。
触发路径:check_intrinsic_type 的巨型匹配表
真正的报错发生在类型检查阶段。check.rs 在处理带 #[rustc_intrinsic] 属性的函数项时,调用 check_intrinsic_type:
/// Remember to add all intrinsics here, in `compiler/rustc_codegen_llvm/src/intrinsic.rs`,
/// and in `library/core/src/intrinsics.rs`.
pub(crate) fn check_intrinsic_type(
tcx: TyCtxt<'_>,
intrinsic_id: LocalDefId,
span: Span,
intrinsic_name: Symbol,
) {
let generics = tcx.generics_of(intrinsic_id);
let param = |n| { ... }; // 将函数第 n 个类型参数构造为 Ty::new_param
...
该函数以 intrinsic_name(也就是用户写下的函数名,如 foo)为 key,在一张按 sym::... 符号组织的巨型 match 表中查找签名。表中每一项的形态是 (类型参数个数 n_tps, 生命周期参数个数 n_lts, 参数类型列表, 返回类型),例如(摘自 intrinsic.rs 中的原子操作片段):
sym::atomic_cxchg | sym::atomic_cxchgweak => (
1,
2,
vec![Ty::new_mut_ptr(tcx, param(0)), param(0), param(0)],
Ty::new_tup(tcx, &[param(0), tcx.types.bool]),
),
sym::atomic_load => (1, 2, vec![Ty::new_imm_ptr(tcx, param(0))], param(0)),
sym::atomic_store => (1, 2, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], tcx.types.unit),
也就是说,rustc 不仅知道每个 intrinsic 的名字,还静态地规定了它的泛型参数个数与完整函数签名。如果函数名在表中找不到,就会落入兜底分支(intrinsic.rs):
other => {
tcx.dcx().emit_err(UnrecognizedIntrinsicFunction { span, name: other });
return;
}
这里 emit_err 发出的正是 E0093。
即便函数名匹配成功,检查并不会就此结束:check_intrinsic_type 会构造出预期的 FnSig 并调用 equate_intrinsic_type,逐项核对你声明的函数与内建函数应有的生命周期参数、类型参数、const 参数个数是否一致,不一致时发出 WrongNumberOfGenericArgumentsToIntrinsic 诊断;个数一致时再走 check_function_signature 做参数与返回类型的逐项匹配。因此从源码结构看,E0093 是 intrinsic 声明校验链条的第一关——先认名字,再校签名。
另外,同一文件中的 intrinsic_operation_unsafety 还维护了一份“安全可调用”的 intrinsic 名单(如 black_box、align_of、breakpoint、discriminant_value 等),用于决定声明时是否允许省略 unsafe。这解释了为什么有些 intrinsic 在调用侧可以不需要 unsafe 块。
合法内建函数名从哪里查
错误文档明确指引:所有内建函数都定义在 library/core/src/intrinsics。在仓库中对应 library/core/src/intrinsics/mod.rs,它把编译器暴露的各类 intrinsic 集中再导出,供 core::intrinsics 使用。排查 E0093 时,对照这张清单核对函数名即可:
- 名字不存在(或拼错)→ E0093,即本文主题;
- 名字存在,但泛型参数个数或签名与表中规定不符 → 其他诊断(如
WrongNumberOfGenericArgumentsToIntrinsic); - 名字与签名在
rustc_hir_analysis这关都通过了,但代码生成后端不认识该名字 → 见下一节的 E0511 场景。
新增 intrinsic 时必须同步修改的三处
前面 help 文案与 check_intrinsic_type 的文档注释共同构成了 rustc 对贡献者的“同步清单”。从源码注释看,新增一个内建函数通常需要同时更新:
- compiler/rustc_hir_analysis/src/check/intrinsic.rs 中的
check_intrinsic_type匹配表(签名校验)与intrinsic_operation_unsafety安全名单(如需); - compiler/rustc_codegen_llvm/src/intrinsic.rs——LLVM 代码生成后端的 intrinsic 名到 LLVM IR 指令的映射;
- library/core/src/intrinsics/mod.rs——标准库侧的声明与再导出。
遗漏第 1 处,用户代码在类型检查阶段就会撞见 E0093;遗漏第 2 处,则会把问题推迟到代码生成阶段才暴露。
与 E0511 的区别:两道关卡,两个阶段
值得注意的是,unrecognized intrinsic 类错误在编译流程中存在两次拦截。除本文的 E0093(类型检查期、针对显式声明)外,代码生成期还有一道兜底:
- rustc_codegen_ssa 诊断定义:
invalid monomorphization of ... intrinsic: unrecognized intrinsic ...(E0511); - rustc_codegen_llvm 的 intrinsic.rs 在单态化时若遇到后端不认识的内建函数名,会直接
emit_fatal(UnknownIntrinsic { ... })。
可以推断这两道关卡的分工是:E0093 面向“声明者”,在 rustc_hir_analysis 阶段尽早拦截名字错误;E0511 面向“实例化者”,防止某个合法声明在特定单态化场景下映射到后端缺失的实现。排查时若你在类型检查期看到 E0093,应优先核对名字拼写与 library/core/src/intrinsics 清单;若错误延迟到代码生成期,则属于另一类问题。
排查清单
综合错误文档与源码实现,遇到 E0093 时建议按以下顺序处理:
- 核对拼写:确认
#[rustc_intrinsic]后的函数名与 library/core/src/intrinsics/mod.rs 中的条目逐字符一致(错误信息会原样回显名字,方便比对); - 确认 feature gate:
#![feature(intrinsics)]与#![allow(internal_features)]是否齐全,属性拼写是否为rustc_intrinsic; - 检查声明形式:确认函数只有名字与泛型/签名,声明体为空(
unsafe fn foo();),且泛型参数个数、返回类型与 rustc 内置规定一致; - 贡献者路径:如果确实是在给编译器新增 intrinsic,按前述三处同步清单逐一修改,尤其别漏掉
check_intrinsic_type,否则 E0093 会立即出现在所有使用方。
小结
E0093 是 rustc intrinsic 机制在“名字”层面的守门员:诊断定义于 rustc_hir_analysis/src/diagnostics.rs,触发于 check_intrinsic_type 中匹配表的兜底分支(L794-L797)。它提醒我们 rustc 的 intrinsic 不是自由命名的“黑盒函数”,而是一张由编译器源码静态规定名字、泛型个数与签名的严格清单;合法名字的权威来源是 library/core/src/intrinsics。理解这条从 check.rs 到 intrinsic.rs 的调用链后,无论是修复拼写错误还是为编译器新增内建函数,都能做到有的放矢。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00