Skip to content

Commit 6b250a2

Browse files
committed
Auto merge of #16687 - kilpkonn:master, r=Veykril
feat: Add "make tuple" tactic to term search Follow up to #16092 Now term search also supports tuples. ```rust let a: i32 = 1; let b: f64 = 0.0; let c: (i32, (f64, i32)) = todo!(); // Finds (a, (b, a)) ``` In addition to new tactic that handles tuples I changed how the generics are handled. Previously it tried all possible options from types we had in scope but now it only tries useful ones that help us directly towards the goal or at least towards calling some other function. This changes O(2^n) to O(n^2) where n is amount of rounds which in practice allows using types that take generics for multiple rounds (previously limited to 1). Average case that also used to be exponential is now roughly linear. This means that deeply nested generics also work. ````rust // Finds all valid combos, including `Some(Some(Some(...)))` let a: Option<Option<Option<bool>>> = todo!(); ```` _Note that although the complexity is smaller allowing more types with generics the search overall slows down considerably. I hope it's fine tho as the autocomplete is disabled by default and for code actions it's not super slow. Might have to tweak the depth hyper parameter tho_ This resulted in a huge increase of results found (benchmarks on `ripgrep` crate): Before ```` Tail Expr syntactic hits: 149/1692 (8%) Tail Exprs found: 749/1692 (44%) Term search avg time: 18ms ``` After ``` Tail Expr syntactic hits: 291/1692 (17%) Tail Exprs found: 1253/1692 (74%) Term search avg time: 139ms ```` Most changes are local to term search except some tuple related stuff on `hir::Type`.
2 parents d8c5a61 + a2bf15e commit 6b250a2

File tree

7 files changed

+223
-76
lines changed

7 files changed

+223
-76
lines changed

crates/hir/src/lib.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3856,6 +3856,11 @@ impl Type {
38563856
Type { env: ty.env, ty: TyBuilder::slice(ty.ty) }
38573857
}
38583858

3859+
pub fn new_tuple(krate: CrateId, tys: &[Type]) -> Type {
3860+
let tys = tys.iter().map(|it| it.ty.clone());
3861+
Type { env: TraitEnvironment::empty(krate), ty: TyBuilder::tuple_with(tys) }
3862+
}
3863+
38593864
pub fn is_unit(&self) -> bool {
38603865
matches!(self.ty.kind(Interner), TyKind::Tuple(0, ..))
38613866
}
@@ -4320,8 +4325,10 @@ impl Type {
43204325
self.ty
43214326
.strip_references()
43224327
.as_adt()
4328+
.map(|(_, substs)| substs)
4329+
.or_else(|| self.ty.strip_references().as_tuple())
43234330
.into_iter()
4324-
.flat_map(|(_, substs)| substs.iter(Interner))
4331+
.flat_map(|substs| substs.iter(Interner))
43254332
.filter_map(|arg| arg.ty(Interner).cloned())
43264333
.map(move |ty| self.derived(ty))
43274334
}

crates/hir/src/term_search.rs

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ impl AlternativeExprs {
7272
AlternativeExprs::Many => (),
7373
}
7474
}
75+
76+
fn is_many(&self) -> bool {
77+
matches!(self, AlternativeExprs::Many)
78+
}
7579
}
7680

7781
/// # Lookup table for term search
@@ -103,27 +107,36 @@ struct LookupTable {
103107

104108
impl LookupTable {
105109
/// Initialize lookup table
106-
fn new(many_threshold: usize) -> Self {
110+
fn new(many_threshold: usize, goal: Type) -> Self {
107111
let mut res = Self { many_threshold, ..Default::default() };
108112
res.new_types.insert(NewTypesKey::ImplMethod, Vec::new());
109113
res.new_types.insert(NewTypesKey::StructProjection, Vec::new());
114+
res.types_wishlist.insert(goal);
110115
res
111116
}
112117

113118
/// Find all `Expr`s that unify with the `ty`
114-
fn find(&self, db: &dyn HirDatabase, ty: &Type) -> Option<Vec<Expr>> {
115-
self.data
119+
fn find(&mut self, db: &dyn HirDatabase, ty: &Type) -> Option<Vec<Expr>> {
120+
let res = self
121+
.data
116122
.iter()
117123
.find(|(t, _)| t.could_unify_with_deeply(db, ty))
118-
.map(|(t, tts)| tts.exprs(t))
124+
.map(|(t, tts)| tts.exprs(t));
125+
126+
if res.is_none() {
127+
self.types_wishlist.insert(ty.clone());
128+
}
129+
130+
res
119131
}
120132

121133
/// Same as find but automatically creates shared reference of types in the lookup
122134
///
123135
/// For example if we have type `i32` in data and we query for `&i32` it map all the type
124136
/// trees we have for `i32` with `Expr::Reference` and returns them.
125-
fn find_autoref(&self, db: &dyn HirDatabase, ty: &Type) -> Option<Vec<Expr>> {
126-
self.data
137+
fn find_autoref(&mut self, db: &dyn HirDatabase, ty: &Type) -> Option<Vec<Expr>> {
138+
let res = self
139+
.data
127140
.iter()
128141
.find(|(t, _)| t.could_unify_with_deeply(db, ty))
129142
.map(|(t, it)| it.exprs(t))
@@ -139,7 +152,13 @@ impl LookupTable {
139152
.map(|expr| Expr::Reference(Box::new(expr)))
140153
.collect()
141154
})
142-
})
155+
});
156+
157+
if res.is_none() {
158+
self.types_wishlist.insert(ty.clone());
159+
}
160+
161+
res
143162
}
144163

