mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
add opt in attribute for stable-in-unstable items
This commit is contained in:
parent
a13f30036a
commit
b55453dbad
@ -137,7 +137,7 @@ impl ConstStability {
|
|||||||
pub enum StabilityLevel {
|
pub enum StabilityLevel {
|
||||||
// Reason for the current stability level and the relevant rust-lang issue
|
// Reason for the current stability level and the relevant rust-lang issue
|
||||||
Unstable { reason: Option<Symbol>, issue: Option<NonZeroU32>, is_soft: bool },
|
Unstable { reason: Option<Symbol>, issue: Option<NonZeroU32>, is_soft: bool },
|
||||||
Stable { since: Symbol },
|
Stable { since: Symbol, allowed_through_unstable_modules: bool },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StabilityLevel {
|
impl StabilityLevel {
|
||||||
@ -172,6 +172,7 @@ where
|
|||||||
let mut stab: Option<(Stability, Span)> = None;
|
let mut stab: Option<(Stability, Span)> = None;
|
||||||
let mut const_stab: Option<(ConstStability, Span)> = None;
|
let mut const_stab: Option<(ConstStability, Span)> = None;
|
||||||
let mut promotable = false;
|
let mut promotable = false;
|
||||||
|
let mut allowed_through_unstable_modules = false;
|
||||||
|
|
||||||
let diagnostic = &sess.parse_sess.span_diagnostic;
|
let diagnostic = &sess.parse_sess.span_diagnostic;
|
||||||
|
|
||||||
@ -182,6 +183,7 @@ where
|
|||||||
sym::unstable,
|
sym::unstable,
|
||||||
sym::stable,
|
sym::stable,
|
||||||
sym::rustc_promotable,
|
sym::rustc_promotable,
|
||||||
|
sym::rustc_allowed_through_unstable_modules,
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
.any(|&s| attr.has_name(s))
|
.any(|&s| attr.has_name(s))
|
||||||
@ -193,6 +195,8 @@ where
|
|||||||
|
|
||||||
if attr.has_name(sym::rustc_promotable) {
|
if attr.has_name(sym::rustc_promotable) {
|
||||||
promotable = true;
|
promotable = true;
|
||||||
|
} else if attr.has_name(sym::rustc_allowed_through_unstable_modules) {
|
||||||
|
allowed_through_unstable_modules = true;
|
||||||
}
|
}
|
||||||
// attributes with data
|
// attributes with data
|
||||||
else if let Some(MetaItem { kind: MetaItemKind::List(ref metas), .. }) = meta {
|
else if let Some(MetaItem { kind: MetaItemKind::List(ref metas), .. }) = meta {
|
||||||
@ -406,7 +410,7 @@ where
|
|||||||
|
|
||||||
match (feature, since) {
|
match (feature, since) {
|
||||||
(Some(feature), Some(since)) => {
|
(Some(feature), Some(since)) => {
|
||||||
let level = Stable { since };
|
let level = Stable { since, allowed_through_unstable_modules: false };
|
||||||
if sym::stable == meta_name {
|
if sym::stable == meta_name {
|
||||||
stab = Some((Stability { level, feature }, attr.span));
|
stab = Some((Stability { level, feature }, attr.span));
|
||||||
} else {
|
} 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)
|
(stab, const_stab)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -644,4 +644,5 @@ E0788: include_str!("./error_codes/E0788.md"),
|
|||||||
// E0721, // `await` keyword
|
// E0721, // `await` keyword
|
||||||
// E0723, // unstable feature in `const` context
|
// E0723, // unstable feature in `const` context
|
||||||
// E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`.
|
// E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`.
|
||||||
|
E0788, // rustc_allowed_through_unstable_modules without stability attribute
|
||||||
}
|
}
|
||||||
|
@ -512,6 +512,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||||||
allow_internal_unsafe, Normal, template!(Word), WarnFollowing,
|
allow_internal_unsafe, Normal, template!(Word), WarnFollowing,
|
||||||
"allow_internal_unsafe side-steps the unsafe_code lint",
|
"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:
|
// Internal attributes: Type system related:
|
||||||
|
@ -139,6 +139,7 @@ impl CheckAttrVisitor<'_> {
|
|||||||
| sym::rustc_const_stable
|
| sym::rustc_const_stable
|
||||||
| sym::unstable
|
| sym::unstable
|
||||||
| sym::stable
|
| sym::stable
|
||||||
|
| sym::rustc_allowed_through_unstable_modules
|
||||||
| sym::rustc_promotable => self.check_stability_promotable(&attr, span, target),
|
| sym::rustc_promotable => self.check_stability_promotable(&attr, span, target),
|
||||||
_ => true,
|
_ => true,
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
//! A pass that annotates every item and method with its stability level,
|
//! A pass that annotates every item and method with its stability level,
|
||||||
//! propagating default levels lexically from parent to children ast nodes.
|
//! propagating default levels lexically from parent to children ast nodes.
|
||||||
|
|
||||||
|
use attr::StabilityLevel;
|
||||||
use rustc_attr::{self as attr, ConstStability, Stability};
|
use rustc_attr::{self as attr, ConstStability, Stability};
|
||||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||||
use rustc_errors::struct_span_err;
|
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,
|
// Check if deprecated_since < stable_since. If it is,
|
||||||
// this is *almost surely* an accident.
|
// 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)
|
(&depr.as_ref().and_then(|(d, _)| d.since), &stab.level)
|
||||||
{
|
{
|
||||||
// Explicit version of iter::order::lt to handle parse errors properly
|
// 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
|
// 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
|
// 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
|
// 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
|
// We check here rather than in `visit_path_segment` to prevent visiting the last
|
||||||
// path segment twice
|
// 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);
|
let parents = path.segments.iter().rev().skip(1);
|
||||||
for path_segment in parents {
|
for path_segment in parents {
|
||||||
if let Some(def_id) = path_segment.res.as_ref().and_then(Res::opt_def_id) {
|
if let Some(def_id) = path_segment.res.as_ref().and_then(Res::opt_def_id) {
|
||||||
|
@ -1191,6 +1191,7 @@ symbols! {
|
|||||||
rustc_allocator_nounwind,
|
rustc_allocator_nounwind,
|
||||||
rustc_allow_const_fn_unstable,
|
rustc_allow_const_fn_unstable,
|
||||||
rustc_allow_incoherent_impl,
|
rustc_allow_incoherent_impl,
|
||||||
|
rustc_allowed_through_unstable_modules,
|
||||||
rustc_attrs,
|
rustc_attrs,
|
||||||
rustc_box,
|
rustc_box,
|
||||||
rustc_builtin_macro,
|
rustc_builtin_macro,
|
||||||
|
@ -1457,6 +1457,7 @@ extern "rust-intrinsic" {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[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_const_stable(feature = "const_transmute", since = "1.56.0")]
|
||||||
#[rustc_diagnostic_item = "transmute"]
|
#[rustc_diagnostic_item = "transmute"]
|
||||||
pub fn transmute<T, U>(e: T) -> U;
|
pub fn transmute<T, U>(e: T) -> U;
|
||||||
|
@ -854,7 +854,7 @@ fn render_stability_since_raw(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let const_title_and_stability = match const_stability {
|
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 =>
|
if Some(since) != containing_const_ver =>
|
||||||
{
|
{
|
||||||
Some((format!("const since {}", since), format!("const: {}", since)))
|
Some((format!("const since {}", since), format!("const: {}", since)))
|
||||||
|
@ -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
|
@ -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`.
|
@ -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(());
|
@ -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'
|
@ -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`.
|
@ -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 {}
|
||||||
|
}
|
@ -353,7 +353,7 @@ fn check_terminator<'a, 'tcx>(
|
|||||||
fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<RustcVersion>) -> bool {
|
fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<RustcVersion>) -> bool {
|
||||||
tcx.is_const_fn(def_id)
|
tcx.is_const_fn(def_id)
|
||||||
&& tcx.lookup_const_stability(def_id).map_or(true, |const_stab| {
|
&& 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
|
// 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`.
|
// 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.
|
// as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
|
||||||
|
Loading…
Reference in New Issue
Block a user