Skip to content

Commit

Permalink
Detect cyclic subtype goals in new solver
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Apr 12, 2023
1 parent e7271f4 commit a62ecba
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 21 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ impl<'tcx> InferCtxtInner<'tcx> {
}

#[inline]
fn type_variables(&mut self) -> type_variable::TypeVariableTable<'_, 'tcx> {
pub fn type_variables(&mut self) -> type_variable::TypeVariableTable<'_, 'tcx> {
self.type_variable_storage.with_log(&mut self.undo_log)
}

Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_middle/src/traits/solve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ pub struct ExternalConstraintsData<'tcx> {
// FIXME: implement this.
pub region_constraints: QueryRegionConstraints<'tcx>,
pub opaque_types: Vec<(Ty<'tcx>, Ty<'tcx>)>,
pub stalled_subtypes: Vec<(Ty<'tcx>, Ty<'tcx>)>,
}

// FIXME: Having to clone `region_constraints` for folding feels bad and
Expand All @@ -144,13 +145,23 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> {
.iter()
.map(|opaque| opaque.try_fold_with(folder))
.collect::<Result<_, F::Error>>()?,
stalled_subtypes: self
.stalled_subtypes
.iter()
.map(|subtype| subtype.try_fold_with(folder))
.collect::<Result<_, F::Error>>()?,
}))
}

fn fold_with<F: TypeFolder<TyCtxt<'tcx>>>(self, folder: &mut F) -> Self {
TypeFolder::interner(folder).mk_external_constraints(ExternalConstraintsData {
region_constraints: self.region_constraints.clone().fold_with(folder),
opaque_types: self.opaque_types.iter().map(|opaque| opaque.fold_with(folder)).collect(),
stalled_subtypes: self
.stalled_subtypes
.iter()
.map(|subtype| subtype.fold_with(folder))
.collect(),
})
}
}
Expand All @@ -162,6 +173,7 @@ impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> {
) -> std::ops::ControlFlow<V::BreakTy> {
self.region_constraints.visit_with(visitor)?;
self.opaque_types.visit_with(visitor)?;
self.stalled_subtypes.visit_with(visitor)?;
ControlFlow::Continue(())
}
}
8 changes: 8 additions & 0 deletions compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ pub struct EvalCtxt<'a, 'tcx> {
pub(super) search_graph: &'a mut SearchGraph<'tcx>,

pub(super) nested_goals: NestedGoals<'tcx>,

// Subtyping predicates which have stalled. These are passed down in query
// responses to help detect cycle errors in cases where we're computing a
// goal like `?0 <: Box<?1>` when we already know that `?0 <: ?1`.
pub(super) stalled_subtypes: Vec<(Ty<'tcx>, Ty<'tcx>)>,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -121,6 +126,7 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
max_input_universe: ty::UniverseIndex::ROOT,
var_values: CanonicalVarValues::dummy(),
nested_goals: NestedGoals::new(),
stalled_subtypes: vec![],
};
let result = ecx.evaluate_goal(IsNormalizesToHack::No, goal);

Expand Down Expand Up @@ -172,6 +178,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
max_input_universe: canonical_goal.max_universe,
search_graph,
nested_goals: NestedGoals::new(),
stalled_subtypes: vec![],
};
ecx.compute_goal(goal)
})
Expand Down Expand Up @@ -404,6 +411,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
max_input_universe: self.max_input_universe,
search_graph: self.search_graph,
nested_goals: self.nested_goals.clone(),
stalled_subtypes: self.stalled_subtypes.clone(),
};
self.infcx.probe(|_| f(&mut ecx))
}
Expand Down
31 changes: 24 additions & 7 deletions compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rustc_infer::infer::canonical::CanonicalVarValues;
use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints};
use rustc_middle::traits::query::NoSolution;
use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData};
use rustc_middle::ty::{self, GenericArgKind};
use rustc_middle::ty::{self, GenericArgKind, Ty};
use rustc_span::DUMMY_SP;
use std::iter;
use std::ops::Deref;
Expand Down Expand Up @@ -79,9 +79,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
)
});
let opaque_types = self.infcx.clone_opaque_types_for_query_response();
Ok(self
.tcx()
.mk_external_constraints(ExternalConstraintsData { region_constraints, opaque_types }))
Ok(self.tcx().mk_external_constraints(ExternalConstraintsData {
region_constraints,
opaque_types,
stalled_subtypes: self.stalled_subtypes.clone(),
}))
}

/// After calling a canonical query, we apply the constraints returned
Expand All @@ -102,12 +104,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
let Response { var_values, external_constraints, certainty } =
response.substitute(self.tcx(), &substitution);

