Skip to content

Commit cf21a08

Browse files
committed
Auto merge of #116437 - nnethercote:rustc_features, r=Nilstrieb
Clean up `rustc_features` Plenty more to be done, but this is a decent start. r? `@Nilstrieb`
2 parents 598e29b + 81d1f7e commit cf21a08

File tree

8 files changed

+149
-166
lines changed

8 files changed

+149
-166
lines changed

compiler/rustc_expand/src/config.rs

+79-83
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,16 @@ use rustc_ast::NodeId;
1313
use rustc_ast::{self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem};
1414
use rustc_attr as attr;
1515
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
16-
use rustc_data_structures::fx::FxHashMap;
16+
use rustc_data_structures::fx::FxHashSet;
1717
use rustc_feature::{Feature, Features, State as FeatureState};
18-
use rustc_feature::{
19-
ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES, STABLE_REMOVED_FEATURES,
20-
};
18+
use rustc_feature::{ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES};
2119
use rustc_parse::validate_attr;
2220
use rustc_session::parse::feature_err;
2321
use rustc_session::Session;
2422
use rustc_span::edition::{Edition, ALL_EDITIONS};
2523
use rustc_span::symbol::{sym, Symbol};
26-
use rustc_span::{Span, DUMMY_SP};
24+
use rustc_span::Span;
25+
use thin_vec::ThinVec;
2726

2827
/// A folder that strips out items that do not belong in the current configuration.
2928
pub struct StripUnconfigured<'a> {
@@ -37,13 +36,6 @@ pub struct StripUnconfigured<'a> {
3736
}
3837

3938
pub fn features(sess: &Session, krate_attrs: &[Attribute]) -> Features {
40-
fn feature_removed(sess: &Session, span: Span, reason: Option<&str>) {
41-
sess.emit_err(FeatureRemoved {
42-
span,
43-
reason: reason.map(|reason| FeatureRemovedReason { reason }),
44-
});
45-
}
46-
4739
fn active_features_up_to(edition: Edition) -> impl Iterator<Item = &'static Feature> {
4840
ACTIVE_FEATURES.iter().filter(move |feature| {
4941
if let Some(feature_edition) = feature.edition {
@@ -54,67 +46,49 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute]) -> Features {
5446
})
5547
}
5648

