Skip to content
Open
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
146 changes: 137 additions & 9 deletions compiler/rustc_next_trait_solver/src/canonical/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@
//! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html

use std::iter;
use std::marker::PhantomData;

use canonicalizer::Canonicalizer;
use rustc_index::IndexVec;
use rustc_type_ir::inherent::*;
use rustc_type_ir::relate::solver_relating::RelateExt;
use rustc_type_ir::{
self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner,
TypeFoldable, TypingMode, TypingModeEqWrapper,
TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, TypingMode, TypingModeEqWrapper,
};
use tracing::instrument;

Expand Down Expand Up @@ -150,12 +151,29 @@ where
// FIXME: Longterm canonical queries should deal with all placeholders
// created inside of the query directly instead of returning them to the
// caller.
let prev_universe = delegate.universe();
let universes_created_in_query = response.max_universe.index();
for _ in 0..universes_created_in_query {
delegate.create_next_universe();
}
let prev_universe = create_universes_for_response(delegate, response);

compute_query_response_instantiation_values_in_universe(
delegate,
original_values,
response,
span,
prev_universe,
)
}

fn compute_query_response_instantiation_values_in_universe<D, I, T>(
delegate: &D,
original_values: &[I::GenericArg],
response: &Canonical<I, T>,
span: I::Span,
prev_universe: ty::UniverseIndex,
) -> CanonicalVarValues<I>
where
D: SolverDelegate<Interner = I>,
I: Interner,
T: ResponseT<I>,
{
let var_values = response.value.var_values();
assert_eq!(original_values.len(), var_values.len());

Expand Down Expand Up @@ -239,6 +257,106 @@ where
})
}

fn create_universes_for_response<D, I, T>(
delegate: &D,
response: &Canonical<I, T>,
) -> ty::UniverseIndex
where
D: SolverDelegate<Interner = I>,
I: Interner,
{
let prev_universe = delegate.universe();
let universes_created_in_query = response.max_universe.index();
for _ in 0..universes_created_in_query {
delegate.create_next_universe();
}
prev_universe
}

// Recover the caller-side max input universe for replaying a canonical state.
//
// We want the base universe the canonical state was relative to when it was
// first recorded, not the max universe of the current resolved contents of
// `orig_values`. Resolved inference vars may now point at placeholders from
// later replay work, so looking through them would overestimate the base
// universe and shift the replayed placeholders.
fn max_input_universe_for_canonical_state<D, I>(
delegate: &D,
orig_values: &[I::GenericArg],
) -> ty::UniverseIndex
where
D: SolverDelegate<Interner = I>,
I: Interner,
{
struct MaxUniverseVisitor<'a, D, I> {
delegate: &'a D,
max_universe: ty::UniverseIndex,
_interner: PhantomData<I>,
}

impl<D, I> TypeVisitor<I> for MaxUniverseVisitor<'_, D, I>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I tried using rustc_type_ir::region_constraint::max_universe here, but
it doesn't match what this replay path needs.

In this case we need the max input universe from when the canonical
state was originally recorded. max_universe looks at the current
resolved contents of orig_values, and after more solving those can
already mention placeholders from later replay work.

That makes it compute a larger base universe than the original one,
which shifts the replayed placeholders and brings back the same mismatch
during instantiation. So I kept a local helper for this path.

where
D: SolverDelegate<Interner = I>,
I: Interner,
{
type Result = ();

fn visit_ty(&mut self, t: I::Ty) -> Self::Result {
match t.kind() {
ty::Placeholder(placeholder) => {
self.max_universe = self.max_universe.max(placeholder.universe());
}
ty::Infer(ty::TyVar(vid)) => {
if let Some(universe) = self.delegate.universe_of_ty(vid) {
self.max_universe = self.max_universe.max(universe);
}
}
_ => {}
}
t.super_visit_with(self)
}

fn visit_region(&mut self, r: I::Region) -> Self::Result {
match r.kind() {
ty::RePlaceholder(placeholder) => {
self.max_universe = self.max_universe.max(placeholder.universe());
}
ty::ReVar(vid) => {
if let Some(universe) = self.delegate.universe_of_lt(vid) {
self.max_universe = self.max_universe.max(universe);
}
}
_ => {}
}
}

fn visit_const(&mut self, c: I::Const) -> Self::Result {
match c.kind() {
ty::ConstKind::Placeholder(placeholder) => {
self.max_universe = self.max_universe.max(placeholder.universe());
}
ty::ConstKind::Infer(ty::InferConst::Var(vid)) => {
if let Some(universe) = self.delegate.universe_of_ct(vid) {
self.max_universe = self.max_universe.max(universe);
}
}
_ => {}
}
c.super_visit_with(self)
}
}

