diff options
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_attr/src/builtin.rs | 68 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/check_consts/check.rs | 45 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/check_consts/mod.rs | 20 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/lib.rs | 19 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/base.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/messages.ftl | 2 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/errors.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/lib.rs | 203 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/pass_manager.rs | 34 | ||||
| -rw-r--r-- | compiler/rustc_passes/src/stability.rs | 199 | ||||
| -rw-r--r-- | compiler/rustc_trait_selection/src/errors.rs | 10 |
11 files changed, 384 insertions, 226 deletions
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 2753ac529d1..94f9727eb7f 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -16,9 +16,9 @@ use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::UNEXPECTED_CFGS; use rustc_session::parse::feature_err; use rustc_session::{RustcVersion, Session}; +use rustc_span::Span; use rustc_span::hygiene::Transparency; use rustc_span::symbol::{Symbol, kw, sym}; -use rustc_span::{DUMMY_SP, Span}; use crate::fluent_generated; use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause}; @@ -92,9 +92,7 @@ impl Stability { #[derive(HashStable_Generic)] pub struct ConstStability { pub level: StabilityLevel, - /// This can be `None` for functions that do not have an explicit const feature. - /// We still track them for recursive const stability checks. - pub feature: Option<Symbol>, + pub feature: Symbol, /// This is true iff the `const_stable_indirect` attribute is present. pub const_stable_indirect: bool, /// whether the function has a `#[rustc_promotable]` attribute @@ -272,22 +270,19 @@ pub fn find_stability( /// Collects stability info from `rustc_const_stable`/`rustc_const_unstable`/`rustc_promotable` /// attributes in `attrs`. Returns `None` if no stability attributes are found. -/// -/// `is_const_fn` indicates whether this is a function marked as `const`. pub fn find_const_stability( sess: &Session, attrs: &[Attribute], item_sp: Span, - is_const_fn: bool, ) -> Option<(ConstStability, Span)> { let mut const_stab: Option<(ConstStability, Span)> = None; let mut promotable = false; - let mut const_stable_indirect = None; + let mut const_stable_indirect = false; for attr in attrs { match attr.name_or_empty() { sym::rustc_promotable => promotable = true, - sym::rustc_const_stable_indirect => const_stable_indirect = Some(attr.span), + sym::rustc_const_stable_indirect => const_stable_indirect = true, sym::rustc_const_unstable => { if const_stab.is_some() { sess.dcx() @@ -299,7 +294,7 @@ pub fn find_const_stability( const_stab = Some(( ConstStability { level, - feature: Some(feature), + feature, const_stable_indirect: false, promotable: false, }, @@ -317,7 +312,7 @@ pub fn find_const_stability( const_stab = Some(( ConstStability { level, - feature: Some(feature), + feature, const_stable_indirect: false, promotable: false, }, @@ -340,7 +335,7 @@ pub fn find_const_stability( } } } - if const_stable_indirect.is_some() { + if const_stable_indirect { match &mut const_stab { Some((stab, _)) => { if stab.is_const_unstable() { @@ -351,36 +346,37 @@ pub fn find_const_stability( }) } } - _ => {} + _ => { + // This function has no const stability attribute, but has `const_stable_indirect`. + // We ignore that; unmarked functions are subject to recursive const stability + // checks by default so we do carry out the user's intent. + } } } - // Make sure if `const_stable_indirect` is present, that is recorded. Also make sure all `const - // fn` get *some* marker, since we are a staged_api crate and therefore will do recursive const - // stability checks for them. We need to do this because the default for whether an unmarked - // function enforces recursive stability differs between staged-api crates and force-unmarked - // crates: in force-unmarked crates, only functions *explicitly* marked `const_stable_indirect` - // enforce recursive stability. Therefore when `lookup_const_stability` is `None`, we have to - // assume the function does not have recursive stability. All functions that *do* have recursive - // stability must explicitly record this, and so that's what we do for all `const fn` in a - // staged_api crate. - if (is_const_fn || const_stable_indirect.is_some()) && const_stab.is_none() { - let c = ConstStability { - feature: None, - const_stable_indirect: const_stable_indirect.is_some(), - promotable: false, - level: StabilityLevel::Unstable { - reason: UnstableReason::Default, - issue: None, - is_soft: false, - implied_by: None, - }, - }; - const_stab = Some((c, const_stable_indirect.unwrap_or(DUMMY_SP))); - } const_stab } +/// Calculates the const stability for a const function in a `-Zforce-unstable-if-unmarked` crate +/// without the `staged_api` feature. +pub fn unmarked_crate_const_stab( + _sess: &Session, + attrs: &[Attribute], + regular_stab: Stability, +) -> ConstStability { + assert!(regular_stab.level.is_unstable()); + // The only attribute that matters here is `rustc_const_stable_indirect`. + // We enforce recursive const stability rules for those functions. + let const_stable_indirect = + attrs.iter().any(|a| a.name_or_empty() == sym::rustc_const_stable_indirect); + ConstStability { + feature: regular_stab.feature, + const_stable_indirect, + promotable: false, + level: regular_stab.level, + } +} + /// Collects stability info from `rustc_default_body_unstable` attributes in `attrs`. /// Returns `None` if no stability attributes are found. pub fn find_body_stability( diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 8cd0ecb3e4e..c3efca28c68 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -3,6 +3,7 @@ use std::assert_matches::assert_matches; use std::borrow::Cow; use std::mem; +use std::num::NonZero; use std::ops::Deref; use rustc_attr::{ConstStability, StabilityLevel}; @@ -709,6 +710,13 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // Intrinsics are language primitives, not regular calls, so treat them separately. if let Some(intrinsic) = tcx.intrinsic(callee) { + if !tcx.is_const_fn(callee) { + // Non-const intrinsic. + self.check_op(ops::IntrinsicNonConst { name: intrinsic.name }); + // If we allowed this, we're in miri-unleashed mode, so we might + // as well skip the remaining checks. + return; + } // We use `intrinsic.const_stable` to determine if this can be safely exposed to // stable code, rather than `const_stable_indirect`. This is to make // `#[rustc_const_stable_indirect]` an attribute that is always safe to add. @@ -716,17 +724,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // fallback body is safe to expose on stable. let is_const_stable = intrinsic.const_stable || (!intrinsic.must_be_overridden - && tcx.is_const_fn(callee) && is_safe_to_expose_on_stable_const_fn(tcx, callee)); match tcx.lookup_const_stability(callee) { None => { - // Non-const intrinsic. - self.check_op(ops::IntrinsicNonConst { name: intrinsic.name }); - } - Some(ConstStability { feature: None, .. }) => { - // Intrinsic does not need a separate feature gate (we rely on the - // regular stability checker). However, we have to worry about recursive - // const stability. + // This doesn't need a separate const-stability check -- const-stability equals + // regular stability, and regular stability is checked separately. + // However, we *do* have to worry about *recursive* const stability. if !is_const_stable && self.enforce_recursive_const_stability() { self.dcx().emit_err(errors::UnmarkedIntrinsicExposed { span: self.span, @@ -735,8 +738,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } } Some(ConstStability { - feature: Some(feature), level: StabilityLevel::Unstable { .. }, + feature, .. }) => { self.check_op(ops::IntrinsicUnstable { @@ -773,7 +776,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => { // All good. } - None | Some(ConstStability { feature: None, .. }) => { + None => { // This doesn't need a separate const-stability check -- const-stability equals // regular stability, and regular stability is checked separately. // However, we *do* have to worry about *recursive* const stability. @@ -787,8 +790,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } } Some(ConstStability { - feature: Some(feature), - level: StabilityLevel::Unstable { implied_by: implied_feature, .. }, + level: StabilityLevel::Unstable { implied_by: implied_feature, issue, .. }, + feature, .. }) => { // An unstable const fn with a feature gate. @@ -810,7 +813,23 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // to allow this. let feature_enabled = callee.is_local() || tcx.features().enabled(feature) - || implied_feature.is_some_and(|f| tcx.features().enabled(f)); + || implied_feature.is_some_and(|f| tcx.features().enabled(f)) + || { + // When we're compiling the compiler itself we may pull in + // crates from crates.io, but those crates may depend on other + // crates also pulled in from crates.io. We want to ideally be + // able to compile everything without requiring upstream + // modifications, so in the case that this looks like a + // `rustc_private` crate (e.g., a compiler crate) and we also have + // the `-Z force-unstable-if-unmarked` flag present (we're + // compiling a compiler crate), then let this missing feature + // annotation slide. + // This matches what we do in `eval_stability_allow_unstable` for + // regular stability. + feature == sym::rustc_private + && issue == NonZero::new(27812) + && self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked + }; // We do *not* honor this if we are in the "danger zone": we have to enforce // recursive const-stability and the callee is not safe-to-expose. In that // case we need `check_op` to do the check. diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index dcdaafaecc2..ebdd55a4f70 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -53,10 +53,11 @@ impl<'mir, 'tcx> ConstCx<'mir, 'tcx> { } pub fn enforce_recursive_const_stability(&self) -> bool { - // We can skip this if `staged_api` is not enabled, since in such crates - // `lookup_const_stability` will always be `None`. + // We can skip this if neither `staged_api` nor `-Zforce-unstable-if-unmarked` are enabled, + // since in such crates `lookup_const_stability` will always be `None`. self.const_kind == Some(hir::ConstContext::ConstFn) - && self.tcx.features().staged_api() + && (self.tcx.features().staged_api() + || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked) && is_safe_to_expose_on_stable_const_fn(self.tcx, self.def_id().to_def_id()) } @@ -109,14 +110,15 @@ pub fn is_safe_to_expose_on_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> b match tcx.lookup_const_stability(def_id) { None => { - // Only marked functions can be trusted. Note that this may be a function in a - // non-staged-API crate where no recursive checks were done! - false + // In a `staged_api` crate, we do enforce recursive const stability for all unmarked + // functions, so we can trust local functions. But in another crate we don't know which + // rules were applied, so we can't trust that. + def_id.is_local() && tcx.features().staged_api() } Some(stab) => { - // We consider things safe-to-expose if they are stable, if they don't have any explicit - // const stability attribute, or if they are marked as `const_stable_indirect`. - stab.is_const_stable() || stab.feature.is_none() || stab.const_stable_indirect + // We consider things safe-to-expose if they are stable or if they are marked as + // `const_stable_indirect`. + stab.is_const_stable() || stab.const_stable_indirect } } } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 94365a89adc..98200c367f9 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -623,12 +623,25 @@ impl Drop for DiagCtxtInner { self.flush_delayed() } + // Sanity check: did we use some of the expensive `trimmed_def_paths` functions + // unexpectedly, that is, without producing diagnostics? If so, for debugging purposes, we + // suggest where this happened and how to avoid it. if !self.has_printed && !self.suppressed_expected_diag && !std::thread::panicking() { if let Some(backtrace) = &self.must_produce_diag { + let suggestion = match backtrace.status() { + BacktraceStatus::Disabled => String::from( + "Backtraces are currently disabled: set `RUST_BACKTRACE=1` and re-run \ + to see where it happened.", + ), + BacktraceStatus::Captured => format!( + "This happened in the following `must_produce_diag` call's backtrace:\n\ + {backtrace}", + ), + _ => String::from("(impossible to capture backtrace where this happened)"), + }; panic!( - "must_produce_diag: `trimmed_def_paths` called but no diagnostics emitted; \ - `with_no_trimmed_paths` for debugging. \ - called at: {backtrace}" + "`trimmed_def_paths` called, diagnostics were expected but none were emitted. \ + Use `with_no_trimmed_paths` for debugging. {suggestion}" ); } } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 7e4bc508e5c..bed500c3032 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -866,9 +866,7 @@ impl SyntaxExtension { }) .unwrap_or_else(|| (None, helper_attrs)); let stability = attr::find_stability(sess, attrs, span); - // We set `is_const_fn` false to avoid getting any implicit const stability. - let const_stability = - attr::find_const_stability(sess, attrs, span, /* is_const_fn */ false); + let const_stability = attr::find_const_stability(sess, attrs, span); let body_stability = attr::find_body_stability(sess, attrs); if let Some((_, sp)) = const_stability { sess.dcx().emit_err(errors::MacroConstStability { diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl index c8992b8b834..9bbfae17fd9 100644 --- a/compiler/rustc_mir_transform/messages.ftl +++ b/compiler/rustc_mir_transform/messages.ftl @@ -34,3 +34,5 @@ mir_transform_undefined_transmute = pointers cannot be transmuted to integers du .note = at compile-time, pointers do not have an integer value .note2 = avoiding this restriction via `union` or raw pointers leads to compile-time undefined behavior .help = for more information, see https://doc.rust-lang.org/std/mem/fn.transmute.html + +mir_transform_unknown_pass_name = MIR pass `{$name}` is unknown and will be ignored diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 8b309147c64..2d9eeddea2e 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -38,6 +38,12 @@ pub(crate) struct UnalignedPackedRef { pub span: Span, } +#[derive(Diagnostic)] +#[diag(mir_transform_unknown_pass_name)] +pub(crate) struct UnknownPassName<'a> { + pub(crate) name: &'a str, +} + pub(crate) struct AssertLint<P> { pub span: Span, pub assert_kind: AssertKind<P>, diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index e18f66ac0a3..d2d5facbbdc 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -40,77 +40,158 @@ use tracing::{debug, trace}; #[macro_use] mod pass_manager; +use std::sync::LazyLock; + use pass_manager::{self as pm, Lint, MirLint, MirPass, WithMinOptLevel}; -mod abort_unwinding_calls; -mod add_call_guards; -mod add_moves_for_packed_drops; -mod add_retag; -mod add_subtyping_projections; -mod check_alignment; -mod check_const_item_mutation; -mod check_packed_ref; -mod check_undefined_transmutes; -// This pass is public to allow external drivers to perform MIR cleanup -pub mod cleanup_post_borrowck; -mod copy_prop; -mod coroutine; mod cost_checker; -mod coverage; mod cross_crate_inline; -mod ctfe_limit; -mod dataflow_const_prop; -mod dead_store_elimination; mod deduce_param_attrs; -mod deduplicate_blocks; -mod deref_separator; -mod dest_prop; -pub mod dump_mir; -mod early_otherwise_branch; -mod elaborate_box_derefs; -mod elaborate_drops; mod errors; mod ffi_unwind_calls; -mod function_item_references; -mod gvn; -// Made public so that `mir_drops_elaborated_and_const_checked` can be overridden -// by custom rustc drivers, running all the steps by themselves. See #114628. -pub mod inline; -mod instsimplify; -mod jump_threading; -mod known_panics_lint; -mod large_enums; mod lint; -mod lower_intrinsics; -mod lower_slice_len; -mod match_branches; -mod mentioned_items; -mod multiple_return_terminators; -mod nrvo; -mod post_drop_elaboration; -mod prettify; -mod promote_consts; -mod ref_prop; -mod remove_noop_landing_pads; -mod remove_place_mention; -mod remove_storage_markers; -mod remove_uninit_drops; -mod remove_unneeded_drops; -mod remove_zsts; -mod required_consts; -mod reveal_all; -mod sanity_check; mod shim; mod ssa; -// This pass is public to allow external drivers to perform MIR cleanup -pub mod simplify; -mod simplify_branches; -mod simplify_comparison_integral; -mod single_use_consts; -mod sroa; -mod unreachable_enum_branching; -mod unreachable_prop; -mod validate; + +/// We import passes via this macro so that we can have a static list of pass names +/// (used to verify CLI arguments). It takes a list of modules, followed by the passes +/// declared within them. +/// ```ignore,macro-test +/// declare_passes! { +/// // Declare a single pass from the module `abort_unwinding_calls` +/// mod abort_unwinding_calls : AbortUnwindingCalls; +/// // When passes are grouped together as an enum, declare the two constituent passes +/// mod add_call_guards : AddCallGuards { +/// AllCallEdges, +/// CriticalCallEdges +/// }; +/// // Declares multiple pass groups, each containing their own constituent passes +/// mod simplify : SimplifyCfg { +/// Initial, +/// /* omitted */ +/// }, SimplifyLocals { +/// BeforeConstProp, +/// /* omitted */ +/// }; +/// } +/// ``` +macro_rules! declare_passes { + ( + $( + $vis:vis mod $mod_name:ident : $($pass_name:ident $( { $($ident:ident),* } )?),+ $(,)?; + )* + ) => { + $( + $vis mod $mod_name; + $( + // Make sure the type name is correct + #[allow(unused_imports)] + use $mod_name::$pass_name as _; + )+ + )* + + static PASS_NAMES: LazyLock<FxIndexSet<&str>> = LazyLock::new(|| [ + // Fake marker pass + "PreCodegen", + $( + $( + stringify!($pass_name), + $( + $( + $mod_name::$pass_name::$ident.name(), + )* + )? + )+ + )* + ].into_iter().collect()); + }; +} + +declare_passes! { + mod abort_unwinding_calls : AbortUnwindingCalls; + mod add_call_guards : AddCallGuards { AllCallEdges, CriticalCallEdges }; + mod add_moves_for_packed_drops : AddMovesForPackedDrops; + mod add_retag : AddRetag; + mod add_subtyping_projections : Subtyper; + mod check_alignment : CheckAlignment; + mod check_const_item_mutation : CheckConstItemMutation; + mod check_packed_ref : CheckPackedRef; + mod check_undefined_transmutes : CheckUndefinedTransmutes; + // This pass is public to allow external drivers to perform MIR cleanup + pub mod cleanup_post_borrowck : CleanupPostBorrowck; + + mod copy_prop : CopyProp; + mod coroutine : StateTransform; + mod coverage : InstrumentCoverage; + mod ctfe_limit : CtfeLimit; + mod dataflow_const_prop : DataflowConstProp; + mod dead_store_elimination : DeadStoreElimination { + Initial, + Final + }; + mod deduplicate_blocks : DeduplicateBlocks; + mod deref_separator : Derefer; + mod dest_prop : DestinationPropagation; + pub mod dump_mir : Marker; + mod early_otherwise_branch : EarlyOtherwiseBranch; + mod elaborate_box_derefs : ElaborateBoxDerefs; + mod elaborate_drops : ElaborateDrops; + mod function_item_references : FunctionItemReferences; + mod gvn : GVN; + // Made public so that `mir_drops_elaborated_and_const_checked` can be overridden + // by custom rustc drivers, running all the steps by themselves. See #114628. + pub mod inline : Inline; + mod instsimplify : InstSimplify { BeforeInline, AfterSimplifyCfg }; + mod jump_threading : JumpThreading; + mod known_panics_lint : KnownPanicsLint; + mod large_enums : EnumSizeOpt; + mod lower_intrinsics : LowerIntrinsics; + mod lower_slice_len : LowerSliceLenCalls; + mod match_branches : MatchBranchSimplification; + mod mentioned_items : MentionedItems; + mod multiple_return_terminators : MultipleReturnTerminators; + mod nrvo : RenameReturnPlace; + mod post_drop_elaboration : CheckLiveDrops; + mod prettify : ReorderBasicBlocks, ReorderLocals; + mod promote_consts : PromoteTemps; + mod ref_prop : ReferencePropagation; + mod remove_noop_landing_pads : RemoveNoopLandingPads; + mod remove_place_mention : RemovePlaceMention; + mod remove_storage_markers : RemoveStorageMarkers; + mod remove_uninit_drops : RemoveUninitDrops; + mod remove_unneeded_drops : RemoveUnneededDrops; + mod remove_zsts : RemoveZsts; + mod required_consts : RequiredConstsVisitor; + mod reveal_all : RevealAll; + mod sanity_check : SanityCheck; + // This pass is public to allow external drivers to perform MIR cleanup + pub mod simplify : + SimplifyCfg { + Initial, + PromoteConsts, + RemoveFalseEdges, + PostAnalysis, + PreOptimizations, + Final, + MakeShim, + AfterUnreachableEnumBranching + }, + SimplifyLocals { + BeforeConstProp, + AfterGVN, + Final + }; + mod simplify_branches : SimplifyConstCondition { + AfterConstProp, + Final + }; + mod simplify_comparison_integral : SimplifyComparisonIntegral; + mod single_use_consts : SingleUseConsts; + mod sroa : ScalarReplacementOfAggregates; + mod unreachable_enum_branching : UnreachableEnumBranching; + mod unreachable_prop : UnreachablePropagation; + mod validate : Validator; +} rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 29f8b4f6e4d..779e7f22101 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -1,24 +1,25 @@ use std::cell::RefCell; use std::collections::hash_map::Entry; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_middle::mir::{self, Body, MirPhase, RuntimePhase}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use tracing::trace; use crate::lint::lint_body; -use crate::validate; +use crate::{errors, validate}; thread_local! { - static PASS_NAMES: RefCell<FxHashMap<&'static str, &'static str>> = { + /// Maps MIR pass names to a snake case form to match profiling naming style + static PASS_TO_PROFILER_NAMES: RefCell<FxHashMap<&'static str, &'static str>> = { RefCell::new(FxHashMap::default()) }; } /// Converts a MIR pass name into a snake case form to match the profiling naming style. fn to_profiler_name(type_name: &'static str) -> &'static str { - PASS_NAMES.with(|names| match names.borrow_mut().entry(type_name) { + PASS_TO_PROFILER_NAMES.with(|names| match names.borrow_mut().entry(type_name) { Entry::Occupied(e) => *e.get(), Entry::Vacant(e) => { let snake_case: String = type_name @@ -198,6 +199,31 @@ fn run_passes_inner<'tcx>( let overridden_passes = &tcx.sess.opts.unstable_opts.mir_enable_passes; trace!(?overridden_passes); + let named_passes: FxIndexSet<_> = + overridden_passes.iter().map(|(name, _)| name.as_str()).collect(); + + for &name in named_passes.difference(&*crate::PASS_NAMES) { + tcx.dcx().emit_warn(errors::UnknownPassName { name }); + } + + // Verify that no passes are missing from the `declare_passes` invocation + #[cfg(debug_assertions)] + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] + { + let used_passes: FxIndexSet<_> = passes.iter().map(|p| p.name()).collect(); + + let undeclared = used_passes.difference(&*crate::PASS_NAMES).collect::<Vec<_>>(); + if let Some((name, rest)) = undeclared.split_first() { + let mut err = + tcx.dcx().struct_bug(format!("pass `{name}` is not declared in `PASS_NAMES`")); + for name in rest { + err.note(format!("pass `{name}` is also not declared in `PASS_NAMES`")); + } + err.emit(); + } + } + let prof_arg = tcx.sess.prof.enabled().then(|| format!("{:?}", body.source.def_id())); if !body.should_skip() { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index cd47c8ece60..4a793f1875e 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -16,7 +16,7 @@ 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}; -use rustc_hir::{Constness, FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant}; +use rustc_hir::{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; @@ -149,6 +149,11 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { 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()) { + let const_stab = + attr::unmarked_crate_const_stab(self.tcx.sess, attrs, stab); + self.index.const_stab_map.insert(def_id, const_stab); + } } } @@ -161,68 +166,11 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { return; } + // # Regular and body stability + let stab = attr::find_stability(self.tcx.sess, attrs, item_sp); - let const_stab = attr::find_const_stability( - self.tcx.sess, - attrs, - item_sp, - fn_sig.is_some_and(|s| s.header.is_const()), - ); let body_stab = attr::find_body_stability(self.tcx.sess, attrs); - // 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 }); - } - - // 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 }); - } - - // 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(( - ConstStability { level: Unstable { .. }, feature: Some(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, - }); - } - } - - let const_stab = const_stab.map(|(const_stab, _span)| { - self.index.const_stab_map.insert(def_id, const_stab); - const_stab - }); - - // `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); - } - } - } - if let Some((depr, span)) = &depr && depr.is_since_rustc_version() && stab.is_none() @@ -289,15 +237,6 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { self.index.implications.insert(implied_by, feature); } - if let Some(ConstStability { - level: Unstable { implied_by: Some(implied_by), .. }, - feature, - .. - }) = const_stab - { - self.index.implications.insert(implied_by, feature.unwrap()); - } - self.index.stab_map.insert(def_id, stab); stab }); @@ -311,6 +250,91 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } + let final_stab = self.index.stab_map.get(&def_id); + + // # Const stability + + let const_stab = attr::find_const_stability(self.tcx.sess, attrs, item_sp); + + // If the current node is a function with const stability attributes (directly given or + // implied), check if the function/method 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 }); + } + + // 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 }); + } + + // 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((ConstStability { level: 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, + }); + } + } + + // 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)| stab); + + // 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()) + { + 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, + }); + } + + // 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: Unstable { implied_by: Some(implied_by), .. }, + feature, + .. + }) = const_stab + { + 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, @@ -565,13 +589,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { } } - fn check_missing_or_wrong_const_stability(&self, def_id: LocalDefId, span: Span) { - // The visitor runs for "unstable-if-unmarked" crates, but we don't yet support - // that on the const side. - if !self.tcx.features().staged_api() { - return; - } - + fn check_missing_const_stability(&self, def_id: LocalDefId, span: Span) { // if the const impl is derived using the `derive_const` attribute, // then it would be "stable" at least for the impl. // We gate usages of it using `feature(const_trait_impl)` anyways @@ -582,12 +600,12 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { let is_const = self.tcx.is_const_fn(def_id.to_def_id()) || self.tcx.is_const_trait_impl(def_id.to_def_id()); - let is_stable = - self.tcx.lookup_stability(def_id).is_some_and(|stability| stability.level.is_stable()); - let missing_const_stability_attribute = - self.tcx.lookup_const_stability(def_id).is_none_or(|s| s.feature.is_none()); - if is_const && is_stable && missing_const_stability_attribute { + // Reachable const fn must have a stability attribute. + if is_const + && self.effective_visibilities.is_reachable(def_id) + && self.tcx.lookup_const_stability(def_id).is_none() + { let descr = self.tcx.def_descr(def_id.to_def_id()); self.tcx.dcx().emit_err(errors::MissingConstStabAttr { span, descr }); } @@ -615,7 +633,7 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { } // Ensure stable `const fn` have a const stability attribute. - self.check_missing_or_wrong_const_stability(i.owner_id.def_id, i.span); + self.check_missing_const_stability(i.owner_id.def_id, i.span); intravisit::walk_item(self, i) } @@ -629,7 +647,7 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { 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_or_wrong_const_stability(ii.owner_id.def_id, ii.span); + self.check_missing_const_stability(ii.owner_id.def_id, ii.span); } intravisit::walk_impl_item(self, ii); } @@ -760,23 +778,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // For implementations of traits, check the stability of each item // individually as it's possible to have a stable trait with unstable // items. - hir::ItemKind::Impl(hir::Impl { - constness, - of_trait: Some(ref t), - self_ty, - items, - .. - }) => { + hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref t), self_ty, items, .. }) => { let features = self.tcx.features(); if features.staged_api() { let attrs = self.tcx.hir().attrs(item.hir_id()); let stab = attr::find_stability(self.tcx.sess, attrs, item.span); - let const_stab = attr::find_const_stability( - self.tcx.sess, - attrs, - item.span, - matches!(constness, Constness::Const), - ); + let const_stab = attr::find_const_stability(self.tcx.sess, attrs, item.span); // If this impl block has an #[unstable] attribute, give an // error if all involved types and traits are stable, because diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 3e06d0807d8..afac6fc6004 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1907,10 +1907,18 @@ impl Subdiagnostic for AddPreciseCapturingForOvercapture { diag: &mut Diag<'_, G>, _f: &F, ) { + let applicability = if self.apit_spans.is_empty() { + Applicability::MachineApplicable + } else { + // If there are APIT that are converted to regular parameters, + // then this may make the API turbofishable in ways that were + // not intended. + Applicability::MaybeIncorrect + }; diag.multipart_suggestion_verbose( fluent::trait_selection_precise_capturing_overcaptures, self.suggs, - Applicability::MaybeIncorrect, + applicability, ); if !self.apit_spans.is_empty() { diag.span_note( |
