Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Canonicalize root var when making response from new solver #108839

Merged
merged 2 commits into from
Mar 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -376,9 +376,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 @@ -469,9 +478,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 @@ -1356,6 +1356,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
Copy link
Member Author

@compiler-errors compiler-errors Mar 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both of these UI tests I added in the PR fail here. That's because the inference guidance we applied from a Certainty::AMBIGUOUS response can make the goal pass the next time it's evaluated.

This seems like it's to be expected though, as long as we're returning canonical var values in ambiguous responses 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't be, the issue again that the solver proves goals in sequence and doesn't reprove previous goals.

I guess disabling it for now is alright but I do want us to get to a point where we always internally keep reproving goals until we hit a fixpoint

{
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());
}