//! Trait Resolution. See the [rustc-dev-guide] for more information on how this works. //! //! [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html use std::{ cmp, hash::{Hash, Hasher}, }; use hir_def::TraitId; use macros::{TypeFoldable, TypeVisitable}; use rustc_type_ir::elaborate::Elaboratable; use rustc_type_ir::{ Upcast, solve::{Certainty, NoSolution, inspect}, }; use tracing::debug; use crate::next_solver::{ Clause, DbInterner, Goal, ParamEnv, PolyTraitPredicate, Predicate, Span, TraitPredicate, TraitRef, Ty, }; use super::InferCtxt; /// The reason why we incurred this obligation; used for error reporting. /// /// Non-misc `ObligationCauseCode`s are stored on the heap. This gives the /// best trade-off between keeping the type small (which makes copies cheaper) /// while not doing too many heap allocations. /// /// We do not want to intern this as there are a lot of obligation causes which /// only live for a short period of time. #[derive(Clone, Debug, PartialEq, Eq)] pub struct ObligationCause { // FIXME: This should contain an `ExprId`/`PatId` etc., and a cause code. But for now we // don't report trait solving diagnostics, so this is irrelevant. _private: (), } impl ObligationCause { #[inline] pub fn new() -> ObligationCause { ObligationCause { _private: () } } #[inline] pub fn dummy() -> ObligationCause { ObligationCause::new() } #[inline] pub fn misc() -> ObligationCause { ObligationCause::new() } } impl Default for ObligationCause { #[inline] fn default() -> Self { Self::new() } } /// An `Obligation` represents some trait reference (e.g., `i32: Eq`) for /// which the "impl_source" must be found. The process of finding an "impl_source" is /// called "resolving" the `Obligation`. This process consists of /// either identifying an `impl` (e.g., `impl Eq for i32`) that /// satisfies the obligation, or else finding a bound that is in /// scope. The eventual result is usually a `Selection` (defined below). #[derive(Clone, Debug, TypeVisitable, TypeFoldable)] pub struct Obligation<'db, T> { #[type_foldable(identity)] #[type_visitable(ignore)] /// The reason we have to prove this thing. pub cause: ObligationCause, /// The environment in which we should prove this thing. pub param_env: ParamEnv<'db>, /// The thing we are trying to prove. pub predicate: T, /// If we started proving this as a result of trying to prove /// something else, track the total depth to ensure termination. /// If this goes over a certain threshold, we abort compilation -- /// in such cases, we can not say whether or not the predicate /// holds for certain. Stupid halting problem; such a drag. pub recursion_depth: usize, } /// A callback that can be provided to `inspect_typeck`. Invoked on evaluation /// of root obligations. pub type ObligationInspector<'db> = fn( &InferCtxt<'db>, &PredicateObligation<'db>, Result, Option>>, ); /// For [`Obligation`], a sub-obligation is combined with the current obligation's /// param-env and cause code. impl<'db> Elaboratable> for PredicateObligation<'db> { fn predicate(&self) -> Predicate<'db> { self.predicate } fn child(&self, clause: Clause<'db>) -> Self { Obligation { cause: self.cause.clone(), param_env: self.param_env, recursion_depth: 0, predicate: clause.as_predicate(), } } fn child_with_derived_cause( &self, clause: Clause<'db>, _span: Span, _parent_trait_pred: PolyTraitPredicate<'db>, _index: usize, ) -> Self { let cause = ObligationCause::new(); Obligation { cause, param_env: self.param_env, recursion_depth: 0, predicate: clause.as_predicate(), } } } impl<'db, T: Copy> Obligation<'db, T> { pub fn as_goal(&self) -> Goal<'db, T> { Goal { param_env: self.param_env, predicate: self.predicate } } } impl<'db, T: PartialEq> PartialEq> for Obligation<'db, T> { #[inline] fn eq(&self, other: &Obligation<'db, T>) -> bool { // Ignore `cause` and `recursion_depth`. This is a small performance // win for a few crates, and a huge performance win for the crate in // https://github.com/rust-lang/rustc-perf/pull/1680, which greatly // stresses the trait system. self.param_env == other.param_env && self.predicate == other.predicate } } impl<'db, T: Eq> Eq for Obligation<'db, T> {} impl<'db, T: Hash> Hash for Obligation<'db, T> { fn hash(&self, state: &mut H) { // See the comment on `Obligation::eq`. self.param_env.hash(state); self.predicate.hash(state); } } impl<'db, P> From> for Goal<'db, P> { fn from(value: Obligation<'db, P>) -> Self { Goal { param_env: value.param_env, predicate: value.predicate } } } pub(crate) type PredicateObligation<'db> = Obligation<'db, Predicate<'db>>; pub(crate) type TraitObligation<'db> = Obligation<'db, TraitPredicate<'db>>; pub(crate) type PredicateObligations<'db> = Vec>; impl<'db> PredicateObligation<'db> { /// Flips the polarity of the inner predicate. /// /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. pub fn flip_polarity(&self, _interner: DbInterner<'db>) -> Option> { Some(PredicateObligation { cause: self.cause.clone(), param_env: self.param_env, predicate: self.predicate.flip_polarity()?, recursion_depth: self.recursion_depth, }) } } impl<'db, O> Obligation<'db, O> { pub fn new( tcx: DbInterner<'db>, cause: ObligationCause, param_env: ParamEnv<'db>, predicate: impl Upcast, O>, ) -> Obligation<'db, O> { Self::with_depth(tcx, cause, 0, param_env, predicate) } /// We often create nested obligations without setting the correct depth. /// /// To deal with this evaluate and fulfill explicitly update the depth /// of nested obligations using this function. pub fn set_depth_from_parent(&mut self, parent_depth: usize) { self.recursion_depth = cmp::max(parent_depth + 1, self.recursion_depth); } pub fn with_depth( tcx: DbInterner<'db>, cause: ObligationCause, recursion_depth: usize, param_env: ParamEnv<'db>, predicate: impl Upcast, O>, ) -> Obligation<'db, O> { let predicate = predicate.upcast(tcx); Obligation { cause, param_env, recursion_depth, predicate } } pub fn with

( &self, tcx: DbInterner<'db>, value: impl Upcast, P>, ) -> Obligation<'db, P> { Obligation::with_depth(tcx, self.cause.clone(), self.recursion_depth, self.param_env, value) } } /// Determines whether the type `ty` is known to meet `bound` and /// returns true if so. Returns false if `ty` either does not meet /// `bound` or is not known to meet bound (note that this is /// conservative towards *no impl*, which is the opposite of the /// `evaluate` methods). pub(crate) fn type_known_to_meet_bound_modulo_regions<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>, def_id: TraitId, ) -> bool { let trait_ref = TraitRef::new(infcx.interner, def_id.into(), [ty]); pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref) } /// FIXME(@lcnr): this function doesn't seem right and shouldn't exist? /// /// Ping me on zulip if you want to use this method and need help with finding /// an appropriate replacement. fn pred_known_to_hold_modulo_regions<'db>( infcx: &InferCtxt<'db>, param_env: ParamEnv<'db>, pred: impl Upcast, Predicate<'db>>, ) -> bool { let obligation = Obligation::new(infcx.interner, ObligationCause::dummy(), param_env, pred); let result = infcx.evaluate_obligation(&obligation); debug!(?result); result.must_apply_modulo_regions() }