mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
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.)
This commit is contained in:
parent
c388130f5f
commit
c8b2ec8c20
4 changed files with 113 additions and 11 deletions
|
@ -9,7 +9,8 @@ use hir_expand::{
|
||||||
};
|
};
|
||||||
use ra_prof::profile;
|
use ra_prof::profile;
|
||||||
use ra_syntax::ast::{
|
use ra_syntax::ast::{
|
||||||
self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner, VisibilityOwner,
|
self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner, TypeBoundsOwner,
|
||||||
|
VisibilityOwner,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -106,6 +107,7 @@ pub struct TypeAliasData {
|
||||||
pub name: Name,
|
pub name: Name,
|
||||||
pub type_ref: Option<TypeRef>,
|
pub type_ref: Option<TypeRef>,
|
||||||
pub visibility: RawVisibility,
|
pub visibility: RawVisibility,
|
||||||
|
pub bounds: Vec<TypeBound>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypeAliasData {
|
impl TypeAliasData {
|
||||||
|
@ -118,9 +120,17 @@ impl TypeAliasData {
|
||||||
let name = node.value.name().map_or_else(Name::missing, |n| n.as_name());
|
let name = node.value.name().map_or_else(Name::missing, |n| n.as_name());
|
||||||
let type_ref = node.value.type_ref().map(TypeRef::from_ast);
|
let type_ref = node.value.type_ref().map(TypeRef::from_ast);
|
||||||
let vis_default = RawVisibility::default_for_container(loc.container);
|
let vis_default = RawVisibility::default_for_container(loc.container);
|
||||||
let visibility =
|
let visibility = RawVisibility::from_ast_with_default(
|
||||||
RawVisibility::from_ast_with_default(db, vis_default, node.map(|n| n.visibility()));
|
db,
|
||||||
Arc::new(TypeAliasData { name, type_ref, visibility })
|
vis_default,
|
||||||
|
node.as_ref().map(|n| n.visibility()),
|
||||||
|
);
|
||||||
|
let bounds = if let Some(bound_list) = node.value.type_bound_list() {
|
||||||
|
bound_list.bounds().map(TypeBound::from_ast).collect()
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
};
|
||||||
|
Arc::new(TypeAliasData { name, type_ref, visibility, bounds })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2022,6 +2022,33 @@ fn main() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn associated_type_bound() {
|
||||||
|
let t = type_at(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
pub trait Trait {
|
||||||
|
type Item: OtherTrait<u32>;
|
||||||
|
}
|
||||||
|
pub trait OtherTrait<T> {
|
||||||
|
fn foo(&self) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is just a workaround for chalk#234
|
||||||
|
pub struct S<T>;
|
||||||
|
impl<T: Trait> Trait for S<T> {
|
||||||
|
type Item = <T as Trait>::Item;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test<T: Trait>() {
|
||||||
|
let y: <S<T> as Trait>::Item = no_matter;
|
||||||
|
y.foo()<|>;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
assert_eq!(t, "u32");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dyn_trait_through_chalk() {
|
fn dyn_trait_through_chalk() {
|
||||||
let t = type_at(
|
let t = type_at(
|
||||||
|
|
|
@ -194,13 +194,16 @@ fn solve(
|
||||||
}
|
}
|
||||||
remaining > 0
|
remaining > 0
|
||||||
};
|
};
|
||||||
let mut solve = || solver.solve_limited(&context, goal, should_continue);
|
let mut solve = || {
|
||||||
|
let solution = solver.solve_limited(&context, goal, should_continue);
|
||||||
|
log::debug!("solve({:?}) => {:?}", goal, solution);
|
||||||
|
solution
|
||||||
|
};
|
||||||
// don't set the TLS for Chalk unless Chalk debugging is active, to make
|
// don't set the TLS for Chalk unless Chalk debugging is active, to make
|
||||||
// extra sure we only use it for debugging
|
// extra sure we only use it for debugging
|
||||||
let solution =
|
let solution =
|
||||||
if is_chalk_debug() { chalk::tls::set_current_program(db, solve) } else { solve() };
|
if is_chalk_debug() { chalk::tls::set_current_program(db, solve) } else { solve() };
|
||||||
|
|
||||||
log::debug!("solve({:?}) => {:?}", goal, solution);
|
|
||||||
solution
|
solution
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -626,6 +626,55 @@ fn convert_where_clauses(
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generic_predicate_to_inline_bound(
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
pred: &GenericPredicate,
|
||||||
|
self_ty: &Ty,
|
||||||
|
) -> Option<chalk_rust_ir::InlineBound<Interner>> {
|
||||||
|
// An InlineBound is like a GenericPredicate, except the self type is left out.
|
||||||
|
// We don't have a special type for this, but Chalk does.
|
||||||
|
match pred {
|
||||||
|
GenericPredicate::Implemented(trait_ref) => {
|
||||||
|
if &trait_ref.substs[0] != self_ty {
|
||||||
|
// we can only convert predicates back to type bounds if they
|
||||||
|
// have the expected self type
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let args_no_self = trait_ref.substs[1..]
|
||||||
|
.iter()
|
||||||
|
.map(|ty| ty.clone().to_chalk(db).cast(&Interner))
|
||||||
|
.collect();
|
||||||
|
let trait_bound =
|
||||||
|
chalk_rust_ir::TraitBound { trait_id: trait_ref.trait_.to_chalk(db), args_no_self };
|
||||||
|
Some(chalk_rust_ir::InlineBound::TraitBound(trait_bound))
|
||||||
|
}
|
||||||
|
GenericPredicate::Projection(proj) => {
|
||||||
|
if &proj.projection_ty.parameters[0] != self_ty {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let trait_ = match proj.projection_ty.associated_ty.lookup(db.upcast()).container {
|
||||||
|
AssocContainerId::TraitId(t) => t,
|
||||||
|
_ => panic!("associated type not in trait"),
|
||||||
|
};
|
||||||
|
let args_no_self = proj.projection_ty.parameters[1..]
|
||||||
|
.iter()
|
||||||
|
.map(|ty| ty.clone().to_chalk(db).cast(&Interner))
|
||||||
|
.collect();
|
||||||
|
let alias_eq_bound = chalk_rust_ir::AliasEqBound {
|
||||||
|
value: proj.ty.clone().to_chalk(db),
|
||||||
|
trait_bound: chalk_rust_ir::TraitBound {
|
||||||
|
trait_id: trait_.to_chalk(db),
|
||||||
|
args_no_self,
|
||||||
|
},
|
||||||
|
associated_ty_id: proj.projection_ty.associated_ty.to_chalk(db),
|
||||||
|
parameters: Vec::new(), // FIXME we don't support generic associated types yet
|
||||||
|
};
|
||||||
|
Some(chalk_rust_ir::InlineBound::AliasEqBound(alias_eq_bound))
|
||||||
|
}
|
||||||
|
GenericPredicate::Error => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
|
||||||
fn associated_ty_data(&self, id: AssocTypeId) -> Arc<AssociatedTyDatum> {
|
fn associated_ty_data(&self, id: AssocTypeId) -> Arc<AssociatedTyDatum> {
|
||||||
self.db.associated_ty_data(id)
|
self.db.associated_ty_data(id)
|
||||||
|
@ -708,12 +757,25 @@ pub(crate) fn associated_ty_data_query(
|
||||||
AssocContainerId::TraitId(t) => t,
|
AssocContainerId::TraitId(t) => t,
|
||||||
_ => panic!("associated type not in trait"),
|
_ => panic!("associated type not in trait"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Lower bounds -- we could/should maybe move this to a separate query in `lower`
|
||||||
|
let type_alias_data = db.type_alias_data(type_alias);
|
||||||
let generic_params = generics(db.upcast(), type_alias.into());
|
let generic_params = generics(db.upcast(), type_alias.into());
|
||||||
let bound_data = chalk_rust_ir::AssociatedTyDatumBound {
|
let bound_vars = Substs::bound_vars(&generic_params);
|
||||||
// FIXME add bounds and where clauses
|
let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast());
|
||||||
bounds: vec![],
|
let ctx = crate::TyLoweringContext::new(db, &resolver)
|
||||||
where_clauses: vec![],
|
.with_type_param_mode(crate::lower::TypeParamLoweringMode::Variable);
|
||||||
};
|
let self_ty = Ty::Bound(crate::BoundVar::new(crate::DebruijnIndex::INNERMOST, 0));
|
||||||
|
let bounds = type_alias_data
|
||||||
|
.bounds
|
||||||
|
.iter()
|
||||||
|
.flat_map(|bound| GenericPredicate::from_type_bound(&ctx, bound, self_ty.clone()))
|
||||||
|
.filter_map(|pred| generic_predicate_to_inline_bound(db, &pred, &self_ty))
|
||||||
|
.map(|bound| make_binders(bound.shifted_in(&Interner), 0))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars);
|
||||||
|
let bound_data = chalk_rust_ir::AssociatedTyDatumBound { bounds, where_clauses };
|
||||||
let datum = AssociatedTyDatum {
|
let datum = AssociatedTyDatum {
|
||||||
trait_id: trait_.to_chalk(db),
|
trait_id: trait_.to_chalk(db),
|
||||||
id,
|
id,
|
||||||
|
|
Loading…
Reference in a new issue