Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce implied_by in #[unstable] attribute #99212

Merged
merged 6 commits into from
Jul 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 44 additions & 5 deletions compiler/rustc_attr/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,42 @@ impl ConstStability {
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
#[derive(HashStable_Generic)]
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, allowed_through_unstable_modules: bool },
/// `#[unstable]`
Unstable {
/// Reason for the current stability level.
reason: Option<Symbol>,
/// Relevant `rust-lang/rust` issue.
issue: Option<NonZeroU32>,
is_soft: bool,
/// If part of a feature is stabilized and a new feature is added for the remaining parts,
/// then the `implied_by` attribute is used to indicate which now-stable feature previously
/// contained a item.
///
/// ```pseudo-Rust
/// #[unstable(feature = "foo", issue = "...")]
/// fn foo() {}
/// #[unstable(feature = "foo", issue = "...")]
/// fn foobar() {}
/// ```
///
/// ...becomes...
///
/// ```pseudo-Rust
/// #[stable(feature = "foo", since = "1.XX.X")]
/// fn foo() {}
/// #[unstable(feature = "foobar", issue = "...", implied_by = "foo")]
/// fn foobar() {}
/// ```
implied_by: Option<Symbol>,
},
/// `#[stable]`
Stable {
/// Rust release which stabilized this feature.
since: Symbol,
/// Is this item allowed to be referred to on stable, despite being contained in unstable
/// modules?
allowed_through_unstable_modules: bool,
},
}

