Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 750bf36

Browse files
committedDec 20, 2022
dedup assembly
1 parent a213bb3 commit 750bf36

File tree

4 files changed

+267
-298
lines changed

4 files changed

+267
-298
lines changed
 
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
//! Code shared by trait and projection goals for candidate assembly.
2+
3+
use super::infcx_ext::InferCtxtExt;
4+
use super::{
5+
fixme_instantiate_canonical_query_response, CanonicalGoal, CanonicalResponse, Certainty,
6+
EvalCtxt, Goal,
7+
};
8+
use rustc_hir::def_id::DefId;
9+
use rustc_infer::infer::TyCtxtInferExt;
10+
use rustc_infer::infer::{
11+
canonical::{CanonicalVarValues, OriginalQueryValues},
12+
InferCtxt,
13+
};
14+
use rustc_infer::traits::query::NoSolution;
15+
use rustc_middle::ty::TypeFoldable;
16+
use rustc_middle::ty::{self, Ty, TyCtxt};
17+
use rustc_span::DUMMY_SP;
18+
use std::fmt::Debug;
19+
20+
/// A candidate is a possible way to prove a goal.
21+
///
22+
/// It consists of both the `source`, which describes how that goal would be proven,
23+
/// and the `result` when using the given `source`.
24+
///
25+
/// For the list of possible candidates, please look at the documentation of
26+
/// [super::trait_goals::CandidateSource] and [super::project_goals::CandidateSource].
27+
#[derive(Debug, Clone)]
28+
pub(super) struct Candidate<'tcx, G: GoalKind<'tcx>> {
29+
pub(super) source: G::CandidateSource,
30+
pub(super) result: CanonicalResponse<'tcx>,
31+
}
32+
33+
pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy {
34+
type CandidateSource: Debug + Copy;
35+
36+
fn self_ty(self) -> Ty<'tcx>;
37+
38+
fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self;
39+
40+
fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId;
41+
42+
fn consider_impl_candidate(
43+
acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
44+
goal: Goal<'tcx, Self>,
45+
impl_def_id: DefId,
46+
);
47+
}
48+
49+
/// An abstraction which correctly deals with the canonical results for candidates.
50+
///
51+
/// It also deduplicates the behavior between trait and projection predicates.
52+
pub(super) struct AssemblyCtxt<'a, 'tcx, G: GoalKind<'tcx>> {
53+
pub(super) cx: &'a mut EvalCtxt<'tcx>,
54+
pub(super) infcx: &'a InferCtxt<'tcx>,
55+
var_values: CanonicalVarValues<'tcx>,
56+
candidates: Vec<Candidate<'tcx, G>>,
57+
}
58+
59+
impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> {
60+
pub(super) fn assemble_and_evaluate_candidates(
61+
cx: &'a mut EvalCtxt<'tcx>,
62+
goal: CanonicalGoal<'tcx, G>,
63+
) -> Vec<Candidate<'tcx, G>> {
64+
let (ref infcx, goal, var_values) =
65+
cx.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal);
66+
let mut acx = AssemblyCtxt { cx, infcx, var_values, candidates: Vec::new() };
67+
68+
acx.assemble_candidates_after_normalizing_self_ty(goal);
69+
70+
acx.assemble_impl_candidates(goal);
71+
72+
acx.candidates
73+
}
74+
75+
pub(super) fn try_insert_candidate(
76+
&mut self,
77+
source: G::CandidateSource,
78+
certainty: Certainty,
79+
) {
80+
match self.infcx.make_canonical_response(self.var_values.clone(), certainty) {
81+
Ok(result) => self.candidates.push(Candidate { source, result }),
82+
Err(NoSolution) => debug!(?source, ?certainty, "failed leakcheck"),
83+
}
84+
}
85+
86+
/// If the self type of a goal is a projection, computing the relevant candidates is difficult.
87+
///
88+
/// To deal with this, we first try to normalize the self type and add the candidates for the normalized
89+
/// self type to the list of candidates in case that succeeds. Note that we can't just eagerly return in
90+
/// this case as projections as self types add `
91+
fn assemble_candidates_after_normalizing_self_ty(&mut self, goal: Goal<'tcx, G>) {
92+
let tcx = self.cx.tcx;
93+
// FIXME: We also have to normalize opaque types, not sure where to best fit that in.
94+
let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.self_ty().kind() else {
95+
return
96+
};
97+
self.infcx.probe(|_| {
98+
let normalized_ty = self.infcx.next_ty_infer();
99+
let normalizes_to_goal = goal.with(
100+
tcx,
101+
ty::Binder::dummy(ty::ProjectionPredicate {
102+
projection_ty,
103+
term: normalized_ty.into(),
104+
}),
105+
);
106+
let normalization_certainty =
107+
match self.cx.evaluate_goal(&self.infcx, normalizes_to_goal) {
108+
Ok((_, certainty)) => certainty,
109+
Err(NoSolution) => return,
110+
};
111+
112+
// NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate.
113+
// This doesn't work as long as we use `CandidateSource` in both winnowing and to resolve associated items.
114+
let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
115+
let mut orig_values = OriginalQueryValues::default();
116+
let goal = self.infcx.canonicalize_query(goal, &mut orig_values);
117+
let normalized_candidates =
118+
AssemblyCtxt::assemble_and_evaluate_candidates(self.cx, goal);
119+
120+
// Map each candidate from being canonical wrt the current inference context to being
121+
// canonical wrt the caller.
122+
for Candidate { source, result } in normalized_candidates {
123+
self.infcx.probe(|_| {
124+
let candidate_certainty = fixme_instantiate_canonical_query_response(
125+
&self.infcx,
126+
&orig_values,
127+
result,
128+
);
129+
130+
// FIXME: This is a bit scary if the `normalizes_to_goal` overflows.
131+
//
132+
// If we have an ambiguous candidate it hides that normalization
133+
// caused an overflow which may cause issues.
134+
self.try_insert_candidate(
135+
source,
136+
normalization_certainty.unify_and(candidate_certainty),
137+
)
138+
})
139+
}
140+
})
141+
}
142+
143+
fn assemble_impl_candidates(&mut self, goal: Goal<'tcx, G>) {
144+
self.cx.tcx.for_each_relevant_impl(
145+
goal.predicate.trait_def_id(self.cx.tcx),
146+
goal.predicate.self_ty(),
147+
|impl_def_id| G::consider_impl_candidate(self, goal, impl_def_id),
148+
);
149+
}
150+
}

