Skip to content

Commit

Permalink
terminology: #[feature] *activates* a feature (instead of "declaring"…
Browse files Browse the repository at this point in the history
… it)
  • Loading branch information
RalfJung committed Oct 6, 2024
1 parent 7d53688 commit 8c5bf45
Show file tree
Hide file tree
Showing 16 changed files with 101 additions and 114 deletions.
87 changes: 43 additions & 44 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -600,60 +600,61 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
}

fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) {
// checks if `#![feature]` has been used to enable any lang feature
// does not check the same for lib features unless there's at least one
// declared lang feature
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);
// checks if `#![feature]` has been used to enable any feature.
if sess.opts.unstable_features.is_nightly_build() {
return;
}
if features.active_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
.active_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;
}
sess.dcx().emit_err(err);
}
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 `active_features`.
assert!(errored);
}

fn check_incompatible_features(sess: &Session, features: &Features) {
let declared_features = features
.declared_lang_features
let active_features = features
.active_lang_features
.iter()
.copied()
.map(|(name, span, _)| (name, span))
.chain(features.declared_lib_features.iter().copied());
.chain(features.active_lib_features.iter().copied());

for (f1, f2) in rustc_feature::INCOMPATIBLE_FEATURES
.iter()
.filter(|&&(f1, f2)| features.active(f1) && features.active(f2))
{
if let Some((f1_name, f1_span)) = declared_features.clone().find(|(name, _)| name == f1) {
if let Some((f2_name, f2_span)) = declared_features.clone().find(|(name, _)| name == f2)
{
if let Some((f1_name, f1_span)) = active_features.clone().find(|(name, _)| name == f1) {
if let Some((f2_name, f2_span)) = active_features.clone().find(|(name, _)| name == f2) {
let spans = vec![f1_span, f2_span];
sess.dcx().emit_err(errors::IncompatibleFeatures {
spans,
Expand All @@ -671,10 +672,8 @@ fn check_new_solver_banned_features(sess: &Session, features: &Features) {
}

// Ban GCE with the new solver, because it does not implement GCE correctly.
if let Some(&(_, gce_span, _)) = features
.declared_lang_features
.iter()
.find(|&&(feat, _, _)| feat == sym::generic_const_exprs)
if let Some(&(_, gce_span, _)) =
features.active_lang_features.iter().find(|&&(feat, _, _)| feat == sym::generic_const_exprs)
{
sess.dcx().emit_err(errors::IncompatibleFeatures {
spans: vec![gce_span],
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_const_eval/src/check_consts/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -699,10 +699,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
// Calling an unstable function *always* requires that the corresponding gate
// (or implied gate) be enabled, even if the function has
// `#[rustc_allow_const_fn_unstable(the_gate)]`.
let gate_declared = |gate| tcx.features().declared(gate);
let feature_gate_declared = gate_declared(gate);
let implied_gate_declared = implied_by.is_some_and(gate_declared);
if !feature_gate_declared && !implied_gate_declared {
let gate_active = |gate| tcx.features().active(gate);
let feature_gate_active = gate_active(gate);
let implied_gate_active = implied_by.is_some_and(gate_active);
if !feature_gate_active && !implied_gate_active {
self.check_op(ops::FnCallUnstable(callee, Some(gate)));
return;
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_error_codes/src/error_codes/E0636.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
A `#![feature]` attribute was declared multiple times.
The same feature is activated multiple times with `#![feature]` attributes

Erroneous code example:

```compile_fail,E0636
#![allow(stable_features)]
#![feature(rust1)]
#![feature(rust1)] // error: the feature `rust1` has already been declared
#![feature(rust1)] // error: the feature `rust1` has already been activated
```
2 changes: 1 addition & 1 deletion compiler/rustc_error_codes/src/error_codes/E0705.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#### 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.

Erroneous code example:
Expand Down
16 changes: 8 additions & 8 deletions compiler/rustc_expand/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -

let mut features = Features::default();

// Process all features declared in the code.
// Process all features activated in the code.
for attr in krate_attrs {
for mi in feature_list(attr) {
let name = match mi.ident() {
Expand All @@ -75,7 +75,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
}
};

// If the declared feature has been removed, issue an error.
// If the activated feature has been removed, issue an error.
if let Some(f) = REMOVED_FEATURES.iter().find(|f| name == f.feature.name) {
sess.dcx().emit_err(FeatureRemoved {
span: mi.span(),
Expand All @@ -84,14 +84,14 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
continue;
}

// If the declared feature is stable, record it.
// If the activated feature is stable, record it.
if let Some(f) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) {
let since = Some(Symbol::intern(f.since));
features.set_declared_lang_feature(name, mi.span(), since);
features.set_active_lang_feature(name, mi.span(), since);
continue;
}

// If `-Z allow-features` is used and the declared feature is
// If `-Z allow-features` is used and the activated feature is
// unstable and not also listed as one of the allowed features,
// issue an error.
if let Some(allowed) = sess.opts.unstable_opts.allow_features.as_ref() {
Expand All @@ -101,7 +101,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
}
}

// If the declared feature is unstable, record it.
// If the activated feature is unstable, record it.
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
Expand All @@ -114,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);
}
features.set_declared_lang_feature(name, mi.span(), None);
features.set_active_lang_feature(name, mi.span(), None);
continue;
}

// Otherwise, the feature is unknown. Record it as a lib feature.
// It will be checked later.
features.set_declared_lib_feature(name, mi.span());
features.set_active_lib_feature(name, mi.span());

// Similar to above, detect internal lib features to suppress
// the ICE message that asks for a report.
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_expand/src/mbe/quoted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ pub(super) fn parse(
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 activated
fn maybe_emit_macro_metavar_expr_feature(features: &Features, sess: &Session, span: Span) {
if !features.macro_metavar_expr {
let msg = "meta-variable expressions are unstable";
Expand Down
52 changes: 21 additions & 31 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,61 +54,48 @@ macro_rules! declare_features {
#[derive(Clone, Default, Debug)]
pub struct Features {
/// `#![feature]` attrs for language features, for error reporting.
/// "declared" here means that the feature is actually enabled in the current crate.
pub declared_lang_features: Vec<(Symbol, Span, Option<Symbol>)>,
pub active_lang_features: Vec<(Symbol, Span, Option<Symbol>)>,
/// `#![feature]` attrs for non-language (library) features.
/// "declared" here means that the feature is actually enabled in the current crate.
pub declared_lib_features: Vec<(Symbol, Span)>,
/// `declared_lang_features` + `declared_lib_features`.
pub declared_features: FxHashSet<Symbol>,
/// Active state of individual features (unstable only).
pub active_lib_features: Vec<(Symbol, Span)>,
/// `active_lang_features` + `active_lib_features`.
pub active_features: FxHashSet<Symbol>,
/// Active state of individual features (unstable lang features only).
/// This is `true` if and only if the corresponding feature is listed in `active_lang_features`.
$(
$(#[doc = $doc])*
pub $feature: bool
),+
}

impl Features {
pub fn set_declared_lang_feature(
pub fn set_active_lang_feature(
&mut self,
symbol: Symbol,
span: Span,
since: Option<Symbol>
) {
self.declared_lang_features.push((symbol, span, since));
self.declared_features.insert(symbol);
self.active_lang_features.push((symbol, span, since));
self.active_features.insert(symbol);
}

pub fn set_declared_lib_feature(&mut self, symbol: Symbol, span: Span) {
self.declared_lib_features.push((symbol, span));
self.declared_features.insert(symbol);
pub fn set_active_lib_feature(&mut self, symbol: Symbol, span: Span) {
self.active_lib_features.push((symbol, span));
self.active_features.insert(symbol);
}

/// This is intended for hashing the set of active features.
/// This is intended for hashing the set of active language features.
///
/// 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.
#[inline]
pub fn all_features(&self) -> [u8; NUM_FEATURES] {
pub fn all_lang_features(&self) -> [u8; NUM_FEATURES] {
[$(self.$feature as u8),+]
}

/// Is the given feature explicitly declared, i.e. named in a
/// `#![feature(...)]` within the code?
pub fn declared(&self, feature: Symbol) -> bool {
self.declared_features.contains(&feature)
}

/// Is the given feature active (enabled by the user)?
///
/// 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),
}
self.active_features.contains(&feature)
}

/// Some features are known to be incomplete and using them is likely to have
Expand All @@ -119,8 +106,11 @@ macro_rules! declare_features {
$(
sym::$feature => status_to_enum!($status) == FeatureStatus::Incomplete,
)*
// Accepted/removed features aren't in this file but are never incomplete.
_ if self.declared_features.contains(&feature) => false,
_ if self.active_features.contains(&feature) => {
// Accepted/removed features and library features aren't in this file but
// are never incomplete.
false
}
_ => panic!("`{}` was not listed in `declare_features`", feature),
}
}
Expand All @@ -132,7 +122,7 @@ macro_rules! declare_features {
$(
sym::$feature => status_to_enum!($status) == FeatureStatus::Internal,
)*
_ if self.declared_features.contains(&feature) => {
_ if self.active_features.contains(&feature) => {
// This could be accepted/removed, or a libs feature.
// Accepted/removed features aren't in this file but are never internal
// (a removed feature might have been internal, but that's now irrelevant).
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2288,10 +2288,10 @@ impl EarlyLintPass for IncompleteInternalFeatures {
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
let features = cx.builder.features();
features
.declared_lang_features
.active_lang_features
.iter()
.map(|(name, span, _)| (name, span))
.chain(features.declared_lib_features.iter().map(|(name, span)| (name, span)))
.chain(features.active_lib_features.iter().map(|(name, span)| (name, span)))
.filter(|(&name, _)| features.incomplete(name) || features.internal(name))
.for_each(|(&name, &span)| {
if features.incomplete(name) {
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_middle/src/middle/stability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,15 +422,15 @@ impl<'tcx> TyCtxt<'tcx> {
debug!("stability: skipping span={:?} since it is internal", span);
return EvalResult::Allow;
}
if self.features().declared(feature) {
if self.features().active(feature) {
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().declared(implied_by)
&& self.features().active(implied_by)
{
return EvalResult::Allow;
}
Expand Down Expand Up @@ -509,7 +509,7 @@ impl<'tcx> TyCtxt<'tcx> {
debug!("body stability: skipping span={:?} since it is internal", span);
return EvalResult::Allow;
}
if self.features().declared(feature) {
if self.features().active(feature) {
return EvalResult::Allow;
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3092,7 +3092,7 @@ impl<'tcx> TyCtxt<'tcx> {
Some(stability) if stability.is_const_unstable() => {
// has a `rustc_const_unstable` attribute, check whether the user enabled the
// corresponding feature gate.
self.features().declared(stability.feature)
self.features().active(stability.feature)
}
// functions without const stability are either stable user written
// const fn or the user is using feature gates and we thus don't
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ passes_duplicate_diagnostic_item_in_crate =
.note = the diagnostic item is first defined in crate `{$orig_crate_name}`
passes_duplicate_feature_err =
the feature `{$feature}` has already been declared
the feature `{$feature}` has already been activated
passes_duplicate_lang_item =
found duplicate lang item `{$lang_item_name}`
Expand Down
Loading

0 comments on commit 8c5bf45

Please sign in to comment.