Lower fully qualified associated type paths

I.e. `<T as Trait>::Foo`.
This commit is contained in:
Florian Diebold 2019-08-05 22:42:38 +02:00
parent 6cfdfdecba
commit 22724f37f3
5 changed files with 128 additions and 23 deletions

View File

@ -71,6 +71,7 @@ fn compare_path_segment(a: &SmolStr, b: &ast::PathSegment) -> bool {
ast::PathSegmentKind::SelfKw => a == "self", ast::PathSegmentKind::SelfKw => a == "self",
ast::PathSegmentKind::SuperKw => a == "super", ast::PathSegmentKind::SuperKw => a == "super",
ast::PathSegmentKind::CrateKw => a == "crate", ast::PathSegmentKind::CrateKw => a == "crate",
ast::PathSegmentKind::Type { .. } => false, // not allowed in imports
} }
} else { } else {
false false

View File

@ -25,6 +25,12 @@ pub struct PathSegment {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct GenericArgs { pub struct GenericArgs {
pub args: Vec<GenericArg>, pub args: Vec<GenericArg>,
/// This specifies whether the args contain a Self type as the first
/// element. This is the case for path segments like `<T as Trait>`, where
/// `T` is actually a type parameter for the path `Trait` specifying the
/// Self type. Otherwise, when we have a path `Trait<X, Y>`, the Self type
/// is left out.
pub has_self_type: bool,
// someday also bindings // someday also bindings
} }
@ -74,6 +80,28 @@ impl Path {
let segment = PathSegment { name: name.as_name(), args_and_bindings: args }; let segment = PathSegment { name: name.as_name(), args_and_bindings: args };
segments.push(segment); segments.push(segment);
} }
ast::PathSegmentKind::Type { type_ref, trait_ref } => {
assert!(path.qualifier().is_none()); // this can only occur at the first segment
// FIXME: handle <T> syntax (type segments without trait)
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
let path = Path::from_ast(trait_ref?.path()?)?;
kind = path.kind;
let mut prefix_segments = path.segments;
prefix_segments.reverse();
segments.extend(prefix_segments);
// Insert the type reference (T in the above example) as Self parameter for the trait
let self_type = TypeRef::from_ast(type_ref?);
let mut last_segment = segments.last_mut()?;
if last_segment.args_and_bindings.is_none() {
last_segment.args_and_bindings = Some(Arc::new(GenericArgs::empty()));
};
let args = last_segment.args_and_bindings.as_mut().unwrap();
let mut args_inner = Arc::make_mut(args);
args_inner.has_self_type = true;
args_inner.args.insert(0, GenericArg::Type(self_type));
}
ast::PathSegmentKind::CrateKw => { ast::PathSegmentKind::CrateKw => {
kind = PathKind::Crate; kind = PathKind::Crate;
break; break;
@ -144,11 +172,15 @@ impl GenericArgs {
} }
// lifetimes and assoc type args ignored for now // lifetimes and assoc type args ignored for now
if !args.is_empty() { if !args.is_empty() {
Some(GenericArgs { args }) Some(GenericArgs { args, has_self_type: false })
} else { } else {
None None
} }
} }
pub(crate) fn empty() -> GenericArgs {
GenericArgs { args: Vec::new(), has_self_type: false }
}
} }
impl From<Name> for Path { impl From<Name> for Path {
@ -236,6 +268,10 @@ fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> {
} }
Path { kind: PathKind::Super, segments: Vec::new() } Path { kind: PathKind::Super, segments: Vec::new() }
} }
ast::PathSegmentKind::Type { .. } => {
// not allowed in imports
return None;
}
}; };
Some(res) Some(res)
} }

View File