‎compiler/rustc_trait_selection/src/solve/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use rustc_span::DUMMY_SP;
3030

3131
use self::infcx_ext::InferCtxtExt;
3232

33+
mod assembly;
3334
mod cache;
3435
mod fulfill;
3536
mod infcx_ext;
Lines changed: 61 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,40 @@
11
use crate::traits::{specialization_graph, translate_substs};
22

3-
use super::infcx_ext::InferCtxtExt;
4-
use super::{
5-
fixme_instantiate_canonical_query_response, CanonicalGoal, CanonicalResponse, Certainty,
6-
EvalCtxt, Goal, QueryResult,
7-
};
3+
use super::assembly::{self, AssemblyCtxt};
4+
use super::{CanonicalGoal, EvalCtxt, Goal, QueryResult};
85
use rustc_errors::ErrorGuaranteed;
96
use rustc_hir::def::DefKind;
107
use rustc_hir::def_id::DefId;
11-
use rustc_infer::infer::canonical::{CanonicalVarValues, OriginalQueryValues};
12-
use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt};
8+
use rustc_infer::infer::{InferCtxt, InferOk};
139
use rustc_infer::traits::query::NoSolution;
1410
use rustc_infer::traits::specialization_graph::LeafDef;
1511
use rustc_infer::traits::{ObligationCause, Reveal};
16-
use rustc_middle::ty;
1712
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
1813
use rustc_middle::ty::ProjectionPredicate;
1914
use rustc_middle::ty::TypeVisitable;
15+
use rustc_middle::ty::{self, Ty, TyCtxt};
2016
use rustc_span::DUMMY_SP;
2117
use std::iter;
2218

