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.rs161
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`.
3use hir_def::{expr::Expr, lang_item::LangItemTarget, TraitId, TypeAliasId};
4use hir_expand::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 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
44pub(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
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 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
118fn 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
151fn 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}