Skip to content

Commit

Permalink
Expand target for autocompletion
Browse files Browse the repository at this point in the history
  • Loading branch information
kilpkonn committed Jan 9, 2024
1 parent 868e5eb commit e860e1a
Show file tree
Hide file tree
Showing 15 changed files with 275 additions and 313 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ authors = ["rust-analyzer team"]
[profile.dev]
# Disabling debug info speeds up builds a bunch,
# and we don't rely on it for debugging that much.
debug = 2
debug = 0

[profile.dev.package]
# These speed up local tests.
Expand Down
4 changes: 0 additions & 4 deletions crates/hir-ty/src/infer/unify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,6 @@ pub fn could_unify_deeply(
let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner);
let ty1_with_vars = table.normalize_associated_types_in(ty1_with_vars);
let ty2_with_vars = table.normalize_associated_types_in(ty2_with_vars);
// table.resolve_obligations_as_possible();
// table.propagate_diverging_flag();
// let ty1_with_vars = table.resolve_completely(ty1_with_vars);
// let ty2_with_vars = table.resolve_completely(ty2_with_vars);
table.unify_deeply(&ty1_with_vars, &ty2_with_vars)
}

Expand Down
104 changes: 51 additions & 53 deletions crates/hir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1068,20 +1068,21 @@ impl Field {
Type::new(db, var_id, ty)
}