23-
// FIXME: Deduplicate the candidate code between projection and trait goal.
24-
25-
/// Similar to [super::trait_goals::Candidate] but for `Projection` goals.
26-
#[derive(Debug, Clone)]
27-
struct Candidate<'tcx> {
28-
source: CandidateSource,
29-
result: CanonicalResponse<'tcx>,
30-
}
31-
3219
#[allow(dead_code)] // FIXME: implement and use all variants.
3320
#[derive(Debug, Clone, Copy)]
34-
enum CandidateSource {
21+
pub(super) enum CandidateSource {
3522
Impl(DefId),
3623
ParamEnv(usize),
3724
Builtin,
3825
}
3926

27+
type Candidate<'tcx> = assembly::Candidate<'tcx, ProjectionPredicate<'tcx>>;
28+
4029
impl<'tcx> EvalCtxt<'tcx> {
4130
pub(super) fn compute_projection_goal(
4231
&mut self,
4332
goal: CanonicalGoal<'tcx, ProjectionPredicate<'tcx>>,
4433
) -> QueryResult<'tcx> {
45-
let candidates = self.assemble_and_evaluate_project_candidates(goal);
34+
let candidates = AssemblyCtxt::assemble_and_evaluate_candidates(self, goal);
4635
self.merge_project_candidates(candidates)
4736
}
4837

49-
fn assemble_and_evaluate_project_candidates(
50-
&mut self,
51-
goal: CanonicalGoal<'tcx, ProjectionPredicate<'tcx>>,
52-
) -> Vec<Candidate<'tcx>> {
53-
let (ref infcx, goal, var_values) =
54-
self.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal);
55-
let mut acx = AssemblyCtxt { cx: self, infcx, var_values, candidates: Vec::new() };
56-
57-
acx.assemble_candidates_after_normalizing_self_ty(goal);
58-
acx.assemble_impl_candidates(goal);
59-
acx.candidates
60-
}
61-
6238
fn merge_project_candidates(
6339
&mut self,
6440
mut candidates: Vec<Candidate<'tcx>>,
@@ -112,83 +88,27 @@ impl<'tcx> EvalCtxt<'tcx> {
11288
}
11389
}
11490

115-
/// Similar to [super::trait_goals::AssemblyCtxt] but for `Projection` goals.
116-
struct AssemblyCtxt<'a, 'tcx> {
117-
cx: &'a mut EvalCtxt<'tcx>,
118-
infcx: &'a InferCtxt<'tcx>,
119-
var_values: CanonicalVarValues<'tcx>,
120-
candidates: Vec<Candidate<'tcx>>,
121-
}
91+
impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
92+
type CandidateSource = CandidateSource;
12293

123-
impl<'tcx> AssemblyCtxt<'_, 'tcx> {
124-
fn try_insert_candidate(&mut self, source: CandidateSource, certainty: Certainty) {
125-
match self.infcx.make_canonical_response(self.var_values.clone(), certainty) {
126-
Ok(result) => self.candidates.push(Candidate { source, result }),
127-
Err(NoSolution) => debug!(?source, ?certainty, "failed leakcheck"),
128-
}
94+
fn self_ty(self) -> Ty<'tcx> {
95+
self.self_ty()
12996
}
13097

131-
fn assemble_candidates_after_normalizing_self_ty(
132-
&mut self,
133-
goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
134-
) {
135-
let tcx = self.cx.tcx;
136-
let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.projection_ty.self_ty().kind() else {
137-
return
138-
};
139-
self.infcx.probe(|_| {
140-
let normalized_ty = self.infcx.next_ty_infer();
141-
let normalizes_to_goal = goal.with(
142-
tcx,
143-
ty::Binder::dummy(ty::ProjectionPredicate {
144-
projection_ty,
145-
term: normalized_ty.into(),
146-
}),
147-
);
148-
let normalization_certainty =
149-
match self.cx.evaluate_goal(&self.infcx, normalizes_to_goal) {
150-
Ok((_, certainty)) => certainty,
151-
Err(NoSolution) => return,
152-
};
153-
154-
// NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate.
155-
// This doesn't work as long as we use `CandidateSource` in both winnowing and to resolve associated items.
156-
let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
157-
let mut orig_values = OriginalQueryValues::default();
158-
let goal = self.infcx.canonicalize_query(goal, &mut orig_values);
159-
let normalized_candidates = self.cx.assemble_and_evaluate_project_candidates(goal);
160-
// Map each candidate from being canonical wrt the current inference context to being
161-
// canonical wrt the caller.
162-
for Candidate { source, result } in normalized_candidates {
163-
self.infcx.probe(|_| {
164-
let candidate_certainty = fixme_instantiate_canonical_query_response(
165-
self.infcx,
166-
&orig_values,
167-
result,
168-
);
169-
self.try_insert_candidate(
170-
source,
171-
normalization_certainty.unify_and(candidate_certainty),
172-
)
173-
})
174-
}
175-
})
98+
fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
99+
self.with_self_ty(tcx, self_ty)
176100
}
177101

