diff options
author | Florian Diebold <[email protected]> | 2019-09-09 21:10:58 +0100 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2019-09-24 22:05:12 +0100 |
commit | 3b06faad26fdb1485c542970374ff8bbcb0e01c4 (patch) | |
tree | 370ec4c128b6160b2fd72351eb3875f04e3a94fd /crates/ra_hir | |
parent | 619a8185a607b216c64b58d230c3949ccef98a37 (diff) |
Make closures impl closure traits
Diffstat (limited to 'crates/ra_hir')
-rw-r--r-- | crates/ra_hir/src/db.rs | 6 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 55 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/traits.rs | 36 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/traits/chalk.rs | 132 |
4 files changed, 188 insertions, 41 deletions
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::{ | |||
13 | nameres::{CrateDefMap, ImportSourceMap, Namespace, RawItems}, | 13 | nameres::{CrateDefMap, ImportSourceMap, Namespace, RawItems}, |
14 | traits::TraitData, | 14 | traits::TraitData, |
15 | ty::{ | 15 | ty::{ |
16 | method_resolution::CrateImplBlocks, CallableDef, FnSig, GenericPredicate, InferenceResult, | 16 | method_resolution::CrateImplBlocks, traits::Impl, CallableDef, FnSig, GenericPredicate, |
17 | Substs, Ty, TypableDef, TypeCtor, | 17 | InferenceResult, Substs, Ty, TypableDef, TypeCtor, |
18 | }, | 18 | }, |
19 | type_alias::TypeAliasData, | 19 | type_alias::TypeAliasData, |
20 | AstIdMap, Const, ConstData, Crate, DefWithBody, Enum, ErasedFileAstId, ExprScopes, FnData, | 20 | AstIdMap, Const, ConstData, Crate, DefWithBody, Enum, ErasedFileAstId, ExprScopes, FnData, |
@@ -50,7 +50,7 @@ pub trait InternDatabase: SourceDatabase { | |||
50 | #[salsa::interned] | 50 | #[salsa::interned] |
51 | fn intern_type_ctor(&self, type_ctor: TypeCtor) -> ids::TypeCtorId; | 51 | fn intern_type_ctor(&self, type_ctor: TypeCtor) -> ids::TypeCtorId; |
52 | #[salsa::interned] | 52 | #[salsa::interned] |
53 | fn intern_impl_block(&self, impl_block: ImplBlock) -> ids::GlobalImplId; | 53 | fn intern_impl(&self, impl_: Impl) -> ids::GlobalImplId; |
54 | } | 54 | } |
55 | 55 | ||
56 | /// This database has access to source code, so queries here are not really | 56 | /// 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<F: FnOnce(u32, u64) -> u128>(f: F) { | |||
3990 | fn closure_1() { | 3990 | fn closure_1() { |
3991 | assert_snapshot!( | 3991 | assert_snapshot!( |
3992 | infer(r#" | 3992 | infer(r#" |
3993 | #[lang = "fn_once"] | ||
3993 | trait FnOnce<Args> { | 3994 | trait FnOnce<Args> { |
3994 | type Output; | 3995 | type Output; |
3995 | } | 3996 | } |
@@ -4000,39 +4001,39 @@ impl<T> Option<T> { | |||
4000 | } | 4001 | } |
4001 | 4002 | ||
4002 | fn test() { | 4003 | fn test() { |
4003 | let x = Option::Some(1i32); | 4004 | let x = Option::Some(1u32); |
4004 | x.map(|v| v + 1); | 4005 | x.map(|v| v + 1); |
4005 | x.map(|_v| 1u64); | 4006 | x.map(|_v| 1u64); |
4006 | let y: Option<i64> = x.map(|_v| 1); | 4007 | let y: Option<i64> = x.map(|_v| 1); |
4007 | } | 4008 | } |
4008 | "#), | 4009 | "#), |
4009 | @r###" | 4010 | @r###" |
4010 | [128; 132) 'self': Option<T> | 4011 | [148; 152) 'self': Option<T> |
4011 | [134; 135) 'f': F | 4012 | [154; 155) 'f': F |
4012 | [145; 147) '{}': () | 4013 | [165; 167) '{}': () |
4013 | [161; 280) '{ ... 1); }': () | 4014 | [181; 300) '{ ... 1); }': () |
4014 | [171; 172) 'x': Option<i32> | 4015 | [191; 192) 'x': Option<u32> |
4015 | [175; 187) 'Option::Some': Some<i32>(T) -> Option<T> | 4016 | [195; 207) 'Option::Some': Some<u32>(T) -> Option<T> |
4016 | [175; 193) 'Option...(1i32)': Option<i32> | 4017 | [195; 213) 'Option...(1u32)': Option<u32> |
4017 | [188; 192) '1i32': i32 | 4018 | [208; 212) '1u32': u32 |
4018 | [199; 200) 'x': Option<i32> | 4019 | [219; 220) 'x': Option<u32> |
4019 | [199; 215) 'x.map(...v + 1)': {unknown} | 4020 | [219; 235) 'x.map(...v + 1)': {unknown} |
4020 | [205; 214) '|v| v + 1': |{unknown}| -> i32 | 4021 | [225; 234) '|v| v + 1': |u32| -> i32 |
4021 | [206; 207) 'v': {unknown} | 4022 | [226; 227) 'v': u32 |
4022 | [209; 210) 'v': {unknown} | 4023 | [229; 230) 'v': u32 |
4023 | [209; 214) 'v + 1': i32 | 4024 | [229; 234) 'v + 1': i32 |
4024 | [213; 214) '1': i32 | 4025 | [233; 234) '1': i32 |
4025 | [221; 222) 'x': Option<i32> | 4026 | [241; 242) 'x': Option<u32> |
4026 | [221; 237) 'x.map(... 1u64)': {unknown} | 4027 | [241; 257) 'x.map(... 1u64)': {unknown} |
4027 | [227; 236) '|_v| 1u64': |{unknown}| -> u64 | 4028 | [247; 256) '|_v| 1u64': |u32| -> u64 |
4028 | [228; 230) '_v': {unknown} | 4029 | [248; 250) '_v': u32 |
4029 | [232; 236) '1u64': u64 | 4030 | [252; 256) '1u64': u64 |
4030 | [247; 248) 'y': Option<i64> | 4031 | [267; 268) 'y': Option<i64> |
4031 | [264; 265) 'x': Option<i32> | 4032 | [284; 285) 'x': Option<u32> |
4032 | [264; 277) 'x.map(|_v| 1)': Option<i64> | 4033 | [284; 297) 'x.map(|_v| 1)': Option<i64> |
4033 | [270; 276) '|_v| 1': |{unknown}| -> i32 | 4034 | [290; 296) '|_v| 1': |u32| -> i32 |
4034 | [271; 273) '_v': {unknown} | 4035 | [291; 293) '_v': u32 |
4035 | [275; 276) '1': i32 | 4036 | [295; 296) '1': i32 |
4036 | "### | 4037 | "### |
4037 | ); | 4038 | ); |
4038 | } | 4039 | } |
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; | |||
9 | use rustc_hash::FxHashSet; | 9 | use rustc_hash::FxHashSet; |
10 | 10 | ||
11 | use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk}; | 11 | use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk}; |
12 | use crate::{db::HirDatabase, Crate, ImplBlock, Trait}; | 12 | use crate::{db::HirDatabase, expr::ExprId, Crate, DefWithBody, ImplBlock, Trait}; |
13 | 13 | ||
14 | use self::chalk::{from_chalk, ToChalk}; | 14 | use self::chalk::{from_chalk, ToChalk}; |
15 | 15 | ||
@@ -252,3 +252,37 @@ pub enum Guidance { | |||
252 | /// There's no useful information to feed back to type inference | 252 | /// There's no useful information to feed back to type inference |
253 | Unknown, | 253 | Unknown, |
254 | } | 254 | } |
255 | |||
256 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||
257 | pub enum FnTrait { | ||
258 | FnOnce, | ||
259 | FnMut, | ||
260 | Fn, | ||
261 | } | ||
262 | |||
263 | impl FnTrait { | ||
264 | fn lang_item_name(self) -> &'static str { | ||
265 | match self { | ||
266 | FnTrait::FnOnce => "fn_once", | ||
267 | FnTrait::FnMut => "fn_mut", | ||
268 | FnTrait::Fn => "fn", | ||
269 | } | ||
270 | } | ||
271 | } | ||
272 | |||
273 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
274 | pub struct ClosureFnTraitImplData { | ||
275 | def: DefWithBody, | ||
276 | expr: ExprId, | ||
277 | fn_trait: FnTrait, | ||
278 | } | ||
279 | |||
280 | /// An impl. Usually this comes from an impl block, but some built-in types get | ||
281 | /// synthetic impls. | ||
282 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
283 | pub enum Impl { | ||
284 | /// A normal impl from an impl block. | ||
285 | ImplBlock(ImplBlock), | ||
286 | /// Closure types implement the Fn traits synthetically. | ||
287 | ClosureFnTraitImpl(ClosureFnTraitImplData), | ||
288 | } | ||
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}; | |||
12 | use ra_db::salsa::{InternId, InternKey}; | 12 | use ra_db::salsa::{InternId, InternKey}; |
13 | use test_utils::tested_by; | 13 | use test_utils::tested_by; |
14 | 14 | ||
15 | use super::{Canonical, ChalkContext, Obligation}; | 15 | use super::{Canonical, ChalkContext, Impl, Obligation}; |
16 | use crate::{ | 16 | use crate::{ |
17 | db::HirDatabase, | 17 | db::HirDatabase, |
18 | generics::GenericDef, | 18 | generics::GenericDef, |
@@ -111,7 +111,7 @@ impl ToChalk for Ty { | |||
111 | } | 111 | } |
112 | chalk_ir::Ty::ForAll(_) => unimplemented!(), | 112 | chalk_ir::Ty::ForAll(_) => unimplemented!(), |
113 | chalk_ir::Ty::BoundVar(idx) => Ty::Bound(idx as u32), | 113 | chalk_ir::Ty::BoundVar(idx) => Ty::Bound(idx as u32), |
114 | chalk_ir::Ty::InferenceVar(_iv) => panic!("unexpected chalk infer ty"), | 114 | chalk_ir::Ty::InferenceVar(_iv) => Ty::Unknown, |
115 | } | 115 | } |
116 | } | 116 | } |
117 | } | 117 | } |
@@ -175,15 +175,15 @@ impl ToChalk for TypeCtor { | |||
175 | } | 175 | } |
176 | } | 176 | } |
177 | 177 | ||
178 | impl ToChalk for ImplBlock { | 178 | impl ToChalk for Impl { |
179 | type Chalk = chalk_ir::ImplId; | 179 | type Chalk = chalk_ir::ImplId; |
180 | 180 | ||
181 | fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::ImplId { | 181 | fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::ImplId { |
182 | db.intern_impl_block(self).into() | 182 | db.intern_impl(self).into() |
183 | } | 183 | } |
184 | 184 | ||
185 | fn from_chalk(db: &impl HirDatabase, impl_id: chalk_ir::ImplId) -> ImplBlock { | 185 | fn from_chalk(db: &impl HirDatabase, impl_id: chalk_ir::ImplId) -> Impl { |
186 | db.lookup_intern_impl_block(impl_id.into()) | 186 | db.lookup_intern_impl(impl_id.into()) |
187 | } | 187 | } |
188 | } | 188 | } |
189 | 189 | ||
@@ -388,19 +388,36 @@ where | |||
388 | fn impls_for_trait( | 388 | fn impls_for_trait( |
389 | &self, | 389 | &self, |
390 | trait_id: chalk_ir::TraitId, | 390 | trait_id: chalk_ir::TraitId, |
391 | _parameters: &[Parameter], | 391 | parameters: &[Parameter], |
392 | ) -> Vec<ImplId> { | 392 | ) -> Vec<ImplId> { |
393 | debug!("impls_for_trait {:?}", trait_id); | 393 | debug!("impls_for_trait {:?}", trait_id); |
394 | if trait_id == UNKNOWN_TRAIT { | 394 | if trait_id == UNKNOWN_TRAIT { |
395 | return Vec::new(); | 395 | return Vec::new(); |
396 | } | 396 | } |
397 | let trait_: Trait = from_chalk(self.db, trait_id); | 397 | let trait_: Trait = from_chalk(self.db, trait_id); |
398 | let result: Vec<_> = self | 398 | let mut result: Vec<_> = self |
399 | .db | 399 | .db |
400 | .impls_for_trait(self.krate, trait_) | 400 | .impls_for_trait(self.krate, trait_) |
401 | .iter() | 401 | .iter() |
402 | .map(|impl_block| impl_block.to_chalk(self.db)) | 402 | .copied() |
403 | .map(Impl::ImplBlock) | ||
404 | .map(|impl_| impl_.to_chalk(self.db)) | ||
403 | .collect(); | 405 | .collect(); |
406 | |||
407 | let ty: Ty = from_chalk(self.db, parameters[0].assert_ty_ref().clone()); | ||
408 | if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { def, expr }, .. }) = ty { | ||
409 | for fn_trait in | ||
410 | [super::FnTrait::FnOnce, super::FnTrait::FnMut, super::FnTrait::Fn].iter().copied() | ||
411 | { | ||
412 | if let Some(actual_trait) = get_fn_trait(self.db, self.krate, fn_trait) { | ||
413 | if trait_ == actual_trait { | ||
414 | let impl_ = super::ClosureFnTraitImplData { def, expr, fn_trait }; | ||
415 | result.push(Impl::ClosureFnTraitImpl(impl_).to_chalk(self.db)); | ||
416 | } | ||
417 | } | ||
418 | } | ||
419 | } | ||
420 | |||
404 | debug!("impls_for_trait returned {} impls", result.len()); | 421 | debug!("impls_for_trait returned {} impls", result.len()); |
405 | result | 422 | result |
406 | } | 423 | } |
@@ -602,7 +619,21 @@ pub(crate) fn impl_datum_query( | |||
602 | ) -> Arc<ImplDatum> { | 619 | ) -> Arc<ImplDatum> { |
603 | let _p = ra_prof::profile("impl_datum"); | 620 | let _p = ra_prof::profile("impl_datum"); |
604 | debug!("impl_datum {:?}", impl_id); | 621 | debug!("impl_datum {:?}", impl_id); |
605 | let impl_block: ImplBlock = from_chalk(db, impl_id); | 622 | let impl_: Impl = from_chalk(db, impl_id); |
623 | match impl_ { | ||
624 | Impl::ImplBlock(impl_block) => impl_block_datum(db, krate, impl_id, impl_block), | ||
625 | Impl::ClosureFnTraitImpl(data) => { | ||
626 | closure_fn_trait_impl_datum(db, krate, impl_id, data).unwrap_or_else(invalid_impl_datum) | ||
627 | } | ||
628 | } | ||
629 | } | ||
630 | |||
631 | fn impl_block_datum( | ||
632 | db: &impl HirDatabase, | ||
633 | krate: Crate, | ||
634 | impl_id: ImplId, | ||
635 | impl_block: ImplBlock, | ||
636 | ) -> Arc<ImplDatum> { | ||
606 | let generic_params = impl_block.generic_params(db); | 637 | let generic_params = impl_block.generic_params(db); |
607 | let bound_vars = Substs::bound_vars(&generic_params); | 638 | let bound_vars = Substs::bound_vars(&generic_params); |
608 | let trait_ref = impl_block | 639 | let trait_ref = impl_block |
@@ -661,6 +692,87 @@ pub(crate) fn impl_datum_query( | |||
661 | Arc::new(impl_datum) | 692 | Arc::new(impl_datum) |
662 | } | 693 | } |
663 | 694 | ||
695 | fn invalid_impl_datum() -> Arc<ImplDatum> { | ||
696 | let trait_ref = chalk_ir::TraitRef { | ||
697 | trait_id: UNKNOWN_TRAIT, | ||
698 | parameters: vec![chalk_ir::Ty::BoundVar(0).cast()], | ||
699 | }; | ||
700 | let impl_datum_bound = chalk_rust_ir::ImplDatumBound { | ||
701 | trait_ref: chalk_rust_ir::PolarizedTraitRef::Positive(trait_ref), | ||
702 | where_clauses: Vec::new(), | ||
703 | associated_ty_values: Vec::new(), | ||
704 | impl_type: chalk_rust_ir::ImplType::External, | ||
705 | }; | ||
706 | let impl_datum = ImplDatum { binders: make_binders(impl_datum_bound, 1) }; | ||
707 | Arc::new(impl_datum) | ||
708 | } | ||
709 | |||
710 | fn closure_fn_trait_impl_datum( | ||
711 | db: &impl HirDatabase, | ||
712 | krate: Crate, | ||
713 | impl_id: ImplId, | ||
714 | data: super::ClosureFnTraitImplData, | ||
715 | ) -> Option<Arc<ImplDatum>> { | ||
716 | // for some closure |X, Y| -> Z: | ||
717 | // impl<T, U, V> Fn<(T, U)> for closure<fn(T, U) -> V> { Output = V } | ||
718 | |||
719 | let fn_once_trait = get_fn_trait(db, krate, super::FnTrait::FnOnce)?; | ||
720 | let trait_ = get_fn_trait(db, krate, data.fn_trait)?; // get corresponding fn trait | ||
721 | |||
722 | let num_args: u16 = match &db.body_hir(data.def)[data.expr] { | ||
723 | crate::expr::Expr::Lambda { args, .. } => args.len() as u16, | ||
724 | _ => { | ||
725 | log::warn!("closure for closure type {:?} not found", data); | ||
726 | 0 | ||
727 | } | ||
728 | }; | ||
729 | |||
730 | let arg_ty = Ty::apply( | ||
731 | TypeCtor::Tuple { cardinality: num_args }, | ||
732 | (0..num_args).map(|i| Ty::Bound(i.into())).collect::<Vec<_>>().into(), | ||
733 | ); | ||
734 | let output_ty = Ty::Bound(num_args.into()); | ||
735 | let sig_ty = Ty::apply( | ||
736 | TypeCtor::FnPtr { num_args }, | ||
737 | (0..num_args + 1).map(|i| Ty::Bound(i.into())).collect::<Vec<_>>().into(), | ||
738 | ); | ||
739 | |||
740 | let self_ty = Ty::apply_one(TypeCtor::Closure { def: data.def, expr: data.expr }, sig_ty); | ||
741 | |||
742 | let trait_ref = TraitRef { trait_, substs: vec![self_ty, arg_ty].into() }; | ||
743 | |||
744 | let output_ty_id = fn_once_trait.associated_type_by_name(db, &crate::name::OUTPUT_TYPE)?; | ||
745 | |||
746 | let output_ty_value = chalk_rust_ir::AssociatedTyValue { | ||
747 | associated_ty_id: output_ty_id.to_chalk(db), | ||
748 | impl_id, | ||
749 | value: make_binders( | ||
750 | chalk_rust_ir::AssociatedTyValueBound { ty: output_ty.to_chalk(db) }, | ||
751 | 0, | ||
752 | ), | ||
753 | }; | ||
754 | |||
755 | let impl_type = chalk_rust_ir::ImplType::External; | ||
756 | |||
757 | let impl_datum_bound = chalk_rust_ir::ImplDatumBound { | ||
758 | trait_ref: chalk_rust_ir::PolarizedTraitRef::Positive(trait_ref.to_chalk(db)), | ||
759 | where_clauses: Vec::new(), | ||
760 | associated_ty_values: vec![output_ty_value], | ||
761 | impl_type, | ||
762 | }; | ||
763 | let impl_datum = ImplDatum { binders: make_binders(impl_datum_bound, num_args as usize + 1) }; | ||
764 | Some(Arc::new(impl_datum)) | ||
765 | } | ||
766 | |||
767 | fn get_fn_trait(db: &impl HirDatabase, krate: Crate, fn_trait: super::FnTrait) -> Option<Trait> { | ||
768 | let lang_items = db.lang_items(krate); | ||
769 | let target = lang_items.target(fn_trait.lang_item_name())?; | ||
770 | match target { | ||
771 | crate::lang_item::LangItemTarget::Trait(t) => Some(*t), | ||
772 | _ => None, | ||
773 | } | ||
774 | } | ||
775 | |||
664 | fn id_from_chalk<T: InternKey>(chalk_id: chalk_ir::RawId) -> T { | 776 | fn id_from_chalk<T: InternKey>(chalk_id: chalk_ir::RawId) -> T { |
665 | T::from_intern_id(InternId::from(chalk_id.index)) | 777 | T::from_intern_id(InternId::from(chalk_id.index)) |
666 | } | 778 | } |