]> git.lizzy.rs Git - rust.git/commitdiff
Merge #1677
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>
Mon, 12 Aug 2019 19:43:57 +0000 (19:43 +0000)
committerbors[bot] <26634292+bors[bot]@users.noreply.github.com>
Mon, 12 Aug 2019 19:43:57 +0000 (19:43 +0000)
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>
crates/ra_assists/src/auto_import.rs
crates/ra_hir/src/code_model.rs
crates/ra_hir/src/path.rs
crates/ra_hir/src/ty.rs
crates/ra_hir/src/ty/infer.rs
crates/ra_hir/src/ty/lower.rs
crates/ra_hir/src/ty/tests.rs
crates/ra_hir/src/ty/traits.rs
crates/ra_hir/src/ty/traits/chalk.rs
crates/ra_syntax/src/ast/extensions.rs

index a32e2f9b6103cad2443f45de5ecbca4cb5b1b370..1158adbbc0f7ef5feffc3e6a258ec9ba77e1c58d 100644 (file)
@@ -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
index 779764590cab63873df4b82f762eaf4e431bd5c5..89fc1d1a1cec668fe1764d584d569015e7b37cf2 100644 (file)
@@ -838,6 +838,10 @@ pub fn module(self, db: &impl DefDatabase) -> Module {
         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));
index 882db76816209175bff5ca16428bb34d6cfb005a..5ee71e421e3add42dbbbc5b25df24e4dc82ea022 100644 (file)
@@ -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 @@ pub fn from_ast(mut path: ast::Path) -> Option<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 @@ pub(crate) fn from_ast(node: ast::TypeArgList) -> Option<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)
 }
index 82589e504fc5f93794504b96d4d8190a72ae5be2..642dd02cbe0b481737df6b7ca82c1e4d255eaf49 100644 (file)
@@ -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 @@ pub fn walk(&self, f: &mut impl FnMut(&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 @@ fn walk_mut(&mut self, f: &mut impl FnMut(&mut 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 @@ fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
                     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 @@ fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
         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)
+            ),
+        }
+    }
+}
index 594c5bc795ecfe1e76d33543997ae5560566a4e0..675df4a22f40afe72aeb8a3f7bee2fb66e23ea18 100644 (file)
@@ -245,7 +245,8 @@ fn make_ty(&mut self, type_ref: &TypeRef) -> Ty {
             &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 @@ fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> {
         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 @@ fn infer_path_expr(&mut self, resolver: &Resolver, path: &Path, id: ExprOrPatId)
                 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 @@ fn infer_tuple_struct_pat(
                 .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 @@ fn infer_struct_pat(
             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 @@ fn infer_method_call(
         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 @@ fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
                 };
                 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 @@ fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
                     _ => 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());
index 894ba06955a0ea9b8299a82793a084e53d88e8ba..debedcbb8b194bae283c5cc45296284447fc4feb 100644 (file)
@@ -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 @@ pub(crate) fn from_hir(db: &impl HirDatabase, resolver: &Resolver, type_ref: &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 @@ pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &P
                 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 @@ pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &P
                 };
             }
             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 @@ pub(crate) fn from_path(
             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 @@ pub(crate) fn from_hir(
     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 {
index d5f7a4d2503b71fdc68905a786b3213a2e9ef907..28727bb181e16bcfd9c82f9266573d6a3f1dc414 100644 (file)
@@ -2508,15 +2508,55 @@ trait Iterable {
 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 @@ impl<T> Deref for T where T: Trait {
     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();
index 0769e6e179c35d0b0563fb7b31698bc1aa7aaefc..fde5d8a47d1411f7f1f9c8430354a0946fc7831b 100644 (file)
@@ -7,7 +7,7 @@
 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 };
index 9e7ae07247aedbe08f269103eebbf72650a95564..6df7094c580cb87ea7f0a8a4a9978850cbb461ce 100644 (file)
@@ -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 @@ fn to_chalk(self, db: &impl HirDatabase) -> chalk_ir::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 @@ fn split_projection<'p>(
         &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,
index d4873b39a81bbe4078ba314cd7be4b75047730de..2a59cf653734ff92504b6dd9bacac6bd3d8cb09f 100644 (file)
@@ -91,6 +91,7 @@ pub fn as_key_value(&self) -> Option<(SmolStr, SmolStr)> {
 #[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 @@ pub fn kind(&self) -> Option<PathSegmentKind> {
                 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,
             }
         };