Skip to content

Commit ed63201

Browse files
committed
replace usage of evaluate_goal with a new add_goal
1 parent 7ac4b82 commit ed63201

File tree

8 files changed

+336
-254
lines changed

8 files changed

+336
-254
lines changed

compiler/rustc_trait_selection/src/solve/assembly.rs

+20-27
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
224224
if goal.predicate.self_ty().is_ty_var() {
225225
return vec![Candidate {
226226
source: CandidateSource::BuiltinImpl,
227-
result: self.make_canonical_response(Certainty::AMBIGUOUS).unwrap(),
227+
result: self
228+
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
229+
.unwrap(),
228230
}];
229231
}
230232

@@ -261,37 +263,26 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
261263
let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.self_ty().kind() else {
262264
return
263265
};
264-
self.probe(|this| {
265-
let normalized_ty = this.next_ty_infer();
266+
267+
self.probe(|ecx| {
268+
let normalized_ty = ecx.next_ty_infer();
266269
let normalizes_to_goal = goal.with(
267270
tcx,
268271
ty::Binder::dummy(ty::ProjectionPredicate {
269272
projection_ty,
270273
term: normalized_ty.into(),
271274
}),
272275
);
273-
let normalization_certainty = match this.evaluate_goal(normalizes_to_goal) {
274-
Ok((_, certainty)) => certainty,
275-
Err(NoSolution) => return,
276-
};
277-
let normalized_ty = this.resolve_vars_if_possible(normalized_ty);
278-
279-
// NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate.
280-
// This doesn't work as long as we use `CandidateSource` in winnowing.
281-
let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
282-
let normalized_candidates = this.assemble_and_evaluate_candidates(goal);
283-
for mut normalized_candidate in normalized_candidates {
284-
normalized_candidate.result =
285-
normalized_candidate.result.unchecked_map(|mut response| {
286-
// FIXME: This currently hides overflow in the normalization step of the self type
287-
// which is probably wrong. Maybe `unify_and` should actually keep overflow as
288-
// we treat it as non-fatal anyways.
289-
response.certainty = response.certainty.unify_and(normalization_certainty);
290-
response
291-
});
292-
candidates.push(normalized_candidate);
276+
ecx.add_goal(normalizes_to_goal);
277+
if let Ok(_) = ecx.try_evaluate_added_goals() {
278+
let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty);
279+
280+
// NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate.
281+
// This doesn't work as long as we use `CandidateSource` in winnowing.
282+
let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
283+
candidates.extend(ecx.assemble_and_evaluate_candidates(goal));
293284
}
294-
})
285+
});
295286
}
296287

297288
fn assemble_impl_candidates<G: GoalKind<'tcx>>(
@@ -516,7 +507,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
516507
} else {
517508
Certainty::AMBIGUOUS
518509
};
519-
return self.make_canonical_response(certainty);
510+
return self.evaluate_added_goals_and_make_canonical_response(certainty);
520511
}
521512
}
522513

@@ -538,14 +529,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
538529
}
539530
}
540531

