Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir/src/term_search/tactics.rs')
| -rw-r--r-- | crates/hir/src/term_search/tactics.rs | 195 |
1 files changed, 134 insertions, 61 deletions
diff --git a/crates/hir/src/term_search/tactics.rs b/crates/hir/src/term_search/tactics.rs index edbf75affe..102e0ca4c3 100644 --- a/crates/hir/src/term_search/tactics.rs +++ b/crates/hir/src/term_search/tactics.rs @@ -109,7 +109,6 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( lookup: &mut LookupTable, parent_enum: Enum, variant: Variant, - goal: &Type, config: &TermSearchConfig, ) -> Vec<(Type, Vec<Expr>)> { // Ignore unstable @@ -143,11 +142,14 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( let non_default_type_params_len = type_params.iter().filter(|it| it.default(db).is_none()).count(); + let enum_ty_shallow = Adt::from(parent_enum).ty(db); let generic_params = lookup - .iter_types() - .collect::<Vec<_>>() // Force take ownership + .types_wishlist() + .clone() .into_iter() - .permutations(non_default_type_params_len); + .filter(|ty| ty.could_unify_with(db, &enum_ty_shallow)) + .map(|it| it.type_arguments().collect::<Vec<Type>>()) + .chain((non_default_type_params_len == 0).then_some(Vec::new())); generic_params .filter_map(move |generics| { @@ -155,17 +157,11 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( let mut g = generics.into_iter(); let generics: Vec<_> = type_params .iter() - .map(|it| it.default(db).unwrap_or_else(|| g.next().expect("No generic"))) - .collect(); + .map(|it| it.default(db).or_else(|| g.next())) + .collect::<Option<_>>()?; let enum_ty = Adt::from(parent_enum).ty_with_args(db, generics.iter().cloned()); - // Allow types with generics only if they take us straight to goal for - // performance reasons - if !generics.is_empty() && !enum_ty.could_unify_with_deeply(db, goal) { - return None; - } - // Ignore types that have something to do with lifetimes if config.enable_borrowcheck && enum_ty.contains_reference(db) { return None; @@ -199,21 +195,37 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( .filter_map(move |def| match def { ScopeDef::ModuleDef(ModuleDef::Variant(it)) => { let variant_exprs = - variant_helper(db, lookup, it.parent_enum(db), *it, &ctx.goal, &ctx.config); + variant_helper(db, lookup, it.parent_enum(db), *it, &ctx.config); if variant_exprs.is_empty() { return None; } - lookup.mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Variant(*it))); + if GenericDef::from(it.parent_enum(db)) + .type_or_const_params(db) + .into_iter() + .filter_map(|it| it.as_type_param(db)) + .all(|it| it.default(db).is_some()) + { + lookup.mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Variant(*it))); + } Some(variant_exprs) } ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Enum(enum_))) => { let exprs: Vec<(Type, Vec<Expr>)> = enum_ .variants(db) .into_iter() - .flat_map(|it| variant_helper(db, lookup, *enum_, it, &ctx.goal, &ctx.config)) + .flat_map(|it| variant_helper(db, lookup, *enum_, it, &ctx.config)) .collect(); - if !exprs.is_empty() { + if exprs.is_empty() { + return None; + } + + if GenericDef::from(*enum_) + .type_or_const_params(db) + .into_iter() + .filter_map(|it| it.as_type_param(db)) + .all(|it| it.default(db).is_some()) + { lookup.mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Enum(*enum_)))); } @@ -249,11 +261,14 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( let non_default_type_params_len = type_params.iter().filter(|it| it.default(db).is_none()).count(); + let struct_ty_shallow = Adt::from(*it).ty(db); let generic_params = lookup - .iter_types() - .collect::<Vec<_>>() // Force take ownership + .types_wishlist() + .clone() .into_iter() - .permutations(non_default_type_params_len); + .filter(|ty| ty.could_unify_with(db, &struct_ty_shallow)) + .map(|it| it.type_arguments().collect::<Vec<Type>>()) + .chain((non_default_type_params_len == 0).then_some(Vec::new())); let exprs = generic_params .filter_map(|generics| { @@ -261,22 +276,11 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( let mut g = generics.into_iter(); let generics: Vec<_> = type_params .iter() - .map(|it| { - it.default(db) - .unwrap_or_else(|| g.next().expect("Missing type param")) - }) - .collect(); + .map(|it| it.default(db).or_else(|| g.next())) + .collect::<Option<_>>()?; let struct_ty = Adt::from(*it).ty_with_args(db, generics.iter().cloned()); - // Allow types with generics only if they take us straight to goal for - // performance reasons - if non_default_type_params_len != 0 - && struct_ty.could_unify_with_deeply(db, &ctx.goal) - { - return None; - } - // Ignore types that have something to do with lifetimes if ctx.config.enable_borrowcheck && struct_ty.contains_reference(db) { return None; @@ -309,8 +313,12 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( .collect() }; - lookup - .mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Struct(*it)))); + if non_default_type_params_len == 0 { + // Fulfilled only if there are no generic parameters + lookup.mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Adt( + Adt::Struct(*it), + ))); + } lookup.insert(struct_ty.clone(), struct_exprs.iter().cloned()); Some((struct_ty, struct_exprs)) @@ -525,14 +533,17 @@ pub(super) fn impl_method<'a, DB: HirDatabase>( return None; } - let non_default_type_params_len = imp_type_params - .iter() - .chain(fn_type_params.iter()) - .filter(|it| it.default(db).is_none()) - .count(); + // Double check that we have fully known type + if ty.type_arguments().any(|it| it.contains_unknown()) { + return None; + } + + let non_default_fn_type_params_len = + fn_type_params.iter().filter(|it| it.default(db).is_none()).count(); - // Ignore bigger number of generics for now as they kill the performance - if non_default_type_params_len > 0 { + // Ignore functions with generics for now as they kill the performance + // Also checking bounds for generics is problematic + if non_default_fn_type_params_len > 0 { return None; } @@ -540,23 +551,23 @@ pub(super) fn impl_method<'a, DB: HirDatabase>( .iter_types() .collect::<Vec<_>>() // Force take ownership .into_iter() - .permutations(non_default_type_params_len); + .permutations(non_default_fn_type_params_len); let exprs: Vec<_> = generic_params .filter_map(|generics| { // Insert default type params let mut g = generics.into_iter(); - let generics: Vec<_> = imp_type_params - .iter() - .chain(fn_type_params.iter()) - .map(|it| match it.default(db) { + let generics: Vec<_> = ty + .type_arguments() + .map(Some) + .chain(fn_type_params.iter().map(|it| match it.default(db) { Some(ty) => Some(ty), None => { let generic = g.next().expect("Missing type param"); // Filter out generics that do not unify due to trait bounds it.ty(db).could_unify_with(db, &generic).then_some(generic) } - }) + })) .collect::<Option<_>>()?; let ret_ty = it.ret_type_with_args( @@ -713,7 +724,8 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( let db = ctx.sema.db; let module = ctx.scope.module(); lookup - .take_types_wishlist() + .types_wishlist() + .clone() .into_iter() .chain(iter::once(ctx.goal.clone())) .flat_map(|ty| { @@ -768,14 +780,17 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( return None; } - let non_default_type_params_len = imp_type_params - .iter() - .chain(fn_type_params.iter()) - .filter(|it| it.default(db).is_none()) - .count(); + // Double check that we have fully known type + if ty.type_arguments().any(|it| it.contains_unknown()) { + return None; + } + + let non_default_fn_type_params_len = + fn_type_params.iter().filter(|it| it.default(db).is_none()).count(); - // Ignore bigger number of generics for now as they kill the performance - if non_default_type_params_len > 1 { + // Ignore functions with generics for now as they kill the performance + // Also checking bounds for generics is problematic + if non_default_fn_type_params_len > 0 { return None; } @@ -783,16 +798,16 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( .iter_types() .collect::<Vec<_>>() // Force take ownership .into_iter() - .permutations(non_default_type_params_len); + .permutations(non_default_fn_type_params_len); let exprs: Vec<_> = generic_params .filter_map(|generics| { // Insert default type params let mut g = generics.into_iter(); - let generics: Vec<_> = imp_type_params - .iter() - .chain(fn_type_params.iter()) - .map(|it| match it.default(db) { + let generics: Vec<_> = ty + .type_arguments() + .map(Some) + .chain(fn_type_params.iter().map(|it| match it.default(db) { Some(ty) => Some(ty), None => { let generic = g.next().expect("Missing type param"); @@ -802,7 +817,7 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( // Filter out generics that do not unify due to trait bounds it.ty(db).could_unify_with(db, &generic).then_some(generic) } - }) + })) .collect::<Option<_>>()?; let ret_ty = it.ret_type_with_args( @@ -857,3 +872,61 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) .flatten() } + +/// # Make tuple tactic +/// +/// Attempts to create tuple types if any are listed in types wishlist +/// +/// Updates lookup by new types reached and returns iterator that yields +/// elements that unify with `goal`. +/// +/// # Arguments +/// * `ctx` - Context for the term search +/// * `defs` - Set of items in scope at term search target location +/// * `lookup` - Lookup table for types +pub(super) fn make_tuple<'a, DB: HirDatabase>( + ctx: &'a TermSearchCtx<'a, DB>, + _defs: &'a FxHashSet<ScopeDef>, + lookup: &'a mut LookupTable, +) -> impl Iterator<Item = Expr> + 'a { + let db = ctx.sema.db; + let module = ctx.scope.module(); + + lookup + .types_wishlist() + .clone() + .into_iter() + .filter(|ty| ty.is_tuple()) + .filter_map(move |ty| { + // Double check to not contain unknown + if ty.contains_unknown() { + return None; + } + + // Ignore types that have something to do with lifetimes + if ctx.config.enable_borrowcheck && ty.contains_reference(db) { + return None; + } + + // Early exit if some param cannot be filled from lookup + let param_exprs: Vec<Vec<Expr>> = + ty.type_arguments().map(|field| lookup.find(db, &field)).collect::<Option<_>>()?; + + let exprs: Vec<Expr> = param_exprs + .into_iter() + .multi_cartesian_product() + .map(|params| { + let tys: Vec<Type> = params.iter().map(|it| it.ty(db)).collect(); + let tuple_ty = Type::new_tuple(module.krate().into(), &tys); + + let expr = Expr::Tuple { ty: tuple_ty.clone(), params }; + lookup.insert(tuple_ty, iter::once(expr.clone())); + expr + }) + .collect(); + + Some(exprs) + }) + .flatten() + .filter_map(|expr| expr.ty(db).could_unify_with_deeply(db, &ctx.goal).then_some(expr)) +} |