From 78dd5482438b1ba13b4aa2eaa9a7c443a3342ce4 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 20 Nov 2020 18:00:34 +0100 Subject: Upgrade Chalk Also make overflow depth and max type size configurable through env variables. This can be helpful at least for debugging. Fixes #6628. --- crates/hir_ty/Cargo.toml | 6 ++-- crates/hir_ty/src/db.rs | 6 ++++ crates/hir_ty/src/tests/regression.rs | 43 ++++++++++++++++++++++++++ crates/hir_ty/src/traits.rs | 13 +++----- crates/hir_ty/src/traits/chalk.rs | 49 ++++++++++++++++++++++++++++-- crates/hir_ty/src/traits/chalk/interner.rs | 16 ++++++++++ crates/hir_ty/src/traits/chalk/mapping.rs | 13 ++++++-- 7 files changed, 129 insertions(+), 17 deletions(-) (limited to 'crates/hir_ty') diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml index cf5c38a23..289e812fe 100644 --- a/crates/hir_ty/Cargo.toml +++ b/crates/hir_ty/Cargo.toml @@ -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" } diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs index 25cf9eb7f..66bdb8e88 100644 --- a/crates/hir_ty/src/db.rs +++ b/crates/hir_ty/src/db.rs @@ -99,6 +99,12 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::traits::chalk::fn_def_datum_query)] fn fn_def_datum(&self, krate: CrateId, fn_def_id: chalk::FnDefId) -> Arc; + #[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, diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs index 94d86b0d1..8cf4e7012 100644 --- a/crates/hir_ty/src/tests/regression.rs +++ b/crates/hir_ty/src/tests/regression.rs @@ -840,3 +840,46 @@ fn issue_4966() { "#]], ); } + +#[test] +fn issue_6628() { + check_infer( + r#" + #[lang = "fn_once"] + pub trait FnOnce { + type Output; + } + + struct S(); + impl S { + fn f(&self, _t: T) {} + fn g(&self, _f: F) {} + } + fn main() { + let s = S(); + s.g(|_x| {}); + s.f(10); + } + "#, + expect![[r#" + 105..109 'self': &S + 111..113 '_t': T + 118..120 '{}': () + 146..150 'self': &S + 152..154 '_f': F + 159..161 '{}': () + 174..225 '{ ...10); }': () + 184..185 's': S + 188..189 'S': S() -> S + 188..191 'S()': S + 197..198 's': S + 197..209 's.g(|_x| {})': () + 201..208 '|_x| {}': |&i32| -> () + 202..204 '_x': &i32 + 206..208 '{}': () + 215..216 's': S + 215..222 's.f(10)': () + 219..221 '10': i32 + "#]], + ); +} diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs index ce1174cbe..dfa51896b 100644 --- a/crates/hir_ty/src/traits.rs +++ b/crates/hir_ty/src/traits.rs @@ -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 { - 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: diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs index 55e2c3a3e..69eae6f79 100644 --- a/crates/hir_ty/src/traits/chalk.rs +++ b/crates/hir_ty/src/traits/chalk.rs @@ -104,7 +104,7 @@ impl<'a> chalk_solve::RustIrDatabase 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. Symbol’s 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 for ChalkContext<'a> { Some((trait_, alias)) }) { - // Making up `AsyncBlock: Future` + // Making up Symbol’s value as variable is void: AsyncBlock: // // |--------------------OpaqueTyDatum-------------------| // |-------------OpaqueTyDatumBound--------------| @@ -242,7 +242,7 @@ impl<'a> chalk_solve::RustIrDatabase 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 Symbol’s 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 for ChalkContext<'a> { // FIXME unimplemented!() } + + fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase { + self + } +} + +impl<'a> chalk_ir::UnificationDatabase for ChalkContext<'a> { + fn fn_def_variance( + &self, + fn_def_id: chalk_ir::FnDefId, + ) -> chalk_ir::Variances { + self.db.fn_def_variance(self.krate, fn_def_id) + } + + fn adt_variance(&self, adt_id: chalk_ir::AdtId) -> chalk_ir::Variances { + 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 for crate::db::InternedCallableDefId { fn from(fn_def_id: FnDefId) -> Self { InternKey::from_intern_id(fn_def_id.0) diff --git a/crates/hir_ty/src/traits/chalk/interner.rs b/crates/hir_ty/src/traits/chalk/interner.rs index 39569e690..6a4aa8333 100644 --- a/crates/hir_ty/src/traits/chalk/interner.rs +++ b/crates/hir_ty/src/traits/chalk/interner.rs @@ -25,6 +25,7 @@ pub(crate) type FnDefId = chalk_ir::FnDefId; pub(crate) type FnDefDatum = chalk_solve::rust_ir::FnDefDatum; pub(crate) type OpaqueTyId = chalk_ir::OpaqueTyId; pub(crate) type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum; +pub(crate) type Variances = chalk_ir::Variances; impl chalk_ir::interner::Interner for Interner { type InternedType = Arc>; @@ -41,6 +42,7 @@ impl chalk_ir::interner::Interner for Interner { type InternedVariableKinds = Vec>; type InternedCanonicalVarKinds = Vec>; type InternedConstraints = Vec>>; + 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 { None } + + fn intern_variances( + &self, + data: impl IntoIterator>, + ) -> Result { + 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 { diff --git a/crates/hir_ty/src/traits/chalk/mapping.rs b/crates/hir_ty/src/traits/chalk/mapping.rs index 86cbc4c7e..8700d664e 100644 --- a/crates/hir_ty/src/traits/chalk/mapping.rs +++ b/crates/hir_ty/src/traits/chalk/mapping.rs @@ -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(); -- cgit v1.2.3