Rust 错误 E0789 详解:rustc_allowed_through_unstable_modules必须与#[stable]成对出现
【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust
本篇文章深入解析 rustc 编译器内部错误码E0789的触发条件、底层实现与修复方法。该错误与编译器内部属性#[rustc_allowed_through_unstable_modules]密切相关,仅在 rustc 编译器与标准库自身使用#[stable]/#[unstable]稳定性标记(staged API)的场景中出现,普通用户代码不会触发。读完本文,你将理解该属性在稳定性检查体系中的定位、E0789 的产生链路,以及如何正确配对属性避免该错误,并能结合仓库源码与测试用例验证其行为。
错误码 E0789 是什么
E0789 是 rustc 编译器内部错误码,其完整诊断信息定义在 compiler/rustc_error_codes/src/error_codes/E0789.md 中。该文档开篇明确说明:
This error code is internal to the compiler and will not be emitted with normal Rust code.
即E0789 是编译器内部错误,普通 Rust 代码不会触发它。它只在 rustc 自身与标准库(standard library)的构建过程中,当稳定性属性使用不当时才会被报告。
该错误的本质是:内部属性rustc_allowed_through_unstable_modules必须被用在带有#[stable]属性的条目(item)上,否则编译器就报出 E0789。
触发场景:稳定性属性的配对关系
staged API 与稳定性标记体系
要理解 E0789,首先需要了解 Rust 的 staged API(分阶段 API)机制。标准库和编译器内部通过#[stable(feature = "...", since = "...")]与#[unstable(feature = "...", issue = "...")]属性来管理 API 的稳定性,这依赖于staged_api特性。编译器内部属性(以rustc_前缀开头)则依赖rustc_attrs特性,这些特性都是 perma-unstable(永久不稳定)的,绝不允许在编译器与标准库之外使用。
这些内部属性在 compiler/rustc_feature/src/builtin_attrs.rs 中注册,rustc_allowed_through_unstable_modules与staged_api、rustc_attrs同属内部属性列表,并在 compiler/rustc_attr_ir/src/data_structures.rs 中被统一表示为Stability这一属性种类——该注释明确写着它同时代表#[stable]、#[unstable]与#[rustc_allowed_through_unstable_modules],可见三者共享同一套稳定性数据结构。
稳定的条目不能出现在不稳定模块中
默认情况下,当一个条目带有#[stable]属性时,包裹它的模块也必须带有#[stable]属性,否则该条目会变成de facto(事实上)不稳定的——因为用户要使用它,就必须先经过一个不稳定的路径。
#[rustc_allowed_through_unstable_modules]正是为解决这一问题而存在的变通方案(workaround):它允许一个已稳定的条目"逃逸"(escape)出其不稳定的父模块。典型场景是历史上一些条目(如core::intrinsics::transmute)在路径稳定性检查加入之前就"意外地"通过不稳定路径被稳定化,为保持向后兼容而使用该属性放行。
E0789 的产生:缺少配对的#[stable]
E0789 在该属性单独出现、没有配套的#[stable]属性时被触发。下面给出仓库文档中的原始错误示例(注意其中#[stable]被注释掉了):
// NOTE: both of these attributes are perma-unstable and should *never* be // used outside of the compiler and standard library. #![feature(rustc_attrs)] #![feature(staged_api)] #![allow(internal_features)] #![unstable(feature = "foo_module", reason = "...", issue = "123")] #[rustc_allowed_through_unstable_modules( message = "deprecation message", module = "stable_module", )] // #[stable(feature = "foo", since = "1.0")] struct Foo; // ^^^ error: `rustc_allowed_through_unstable_modules` attribute must be // paired with a `stable` attribute编译这段代码会得到:
error[E0789]: `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute其中message与module参数是必需的,分别表示"迁移提示文案"与"建议用户迁移到的新模块路径",具体规则见下文源码解析。
源码级解析:错误在哪里被抛出
1. 属性解析阶段:收集并校验配对
E0789 的实际抛出处位于 compiler/rustc_attr_parsing/src/diagnostics.rs:
#[derive(Diagnostic)] #[diag("`rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute", code = E0789)] pub(crate) struct RustcAllowedUnstablePairing { #[primary_span] pub span: Span, }而触发该诊断的逻辑在 compiler/rustc_attr_parsing/src/attributes/stability.rs 的finalize阶段:
fn finalize(mut self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> { if let Some(atum) = self.allowed_through_unstable_modules { if let Some(( Stability { level: StabilityLevel::Stable { ref mut allowed_through_unstable_modules, .. }, .. }, _, )) = self.stability { *allowed_through_unstable_modules = Some(atum); } else { cx.dcx() .emit_err(diagnostics::RustcAllowedUnstablePairing { span: cx.target_span }); } } // ... }从源码结构可以清晰看到校验逻辑:
- 属性解析器首先收集条目上出现的
rustc_allowed_through_unstable_modules参数(atum); - 在
finalize阶段,检查该条目是否同时带有#[stable]属性(即StabilityLevel::Stable); - 有
#[stable]:把(message, module)二元组写入稳定级中保存(*allowed_through_unstable_modules = Some(atum)),配对成功; - 没有
#[stable]:立即通过dcx().emit_err(...)抛出 E0789 错误。
2. 参数解析规则:message与module缺一不可
在同一文件的 属性解析模板 中,该属性被定义为列表形式message = "...", module = "...":
( &[sym::rustc_allowed_through_unstable_modules], template!(List: &[r#"message = "...", module = "..."#]), unstable!(staged_api), |this, cx, args| { // 依次解析 name-value 对 match name.name { sym::message => { /* 记录 message */ } sym::module => { /* 记录 module */ } _ => { cx.adcx().expected_specific_argument(name.span, &[sym::message, sym::module]); } } // ... let allowed_through_unstable_modules = try { (message?, module?) }; if allowed_through_unstable_modules.is_none() { cx.emit_err(diagnostics::RustcAtumMissingParams { span: cx.attr_span }); } this.allowed_through_unstable_modules = allowed_through_unstable_modules; }, ),关键约束(均有源码依据):
message与module两个参数都是必需的:任何一个缺失,try { (message?, module?) }求值失败,会报出RustcAtumMissingParams诊断("rustc_allowed_through_unstable_modulesattribute must havemessageandmoduleparams",定义在 diagnostics.rs);- 不允许重复键:同一个键出现两次会触发
duplicate_key诊断; - 不允许未知键:出现
message/module之外的键会触发expected_specific_argument诊断; - 该属性本身依赖
staged_api特性门控(unstable!(staged_api))。
3. 路径检查阶段:运行时的放行逻辑
配对成功后,该属性在稳定性检查 pass 中发挥作用。核心逻辑位于 compiler/rustc_passes/src/stability.rs 的visit_path:
- 当路径指向的条目本身是稳定的(
item_is_allowed),编译器会进一步检查路径的父模块段是否稳定(path.segments.iter().rev().skip(1),跳过最后一段避免重复检查); - 如果该条目带有
allowed_through_unstable_modules(即配对成功的(message, module)),则对父模块段改用eval_stability_allow_unstable直接评估(stability.rs); - 若评估结果为不允许(如模块已迁移或用户路径不对),则发出弃用(deprecated)警告,并借助
RustcAtumSuggestion诊断给出迁移提示,引导用户从module参数指定的新路径导入。
源码注释也直接印证了该属性的历史定位(stability.rs):
We include special cases via
#[rustc_allowed_through_unstable_modules]for items that were accidentally stabilized through unstable paths before this check was added, such ascore::intrinsics::transmute.
即:该属性是给"在此检查加入之前、通过不稳定路径被意外稳定化"的条目(例如core::intrinsics::transmute)保留的特殊例外。
正确用法与修复方式
修复 E0789:补上#[stable]属性
修复方式即是为条目同时标注#[stable]与#[rustc_allowed_through_unstable_modules]:
#![feature(rustc_attrs)] #![feature(staged_api)] #![allow(internal_features)] #![unstable(feature = "foo_module", reason = "...", issue = "123")] #[stable(feature = "foo", since = "1.0")] #[rustc_allowed_through_unstable_modules( message = "deprecation message", module = "stable_module", )] struct Foo;注意顺序与要点:
#[stable]是前提,message与module参数必须同时给出;module参数填写建议用户迁移到的新稳定模块路径(字符串形式);message参数填写展示给用户的迁移提示文案;- 该属性只能在编译器与标准库内部使用(依赖 perma-unstable 的
rustc_attrs、staged_api特性)。
仓库中的真实使用范例
仓库测试用例 tests/ui/stability-attribute/auxiliary/allowed-through-unstable-core.rs 给出了该属性的标准写法:
#![crate_type = "lib"] #![feature(staged_api)] #![feature(rustc_attrs)] #![stable(feature = "stable_test_feature", since = "1.2.0")] #[unstable(feature = "unstable_test_feature", issue = "1")] pub mod unstable_module { #[stable(feature = "stable_test_feature", since = "1.2.0")] #[rustc_allowed_through_unstable_modules( message = "use the new path instead", module = "stable", )] pub trait OldStableTraitAllowedThoughUnstable {} #[stable(feature = "stable_test_feature", since = "1.2.0")] pub trait NewStableTraitNotAllowedThroughUnstable {} }对比之下,同模块内未加该属性的NewStableTraitNotAllowedThroughUnstable在从外部通过不稳定路径导入时,会被正常拦截并报出E0658: use of unstable library feature。
行为验证:测试用例与预期输出
仓库的编译测试 tests/ui/stability-attribute/allowed-through-unstable.rs 用//@ aux-build机制构建上述辅助 crate,然后分别导入两个 trait:
extern crate allowed_through_unstable_core; use allowed_through_unstable_core::unstable_module::OldStableTraitAllowedThoughUnstable; //~WARN use of deprecated import through accidentally stabilized module `unstable_module` use allowed_through_unstable_core::unstable_module::NewStableTraitNotAllowedThroughUnstable; //~ ERROR use of unstable library feature `unstable_test_feature`预期的.stderr输出(见 allowed-through-unstable.stderr)精确展示了两种行为的差异:
- 对带该属性的
OldStableTraitAllowedThoughUnstable:只产生一条warning(默认#[warn(deprecated)]),提示"use of deprecated import through accidentally stabilized module",并给出help: use the new path instead的迁移建议; - 对不带该属性的
NewStableTraitNotAllowedThroughUnstable:直接产生error E0658(使用不稳定特性unstable_test_feature),编译失败。
这一对照充分说明:rustc_allowed_through_unstable_modules的效果是把"硬性错误(E0658)"降级为"可修复的弃用警告",同时保留对历史路径的兼容,并通过module参数引导用户迁移到新路径。
相关错误码与延伸阅读
- E0658:
use of unstable library feature,即未放行时从不稳定路径访问稳定条目会触发的错误(同一测试用例中有对照输出); - E0717:
rustc_promotable属性必须与rustc_const_unstable/rustc_const_stable配对,属于同类的"内部属性配对"校验(定义于 diagnostics.rs); - 稳定性检查的完整 pass 位于 compiler/rustc_passes/src/stability.rs,路径检查与放行逻辑集中在其
visit_path实现中; - 完整的错误码文档目录位于 compiler/rustc_error_codes/src/error_codes/,可用
rustc --explain E0789在本地查看该错误说明。
小结
- E0789 是编译器内部错误,普通用户代码不会触发;它只在 rustc 与标准库内部,当
#[rustc_allowed_through_unstable_modules]与#[stable]配对缺失时出现; - 该属性的作用是允许稳定条目从"意外稳定化"的不稳定父模块路径中被访问,并把硬错误降级为带迁移提示的弃用警告;
- 修复方式是为条目同时标注
#[stable]与#[rustc_allowed_through_unstable_modules(message = "...", module = "...")],两个参数缺一不可; - 源码证据链完整:属性注册在 builtin_attrs.rs,配对校验在 attributes/stability.rs,诊断定义在 diagnostics.rs,路径放行逻辑在 rustc_passes/src/stability.rs,并有 tests/ui/stability-attribute/allowed-through-unstable.rs 系列测试用例验证其行为。
【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考