diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-09-24 22:12:26 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2019-09-24 22:12:26 +0100 |
commit | c7420ddaaa76741d1eebe393406b38ba5596e54a (patch) | |
tree | ff9c2f3c665ef95ebf509dc69ff82d39ec42d176 /crates/ra_hir/src/ty/traits/chalk.rs | |
parent | 36fb3f53d712a11b7e3fc4bbd92094d1c8f19522 (diff) | |
parent | 6a8670665032f6103ca14e38ed9106126b20063d (diff) |
Merge #1845
1845: Closure types r=flodiebold a=flodiebold
This adds types for closures and makes them implement the `Fn` traits (we don't currently care or try to infer `Fn` vs. `FnMut` vs. `FnOnce`; this would require move analysis, I think).
This requires some changes in Chalk; one is that we need to know the self type when asked for impls, so we can synthesize `Fn` trait impls for closures; but also there's a problem that prevents us from normalizing the closure output type correctly that I _think_ will be fixed on the Chalk side (basically, we ask too early and try to solve `Normalize(<?1 as FnOnce<(u32,)>>::Output => ?0)` -- note the variable in the self type -- and instead of an ambiguous answer, we get back that it can't be solved, so we don't try again. Niko mentioned he's making all goals where the self type is unconstrained flounder, which I think would mean this would be ambiguous).
Co-authored-by: Florian Diebold <[email protected]>
Diffstat (limited to 'crates/ra_hir/src/ty/traits/chalk.rs')
-rw-r--r-- | crates/ra_hir/src/ty/traits/chalk.rs | 136 |
1 files changed, 126 insertions, 10 deletions
diff --git a/crates/ra_hir/src/ty/traits/chalk.rs b/crates/ra_hir/src/ty/traits/chalk.rs index 462156021..d83706f86 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() | ||
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 | } |
@@ -571,6 +588,10 @@ pub(crate) fn struct_datum_query( | |||
571 | type_alias.krate(db) != Some(krate), | 588 | type_alias.krate(db) != Some(krate), |
572 | ) | 589 | ) |
573 | } | 590 | } |
591 | TypeCtor::Closure { def, .. } => { | ||
592 | let upstream = def.krate(db) != Some(krate); | ||
593 | (1, vec![], upstream) | ||
594 | } | ||
574 | }; | 595 | }; |
575 | let flags = chalk_rust_ir::StructFlags { | 596 | let flags = chalk_rust_ir::StructFlags { |
576 | upstream, | 597 | upstream, |
@@ -598,7 +619,21 @@ pub(crate) fn impl_datum_query( | |||
598 | ) -> Arc<ImplDatum> { | 619 | ) -> Arc<ImplDatum> { |
599 | let _p = ra_prof::profile("impl_datum"); | 620 | let _p = ra_prof::profile("impl_datum"); |
600 | debug!("impl_datum {:?}", impl_id); | 621 | debug!("impl_datum {:?}", impl_id); |
601 | 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> { | ||
602 | let generic_params = impl_block.generic_params(db); | 637 | let generic_params = impl_block.generic_params(db); |
603 | let bound_vars = Substs::bound_vars(&generic_params); | 638 | let bound_vars = Substs::bound_vars(&generic_params); |
604 | let trait_ref = impl_block | 639 | let trait_ref = impl_block |
@@ -657,6 +692,87 @@ pub(crate) fn impl_datum_query( | |||
657 | Arc::new(impl_datum) | 692 | Arc::new(impl_datum) |
658 | } | 693 | } |
659 | 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 | |||
660 | 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 { |
661 | T::from_intern_id(InternId::from(chalk_id.index)) | 777 | T::from_intern_id(InternId::from(chalk_id.index)) |
662 | } | 778 | } |