Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir/src/term_search/mod.rs')
| -rw-r--r-- | crates/hir/src/term_search/mod.rs | 132 |
1 files changed, 64 insertions, 68 deletions
diff --git a/crates/hir/src/term_search/mod.rs b/crates/hir/src/term_search/mod.rs index a519324cda..f0c3fdb4d0 100644 --- a/crates/hir/src/term_search/mod.rs +++ b/crates/hir/src/term_search/mod.rs @@ -7,8 +7,8 @@ use rustc_hash::{FxHashMap, FxHashSet}; use crate::{ModuleDef, ScopeDef, Semantics, SemanticsScope, Type}; -pub mod type_tree; -pub use type_tree::TypeTree; +mod expr; +pub use expr::Expr; mod tactics; @@ -19,48 +19,57 @@ enum NewTypesKey { StructProjection, } +/// Helper enum to squash big number of alternative trees into `Many` variant as there is too many +/// to take into account. #[derive(Debug)] -enum AlternativeTrees { - Few(FxHashSet<TypeTree>), - Many(Type), +enum AlternativeExprs { + /// There are few trees, so we keep track of them all + Few(FxHashSet<Expr>), + /// There are too many trees to keep track of + Many, } -impl AlternativeTrees { - pub fn new( - threshold: usize, - ty: Type, - trees: impl Iterator<Item = TypeTree>, - ) -> AlternativeTrees { - let mut it = AlternativeTrees::Few(Default::default()); - it.extend_with_threshold(threshold, ty, trees); +impl AlternativeExprs { + /// Construct alternative trees + /// + /// # Arguments + /// `threshold` - threshold value for many trees (more than that is many) + /// `exprs` - expressions iterator + fn new(threshold: usize, exprs: impl Iterator<Item = Expr>) -> AlternativeExprs { + let mut it = AlternativeExprs::Few(Default::default()); + it.extend_with_threshold(threshold, exprs); it } - pub fn trees(&self) -> Vec<TypeTree> { + /// Get type trees stored in alternative trees (or `Expr::Many` in case of many) + /// + /// # Arguments + /// `ty` - Type of expressions queried (this is used to give type to `Expr::Many`) + fn exprs(&self, ty: &Type) -> Vec<Expr> { match self { - AlternativeTrees::Few(trees) => trees.iter().cloned().collect(), - AlternativeTrees::Many(ty) => vec![TypeTree::Many(ty.clone())], + AlternativeExprs::Few(exprs) => exprs.iter().cloned().collect(), + AlternativeExprs::Many => vec![Expr::Many(ty.clone())], } } - pub fn extend_with_threshold( - &mut self, - threshold: usize, - ty: Type, - mut trees: impl Iterator<Item = TypeTree>, - ) { + /// Extend alternative expressions + /// + /// # Arguments + /// `threshold` - threshold value for many trees (more than that is many) + /// `exprs` - expressions iterator + fn extend_with_threshold(&mut self, threshold: usize, mut exprs: impl Iterator<Item = Expr>) { match self { - AlternativeTrees::Few(tts) => { - while let Some(it) = trees.next() { + AlternativeExprs::Few(tts) => { + while let Some(it) = exprs.next() { if tts.len() > threshold { - *self = AlternativeTrees::Many(ty); + *self = AlternativeExprs::Many; break; } tts.insert(it); } } - AlternativeTrees::Many(_) => (), + AlternativeExprs::Many => (), } } } @@ -76,8 +85,8 @@ impl AlternativeTrees { /// not produce any new results. #[derive(Default, Debug)] struct LookupTable { - /// All the `TypeTree`s in "value" produce the type of "key" - data: FxHashMap<Type, AlternativeTrees>, + /// All the `Expr`s in "value" produce the type of "key" + data: FxHashMap<Type, AlternativeExprs>, /// New types reached since last query by the `NewTypesKey` new_types: FxHashMap<NewTypesKey, Vec<Type>>, /// ScopeDefs that are not interesting any more @@ -94,40 +103,40 @@ struct LookupTable { impl LookupTable { /// Initialize lookup table - fn new() -> Self { - let mut res: Self = Default::default(); + fn new(many_threshold: usize) -> Self { + let mut res = Self { many_threshold, ..Default::default() }; res.new_types.insert(NewTypesKey::ImplMethod, Vec::new()); res.new_types.insert(NewTypesKey::StructProjection, Vec::new()); res } - /// Find all `TypeTree`s that unify with the `ty` - fn find(&self, db: &dyn HirDatabase, ty: &Type) -> Option<Vec<TypeTree>> { + /// Find all `Expr`s that unify with the `ty` + fn find(&self, db: &dyn HirDatabase, ty: &Type) -> Option<Vec<Expr>> { self.data .iter() .find(|(t, _)| t.could_unify_with_deeply(db, ty)) - .map(|(_, tts)| tts.trees()) + .map(|(t, tts)| tts.exprs(t)) } /// Same as find but automatically creates shared reference of types in the lookup /// /// For example if we have type `i32` in data and we query for `&i32` it map all the type - /// trees we have for `i32` with `TypeTree::Reference` and returns them. - fn find_autoref(&self, db: &dyn HirDatabase, ty: &Type) -> Option<Vec<TypeTree>> { + /// trees we have for `i32` with `Expr::Reference` and returns them. + fn find_autoref(&self, db: &dyn HirDatabase, ty: &Type) -> Option<Vec<Expr>> { self.data .iter() .find(|(t, _)| t.could_unify_with_deeply(db, ty)) - .map(|(_, tts)| tts.trees()) + .map(|(t, it)| it.exprs(t)) .or_else(|| { self.data .iter() .find(|(t, _)| { Type::reference(t, Mutability::Shared).could_unify_with_deeply(db, &ty) }) - .map(|(_, tts)| { - tts.trees() + .map(|(t, it)| { + it.exprs(t) .into_iter() - .map(|tt| TypeTree::Reference(Box::new(tt))) + .map(|expr| Expr::Reference(Box::new(expr))) .collect() }) }) @@ -138,14 +147,11 @@ impl LookupTable { /// Note that the types have to be the same, unification is not enough as unification is not /// transitive. For example Vec<i32> and FxHashSet<i32> both unify with Iterator<Item = i32>, /// but they clearly do not unify themselves. - fn insert(&mut self, ty: Type, trees: impl Iterator<Item = TypeTree>) { + fn insert(&mut self, ty: Type, exprs: impl Iterator<Item = Expr>) { match self.data.get_mut(&ty) { - Some(it) => it.extend_with_threshold(self.many_threshold, ty, trees), + Some(it) => it.extend_with_threshold(self.many_threshold, exprs), None => { - self.data.insert( - ty.clone(), - AlternativeTrees::new(self.many_threshold, ty.clone(), trees), - ); + self.data.insert(ty.clone(), AlternativeExprs::new(self.many_threshold, exprs)); for it in self.new_types.values_mut() { it.push(ty.clone()); } @@ -206,6 +212,7 @@ impl LookupTable { } /// Context for the `term_search` function +#[derive(Debug)] pub struct TermSearchCtx<'a, DB: HirDatabase> { /// Semantics for the program pub sema: &'a Semantics<'a, DB>, @@ -230,7 +237,7 @@ pub struct TermSearchConfig { impl Default for TermSearchConfig { fn default() -> Self { - Self { enable_borrowcheck: true, many_alternatives_threshold: 1, depth: 5 } + Self { enable_borrowcheck: true, many_alternatives_threshold: 1, depth: 6 } } } @@ -239,9 +246,7 @@ impl Default for TermSearchConfig { /// Search for terms (expressions) that unify with the `goal` type. /// /// # Arguments -/// * `sema` - Semantics for the program -/// * `scope` - Semantic scope, captures context for the term search -/// * `goal` - Target / expected output type +/// * `ctx` - Context for term search /// /// Internally this function uses Breadth First Search to find path to `goal` type. /// The general idea is following: @@ -258,7 +263,7 @@ impl Default for TermSearchConfig { /// Note that there are usually more ways we can get to the `goal` type but some are discarded to /// reduce the memory consumption. It is also unlikely anyone is willing ti browse through /// thousands of possible responses so we currently take first 10 from every tactic. -pub fn term_search<DB: HirDatabase>(ctx: TermSearchCtx<'_, DB>) -> Vec<TypeTree> { +pub fn term_search<DB: HirDatabase>(ctx: &TermSearchCtx<'_, DB>) -> Vec<Expr> { let module = ctx.scope.module(); let mut defs = FxHashSet::default(); defs.insert(ScopeDef::ModuleDef(ModuleDef::Module(module))); @@ -267,30 +272,21 @@ pub fn term_search<DB: HirDatabase>(ctx: TermSearchCtx<'_, DB>) -> Vec<TypeTree> defs.insert(def); }); - let mut lookup = LookupTable::new(); + let mut lookup = LookupTable::new(ctx.config.many_alternatives_threshold); // Try trivial tactic first, also populates lookup table - let mut solutions: Vec<TypeTree> = tactics::trivial(&ctx, &defs, &mut lookup).collect(); + let mut solutions: Vec<Expr> = tactics::trivial(ctx, &defs, &mut lookup).collect(); // Use well known types tactic before iterations as it does not depend on other tactics - solutions.extend(tactics::famous_types(&ctx, &defs, &mut lookup)); - - let mut solution_found = !solutions.is_empty(); + solutions.extend(tactics::famous_types(ctx, &defs, &mut lookup)); for _ in 0..ctx.config.depth { lookup.new_round(); - solutions.extend(tactics::type_constructor(&ctx, &defs, &mut lookup)); - solutions.extend(tactics::free_function(&ctx, &defs, &mut lookup)); - solutions.extend(tactics::impl_method(&ctx, &defs, &mut lookup)); - solutions.extend(tactics::struct_projection(&ctx, &defs, &mut lookup)); - solutions.extend(tactics::impl_static_method(&ctx, &defs, &mut lookup)); - - // Break after 1 round after successful solution - if solution_found { - break; - } - - solution_found = !solutions.is_empty(); + solutions.extend(tactics::type_constructor(ctx, &defs, &mut lookup)); + solutions.extend(tactics::free_function(ctx, &defs, &mut lookup)); + solutions.extend(tactics::impl_method(ctx, &defs, &mut lookup)); + solutions.extend(tactics::struct_projection(ctx, &defs, &mut lookup)); + solutions.extend(tactics::impl_static_method(ctx, &defs, &mut lookup)); // Discard not interesting `ScopeDef`s for speedup for def in lookup.exhausted_scopedefs() { @@ -298,5 +294,5 @@ pub fn term_search<DB: HirDatabase>(ctx: TermSearchCtx<'_, DB>) -> Vec<TypeTree> } } - solutions.into_iter().unique().collect() + solutions.into_iter().filter(|it| !it.is_many()).unique().collect() } |