Implement inline associated type bounds
Like `Iterator<Item: SomeTrait>`. This is an unstable feature, but it's used in the standard library e.g. in the definition of Flatten, so we can't get away with not implementing it :)
This commit is contained in:
parent
c388130f5f
commit
db32a2e421
@ -15,7 +15,7 @@
|
||||
use crate::{
|
||||
attr::Attrs,
|
||||
db::DefDatabase,
|
||||
path::{path, GenericArgs, Path},
|
||||
path::{path, AssociatedTypeBinding, GenericArgs, Path},
|
||||
src::HasSource,
|
||||
type_ref::{Mutability, TypeBound, TypeRef},
|
||||
visibility::RawVisibility,
|
||||
@ -95,7 +95,11 @@ fn desugar_future_path(orig: TypeRef) -> Path {
|
||||
let path = path![std::future::Future];
|
||||
let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect();
|
||||
let mut last = GenericArgs::empty();
|
||||
last.bindings.push((name![Output], orig));
|
||||
last.bindings.push(AssociatedTypeBinding {
|
||||
name: name![Output],
|
||||
type_ref: Some(orig),
|
||||
bounds: Vec::new(),
|
||||
});
|
||||
generic_args.push(Some(Arc::new(last)));
|
||||
|
||||
Path::from_known_path(path, generic_args)
|
||||
|
@ -14,7 +14,10 @@
|
||||
use ra_db::CrateId;
|
||||
use ra_syntax::ast;
|
||||
|
||||
use crate::{type_ref::TypeRef, InFile};
|
||||
use crate::{
|
||||
type_ref::{TypeBound, TypeRef},
|
||||
InFile,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct ModPath {
|
||||
@ -111,7 +114,21 @@ pub struct GenericArgs {
|
||||
/// is left out.
|
||||
pub has_self_type: bool,
|
||||
/// Associated type bindings like in `Iterator<Item = T>`.
|
||||
pub bindings: Vec<(Name, TypeRef)>,
|
||||
pub bindings: Vec<AssociatedTypeBinding>,
|
||||
}
|
||||
|
||||
/// An associated type binding like in `Iterator<Item = T>`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct AssociatedTypeBinding {
|
||||
/// The name of the associated type.
|
||||
pub name: Name,
|
||||
/// The type bound to this associated type (in `Item = T`, this would be the
|
||||
/// `T`). This can be `None` if there are bounds instead.
|
||||
pub type_ref: Option<TypeRef>,
|
||||
/// Bounds for the associated type, like in `Iterator<Item:
|
||||
/// SomeOtherTrait>`. (This is the unstable `associated_type_bounds`
|
||||
/// feature.)
|
||||
pub bounds: Vec<TypeBound>,
|
||||
}
|
||||
|
||||
/// A single generic argument.
|
||||
|
@ -9,11 +9,12 @@
|
||||
hygiene::Hygiene,
|
||||
name::{name, AsName},
|
||||
};
|
||||
use ra_syntax::ast::{self, AstNode, TypeAscriptionOwner};
|
||||
use ra_syntax::ast::{self, AstNode, TypeAscriptionOwner, TypeBoundsOwner};
|
||||
|
||||
use super::AssociatedTypeBinding;
|
||||
use crate::{
|
||||
path::{GenericArg, GenericArgs, ModPath, Path, PathKind},
|
||||
type_ref::TypeRef,
|
||||
type_ref::{TypeBound, TypeRef},
|
||||
};
|
||||
|
||||
pub(super) use lower_use::lower_use_tree;
|
||||
@ -136,10 +137,16 @@ pub(super) fn lower_generic_args(node: ast::TypeArgList) -> Option<GenericArgs>
|
||||
// lifetimes ignored for now
|
||||
let mut bindings = Vec::new();
|
||||
for assoc_type_arg in node.assoc_type_args() {
|
||||
let assoc_type_arg: ast::AssocTypeArg = assoc_type_arg;
|
||||
if let Some(name_ref) = assoc_type_arg.name_ref() {
|
||||
let name = name_ref.as_name();
|
||||
let type_ref = TypeRef::from_ast_opt(assoc_type_arg.type_ref());
|
||||
bindings.push((name, type_ref));
|
||||
let type_ref = assoc_type_arg.type_ref().map(TypeRef::from_ast);
|
||||
let bounds = if let Some(l) = assoc_type_arg.type_bound_list() {
|
||||
l.bounds().map(TypeBound::from_ast).collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
bindings.push(AssociatedTypeBinding { name, type_ref, bounds });
|
||||
}
|
||||
}
|
||||
if args.is_empty() && bindings.is_empty() {
|
||||
@ -168,7 +175,11 @@ fn lower_generic_args_from_fn_path(
|
||||
}
|
||||
if let Some(ret_type) = ret_type {
|
||||
let type_ref = TypeRef::from_ast_opt(ret_type.type_ref());
|
||||
bindings.push((name![Output], type_ref))
|
||||
bindings.push(AssociatedTypeBinding {
|
||||
name: name![Output],
|
||||
type_ref: Some(type_ref),
|
||||
bounds: Vec::new(),
|
||||
});
|
||||
}
|
||||
if args.is_empty() && bindings.is_empty() {
|
||||
None
|
||||
|
@ -163,8 +163,16 @@ fn go_path(path: &Path, f: &mut impl FnMut(&TypeRef)) {
|
||||
let crate::path::GenericArg::Type(type_ref) = arg;
|
||||
go(type_ref, f);
|
||||
}
|
||||
for (_, type_ref) in &args_and_bindings.bindings {
|
||||
go(type_ref, f);
|
||||
for binding in &args_and_bindings.bindings {
|
||||
if let Some(type_ref) = &binding.type_ref {
|
||||
go(type_ref, f);
|
||||
}
|
||||
for bound in &binding.bounds {
|
||||
match bound {
|
||||
TypeBound::Path(path) => go_path(path, f),
|
||||
TypeBound::Error => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,8 @@
|
||||
use std::iter;
|
||||
use std::sync::Arc;
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use hir_def::{
|
||||
adt::StructKind,
|
||||
builtin_type::BuiltinType,
|
||||
@ -596,21 +598,35 @@ fn assoc_type_bindings_from_type_bound<'a>(
|
||||
.into_iter()
|
||||
.flat_map(|segment| segment.args_and_bindings.into_iter())
|
||||
.flat_map(|args_and_bindings| args_and_bindings.bindings.iter())
|
||||
.map(move |(name, type_ref)| {
|
||||
.flat_map(move |binding| {
|
||||
let associated_ty = associated_type_by_name_including_super_traits(
|
||||
ctx.db.upcast(),
|
||||
trait_ref.trait_,
|
||||
&name,
|
||||
&binding.name,
|
||||
);
|
||||
let associated_ty = match associated_ty {
|
||||
None => return GenericPredicate::Error,
|
||||
None => return SmallVec::<[GenericPredicate; 1]>::new(),
|
||||
Some(t) => t,
|
||||
};
|
||||
let projection_ty =
|
||||
ProjectionTy { associated_ty, parameters: trait_ref.substs.clone() };
|
||||
let ty = Ty::from_hir(ctx, type_ref);
|
||||
let projection_predicate = ProjectionPredicate { projection_ty, ty };
|
||||
GenericPredicate::Projection(projection_predicate)
|
||||
let mut preds = SmallVec::with_capacity(
|
||||
binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
|
||||
);
|
||||
if let Some(type_ref) = &binding.type_ref {
|
||||
let ty = Ty::from_hir(ctx, type_ref);
|
||||
let projection_predicate =
|
||||
ProjectionPredicate { projection_ty: projection_ty.clone(), ty };
|
||||
preds.push(GenericPredicate::Projection(projection_predicate));
|
||||
}
|
||||
for bound in &binding.bounds {
|
||||
preds.extend(GenericPredicate::from_type_bound(
|
||||
ctx,
|
||||
bound,
|
||||
Ty::Projection(projection_ty.clone()),
|
||||
));
|
||||
}
|
||||
preds
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1923,6 +1923,53 @@ fn test<T, U>() where T: Trait<U::Item>, U: Trait<T::Item> {
|
||||
assert_eq!(t, "{unknown}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inline_assoc_type_bounds_1() {
|
||||
let t = type_at(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Iterator {
|
||||
type Item;
|
||||
}
|
||||
trait OtherTrait<T> {
|
||||
fn foo(&self) -> T;
|
||||
}
|
||||
|
||||
// workaround for Chalk assoc type normalization problems
|
||||
pub struct S<T>;
|
||||
impl<T: Iterator> Iterator for S<T> {
|
||||
type Item = <T as Iterator>::Item;
|
||||
}
|
||||
|
||||
fn test<I: Iterator<Item: OtherTrait<u32>>>() {
|
||||
let x: <S<I> as Iterator>::Item;
|
||||
x.foo()<|>;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u32");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inline_assoc_type_bounds_2() {
|
||||
let t = type_at(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Iterator {
|
||||
type Item;
|
||||
}
|
||||
|
||||
fn test<I: Iterator<Item: Iterator<Item = u32>>>() {
|
||||
let x: <<I as Iterator>::Item as Iterator>::Item;
|
||||
x<|>;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
// assert_eq!(t, "u32");
|
||||
// doesn't currently work, Chalk #234
|
||||
assert_eq!(t, "{unknown}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unify_impl_trait() {
|
||||
assert_snapshot!(
|
||||
|
Loading…
Reference in New Issue
Block a user