aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_ty/src/traits/builtin.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_ty/src/traits/builtin.rs')
-rw-r--r--crates/ra_hir_ty/src/traits/builtin.rs178
1 files changed, 178 insertions, 0 deletions
diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs
new file mode 100644
index 000000000..dd41176f0
--- /dev/null
+++ b/crates/ra_hir_ty/src/traits/builtin.rs
@@ -0,0 +1,178 @@
1//! This module provides the built-in trait implementations, e.g. to make
2//! closures implement `Fn`.
3use hir_def::{expr::Expr, lang_item::LangItemTarget, TraitId, TypeAliasId};
4use hir_expand::name::name;
5use ra_db::CrateId;
6
7use super::{AssocTyValue, Impl};
8use crate::{db::HirDatabase, ApplicationTy, Substs, TraitRef, Ty, TypeCtor};
9
10pub(super) struct BuiltinImplData {
11 pub num_vars: usize,
12 pub trait_ref: TraitRef,
13 pub where_clauses: Vec<super::GenericPredicate>,
14 pub assoc_ty_values: Vec<AssocTyValue>,
15}
16
17pub(super) struct BuiltinImplAssocTyValueData {
18 pub impl_: Impl,
19 pub assoc_ty_id: TypeAliasId,
20 pub num_vars: usize,
21 pub value: Ty,
22}
23
24pub(super) fn get_builtin_impls(
25 db: &impl HirDatabase,
26 krate: CrateId,
27 ty: &Ty,
28 trait_: TraitId,
29 mut callback: impl FnMut(Impl),
30) {
31 // Note: since impl_datum needs to be infallible, we need to make sure here
32 // that we have all prerequisites to build the respective impls.
33 if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { def, expr }, .. }) = ty {
34 for &fn_trait in [super::FnTrait::FnOnce, super::FnTrait::FnMut, super::FnTrait::Fn].iter()
35 {
36 if let Some(actual_trait) = get_fn_trait(db, krate, fn_trait) {
37 if trait_ == actual_trait {
38 let impl_ = super::ClosureFnTraitImplData { def: *def, expr: *expr, fn_trait };
39 if check_closure_fn_trait_impl_prerequisites(db, krate, impl_) {
40 callback(Impl::ClosureFnTraitImpl(impl_));
41 }
42 }
43 }
44 }
45 }
46}
47
48pub(super) fn impl_datum(db: &impl HirDatabase, krate: CrateId, impl_: Impl) -> BuiltinImplData {
49 match impl_ {
50 Impl::ImplBlock(_) => unreachable!(),
51 Impl::ClosureFnTraitImpl(data) => closure_fn_trait_impl_datum(db, krate, data),
52 }
53}
54
55pub(super) fn associated_ty_value(
56 db: &impl HirDatabase,
57 krate: CrateId,
58 data: AssocTyValue,
59) -> BuiltinImplAssocTyValueData {
60 match data {
61 AssocTyValue::TypeAlias(_) => unreachable!(),
62 AssocTyValue::ClosureFnTraitImplOutput(data) => {
63 closure_fn_trait_output_assoc_ty_value(db, krate, data)
64 }
65 }
66}
67
68fn check_closure_fn_trait_impl_prerequisites(
69 db: &impl HirDatabase,
70 krate: CrateId,
71 data: super::ClosureFnTraitImplData,
72) -> bool {
73 // the respective Fn/FnOnce/FnMut trait needs to exist
74 if get_fn_trait(db, krate, data.fn_trait).is_none() {
75 return false;
76 }
77
78 // FIXME: there are more assumptions that we should probably check here:
79 // the traits having no type params, FnOnce being a supertrait
80
81 // the FnOnce trait needs to exist and have an assoc type named Output
82 let fn_once_trait = match get_fn_trait(db, krate, super::FnTrait::FnOnce) {
83 Some(t) => t,
84 None => return false,
85 };
86 db.trait_data(fn_once_trait).associated_type_by_name(&name![Output]).is_some()
87}
88
89fn closure_fn_trait_impl_datum(
90 db: &impl HirDatabase,
91 krate: CrateId,
92 data: super::ClosureFnTraitImplData,
93) -> BuiltinImplData {
94 // for some closure |X, Y| -> Z:
95 // impl<T, U, V> Fn<(T, U)> for closure<fn(T, U) -> V> { Output = V }
96
97 let trait_ = get_fn_trait(db, krate, data.fn_trait) // get corresponding fn trait
98 // the existence of the Fn trait has been checked before
99 .expect("fn trait for closure impl missing");
100
101 let num_args: u16 = match &db.body(data.def.into())[data.expr] {
102 Expr::Lambda { args, .. } => args.len() as u16,
103 _ => {
104 log::warn!("closure for closure type {:?} not found", data);
105 0
106 }
107 };
108
109 let arg_ty = Ty::apply(
110 TypeCtor::Tuple { cardinality: num_args },
111 Substs::builder(num_args as usize).fill_with_bound_vars(0).build(),
112 );
113 let sig_ty = Ty::apply(
114 TypeCtor::FnPtr { num_args },
115 Substs::builder(num_args as usize + 1).fill_with_bound_vars(0).build(),
116 );
117
118 let self_ty = Ty::apply_one(TypeCtor::Closure { def: data.def, expr: data.expr }, sig_ty);
119
120 let trait_ref = TraitRef {
121 trait_: trait_.into(),
122 substs: Substs::build_for_def(db, trait_).push(self_ty).push(arg_ty).build(),
123 };
124
125 let output_ty_id = AssocTyValue::ClosureFnTraitImplOutput(data.clone());
126
127 BuiltinImplData {
128 num_vars: num_args as usize + 1,
129 trait_ref,
130 where_clauses: Vec::new(),
131 assoc_ty_values: vec![output_ty_id],
132 }
133}
134
135fn closure_fn_trait_output_assoc_ty_value(
136 db: &impl HirDatabase,
137 krate: CrateId,
138 data: super::ClosureFnTraitImplData,
139) -> BuiltinImplAssocTyValueData {
140 let impl_ = Impl::ClosureFnTraitImpl(data.clone());
141
142 let num_args: u16 = match &db.body(data.def.into())[data.expr] {
143 Expr::Lambda { args, .. } => args.len() as u16,
144 _ => {
145 log::warn!("closure for closure type {:?} not found", data);
146 0
147 }
148 };
149
150 let output_ty = Ty::Bound(num_args.into());
151
152 let fn_once_trait =
153 get_fn_trait(db, krate, super::FnTrait::FnOnce).expect("assoc ty value should not exist");
154
155 let output_ty_id = db
156 .trait_data(fn_once_trait)
157 .associated_type_by_name(&name![Output])
158 .expect("assoc ty value should not exist");
159
160 BuiltinImplAssocTyValueData {
161 impl_,
162 assoc_ty_id: output_ty_id,
163 num_vars: num_args as usize + 1,
164 value: output_ty,
165 }
166}
167
168fn get_fn_trait(
169 db: &impl HirDatabase,
170 krate: CrateId,
171 fn_trait: super::FnTrait,
172) -> Option<TraitId> {
173 let target = db.lang_item(krate, fn_trait.lang_item_name().into())?;
174 match target {
175 LangItemTarget::TraitId(t) => Some(t),
176 _ => None,
177 }
178}