2818: Don't panic if chalk panics r=matklad a=matklad

r? @flodiebold 

Trying to paper-over panicking chalk. Not sure if this'll make situation better or worse, but I hope it'll be better, as we won't be tearing down type-inference as a whole

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2020-01-13 18:01:15 +00:00 committed by GitHub
commit ea0ee8e5bf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,10 +1,12 @@
//! Trait solving using Chalk. //! Trait solving using Chalk.
use std::sync::{Arc, Mutex}; use std::{
panic,
sync::{Arc, Mutex},
};
use chalk_ir::cast::Cast; use chalk_ir::cast::Cast;
use hir_def::{expr::ExprId, DefWithBodyId, ImplId, TraitId, TypeAliasId}; use hir_def::{expr::ExprId, DefWithBodyId, ImplId, TraitId, TypeAliasId};
use log::debug; use ra_db::{impl_intern_key, salsa, Canceled, CrateId};
use ra_db::{impl_intern_key, salsa, CrateId};
use ra_prof::profile; use ra_prof::profile;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
@ -39,7 +41,7 @@ impl TraitSolver {
goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<TypeFamily>>>, goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<TypeFamily>>>,
) -> Option<chalk_solve::Solution<TypeFamily>> { ) -> Option<chalk_solve::Solution<TypeFamily>> {
let context = ChalkContext { db, krate: self.krate }; let context = ChalkContext { db, krate: self.krate };
debug!("solve goal: {:?}", goal); log::debug!("solve goal: {:?}", goal);
let mut solver = match self.inner.lock() { let mut solver = match self.inner.lock() {
Ok(it) => it, Ok(it) => it,
// Our cancellation works via unwinding, but, as chalk is not // Our cancellation works via unwinding, but, as chalk is not
@ -47,8 +49,28 @@ impl TraitSolver {
// Ideally, we should also make chalk panic-safe. // Ideally, we should also make chalk panic-safe.
Err(_) => ra_db::Canceled::throw(), Err(_) => ra_db::Canceled::throw(),
}; };
let solution = solver.solve(&context, goal);
debug!("solve({:?}) => {:?}", goal, solution); let solution = panic::catch_unwind({
let solver = panic::AssertUnwindSafe(&mut solver);
let context = panic::AssertUnwindSafe(&context);
move || solver.0.solve(context.0, goal)
});
let solution = match solution {
Ok(it) => it,
Err(err) => {
if err.downcast_ref::<Canceled>().is_some() {
panic::resume_unwind(err)
} else {
log::error!("chalk panicked :-(");
// Reset the solver, as it is not panic-safe.
*solver = create_chalk_solver();
None
}
}
};
log::debug!("solve({:?}) => {:?}", goal, solution);
solution solution
} }
} }
@ -70,9 +92,13 @@ pub(crate) fn trait_solver_query(
) -> TraitSolver { ) -> TraitSolver {
db.salsa_runtime().report_untracked_read(); db.salsa_runtime().report_untracked_read();
// krate parameter is just so we cache a unique solver per crate // krate parameter is just so we cache a unique solver per crate
log::debug!("Creating new solver for crate {:?}", krate);
TraitSolver { krate, inner: Arc::new(Mutex::new(create_chalk_solver())) }
}
fn create_chalk_solver() -> chalk_solve::Solver<TypeFamily> {
let solver_choice = chalk_solve::SolverChoice::SLG { max_size: CHALK_SOLVER_MAX_SIZE }; let solver_choice = chalk_solve::SolverChoice::SLG { max_size: CHALK_SOLVER_MAX_SIZE };
debug!("Creating new solver for crate {:?}", krate); solver_choice.into_solver()
TraitSolver { krate, inner: Arc::new(Mutex::new(solver_choice.into_solver())) }
} }
/// Collects impls for the given trait in the whole dependency tree of `krate`. /// Collects impls for the given trait in the whole dependency tree of `krate`.
@ -181,7 +207,7 @@ pub(crate) fn trait_solve_query(
goal: Canonical<InEnvironment<Obligation>>, goal: Canonical<InEnvironment<Obligation>>,
) -> Option<Solution> { ) -> Option<Solution> {
let _p = profile("trait_solve_query"); let _p = profile("trait_solve_query");
debug!("trait_solve_query({})", goal.value.value.display(db)); log::debug!("trait_solve_query({})", goal.value.value.display(db));
if let Obligation::Projection(pred) = &goal.value.value { if let Obligation::Projection(pred) = &goal.value.value {
if let Ty::Bound(_) = &pred.projection_ty.parameters[0] { if let Ty::Bound(_) = &pred.projection_ty.parameters[0] {