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

Detect cyclic subtype goals in new solver #110202

Closed
Closed
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
39 changes: 30 additions & 9 deletions compiler/rustc_hir_analysis/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::errors::AutoDerefReachedRecursionLimit;
use crate::traits::query::evaluate_obligation::InferCtxtExt;
use crate::traits::NormalizeExt;
use crate::traits::{self, TraitEngine, TraitEngineExt};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::InferCtxt;
use rustc_middle::ty::TypeVisitableExt;
use rustc_middle::ty::{self, Ty, TyCtxt};
Expand Down Expand Up @@ -138,22 +139,42 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
return None;
}

let normalized_ty = self
.infcx
.at(&cause, self.param_env)
.normalize(tcx.mk_projection(tcx.lang_items().deref_target()?, trait_ref.substs));
let mut fulfillcx = <dyn TraitEngine<'tcx>>::new_in_snapshot(tcx);
let normalized_ty =
normalized_ty.into_value_registering_obligations(self.infcx, &mut *fulfillcx);
let errors = fulfillcx.select_where_possible(&self.infcx);
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new_in_snapshot(tcx);
let normalized_ty = if tcx.trait_solver_next() {
let ty_var = self.infcx.next_ty_var(TypeVariableOrigin {
span: self.span,
kind: TypeVariableOriginKind::NormalizeProjectionType,
});
fulfill_cx.register_predicate_obligation(
self.infcx,
traits::Obligation::new(
tcx,
cause.clone(),
self.param_env,
ty::Binder::dummy(ty::ProjectionPredicate {
projection_ty: tcx.mk_alias_ty(tcx.lang_items().deref_target()?, [ty]),
term: ty_var.into(),
}),
),
);
ty_var
} else {
let normalized_ty = self
.infcx
.at(&cause, self.param_env)
.normalize(tcx.mk_projection(tcx.lang_items().deref_target()?, trait_ref.substs));
normalized_ty.into_value_registering_obligations(self.infcx, &mut *fulfill_cx)
};

let errors = fulfill_cx.select_where_possible(&self.infcx);
if !errors.is_empty() {
// This shouldn't happen, except for evaluate/fulfill mismatches,
// but that's not a reason for an ICE (`predicate_may_hold` is conservative
// by design).
debug!("overloaded_deref_ty: encountered errors {:?} while fulfilling", errors);
return None;
}
let obligations = fulfillcx.pending_obligations();
let obligations = fulfill_cx.pending_obligations();
debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
self.state.obligations.extend(obligations);

Expand Down
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/autoderef-infinite.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// compile-flags: -Ztrait-solver=next

fn main() {
let y = [Default::default()];
y[0].method();
//~^ ERROR type annotations needed
//~| ERROR no method named `method` found
}
16 changes: 16 additions & 0 deletions tests/ui/traits/new-solver/autoderef-infinite.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error[E0282]: type annotations needed
--> $DIR/autoderef-infinite.rs:5:10
|
LL | y[0].method();
| ^^^^^^ cannot infer type

error[E0599]: no method named `method` found for associated type `<usize as SliceIndex<[_]>>::Output` in the current scope
--> $DIR/autoderef-infinite.rs:5:10
|
LL | y[0].method();
| ^^^^^^ method not found in `<usize as SliceIndex<[_]>>::Output`

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0282, E0599.
For more information about an error, try `rustc --explain E0282`.
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`.
11 changes: 4 additions & 7 deletions tests/ui/traits/new-solver/equating-projection-cyclically.stderr
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
error[E0308]: mismatched types
--> $DIR/equating-projection-cyclically.rs:22:19
--> $DIR/equating-projection-cyclically.rs:22:9
|
LL | x = transform(x);
| ^ expected inferred type, found associated type
|
= 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: try using a conversion method: `.to_string()`
| |
| cyclic type of infinite size

error: aborting due to previous error

Expand Down
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