首页
/ rustc 错误 E0093 深度解析:`[rustc_intrinsic]` 声明未识别内建函数时的诊断与源码机制

rustc 错误 E0093 深度解析:`[rustc_intrinsic]` 声明未识别内建函数时的诊断与源码机制

2026-09-06 14:50:43作者:裴麒琰

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_boxalign_ofbreakpointdiscriminant_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 对贡献者的“同步清单”。从源码注释看,新增一个内建函数通常需要同时更新:

  1. compiler/rustc_hir_analysis/src/check/intrinsic.rs 中的 check_intrinsic_type 匹配表(签名校验)与 intrinsic_operation_unsafety 安全名单(如需);
  2. compiler/rustc_codegen_llvm/src/intrinsic.rs——LLVM 代码生成后端的 intrinsic 名到 LLVM IR 指令的映射;
  3. library/core/src/intrinsics/mod.rs——标准库侧的声明与再导出。

遗漏第 1 处,用户代码在类型检查阶段就会撞见 E0093;遗漏第 2 处,则会把问题推迟到代码生成阶段才暴露。

与 E0511 的区别:两道关卡,两个阶段

值得注意的是,unrecognized intrinsic 类错误在编译流程中存在两次拦截。除本文的 E0093(类型检查期、针对显式声明)外,代码生成期还有一道兜底:

可以推断这两道关卡的分工是:E0093 面向“声明者”,在 rustc_hir_analysis 阶段尽早拦截名字错误;E0511 面向“实例化者”,防止某个合法声明在特定单态化场景下映射到后端缺失的实现。排查时若你在类型检查期看到 E0093,应优先核对名字拼写与 library/core/src/intrinsics 清单;若错误延迟到代码生成期,则属于另一类问题。

排查清单

综合错误文档与源码实现,遇到 E0093 时建议按以下顺序处理:

  1. 核对拼写:确认 #[rustc_intrinsic] 后的函数名与 library/core/src/intrinsics/mod.rs 中的条目逐字符一致(错误信息会原样回显名字,方便比对);
  2. 确认 feature gate#![feature(intrinsics)]#![allow(internal_features)] 是否齐全,属性拼写是否为 rustc_intrinsic
  3. 检查声明形式:确认函数只有名字与泛型/签名,声明体为空(unsafe fn foo();),且泛型参数个数、返回类型与 rustc 内置规定一致;
  4. 贡献者路径:如果确实是在给编译器新增 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.rsintrinsic.rs 的调用链后,无论是修复拼写错误还是为编译器新增内建函数,都能做到有的放矢。

登录后查看全文
热门项目推荐
相关项目推荐