From 750bf36c33bb6d2d4d7fdc609020507e5f966037 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 19 Dec 2022 07:01:38 +0000 Subject: [PATCH] dedup assembly --- .../src/solve/assembly.rs | 150 +++++++++++++ .../rustc_trait_selection/src/solve/mod.rs | 1 + .../src/solve/project_goals.rs | 202 +++++------------ .../src/solve/trait_goals.rs | 212 +++++------------- 4 files changed, 267 insertions(+), 298 deletions(-) create mode 100644 compiler/rustc_trait_selection/src/solve/assembly.rs diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs new file mode 100644 index 0000000000000..e9ddad11ff23e --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/assembly.rs @@ -0,0 +1,150 @@ +//! Code shared by trait and projection goals for candidate assembly. + +use super::infcx_ext::InferCtxtExt; +use super::{ + fixme_instantiate_canonical_query_response, CanonicalGoal, CanonicalResponse, Certainty, + EvalCtxt, Goal, +}; +use rustc_hir::def_id::DefId; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::{ + canonical::{CanonicalVarValues, OriginalQueryValues}, + InferCtxt, +}; +use rustc_infer::traits::query::NoSolution; +use rustc_middle::ty::TypeFoldable; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::DUMMY_SP; +use std::fmt::Debug; + +/// A candidate is a possible way to prove a goal. +/// +/// 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) result: CanonicalResponse<'tcx>, +} + +pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy { + type CandidateSource: Debug + Copy; + + fn self_ty(self) -> Ty<'tcx>; + + fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self; + + fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId; + + fn consider_impl_candidate( + acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + goal: Goal<'tcx, Self>, + impl_def_id: DefId, + ); +} + +/// 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, 'tcx, G: GoalKind<'tcx>> { + pub(super) cx: &'a mut EvalCtxt<'tcx>, + pub(super) infcx: &'a InferCtxt<'tcx>, + var_values: CanonicalVarValues<'tcx>, + candidates: Vec>, +} + +impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> { + pub(super) fn assemble_and_evaluate_candidates( + cx: &'a mut EvalCtxt<'tcx>, + goal: CanonicalGoal<'tcx, G>, + ) -> Vec> { + let (ref infcx, goal, var_values) = + cx.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); + let mut acx = AssemblyCtxt { cx, infcx, var_values, candidates: Vec::new() }; + + acx.assemble_candidates_after_normalizing_self_ty(goal); + + acx.assemble_impl_candidates(goal); + + acx.candidates + } + + pub(super) fn try_insert_candidate( + &mut self, + source: G::CandidateSource, + certainty: Certainty, + ) { + match self.infcx.make_canonical_response(self.var_values.clone(), certainty) { + Ok(result) => self.candidates.push(Candidate { source, result }), + Err(NoSolution) => debug!(?source, ?certainty, "failed leakcheck"), + } + } + + /// If the self type of a goal is a projection, computing the relevant candidates is difficult. + /// + /// 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; + // 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 + }; + self.infcx.probe(|_| { + let normalized_ty = self.infcx.next_ty_infer(); + let normalizes_to_goal = goal.with( + tcx, + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty, + term: normalized_ty.into(), + }), + ); + let normalization_certainty = + match self.cx.evaluate_goal(&self.infcx, 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. + let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty)); + let mut orig_values = OriginalQueryValues::default(); + let goal = self.infcx.canonicalize_query(goal, &mut orig_values); + let normalized_candidates = + AssemblyCtxt::assemble_and_evaluate_candidates(self.cx, goal); + + // Map each candidate from being canonical wrt the current inference context to being + // canonical wrt the caller. + for Candidate { source, result } in normalized_candidates { + self.infcx.probe(|_| { + let candidate_certainty = fixme_instantiate_canonical_query_response( + &self.infcx, + &orig_values, + result, + ); + + // FIXME: This is a bit scary if the `normalizes_to_goal` overflows. + // + // If we have an ambiguous candidate it hides that normalization + // caused an overflow which may cause issues. + self.try_insert_candidate( + source, + normalization_certainty.unify_and(candidate_certainty), + ) + }) + } + }) + } + + fn assemble_impl_candidates(&mut self, goal: Goal<'tcx, G>) { + self.cx.tcx.for_each_relevant_impl( + goal.predicate.trait_def_id(self.cx.tcx), + goal.predicate.self_ty(), + |impl_def_id| G::consider_impl_candidate(self, goal, impl_def_id), + ); + } +} diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 5d6529f85423b..7f5e3208f4e7c 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -30,6 +30,7 @@ use rustc_span::DUMMY_SP; use self::infcx_ext::InferCtxtExt; +mod assembly; mod cache; mod fulfill; mod infcx_ext; diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 47a8f59ceb77f..b50f42c4d9416 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -1,64 +1,40 @@ use crate::traits::{specialization_graph, translate_substs}; -use super::infcx_ext::InferCtxtExt; -use super::{ - fixme_instantiate_canonical_query_response, CanonicalGoal, CanonicalResponse, Certainty, - EvalCtxt, Goal, QueryResult, -}; +use super::assembly::{self, AssemblyCtxt}; +use super::{CanonicalGoal, EvalCtxt, Goal, QueryResult}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_infer::infer::canonical::{CanonicalVarValues, OriginalQueryValues}; -use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; +use rustc_infer::infer::{InferCtxt, InferOk}; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::specialization_graph::LeafDef; use rustc_infer::traits::{ObligationCause, Reveal}; -use rustc_middle::ty; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::ProjectionPredicate; use rustc_middle::ty::TypeVisitable; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::DUMMY_SP; use std::iter; -// FIXME: Deduplicate the candidate code between projection and trait goal. - -/// Similar to [super::trait_goals::Candidate] but for `Projection` goals. -#[derive(Debug, Clone)] -struct Candidate<'tcx> { - source: CandidateSource, - result: CanonicalResponse<'tcx>, -} - #[allow(dead_code)] // FIXME: implement and use all variants. #[derive(Debug, Clone, Copy)] -enum CandidateSource { +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: CanonicalGoal<'tcx, ProjectionPredicate<'tcx>>, ) -> QueryResult<'tcx> { - let candidates = self.assemble_and_evaluate_project_candidates(goal); + let candidates = AssemblyCtxt::assemble_and_evaluate_candidates(self, goal); self.merge_project_candidates(candidates) } - fn assemble_and_evaluate_project_candidates( - &mut self, - goal: CanonicalGoal<'tcx, ProjectionPredicate<'tcx>>, - ) -> Vec> { - let (ref infcx, goal, var_values) = - self.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); - let mut acx = AssemblyCtxt { cx: self, infcx, var_values, candidates: Vec::new() }; - - acx.assemble_candidates_after_normalizing_self_ty(goal); - acx.assemble_impl_candidates(goal); - acx.candidates - } - fn merge_project_candidates( &mut self, mut candidates: Vec>, @@ -112,83 +88,27 @@ impl<'tcx> EvalCtxt<'tcx> { } } -/// Similar to [super::trait_goals::AssemblyCtxt] but for `Projection` goals. -struct AssemblyCtxt<'a, 'tcx> { - cx: &'a mut EvalCtxt<'tcx>, - infcx: &'a InferCtxt<'tcx>, - var_values: CanonicalVarValues<'tcx>, - candidates: Vec>, -} +impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { + type CandidateSource = CandidateSource; -impl<'tcx> AssemblyCtxt<'_, 'tcx> { - fn try_insert_candidate(&mut self, source: CandidateSource, certainty: Certainty) { - match self.infcx.make_canonical_response(self.var_values.clone(), certainty) { - Ok(result) => self.candidates.push(Candidate { source, result }), - Err(NoSolution) => debug!(?source, ?certainty, "failed leakcheck"), - } + fn self_ty(self) -> Ty<'tcx> { + self.self_ty() } - fn assemble_candidates_after_normalizing_self_ty( - &mut self, - goal: Goal<'tcx, ProjectionPredicate<'tcx>>, - ) { - let tcx = self.cx.tcx; - let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.projection_ty.self_ty().kind() else { - return - }; - self.infcx.probe(|_| { - let normalized_ty = self.infcx.next_ty_infer(); - let normalizes_to_goal = goal.with( - tcx, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty, - term: normalized_ty.into(), - }), - ); - let normalization_certainty = - match self.cx.evaluate_goal(&self.infcx, 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. - let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty)); - let mut orig_values = OriginalQueryValues::default(); - let goal = self.infcx.canonicalize_query(goal, &mut orig_values); - let normalized_candidates = self.cx.assemble_and_evaluate_project_candidates(goal); - // Map each candidate from being canonical wrt the current inference context to being - // canonical wrt the caller. - for Candidate { source, result } in normalized_candidates { - self.infcx.probe(|_| { - let candidate_certainty = fixme_instantiate_canonical_query_response( - self.infcx, - &orig_values, - result, - ); - self.try_insert_candidate( - source, - normalization_certainty.unify_and(candidate_certainty), - ) - }) - } - }) + fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { + self.with_self_ty(tcx, self_ty) } - fn assemble_impl_candidates(&mut self, goal: Goal<'tcx, ProjectionPredicate<'tcx>>) { - self.cx.tcx.for_each_relevant_impl( - goal.predicate.trait_def_id(self.cx.tcx), - goal.predicate.self_ty(), - |impl_def_id| self.consider_impl_candidate(goal, impl_def_id), - ); + fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId { + self.trait_def_id(tcx) } fn consider_impl_candidate( - &mut self, + acx: &mut AssemblyCtxt<'_, 'tcx, ProjectionPredicate<'tcx>>, goal: Goal<'tcx, ProjectionPredicate<'tcx>>, impl_def_id: DefId, ) { - let tcx = self.cx.tcx; + let tcx = acx.cx.tcx; let goal_trait_ref = goal.predicate.projection_ty.trait_ref(tcx); let impl_trait_ref = tcx.bound_impl_trait_ref(impl_def_id).unwrap(); let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder }; @@ -198,11 +118,11 @@ impl<'tcx> AssemblyCtxt<'_, 'tcx> { return; } - self.infcx.probe(|_| { - let impl_substs = self.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); + acx.infcx.probe(|_| { + let impl_substs = acx.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, .. }) = self + let Ok(InferOk { obligations, .. }) = acx .infcx .at(&ObligationCause::dummy(), goal.param_env) .define_opaque_types(false) @@ -213,9 +133,10 @@ impl<'tcx> AssemblyCtxt<'_, 'tcx> { }; let nested_goals = obligations.into_iter().map(|o| o.into()).collect(); - let Ok(trait_ref_certainty) = self.cx.evaluate_all(self.infcx, nested_goals) else { return }; + let Ok(trait_ref_certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return }; - let Some(assoc_def) = self.fetch_eligible_assoc_item_def( + let Some(assoc_def) = fetch_eligible_assoc_item_def( + acx.infcx, goal.param_env, goal_trait_ref, goal.predicate.def_id(), @@ -247,7 +168,7 @@ impl<'tcx> AssemblyCtxt<'_, 'tcx> { impl_trait_ref.substs, ); let substs = translate_substs( - self.infcx, + acx.infcx, goal.param_env, impl_def_id, impl_substs_with_gat, @@ -267,7 +188,7 @@ impl<'tcx> AssemblyCtxt<'_, 'tcx> { ty.map_bound(|ty| ty.into()) }; - let Ok(InferOk { obligations, .. }) = self + let Ok(InferOk { obligations, .. }) = acx .infcx .at(&ObligationCause::dummy(), goal.param_env) .define_opaque_types(false) @@ -278,47 +199,46 @@ impl<'tcx> AssemblyCtxt<'_, 'tcx> { }; let nested_goals = obligations.into_iter().map(|o| o.into()).collect(); - let Ok(rhs_certainty) = self.cx.evaluate_all(self.infcx, nested_goals) else { return }; + let Ok(rhs_certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return }; let certainty = trait_ref_certainty.unify_and(rhs_certainty); - self.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty); + acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty); }) } +} - /// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code. - /// - /// FIXME: We should merge these 3 implementations as it's likely that they otherwise - /// diverge. - #[instrument(level = "debug", skip(self, param_env), ret)] - fn fetch_eligible_assoc_item_def( - &self, - param_env: ty::ParamEnv<'tcx>, - goal_trait_ref: ty::TraitRef<'tcx>, - trait_assoc_def_id: DefId, - impl_def_id: DefId, - ) -> Option { - let node_item = - specialization_graph::assoc_def(self.cx.tcx, impl_def_id, trait_assoc_def_id) - .map_err(|ErrorGuaranteed { .. }| ()) - .ok()?; - - let eligible = if node_item.is_final() { - // Non-specializable items are always projectable. - true +/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code. +/// +/// FIXME: We should merge these 3 implementations as it's likely that they otherwise +/// diverge. +#[instrument(level = "debug", skip(infcx, param_env), ret)] +fn fetch_eligible_assoc_item_def<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + goal_trait_ref: ty::TraitRef<'tcx>, + trait_assoc_def_id: DefId, + impl_def_id: DefId, +) -> Option { + let node_item = specialization_graph::assoc_def(infcx.tcx, impl_def_id, trait_assoc_def_id) + .map_err(|ErrorGuaranteed { .. }| ()) + .ok()?; + + let eligible = if node_item.is_final() { + // Non-specializable items are always projectable. + true + } else { + // Only reveal a specializable default if we're past type-checking + // and the obligation is monomorphic, otherwise passes such as + // transmute checking and polymorphic MIR optimizations could + // get a result which isn't correct for all monomorphizations. + if param_env.reveal() == Reveal::All { + let poly_trait_ref = infcx.resolve_vars_if_possible(goal_trait_ref); + !poly_trait_ref.still_further_specializable() } else { - // Only reveal a specializable default if we're past type-checking - // and the obligation is monomorphic, otherwise passes such as - // transmute checking and polymorphic MIR optimizations could - // get a result which isn't correct for all monomorphizations. - if param_env.reveal() == Reveal::All { - let poly_trait_ref = self.infcx.resolve_vars_if_possible(goal_trait_ref); - !poly_trait_ref.still_further_specializable() - } else { - debug!(?node_item.item.def_id, "not eligible due to default"); - false - } - }; + debug!(?node_item.item.def_id, "not eligible due to default"); + false + } + }; - if eligible { Some(node_item) } else { None } - } + if eligible { Some(node_item) } else { None } } diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 4ea9081bf46a6..10b45a77dabb0 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -2,35 +2,17 @@ use std::iter; -use super::infcx_ext::InferCtxtExt; -use super::{ - fixme_instantiate_canonical_query_response, CanonicalGoal, CanonicalResponse, Certainty, - EvalCtxt, Goal, QueryResult, -}; +use super::assembly::{self, AssemblyCtxt}; +use super::{CanonicalGoal, EvalCtxt, Goal, QueryResult}; use rustc_hir::def_id::DefId; -use rustc_infer::infer::canonical::{CanonicalVarValues, OriginalQueryValues}; -use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::infer::{InferCtxt, InferOk}; +use rustc_infer::infer::InferOk; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::ObligationCause; -use rustc_middle::ty; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::TraitPredicate; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::DUMMY_SP; -/// A candidate is a possible way to prove a goal. -/// -/// 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 [CandidateSource]. -#[derive(Debug, Clone)] -pub(super) struct Candidate<'tcx> { - source: CandidateSource, - result: CanonicalResponse<'tcx>, -} - #[allow(dead_code)] // FIXME: implement and use all variants. #[derive(Debug, Clone, Copy)] pub(super) enum CandidateSource { @@ -67,11 +49,56 @@ pub(super) enum CandidateSource { AutoImpl, } -struct AssemblyCtxt<'a, 'tcx> { - cx: &'a mut EvalCtxt<'tcx>, - infcx: &'a InferCtxt<'tcx>, - var_values: CanonicalVarValues<'tcx>, - candidates: Vec>, +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() + } + + fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { + self.with_self_ty(tcx, self_ty) + } + + fn trait_def_id(self, _: TyCtxt<'tcx>) -> DefId { + self.def_id() + } + + fn consider_impl_candidate( + acx: &mut AssemblyCtxt<'_, 'tcx, Self>, + goal: Goal<'tcx, TraitPredicate<'tcx>>, + impl_def_id: DefId, + ) { + let impl_trait_ref = acx.cx.tcx.bound_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; + } + + acx.infcx.probe(|_| { + let impl_substs = acx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); + let impl_trait_ref = impl_trait_ref.subst(acx.cx.tcx, impl_substs); + + let Ok(InferOk { obligations, .. }) = acx + .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 + }; + + let nested_goals = obligations.into_iter().map(|o| o.into()).collect(); + + let Ok(certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return }; + acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty); + }) + } } impl<'tcx> EvalCtxt<'tcx> { @@ -79,25 +106,10 @@ impl<'tcx> EvalCtxt<'tcx> { &mut self, goal: CanonicalGoal<'tcx, TraitPredicate<'tcx>>, ) -> QueryResult<'tcx> { - let candidates = self.assemble_and_evaluate_trait_candidates(goal); + let candidates = AssemblyCtxt::assemble_and_evaluate_candidates(self, goal); self.merge_trait_candidates_discard_reservation_impls(candidates) } - pub(super) fn assemble_and_evaluate_trait_candidates( - &mut self, - goal: CanonicalGoal<'tcx, TraitPredicate<'tcx>>, - ) -> Vec> { - let (ref infcx, goal, var_values) = - self.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); - let mut acx = AssemblyCtxt { cx: self, infcx, var_values, candidates: Vec::new() }; - - acx.assemble_candidates_after_normalizing_self_ty(goal); - acx.assemble_impl_candidates(goal); - - // FIXME: Remaining candidates - acx.candidates - } - #[instrument(level = "debug", skip(self), ret)] pub(super) fn merge_trait_candidates_discard_reservation_impls( &mut self, @@ -166,117 +178,3 @@ impl<'tcx> EvalCtxt<'tcx> { candidate } } - -impl<'tcx> AssemblyCtxt<'_, 'tcx> { - /// Adds a new candidate using the current state of the inference context. - /// - /// This does require each assembly method to correctly use `probe` to not taint - /// the results of other candidates. - fn try_insert_candidate(&mut self, source: CandidateSource, certainty: Certainty) { - match self.infcx.make_canonical_response(self.var_values.clone(), certainty) { - Ok(result) => self.candidates.push(Candidate { source, result }), - Err(NoSolution) => debug!(?source, ?certainty, "failed leakcheck"), - } - } - - /// If the self type of a trait goal is a projection, computing the relevant candidates is difficult. - /// - /// 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, TraitPredicate<'tcx>>, - ) { - let tcx = self.cx.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 - }; - self.infcx.probe(|_| { - let normalized_ty = self.infcx.next_ty_infer(); - let normalizes_to_goal = goal.with( - tcx, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty, - term: normalized_ty.into(), - }), - ); - let normalization_certainty = - match self.cx.evaluate_goal(&self.infcx, 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. - let goal = goal.with(tcx, goal.predicate.with_self_type(tcx, normalized_ty)); - let mut orig_values = OriginalQueryValues::default(); - let goal = self.infcx.canonicalize_query(goal, &mut orig_values); - let normalized_candidates = self.cx.assemble_and_evaluate_trait_candidates(goal); - - // Map each candidate from being canonical wrt the current inference context to being - // canonical wrt the caller. - for Candidate { source, result } in normalized_candidates { - self.infcx.probe(|_| { - let candidate_certainty = fixme_instantiate_canonical_query_response( - self.infcx, - &orig_values, - result, - ); - - // FIXME: This is a bit scary if the `normalizes_to_goal` overflows. - // - // If we have an ambiguous candidate it hides that normalization - // caused an overflow which may cause issues. - self.try_insert_candidate( - source, - normalization_certainty.unify_and(candidate_certainty), - ) - }) - } - }) - } - - fn assemble_impl_candidates(&mut self, goal: Goal<'tcx, TraitPredicate<'tcx>>) { - self.cx.tcx.for_each_relevant_impl( - goal.predicate.def_id(), - goal.predicate.self_ty(), - |impl_def_id| self.consider_impl_candidate(goal, impl_def_id), - ); - } - - fn consider_impl_candidate( - &mut self, - goal: Goal<'tcx, TraitPredicate<'tcx>>, - impl_def_id: DefId, - ) { - let impl_trait_ref = self.cx.tcx.bound_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; - } - - self.infcx.probe(|_| { - let impl_substs = self.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); - let impl_trait_ref = impl_trait_ref.subst(self.cx.tcx, impl_substs); - - let Ok(InferOk { obligations, .. }) = self - .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 - }; - - let nested_goals = obligations.into_iter().map(|o| o.into()).collect(); - - let Ok(certainty) = self.cx.evaluate_all(self.infcx, nested_goals) else { return }; - self.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty); - }) - } -}