Skip to content

Commit bca5fde

Browse files
committed
Auto merge of #131321 - RalfJung:feature-activation, r=nnethercote
terminology: #[feature] *enables* a feature (instead of "declaring" or "activating" it) Mostly, we currently call a feature that has a corresponding `#[feature(name)]` attribute in the current crate a "declared" feature. I think that is confusing as it does not align with what "declaring" usually means. Furthermore, we *also* refer to `#[stable]`/`#[unstable]` as *declaring* a feature (e.g. in [these diagnostics](https://github.com/rust-lang/rust/blob/f25e5abea229a6b6aa77b45e21cb784e785c6040/compiler/rustc_passes/messages.ftl#L297-L301)), which aligns better with what "declaring" usually means. To make things worse, the functions `tcx.features().active(...)` and `tcx.features().declared(...)` both exist and they are doing almost the same thing (testing whether a corresponding `#[feature(name)]` exists) except that `active` would ICE if the feature is not an unstable lang feature. On top of this, the callback when a feature is activated/declared is called `set_enabled`, and many comments also talk about "enabling" a feature. So really, our terminology is just a mess. I would suggest we use "declaring a feature" for saying that something is/was guarded by a feature (e.g. `#[stable]`/`#[unstable]`), and "enabling a feature" for `#[feature(name)]`. This PR implements that.
2 parents 916e9ce + 1381773 commit bca5fde

File tree

21 files changed

+151
-129
lines changed

21 files changed

+151
-129
lines changed

compiler/rustc_ast_passes/src/feature_gate.rs

+43-41
Original file line numberDiff line numberDiff line change
@@ -600,59 +600,61 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
600600
}
601601

