Skip to content

Commit

Permalink
Rollup merge of #108839 - compiler-errors:canonicalize-the-root-var, …
Browse files Browse the repository at this point in the history
…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``
  • Loading branch information
matthiaskrgr authored Mar 8, 2023
2 parents 23f46c5 + 3bfcfd0 commit 2428083
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 15 deletions.
26 changes: 22 additions & 4 deletions compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,9 +374,18 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
}
}

fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
fn fold_ty(&mut self, mut t: Ty<'tcx>) -> Ty<'tcx> {
match *t.kind() {
ty::Infer(ty::TyVar(vid)) => {
ty::Infer(ty::TyVar(mut vid)) => {
// We need to canonicalize the *root* of our ty var.
// This is so that our canonical response correctly reflects
// any equated inference vars correctly!
let root_vid = self.infcx.root_var(vid);
if root_vid != vid {
t = self.infcx.tcx.mk_ty_var(root_vid);
vid = root_vid;
}

debug!("canonical: type var found with vid {:?}", vid);
match self.infcx.probe_ty_var(vid) {
// `t` could be a float / int variable; canonicalize that instead.
Expand Down Expand Up @@ -467,9 +476,18 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
}
}

fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
fn fold_const(&mut self, mut ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
match ct.kind() {
ty::ConstKind::Infer(InferConst::Var(vid)) => {
ty::ConstKind::Infer(InferConst::Var(mut vid)) => {
// We need to canonicalize the *root* of our const var.
// This is so that our canonical response correctly reflects
// any equated inference vars correctly!
let root_vid = self.infcx.root_const_var(vid);
if root_vid != vid {
ct = self.infcx.tcx.mk_const(ty::InferConst::Var(root_vid), ct.ty());
vid = root_vid;
}

debug!("canonical: const var found with vid {:?}", vid);
match self.infcx.probe_const_var(vid) {
Ok(c) => {
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1359,6 +1359,10 @@ impl<'tcx> InferCtxt<'tcx> {
self.inner.borrow_mut().type_variables().root_var(var)
}

pub fn root_const_var(&self, var: ty::ConstVid<'tcx>) -> ty::ConstVid<'tcx> {
self.inner.borrow_mut().const_unification_table().find(var)
}

/// Where possible, replaces type/const variables in
/// `value` with their final value. Note that region variables
/// are unaffected. If a type/const variable has not been unified, it
Expand Down
43 changes: 32 additions & 11 deletions compiler/rustc_trait_selection/src/solve/canonical/canonicalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,12 +261,23 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
self.interner().mk_re_late_bound(self.binder_index, br)
}

fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
fn fold_ty(&mut self, mut t: Ty<'tcx>) -> Ty<'tcx> {
let kind = match *t.kind() {
ty::Infer(ty::TyVar(vid)) => match self.infcx.probe_ty_var(vid) {
Ok(t) => return self.fold_ty(t),
Err(ui) => CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)),
},
ty::Infer(ty::TyVar(mut vid)) => {
// We need to canonicalize the *root* of our ty var.
// This is so that our canonical response correctly reflects
// any equated inference vars correctly!
let root_vid = self.infcx.root_var(vid);
if root_vid != vid {
t = self.infcx.tcx.mk_ty_var(root_vid);
vid = root_vid;
}

match self.infcx.probe_ty_var(vid) {
Ok(t) => return self.fold_ty(t),
Err(ui) => CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)),
}
}
ty::Infer(ty::IntVar(_)) => {
let nt = self.infcx.shallow_resolve(t);
if nt != t {
Expand Down Expand Up @@ -338,13 +349,23 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
self.interner().mk_bound(self.binder_index, bt)
}

fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
fn fold_const(&mut self, mut c: ty::Const<'tcx>) -> ty::Const<'tcx> {
let kind = match c.kind() {
ty::ConstKind::Infer(ty::InferConst::Var(vid)) => match self.infcx.probe_const_var(vid)
{
Ok(c) => return self.fold_const(c),
Err(universe) => CanonicalVarKind::Const(universe, c.ty()),
},
ty::ConstKind::Infer(ty::InferConst::Var(mut vid)) => {
// We need to canonicalize the *root* of our const var.
// This is so that our canonical response correctly reflects
// any equated inference vars correctly!
let root_vid = self.infcx.root_const_var(vid);
if root_vid != vid {
c = self.infcx.tcx.mk_const(ty::InferConst::Var(root_vid), c.ty());
vid = root_vid;
}

match self.infcx.probe_const_var(vid) {
Ok(c) => return self.fold_const(c),
Err(universe) => CanonicalVarKind::Const(universe, c.ty()),
}
}
ty::ConstKind::Infer(ty::InferConst::Fresh(_)) => {
bug!("fresh var during canonicalization: {c:?}")
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_trait_selection/src/solve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
&& has_changed
&& !self.in_projection_eq_hack
&& !self.search_graph.in_cycle()
&& false
{
let (_orig_values, canonical_goal) = self.canonicalize_goal(goal);
let canonical_response =
Expand Down
39 changes: 39 additions & 0 deletions tests/ui/traits/new-solver/canonical-ty-var-eq-in-response.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// check-pass
// compile-flags: -Ztrait-solver=next

trait Mirror {
type Item;
}

struct Wrapper<T>(T);
impl<T> Mirror for Wrapper<T> {
type Item = T;
}

fn mirror<T>()
where
Wrapper<T>: Mirror<Item = i32>,
{
}

fn main() {
mirror::<_ /* ?0 */>();

// Solving `<Wrapper<?0> as Mirror>::Item = i32`

// First, we replace the term with a fresh infer var:
// `<Wrapper<?0> as Mirror>::Item = ?1`

// We select the impl candidate on line #6, which leads us to learn that
// `?0 == ?1`.

// That should be reflected in our canonical response, which should have
// `^0 = ^0, ^1 = ^0`
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !! We used to return a totally unconstrained response here :< !!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

// Then, during the "equate term" part of the projection solving, we
// instantiate the response from the unconstrained projection predicate,
// and equate `?0 == i32`.
}
6 changes: 6 additions & 0 deletions tests/ui/traits/new-solver/deduce-ty-from-object.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// check-pass
// compile-flags: -Ztrait-solver=next

fn main() {
let x: Box<dyn Iterator<Item = ()>> = Box::new(std::iter::empty());
}

0 comments on commit 2428083

Please sign in to comment.