Merge #1677
1677: Associated types r=flodiebold a=flodiebold This implements basic support for (fully qualified) associated type projections: - handle fully qualified paths like `<T as Trait>::AssocType` (basically desugaring to something like `Trait<Self=T>::AssocType`) - lower these to a new `Ty::Projection` enum variant - also introduce `Ty::UnselectedProjection` for cases like `T::AssocType` where the trait from which the type comes isn't specified, but these aren't handled further so far - in inference, normalize these projections using Chalk: basically, when encountering a type e.g. from a type annotation or signature, we replace these `Ty::Projection`s by type variables and add obligations to normalize the associated type Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
commit
2c65a05984
@ -71,6 +71,7 @@ fn compare_path_segment(a: &SmolStr, b: &ast::PathSegment) -> bool {
|
||||
ast::PathSegmentKind::SelfKw => a == "self",
|
||||
ast::PathSegmentKind::SuperKw => a == "super",
|
||||
ast::PathSegmentKind::CrateKw => a == "crate",
|
||||
ast::PathSegmentKind::Type { .. } => false, // not allowed in imports
|
||||
}
|
||||
} else {
|
||||
false
|
||||
|
@ -838,6 +838,10 @@ impl TypeAlias {
|
||||
self.id.module(db)
|
||||
}
|
||||
|
||||
pub fn krate(self, db: &impl DefDatabase) -> Option<Crate> {
|
||||
self.module(db).krate(db)
|
||||
}
|
||||
|
||||
/// The containing impl block, if this is a method.
|
||||
pub fn impl_block(self, db: &impl DefDatabase) -> Option<ImplBlock> {
|
||||
let module_impls = db.impls_in_module(self.module(db));
|
||||
|
@ -25,6 +25,12 @@ pub struct PathSegment {
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct GenericArgs {
|
||||
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
|
||||
}
|
||||
|
||||
@ -74,6 +80,28 @@ impl Path {
|
||||
let segment = PathSegment { name: name.as_name(), args_and_bindings: args };
|
||||
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 => {
|
||||
kind = PathKind::Crate;
|
||||
break;
|
||||
@ -144,11 +172,15 @@ impl GenericArgs {
|
||||
}
|
||||
// lifetimes and assoc type args ignored for now
|
||||
if !args.is_empty() {
|
||||
Some(GenericArgs { args })
|
||||
Some(GenericArgs { args, has_self_type: false })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn empty() -> GenericArgs {
|
||||
GenericArgs { args: Vec::new(), has_self_type: false }
|
||||
}
|
||||
}
|
||||
|
||||
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() }
|
||||
}
|
||||
ast::PathSegmentKind::Type { .. } => {
|
||||
// not allowed in imports
|
||||
return None;
|
||||
}
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
|
@ -94,6 +94,12 @@ pub enum TypeCtor {
|
||||
|
||||
/// A tuple type. For example, `(i32, bool)`.
|
||||
Tuple { cardinality: u16 },
|
||||
|
||||
/// Represents an associated item like `Iterator::Item`. This is used
|
||||
/// when we have tried to normalize a projection like `T::Item` but
|
||||
/// couldn't find a better representation. In that case, we generate
|
||||
/// an **application type** like `(Iterator::Item)<T>`.
|
||||
AssociatedType(TypeAlias),
|
||||
}
|
||||
|
||||
/// A nominal type with (maybe 0) type parameters. This might be a primitive
|
||||
@ -114,6 +120,12 @@ pub struct ProjectionTy {
|
||||
pub parameters: Substs,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct UnselectedProjectionTy {
|
||||
pub type_name: Name,
|
||||
pub parameters: Substs,
|
||||
}
|
||||
|
||||
/// A type.
|
||||
///
|
||||
/// See also the `TyKind` enum in rustc (librustc/ty/sty.rs), which represents
|
||||
@ -127,6 +139,18 @@ pub enum Ty {
|
||||
/// several other things.
|
||||
Apply(ApplicationTy),
|
||||
|
||||
/// A "projection" type corresponds to an (unnormalized)
|
||||
/// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the
|
||||
/// trait and all its parameters are fully known.
|
||||
Projection(ProjectionTy),
|
||||
|
||||
/// This is a variant of a projection in which the trait is
|
||||
/// **not** known. It corresponds to a case where people write
|
||||
/// `T::Item` without specifying the trait. We would then try to
|
||||
/// figure out the trait by looking at all the traits that are in
|
||||
/// scope.
|
||||
UnselectedProjection(UnselectedProjectionTy),
|
||||
|
||||
/// A type parameter; for example, `T` in `fn f<T>(x: T) {}
|
||||
Param {
|
||||
/// The index of the parameter (starting with parameters from the
|
||||
@ -352,6 +376,16 @@ impl Ty {
|
||||
t.walk(f);
|
||||
}
|
||||
}
|
||||
Ty::Projection(p_ty) => {
|
||||
for t in p_ty.parameters.iter() {
|
||||
t.walk(f);
|
||||
}
|
||||
}
|
||||
Ty::UnselectedProjection(p_ty) => {
|
||||
for t in p_ty.parameters.iter() {
|
||||
t.walk(f);
|
||||
}
|
||||
}
|
||||
Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
|
||||
}
|
||||
f(self);
|
||||
@ -362,6 +396,12 @@ impl Ty {
|
||||
Ty::Apply(a_ty) => {
|
||||
a_ty.parameters.walk_mut(f);
|
||||
}
|
||||
Ty::Projection(p_ty) => {
|
||||
p_ty.parameters.walk_mut(f);
|
||||
}
|
||||
Ty::UnselectedProjection(p_ty) => {
|
||||
p_ty.parameters.walk_mut(f);
|
||||
}
|
||||
Ty::Param { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
|
||||
}
|
||||
f(self);
|
||||
@ -572,15 +612,61 @@ impl HirDisplay for ApplicationTy {
|
||||
write!(f, ">")?;
|
||||
}
|
||||
}
|
||||
TypeCtor::AssociatedType(type_alias) => {
|
||||
let trait_name = type_alias
|
||||
.parent_trait(f.db)
|
||||
.and_then(|t| t.name(f.db))
|
||||
.unwrap_or_else(Name::missing);
|
||||
let name = type_alias.name(f.db);
|
||||
write!(f, "{}::{}", trait_name, name)?;
|
||||
if self.parameters.len() > 0 {
|
||||
write!(f, "<")?;
|
||||
f.write_joined(&*self.parameters.0, ", ")?;
|
||||
write!(f, ">")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl HirDisplay for ProjectionTy {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
|
||||
let trait_name = self
|
||||
.associated_ty
|
||||
.parent_trait(f.db)
|
||||
.and_then(|t| t.name(f.db))
|
||||
.unwrap_or_else(Name::missing);
|
||||
write!(f, "<{} as {}", self.parameters[0].display(f.db), trait_name,)?;
|
||||
if self.parameters.len() > 1 {
|
||||
write!(f, "<")?;
|
||||
f.write_joined(&self.parameters[1..], ", ")?;
|
||||
write!(f, ">")?;
|
||||
}
|
||||
write!(f, ">::{}", self.associated_ty.name(f.db))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl HirDisplay for UnselectedProjectionTy {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
|
||||
write!(f, "{}", self.parameters[0].display(f.db))?;
|
||||
if self.parameters.len() > 1 {
|
||||
write!(f, "<")?;
|
||||
f.write_joined(&self.parameters[1..], ", ")?;
|
||||
write!(f, ">")?;
|
||||
}
|
||||
write!(f, "::{}", self.type_name)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl HirDisplay for Ty {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
|
||||
match self {
|
||||
Ty::Apply(a_ty) => a_ty.hir_fmt(f)?,
|
||||
Ty::Projection(p_ty) => p_ty.hir_fmt(f)?,
|
||||
Ty::UnselectedProjection(p_ty) => p_ty.hir_fmt(f)?,
|
||||
Ty::Param { name, .. } => write!(f, "{}", name)?,
|
||||
Ty::Bound(idx) => write!(f, "?{}", idx)?,
|
||||
Ty::Unknown => write!(f, "{{unknown}}")?,
|
||||
@ -606,3 +692,17 @@ impl HirDisplay for TraitRef {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl HirDisplay for Obligation {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
|
||||
match self {
|
||||
Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db)),
|
||||
Obligation::Projection(proj) => write!(
|
||||
f,
|
||||
"Normalize({} => {})",
|
||||
proj.projection_ty.display(f.db),
|
||||
proj.ty.display(f.db)
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -245,7 +245,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
&self.resolver,
|
||||
type_ref,
|
||||
);
|
||||
self.insert_type_vars(ty)
|
||||
let ty = self.insert_type_vars(ty);
|
||||
self.normalize_associated_types_in(ty)
|
||||
}
|
||||
|
||||
fn unify_substs(&mut self, substs1: &Substs, substs2: &Substs, depth: usize) -> bool {
|
||||
@ -411,6 +412,32 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
ty
|
||||
}
|
||||
|
||||
/// Recurses through the given type, normalizing associated types mentioned
|
||||
/// in it by replacing them by type variables and registering obligations to
|
||||
/// resolve later. This should be done once for every type we get from some
|
||||
/// type annotation (e.g. from a let type annotation, field type or function
|
||||
/// call). `make_ty` handles this already, but e.g. for field types we need
|
||||
/// to do it as well.
|
||||
fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
|
||||
let ty = self.resolve_ty_as_possible(&mut vec![], ty);
|
||||
ty.fold(&mut |ty| match ty {
|
||||
Ty::Projection(proj_ty) => self.normalize_projection_ty(proj_ty),
|
||||
Ty::UnselectedProjection(proj_ty) => {
|
||||
// FIXME use Chalk's unselected projection support
|
||||
Ty::UnselectedProjection(proj_ty)
|
||||
}
|
||||
_ => ty,
|
||||
})
|
||||
}
|
||||
|
||||
fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty {
|
||||
let var = self.new_type_var();
|
||||
let predicate = ProjectionPredicate { projection_ty: proj_ty.clone(), ty: var.clone() };
|
||||
let obligation = Obligation::Projection(predicate);
|
||||
self.obligations.push(obligation);
|
||||
var
|
||||
}
|
||||
|
||||
/// Resolves the type completely; type variables without known type are
|
||||
/// replaced by Ty::Unknown.
|
||||
fn resolve_ty_completely(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty {
|
||||
@ -549,6 +576,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable);
|
||||
let ty = ty.subst(&substs);
|
||||
let ty = self.insert_type_vars(ty);
|
||||
let ty = self.normalize_associated_types_in(ty);
|
||||
Some(ty)
|
||||
}
|
||||
Resolution::LocalBinding(pat) => {
|
||||
@ -670,6 +698,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
.and_then(|d| d.field(self.db, &Name::tuple_field_name(i)))
|
||||
.map_or(Ty::Unknown, |field| field.ty(self.db))
|
||||
.subst(&substs);
|
||||
let expected_ty = self.normalize_associated_types_in(expected_ty);
|
||||
self.infer_pat(subpat, &expected_ty, default_bm);
|
||||
}
|
||||
|
||||
@ -697,6 +726,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
let matching_field = def.and_then(|it| it.field(self.db, &subpat.name));
|
||||
let expected_ty =
|
||||
matching_field.map_or(Ty::Unknown, |field| field.ty(self.db)).subst(&substs);
|
||||
let expected_ty = self.normalize_associated_types_in(expected_ty);
|
||||
self.infer_pat(subpat.pat, &expected_ty, default_bm);
|
||||
}
|
||||
|
||||
@ -927,9 +957,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
self.unify(&expected_receiver_ty, &actual_receiver_ty);
|
||||
|
||||
let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown));
|
||||
for (arg, param) in args.iter().zip(param_iter) {
|
||||
self.infer_expr(*arg, &Expectation::has_type(param));
|
||||
for (arg, param_ty) in args.iter().zip(param_iter) {
|
||||
let param_ty = self.normalize_associated_types_in(param_ty);
|
||||
self.infer_expr(*arg, &Expectation::has_type(param_ty));
|
||||
}
|
||||
let ret_ty = self.normalize_associated_types_in(ret_ty);
|
||||
ret_ty
|
||||
}
|
||||
|
||||
@ -1020,9 +1052,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
};
|
||||
self.register_obligations_for_call(&callee_ty);
|
||||
let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown));
|
||||
for (arg, param) in args.iter().zip(param_iter) {
|
||||
self.infer_expr(*arg, &Expectation::has_type(param));
|
||||
for (arg, param_ty) in args.iter().zip(param_iter) {
|
||||
let param_ty = self.normalize_associated_types_in(param_ty);
|
||||
self.infer_expr(*arg, &Expectation::has_type(param_ty));
|
||||
}
|
||||
let ret_ty = self.normalize_associated_types_in(ret_ty);
|
||||
ret_ty
|
||||
}
|
||||
Expr::MethodCall { receiver, args, method_name, generic_args } => self
|
||||
@ -1120,7 +1154,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or(Ty::Unknown);
|
||||
self.insert_type_vars(ty)
|
||||
let ty = self.insert_type_vars(ty);
|
||||
self.normalize_associated_types_in(ty)
|
||||
}
|
||||
Expr::Await { expr } => {
|
||||
let inner_ty = self.infer_expr(*expr, &Expectation::none());
|
||||
|
@ -8,7 +8,7 @@
|
||||
use std::iter;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::{FnSig, GenericPredicate, Substs, TraitRef, Ty, TypeCtor};
|
||||
use super::{FnSig, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor};
|
||||
use crate::{
|
||||
adt::VariantDef,
|
||||
generics::HasGenericParams,
|
||||
@ -64,7 +64,8 @@ impl Ty {
|
||||
|
||||
pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Self {
|
||||
// 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 {
|
||||
Some(Resolution::Def(def)) => def,
|
||||
@ -73,6 +74,10 @@ impl Ty {
|
||||
panic!("path resolved to local binding in type ns");
|
||||
}
|
||||
Some(Resolution::GenericParam(idx)) => {
|
||||
if remaining_index.is_some() {
|
||||
// e.g. T::Item
|
||||
return Ty::Unknown;
|
||||
}
|
||||
return Ty::Param {
|
||||
idx,
|
||||
// FIXME: maybe return name in resolution?
|
||||
@ -83,18 +88,54 @@ impl Ty {
|
||||
};
|
||||
}
|
||||
Some(Resolution::SelfType(impl_block)) => {
|
||||
if remaining_index.is_some() {
|
||||
// e.g. Self::Item
|
||||
return Ty::Unknown;
|
||||
}
|
||||
return impl_block.target_ty(db);
|
||||
}
|
||||
None => return Ty::Unknown,
|
||||
None => {
|
||||
// path did not resolve
|
||||
return Ty::Unknown;
|
||||
}
|
||||
};
|
||||
|
||||
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)
|
||||
if let ModuleDef::Trait(trait_) = def {
|
||||
let segment = match remaining_index {
|
||||
None => path.segments.last().expect("resolved path has at least one element"),
|
||||
Some(i) => &path.segments[i - 1],
|
||||
};
|
||||
let trait_ref = TraitRef::from_resolved_path(db, resolver, trait_, segment, None);
|
||||
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;
|
||||
}
|
||||
};
|
||||
// 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(
|
||||
@ -219,14 +260,25 @@ impl TraitRef {
|
||||
Resolution::Def(ModuleDef::Trait(tr)) => tr,
|
||||
_ => 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 {
|
||||
// FIXME this could be nicer
|
||||
let mut substs_vec = substs.0.to_vec();
|
||||
substs_vec[0] = self_ty;
|
||||
substs.0 = substs_vec.into();
|
||||
}
|
||||
Some(TraitRef { trait_: resolved, substs })
|
||||
TraitRef { trait_: resolved, substs }
|
||||
}
|
||||
|
||||
pub(crate) fn from_hir(
|
||||
@ -245,11 +297,12 @@ impl TraitRef {
|
||||
fn substs_from_path(
|
||||
db: &impl HirDatabase,
|
||||
resolver: &Resolver,
|
||||
path: &Path,
|
||||
segment: &PathSegment,
|
||||
resolved: Trait,
|
||||
) -> Substs {
|
||||
let segment = path.segments.last().expect("path should have at least one segment");
|
||||
substs_from_path_segment(db, resolver, segment, Some(resolved.into()), true)
|
||||
let has_self_param =
|
||||
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 {
|
||||
|
@ -2508,15 +2508,55 @@ struct S;
|
||||
impl Iterable for S { type Item = u32; }
|
||||
fn test<T: Iterable>() {
|
||||
let x: <S as Iterable>::Item = 1;
|
||||
let y: T::Item = no_matter;
|
||||
let y: <T as Iterable>::Item = no_matter;
|
||||
let z: T::Item = no_matter;
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
[108; 181) '{ ...ter; }': ()
|
||||
[118; 119) 'x': i32
|
||||
[145; 146) '1': i32
|
||||
[156; 157) 'y': {unknown}
|
||||
[169; 178) 'no_matter': {unknown}"###
|
||||
⋮
|
||||
⋮[108; 227) '{ ...ter; }': ()
|
||||
⋮[118; 119) 'x': u32
|
||||
⋮[145; 146) '1': u32
|
||||
⋮[156; 157) 'y': {unknown}
|
||||
⋮[183; 192) 'no_matter': {unknown}
|
||||
⋮[202; 203) 'z': {unknown}
|
||||
⋮[215; 224) 'no_matter': {unknown}
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_return_associated_type() {
|
||||
assert_snapshot_matches!(
|
||||
infer(r#"
|
||||
trait Iterable {
|
||||
type Item;
|
||||
}
|
||||
struct S;
|
||||
impl Iterable for S { type Item = u32; }
|
||||
fn foo1<T: Iterable>(t: T) -> T::Item {}
|
||||
fn foo2<T: Iterable>(t: T) -> <T as Iterable>::Item {}
|
||||
fn test() {
|
||||
let x = foo1(S);
|
||||
let y = foo2(S);
|
||||
}
|
||||
"#),
|
||||
@r###"
|
||||
⋮
|
||||
⋮[106; 107) 't': T
|
||||
⋮[123; 125) '{}': ()
|
||||
⋮[147; 148) 't': T
|
||||
⋮[178; 180) '{}': ()
|
||||
⋮[191; 236) '{ ...(S); }': ()
|
||||
⋮[201; 202) 'x': {unknown}
|
||||
⋮[205; 209) 'foo1': fn foo1<S>(T) -> {unknown}
|
||||
⋮[205; 212) 'foo1(S)': {unknown}
|
||||
⋮[210; 211) 'S': S
|
||||
⋮[222; 223) 'y': u32
|
||||
⋮[226; 230) 'foo2': fn foo2<S>(T) -> <T as Iterable>::Item
|
||||
⋮[226; 233) 'foo2(S)': u32
|
||||
⋮[231; 232) 'S': S
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
@ -3141,6 +3181,55 @@ fn test<T: Trait>(t: T) { (*t)<|>; }
|
||||
assert_eq!(t, "i128");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn associated_type_placeholder() {
|
||||
let t = type_at(
|
||||
r#"
|
||||
//- /main.rs
|
||||
pub trait ApplyL {
|
||||
type Out;
|
||||
}
|
||||
|
||||
pub struct RefMutL<T>;
|
||||
|
||||
impl<T> ApplyL for RefMutL<T> {
|
||||
type Out = <T as ApplyL>::Out;
|
||||
}
|
||||
|
||||
fn test<T: ApplyL>() {
|
||||
let y: <RefMutL<T> as ApplyL>::Out = no_matter;
|
||||
y<|>;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
// inside the generic function, the associated type gets normalized to a placeholder `ApplL::Out<T>` [https://rust-lang.github.io/rustc-guide/traits/associated-types.html#placeholder-associated-types].
|
||||
// FIXME: fix type parameter names going missing when going through Chalk
|
||||
assert_eq!(t, "ApplyL::Out<[missing name]>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn associated_type_placeholder_2() {
|
||||
let t = type_at(
|
||||
r#"
|
||||
//- /main.rs
|
||||
pub trait ApplyL {
|
||||
type Out;
|
||||
}
|
||||
fn foo<T: ApplyL>(t: T) -> <T as ApplyL>::Out;
|
||||
|
||||
fn test<T: ApplyL>(t: T) {
|
||||
let y = foo(t);
|
||||
y<|>;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
// FIXME here Chalk doesn't normalize the type to a placeholder. I think we
|
||||
// need to add a rule like Normalize(<T as ApplyL>::Out -> ApplyL::Out<T>)
|
||||
// to the trait env ourselves here; probably Chalk can't do this by itself.
|
||||
// assert_eq!(t, "ApplyL::Out<[missing name]>");
|
||||
assert_eq!(t, "{unknown}");
|
||||
}
|
||||
|
||||
fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
|
||||
let file = db.parse(pos.file_id).ok().unwrap();
|
||||
let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();
|
||||
|
@ -7,7 +7,7 @@ use parking_lot::Mutex;
|
||||
use ra_prof::profile;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use super::{Canonical, GenericPredicate, ProjectionTy, TraitRef, Ty};
|
||||
use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty};
|
||||
use crate::{db::HirDatabase, Crate, ImplBlock, Trait};
|
||||
|
||||
use self::chalk::{from_chalk, ToChalk};
|
||||
@ -61,7 +61,6 @@ fn solve(
|
||||
) -> Option<chalk_solve::Solution> {
|
||||
let context = ChalkContext { db, krate };
|
||||
let solver = db.trait_solver(krate);
|
||||
debug!("solve goal: {:?}", goal);
|
||||
let solution = solver.lock().solve(&context, goal);
|
||||
debug!("solve({:?}) => {:?}", goal, solution);
|
||||
solution
|
||||
@ -120,10 +119,11 @@ pub struct ProjectionPredicate {
|
||||
pub(crate) fn trait_solve_query(
|
||||
db: &impl HirDatabase,
|
||||
krate: Crate,
|
||||
trait_ref: Canonical<InEnvironment<Obligation>>,
|
||||
goal: Canonical<InEnvironment<Obligation>>,
|
||||
) -> Option<Solution> {
|
||||
let _p = profile("trait_solve_query");
|
||||
let canonical = trait_ref.to_chalk(db).cast();
|
||||
debug!("trait_solve_query({})", goal.value.value.display(db));
|
||||
let canonical = goal.to_chalk(db).cast();
|
||||
// We currently don't deal with universes (I think / hope they're not yet
|
||||
// relevant for our use cases?)
|
||||
let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 };
|
||||
|
@ -45,11 +45,33 @@ impl ToChalk for Ty {
|
||||
fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::Ty {
|
||||
match self {
|
||||
Ty::Apply(apply_ty) => {
|
||||
let struct_id = apply_ty.ctor.to_chalk(db);
|
||||
let name = TypeName::TypeKindId(struct_id.into());
|
||||
let name = match apply_ty.ctor {
|
||||
TypeCtor::AssociatedType(type_alias) => {
|
||||
let type_id = type_alias.to_chalk(db);
|
||||
TypeName::AssociatedType(type_id)
|
||||
}
|
||||
_ => {
|
||||
// other TypeCtors get interned and turned into a chalk StructId
|
||||
let struct_id = apply_ty.ctor.to_chalk(db);
|
||||
TypeName::TypeKindId(struct_id.into())
|
||||
}
|
||||
};
|
||||
let parameters = apply_ty.parameters.to_chalk(db);
|
||||
chalk_ir::ApplicationTy { name, parameters }.cast()
|
||||
}
|
||||
Ty::Projection(proj_ty) => {
|
||||
let associated_ty_id = proj_ty.associated_ty.to_chalk(db);
|
||||
let parameters = proj_ty.parameters.to_chalk(db);
|
||||
chalk_ir::ProjectionTy { associated_ty_id, parameters }.cast()
|
||||
}
|
||||
Ty::UnselectedProjection(proj_ty) => {
|
||||
let type_name = lalrpop_intern::intern(&proj_ty.type_name.to_string());
|
||||
let parameters = proj_ty.parameters.to_chalk(db);
|
||||
chalk_ir::Ty::UnselectedProjection(chalk_ir::UnselectedProjectionTy {
|
||||
type_name,
|
||||
parameters,
|
||||
})
|
||||
}
|
||||
Ty::Param { idx, .. } => {
|
||||
PlaceholderIndex { ui: UniverseIndex::ROOT, idx: idx as usize }.to_ty()
|
||||
}
|
||||
@ -66,15 +88,21 @@ impl ToChalk for Ty {
|
||||
fn from_chalk(db: &impl HirDatabase, chalk: chalk_ir::Ty) -> Self {
|
||||
match chalk {
|
||||
chalk_ir::Ty::Apply(apply_ty) => {
|
||||
// FIXME this is kind of hacky due to the fact that
|
||||
// TypeName::Placeholder is a Ty::Param on our side
|
||||
match apply_ty.name {
|
||||
TypeName::TypeKindId(TypeKindId::StructId(struct_id)) => {
|
||||
let ctor = from_chalk(db, struct_id);
|
||||
let parameters = from_chalk(db, apply_ty.parameters);
|
||||
Ty::Apply(ApplicationTy { ctor, parameters })
|
||||
}
|
||||
TypeName::AssociatedType(type_id) => {
|
||||
let ctor = TypeCtor::AssociatedType(from_chalk(db, type_id));
|
||||
let parameters = from_chalk(db, apply_ty.parameters);
|
||||
Ty::Apply(ApplicationTy { ctor, parameters })
|
||||
}
|
||||
// FIXME handle TypeKindId::Trait/Type here
|
||||
TypeName::TypeKindId(_) => unimplemented!(),
|
||||
TypeName::AssociatedType(_) => unimplemented!(),
|
||||
TypeName::Placeholder(idx) => {
|
||||
assert_eq!(idx.ui, UniverseIndex::ROOT);
|
||||
Ty::Param { idx: idx.idx as u32, name: crate::Name::missing() }
|
||||
@ -389,11 +417,12 @@ where
|
||||
&self,
|
||||
projection: &'p chalk_ir::ProjectionTy,
|
||||
) -> (Arc<AssociatedTyDatum>, &'p [Parameter], &'p [Parameter]) {
|
||||
debug!("split_projection {:?}", projection);
|
||||
unimplemented!()
|
||||
let proj_ty: ProjectionTy = from_chalk(self.db, projection.clone());
|
||||
debug!("split_projection {:?} = {}", projection, proj_ty.display(self.db));
|
||||
// we don't support GATs, so I think this should always be correct currently
|
||||
(self.db.associated_ty_data(projection.associated_ty_id), &projection.parameters, &[])
|
||||
}
|
||||
fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause> {
|
||||
debug!("custom_clauses");
|
||||
vec![]
|
||||
}
|
||||
fn all_structs(&self) -> Vec<chalk_ir::StructId> {
|
||||
@ -529,6 +558,16 @@ pub(crate) fn struct_datum_query(
|
||||
adt.krate(db) != Some(krate),
|
||||
)
|
||||
}
|
||||
TypeCtor::AssociatedType(type_alias) => {
|
||||
let generic_params = type_alias.generic_params(db);
|
||||
let bound_vars = Substs::bound_vars(&generic_params);
|
||||
let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars);
|
||||
(
|
||||
generic_params.count_params_including_parent(),
|
||||
where_clauses,
|
||||
type_alias.krate(db) != Some(krate),
|
||||
)
|
||||
}
|
||||
};
|
||||
let flags = chalk_rust_ir::StructFlags {
|
||||
upstream,
|
||||
|
@ -91,6 +91,7 @@ impl ast::Attr {
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum PathSegmentKind {
|
||||
Name(ast::NameRef),
|
||||
Type { type_ref: Option<ast::TypeRef>, trait_ref: Option<ast::PathType> },
|
||||
SelfKw,
|
||||
SuperKw,
|
||||
CrateKw,
|
||||
@ -112,6 +113,15 @@ impl ast::PathSegment {
|
||||
T![self] => PathSegmentKind::SelfKw,
|
||||
T![super] => PathSegmentKind::SuperKw,
|
||||
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,
|
||||
}
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user