1use std::iter;
12
13use hir_ty::{db::HirDatabase, mir::BorrowKind};
14use itertools::Itertools;
15use rustc_hash::FxHashSet;
16
17use crate::{
18 Adt, AssocItem, BuiltinType, GenericDef, GenericParam, HasAttrs, HasVisibility, Impl,
19 ModuleDef, ScopeDef, Type, TypeParam, term_search::Expr,
20};
21
22use super::{LookupTable, NewTypesKey, TermSearchCtx};
23
24pub(super) fn trivial<'a, 'lt, 'db, DB: HirDatabase>(
39 ctx: &'a TermSearchCtx<'_, 'db, DB>,
40 defs: &'a FxHashSet<ScopeDef>,
41 lookup: &'lt mut LookupTable<'db>,
42) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
43 let db = ctx.sema.db;
44 defs.iter().filter_map(|def| {
45 let expr = match def {
46 ScopeDef::ModuleDef(ModuleDef::Const(it)) => Some(Expr::Const(*it)),
47 ScopeDef::ModuleDef(ModuleDef::Static(it)) => Some(Expr::Static(*it)),
48 ScopeDef::GenericParam(GenericParam::ConstParam(it)) => Some(Expr::ConstParam(*it)),
49 ScopeDef::Local(it) => {
50 if ctx.config.enable_borrowcheck {
51 let borrowck = db.borrowck(it.parent_infer).ok()?;
52
53 let invalid = borrowck.iter().any(|b| {
54 let mir_body = b.mir_body(ctx.sema.db);
55 b.partially_moved.iter().any(|moved| {
56 Some(&moved.local) == mir_body.binding_locals.get(it.binding_id)
57 }) || b.borrow_regions.iter().any(|region| {
58 Some(®ion.local) == mir_body.binding_locals.get(it.binding_id)
60 && region.kind != BorrowKind::Shared
61 })
62 });
63
64 if invalid {
65 return None;
66 }
67 }
68
69 Some(Expr::Local(*it))
70 }
71 _ => None,
72 }?;
73
74 let ty = expr.ty(db);
75 lookup.insert(ty.clone(), std::iter::once(expr.clone()));
76
77 if matches!(expr, Expr::Local(_))
79 && ty.contains_reference(db)
80 && ctx.config.enable_borrowcheck
81 {
82 return None;
83 }
84
85 ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(expr)
86 })
87}
88
89pub(super) fn assoc_const<'a, 'lt, 'db, DB: HirDatabase>(
104 ctx: &'a TermSearchCtx<'_, 'db, DB>,
105 defs: &'a FxHashSet<ScopeDef>,
106 lookup: &'lt mut LookupTable<'db>,
107) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
108 let db = ctx.sema.db;
109 let module = ctx.scope.module();
110
111 defs.iter()
112 .filter_map(|def| match def {
113 ScopeDef::ModuleDef(ModuleDef::Adt(it)) => Some(it),
114 _ => None,
115 })
116 .flat_map(|it| Impl::all_for_type(db, it.ty(db)))
117 .filter(|it| !it.is_unsafe(db))
118 .flat_map(|it| it.items(db))
119 .filter(move |it| it.is_visible_from(db, module))
120 .filter_map(AssocItem::as_const)
121 .filter_map(|it| {
122 if it.attrs(db).is_unstable() {
123 return None;
124 }
125
126 let expr = Expr::Const(it);
127 let ty = it.ty(db);
128
129 if ty.contains_unknown() {
130 return None;
131 }
132
133 lookup.insert(ty.clone(), std::iter::once(expr.clone()));
134
135 ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(expr)
136 })
137}
138
139pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>(
152 ctx: &'a TermSearchCtx<'_, 'db, DB>,
153 _defs: &'a FxHashSet<ScopeDef>,
154 lookup: &'lt mut LookupTable<'db>,
155 should_continue: &'a dyn std::ops::Fn() -> bool,
156) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
157 let db = ctx.sema.db;
158 let module = ctx.scope.module();
159 lookup
160 .types_wishlist()
161 .clone()
162 .into_iter()
163 .chain(iter::once(ctx.goal.clone()))
164 .filter_map(|ty| ty.as_adt().map(|adt| (adt, ty)))
165 .filter(|_| should_continue())
166 .filter_map(move |(adt, ty)| match adt {
167 Adt::Struct(strukt) => {
168 if strukt.is_unstable(db) || !strukt.is_visible_from(db, module) {
170 return None;
171 }
172
173 let generics = GenericDef::from(strukt);
174
175 if !generics.lifetime_params(db).is_empty() {
178 return None;
179 }
180
181 if ty.contains_unknown() {
182 return None;
183 }
184
185 if ctx.config.enable_borrowcheck && ty.contains_reference(db) {
187 return None;
188 }
189 let fields = strukt.fields(db);
190 if fields.iter().any(|it| !it.is_visible_from(db, module)) {
192 return None;
193 }
194
195 let generics: Vec<_> = ty.type_arguments().collect();
196
197 let param_exprs: Vec<Vec<Expr<'_>>> = fields
199 .into_iter()
200 .map(|field| {
201 lookup.find(db, &field.ty(db).instantiate(generics.iter().cloned()))
202 })
203 .collect::<Option<_>>()?;
204
205 let exprs: Vec<Expr<'_>> = if param_exprs.is_empty() {
208 vec![Expr::Struct { strukt, generics, params: Vec::new() }]
209 } else {
210 param_exprs
211 .into_iter()
212 .multi_cartesian_product()
213 .map(|params| Expr::Struct { strukt, generics: generics.clone(), params })
214 .collect()
215 };
216
217 lookup.insert(ty.clone(), exprs.iter().cloned());
218 Some((ty, exprs))
219 }
220 Adt::Enum(enum_) => {
221 if enum_.is_unstable(db) || !enum_.is_visible_from(db, module) {
223 return None;
224 }
225
226 let generics = GenericDef::from(enum_);
227 if !generics.lifetime_params(db).is_empty() {
230 return None;
231 }
232
233 if ty.contains_unknown() {
234 return None;
235 }
236
237 if ctx.config.enable_borrowcheck && ty.contains_reference(db) {
239 return None;
240 }
241
242 let generics: Vec<_> = ty.type_arguments().collect();
243 let exprs = enum_
244 .variants(db)
245 .into_iter()
246 .filter_map(|variant| {
247 let param_exprs: Vec<Vec<Expr<'_>>> = variant
249 .fields(db)
250 .into_iter()
251 .map(|field| {
252 lookup.find(db, &field.ty(db).instantiate(generics.iter().cloned()))
253 })
254 .collect::<Option<_>>()?;
255
256 let variant_exprs: Vec<Expr<'_>> = if param_exprs.is_empty() {
259 vec![Expr::Variant {
260 variant,
261 generics: generics.clone(),
262 params: Vec::new(),
263 }]
264 } else {
265 param_exprs
266 .into_iter()
267 .multi_cartesian_product()
268 .map(|params| Expr::Variant {
269 variant,
270 generics: generics.clone(),
271 params,
272 })
273 .collect()
274 };
275 lookup.insert(ty.clone(), variant_exprs.iter().cloned());
276 Some(variant_exprs)
277 })
278 .flatten()
279 .collect();
280
281 Some((ty, exprs))
282 }
283 Adt::Union(_) => None,
284 })
285 .filter_map(|(ty, exprs)| {
286 ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs)
287 })
288 .flatten()
289}
290
291pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>(
305 ctx: &'a TermSearchCtx<'_, 'db, DB>,
306 defs: &'a FxHashSet<ScopeDef>,
307 lookup: &'lt mut LookupTable<'db>,
308 should_continue: &'a dyn std::ops::Fn() -> bool,
309) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
310 let db = ctx.sema.db;
311 let module = ctx.scope.module();
312 defs.iter()
313 .filter_map(move |def| match def {
314 ScopeDef::ModuleDef(ModuleDef::Function(it)) => {
315 let generics = GenericDef::from(*it);
316
317 let type_params = generics
319 .type_or_const_params(db)
320 .into_iter()
321 .map(|it| it.as_type_param(db))
322 .collect::<Option<Vec<TypeParam>>>()?;
323
324 if !generics.lifetime_params(db).is_empty() {
326 return None;
327 }
328
329 if type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none()) {
332 return None;
333 }
334
335 let non_default_type_params_len =
336 type_params.iter().filter(|it| it.default(db).is_none()).count();
337
338 if non_default_type_params_len > 0 {
340 return None;
341 }
342
343 let generic_params = lookup
344 .iter_types()
345 .collect::<Vec<_>>() .into_iter()
347 .permutations(non_default_type_params_len);
348
349 let exprs: Vec<_> = generic_params
350 .filter(|_| should_continue())
351 .filter_map(|generics| {
352 let mut g = generics.into_iter();
354 let generics: Vec<_> = type_params
355 .iter()
356 .map(|it| match it.default(db) {
357 Some(ty) => Some(ty),
358 None => {
359 let generic = g.next().expect("Missing type param");
360 it.ty(db).could_unify_with(db, &generic).then_some(generic)
362 }
363 })
364 .collect::<Option<_>>()?;
365
366 let ret_ty = it.ret_type(db).instantiate(generics.iter().cloned());
367 if !it.is_visible_from(db, module)
369 || it.is_unsafe_to_call(
370 db,
371 None,
372 crate::Crate::from(ctx.scope.resolver().krate()).edition(db),
373 )
374 || it.is_unstable(db)
375 || ctx.config.enable_borrowcheck && ret_ty.contains_reference(db)
376 || ret_ty.is_raw_ptr()
377 {
378 return None;
379 }
380
381 let param_exprs: Vec<Vec<Expr<'_>>> = it
383 .params_without_self(db)
384 .into_iter()
385 .map(|field| {
386 let ty = &field.ty().instantiate(&generics);
387 match ty.is_mutable_reference() {
388 true => None,
389 false => lookup.find_autoref(db, ty),
390 }
391 })
392 .collect::<Option<_>>()?;
393
394 let fn_exprs: Vec<Expr<'_>> = if param_exprs.is_empty() {
397 vec![Expr::Function { func: *it, generics, params: Vec::new() }]
398 } else {
399 param_exprs
400 .into_iter()
401 .multi_cartesian_product()
402 .map(|params| Expr::Function {
403 func: *it,
404 generics: generics.clone(),
405
406 params,
407 })
408 .collect()
409 };
410
411 lookup.insert(ret_ty.clone(), fn_exprs.iter().cloned());
412 Some((ret_ty, fn_exprs))
413 })
414 .collect();
415 Some(exprs)
416 }
417 _ => None,
418 })
419 .flatten()
420 .filter_map(|(ty, exprs)| {
421 ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs)
422 })
423 .flatten()
424}
425
426pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>(
442 ctx: &'a TermSearchCtx<'_, 'db, DB>,
443 _defs: &'a FxHashSet<ScopeDef>,
444 lookup: &'lt mut LookupTable<'db>,
445 should_continue: &'a dyn std::ops::Fn() -> bool,
446) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
447 let db = ctx.sema.db;
448 let module = ctx.scope.module();
449 lookup
450 .new_types(NewTypesKey::ImplMethod)
451 .into_iter()
452 .filter(|ty| !ty.type_arguments().any(|it| it.contains_unknown()))
453 .filter(|_| should_continue())
454 .flat_map(|ty| {
455 Impl::all_for_type(db, ty.clone()).into_iter().map(move |imp| (ty.clone(), imp))
456 })
457 .flat_map(|(ty, imp)| imp.items(db).into_iter().map(move |item| (imp, ty.clone(), item)))
458 .filter_map(|(imp, ty, it)| match it {
459 AssocItem::Function(f) => Some((imp, ty, f)),
460 _ => None,
461 })
462 .filter(|_| should_continue())
463 .filter_map(move |(imp, ty, it)| {
464 let fn_generics = GenericDef::from(it);
465 let imp_generics = GenericDef::from(imp);
466
467 if !fn_generics.lifetime_params(db).is_empty()
469 || !imp_generics.lifetime_params(db).is_empty()
470 {
471 return None;
472 }
473
474 if !it.has_self_param(db) {
476 return None;
477 }
478
479 if !it.is_visible_from(db, module)
481 || it.is_unsafe_to_call(
482 db,
483 None,
484 crate::Crate::from(ctx.scope.resolver().krate()).edition(db),
485 )
486 || it.is_unstable(db)
487 {
488 return None;
489 }
490
491 if !fn_generics.type_or_const_params(db).is_empty() {
494 return None;
495 }
496
497 let ret_ty = it.ret_type(db).instantiate(ty.type_arguments());
498 if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr()
500 {
501 return None;
502 }
503
504 if ty.instantiate_with_errors().could_unify_with_deeply(db, &ret_ty) {
506 return None;
507 }
508
509 let self_ty =
510 it.self_param(db).expect("No self param").ty(db).instantiate(ty.type_arguments());
511
512 if !self_ty.autoderef(db).any(|s_ty| ty == s_ty) {
514 return None;
515 }
516
517 let target_type_exprs = lookup.find(db, &ty).expect("Type not in lookup");
518
519 let param_exprs: Vec<Vec<Expr<'_>>> = it
521 .params_without_self(db)
522 .into_iter()
523 .map(|field| lookup.find_autoref(db, &field.ty().instantiate(ty.type_arguments())))
524 .collect::<Option<_>>()?;
525
526 let generics: Vec<_> = ty.type_arguments().collect();
527 let fn_exprs: Vec<Expr<'_>> = std::iter::once(target_type_exprs)
528 .chain(param_exprs)
529 .multi_cartesian_product()
530 .map(|params| {
531 let mut params = params.into_iter();
532 let target = Box::new(params.next().unwrap());
533 Expr::Method {
534 func: it,
535 generics: generics.clone(),
536 target,
537 params: params.collect(),
538 }
539 })
540 .collect();
541
542 Some((ret_ty, fn_exprs))
543 })
544 .filter_map(|(ty, exprs)| {
545 ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs)
546 })
547 .flatten()
548}
549
550pub(super) fn struct_projection<'a, 'lt, 'db, DB: HirDatabase>(
563 ctx: &'a TermSearchCtx<'_, 'db, DB>,
564 _defs: &'a FxHashSet<ScopeDef>,
565 lookup: &'lt mut LookupTable<'db>,
566 should_continue: &'a dyn std::ops::Fn() -> bool,
567) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
568 let db = ctx.sema.db;
569 let module = ctx.scope.module();
570 lookup
571 .new_types(NewTypesKey::StructProjection)
572 .into_iter()
573 .map(|ty| (ty.clone(), lookup.find(db, &ty).expect("Expr not in lookup")))
574 .filter(|_| should_continue())
575 .flat_map(move |(ty, targets)| {
576 ty.fields(db).into_iter().filter_map(move |(field, filed_ty)| {
577 if !field.is_visible_from(db, module) {
578 return None;
579 }
580 let exprs = targets
581 .clone()
582 .into_iter()
583 .map(move |target| Expr::Field { field, expr: Box::new(target) });
584 Some((filed_ty, exprs))
585 })
586 })
587 .filter_map(|(ty, exprs)| {
588 ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs)
589 })
590 .flatten()
591}
592
593pub(super) fn famous_types<'a, 'lt, 'db, DB: HirDatabase>(
607 ctx: &'a TermSearchCtx<'_, 'db, DB>,
608 _defs: &'a FxHashSet<ScopeDef>,
609 lookup: &'lt mut LookupTable<'db>,
610) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
611 let db = ctx.sema.db;
612 let bool_ty = BuiltinType::bool().ty(db);
613 let unit_ty = Type::new_unit();
614 [
615 Expr::FamousType { ty: bool_ty.clone(), value: "true" },
616 Expr::FamousType { ty: bool_ty, value: "false" },
617 Expr::FamousType { ty: unit_ty, value: "()" },
618 ]
619 .into_iter()
620 .inspect(|exprs| {
621 lookup.insert(exprs.ty(db), std::iter::once(exprs.clone()));
622 })
623 .filter(|expr| expr.ty(db).instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal))
624}
625
626pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>(
639 ctx: &'a TermSearchCtx<'_, 'db, DB>,
640 _defs: &'a FxHashSet<ScopeDef>,
641 lookup: &'lt mut LookupTable<'db>,
642 should_continue: &'a dyn std::ops::Fn() -> bool,
643) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
644 let db = ctx.sema.db;
645 let module = ctx.scope.module();
646 lookup
647 .types_wishlist()
648 .clone()
649 .into_iter()
650 .chain(iter::once(ctx.goal.clone()))
651 .filter(|ty| !ty.type_arguments().any(|it| it.contains_unknown()))
652 .filter(|_| should_continue())
653 .flat_map(|ty| {
654 Impl::all_for_type(db, ty.clone()).into_iter().map(move |imp| (ty.clone(), imp))
655 })
656 .filter(|(_, imp)| !imp.is_unsafe(db))
657 .flat_map(|(ty, imp)| imp.items(db).into_iter().map(move |item| (imp, ty.clone(), item)))
658 .filter_map(|(imp, ty, it)| match it {
659 AssocItem::Function(f) => Some((imp, ty, f)),
660 _ => None,
661 })
662 .filter(|_| should_continue())
663 .filter_map(move |(imp, ty, it)| {
664 let fn_generics = GenericDef::from(it);
665 let imp_generics = GenericDef::from(imp);
666
667 if !fn_generics.lifetime_params(db).is_empty()
669 || !imp_generics.lifetime_params(db).is_empty()
670 {
671 return None;
672 }
673
674 if it.has_self_param(db) {
676 return None;
677 }
678
679 if !it.is_visible_from(db, module)
681 || it.is_unsafe_to_call(
682 db,
683 None,
684 crate::Crate::from(ctx.scope.resolver().krate()).edition(db),
685 )
686 || it.is_unstable(db)
687 {
688 return None;
689 }
690
691 if !fn_generics.type_or_const_params(db).is_empty() {
694 return None;
695 }
696
697 let ret_ty = it.ret_type(db).instantiate(ty.type_arguments());
698 if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr()
700 {
701 return None;
702 }
703
704 let param_exprs: Vec<Vec<Expr<'_>>> = it
706 .params_without_self(db)
707 .into_iter()
708 .map(|field| lookup.find_autoref(db, &field.ty().instantiate(ty.type_arguments())))
709 .collect::<Option<_>>()?;
710
711 let generics = ty.type_arguments().collect();
714 let fn_exprs: Vec<Expr<'_>> = if param_exprs.is_empty() {
715 vec![Expr::Function { func: it, generics, params: Vec::new() }]
716 } else {
717 param_exprs
718 .into_iter()
719 .multi_cartesian_product()
720 .map(|params| Expr::Function { func: it, generics: generics.clone(), params })
721 .collect()
722 };
723
724 lookup.insert(ret_ty.clone(), fn_exprs.iter().cloned());
725
726 Some((ret_ty, fn_exprs))
727 })
728 .filter_map(|(ty, exprs)| {
729 ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs)
730 })
731 .flatten()
732}
733
734pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>(
747 ctx: &'a TermSearchCtx<'_, 'db, DB>,
748 _defs: &'a FxHashSet<ScopeDef>,
749 lookup: &'lt mut LookupTable<'db>,
750 should_continue: &'a dyn std::ops::Fn() -> bool,
751) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
752 let db = ctx.sema.db;
753
754 lookup
755 .types_wishlist()
756 .clone()
757 .into_iter()
758 .filter(|_| should_continue())
759 .filter(|ty| ty.is_tuple())
760 .filter_map(move |ty| {
761 if ty.contains_unknown() {
763 return None;
764 }
765
766 if ctx.config.enable_borrowcheck && ty.contains_reference(db) {
768 return None;
769 }
770
771 let param_exprs: Vec<Vec<Expr<'db>>> =
773 ty.type_arguments().map(|field| lookup.find(db, &field)).collect::<Option<_>>()?;
774
775 let exprs: Vec<Expr<'db>> = param_exprs
776 .into_iter()
777 .multi_cartesian_product()
778 .filter(|_| should_continue())
779 .map(|params| {
780 let tys: Vec<Type<'_>> = params.iter().map(|it| it.ty(db)).collect();
781 let tuple_ty = Type::new_tuple(db, &tys);
782
783 let expr = Expr::Tuple { ty: tuple_ty.clone(), params };
784 lookup.insert(tuple_ty, iter::once(expr.clone()));
785 expr
786 })
787 .collect();
788
789 Some(exprs)
790 })
791 .flatten()
792 .filter_map(|expr| {
793 expr.ty(db)
794 .instantiate_with_errors()
795 .could_unify_with_deeply(db, &ctx.goal)
796 .then_some(expr)
797 })
798}