178-
fn assemble_impl_candidates(&mut self, goal: Goal<'tcx, ProjectionPredicate<'tcx>>) {
179-
self.cx.tcx.for_each_relevant_impl(
180-
goal.predicate.trait_def_id(self.cx.tcx),
181-
goal.predicate.self_ty(),
182-
|impl_def_id| self.consider_impl_candidate(goal, impl_def_id),
183-
);
102+
fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId {
103+
self.trait_def_id(tcx)
184104
}
185105

186106
fn consider_impl_candidate(
187-
&mut self,
107+
acx: &mut AssemblyCtxt<'_, 'tcx, ProjectionPredicate<'tcx>>,
188108
goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
189109
impl_def_id: DefId,
190110
) {
191-
let tcx = self.cx.tcx;
111+
let tcx = acx.cx.tcx;
192112
let goal_trait_ref = goal.predicate.projection_ty.trait_ref(tcx);
193113
let impl_trait_ref = tcx.bound_impl_trait_ref(impl_def_id).unwrap();
194114
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder };
@@ -198,11 +118,11 @@ impl<'tcx> AssemblyCtxt<'_, 'tcx> {
198118
return;
199119
}
200120

201-
self.infcx.probe(|_| {
202-
let impl_substs = self.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
121+
acx.infcx.probe(|_| {
122+
let impl_substs = acx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
203123
let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
204124

205-
let Ok(InferOk { obligations, .. }) = self
125+
let Ok(InferOk { obligations, .. }) = acx
206126
.infcx
207127
.at(&ObligationCause::dummy(), goal.param_env)
208128
.define_opaque_types(false)
@@ -213,9 +133,10 @@ impl<'tcx> AssemblyCtxt<'_, 'tcx> {
213133
};
214134

215135
let nested_goals = obligations.into_iter().map(|o| o.into()).collect();
216-
let Ok(trait_ref_certainty) = self.cx.evaluate_all(self.infcx, nested_goals) else { return };
136+
let Ok(trait_ref_certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return };
217137

218-
let Some(assoc_def) = self.fetch_eligible_assoc_item_def(
138+
let Some(assoc_def) = fetch_eligible_assoc_item_def(
139+
acx.infcx,
219140
goal.param_env,
220141
goal_trait_ref,
221142
goal.predicate.def_id(),
@@ -247,7 +168,7 @@ impl<'tcx> AssemblyCtxt<'_, 'tcx> {
247168
impl_trait_ref.substs,
248169
);
249170
let substs = translate_substs(
250-
self.infcx,
171+
acx.infcx,
251172
goal.param_env,
252173
impl_def_id,
253174
impl_substs_with_gat,
@@ -267,7 +188,7 @@ impl<'tcx> AssemblyCtxt<'_, 'tcx> {
267188
ty.map_bound(|ty| ty.into())
268189
};
269190

270-
let Ok(InferOk { obligations, .. }) = self
191+
let Ok(InferOk { obligations, .. }) = acx
271192
.infcx
272193
.at(&ObligationCause::dummy(), goal.param_env)
273194
.define_opaque_types(false)
@@ -278,47 +199,46 @@ impl<'tcx> AssemblyCtxt<'_, 'tcx> {
278199
};
279200

280201
let nested_goals = obligations.into_iter().map(|o| o.into()).collect();
281-
let Ok(rhs_certainty) = self.cx.evaluate_all(self.infcx, nested_goals) else { return };
202+
let Ok(rhs_certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return };
282203

283204
let certainty = trait_ref_certainty.unify_and(rhs_certainty);
284-
self.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty);
205+
acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty);
285206
})
286207
}
208+
}
287209