145164
/// Insert new type trees for type
@@ -149,7 +168,12 @@ impl LookupTable {
149168
/// but they clearly do not unify themselves.
150169
fn insert(&mut self, ty: Type, exprs: impl Iterator<Item = Expr>) {
151170
match self.data.get_mut(&ty) {
152-
Some(it) => it.extend_with_threshold(self.many_threshold, exprs),
171+
Some(it) => {
172+
it.extend_with_threshold(self.many_threshold, exprs);
173+
if it.is_many() {
174+
self.types_wishlist.remove(&ty);
175+
}
176+
}
153177
None => {
154178
self.data.insert(ty.clone(), AlternativeExprs::new(self.many_threshold, exprs));
155179
for it in self.new_types.values_mut() {
@@ -206,8 +230,8 @@ impl LookupTable {
206230
}
207231

208232
/// Types queried but not found
209-
fn take_types_wishlist(&mut self) -> FxHashSet<Type> {
210-
std::mem::take(&mut self.types_wishlist)
233+
fn types_wishlist(&mut self) -> &FxHashSet<Type> {
234+
&self.types_wishlist
211235
}
212236
}
213237

@@ -272,7 +296,7 @@ pub fn term_search<DB: HirDatabase>(ctx: &TermSearchCtx<'_, DB>) -> Vec<Expr> {
272296
defs.insert(def);
273297
});
274298

275-
let mut lookup = LookupTable::new(ctx.config.many_alternatives_threshold);
299+
let mut lookup = LookupTable::new(ctx.config.many_alternatives_threshold, ctx.goal.clone());
276300

277301
// Try trivial tactic first, also populates lookup table
278302
let mut solutions: Vec<Expr> = tactics::trivial(ctx, &defs, &mut lookup).collect();
@@ -287,6 +311,7 @@ pub fn term_search<DB: HirDatabase>(ctx: &TermSearchCtx<'_, DB>) -> Vec<Expr> {
287311
solutions.extend(tactics::impl_method(ctx, &defs, &mut lookup));
288312
solutions.extend(tactics::struct_projection(ctx, &defs, &mut lookup));
289313
solutions.extend(tactics::impl_static_method(ctx, &defs, &mut lookup));
314+
solutions.extend(tactics::make_tuple(ctx, &defs, &mut lookup));
290315

291316
// Discard not interesting `ScopeDef`s for speedup
292317
for def in lookup.exhausted_scopedefs() {

crates/hir/src/term_search/expr.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ pub enum Expr {
138138
Variant { variant: Variant, generics: Vec<Type>, params: Vec<Expr> },
139139
/// Struct construction
140140
Struct { strukt: Struct, generics: Vec<Type>, params: Vec<Expr> },
141+
/// Tuple construction
142+
Tuple { ty: Type, params: Vec<Expr> },
141143
/// Struct field access
142144
Field { expr: Box<Expr>, field: Field },
143145
/// Passing type as reference (with `&`)
@@ -366,6 +368,18 @@ impl Expr {
366368
let prefix = mod_item_path_str(sema_scope, &ModuleDef::Adt(Adt::Struct(*strukt)))?;
367369
Ok(format!("{prefix}{inner}"))
368370
}
371+
Expr::Tuple { params, .. } => {
372+
let args = params
373+
.iter()
374+
.map(|a| {
375+
a.gen_source_code(sema_scope, many_formatter, prefer_no_std, prefer_prelude)
376+
})
377+
.collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
378+
.into_iter()
379+
.join(", ");
380+
let res = format!("({args})");
381+
Ok(res)
382+
}
369383
Expr::Field { expr, field } => {
370384
if expr.contains_many_in_illegal_pos() {
371385
return Ok(many_formatter(&expr.ty(db)));
@@ -420,6 +434,7 @@ impl Expr {
420434
Expr::Struct { strukt, generics, .. } => {
421435
Adt::from(*strukt).ty_with_args(db, generics.iter().cloned())
422436
}
437+
Expr::Tuple { ty, .. } => ty.clone(),
423438
Expr::Field { expr, field } => field.ty_with_args(db, expr.ty(db).type_arguments()),
424439
Expr::Reference(it) => it.ty(db),
425440
Expr::Many(ty) => ty.clone(),

0 commit comments

Comments
 (0)