From 3b06faad26fdb1485c542970374ff8bbcb0e01c4 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Mon, 9 Sep 2019 22:10:58 +0200 Subject: Make closures impl closure traits --- crates/ra_hir/src/db.rs | 6 +- crates/ra_hir/src/ty/tests.rs | 55 ++++++++------- crates/ra_hir/src/ty/traits.rs | 36 +++++++++- crates/ra_hir/src/ty/traits/chalk.rs | 132 ++++++++++++++++++++++++++++++++--- 4 files changed, 188 insertions(+), 41 deletions(-) (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 05259dcbb..2b20ae02b 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -13,8 +13,8 @@ use crate::{ nameres::{CrateDefMap, ImportSourceMap, Namespace, RawItems}, traits::TraitData, ty::{ - method_resolution::CrateImplBlocks, CallableDef, FnSig, GenericPredicate, InferenceResult, - Substs, Ty, TypableDef, TypeCtor, + method_resolution::CrateImplBlocks, traits::Impl, CallableDef, FnSig, GenericPredicate, + InferenceResult, Substs, Ty, TypableDef, TypeCtor, }, type_alias::TypeAliasData, AstIdMap, Const, ConstData, Crate, DefWithBody, Enum, ErasedFileAstId, ExprScopes, FnData, @@ -50,7 +50,7 @@ pub trait InternDatabase: SourceDatabase { #[salsa::interned] fn intern_type_ctor(&self, type_ctor: TypeCtor) -> ids::TypeCtorId; #[salsa::interned] - fn intern_impl_block(&self, impl_block: ImplBlock) -> ids::GlobalImplId; + fn intern_impl(&self, impl_: Impl) -> ids::GlobalImplId; } /// This database has access to source code, so queries here are not really diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index eb8770ec4..13090f89d 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3990,6 +3990,7 @@ fn test u128>(f: F) { fn closure_1() { assert_snapshot!( infer(r#" +#[lang = "fn_once"] trait FnOnce { type Output; } @@ -4000,39 +4001,39 @@ impl Option { } fn test() { - let x = Option::Some(1i32); + let x = Option::Some(1u32); x.map(|v| v + 1); x.map(|_v| 1u64); let y: Option = x.map(|_v| 1); } "#), @r###" - [128; 132) 'self': Option - [134; 135) 'f': F - [145; 147) '{}': () - [161; 280) '{ ... 1); }': () - [171; 172) 'x': Option - [175; 187) 'Option::Some': Some(T) -> Option - [175; 193) 'Option...(1i32)': Option - [188; 192) '1i32': i32 - [199; 200) 'x': Option - [199; 215) 'x.map(...v + 1)': {unknown} - [205; 214) '|v| v + 1': |{unknown}| -> i32 - [206; 207) 'v': {unknown} - [209; 210) 'v': {unknown} - [209; 214) 'v + 1': i32 - [213; 214) '1': i32 - [221; 222) 'x': Option - [221; 237) 'x.map(... 1u64)': {unknown} - [227; 236) '|_v| 1u64': |{unknown}| -> u64 - [228; 230) '_v': {unknown} - [232; 236) '1u64': u64 - [247; 248) 'y': Option - [264; 265) 'x': Option - [264; 277) 'x.map(|_v| 1)': Option - [270; 276) '|_v| 1': |{unknown}| -> i32 - [271; 273) '_v': {unknown} - [275; 276) '1': i32 + [148; 152) 'self': Option + [154; 155) 'f': F + [165; 167) '{}': () + [181; 300) '{ ... 1); }': () + [191; 192) 'x': Option + [195; 207) 'Option::Some': Some(T) -> Option + [195; 213) 'Option...(1u32)': Option + [208; 212) '1u32': u32 + [219; 220) 'x': Option + [219; 235) 'x.map(...v + 1)': {unknown} + [225; 234) '|v| v + 1': |u32| -> i32 + [226; 227) 'v': u32 + [229; 230) 'v': u32 + [229; 234) 'v + 1': i32 + [233; 234) '1': i32 + [241; 242) 'x': Option + [241; 257) 'x.map(... 1u64)': {unknown} + [247; 256) '|_v| 1u64': |u32| -> u64 + [248; 250) '_v': u32 + [252; 256) '1u64': u64 + [267; 268) 'y': Option + [284; 285) 'x': Option + [284; 297) 'x.map(|_v| 1)': Option + [290; 296) '|_v| 1': |u32| -> i32 + [291; 293) '_v': u32 + [295; 296) '1': i32 "### ); } diff --git a/crates/ra_hir/src/ty/traits.rs b/crates/ra_hir/src/ty/traits.rs index c0c132809..8e256341d 100644 --- a/crates/ra_hir/src/ty/traits.rs +++ b/crates/ra_hir/src/ty/traits.rs @@ -9,7 +9,7 @@ use ra_prof::profile; use rustc_hash::FxHashSet; use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk}; -use crate::{db::HirDatabase, Crate, ImplBlock, Trait}; +use crate::{db::HirDatabase, expr::ExprId, Crate, DefWithBody, ImplBlock, Trait}; use self::chalk::{from_chalk, ToChalk}; @@ -252,3 +252,37 @@ pub enum Guidance { /// There's no useful information to feed back to type inference Unknown, } + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum FnTrait { + FnOnce, + FnMut, + Fn, +} + +impl FnTrait { + fn lang_item_name(self) -> &'static str { + match self { + FnTrait::FnOnce => "fn_once", + FnTrait::FnMut => "fn_mut", + FnTrait::Fn => "fn", + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ClosureFnTraitImplData { + def: DefWithBody, + expr: ExprId, + fn_trait: FnTrait, +} + +/// An impl. Usually this comes from an impl block, but some built-in types get +/// synthetic impls. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Impl { + /// A normal impl from an impl block. + ImplBlock(ImplBlock), + /// Closure types implement the Fn traits synthetically. + ClosureFnTraitImpl(ClosureFnTraitImplData), +} diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index 34e623931..f229b1aef 100644 --- a/crates/ra_hir/src/ty/traits/chalk.rs +++ b/crates/ra_hir/src/ty/traits/chalk.rs @@ -12,7 +12,7 @@ use chalk_rust_ir::{AssociatedTyDatum, ImplDatum, StructDatum, TraitDatum}; use ra_db::salsa::{InternId, InternKey}; use test_utils::tested_by; -use super::{Canonical, ChalkContext, Obligation}; +use super::{Canonical, ChalkContext, Impl, Obligation}; use crate::{ db::HirDatabase, generics::GenericDef, @@ -111,7 +111,7 @@ impl ToChalk for Ty { } chalk_ir::Ty::ForAll(_) => unimplemented!(), chalk_ir::Ty::BoundVar(idx) => Ty::Bound(idx as u32), - chalk_ir::Ty::InferenceVar(_iv) => panic!("unexpected chalk infer ty"), + chalk_ir::Ty::InferenceVar(_iv) => Ty::Unknown, } } } @@ -175,15 +175,15 @@ impl ToChalk for TypeCtor { } } -impl ToChalk for ImplBlock { +impl ToChalk for Impl { type Chalk = chalk_ir::ImplId; fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::ImplId { - db.intern_impl_block(self).into() + db.intern_impl(self).into() } - fn from_chalk(db: &impl HirDatabase, impl_id: chalk_ir::ImplId) -> ImplBlock { - db.lookup_intern_impl_block(impl_id.into()) + fn from_chalk(db: &impl HirDatabase, impl_id: chalk_ir::ImplId) -> Impl { + db.lookup_intern_impl(impl_id.into()) } } @@ -388,19 +388,36 @@ where fn impls_for_trait( &self, trait_id: chalk_ir::TraitId, - _parameters: &[Parameter], + parameters: &[Parameter], ) -> Vec { debug!("impls_for_trait {:?}", trait_id); if trait_id == UNKNOWN_TRAIT { return Vec::new(); } let trait_: Trait = from_chalk(self.db, trait_id); - let result: Vec<_> = self + let mut result: Vec<_> = self .db .impls_for_trait(self.krate, trait_) .iter() - .map(|impl_block| impl_block.to_chalk(self.db)) + .copied() + .map(Impl::ImplBlock) + .map(|impl_| impl_.to_chalk(self.db)) .collect(); + + let ty: Ty = from_chalk(self.db, parameters[0].assert_ty_ref().clone()); + if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { def, expr }, .. }) = ty { + for fn_trait in + [super::FnTrait::FnOnce, super::FnTrait::FnMut, super::FnTrait::Fn].iter().copied() + { + if let Some(actual_trait) = get_fn_trait(self.db, self.krate, fn_trait) { + if trait_ == actual_trait { + let impl_ = super::ClosureFnTraitImplData { def, expr, fn_trait }; + result.push(Impl::ClosureFnTraitImpl(impl_).to_chalk(self.db)); + } + } + } + } + debug!("impls_for_trait returned {} impls", result.len()); result } @@ -602,7 +619,21 @@ pub(crate) fn impl_datum_query( ) -> Arc { let _p = ra_prof::profile("impl_datum"); debug!("impl_datum {:?}", impl_id); - let impl_block: ImplBlock = from_chalk(db, impl_id); + let impl_: Impl = from_chalk(db, impl_id); + match impl_ { + Impl::ImplBlock(impl_block) => impl_block_datum(db, krate, impl_id, impl_block), + Impl::ClosureFnTraitImpl(data) => { + closure_fn_trait_impl_datum(db, krate, impl_id, data).unwrap_or_else(invalid_impl_datum) + } + } +} + +fn impl_block_datum( + db: &impl HirDatabase, + krate: Crate, + impl_id: ImplId, + impl_block: ImplBlock, +) -> Arc { let generic_params = impl_block.generic_params(db); let bound_vars = Substs::bound_vars(&generic_params); let trait_ref = impl_block @@ -661,6 +692,87 @@ pub(crate) fn impl_datum_query( Arc::new(impl_datum) } +fn invalid_impl_datum() -> Arc { + let trait_ref = chalk_ir::TraitRef { + trait_id: UNKNOWN_TRAIT, + parameters: vec![chalk_ir::Ty::BoundVar(0).cast()], + }; + let impl_datum_bound = chalk_rust_ir::ImplDatumBound { + trait_ref: chalk_rust_ir::PolarizedTraitRef::Positive(trait_ref), + where_clauses: Vec::new(), + associated_ty_values: Vec::new(), + impl_type: chalk_rust_ir::ImplType::External, + }; + let impl_datum = ImplDatum { binders: make_binders(impl_datum_bound, 1) }; + Arc::new(impl_datum) +} + +fn closure_fn_trait_impl_datum( + db: &impl HirDatabase, + krate: Crate, + impl_id: ImplId, + data: super::ClosureFnTraitImplData, +) -> Option> { + // for some closure |X, Y| -> Z: + // impl Fn<(T, U)> for closure V> { Output = V } + + let fn_once_trait = get_fn_trait(db, krate, super::FnTrait::FnOnce)?; + let trait_ = get_fn_trait(db, krate, data.fn_trait)?; // get corresponding fn trait + + let num_args: u16 = match &db.body_hir(data.def)[data.expr] { + crate::expr::Expr::Lambda { args, .. } => args.len() as u16, + _ => { + log::warn!("closure for closure type {:?} not found", data); + 0 + } + }; + + let arg_ty = Ty::apply( + TypeCtor::Tuple { cardinality: num_args }, + (0..num_args).map(|i| Ty::Bound(i.into())).collect::>().into(), + ); + let output_ty = Ty::Bound(num_args.into()); + let sig_ty = Ty::apply( + TypeCtor::FnPtr { num_args }, + (0..num_args + 1).map(|i| Ty::Bound(i.into())).collect::>().into(), + ); + + let self_ty = Ty::apply_one(TypeCtor::Closure { def: data.def, expr: data.expr }, sig_ty); + + let trait_ref = TraitRef { trait_, substs: vec![self_ty, arg_ty].into() }; + + let output_ty_id = fn_once_trait.associated_type_by_name(db, &crate::name::OUTPUT_TYPE)?; + + let output_ty_value = chalk_rust_ir::AssociatedTyValue { + associated_ty_id: output_ty_id.to_chalk(db), + impl_id, + value: make_binders( + chalk_rust_ir::AssociatedTyValueBound { ty: output_ty.to_chalk(db) }, + 0, + ), + }; + + let impl_type = chalk_rust_ir::ImplType::External; + + let impl_datum_bound = chalk_rust_ir::ImplDatumBound { + trait_ref: chalk_rust_ir::PolarizedTraitRef::Positive(trait_ref.to_chalk(db)), + where_clauses: Vec::new(), + associated_ty_values: vec![output_ty_value], + impl_type, + }; + let impl_datum = ImplDatum { binders: make_binders(impl_datum_bound, num_args as usize + 1) }; + Some(Arc::new(impl_datum)) +} + +fn get_fn_trait(db: &impl HirDatabase, krate: Crate, fn_trait: super::FnTrait) -> Option { + let lang_items = db.lang_items(krate); + let target = lang_items.target(fn_trait.lang_item_name())?; + match target { + crate::lang_item::LangItemTarget::Trait(t) => Some(*t), + _ => None, + } +} + fn id_from_chalk(chalk_id: chalk_ir::RawId) -> T { T::from_intern_id(InternId::from(chalk_id.index)) } -- cgit v1.2.3