288-
/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code.
289-
///
290-
/// FIXME: We should merge these 3 implementations as it's likely that they otherwise
291-
/// diverge.
292-
#[instrument(level = "debug", skip(self, param_env), ret)]
293-
fn fetch_eligible_assoc_item_def(
294-
&self,
295-
param_env: ty::ParamEnv<'tcx>,
296-
goal_trait_ref: ty::TraitRef<'tcx>,
297-
trait_assoc_def_id: DefId,
298-
impl_def_id: DefId,
299-
) -> Option<LeafDef> {
300-
let node_item =
301-
specialization_graph::assoc_def(self.cx.tcx, impl_def_id, trait_assoc_def_id)
302-
.map_err(|ErrorGuaranteed { .. }| ())
303-
.ok()?;
304-
305-
let eligible = if node_item.is_final() {
306-
// Non-specializable items are always projectable.
307-
true
210+
/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code.
211+
///
212+
/// FIXME: We should merge these 3 implementations as it's likely that they otherwise
213+
/// diverge.
214+
#[instrument(level = "debug", skip(infcx, param_env), ret)]
215+
fn fetch_eligible_assoc_item_def<'tcx>(
216+
infcx: &InferCtxt<'tcx>,
217+
param_env: ty::ParamEnv<'tcx>,
218+
goal_trait_ref: ty::TraitRef<'tcx>,
219+
trait_assoc_def_id: DefId,
220+
impl_def_id: DefId,
221+
) -> Option<LeafDef> {
222+
let node_item = specialization_graph::assoc_def(infcx.tcx, impl_def_id, trait_assoc_def_id)
223+
.map_err(|ErrorGuaranteed { .. }| ())
224+
.ok()?;
225+
226+
let eligible = if node_item.is_final() {
227+
// Non-specializable items are always projectable.
228+
true
229+
} else {
230+
// Only reveal a specializable default if we're past type-checking
231+
// and the obligation is monomorphic, otherwise passes such as
232+
// transmute checking and polymorphic MIR optimizations could
233+
// get a result which isn't correct for all monomorphizations.
234+
if param_env.reveal() == Reveal::All {
235+
let poly_trait_ref = infcx.resolve_vars_if_possible(goal_trait_ref);
236+
!poly_trait_ref.still_further_specializable()
308237
} else {
309-
// Only reveal a specializable default if we're past type-checking
310-
// and the obligation is monomorphic, otherwise passes such as
311-
// transmute checking and polymorphic MIR optimizations could
312-
// get a result which isn't correct for all monomorphizations.
313-
if param_env.reveal() == Reveal::All {
314-
let poly_trait_ref = self.infcx.resolve_vars_if_possible(goal_trait_ref);
315-
!poly_trait_ref.still_further_specializable()
316-
} else {
317-
debug!(?node_item.item.def_id, "not eligible due to default");
318-
false
319-
}
320-
};
238+
debug!(?node_item.item.def_id, "not eligible due to default");
239+
false
240+
}
241+
};
321242

322-
if eligible { Some(node_item) } else { None }
323-
}
243+
if eligible { Some(node_item) } else { None }
324244
}

‎compiler/rustc_trait_selection/src/solve/trait_goals.rs

Lines changed: 55 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,17 @@
22
33
use std::iter;
44

5-
use super::infcx_ext::InferCtxtExt;
6-
use super::{
7-
fixme_instantiate_canonical_query_response, CanonicalGoal, CanonicalResponse, Certainty,
8-
EvalCtxt, Goal, QueryResult,
9-
};
5+
use super::assembly::{self, AssemblyCtxt};
6+
use super::{CanonicalGoal, EvalCtxt, Goal, QueryResult};
107
use rustc_hir::def_id::DefId;
11-
use rustc_infer::infer::canonical::{CanonicalVarValues, OriginalQueryValues};
12-
use rustc_infer::infer::TyCtxtInferExt;
13-
use rustc_infer::infer::{InferCtxt, InferOk};
8+
use rustc_infer::infer::InferOk;
149
use rustc_infer::traits::query::NoSolution;
1510
use rustc_infer::traits::ObligationCause;
16-
use rustc_middle::ty;
1711
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
1812
use rustc_middle::ty::TraitPredicate;
13+
use rustc_middle::ty::{self, Ty, TyCtxt};
1914
use rustc_span::DUMMY_SP;
2015

