Skip to content

Commit 2bab422

Browse files
Return nested obligations from canonical response var unification
1 parent df7fd99 commit 2bab422

File tree

5 files changed

+78
-25
lines changed

5 files changed

+78
-25
lines changed

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

+8-14
Original file line numberDiff line numberDiff line change
@@ -99,20 +99,20 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
9999
param_env: ty::ParamEnv<'tcx>,
100100
original_values: Vec<ty::GenericArg<'tcx>>,
101101
response: CanonicalResponse<'tcx>,
102-
) -> Result<Certainty, NoSolution> {
102+
) -> Result<(Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
103103
let substitution = self.compute_query_response_substitution(&original_values, &response);
104104

105105
let Response { var_values, external_constraints, certainty } =
106106
response.substitute(self.tcx(), &substitution);
107107

108-
self.unify_query_var_values(param_env, &original_values, var_values)?;
108+
let nested_goals = self.unify_query_var_values(param_env, &original_values, var_values)?;
109109

110110
// FIXME: implement external constraints.
111111
let ExternalConstraintsData { region_constraints, opaque_types: _ } =
112112
external_constraints.deref();
113113
self.register_region_constraints(region_constraints);
114114

115-
Ok(certainty)
115+
Ok((certainty, nested_goals))
116116
}
117117

118118
/// This returns the substitutions to instantiate the bound variables of
@@ -205,21 +205,15 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
205205
param_env: ty::ParamEnv<'tcx>,
206206
original_values: &[ty::GenericArg<'tcx>],
207207
var_values: CanonicalVarValues<'tcx>,
208-
) -> Result<(), NoSolution> {
208+
) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> {
209209
assert_eq!(original_values.len(), var_values.len());
210+
211+
let mut nested_goals = vec![];
210212
for (&orig, response) in iter::zip(original_values, var_values.var_values) {
211-
// This can fail due to the occurs check, see
212-
// `tests/ui/typeck/lazy-norm/equating-projection-cyclically.rs` for an example
213-
// where that can happen.
214-
//
215-
// FIXME: To deal with #105787 I also expect us to emit nested obligations here at
216-
// some point. We can figure out how to deal with this once we actually have
217-
// an ICE.
218-
let nested_goals = self.eq_and_get_goals(param_env, orig, response)?;
219-
assert!(nested_goals.is_empty(), "{nested_goals:?}");
213+
nested_goals.extend(self.eq_and_get_goals(param_env, orig, response)?);
220214
}
221215

222-
Ok(())
216+
Ok(nested_goals)
223217
}
224218

225219
fn register_region_constraints(&mut self, region_constraints: &QueryRegionConstraints<'tcx>) {

compiler/rustc_trait_selection/src/solve/eval_ctxt.rs

+9-8
Original file line numberDiff line numberDiff line change
@@ -70,17 +70,16 @@ pub trait InferCtxtEvalExt<'tcx> {
7070
fn evaluate_root_goal(
7171
&self,
7272
goal: Goal<'tcx, ty::Predicate<'tcx>>,
73-
) -> Result<(bool, Certainty), NoSolution>;
73+
) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>;
7474
}
7575

7676
impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
7777
#[instrument(level = "debug", skip(self))]
7878
fn evaluate_root_goal(
7979
&self,
8080
goal: Goal<'tcx, ty::Predicate<'tcx>>,
81-
) -> Result<(bool, Certainty), NoSolution> {
81+
) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
8282
let mode = if self.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
83-
8483
let mut search_graph = search_graph::SearchGraph::new(self.tcx, mode);
8584

8685
let mut ecx = EvalCtxt {
@@ -152,13 +151,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
152151
&mut self,
153152
is_normalizes_to_hack: IsNormalizesToHack,
154153
goal: Goal<'tcx, ty::Predicate<'tcx>>,
155-
) -> Result<(bool, Certainty), NoSolution> {
154+
) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
156155
let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
157156
let canonical_response =
158157
EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
159158

160159
let has_changed = !canonical_response.value.var_values.is_identity();
161-
let certainty = self.instantiate_and_apply_query_response(
160+
let (certainty, nested_goals) = self.instantiate_and_apply_query_response(
162161
goal.param_env,
163162
orig_values,
164163
canonical_response,
@@ -186,7 +185,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
186185
assert_eq!(certainty, canonical_response.value.certainty);
187186
}
188187

189-
Ok((has_changed, certainty))
188+
Ok((has_changed, certainty, nested_goals))
190189
}
191190

