From 0e0d12ccb3546ddc2c5daf6215cc4cdad173d8db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Thu, 9 Apr 2026 13:44:43 +0200 Subject: [PATCH 1/6] introduce TypingModeEqWrapper and make TypingMode !Eq --- .../src/check_consts/qualifs.rs | 6 +- .../src/const_eval/eval_queries.rs | 16 +++- .../src/const_eval/valtrees.rs | 15 +++- .../src/interpret/eval_context.rs | 16 +++- .../src/infer/canonical/canonicalizer.rs | 3 +- compiler/rustc_infer/src/infer/mod.rs | 7 +- compiler/rustc_lint/src/context.rs | 13 ++- compiler/rustc_middle/src/mir/mod.rs | 4 +- compiler/rustc_middle/src/ty/mod.rs | 25 ++++-- compiler/rustc_middle/src/ty/sty.rs | 1 + .../rustc_mir_transform/src/elaborate_drop.rs | 11 ++- .../src/canonical/mod.rs | 7 +- .../src/solve/assembly/mod.rs | 13 ++- .../src/solve/search_graph.rs | 2 +- .../src/traits/effects.rs | 16 ++-- compiler/rustc_ty_utils/src/instance.rs | 2 +- compiler/rustc_type_ir/src/canonical.rs | 4 +- compiler/rustc_type_ir/src/infer_ctxt.rs | 90 ++++++++++++++++++- 18 files changed, 205 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 1fbd0cd234057..d068b4bc84cf4 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -9,7 +9,7 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::LangItem; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::*; -use rustc_middle::ty::{self, AdtDef, Ty}; +use rustc_middle::ty::{self, AdtDef, Ty, TypingModeEqWrapper}; use rustc_middle::{bug, mir}; use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; use tracing::instrument; @@ -104,10 +104,10 @@ impl Qualif for HasMutInterior { // typeck results without causing query cycles, we should use this here instead of defining // opaque types. let typing_env = ty::TypingEnv { - typing_mode: ty::TypingMode::analysis_in_body( + typing_mode: TypingModeEqWrapper(ty::TypingMode::analysis_in_body( cx.tcx, cx.body.source.def_id().expect_local(), - ), + )), param_env: cx.typing_env.param_env, }; let (infcx, param_env) = cx.tcx.infer_ctxt().build_with_typing_env(typing_env); diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index ccfdf571fb27c..351eceb48d179 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -371,10 +371,20 @@ pub fn eval_to_allocation_raw_provider<'tcx>( // This shouldn't be used for statics, since statics are conceptually places, // not values -- so what we do here could break pointer identity. assert!(key.value.promoted.is_some() || !tcx.is_static(key.value.instance.def_id())); - // Const eval always happens in PostAnalysis mode . See the comment in - // `InterpCx::new` for more details. - debug_assert_eq!(key.typing_env.typing_mode, ty::TypingMode::PostAnalysis); + if cfg!(debug_assertions) { + match key.typing_env.typing_mode.0 { + ty::TypingMode::PostAnalysis => {} + ty::TypingMode::Coherence + | ty::TypingMode::Analysis { .. } + | ty::TypingMode::Borrowck { .. } + | ty::TypingMode::PostBorrowckAnalysis { .. } => { + bug!( + "Const eval should always happens in PostAnalysis mode. See the comment in `InterpCx::new` for more details." + ) + } + } + // Make sure we format the instance even if we do not print it. // This serves as a regression test against an ICE on printing. // The next two lines concatenated contain some discussion: diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 4323debd80149..5928ad89fd69e 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -236,9 +236,18 @@ pub(crate) fn eval_to_valtree<'tcx>( typing_env: ty::TypingEnv<'tcx>, cid: GlobalId<'tcx>, ) -> EvalToValTreeResult<'tcx> { - // Const eval always happens in PostAnalysis mode . See the comment in - // `InterpCx::new` for more details. - debug_assert_eq!(typing_env.typing_mode, ty::TypingMode::PostAnalysis); + #[cfg(debug_assertions)] + match typing_env.typing_mode.0 { + ty::TypingMode::PostAnalysis => {} + ty::TypingMode::Coherence + | ty::TypingMode::Analysis { .. } + | ty::TypingMode::Borrowck { .. } + | ty::TypingMode::PostBorrowckAnalysis { .. } => { + bug!( + "Const eval should always happens in PostAnalysis mode. See the comment in `InterpCx::new` for more details." + ) + } + } let const_alloc = tcx.eval_to_allocation_raw(typing_env.as_query_input(cid))?; // FIXME Need to provide a span to `eval_to_valtree` diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 04f0e7099d840..20f85b7bf4072 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -243,7 +243,21 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // opaque types. This is needed for trivial things like `size_of`, but also for using associated // types that are not specified in the opaque type. We also use MIR bodies whose opaque types have // already been revealed, so we'd be able to at least partially observe the hidden types anyways. - debug_assert_matches!(typing_env.typing_mode, ty::TypingMode::PostAnalysis); + debug_assert_matches!(typing_env.typing_mode.0, ty::TypingMode::PostAnalysis); + if cfg!(debug_assertions) { + match typing_env.typing_mode.0 { + ty::TypingMode::PostAnalysis => {} + ty::TypingMode::Coherence + | ty::TypingMode::Analysis { .. } + | ty::TypingMode::Borrowck { .. } + | ty::TypingMode::PostBorrowckAnalysis { .. } => { + use rustc_middle::bug; + + bug!("Const eval should always happens in PostAnalysis mode.") + } + } + } + InterpCx { machine, tcx: tcx.at(root_span), diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 87d389a5dea5a..c889381586753 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -13,6 +13,7 @@ use rustc_middle::ty::{ self, BoundVar, GenericArg, InferConst, List, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; +use rustc_type_ir::TypingModeEqWrapper; use smallvec::SmallVec; use tracing::debug; @@ -72,7 +73,7 @@ impl<'tcx> InferCtxt<'tcx> { query_state, ) .unchecked_map(|(param_env, value)| param_env.and(value)); - CanonicalQueryInput { canonical, typing_mode: self.typing_mode() } + CanonicalQueryInput { canonical, typing_mode: TypingModeEqWrapper(self.typing_mode()) } } /// Canonicalizes a query *response* `V`. When we canonicalize a diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 583eb1a6dbc4a..b18d820f08295 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -33,6 +33,7 @@ use rustc_middle::ty::{ TypeSuperFoldable, TypeVisitable, TypeVisitableExt, TypingEnv, TypingMode, fold_regions, }; use rustc_span::{DUMMY_SP, Span, Symbol}; +use rustc_type_ir::TypingModeEqWrapper; use snapshot::undo_log::InferCtxtUndoLogs; use tracing::{debug, instrument}; use type_variable::TypeVariableOrigin; @@ -564,7 +565,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { where T: TypeFoldable>, { - let infcx = self.build(input.typing_mode); + let infcx = self.build(input.typing_mode.0); let (value, args) = infcx.instantiate_canonical(span, &input.canonical); (infcx, value, args) } @@ -573,7 +574,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { mut self, TypingEnv { typing_mode, param_env }: TypingEnv<'tcx>, ) -> (InferCtxt<'tcx>, ty::ParamEnv<'tcx>) { - (self.build(typing_mode), param_env) + (self.build(typing_mode.0), param_env) } pub fn build(&mut self, typing_mode: TypingMode<'tcx>) -> InferCtxt<'tcx> { @@ -1376,7 +1377,7 @@ impl<'tcx> InferCtxt<'tcx> { | ty::TypingMode::PostBorrowckAnalysis { .. } | ty::TypingMode::PostAnalysis) => mode, }; - ty::TypingEnv { typing_mode, param_env } + ty::TypingEnv { typing_mode: TypingModeEqWrapper(typing_mode), param_env } } /// Similar to [`Self::canonicalize_query`], except that it returns diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 752c2220d4147..3372dc70c5200 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -24,8 +24,12 @@ use rustc_middle::lint::LevelAndSource; use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths}; -use rustc_middle::ty::{self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode}; -use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintExpectationId, LintId}; +use rustc_middle::ty::{ + self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode, TypingModeEqWrapper, +}; +use rustc_session::lint::{ + CheckLintNameResult, FutureIncompatibleInfo, Lint, LintExpectationId, LintId, +}; use rustc_session::{DynLintStore, Session}; use rustc_span::edit_distance::find_best_match_for_names; use rustc_span::{Ident, Span, Symbol, sym}; @@ -637,7 +641,10 @@ impl<'tcx> LateContext<'tcx> { } pub fn typing_env(&self) -> TypingEnv<'tcx> { - TypingEnv { typing_mode: self.typing_mode(), param_env: self.param_env } + TypingEnv { + typing_mode: TypingModeEqWrapper(self.typing_mode()), + param_env: self.param_env, + } } pub fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 1c14b94b8d7d3..c5fa3464b8a03 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -33,7 +33,7 @@ use crate::ty::codec::{TyDecoder, TyEncoder}; use crate::ty::print::{FmtPrinter, Printer, pretty_print_const, with_no_trimmed_paths}; use crate::ty::{ self, GenericArg, GenericArgsRef, Instance, InstanceKind, List, Ty, TyCtxt, TypeVisitableExt, - TypingEnv, UserTypeAnnotationIndex, + TypingEnv, TypingModeEqWrapper, UserTypeAnnotationIndex, }; mod basic_blocks; @@ -416,7 +416,7 @@ impl<'tcx> Body<'tcx> { match self.phase { // FIXME(#132279): we should reveal the opaques defined in the body during analysis. MirPhase::Built | MirPhase::Analysis(_) => TypingEnv { - typing_mode: ty::TypingMode::non_body_analysis(), + typing_mode: TypingModeEqWrapper(ty::TypingMode::non_body_analysis()), param_env: tcx.param_env(self.source.def_id()), }, MirPhase::Runtime(_) => TypingEnv::post_analysis(tcx, self.source.def_id()), diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index dbf7d643a42ce..05df583907ca1 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -105,7 +105,8 @@ pub use self::sty::{ AliasTy, AliasTyKind, Article, Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVariableKind, CanonicalPolyFnSig, CoroutineArgsExt, EarlyBinder, FnSig, InlineConstArgs, InlineConstArgsParts, ParamConst, ParamTy, PlaceholderConst, - PlaceholderRegion, PlaceholderType, PolyFnSig, TyKind, TypeAndMut, TypingMode, UpvarArgs, + PlaceholderRegion, PlaceholderType, PolyFnSig, TyKind, TypeAndMut, TypingMode, + TypingModeEqWrapper, UpvarArgs, }; pub use self::trait_def::TraitDef; pub use self::typeck_results::{ @@ -980,7 +981,7 @@ pub struct ParamEnvAnd<'tcx, T> { pub struct TypingEnv<'tcx> { #[type_foldable(identity)] #[type_visitable(ignore)] - pub typing_mode: TypingMode<'tcx>, + pub typing_mode: TypingModeEqWrapper<'tcx>, pub param_env: ParamEnv<'tcx>, } @@ -993,7 +994,10 @@ impl<'tcx> TypingEnv<'tcx> { /// use `TypingMode::PostAnalysis`, they may still have where-clauses /// in scope. pub fn fully_monomorphized() -> TypingEnv<'tcx> { - TypingEnv { typing_mode: TypingMode::PostAnalysis, param_env: ParamEnv::empty() } + TypingEnv { + typing_mode: TypingModeEqWrapper(TypingMode::PostAnalysis), + param_env: ParamEnv::empty(), + } } /// Create a typing environment for use during analysis outside of a body. @@ -1006,7 +1010,10 @@ impl<'tcx> TypingEnv<'tcx> { def_id: impl IntoQueryKey, ) -> TypingEnv<'tcx> { let def_id = def_id.into_query_key(); - TypingEnv { typing_mode: TypingMode::non_body_analysis(), param_env: tcx.param_env(def_id) } + TypingEnv { + typing_mode: TypingModeEqWrapper(TypingMode::non_body_analysis()), + param_env: tcx.param_env(def_id), + } } pub fn post_analysis(tcx: TyCtxt<'tcx>, def_id: impl IntoQueryKey) -> TypingEnv<'tcx> { @@ -1018,8 +1025,12 @@ impl<'tcx> TypingEnv<'tcx> { /// opaque types in the `param_env`. pub fn with_post_analysis_normalized(self, tcx: TyCtxt<'tcx>) -> TypingEnv<'tcx> { let TypingEnv { typing_mode, param_env } = self; - if let TypingMode::PostAnalysis = typing_mode { - return self; + match typing_mode.0 { + TypingMode::Coherence + | TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } => {} + TypingMode::PostAnalysis => return self, } // No need to reveal opaques with the new solver enabled, @@ -1029,7 +1040,7 @@ impl<'tcx> TypingEnv<'tcx> { } else { ParamEnv::new(tcx.reveal_opaque_types_in_bounds(param_env.caller_bounds())) }; - TypingEnv { typing_mode: TypingMode::PostAnalysis, param_env } + TypingEnv { typing_mode: TypingModeEqWrapper(TypingMode::PostAnalysis), param_env } } /// Combine this typing environment with the given `value` to be used by diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 08bfe15137a24..9164f7b57e648 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -40,6 +40,7 @@ pub type FnSig<'tcx> = ir::FnSig>; pub type Binder<'tcx, T> = ir::Binder, T>; pub type EarlyBinder<'tcx, T> = ir::EarlyBinder, T>; pub type TypingMode<'tcx> = ir::TypingMode>; +pub type TypingModeEqWrapper<'tcx> = ir::TypingModeEqWrapper>; pub type Placeholder<'tcx, T> = ir::Placeholder, T>; pub type PlaceholderRegion<'tcx> = ir::PlaceholderRegion>; pub type PlaceholderType<'tcx> = ir::PlaceholderType>; diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs index 1fe745a5d5197..fef2a787414b0 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -548,7 +548,16 @@ where let subpath = self.elaborator.field_subpath(variant_path, field_idx); let tcx = self.tcx(); - assert_eq!(self.elaborator.typing_env().typing_mode, ty::TypingMode::PostAnalysis); + match self.elaborator.typing_env().typing_mode.0 { + ty::TypingMode::PostAnalysis => {} + ty::TypingMode::Coherence + | ty::TypingMode::Analysis { .. } + | ty::TypingMode::Borrowck { .. } + | ty::TypingMode::PostBorrowckAnalysis { .. } => { + bug!() + } + } + let field_ty = field.ty(tcx, args); // We silently leave an unnormalized type here to support polymorphic drop // elaboration for users of rustc internal APIs diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index 1f64f09fe787f..7fdbfa023af4b 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -17,7 +17,7 @@ use rustc_type_ir::inherent::*; use rustc_type_ir::relate::solver_relating::RelateExt; use rustc_type_ir::{ self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner, - TypeFoldable, + TypeFoldable, TypingModeEqWrapper, }; use tracing::instrument; @@ -66,7 +66,10 @@ where predefined_opaques_in_body: delegate.cx().mk_predefined_opaques_in_body(opaque_types), }, ); - let query_input = ty::CanonicalQueryInput { canonical, typing_mode: delegate.typing_mode() }; + let query_input = ty::CanonicalQueryInput { + canonical, + typing_mode: TypingModeEqWrapper(delegate.typing_mode()), + }; (orig_values, query_input) } diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index a4f331d3fe718..8d855be720252 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -466,15 +466,20 @@ where // as we may want to weaken inference guidance in the future and don't want // to worry about causing major performance regressions when doing so. // See trait-system-refactor-initiative#226 for some ideas here. - if TypingMode::Coherence == self.typing_mode() - || !candidates.iter().any(|c| { + let assemble_impls = match self.typing_mode() { + TypingMode::Coherence => true, + TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => !candidates.iter().any(|c| { matches!( c.source, CandidateSource::ParamEnv(ParamEnvSource::NonGlobal) | CandidateSource::AliasBound(_) ) && has_no_inference_or_external_constraints(c.result) - }) - { + }), + }; + if assemble_impls { self.assemble_impl_candidates(goal, &mut candidates); self.assemble_object_bound_candidates(goal, &mut candidates); } diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 73044b7943aeb..0490b285aedf0 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -62,7 +62,7 @@ where // See `tests/ui/traits/next-solver/cycles/unproductive-in-coherence.rs` for an // example where this would matter. We likely should change these cycles to `NoSolution` // even in coherence once this is a bit more settled. - PathKind::Inductive => match input.typing_mode { + PathKind::Inductive => match input.typing_mode.0 { TypingMode::Coherence => { response_no_constraints(cx, input, Certainty::overflow(false)) } diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index 45e0b5d74af7d..328d4217072d7 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -25,11 +25,17 @@ pub fn evaluate_host_effect_obligation<'tcx>( selcx: &mut SelectionContext<'_, 'tcx>, obligation: &HostEffectObligation<'tcx>, ) -> Result>, EvaluationFailure> { - if selcx.infcx.typing_mode() == TypingMode::Coherence { - span_bug!( - obligation.cause.span, - "should not select host obligation in old solver in intercrate mode" - ); + match selcx.infcx.typing_mode() { + TypingMode::Coherence => { + span_bug!( + obligation.cause.span, + "should not select host obligation in old solver in intercrate mode" + ); + } + TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => {} } let ref obligation = selcx.infcx.resolve_vars_if_possible(obligation.clone()); diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index cf881961b3ce3..8f6139bee27b3 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -154,7 +154,7 @@ fn resolve_associated_item<'tcx>( // and the obligation is monomorphic, otherwise passes such as // transmute checking and polymorphic MIR optimizations could // get a result which isn't correct for all monomorphizations. - match typing_env.typing_mode { + match typing_env.typing_mode.0 { ty::TypingMode::Coherence | ty::TypingMode::Analysis { .. } | ty::TypingMode::Borrowck { .. } diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index 8e7414674c6e7..be5e483c808d4 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -11,7 +11,7 @@ use rustc_type_ir_macros::{ use crate::data_structures::HashMap; use crate::inherent::*; -use crate::{self as ty, Interner, TypingMode, UniverseIndex}; +use crate::{self as ty, Interner, TypingModeEqWrapper, UniverseIndex}; #[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, V)] #[derive_where(Copy; I: Interner, V: Copy)] @@ -21,7 +21,7 @@ use crate::{self as ty, Interner, TypingMode, UniverseIndex}; )] pub struct CanonicalQueryInput { pub canonical: Canonical, - pub typing_mode: TypingMode, + pub typing_mode: TypingModeEqWrapper, } impl Eq for CanonicalQueryInput {} diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index feafcee7bad9e..1bf2e5378a1ff 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -1,3 +1,5 @@ +use std::hash::{Hash, Hasher}; + use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; @@ -18,11 +20,28 @@ use crate::{self as ty, Interner, TyVid}; /// /// If neither of these functions are available, feel free to reach out to /// t-types for help. -#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] +/// +/// Because typing rules get subtly different based on what typing mode we're in, +/// subtle enough that changing the behavior of typing modes can sometimes cause +/// changes that we don't even have tests for, we'd like to enforce the rule that +/// any place where we specialize behavior based on the typing mode, we match +/// *exhaustively* on the typing mode. That way, it's easy to determine all the +/// places that must change when anything about typing modes changes. +/// +/// Hence, `TypingMode` does not implement `Eq`, though [`TypingModeEqWrapper`] is available +/// in the rare case that you do need this. Most cases where this currently matters is +/// where we pass typing modes through the query system and want to cache based on it. +/// See also `#[rustc_must_match_exhaustively]`, which tries to detect non-exhaustive +/// matches. +/// +/// Since matching on typing mode to single out `Coherence` is so common, and `Coherence` +/// is so different from the other modes: see also [`is_coherence`](TypingMode::is_coherence) +#[derive_where(Clone, Copy, Hash, Debug; I: Interner)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) )] +#[cfg_attr(not(bootstrap), rustc_must_match_exhaustively)] pub enum TypingMode { /// When checking whether impls overlap, we check whether any obligations /// are guaranteed to never hold when unifying the impls. This requires us @@ -90,9 +109,64 @@ pub enum TypingMode { PostAnalysis, } -impl Eq for TypingMode {} +/// We want to highly discourage using equality checks on typing modes. +/// Instead you should match, **exhaustively**, so when we ever modify the enum we get a compile +/// error. Only use `TypingModeEqWrapper` when you really really really have to. +/// Prefer unwrapping `TypingModeEqWrapper` in apis that should return a `TypingMode` whenever +/// possible, and if you ever get an `TypingModeEqWrapper`, prefer unwrapping it and matching on it **exhaustively**. +#[derive_where(Clone, Copy, Debug; I: Interner)] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) +)] +pub struct TypingModeEqWrapper(pub TypingMode); + +impl Hash for TypingModeEqWrapper { + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + +impl PartialEq for TypingModeEqWrapper { + fn eq(&self, other: &Self) -> bool { + match (self.0, other.0) { + (TypingMode::Coherence, TypingMode::Coherence) => true, + ( + TypingMode::Analysis { defining_opaque_types_and_generators: l }, + TypingMode::Analysis { defining_opaque_types_and_generators: r }, + ) => l == r, + ( + TypingMode::Borrowck { defining_opaque_types: l }, + TypingMode::Borrowck { defining_opaque_types: r }, + ) => l == r, + ( + TypingMode::PostBorrowckAnalysis { defined_opaque_types: l }, + TypingMode::PostBorrowckAnalysis { defined_opaque_types: r }, + ) => l == r, + (TypingMode::PostAnalysis, TypingMode::PostAnalysis) => true, + _ => false, + } + } +} + +impl Eq for TypingModeEqWrapper {} impl TypingMode { + /// There are a bunch of places in the compiler where we single out `Coherence`, + /// and alter behavior. We'd like to *always* match on `TypingMode` exhaustively, + /// but not having this method leads to a bunch of noisy code. + /// + /// See also the documentation on [`TypingMode`] about exhaustive matching. + pub fn is_coherence(&self) -> bool { + match self { + TypingMode::Coherence => true, + TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => false, + } + } + /// Analysis outside of a body does not define any opaque types. pub fn non_body_analysis() -> TypingMode { TypingMode::Analysis { defining_opaque_types_and_generators: Default::default() } @@ -127,6 +201,7 @@ impl TypingMode { pub fn post_borrowck_analysis(cx: I, body_def_id: I::LocalDefId) -> TypingMode { let defined_opaque_types = cx.opaque_types_defined_by(body_def_id); + if defined_opaque_types.is_empty() { TypingMode::non_body_analysis() } else { @@ -322,6 +397,13 @@ where // Note: `feature_bound_holds_in_crate` does not consider a feature to be enabled // if we are in std/core even if there is a corresponding `feature` attribute on the crate. - (infcx.typing_mode() == TypingMode::PostAnalysis) - || infcx.cx().features().feature_bound_holds_in_crate(symbol) + match infcx.typing_mode() { + TypingMode::Coherence + | TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } => { + infcx.cx().features().feature_bound_holds_in_crate(symbol) + } + TypingMode::PostAnalysis => true, + } } From 63c212e62b6ac8ae1b1b6f341914d720812b58a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Thu, 9 Apr 2026 13:56:45 +0200 Subject: [PATCH 2/6] make typing_mode getter --- .../src/check_consts/qualifs.rs | 13 +++++------- .../src/const_eval/eval_queries.rs | 2 +- .../src/const_eval/valtrees.rs | 21 ++++++++++--------- .../src/interpret/eval_context.rs | 21 ++++++++----------- compiler/rustc_infer/src/infer/mod.rs | 7 +++---- .../rustc_infer/src/infer/opaque_types/mod.rs | 2 +- .../src/infer/relate/generalize.rs | 6 +++--- compiler/rustc_lint/src/context.rs | 13 +++--------- compiler/rustc_middle/src/mir/mod.rs | 10 ++++----- compiler/rustc_middle/src/ty/mod.rs | 20 ++++++++++-------- .../rustc_mir_transform/src/elaborate_drop.rs | 2 +- .../src/solve/normalizes_to/opaque_types.rs | 4 +++- .../src/solve/trait_goals.rs | 2 +- .../src/traits/effects.rs | 18 ++++++---------- .../src/traits/fulfill.rs | 6 ++---- .../src/traits/select/candidate_assembly.rs | 4 ++-- .../src/traits/select/mod.rs | 12 ++++++----- compiler/rustc_ty_utils/src/instance.rs | 2 +- compiler/rustc_type_ir/src/infer_ctxt.rs | 10 +++++++-- 19 files changed, 83 insertions(+), 92 deletions(-) diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index d068b4bc84cf4..a4f33ea05b2d3 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -9,7 +9,7 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::LangItem; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::*; -use rustc_middle::ty::{self, AdtDef, Ty, TypingModeEqWrapper}; +use rustc_middle::ty::{self, AdtDef, Ty}; use rustc_middle::{bug, mir}; use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; use tracing::instrument; @@ -103,13 +103,10 @@ impl Qualif for HasMutInterior { // FIXME(#132279): Once we've got a typing mode which reveals opaque types using the HIR // typeck results without causing query cycles, we should use this here instead of defining // opaque types. - let typing_env = ty::TypingEnv { - typing_mode: TypingModeEqWrapper(ty::TypingMode::analysis_in_body( - cx.tcx, - cx.body.source.def_id().expect_local(), - )), - param_env: cx.typing_env.param_env, - }; + let typing_env = ty::TypingEnv::new( + cx.typing_env.param_env, + ty::TypingMode::analysis_in_body(cx.tcx, cx.body.source.def_id().expect_local()), + ); let (infcx, param_env) = cx.tcx.infer_ctxt().build_with_typing_env(typing_env); let ocx = ObligationCtxt::new(&infcx); let obligation = Obligation::new( diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 351eceb48d179..316fc9ba5f97d 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -373,7 +373,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>( assert!(key.value.promoted.is_some() || !tcx.is_static(key.value.instance.def_id())); if cfg!(debug_assertions) { - match key.typing_env.typing_mode.0 { + match key.typing_env.typing_mode() { ty::TypingMode::PostAnalysis => {} ty::TypingMode::Coherence | ty::TypingMode::Analysis { .. } diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 5928ad89fd69e..9e23f56d372b2 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -236,16 +236,17 @@ pub(crate) fn eval_to_valtree<'tcx>( typing_env: ty::TypingEnv<'tcx>, cid: GlobalId<'tcx>, ) -> EvalToValTreeResult<'tcx> { - #[cfg(debug_assertions)] - match typing_env.typing_mode.0 { - ty::TypingMode::PostAnalysis => {} - ty::TypingMode::Coherence - | ty::TypingMode::Analysis { .. } - | ty::TypingMode::Borrowck { .. } - | ty::TypingMode::PostBorrowckAnalysis { .. } => { - bug!( - "Const eval should always happens in PostAnalysis mode. See the comment in `InterpCx::new` for more details." - ) + if cfg!(debug_assertions) { + match typing_env.typing_mode() { + ty::TypingMode::PostAnalysis => {} + ty::TypingMode::Coherence + | ty::TypingMode::Analysis { .. } + | ty::TypingMode::Borrowck { .. } + | ty::TypingMode::PostBorrowckAnalysis { .. } => { + bug!( + "Const eval should always happens in PostAnalysis mode. See the comment in `InterpCx::new` for more details." + ) + } } } let const_alloc = tcx.eval_to_allocation_raw(typing_env.as_query_input(cid))?; diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 20f85b7bf4072..9caed8bac4f83 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -1,5 +1,3 @@ -use std::debug_assert_matches; - use either::{Left, Right}; use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout}; use rustc_hir::def_id::DefId; @@ -11,7 +9,8 @@ use rustc_middle::ty::layout::{ LayoutOfHelpers, TyAndLayout, }; use rustc_middle::ty::{ - self, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingEnv, Variance, + self, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingEnv, TypingMode, + Variance, }; use rustc_middle::{mir, span_bug}; use rustc_span::Span; @@ -243,17 +242,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // opaque types. This is needed for trivial things like `size_of`, but also for using associated // types that are not specified in the opaque type. We also use MIR bodies whose opaque types have // already been revealed, so we'd be able to at least partially observe the hidden types anyways. - debug_assert_matches!(typing_env.typing_mode.0, ty::TypingMode::PostAnalysis); if cfg!(debug_assertions) { - match typing_env.typing_mode.0 { - ty::TypingMode::PostAnalysis => {} - ty::TypingMode::Coherence - | ty::TypingMode::Analysis { .. } - | ty::TypingMode::Borrowck { .. } - | ty::TypingMode::PostBorrowckAnalysis { .. } => { + match typing_env.typing_mode() { + TypingMode::PostAnalysis => {} + TypingMode::Coherence + | TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } => { use rustc_middle::bug; - - bug!("Const eval should always happens in PostAnalysis mode.") + bug!("Const eval should always happens in PostAnalysis mode."); } } } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index b18d820f08295..a38d4e819e298 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -33,7 +33,6 @@ use rustc_middle::ty::{ TypeSuperFoldable, TypeVisitable, TypeVisitableExt, TypingEnv, TypingMode, fold_regions, }; use rustc_span::{DUMMY_SP, Span, Symbol}; -use rustc_type_ir::TypingModeEqWrapper; use snapshot::undo_log::InferCtxtUndoLogs; use tracing::{debug, instrument}; use type_variable::TypeVariableOrigin; @@ -572,9 +571,9 @@ impl<'tcx> InferCtxtBuilder<'tcx> { pub fn build_with_typing_env( mut self, - TypingEnv { typing_mode, param_env }: TypingEnv<'tcx>, + typing_env: TypingEnv<'tcx>, ) -> (InferCtxt<'tcx>, ty::ParamEnv<'tcx>) { - (self.build(typing_mode.0), param_env) + (self.build(typing_env.typing_mode()), typing_env.param_env) } pub fn build(&mut self, typing_mode: TypingMode<'tcx>) -> InferCtxt<'tcx> { @@ -1377,7 +1376,7 @@ impl<'tcx> InferCtxt<'tcx> { | ty::TypingMode::PostBorrowckAnalysis { .. } | ty::TypingMode::PostAnalysis) => mode, }; - ty::TypingEnv { typing_mode: TypingModeEqWrapper(typing_mode), param_env } + ty::TypingEnv::new(param_env, typing_mode) } /// Similar to [`Self::canonicalize_query`], except that it returns diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index 29cbbe5a1c9ed..0c447f7c69973 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -89,7 +89,7 @@ impl<'tcx> InferCtxt<'tcx> { if def_id.is_local() => { let def_id = def_id.expect_local(); - if let ty::TypingMode::Coherence = self.typing_mode() { + if self.typing_mode().is_coherence() { // See comment on `insert_hidden_type` for why this is sufficient in coherence return Some(self.register_hidden_type( OpaqueTypeKey { def_id, args }, diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index ffc04eabcec16..0229af53f2a67 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -7,7 +7,7 @@ use rustc_middle::bug; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{ self, AliasRelationDirection, InferConst, Term, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, TypeVisitor, TypingMode, + TypeVisitableExt, TypeVisitor, }; use rustc_span::Span; use tracing::{debug, instrument, warn}; @@ -603,7 +603,7 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { // // cc trait-system-refactor-initiative#108 if self.infcx.next_trait_solver() - && !matches!(self.infcx.typing_mode(), TypingMode::Coherence) + && !self.infcx.typing_mode().is_coherence() && self.in_alias { inner.type_variables().equate(vid, new_var_id); @@ -735,7 +735,7 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { // See the comment for type inference variables // for more details. if self.infcx.next_trait_solver() - && !matches!(self.infcx.typing_mode(), TypingMode::Coherence) + && !self.infcx.typing_mode().is_coherence() && self.in_alias { variable_table.union(vid, new_var_id); diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 3372dc70c5200..acfa3ec343975 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -24,12 +24,8 @@ use rustc_middle::lint::LevelAndSource; use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths}; -use rustc_middle::ty::{ - self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode, TypingModeEqWrapper, -}; -use rustc_session::lint::{ - CheckLintNameResult, FutureIncompatibleInfo, Lint, LintExpectationId, LintId, -}; +use rustc_middle::ty::{self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode}; +use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintExpectationId, LintId}; use rustc_session::{DynLintStore, Session}; use rustc_span::edit_distance::find_best_match_for_names; use rustc_span::{Ident, Span, Symbol, sym}; @@ -641,10 +637,7 @@ impl<'tcx> LateContext<'tcx> { } pub fn typing_env(&self) -> TypingEnv<'tcx> { - TypingEnv { - typing_mode: TypingModeEqWrapper(self.typing_mode()), - param_env: self.param_env, - } + TypingEnv::new(self.param_env, self.typing_mode()) } pub fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index c5fa3464b8a03..36752bba9f722 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -33,7 +33,7 @@ use crate::ty::codec::{TyDecoder, TyEncoder}; use crate::ty::print::{FmtPrinter, Printer, pretty_print_const, with_no_trimmed_paths}; use crate::ty::{ self, GenericArg, GenericArgsRef, Instance, InstanceKind, List, Ty, TyCtxt, TypeVisitableExt, - TypingEnv, TypingModeEqWrapper, UserTypeAnnotationIndex, + TypingEnv, UserTypeAnnotationIndex, }; mod basic_blocks; @@ -415,10 +415,10 @@ impl<'tcx> Body<'tcx> { pub fn typing_env(&self, tcx: TyCtxt<'tcx>) -> TypingEnv<'tcx> { match self.phase { // FIXME(#132279): we should reveal the opaques defined in the body during analysis. - MirPhase::Built | MirPhase::Analysis(_) => TypingEnv { - typing_mode: TypingModeEqWrapper(ty::TypingMode::non_body_analysis()), - param_env: tcx.param_env(self.source.def_id()), - }, + MirPhase::Built | MirPhase::Analysis(_) => TypingEnv::new( + tcx.param_env(self.source.def_id()), + ty::TypingMode::non_body_analysis(), + ), MirPhase::Runtime(_) => TypingEnv::post_analysis(tcx, self.source.def_id()), } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 05df583907ca1..bc971d7a43705 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -981,11 +981,19 @@ pub struct ParamEnvAnd<'tcx, T> { pub struct TypingEnv<'tcx> { #[type_foldable(identity)] #[type_visitable(ignore)] - pub typing_mode: TypingModeEqWrapper<'tcx>, + typing_mode: TypingModeEqWrapper<'tcx>, pub param_env: ParamEnv<'tcx>, } impl<'tcx> TypingEnv<'tcx> { + pub fn new(param_env: ParamEnv<'tcx>, typing_mode: TypingMode<'tcx>) -> Self { + Self { typing_mode: TypingModeEqWrapper(typing_mode), param_env } + } + + pub fn typing_mode(&self) -> TypingMode<'tcx> { + self.typing_mode.0 + } + /// Create a typing environment with no where-clauses in scope /// where all opaque types and default associated items are revealed. /// @@ -994,10 +1002,7 @@ impl<'tcx> TypingEnv<'tcx> { /// use `TypingMode::PostAnalysis`, they may still have where-clauses /// in scope. pub fn fully_monomorphized() -> TypingEnv<'tcx> { - TypingEnv { - typing_mode: TypingModeEqWrapper(TypingMode::PostAnalysis), - param_env: ParamEnv::empty(), - } + Self::new(ParamEnv::empty(), TypingMode::PostAnalysis) } /// Create a typing environment for use during analysis outside of a body. @@ -1010,10 +1015,7 @@ impl<'tcx> TypingEnv<'tcx> { def_id: impl IntoQueryKey, ) -> TypingEnv<'tcx> { let def_id = def_id.into_query_key(); - TypingEnv { - typing_mode: TypingModeEqWrapper(TypingMode::non_body_analysis()), - param_env: tcx.param_env(def_id), - } + Self::new(tcx.param_env(def_id), TypingMode::non_body_analysis()) } pub fn post_analysis(tcx: TyCtxt<'tcx>, def_id: impl IntoQueryKey) -> TypingEnv<'tcx> { diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs index fef2a787414b0..3da9d6aac9be9 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -548,7 +548,7 @@ where let subpath = self.elaborator.field_subpath(variant_path, field_idx); let tcx = self.tcx(); - match self.elaborator.typing_env().typing_mode.0 { + match self.elaborator.typing_env().typing_mode() { ty::TypingMode::PostAnalysis => {} ty::TypingMode::Coherence | ty::TypingMode::Analysis { .. } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs index a5f857a1dd85b..9b2bd7cb74ff0 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs @@ -93,7 +93,9 @@ where }); self.eq(goal.param_env, expected, actual)?; } - _ => unreachable!(), + TypingMode::Coherence + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => unreachable!(), } } diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 44a570fc4fa79..33c165fbea6c4 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1421,7 +1421,7 @@ where mut candidates: Vec>, failed_candidate_info: FailedCandidateInfo, ) -> Result<(CanonicalResponse, Option), NoSolution> { - if let TypingMode::Coherence = self.typing_mode() { + if self.typing_mode().is_coherence() { return if let Some((response, _)) = self.try_merge_candidates(&candidates) { Ok((response, Some(TraitGoalProvenVia::Misc))) } else { diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index 328d4217072d7..9959c92a6f897 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -8,7 +8,7 @@ use rustc_middle::span_bug; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::elaborate::elaborate; use rustc_middle::ty::fast_reject::DeepRejectCtxt; -use rustc_middle::ty::{self, Ty, TypingMode}; +use rustc_middle::ty::{self, Ty}; use thin_vec::{ThinVec, thin_vec}; use super::SelectionContext; @@ -25,17 +25,11 @@ pub fn evaluate_host_effect_obligation<'tcx>( selcx: &mut SelectionContext<'_, 'tcx>, obligation: &HostEffectObligation<'tcx>, ) -> Result>, EvaluationFailure> { - match selcx.infcx.typing_mode() { - TypingMode::Coherence => { - span_bug!( - obligation.cause.span, - "should not select host obligation in old solver in intercrate mode" - ); - } - TypingMode::Analysis { .. } - | TypingMode::Borrowck { .. } - | TypingMode::PostBorrowckAnalysis { .. } - | TypingMode::PostAnalysis => {} + if selcx.infcx.typing_mode().is_coherence() { + span_bug!( + obligation.cause.span, + "should not select host obligation in old solver in intercrate mode" + ); } let ref obligation = selcx.infcx.resolve_vars_if_possible(obligation.clone()); diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index a575630a05035..2dbeff4a50508 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -841,8 +841,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { stalled_on: &mut Vec, ) -> ProcessResult, FulfillmentErrorCode<'tcx>> { let infcx = self.selcx.infcx; - if obligation.predicate.is_global() && !matches!(infcx.typing_mode(), TypingMode::Coherence) - { + if obligation.predicate.is_global() && !infcx.typing_mode().is_coherence() { // no type variables present, can use evaluation for better caching. // FIXME: consider caching errors too. if infcx.predicate_must_hold_considering_regions(obligation) { @@ -896,8 +895,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { ) -> ProcessResult, FulfillmentErrorCode<'tcx>> { let tcx = self.selcx.tcx(); let infcx = self.selcx.infcx; - if obligation.predicate.is_global() && !matches!(infcx.typing_mode(), TypingMode::Coherence) - { + if obligation.predicate.is_global() && !infcx.typing_mode().is_coherence() { // no type variables present, can use evaluation for better caching. // FIXME: consider caching errors too. if infcx.predicate_must_hold_considering_regions(obligation) { diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index ab8e1354b6b3c..f7614e7c9730a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -15,7 +15,7 @@ use rustc_hir::{self as hir, CoroutineDesugaring, CoroutineKind}; use rustc_infer::traits::{Obligation, PolyTraitObligation, PredicateObligation, SelectionError}; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::{ - self, FieldInfo, SizedTraitKind, TraitRef, Ty, TypeVisitableExt, TypingMode, elaborate, + self, FieldInfo, SizedTraitKind, TraitRef, Ty, TypeVisitableExt, elaborate, }; use rustc_middle::{bug, span_bug}; use rustc_span::DUMMY_SP; @@ -849,7 +849,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // // Note that this is only sound as projection candidates of opaque types // are always applicable for auto traits. - } else if let TypingMode::Coherence = self.infcx.typing_mode() { + } else if self.infcx.typing_mode().is_coherence() { // We do not emit auto trait candidates for opaque types in coherence. // Doing so can result in weird dependency cycles. candidates.ambiguous = true; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 527353bed5ade..a564e8060d930 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -3,9 +3,9 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html#selection use std::cell::{Cell, RefCell}; +use std::cmp; use std::fmt::{self, Display}; use std::ops::ControlFlow; -use std::{assert_matches, cmp}; use hir::def::DefKind; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; @@ -210,8 +210,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Enables tracking of intercrate ambiguity causes. See /// the documentation of [`Self::intercrate_ambiguity_causes`] for more. pub fn enable_tracking_intercrate_ambiguity_causes(&mut self) { - assert_matches!(self.infcx.typing_mode(), TypingMode::Coherence); + assert!(self.infcx.typing_mode().is_coherence()); assert!(self.intercrate_ambiguity_causes.is_none()); + self.intercrate_ambiguity_causes = Some(FxIndexSet::default()); debug!("selcx: enable_tracking_intercrate_ambiguity_causes"); } @@ -222,7 +223,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { pub fn take_intercrate_ambiguity_causes( &mut self, ) -> FxIndexSet> { - assert_matches!(self.infcx.typing_mode(), TypingMode::Coherence); + assert!(self.infcx.typing_mode().is_coherence()); + self.intercrate_ambiguity_causes.take().unwrap_or_default() } @@ -1016,7 +1018,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { previous_stack: TraitObligationStackList<'o, 'tcx>, mut obligation: PolyTraitObligation<'tcx>, ) -> Result { - if !matches!(self.infcx.typing_mode(), TypingMode::Coherence) + if !self.infcx.typing_mode().is_coherence() && obligation.is_global() && obligation.param_env.caller_bounds().iter().all(|bound| bound.has_param()) { @@ -2548,7 +2550,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { nested_obligations.extend(obligations); if impl_trait_header.polarity == ty::ImplPolarity::Reservation - && !matches!(self.infcx.typing_mode(), TypingMode::Coherence) + && !self.infcx.typing_mode().is_coherence() { debug!("reservation impls only apply in intercrate mode"); return Err(()); diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 8f6139bee27b3..e48e525e571d0 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -154,7 +154,7 @@ fn resolve_associated_item<'tcx>( // and the obligation is monomorphic, otherwise passes such as // transmute checking and polymorphic MIR optimizations could // get a result which isn't correct for all monomorphizations. - match typing_env.typing_mode.0 { + match typing_env.typing_mode() { ty::TypingMode::Coherence | ty::TypingMode::Analysis { .. } | ty::TypingMode::Borrowck { .. } diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 1bf2e5378a1ff..59334d4ebc747 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -144,7 +144,14 @@ impl PartialEq for TypingModeEqWrapper { TypingMode::PostBorrowckAnalysis { defined_opaque_types: r }, ) => l == r, (TypingMode::PostAnalysis, TypingMode::PostAnalysis) => true, - _ => false, + ( + TypingMode::Coherence + | TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis, + _, + ) => false, } } } @@ -201,7 +208,6 @@ impl TypingMode { pub fn post_borrowck_analysis(cx: I, body_def_id: I::LocalDefId) -> TypingMode { let defined_opaque_types = cx.opaque_types_defined_by(body_def_id); - if defined_opaque_types.is_empty() { TypingMode::non_body_analysis() } else { From 2facd34bc8c9ccd45165c19efefd8fa5652d5793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Thu, 9 Apr 2026 11:17:16 +0200 Subject: [PATCH 3/6] add `#[rustc_must_match_exhaustively]` --- .../src/attributes/rustc_internal.rs | 9 ++ compiler/rustc_attr_parsing/src/context.rs | 1 + compiler/rustc_feature/src/builtin_attrs.rs | 4 + .../rustc_hir/src/attrs/data_structures.rs | 3 + .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_lint/src/internal.rs | 91 ++++++++++++++++++- compiler/rustc_lint/src/lib.rs | 3 + compiler/rustc_lint/src/lints.rs | 12 +++ compiler/rustc_passes/src/check_attr.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + .../internal-lints/must_match_exhaustively.rs | 49 ++++++++++ .../must_match_exhaustively.stderr | 71 +++++++++++++++ 12 files changed, 244 insertions(+), 2 deletions(-) create mode 100644 tests/ui-fulldeps/internal-lints/must_match_exhaustively.rs create mode 100644 tests/ui-fulldeps/internal-lints/must_match_exhaustively.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index d77c804af697f..a3ae9737d4f2f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -1349,3 +1349,12 @@ impl NoArgsAttributeParser for RustcIntrinsicConstStableIndirectPar const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcIntrinsicConstStableIndirect; } + +pub(crate) struct RustcExhaustiveParser; + +impl NoArgsAttributeParser for RustcExhaustiveParser { + const PATH: &'static [Symbol] = &[sym::rustc_must_match_exhaustively]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Enum)]); + const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcMustMatchExhaustively; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 6ab3f98e2015c..836c6b5edb25a 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -297,6 +297,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index acbcba90fbcc0..d02bd73f42e59 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1414,6 +1414,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_scalable_vector, Normal, template!(List: &["count"]), WarnFollowing, EncodeCrossCrate::Yes, "`#[rustc_scalable_vector]` defines a scalable vector type" ), + rustc_attr!( + rustc_must_match_exhaustively, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, + "enums with `#[rustc_must_match_exhaustively]` must be matched on with a match block that mentions all variants explicitly" + ), // ========================================================================== // Internal attributes, Testing: diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index a18ddff947099..f7be4515546a1 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1471,6 +1471,9 @@ pub enum AttributeKind { fn_names: ThinVec, }, + /// Represents `#[rustc_must_match_exhaustively]` + RustcMustMatchExhaustively(Span), + /// Represents `#[rustc_never_returns_null_ptr]` RustcNeverReturnsNullPtr, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index c19fc6976c6e6..85bc9dfce9267 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -156,6 +156,7 @@ impl AttributeKind { RustcMain => No, RustcMir(..) => Yes, RustcMustImplementOneOf { .. } => No, + RustcMustMatchExhaustively(..) => Yes, RustcNeverReturnsNullPtr => Yes, RustcNeverTypeOptions { .. } => No, RustcNoImplicitAutorefs => Yes, diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 357aa94feb1ed..c0b113610f67a 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -15,8 +15,9 @@ use rustc_span::{Span, sym}; use crate::lints::{ AttributeKindInFindAttr, BadOptAccessDiag, DefaultHashTypesDiag, ImplicitSysrootCrateImportDiag, LintPassByHand, NonGlobImportTypeIrInherent, QueryInstability, - QueryUntracked, SpanUseEqCtxtDiag, SymbolInternStringLiteralDiag, TyQualified, TykindDiag, - TykindKind, TypeIrDirectUse, TypeIrInherentUsage, TypeIrTraitUsage, + QueryUntracked, RustcMustMatchExhaustivelyNotExhaustive, SpanUseEqCtxtDiag, + SymbolInternStringLiteralDiag, TyQualified, TykindDiag, TykindKind, TypeIrDirectUse, + TypeIrInherentUsage, TypeIrTraitUsage, }; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; @@ -713,3 +714,89 @@ impl EarlyLintPass for BadUseOfFindAttr { } } } + +declare_tool_lint! { + pub rustc::RUSTC_MUST_MATCH_EXHAUSTIVELY, + Allow, + "Forbids matches with wildcards, or if-let matching on enums marked with `#[rustc_must_match_exhaustively]`", + report_in_external_macro: true +} +declare_lint_pass!(RustcMustMatchExhaustively => [RUSTC_MUST_MATCH_EXHAUSTIVELY]); + +fn is_rustc_must_match_exhaustively(cx: &LateContext<'_>, id: HirId) -> Option { + let res = cx.typeck_results(); + + let ty = res.node_type(id); + + let ty = if let ty::Ref(_, ty, _) = ty.kind() { *ty } else { ty }; + + if let Some(adt_def) = ty.ty_adt_def() + && adt_def.is_enum() + { + find_attr!(cx.tcx, adt_def.did(), RustcMustMatchExhaustively(span) => *span) + } else { + None + } +} + +fn pat_is_not_exhaustive_heuristic(pat: &hir::Pat<'_>) -> Option<(Span, &'static str)> { + match pat.kind { + hir::PatKind::Missing => None, + hir::PatKind::Wild => Some((pat.span, "because of this wildcard pattern")), + hir::PatKind::Binding(_, _, _, Some(pat)) => pat_is_not_exhaustive_heuristic(pat), + hir::PatKind::Binding(..) => Some((pat.span, "because of this variable binding")), + hir::PatKind::Struct(..) => None, + hir::PatKind::TupleStruct(..) => None, + hir::PatKind::Or(..) => None, + hir::PatKind::Never => None, + hir::PatKind::Tuple(..) => None, + hir::PatKind::Box(pat) => pat_is_not_exhaustive_heuristic(&*pat), + hir::PatKind::Deref(pat) => pat_is_not_exhaustive_heuristic(&*pat), + hir::PatKind::Ref(pat, _, _) => pat_is_not_exhaustive_heuristic(&*pat), + hir::PatKind::Expr(..) => None, + hir::PatKind::Guard(..) => None, + hir::PatKind::Range(..) => None, + hir::PatKind::Slice(..) => None, + hir::PatKind::Err(..) => None, + } +} + +impl<'tcx> LateLintPass<'tcx> for RustcMustMatchExhaustively { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) { + match expr.kind { + // This is not perfect exhaustiveness checking, that's why this is just a rustc internal + // attribute. But it catches most reasonable cases + hir::ExprKind::Match(expr, arms, _) => { + if let Some(attr_span) = is_rustc_must_match_exhaustively(cx, expr.hir_id) { + for arm in arms { + if let Some((span, message)) = pat_is_not_exhaustive_heuristic(arm.pat) { + cx.emit_span_lint( + RUSTC_MUST_MATCH_EXHAUSTIVELY, + expr.span, + RustcMustMatchExhaustivelyNotExhaustive { + attr_span, + pat_span: span, + message, + }, + ); + } + } + } + } + hir::ExprKind::If(expr, ..) if let ExprKind::Let(expr) = expr.kind => { + if let Some(attr_span) = is_rustc_must_match_exhaustively(cx, expr.init.hir_id) { + cx.emit_span_lint( + RUSTC_MUST_MATCH_EXHAUSTIVELY, + expr.span, + RustcMustMatchExhaustivelyNotExhaustive { + attr_span, + pat_span: expr.span, + message: "using if let only matches on one variant (try using `match`)", + }, + ); + } + } + _ => {} + } + } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 30b1e736ef3b1..9fa5501433453 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -668,6 +668,8 @@ fn register_internals(store: &mut LintStore) { store.register_early_pass(|| Box::new(ImplicitSysrootCrateImport)); store.register_lints(&BadUseOfFindAttr::lint_vec()); store.register_early_pass(|| Box::new(BadUseOfFindAttr)); + store.register_lints(&RustcMustMatchExhaustively::lint_vec()); + store.register_late_pass(|_| Box::new(RustcMustMatchExhaustively)); store.register_group( false, "rustc::internal", @@ -688,6 +690,7 @@ fn register_internals(store: &mut LintStore) { LintId::of(DIRECT_USE_OF_RUSTC_TYPE_IR), LintId::of(IMPLICIT_SYSROOT_CRATE_IMPORT), LintId::of(BAD_USE_OF_FIND_ATTR), + LintId::of(RUSTC_MUST_MATCH_EXHAUSTIVELY), ], ); } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 4279ab230df55..bfc3d3989e10b 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1162,6 +1162,18 @@ pub(crate) struct ImplicitSysrootCrateImportDiag<'a> { #[help("remove `AttributeKind`")] pub(crate) struct AttributeKindInFindAttr; +#[derive(Diagnostic)] +#[diag("match is not exhaustive")] +#[help("explicitly list all variants of the enum in a `match`")] +pub(crate) struct RustcMustMatchExhaustivelyNotExhaustive { + #[label("required because of this attribute")] + pub attr_span: Span, + + #[note("{$message}")] + pub pat_span: Span, + pub message: &'static str, +} + // let_underscore.rs #[derive(Diagnostic)] pub(crate) enum NonBindingLet { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 6aeb0ae57e752..9afaa4317610b 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -339,6 +339,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcMacroTransparency(_) | AttributeKind::RustcMain | AttributeKind::RustcMir(_) + | AttributeKind::RustcMustMatchExhaustively(..) | AttributeKind::RustcNeverReturnsNullPtr | AttributeKind::RustcNeverTypeOptions {..} | AttributeKind::RustcNoImplicitAutorefs diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 33bc5a578e8b6..f2276658ebda8 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1753,6 +1753,7 @@ symbols! { rustc_main, rustc_mir, rustc_must_implement_one_of, + rustc_must_match_exhaustively, rustc_never_returns_null_ptr, rustc_never_type_options, rustc_no_implicit_autorefs, diff --git a/tests/ui-fulldeps/internal-lints/must_match_exhaustively.rs b/tests/ui-fulldeps/internal-lints/must_match_exhaustively.rs new file mode 100644 index 0000000000000..cb76c9754dc4d --- /dev/null +++ b/tests/ui-fulldeps/internal-lints/must_match_exhaustively.rs @@ -0,0 +1,49 @@ +//@ compile-flags: -Z unstable-options +//@ ignore-stage1 + +#![feature(rustc_private)] +#![feature(rustc_attrs)] +#![deny(rustc::rustc_must_match_exhaustively)] + + +#[rustc_must_match_exhaustively] +#[derive(Copy, Clone)] +enum Foo { + A {field: u32}, + B, +} + +fn foo(f: Foo) { + match f { + Foo::A {..}=> {} + Foo::B => {} + } + + match f { + //~^ ERROR match is not exhaustive + Foo::A {..} => {} + _ => {} + } + + match f { + //~^ ERROR match is not exhaustive + Foo::A {..} => {} + a => {} + } + + match &f { + //~^ ERROR match is not exhaustive + Foo::A {..} => {} + a => {} + } + + match f { + Foo::A {..} => {} + a@Foo::B => {} + } + + if let Foo::A {..} = f {} + //~^ ERROR match is not exhaustive +} + +fn main() {} diff --git a/tests/ui-fulldeps/internal-lints/must_match_exhaustively.stderr b/tests/ui-fulldeps/internal-lints/must_match_exhaustively.stderr new file mode 100644 index 0000000000000..04f112bbe00b4 --- /dev/null +++ b/tests/ui-fulldeps/internal-lints/must_match_exhaustively.stderr @@ -0,0 +1,71 @@ +error: match is not exhaustive + --> $DIR/must_match_exhaustively.rs:22:11 + | +LL | #[rustc_must_match_exhaustively] + | -------------------------------- required because of this attribute +... +LL | match f { + | ^ + | + = help: explicitly list all variants of the enum in a `match` +note: because of this wildcard pattern + --> $DIR/must_match_exhaustively.rs:25:9 + | +LL | _ => {} + | ^ +note: the lint level is defined here + --> $DIR/must_match_exhaustively.rs:6:9 + | +LL | #![deny(rustc::rustc_must_match_exhaustively)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: match is not exhaustive + --> $DIR/must_match_exhaustively.rs:28:11 + | +LL | #[rustc_must_match_exhaustively] + | -------------------------------- required because of this attribute +... +LL | match f { + | ^ + | + = help: explicitly list all variants of the enum in a `match` +note: because of this variable binding + --> $DIR/must_match_exhaustively.rs:31:9 + | +LL | a => {} + | ^ + +error: match is not exhaustive + --> $DIR/must_match_exhaustively.rs:34:11 + | +LL | #[rustc_must_match_exhaustively] + | -------------------------------- required because of this attribute +... +LL | match &f { + | ^^ + | + = help: explicitly list all variants of the enum in a `match` +note: because of this variable binding + --> $DIR/must_match_exhaustively.rs:37:9 + | +LL | a => {} + | ^ + +error: match is not exhaustive + --> $DIR/must_match_exhaustively.rs:45:8 + | +LL | #[rustc_must_match_exhaustively] + | -------------------------------- required because of this attribute +... +LL | if let Foo::A {..} = f {} + | ^^^^^^^^^^^^^^^^^^^ + | + = help: explicitly list all variants of the enum in a `match` +note: using if let only matches on one variant (try using `match`) + --> $DIR/must_match_exhaustively.rs:45:8 + | +LL | if let Foo::A {..} = f {} + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + From d5d0153254bdd383d69387a8e076087625425759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Thu, 9 Apr 2026 16:55:01 +0200 Subject: [PATCH 4/6] make all typing-mode conditional code an exhaustive match --- .../src/interpret/eval_context.rs | 2 +- compiler/rustc_type_ir/src/infer_ctxt.rs | 2 +- .../internal-lints/must_match_exhaustively.rs | 17 ++++++++--------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 9caed8bac4f83..4f7be16bb8eba 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -2,6 +2,7 @@ use either::{Left, Right}; use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout}; use rustc_hir::def_id::DefId; use rustc_hir::limit::Limit; +use rustc_middle::bug; use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{ @@ -249,7 +250,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | TypingMode::Analysis { .. } | TypingMode::Borrowck { .. } | TypingMode::PostBorrowckAnalysis { .. } => { - use rustc_middle::bug; bug!("Const eval should always happens in PostAnalysis mode."); } } diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 59334d4ebc747..ac6e345ec1de1 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -41,7 +41,7 @@ use crate::{self as ty, Interner, TyVid}; feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) )] -#[cfg_attr(not(bootstrap), rustc_must_match_exhaustively)] +#[cfg_attr(feature = "nightly", cfg_attr(not(bootstrap), rustc_must_match_exhaustively))] pub enum TypingMode { /// When checking whether impls overlap, we check whether any obligations /// are guaranteed to never hold when unifying the impls. This requires us diff --git a/tests/ui-fulldeps/internal-lints/must_match_exhaustively.rs b/tests/ui-fulldeps/internal-lints/must_match_exhaustively.rs index cb76c9754dc4d..cc3dcebd11cdd 100644 --- a/tests/ui-fulldeps/internal-lints/must_match_exhaustively.rs +++ b/tests/ui-fulldeps/internal-lints/must_match_exhaustively.rs @@ -5,44 +5,43 @@ #![feature(rustc_attrs)] #![deny(rustc::rustc_must_match_exhaustively)] - #[rustc_must_match_exhaustively] #[derive(Copy, Clone)] enum Foo { - A {field: u32}, + A { field: u32 }, B, } fn foo(f: Foo) { match f { - Foo::A {..}=> {} + Foo::A { .. } => {} Foo::B => {} } match f { //~^ ERROR match is not exhaustive - Foo::A {..} => {} + Foo::A { .. } => {} _ => {} } match f { //~^ ERROR match is not exhaustive - Foo::A {..} => {} + Foo::A { .. } => {} a => {} } match &f { //~^ ERROR match is not exhaustive - Foo::A {..} => {} + Foo::A { .. } => {} a => {} } match f { - Foo::A {..} => {} - a@Foo::B => {} + Foo::A { .. } => {} + a @ Foo::B => {} } - if let Foo::A {..} = f {} + if let Foo::A { .. } = f {} //~^ ERROR match is not exhaustive } From 18d118a7b7a82cc43267d11ed6a64a9afb02f482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Thu, 9 Apr 2026 17:19:56 +0200 Subject: [PATCH 5/6] fixup rustdoc,clippy,rustfmt --- .../src/interpret/eval_context.rs | 3 +-- src/librustdoc/core.rs | 5 +--- .../derive/derive_partial_eq_without_eq.rs | 6 ++--- .../must_match_exhaustively.stderr | 24 +++++++++---------- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 4f7be16bb8eba..466dcff359829 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -2,7 +2,6 @@ use either::{Left, Right}; use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout}; use rustc_hir::def_id::DefId; use rustc_hir::limit::Limit; -use rustc_middle::bug; use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{ @@ -13,7 +12,7 @@ use rustc_middle::ty::{ self, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingEnv, TypingMode, Variance, }; -use rustc_middle::{mir, span_bug}; +use rustc_middle::{bug, mir, span_bug}; use rustc_span::Span; use rustc_target::callconv::FnAbi; use tracing::{debug, trace}; diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 42d7237f68e53..3c5b1e55de644 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -87,10 +87,7 @@ impl<'tcx> DocContext<'tcx> { } pub(crate) fn typing_env(&self) -> ty::TypingEnv<'tcx> { - ty::TypingEnv { - typing_mode: ty::TypingMode::non_body_analysis(), - param_env: self.param_env, - } + ty::TypingEnv::new(self.param_env, ty::TypingMode::non_body_analysis()) } /// Call the closure with the given parameters set as diff --git a/src/tools/clippy/clippy_lints/src/derive/derive_partial_eq_without_eq.rs b/src/tools/clippy/clippy_lints/src/derive/derive_partial_eq_without_eq.rs index fbace0bd73acf..22943cd9ee5e2 100644 --- a/src/tools/clippy/clippy_lints/src/derive/derive_partial_eq_without_eq.rs +++ b/src/tools/clippy/clippy_lints/src/derive/derive_partial_eq_without_eq.rs @@ -85,8 +85,8 @@ fn typing_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> .upcast(tcx) }), ))); - ty::TypingEnv { - typing_mode: ty::TypingMode::non_body_analysis(), + ty::TypingEnv::new( param_env, - } + ty::TypingMode::non_body_analysis(), + ) } diff --git a/tests/ui-fulldeps/internal-lints/must_match_exhaustively.stderr b/tests/ui-fulldeps/internal-lints/must_match_exhaustively.stderr index 04f112bbe00b4..e17cfcefc4093 100644 --- a/tests/ui-fulldeps/internal-lints/must_match_exhaustively.stderr +++ b/tests/ui-fulldeps/internal-lints/must_match_exhaustively.stderr @@ -1,5 +1,5 @@ error: match is not exhaustive - --> $DIR/must_match_exhaustively.rs:22:11 + --> $DIR/must_match_exhaustively.rs:21:11 | LL | #[rustc_must_match_exhaustively] | -------------------------------- required because of this attribute @@ -9,7 +9,7 @@ LL | match f { | = help: explicitly list all variants of the enum in a `match` note: because of this wildcard pattern - --> $DIR/must_match_exhaustively.rs:25:9 + --> $DIR/must_match_exhaustively.rs:24:9 | LL | _ => {} | ^ @@ -20,7 +20,7 @@ LL | #![deny(rustc::rustc_must_match_exhaustively)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: match is not exhaustive - --> $DIR/must_match_exhaustively.rs:28:11 + --> $DIR/must_match_exhaustively.rs:27:11 | LL | #[rustc_must_match_exhaustively] | -------------------------------- required because of this attribute @@ -30,13 +30,13 @@ LL | match f { | = help: explicitly list all variants of the enum in a `match` note: because of this variable binding - --> $DIR/must_match_exhaustively.rs:31:9 + --> $DIR/must_match_exhaustively.rs:30:9 | LL | a => {} | ^ error: match is not exhaustive - --> $DIR/must_match_exhaustively.rs:34:11 + --> $DIR/must_match_exhaustively.rs:33:11 | LL | #[rustc_must_match_exhaustively] | -------------------------------- required because of this attribute @@ -46,26 +46,26 @@ LL | match &f { | = help: explicitly list all variants of the enum in a `match` note: because of this variable binding - --> $DIR/must_match_exhaustively.rs:37:9 + --> $DIR/must_match_exhaustively.rs:36:9 | LL | a => {} | ^ error: match is not exhaustive - --> $DIR/must_match_exhaustively.rs:45:8 + --> $DIR/must_match_exhaustively.rs:44:8 | LL | #[rustc_must_match_exhaustively] | -------------------------------- required because of this attribute ... -LL | if let Foo::A {..} = f {} - | ^^^^^^^^^^^^^^^^^^^ +LL | if let Foo::A { .. } = f {} + | ^^^^^^^^^^^^^^^^^^^^^ | = help: explicitly list all variants of the enum in a `match` note: using if let only matches on one variant (try using `match`) - --> $DIR/must_match_exhaustively.rs:45:8 + --> $DIR/must_match_exhaustively.rs:44:8 | -LL | if let Foo::A {..} = f {} - | ^^^^^^^^^^^^^^^^^^^ +LL | if let Foo::A { .. } = f {} + | ^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 4 previous errors From 376e5edf0670ef45f1cceb539db130cb474ec4f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Fri, 10 Apr 2026 13:16:00 +0200 Subject: [PATCH 6/6] use typing mode postanalysis in qualif in the next solver --- compiler/rustc_const_eval/src/check_consts/qualifs.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index a4f33ea05b2d3..9d54286d7eff3 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -105,7 +105,11 @@ impl Qualif for HasMutInterior { // opaque types. let typing_env = ty::TypingEnv::new( cx.typing_env.param_env, - ty::TypingMode::analysis_in_body(cx.tcx, cx.body.source.def_id().expect_local()), + if cx.tcx.next_trait_solver_globally() { + ty::TypingMode::PostAnalysis + } else { + ty::TypingMode::analysis_in_body(cx.tcx, cx.body.source.def_id().expect_local()) + }, ); let (infcx, param_env) = cx.tcx.infer_ctxt().build_with_typing_env(typing_env); let ocx = ObligationCtxt::new(&infcx);