21-
/// A candidate is a possible way to prove a goal.
22-
///
23-
/// It consists of both the `source`, which describes how that goal
24-
/// would be proven, and the `result` when using the given `source`.
25-
///
26-
/// For the list of possible candidates, please look at the documentation
27-
/// of [CandidateSource].
28-
#[derive(Debug, Clone)]
29-
pub(super) struct Candidate<'tcx> {
30-
source: CandidateSource,
31-
result: CanonicalResponse<'tcx>,
32-
}
33-
3416
#[allow(dead_code)] // FIXME: implement and use all variants.
3517
#[derive(Debug, Clone, Copy)]
3618
pub(super) enum CandidateSource {
@@ -67,37 +49,67 @@ pub(super) enum CandidateSource {
6749
AutoImpl,
6850
}
6951

70-
struct AssemblyCtxt<'a, 'tcx> {
71-
cx: &'a mut EvalCtxt<'tcx>,
72-
infcx: &'a InferCtxt<'tcx>,
73-
var_values: CanonicalVarValues<'tcx>,
74-
candidates: Vec<Candidate<'tcx>>,
52+
type Candidate<'tcx> = assembly::Candidate<'tcx, TraitPredicate<'tcx>>;
53+
54+
impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
55+
type CandidateSource = CandidateSource;
56+
57+
fn self_ty(self) -> Ty<'tcx> {
58+
self.self_ty()
59+
}
60+
61+
fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
62+
self.with_self_ty(tcx, self_ty)
63+
}
64+
65+
fn trait_def_id(self, _: TyCtxt<'tcx>) -> DefId {
66+
self.def_id()
67+
}
68+
69+
fn consider_impl_candidate(
70+
acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
71+
goal: Goal<'tcx, TraitPredicate<'tcx>>,
72+
impl_def_id: DefId,
73+
) {
74+
let impl_trait_ref = acx.cx.tcx.bound_impl_trait_ref(impl_def_id).unwrap();
75+
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder };
76+
if iter::zip(goal.predicate.trait_ref.substs, impl_trait_ref.skip_binder().substs)
77+
.any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp))
78+
{
79+
return;
80+
}
81+
82+
acx.infcx.probe(|_| {
83+
let impl_substs = acx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
84+
let impl_trait_ref = impl_trait_ref.subst(acx.cx.tcx, impl_substs);
85+
86+
let Ok(InferOk { obligations, .. }) = acx
87+
.infcx
88+
.at(&ObligationCause::dummy(), goal.param_env)
89+
.define_opaque_types(false)
90+
.eq(goal.predicate.trait_ref, impl_trait_ref)
91+
.map_err(|e| debug!("failed to equate trait refs: {e:?}"))
92+
else {
93+
return
94+
};
95+
96+
let nested_goals = obligations.into_iter().map(|o| o.into()).collect();
97+
98+
let Ok(certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return };
99+
acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty);
100+
})
101+
}
75102
}
76103

77104
impl<'tcx> EvalCtxt<'tcx> {
78105
pub(super) fn compute_trait_goal(
79106
&mut self,
80107
goal: CanonicalGoal<'tcx, TraitPredicate<'tcx>>,
81108
) -> QueryResult<'tcx> {
82-
let candidates = self.assemble_and_evaluate_trait_candidates(goal);
109+
let candidates = AssemblyCtxt::assemble_and_evaluate_candidates(self, goal);
83110
self.merge_trait_candidates_discard_reservation_impls(candidates)
84111
}
85112

