| //! Candidate selection. 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#selection | |
| use rustc_errors::ErrorGuaranteed; | |
| use rustc_hir::def_id::DefId; | |
| use rustc_macros::{HashStable, TypeVisitable}; | |
| use rustc_type_ir::solve::AliasBoundKind; | |
| use self::EvaluationResult::*; | |
| use super::{SelectionError, SelectionResult}; | |
| use crate::traits::cache::WithDepNodeCache; | |
| use crate::ty; | |
| pub type SelectionCache<'tcx, ENV> = WithDepNodeCache< | |
| (ENV, ty::TraitPredicate<'tcx>), | |
| SelectionResult<'tcx, SelectionCandidate<'tcx>>, | |
| >; | |
| pub type EvaluationCache<'tcx, ENV> = | |
| WithDepNodeCache<(ENV, ty::PolyTraitPredicate<'tcx>), EvaluationResult>; | |
| /// The selection process begins by considering all impls, where | |
| /// clauses, and so forth that might resolve an obligation. Sometimes | |
| /// we'll be able to say definitively that (e.g.) an impl does not | |
| /// apply to the obligation: perhaps it is defined for `usize` but the | |
| /// obligation is for `i32`. In that case, we drop the impl out of the | |
| /// list. But the other cases are considered *candidates*. | |
| /// | |
| /// For selection to succeed, there must be exactly one matching | |
| /// candidate. If the obligation is fully known, this is guaranteed | |
| /// by coherence. However, if the obligation contains type parameters | |
| /// or variables, there may be multiple such impls. | |
| /// | |
| /// It is not a real problem if multiple matching impls exist because | |
| /// of type variables - it just means the obligation isn't sufficiently | |
| /// elaborated. In that case we report an ambiguity, and the caller can | |
| /// try again after more type information has been gathered or report a | |
| /// "type annotations needed" error. | |
| /// | |
| /// However, with type parameters, this can be a real problem - type | |
| /// parameters don't unify with regular types, but they *can* unify | |
| /// with variables from blanket impls, and (unless we know its bounds | |
| /// will always be satisfied) picking the blanket impl will be wrong | |
| /// for at least *some* generic parameters. To make this concrete, if | |
| /// we have | |
| /// | |
| /// ```rust, ignore | |
| /// trait AsDebug { type Out: fmt::Debug; fn debug(self) -> Self::Out; } | |
| /// impl<T: fmt::Debug> AsDebug for T { | |
| /// type Out = T; | |
| /// fn debug(self) -> fmt::Debug { self } | |
| /// } | |
| /// fn foo<T: AsDebug>(t: T) { println!("{:?}", <T as AsDebug>::debug(t)); } | |
| /// ``` | |
| /// | |
| /// we can't just use the impl to resolve the `<T as AsDebug>` obligation | |
| /// -- a type from another crate (that doesn't implement `fmt::Debug`) could | |
| /// implement `AsDebug`. | |
| /// | |
| /// Because where-clauses match the type exactly, multiple clauses can | |
| /// only match if there are unresolved variables, and we can mostly just | |
| /// report this ambiguity in that case. This is still a problem - we can't | |
| /// *do anything* with ambiguities that involve only regions. This is issue | |
| /// #21974. | |
| /// | |
| /// If a single where-clause matches and there are no inference | |
| /// variables left, then it definitely matches and we can just select | |
| /// it. | |
| /// | |
| /// In fact, we even select the where-clause when the obligation contains | |
| /// inference variables. The can lead to inference making "leaps of logic", | |
| /// for example in this situation: | |
| /// | |
| /// ```rust, ignore | |
| /// pub trait Foo<T> { fn foo(&self) -> T; } | |
| /// impl<T> Foo<()> for T { fn foo(&self) { } } | |
| /// impl Foo<bool> for bool { fn foo(&self) -> bool { *self } } | |
| /// | |
| /// pub fn foo<T>(t: T) where T: Foo<bool> { | |
| /// println!("{:?}", <T as Foo<_>>::foo(&t)); | |
| /// } | |
| /// fn main() { foo(false); } | |
| /// ``` | |
| /// | |
| /// Here the obligation `<T as Foo<$0>>` can be matched by both the blanket | |
| /// impl and the where-clause. We select the where-clause and unify `$0=bool`, | |
| /// so the program prints "false". However, if the where-clause is omitted, | |
| /// the blanket impl is selected, we unify `$0=()`, and the program prints | |
| /// "()". | |
| /// | |
| /// Exactly the same issues apply to projection and object candidates, except | |
| /// that we can have both a projection candidate and a where-clause candidate | |
| /// for the same obligation. In that case either would do (except that | |
| /// different "leaps of logic" would occur if inference variables are | |
| /// present), and we just pick the where-clause. This is, for example, | |
| /// required for associated types to work in default impls, as the bounds | |
| /// are visible both as projection bounds and as where-clauses from the | |
| /// parameter environment. | |
| #[derive(PartialEq, Eq, Debug, Clone, TypeVisitable)] | |
| pub enum SelectionCandidate<'tcx> { | |
| /// A built-in implementation for the `Sized` trait. This is preferred | |
| /// over all other candidates. | |
| SizedCandidate, | |
| /// A builtin implementation for some specific traits, used in cases | |
| /// where we cannot rely an ordinary library implementations. | |
| /// | |
| /// The most notable examples are `Copy` and `Clone`. This is also | |
| /// used for the `DiscriminantKind` and `Pointee` trait, both of which have | |
| /// an associated type. | |
| BuiltinCandidate, | |
| /// Implementation of transmutability trait. | |
| TransmutabilityCandidate, | |
| ParamCandidate(ty::PolyTraitPredicate<'tcx>), | |
| ImplCandidate(DefId), | |
| AutoImplCandidate, | |
| /// This is a trait matching with a projected type as `Self`, and we found | |
| /// an applicable bound in the trait definition. The `usize` is an index | |
| /// into the list returned by `tcx.item_bounds` and the `AliasBoundKind` | |
| /// is whether this is candidate from recursion on the self type of a | |
| /// projection. | |
| ProjectionCandidate { | |
| idx: usize, | |
| kind: AliasBoundKind, | |
| }, | |
| /// Implementation of a `Fn`-family trait by one of the anonymous types | |
| /// generated for an `||` expression. | |
| ClosureCandidate { | |
| is_const: bool, | |
| }, | |
| /// Implementation of an `AsyncFn`-family trait by one of the anonymous types | |
| /// generated for an `async ||` expression. | |
| AsyncClosureCandidate, | |
| /// Implementation of the `AsyncFnKindHelper` helper trait, which | |
| /// is used internally to delay computation for async closures until after | |
| /// upvar analysis is performed in HIR typeck. | |
| AsyncFnKindHelperCandidate, | |
| /// Implementation of a `Coroutine` trait by one of the anonymous types | |
| /// generated for a coroutine. | |
| CoroutineCandidate, | |
| /// Implementation of a `Future` trait by one of the coroutine types | |
| /// generated for an async construct. | |
| FutureCandidate, | |
| /// Implementation of an `Iterator` trait by one of the coroutine types | |
| /// generated for a `gen` construct. | |
| IteratorCandidate, | |
| /// Implementation of an `AsyncIterator` trait by one of the coroutine types | |
| /// generated for a `async gen` construct. | |
| AsyncIteratorCandidate, | |
| /// Implementation of a `Fn`-family trait by one of the anonymous | |
| /// types generated for a fn pointer type (e.g., `fn(int) -> int`) | |
| FnPointerCandidate, | |
| TraitAliasCandidate, | |
| /// Matching `dyn Trait` with a supertrait of `Trait`. The index is the | |
| /// position in the iterator returned by | |
| /// `rustc_infer::traits::util::supertraits`. | |
| ObjectCandidate(usize), | |
| /// Perform trait upcasting coercion of `dyn Trait` to a supertrait of `Trait`. | |
| /// The index is the position in the iterator returned by | |
| /// `rustc_infer::traits::util::supertraits`. | |
| TraitUpcastingUnsizeCandidate(usize), | |
| BuiltinObjectCandidate, | |
| BuiltinUnsizeCandidate, | |
| BikeshedGuaranteedNoDropCandidate, | |
| } | |
| /// The result of trait evaluation. The order is important | |
| /// here as the evaluation of a list is the maximum of the | |
| /// evaluations. | |
| /// | |
| /// The evaluation results are ordered: | |
| /// - `EvaluatedToOk` implies `EvaluatedToOkModuloRegions` | |
| /// implies `EvaluatedToAmbig` implies `EvaluatedToAmbigStackDependent` | |
| /// - the "union" of evaluation results is equal to their maximum - | |
| /// all the "potential success" candidates can potentially succeed, | |
| /// so they are noops when unioned with a definite error, and within | |
| /// the categories it's easy to see that the unions are correct. | |
| #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, HashStable)] | |
| pub enum EvaluationResult { | |
| /// Evaluation successful. | |
| EvaluatedToOk, | |
| /// Evaluation successful, but there were unevaluated region obligations. | |
| EvaluatedToOkModuloRegions, | |
| /// Evaluation successful, but need to rerun because opaque types got | |
| /// hidden types assigned without it being known whether the opaque types | |
| /// are within their defining scope | |
| EvaluatedToOkModuloOpaqueTypes, | |
| /// Evaluation is known to be ambiguous -- it *might* hold for some | |
| /// assignment of inference variables, but it might not. | |
| /// | |
| /// While this has the same meaning as `EvaluatedToAmbigStackDependent` -- we can't | |
| /// know whether this obligation holds or not -- it is the result we | |
| /// would get with an empty stack, and therefore is cacheable. | |
| EvaluatedToAmbig, | |
| /// Evaluation failed because of recursion involving inference | |
| /// variables. We are somewhat imprecise there, so we don't actually | |
| /// know the real result. | |
| /// | |
| /// This can't be trivially cached because the result depends on the | |
| /// stack results. | |
| EvaluatedToAmbigStackDependent, | |
| /// Evaluation failed. | |
| EvaluatedToErr, | |
| } | |
| impl EvaluationResult { | |
| /// Returns `true` if this evaluation result is known to apply, even | |
| /// considering outlives constraints. | |
| pub fn must_apply_considering_regions(self) -> bool { | |
| self == EvaluatedToOk | |
| } | |
| /// Returns `true` if this evaluation result is known to apply, ignoring | |
| /// outlives constraints. | |
| pub fn must_apply_modulo_regions(self) -> bool { | |
| self <= EvaluatedToOkModuloRegions | |
| } | |
| pub fn may_apply(self) -> bool { | |
| match self { | |
| EvaluatedToOkModuloOpaqueTypes | |
| | EvaluatedToOk | |
| | EvaluatedToOkModuloRegions | |
| | EvaluatedToAmbig | |
| | EvaluatedToAmbigStackDependent => true, | |
| EvaluatedToErr => false, | |
| } | |
| } | |
| pub fn is_stack_dependent(self) -> bool { | |
| match self { | |
| EvaluatedToAmbigStackDependent => true, | |
| EvaluatedToOkModuloOpaqueTypes | |
| | EvaluatedToOk | |
| | EvaluatedToOkModuloRegions | |
| | EvaluatedToAmbig | |
| | EvaluatedToErr => false, | |
| } | |
| } | |
| } | |
| /// Indicates that trait evaluation caused overflow and in which pass. | |
| #[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable)] | |
| pub enum OverflowError { | |
| Error(ErrorGuaranteed), | |
| Canonical, | |
| } | |
| impl From<ErrorGuaranteed> for OverflowError { | |
| fn from(e: ErrorGuaranteed) -> OverflowError { | |
| OverflowError::Error(e) | |
| } | |
| } | |
| impl<'tcx> From<OverflowError> for SelectionError<'tcx> { | |
| fn from(overflow_error: OverflowError) -> SelectionError<'tcx> { | |
| match overflow_error { | |
| OverflowError::Error(e) => SelectionError::Overflow(OverflowError::Error(e)), | |
| OverflowError::Canonical => SelectionError::Overflow(OverflowError::Canonical), | |
| } | |
| } | |
| } |