@ -8,7 +8,7 @@
use std::iter; use std::iter;
use std::sync::Arc; use std::sync::Arc;
use super::{FnSig, GenericPredicate, Substs, TraitRef, Ty, TypeCtor}; use super::{FnSig, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor};
use crate::{ use crate::{
adt::VariantDef, adt::VariantDef,
generics::HasGenericParams, generics::HasGenericParams,
@ -64,7 +64,8 @@ impl Ty {
pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Self { pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Self {
// Resolve the path (in type namespace) // Resolve the path (in type namespace)
let resolution = resolver.resolve_path_without_assoc_items(db, path).take_types(); let (resolution, remaining_index) = resolver.resolve_path_segments(db, path).into_inner();
let resolution = resolution.take_types();
let def = match resolution { let def = match resolution {
Some(Resolution::Def(def)) => def, Some(Resolution::Def(def)) => def,
@ -73,6 +74,10 @@ impl Ty {
panic!("path resolved to local binding in type ns"); panic!("path resolved to local binding in type ns");
} }
Some(Resolution::GenericParam(idx)) => { Some(Resolution::GenericParam(idx)) => {
if remaining_index.is_some() {
// e.g. T::Item
return Ty::Unknown;
}
return Ty::Param { return Ty::Param {
idx, idx,
// FIXME: maybe return name in resolution? // FIXME: maybe return name in resolution?
@ -83,18 +88,59 @@ impl Ty {
}; };
} }
Some(Resolution::SelfType(impl_block)) => { Some(Resolution::SelfType(impl_block)) => {
if remaining_index.is_some() {
// e.g. Self::Item
return Ty::Unknown;
}
return impl_block.target_ty(db); return impl_block.target_ty(db);
} }
None => return Ty::Unknown, None => {
// path did not resolve
return Ty::Unknown;
}
}; };
let typable: TypableDef = match def.into() { if let ModuleDef::Trait(trait_) = def {
None => return Ty::Unknown, let segment = match remaining_index {
Some(it) => it, None => path.segments.last().expect("resolved path has at least one element"),
}; Some(i) => &path.segments[i - 1],
let ty = db.type_for_def(typable, Namespace::Types); };
let substs = Ty::substs_from_path(db, resolver, path, typable); let trait_ref = TraitRef::from_resolved_path(db, resolver, trait_, segment, None);
ty.subst(&substs) if let Some(remaining_index) = remaining_index {
if remaining_index == path.segments.len() - 1 {
let segment = &path.segments[remaining_index];
let associated_ty =
match trait_ref.trait_.associated_type_by_name(db, segment.name.clone()) {
Some(t) => t,
None => {
// associated type not found
return Ty::Unknown;
}
};
eprintln!(
"assoc ty: {:?}, parameters: {:?}",
associated_ty.name(db),
trait_ref.substs
);
// FIXME handle type parameters on the segment
Ty::Projection(ProjectionTy { associated_ty, parameters: trait_ref.substs })
} else {
// FIXME more than one segment remaining, is this possible?
Ty::Unknown
}
} else {
// FIXME dyn Trait without the dyn
Ty::Unknown
}
} else {
let typable: TypableDef = match def.into() {
None => return Ty::Unknown,
Some(it) => it,
};
let ty = db.type_for_def(typable, Namespace::Types);
let substs = Ty::substs_from_path(db, resolver, path, typable);
ty.subst(&substs)
}
} }
pub(super) fn substs_from_path_segment( pub(super) fn substs_from_path_segment(
@ -219,14 +265,25 @@ impl TraitRef {
Resolution::Def(ModuleDef::Trait(tr)) => tr, Resolution::Def(ModuleDef::Trait(tr)) => tr,
_ => return None, _ => return None,
}; };
let mut substs = Self::substs_from_path(db, resolver, path, resolved); let segment = path.segments.last().expect("path should have at least one segment");
Some(TraitRef::from_resolved_path(db, resolver, resolved, segment, explicit_self_ty))
}
fn from_resolved_path(
db: &impl HirDatabase,
resolver: &Resolver,
resolved: Trait,
segment: &PathSegment,
explicit_self_ty: Option<Ty>,
) -> Self {
let mut substs = TraitRef::substs_from_path(db, resolver, segment, resolved);
if let Some(self_ty) = explicit_self_ty { if let Some(self_ty) = explicit_self_ty {
// FIXME this could be nicer // FIXME this could be nicer
let mut substs_vec = substs.0.to_vec(); let mut substs_vec = substs.0.to_vec();
substs_vec[0] = self_ty; substs_vec[0] = self_ty;
substs.0 = substs_vec.into(); substs.0 = substs_vec.into();
} }
Some(TraitRef { trait_: resolved, substs }) TraitRef { trait_: resolved, substs }
} }
pub(crate) fn from_hir( pub(crate) fn from_hir(
@ -245,11 +302,12 @@ impl TraitRef {
fn substs_from_path( fn substs_from_path(
db: &impl HirDatabase, db: &impl HirDatabase,
resolver: &Resolver, resolver: &Resolver,
path: &Path, segment: &PathSegment,
resolved: Trait, resolved: Trait,
) -> Substs { ) -> Substs {
let segment = path.segments.last().expect("path should have at least one segment"); let has_self_param =
substs_from_path_segment(db, resolver, segment, Some(resolved.into()), true) segment.args_and_bindings.as_ref().map(|a| a.has_self_type).unwrap_or(false);
substs_from_path_segment(db, resolver, segment, Some(resolved.into()), !has_self_param)
} }
pub(crate) fn for_trait(db: &impl HirDatabase, trait_: Trait) -> TraitRef { pub(crate) fn for_trait(db: &impl HirDatabase, trait_: Trait) -> TraitRef {

View File

@ -2515,10 +2515,10 @@ fn test<T: Iterable>() {
@r###" @r###"
[108; 227) '{ ...ter; }': () [108; 227) '{ ...ter; }': ()
[118; 119) 'x': i32 [118; 119) 'x': <S as Iterable>::Item
[145; 146) '1': i32 [145; 146) '1': <S as Iterable>::Item
[156; 157) 'y': {unknown} [156; 157) 'y': <T as Iterable>::Item
[183; 192) 'no_matter': {unknown} [183; 192) 'no_matter': <T as Iterable>::Item
[202; 203) 'z': {unknown} [202; 203) 'z': {unknown}
[215; 224) 'no_matter': {unknown} [215; 224) 'no_matter': {unknown}
"### "###
@ -2552,9 +2552,9 @@ fn test() {
[205; 209) 'foo1': fn foo1<S>(T) -> {unknown} [205; 209) 'foo1': fn foo1<S>(T) -> {unknown}
[205; 212) 'foo1(S)': {unknown} [205; 212) 'foo1(S)': {unknown}
[210; 211) 'S': S [210; 211) 'S': S
[222; 223) 'y': {unknown} [222; 223) 'y': <S as Iterable>::Item
[226; 230) 'foo2': fn foo2<S>(T) -> {unknown} [226; 230) 'foo2': fn foo2<S>(T) -> <T as Iterable>::Item
[226; 233) 'foo2(S)': {unknown} [226; 233) 'foo2(S)': <S as Iterable>::Item
[231; 232) 'S': S [231; 232) 'S': S
"### "###
); );

View File

@ -91,6 +91,7 @@ impl ast::Attr {
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum PathSegmentKind { pub enum PathSegmentKind {
Name(ast::NameRef), Name(ast::NameRef),
Type { type_ref: Option<ast::TypeRef>, trait_ref: Option<ast::PathType> },
SelfKw, SelfKw,
SuperKw, SuperKw,
CrateKw, CrateKw,
@ -112,6 +113,15 @@ impl ast::PathSegment {
T![self] => PathSegmentKind::SelfKw, T![self] => PathSegmentKind::SelfKw,
T![super] => PathSegmentKind::SuperKw, T![super] => PathSegmentKind::SuperKw,
T![crate] => PathSegmentKind::CrateKw, T![crate] => PathSegmentKind::CrateKw,
T![<] => {
// <T> or <T as Trait>
// T is any TypeRef, Trait has to be a PathType
let mut type_refs =
self.syntax().children().filter(|node| ast::TypeRef::can_cast(node.kind()));
let type_ref = type_refs.next().and_then(ast::TypeRef::cast);
let trait_ref = type_refs.next().and_then(ast::PathType::cast);
PathSegmentKind::Type { type_ref, trait_ref }
}
_ => return None, _ => return None,
} }
}; };