192191
fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> {
@@ -263,13 +262,14 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
263262
let mut has_changed = Err(Certainty::Yes);
264263

265264
if let Some(goal) = goals.normalizes_to_hack_goal.take() {
266-
let (_, certainty) = match this.evaluate_goal(
265+
let (_, certainty, nested_goals) = match this.evaluate_goal(
267266
IsNormalizesToHack::Yes,
268267
goal.with(this.tcx(), ty::Binder::dummy(goal.predicate)),
269268
) {
270269
Ok(r) => r,
271270
Err(NoSolution) => return Some(Err(NoSolution)),
272271
};
272+
new_goals.goals.extend(nested_goals);
273273

274274
if goal.predicate.projection_ty
275275
!= this.resolve_vars_if_possible(goal.predicate.projection_ty)
@@ -308,11 +308,12 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
308308
}
309309

310310
for nested_goal in goals.goals.drain(..) {
311-
let (changed, certainty) =
311+
let (changed, certainty, nested_goals) =
312312
match this.evaluate_goal(IsNormalizesToHack::No, nested_goal) {
313313
Ok(result) => result,
314314
Err(NoSolution) => return Some(Err(NoSolution)),
315315
};
316+
new_goals.goals.extend(nested_goals);
316317

317318
if changed {
318319
has_changed = Ok(());

compiler/rustc_trait_selection/src/solve/fulfill.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::mem;
22

33
use rustc_infer::infer::InferCtxt;
4+
use rustc_infer::traits::Obligation;
45
use rustc_infer::traits::{
56
query::NoSolution, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes,
67
PredicateObligation, SelectionError, TraitEngine,
@@ -61,7 +62,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
6162
let mut has_changed = false;
6263
for obligation in mem::take(&mut self.obligations) {
6364
let goal = obligation.clone().into();
64-
let (changed, certainty) = match infcx.evaluate_root_goal(goal) {
65+
let (changed, certainty, nested_goals) = match infcx.evaluate_root_goal(goal) {
6566
Ok(result) => result,
6667
Err(NoSolution) => {
6768
errors.push(FulfillmentError {
@@ -125,7 +126,16 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
125126
continue;
126127
}
127128
};
128-
129+
// Push any nested goals that we get from unifying our canonical response
130+
// with our obligation onto the fulfillment context.
131+
self.obligations.extend(nested_goals.into_iter().map(|goal| {
132+
Obligation::new(
133+
infcx.tcx,
134+
obligation.cause.clone(),
135+
goal.param_env,
136+
goal.predicate,
137+
)
138+
}));
129139
has_changed |= changed;
130140
match certainty {
131141
Certainty::Yes => {}

compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,18 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
8181

8282
if self.tcx.trait_solver_next() {
8383
self.probe(|snapshot| {
84-
if let Ok((_, certainty)) =
84+
if let Ok((_, certainty, nested_goals)) =
8585
self.evaluate_root_goal(Goal::new(self.tcx, param_env, obligation.predicate))
8686
{
8787
match certainty {
88+
// If we have nested obligations from instantiating the canonical
89+
// response from this goal, just treat the response as ambiguous.
90+
//
91+
// FIXME(deferred_projection_equality): We need to process this
92+
// in a loop probably... can't be worse than an ICE though
93+
Certainty::Yes if !nested_goals.is_empty() => {
94+
Ok(EvaluationResult::EvaluatedToAmbig)
95+
}
8896
Certainty::Yes => {
8997
if self.opaque_types_added_in_snapshot(snapshot) {
9098
Ok(EvaluationResult::EvaluatedToOkModuloOpaqueTypes)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// check-pass
2+
// compile-flags: -Ztrait-solver=next
3+
4+
trait Foo {
5+
type Gat<'a>
6+
where
7+
Self: 'a;
8+
fn bar(&self) -> Self::Gat<'_>;
9+
}
10+
11+
enum Option<T> {
12+
Some(T),
13+
None,
14+
}
15+
16+
impl<T> Option<T> {
17+
fn as_ref(&self) -> Option<&T> {
18+
match self {
19+
Option::Some(t) => Option::Some(t),
20+
Option::None => Option::None,
21+
}
22+
}
23+
24+
fn map<U>(self, f: impl FnOnce(T) -> U) -> Option<U> {
25+
match self {
26+
Option::Some(t) => Option::Some(f(t)),
27+
Option::None => Option::None,
28+
}
29+
}
30+
}
31+
32+
impl<T: Foo + 'static> Foo for Option<T> {
33+
type Gat<'a> = Option<<T as Foo>::Gat<'a>> where Self: 'a;
34+
35+
fn bar(&self) -> Self::Gat<'_> {
36+
self.as_ref().map(Foo::bar)
37+
}
38+
}
39+
40+
fn main() {}

0 commit comments

Comments
 (0)