|
| 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 | +} |
0 commit comments