let nested_goals = self.unify_query_var_values(param_env, &original_values, var_values)?;

// FIXME: implement external constraints.
let ExternalConstraintsData { region_constraints, opaque_types: _ } =
let ExternalConstraintsData { region_constraints, stalled_subtypes, opaque_types: _ } =
external_constraints.deref();
self.register_region_constraints(region_constraints);
assert!(
stalled_subtypes.is_empty() || certainty != Certainty::Yes,
"stalled_subtypes is non-empty: {stalled_subtypes:?}, but certainty is Yes"
);
self.register_stalled_subtypes(stalled_subtypes);

let nested_goals = self.unify_query_var_values(param_env, &original_values, var_values)?;

Ok((certainty, nested_goals))
}
Expand Down Expand Up @@ -227,4 +234,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
let _ = member_constraint;
}
}

fn register_stalled_subtypes(&mut self, stalled_subtypes: &[(Ty<'tcx>, Ty<'tcx>)]) {
for (sub, sup) in stalled_subtypes {
if let ty::Infer(ty::TyVar(sub_vid)) = *self.infcx.shallow_resolve(*sub).kind()
&& let ty::Infer(ty::TyVar(sup_vid)) = *self.infcx.shallow_resolve(*sup).kind()
{
self.infcx.inner.borrow_mut().type_variables().sub(sub_vid, sup_vid);
}
}
}
}
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 @@ -105,6 +105,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
goal: Goal<'tcx, SubtypePredicate<'tcx>>,
) -> QueryResult<'tcx> {
if goal.predicate.a.is_ty_var() && goal.predicate.b.is_ty_var() {
self.stalled_subtypes.push((goal.predicate.a, goal.predicate.b));
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
} else {
self.sub(goal.param_env, goal.predicate.a, goal.predicate.b)?;
Expand Down
8 changes: 8 additions & 0 deletions tests/ui/traits/new-solver/cyclic-type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// compile-flags: -Ztrait-solver=next

fn main() {
let x;
x = Box::new(x);
//~^ ERROR mismatched types
//~| NOTE cyclic type of infinite size
}
14 changes: 14 additions & 0 deletions tests/ui/traits/new-solver/cyclic-type.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0308]: mismatched types
--> $DIR/cyclic-type.rs:5:9
|
LL | x = Box::new(x);
| ^^^^^^^^^^^ cyclic type of infinite size
|
help: consider unboxing the value
|
LL | x = *Box::new(x);
| +

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
36 changes: 27 additions & 9 deletions tests/ui/traits/new-solver/equating-projection-cyclically.stderr
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
error[E0308]: mismatched types
--> $DIR/equating-projection-cyclically.rs:22:19
error[E0055]: reached the recursion limit while auto-dereferencing `<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<_ as Test>::Assoc as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target`
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`equating_projection_cyclically`)

error[E0055]: reached the recursion limit while auto-dereferencing `<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<_ as Test>::Assoc as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target`
--> $DIR/equating-projection-cyclically.rs:22:9
|
LL | x = transform(x);
| ^ expected inferred type, found associated type
| ^^^^^^^^^^^^ deref recursion limit reached
|
= note: expected type `_`
found associated type `<_ as Test>::Assoc`
= help: consider constraining the associated type `<_ as Test>::Assoc` to `_`
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`equating_projection_cyclically`)

error[E0055]: reached the recursion limit while auto-dereferencing `<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<_ as Test>::Assoc as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target as Deref>::Target`
--> $DIR/equating-projection-cyclically.rs:22:9
|
LL | x = transform(x);
| ^^^^^^^^^^^^ deref recursion limit reached
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`equating_projection_cyclically`)

error[E0308]: mismatched types
--> $DIR/equating-projection-cyclically.rs:22:9
|
LL | x = transform(x);
| ^^^^^^^^^^^^- help: try using a conversion method: `.to_string()`
| |
| cyclic type of infinite size

error: aborting due to previous error
error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0308`.
Some errors have detailed explanations: E0055, E0308.
For more information about an error, try `rustc --explain E0055`.
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
error[E0282]: type annotations needed
--> $DIR/normalizes_to_ignores_unnormalizable_candidate.rs:36:5
--> $DIR/normalizes_to_ignores_unnormalizable_candidate.rs:36:9
|
LL | foo(unconstrained())
| ^^^ cannot infer type of the type parameter `T` declared on the function `foo`
| ^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `unconstrained`
|
help: consider specifying the generic argument
|
LL | foo::<T>(unconstrained())
| +++++
LL | foo(unconstrained::<T>())
| +++++

error: aborting due to previous error

Expand Down

0 comments on commit a62ecba

Please sign in to comment.