86-
pub(super) fn assemble_and_evaluate_trait_candidates(
87-
&mut self,
88-
goal: CanonicalGoal<'tcx, TraitPredicate<'tcx>>,
89-
) -> Vec<Candidate<'tcx>> {
90-
let (ref infcx, goal, var_values) =
91-
self.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal);
92-
let mut acx = AssemblyCtxt { cx: self, infcx, var_values, candidates: Vec::new() };
93-
94-
acx.assemble_candidates_after_normalizing_self_ty(goal);
95-
acx.assemble_impl_candidates(goal);
96-
97-
// FIXME: Remaining candidates
98-
acx.candidates
99-
}
100-
101113
#[instrument(level = "debug", skip(self), ret)]
102114
pub(super) fn merge_trait_candidates_discard_reservation_impls(
103115
&mut self,
@@ -166,117 +178,3 @@ impl<'tcx> EvalCtxt<'tcx> {
166178
candidate
167179
}
168180
}
169-
170-
impl<'tcx> AssemblyCtxt<'_, 'tcx> {
171-
/// Adds a new candidate using the current state of the inference context.
172-
///
173-
/// This does require each assembly method to correctly use `probe` to not taint
174-
/// the results of other candidates.
175-
fn try_insert_candidate(&mut self, source: CandidateSource, certainty: Certainty) {
176-
match self.infcx.make_canonical_response(self.var_values.clone(), certainty) {
177-
Ok(result) => self.candidates.push(Candidate { source, result }),
178-
Err(NoSolution) => debug!(?source, ?certainty, "failed leakcheck"),
179-
}
180-
}
181-
182-
/// If the self type of a trait goal is a projection, computing the relevant candidates is difficult.
183-
///
184-
/// To deal with this, we first try to normalize the self type and add the candidates for the normalized
185-
/// self type to the list of candidates in case that succeeds. Note that we can't just eagerly return in
186-
/// this case as projections as self types add `
187-
fn assemble_candidates_after_normalizing_self_ty(
188-
&mut self,
189-
goal: Goal<'tcx, TraitPredicate<'tcx>>,
190-
) {
191-
let tcx = self.cx.tcx;
192-
// FIXME: We also have to normalize opaque types, not sure where to best fit that in.
193-
let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.self_ty().kind() else {
194-
return
195-
};
196-
self.infcx.probe(|_| {
197-
let normalized_ty = self.infcx.next_ty_infer();
198-
let normalizes_to_goal = goal.with(
199-
tcx,
200-
ty::Binder::dummy(ty::ProjectionPredicate {
201-
projection_ty,
202-
term: normalized_ty.into(),
203-
}),
204-
);
205-
let normalization_certainty =
206-
match self.cx.evaluate_goal(&self.infcx, normalizes_to_goal) {
207-
Ok((_, certainty)) => certainty,
208-
Err(NoSolution) => return,
209-
};
210-
211-
// NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate.
212-
// This doesn't work as long as we use `CandidateSource` in both winnowing and to resolve associated items.
213-
let goal = goal.with(tcx, goal.predicate.with_self_type(tcx, normalized_ty));
214-
let mut orig_values = OriginalQueryValues::default();
215-
let goal = self.infcx.canonicalize_query(goal, &mut orig_values);
216-
let normalized_candidates = self.cx.assemble_and_evaluate_trait_candidates(goal);
217-
218-
// Map each candidate from being canonical wrt the current inference context to being
219-
// canonical wrt the caller.
220-
for Candidate { source, result } in normalized_candidates {
221-
self.infcx.probe(|_| {
222-
let candidate_certainty = fixme_instantiate_canonical_query_response(
223-
self.infcx,
224-
&orig_values,
225-
result,
226-
);
227-
228-
// FIXME: This is a bit scary if the `normalizes_to_goal` overflows.
229-
//
230-
// If we have an ambiguous candidate it hides that normalization
231-
// caused an overflow which may cause issues.
232-
self.try_insert_candidate(
233-
source,
234-
normalization_certainty.unify_and(candidate_certainty),
235-
)
236-
})
237-
}
238-
})
239-
}
240-
241-
fn assemble_impl_candidates(&mut self, goal: Goal<'tcx, TraitPredicate<'tcx>>) {
242-
self.cx.tcx.for_each_relevant_impl(
243-
goal.predicate.def_id(),
244-
goal.predicate.self_ty(),
245-
|impl_def_id| self.consider_impl_candidate(goal, impl_def_id),
246-
);
247-
}
248-
249-
fn consider_impl_candidate(
250-
&mut self,
251-
goal: Goal<'tcx, TraitPredicate<'tcx>>,
252-
impl_def_id: DefId,
253-
) {
254-
let impl_trait_ref = self.cx.tcx.bound_impl_trait_ref(impl_def_id).unwrap();
255-
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder };
256-
if iter::zip(goal.predicate.trait_ref.substs, impl_trait_ref.skip_binder().substs)
257-
.any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp))
258-
{
259-
return;
260-
}
261-
262-
self.infcx.probe(|_| {
263-
let impl_substs = self.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
264-
let impl_trait_ref = impl_trait_ref.subst(self.cx.tcx, impl_substs);
265-
266-
let Ok(InferOk { obligations, .. }) = self
267-
.infcx
268-
.at(&ObligationCause::dummy(), goal.param_env)
269-
.define_opaque_types(false)
270-
.eq(goal.predicate.trait_ref, impl_trait_ref)
271-
.map_err(|e| debug!("failed to equate trait refs: {e:?}"))
272-
else {
273-
return
274-
};
275-
276-
let nested_goals = obligations.into_iter().map(|o| o.into()).collect();
277-
278-
let Ok(certainty) = self.cx.evaluate_all(self.infcx, nested_goals) else { return };
279-
self.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty);
280-
})
281-
}
282-
}

0 commit comments

Comments
 (0)
Please sign in to comment.