let mut visitor = MaxUniverseVisitor {
delegate,
max_universe: ty::UniverseIndex::ROOT,
_interner: PhantomData,
};
for value in orig_values {
value.visit_with(&mut visitor);
}
visitor.max_universe
}

/// Unify the `original_values` with the `var_values` returned by the canonical query..
///
/// This assumes that this unification will always succeed. This is the case when
Expand Down Expand Up @@ -353,14 +471,24 @@ where
{
// In case any fresh inference variables have been created between `state`
// and the previous instantiation, extend `orig_values` for it.
let prev_universe = max_input_universe_for_canonical_state(delegate, orig_values);
let max_universe = prev_universe + state.max_universe.index();
while delegate.universe() < max_universe {
delegate.create_next_universe();
}
orig_values.extend(
state.value.var_values.var_values.as_slice()[orig_values.len()..]
.iter()
.map(|&arg| delegate.fresh_var_for_kind_with_span(arg, span)),
.map(|&arg| delegate.fresh_var_for_kind(arg, span, max_universe)),
);

let instantiation =
compute_query_response_instantiation_values(delegate, orig_values, &state, span);
let instantiation = compute_query_response_instantiation_values_in_universe(
delegate,
orig_values,
&state,
span,
prev_universe,
);

let inspect::State { var_values, data } = delegate.instantiate_canonical(state, instantiation);

Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_next_trait_solver/src/delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
span: <Self::Interner as Interner>::Span,
) -> Option<Certainty>;

fn fresh_var_for_kind_with_span(
fn fresh_var_for_kind(
&self,
arg: <Self::Interner as Interner>::GenericArg,
span: <Self::Interner as Interner>::Span,
universe: ty::UniverseIndex,
) -> <Self::Interner as Interner>::GenericArg;

// FIXME: Uplift the leak check into this crate.
Expand Down
9 changes: 5 additions & 4 deletions compiler/rustc_trait_selection/src/solve/delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,17 +197,18 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
}
}

fn fresh_var_for_kind_with_span(
fn fresh_var_for_kind(
&self,
arg: ty::GenericArg<'tcx>,
span: Span,
universe: ty::UniverseIndex,
) -> ty::GenericArg<'tcx> {
match arg.kind() {
ty::GenericArgKind::Lifetime(_) => {
self.next_region_var(RegionVariableOrigin::Misc(span)).into()
self.next_region_var_in_universe(RegionVariableOrigin::Misc(span), universe).into()
}
ty::GenericArgKind::Type(_) => self.next_ty_var(span).into(),
ty::GenericArgKind::Const(_) => self.next_const_var(span).into(),
ty::GenericArgKind::Type(_) => self.next_ty_var_in_universe(span, universe).into(),
ty::GenericArgKind::Const(_) => self.next_const_var_in_universe(span, universe).into(),
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//@ compile-flags: -Znext-solver=globally

#![allow(incomplete_features)]
#![feature(non_lifetime_binders)]

fn auto_trait()
where
for<T> T: PartialEq + PartialOrd,
{}

fn main() {
auto_trait();
//~^ ERROR can't compare `T` with `T`
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
error[E0277]: can't compare `T` with `T`
--> $DIR/foreach-partial-eq-next-solver.rs:12:5
|
LL | auto_trait();
| ^^^^^^^^^^^^ no implementation for `T < T` and `T > T`
|
= help: the trait `PartialOrd` is not implemented for `T`
note: required by a bound in `auto_trait`
--> $DIR/foreach-partial-eq-next-solver.rs:8:27
|
LL | fn auto_trait()
| ---------- required by a bound in this function
LL | where
LL | for<T> T: PartialEq + PartialOrd,
| ^^^^^^^^^^ required by this bound in `auto_trait`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.
Loading