diff options
Diffstat (limited to 'crates/ra_hir/src/ty/traits.rs')
-rw-r--r-- | crates/ra_hir/src/ty/traits.rs | 326 |
1 files changed, 0 insertions, 326 deletions
diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs deleted file mode 100644 index 268fa09e4..000000000 --- a/crates/ra_hir/src/ty/traits.rs +++ /dev/null | |||
@@ -1,326 +0,0 @@ | |||
1 | //! Trait solving using Chalk. | ||
2 | use std::sync::{Arc, Mutex}; | ||
3 | |||
4 | use chalk_ir::{cast::Cast, family::ChalkIr}; | ||
5 | use log::debug; | ||
6 | use ra_db::{impl_intern_key, salsa}; | ||
7 | use ra_prof::profile; | ||
8 | use rustc_hash::FxHashSet; | ||
9 | |||
10 | use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk}; | ||
11 | use crate::{db::HirDatabase, expr::ExprId, Crate, DefWithBody, ImplBlock, Trait, TypeAlias}; | ||
12 | |||
13 | use self::chalk::{from_chalk, ToChalk}; | ||
14 | |||
15 | pub(crate) mod chalk; | ||
16 | |||
17 | #[derive(Debug, Clone)] | ||
18 | pub struct TraitSolver { | ||
19 | krate: Crate, | ||
20 | inner: Arc<Mutex<chalk_solve::Solver<ChalkIr>>>, | ||
21 | } | ||
22 | |||
23 | /// We need eq for salsa | ||
24 | impl PartialEq for TraitSolver { | ||
25 | fn eq(&self, other: &TraitSolver) -> bool { | ||
26 | Arc::ptr_eq(&self.inner, &other.inner) | ||
27 | } | ||
28 | } | ||
29 | |||
30 | impl Eq for TraitSolver {} | ||
31 | |||
32 | impl TraitSolver { | ||
33 | fn solve( | ||
34 | &self, | ||
35 | db: &impl HirDatabase, | ||
36 | goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<ChalkIr>>>, | ||
37 | ) -> Option<chalk_solve::Solution<ChalkIr>> { | ||
38 | let context = ChalkContext { db, krate: self.krate }; | ||
39 | debug!("solve goal: {:?}", goal); | ||
40 | let mut solver = match self.inner.lock() { | ||
41 | Ok(it) => it, | ||
42 | // Our cancellation works via unwinding, but, as chalk is not | ||
43 | // panic-safe, we need to make sure to propagate the cancellation. | ||
44 | // Ideally, we should also make chalk panic-safe. | ||
45 | Err(_) => ra_db::Canceled::throw(), | ||
46 | }; | ||
47 | let solution = solver.solve(&context, goal); | ||
48 | debug!("solve({:?}) => {:?}", goal, solution); | ||
49 | solution | ||
50 | } | ||
51 | } | ||
52 | |||
53 | /// This controls the maximum size of types Chalk considers. If we set this too | ||
54 | /// high, we can run into slow edge cases; if we set it too low, Chalk won't | ||
55 | /// find some solutions. | ||
56 | const CHALK_SOLVER_MAX_SIZE: usize = 4; | ||
57 | |||
58 | #[derive(Debug, Copy, Clone)] | ||
59 | struct ChalkContext<'a, DB> { | ||
60 | db: &'a DB, | ||
61 | krate: Crate, | ||
62 | } | ||
63 | |||
64 | pub(crate) fn trait_solver_query( | ||
65 | db: &(impl HirDatabase + salsa::Database), | ||
66 | krate: Crate, | ||
67 | ) -> TraitSolver { | ||
68 | db.salsa_runtime().report_untracked_read(); | ||
69 | // krate parameter is just so we cache a unique solver per crate | ||
70 | let solver_choice = chalk_solve::SolverChoice::SLG { max_size: CHALK_SOLVER_MAX_SIZE }; | ||
71 | debug!("Creating new solver for crate {:?}", krate); | ||
72 | TraitSolver { krate, inner: Arc::new(Mutex::new(solver_choice.into_solver())) } | ||
73 | } | ||
74 | |||
75 | /// Collects impls for the given trait in the whole dependency tree of `krate`. | ||
76 | pub(crate) fn impls_for_trait_query( | ||
77 | db: &impl HirDatabase, | ||
78 | krate: Crate, | ||
79 | trait_: Trait, | ||
80 | ) -> Arc<[ImplBlock]> { | ||
81 | let mut impls = FxHashSet::default(); | ||
82 | // We call the query recursively here. On the one hand, this means we can | ||
83 | // reuse results from queries for different crates; on the other hand, this | ||
84 | // will only ever get called for a few crates near the root of the tree (the | ||
85 | // ones the user is editing), so this may actually be a waste of memory. I'm | ||
86 | // doing it like this mainly for simplicity for now. | ||
87 | for dep in krate.dependencies(db) { | ||
88 | impls.extend(db.impls_for_trait(dep.krate, trait_).iter()); | ||
89 | } | ||
90 | let crate_impl_blocks = db.impls_in_crate(krate); | ||
91 | impls.extend(crate_impl_blocks.lookup_impl_blocks_for_trait(trait_)); | ||
92 | impls.into_iter().collect() | ||
93 | } | ||
94 | |||
95 | /// A set of clauses that we assume to be true. E.g. if we are inside this function: | ||
96 | /// ```rust | ||
97 | /// fn foo<T: Default>(t: T) {} | ||
98 | /// ``` | ||
99 | /// we assume that `T: Default`. | ||
100 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
101 | pub struct TraitEnvironment { | ||
102 | pub predicates: Vec<GenericPredicate>, | ||
103 | } | ||
104 | |||
105 | impl TraitEnvironment { | ||
106 | /// Returns trait refs with the given self type which are supposed to hold | ||
107 | /// in this trait env. E.g. if we are in `foo<T: SomeTrait>()`, this will | ||
108 | /// find that `T: SomeTrait` if we call it for `T`. | ||
109 | pub(crate) fn trait_predicates_for_self_ty<'a>( | ||
110 | &'a self, | ||
111 | ty: &'a Ty, | ||
112 | ) -> impl Iterator<Item = &'a TraitRef> + 'a { | ||
113 | self.predicates.iter().filter_map(move |pred| match pred { | ||
114 | GenericPredicate::Implemented(tr) if tr.self_ty() == ty => Some(tr), | ||
115 | _ => None, | ||
116 | }) | ||
117 | } | ||
118 | } | ||
119 | |||
120 | /// Something (usually a goal), along with an environment. | ||
121 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
122 | pub struct InEnvironment<T> { | ||
123 | pub environment: Arc<TraitEnvironment>, | ||
124 | pub value: T, | ||
125 | } | ||
126 | |||
127 | impl<T> InEnvironment<T> { | ||
128 | pub fn new(environment: Arc<TraitEnvironment>, value: T) -> InEnvironment<T> { | ||
129 | InEnvironment { environment, value } | ||
130 | } | ||
131 | } | ||
132 | |||
133 | /// Something that needs to be proven (by Chalk) during type checking, e.g. that | ||
134 | /// a certain type implements a certain trait. Proving the Obligation might | ||
135 | /// result in additional information about inference variables. | ||
136 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
137 | pub enum Obligation { | ||
138 | /// Prove that a certain type implements a trait (the type is the `Self` type | ||
139 | /// parameter to the `TraitRef`). | ||
140 | Trait(TraitRef), | ||
141 | Projection(ProjectionPredicate), | ||
142 | } | ||
143 | |||
144 | impl Obligation { | ||
145 | pub fn from_predicate(predicate: GenericPredicate) -> Option<Obligation> { | ||
146 | match predicate { | ||
147 | GenericPredicate::Implemented(trait_ref) => Some(Obligation::Trait(trait_ref)), | ||
148 | GenericPredicate::Projection(projection_pred) => { | ||
149 | Some(Obligation::Projection(projection_pred)) | ||
150 | } | ||
151 | GenericPredicate::Error => None, | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | |||
156 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
157 | pub struct ProjectionPredicate { | ||
158 | pub projection_ty: ProjectionTy, | ||
159 | pub ty: Ty, | ||
160 | } | ||
161 | |||
162 | impl TypeWalk for ProjectionPredicate { | ||
163 | fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
164 | self.projection_ty.walk(f); | ||
165 | self.ty.walk(f); | ||
166 | } | ||
167 | |||
168 | fn walk_mut_binders(&mut self, f: &mut impl FnMut(&mut Ty, usize), binders: usize) { | ||
169 | self.projection_ty.walk_mut_binders(f, binders); | ||
170 | self.ty.walk_mut_binders(f, binders); | ||
171 | } | ||
172 | } | ||
173 | |||
174 | /// Solve a trait goal using Chalk. | ||
175 | pub(crate) fn trait_solve_query( | ||
176 | db: &impl HirDatabase, | ||
177 | krate: Crate, | ||
178 | goal: Canonical<InEnvironment<Obligation>>, | ||
179 | ) -> Option<Solution> { | ||
180 | let _p = profile("trait_solve_query"); | ||
181 | debug!("trait_solve_query({})", goal.value.value.display(db)); | ||
182 | |||
183 | if let Obligation::Projection(pred) = &goal.value.value { | ||
184 | if let Ty::Bound(_) = &pred.projection_ty.parameters[0] { | ||
185 | // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible | ||
186 | return Some(Solution::Ambig(Guidance::Unknown)); | ||
187 | } | ||
188 | } | ||
189 | |||
190 | let canonical = goal.to_chalk(db).cast(); | ||
191 | |||
192 | // We currently don't deal with universes (I think / hope they're not yet | ||
193 | // relevant for our use cases?) | ||
194 | let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 }; | ||
195 | let solution = db.trait_solver(krate).solve(db, &u_canonical); | ||
196 | solution.map(|solution| solution_from_chalk(db, solution)) | ||
197 | } | ||
198 | |||
199 | fn solution_from_chalk( | ||
200 | db: &impl HirDatabase, | ||
201 | solution: chalk_solve::Solution<ChalkIr>, | ||
202 | ) -> Solution { | ||
203 | let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution<ChalkIr>>| { | ||
204 | let value = subst | ||
205 | .value | ||
206 | .parameters | ||
207 | .into_iter() | ||
208 | .map(|p| { | ||
209 | let ty = match p { | ||
210 | chalk_ir::Parameter(chalk_ir::ParameterKind::Ty(ty)) => from_chalk(db, ty), | ||
211 | chalk_ir::Parameter(chalk_ir::ParameterKind::Lifetime(_)) => unimplemented!(), | ||
212 | }; | ||
213 | ty | ||
214 | }) | ||
215 | .collect(); | ||
216 | let result = Canonical { value, num_vars: subst.binders.len() }; | ||
217 | SolutionVariables(result) | ||
218 | }; | ||
219 | match solution { | ||
220 | chalk_solve::Solution::Unique(constr_subst) => { | ||
221 | let subst = chalk_ir::Canonical { | ||
222 | value: constr_subst.value.subst, | ||
223 | binders: constr_subst.binders, | ||
224 | }; | ||
225 | Solution::Unique(convert_subst(subst)) | ||
226 | } | ||
227 | chalk_solve::Solution::Ambig(chalk_solve::Guidance::Definite(subst)) => { | ||
228 | Solution::Ambig(Guidance::Definite(convert_subst(subst))) | ||
229 | } | ||
230 | chalk_solve::Solution::Ambig(chalk_solve::Guidance::Suggested(subst)) => { | ||
231 | Solution::Ambig(Guidance::Suggested(convert_subst(subst))) | ||
232 | } | ||
233 | chalk_solve::Solution::Ambig(chalk_solve::Guidance::Unknown) => { | ||
234 | Solution::Ambig(Guidance::Unknown) | ||
235 | } | ||
236 | } | ||
237 | } | ||
238 | |||
239 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
240 | pub struct SolutionVariables(pub Canonical<Vec<Ty>>); | ||
241 | |||
242 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
243 | /// A (possible) solution for a proposed goal. | ||
244 | pub enum Solution { | ||
245 | /// The goal indeed holds, and there is a unique value for all existential | ||
246 | /// variables. | ||
247 | Unique(SolutionVariables), | ||
248 | |||
249 | /// The goal may be provable in multiple ways, but regardless we may have some guidance | ||
250 | /// for type inference. In this case, we don't return any lifetime | ||
251 | /// constraints, since we have not "committed" to any particular solution | ||
252 | /// yet. | ||
253 | Ambig(Guidance), | ||
254 | } | ||
255 | |||
256 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
257 | /// When a goal holds ambiguously (e.g., because there are multiple possible | ||
258 | /// solutions), we issue a set of *guidance* back to type inference. | ||
259 | pub enum Guidance { | ||
260 | /// The existential variables *must* have the given values if the goal is | ||
261 | /// ever to hold, but that alone isn't enough to guarantee the goal will | ||
262 | /// actually hold. | ||
263 | Definite(SolutionVariables), | ||
264 | |||
265 | /// There are multiple plausible values for the existentials, but the ones | ||
266 | /// here are suggested as the preferred choice heuristically. These should | ||
267 | /// be used for inference fallback only. | ||
268 | Suggested(SolutionVariables), | ||
269 | |||
270 | /// There's no useful information to feed back to type inference | ||
271 | Unknown, | ||
272 | } | ||
273 | |||
274 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||
275 | pub enum FnTrait { | ||
276 | FnOnce, | ||
277 | FnMut, | ||
278 | Fn, | ||
279 | } | ||
280 | |||
281 | impl FnTrait { | ||
282 | fn lang_item_name(self) -> &'static str { | ||
283 | match self { | ||
284 | FnTrait::FnOnce => "fn_once", | ||
285 | FnTrait::FnMut => "fn_mut", | ||
286 | FnTrait::Fn => "fn", | ||
287 | } | ||
288 | } | ||
289 | } | ||
290 | |||
291 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
292 | pub struct ClosureFnTraitImplData { | ||
293 | def: DefWithBody, | ||
294 | expr: ExprId, | ||
295 | fn_trait: FnTrait, | ||
296 | } | ||
297 | |||
298 | /// An impl. Usually this comes from an impl block, but some built-in types get | ||
299 | /// synthetic impls. | ||
300 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
301 | pub enum Impl { | ||
302 | /// A normal impl from an impl block. | ||
303 | ImplBlock(ImplBlock), | ||
304 | /// Closure types implement the Fn traits synthetically. | ||
305 | ClosureFnTraitImpl(ClosureFnTraitImplData), | ||
306 | } | ||
307 | /// This exists just for Chalk, because our ImplIds are only unique per module. | ||
308 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
309 | pub struct GlobalImplId(salsa::InternId); | ||
310 | impl_intern_key!(GlobalImplId); | ||
311 | |||
312 | /// An associated type value. Usually this comes from a `type` declaration | ||
313 | /// inside an impl block, but for built-in impls we have to synthesize it. | ||
314 | /// (We only need this because Chalk wants a unique ID for each of these.) | ||
315 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
316 | pub enum AssocTyValue { | ||
317 | /// A normal assoc type value from an impl block. | ||
318 | TypeAlias(TypeAlias), | ||
319 | /// The output type of the Fn trait implementation. | ||
320 | ClosureFnTraitImplOutput(ClosureFnTraitImplData), | ||
321 | } | ||
322 | /// This exists just for Chalk, because it needs a unique ID for each associated | ||
323 | /// type value in an impl (even synthetic ones). | ||
324 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
325 | pub struct AssocTyValueId(salsa::InternId); | ||
326 | impl_intern_key!(AssocTyValueId); | ||