#![expect(dead_code, reason = "this is used by rustc")]
use std::ops::ControlFlow;
use hir_def::TraitId;
use macros::{TypeFoldable, TypeVisitable};
use rustc_type_ir::{
Interner,
solve::{BuiltinImplSource, CandidateSource, Certainty, inspect::ProbeKind},
};
use crate::{
db::InternedOpaqueTyId,
next_solver::{
AnyImplId, Const, ErrorGuaranteed, GenericArgs, Goal, TraitRef, Ty, TypeError,
infer::{
InferCtxt,
select::EvaluationResult::*,
traits::{Obligation, ObligationCause, PredicateObligation, TraitObligation},
},
inspect::{InspectCandidate, InspectGoal, ProofTreeVisitor},
},
};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SelectionError<'db> {
/// The trait is not implemented.
Unimplemented,
/// After a closure impl has selected, its "outputs" were evaluated
/// (which for closures includes the "input" type params) and they
/// didn't resolve. See `confirm_poly_trait_refs` for more.
SignatureMismatch(Box<SignatureMismatchData<'db>>),
/// The trait pointed by `DefId` is dyn-incompatible.
TraitDynIncompatible(TraitId),
/// A given constant couldn't be evaluated.
NotConstEvaluatable(NotConstEvaluatable),
/// Exceeded the recursion depth during type projection.
Overflow(OverflowError),
/// Computing an opaque type's hidden type caused an error (e.g. a cycle error).
/// We can thus not know whether the hidden type implements an auto trait, so
/// we should not presume anything about it.
OpaqueTypeAutoTraitLeakageUnknown(InternedOpaqueTyId),
/// Error for a `ConstArgHasType` goal
ConstArgHasWrongType { ct: Const<'db>, ct_ty: Ty<'db>, expected_ty: Ty<'db> },
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum NotConstEvaluatable {
Error(ErrorGuaranteed),
MentionsInfer,
MentionsParam,
}
/// 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)]
pub(crate) 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(crate) fn must_apply_considering_regions(self) -> bool {
self == EvaluatedToOk
}
/// Returns `true` if this evaluation result is known to apply, ignoring
/// outlives constraints.
pub(crate) fn must_apply_modulo_regions(self) -> bool {
self <= EvaluatedToOkModuloRegions
}
pub(crate) fn may_apply(self) -> bool {
match self {
EvaluatedToOkModuloOpaqueTypes
| EvaluatedToOk
| EvaluatedToOkModuloRegions
| EvaluatedToAmbig
| EvaluatedToAmbigStackDependent => true,
EvaluatedToErr => false,
}
}
pub(crate) 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, Hash)]
pub enum OverflowError {
Error(ErrorGuaranteed),
Canonical,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SignatureMismatchData<'db> {
pub(crate) found_trait_ref: TraitRef<'db>,
pub(crate) expected_trait_ref: TraitRef<'db>,
pub(crate) terr: TypeError<'db>,
}
/// When performing resolution, it is typically the case that there
/// can be one of three outcomes:
///
/// - `Ok(Some(r))`: success occurred with result `r`
/// - `Ok(None)`: could not definitely determine anything, usually due
/// to inconclusive type inference.
/// - `Err(e)`: error `e` occurred
pub(crate) type SelectionResult<'db, T> = Result<Option<T>, SelectionError<'db>>;
/// Given the successful resolution of an obligation, the `ImplSource`
/// indicates where the impl comes from.
///
/// For example, the obligation may be satisfied by a specific impl (case A),
/// or it may be relative to some bound that is in scope (case B).
///
/// ```ignore (illustrative)
/// impl<T:Clone> Clone<T> for Option<T> { ... } // Impl_1
/// impl<T:Clone> Clone<T> for Box<T> { ... } // Impl_2
/// impl Clone for i32 { ... } // Impl_3
///
/// fn foo<T: Clone>(concrete: Option<Box<i32>>, param: T, mixed: Option<T>) {
/// // Case A: ImplSource points at a specific impl. Only possible when
/// // type is concretely known. If the impl itself has bounded
/// // type parameters, ImplSource will carry resolutions for those as well:
/// concrete.clone(); // ImplSource(Impl_1, [ImplSource(Impl_2, [ImplSource(Impl_3)])])
///
/// // Case B: ImplSource must be provided by caller. This applies when
/// // type is a type parameter.
/// param.clone(); // ImplSource::Param
///
/// // Case C: A mix of cases A and B.
/// mixed.clone(); // ImplSource(Impl_1, [ImplSource::Param])
/// }
/// ```
///
/// ### The type parameter `N`
///
/// See explanation on `ImplSourceUserDefinedData`.
#[derive(Debug, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)]
pub(crate) enum ImplSource<'db, N> {
/// ImplSource identifying a particular impl.
UserDefined(ImplSourceUserDefinedData<'db, N>),
/// Successful resolution to an obligation provided by the caller
/// for some type parameter. The `Vec<N>` represents the
/// obligations incurred from normalizing the where-clause (if
/// any).
Param(Vec<N>),
/// Successful resolution for a builtin impl.
Builtin(BuiltinImplSource, Vec<N>),
}
impl<'db, N> ImplSource<'db, N> {
pub(crate) fn nested_obligations(self) -> Vec<N> {
match self {
ImplSource::UserDefined(i) => i.nested,
ImplSource::Param(n) | ImplSource::Builtin(_, n) => n,
}
}
pub(crate) fn borrow_nested_obligations(&self) -> &[N] {
match self {
ImplSource::UserDefined(i) => &i.nested,
ImplSource::Param(n) | ImplSource::Builtin(_, n) => n,
}
}
pub(crate) fn borrow_nested_obligations_mut(&mut self) -> &mut [N] {
match self {
ImplSource::UserDefined(i) => &mut i.nested,
ImplSource::Param(n) | ImplSource::Builtin(_, n) => n,
}
}
pub(crate) fn map<M, F>(self, f: F) -> ImplSource<'db, M>
where
F: FnMut(N) -> M,
{
match self {
ImplSource::UserDefined(i) => ImplSource::UserDefined(ImplSourceUserDefinedData {
impl_def_id: i.impl_def_id,
args: i.args,
nested: i.nested.into_iter().map(f).collect(),
}),
ImplSource::Param(n) => ImplSource::Param(n.into_iter().map(f).collect()),
ImplSource::Builtin(source, n) => {
ImplSource::Builtin(source, n.into_iter().map(f).collect())
}
}
}
}
/// Identifies a particular impl in the source, along with a set of
/// generic parameters from the impl's type/lifetime parameters. The
/// `nested` vector corresponds to the nested obligations attached to
/// the impl's type parameters.
///
/// The type parameter `N` indicates the type used for "nested
/// obligations" that are required by the impl. During type-check, this
/// is `Obligation`, as one might expect. During codegen, however, this
/// is `()`, because codegen only requires a shallow resolution of an
/// impl, and nested obligations are satisfied later.
#[derive(Debug, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)]
pub(crate) struct ImplSourceUserDefinedData<'db, N> {
#[type_visitable(ignore)]
#[type_foldable(identity)]
pub(crate) impl_def_id: AnyImplId,
pub(crate) args: GenericArgs<'db>,
pub(crate) nested: Vec<N>,
}
pub(crate) type Selection<'db> = ImplSource<'db, PredicateObligation<'db>>;
impl<'db> InferCtxt<'db> {
pub(crate) fn select(
&self,
obligation: &TraitObligation<'db>,
) -> SelectionResult<'db, Selection<'db>> {
self.visit_proof_tree(
Goal::new(self.interner, obligation.param_env, obligation.predicate),
&mut Select {},
)
.break_value()
.unwrap()
}
}
struct Select {}
impl<'db> ProofTreeVisitor<'db> for Select {
type Result = ControlFlow<SelectionResult<'db, Selection<'db>>>;
fn visit_goal(&mut self, goal: &InspectGoal<'_, 'db>) -> Self::Result {
let mut candidates = goal.candidates();
candidates.retain(|cand| cand.result().is_ok());
// No candidates -- not implemented.
if candidates.is_empty() {
return ControlFlow::Break(Err(SelectionError::Unimplemented));
}
// One candidate, no need to winnow.
if candidates.len() == 1 {
return ControlFlow::Break(Ok(to_selection(candidates.into_iter().next().unwrap())));
}
// Don't winnow until `Certainty::Yes` -- we don't need to winnow until
// codegen, and only on the good path.
if matches!(goal.result().unwrap(), Certainty::Maybe { .. }) {
return ControlFlow::Break(Ok(None));
}
// We need to winnow. See comments on `candidate_should_be_dropped_in_favor_of`.
let mut i = 0;
while i < candidates.len() {
let should_drop_i = (0..candidates.len())
.filter(|&j| i != j)
.any(|j| candidate_should_be_dropped_in_favor_of(&candidates[i], &candidates[j]));
if should_drop_i {
candidates.swap_remove(i);
} else {
i += 1;
if i > 1 {
return ControlFlow::Break(Ok(None));
}
}
}
ControlFlow::Break(Ok(to_selection(candidates.into_iter().next().unwrap())))
}
}
/// This is a lot more limited than the old solver's equivalent method. This may lead to more `Ok(None)`
/// results when selecting traits in polymorphic contexts, but we should never rely on the lack of ambiguity,
/// and should always just gracefully fail here. We shouldn't rely on this incompleteness.
fn candidate_should_be_dropped_in_favor_of<'db>(
victim: &InspectCandidate<'_, 'db>,
other: &InspectCandidate<'_, 'db>,
) -> bool {
// Don't winnow until `Certainty::Yes` -- we don't need to winnow until
// codegen, and only on the good path.
if matches!(other.result().unwrap(), Certainty::Maybe { .. }) {
return false;
}
let ProbeKind::TraitCandidate { source: victim_source, result: _ } = victim.kind() else {
return false;
};
let ProbeKind::TraitCandidate { source: other_source, result: _ } = other.kind() else {
return false;
};
match (victim_source, other_source) {
(_, CandidateSource::CoherenceUnknowable) | (CandidateSource::CoherenceUnknowable, _) => {
panic!("should not have assembled a CoherenceUnknowable candidate")
}
// In the old trait solver, we arbitrarily choose lower vtable candidates
// over higher ones.
(
CandidateSource::BuiltinImpl(BuiltinImplSource::Object(a)),
CandidateSource::BuiltinImpl(BuiltinImplSource::Object(b)),
) => a >= b,
(
CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting(a)),
CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting(b)),
) => a >= b,
// Prefer dyn candidates over non-dyn candidates. This is necessary to
// handle the unsoundness between `impl<T: ?Sized> Any for T` and `dyn Any: Any`.
(
CandidateSource::Impl(_)
| CandidateSource::ParamEnv(_)
| CandidateSource::AliasBound(_),
CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }),
) => true,
// Prefer specializing candidates over specialized candidates.
(CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => {
victim.goal().infcx().interner.impl_specializes(other_def_id, victim_def_id)
}
_ => false,
}
}
fn to_selection<'db>(cand: InspectCandidate<'_, 'db>) -> Option<Selection<'db>> {
if let Certainty::Maybe { .. } = cand.shallow_certainty() {
return None;
}
let nested = match cand.result().expect("expected positive result") {
Certainty::Yes => Vec::new(),
Certainty::Maybe { .. } => cand
.instantiate_nested_goals()
.into_iter()
.map(|nested| {
Obligation::new(
nested.infcx().interner,
ObligationCause::dummy(),
nested.goal().param_env,
nested.goal().predicate,
)
})
.collect(),
};
Some(match cand.kind() {
ProbeKind::TraitCandidate { source, result: _ } => match source {
CandidateSource::Impl(impl_def_id) => {
// FIXME: Remove this in favor of storing this in the tree
// For impl candidates, we do the rematch manually to compute the args.
ImplSource::UserDefined(ImplSourceUserDefinedData {
impl_def_id,
args: cand.instantiate_impl_args(),
nested,
})
}
CandidateSource::BuiltinImpl(builtin) => ImplSource::Builtin(builtin, nested),
CandidateSource::ParamEnv(_) | CandidateSource::AliasBound(_) => {
ImplSource::Param(nested)
}
CandidateSource::CoherenceUnknowable => {
panic!("didn't expect to select an unknowable candidate")
}
},
ProbeKind::NormalizedSelfTyAssembly
| ProbeKind::UnsizeAssembly
| ProbeKind::ProjectionCompatibility
| ProbeKind::OpaqueTypeStorageLookup { result: _ }
| ProbeKind::Root { result: _ }
| ProbeKind::ShadowedEnvProbing
| ProbeKind::RigidAlias { result: _ } => {
panic!("didn't expect to assemble trait candidate from {:#?}", cand.kind())
}
})
}