aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/ty/traits.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/ty/traits.rs')
-rw-r--r--crates/ra_hir/src/ty/traits.rs223
1 files changed, 134 insertions, 89 deletions
diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs
index 06f483899..a1ed0c028 100644
--- a/crates/ra_hir/src/ty/traits.rs
+++ b/crates/ra_hir/src/ty/traits.rs
@@ -1,47 +1,65 @@
1//! Stuff that will probably mostly replaced by Chalk. 1//! Trait solving using Chalk.
2use std::collections::HashMap; 2use std::sync::{Arc, Mutex};
3 3
4use crate::{db::HirDatabase, generics::HasGenericParams}; 4use log::debug;
5use super::{TraitRef, Substs, infer::{TypeVarId, InferTy}, Ty}; 5use chalk_ir::cast::Cast;
6 6
7// Copied (and simplified) from Chalk 7use crate::{Crate, Trait, db::HirDatabase, ImplBlock};
8use super::{TraitRef, Ty, Canonical};
8 9
9#[derive(Clone, Debug, PartialEq, Eq)] 10use self::chalk::{ToChalk, from_chalk};
10/// A (possible) solution for a proposed goal. Usually packaged in a `Result`,
11/// where `Err` represents definite *failure* to prove a goal.
12pub enum Solution {
13 /// The goal indeed holds, and there is a unique value for all existential
14 /// variables.
15 Unique(Substs),
16 11
17 /// The goal may be provable in multiple ways, but regardless we may have some guidance 12mod chalk;
18 /// for type inference. 13
19 Ambig(Guidance), 14pub(crate) type Solver = chalk_solve::Solver;
15
16#[derive(Debug, Copy, Clone)]
17struct ChalkContext<'a, DB> {
18 db: &'a DB,
19 krate: Crate,
20} 20}
21 21
22#[derive(Clone, Debug, PartialEq, Eq)] 22pub(crate) fn solver(_db: &impl HirDatabase, _krate: Crate) -> Arc<Mutex<Solver>> {
23/// When a goal holds ambiguously (e.g., because there are multiple possible 23 // krate parameter is just so we cache a unique solver per crate
24/// solutions), we issue a set of *guidance* back to type inference. 24 let solver_choice = chalk_solve::SolverChoice::SLG { max_size: 10 };
25pub enum Guidance { 25 Arc::new(Mutex::new(solver_choice.into_solver()))
26 /// The existential variables *must* have the given values if the goal is 26}
27 /// ever to hold, but that alone isn't enough to guarantee the goal will
28 /// actually hold.
29 Definite(Substs),
30 27
31 /// There are multiple plausible values for the existentials, but the ones 28/// Collects impls for the given trait in the whole dependency tree of `krate`.
32 /// here are suggested as the preferred choice heuristically. These should 29pub(crate) fn impls_for_trait(
33 /// be used for inference fallback only. 30 db: &impl HirDatabase,
34 Suggested(Substs), 31 krate: Crate,
32 trait_: Trait,
33) -> Arc<[ImplBlock]> {
34 let mut impls = Vec::new();
35 // We call the query recursively here. On the one hand, this means we can
36 // reuse results from queries for different crates; on the other hand, this
37 // will only ever get called for a few crates near the root of the tree (the
38 // ones the user is editing), so this may actually be a waste of memory. I'm
39 // doing it like this mainly for simplicity for now.
40 for dep in krate.dependencies(db) {
41 impls.extend(db.impls_for_trait(dep.krate, trait_).iter());
42 }
43 let crate_impl_blocks = db.impls_in_crate(krate);
44 impls.extend(crate_impl_blocks.lookup_impl_blocks_for_trait(&trait_));
45 impls.into()
46}
35 47
36 /// There's no useful information to feed back to type inference 48fn solve(
37 Unknown, 49 db: &impl HirDatabase,
50 krate: Crate,
51 goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal>>,
52) -> Option<chalk_solve::Solution> {
53 let context = ChalkContext { db, krate };
54 let solver = db.solver(krate);
55 let solution = solver.lock().unwrap().solve(&context, goal);
56 debug!("solve({:?}) => {:?}", goal, solution);
57 solution
38} 58}
39 59
40/// Something that needs to be proven (by Chalk) during type checking, e.g. that 60/// Something that needs to be proven (by Chalk) during type checking, e.g. that
41/// a certain type implements a certain trait. Proving the Obligation might 61/// a certain type implements a certain trait. Proving the Obligation might
42/// result in additional information about inference variables. 62/// result in additional information about inference variables.
43///
44/// This might be handled by Chalk when we integrate it?
45#[derive(Clone, Debug, PartialEq, Eq)] 63#[derive(Clone, Debug, PartialEq, Eq)]
46pub enum Obligation { 64pub enum Obligation {
47 /// Prove that a certain type implements a trait (the type is the `Self` type 65 /// Prove that a certain type implements a trait (the type is the `Self` type
@@ -49,67 +67,94 @@ pub enum Obligation {
49 Trait(TraitRef), 67 Trait(TraitRef),
50} 68}
51 69
52/// Rudimentary check whether an impl exists for a given type and trait; this 70/// Check using Chalk whether trait is implemented for given parameters including `Self` type.
53/// will actually be done by chalk. 71pub(crate) fn implements(
54pub(crate) fn implements(db: &impl HirDatabase, trait_ref: TraitRef) -> Option<Solution> { 72 db: &impl HirDatabase,
55 // FIXME use all trait impls in the whole crate graph 73 krate: Crate,
56 let krate = trait_ref.trait_.module(db).krate(db); 74 trait_ref: Canonical<TraitRef>,
57 let krate = match krate { 75) -> Option<Solution> {
58 Some(krate) => krate, 76 let goal: chalk_ir::Goal = trait_ref.value.to_chalk(db).cast();
59 None => return None, 77 debug!("goal: {:?}", goal);
60 }; 78 let env = chalk_ir::Environment::new();
61 let crate_impl_blocks = db.impls_in_crate(krate); 79 let in_env = chalk_ir::InEnvironment::new(&env, goal);
62 let mut impl_blocks = crate_impl_blocks 80 let parameter = chalk_ir::ParameterKind::Ty(chalk_ir::UniverseIndex::ROOT);
63 .lookup_impl_blocks_for_trait(&trait_ref.trait_) 81 let canonical =
64 // we don't handle where clauses at all, waiting for Chalk for that 82 chalk_ir::Canonical { value: in_env, binders: vec![parameter; trait_ref.num_vars] };
65 .filter(|impl_block| impl_block.generic_params(db).where_predicates.is_empty()); 83 // We currently don't deal with universes (I think / hope they're not yet
66 impl_blocks 84 // relevant for our use cases?)
67 .find_map(|impl_block| unify_trait_refs(&trait_ref, &impl_block.target_trait_ref(db)?)) 85 let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 };
86 let solution = solve(db, krate, &u_canonical);
87 solution.map(|solution| solution_from_chalk(db, solution))
68} 88}
69 89
70pub(super) fn canonicalize(trait_ref: TraitRef) -> (TraitRef, Vec<TypeVarId>) { 90fn solution_from_chalk(db: &impl HirDatabase, solution: chalk_solve::Solution) -> Solution {
71 let mut canonical = HashMap::new(); // mapping uncanonical -> canonical 91 let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution>| {
72 let mut uncanonical = Vec::new(); // mapping canonical -> uncanonical (which is dense) 92 let value = subst
73 let mut substs = trait_ref.substs.0.to_vec(); 93 .value
74 for ty in &mut substs { 94 .parameters
75 ty.walk_mut(&mut |ty| match ty { 95 .into_iter()
76 Ty::Infer(InferTy::TypeVar(tv)) => { 96 .map(|p| {
77 let tv: &mut TypeVarId = tv; 97 let ty = match p {
78 *tv = *canonical.entry(*tv).or_insert_with(|| { 98 chalk_ir::Parameter(chalk_ir::ParameterKind::Ty(ty)) => from_chalk(db, ty),
79 let i = uncanonical.len(); 99 chalk_ir::Parameter(chalk_ir::ParameterKind::Lifetime(_)) => unimplemented!(),
80 uncanonical.push(*tv); 100 };
81 TypeVarId(i as u32) 101 ty
82 }); 102 })
83 } 103 .collect();
84 _ => {} 104 let result = Canonical { value, num_vars: subst.binders.len() };
85 }); 105 SolutionVariables(result)
106 };
107 match solution {
108 chalk_solve::Solution::Unique(constr_subst) => {
109 let subst = chalk_ir::Canonical {
110 value: constr_subst.value.subst,
111 binders: constr_subst.binders,
112 };
113 Solution::Unique(convert_subst(subst))
114 }
115 chalk_solve::Solution::Ambig(chalk_solve::Guidance::Definite(subst)) => {
116 Solution::Ambig(Guidance::Definite(convert_subst(subst)))
117 }
118 chalk_solve::Solution::Ambig(chalk_solve::Guidance::Suggested(subst)) => {
119 Solution::Ambig(Guidance::Suggested(convert_subst(subst)))
120 }
121 chalk_solve::Solution::Ambig(chalk_solve::Guidance::Unknown) => {
122 Solution::Ambig(Guidance::Unknown)
123 }
86 } 124 }
87 (TraitRef { substs: substs.into(), ..trait_ref }, uncanonical)
88} 125}
89 126
90fn unify_trait_refs(tr1: &TraitRef, tr2: &TraitRef) -> Option<Solution> { 127#[derive(Clone, Debug, PartialEq, Eq)]
91 if tr1.trait_ != tr2.trait_ { 128pub(crate) struct SolutionVariables(pub Canonical<Vec<Ty>>);
92 return None; 129
93 } 130#[derive(Clone, Debug, PartialEq, Eq)]
94 let mut solution_substs = Vec::new(); 131/// A (possible) solution for a proposed goal.
95 for (t1, t2) in tr1.substs.0.iter().zip(tr2.substs.0.iter()) { 132pub(crate) enum Solution {
96 // this is very bad / hacky 'unification' logic, just enough to make the simple tests pass 133 /// The goal indeed holds, and there is a unique value for all existential
97 match (t1, t2) { 134 /// variables.
98 (_, Ty::Infer(InferTy::TypeVar(_))) | (_, Ty::Unknown) | (_, Ty::Param { .. }) => { 135 Unique(SolutionVariables),
99 // type variable (or similar) in the impl, we just assume it works 136
100 } 137 /// The goal may be provable in multiple ways, but regardless we may have some guidance
101 (Ty::Infer(InferTy::TypeVar(v1)), _) => { 138 /// for type inference. In this case, we don't return any lifetime
102 // type variable in the query and fixed type in the impl, record its value 139 /// constraints, since we have not "committed" to any particular solution
103 solution_substs.resize_with(v1.0 as usize + 1, || Ty::Unknown); 140 /// yet.
104 solution_substs[v1.0 as usize] = t2.clone(); 141 Ambig(Guidance),
105 } 142}
106 _ => { 143
107 // check that they're equal (actually we'd have to recurse etc.) 144#[derive(Clone, Debug, PartialEq, Eq)]
108 if t1 != t2 { 145/// When a goal holds ambiguously (e.g., because there are multiple possible
109 return None; 146/// solutions), we issue a set of *guidance* back to type inference.
110 } 147pub(crate) enum Guidance {
111 } 148 /// The existential variables *must* have the given values if the goal is
112 } 149 /// ever to hold, but that alone isn't enough to guarantee the goal will
113 } 150 /// actually hold.
114 Some(Solution::Unique(solution_substs.into())) 151 Definite(SolutionVariables),
152
153 /// There are multiple plausible values for the existentials, but the ones
154 /// here are suggested as the preferred choice heuristically. These should
155 /// be used for inference fallback only.
156 Suggested(SolutionVariables),
157
158 /// There's no useful information to feed back to type inference
159 Unknown,
115} 160}