impl StabilityLevel {
Expand Down Expand Up @@ -243,6 +276,7 @@ where
let mut issue = None;
let mut issue_num = None;
let mut is_soft = false;
let mut implied_by = None;
for meta in metas {
let Some(mi) = meta.meta_item() else {
handle_errors(
Expand Down Expand Up @@ -308,6 +342,11 @@ where
}
is_soft = true;
}
sym::implied_by => {
if !get(mi, &mut implied_by) {
continue 'outer;
}
}
_ => {
handle_errors(
&sess.parse_sess,
Expand All @@ -332,7 +371,7 @@ where
);
continue;
}
let level = Unstable { reason, issue: issue_num, is_soft };
let level = Unstable { reason, issue: issue_num, is_soft, implied_by };
if sym::unstable == meta_name {
stab = Some((Stability { level, feature }, attr.span));
} else {
Expand Down Expand Up @@ -391,7 +430,7 @@ where
meta.span(),
AttrError::UnknownMetaItem(
pprust::path_to_string(&mi.path),
&["since", "note"],
&["feature", "since"],
),
);
continue 'outer;
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_metadata/src/rmeta/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
tcx.arena.alloc_from_iter(self.root.lib_features.decode(self))
}

/// Iterates over the stability implications in the given crate (when a `#[unstable]` attribute
/// has an `implied_by` meta item, then the mapping from the implied feature to the actual
/// feature is a stability implication).
fn get_stability_implications(self, tcx: TyCtxt<'tcx>) -> &'tcx [(Symbol, Symbol)] {
tcx.arena.alloc_from_iter(self.root.stability_implications.decode(self))
}

/// Iterates over the language items in the given crate.
fn get_lang_items(self, tcx: TyCtxt<'tcx>) -> &'tcx [(DefId, usize)] {
tcx.arena.alloc_from_iter(
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,9 @@ provide! { <'tcx> tcx, def_id, other, cdata,
tcx.arena.alloc_slice(&result)
}
defined_lib_features => { cdata.get_lib_features(tcx) }
stability_implications => {
cdata.get_stability_implications(tcx).iter().copied().collect()
}
is_intrinsic => { cdata.get_is_intrinsic(def_id.index) }
defined_lang_items => { cdata.get_lang_items(tcx) }
diagnostic_items => { cdata.get_diagnostic_items() }
Expand Down
15 changes: 15 additions & 0 deletions compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
let lib_features = self.encode_lib_features();
let lib_feature_bytes = self.position() - i;

// Encode the stability implications.
i = self.position();
let stability_implications = self.encode_stability_implications();
let stability_implications_bytes = self.position() - i;

// Encode the language items.
i = self.position();
let lang_items = self.encode_lang_items();
Expand Down Expand Up @@ -686,6 +691,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
crate_deps,
dylib_dependency_formats,
lib_features,
stability_implications,
lang_items,
diagnostic_items,
lang_items_missing,
Expand All @@ -710,6 +716,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
let computed_total_bytes = preamble_bytes
+ dep_bytes
+ lib_feature_bytes
+ stability_implications_bytes
+ lang_item_bytes
+ diagnostic_item_bytes
+ native_lib_bytes
Expand Down Expand Up @@ -761,6 +768,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
p("preamble", preamble_bytes);
p("dep", dep_bytes);
p("lib feature", lib_feature_bytes);
p("stability_implications", stability_implications_bytes);
p("lang item", lang_item_bytes);
p("diagnostic item", diagnostic_item_bytes);
p("native lib", native_lib_bytes);
Expand Down Expand Up @@ -1777,6 +1785,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
self.lazy_array(lib_features.to_vec())
}

fn encode_stability_implications(&mut self) -> LazyArray<(Symbol, Symbol)> {
empty_proc_macro!(self);
let tcx = self.tcx;
let implications = tcx.stability_implications(LOCAL_CRATE);
self.lazy_array(implications.iter().map(|(k, v)| (*k, *v)))
}

fn encode_diagnostic_items(&mut self) -> LazyArray<(Symbol, DefIndex)> {
empty_proc_macro!(self);
let tcx = self.tcx;
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_metadata/src/rmeta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ pub(crate) struct CrateRoot {
crate_deps: LazyArray<CrateDep>,
dylib_dependency_formats: LazyArray<Option<LinkagePreference>>,
lib_features: LazyArray<(Symbol, Option<Symbol>)>,
stability_implications: LazyArray<(Symbol, Symbol)>,
lang_items: LazyArray<(DefIndex, usize)>,
lang_items_missing: LazyArray<lang_items::LangItem>,
diagnostic_items: LazyArray<(Symbol, DefIndex)>,
Expand Down
14 changes: 7 additions & 7 deletions compiler/rustc_middle/src/middle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@ pub mod dependency_format;
pub mod exported_symbols;
pub mod lang_items;
pub mod lib_features {
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_span::symbol::Symbol;
use rustc_data_structures::fx::FxHashMap;
use rustc_span::{symbol::Symbol, Span};

#[derive(HashStable, Debug)]
pub struct LibFeatures {
// A map from feature to stabilisation version.
pub stable: FxHashMap<Symbol, Symbol>,
pub unstable: FxHashSet<Symbol>,
/// A map from feature to stabilisation version.
pub stable: FxHashMap<Symbol, (Symbol, Span)>,
pub unstable: FxHashMap<Symbol, Span>,
}

impl LibFeatures {
pub fn to_vec(&self) -> Vec<(Symbol, Option<Symbol>)> {
let mut all_features: Vec<_> = self
.stable
.iter()
.map(|(f, s)| (*f, Some(*s)))
.chain(self.unstable.iter().map(|f| (*f, None)))
.map(|(f, (s, _))| (*f, Some(*s)))
.chain(self.unstable.iter().map(|(f, _)| (*f, None)))
.collect();
all_features.sort_unstable_by(|a, b| a.0.as_str().partial_cmp(b.0.as_str()).unwrap());
all_features
Expand Down
24 changes: 23 additions & 1 deletion compiler/rustc_middle/src/middle/stability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ pub struct Index {
pub stab_map: FxHashMap<LocalDefId, Stability>,
pub const_stab_map: FxHashMap<LocalDefId, ConstStability>,
pub depr_map: FxHashMap<LocalDefId, DeprecationEntry>,
/// Mapping from feature name to feature name based on the `implied_by` field of `#[unstable]`
/// attributes. If a `#[unstable(feature = "implier", implied_by = "impliee")]` attribute
/// exists, then this map will have a `impliee -> implier` entry.
///
/// This mapping is necessary unless both the `#[stable]` and `#[unstable]` attributes should
/// specify their implications (both `implies` and `implied_by`). If only one of the two
/// attributes do (as in the current implementation, `implied_by` in `#[unstable]`), then this
/// mapping is necessary for diagnostics. When a "unnecessary feature attribute" error is
/// reported, only the `#[stable]` attribute information is available, so the map is necessary
/// to know that the feature implies another feature. If it were reversed, and the `#[stable]`
/// attribute had an `implies` meta item, then a map would be necessary when avoiding a "use of
/// unstable feature" error for a feature that was implied.
davidtwco marked this conversation as resolved.
Show resolved Hide resolved
pub implications: FxHashMap<Symbol, Symbol>,
}

impl Index {
Expand Down Expand Up @@ -423,7 +436,9 @@ impl<'tcx> TyCtxt<'tcx> {

match stability {
Some(Stability {
level: attr::Unstable { reason, issue, is_soft }, feature, ..
level: attr::Unstable { reason, issue, is_soft, implied_by },
feature,
..
}) => {
if span.allows_unstable(feature) {
debug!("stability: skipping span={:?} since it is internal", span);
Expand All @@ -433,6 +448,13 @@ impl<'tcx> TyCtxt<'tcx> {
return EvalResult::Allow;
}

// If this item was previously part of a now-stabilized feature which is still
// active (i.e. the user hasn't removed the attribute for the stabilized feature
// yet) then allow use of this item.
if let Some(implied_by) = implied_by && self.features().active(implied_by) {
return EvalResult::Allow;
}

// When we're compiling the compiler itself we may pull in
// crates from crates.io, but those crates may depend on other
// crates also pulled in from crates.io. We want to ideally be
Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1634,11 +1634,15 @@ rustc_queries! {
storage(ArenaCacheSelector<'tcx>)
desc { "calculating the lib features map" }
}
query defined_lib_features(_: CrateNum)
-> &'tcx [(Symbol, Option<Symbol>)] {
query defined_lib_features(_: CrateNum) -> &'tcx [(Symbol, Option<Symbol>)] {
desc { "calculating the lib features defined in a crate" }
separate_provide_extern
}
query stability_implications(_: CrateNum) -> FxHashMap<Symbol, Symbol> {
storage(ArenaCacheSelector<'tcx>)
desc { "calculating the implications between `#[unstable]` features defined in a crate" }
separate_provide_extern
}
/// Whether the function is an intrinsic
query is_intrinsic(def_id: DefId) -> bool {
desc { |tcx| "is_intrinsic({})", tcx.def_path_str(def_id) }
Expand Down
18 changes: 9 additions & 9 deletions compiler/rustc_passes/src/lib_features.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Detecting lib features (i.e., features that are not lang features).
//
// These are declared using stability attributes (e.g., `#[stable (..)]`
// and `#[unstable (..)]`), but are not declared in one single location
// (unlike lang features), which means we need to collect them instead.
//! Detecting lib features (i.e., features that are not lang features).
//!
//! These are declared using stability attributes (e.g., `#[stable (..)]` and `#[unstable (..)]`),
//! but are not declared in one single location (unlike lang features), which means we need to
//! collect them instead.

use rustc_ast::{Attribute, MetaItemKind};
use rustc_errors::struct_span_err;
Expand Down Expand Up @@ -71,11 +71,11 @@ impl<'tcx> LibFeatureCollector<'tcx> {

fn collect_feature(&mut self, feature: Symbol, since: Option<Symbol>, span: Span) {
let already_in_stable = self.lib_features.stable.contains_key(&feature);
let already_in_unstable = self.lib_features.unstable.contains(&feature);
let already_in_unstable = self.lib_features.unstable.contains_key(&feature);

match (since, already_in_stable, already_in_unstable) {
(Some(since), _, false) => {
if let Some(prev_since) = self.lib_features.stable.get(&feature) {
if let Some((prev_since, _)) = self.lib_features.stable.get(&feature) {
if *prev_since != since {
self.span_feature_error(
span,
Expand All @@ -89,10 +89,10 @@ impl<'tcx> LibFeatureCollector<'tcx> {
}
}

self.lib_features.stable.insert(feature, since);
self.lib_features.stable.insert(feature, (since, span));
}
(None, false, _) => {
self.lib_features.unstable.insert(feature);
self.lib_features.unstable.insert(feature, span);
}
(Some(_), _, true) | (None, true, _) => {
self.span_feature_error(
Expand Down
Loading