add opt in attribute for stable-in-unstable items

This commit is contained in:
Jane Lusby 2022-05-09 15:18:53 -07:00 committed by Jane Losare-Lusby
parent a13f30036a
commit b55453dbad
15 changed files with 119 additions and 6 deletions

View File

@ -137,7 +137,7 @@ impl ConstStability {
pub enum StabilityLevel {
// Reason for the current stability level and the relevant rust-lang issue
Unstable { reason: Option<Symbol>, issue: Option<NonZeroU32>, 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)
}

View File

@ -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
}

View File

@ -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:

View File

@ -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,
};

View File

@ -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) {

View File

@ -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,

View File

@ -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<T, U>(e: T) -> U;

View File

@ -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)))

View File

@ -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

View File

@ -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`.

View File

@ -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(());

View File

@ -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'

View File

@ -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 <https://github.com/rust-lang/rust/issues/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`.

View File

@ -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 {}
}

View File

@ -353,7 +353,7 @@ fn check_terminator<'a, 'tcx>(
fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<RustcVersion>) -> 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.