602602
fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) {
603-
// checks if `#![feature]` has been used to enable any lang feature
604-
// does not check the same for lib features unless there's at least one
605-
// declared lang feature
606-
if !sess.opts.unstable_features.is_nightly_build() {
607-
if features.declared_features.is_empty() {
608-
return;
609-
}
610-
for attr in krate.attrs.iter().filter(|attr| attr.has_name(sym::feature)) {
611-
let mut err = errors::FeatureOnNonNightly {
612-
span: attr.span,
613-
channel: option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)"),
614-
stable_features: vec![],
615-
sugg: None,
616-
};
617-
618-
let mut all_stable = true;
619-
for ident in
620-
attr.meta_item_list().into_iter().flatten().flat_map(|nested| nested.ident())
621-
{
622-
let name = ident.name;
623-
let stable_since = features
624-
.declared_lang_features
625-
.iter()
626-
.flat_map(|&(feature, _, since)| if feature == name { since } else { None })
627-
.next();
628-
if let Some(since) = stable_since {
629-
err.stable_features.push(errors::StableFeature { name, since });
630-
} else {
631-
all_stable = false;
632-
}
633-
}
634-
if all_stable {
635-
err.sugg = Some(attr.span);
603+
// checks if `#![feature]` has been used to enable any feature.
604+
if sess.opts.unstable_features.is_nightly_build() {
605+
return;
606+
}
607+
if features.enabled_features().is_empty() {
608+
return;
609+
}
610+
let mut errored = false;
611+
for attr in krate.attrs.iter().filter(|attr| attr.has_name(sym::feature)) {
612+
// `feature(...)` used on non-nightly. This is definitely an error.
613+
let mut err = errors::FeatureOnNonNightly {
614+
span: attr.span,
615+
channel: option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)"),
616+
stable_features: vec![],
617+
sugg: None,
618+
};
619+
620+
let mut all_stable = true;
621+
for ident in attr.meta_item_list().into_iter().flatten().flat_map(|nested| nested.ident()) {
622+
let name = ident.name;
623+
let stable_since = features
624+
.enabled_lang_features()
625+
.iter()
626+
.flat_map(|&(feature, _, since)| if feature == name { since } else { None })
627+
.next();
628+
if let Some(since) = stable_since {
629+
err.stable_features.push(errors::StableFeature { name, since });
630+
} else {
631+
all_stable = false;
636632
}
637-
sess.dcx().emit_err(err);
638633
}
634+
if all_stable {
635+
err.sugg = Some(attr.span);
636+
}
637+
sess.dcx().emit_err(err);
638+
errored = true;
639639
}
640+
// Just make sure we actually error if anything is listed in `enabled_features`.
641+
assert!(errored);
640642
}
641643

642644
fn check_incompatible_features(sess: &Session, features: &Features) {
643-
let declared_features = features
644-
.declared_lang_features
645+
let enabled_features = features
646+
.enabled_lang_features()
645647
.iter()
646648
.copied()
647649
.map(|(name, span, _)| (name, span))
648-
.chain(features.declared_lib_features.iter().copied());
650+
.chain(features.enabled_lib_features().iter().copied());
649651

650652
for (f1, f2) in rustc_feature::INCOMPATIBLE_FEATURES
651653
.iter()
652-
.filter(|&&(f1, f2)| features.active(f1) && features.active(f2))
654+
.filter(|&&(f1, f2)| features.enabled(f1) && features.enabled(f2))
653655
{
654-
if let Some((f1_name, f1_span)) = declared_features.clone().find(|(name, _)| name == f1) {
655-
if let Some((f2_name, f2_span)) = declared_features.clone().find(|(name, _)| name == f2)
656+
if let Some((f1_name, f1_span)) = enabled_features.clone().find(|(name, _)| name == f1) {
657+
if let Some((f2_name, f2_span)) = enabled_features.clone().find(|(name, _)| name == f2)
656658
{
657659
let spans = vec![f1_span, f2_span];
658660
sess.dcx().emit_err(errors::IncompatibleFeatures {
@@ -672,7 +674,7 @@ fn check_new_solver_banned_features(sess: &Session, features: &Features) {
672674

673675
// Ban GCE with the new solver, because it does not implement GCE correctly.
674676
if let Some(&(_, gce_span, _)) = features
675-
.declared_lang_features
677+
.enabled_lang_features()
676678
.iter()
677679
.find(|&&(feat, _, _)| feat == sym::generic_const_exprs)
678680
{

compiler/rustc_const_eval/src/check_consts/check.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
276276
let gate = match op.status_in_item(self.ccx) {
277277
Status::Allowed => return,
278278

279-
Status::Unstable(gate) if self.tcx.features().active(gate) => {
279+
Status::Unstable(gate) if self.tcx.features().enabled(gate) => {
280280
let unstable_in_stable = self.ccx.is_const_stable_const_fn()
281281
&& !super::rustc_allow_const_fn_unstable(self.tcx, self.def_id(), gate);
282282
if unstable_in_stable {
@@ -700,10 +700,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
700700
// Calling an unstable function *always* requires that the corresponding gate
701701
// (or implied gate) be enabled, even if the function has
702702
// `#[rustc_allow_const_fn_unstable(the_gate)]`.
703-
let gate_declared = |gate| tcx.features().declared(gate);
704-
let feature_gate_declared = gate_declared(gate);
705-
let implied_gate_declared = implied_by.is_some_and(gate_declared);
706-
if !feature_gate_declared && !implied_gate_declared {
703+
let gate_enabled = |gate| tcx.features().enabled(gate);
704+
let feature_gate_enabled = gate_enabled(gate);
705+
let implied_gate_enabled = implied_by.is_some_and(gate_enabled);
706+
if !feature_gate_enabled && !implied_gate_enabled {
707707
self.check_op(ops::FnCallUnstable(callee, Some(gate)));
708708
return;
709709
}
@@ -717,7 +717,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
717717

718718
// Otherwise, we are something const-stable calling a const-unstable fn.
719719
if super::rustc_allow_const_fn_unstable(tcx, caller, gate) {
720-
trace!("rustc_allow_const_fn_unstable gate active");
720+
trace!("rustc_allow_const_fn_unstable gate enabled");
721721
return;
722722
}
723723

Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
A `#![feature]` attribute was declared multiple times.
1+
The same feature is enabled multiple times with `#![feature]` attributes
22

33
Erroneous code example:
44

55
```compile_fail,E0636
66
#![allow(stable_features)]
77
#![feature(rust1)]
8-
#![feature(rust1)] // error: the feature `rust1` has already been declared
8+
#![feature(rust1)] // error: the feature `rust1` has already been enabled
99
```

compiler/rustc_error_codes/src/error_codes/E0705.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#### Note: this error code is no longer emitted by the compiler.
22

3-
A `#![feature]` attribute was declared for a feature that is stable in the
3+
A `#![feature]` attribute was used for a feature that is stable in the
44
current edition, but not in all editions.
55

66
Erroneous code example:

compiler/rustc_expand/src/config.rs

+10-11
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
5252

5353
let mut features = Features::default();
5454

55-
// Process all features declared in the code.
55+
// Process all features enabled in the code.
5656
for attr in krate_attrs {
5757
for mi in feature_list(attr) {
5858
let name = match mi.ident() {
@@ -76,7 +76,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
7676
}
7777
};
7878

79-
// If the declared feature has been removed, issue an error.
79+
// If the enabled feature has been removed, issue an error.
8080
if let Some(f) = REMOVED_FEATURES.iter().find(|f| name == f.feature.name) {
8181
sess.dcx().emit_err(FeatureRemoved {
8282
span: mi.span(),
@@ -85,14 +85,14 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
8585
continue;
8686
}
8787

88-
// If the declared feature is stable, record it.
88+
// If the enabled feature is stable, record it.
8989
if let Some(f) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) {
9090
let since = Some(Symbol::intern(f.since));
91-
features.set_declared_lang_feature(name, mi.span(), since);
91+
features.set_enabled_lang_feature(name, mi.span(), since, None);
9292
continue;
9393
}
9494

95-
// If `-Z allow-features` is used and the declared feature is
95+
// If `-Z allow-features` is used and the enabled feature is
9696
// unstable and not also listed as one of the allowed features,
9797
// issue an error.
9898
if let Some(allowed) = sess.opts.unstable_opts.allow_features.as_ref() {
@@ -102,9 +102,8 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
102102
}
103103
}
104104

105-
// If the declared feature is unstable, record it.
105+
// If the enabled feature is unstable, record it.
106106
if let Some(f) = UNSTABLE_FEATURES.iter().find(|f| name == f.feature.name) {
107-
(f.set_enabled)(&mut features);
108107
// When the ICE comes from core, alloc or std (approximation of the standard
109108
// library), there's a chance that the person hitting the ICE may be using
110109
// -Zbuild-std or similar with an untested target. The bug is probably in the
@@ -115,13 +114,13 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
115114
{
116115
sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed);
117116
}
118-
features.set_declared_lang_feature(name, mi.span(), None);
117+
features.set_enabled_lang_feature(name, mi.span(), None, Some(f));
119118
continue;
120119
}
121120

122-
// Otherwise, the feature is unknown. Record it as a lib feature.
123-
// It will be checked later.
124-
features.set_declared_lib_feature(name, mi.span());
121+
// Otherwise, the feature is unknown. Enable it as a lib feature.
122+
// It will be checked later whether the feature really exists.
123+
features.set_enabled_lib_feature(name, mi.span());
125124

126125
// Similar to above, detect internal lib features to suppress
127126
// the ICE message that asks for a report.

compiler/rustc_expand/src/mbe/quoted.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ pub(super) fn parse(
119119
result
120120
}
121121

122-
/// Asks for the `macro_metavar_expr` feature if it is not already declared
122+
/// Asks for the `macro_metavar_expr` feature if it is not enabled
123123
fn maybe_emit_macro_metavar_expr_feature(features: &Features, sess: &Session, span: Span) {
124124
if !features.macro_metavar_expr {
125125
let msg = "meta-variable expressions are unstable";

compiler/rustc_feature/src/unstable.rs

+59-32
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use super::{Feature, to_nonzero};
88

99
pub struct UnstableFeature {
1010
pub feature: Feature,
11-
pub set_enabled: fn(&mut Features),
11+
set_enabled: fn(&mut Features),
1212
}
1313

1414
#[derive(PartialEq)]
@@ -54,61 +54,85 @@ macro_rules! declare_features {
5454
#[derive(Clone, Default, Debug)]
5555
pub struct Features {
5656
/// `#![feature]` attrs for language features, for error reporting.
57-
/// "declared" here means that the feature is actually enabled in the current crate.
58-
pub declared_lang_features: Vec<(Symbol, Span, Option<Symbol>)>,
57+
enabled_lang_features: Vec<(Symbol, Span, Option<Symbol>)>,
5958
/// `#![feature]` attrs for non-language (library) features.
60-
/// "declared" here means that the feature is actually enabled in the current crate.
61-
pub declared_lib_features: Vec<(Symbol, Span)>,
62-
/// `declared_lang_features` + `declared_lib_features`.
63-
pub declared_features: FxHashSet<Symbol>,
64-
/// Active state of individual features (unstable only).
59+
enabled_lib_features: Vec<(Symbol, Span)>,
60+
/// `enabled_lang_features` + `enabled_lib_features`.
61+
enabled_features: FxHashSet<Symbol>,
62+
/// State of individual features (unstable lang features only).
63+
/// This is `true` if and only if the corresponding feature is listed in `enabled_lang_features`.
6564
$(
6665
$(#[doc = $doc])*
6766
pub $feature: bool
6867
),+
6968
}
7069

7170
impl Features {
72-
pub fn set_declared_lang_feature(
71+
pub fn set_enabled_lang_feature(
7372
&mut self,
74-
symbol: Symbol,
73+
name: Symbol,
7574
span: Span,
76-
since: Option<Symbol>
75+
since: Option<Symbol>,
76+
feature: Option<&UnstableFeature>,
7777
) {
78-
self.declared_lang_features.push((symbol, span, since));
79-
self.declared_features.insert(symbol);
78+
self.enabled_lang_features.push((name, span, since));
79+
self.enabled_features.insert(name);
80+
if let Some(feature) = feature {
81+
assert_eq!(feature.feature.name, name);
82+
(feature.set_enabled)(self);
83+
} else {
84+
// Ensure we don't skip a `set_enabled` call.
85+
debug_assert!(UNSTABLE_FEATURES.iter().find(|f| name == f.feature.name).is_none());
86+
}
8087
}
8188

82-
pub fn set_declared_lib_feature(&mut self, symbol: Symbol, span: Span) {
83-
self.declared_lib_features.push((symbol, span));
84-
self.declared_features.insert(symbol);
89+
pub fn set_enabled_lib_feature(&mut self, name: Symbol, span: Span) {
90+
self.enabled_lib_features.push((name, span));
91+
self.enabled_features.insert(name);
92+
// Ensure we don't skip a `set_enabled` call.
93+
debug_assert!(UNSTABLE_FEATURES.iter().find(|f| name == f.feature.name).is_none());
8594
}
8695

87-
/// This is intended for hashing the set of active features.
96+
/// This is intended for hashing the set of enabled language features.
8897
///
8998
/// The expectation is that this produces much smaller code than other alternatives.
9099
///
91100
/// Note that the total feature count is pretty small, so this is not a huge array.
92101
#[inline]
93-
pub fn all_features(&self) -> [u8; NUM_FEATURES] {
102+
pub fn all_lang_features(&self) -> [u8; NUM_FEATURES] {
94103
[$(self.$feature as u8),+]
95104
}
96105

97-
/// Is the given feature explicitly declared, i.e. named in a
98-
/// `#![feature(...)]` within the code?
99-
pub fn declared(&self, feature: Symbol) -> bool {
100-
self.declared_features.contains(&feature)
106+
pub fn enabled_lang_features(&self) -> &Vec<(Symbol, Span, Option<Symbol>)> {
107+
&self.enabled_lang_features
101108
}
102109

103-
/// Is the given feature active (enabled by the user)?
104-
///
105-
/// Panics if the symbol doesn't correspond to a declared feature.
106-
pub fn active(&self, feature: Symbol) -> bool {
107-
match feature {
108-
$( sym::$feature => self.$feature, )*
110+
pub fn enabled_lib_features(&self) -> &Vec<(Symbol, Span)> {
111+
&self.enabled_lib_features
112+
}
109113

110-
_ => panic!("`{}` was not listed in `declare_features`", feature),
114+
pub fn enabled_features(&self) -> &FxHashSet<Symbol> {
115+
&self.enabled_features
116+
}
117+
118+
/// Is the given feature enabled (via `#[feature(...)]`)?
119+
pub fn enabled(&self, feature: Symbol) -> bool {
120+
let e = self.enabled_features.contains(&feature);
121+
if cfg!(debug_assertions) {
122+
// Ensure this matches `self.$feature`, if that exists.
123+
let e2 = match feature {
124+
$( sym::$feature => Some(self.$feature), )*
125+
_ => None,
126+
};
127+
if let Some(e2) = e2 {
128+
assert_eq!(
129+
e, e2,
130+
"mismatch in feature state for `{feature}`: \
131+
`enabled_features` says {e} but `self.{feature}` says {e2}"
132+
);
133+
}
111134
}
135+
e
112136
}
113137

114138
/// Some features are known to be incomplete and using them is likely to have
@@ -119,8 +143,11 @@ macro_rules! declare_features {
119143
$(
120144
sym::$feature => status_to_enum!($status) == FeatureStatus::Incomplete,
121145
)*
122-
// Accepted/removed features aren't in this file but are never incomplete.
123-
_ if self.declared_features.contains(&feature) => false,
146+
_ if self.enabled_features.contains(&feature) => {
147+
// Accepted/removed features and library features aren't in this file but
148+
// are never incomplete.
149+
false
150+
}
124151
_ => panic!("`{}` was not listed in `declare_features`", feature),
125152
}
126153
}
@@ -132,7 +159,7 @@ macro_rules! declare_features {
132159
$(
133160
sym::$feature => status_to_enum!($status) == FeatureStatus::Internal,
134161
)*
135-
_ if self.declared_features.contains(&feature) => {
162+
_ if self.enabled_features.contains(&feature) => {
136163
// This could be accepted/removed, or a libs feature.
137164
// Accepted/removed features aren't in this file but are never internal
138165
// (a removed feature might have been internal, but that's now irrelevant).

compiler/rustc_lint/src/builtin.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2288,10 +2288,10 @@ impl EarlyLintPass for IncompleteInternalFeatures {
22882288
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
22892289
let features = cx.builder.features();
22902290
features
2291-
.declared_lang_features
2291+
.enabled_lang_features()
22922292
.iter()
22932293
.map(|(name, span, _)| (name, span))
2294-
.chain(features.declared_lib_features.iter().map(|(name, span)| (name, span)))
2294+
.chain(features.enabled_lib_features().iter().map(|(name, span)| (name, span)))
22952295
.filter(|(&name, _)| features.incomplete(name) || features.internal(name))
22962296
.for_each(|(&name, &span)| {
22972297
if features.incomplete(name) {

0 commit comments

Comments
 (0)