Skip to content

Commit 2428083

Browse files
authored
Rollup merge of #108839 - compiler-errors:canonicalize-the-root-var, r=lcnr
Canonicalize root var when making response from new solver During trait solving, if we equate two inference variables `?0` and `?1` but don't equate them with any rigid types, then `InferCtxt::probe_ty_var` will return `Err` for both of these. The canonicalizer code will then canonicalize the variables independently(!), and the response will not reflect the fact that these two variables have been made equal. This hinders inference and I also don't think it's sound? I haven't thought too much about it past that, so let's talk about it. r? ``@lcnr``
2 parents 23f46c5 + 3bfcfd0 commit 2428083

File tree

6 files changed

+104
-15
lines changed

6 files changed

+104
-15
lines changed

compiler/rustc_infer/src/infer/canonical/canonicalizer.rs

+22-4
Original file line numberDiff line numberDiff line change
@@ -374,9 +374,18 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
374374
}
375375
}
376376

377-
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
377+
fn fold_ty(&mut self, mut t: Ty<'tcx>) -> Ty<'tcx> {
378378
match *t.kind() {
379-
ty::Infer(ty::TyVar(vid)) => {
379+
ty::Infer(ty::TyVar(mut vid)) => {
380+
// We need to canonicalize the *root* of our ty var.
381+
// This is so that our canonical response correctly reflects
382+
// any equated inference vars correctly!
383+
let root_vid = self.infcx.root_var(vid);
384+
if root_vid != vid {
385+
t = self.infcx.tcx.mk_ty_var(root_vid);
386+
vid = root_vid;
387+
}
388+
380389
debug!("canonical: type var found with vid {:?}", vid);
381390
match self.infcx.probe_ty_var(vid) {
382391
// `t` could be a float / int variable; canonicalize that instead.
@@ -467,9 +476,18 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
467476
}
468477
}
469478

470-
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
479+
fn fold_const(&mut self, mut ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
471480
match ct.kind() {
472-
ty::ConstKind::Infer(InferConst::Var(vid)) => {
481+
ty::ConstKind::Infer(InferConst::Var(mut vid)) => {
482+
// We need to canonicalize the *root* of our const var.
483+
// This is so that our canonical response correctly reflects
484+
// any equated inference vars correctly!
485+
let root_vid = self.infcx.root_const_var(vid);
486+
if root_vid != vid {
487+
ct = self.infcx.tcx.mk_const(ty::InferConst::Var(root_vid), ct.ty());
488+
vid = root_vid;
489+
}
490+
473491
debug!("canonical: const var found with vid {:?}", vid);
474492
match self.infcx.probe_const_var(vid) {
475493
Ok(c) => {

compiler/rustc_infer/src/infer/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1359,6 +1359,10 @@ impl<'tcx> InferCtxt<'tcx> {
13591359
self.inner.borrow_mut().type_variables().root_var(var)
13601360
}
13611361

1362+
pub fn root_const_var(&self, var: ty::ConstVid<'tcx>) -> ty::ConstVid<'tcx> {
1363+
self.inner.borrow_mut().const_unification_table().find(var)
1364+
}
1365+
13621366
/// Where possible, replaces type/const variables in
13631367
/// `value` with their final value. Note that region variables
13641368
/// are unaffected. If a type/const variable has not been unified, it

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

+32-11
Original file line numberDiff line numberDiff line change
@@ -261,12 +261,23 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
261261
self.interner().mk_re_late_bound(self.binder_index, br)
262262
}
263263

264-
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
264+
fn fold_ty(&mut self, mut t: Ty<'tcx>) -> Ty<'tcx> {
265265
let kind = match *t.kind() {
266-
ty::Infer(ty::TyVar(vid)) => match self.infcx.probe_ty_var(vid) {
267-
Ok(t) => return self.fold_ty(t),
268-
Err(ui) => CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)),
269-
},
266+
ty::Infer(ty::TyVar(mut vid)) => {
267+
// We need to canonicalize the *root* of our ty var.
268+
// This is so that our canonical response correctly reflects
269+
// any equated inference vars correctly!
270+
let root_vid = self.infcx.root_var(vid);
271+
if root_vid != vid {
272+
t = self.infcx.tcx.mk_ty_var(root_vid);
273+
vid = root_vid;
274+
}
275+
276+
match self.infcx.probe_ty_var(vid) {
277+
Ok(t) => return self.fold_ty(t),
278+
Err(ui) => CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)),
279+
}
280+
}
270281
ty::Infer(ty::IntVar(_)) => {
271282
let nt = self.infcx.shallow_resolve(t);
272283
if nt != t {
@@ -338,13 +349,23 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
338349
self.interner().mk_bound(self.binder_index, bt)
339350
}
340351

341-
fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
352+
fn fold_const(&mut self, mut c: ty::Const<'tcx>) -> ty::Const<'tcx> {
342353
let kind = match c.kind() {
343-
ty::ConstKind::Infer(ty::InferConst::Var(vid)) => match self.infcx.probe_const_var(vid)
344-
{
345-
Ok(c) => return self.fold_const(c),
346-
Err(universe) => CanonicalVarKind::Const(universe, c.ty()),
347-
},
354+
ty::ConstKind::Infer(ty::InferConst::Var(mut vid)) => {
355+
// We need to canonicalize the *root* of our const var.
356+
// This is so that our canonical response correctly reflects
357+
// any equated inference vars correctly!
358+
let root_vid = self.infcx.root_const_var(vid);
359+
if root_vid != vid {
360+
c = self.infcx.tcx.mk_const(ty::InferConst::Var(root_vid), c.ty());
361+
vid = root_vid;
362+
}
363+
364+
match self.infcx.probe_const_var(vid) {
365+
Ok(c) => return self.fold_const(c),
366+
Err(universe) => CanonicalVarKind::Const(universe, c.ty()),
367+
}
368+
}
348369
ty::ConstKind::Infer(ty::InferConst::Fresh(_)) => {
349370
bug!("fresh var during canonicalization: {c:?}")
350371
}

compiler/rustc_trait_selection/src/solve/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
238238
&& has_changed
239239
&& !self.in_projection_eq_hack
240240
&& !self.search_graph.in_cycle()
241+
&& false
241242
{
242243
let (_orig_values, canonical_goal) = self.canonicalize_goal(goal);
243244
let canonical_response =
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// check-pass
2+
// compile-flags: -Ztrait-solver=next
3+
4+
trait Mirror {
5+
type Item;
6+
}
7+
8+
struct Wrapper<T>(T);
9+
impl<T> Mirror for Wrapper<T> {
10+
type Item = T;
11+
}
12+
13+
fn mirror<T>()
14+
where
15+
Wrapper<T>: Mirror<Item = i32>,
16+
{
17+
}
18+
19+
fn main() {
20+
mirror::<_ /* ?0 */>();
21+
22+
// Solving `<Wrapper<?0> as Mirror>::Item = i32`
23+
24+
// First, we replace the term with a fresh infer var:
25+
// `<Wrapper<?0> as Mirror>::Item = ?1`
26+
27+
// We select the impl candidate on line #6, which leads us to learn that
28+
// `?0 == ?1`.
29+
30+
// That should be reflected in our canonical response, which should have
31+
// `^0 = ^0, ^1 = ^0`
32+
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
33+
// !! We used to return a totally unconstrained response here :< !!
34+
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
35+
36+
// Then, during the "equate term" part of the projection solving, we
37+
// instantiate the response from the unconstrained projection predicate,
38+
// and equate `?0 == i32`.
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// check-pass
2+
// compile-flags: -Ztrait-solver=next
3+
4+
fn main() {
5+
let x: Box<dyn Iterator<Item = ()>> = Box::new(std::iter::empty());
6+
}

0 commit comments

Comments
 (0)