aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2020-04-12 11:28:24 +0100
committerFlorian Diebold <[email protected]>2020-04-13 14:57:28 +0100
commitc8b2ec8c20be44ae19d15e90ff812745f029899e (patch)
tree98ed585238a37d722159a489db24e0514ae562ce
parentc388130f5ffbcbe7d3131213a24d12d02f769b87 (diff)
Add support for bounds on associated types in trait definitions
E.g. ``` trait Trait { type Item: SomeOtherTrait; } ``` Note that these don't simply desugar to where clauses; as I understand it, where clauses have to be proved by the *user* of the trait, but these bounds are proved by the *implementor*. (Also, where clauses on associated types are unstable.)
-rw-r--r--crates/ra_hir_def/src/data.rs18
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs27
-rw-r--r--crates/ra_hir_ty/src/traits.rs7
-rw-r--r--crates/ra_hir_ty/src/traits/chalk.rs72
4 files changed, 113 insertions, 11 deletions
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs
index 56a20c5bd..5dfde75d9 100644
--- a/crates/ra_hir_def/src/data.rs
+++ b/crates/ra_hir_def/src/data.rs
@@ -9,7 +9,8 @@ use hir_expand::{
9}; 9};
10use ra_prof::profile; 10use ra_prof::profile;
11use ra_syntax::ast::{ 11use ra_syntax::ast::{
12 self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner, VisibilityOwner, 12 self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner, TypeBoundsOwner,
13 VisibilityOwner,
13}; 14};
14 15
15use crate::{ 16use crate::{
@@ -106,6 +107,7 @@ pub struct TypeAliasData {
106 pub name: Name, 107 pub name: Name,
107 pub type_ref: Option<TypeRef>, 108 pub type_ref: Option<TypeRef>,
108 pub visibility: RawVisibility, 109 pub visibility: RawVisibility,
110 pub bounds: Vec<TypeBound>,
109} 111}
110 112
111impl TypeAliasData { 113impl TypeAliasData {
@@ -118,9 +120,17 @@ impl TypeAliasData {
118 let name = node.value.name().map_or_else(Name::missing, |n| n.as_name()); 120 let name = node.value.name().map_or_else(Name::missing, |n| n.as_name());
119 let type_ref = node.value.type_ref().map(TypeRef::from_ast); 121 let type_ref = node.value.type_ref().map(TypeRef::from_ast);
120 let vis_default = RawVisibility::default_for_container(loc.container); 122 let vis_default = RawVisibility::default_for_container(loc.container);
121 let visibility = 123 let visibility = RawVisibility::from_ast_with_default(
122 RawVisibility::from_ast_with_default(db, vis_default, node.map(|n| n.visibility())); 124 db,
123 Arc::new(TypeAliasData { name, type_ref, visibility }) 125 vis_default,
126 node.as_ref().map(|n| n.visibility()),
127 );
128 let bounds = if let Some(bound_list) = node.value.type_bound_list() {
129 bound_list.bounds().map(TypeBound::from_ast).collect()
130 } else {
131 Vec::new()
132 };
133 Arc::new(TypeAliasData { name, type_ref, visibility, bounds })
124 } 134 }
125} 135}
126 136
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index 22ae6ca90..af8c63d64 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -2023,6 +2023,33 @@ fn main() {
2023} 2023}
2024 2024
2025#[test] 2025#[test]
2026fn associated_type_bound() {
2027 let t = type_at(
2028 r#"
2029//- /main.rs
2030pub trait Trait {
2031 type Item: OtherTrait<u32>;
2032}
2033pub trait OtherTrait<T> {
2034 fn foo(&self) -> T;
2035}
2036
2037// this is just a workaround for chalk#234
2038pub struct S<T>;
2039impl<T: Trait> Trait for S<T> {
2040 type Item = <T as Trait>::Item;
2041}
2042
2043fn test<T: Trait>() {
2044 let y: <S<T> as Trait>::Item = no_matter;
2045 y.foo()<|>;
2046}
2047"#,
2048 );
2049 assert_eq!(t, "u32");
2050}
2051
2052#[test]
2026fn dyn_trait_through_chalk() { 2053fn dyn_trait_through_chalk() {
2027 let t = type_at( 2054 let t = type_at(
2028 r#" 2055 r#"
diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs
index 43d8d1e80..44fbdb197 100644
--- a/crates/ra_hir_ty/src/traits.rs
+++ b/crates/ra_hir_ty/src/traits.rs
@@ -194,13 +194,16 @@ fn solve(
194 } 194 }
195 remaining > 0 195 remaining > 0
196 }; 196 };
197 let mut solve = || solver.solve_limited(&context, goal, should_continue); 197 let mut solve = || {
198 let solution = solver.solve_limited(&context, goal, should_continue);
199 log::debug!("solve({:?}) => {:?}", goal, solution);
200 solution
201 };
198 // don't set the TLS for Chalk unless Chalk debugging is active, to make 202 // don't set the TLS for Chalk unless Chalk debugging is active, to make
199 // extra sure we only use it for debugging 203 // extra sure we only use it for debugging
200 let solution = 204 let solution =
201 if is_chalk_debug() { chalk::tls::set_current_program(db, solve) } else { solve() }; 205 if is_chalk_debug() { chalk::tls::set_current_program(db, solve) } else { solve() };
202 206
203 log::debug!("solve({:?}) => {:?}", goal, solution);
204 solution 207 solution
205} 208}
206 209
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs
index e05fea843..12ffa69a0 100644
--- a/crates/ra_hir_ty/src/traits/chalk.rs
+++ b/crates/ra_hir_ty/src/traits/chalk.rs
@@ -626,6 +626,55 @@ fn convert_where_clauses(
626 result 626 result
627} 627}
628 628
629fn generic_predicate_to_inline_bound(
630 db: &dyn HirDatabase,
631 pred: &GenericPredicate,
632 self_ty: &Ty,
633) -> Option<chalk_rust_ir::InlineBound<Interner>> {
634 // An InlineBound is like a GenericPredicate, except the self type is left out.
635 // We don't have a special type for this, but Chalk does.
636 match pred {
637 GenericPredicate::Implemented(trait_ref) => {
638 if &trait_ref.substs[0] != self_ty {
639 // we can only convert predicates back to type bounds if they
640 // have the expected self type
641 return None;
642 }
643 let args_no_self = trait_ref.substs[1..]
644 .iter()
645 .map(|ty| ty.clone().to_chalk(db).cast(&Interner))
646 .collect();
647 let trait_bound =
648 chalk_rust_ir::TraitBound { trait_id: trait_ref.trait_.to_chalk(db), args_no_self };
649 Some(chalk_rust_ir::InlineBound::TraitBound(trait_bound))
650 }
651 GenericPredicate::Projection(proj) => {
652 if &proj.projection_ty.parameters[0] != self_ty {
653 return None;
654 }
655 let trait_ = match proj.projection_ty.associated_ty.lookup(db.upcast()).container {
656 AssocContainerId::TraitId(t) => t,
657 _ => panic!("associated type not in trait"),
658 };
659 let args_no_self = proj.projection_ty.parameters[1..]
660 .iter()
661 .map(|ty| ty.clone().to_chalk(db).cast(&Interner))
662 .collect();
663 let alias_eq_bound = chalk_rust_ir::AliasEqBound {
664 value: proj.ty.clone().to_chalk(db),
665 trait_bound: chalk_rust_ir::TraitBound {
666 trait_id: trait_.to_chalk(db),
667 args_no_self,
668 },
669 associated_ty_id: proj.projection_ty.associated_ty.to_chalk(db),
670 parameters: Vec::new(), // FIXME we don't support generic associated types yet
671 };
672 Some(chalk_rust_ir::InlineBound::AliasEqBound(alias_eq_bound))
673 }
674 GenericPredicate::Error => None,
675 }
676}
677
629impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { 678impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
630 fn associated_ty_data(&self, id: AssocTypeId) -> Arc<AssociatedTyDatum> { 679 fn associated_ty_data(&self, id: AssocTypeId) -> Arc<AssociatedTyDatum> {
631 self.db.associated_ty_data(id) 680 self.db.associated_ty_data(id)
@@ -708,12 +757,25 @@ pub(crate) fn associated_ty_data_query(
708 AssocContainerId::TraitId(t) => t, 757 AssocContainerId::TraitId(t) => t,
709 _ => panic!("associated type not in trait"), 758 _ => panic!("associated type not in trait"),
710 }; 759 };
760
761 // Lower bounds -- we could/should maybe move this to a separate query in `lower`
762 let type_alias_data = db.type_alias_data(type_alias);
711 let generic_params = generics(db.upcast(), type_alias.into()); 763 let generic_params = generics(db.upcast(), type_alias.into());
712 let bound_data = chalk_rust_ir::AssociatedTyDatumBound { 764 let bound_vars = Substs::bound_vars(&generic_params);
713 // FIXME add bounds and where clauses 765 let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast());
714 bounds: vec![], 766 let ctx = crate::TyLoweringContext::new(db, &resolver)
715 where_clauses: vec![], 767 .with_type_param_mode(crate::lower::TypeParamLoweringMode::Variable);
716 }; 768 let self_ty = Ty::Bound(crate::BoundVar::new(crate::DebruijnIndex::INNERMOST, 0));
769 let bounds = type_alias_data
770 .bounds
771 .iter()
772 .flat_map(|bound| GenericPredicate::from_type_bound(&ctx, bound, self_ty.clone()))
773 .filter_map(|pred| generic_predicate_to_inline_bound(db, &pred, &self_ty))
774 .map(|bound| make_binders(bound.shifted_in(&Interner), 0))
775 .collect();
776
777 let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars);
778 let bound_data = chalk_rust_ir::AssociatedTyDatumBound { bounds, where_clauses };
717 let datum = AssociatedTyDatum { 779 let datum = AssociatedTyDatum {
718 trait_id: trait_.to_chalk(db), 780 trait_id: trait_.to_chalk(db),
719 id, 781 id,