diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 1fbd0cd23405..d068b4bc84cf 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_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 @@ fn in_any_value_of_ty<'tcx>(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { // 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 ccfdf571fb27..351eceb48d17 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 4323debd8014..5928ad89fd69 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 04f0e7099d84..20f85b7bf407 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 @@ pub fn new( // 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 87d389a5dea5..c88938158675 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -13,6 +13,7 @@ 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 @@ pub fn canonicalize_query( 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 583eb1a6dbc4..b18d820f0829 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -33,6 +33,7 @@ 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 @@ pub fn build_with_canonical( 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 @@ pub fn build_with_typing_env( 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 @@ pub fn typing_env(&self, param_env: ty::ParamEnv<'tcx>) -> ty::TypingEnv<'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 752c2220d414..3372dc70c520 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -24,8 +24,12 @@ 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 @@ pub fn typing_mode(&self) -> TypingMode<'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 1c14b94b8d7d..c5fa3464b8a0 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -33,7 +33,7 @@ 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 @@ 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: 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 dbf7d643a42c..05df583907ca 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -105,7 +105,8 @@ 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 @@ pub fn non_body_analysis( 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 @@ pub fn post_analysis(tcx: TyCtxt<'tcx>, def_id: impl IntoQueryKey) -> Typ /// 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 @@ pub fn with_post_analysis_normalized(self, tcx: TyCtxt<'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 08bfe15137a2..9164f7b57e64 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -40,6 +40,7 @@ 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 1fe745a5d519..fef2a787414b 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -548,7 +548,16 @@ fn move_paths_for_fields( 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 1f64f09fe787..7fdbfa023af4 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::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 @@ pub(super) fn canonicalize_goal( 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 a4f331d3fe71..8d855be72025 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 @@ pub(super) fn assemble_and_evaluate_candidates>( // 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 73044b7943ae..0490b285aedf 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 @@ fn initial_provisional_result( // 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 45e0b5d74af7..328d4217072d 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 cf881961b3ce..8f6139bee27b 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 8e7414674c6e..be5e483c808d 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -11,7 +11,7 @@ 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 @@ )] 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 feafcee7bad9..1bf2e5378a1f 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 @@ /// /// 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 @@ pub fn borrowck(cx: I, body_def_id: I::LocalDefId) -> 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 @@ pub fn may_use_unstable_feature<'a, I: Interner, Infcx>( // 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, + } }