Skip to content

Commit c8b2ec8

Browse files
committed
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.)
1 parent c388130 commit c8b2ec8

File tree

4 files changed

+113
-11
lines changed

4 files changed

+113
-11
lines changed

crates/ra_hir_def/src/data.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use hir_expand::{
99
};
1010
use ra_prof::profile;
1111
use ra_syntax::ast::{
12-
self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner, VisibilityOwner,
12+
self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner, TypeBoundsOwner,
13+
VisibilityOwner,
1314
};
1415

1516
use crate::{
@@ -106,6 +107,7 @@ pub struct TypeAliasData {
106107
pub name: Name,
107108
pub type_ref: Option<TypeRef>,
108109
pub visibility: RawVisibility,
110+
pub bounds: Vec<TypeBound>,
109111
}
110112

111113
impl TypeAliasData {
@@ -118,9 +120,17 @@ impl TypeAliasData {
118120
let name = node.value.name().map_or_else(Name::missing, |n| n.as_name());
119121
let type_ref = node.value.type_ref().map(TypeRef::from_ast);
120122
let vis_default = RawVisibility::default_for_container(loc.container);
121-
let visibility =
122-
RawVisibility::from_ast_with_default(db, vis_default, node.map(|n| n.visibility()));
123-
Arc::new(TypeAliasData { name, type_ref, visibility })
123+
let visibility = RawVisibility::from_ast_with_default(
124+
db,
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 })
124134
}
125135
}
126136

crates/ra_hir_ty/src/tests/traits.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2022,6 +2022,33 @@ fn main() {
20222022
);
20232023
}
20242024

2025+
#[test]
2026+
fn associated_type_bound() {
2027+
let t = type_at(
2028+
r#"
2029+
//- /main.rs
2030+
pub trait Trait {
2031+
type Item: OtherTrait<u32>;
2032+
}
2033+
pub trait OtherTrait<T> {
2034+
fn foo(&self) -> T;
2035+
}
2036+
2037+
// this is just a workaround for chalk#234
2038+
pub struct S<T>;
2039+
impl<T: Trait> Trait for S<T> {
2040+
type Item = <T as Trait>::Item;
2041+
}
2042+
2043+
fn 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+
20252052
#[test]
20262053
fn dyn_trait_through_chalk() {
20272054
let t = type_at(

crates/ra_hir_ty/src/traits.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,13 +194,16 @@ fn solve(
194194
}
195195
remaining > 0
196196
};
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+
};
198202
// don't set the TLS for Chalk unless Chalk debugging is active, to make
199203
// extra sure we only use it for debugging
200204
let solution =
201205
if is_chalk_debug() { chalk::tls::set_current_program(db, solve) } else { solve() };
202206

203-
log::debug!("solve({:?}) => {:?}", goal, solution);
204207
solution
205208
}
206209

crates/ra_hir_ty/src/traits/chalk.rs

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,55 @@ fn convert_where_clauses(
626626
result
627627
}
628628

629+
fn 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+
629678
impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
630679
fn associated_ty_data(&self, id: AssocTypeId) -> Arc<AssociatedTyDatum> {
631680
self.db.associated_ty_data(id)
@@ -708,12 +757,25 @@ pub(crate) fn associated_ty_data_query(
708757
AssocContainerId::TraitId(t) => t,
709758
_ => panic!("associated type not in trait"),
710759
};
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);
711763
let generic_params = generics(db.upcast(), type_alias.into());
712-
let bound_data = chalk_rust_ir::AssociatedTyDatumBound {
713-
// FIXME add bounds and where clauses
714-
bounds: vec![],
715-
where_clauses: vec![],
716-
};
764+
let bound_vars = Substs::bound_vars(&generic_params);
765+
let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast());
766+
let ctx = crate::TyLoweringContext::new(db, &resolver)
767+
.with_type_param_mode(crate::lower::TypeParamLoweringMode::Variable);
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 };
717779
let datum = AssociatedTyDatum {
718780
trait_id: trait_.to_chalk(db),
719781
id,

0 commit comments

Comments
 (0)