ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Rust E0791 解析:`[linkage]` 属性下外部静态变量的合法类型与编译器检查机制

Rust E0791 解析:`[linkage]` 属性下外部静态变量的合法类型与编译器检查机制 Rust E0791 解析#[linkage]属性下外部静态变量的合法类型与编译器检查机制【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rustRust 编译器错误 E0791invalid type for variable with#[linkage]attribute约束的是extern块中带有#[linkage]属性的静态变量可以声明为哪些类型。本文基于 Rust 编译器仓库中的错误码文档 E0791.md 及其在rustc_hir_analysis中的实际检查实现完整讲解该规则的合法类型清单、报错触发条件、底层源码检查逻辑以及如何通过仓库内的测试用例验证你的写法。一、E0791 是什么E0791 的完整错误信息定义为invalid type for variable with #[linkage] attribute其诊断结构体定义在 diagnostics.rs#[derive(Diagnostic)] #[diag(invalid type for variable with #[linkage] attribute, code E0791)] pub(crate) struct LinkageType { #[primary_span] pub span: Span, }触发场景非常具体在extern C { ... }块中为静态变量声明了#[linkage]属性通常用于声明弱符号如extern_weak但该静态变量的类型不是 C 语义下可空指针的等价类型。编译器会在类型检查阶段rustc_hir_analysis的check_static_item流程发出此错误。二、背景#[linkage]属性与linkage特性#[linkage]是不稳定特性linkage提供的属性用于在 LLVM 层控制符号的链接语义。其解析实现位于 link_attrs.rs从中可以确认两个关键事实1. 允许的属性取值共 9 个命名对齐 LLVM LangRef 文档属性值内部枚举available_externallyLinkage::AvailableExternallycommonLinkage::Commonextern_weakLinkage::ExternalWeakexternalLinkage::ExternalinternalLinkage::InternallinkonceLinkage::LinkOnceAnylinkonce_odrLinkage::LinkOnceODRweakLinkage::WeakAnyweak_odrLinkage::WeakODR源码注释还特别指出ghost、dllimport、dllexport和linkonce_odr_autohide不在支持列表中因为 LLVM 将它们视为 no-op。2. 允许施加的声明目标函数但const fn被明确拒绝见测试中的 const fnare incompatible with thelinkageattribute 报错、固有多数方法与 trait 方法、本地静态变量、外部静态变量extern块中的 staticextern static mut被拒绝、外部函数。属性本身标注为unstable!(linkage)因此使用时需要#![feature(linkage)]。E0791 检查的正是其中外部静态变量 import_linkage这一组合——即通过#[linkage]引入的外部符号。三、合法类型规则E0791 的核心内容根据 E0791.md 的定义extern块中带#[linkage]属性的静态变量必须是与 C 的可空指针nullable pointer等价的类型具体只有两类规则 1裸指针*mut T或*const T其中T可以是任意类型。裸指针天然可以表示空null与 C 弱符号在链接失败时呈现为空地址的语义一致。规则 2两变体枚举形态的Option 指针一个满足全部条件的枚举类型没有#[repr]属性即使用 Rust 原生枚举布局不禁止枚举布局优化恰好两个变体其中一个变体无字段另一个变体有且只有一个字段该字段类型必须是非可空non-nullable类型且只能是以下两种之一引用类型static T/static mut T对应ty::Ref函数指针类型unsafe extern C fn(...)对应ty::FnPtr两个变体的出现顺序不限。这正是Optionstatic T和Optionunsafe extern C fn()的形状Rust 枚举布局优化使它们与可为 null 的指针具有相同的机器布局从而与 C 侧的 nullable pointer 完全等价。一个非法示例#![feature(linkage)] extern C { #[linkage extern_weak] static foo: i8; // E0791i8 不是可空指针类型 }合法示例原错误码文档中的完整示例#![feature(linkage)] extern C { // Option 包裹的 C ABI 函数指针 #[linkage extern_weak] static foo: Optionunsafe extern C fn(); // Option 包裹的静态引用 #[linkage extern_weak] static bar: Optionstatic i8; // 裸指针 #[linkage extern_weak] static baz: *mut i8; }四、源码级实现编译器如何执行这条规则E0791 的实际检查逻辑位于 check.rs由check_static_item在处理静态变量时调用check_static_linkagefn check_static_linkage(tcx: TyCtxt_, def_id: LocalDefId) { if tcx.codegen_fn_attrs(def_id).import_linkage.is_some() { if match tcx.type_of(def_id).instantiate_identity().skip_norm_wip().kind() { ty::RawPtr(_, _) false, // 裸指针合法 ty::Adt(adt_def, args) !is_enum_of_nonnullable_ptr(...), // 枚举走辅助判断 _ true, // 其他类型一律报错 } { tcx.dcx().emit_err(diagnostics::LinkageType { span: tcx.def_span(def_id) }); } } }关键设计点只对import_linkage.is_some()的声明生效——即只有真正带了#[linkage]的外部静态变量才进入该检查普通类型不受影响默认拒绝deny-by-default匹配到ty::RawPtr即放行匹配到 ADT枚举/结构体等则交给is_enum_of_nonnullable_ptr判断其余任何类型整数、裸结构体、元组等一律报 E0791。辅助函数 is_enum_of_nonnullable_ptr 逐条落实了规则 2 的三个条件fn is_enum_of_nonnullable_ptrtcx( tcx: TyCtxttcx, adt_def: AdtDeftcx, args: GenericArgsReftcx, ) - bool { if adt_def.repr().inhibit_enum_layout_opt() { return false; // 条件1带禁止枚举布局优化的 repr 属性则拒绝 } let [var_one, var_two] adt_def.variants().raw[..] else { return false; // 条件2a必须恰好两个变体 }; let (([], [field]) | ([field], [])) (var_one.fields.raw[..], var_two.fields.raw[..]) else { return false; // 条件2b一个变体无字段另一个恰好一个字段顺序不限 }; matches!(field.ty(tcx, args).skip_norm_wip().kind(), ty::FnPtr(..) | ty::Ref(..)) // 条件2c字段类型必须是函数指针或引用 }可以推断这里采用枚举布局等价性而非简单的类型名匹配作为判据是因为该规则的本质要求是机器布局与 C 可空指针一致——只要枚举能享受 Rust 的 niche/双变体指针布局优化OptionTT 非可空就与裸指针同形。这也解释了为什么带#[repr(C)]等 repr 的枚举会被拒绝inhibit_enum_layout_opt()为真时该布局等价性不再成立。五、用仓库测试用例验证仓库中的 UI 测试覆盖了#[linkage]属性的各种边界可用于对照验证本文规则tests/ui/attributes/linkage.rs验证#[linkage]的合法/非法施加目标。其中第 31-37 行的extern C块展示了合法写法——外部静态变量static A: *const ()带#[linkage weak]是允许的同时第 45-47 行确认const fn与linkage属性不兼容tests/ui/linkage-attr/ 目录包含linkage-import.rs跨 crate 引入外部弱符号并打印其地址、common-linkage-non-zero-init.rs、linkage-attr-mutable-static.rs等场景测试展示#[linkage]在extern块中的实际链接行为tests/ui/feature-gates/feature-gate-linkage.rs验证未启用#![feature(linkage)]时该属性被拒绝。六、规则速查表声明类型是否合法说明*mut T/*const T任意 T合法裸指针直接对应 C nullable pointerOptionstatic T等价两变体枚举合法字段为引用非可空Optionunsafe extern C fn(...)合法字段为函数指针非可空i8、整数、结构体等其他类型E0791无法表达符号缺失/为空语义带#[repr(C)]/整型 repr 的 Option 形态枚举E0791inhibit_enum_layout_opt()为真布局等价性不成立三变体及以上、或多字段变体的枚举E0791不满足两变体 单字段约束七、总结E0791 是一条服务于 FFI 场景的类型规则extern块中的#[linkage]静态变量通常用于声明 C 侧的弱符号extern_weak/weak等这些符号可能因链接期未提供而呈现为空地址因此 Rust 侧必须用能与 C 可空指针布局对齐的类型来承接它——即裸指针或无 repr 修饰的无字段变体 单个非可空字段变体枚举实践中就是Optionstatic T和Optionunsafe extern C fn()。规则的文字描述见 E0791.md执行逻辑在 check_static_linkage属性解析在 LinkageParser。如果你的代码触发了 E0791将其类型改为上述合法形态裸指针或Option包装的非可空指针类型即可通过编译。【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表