Upgrade Chalk

Also make overflow depth and max type size configurable through env variables.
This can be helpful at least for debugging.

Fixes #6628.
This commit is contained in:
Florian Diebold 2020-11-20 18:00:34 +01:00
parent a0fa522fda
commit 78dd548243
8 changed files with 137 additions and 25 deletions

16
Cargo.lock generated
View file

@ -168,9 +168,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chalk-derive"
version = "0.37.0"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "564b529b0d620da43dc7ea46fa95b5c602e783e1870aeb07e8cbb6d7ff71bee6"
checksum = "e2d9e0c8adcced1ab0fea5cb8a38647922893d5b495e363e1814299fd380469b"
dependencies = [
"proc-macro2",
"quote",
@ -180,9 +180,9 @@ dependencies = [
[[package]]
name = "chalk-ir"
version = "0.37.0"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e67d29482387f4cbed6d8f1b1f7d24f00ff10612c700c65fe4af220df11e4d24"
checksum = "c5218266a5709bc4943de997e64d3fab41c9e9f68efd54a898de53135e987bd3"
dependencies = [
"chalk-derive",
"lazy_static",
@ -190,9 +190,9 @@ dependencies = [
[[package]]
name = "chalk-recursive"
version = "0.37.0"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c52032be6fbdf91b6a7df3cafba3a6683fdabeff88e7ab73eea96e28657d973"
checksum = "ed8f34f13fd4f30251f9f6f1dc56f80363201390ecbcac2fdfc8e33036cd9c4a"
dependencies = [
"chalk-derive",
"chalk-ir",
@ -203,9 +203,9 @@ dependencies = [
[[package]]
name = "chalk-solve"
version = "0.37.0"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0378bdfe1547b6fd545f518373b08c1e0c14920f7555a62d049021250a2b89b"
checksum = "379c9f584488346044709d4c638c38d61a06fe593d4de2ac5f15fd2b0ba4cd9d"
dependencies = [
"chalk-derive",
"chalk-ir",

View file

@ -17,9 +17,9 @@ ena = "0.14.0"
log = "0.4.8"
rustc-hash = "1.1.0"
scoped-tls = "1"
chalk-solve = { version = "0.37", default-features = false }
chalk-ir = "0.37"
chalk-recursive = "0.37"
chalk-solve = { version = "0.43", default-features = false }
chalk-ir = "0.43"
chalk-recursive = "0.43"
stdx = { path = "../stdx", version = "0.0.0" }
hir_def = { path = "../hir_def", version = "0.0.0" }

View file

@ -99,6 +99,12 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::invoke(crate::traits::chalk::fn_def_datum_query)]
fn fn_def_datum(&self, krate: CrateId, fn_def_id: chalk::FnDefId) -> Arc<chalk::FnDefDatum>;
#[salsa::invoke(crate::traits::chalk::fn_def_variance_query)]
fn fn_def_variance(&self, krate: CrateId, fn_def_id: chalk::FnDefId) -> chalk::Variances;
#[salsa::invoke(crate::traits::chalk::adt_variance_query)]
fn adt_variance(&self, krate: CrateId, adt_id: chalk::AdtId) -> chalk::Variances;
#[salsa::invoke(crate::traits::chalk::associated_ty_value_query)]
fn associated_ty_value(
&self,

View file

@ -840,3 +840,46 @@ fn issue_4966() {
"#]],
);
}
#[test]
fn issue_6628() {
check_infer(
r#"
#[lang = "fn_once"]
pub trait FnOnce<Args> {
type Output;
}
struct S<T>();
impl<T> S<T> {
fn f(&self, _t: T) {}
fn g<F: FnOnce(&T)>(&self, _f: F) {}
}
fn main() {
let s = S();
s.g(|_x| {});
s.f(10);
}
"#,
expect![[r#"
105..109 'self': &S<T>
111..113 '_t': T
118..120 '{}': ()
146..150 'self': &S<T>
152..154 '_f': F
159..161 '{}': ()
174..225 '{ ...10); }': ()
184..185 's': S<i32>
188..189 'S': S<i32>() -> S<i32>
188..191 'S()': S<i32>
197..198 's': S<i32>
197..209 's.g(|_x| {})': ()
201..208 '|_x| {}': |&i32| -> ()
202..204 '_x': &i32
206..208 '{}': ()
215..216 's': S<i32>
215..222 's.f(10)': ()
219..221 '10': i32
"#]],
);
}

View file

@ -1,4 +1,5 @@
//! Trait solving using Chalk.
use std::env::var;
use std::sync::Arc;
use base_db::CrateId;
@ -15,12 +16,6 @@ use self::chalk::{from_chalk, Interner, ToChalk};
pub(crate) mod chalk;
// This controls the maximum size of types Chalk considers. If we set this too
// high, we can run into slow edge cases; if we set it too low, Chalk won't
// find some solutions.
// FIXME this is currently hardcoded in the recursive solver
// const CHALK_SOLVER_MAX_SIZE: usize = 10;
/// This controls how much 'time' we give the Chalk solver before giving up.
const CHALK_SOLVER_FUEL: i32 = 100;
@ -31,9 +26,11 @@ struct ChalkContext<'a> {
}
fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> {
let overflow_depth = 100;
let overflow_depth =
var("CHALK_OVERFLOW_DEPTH").ok().and_then(|s| s.parse().ok()).unwrap_or(100);
let caching_enabled = true;
chalk_recursive::RecursiveSolver::new(overflow_depth, caching_enabled)
let max_size = var("CHALK_SOLVER_MAX_SIZE").ok().and_then(|s| s.parse().ok()).unwrap_or(30);
chalk_recursive::RecursiveSolver::new(overflow_depth, max_size, caching_enabled)
}
/// A set of clauses that we assume to be true. E.g. if we are inside this function:

View file

@ -104,7 +104,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
};
// Note: Since we're using impls_for_trait, only impls where the trait
// can be resolved should ever reach Chalk. `impl_datum` relies on that
// can be resolved should ever reach Chalk. Symbols value as variable is void: impl_datum relies on that
// and will panic if the trait can't be resolved.
let in_deps = self.db.trait_impls_in_deps(self.krate);
let in_self = self.db.trait_impls_in_crate(self.krate);
@ -206,7 +206,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
Some((trait_, alias))
})
{
// Making up `AsyncBlock<T>: Future<Output = T>`
// Making up Symbols value as variable is void: AsyncBlock<T>:
//
// |--------------------OpaqueTyDatum-------------------|
// |-------------OpaqueTyDatumBound--------------|
@ -242,7 +242,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
// The opaque type has 1 parameter.
make_binders(bound, 1)
} else {
// If failed to find `Future::Output`, return empty bounds as fallback.
// If failed to find Symbols value as variable is void: Future::Output, return empty bounds as fallback.
let bound = OpaqueTyDatumBound {
bounds: make_binders(vec![], 0),
where_clauses: make_binders(vec![], 0),
@ -343,6 +343,23 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
// FIXME
unimplemented!()
}
fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase<Interner> {
self
}
}
impl<'a> chalk_ir::UnificationDatabase<Interner> for ChalkContext<'a> {
fn fn_def_variance(
&self,
fn_def_id: chalk_ir::FnDefId<Interner>,
) -> chalk_ir::Variances<Interner> {
self.db.fn_def_variance(self.krate, fn_def_id)
}
fn adt_variance(&self, adt_id: chalk_ir::AdtId<Interner>) -> chalk_ir::Variances<Interner> {
self.db.adt_variance(self.krate, adt_id)
}
}
pub(crate) fn program_clauses_for_chalk_env_query(
@ -644,6 +661,32 @@ pub(crate) fn fn_def_datum_query(
Arc::new(datum)
}
pub(crate) fn fn_def_variance_query(
db: &dyn HirDatabase,
_krate: CrateId,
fn_def_id: FnDefId,
) -> Variances {
let callable_def: CallableDefId = from_chalk(db, fn_def_id);
let generic_params = generics(db.upcast(), callable_def.into());
Variances::from(
&Interner,
std::iter::repeat(chalk_ir::Variance::Invariant).take(generic_params.len()),
)
}
pub(crate) fn adt_variance_query(
db: &dyn HirDatabase,
_krate: CrateId,
adt_id: AdtId,
) -> Variances {
let adt: crate::AdtId = from_chalk(db, adt_id);
let generic_params = generics(db.upcast(), adt.into());
Variances::from(
&Interner,
std::iter::repeat(chalk_ir::Variance::Invariant).take(generic_params.len()),
)
}
impl From<FnDefId> for crate::db::InternedCallableDefId {
fn from(fn_def_id: FnDefId) -> Self {
InternKey::from_intern_id(fn_def_id.0)

View file

@ -25,6 +25,7 @@ pub(crate) type FnDefId = chalk_ir::FnDefId<Interner>;
pub(crate) type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>;
pub(crate) type OpaqueTyId = chalk_ir::OpaqueTyId<Interner>;
pub(crate) type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum<Interner>;
pub(crate) type Variances = chalk_ir::Variances<Interner>;
impl chalk_ir::interner::Interner for Interner {
type InternedType = Arc<chalk_ir::TyData<Self>>;
@ -41,6 +42,7 @@ impl chalk_ir::interner::Interner for Interner {
type InternedVariableKinds = Vec<chalk_ir::VariableKind<Self>>;
type InternedCanonicalVarKinds = Vec<chalk_ir::CanonicalVarKind<Self>>;
type InternedConstraints = Vec<chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>>;
type InternedVariances = Arc<[chalk_ir::Variance]>;
type DefId = InternId;
type InternedAdtId = hir_def::AdtId;
type Identifier = TypeAliasId;
@ -370,6 +372,20 @@ impl chalk_ir::interner::Interner for Interner {
) -> Option<fmt::Result> {
None
}
fn intern_variances<E>(
&self,
data: impl IntoIterator<Item = Result<chalk_ir::Variance, E>>,
) -> Result<Self::InternedVariances, E> {
data.into_iter().collect()
}
fn variances_data<'a>(
&self,
variances: &'a Self::InternedVariances,
) -> &'a [chalk_ir::Variance] {
&variances
}
}
impl chalk_ir::interner::HasInterner for Interner {

View file

@ -31,7 +31,8 @@ impl ToChalk for Ty {
TypeCtor::Ref(m) => ref_to_chalk(db, m, apply_ty.parameters),
TypeCtor::Array => array_to_chalk(db, apply_ty.parameters),
TypeCtor::FnPtr { num_args: _, is_varargs } => {
let substitution = apply_ty.parameters.to_chalk(db).shifted_in(&Interner);
let substitution =
chalk_ir::FnSubst(apply_ty.parameters.to_chalk(db).shifted_in(&Interner));
chalk_ir::TyKind::Function(chalk_ir::FnPointer {
num_binders: 0,
sig: chalk_ir::FnSig {
@ -183,7 +184,7 @@ impl ToChalk for Ty {
assert_eq!(num_binders, 0);
let parameters: Substs = from_chalk(
db,
substitution.shifted_out(&Interner).expect("fn ptr should have no binders"),
substitution.0.shifted_out(&Interner).expect("fn ptr should have no binders"),
);
Ty::Apply(ApplicationTy {
ctor: TypeCtor::FnPtr {
@ -536,6 +537,7 @@ impl ToChalk for GenericPredicate {
// we don't produce any where clauses with binders and can't currently deal with them
match where_clause
.skip_binders()
.clone()
.shifted_out(&Interner)
.expect("unexpected bound vars in where clause")
{
@ -661,7 +663,12 @@ where
chalk_ir::TyVariableKind::Integer => TyKind::Integer,
chalk_ir::TyVariableKind::Float => TyKind::Float,
},
chalk_ir::VariableKind::Lifetime => panic!("unexpected lifetime from Chalk"),
// HACK: Chalk can sometimes return new lifetime variables. We
// want to just skip them, but to not mess up the indices of
// other variables, we'll just create a new type variable in
// their place instead. This should not matter (we never see the
// actual *uses* of the lifetime variable).
chalk_ir::VariableKind::Lifetime => TyKind::General,
chalk_ir::VariableKind::Const(_) => panic!("unexpected const from Chalk"),
})
.collect();