diff options
| author | bors <bors@rust-lang.org> | 2025-07-18 16:27:59 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2025-07-18 16:27:59 +0000 |
| commit | 8f08b3a32478b8d0507732800ecb548a76e0fd0c (patch) | |
| tree | abdd62e27dec75c21bc2569e90a650de0513fb2c /compiler | |
| parent | 82310651b93a594a3fd69015e1562186a080d94c (diff) | |
| parent | 7662731d75ceb19f6242c61a6812c22809e6adc2 (diff) | |
| download | rust-8f08b3a32478b8d0507732800ecb548a76e0fd0c.tar.gz rust-8f08b3a32478b8d0507732800ecb548a76e0fd0c.zip | |
Auto merge of #143845 - cjgillot:stability-query, r=jieyouxu
Split-up stability_index query
This PR aims to move deprecation and stability processing away from the monolithic `stability_index` query, and directly implement `lookup_{deprecation,stability,body_stability,const_stability}` queries.
The basic idea is to:
- move per-attribute sanity checks into `check_attr.rs`;
- move attribute compatibility checks into the `MissingStabilityAnnotations` visitor;
- progressively dismantle the `Annotator` visitor and the `stability_index` query.
The first commit contains functional change, and now warns when `#[automatically_derived]` is applied on a non-trait impl block. The other commits should not change visible behaviour.
Perf in https://github.com/rust-lang/rust/pull/143845#issuecomment-3066308630 shows small but consistent improvement, except for unused-warnings case. That case being a stress test, I'm leaning towards accepting the regression.
This PR changes `check_attr`, so has a high conflict rate on that file. This should not cause issues for review.
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_attr_data_structures/src/lib.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_attr_data_structures/src/stability.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_attr_parsing/src/attributes/stability.rs | 8 | ||||
| -rw-r--r-- | compiler/rustc_hir/src/target.rs | 29 | ||||
| -rw-r--r-- | compiler/rustc_interface/src/passes.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/middle/stability.rs | 45 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/query/mod.rs | 19 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/context.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_passes/messages.ftl | 3 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/check_attr.rs | 88 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/errors.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/lang_items.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/lib_features.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/stability.rs | 940 |
14 files changed, 507 insertions, 653 deletions
diff --git a/compiler/rustc_attr_data_structures/src/lib.rs b/compiler/rustc_attr_data_structures/src/lib.rs index 8f8ce575a18..ecca0e39063 100644 --- a/compiler/rustc_attr_data_structures/src/lib.rs +++ b/compiler/rustc_attr_data_structures/src/lib.rs @@ -24,7 +24,7 @@ use rustc_ast::token::CommentKind; use rustc_ast::{AttrStyle, IntTy, UintTy}; use rustc_ast_pretty::pp::Printer; use rustc_span::hygiene::Transparency; -use rustc_span::{Span, Symbol}; +use rustc_span::{ErrorGuaranteed, Span, Symbol}; pub use stability::*; use thin_vec::ThinVec; pub use version::*; @@ -170,7 +170,7 @@ macro_rules! print_tup { } print_tup!(A B C D E F G H); -print_skip!(Span, ()); +print_skip!(Span, (), ErrorGuaranteed); print_disp!(u16, bool, NonZero<u32>); print_debug!(Symbol, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency); diff --git a/compiler/rustc_attr_data_structures/src/stability.rs b/compiler/rustc_attr_data_structures/src/stability.rs index 218e771c745..bd31c062117 100644 --- a/compiler/rustc_attr_data_structures/src/stability.rs +++ b/compiler/rustc_attr_data_structures/src/stability.rs @@ -1,7 +1,7 @@ use std::num::NonZero; use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute}; -use rustc_span::{Symbol, sym}; +use rustc_span::{ErrorGuaranteed, Symbol, sym}; use crate::{PrintAttribute, RustcVersion}; @@ -153,7 +153,7 @@ pub enum StableSince { /// Stabilized in the upcoming version, whatever number that is. Current, /// Failed to parse a stabilization version. - Err, + Err(ErrorGuaranteed), } impl StabilityLevel { diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 8f405e5aad9..59337749c87 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -292,12 +292,12 @@ pub(crate) fn parse_stability<S: Stage>( } else if let Some(version) = parse_version(since) { StableSince::Version(version) } else { - cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span }); - StableSince::Err + let err = cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span }); + StableSince::Err(err) } } else { - cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span }); - StableSince::Err + let err = cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span }); + StableSince::Err(err) }; match feature { diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs index 601898023fc..d617f44f8d8 100644 --- a/compiler/rustc_hir/src/target.rs +++ b/compiler/rustc_hir/src/target.rs @@ -41,7 +41,7 @@ pub enum Target { Union, Trait, TraitAlias, - Impl, + Impl { of_trait: bool }, Expression, Statement, Arm, @@ -51,7 +51,7 @@ pub enum Target { ForeignFn, ForeignStatic, ForeignTy, - GenericParam(GenericParamKind), + GenericParam { kind: GenericParamKind, has_default: bool }, MacroDef, Param, PatField, @@ -86,14 +86,14 @@ impl Target { | Target::Union | Target::Trait | Target::TraitAlias - | Target::Impl + | Target::Impl { .. } | Target::Expression | Target::Statement | Target::Arm | Target::ForeignFn | Target::ForeignStatic | Target::ForeignTy - | Target::GenericParam(_) + | Target::GenericParam { .. } | Target::MacroDef | Target::Param | Target::PatField @@ -119,7 +119,7 @@ impl Target { ItemKind::Union(..) => Target::Union, ItemKind::Trait(..) => Target::Trait, ItemKind::TraitAlias(..) => Target::TraitAlias, - ItemKind::Impl { .. } => Target::Impl, + ItemKind::Impl(imp_) => Target::Impl { of_trait: imp_.of_trait.is_some() }, } } @@ -141,7 +141,7 @@ impl Target { DefKind::Union => Target::Union, DefKind::Trait => Target::Trait, DefKind::TraitAlias => Target::TraitAlias, - DefKind::Impl { .. } => Target::Impl, + DefKind::Impl { of_trait } => Target::Impl { of_trait }, _ => panic!("impossible case reached"), } } @@ -169,11 +169,17 @@ impl Target { pub fn from_generic_param(generic_param: &hir::GenericParam<'_>) -> Target { match generic_param.kind { - hir::GenericParamKind::Type { .. } => Target::GenericParam(GenericParamKind::Type), + hir::GenericParamKind::Type { default, .. } => Target::GenericParam { + kind: GenericParamKind::Type, + has_default: default.is_some(), + }, hir::GenericParamKind::Lifetime { .. } => { - Target::GenericParam(GenericParamKind::Lifetime) + Target::GenericParam { kind: GenericParamKind::Lifetime, has_default: false } } - hir::GenericParamKind::Const { .. } => Target::GenericParam(GenericParamKind::Const), + hir::GenericParamKind::Const { default, .. } => Target::GenericParam { + kind: GenericParamKind::Const, + has_default: default.is_some(), + }, } } @@ -196,7 +202,8 @@ impl Target { Target::Union => "union", Target::Trait => "trait", Target::TraitAlias => "trait alias", - Target::Impl => "implementation block", + Target::Impl { of_trait: false } => "inherent implementation block", + Target::Impl { of_trait: true } => "trait implementation block", Target::Expression => "expression", Target::Statement => "statement", Target::Arm => "match arm", @@ -210,7 +217,7 @@ impl Target { Target::ForeignFn => "foreign function", Target::ForeignStatic => "foreign static item", Target::ForeignTy => "foreign type", - Target::GenericParam(kind) => match kind { + Target::GenericParam { kind, has_default: _ } => match kind { GenericParamKind::Type => "type parameter", GenericParamKind::Lifetime => "lifetime parameter", GenericParamKind::Const => "const parameter", diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index a438cde018c..fb6897c7d89 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1055,17 +1055,11 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { }); }, { - sess.time("unused_lib_feature_checking", || { - rustc_passes::stability::check_unused_or_stable_features(tcx) - }); - }, - { // We force these queries to run, // since they might not otherwise get called. // This marks the corresponding crate-level attributes // as used, and ensures that their values are valid. tcx.ensure_ok().limits(()); - tcx.ensure_ok().stability_index(()); } ); }); diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 99faba7b2c0..dc9311188e8 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -7,10 +7,9 @@ use rustc_ast::NodeId; use rustc_attr_data_structures::{ self as attrs, ConstStability, DefaultBodyStability, DeprecatedSince, Deprecation, Stability, }; -use rustc_data_structures::unord::UnordMap; use rustc_errors::{Applicability, Diag, EmissionGuarantee}; use rustc_feature::GateIssue; -use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap}; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, HirId}; use rustc_macros::{Decodable, Encodable, HashStable, Subdiagnostic}; use rustc_session::Session; @@ -65,48 +64,6 @@ impl DeprecationEntry { } } -/// A stability index, giving the stability level for items and methods. -#[derive(HashStable, Debug)] -pub struct Index { - /// This is mostly a cache, except the stabilities of local items - /// are filled by the annotator. - pub stab_map: LocalDefIdMap<Stability>, - pub const_stab_map: LocalDefIdMap<ConstStability>, - pub default_body_stab_map: LocalDefIdMap<DefaultBodyStability>, - pub depr_map: LocalDefIdMap<DeprecationEntry>, - /// Mapping from feature name to feature name based on the `implied_by` field of `#[unstable]` - /// attributes. If a `#[unstable(feature = "implier", implied_by = "impliee")]` attribute - /// exists, then this map will have a `impliee -> implier` entry. - /// - /// This mapping is necessary unless both the `#[stable]` and `#[unstable]` attributes should - /// specify their implications (both `implies` and `implied_by`). If only one of the two - /// attributes do (as in the current implementation, `implied_by` in `#[unstable]`), then this - /// mapping is necessary for diagnostics. When a "unnecessary feature attribute" error is - /// reported, only the `#[stable]` attribute information is available, so the map is necessary - /// to know that the feature implies another feature. If it were reversed, and the `#[stable]` - /// attribute had an `implies` meta item, then a map would be necessary when avoiding a "use of - /// unstable feature" error for a feature that was implied. - pub implications: UnordMap<Symbol, Symbol>, -} - -impl Index { - pub fn local_stability(&self, def_id: LocalDefId) -> Option<Stability> { - self.stab_map.get(&def_id).copied() - } - - pub fn local_const_stability(&self, def_id: LocalDefId) -> Option<ConstStability> { - self.const_stab_map.get(&def_id).copied() - } - - pub fn local_default_body_stability(&self, def_id: LocalDefId) -> Option<DefaultBodyStability> { - self.default_body_stab_map.get(&def_id).copied() - } - - pub fn local_deprecation_entry(&self, def_id: LocalDefId) -> Option<DeprecationEntry> { - self.depr_map.get(&def_id).cloned() - } -} - pub fn report_unstable( sess: &Session, feature: Symbol, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index d5c2b6de4ae..ae8c8259be4 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -112,7 +112,7 @@ use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use crate::middle::lib_features::LibFeatures; use crate::middle::privacy::EffectiveVisibilities; use crate::middle::resolve_bound_vars::{ObjectLifetimeDefault, ResolveBoundVars, ResolvedArg}; -use crate::middle::stability::{self, DeprecationEntry}; +use crate::middle::stability::DeprecationEntry; use crate::mir::interpret::{ EvalStaticInitializerRawResult, EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, GlobalId, LitToConstInput, @@ -2171,6 +2171,18 @@ rustc_queries! { separate_provide_extern arena_cache } + /// Mapping from feature name to feature name based on the `implied_by` field of `#[unstable]` + /// attributes. If a `#[unstable(feature = "implier", implied_by = "impliee")]` attribute + /// exists, then this map will have a `impliee -> implier` entry. + /// + /// This mapping is necessary unless both the `#[stable]` and `#[unstable]` attributes should + /// specify their implications (both `implies` and `implied_by`). If only one of the two + /// attributes do (as in the current implementation, `implied_by` in `#[unstable]`), then this + /// mapping is necessary for diagnostics. When a "unnecessary feature attribute" error is + /// reported, only the `#[stable]` attribute information is available, so the map is necessary + /// to know that the feature implies another feature. If it were reversed, and the `#[stable]` + /// attribute had an `implies` meta item, then a map would be necessary when avoiding a "use of + /// unstable feature" error for a feature that was implied. query stability_implications(_: CrateNum) -> &'tcx UnordMap<Symbol, Symbol> { arena_cache desc { "calculating the implications between `#[unstable]` features defined in a crate" } @@ -2277,11 +2289,6 @@ rustc_queries! { desc { "fetching potentially unused trait imports" } } - query stability_index(_: ()) -> &'tcx stability::Index { - arena_cache - eval_always - desc { "calculating the stability index for the local crate" } - } /// All available crates in the graph, including those that should not be user-facing /// (such as private crates). query crates(_: ()) -> &'tcx [CrateNum] { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 81c13e55dd8..7e6bcfee025 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -65,7 +65,7 @@ use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarKind, Canonica use crate::lint::lint_level; use crate::metadata::ModChild; use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature}; -use crate::middle::{resolve_bound_vars, stability}; +use crate::middle::resolve_bound_vars; use crate::mir::interpret::{self, Allocation, ConstAllocation}; use crate::mir::{Body, Local, Place, PlaceElem, ProjectionKind, Promoted}; use crate::query::plumbing::QuerySystem; @@ -1807,10 +1807,6 @@ impl<'tcx> TyCtxt<'tcx> { ) } - pub fn stability(self) -> &'tcx stability::Index { - self.stability_index(()) - } - pub fn features(self) -> &'tcx rustc_feature::Features { self.features_query(()) } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index d1b856ca415..51e23edb9bb 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -560,7 +560,8 @@ passes_only_has_effect_on = `#[{$attr_name}]` only has an effect on {$target_name -> [function] functions [module] modules - [implementation_block] implementation blocks + [trait_implementation_block] trait implementation blocks + [inherent_implementation_block] inherent implementation blocks *[unspecified] (unspecified--this is a compiler bug) } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 2766b14bb8d..3ec6a1124a6 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -11,11 +11,17 @@ use std::slice; use rustc_abi::{Align, ExternAbi, Size}; use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, ast, join_path_syms}; -use rustc_attr_data_structures::{AttributeKind, InlineAttr, ReprAttr, find_attr}; +use rustc_attr_data_structures::{ + AttributeKind, InlineAttr, PartialConstStability, ReprAttr, Stability, StabilityLevel, + find_attr, +}; use rustc_attr_parsing::{AttributeParser, Late}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey}; -use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; +use rustc_feature::{ + ACCEPTED_LANG_FEATURES, AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, + BuiltinAttribute, +}; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; @@ -36,6 +42,7 @@ use rustc_session::lint; use rustc_session::lint::builtin::{ CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS, MALFORMED_DIAGNOSTIC_ATTRIBUTES, MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, + USELESS_DEPRECATED, }; use rustc_session::parse::feature_err; use rustc_span::edition::Edition; @@ -161,12 +168,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> { sym::automatically_derived, *attr_span, target, - Target::Impl, + Target::Impl { of_trait: true }, ), Attribute::Parsed( - AttributeKind::Stability { span, .. } - | AttributeKind::ConstStability { span, .. }, - ) => self.check_stability_promotable(*span, target), + AttributeKind::Stability { + span: attr_span, + stability: Stability { level, feature }, + } + | AttributeKind::ConstStability { + span: attr_span, + stability: PartialConstStability { level, feature, .. }, + }, + ) => self.check_stability(*attr_span, span, level, *feature, target), Attribute::Parsed(AttributeKind::Inline(InlineAttr::Force { .. }, ..)) => {} // handled separately below Attribute::Parsed(AttributeKind::Inline(kind, attr_span)) => { self.check_inline(hir_id, *attr_span, span, kind, target) @@ -494,7 +507,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr: &Attribute, item: Option<ItemLike<'_>>, ) { - if !matches!(target, Target::Impl) + if !matches!(target, Target::Impl { .. }) || matches!( item, Some(ItemLike::Item(hir::Item { kind: hir::ItemKind::Impl(_impl),.. })) @@ -598,7 +611,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Target::Fn | Target::Closure | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) - | Target::Impl + | Target::Impl { .. } | Target::Mod => return, // These are "functions", but they aren't allowed because they don't @@ -987,9 +1000,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let span = meta.span(); if let Some(location) = match target { Target::AssocTy => { - let parent_def_id = self.tcx.hir_get_parent_item(hir_id).def_id; - let containing_item = self.tcx.hir_expect_item(parent_def_id); - if Target::from_item(containing_item) == Target::Impl { + if let DefKind::Impl { .. } = + self.tcx.def_kind(self.tcx.local_parent(hir_id.owner.def_id)) + { Some("type alias in implementation block") } else { None @@ -1012,7 +1025,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | Target::Arm | Target::ForeignMod | Target::Closure - | Target::Impl + | Target::Impl { .. } | Target::WherePredicate => Some(target.name()), Target::ExternCrate | Target::Use @@ -1033,7 +1046,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | Target::ForeignFn | Target::ForeignStatic | Target::ForeignTy - | Target::GenericParam(..) + | Target::GenericParam { .. } | Target::MacroDef | Target::PatField | Target::ExprField => None, @@ -1590,7 +1603,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let article = match target { Target::ExternCrate | Target::Enum - | Target::Impl + | Target::Impl { .. } | Target::Expression | Target::Arm | Target::AssocConst @@ -2274,7 +2287,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { // FIXME(staged_api): There's no reason we can't support more targets here. We're just // being conservative to begin with. - Target::Fn | Target::Impl => {} + Target::Fn | Target::Impl { .. } => {} Target::ExternCrate | Target::Use | Target::Static @@ -2300,7 +2313,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | Target::ForeignFn | Target::ForeignStatic | Target::ForeignTy - | Target::GenericParam(_) + | Target::GenericParam { .. } | Target::MacroDef | Target::Param | Target::PatField @@ -2320,13 +2333,30 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_stability_promotable(&self, span: Span, target: Target) { + fn check_stability( + &self, + attr_span: Span, + item_span: Span, + level: &StabilityLevel, + feature: Symbol, + target: Target, + ) { match target { Target::Expression => { - self.dcx().emit_err(errors::StabilityPromotable { attr_span: span }); + self.dcx().emit_err(errors::StabilityPromotable { attr_span }); } _ => {} } + + // Stable *language* features shouldn't be used as unstable library features. + // (Not doing this for stable library features is checked by tidy.) + if level.is_unstable() + && ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some() + { + self.tcx + .dcx() + .emit_err(errors::UnstableAttrForAlreadyStableFeature { attr_span, item_span }); + } } fn check_link_ordinal(&self, attr_span: Span, _span: Span, target: Target) { @@ -2354,6 +2384,28 @@ impl<'tcx> CheckAttrVisitor<'tcx> { errors::Deprecated, ); } + Target::Impl { of_trait: true } + | Target::GenericParam { has_default: false, kind: _ } => { + self.tcx.emit_node_span_lint( + USELESS_DEPRECATED, + hir_id, + attr.span(), + errors::DeprecatedAnnotationHasNoEffect { span: attr.span() }, + ); + } + Target::AssocConst | Target::Method(..) | Target::AssocTy + if matches!( + self.tcx.def_kind(self.tcx.local_parent(hir_id.owner.def_id)), + DefKind::Impl { of_trait: true } + ) => + { + self.tcx.emit_node_span_lint( + USELESS_DEPRECATED, + hir_id, + attr.span(), + errors::DeprecatedAnnotationHasNoEffect { span: attr.span() }, + ); + } _ => {} } } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 37330c0ed6e..c6ab6b0d601 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1373,9 +1373,9 @@ pub(crate) struct UnstableAttrForAlreadyStableFeature { #[primary_span] #[label] #[help] - pub span: Span, + pub attr_span: Span, #[label(passes_item)] - pub item_sp: Span, + pub item_span: Span, } #[derive(Diagnostic)] diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 3afed9784de..6fac01827a4 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -287,7 +287,7 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> { ast::ItemKind::Union(..) => Target::Union, ast::ItemKind::Trait(_) => Target::Trait, ast::ItemKind::TraitAlias(..) => Target::TraitAlias, - ast::ItemKind::Impl(_) => Target::Impl, + ast::ItemKind::Impl(imp_) => Target::Impl { of_trait: imp_.of_trait.is_some() }, ast::ItemKind::MacroDef(..) => Target::MacroDef, ast::ItemKind::MacCall(_) | ast::ItemKind::DelegationMac(_) => { unreachable!("macros should have been expanded") diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index 127e0df1332..35d2a655991 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -44,7 +44,7 @@ impl<'tcx> LibFeatureCollector<'tcx> { StabilityLevel::Stable { since, .. } => FeatureStability::AcceptedSince(match since { StableSince::Version(v) => Symbol::intern(&v.to_string()), StableSince::Current => sym::env_CFG_RELEASE, - StableSince::Err => return None, + StableSince::Err(_) => return None, }), }; diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index e5530d52686..40999d622dc 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -1,37 +1,31 @@ //! A pass that annotates every item and method with its stability level, //! propagating default levels lexically from parent to children ast nodes. -use std::mem::replace; use std::num::NonZero; use rustc_ast_lowering::stability::extern_abi_stability; use rustc_attr_data_structures::{ - self as attrs, AttributeKind, ConstStability, DeprecatedSince, PartialConstStability, - Stability, StabilityLevel, StableSince, UnstableReason, VERSION_PLACEHOLDER, find_attr, + self as attrs, AttributeKind, ConstStability, DefaultBodyStability, DeprecatedSince, Stability, + StabilityLevel, StableSince, UnstableReason, VERSION_PLACEHOLDER, find_attr, }; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet}; -use rustc_feature::{ACCEPTED_LANG_FEATURES, EnabledLangFeature, EnabledLibFeature}; +use rustc_feature::{EnabledLangFeature, EnabledLibFeature}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModDefId}; -use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{self, Visitor, VisitorExt}; use rustc_hir::{self as hir, AmbigArg, FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant}; use rustc_middle::hir::nested_filter; use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures}; use rustc_middle::middle::privacy::EffectiveVisibilities; -use rustc_middle::middle::stability::{ - AllowUnstable, Deprecated, DeprecationEntry, EvalResult, Index, -}; -use rustc_middle::query::Providers; +use rustc_middle::middle::stability::{AllowUnstable, Deprecated, DeprecationEntry, EvalResult}; +use rustc_middle::query::{LocalCrate, Providers}; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_session::lint; -use rustc_session::lint::builtin::{ - DEPRECATED, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED, -}; +use rustc_session::lint::builtin::{DEPRECATED, INEFFECTIVE_UNSTABLE_TRAIT_IMPL}; use rustc_span::{Span, Symbol, sym}; -use tracing::{debug, info}; +use tracing::instrument; use crate::errors; @@ -47,359 +41,263 @@ enum AnnotationKind { Container, } -/// Whether to inherit deprecation flags for nested items. In most cases, we do want to inherit -/// deprecation, because nested items rarely have individual deprecation attributes, and so -/// should be treated as deprecated if their parent is. However, default generic parameters -/// have separate deprecation attributes from their parents, so we do not wish to inherit -/// deprecation in this case. For example, inheriting deprecation for `T` in `Foo<T>` -/// would cause a duplicate warning arising from both `Foo` and `T` being deprecated. -#[derive(Clone)] -enum InheritDeprecation { - Yes, - No, +fn inherit_deprecation(def_kind: DefKind) -> bool { + match def_kind { + DefKind::LifetimeParam | DefKind::TyParam | DefKind::ConstParam => false, + _ => true, + } } -impl InheritDeprecation { - fn yes(&self) -> bool { - matches!(self, InheritDeprecation::Yes) +fn inherit_const_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { + let def_kind = tcx.def_kind(def_id); + match def_kind { + DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => { + match tcx.def_kind(tcx.local_parent(def_id)) { + DefKind::Impl { of_trait: true } => true, + _ => false, + } + } + _ => false, } } -/// Whether to inherit const stability flags for nested items. In most cases, we do not want to -/// inherit const stability: just because an enclosing `fn` is const-stable does not mean -/// all `extern` imports declared in it should be const-stable! However, trait methods -/// inherit const stability attributes from their parent and do not have their own. -enum InheritConstStability { - Yes, - No, -} +fn annotation_kind(tcx: TyCtxt<'_>, def_id: LocalDefId) -> AnnotationKind { + let def_kind = tcx.def_kind(def_id); + match def_kind { + // Inherent impls and foreign modules serve only as containers for other items, + // they don't have their own stability. They still can be annotated as unstable + // and propagate this unstability to children, but this annotation is completely + // optional. They inherit stability from their parents when unannotated. + DefKind::Impl { of_trait: false } | DefKind::ForeignMod => AnnotationKind::Container, + DefKind::Impl { of_trait: true } => AnnotationKind::DeprecationProhibited, + + // Allow stability attributes on default generic arguments. + DefKind::TyParam | DefKind::ConstParam => { + match &tcx.hir_node_by_def_id(def_id).expect_generic_param().kind { + hir::GenericParamKind::Type { default: Some(_), .. } + | hir::GenericParamKind::Const { default: Some(_), .. } => { + AnnotationKind::Container + } + _ => AnnotationKind::Prohibited, + } + } + + // Impl items in trait impls cannot have stability. + DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst => { + match tcx.def_kind(tcx.local_parent(def_id)) { + DefKind::Impl { of_trait: true } => AnnotationKind::Prohibited, + _ => AnnotationKind::Required, + } + } -impl InheritConstStability { - fn yes(&self) -> bool { - matches!(self, InheritConstStability::Yes) + _ => AnnotationKind::Required, } } -enum InheritStability { - Yes, - No, -} +fn lookup_deprecation_entry(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DeprecationEntry> { + let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id)); + let depr = attrs::find_attr!(attrs, + AttributeKind::Deprecation { deprecation, span: _ } => *deprecation + ); -impl InheritStability { - fn yes(&self) -> bool { - matches!(self, InheritStability::Yes) - } + let Some(depr) = depr else { + if inherit_deprecation(tcx.def_kind(def_id)) { + let parent_id = tcx.opt_local_parent(def_id)?; + let parent_depr = tcx.lookup_deprecation_entry(parent_id)?; + return Some(parent_depr); + } + + return None; + }; + + // `Deprecation` is just two pointers, no need to intern it + Some(DeprecationEntry::local(depr, def_id)) } -/// A private tree-walker for producing an `Index`. -struct Annotator<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - index: &'a mut Index, - parent_stab: Option<Stability>, - parent_const_stab: Option<ConstStability>, - parent_depr: Option<DeprecationEntry>, - in_trait_impl: bool, +fn inherit_stability(def_kind: DefKind) -> bool { + match def_kind { + DefKind::Field | DefKind::Variant | DefKind::Ctor(..) => true, + _ => false, + } } -impl<'a, 'tcx> Annotator<'a, 'tcx> { - /// Determine the stability for a node based on its attributes and inherited stability. The - /// stability is recorded in the index and used as the parent. If the node is a function, - /// `fn_sig` is its signature. - fn annotate<F>( - &mut self, - def_id: LocalDefId, - item_sp: Span, - fn_sig: Option<&'tcx hir::FnSig<'tcx>>, - kind: AnnotationKind, - inherit_deprecation: InheritDeprecation, - inherit_const_stability: InheritConstStability, - inherit_from_parent: InheritStability, - visit_children: F, - ) where - F: FnOnce(&mut Self), - { - let attrs = self.tcx.hir_attrs(self.tcx.local_def_id_to_hir_id(def_id)); - debug!("annotate(id = {:?}, attrs = {:?})", def_id, attrs); - - let depr = attrs::find_attr!(attrs, AttributeKind::Deprecation{deprecation, span} => (*deprecation, *span)); - let const_stability_indirect = find_attr!(attrs, AttributeKind::ConstStabilityIndirect); - - let mut is_deprecated = false; - if let Some((depr, span)) = &depr { - is_deprecated = true; - - if matches!(kind, AnnotationKind::Prohibited | AnnotationKind::DeprecationProhibited) { - let hir_id = self.tcx.local_def_id_to_hir_id(def_id); - self.tcx.emit_node_span_lint( - USELESS_DEPRECATED, - hir_id, - *span, - errors::DeprecatedAnnotationHasNoEffect { span: *span }, - ); - } +/// If the `-Z force-unstable-if-unmarked` flag is passed then we provide +/// a parent stability annotation which indicates that this is private +/// with the `rustc_private` feature. This is intended for use when +/// compiling library and `rustc_*` crates themselves so we can leverage crates.io +/// while maintaining the invariant that all sysroot crates are unstable +/// by default and are unable to be used. +const FORCE_UNSTABLE: Stability = Stability { + level: attrs::StabilityLevel::Unstable { + reason: UnstableReason::Default, + issue: NonZero::new(27812), + is_soft: false, + implied_by: None, + old_name: None, + }, + feature: sym::rustc_private, +}; - // `Deprecation` is just two pointers, no need to intern it - let depr_entry = DeprecationEntry::local(*depr, def_id); - self.index.depr_map.insert(def_id, depr_entry); - } else if let Some(parent_depr) = self.parent_depr { - if inherit_deprecation.yes() { - is_deprecated = true; - info!("tagging child {:?} as deprecated from parent", def_id); - self.index.depr_map.insert(def_id, parent_depr); - } +#[instrument(level = "debug", skip(tcx))] +fn lookup_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<Stability> { + // Propagate unstability. This can happen even for non-staged-api crates in case + // -Zforce-unstable-if-unmarked is set. + if !tcx.features().staged_api() { + if !tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { + return None; } - if !self.tcx.features().staged_api() { - // Propagate unstability. This can happen even for non-staged-api crates in case - // -Zforce-unstable-if-unmarked is set. - if let Some(stab) = self.parent_stab { - if inherit_deprecation.yes() && stab.is_unstable() { - self.index.stab_map.insert(def_id, stab); - if fn_sig.is_some_and(|s| s.header.is_const()) { - self.index.const_stab_map.insert( - def_id, - ConstStability::unmarked(const_stability_indirect, stab), - ); - } - } - } + let Some(parent) = tcx.opt_local_parent(def_id) else { return Some(FORCE_UNSTABLE) }; - self.recurse_with_stability_attrs( - depr.map(|(d, _)| DeprecationEntry::local(d, def_id)), - None, - None, - visit_children, - ); - return; + if inherit_deprecation(tcx.def_kind(def_id)) { + let parent = tcx.lookup_stability(parent)?; + if parent.is_unstable() { + return Some(parent); + } } - // # Regular and body stability - let stab = attrs::find_attr!(attrs, AttributeKind::Stability { stability, span } => (*stability, *span)); - let body_stab = - attrs::find_attr!(attrs, AttributeKind::BodyStability { stability, .. } => *stability); + return None; + } - if let Some((depr, span)) = &depr - && depr.is_since_rustc_version() - && stab.is_none() - { - self.tcx.dcx().emit_err(errors::DeprecatedAttribute { span: *span }); - } + // # Regular stability + let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id)); + let stab = + attrs::find_attr!(attrs, AttributeKind::Stability { stability, span: _ } => *stability); - if let Some(body_stab) = body_stab { - // FIXME: check that this item can have body stability + if let Some(stab) = stab { + return Some(stab); + } - self.index.default_body_stab_map.insert(def_id, body_stab); - debug!(?self.index.default_body_stab_map); + if inherit_deprecation(tcx.def_kind(def_id)) { + let Some(parent) = tcx.opt_local_parent(def_id) else { + return tcx + .sess + .opts + .unstable_opts + .force_unstable_if_unmarked + .then_some(FORCE_UNSTABLE); + }; + let parent = tcx.lookup_stability(parent)?; + if parent.is_unstable() || inherit_stability(tcx.def_kind(def_id)) { + return Some(parent); } + } - let stab = stab.map(|(stab, span)| { - // Error if prohibited, or can't inherit anything from a container. - if kind == AnnotationKind::Prohibited - || (kind == AnnotationKind::Container && stab.level.is_stable() && is_deprecated) - { - self.tcx.dcx().emit_err(errors::UselessStability { span, item_sp }); - } + None +} - debug!("annotate: found {:?}", stab); +#[instrument(level = "debug", skip(tcx))] +fn lookup_default_body_stability( + tcx: TyCtxt<'_>, + def_id: LocalDefId, +) -> Option<DefaultBodyStability> { + if !tcx.features().staged_api() { + return None; + } - // Check if deprecated_since < stable_since. If it is, - // this is *almost surely* an accident. - if let ( - &Some(DeprecatedSince::RustcVersion(dep_since)), - &attrs::StabilityLevel::Stable { since: stab_since, .. }, - ) = (&depr.as_ref().map(|(d, _)| d.since), &stab.level) - { - match stab_since { - StableSince::Current => { - self.tcx - .dcx() - .emit_err(errors::CannotStabilizeDeprecated { span, item_sp }); - } - StableSince::Version(stab_since) => { - if dep_since < stab_since { - self.tcx - .dcx() - .emit_err(errors::CannotStabilizeDeprecated { span, item_sp }); - } - } - StableSince::Err => { - // An error already reported. Assume the unparseable stabilization - // version is older than the deprecation version. - } - } - } + let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id)); + // FIXME: check that this item can have body stability + attrs::find_attr!(attrs, AttributeKind::BodyStability { stability, .. } => *stability) +} - // Stable *language* features shouldn't be used as unstable library features. - // (Not doing this for stable library features is checked by tidy.) - if let Stability { level: StabilityLevel::Unstable { .. }, feature } = stab { - if ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some() { - self.tcx - .dcx() - .emit_err(errors::UnstableAttrForAlreadyStableFeature { span, item_sp }); - } - } - if let Stability { - level: StabilityLevel::Unstable { implied_by: Some(implied_by), .. }, - feature, - } = stab +#[instrument(level = "debug", skip(tcx))] +fn lookup_const_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ConstStability> { + if !tcx.features().staged_api() { + // Propagate unstability. This can happen even for non-staged-api crates in case + // -Zforce-unstable-if-unmarked is set. + if inherit_deprecation(tcx.def_kind(def_id)) { + let parent = tcx.opt_local_parent(def_id)?; + let parent_stab = tcx.lookup_stability(parent)?; + if parent_stab.is_unstable() + && let Some(fn_sig) = tcx.hir_node_by_def_id(def_id).fn_sig() + && fn_sig.header.is_const() { - self.index.implications.insert(implied_by, feature); - } - - self.index.stab_map.insert(def_id, stab); - stab - }); - - if stab.is_none() { - debug!("annotate: stab not found, parent = {:?}", self.parent_stab); - if let Some(stab) = self.parent_stab { - if inherit_deprecation.yes() && stab.is_unstable() || inherit_from_parent.yes() { - self.index.stab_map.insert(def_id, stab); - } + let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id)); + let const_stability_indirect = + find_attr!(attrs, AttributeKind::ConstStabilityIndirect); + return Some(ConstStability::unmarked(const_stability_indirect, parent_stab)); } } - let final_stab = self.index.stab_map.get(&def_id); + return None; + } - // # Const stability + let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id)); + let const_stability_indirect = find_attr!(attrs, AttributeKind::ConstStabilityIndirect); + let const_stab = attrs::find_attr!(attrs, AttributeKind::ConstStability { stability, span: _ } => *stability); + + // After checking the immediate attributes, get rid of the span and compute implied + // const stability: inherit feature gate from regular stability. + let mut const_stab = const_stab + .map(|const_stab| ConstStability::from_partial(const_stab, const_stability_indirect)); + + // If this is a const fn but not annotated with stability markers, see if we can inherit + // regular stability. + if let Some(fn_sig) = tcx.hir_node_by_def_id(def_id).fn_sig() + && fn_sig.header.is_const() + && const_stab.is_none() + // We only ever inherit unstable features. + && let Some(inherit_regular_stab) = tcx.lookup_stability(def_id) + && inherit_regular_stab.is_unstable() + { + const_stab = Some(ConstStability { + // We subject these implicitly-const functions to recursive const stability. + const_stable_indirect: true, + promotable: false, + level: inherit_regular_stab.level, + feature: inherit_regular_stab.feature, + }); + } - let const_stab = attrs::find_attr!(attrs, AttributeKind::ConstStability { stability, span } => (*stability, *span)); + if let Some(const_stab) = const_stab { + return Some(const_stab); + } - // If the current node is a function with const stability attributes (directly given or - // implied), check if the function/method is const or the parent impl block is const. - if let Some(fn_sig) = fn_sig - && !fn_sig.header.is_const() - && const_stab.is_some() - { - self.tcx.dcx().emit_err(errors::MissingConstErr { fn_sig_span: fn_sig.span }); + // `impl const Trait for Type` items forward their const stability to their immediate children. + // FIXME(const_trait_impl): how is this supposed to interact with `#[rustc_const_stable_indirect]`? + // Currently, once that is set, we do not inherit anything from the parent any more. + if inherit_const_stability(tcx, def_id) { + let parent = tcx.opt_local_parent(def_id)?; + let parent = tcx.lookup_const_stability(parent)?; + if parent.is_const_unstable() { + return Some(parent); } + } - // If this is marked const *stable*, it must also be regular-stable. - if let Some((const_stab, const_span)) = const_stab - && let Some(fn_sig) = fn_sig - && const_stab.is_const_stable() - && !stab.is_some_and(|s| s.is_stable()) - { - self.tcx - .dcx() - .emit_err(errors::ConstStableNotStable { fn_sig_span: fn_sig.span, const_span }); - } + None +} - // Stable *language* features shouldn't be used as unstable library features. - // (Not doing this for stable library features is checked by tidy.) - if let Some(( - PartialConstStability { level: StabilityLevel::Unstable { .. }, feature, .. }, - const_span, - )) = const_stab - { - if ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some() { - self.tcx.dcx().emit_err(errors::UnstableAttrForAlreadyStableFeature { - span: const_span, - item_sp, - }); - } - } +/// A private tree-walker for producing an `Index`. +struct Annotator<'tcx> { + tcx: TyCtxt<'tcx>, + implications: UnordMap<Symbol, Symbol>, +} - if let Some((stab, span)) = &const_stab - && stab.is_const_stable() - && const_stability_indirect - { - self.tcx.dcx().emit_err(errors::RustcConstStableIndirectPairing { span: *span }); +impl<'tcx> Annotator<'tcx> { + /// Determine the stability for a node based on its attributes and inherited stability. The + /// stability is recorded in the index and used as the parent. If the node is a function, + /// `fn_sig` is its signature. + #[instrument(level = "trace", skip(self))] + fn annotate(&mut self, def_id: LocalDefId) { + if !self.tcx.features().staged_api() { + return; } - // After checking the immediate attributes, get rid of the span and compute implied - // const stability: inherit feature gate from regular stability. - let mut const_stab = const_stab - .map(|(stab, _span)| ConstStability::from_partial(stab, const_stability_indirect)); - - // If this is a const fn but not annotated with stability markers, see if we can inherit regular stability. - if fn_sig.is_some_and(|s| s.header.is_const()) && const_stab.is_none() && - // We only ever inherit unstable features. - let Some(inherit_regular_stab) = - final_stab.filter(|s| s.is_unstable()) + if let Some(stability) = self.tcx.lookup_stability(def_id) + && let StabilityLevel::Unstable { implied_by: Some(implied_by), .. } = stability.level { - const_stab = Some(ConstStability { - // We subject these implicitly-const functions to recursive const stability. - const_stable_indirect: true, - promotable: false, - level: inherit_regular_stab.level, - feature: inherit_regular_stab.feature, - }); + self.implications.insert(implied_by, stability.feature); } - // Now that everything is computed, insert it into the table. - const_stab.inspect(|const_stab| { - self.index.const_stab_map.insert(def_id, *const_stab); - }); - - if let Some(ConstStability { - level: StabilityLevel::Unstable { implied_by: Some(implied_by), .. }, - feature, - .. - }) = const_stab + if let Some(stability) = self.tcx.lookup_const_stability(def_id) + && let StabilityLevel::Unstable { implied_by: Some(implied_by), .. } = stability.level { - self.index.implications.insert(implied_by, feature); - } - - // `impl const Trait for Type` items forward their const stability to their - // immediate children. - // FIXME(const_trait_impl): how is this supposed to interact with `#[rustc_const_stable_indirect]`? - // Currently, once that is set, we do not inherit anything from the parent any more. - if const_stab.is_none() { - debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab); - if let Some(parent) = self.parent_const_stab { - if parent.is_const_unstable() { - self.index.const_stab_map.insert(def_id, parent); - } - } - } - - self.recurse_with_stability_attrs( - depr.map(|(d, _)| DeprecationEntry::local(d, def_id)), - stab, - inherit_const_stability.yes().then_some(const_stab).flatten(), - visit_children, - ); - } - - fn recurse_with_stability_attrs( - &mut self, - depr: Option<DeprecationEntry>, - stab: Option<Stability>, - const_stab: Option<ConstStability>, - f: impl FnOnce(&mut Self), - ) { - // These will be `Some` if this item changes the corresponding stability attribute. - let mut replaced_parent_depr = None; - let mut replaced_parent_stab = None; - let mut replaced_parent_const_stab = None; - - if let Some(depr) = depr { - replaced_parent_depr = Some(replace(&mut self.parent_depr, Some(depr))); - } - if let Some(stab) = stab { - replaced_parent_stab = Some(replace(&mut self.parent_stab, Some(stab))); - } - if let Some(const_stab) = const_stab { - replaced_parent_const_stab = - Some(replace(&mut self.parent_const_stab, Some(const_stab))); - } - - f(self); - - if let Some(orig_parent_depr) = replaced_parent_depr { - self.parent_depr = orig_parent_depr; - } - if let Some(orig_parent_stab) = replaced_parent_stab { - self.parent_stab = orig_parent_stab; - } - if let Some(orig_parent_const_stab) = replaced_parent_const_stab { - self.parent_const_stab = orig_parent_const_stab; + self.implications.insert(implied_by, stability.feature); } } } -impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { +impl<'tcx> Visitor<'tcx> for Annotator<'tcx> { /// Because stability levels are scoped lexically, we want to walk /// nested items in the context of the outer item, so enable /// deep-walking. @@ -410,184 +308,51 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { } fn visit_item(&mut self, i: &'tcx Item<'tcx>) { - let orig_in_trait_impl = self.in_trait_impl; - let mut kind = AnnotationKind::Required; - let mut const_stab_inherit = InheritConstStability::No; - let mut fn_sig = None; - match i.kind { - // Inherent impls and foreign modules serve only as containers for other items, - // they don't have their own stability. They still can be annotated as unstable - // and propagate this instability to children, but this annotation is completely - // optional. They inherit stability from their parents when unannotated. - hir::ItemKind::Impl(hir::Impl { of_trait: None, .. }) - | hir::ItemKind::ForeignMod { .. } => { - self.in_trait_impl = false; - kind = AnnotationKind::Container; - } - hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => { - self.in_trait_impl = true; - kind = AnnotationKind::DeprecationProhibited; - const_stab_inherit = InheritConstStability::Yes; - } hir::ItemKind::Struct(_, _, ref sd) => { if let Some(ctor_def_id) = sd.ctor_def_id() { - self.annotate( - ctor_def_id, - i.span, - None, - AnnotationKind::Required, - InheritDeprecation::Yes, - InheritConstStability::No, - InheritStability::Yes, - |_| {}, - ) + self.annotate(ctor_def_id); } } - hir::ItemKind::Fn { sig: ref item_fn_sig, .. } => { - fn_sig = Some(item_fn_sig); - } _ => {} } - self.annotate( - i.owner_id.def_id, - i.span, - fn_sig, - kind, - InheritDeprecation::Yes, - const_stab_inherit, - InheritStability::No, - |v| intravisit::walk_item(v, i), - ); - self.in_trait_impl = orig_in_trait_impl; + self.annotate(i.owner_id.def_id); + intravisit::walk_item(self, i) } fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) { - let fn_sig = match ti.kind { - hir::TraitItemKind::Fn(ref fn_sig, _) => Some(fn_sig), - _ => None, - }; - - self.annotate( - ti.owner_id.def_id, - ti.span, - fn_sig, - AnnotationKind::Required, - InheritDeprecation::Yes, - InheritConstStability::No, - InheritStability::No, - |v| { - intravisit::walk_trait_item(v, ti); - }, - ); + self.annotate(ti.owner_id.def_id); + intravisit::walk_trait_item(self, ti); } fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) { - let kind = - if self.in_trait_impl { AnnotationKind::Prohibited } else { AnnotationKind::Required }; - - let fn_sig = match ii.kind { - hir::ImplItemKind::Fn(ref fn_sig, _) => Some(fn_sig), - _ => None, - }; - - self.annotate( - ii.owner_id.def_id, - ii.span, - fn_sig, - kind, - InheritDeprecation::Yes, - InheritConstStability::No, - InheritStability::No, - |v| { - intravisit::walk_impl_item(v, ii); - }, - ); + self.annotate(ii.owner_id.def_id); + intravisit::walk_impl_item(self, ii); } fn visit_variant(&mut self, var: &'tcx Variant<'tcx>) { - self.annotate( - var.def_id, - var.span, - None, - AnnotationKind::Required, - InheritDeprecation::Yes, - InheritConstStability::No, - InheritStability::Yes, - |v| { - if let Some(ctor_def_id) = var.data.ctor_def_id() { - v.annotate( - ctor_def_id, - var.span, - None, - AnnotationKind::Required, - InheritDeprecation::Yes, - InheritConstStability::No, - InheritStability::Yes, - |_| {}, - ); - } + self.annotate(var.def_id); + if let Some(ctor_def_id) = var.data.ctor_def_id() { + self.annotate(ctor_def_id); + } - intravisit::walk_variant(v, var) - }, - ) + intravisit::walk_variant(self, var) } fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) { - self.annotate( - s.def_id, - s.span, - None, - AnnotationKind::Required, - InheritDeprecation::Yes, - InheritConstStability::No, - InheritStability::Yes, - |v| { - intravisit::walk_field_def(v, s); - }, - ); + self.annotate(s.def_id); + intravisit::walk_field_def(self, s); } fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) { - let fn_sig = match &i.kind { - rustc_hir::ForeignItemKind::Fn(fn_sig, ..) => Some(fn_sig), - _ => None, - }; - self.annotate( - i.owner_id.def_id, - i.span, - fn_sig, - AnnotationKind::Required, - InheritDeprecation::Yes, - InheritConstStability::No, - InheritStability::No, - |v| { - intravisit::walk_foreign_item(v, i); - }, - ); + self.annotate(i.owner_id.def_id); + intravisit::walk_foreign_item(self, i); } fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { - let kind = match &p.kind { - // Allow stability attributes on default generic arguments. - hir::GenericParamKind::Type { default: Some(_), .. } - | hir::GenericParamKind::Const { default: Some(_), .. } => AnnotationKind::Container, - _ => AnnotationKind::Prohibited, - }; - - self.annotate( - p.def_id, - p.span, - None, - kind, - InheritDeprecation::No, - InheritConstStability::No, - InheritStability::No, - |v| { - intravisit::walk_generic_param(v, p); - }, - ); + self.annotate(p.def_id); + intravisit::walk_generic_param(self, p); } } @@ -597,18 +362,119 @@ struct MissingStabilityAnnotations<'tcx> { } impl<'tcx> MissingStabilityAnnotations<'tcx> { - fn check_missing_stability(&self, def_id: LocalDefId, span: Span) { - let stab = self.tcx.stability().local_stability(def_id); + /// Verify that deprecation and stability attributes make sense with one another. + #[instrument(level = "trace", skip(self))] + fn check_compatible_stability(&self, def_id: LocalDefId) { + if !self.tcx.features().staged_api() { + return; + } + + let depr = self.tcx.lookup_deprecation_entry(def_id); + let stab = self.tcx.lookup_stability(def_id); + let const_stab = self.tcx.lookup_const_stability(def_id); + + macro_rules! find_attr_span { + ($name:ident) => {{ + let attrs = self.tcx.hir_attrs(self.tcx.local_def_id_to_hir_id(def_id)); + attrs::find_attr!(attrs, AttributeKind::$name { span, .. } => *span) + }} + } + + if stab.is_none() + && depr.map_or(false, |d| d.attr.is_since_rustc_version()) + && let Some(span) = find_attr_span!(Deprecation) + { + self.tcx.dcx().emit_err(errors::DeprecatedAttribute { span }); + } + + if let Some(stab) = stab { + // Error if prohibited, or can't inherit anything from a container. + let kind = annotation_kind(self.tcx, def_id); + if kind == AnnotationKind::Prohibited + || (kind == AnnotationKind::Container && stab.level.is_stable() && depr.is_some()) + { + if let Some(span) = find_attr_span!(Stability) { + let item_sp = self.tcx.def_span(def_id); + self.tcx.dcx().emit_err(errors::UselessStability { span, item_sp }); + } + } + + // Check if deprecated_since < stable_since. If it is, + // this is *almost surely* an accident. + if let Some(depr) = depr + && let DeprecatedSince::RustcVersion(dep_since) = depr.attr.since + && let attrs::StabilityLevel::Stable { since: stab_since, .. } = stab.level + && let Some(span) = find_attr_span!(Stability) + { + let item_sp = self.tcx.def_span(def_id); + match stab_since { + StableSince::Current => { + self.tcx + .dcx() + .emit_err(errors::CannotStabilizeDeprecated { span, item_sp }); + } + StableSince::Version(stab_since) => { + if dep_since < stab_since { + self.tcx + .dcx() + .emit_err(errors::CannotStabilizeDeprecated { span, item_sp }); + } + } + StableSince::Err(_) => { + // An error already reported. Assume the unparseable stabilization + // version is older than the deprecation version. + } + } + } + } + + // If the current node is a function with const stability attributes (directly given or + // implied), check if the function/method is const or the parent impl block is const. + let fn_sig = self.tcx.hir_node_by_def_id(def_id).fn_sig(); + if let Some(fn_sig) = fn_sig + && !fn_sig.header.is_const() + && const_stab.is_some() + && find_attr_span!(ConstStability).is_some() + { + self.tcx.dcx().emit_err(errors::MissingConstErr { fn_sig_span: fn_sig.span }); + } + + // If this is marked const *stable*, it must also be regular-stable. + if let Some(const_stab) = const_stab + && let Some(fn_sig) = fn_sig + && const_stab.is_const_stable() + && !stab.is_some_and(|s| s.is_stable()) + && let Some(const_span) = find_attr_span!(ConstStability) + { + self.tcx + .dcx() + .emit_err(errors::ConstStableNotStable { fn_sig_span: fn_sig.span, const_span }); + } + + if let Some(stab) = &const_stab + && stab.is_const_stable() + && stab.const_stable_indirect + && let Some(span) = find_attr_span!(ConstStability) + { + self.tcx.dcx().emit_err(errors::RustcConstStableIndirectPairing { span }); + } + } + + #[instrument(level = "debug", skip(self))] + fn check_missing_stability(&self, def_id: LocalDefId) { + let stab = self.tcx.lookup_stability(def_id); + self.tcx.ensure_ok().lookup_const_stability(def_id); if !self.tcx.sess.is_test_crate() && stab.is_none() && self.effective_visibilities.is_reachable(def_id) { let descr = self.tcx.def_descr(def_id.to_def_id()); + let span = self.tcx.def_span(def_id); self.tcx.dcx().emit_err(errors::MissingStabilityAttr { span, descr }); } } - fn check_missing_const_stability(&self, def_id: LocalDefId, span: Span) { + fn check_missing_const_stability(&self, def_id: LocalDefId) { let is_const = self.tcx.is_const_fn(def_id.to_def_id()) || (self.tcx.def_kind(def_id.to_def_id()) == DefKind::Trait && self.tcx.is_const_trait(def_id.to_def_id())); @@ -618,6 +484,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { && self.effective_visibilities.is_reachable(def_id) && self.tcx.lookup_const_stability(def_id).is_none() { + let span = self.tcx.def_span(def_id); let descr = self.tcx.def_descr(def_id.to_def_id()); self.tcx.dcx().emit_err(errors::MissingConstStabAttr { span, descr }); } @@ -632,6 +499,8 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { } fn visit_item(&mut self, i: &'tcx Item<'tcx>) { + self.check_compatible_stability(i.owner_id.def_id); + // Inherent impls and foreign modules serve only as containers for other items, // they don't have their own stability. They still can be annotated as unstable // and propagate this instability to children, but this annotation is completely @@ -641,119 +510,97 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { hir::ItemKind::Impl(hir::Impl { of_trait: None, .. }) | hir::ItemKind::ForeignMod { .. } ) { - self.check_missing_stability(i.owner_id.def_id, i.span); + self.check_missing_stability(i.owner_id.def_id); } // Ensure stable `const fn` have a const stability attribute. - self.check_missing_const_stability(i.owner_id.def_id, i.span); + self.check_missing_const_stability(i.owner_id.def_id); intravisit::walk_item(self, i) } fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) { - self.check_missing_stability(ti.owner_id.def_id, ti.span); + self.check_compatible_stability(ti.owner_id.def_id); + self.check_missing_stability(ti.owner_id.def_id); intravisit::walk_trait_item(self, ti); } fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) { + self.check_compatible_stability(ii.owner_id.def_id); let impl_def_id = self.tcx.hir_get_parent_item(ii.hir_id()); if self.tcx.impl_trait_ref(impl_def_id).is_none() { - self.check_missing_stability(ii.owner_id.def_id, ii.span); - self.check_missing_const_stability(ii.owner_id.def_id, ii.span); + self.check_missing_stability(ii.owner_id.def_id); + self.check_missing_const_stability(ii.owner_id.def_id); } intravisit::walk_impl_item(self, ii); } fn visit_variant(&mut self, var: &'tcx Variant<'tcx>) { - self.check_missing_stability(var.def_id, var.span); + self.check_compatible_stability(var.def_id); + self.check_missing_stability(var.def_id); if let Some(ctor_def_id) = var.data.ctor_def_id() { - self.check_missing_stability(ctor_def_id, var.span); + self.check_missing_stability(ctor_def_id); } intravisit::walk_variant(self, var); } fn visit_field_def(&mut self, s: &'tcx FieldDef<'tcx>) { - self.check_missing_stability(s.def_id, s.span); + self.check_compatible_stability(s.def_id); + self.check_missing_stability(s.def_id); intravisit::walk_field_def(self, s); } fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) { - self.check_missing_stability(i.owner_id.def_id, i.span); + self.check_compatible_stability(i.owner_id.def_id); + self.check_missing_stability(i.owner_id.def_id); intravisit::walk_foreign_item(self, i); } - // Note that we don't need to `check_missing_stability` for default generic parameters, - // as we assume that any default generic parameters without attributes are automatically - // stable (assuming they have not inherited instability from their parent). -} -fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index { - let mut index = Index { - stab_map: Default::default(), - const_stab_map: Default::default(), - default_body_stab_map: Default::default(), - depr_map: Default::default(), - implications: Default::default(), - }; - - { - let mut annotator = Annotator { - tcx, - index: &mut index, - parent_stab: None, - parent_const_stab: None, - parent_depr: None, - in_trait_impl: false, - }; - - // If the `-Z force-unstable-if-unmarked` flag is passed then we provide - // a parent stability annotation which indicates that this is private - // with the `rustc_private` feature. This is intended for use when - // compiling `librustc_*` crates themselves so we can leverage crates.io - // while maintaining the invariant that all sysroot crates are unstable - // by default and are unable to be used. - if tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { - let stability = Stability { - level: attrs::StabilityLevel::Unstable { - reason: UnstableReason::Default, - issue: NonZero::new(27812), - is_soft: false, - implied_by: None, - old_name: None, - }, - feature: sym::rustc_private, - }; - annotator.parent_stab = Some(stability); - } - - annotator.annotate( - CRATE_DEF_ID, - tcx.hir_span(CRATE_HIR_ID), - None, - AnnotationKind::Required, - InheritDeprecation::Yes, - InheritConstStability::No, - InheritStability::No, - |v| tcx.hir_walk_toplevel_module(v), - ); + fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { + self.check_compatible_stability(p.def_id); + // Note that we don't need to `check_missing_stability` for default generic parameters, + // as we assume that any default generic parameters without attributes are automatically + // stable (assuming they have not inherited instability from their parent). + intravisit::walk_generic_param(self, p); } - index +} + +fn stability_implications(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> UnordMap<Symbol, Symbol> { + let mut annotator = Annotator { tcx, implications: Default::default() }; + annotator.annotate(CRATE_DEF_ID); + tcx.hir_walk_toplevel_module(&mut annotator); + annotator.implications } /// Cross-references the feature names of unstable APIs with enabled /// features and possibly prints errors. fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { tcx.hir_visit_item_likes_in_module(module_def_id, &mut Checker { tcx }); + + let is_staged_api = + tcx.sess.opts.unstable_opts.force_unstable_if_unmarked || tcx.features().staged_api(); + if is_staged_api { + let effective_visibilities = &tcx.effective_visibilities(()); + let mut missing = MissingStabilityAnnotations { tcx, effective_visibilities }; + if module_def_id.is_top_level_module() { + missing.check_missing_stability(CRATE_DEF_ID); + } + tcx.hir_visit_item_likes_in_module(module_def_id, &mut missing); + } + + if module_def_id.is_top_level_module() { + check_unused_or_stable_features(tcx) + } } pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { check_mod_unstable_api_usage, - stability_index, - stability_implications: |tcx, _| tcx.stability().implications.clone(), - lookup_stability: |tcx, id| tcx.stability().local_stability(id), - lookup_const_stability: |tcx, id| tcx.stability().local_const_stability(id), - lookup_default_body_stability: |tcx, id| tcx.stability().local_default_body_stability(id), - lookup_deprecation_entry: |tcx, id| tcx.stability().local_deprecation_entry(id), + stability_implications, + lookup_stability, + lookup_const_stability, + lookup_default_body_stability, + lookup_deprecation_entry, ..*providers }; } @@ -1058,7 +905,7 @@ fn is_unstable_reexport(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { }; let def_id = owner.def_id; - let Some(stab) = tcx.stability().local_stability(def_id) else { + let Some(stab) = tcx.lookup_stability(def_id) else { return false; }; @@ -1127,16 +974,9 @@ impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> { /// Given the list of enabled features that were not language features (i.e., that /// were expected to be library features), and the list of features used from /// libraries, identify activated features that don't exist and error about them. +// This is `pub` for rustdoc. rustc should call it through `check_mod_unstable_api_usage`. pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { - let is_staged_api = - tcx.sess.opts.unstable_opts.force_unstable_if_unmarked || tcx.features().staged_api(); - if is_staged_api { - let effective_visibilities = &tcx.effective_visibilities(()); - let mut missing = MissingStabilityAnnotations { tcx, effective_visibilities }; - missing.check_missing_stability(CRATE_DEF_ID, tcx.hir_span(CRATE_HIR_ID)); - tcx.hir_walk_toplevel_module(&mut missing); - tcx.hir_visit_all_item_likes_in_crate(&mut missing); - } + let _prof_timer = tcx.sess.timer("unused_lib_feature_checking"); let enabled_lang_features = tcx.features().enabled_lang_features(); let mut lang_features = UnordSet::default(); |
