Higher-ranked trait bounds for where clauses

This commit is contained in:
Lukas Wirth 2020-12-17 22:01:42 +01:00
parent c8c58d81ec
commit fa65d6ba85
4 changed files with 74 additions and 18 deletions

View file

@ -62,6 +62,7 @@ pub struct GenericParams {
pub enum WherePredicate { pub enum WherePredicate {
TypeBound { target: WherePredicateTypeTarget, bound: TypeBound }, TypeBound { target: WherePredicateTypeTarget, bound: TypeBound },
Lifetime { target: LifetimeRef, bound: LifetimeRef }, Lifetime { target: LifetimeRef, bound: LifetimeRef },
ForLifetime { lifetimes: Box<[Name]>, target: WherePredicateTypeTarget, bound: TypeBound },
} }
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
@ -69,7 +70,6 @@ pub enum WherePredicateTypeTarget {
TypeRef(TypeRef), TypeRef(TypeRef),
/// For desugared where predicates that can directly refer to a type param. /// For desugared where predicates that can directly refer to a type param.
TypeParam(LocalTypeParamId), TypeParam(LocalTypeParamId),
// FIXME: ForLifetime(Vec<LifetimeParamId>, TypeRef)
} }
#[derive(Default)] #[derive(Default)]
@ -234,7 +234,7 @@ impl GenericParams {
for bound in for bound in
node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds()) node.type_bound_list().iter().flat_map(|type_bound_list| type_bound_list.bounds())
{ {
self.add_where_predicate_from_bound(lower_ctx, bound, target.clone()); self.add_where_predicate_from_bound(lower_ctx, bound, None, target.clone());
} }
} }
@ -279,8 +279,25 @@ impl GenericParams {
} else { } else {
continue; continue;
}; };
let lifetimes: Option<Box<_>> = pred.generic_param_list().map(|param_list| {
// Higher-Ranked Trait Bounds
param_list
.lifetime_params()
.map(|lifetime_param| {
lifetime_param
.lifetime()
.map_or_else(Name::missing, |lt| Name::new_lifetime(&lt))
})
.collect()
});
for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) { for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) {
self.add_where_predicate_from_bound(lower_ctx, bound, target.clone()); self.add_where_predicate_from_bound(
lower_ctx,
bound,
lifetimes.as_ref(),
target.clone(),
);
} }
} }
} }
@ -289,6 +306,7 @@ impl GenericParams {
&mut self, &mut self,
lower_ctx: &LowerCtx, lower_ctx: &LowerCtx,
bound: ast::TypeBound, bound: ast::TypeBound,
hrtb_lifetimes: Option<&Box<[Name]>>,
target: Either<TypeRef, LifetimeRef>, target: Either<TypeRef, LifetimeRef>,
) { ) {
if bound.question_mark_token().is_some() { if bound.question_mark_token().is_some() {
@ -297,9 +315,16 @@ impl GenericParams {
} }
let bound = TypeBound::from_ast(lower_ctx, bound); let bound = TypeBound::from_ast(lower_ctx, bound);
let predicate = match (target, bound) { let predicate = match (target, bound) {
(Either::Left(type_ref), bound) => WherePredicate::TypeBound { (Either::Left(type_ref), bound) => match hrtb_lifetimes {
target: WherePredicateTypeTarget::TypeRef(type_ref), Some(hrtb_lifetimes) => WherePredicate::ForLifetime {
bound, lifetimes: hrtb_lifetimes.clone(),
target: WherePredicateTypeTarget::TypeRef(type_ref),
bound,
},
None => WherePredicate::TypeBound {
target: WherePredicateTypeTarget::TypeRef(type_ref),
bound,
},
}, },
(Either::Right(lifetime), TypeBound::Lifetime(bound)) => { (Either::Right(lifetime), TypeBound::Lifetime(bound)) => {
WherePredicate::Lifetime { target: lifetime, bound } WherePredicate::Lifetime { target: lifetime, bound }

View file

@ -675,7 +675,8 @@ impl GenericPredicate {
where_predicate: &'a WherePredicate, where_predicate: &'a WherePredicate,
) -> impl Iterator<Item = GenericPredicate> + 'a { ) -> impl Iterator<Item = GenericPredicate> + 'a {
match where_predicate { match where_predicate {
WherePredicate::TypeBound { target, bound } => { WherePredicate::ForLifetime { target, bound, .. }
| WherePredicate::TypeBound { target, bound } => {
let self_ty = match target { let self_ty = match target {
WherePredicateTypeTarget::TypeRef(type_ref) => Ty::from_hir(ctx, type_ref), WherePredicateTypeTarget::TypeRef(type_ref) => Ty::from_hir(ctx, type_ref),
WherePredicateTypeTarget::TypeParam(param_id) => { WherePredicateTypeTarget::TypeParam(param_id) => {
@ -888,14 +889,13 @@ pub(crate) fn generic_predicates_for_param_query(
.where_predicates_in_scope() .where_predicates_in_scope()
// we have to filter out all other predicates *first*, before attempting to lower them // we have to filter out all other predicates *first*, before attempting to lower them
.filter(|pred| match pred { .filter(|pred| match pred {
WherePredicate::TypeBound { WherePredicate::ForLifetime { target, .. }
target: WherePredicateTypeTarget::TypeRef(type_ref), | WherePredicate::TypeBound { target, .. } => match target {
.. WherePredicateTypeTarget::TypeRef(type_ref) => {
} => Ty::from_hir_only_param(&ctx, type_ref) == Some(param_id), Ty::from_hir_only_param(&ctx, type_ref) == Some(param_id)
WherePredicate::TypeBound { }
target: WherePredicateTypeTarget::TypeParam(local_id), WherePredicateTypeTarget::TypeParam(local_id) => *local_id == param_id.local_id,
.. },
} => *local_id == param_id.local_id,
WherePredicate::Lifetime { .. } => false, WherePredicate::Lifetime { .. } => false,
}) })
.flat_map(|pred| { .flat_map(|pred| {

View file

@ -5,7 +5,9 @@ use std::sync::Arc;
use hir_def::{ use hir_def::{
adt::VariantData, adt::VariantData,
db::DefDatabase, db::DefDatabase,
generics::{GenericParams, TypeParamData, TypeParamProvenance, WherePredicateTypeTarget}, generics::{
GenericParams, TypeParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
},
path::Path, path::Path,
resolver::{HasResolver, TypeNs}, resolver::{HasResolver, TypeNs},
type_ref::TypeRef, type_ref::TypeRef,
@ -27,7 +29,8 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
.where_predicates .where_predicates
.iter() .iter()
.filter_map(|pred| match pred { .filter_map(|pred| match pred {
hir_def::generics::WherePredicate::TypeBound { target, bound } => match target { WherePredicate::ForLifetime { target, bound, .. }
| WherePredicate::TypeBound { target, bound } => match target {
WherePredicateTypeTarget::TypeRef(TypeRef::Path(p)) WherePredicateTypeTarget::TypeRef(TypeRef::Path(p))
if p == &Path::from(name![Self]) => if p == &Path::from(name![Self]) =>
{ {
@ -38,7 +41,7 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
} }
_ => None, _ => None,
}, },
hir_def::generics::WherePredicate::Lifetime { .. } => None, WherePredicate::Lifetime { .. } => None,
}) })
.filter_map(|path| match resolver.resolve_path_in_type_ns_fully(db, path.mod_path()) { .filter_map(|path| match resolver.resolve_path_in_type_ns_fully(db, path.mod_path()) {
Some(TypeNs::TraitId(t)) => Some(t), Some(TypeNs::TraitId(t)) => Some(t),

View file

@ -1077,4 +1077,32 @@ fn foo<'foobar>(_: &'foobar ()) {
}"#, }"#,
) )
} }
#[test]
#[ignore] // requires the HIR to somehow track these hrtb lifetimes
fn goto_lifetime_hrtb() {
check(
r#"trait Foo<T> {}
fn foo<T>() where for<'a> T: Foo<&'a<|> (u8, u16)>, {}
//^^
"#,
);
check(
r#"trait Foo<T> {}
fn foo<T>() where for<'a<|>> T: Foo<&'a (u8, u16)>, {}
//^^
"#,
);
}
#[test]
#[ignore] // requires ForTypes to be implemented
fn goto_lifetime_hrtb_for_type() {
check(
r#"trait Foo<T> {}
fn foo<T>() where T: for<'a> Foo<&'a<|> (u8, u16)>, {}
//^^
"#,
);
}
} }