diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-05-12 19:36:36 +0100 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-05-12 19:36:36 +0100 |
commit | 0f57564f78207946fdb09e0cddc21a55966b1bc5 (patch) | |
tree | e8aa2948b2bb301f453307c35311634cda5de43b /crates/ra_hir/src | |
parent | 02ba107bbf24b1d09e61f76bb64b7355076744c4 (diff) | |
parent | bc59f83991a6444ff2f2364b0e942e8a82943b6d (diff) |
Merge #1266
1266: Chalk integration / method resolution fixes r=matklad a=flodiebold
- fix impl blocks with unresolved target trait being treated as inherent impls
- add traits from prelude for method resolution, and deduplicate them
- blacklist some traits from being considered in where clauses, namely `Send`, `Sync`, `Sized`, and the `Fn` traits. We don't handle these correctly yet for several reasons, and this makes us much less likely to run into cases where Chalk gets very slow (because these usually only happen if there is no solution, and that's more likely to happen for these traits).
- when there's an errored where clause, return just that one (since it will be always false anyway). This also makes things easier on Chalk ;)
Co-authored-by: Florian Diebold <[email protected]>
Diffstat (limited to 'crates/ra_hir/src')
-rw-r--r-- | crates/ra_hir/src/resolve.rs | 25 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/method_resolution.rs | 14 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 41 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/traits/chalk.rs | 29 |
4 files changed, 83 insertions, 26 deletions
diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index 707556ef8..2fb219908 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs | |||
@@ -3,7 +3,7 @@ use std::sync::Arc; | |||
3 | 3 | ||
4 | use ra_syntax::ast; | 4 | use ra_syntax::ast; |
5 | 5 | ||
6 | use rustc_hash::FxHashMap; | 6 | use rustc_hash::{FxHashMap, FxHashSet}; |
7 | 7 | ||
8 | use crate::{ | 8 | use crate::{ |
9 | ModuleDef, Trait, | 9 | ModuleDef, Trait, |
@@ -193,19 +193,18 @@ impl Resolver { | |||
193 | names | 193 | names |
194 | } | 194 | } |
195 | 195 | ||
196 | pub(crate) fn traits_in_scope<'a>(&'a self) -> impl Iterator<Item = Trait> + 'a { | 196 | pub(crate) fn traits_in_scope(&self, db: &impl HirDatabase) -> FxHashSet<Trait> { |
197 | // FIXME prelude | 197 | let mut traits = FxHashSet::default(); |
198 | self.scopes | 198 | for scope in &self.scopes { |
199 | .iter() | 199 | if let Scope::ModuleScope(m) = scope { |
200 | .rev() | 200 | if let Some(prelude) = m.crate_def_map.prelude() { |
201 | .flat_map(|scope| { | 201 | let prelude_def_map = db.crate_def_map(prelude.krate); |
202 | match scope { | 202 | traits.extend(prelude_def_map[prelude.module_id].scope.traits()); |
203 | Scope::ModuleScope(m) => Some(m.crate_def_map[m.module_id].scope.traits()), | ||
204 | _ => None, | ||
205 | } | 203 | } |
206 | .into_iter() | 204 | traits.extend(m.crate_def_map[m.module_id].scope.traits()); |
207 | }) | 205 | } |
208 | .flatten() | 206 | } |
207 | traits | ||
209 | } | 208 | } |
210 | 209 | ||
211 | fn module(&self) -> Option<(&CrateDefMap, CrateModuleId)> { | 210 | fn module(&self) -> Option<(&CrateDefMap, CrateModuleId)> { |
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index d8b8c836c..34817a5ec 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs | |||
@@ -75,11 +75,13 @@ impl CrateImplBlocks { | |||
75 | 75 | ||
76 | let target_ty = impl_block.target_ty(db); | 76 | let target_ty = impl_block.target_ty(db); |
77 | 77 | ||
78 | if let Some(tr) = impl_block.target_trait_ref(db) { | 78 | if impl_block.target_trait(db).is_some() { |
79 | self.impls_by_trait | 79 | if let Some(tr) = impl_block.target_trait_ref(db) { |
80 | .entry(tr.trait_) | 80 | self.impls_by_trait |
81 | .or_insert_with(Vec::new) | 81 | .entry(tr.trait_) |
82 | .push((module.module_id, impl_id)); | 82 | .or_insert_with(Vec::new) |
83 | .push((module.module_id, impl_id)); | ||
84 | } | ||
83 | } else { | 85 | } else { |
84 | if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) { | 86 | if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) { |
85 | self.impls | 87 | self.impls |
@@ -183,7 +185,7 @@ fn iterate_trait_method_candidates<T>( | |||
183 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | 185 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, |
184 | ) -> Option<T> { | 186 | ) -> Option<T> { |
185 | let krate = resolver.krate()?; | 187 | let krate = resolver.krate()?; |
186 | 'traits: for t in resolver.traits_in_scope() { | 188 | 'traits: for t in resolver.traits_in_scope(db) { |
187 | let data = t.trait_data(db); | 189 | let data = t.trait_data(db); |
188 | // we'll be lazy about checking whether the type implements the | 190 | // we'll be lazy about checking whether the type implements the |
189 | // trait, but if we find out it doesn't, we'll skip the rest of the | 191 | // trait, but if we find out it doesn't, we'll skip the rest of the |
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 59c85daed..978cc2587 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -2502,6 +2502,35 @@ fn test() { (&S).foo()<|>; } | |||
2502 | } | 2502 | } |
2503 | 2503 | ||
2504 | #[test] | 2504 | #[test] |
2505 | fn method_resolution_trait_from_prelude() { | ||
2506 | let (mut db, pos) = MockDatabase::with_position( | ||
2507 | r#" | ||
2508 | //- /main.rs | ||
2509 | struct S; | ||
2510 | impl Clone for S {} | ||
2511 | |||
2512 | fn test() { | ||
2513 | S.clone()<|>; | ||
2514 | } | ||
2515 | |||
2516 | //- /lib.rs | ||
2517 | #[prelude_import] use foo::*; | ||
2518 | |||
2519 | mod foo { | ||
2520 | trait Clone { | ||
2521 | fn clone(&self) -> Self; | ||
2522 | } | ||
2523 | } | ||
2524 | "#, | ||
2525 | ); | ||
2526 | db.set_crate_graph_from_fixture(crate_graph! { | ||
2527 | "main": ("/main.rs", ["other_crate"]), | ||
2528 | "other_crate": ("/lib.rs", []), | ||
2529 | }); | ||
2530 | assert_eq!("S", type_at_pos(&db, pos)); | ||
2531 | } | ||
2532 | |||
2533 | #[test] | ||
2505 | fn method_resolution_where_clause_for_unknown_trait() { | 2534 | fn method_resolution_where_clause_for_unknown_trait() { |
2506 | // The blanket impl shouldn't apply because we can't even resolve UnknownTrait | 2535 | // The blanket impl shouldn't apply because we can't even resolve UnknownTrait |
2507 | let t = type_at( | 2536 | let t = type_at( |
@@ -2620,22 +2649,22 @@ fn method_resolution_slow() { | |||
2620 | let t = type_at( | 2649 | let t = type_at( |
2621 | r#" | 2650 | r#" |
2622 | //- /main.rs | 2651 | //- /main.rs |
2623 | trait Send {} | 2652 | trait SendX {} |
2624 | 2653 | ||
2625 | struct S1; impl Send for S1; | 2654 | struct S1; impl SendX for S1; |
2626 | struct S2; impl Send for S2; | 2655 | struct S2; impl SendX for S2; |
2627 | struct U1; | 2656 | struct U1; |
2628 | 2657 | ||
2629 | trait Trait { fn method(self); } | 2658 | trait Trait { fn method(self); } |
2630 | 2659 | ||
2631 | struct X1<A, B> {} | 2660 | struct X1<A, B> {} |
2632 | impl<A, B> Send for X1<A, B> where A: Send, B: Send {} | 2661 | impl<A, B> SendX for X1<A, B> where A: SendX, B: SendX {} |
2633 | 2662 | ||
2634 | struct S<B, C> {} | 2663 | struct S<B, C> {} |
2635 | 2664 | ||
2636 | trait Fn {} | 2665 | trait FnX {} |
2637 | 2666 | ||
2638 | impl<B, C> Trait for S<B, C> where C: Fn, B: Send {} | 2667 | impl<B, C> Trait for S<B, C> where C: FnX, B: SendX {} |
2639 | 2668 | ||
2640 | fn test() { (S {}).method()<|>; } | 2669 | fn test() { (S {}).method()<|>; } |
2641 | "#, | 2670 | "#, |
diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index 027c5ec4c..78440b258 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs | |||
@@ -190,6 +190,14 @@ fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T> { | |||
190 | } | 190 | } |
191 | } | 191 | } |
192 | 192 | ||
193 | fn blacklisted_trait(db: &impl HirDatabase, trait_: Trait) -> bool { | ||
194 | let name = trait_.name(db).unwrap_or_else(crate::Name::missing).to_string(); | ||
195 | match &*name { | ||
196 | "Send" | "Sync" | "Sized" | "Fn" | "FnMut" | "FnOnce" => true, | ||
197 | _ => false, | ||
198 | } | ||
199 | } | ||
200 | |||
193 | fn convert_where_clauses( | 201 | fn convert_where_clauses( |
194 | db: &impl HirDatabase, | 202 | db: &impl HirDatabase, |
195 | def: GenericDef, | 203 | def: GenericDef, |
@@ -198,6 +206,19 @@ fn convert_where_clauses( | |||
198 | let generic_predicates = db.generic_predicates(def); | 206 | let generic_predicates = db.generic_predicates(def); |
199 | let mut result = Vec::with_capacity(generic_predicates.len()); | 207 | let mut result = Vec::with_capacity(generic_predicates.len()); |
200 | for pred in generic_predicates.iter() { | 208 | for pred in generic_predicates.iter() { |
209 | if pred.is_error() { | ||
210 | // HACK: Return just the single predicate (which is always false | ||
211 | // anyway), otherwise Chalk can easily get into slow situations | ||
212 | return vec![pred.clone().subst(substs).to_chalk(db)]; | ||
213 | } | ||
214 | match pred { | ||
215 | GenericPredicate::Implemented(trait_ref) => { | ||
216 | if blacklisted_trait(db, trait_ref.trait_) { | ||
217 | continue; | ||
218 | } | ||
219 | } | ||
220 | _ => {} | ||
221 | } | ||
201 | result.push(pred.clone().subst(substs).to_chalk(db)); | 222 | result.push(pred.clone().subst(substs).to_chalk(db)); |
202 | } | 223 | } |
203 | result | 224 | result |
@@ -230,6 +251,7 @@ where | |||
230 | return Arc::new(TraitDatum { binders: make_binders(trait_datum_bound, 1) }); | 251 | return Arc::new(TraitDatum { binders: make_binders(trait_datum_bound, 1) }); |
231 | } | 252 | } |
232 | let trait_: Trait = from_chalk(self.db, trait_id); | 253 | let trait_: Trait = from_chalk(self.db, trait_id); |
254 | debug!("trait {:?} = {:?}", trait_id, trait_.name(self.db)); | ||
233 | let generic_params = trait_.generic_params(self.db); | 255 | let generic_params = trait_.generic_params(self.db); |
234 | let bound_vars = Substs::bound_vars(&generic_params); | 256 | let bound_vars = Substs::bound_vars(&generic_params); |
235 | let trait_ref = trait_.trait_ref(self.db).subst(&bound_vars).to_chalk(self.db); | 257 | let trait_ref = trait_.trait_ref(self.db).subst(&bound_vars).to_chalk(self.db); |
@@ -250,6 +272,7 @@ where | |||
250 | fn struct_datum(&self, struct_id: chalk_ir::StructId) -> Arc<StructDatum> { | 272 | fn struct_datum(&self, struct_id: chalk_ir::StructId) -> Arc<StructDatum> { |
251 | debug!("struct_datum {:?}", struct_id); | 273 | debug!("struct_datum {:?}", struct_id); |
252 | let type_ctor = from_chalk(self.db, struct_id); | 274 | let type_ctor = from_chalk(self.db, struct_id); |
275 | debug!("struct {:?} = {:?}", struct_id, type_ctor); | ||
253 | // FIXME might be nicer if we can create a fake GenericParams for the TypeCtor | 276 | // FIXME might be nicer if we can create a fake GenericParams for the TypeCtor |
254 | // FIXME extract this to a method on Ty | 277 | // FIXME extract this to a method on Ty |
255 | let (num_params, where_clauses, upstream) = match type_ctor { | 278 | let (num_params, where_clauses, upstream) = match type_ctor { |
@@ -358,7 +381,11 @@ where | |||
358 | if trait_id == UNKNOWN_TRAIT { | 381 | if trait_id == UNKNOWN_TRAIT { |
359 | return Vec::new(); | 382 | return Vec::new(); |
360 | } | 383 | } |
361 | let trait_ = from_chalk(self.db, trait_id); | 384 | let trait_: Trait = from_chalk(self.db, trait_id); |
385 | let blacklisted = blacklisted_trait(self.db, trait_); | ||
386 | if blacklisted { | ||
387 | return Vec::new(); | ||
388 | } | ||
362 | let result: Vec<_> = self | 389 | let result: Vec<_> = self |
363 | .db | 390 | .db |
364 | .impls_for_trait(self.krate, trait_) | 391 | .impls_for_trait(self.krate, trait_) |