diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 7f3567c08be..43583b5723e 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -339,6 +339,12 @@ TrivialTypeTraversalAndLiftImpls! { } impl<'tcx> CanonicalVarValues<'tcx> { + /// Creates dummy var values which should not be used in a + /// canonical response. + pub fn dummy() -> CanonicalVarValues<'tcx> { + CanonicalVarValues { var_values: Default::default() } + } + #[inline] pub fn len(&self) -> usize { self.var_values.len() diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs index da6bb844a0f..cd6e4d2bccd 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -12,18 +12,73 @@ use std::fmt::Debug; /// /// It consists of both the `source`, which describes how that goal would be proven, /// and the `result` when using the given `source`. -/// -/// For the list of possible candidates, please look at the documentation of -/// [super::trait_goals::CandidateSource] and [super::project_goals::CandidateSource]. #[derive(Debug, Clone)] -pub(super) struct Candidate<'tcx, G: GoalKind<'tcx>> { - pub(super) source: G::CandidateSource, +pub(super) struct Candidate<'tcx> { + pub(super) source: CandidateSource, pub(super) result: CanonicalResponse<'tcx>, } -pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy { - type CandidateSource: Debug + Copy; +/// Possible ways the given goal can be proven. +#[derive(Debug, Clone, Copy)] +pub(super) 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. Not + BuiltinImpl, + /// An assumption from the environment. + /// + /// More precicely 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(usize), +} +pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy { fn self_ty(self) -> Ty<'tcx>; fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self; @@ -31,43 +86,40 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy { fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId; fn consider_impl_candidate( - acx: &mut AssemblyCtxt<'_, '_, 'tcx, Self>, + ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, impl_def_id: DefId, - ); + ) -> Result; + + fn consider_builtin_sized_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Result; + + fn consider_assumption( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + assumption: ty::Predicate<'tcx>, + ) -> Result; } - -/// An abstraction which correctly deals with the canonical results for candidates. -/// -/// It also deduplicates the behavior between trait and projection predicates. -pub(super) struct AssemblyCtxt<'a, 'b, 'tcx, G: GoalKind<'tcx>> { - pub(super) cx: &'a mut EvalCtxt<'b, 'tcx>, - candidates: Vec>, -} - -impl<'a, 'b, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'b, 'tcx, G> { - pub(super) fn assemble_and_evaluate_candidates( - cx: &'a mut EvalCtxt<'b, 'tcx>, - goal: Goal<'tcx, G>, - ) -> Vec> { - let mut acx = AssemblyCtxt { cx, candidates: Vec::new() }; - - acx.assemble_candidates_after_normalizing_self_ty(goal); - - acx.assemble_impl_candidates(goal); - - acx.candidates - } - - pub(super) fn try_insert_candidate( +impl<'tcx> EvalCtxt<'_, 'tcx> { + pub(super) fn assemble_and_evaluate_candidates>( &mut self, - source: G::CandidateSource, - certainty: Certainty, - ) { - match self.cx.make_canonical_response(certainty) { - Ok(result) => self.candidates.push(Candidate { source, result }), - Err(NoSolution) => debug!(?source, ?certainty, "failed leakcheck"), - } + goal: Goal<'tcx, G>, + ) -> Vec> { + let mut candidates = Vec::new(); + + self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates); + + self.assemble_impl_candidates(goal, &mut candidates); + + self.assemble_builtin_impl_candidates(goal, &mut candidates); + + self.assemble_param_env_candidates(goal, &mut candidates); + + self.assemble_alias_bound_candidates(goal, &mut candidates); + + candidates } /// If the self type of a goal is a projection, computing the relevant candidates is difficult. @@ -75,15 +127,18 @@ impl<'a, 'b, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'b, 'tcx, G> { /// To deal with this, we first try to normalize the self type and add the candidates for the normalized /// self type to the list of candidates in case that succeeds. Note that we can't just eagerly return in /// this case as projections as self types add ` - fn assemble_candidates_after_normalizing_self_ty(&mut self, goal: Goal<'tcx, G>) { - let tcx = self.cx.tcx(); - let infcx = self.cx.infcx; + fn assemble_candidates_after_normalizing_self_ty>( + &mut self, + goal: Goal<'tcx, G>, + candidates: &mut Vec>, + ) { + let tcx = self.tcx(); // FIXME: We also have to normalize opaque types, not sure where to best fit that in. let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.self_ty().kind() else { return }; - infcx.probe(|_| { - let normalized_ty = infcx.next_ty_infer(); + self.infcx.probe(|_| { + let normalized_ty = self.infcx.next_ty_infer(); let normalizes_to_goal = goal.with( tcx, ty::Binder::dummy(ty::ProjectionPredicate { @@ -91,33 +146,136 @@ impl<'a, 'b, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'b, 'tcx, G> { term: normalized_ty.into(), }), ); - let normalization_certainty = match self.cx.evaluate_goal(normalizes_to_goal) { + let normalization_certainty = match self.evaluate_goal(normalizes_to_goal) { Ok((_, certainty)) => certainty, Err(NoSolution) => return, }; // NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate. - // This doesn't work as long as we use `CandidateSource` in both winnowing and to resolve associated items. + // This doesn't work as long as we use `CandidateSource` in winnowing. let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty)); - let normalized_candidates = - AssemblyCtxt::assemble_and_evaluate_candidates(self.cx, goal); + // FIXME: This is broken if we care about the `usize` of `AliasBound` because the self type + // could be normalized to yet another projection with different item bounds. + let normalized_candidates = self.assemble_and_evaluate_candidates(goal); for mut normalized_candidate in normalized_candidates { normalized_candidate.result = normalized_candidate.result.unchecked_map(|mut response| { + // FIXME: This currently hides overflow in the normalization step of the self type + // which is probably wrong. Maybe `unify_and` should actually keep overflow as + // we treat it as non-fatal anyways. response.certainty = response.certainty.unify_and(normalization_certainty); response }); - self.candidates.push(normalized_candidate); + candidates.push(normalized_candidate); } }) } - fn assemble_impl_candidates(&mut self, goal: Goal<'tcx, G>) { - let tcx = self.cx.tcx(); + fn assemble_impl_candidates>( + &mut self, + goal: Goal<'tcx, G>, + candidates: &mut Vec>, + ) { + let tcx = self.tcx(); tcx.for_each_relevant_impl( goal.predicate.trait_def_id(tcx), goal.predicate.self_ty(), - |impl_def_id| G::consider_impl_candidate(self, goal, impl_def_id), + |impl_def_id| match G::consider_impl_candidate(self, goal, impl_def_id) + .and_then(|certainty| self.make_canonical_response(certainty)) + { + Ok(result) => candidates + .push(Candidate { source: CandidateSource::Impl(impl_def_id), result }), + Err(NoSolution) => (), + }, ); } + + fn assemble_builtin_impl_candidates>( + &mut self, + goal: Goal<'tcx, G>, + candidates: &mut Vec>, + ) { + let lang_items = self.tcx().lang_items(); + let trait_def_id = goal.predicate.trait_def_id(self.tcx()); + let result = if lang_items.sized_trait() == Some(trait_def_id) { + G::consider_builtin_sized_candidate(self, goal) + } else { + Err(NoSolution) + }; + + match result.and_then(|certainty| self.make_canonical_response(certainty)) { + Ok(result) => { + candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result }) + } + Err(NoSolution) => (), + } + } + + fn assemble_param_env_candidates>( + &mut self, + goal: Goal<'tcx, G>, + candidates: &mut Vec>, + ) { + for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() { + match G::consider_assumption(self, goal, assumption) + .and_then(|certainty| self.make_canonical_response(certainty)) + { + Ok(result) => { + candidates.push(Candidate { source: CandidateSource::ParamEnv(i), result }) + } + Err(NoSolution) => (), + } + } + } + + fn assemble_alias_bound_candidates>( + &mut self, + goal: Goal<'tcx, G>, + candidates: &mut Vec>, + ) { + let alias_ty = match goal.predicate.self_ty().kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::Generator(..) + | ty::GeneratorWitness(_) + | ty::Never + | ty::Tuple(_) + | ty::Param(_) + | ty::Placeholder(..) + | ty::Infer(_) + | ty::Error(_) => return, + ty::Bound(..) => bug!("unexpected bound type: {goal:?}"), + ty::Alias(_, alias_ty) => alias_ty, + }; + + for (i, (assumption, _)) in self + .tcx() + .bound_explicit_item_bounds(alias_ty.def_id) + .subst_iter_copied(self.tcx(), alias_ty.substs) + .enumerate() + { + match G::consider_assumption(self, goal, assumption) + .and_then(|certainty| self.make_canonical_response(certainty)) + { + Ok(result) => { + candidates.push(Candidate { source: CandidateSource::AliasBound(i), result }) + } + Err(NoSolution) => (), + } + } + } } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 3146f468f7d..b086c0684d2 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -2,7 +2,7 @@ use std::mem; use rustc_data_structures::fx::FxHashMap; use rustc_infer::{ - infer::{canonical::OriginalQueryValues, InferCtxt}, + infer::InferCtxt, traits::{ query::NoSolution, FulfillmentError, FulfillmentErrorCode, PredicateObligation, SelectionError, TraitEngine, @@ -10,7 +10,7 @@ use rustc_infer::{ }; use rustc_middle::ty; -use super::{Certainty, EvalCtxt}; +use super::{search_graph, Certainty, EvalCtxt}; /// A trait engine using the new trait solver. /// @@ -68,25 +68,10 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { let mut has_changed = false; for obligation in mem::take(&mut self.obligations) { let goal = obligation.clone().into(); - - // FIXME: Add a better API for that '^^ - let mut orig_values = OriginalQueryValues::default(); - let canonical_goal = infcx.canonicalize_query(goal, &mut orig_values); - let (changed, certainty) = match EvalCtxt::evaluate_canonical_goal( - infcx.tcx, - &mut super::search_graph::SearchGraph::new(infcx.tcx), - canonical_goal, - ) { - Ok(canonical_response) => { - ( - true, // FIXME: check whether `var_values` are an identity substitution. - super::instantiate_canonical_query_response( - infcx, - &orig_values, - canonical_response, - ), - ) - } + let search_graph = &mut search_graph::SearchGraph::new(infcx.tcx); + let mut ecx = EvalCtxt::new_outside_solver(infcx, search_graph); + let (changed, certainty) = match ecx.evaluate_goal(goal) { + Ok(result) => result, Err(NoSolution) => { errors.push(FulfillmentError { obligation: obligation.clone(), diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 0e629664b2b..579cd6a2d59 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -155,6 +155,23 @@ struct EvalCtxt<'a, 'tcx> { } impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + /// Creates a new evaluation context outside of the trait solver. + /// + /// With this solver making a canonical response doesn't make much sense. + /// The `search_graph` for this solver has to be completely empty. + fn new_outside_solver( + infcx: &'a InferCtxt<'tcx>, + search_graph: &'a mut search_graph::SearchGraph<'tcx>, + ) -> EvalCtxt<'a, 'tcx> { + assert!(search_graph.is_empty()); + EvalCtxt { infcx, var_values: CanonicalVarValues::dummy(), search_graph } + } + + #[instrument(level = "debug", skip(tcx, search_graph), ret)] fn evaluate_canonical_goal( tcx: TyCtxt<'tcx>, search_graph: &'a mut search_graph::SearchGraph<'tcx>, @@ -183,10 +200,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { } } - fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - fn make_canonical_response(&self, certainty: Certainty) -> QueryResult<'tcx> { let external_constraints = take_external_constraints(self.infcx)?; diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 1120dfb8098..92c5d4e53f5 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -1,7 +1,7 @@ use crate::traits::{specialization_graph, translate_substs}; -use super::assembly::{self, AssemblyCtxt}; -use super::{EvalCtxt, Goal, QueryResult}; +use super::assembly::{self, Candidate, CandidateSource}; +use super::{Certainty, EvalCtxt, Goal, QueryResult}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -16,22 +16,12 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::DUMMY_SP; use std::iter; -#[allow(dead_code)] // FIXME: implement and use all variants. -#[derive(Debug, Clone, Copy)] -pub(super) enum CandidateSource { - Impl(DefId), - ParamEnv(usize), - Builtin, -} - -type Candidate<'tcx> = assembly::Candidate<'tcx, ProjectionPredicate<'tcx>>; - impl<'tcx> EvalCtxt<'_, 'tcx> { pub(super) fn compute_projection_goal( &mut self, goal: Goal<'tcx, ProjectionPredicate<'tcx>>, ) -> QueryResult<'tcx> { - let candidates = AssemblyCtxt::assemble_and_evaluate_candidates(self, goal); + let candidates = self.assemble_and_evaluate_candidates(goal); self.merge_project_candidates(candidates) } @@ -83,14 +73,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { match (candidate.source, other.source) { (CandidateSource::Impl(_), _) | (CandidateSource::ParamEnv(_), _) - | (CandidateSource::Builtin, _) => unimplemented!(), + | (CandidateSource::BuiltinImpl, _) + | (CandidateSource::AliasBound(_), _) => unimplemented!(), } } } impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { - type CandidateSource = CandidateSource; - fn self_ty(self) -> Ty<'tcx> { self.self_ty() } @@ -104,12 +93,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { } fn consider_impl_candidate( - acx: &mut AssemblyCtxt<'_, '_, 'tcx, ProjectionPredicate<'tcx>>, + ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, ProjectionPredicate<'tcx>>, impl_def_id: DefId, - ) { - let tcx = acx.cx.tcx(); - let infcx = acx.cx.infcx; + ) -> Result { + let tcx = ecx.tcx(); let goal_trait_ref = goal.predicate.projection_ty.trait_ref(tcx); let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); @@ -117,20 +105,20 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { if iter::zip(goal_trait_ref.substs, impl_trait_ref.skip_binder().substs) .any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp)) { - return; + return Err(NoSolution); } - infcx.probe(|_| { - let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); + ecx.infcx.probe(|_| { + let impl_substs = ecx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs); - let Ok(InferOk { obligations, .. }) = infcx + let Ok(InferOk { obligations, .. }) = ecx.infcx .at(&ObligationCause::dummy(), goal.param_env) .define_opaque_types(false) .eq(goal_trait_ref, impl_trait_ref) .map_err(|e| debug!("failed to equate trait refs: {e:?}")) else { - return + return Err(NoSolution) }; let where_clause_bounds = tcx .predicates_of(impl_def_id) @@ -141,16 +129,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { let nested_goals = obligations.into_iter().map(|o| o.into()).chain(where_clause_bounds).collect(); - let Ok(trait_ref_certainty) = acx.cx.evaluate_all(nested_goals) else { return }; + let trait_ref_certainty = ecx.evaluate_all(nested_goals)?; let Some(assoc_def) = fetch_eligible_assoc_item_def( - infcx, + ecx.infcx, goal.param_env, goal_trait_ref, goal.predicate.def_id(), impl_def_id ) else { - return + return Err(NoSolution); }; if !assoc_def.item.defaultness(tcx).has_value() { @@ -176,7 +164,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { impl_substs, ); let substs = translate_substs( - infcx, + ecx.infcx, goal.param_env, impl_def_id, impl_substs_with_gat, @@ -197,22 +185,40 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ty.map_bound(|ty| ty.into()) }; - let Ok(InferOk { obligations, .. }) = infcx + let Ok(InferOk { obligations, .. }) = ecx.infcx .at(&ObligationCause::dummy(), goal.param_env) .define_opaque_types(false) .eq(goal.predicate.term, term.subst(tcx, substs)) .map_err(|e| debug!("failed to equate trait refs: {e:?}")) else { - return + return Err(NoSolution); }; let nested_goals = obligations.into_iter().map(|o| o.into()).collect(); - let Ok(rhs_certainty) = acx.cx.evaluate_all(nested_goals) else { return }; + let rhs_certainty = ecx.evaluate_all(nested_goals)?; - let certainty = trait_ref_certainty.unify_and(rhs_certainty); - acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty); + Ok(trait_ref_certainty.unify_and(rhs_certainty)) }) } + + fn consider_builtin_sized_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Result { + bug!("`Sized` does not have an associated type: {:?}", goal); + } + + fn consider_assumption( + _ecx: &mut EvalCtxt<'_, 'tcx>, + _goal: Goal<'tcx, Self>, + assumption: ty::Predicate<'tcx>, + ) -> Result { + if let Some(_poly_projection_pred) = assumption.to_opt_poly_projection_pred() { + unimplemented!() + } else { + Err(NoSolution) + } + } } /// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code. diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs b/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs index 435e46f211a..cb00fe6bf14 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs @@ -52,6 +52,10 @@ impl<'tcx> ProvisionalCache<'tcx> { ProvisionalCache { entries: Default::default(), lookup_table: Default::default() } } + pub(super) fn is_empty(&self) -> bool { + self.entries.is_empty() && self.lookup_table.is_empty() + } + /// Adds a dependency from the current leaf to `target` in the cache /// to prevent us from moving any goals which depend on the current leaf /// to the global cache while we're still computing `target`. diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index 8d2a3a9cd81..4f48389410b 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -36,6 +36,12 @@ impl<'tcx> SearchGraph<'tcx> { } } + pub(super) fn is_empty(&self) -> bool { + self.stack.is_empty() + && self.provisional_cache.is_empty() + && !self.overflow_data.did_overflow() + } + /// Tries putting the new goal on the stack, returning an error if it is already cached. /// /// This correctly updates the provisional cache if there is a cycle. diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index cc68cfeea1d..3c8314aa565 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -2,8 +2,8 @@ use std::iter; -use super::assembly::{self, AssemblyCtxt}; -use super::{EvalCtxt, Goal, QueryResult}; +use super::assembly::{self, Candidate, CandidateSource}; +use super::{Certainty, EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; use rustc_infer::infer::InferOk; use rustc_infer::traits::query::NoSolution; @@ -13,47 +13,7 @@ use rustc_middle::ty::TraitPredicate; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::DUMMY_SP; -#[allow(dead_code)] // FIXME: implement and use all variants. -#[derive(Debug, Clone, Copy)] -pub(super) enum CandidateSource { - /// Some user-defined impl with the given `DefId`. - Impl(DefId), - /// The n-th caller bound in the `param_env` of our goal. - /// - /// This is pretty much always a bound from the `where`-clauses of the - /// currently checked item. - ParamEnv(usize), - /// A bound on the `self_ty` in case it is a projection or an opaque type. - /// - /// # Examples - /// - /// ```ignore (for syntax highlighting) - /// trait Trait { - /// type Assoc: OtherTrait; - /// } - /// ``` - /// - /// We know that `::Assoc: OtherTrait` holds by looking at - /// the bounds on `Trait::Assoc`. - AliasBound(usize), - /// A builtin implementation for some specific traits, used in cases - /// where we cannot rely an ordinary library implementations. - /// - /// The most notable examples are `Sized`, `Copy` and `Clone`. This is also - /// used for the `DiscriminantKind` and `Pointee` trait, both of which have - /// an associated type. - Builtin, - /// An automatic impl for an auto trait, e.g. `Send`. These impls recursively look - /// at the constituent types of the `self_ty` to check whether the auto trait - /// is implemented for those. - AutoImpl, -} - -type Candidate<'tcx> = assembly::Candidate<'tcx, TraitPredicate<'tcx>>; - impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { - type CandidateSource = CandidateSource; - fn self_ty(self) -> Ty<'tcx> { self.self_ty() } @@ -67,32 +27,31 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } fn consider_impl_candidate( - acx: &mut AssemblyCtxt<'_, '_, 'tcx, Self>, + ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, TraitPredicate<'tcx>>, impl_def_id: DefId, - ) { - let tcx = acx.cx.tcx(); - let infcx = acx.cx.infcx; + ) -> Result { + let tcx = ecx.tcx(); let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder }; if iter::zip(goal.predicate.trait_ref.substs, impl_trait_ref.skip_binder().substs) .any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp)) { - return; + return Err(NoSolution); } - infcx.probe(|_| { - let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); + ecx.infcx.probe(|_| { + let impl_substs = ecx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs); - let Ok(InferOk { obligations, .. }) = infcx + let Ok(InferOk { obligations, .. }) = ecx.infcx .at(&ObligationCause::dummy(), goal.param_env) .define_opaque_types(false) .eq(goal.predicate.trait_ref, impl_trait_ref) .map_err(|e| debug!("failed to equate trait refs: {e:?}")) else { - return + return Err(NoSolution); }; let where_clause_bounds = tcx .predicates_of(impl_def_id) @@ -104,10 +63,28 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let nested_goals = obligations.into_iter().map(|o| o.into()).chain(where_clause_bounds).collect(); - let Ok(certainty) = acx.cx.evaluate_all(nested_goals) else { return }; - acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty); + ecx.evaluate_all(nested_goals) }) } + + fn consider_builtin_sized_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + _goal: Goal<'tcx, Self>, + ) -> Result { + unimplemented!(); + } + + fn consider_assumption( + _ecx: &mut EvalCtxt<'_, 'tcx>, + _goal: Goal<'tcx, Self>, + assumption: ty::Predicate<'tcx>, + ) -> Result { + if let Some(_poly_trait_pred) = assumption.to_opt_poly_trait_pred() { + unimplemented!() + } else { + Err(NoSolution) + } + } } impl<'tcx> EvalCtxt<'_, 'tcx> { @@ -115,7 +92,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &mut self, goal: Goal<'tcx, TraitPredicate<'tcx>>, ) -> QueryResult<'tcx> { - let candidates = AssemblyCtxt::assemble_and_evaluate_candidates(self, goal); + let candidates = self.assemble_and_evaluate_candidates(goal); self.merge_trait_candidates_discard_reservation_impls(candidates) } @@ -169,8 +146,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { (CandidateSource::Impl(_), _) | (CandidateSource::ParamEnv(_), _) | (CandidateSource::AliasBound(_), _) - | (CandidateSource::Builtin, _) - | (CandidateSource::AutoImpl, _) => unimplemented!(), + | (CandidateSource::BuiltinImpl, _) => unimplemented!(), } }