diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index ce7c0eb72cd..46643df84cc 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -137,7 +137,7 @@ impl ConstStability { pub enum StabilityLevel { // Reason for the current stability level and the relevant rust-lang issue Unstable { reason: Option, issue: Option, is_soft: bool }, - Stable { since: Symbol }, + Stable { since: Symbol, allowed_through_unstable_modules: bool }, } impl StabilityLevel { @@ -172,6 +172,7 @@ where let mut stab: Option<(Stability, Span)> = None; let mut const_stab: Option<(ConstStability, Span)> = None; let mut promotable = false; + let mut allowed_through_unstable_modules = false; let diagnostic = &sess.parse_sess.span_diagnostic; @@ -182,6 +183,7 @@ where sym::unstable, sym::stable, sym::rustc_promotable, + sym::rustc_allowed_through_unstable_modules, ] .iter() .any(|&s| attr.has_name(s)) @@ -193,6 +195,8 @@ where if attr.has_name(sym::rustc_promotable) { promotable = true; + } else if attr.has_name(sym::rustc_allowed_through_unstable_modules) { + allowed_through_unstable_modules = true; } // attributes with data else if let Some(MetaItem { kind: MetaItemKind::List(ref metas), .. }) = meta { @@ -406,7 +410,7 @@ where match (feature, since) { (Some(feature), Some(since)) => { - let level = Stable { since }; + let level = Stable { since, allowed_through_unstable_modules: false }; if sym::stable == meta_name { stab = Some((Stability { level, feature }, attr.span)); } else { @@ -447,6 +451,27 @@ where } } + if allowed_through_unstable_modules { + if let Some(( + Stability { + level: StabilityLevel::Stable { ref mut allowed_through_unstable_modules, .. }, + .. + }, + _, + )) = stab + { + *allowed_through_unstable_modules = true; + } else { + struct_span_err!( + diagnostic, + item_sp, + E0788, + "`rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute" + ) + .emit(); + } + } + (stab, const_stab) } diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index d507293ccb0..56f29dcc5ce 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -644,4 +644,5 @@ E0788: include_str!("./error_codes/E0788.md"), // E0721, // `await` keyword // E0723, // unstable feature in `const` context // E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`. + E0788, // rustc_allowed_through_unstable_modules without stability attribute } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 6fcdfe44d8f..c806df82145 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -512,6 +512,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ allow_internal_unsafe, Normal, template!(Word), WarnFollowing, "allow_internal_unsafe side-steps the unsafe_code lint", ), + rustc_attr!(rustc_allowed_through_unstable_modules, Normal, template!(Word), WarnFollowing, + "rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \ + through unstable paths"), // ========================================================================== // Internal attributes: Type system related: diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index d0723c68a77..7bfa4d37e8c 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -139,6 +139,7 @@ impl CheckAttrVisitor<'_> { | sym::rustc_const_stable | sym::unstable | sym::stable + | sym::rustc_allowed_through_unstable_modules | sym::rustc_promotable => self.check_stability_promotable(&attr, span, target), _ => true, }; diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 046fdc3dbf4..cf23e0ae5ef 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -1,6 +1,7 @@ //! A pass that annotates every item and method with its stability level, //! propagating default levels lexically from parent to children ast nodes. +use attr::StabilityLevel; use rustc_attr::{self as attr, ConstStability, Stability}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::struct_span_err; @@ -224,7 +225,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { // Check if deprecated_since < stable_since. If it is, // this is *almost surely* an accident. - if let (&Some(dep_since), &attr::Stable { since: stab_since }) = + if let (&Some(dep_since), &attr::Stable { since: stab_since, .. }) = (&depr.as_ref().and_then(|(d, _)| d.since), &stab.level) { // Explicit version of iter::order::lt to handle parse errors properly @@ -819,7 +820,19 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { }, ); - if item_is_allowed { + let is_allowed_through_unstable_modules = |def_id| { + self.tcx + .lookup_stability(def_id) + .map(|stab| match stab.level { + StabilityLevel::Stable { allowed_through_unstable_modules, .. } => { + allowed_through_unstable_modules + } + _ => false, + }) + .unwrap_or(false) + }; + + if item_is_allowed && !is_allowed_through_unstable_modules(def_id) { // Check parent modules stability as well if the item the path refers to is itself // stable. We only emit warnings for unstable path segments if the item is stable // or allowed because stability is often inherited, so the most common case is that @@ -827,6 +840,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // // We check here rather than in `visit_path_segment` to prevent visiting the last // path segment twice + // + // 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 as `core::intrinsics::transmute` let parents = path.segments.iter().rev().skip(1); for path_segment in parents { if let Some(def_id) = path_segment.res.as_ref().and_then(Res::opt_def_id) { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9b6967621f1..61a3100c049 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1191,6 +1191,7 @@ symbols! { rustc_allocator_nounwind, rustc_allow_const_fn_unstable, rustc_allow_incoherent_impl, + rustc_allowed_through_unstable_modules, rustc_attrs, rustc_box, rustc_builtin_macro, diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index cb6f42f1cc0..9097ffc2cc5 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1457,6 +1457,7 @@ extern "rust-intrinsic" { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(bootstrap), rustc_allowed_through_unstable_modules)] #[rustc_const_stable(feature = "const_transmute", since = "1.56.0")] #[rustc_diagnostic_item = "transmute"] pub fn transmute(e: T) -> U; diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 459b0fed6e8..c1fdece9ec6 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -854,7 +854,7 @@ fn render_stability_since_raw( } let const_title_and_stability = match const_stability { - Some(ConstStability { level: StabilityLevel::Stable { since }, .. }) + Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. }) if Some(since) != containing_const_ver => { Some((format!("const since {}", since), format!("const: {}", since))) diff --git a/src/test/ui/stability-attribute/accidental-stable-in-unstable.rs b/src/test/ui/stability-attribute/accidental-stable-in-unstable.rs new file mode 100644 index 00000000000..f8bbe90cfc5 --- /dev/null +++ b/src/test/ui/stability-attribute/accidental-stable-in-unstable.rs @@ -0,0 +1,10 @@ +#![crate_type = "lib"] +extern crate core; + +// Known accidental stabilizations with no known users, slated for un-stabilization +// fully stable @ core::char::UNICODE_VERSION +use core::unicode::UNICODE_VERSION; //~ ERROR use of unstable library feature 'unicode_internals' + +// Known accidental stabilizations with known users +// fully stable @ core::mem::transmute +use core::intrinsics::transmute; // depended upon by rand_core diff --git a/src/test/ui/stability-attribute/accidental-stable-in-unstable.stderr b/src/test/ui/stability-attribute/accidental-stable-in-unstable.stderr new file mode 100644 index 00000000000..ff733822cab --- /dev/null +++ b/src/test/ui/stability-attribute/accidental-stable-in-unstable.stderr @@ -0,0 +1,11 @@ +error[E0658]: use of unstable library feature 'unicode_internals' + --> $DIR/accidental-stable-in-unstable.rs:6:5 + | +LL | use core::unicode::UNICODE_VERSION; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unicode_internals)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/stability-attribute/allow-through-unstable-misuse.rs b/src/test/ui/stability-attribute/allow-through-unstable-misuse.rs new file mode 100644 index 00000000000..7f942309e04 --- /dev/null +++ b/src/test/ui/stability-attribute/allow-through-unstable-misuse.rs @@ -0,0 +1,8 @@ +#![crate_type = "lib"] +#![feature(staged_api)] +#![feature(rustc_attrs)] +#![stable(feature = "foo", since = "1.0.0")] + +#[unstable(feature = "bar", issue = "none")] +#[rustc_allowed_through_unstable_modules] +pub struct UnstableType(()); diff --git a/src/test/ui/stability-attribute/allowed-through-unstable.rs b/src/test/ui/stability-attribute/allowed-through-unstable.rs new file mode 100644 index 00000000000..ff0228e4da6 --- /dev/null +++ b/src/test/ui/stability-attribute/allowed-through-unstable.rs @@ -0,0 +1,9 @@ +// Test for new `#[rustc_allowed_through_unstable_modules]` attribute +// +// aux-build:allowed-through-unstable-core.rs +#![crate_type = "lib"] + +extern crate allowed_through_unstable_core; + +use allowed_through_unstable_core::unstable_module::OldStableTraitAllowedThoughUnstable; +use allowed_through_unstable_core::unstable_module::NewStableTraitNotAllowedThroughUnstable; //~ ERROR use of unstable library feature 'unstable_test_feature' diff --git a/src/test/ui/stability-attribute/allowed-through-unstable.stderr b/src/test/ui/stability-attribute/allowed-through-unstable.stderr new file mode 100644 index 00000000000..70b1dc3010a --- /dev/null +++ b/src/test/ui/stability-attribute/allowed-through-unstable.stderr @@ -0,0 +1,12 @@ +error[E0658]: use of unstable library feature 'unstable_test_feature' + --> $DIR/allowed-through-unstable.rs:10:5 + | +LL | use allowed_through_unstable_core::unstable_module::NewStableTraitNotAllowedThroughUnstable; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #1 for more information + = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/stability-attribute/auxiliary/allowed-through-unstable-core.rs b/src/test/ui/stability-attribute/auxiliary/allowed-through-unstable-core.rs new file mode 100644 index 00000000000..b597009a309 --- /dev/null +++ b/src/test/ui/stability-attribute/auxiliary/allowed-through-unstable-core.rs @@ -0,0 +1,14 @@ +#![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] + pub trait OldStableTraitAllowedThoughUnstable {} + + #[stable(feature = "stable_test_feature", since = "1.2.0")] + pub trait NewStableTraitNotAllowedThroughUnstable {} +} diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 498dcbb8900..0e19b0296f6 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -353,7 +353,7 @@ fn check_terminator<'a, 'tcx>( fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option) -> bool { tcx.is_const_fn(def_id) && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| { - if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level { + if let rustc_attr::StabilityLevel::Stable { since, .. } = const_stab.level { // Checking MSRV is manually necessary because `rustc` has no such concept. This entire // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`. // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.