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
{
pub span: Span,
pub assert_kind: AssertKind
,
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> = 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> = {
+ /// Maps MIR pass names to a snake case form to match profiling naming style
+ static PASS_TO_PROFILER_NAMES: RefCell> = {
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::>();
+ 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/tests/ui/mir/enable_passes_validation.all_unknown.stderr b/tests/ui/mir/enable_passes_validation.all_unknown.stderr
new file mode 100644
index 00000000000..85a942c00ed
--- /dev/null
+++ b/tests/ui/mir/enable_passes_validation.all_unknown.stderr
@@ -0,0 +1,14 @@
+warning: MIR pass `ThisPass` is unknown and will be ignored
+
+warning: MIR pass `DoesNotExist` is unknown and will be ignored
+
+warning: MIR pass `ThisPass` is unknown and will be ignored
+ |
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+warning: MIR pass `DoesNotExist` is unknown and will be ignored
+ |
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+warning: 4 warnings emitted
+
diff --git a/tests/ui/mir/enable_passes_validation.empty.stderr b/tests/ui/mir/enable_passes_validation.empty.stderr
new file mode 100644
index 00000000000..0e922663acc
--- /dev/null
+++ b/tests/ui/mir/enable_passes_validation.empty.stderr
@@ -0,0 +1,2 @@
+error: incorrect value `` for unstable option `mir-enable-passes` - a comma-separated list of strings, with elements beginning with + or - was expected
+
diff --git a/tests/ui/mir/enable_passes_validation.mixed.stderr b/tests/ui/mir/enable_passes_validation.mixed.stderr
new file mode 100644
index 00000000000..5aace86abc0
--- /dev/null
+++ b/tests/ui/mir/enable_passes_validation.mixed.stderr
@@ -0,0 +1,8 @@
+warning: MIR pass `ThisPassDoesNotExist` is unknown and will be ignored
+
+warning: MIR pass `ThisPassDoesNotExist` is unknown and will be ignored
+ |
+ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/mir/enable_passes_validation.rs b/tests/ui/mir/enable_passes_validation.rs
new file mode 100644
index 00000000000..957e7d4d96d
--- /dev/null
+++ b/tests/ui/mir/enable_passes_validation.rs
@@ -0,0 +1,21 @@
+//@ revisions: empty unprefixed all_unknown all_known mixed
+
+//@[empty] compile-flags: -Zmir-enable-passes=
+//@[empty] error-pattern error: incorrect value `` for unstable option `mir-enable-passes` - a comma-separated list of strings, with elements beginning with + or - was expected
+
+//@[unprefixed] compile-flags: -Zmir-enable-passes=CheckAlignment
+//@[unprefixed] error-pattern error: incorrect value `CheckAlignment` for unstable option `mir-enable-passes` - a comma-separated list of strings, with elements beginning with + or - was expected
+
+//@[all_unknown] check-pass
+//@[all_unknown] compile-flags: -Zmir-enable-passes=+ThisPass,-DoesNotExist
+//@[all_unknown] error-pattern: warning: MIR pass `ThisPass` is unknown and will be ignored
+//@[all_unknown] error-pattern: warning: MIR pass `DoesNotExist` is unknown and will be ignored
+
+//@[all_known] check-pass
+//@[all_known] compile-flags: -Zmir-enable-passes=+CheckAlignment,+LowerIntrinsics
+
+//@[mixed] check-pass
+//@[mixed] compile-flags: -Zmir-enable-passes=+ThisPassDoesNotExist,+CheckAlignment
+//@[mixed] error-pattern: warning: MIR pass `ThisPassDoesNotExist` is unknown and will be ignored
+
+fn main() {}
diff --git a/tests/ui/mir/enable_passes_validation.unprefixed.stderr b/tests/ui/mir/enable_passes_validation.unprefixed.stderr
new file mode 100644
index 00000000000..697589448f4
--- /dev/null
+++ b/tests/ui/mir/enable_passes_validation.unprefixed.stderr
@@ -0,0 +1,2 @@
+error: incorrect value `CheckAlignment` for unstable option `mir-enable-passes` - a comma-separated list of strings, with elements beginning with + or - was expected
+