Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/traits.rs')
| -rw-r--r-- | crates/hir-ty/src/traits.rs | 55 |
1 files changed, 43 insertions, 12 deletions
diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs index 3ab85c68f5..f40b7db3a5 100644 --- a/crates/hir-ty/src/traits.rs +++ b/crates/hir-ty/src/traits.rs @@ -1,22 +1,24 @@ //! Trait solving using Chalk. -use std::{env::var, sync::Arc}; +use std::env::var; -use chalk_ir::GoalData; +use chalk_ir::{fold::TypeFoldable, DebruijnIndex, GoalData}; use chalk_recursive::Cache; -use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver}; +use chalk_solve::{logging_db::LoggingRustIrDatabase, rust_ir, Solver}; use base_db::CrateId; use hir_def::{ lang_item::{LangItem, LangItemTarget}, - TraitId, + BlockId, TraitId, }; +use hir_expand::name::{name, Name}; use stdx::panic_context; +use triomphe::Arc; use crate::{ - db::HirDatabase, infer::unify::InferenceTable, AliasEq, AliasTy, Canonical, DomainGoal, Goal, - Guidance, InEnvironment, Interner, ProjectionTy, ProjectionTyExt, Solution, TraitRefExt, Ty, - TyKind, WhereClause, + db::HirDatabase, infer::unify::InferenceTable, utils::UnevaluatedConstEvaluatorFolder, AliasEq, + AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment, Interner, ProjectionTy, + ProjectionTyExt, Solution, TraitRefExt, Ty, TyKind, WhereClause, }; /// This controls how much 'time' we give the Chalk solver before giving up. @@ -26,6 +28,7 @@ const CHALK_SOLVER_FUEL: i32 = 1000; pub(crate) struct ChalkContext<'a> { pub(crate) db: &'a dyn HirDatabase, pub(crate) krate: CrateId, + pub(crate) block: Option<BlockId>, } fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> { @@ -43,6 +46,7 @@ fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct TraitEnvironment { pub krate: CrateId, + pub block: Option<BlockId>, // FIXME make this a BTreeMap pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>, pub env: chalk_ir::Environment<Interner>, @@ -52,6 +56,7 @@ impl TraitEnvironment { pub fn empty(krate: CrateId) -> Self { TraitEnvironment { krate, + block: None, traits_from_clauses: Vec::new(), env: chalk_ir::Environment::new(Interner), } @@ -78,11 +83,12 @@ pub(crate) fn normalize_projection_query( pub(crate) fn trait_solve_query( db: &dyn HirDatabase, krate: CrateId, + block: Option<BlockId>, goal: Canonical<InEnvironment<Goal>>, ) -> Option<Solution> { let _p = profile::span("trait_solve_query").detail(|| match &goal.value.goal.data(Interner) { GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => { - db.trait_data(it.hir_trait_id()).name.to_string() + db.trait_data(it.hir_trait_id()).name.display(db.upcast()).to_string() } GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_string(), _ => "??".to_string(), @@ -100,18 +106,25 @@ pub(crate) fn trait_solve_query( } } + // Chalk see `UnevaluatedConst` as a unique concrete value, but we see it as an alias for another const. So + // we should get rid of it when talking to chalk. + let goal = goal + .try_fold_with(&mut UnevaluatedConstEvaluatorFolder { db }, DebruijnIndex::INNERMOST) + .unwrap(); + // We currently don't deal with universes (I think / hope they're not yet // relevant for our use cases?) let u_canonical = chalk_ir::UCanonical { canonical: goal, universes: 1 }; - solve(db, krate, &u_canonical) + solve(db, krate, block, &u_canonical) } fn solve( db: &dyn HirDatabase, krate: CrateId, + block: Option<BlockId>, goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<Interner>>>, ) -> Option<chalk_solve::Solution<Interner>> { - let context = ChalkContext { db, krate }; + let context = ChalkContext { db, krate, block }; tracing::debug!("solve goal: {:?}", goal); let mut solver = create_chalk_solver(); @@ -171,8 +184,10 @@ fn is_chalk_print() -> bool { std::env::var("CHALK_PRINT").is_ok() } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum FnTrait { + // Warning: Order is important. If something implements `x` it should also implement + // `y` if `y <= x`. FnOnce, FnMut, Fn, @@ -187,7 +202,23 @@ impl FnTrait { } } - pub fn get_id(&self, db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> { + pub const fn to_chalk_ir(self) -> rust_ir::ClosureKind { + match self { + FnTrait::FnOnce => rust_ir::ClosureKind::FnOnce, + FnTrait::FnMut => rust_ir::ClosureKind::FnMut, + FnTrait::Fn => rust_ir::ClosureKind::Fn, + } + } + + pub fn method_name(self) -> Name { + match self { + FnTrait::FnOnce => name!(call_once), + FnTrait::FnMut => name!(call_mut), + FnTrait::Fn => name!(call), + } + } + + pub fn get_id(self, db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> { let target = db.lang_item(krate, self.lang_item())?; match target { LangItemTarget::Trait(t) => Some(t), |