diff options
author | Florian Diebold <[email protected]> | 2019-12-03 11:16:39 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2019-12-03 11:30:50 +0000 |
commit | 176207f1e87bb1f2c70529cdbc66ae8c96584b03 (patch) | |
tree | dca2b8617d47a9fb498c153ae291c5eedffe7dd1 /crates/ra_hir_ty/src/traits/builtin.rs | |
parent | 3376c08052a563a5d2db487c458972378edebf44 (diff) |
Extract built-in trait implementations to separate module
This untangles the builtin logic from the Chalk translation.
Diffstat (limited to 'crates/ra_hir_ty/src/traits/builtin.rs')
-rw-r--r-- | crates/ra_hir_ty/src/traits/builtin.rs | 161 |
1 files changed, 161 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..598fd81e3 --- /dev/null +++ b/crates/ra_hir_ty/src/traits/builtin.rs | |||
@@ -0,0 +1,161 @@ | |||
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; | ||
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 | if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { def, expr }, .. }) = ty { | ||
32 | for &fn_trait in [super::FnTrait::FnOnce, super::FnTrait::FnMut, super::FnTrait::Fn].iter() | ||
33 | { | ||
34 | if let Some(actual_trait) = get_fn_trait(db, krate, fn_trait) { | ||
35 | if trait_ == actual_trait { | ||
36 | let impl_ = super::ClosureFnTraitImplData { def: *def, expr: *expr, fn_trait }; | ||
37 | callback(Impl::ClosureFnTraitImpl(impl_)); | ||
38 | } | ||
39 | } | ||
40 | } | ||
41 | } | ||
42 | } | ||
43 | |||
44 | pub(super) fn impl_datum( | ||
45 | db: &impl HirDatabase, | ||
46 | krate: CrateId, | ||
47 | impl_: Impl, | ||
48 | ) -> Option<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 closure_fn_trait_impl_datum( | ||
69 | db: &impl HirDatabase, | ||
70 | krate: CrateId, | ||
71 | data: super::ClosureFnTraitImplData, | ||
72 | ) -> Option<BuiltinImplData> { | ||
73 | // for some closure |X, Y| -> Z: | ||
74 | // impl<T, U, V> Fn<(T, U)> for closure<fn(T, U) -> V> { Output = V } | ||
75 | |||
76 | let trait_ = get_fn_trait(db, krate, data.fn_trait)?; // get corresponding fn trait | ||
77 | |||
78 | // validate FnOnce trait, since we need it in the assoc ty value definition | ||
79 | // and don't want to return a valid value only to find out later that FnOnce | ||
80 | // is broken | ||
81 | let fn_once_trait = get_fn_trait(db, krate, super::FnTrait::FnOnce)?; | ||
82 | let _output = db.trait_data(fn_once_trait).associated_type_by_name(&name::OUTPUT_TYPE)?; | ||
83 | |||
84 | let num_args: u16 = match &db.body(data.def.into())[data.expr] { | ||
85 | Expr::Lambda { args, .. } => args.len() as u16, | ||
86 | _ => { | ||
87 | log::warn!("closure for closure type {:?} not found", data); | ||
88 | 0 | ||
89 | } | ||
90 | }; | ||
91 | |||
92 | let arg_ty = Ty::apply( | ||
93 | TypeCtor::Tuple { cardinality: num_args }, | ||
94 | Substs::builder(num_args as usize).fill_with_bound_vars(0).build(), | ||
95 | ); | ||
96 | let sig_ty = Ty::apply( | ||
97 | TypeCtor::FnPtr { num_args }, | ||
98 | Substs::builder(num_args as usize + 1).fill_with_bound_vars(0).build(), | ||
99 | ); | ||
100 | |||
101 | let self_ty = Ty::apply_one(TypeCtor::Closure { def: data.def, expr: data.expr }, sig_ty); | ||
102 | |||
103 | let trait_ref = TraitRef { | ||
104 | trait_: trait_.into(), | ||
105 | substs: Substs::build_for_def(db, trait_).push(self_ty).push(arg_ty).build(), | ||
106 | }; | ||
107 | |||
108 | let output_ty_id = AssocTyValue::ClosureFnTraitImplOutput(data.clone()); | ||
109 | |||
110 | Some(BuiltinImplData { | ||
111 | num_vars: num_args as usize + 1, | ||
112 | trait_ref, | ||
113 | where_clauses: Vec::new(), | ||
114 | assoc_ty_values: vec![output_ty_id], | ||
115 | }) | ||
116 | } | ||
117 | |||
118 | fn closure_fn_trait_output_assoc_ty_value( | ||
119 | db: &impl HirDatabase, | ||
120 | krate: CrateId, | ||
121 | data: super::ClosureFnTraitImplData, | ||
122 | ) -> BuiltinImplAssocTyValueData { | ||
123 | let impl_ = Impl::ClosureFnTraitImpl(data.clone()); | ||
124 | |||
125 | let num_args: u16 = match &db.body(data.def.into())[data.expr] { | ||
126 | Expr::Lambda { args, .. } => args.len() as u16, | ||
127 | _ => { | ||
128 | log::warn!("closure for closure type {:?} not found", data); | ||
129 | 0 | ||
130 | } | ||
131 | }; | ||
132 | |||
133 | let output_ty = Ty::Bound(num_args.into()); | ||
134 | |||
135 | let fn_once_trait = | ||
136 | get_fn_trait(db, krate, super::FnTrait::FnOnce).expect("assoc ty value should not exist"); | ||
137 | |||
138 | let output_ty_id = db | ||
139 | .trait_data(fn_once_trait) | ||
140 | .associated_type_by_name(&name::OUTPUT_TYPE) | ||
141 | .expect("assoc ty value should not exist"); | ||
142 | |||
143 | BuiltinImplAssocTyValueData { | ||
144 | impl_, | ||
145 | assoc_ty_id: output_ty_id, | ||
146 | num_vars: num_args as usize + 1, | ||
147 | value: output_ty, | ||
148 | } | ||
149 | } | ||
150 | |||
151 | fn get_fn_trait( | ||
152 | db: &impl HirDatabase, | ||
153 | krate: CrateId, | ||
154 | fn_trait: super::FnTrait, | ||
155 | ) -> Option<TraitId> { | ||
156 | let target = db.lang_item(krate, fn_trait.lang_item_name().into())?; | ||
157 | match target { | ||
158 | LangItemTarget::TraitId(t) => Some(t), | ||
159 | _ => None, | ||
160 | } | ||
161 | } | ||