aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2019-09-09 21:10:58 +0100
committerFlorian Diebold <[email protected]>2019-09-24 22:05:12 +0100
commit3b06faad26fdb1485c542970374ff8bbcb0e01c4 (patch)
tree370ec4c128b6160b2fd72351eb3875f04e3a94fd /crates/ra_hir
parent619a8185a607b216c64b58d230c3949ccef98a37 (diff)
Make closures impl closure traits
Diffstat (limited to 'crates/ra_hir')
-rw-r--r--crates/ra_hir/src/db.rs6
-rw-r--r--crates/ra_hir/src/ty/tests.rs55
-rw-r--r--crates/ra_hir/src/ty/traits.rs36
-rw-r--r--crates/ra_hir/src/ty/traits/chalk.rs132
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) {
3990fn closure_1() { 3990fn closure_1() {
3991 assert_snapshot!( 3991 assert_snapshot!(
3992 infer(r#" 3992 infer(r#"
3993#[lang = "fn_once"]
3993trait FnOnce<Args> { 3994trait FnOnce<Args> {
3994 type Output; 3995 type Output;
3995} 3996}
@@ -4000,39 +4001,39 @@ impl<T> Option<T> {
4000} 4001}
4001 4002
4002fn test() { 4003fn 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;
9use rustc_hash::FxHashSet; 9use rustc_hash::FxHashSet;
10 10
11use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk}; 11use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk};
12use crate::{db::HirDatabase, Crate, ImplBlock, Trait}; 12use crate::{db::HirDatabase, expr::ExprId, Crate, DefWithBody, ImplBlock, Trait};
13 13
14use self::chalk::{from_chalk, ToChalk}; 14use 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)]
257pub enum FnTrait {
258 FnOnce,
259 FnMut,
260 Fn,
261}
262
263impl 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)]
274pub 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)]
283pub 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};
12use ra_db::salsa::{InternId, InternKey}; 12use ra_db::salsa::{InternId, InternKey};
13use test_utils::tested_by; 13use test_utils::tested_by;
14 14
15use super::{Canonical, ChalkContext, Obligation}; 15use super::{Canonical, ChalkContext, Impl, Obligation};
16use crate::{ 16use 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
178impl ToChalk for ImplBlock { 178impl 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
631fn 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
695fn 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
710fn 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
767fn 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
664fn id_from_chalk<T: InternKey>(chalk_id: chalk_ir::RawId) -> T { 776fn 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}