57-
let mut features = Features::default();
58-
let mut edition_enabled_features = FxHashMap::default();
59-
let crate_edition = sess.edition();
60-
61-
for &edition in ALL_EDITIONS {
62-
if edition <= crate_edition {
63-
// The `crate_edition` implies its respective umbrella feature-gate
64-
// (i.e., `#![feature(rust_20XX_preview)]` isn't needed on edition 20XX).
65-
edition_enabled_features.insert(edition.feature_name(), edition);
49+
fn feature_list(attr: &Attribute) -> ThinVec<ast::NestedMetaItem> {
50+
if attr.has_name(sym::feature) && let Some(list) = attr.meta_item_list() {
51+
list
52+
} else {
53+
ThinVec::new()
6654
}
6755
}
6856

69-
for feature in active_features_up_to(crate_edition) {
70-
feature.set(&mut features, DUMMY_SP);
71-
edition_enabled_features.insert(feature.name, crate_edition);
72-
}
73-
74-
// Process the edition umbrella feature-gates first, to ensure
75-
// `edition_enabled_features` is completed before it's queried.
76-
for attr in krate_attrs {
77-
if !attr.has_name(sym::feature) {
78-
continue;
79-
}
80-
81-
let Some(list) = attr.meta_item_list() else {
82-
continue;
83-
};
84-
85-
for mi in list {
86-
if !mi.is_word() {
87-
continue;
88-
}
89-
90-
let name = mi.name_or_empty();
57+
let mut features = Features::default();
9158

92-
let edition = ALL_EDITIONS.iter().find(|e| name == e.feature_name()).copied();
93-
if let Some(edition) = edition {
94-
if edition <= crate_edition {
95-
continue;
96-
}
59+
// The edition from `--edition`.
60+
let crate_edition = sess.edition();
9761

98-
for feature in active_features_up_to(edition) {
99-
// FIXME(Manishearth) there is currently no way to set
100-
// lib features by edition
101-
feature.set(&mut features, DUMMY_SP);
102-
edition_enabled_features.insert(feature.name, edition);
62+
// The maximum of (a) the edition from `--edition` and (b) any edition
63+
// umbrella feature-gates declared in the code.
64+
// - E.g. if `crate_edition` is 2015 but `rust_2018_preview` is present,
65+
// `feature_edition` is 2018
66+
let mut features_edition = crate_edition;
67+
for attr in krate_attrs {
68+
for mi in feature_list(attr) {
69+
if mi.is_word() {
70+
let name = mi.name_or_empty();
71+
let edition = ALL_EDITIONS.iter().find(|e| name == e.feature_name()).copied();
72+
if let Some(edition) = edition && edition > features_edition {
73+
features_edition = edition;
10374
}
10475
}
10576
}
10677
}
10778

108-
for attr in krate_attrs {
109-
if !attr.has_name(sym::feature) {
110-
continue;
111-
}
112-
113-
let Some(list) = attr.meta_item_list() else {
114-
continue;
115-
};
79+
// Enable edition-dependent features based on `features_edition`.
80+
// - E.g. enable `test_2018_feature` if `features_edition` is 2018 or higher
81+
let mut edition_enabled_features = FxHashSet::default();
82+
for feature in active_features_up_to(features_edition) {
83+
// FIXME(Manishearth) there is currently no way to set lib features by
84+
// edition.
85+
edition_enabled_features.insert(feature.name);
86+
feature.set(&mut features);
87+
}
11688

117-
for mi in list {
89+
// Process all features declared in the code.
90+
for attr in krate_attrs {
91+
for mi in feature_list(attr) {
11892
let name = match mi.ident() {
11993
Some(ident) if mi.is_word() => ident.name,
12094
Some(ident) => {
@@ -136,54 +110,76 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute]) -> Features {
136110
}
137111
};
138112

139-
if let Some(&edition) = edition_enabled_features.get(&name) {
113+
// If the declared feature is an edition umbrella feature-gate,
114+
// warn if it was redundant w.r.t. `crate_edition`.
115+
// - E.g. warn if `rust_2018_preview` is declared when
116+
// `crate_edition` is 2018
117+
// - E.g. don't warn if `rust_2018_preview` is declared when
118+
// `crate_edition` is 2015.
119+
if let Some(&edition) = ALL_EDITIONS.iter().find(|e| name == e.feature_name()) {
120+
if edition <= crate_edition {
121+
sess.emit_warning(FeatureIncludedInEdition {
122+
span: mi.span(),
123+
feature: name,
124+
edition,
125+
});
126+
}
127+
features.set_declared_lang_feature(name, mi.span(), None);
128+
continue;
129+
}
130+
131+
// If the declared feature is edition-dependent and was already
132+
// enabled due to `feature_edition`, give a warning.
133+
// - E.g. warn if `test_2018_feature` is declared when
134+
// `feature_edition` is 2018 or higher.
135+
if edition_enabled_features.contains(&name) {
140136
sess.emit_warning(FeatureIncludedInEdition {
141137
span: mi.span(),
142138
feature: name,
143-
edition,
139+
edition: features_edition,
144140
});
141+
features.set_declared_lang_feature(name, mi.span(), None);
145142
continue;
146143
}
147144

148-
if ALL_EDITIONS.iter().any(|e| name == e.feature_name()) {
149-
// Handled in the separate loop above.
150-
continue;
151-
}
152-
153-
let removed = REMOVED_FEATURES.iter().find(|f| name == f.name);
154-
let stable_removed = STABLE_REMOVED_FEATURES.iter().find(|f| name == f.name);
155-
if let Some(Feature { state, .. }) = removed.or(stable_removed) {
156-
if let FeatureState::Removed { reason } | FeatureState::Stabilized { reason } =
157-
state
158-
{
159-
feature_removed(sess, mi.span(), *reason);
145+
// If the declared feature has been removed, issue an error.
146+
if let Some(Feature { state, .. }) = REMOVED_FEATURES.iter().find(|f| name == f.name) {
147+
if let FeatureState::Removed { reason } = state {
148+
sess.emit_err(FeatureRemoved {
149+
span: mi.span(),
150+
reason: reason.map(|reason| FeatureRemovedReason { reason }),
151+
});
160152
continue;
161153
}
162154
}
163155

156+
// If the declared feature is stable, record it.
164157
if let Some(Feature { since, .. }) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) {
165158
let since = Some(Symbol::intern(since));
166-
features.declared_lang_features.push((name, mi.span(), since));
167-
features.active_features.insert(name);
159+
features.set_declared_lang_feature(name, mi.span(), since);
168160
continue;
169161
}
170162

163+
// If `-Z allow-features` is used and the declared feature is
164+
// unstable and not also listed as one of the allowed features,
165+
// issue an error.
171166
if let Some(allowed) = sess.opts.unstable_opts.allow_features.as_ref() {
172167
if allowed.iter().all(|f| name.as_str() != f) {
173168
sess.emit_err(FeatureNotAllowed { span: mi.span(), name });
174169
continue;
175170
}
176171
}
177172

173+
// If the declared feature is unstable, record it.
178174
if let Some(f) = ACTIVE_FEATURES.iter().find(|f| name == f.name) {
179-
f.set(&mut features, mi.span());
180-
features.declared_lang_features.push((name, mi.span(), None));
181-
features.active_features.insert(name);
175+
f.set(&mut features);
176+
features.set_declared_lang_feature(name, mi.span(), None);
182177
continue;
183178
}
184179

185-
features.declared_lib_features.push((name, mi.span()));
186-
features.active_features.insert(name);
180+
// Otherwise, the feature is unknown. Record it as a lib feature.
181+
// It will be checked later.
182+
features.set_declared_lib_feature(name, mi.span());
187183
}
188184
}
189185

compiler/rustc_feature/src/active.rs

+41-28
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,26 @@ use rustc_span::edition::Edition;
77
use rustc_span::symbol::{sym, Symbol};
88
use rustc_span::Span;
99

10-
macro_rules! set {
11-
($field: ident) => {{
12-
fn f(features: &mut Features, _: Span) {
13-
features.$field = true;
14-
}
15-
f as fn(&mut Features, Span)
16-
}};
17-
}
18-
1910
#[derive(PartialEq)]
2011
enum FeatureStatus {
2112
Default,
2213
Incomplete,
2314
Internal,
2415
}
2516

26-
macro_rules! declare_features {
27-
(__status_to_enum active) => {
17+
macro_rules! status_to_enum {
18+
(active) => {
2819
FeatureStatus::Default
2920
};
30-
(__status_to_enum incomplete) => {
21+
(incomplete) => {
3122
FeatureStatus::Incomplete
3223
};
33-
(__status_to_enum internal) => {
24+
(internal) => {
3425
FeatureStatus::Internal
3526
};
27+
}
28+
29+
macro_rules! declare_features {
3630
($(
3731
$(#[doc = $doc:tt])* ($status:ident, $feature:ident, $ver:expr, $issue:expr, $edition:expr),
3832
)+) => {
@@ -43,7 +37,10 @@ macro_rules! declare_features {
4337
&[$(
4438
// (sym::$feature, $ver, $issue, $edition, set!($feature))
4539
Feature {
46-
state: State::Active { set: set!($feature) },
40+
state: State::Active {
41+
// Sets this feature's corresponding bool within `features`.
42+
set: |features| features.$feature = true,
43+
},
4744
name: sym::$feature,
4845
since: $ver,
4946
issue: to_nonzero($issue),
@@ -58,25 +55,43 @@ macro_rules! declare_features {
5855
pub declared_lang_features: Vec<(Symbol, Span, Option<Symbol>)>,
5956
/// `#![feature]` attrs for non-language (library) features.
6057
pub declared_lib_features: Vec<(Symbol, Span)>,
61-
/// Features enabled for this crate.
62-
pub active_features: FxHashSet<Symbol>,
58+
/// `declared_lang_features` + `declared_lib_features`.
59+
pub declared_features: FxHashSet<Symbol>,
60+
/// Individual features (unstable only).
6361
$(
6462
$(#[doc = $doc])*
6563
pub $feature: bool
6664
),+
6765
}
6866

6967
impl Features {
68+
pub fn set_declared_lang_feature(
69+
&mut self,
70+
symbol: Symbol,
71+
span: Span,
72+
since: Option<Symbol>
73+
) {
74+
self.declared_lang_features.push((symbol, span, since));
75+
self.declared_features.insert(symbol);
76+
}
77+
78+
pub fn set_declared_lib_feature(&mut self, symbol: Symbol, span: Span) {
79+
self.declared_lib_features.push((symbol, span));
80+
self.declared_features.insert(symbol);
81+
}
82+
7083
pub fn walk_feature_fields(&self, mut f: impl FnMut(&str, bool)) {
7184
$(f(stringify!($feature), self.$feature);)+
7285
}
7386

74-
/// Is the given feature active?
75-
pub fn active(&self, feature: Symbol) -> bool {
76-
self.active_features.contains(&feature)
87+
/// Is the given feature explicitly declared, i.e. named in a
88+
/// `#![feature(...)]` within the code?
89+
pub fn declared(&self, feature: Symbol) -> bool {
90+
self.declared_features.contains(&feature)
7791
}
7892

79-
/// Is the given feature enabled?
93+
/// Is the given feature enabled, i.e. declared or automatically
94+
/// enabled due to the edition?
8095
///
8196
/// Panics if the symbol doesn't correspond to a declared feature.
8297
pub fn enabled(&self, feature: Symbol) -> bool {
@@ -93,11 +108,10 @@ macro_rules! declare_features {
93108
pub fn incomplete(&self, feature: Symbol) -> bool {
94109
match feature {
95110
$(
96-
sym::$feature => declare_features!(__status_to_enum $status) == FeatureStatus::Incomplete,
111+
sym::$feature => status_to_enum!($status) == FeatureStatus::Incomplete,
97112
)*
98113
// accepted and removed features aren't in this file but are never incomplete
99-
_ if self.declared_lang_features.iter().any(|f| f.0 == feature) => false,
100-
_ if self.declared_lib_features.iter().any(|f| f.0 == feature) => false,
114+
_ if self.declared_features.contains(&feature) => false,
101115
_ => panic!("`{}` was not listed in `declare_features`", feature),
102116
}
103117
}
@@ -108,12 +122,11 @@ macro_rules! declare_features {
108122
pub fn internal(&self, feature: Symbol) -> bool {
109123
match feature {
110124
$(
111-
sym::$feature => declare_features!(__status_to_enum $status) == FeatureStatus::Internal,
125+
sym::$feature => status_to_enum!($status) == FeatureStatus::Internal,
112126
)*
113127
// accepted and removed features aren't in this file but are never internal
114128
// (a removed feature might have been internal, but it doesn't matter anymore)
115-
_ if self.declared_lang_features.iter().any(|f| f.0 == feature) => false,
116-
_ if self.declared_lib_features.iter().any(|f| f.0 == feature) => false,
129+
_ if self.declared_features.contains(&feature) => false,
117130
_ => panic!("`{}` was not listed in `declare_features`", feature),
118131
}
119132
}
@@ -123,9 +136,9 @@ macro_rules! declare_features {
123136

124137
impl Feature {
125138
/// Sets this feature in `Features`. Panics if called on a non-active feature.
126-
pub fn set(&self, features: &mut Features, span: Span) {
139+
pub fn set(&self, features: &mut Features) {
127140
match self.state {
128-
State::Active { set } => set(features, span),
141+
State::Active { set } => set(features),
129142
_ => panic!("called `set` on feature `{}` which is not `active`", self.name),
130143
}
131144
}

0 commit comments

Comments
 (0)