541-
fn discard_reservation_impl(&self, mut candidate: Candidate<'tcx>) -> Candidate<'tcx> {
532+
fn discard_reservation_impl(&mut self, mut candidate: Candidate<'tcx>) -> Candidate<'tcx> {
542533
if let CandidateSource::Impl(def_id) = candidate.source {
543534
if let ty::ImplPolarity::Reservation = self.tcx().impl_polarity(def_id) {
544535
debug!("Selected reservation impl");
545536
// We assemble all candidates inside of a probe so by
546537
// making a new canonical response here our result will
547538
// have no constraints.
548-
candidate.result = self.make_canonical_response(Certainty::AMBIGUOUS).unwrap();
539+
candidate.result = self
540+
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
541+
.unwrap();
549542
}
550543
}
551544

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

+15-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,20 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
4848
/// - `external_constraints`: additional constraints which aren't expressable
4949
/// using simple unification of inference variables.
5050
#[instrument(level = "debug", skip(self))]
51-
pub(super) fn make_canonical_response(&self, certainty: Certainty) -> QueryResult<'tcx> {
51+
pub(super) fn evaluate_added_goals_and_make_canonical_response(
52+
&mut self,
53+
certainty: Certainty,
54+
) -> QueryResult<'tcx> {
55+
let goals_certainty = self.try_evaluate_added_goals()?;
56+
let certainty = certainty.unify_and(goals_certainty);
57+
58+
if let Certainty::Yes = certainty {
59+
assert!(
60+
self.nested_goals.is_empty(),
61+
"Cannot be certain of query response if unevaluated goals exist"
62+
);
63+
}
64+
5265
let external_constraints = self.compute_external_query_constraints()?;
5366

5467
let response = Response { var_values: self.var_values, external_constraints, certainty };
@@ -209,7 +222,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
209222
// FIXME: To deal with #105787 I also expect us to emit nested obligations here at
210223
// some point. We can figure out how to deal with this once we actually have
211224
// an ICE.
212-
let nested_goals = self.eq(param_env, orig, response)?;
225+
let nested_goals = self.eq_and_get_goals(param_env, orig, response)?;
213226
assert!(nested_goals.is_empty(), "{nested_goals:?}");
214227
}
215228

compiler/rustc_trait_selection/src/solve/eval_ctxt.rs

+54-6
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ use rustc_middle::ty::{
1313
use rustc_span::DUMMY_SP;
1414
use std::ops::ControlFlow;
1515

16-
use super::search_graph::SearchGraph;
17-
use super::Goal;
16+
use super::{search_graph::SearchGraph, Goal};
1817

1918
pub struct EvalCtxt<'a, 'tcx> {
2019
// FIXME: should be private.
@@ -33,14 +32,35 @@ pub struct EvalCtxt<'a, 'tcx> {
3332

3433
pub(super) search_graph: &'a mut SearchGraph<'tcx>,
3534

36-
/// This field is used by a debug assertion in [`EvalCtxt::evaluate_goal`],
37-
/// see the comment in that method for more details.
38-
pub in_projection_eq_hack: bool,
35+
pub(super) nested_goals: NestedGoals<'tcx>,
36+
}
37+
38+
#[derive(Debug, Clone)]
39+
pub(super) struct NestedGoals<'tcx> {
40+
pub(super) projection_eq_hack_goal: Option<Goal<'tcx, ty::ProjectionPredicate<'tcx>>>,
41+
pub(super) goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
42+
}
43+
44+
impl NestedGoals<'_> {
45+
pub(super) fn new() -> Self {
46+
Self { projection_eq_hack_goal: None, goals: Vec::new() }
47+
}
48+
49+
pub(super) fn is_empty(&self) -> bool {
50+
self.projection_eq_hack_goal.is_none() && self.goals.is_empty()
51+
}
3952
}
4053

4154
impl<'tcx> EvalCtxt<'_, 'tcx> {
4255
pub(super) fn probe<T>(&mut self, f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> T) -> T {
43-
self.infcx.probe(|_| f(self))
56+
let mut ecx = EvalCtxt {
57+
infcx: self.infcx,
58+
var_values: self.var_values,
59+
max_input_universe: self.max_input_universe,
60+
search_graph: self.search_graph,
61+
nested_goals: self.nested_goals.clone(),
62+
};
63+
self.infcx.probe(|_| f(&mut ecx))
4464
}
4565

4666
pub(super) fn tcx(&self) -> TyCtxt<'tcx> {
@@ -61,6 +81,15 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
6181
)
6282
}
6383

84+
/// Returns a ty infer or a const infer depending on whether `kind` is a `Ty` or `Const`.
85+
/// If `kind` is an integer inference variable this will still return a ty infer var.
86+
pub(super) fn next_term_infer_of_kind(&self, kind: ty::Term<'tcx>) -> ty::Term<'tcx> {
87+
match kind.unpack() {
88+
ty::TermKind::Ty(_) => self.next_ty_infer().into(),
89+
ty::TermKind::Const(ct) => self.next_const_infer(ct.ty()).into(),
90+
}
91+
}
92+
6493
/// Is the projection predicate is of the form `exists<T> <Ty as Trait>::Assoc = T`.
6594
///
6695
/// This is the case if the `term` is an inference variable in the innermost universe
@@ -137,6 +166,25 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
137166

138167
#[instrument(level = "debug", skip(self, param_env), ret)]
139168
pub(super) fn eq<T: ToTrace<'tcx>>(
169+
&mut self,
170+
param_env: ty::ParamEnv<'tcx>,
171+
lhs: T,
172+
rhs: T,
173+
) -> Result<(), NoSolution> {
174+
self.infcx
175+
.at(&ObligationCause::dummy(), param_env)
176+
.eq(DefineOpaqueTypes::No, lhs, rhs)
177+
.map(|InferOk { value: (), obligations }| {
178+
self.add_goals(obligations.into_iter().map(|o| o.into()));
179+
})
180+
.map_err(|e| {
181+
debug!(?e, "failed to equate");
182+
NoSolution
183+
})
184+
}
185+
186+
#[instrument(level = "debug", skip(self, param_env), ret)]
187+
pub(super) fn eq_and_get_goals<T: ToTrace<'tcx>>(
140188
&self,
141189
param_env: ty::ParamEnv<'tcx>,
142190
lhs: T,

0 commit comments

Comments
 (0)