pub fn ty_with_generics(
&self,
db: &dyn HirDatabase,
mut generics: impl Iterator<Item = Type>,
) -> Type {
pub fn ty_with_args(&self, db: &dyn HirDatabase, generics: impl Iterator<Item = Type>) -> Type {
let var_id = self.parent.into();
let def_id: AdtId = match self.parent {
VariantDef::Struct(it) => it.id.into(),
VariantDef::Union(it) => it.id.into(),
VariantDef::Variant(it) => it.parent.id.into(),
};
let mut generics = generics.map(|it| it.ty.clone());
let substs = TyBuilder::subst_for_def(db, def_id, None)
.fill(|_| {
GenericArg::new(Interner, GenericArgData::Ty(generics.next().unwrap().ty.clone()))
.fill(|x| {
let ty = generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner));
match x {
ParamKind::Type => ty.cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
}
})
.build();
let ty = db.field_types(var_id)[self.id].clone().substitute(Interner, &substs);
Expand Down Expand Up @@ -1141,14 +1142,15 @@ impl Struct {
Type::from_def(db, self.id)
}

pub fn ty_with_generics(
self,
db: &dyn HirDatabase,
mut generics: impl Iterator<Item = Type>,
) -> Type {
pub fn ty_with_args(self, db: &dyn HirDatabase, generics: impl Iterator<Item = Type>) -> Type {
let mut generics = generics.map(|it| it.ty.clone());
let substs = TyBuilder::subst_for_def(db, self.id, None)
.fill(|_| {
GenericArg::new(Interner, GenericArgData::Ty(generics.next().unwrap().ty.clone()))
.fill(|x| {
let ty = generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner));
match x {
ParamKind::Type => ty.cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
}
})
.build();
let ty = db.ty(self.id.into()).substitute(Interner, &substs);
Expand Down Expand Up @@ -1254,16 +1256,18 @@ impl Enum {
Type::from_def(db, self.id)
}

pub fn ty_with_generics(
&self,
db: &dyn HirDatabase,
mut generics: impl Iterator<Item = Type>,
) -> Type {
pub fn ty_with_args(&self, db: &dyn HirDatabase, generics: impl Iterator<Item = Type>) -> Type {
let mut generics = generics.map(|it| it.ty.clone());
let substs = TyBuilder::subst_for_def(db, self.id, None)
.fill(|_| {
GenericArg::new(Interner, GenericArgData::Ty(generics.next().unwrap().ty.clone()))
.fill(|x| {
let ty = generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner));
match x {
ParamKind::Type => ty.cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
}
})
.build();

let ty = db.ty(self.id.into()).substitute(Interner, &substs);
Type::new(db, self.id, ty)
}
Expand Down Expand Up @@ -2070,33 +2074,29 @@ impl Function {
Type::new_with_resolver_inner(db, &resolver, ty)
}

pub fn ret_type_with_generics(
pub fn ret_type_with_args(
self,
db: &dyn HirDatabase,
mut generics: impl Iterator<Item = Type>,
generics: impl Iterator<Item = Type>,
) -> Type {
let resolver = self.id.resolver(db.upcast());
let parent_id: Option<GenericDefId> = match self.id.lookup(db.upcast()).container {
ItemContainerId::ImplId(it) => Some(it.into()),
ItemContainerId::TraitId(it) => Some(it.into()),
ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None,
};
let parent_substs = parent_id.map(|id| {
TyBuilder::subst_for_def(db, id, None)
.fill(|_| {
GenericArg::new(
Interner,
GenericArgData::Ty(generics.next().unwrap().ty.clone()),
)
})
.build()
});
let mut generics = generics.map(|it| it.ty.clone());
let mut filler = |x: &_| {
let ty = generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner));
match x {
ParamKind::Type => ty.cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
}
};

let substs = TyBuilder::subst_for_def(db, self.id, parent_substs)
.fill(|_| {
GenericArg::new(Interner, GenericArgData::Ty(generics.next().unwrap().ty.clone()))
})
.build();
let parent_substs =
parent_id.map(|id| TyBuilder::subst_for_def(db, id, None).fill(&mut filler).build());
let substs = TyBuilder::subst_for_def(db, self.id, parent_substs).fill(&mut filler).build();

let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs);
let ty = callable_sig.ret().clone();
Expand Down Expand Up @@ -2414,11 +2414,7 @@ impl SelfParam {
Type { env: environment, ty }
}

pub fn ty_with_generics(
&self,
db: &dyn HirDatabase,
mut generics: impl Iterator<Item = Type>,
) -> Type {
pub fn ty_with_args(&self, db: &dyn HirDatabase, generics: impl Iterator<Item = Type>) -> Type {
let parent_id: GenericDefId = match self.func.lookup(db.upcast()).container {
ItemContainerId::ImplId(it) => it.into(),
ItemContainerId::TraitId(it) => it.into(),
Expand All @@ -2427,16 +2423,18 @@ impl SelfParam {
}
};

let parent_substs = TyBuilder::subst_for_def(db, parent_id, None)
.fill(|_| {
GenericArg::new(Interner, GenericArgData::Ty(generics.next().unwrap().ty.clone()))
})
.build();
let substs = TyBuilder::subst_for_def(db, self.func, Some(parent_substs))
.fill(|_| {
GenericArg::new(Interner, GenericArgData::Ty(generics.next().unwrap().ty.clone()))
})
.build();
let mut generics = generics.map(|it| it.ty.clone());
let mut filler = |x: &_| {
let ty = generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner));
match x {
ParamKind::Type => ty.cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
}
};

let parent_substs = TyBuilder::subst_for_def(db, parent_id, None).fill(&mut filler).build();
let substs =
TyBuilder::subst_for_def(db, self.func, Some(parent_substs)).fill(&mut filler).build();
let callable_sig =
db.callable_item_signature(self.func.into()).substitute(Interner, &substs);
let environment = db.trait_environment(self.func.into());
Expand Down
88 changes: 44 additions & 44 deletions crates/hir/src/term_search/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,48 +19,61 @@ 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 {
/// There are few trees, so we keep track of them all
Few(FxHashSet<TypeTree>),
Many(Type),
/// 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 {
/// Construct alternative trees
///
/// # Arguments
/// `threshold` - threshold value for many trees (more than that is many)
/// `trees` - trees iterator
fn new(threshold: usize, trees: impl Iterator<Item = TypeTree>) -> AlternativeTrees {
let mut it = AlternativeTrees::Few(Default::default());
it.extend_with_threshold(threshold, ty, trees);
it.extend_with_threshold(threshold, trees);
it
}

pub fn trees(&self) -> Vec<TypeTree> {
/// Get type trees stored in alternative trees (or `TypeTree::Many` in case of many)
///
/// # Arguments
/// `ty` - Type of trees queried (this is used to give type to `TypeTree::Many`)
fn trees(&self, ty: &Type) -> Vec<TypeTree> {
match self {
AlternativeTrees::Few(trees) => trees.iter().cloned().collect(),
AlternativeTrees::Many(ty) => vec![TypeTree::Many(ty.clone())],
AlternativeTrees::Many => vec![TypeTree::Many(ty.clone())],
}
}

pub fn extend_with_threshold(
/// Extend alternative trees
///
/// # Arguments
/// `threshold` - threshold value for many trees (more than that is many)
/// `trees` - trees iterator
fn extend_with_threshold(
&mut self,
threshold: usize,
ty: Type,
mut trees: impl Iterator<Item = TypeTree>,
) {
match self {
AlternativeTrees::Few(tts) => {
while let Some(it) = trees.next() {
if tts.len() > threshold {
*self = AlternativeTrees::Many(ty);
*self = AlternativeTrees::Many;
break;
}

tts.insert(it);
}
}
AlternativeTrees::Many(_) => (),
AlternativeTrees::Many => (),
}
}
}
Expand Down Expand Up @@ -106,7 +119,7 @@ impl LookupTable {
self.data
.iter()
.find(|(t, _)| t.could_unify_with_deeply(db, ty))
.map(|(_, tts)| tts.trees())
.map(|(t, tts)| tts.trees(t))
}

/// Same as find but automatically creates shared reference of types in the lookup
Expand All @@ -117,15 +130,15 @@ impl LookupTable {
self.data
.iter()
.find(|(t, _)| t.could_unify_with_deeply(db, ty))
.map(|(_, tts)| tts.trees())
.map(|(t, tts)| tts.trees(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, tts)| {
tts.trees(t)
.into_iter()
.map(|tt| TypeTree::Reference(Box::new(tt)))
.collect()
Expand All @@ -140,12 +153,9 @@ impl LookupTable {
/// but they clearly do not unify themselves.
fn insert(&mut self, ty: Type, trees: impl Iterator<Item = TypeTree>) {
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, trees),
None => {
self.data.insert(
ty.clone(),
AlternativeTrees::new(self.many_threshold, ty.clone(), trees),
);
self.data.insert(ty.clone(), AlternativeTrees::new(self.many_threshold, trees));
for it in self.new_types.values_mut() {
it.push(ty.clone());
}
Expand Down Expand Up @@ -206,6 +216,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>,
Expand All @@ -230,7 +241,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 }
}
}

Expand All @@ -239,9 +250,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:
Expand All @@ -258,7 +267,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<TypeTree> {
let module = ctx.scope.module();
let mut defs = FxHashSet::default();
defs.insert(ScopeDef::ModuleDef(ModuleDef::Module(module)));
Expand All @@ -270,33 +279,24 @@ pub fn term_search<DB: HirDatabase>(ctx: TermSearchCtx<'_, DB>) -> Vec<TypeTree>
let mut lookup = LookupTable::new();

// Try trivial tactic first, also populates lookup table
let mut solutions: Vec<TypeTree> = tactics::trivial(&ctx, &defs, &mut lookup).collect();
let mut solutions: Vec<TypeTree> = 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() {
defs.remove(def);
}
}

solutions.into_iter().unique().collect()
solutions.into_iter().filter(|it| !it.is_many()).unique().collect()
}
Loading

0 comments on commit e860e1a

Please sign in to comment.