mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
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](f25e5abea2/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.
This commit is contained in:
commit
bca5fdebe0
@ -600,59 +600,61 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) {
|
fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) {
|
||||||
// checks if `#![feature]` has been used to enable any lang feature
|
// checks if `#![feature]` has been used to enable any feature.
|
||||||
// does not check the same for lib features unless there's at least one
|
if sess.opts.unstable_features.is_nightly_build() {
|
||||||
// declared lang feature
|
return;
|
||||||
if !sess.opts.unstable_features.is_nightly_build() {
|
|
||||||
if features.declared_features.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for attr in krate.attrs.iter().filter(|attr| attr.has_name(sym::feature)) {
|
|
||||||
let mut err = errors::FeatureOnNonNightly {
|
|
||||||
span: attr.span,
|
|
||||||
channel: option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)"),
|
|
||||||
stable_features: vec![],
|
|
||||||
sugg: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut all_stable = true;
|
|
||||||
for ident in
|
|
||||||
attr.meta_item_list().into_iter().flatten().flat_map(|nested| nested.ident())
|
|
||||||
{
|
|
||||||
let name = ident.name;
|
|
||||||
let stable_since = features
|
|
||||||
.declared_lang_features
|
|
||||||
.iter()
|
|
||||||
.flat_map(|&(feature, _, since)| if feature == name { since } else { None })
|
|
||||||
.next();
|
|
||||||
if let Some(since) = stable_since {
|
|
||||||
err.stable_features.push(errors::StableFeature { name, since });
|
|
||||||
} else {
|
|
||||||
all_stable = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if all_stable {
|
|
||||||
err.sugg = Some(attr.span);
|
|
||||||
}
|
|
||||||
sess.dcx().emit_err(err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if features.enabled_features().is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut errored = false;
|
||||||
|
for attr in krate.attrs.iter().filter(|attr| attr.has_name(sym::feature)) {
|
||||||
|
// `feature(...)` used on non-nightly. This is definitely an error.
|
||||||
|
let mut err = errors::FeatureOnNonNightly {
|
||||||
|
span: attr.span,
|
||||||
|
channel: option_env!("CFG_RELEASE_CHANNEL").unwrap_or("(unknown)"),
|
||||||
|
stable_features: vec![],
|
||||||
|
sugg: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut all_stable = true;
|
||||||
|
for ident in attr.meta_item_list().into_iter().flatten().flat_map(|nested| nested.ident()) {
|
||||||
|
let name = ident.name;
|
||||||
|
let stable_since = features
|
||||||
|
.enabled_lang_features()
|
||||||
|
.iter()
|
||||||
|
.flat_map(|&(feature, _, since)| if feature == name { since } else { None })
|
||||||
|
.next();
|
||||||
|
if let Some(since) = stable_since {
|
||||||
|
err.stable_features.push(errors::StableFeature { name, since });
|
||||||
|
} else {
|
||||||
|
all_stable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if all_stable {
|
||||||
|
err.sugg = Some(attr.span);
|
||||||
|
}
|
||||||
|
sess.dcx().emit_err(err);
|
||||||
|
errored = true;
|
||||||
|
}
|
||||||
|
// Just make sure we actually error if anything is listed in `enabled_features`.
|
||||||
|
assert!(errored);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_incompatible_features(sess: &Session, features: &Features) {
|
fn check_incompatible_features(sess: &Session, features: &Features) {
|
||||||
let declared_features = features
|
let enabled_features = features
|
||||||
.declared_lang_features
|
.enabled_lang_features()
|
||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.copied()
|
||||||
.map(|(name, span, _)| (name, span))
|
.map(|(name, span, _)| (name, span))
|
||||||
.chain(features.declared_lib_features.iter().copied());
|
.chain(features.enabled_lib_features().iter().copied());
|
||||||
|
|
||||||
for (f1, f2) in rustc_feature::INCOMPATIBLE_FEATURES
|
for (f1, f2) in rustc_feature::INCOMPATIBLE_FEATURES
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|&&(f1, f2)| features.active(f1) && features.active(f2))
|
.filter(|&&(f1, f2)| features.enabled(f1) && features.enabled(f2))
|
||||||
{
|
{
|
||||||
if let Some((f1_name, f1_span)) = declared_features.clone().find(|(name, _)| name == f1) {
|
if let Some((f1_name, f1_span)) = enabled_features.clone().find(|(name, _)| name == f1) {
|
||||||
if let Some((f2_name, f2_span)) = declared_features.clone().find(|(name, _)| name == f2)
|
if let Some((f2_name, f2_span)) = enabled_features.clone().find(|(name, _)| name == f2)
|
||||||
{
|
{
|
||||||
let spans = vec![f1_span, f2_span];
|
let spans = vec![f1_span, f2_span];
|
||||||
sess.dcx().emit_err(errors::IncompatibleFeatures {
|
sess.dcx().emit_err(errors::IncompatibleFeatures {
|
||||||
@ -672,7 +674,7 @@ fn check_new_solver_banned_features(sess: &Session, features: &Features) {
|
|||||||
|
|
||||||
// Ban GCE with the new solver, because it does not implement GCE correctly.
|
// Ban GCE with the new solver, because it does not implement GCE correctly.
|
||||||
if let Some(&(_, gce_span, _)) = features
|
if let Some(&(_, gce_span, _)) = features
|
||||||
.declared_lang_features
|
.enabled_lang_features()
|
||||||
.iter()
|
.iter()
|
||||||
.find(|&&(feat, _, _)| feat == sym::generic_const_exprs)
|
.find(|&&(feat, _, _)| feat == sym::generic_const_exprs)
|
||||||
{
|
{
|
||||||
|
@ -276,7 +276,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
|
|||||||
let gate = match op.status_in_item(self.ccx) {
|
let gate = match op.status_in_item(self.ccx) {
|
||||||
Status::Allowed => return,
|
Status::Allowed => return,
|
||||||
|
|
||||||
Status::Unstable(gate) if self.tcx.features().active(gate) => {
|
Status::Unstable(gate) if self.tcx.features().enabled(gate) => {
|
||||||
let unstable_in_stable = self.ccx.is_const_stable_const_fn()
|
let unstable_in_stable = self.ccx.is_const_stable_const_fn()
|
||||||
&& !super::rustc_allow_const_fn_unstable(self.tcx, self.def_id(), gate);
|
&& !super::rustc_allow_const_fn_unstable(self.tcx, self.def_id(), gate);
|
||||||
if unstable_in_stable {
|
if unstable_in_stable {
|
||||||
@ -700,10 +700,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||||||
// Calling an unstable function *always* requires that the corresponding gate
|
// Calling an unstable function *always* requires that the corresponding gate
|
||||||
// (or implied gate) be enabled, even if the function has
|
// (or implied gate) be enabled, even if the function has
|
||||||
// `#[rustc_allow_const_fn_unstable(the_gate)]`.
|
// `#[rustc_allow_const_fn_unstable(the_gate)]`.
|
||||||
let gate_declared = |gate| tcx.features().declared(gate);
|
let gate_enabled = |gate| tcx.features().enabled(gate);
|
||||||
let feature_gate_declared = gate_declared(gate);
|
let feature_gate_enabled = gate_enabled(gate);
|
||||||
let implied_gate_declared = implied_by.is_some_and(gate_declared);
|
let implied_gate_enabled = implied_by.is_some_and(gate_enabled);
|
||||||
if !feature_gate_declared && !implied_gate_declared {
|
if !feature_gate_enabled && !implied_gate_enabled {
|
||||||
self.check_op(ops::FnCallUnstable(callee, Some(gate)));
|
self.check_op(ops::FnCallUnstable(callee, Some(gate)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -717,7 +717,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||||||
|
|
||||||
// Otherwise, we are something const-stable calling a const-unstable fn.
|
// Otherwise, we are something const-stable calling a const-unstable fn.
|
||||||
if super::rustc_allow_const_fn_unstable(tcx, caller, gate) {
|
if super::rustc_allow_const_fn_unstable(tcx, caller, gate) {
|
||||||
trace!("rustc_allow_const_fn_unstable gate active");
|
trace!("rustc_allow_const_fn_unstable gate enabled");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
A `#![feature]` attribute was declared multiple times.
|
The same feature is enabled multiple times with `#![feature]` attributes
|
||||||
|
|
||||||
Erroneous code example:
|
Erroneous code example:
|
||||||
|
|
||||||
```compile_fail,E0636
|
```compile_fail,E0636
|
||||||
#![allow(stable_features)]
|
#![allow(stable_features)]
|
||||||
#![feature(rust1)]
|
#![feature(rust1)]
|
||||||
#![feature(rust1)] // error: the feature `rust1` has already been declared
|
#![feature(rust1)] // error: the feature `rust1` has already been enabled
|
||||||
```
|
```
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#### Note: this error code is no longer emitted by the compiler.
|
#### Note: this error code is no longer emitted by the compiler.
|
||||||
|
|
||||||
A `#![feature]` attribute was declared for a feature that is stable in the
|
A `#![feature]` attribute was used for a feature that is stable in the
|
||||||
current edition, but not in all editions.
|
current edition, but not in all editions.
|
||||||
|
|
||||||
Erroneous code example:
|
Erroneous code example:
|
||||||
|
@ -52,7 +52,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
|
|||||||
|
|
||||||
let mut features = Features::default();
|
let mut features = Features::default();
|
||||||
|
|
||||||
// Process all features declared in the code.
|
// Process all features enabled in the code.
|
||||||
for attr in krate_attrs {
|
for attr in krate_attrs {
|
||||||
for mi in feature_list(attr) {
|
for mi in feature_list(attr) {
|
||||||
let name = match mi.ident() {
|
let name = match mi.ident() {
|
||||||
@ -76,7 +76,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// If the declared feature has been removed, issue an error.
|
// If the enabled feature has been removed, issue an error.
|
||||||
if let Some(f) = REMOVED_FEATURES.iter().find(|f| name == f.feature.name) {
|
if let Some(f) = REMOVED_FEATURES.iter().find(|f| name == f.feature.name) {
|
||||||
sess.dcx().emit_err(FeatureRemoved {
|
sess.dcx().emit_err(FeatureRemoved {
|
||||||
span: mi.span(),
|
span: mi.span(),
|
||||||
@ -85,14 +85,14 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the declared feature is stable, record it.
|
// If the enabled feature is stable, record it.
|
||||||
if let Some(f) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) {
|
if let Some(f) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) {
|
||||||
let since = Some(Symbol::intern(f.since));
|
let since = Some(Symbol::intern(f.since));
|
||||||
features.set_declared_lang_feature(name, mi.span(), since);
|
features.set_enabled_lang_feature(name, mi.span(), since, None);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If `-Z allow-features` is used and the declared feature is
|
// If `-Z allow-features` is used and the enabled feature is
|
||||||
// unstable and not also listed as one of the allowed features,
|
// unstable and not also listed as one of the allowed features,
|
||||||
// issue an error.
|
// issue an error.
|
||||||
if let Some(allowed) = sess.opts.unstable_opts.allow_features.as_ref() {
|
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) -
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the declared feature is unstable, record it.
|
// If the enabled feature is unstable, record it.
|
||||||
if let Some(f) = UNSTABLE_FEATURES.iter().find(|f| name == f.feature.name) {
|
if let Some(f) = UNSTABLE_FEATURES.iter().find(|f| name == f.feature.name) {
|
||||||
(f.set_enabled)(&mut features);
|
|
||||||
// When the ICE comes from core, alloc or std (approximation of the standard
|
// When the ICE comes from core, alloc or std (approximation of the standard
|
||||||
// library), there's a chance that the person hitting the ICE may be using
|
// library), there's a chance that the person hitting the ICE may be using
|
||||||
// -Zbuild-std or similar with an untested target. The bug is probably in the
|
// -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) -
|
|||||||
{
|
{
|
||||||
sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed);
|
sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
features.set_declared_lang_feature(name, mi.span(), None);
|
features.set_enabled_lang_feature(name, mi.span(), None, Some(f));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, the feature is unknown. Record it as a lib feature.
|
// Otherwise, the feature is unknown. Enable it as a lib feature.
|
||||||
// It will be checked later.
|
// It will be checked later whether the feature really exists.
|
||||||
features.set_declared_lib_feature(name, mi.span());
|
features.set_enabled_lib_feature(name, mi.span());
|
||||||
|
|
||||||
// Similar to above, detect internal lib features to suppress
|
// Similar to above, detect internal lib features to suppress
|
||||||
// the ICE message that asks for a report.
|
// the ICE message that asks for a report.
|
||||||
|
@ -119,7 +119,7 @@ pub(super) fn parse(
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asks for the `macro_metavar_expr` feature if it is not already declared
|
/// Asks for the `macro_metavar_expr` feature if it is not enabled
|
||||||
fn maybe_emit_macro_metavar_expr_feature(features: &Features, sess: &Session, span: Span) {
|
fn maybe_emit_macro_metavar_expr_feature(features: &Features, sess: &Session, span: Span) {
|
||||||
if !features.macro_metavar_expr {
|
if !features.macro_metavar_expr {
|
||||||
let msg = "meta-variable expressions are unstable";
|
let msg = "meta-variable expressions are unstable";
|
||||||
|
@ -8,7 +8,7 @@ use super::{Feature, to_nonzero};
|
|||||||
|
|
||||||
pub struct UnstableFeature {
|
pub struct UnstableFeature {
|
||||||
pub feature: Feature,
|
pub feature: Feature,
|
||||||
pub set_enabled: fn(&mut Features),
|
set_enabled: fn(&mut Features),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
@ -54,14 +54,13 @@ macro_rules! declare_features {
|
|||||||
#[derive(Clone, Default, Debug)]
|
#[derive(Clone, Default, Debug)]
|
||||||
pub struct Features {
|
pub struct Features {
|
||||||
/// `#![feature]` attrs for language features, for error reporting.
|
/// `#![feature]` attrs for language features, for error reporting.
|
||||||
/// "declared" here means that the feature is actually enabled in the current crate.
|
enabled_lang_features: Vec<(Symbol, Span, Option<Symbol>)>,
|
||||||
pub declared_lang_features: Vec<(Symbol, Span, Option<Symbol>)>,
|
|
||||||
/// `#![feature]` attrs for non-language (library) features.
|
/// `#![feature]` attrs for non-language (library) features.
|
||||||
/// "declared" here means that the feature is actually enabled in the current crate.
|
enabled_lib_features: Vec<(Symbol, Span)>,
|
||||||
pub declared_lib_features: Vec<(Symbol, Span)>,
|
/// `enabled_lang_features` + `enabled_lib_features`.
|
||||||
/// `declared_lang_features` + `declared_lib_features`.
|
enabled_features: FxHashSet<Symbol>,
|
||||||
pub declared_features: FxHashSet<Symbol>,
|
/// State of individual features (unstable lang features only).
|
||||||
/// Active state of individual features (unstable only).
|
/// This is `true` if and only if the corresponding feature is listed in `enabled_lang_features`.
|
||||||
$(
|
$(
|
||||||
$(#[doc = $doc])*
|
$(#[doc = $doc])*
|
||||||
pub $feature: bool
|
pub $feature: bool
|
||||||
@ -69,46 +68,71 @@ macro_rules! declare_features {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Features {
|
impl Features {
|
||||||
pub fn set_declared_lang_feature(
|
pub fn set_enabled_lang_feature(
|
||||||
&mut self,
|
&mut self,
|
||||||
symbol: Symbol,
|
name: Symbol,
|
||||||
span: Span,
|
span: Span,
|
||||||
since: Option<Symbol>
|
since: Option<Symbol>,
|
||||||
|
feature: Option<&UnstableFeature>,
|
||||||
) {
|
) {
|
||||||
self.declared_lang_features.push((symbol, span, since));
|
self.enabled_lang_features.push((name, span, since));
|
||||||
self.declared_features.insert(symbol);
|
self.enabled_features.insert(name);
|
||||||
|
if let Some(feature) = feature {
|
||||||
|
assert_eq!(feature.feature.name, name);
|
||||||
|
(feature.set_enabled)(self);
|
||||||
|
} else {
|
||||||
|
// Ensure we don't skip a `set_enabled` call.
|
||||||
|
debug_assert!(UNSTABLE_FEATURES.iter().find(|f| name == f.feature.name).is_none());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_declared_lib_feature(&mut self, symbol: Symbol, span: Span) {
|
pub fn set_enabled_lib_feature(&mut self, name: Symbol, span: Span) {
|
||||||
self.declared_lib_features.push((symbol, span));
|
self.enabled_lib_features.push((name, span));
|
||||||
self.declared_features.insert(symbol);
|
self.enabled_features.insert(name);
|
||||||
|
// Ensure we don't skip a `set_enabled` call.
|
||||||
|
debug_assert!(UNSTABLE_FEATURES.iter().find(|f| name == f.feature.name).is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is intended for hashing the set of active features.
|
/// This is intended for hashing the set of enabled language features.
|
||||||
///
|
///
|
||||||
/// The expectation is that this produces much smaller code than other alternatives.
|
/// The expectation is that this produces much smaller code than other alternatives.
|
||||||
///
|
///
|
||||||
/// Note that the total feature count is pretty small, so this is not a huge array.
|
/// Note that the total feature count is pretty small, so this is not a huge array.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn all_features(&self) -> [u8; NUM_FEATURES] {
|
pub fn all_lang_features(&self) -> [u8; NUM_FEATURES] {
|
||||||
[$(self.$feature as u8),+]
|
[$(self.$feature as u8),+]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the given feature explicitly declared, i.e. named in a
|
pub fn enabled_lang_features(&self) -> &Vec<(Symbol, Span, Option<Symbol>)> {
|
||||||
/// `#![feature(...)]` within the code?
|
&self.enabled_lang_features
|
||||||
pub fn declared(&self, feature: Symbol) -> bool {
|
|
||||||
self.declared_features.contains(&feature)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the given feature active (enabled by the user)?
|
pub fn enabled_lib_features(&self) -> &Vec<(Symbol, Span)> {
|
||||||
///
|
&self.enabled_lib_features
|
||||||
/// Panics if the symbol doesn't correspond to a declared feature.
|
}
|
||||||
pub fn active(&self, feature: Symbol) -> bool {
|
|
||||||
match feature {
|
|
||||||
$( sym::$feature => self.$feature, )*
|
|
||||||
|
|
||||||
_ => panic!("`{}` was not listed in `declare_features`", feature),
|
pub fn enabled_features(&self) -> &FxHashSet<Symbol> {
|
||||||
|
&self.enabled_features
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is the given feature enabled (via `#[feature(...)]`)?
|
||||||
|
pub fn enabled(&self, feature: Symbol) -> bool {
|
||||||
|
let e = self.enabled_features.contains(&feature);
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
// Ensure this matches `self.$feature`, if that exists.
|
||||||
|
let e2 = match feature {
|
||||||
|
$( sym::$feature => Some(self.$feature), )*
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
if let Some(e2) = e2 {
|
||||||
|
assert_eq!(
|
||||||
|
e, e2,
|
||||||
|
"mismatch in feature state for `{feature}`: \
|
||||||
|
`enabled_features` says {e} but `self.{feature}` says {e2}"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
e
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Some features are known to be incomplete and using them is likely to have
|
/// Some features are known to be incomplete and using them is likely to have
|
||||||
@ -119,8 +143,11 @@ macro_rules! declare_features {
|
|||||||
$(
|
$(
|
||||||
sym::$feature => status_to_enum!($status) == FeatureStatus::Incomplete,
|
sym::$feature => status_to_enum!($status) == FeatureStatus::Incomplete,
|
||||||
)*
|
)*
|
||||||
// Accepted/removed features aren't in this file but are never incomplete.
|
_ if self.enabled_features.contains(&feature) => {
|
||||||
_ if self.declared_features.contains(&feature) => false,
|
// Accepted/removed features and library features aren't in this file but
|
||||||
|
// are never incomplete.
|
||||||
|
false
|
||||||
|
}
|
||||||
_ => panic!("`{}` was not listed in `declare_features`", feature),
|
_ => panic!("`{}` was not listed in `declare_features`", feature),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,7 +159,7 @@ macro_rules! declare_features {
|
|||||||
$(
|
$(
|
||||||
sym::$feature => status_to_enum!($status) == FeatureStatus::Internal,
|
sym::$feature => status_to_enum!($status) == FeatureStatus::Internal,
|
||||||
)*
|
)*
|
||||||
_ if self.declared_features.contains(&feature) => {
|
_ if self.enabled_features.contains(&feature) => {
|
||||||
// This could be accepted/removed, or a libs feature.
|
// This could be accepted/removed, or a libs feature.
|
||||||
// Accepted/removed features aren't in this file but are never internal
|
// Accepted/removed features aren't in this file but are never internal
|
||||||
// (a removed feature might have been internal, but that's now irrelevant).
|
// (a removed feature might have been internal, but that's now irrelevant).
|
||||||
|
@ -2288,10 +2288,10 @@ impl EarlyLintPass for IncompleteInternalFeatures {
|
|||||||
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
|
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
|
||||||
let features = cx.builder.features();
|
let features = cx.builder.features();
|
||||||
features
|
features
|
||||||
.declared_lang_features
|
.enabled_lang_features()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, span, _)| (name, span))
|
.map(|(name, span, _)| (name, span))
|
||||||
.chain(features.declared_lib_features.iter().map(|(name, span)| (name, span)))
|
.chain(features.enabled_lib_features().iter().map(|(name, span)| (name, span)))
|
||||||
.filter(|(&name, _)| features.incomplete(name) || features.internal(name))
|
.filter(|(&name, _)| features.incomplete(name) || features.internal(name))
|
||||||
.for_each(|(&name, &span)| {
|
.for_each(|(&name, &span)| {
|
||||||
if features.incomplete(name) {
|
if features.incomplete(name) {
|
||||||
|
@ -858,7 +858,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> bool {
|
fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> bool {
|
||||||
let feature = if let Some(feature) = lint_id.lint.feature_gate
|
let feature = if let Some(feature) = lint_id.lint.feature_gate
|
||||||
&& !self.features.active(feature)
|
&& !self.features.enabled(feature)
|
||||||
{
|
{
|
||||||
// Lint is behind a feature that is not enabled; eventually return false.
|
// Lint is behind a feature that is not enabled; eventually return false.
|
||||||
feature
|
feature
|
||||||
|
@ -422,15 +422,15 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||||||
debug!("stability: skipping span={:?} since it is internal", span);
|
debug!("stability: skipping span={:?} since it is internal", span);
|
||||||
return EvalResult::Allow;
|
return EvalResult::Allow;
|
||||||
}
|
}
|
||||||
if self.features().declared(feature) {
|
if self.features().enabled(feature) {
|
||||||
return EvalResult::Allow;
|
return EvalResult::Allow;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this item was previously part of a now-stabilized feature which is still
|
// 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
|
// enabled (i.e. the user hasn't removed the attribute for the stabilized feature
|
||||||
// yet) then allow use of this item.
|
// yet) then allow use of this item.
|
||||||
if let Some(implied_by) = implied_by
|
if let Some(implied_by) = implied_by
|
||||||
&& self.features().declared(implied_by)
|
&& self.features().enabled(implied_by)
|
||||||
{
|
{
|
||||||
return EvalResult::Allow;
|
return EvalResult::Allow;
|
||||||
}
|
}
|
||||||
@ -509,7 +509,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||||||
debug!("body stability: skipping span={:?} since it is internal", span);
|
debug!("body stability: skipping span={:?} since it is internal", span);
|
||||||
return EvalResult::Allow;
|
return EvalResult::Allow;
|
||||||
}
|
}
|
||||||
if self.features().declared(feature) {
|
if self.features().enabled(feature) {
|
||||||
return EvalResult::Allow;
|
return EvalResult::Allow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3093,7 +3093,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||||||
Some(stability) if stability.is_const_unstable() => {
|
Some(stability) if stability.is_const_unstable() => {
|
||||||
// has a `rustc_const_unstable` attribute, check whether the user enabled the
|
// has a `rustc_const_unstable` attribute, check whether the user enabled the
|
||||||
// corresponding feature gate.
|
// corresponding feature gate.
|
||||||
self.features().declared(stability.feature)
|
self.features().enabled(stability.feature)
|
||||||
}
|
}
|
||||||
// functions without const stability are either stable user written
|
// functions without const stability are either stable user written
|
||||||
// const fn or the user is using feature gates and we thus don't
|
// const fn or the user is using feature gates and we thus don't
|
||||||
|
@ -256,7 +256,7 @@ passes_duplicate_diagnostic_item_in_crate =
|
|||||||
.note = the diagnostic item is first defined in crate `{$orig_crate_name}`
|
.note = the diagnostic item is first defined in crate `{$orig_crate_name}`
|
||||||
|
|
||||||
passes_duplicate_feature_err =
|
passes_duplicate_feature_err =
|
||||||
the feature `{$feature}` has already been declared
|
the feature `{$feature}` has already been enabled
|
||||||
|
|
||||||
passes_duplicate_lang_item =
|
passes_duplicate_lang_item =
|
||||||
found duplicate lang item `{$lang_item_name}`
|
found duplicate lang item `{$lang_item_name}`
|
||||||
|
@ -87,7 +87,7 @@ impl<'tcx> CheckConstVisitor<'tcx> {
|
|||||||
let is_feature_allowed = |feature_gate| {
|
let is_feature_allowed = |feature_gate| {
|
||||||
// All features require that the corresponding gate be enabled,
|
// All features require that the corresponding gate be enabled,
|
||||||
// even if the function has `#[rustc_allow_const_fn_unstable(the_gate)]`.
|
// even if the function has `#[rustc_allow_const_fn_unstable(the_gate)]`.
|
||||||
if !tcx.features().active(feature_gate) {
|
if !tcx.features().enabled(feature_gate) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +135,7 @@ impl<'tcx> CheckConstVisitor<'tcx> {
|
|||||||
|
|
||||||
let required_gates = required_gates.unwrap_or(&[]);
|
let required_gates = required_gates.unwrap_or(&[]);
|
||||||
let missing_gates: Vec<_> =
|
let missing_gates: Vec<_> =
|
||||||
required_gates.iter().copied().filter(|&g| !features.active(g)).collect();
|
required_gates.iter().copied().filter(|&g| !features.enabled(g)).collect();
|
||||||
|
|
||||||
match missing_gates.as_slice() {
|
match missing_gates.as_slice() {
|
||||||
[] => {
|
[] => {
|
||||||
|
@ -935,9 +935,9 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
|
|||||||
tcx.hir().visit_all_item_likes_in_crate(&mut missing);
|
tcx.hir().visit_all_item_likes_in_crate(&mut missing);
|
||||||
}
|
}
|
||||||
|
|
||||||
let declared_lang_features = &tcx.features().declared_lang_features;
|
let enabled_lang_features = tcx.features().enabled_lang_features();
|
||||||
let mut lang_features = UnordSet::default();
|
let mut lang_features = UnordSet::default();
|
||||||
for &(feature, span, since) in declared_lang_features {
|
for &(feature, span, since) in enabled_lang_features {
|
||||||
if let Some(since) = since {
|
if let Some(since) = since {
|
||||||
// Warn if the user has enabled an already-stable lang feature.
|
// Warn if the user has enabled an already-stable lang feature.
|
||||||
unnecessary_stable_feature_lint(tcx, span, feature, since);
|
unnecessary_stable_feature_lint(tcx, span, feature, since);
|
||||||
@ -948,9 +948,9 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let declared_lib_features = &tcx.features().declared_lib_features;
|
let enabled_lib_features = tcx.features().enabled_lib_features();
|
||||||
let mut remaining_lib_features = FxIndexMap::default();
|
let mut remaining_lib_features = FxIndexMap::default();
|
||||||
for (feature, span) in declared_lib_features {
|
for (feature, span) in enabled_lib_features {
|
||||||
if remaining_lib_features.contains_key(&feature) {
|
if remaining_lib_features.contains_key(&feature) {
|
||||||
// Warn if the user enables a lib feature multiple times.
|
// Warn if the user enables a lib feature multiple times.
|
||||||
tcx.dcx().emit_err(errors::DuplicateFeatureErr { span: *span, feature: *feature });
|
tcx.dcx().emit_err(errors::DuplicateFeatureErr { span: *span, feature: *feature });
|
||||||
@ -961,7 +961,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
|
|||||||
// recognise the feature when building std.
|
// recognise the feature when building std.
|
||||||
// Likewise, libtest is handled specially, so `test` isn't
|
// Likewise, libtest is handled specially, so `test` isn't
|
||||||
// available as we'd like it to be.
|
// available as we'd like it to be.
|
||||||
// FIXME: only remove `libc` when `stdbuild` is active.
|
// FIXME: only remove `libc` when `stdbuild` is enabled.
|
||||||
// FIXME: remove special casing for `test`.
|
// FIXME: remove special casing for `test`.
|
||||||
// FIXME(#120456) - is `swap_remove` correct?
|
// FIXME(#120456) - is `swap_remove` correct?
|
||||||
remaining_lib_features.swap_remove(&sym::libc);
|
remaining_lib_features.swap_remove(&sym::libc);
|
||||||
@ -1021,7 +1021,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
|
|||||||
// All local crate implications need to have the feature that implies it confirmed to exist.
|
// All local crate implications need to have the feature that implies it confirmed to exist.
|
||||||
let mut remaining_implications = tcx.stability_implications(LOCAL_CRATE).clone();
|
let mut remaining_implications = tcx.stability_implications(LOCAL_CRATE).clone();
|
||||||
|
|
||||||
// We always collect the lib features declared in the current crate, even if there are
|
// We always collect the lib features enabled in the current crate, even if there are
|
||||||
// no unknown features, because the collection also does feature attribute validation.
|
// no unknown features, because the collection also does feature attribute validation.
|
||||||
let local_defined_features = tcx.lib_features(LOCAL_CRATE);
|
let local_defined_features = tcx.lib_features(LOCAL_CRATE);
|
||||||
if !remaining_lib_features.is_empty() || !remaining_implications.is_empty() {
|
if !remaining_lib_features.is_empty() || !remaining_implications.is_empty() {
|
||||||
|
@ -112,10 +112,10 @@ impl<'tcx> HashStable<StableHashingContext<'tcx>> for rustc_feature::Features {
|
|||||||
fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) {
|
fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) {
|
||||||
// Unfortunately we cannot exhaustively list fields here, since the
|
// Unfortunately we cannot exhaustively list fields here, since the
|
||||||
// struct is macro generated.
|
// struct is macro generated.
|
||||||
self.declared_lang_features.hash_stable(hcx, hasher);
|
self.enabled_lang_features().hash_stable(hcx, hasher);
|
||||||
self.declared_lib_features.hash_stable(hcx, hasher);
|
self.enabled_lib_features().hash_stable(hcx, hasher);
|
||||||
|
|
||||||
self.all_features()[..].hash_stable(hcx, hasher);
|
self.all_lang_features()[..].hash_stable(hcx, hasher);
|
||||||
for feature in rustc_feature::UNSTABLE_FEATURES.iter() {
|
for feature in rustc_feature::UNSTABLE_FEATURES.iter() {
|
||||||
feature.feature.name.hash_stable(hcx, hasher);
|
feature.feature.name.hash_stable(hcx, hasher);
|
||||||
}
|
}
|
||||||
|
@ -1007,10 +1007,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||||||
{
|
{
|
||||||
let feature = stability.feature;
|
let feature = stability.feature;
|
||||||
|
|
||||||
let is_allowed = |feature| {
|
let is_allowed =
|
||||||
self.tcx.features().declared_features.contains(&feature)
|
|feature| self.tcx.features().enabled(feature) || span.allows_unstable(feature);
|
||||||
|| span.allows_unstable(feature)
|
|
||||||
};
|
|
||||||
let allowed_by_implication = implied_by.is_some_and(|feature| is_allowed(feature));
|
let allowed_by_implication = implied_by.is_some_and(|feature| is_allowed(feature));
|
||||||
if !is_allowed(feature) && !allowed_by_implication {
|
if !is_allowed(feature) && !allowed_by_implication {
|
||||||
let lint_buffer = &mut self.lint_buffer;
|
let lint_buffer = &mut self.lint_buffer;
|
||||||
|
@ -184,7 +184,7 @@ pub fn is_enabled(
|
|||||||
) -> Result<(), AbiDisabled> {
|
) -> Result<(), AbiDisabled> {
|
||||||
let s = is_stable(name);
|
let s = is_stable(name);
|
||||||
if let Err(AbiDisabled::Unstable { feature, .. }) = s {
|
if let Err(AbiDisabled::Unstable { feature, .. }) = s {
|
||||||
if features.active(feature) || span.allows_unstable(feature) {
|
if features.enabled(feature) || span.allows_unstable(feature) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,11 +111,7 @@ fn check_int_ty_and_feature(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||||||
let expr_ty = cx.typeck_results().expr_ty(expr);
|
let expr_ty = cx.typeck_results().expr_ty(expr);
|
||||||
match expr_ty.peel_refs().kind() {
|
match expr_ty.peel_refs().kind() {
|
||||||
ty::Uint(_) => true,
|
ty::Uint(_) => true,
|
||||||
ty::Int(_) => cx
|
ty::Int(_) => cx.tcx.features().enabled(Symbol::intern("int_roundings")),
|
||||||
.tcx
|
|
||||||
.features()
|
|
||||||
.declared_features
|
|
||||||
.contains(&Symbol::intern("int_roundings")),
|
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
error[E0636]: the feature `rustdoc_internals` has already been declared
|
error[E0636]: the feature `rustdoc_internals` has already been enabled
|
||||||
--> $DIR/rustc-check-passes.rs:2:12
|
--> $DIR/rustc-check-passes.rs:2:12
|
||||||
|
|
|
|
||||||
LL | #![feature(rustdoc_internals)]
|
LL | #![feature(rustdoc_internals)]
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
#![allow(stable_features)]
|
#![allow(stable_features)]
|
||||||
|
|
||||||
#![feature(rust1)]
|
#![feature(rust1)]
|
||||||
#![feature(rust1)] //~ ERROR the feature `rust1` has already been declared
|
#![feature(rust1)] //~ ERROR the feature `rust1` has already been enabled
|
||||||
|
|
||||||
#![feature(if_let)]
|
#![feature(if_let)]
|
||||||
#![feature(if_let)] //~ ERROR the feature `if_let` has already been declared
|
#![feature(if_let)] //~ ERROR the feature `if_let` has already been enabled
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
error[E0636]: the feature `if_let` has already been declared
|
error[E0636]: the feature `if_let` has already been enabled
|
||||||
--> $DIR/duplicate-features.rs:7:12
|
--> $DIR/duplicate-features.rs:7:12
|
||||||
|
|
|
|
||||||
LL | #![feature(if_let)]
|
LL | #![feature(if_let)]
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
|
|
||||||
error[E0636]: the feature `rust1` has already been declared
|
error[E0636]: the feature `rust1` has already been enabled
|
||||||
--> $DIR/duplicate-features.rs:4:12
|
--> $DIR/duplicate-features.rs:4:12
|
||||||
|
|
|
|
||||||
LL | #![feature(rust1)]
|
LL | #![feature(rust1)]
|
||||||
|
Loading…
Reference in New Issue
Block a user