From 1381773e01210cc7f377a9ab1aa886e621ceafe4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 9 Oct 2024 08:30:43 +0200 Subject: [PATCH] make some rustc_feature internals private, and ensure invariants with debug assertions --- compiler/rustc_ast_passes/src/feature_gate.rs | 10 +-- compiler/rustc_expand/src/config.rs | 5 +- compiler/rustc_feature/src/unstable.rs | 61 +++++++++++++++---- compiler/rustc_lint/src/builtin.rs | 4 +- compiler/rustc_passes/src/stability.rs | 4 +- .../src/ich/impls_syntax.rs | 4 +- 6 files changed, 62 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 376ef85307b..94fcfabc32c 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -604,7 +604,7 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) if sess.opts.unstable_features.is_nightly_build() { return; } - if features.enabled_features.is_empty() { + if features.enabled_features().is_empty() { return; } let mut errored = false; @@ -621,7 +621,7 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) 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 + .enabled_lang_features() .iter() .flat_map(|&(feature, _, since)| if feature == name { since } else { None }) .next(); @@ -643,11 +643,11 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) fn check_incompatible_features(sess: &Session, features: &Features) { let enabled_features = features - .enabled_lang_features + .enabled_lang_features() .iter() .copied() .map(|(name, span, _)| (name, span)) - .chain(features.enabled_lib_features.iter().copied()); + .chain(features.enabled_lib_features().iter().copied()); for (f1, f2) in rustc_feature::INCOMPATIBLE_FEATURES .iter() @@ -674,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. if let Some(&(_, gce_span, _)) = features - .enabled_lang_features + .enabled_lang_features() .iter() .find(|&&(feat, _, _)| feat == sym::generic_const_exprs) { diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index d08ebc5a942..a6b7291ee8f 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -88,7 +88,7 @@ fn feature_list(attr: &Attribute) -> ThinVec { // If the enabled 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_enabled_lang_feature(name, mi.span(), since); + features.set_enabled_lang_feature(name, mi.span(), since, None); continue; } @@ -104,7 +104,6 @@ fn feature_list(attr: &Attribute) -> ThinVec { // If the enabled 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 // 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 @@ -115,7 +114,7 @@ fn feature_list(attr: &Attribute) -> ThinVec { { sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed); } - features.set_enabled_lang_feature(name, mi.span(), None); + features.set_enabled_lang_feature(name, mi.span(), None, Some(f)); continue; } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 166f2af2210..0f9cf34fc18 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -8,7 +8,7 @@ pub struct UnstableFeature { pub feature: Feature, - pub set_enabled: fn(&mut Features), + set_enabled: fn(&mut Features), } #[derive(PartialEq)] @@ -54,11 +54,11 @@ macro_rules! declare_features { #[derive(Clone, Default, Debug)] pub struct Features { /// `#![feature]` attrs for language features, for error reporting. - pub enabled_lang_features: Vec<(Symbol, Span, Option)>, + enabled_lang_features: Vec<(Symbol, Span, Option)>, /// `#![feature]` attrs for non-language (library) features. - pub enabled_lib_features: Vec<(Symbol, Span)>, + enabled_lib_features: Vec<(Symbol, Span)>, /// `enabled_lang_features` + `enabled_lib_features`. - pub enabled_features: FxHashSet, + enabled_features: FxHashSet, /// State of individual features (unstable lang features only). /// This is `true` if and only if the corresponding feature is listed in `enabled_lang_features`. $( @@ -70,17 +70,27 @@ pub struct Features { impl Features { pub fn set_enabled_lang_feature( &mut self, - symbol: Symbol, + name: Symbol, span: Span, - since: Option + since: Option, + feature: Option<&UnstableFeature>, ) { - self.enabled_lang_features.push((symbol, span, since)); - self.enabled_features.insert(symbol); + self.enabled_lang_features.push((name, span, since)); + 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_enabled_lib_feature(&mut self, symbol: Symbol, span: Span) { - self.enabled_lib_features.push((symbol, span)); - self.enabled_features.insert(symbol); + pub fn set_enabled_lib_feature(&mut self, name: Symbol, span: Span) { + self.enabled_lib_features.push((name, span)); + 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 enabled language features. @@ -93,9 +103,36 @@ pub fn set_enabled_lib_feature(&mut self, symbol: Symbol, span: Span) { [$(self.$feature as u8),+] } + pub fn enabled_lang_features(&self) -> &Vec<(Symbol, Span, Option)> { + &self.enabled_lang_features + } + + pub fn enabled_lib_features(&self) -> &Vec<(Symbol, Span)> { + &self.enabled_lib_features + } + + pub fn enabled_features(&self) -> &FxHashSet { + &self.enabled_features + } + /// Is the given feature enabled (via `#[feature(...)]`)? pub fn enabled(&self, feature: Symbol) -> bool { - self.enabled_features.contains(&feature) + 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 diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 8fac1a82e8a..125fe9b3f16 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -2288,10 +2288,10 @@ impl EarlyLintPass for IncompleteInternalFeatures { fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { let features = cx.builder.features(); features - .enabled_lang_features + .enabled_lang_features() .iter() .map(|(name, span, _)| (name, span)) - .chain(features.enabled_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)) .for_each(|(&name, &span)| { if features.incomplete(name) { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 2f94edc83a5..80ac2741c38 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -935,7 +935,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { tcx.hir().visit_all_item_likes_in_crate(&mut missing); } - let enabled_lang_features = &tcx.features().enabled_lang_features; + let enabled_lang_features = tcx.features().enabled_lang_features(); let mut lang_features = UnordSet::default(); for &(feature, span, since) in enabled_lang_features { if let Some(since) = since { @@ -948,7 +948,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { } } - let enabled_lib_features = &tcx.features().enabled_lib_features; + let enabled_lib_features = tcx.features().enabled_lib_features(); let mut remaining_lib_features = FxIndexMap::default(); for (feature, span) in enabled_lib_features { if remaining_lib_features.contains_key(&feature) { diff --git a/compiler/rustc_query_system/src/ich/impls_syntax.rs b/compiler/rustc_query_system/src/ich/impls_syntax.rs index 797db3a17bd..78c37688d34 100644 --- a/compiler/rustc_query_system/src/ich/impls_syntax.rs +++ b/compiler/rustc_query_system/src/ich/impls_syntax.rs @@ -112,8 +112,8 @@ impl<'tcx> HashStable> for rustc_feature::Features { fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) { // Unfortunately we cannot exhaustively list fields here, since the // struct is macro generated. - self.enabled_lang_features.hash_stable(hcx, hasher); - self.enabled_lib_features.hash_stable(hcx, hasher); + self.enabled_lang_features().hash_stable(hcx, hasher); + self.enabled_lib_features().hash_stable(hcx, hasher); self.all_lang_features()[..].hash_stable(hcx, hasher); for feature in rustc_feature::UNSTABLE_FEATURES.iter() {