use std::ops::ControlFlow; use rustc_data_structures::intern::Interned; use crate::infer::canonical::{CanonicalVarValues, QueryRegionConstraints}; use crate::traits::query::NoSolution; use crate::traits::{Canonical, DefiningAnchor}; use crate::ty::{ self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, }; use rustc_span::def_id::DefId; use super::BuiltinImplSource; mod cache; pub mod inspect; pub use cache::{CacheData, EvaluationCache}; /// A goal is a statement, i.e. `predicate`, we want to prove /// given some assumptions, i.e. `param_env`. /// /// Most of the time the `param_env` contains the `where`-bounds of the function /// we're currently typechecking while the `predicate` is some trait bound. #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)] pub struct Goal<'tcx, P> { pub predicate: P, pub param_env: ty::ParamEnv<'tcx>, } impl<'tcx, P> Goal<'tcx, P> { pub fn new( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, predicate: impl ToPredicate<'tcx, P>, ) -> Goal<'tcx, P> { Goal { param_env, predicate: predicate.to_predicate(tcx) } } /// Updates the goal to one with a different `predicate` but the same `param_env`. pub fn with(self, tcx: TyCtxt<'tcx>, predicate: impl ToPredicate<'tcx, Q>) -> Goal<'tcx, Q> { Goal { param_env: self.param_env, predicate: predicate.to_predicate(tcx) } } } #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)] pub struct Response<'tcx> { pub certainty: Certainty, pub var_values: CanonicalVarValues<'tcx>, /// Additional constraints returned by this query. pub external_constraints: ExternalConstraints<'tcx>, } #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)] pub enum Certainty { Yes, Maybe(MaybeCause), } impl Certainty { pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity); pub const OVERFLOW: Certainty = Certainty::Maybe(MaybeCause::Overflow); /// Use this function to merge the certainty of multiple nested subgoals. /// /// Given an impl like `impl Baz for T {}`, we have 2 nested /// subgoals whenever we use the impl as a candidate: `T: Foo` and `T: Bar`. /// If evaluating `T: Foo` results in ambiguity and `T: Bar` results in /// success, we merge these two responses. This results in ambiguity. /// /// If we unify ambiguity with overflow, we return overflow. This doesn't matter /// inside of the solver as we do not distinguish ambiguity from overflow. It does /// however matter for diagnostics. If `T: Foo` resulted in overflow and `T: Bar` /// in ambiguity without changing the inference state, we still want to tell the /// user that `T: Baz` results in overflow. pub fn unify_with(self, other: Certainty) -> Certainty { match (self, other) { (Certainty::Yes, Certainty::Yes) => Certainty::Yes, (Certainty::Yes, Certainty::Maybe(_)) => other, (Certainty::Maybe(_), Certainty::Yes) => self, (Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(MaybeCause::Ambiguity)) => { Certainty::Maybe(MaybeCause::Ambiguity) } (Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(MaybeCause::Overflow)) | (Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Ambiguity)) | (Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Overflow)) => { Certainty::Maybe(MaybeCause::Overflow) } } } } /// Why we failed to evaluate a goal. #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)] pub enum MaybeCause { /// We failed due to ambiguity. This ambiguity can either /// be a true ambiguity, i.e. there are multiple different answers, /// or we hit a case where we just don't bother, e.g. `?x: Trait` goals. Ambiguity, /// We gave up due to an overflow, most often by hitting the recursion limit. Overflow, } #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)] pub struct QueryInput<'tcx, T> { pub goal: Goal<'tcx, T>, pub anchor: DefiningAnchor, pub predefined_opaques_in_body: PredefinedOpaques<'tcx>, } /// Additional constraints returned on success. #[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default)] pub struct PredefinedOpaquesData<'tcx> { pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>, } #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, HashStable)] pub struct PredefinedOpaques<'tcx>(pub(crate) Interned<'tcx, PredefinedOpaquesData<'tcx>>); impl<'tcx> std::ops::Deref for PredefinedOpaques<'tcx> { type Target = PredefinedOpaquesData<'tcx>; fn deref(&self) -> &Self::Target { &self.0 } } pub type CanonicalInput<'tcx, T = ty::Predicate<'tcx>> = Canonical<'tcx, QueryInput<'tcx, T>>; pub type CanonicalResponse<'tcx> = Canonical<'tcx, Response<'tcx>>; /// The result of evaluating a canonical query. /// /// FIXME: We use a different type than the existing canonical queries. This is because /// we need to add a `Certainty` for `overflow` and may want to restructure this code without /// having to worry about changes to currently used code. Once we've made progress on this /// solver, merge the two responses again. pub type QueryResult<'tcx> = Result, NoSolution>; #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, HashStable)] pub struct ExternalConstraints<'tcx>(pub(crate) Interned<'tcx, ExternalConstraintsData<'tcx>>); impl<'tcx> std::ops::Deref for ExternalConstraints<'tcx> { type Target = ExternalConstraintsData<'tcx>; fn deref(&self) -> &Self::Target { &self.0 } } /// Additional constraints returned on success. #[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default, TypeVisitable, TypeFoldable)] pub struct ExternalConstraintsData<'tcx> { // FIXME: implement this. pub region_constraints: QueryRegionConstraints<'tcx>, pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>, } // FIXME: Having to clone `region_constraints` for folding feels bad and // probably isn't great wrt performance. // // Not sure how to fix this, maybe we should also intern `opaque_types` and // `region_constraints` here or something. impl<'tcx> TypeFoldable> for ExternalConstraints<'tcx> { fn try_fold_with>>( self, folder: &mut F, ) -> Result { Ok(FallibleTypeFolder::interner(folder).mk_external_constraints(ExternalConstraintsData { region_constraints: self.region_constraints.clone().try_fold_with(folder)?, opaque_types: self .opaque_types .iter() .map(|opaque| opaque.try_fold_with(folder)) .collect::>()?, })) } fn fold_with>>(self, folder: &mut F) -> Self { TypeFolder::interner(folder).mk_external_constraints(ExternalConstraintsData { region_constraints: self.region_constraints.clone().fold_with(folder), opaque_types: self.opaque_types.iter().map(|opaque| opaque.fold_with(folder)).collect(), }) } } impl<'tcx> TypeVisitable> for ExternalConstraints<'tcx> { fn visit_with>>( &self, visitor: &mut V, ) -> std::ops::ControlFlow { self.region_constraints.visit_with(visitor)?; self.opaque_types.visit_with(visitor)?; ControlFlow::Continue(()) } } // FIXME: Having to clone `region_constraints` for folding feels bad and // probably isn't great wrt performance. // // Not sure how to fix this, maybe we should also intern `opaque_types` and // `region_constraints` here or something. impl<'tcx> TypeFoldable> for PredefinedOpaques<'tcx> { fn try_fold_with>>( self, folder: &mut F, ) -> Result { Ok(FallibleTypeFolder::interner(folder).mk_predefined_opaques_in_body( PredefinedOpaquesData { opaque_types: self .opaque_types .iter() .map(|opaque| opaque.try_fold_with(folder)) .collect::>()?, }, )) } fn fold_with>>(self, folder: &mut F) -> Self { TypeFolder::interner(folder).mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types: self.opaque_types.iter().map(|opaque| opaque.fold_with(folder)).collect(), }) } } impl<'tcx> TypeVisitable> for PredefinedOpaques<'tcx> { fn visit_with>>( &self, visitor: &mut V, ) -> std::ops::ControlFlow { self.opaque_types.visit_with(visitor) } } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable)] pub enum IsNormalizesToHack { Yes, No, } /// Possible ways the given goal can be proven. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CandidateSource { /// A user written impl. /// /// ## Examples /// /// ```rust /// fn main() { /// let x: Vec = Vec::new(); /// // This uses the impl from the standard library to prove `Vec: Clone`. /// let y = x.clone(); /// } /// ``` Impl(DefId), /// A builtin impl generated by the compiler. When adding a new special /// trait, try to use actual impls whenever possible. Builtin impls should /// only be used in cases where the impl cannot be manually be written. /// /// Notable examples are auto traits, `Sized`, and `DiscriminantKind`. /// For a list of all traits with builtin impls, check out the /// `EvalCtxt::assemble_builtin_impl_candidates` method. BuiltinImpl(BuiltinImplSource), /// An assumption from the environment. /// /// More precisely we've used the `n-th` assumption in the `param_env`. /// /// ## Examples /// /// ```rust /// fn is_clone(x: T) -> (T, T) { /// // This uses the assumption `T: Clone` from the `where`-bounds /// // to prove `T: Clone`. /// (x.clone(), x) /// } /// ``` ParamEnv(usize), /// If the self type is an alias type, e.g. an opaque type or a projection, /// we know the bounds on that alias to hold even without knowing its concrete /// underlying type. /// /// More precisely this candidate is using the `n-th` bound in the `item_bounds` of /// the self type. /// /// ## Examples /// /// ```rust /// trait Trait { /// type Assoc: Clone; /// } /// /// fn foo(x: ::Assoc) { /// // We prove `::Assoc` by looking at the bounds on `Assoc` in /// // in the trait definition. /// let _y = x.clone(); /// } /// ``` AliasBound, }