diff options
Diffstat (limited to 'crates/ra_hir_ty/src/traits/builtin.rs')
-rw-r--r-- | crates/ra_hir_ty/src/traits/builtin.rs | 178 |
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`. | ||
3 | use hir_def::{expr::Expr, lang_item::LangItemTarget, TraitId, TypeAliasId}; | ||
4 | use hir_expand::name::name; | ||
5 | use ra_db::CrateId; | ||
6 | |||
7 | use super::{AssocTyValue, Impl}; | ||
8 | use crate::{db::HirDatabase, ApplicationTy, Substs, TraitRef, Ty, TypeCtor}; | ||
9 | |||
10 | pub(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 | |||
17 | pub(super) struct BuiltinImplAssocTyValueData { | ||
18 | pub impl_: Impl, | ||
19 | pub assoc_ty_id: TypeAliasId, | ||
20 | pub num_vars: usize, | ||
21 | pub value: Ty, | ||
22 | } | ||
23 | |||
24 | pub(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 | |||
48 | pub(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 | |||
55 | pub(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 | |||
68 | fn 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 | |||
89 | fn 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 | |||
135 | fn 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 | |||
168 | fn 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 | } | ||