Repository: rust-analyzer/rust-analyzer
Pull request #7822 (2021-03-01 11:25:01)

Paperover a bug in cargo-workspace for publish RA
r? @lnicola 

See also https://github.com/pksunkara/cargo-workspaces/issues/39


Patch:
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index 5a1691effe..4109b2ea84 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -31,9 +31,9 @@ use hir_ty::{
     display::{write_bounds_like_dyn_trait_with_prefix, HirDisplayError, HirFormatter},
     method_resolution,
     traits::{FnTrait, Solution, SolutionVariables},
-    BoundVar, CallableDefId, CallableSig, Canonical, DebruijnIndex, GenericPredicate,
+    ApplicationTy, BoundVar, CallableDefId, Canonical, DebruijnIndex, FnSig, GenericPredicate,
     InEnvironment, Obligation, ProjectionPredicate, ProjectionTy, Scalar, Substs, TraitEnvironment,
-    Ty, TyDefId, TyKind,
+    Ty, TyDefId, TyKind, TypeCtor,
 };
 use rustc_hash::FxHashSet;
 use stdx::{format_to, impl_from};
@@ -1547,19 +1547,28 @@ impl Type {
     }
 
     pub fn is_unit(&self) -> bool {
-        matches!(self.ty.value, Ty::Tuple(0, ..))
+        matches!(
+            self.ty.value,
+            Ty::Apply(ApplicationTy { ctor: TypeCtor::Tuple { cardinality: 0 }, .. })
+        )
     }
     pub fn is_bool(&self) -> bool {
-        matches!(self.ty.value, Ty::Scalar(Scalar::Bool))
+        matches!(
+            self.ty.value,
+            Ty::Apply(ApplicationTy { ctor: TypeCtor::Scalar(Scalar::Bool), .. })
+        )
     }
 
     pub fn is_mutable_reference(&self) -> bool {
-        matches!(self.ty.value, Ty::Ref(Mutability::Mut, ..))
+        matches!(
+            self.ty.value,
+            Ty::Apply(ApplicationTy { ctor: TypeCtor::Ref(Mutability::Mut), .. })
+        )
     }
 
     pub fn remove_ref(&self) -> Option<Type> {
-        if let Ty::Ref(.., substs) = &self.ty.value {
-            Some(self.derived(substs[0].clone()))
+        if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Ref(_), .. }) = self.ty.value {
+            self.ty.value.substs().map(|substs| self.derived(substs[0].clone()))
         } else {
             None
         }
@@ -1679,7 +1688,7 @@ impl Type {
 
     pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> {
         let def = match self.ty.value {
-            Ty::FnDef(def, _) => Some(def),
+            Ty::Apply(ApplicationTy { ctor: TypeCtor::FnDef(def), parameters: _ }) => Some(def),
             _ => None,
         };
 
@@ -1688,16 +1697,20 @@ impl Type {
     }
 
     pub fn is_closure(&self) -> bool {
-        matches!(&self.ty.value, Ty::Closure { .. })
+        matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { .. }, .. }))
     }
 
     pub fn is_fn(&self) -> bool {
-        matches!(&self.ty.value, Ty::FnDef(..) | Ty::Function { .. })
+        matches!(
+            &self.ty.value,
+            Ty::Apply(ApplicationTy { ctor: TypeCtor::FnDef(..), .. })
+                | Ty::Apply(ApplicationTy { ctor: TypeCtor::FnPtr { .. }, .. })
+        )
     }
 
     pub fn is_packed(&self, db: &dyn HirDatabase) -> bool {
         let adt_id = match self.ty.value {
-            Ty::Adt(adt_id, ..) => adt_id,
+            Ty::Apply(ApplicationTy { ctor: TypeCtor::Adt(adt_id), .. }) => adt_id,
             _ => return false,
         };
 
@@ -1709,7 +1722,7 @@ impl Type {
     }
 
     pub fn is_raw_ptr(&self) -> bool {
-        matches!(&self.ty.value, Ty::RawPtr(..))
+        matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. }))
     }
 
     pub fn contains_unknown(&self) -> bool {
@@ -1718,34 +1731,44 @@ impl Type {
         fn go(ty: &Ty) -> bool {
             match ty {
                 Ty::Unknown => true,
-                _ => ty.substs().map_or(false, |substs| substs.iter().any(go)),
+                Ty::Apply(a_ty) => a_ty.parameters.iter().any(go),
+                _ => false,
             }
         }
     }
 
     pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Field, Type)> {
-        let (variant_id, substs) = match self.ty.value {
-            Ty::Adt(AdtId::StructId(s), ref substs) => (s.into(), substs),
-            Ty::Adt(AdtId::UnionId(u), ref substs) => (u.into(), substs),
-            _ => return Vec::new(),
+        if let Ty::Apply(a_ty) = &self.ty.value {
+            let variant_id = match a_ty.ctor {
+                TypeCtor::Adt(AdtId::StructId(s)) => s.into(),
+                TypeCtor::Adt(AdtId::UnionId(u)) => u.into(),
+                _ => return Vec::new(),
+            };
+
+            return db
+                .field_types(variant_id)
+                .iter()
+                .map(|(local_id, ty)| {
+                    let def = Field { parent: variant_id.into(), id: local_id };
+                    let ty = ty.clone().subst(&a_ty.parameters);
+                    (def, self.derived(ty))
+                })
+                .collect();
         };
-
-        db.field_types(variant_id)
-            .iter()
-            .map(|(local_id, ty)| {
-                let def = Field { parent: variant_id.into(), id: local_id };
-                let ty = ty.clone().subst(substs);
-                (def, self.derived(ty))
-            })
-            .collect()
+        Vec::new()
     }
 
     pub fn tuple_fields(&self, _db: &dyn HirDatabase) -> Vec<Type> {
-        if let Ty::Tuple(_, substs) = &self.ty.value {
-            substs.iter().map(|ty| self.derived(ty.clone())).collect()
-        } else {
-            Vec::new()
-        }
+        let mut res = Vec::new();
+        if let Ty::Apply(a_ty) = &self.ty.value {
+            if let TypeCtor::Tuple { .. } = a_ty.ctor {
+                for ty in a_ty.parameters.iter() {
+                    let ty = ty.clone();
+                    res.push(self.derived(ty));
+                }
+            }
+        };
+        res
     }
 
     pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a {
@@ -1782,13 +1805,15 @@ impl Type {
     }
 
     pub fn type_parameters(&self) -> impl Iterator<Item = Type> + '_ {
-        self.ty
-            .value
-            .strip_references()
-            .substs()
-            .into_iter()
-            .flat_map(|substs| substs.iter())
-            .map(move |ty| self.derived(ty.clone()))
+        let ty = self.ty.value.strip_references();
+        let substs = match ty {
+            Ty::Apply(apply_ty) => &apply_ty.parameters,
+            Ty::Opaque(opaque_ty) => &opaque_ty.parameters,
+            _ => return Either::Left(iter::empty()),
+        };
+
+        let iter = substs.iter().map(move |ty| self.derived(ty.clone()));
+        Either::Right(iter)
     }
 
     pub fn iterate_method_candidates<T>(
@@ -1878,8 +1903,17 @@ impl Type {
 
     // FIXME: provide required accessors such that it becomes implementable from outside.
     pub fn is_equal_for_find_impls(&self, other: &Type) -> bool {
-        let rref = other.remove_ref();
-        self.ty.value.equals_ctor(rref.as_ref().map_or(&other.ty.value, |it| &it.ty.value))
+        match (&self.ty.value, &other.ty.value) {
+            (Ty::Apply(a_original_ty), Ty::Apply(ApplicationTy { ctor, parameters })) => match ctor
+            {
+                TypeCtor::Ref(..) => match parameters.as_single() {
+                    Ty::Apply(a_ty) => a_original_ty.ctor == a_ty.ctor,
+                    _ => false,
+                },
+                _ => a_original_ty.ctor == *ctor,
+            },
+            _ => false,
+        }
     }
 
     fn derived(&self, ty: Ty) -> Type {
@@ -1924,18 +1958,26 @@ impl Type {
         fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) {
             let ty = type_.ty.value.strip_references();
             match ty {
-                Ty::Adt(..) => {
-                    cb(type_.derived(ty.clone()));
-                }
-                Ty::AssociatedType(..) => {
-                    if let Some(_) = ty.associated_type_parent_trait(db) {
-                        cb(type_.derived(ty.clone()));
-                    }
-                }
-                Ty::OpaqueType(..) => {
-                    if let Some(bounds) = ty.impl_trait_bounds(db) {
-                        walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
+                Ty::Apply(ApplicationTy { ctor, parameters }) => {
+                    match ctor {
+                        TypeCtor::Adt(_) => {
+                            cb(type_.derived(ty.clone()));
+                        }
+                        TypeCtor::AssociatedType(_) => {
+                            if let Some(_) = ty.associated_type_parent_trait(db) {
+                                cb(type_.derived(ty.clone()));
+                            }
+                        }
+                        TypeCtor::OpaqueType(..) => {
+                            if let Some(bounds) = ty.impl_trait_bounds(db) {
+                                walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
+                            }
+                        }
+                        _ => (),
                     }
+
+                    // adt params, tuples, etc...
+                    walk_substs(db, type_, parameters, cb);
                 }
                 Ty::Opaque(opaque_ty) => {
                     if let Some(bounds) = ty.impl_trait_bounds(db) {
@@ -1953,10 +1995,7 @@ impl Type {
                     walk_bounds(db, &type_.derived(ty.clone()), bounds.as_ref(), cb);
                 }
 
-                _ => {}
-            }
-            if let Some(substs) = ty.substs() {
-                walk_substs(db, type_, &substs, cb);
+                _ => (),
             }
         }
 
@@ -1974,7 +2013,7 @@ impl HirDisplay for Type {
 #[derive(Debug)]
 pub struct Callable {
     ty: Type,
-    sig: CallableSig,
+    sig: FnSig,
     def: Option<CallableDefId>,
     pub(crate) is_bound_method: bool,
 }
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 64ce4add15..dc21f6051a 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -20,7 +20,7 @@ use hir_def::{
 use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile};
 use hir_ty::{
     diagnostics::{record_literal_missing_fields, record_pattern_missing_fields},
-    InferenceResult, Substs,
+    InferenceResult, Substs, Ty,
 };
 use syntax::{
     ast::{self, AstNode},
@@ -299,11 +299,14 @@ impl SourceAnalyzer {
         let infer = self.infer.as_ref()?;
 
         let expr_id = self.expr_id(db, &literal.clone().into())?;
-        let substs = infer.type_of_expr[expr_id].substs()?;
+        let substs = match &infer.type_of_expr[expr_id] {
+            Ty::Apply(a_ty) => &a_ty.parameters,
+            _ => return None,
+        };
 
         let (variant, missing_fields, _exhaustive) =
             record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?;
-        let res = self.missing_fields(db, krate, &substs, variant, missing_fields);
+        let res = self.missing_fields(db, krate, substs, variant, missing_fields);
         Some(res)
     }
 
@@ -317,11 +320,14 @@ impl SourceAnalyzer {
         let infer = self.infer.as_ref()?;
 
         let pat_id = self.pat_id(&pattern.clone().into())?;
-        let substs = infer.type_of_pat[pat_id].substs()?;
+        let substs = match &infer.type_of_pat[pat_id] {
+            Ty::Apply(a_ty) => &a_ty.parameters,
+            _ => return None,
+        };
 
         let (variant, missing_fields, _exhaustive) =
             record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?;
-        let res = self.missing_fields(db, krate, &substs, variant, missing_fields);
+        let res = self.missing_fields(db, krate, substs, variant, missing_fields);
         Some(res)
     }
 
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index 66a88e2b62..d740b72655 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -17,7 +17,7 @@ use crate::{
         MissingPatFields, RemoveThisSemicolon,
     },
     utils::variant_data,
-    InferenceResult, Ty,
+    ApplicationTy, InferenceResult, Ty, TypeCtor,
 };
 
 pub(crate) use hir_def::{
@@ -381,11 +381,14 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
             _ => return,
         };
 
-        let (params, required) = match mismatch.expected {
-            Ty::Adt(AdtId::EnumId(enum_id), ref parameters) if enum_id == core_result_enum => {
+        let core_result_ctor = TypeCtor::Adt(AdtId::EnumId(core_result_enum));
+        let core_option_ctor = TypeCtor::Adt(AdtId::EnumId(core_option_enum));
+
+        let (params, required) = match &mismatch.expected {
+            Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &core_result_ctor => {
                 (parameters, "Ok".to_string())
             }
-            Ty::Adt(AdtId::EnumId(enum_id), ref parameters) if enum_id == core_option_enum => {
+            Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &core_option_ctor => {
                 (parameters, "Some".to_string())
             }
             _ => return,
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index 86fee0050f..1c1423fbfe 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -227,7 +227,7 @@ use hir_def::{
 use la_arena::Idx;
 use smallvec::{smallvec, SmallVec};
 
-use crate::{db::HirDatabase, InferenceResult, Ty};
+use crate::{db::HirDatabase, ApplicationTy, InferenceResult, Ty, TypeCtor};
 
 #[derive(Debug, Clone, Copy)]
 /// Either a pattern from the source code being analyzed, represented as
@@ -627,12 +627,14 @@ pub(super) fn is_useful(
     // - `!` type
     // In those cases, no match arm is useful.
     match cx.infer[cx.match_expr].strip_references() {
-        Ty::Adt(AdtId::EnumId(enum_id), ..) => {
+        Ty::Apply(ApplicationTy { ctor: TypeCtor::Adt(AdtId::EnumId(enum_id)), .. }) => {
             if cx.db.enum_data(*enum_id).variants.is_empty() {
                 return Ok(Usefulness::NotUseful);
             }
         }
-        Ty::Never => return Ok(Usefulness::NotUseful),
+        Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }) => {
+            return Ok(Usefulness::NotUseful);
+        }
         _ => (),
     }
 
diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs
index b439915c7a..9c506112da 100644
--- a/crates/hir_ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs
@@ -11,7 +11,9 @@ use hir_def::{
 };
 use hir_expand::diagnostics::DiagnosticSink;
 
-use crate::{db::HirDatabase, diagnostics::MissingUnsafe, InferenceResult, Ty};
+use crate::{
+    db::HirDatabase, diagnostics::MissingUnsafe, ApplicationTy, InferenceResult, Ty, TypeCtor,
+};
 
 pub(super) struct UnsafeValidator<'a, 'b: 'a> {
     owner: DefWithBodyId,
@@ -110,7 +112,7 @@ fn walk_unsafe(
             }
         }
         Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
-            if let Ty::RawPtr(..) = &infer[*expr] {
+            if let Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. }) = &infer[*expr] {
                 unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block });
             }
         }
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index 179f7ff442..b4801cb215 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -3,8 +3,9 @@
 use std::{borrow::Cow, fmt};
 
 use crate::{
-    db::HirDatabase, primitive, utils::generics, CallableDefId, CallableSig, GenericPredicate,
-    Lifetime, Obligation, OpaqueTy, OpaqueTyId, ProjectionTy, Scalar, Substs, TraitRef, Ty,
+    db::HirDatabase, primitive, utils::generics, ApplicationTy, CallableDefId, FnSig,
+    GenericPredicate, Lifetime, Obligation, OpaqueTy, OpaqueTyId, ProjectionTy, Scalar, Substs,
+    TraitRef, Ty, TypeCtor,
 };
 use arrayvec::ArrayVec;
 use hir_def::{
@@ -234,62 +235,39 @@ impl HirDisplay for &Ty {
     }
 }
 
-impl HirDisplay for ProjectionTy {
-    fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
-        if f.should_truncate() {
-            return write!(f, "{}", TYPE_HINT_TRUNCATION);
-        }
-
-        let trait_ = f.db.trait_data(self.trait_(f.db));
-        let first_parameter = self.parameters[0].into_displayable(
-            f.db,
-            f.max_size,
-            f.omit_verbose_types,
-            f.display_target,
-        );
-        write!(f, "<{} as {}", first_parameter, trait_.name)?;
-        if self.parameters.len() > 1 {
-            write!(f, "<")?;
-            f.write_joined(&self.parameters[1..], ", ")?;
-            write!(f, ">")?;
-        }
-        write!(f, ">::{}", f.db.type_alias_data(self.associated_ty).name)?;
-        Ok(())
-    }
-}
-
-impl HirDisplay for Ty {
+impl HirDisplay for ApplicationTy {
     fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
         if f.should_truncate() {
             return write!(f, "{}", TYPE_HINT_TRUNCATION);
         }
 
-        match self {
-            Ty::Never => write!(f, "!")?,
-            Ty::Str => write!(f, "str")?,
-            Ty::Scalar(Scalar::Bool) => write!(f, "bool")?,
-            Ty::Scalar(Scalar::Char) => write!(f, "char")?,
-            &Ty::Scalar(Scalar::Float(t)) => write!(f, "{}", primitive::float_ty_to_string(t))?,
-            &Ty::Scalar(Scalar::Int(t)) => write!(f, "{}", primitive::int_ty_to_string(t))?,
-            &Ty::Scalar(Scalar::Uint(t)) => write!(f, "{}", primitive::uint_ty_to_string(t))?,
-            Ty::Slice(parameters) => {
-                let t = parameters.as_single();
+        match self.ctor {
+            TypeCtor::Scalar(Scalar::Bool) => write!(f, "bool")?,
+            TypeCtor::Scalar(Scalar::Char) => write!(f, "char")?,
+            TypeCtor::Scalar(Scalar::Float(t)) => {
+                write!(f, "{}", primitive::float_ty_to_string(t))?
+            }
+            TypeCtor::Scalar(Scalar::Int(t)) => write!(f, "{}", primitive::int_ty_to_string(t))?,
+            TypeCtor::Scalar(Scalar::Uint(t)) => write!(f, "{}", primitive::uint_ty_to_string(t))?,
+            TypeCtor::Str => write!(f, "str")?,
+            TypeCtor::Slice => {
+                let t = self.parameters.as_single();
                 write!(f, "[")?;
                 t.hir_fmt(f)?;
                 write!(f, "]")?;
             }
-            Ty::Array(parameters) => {
-                let t = parameters.as_single();
+            TypeCtor::Array => {
+                let t = self.parameters.as_single();
                 write!(f, "[")?;
                 t.hir_fmt(f)?;
                 write!(f, "; _]")?;
             }
-            Ty::RawPtr(m, parameters) | Ty::Ref(m, parameters) => {
-                let t = parameters.as_single();
+            TypeCtor::RawPtr(m) | TypeCtor::Ref(m) => {
+                let t = self.parameters.as_single();
                 let ty_display =
                     t.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target);
 
-                if matches!(self, Ty::RawPtr(..)) {
+                if matches!(self.ctor, TypeCtor::RawPtr(_)) {
                     write!(f, "*{}", m.as_keyword_for_ptr())?;
                 } else {
                     write!(f, "&{}", m.as_keyword_for_ref())?;
@@ -330,24 +308,25 @@ impl HirDisplay for Ty {
                     write!(f, "{}", ty_display)?;
                 }
             }
-            Ty::Tuple(_, substs) => {
-                if substs.len() == 1 {
+            TypeCtor::Never => write!(f, "!")?,
+            TypeCtor::Tuple { .. } => {
+                let ts = &self.parameters;
+                if ts.len() == 1 {
                     write!(f, "(")?;
-                    substs[0].hir_fmt(f)?;
+                    ts[0].hir_fmt(f)?;
                     write!(f, ",)")?;
                 } else {
                     write!(f, "(")?;
-                    f.write_joined(&*substs.0, ", ")?;
+                    f.write_joined(&*ts.0, ", ")?;
                     write!(f, ")")?;
                 }
             }
-            Ty::Function(fn_ptr) => {
-                let sig = CallableSig::from_fn_ptr(fn_ptr);
+            TypeCtor::FnPtr { is_varargs, .. } => {
+                let sig = FnSig::from_fn_ptr_substs(&self.parameters, is_varargs);
                 sig.hir_fmt(f)?;
             }
-            Ty::FnDef(def, parameters) => {
-                let def = *def;
-                let sig = f.db.callable_item_signature(def).subst(parameters);
+            TypeCtor::FnDef(def) => {
+                let sig = f.db.callable_item_signature(def).subst(&self.parameters);
                 match def {
                     CallableDefId::FunctionId(ff) => {
                         write!(f, "fn {}", f.db.function_data(ff).name)?
@@ -357,7 +336,7 @@ impl HirDisplay for Ty {
                         write!(f, "{}", f.db.enum_data(e.parent).variants[e.local_id].name)?
                     }
                 };
-                if parameters.len() > 0 {
+                if self.parameters.len() > 0 {
                     let generics = generics(f.db.upcast(), def.into());
                     let (parent_params, self_param, type_params, _impl_trait_params) =
                         generics.provenance_split();
@@ -365,7 +344,7 @@ impl HirDisplay for Ty {
                     // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self?
                     if total_len > 0 {
                         write!(f, "<")?;
-                        f.write_joined(&parameters.0[..total_len], ", ")?;
+                        f.write_joined(&self.parameters.0[..total_len], ", ")?;
                         write!(f, ">")?;
                     }
                 }
@@ -384,10 +363,10 @@ impl HirDisplay for Ty {
                     write!(f, " -> {}", ret_display)?;
                 }
             }
-            Ty::Adt(def_id, parameters) => {
+            TypeCtor::Adt(def_id) => {
                 match f.display_target {
                     DisplayTarget::Diagnostics | DisplayTarget::Test => {
-                        let name = match *def_id {
+                        let name = match def_id {
                             AdtId::StructId(it) => f.db.struct_data(it).name.clone(),
                             AdtId::UnionId(it) => f.db.union_data(it).name.clone(),
                             AdtId::EnumId(it) => f.db.enum_data(it).name.clone(),
@@ -397,7 +376,7 @@ impl HirDisplay for Ty {
                     DisplayTarget::SourceCode { module_id } => {
                         if let Some(path) = find_path::find_path(
                             f.db.upcast(),
-                            ItemInNs::Types((*def_id).into()),
+                            ItemInNs::Types(def_id.into()),
                             module_id,
                         ) {
                             write!(f, "{}", path)?;
@@ -409,18 +388,19 @@ impl HirDisplay for Ty {
                     }
                 }
 
-                if parameters.len() > 0 {
+                if self.parameters.len() > 0 {
                     let parameters_to_write =
                         if f.display_target.is_source_code() || f.omit_verbose_types() {
                             match self
+                                .ctor
                                 .as_generic_def()
                                 .map(|generic_def_id| f.db.generic_defaults(generic_def_id))
                                 .filter(|defaults| !defaults.is_empty())
                             {
-                                None => parameters.0.as_ref(),
+                                None => self.parameters.0.as_ref(),
                                 Some(default_parameters) => {
                                     let mut default_from = 0;
-                                    for (i, parameter) in parameters.iter().enumerate() {
+                                    for (i, parameter) in self.parameters.iter().enumerate() {
                                         match (parameter, default_parameters.get(i)) {
                                             (&Ty::Unknown, _) | (_, None) => {
                                                 default_from = i + 1;
@@ -428,18 +408,18 @@ impl HirDisplay for Ty {
                                             (_, Some(default_parameter)) => {
                                                 let actual_default = default_parameter
                                                     .clone()
-                                                    .subst(&parameters.prefix(i));
+                                                    .subst(&self.parameters.prefix(i));
                                                 if parameter != &actual_default {
                                                     default_from = i + 1;
                                                 }
                                             }
                                         }
                                     }
-                                    &parameters.0[0..default_from]
+                                    &self.parameters.0[0..default_from]
                                 }
                             }
                         } else {
-                            parameters.0.as_ref()
+                            self.parameters.0.as_ref()
                         };
                     if !parameters_to_write.is_empty() {
                         write!(f, "<")?;
@@ -448,54 +428,61 @@ impl HirDisplay for Ty {
                     }
                 }
             }
-            Ty::AssociatedType(type_alias, parameters) => {
+            TypeCtor::AssociatedType(type_alias) => {
                 let trait_ = match type_alias.lookup(f.db.upcast()).container {
                     AssocContainerId::TraitId(it) => it,
                     _ => panic!("not an associated type"),
                 };
                 let trait_ = f.db.trait_data(trait_);
-                let type_alias_data = f.db.type_alias_data(*type_alias);
+                let type_alias_data = f.db.type_alias_data(type_alias);
 
                 // Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types)
                 if f.display_target.is_test() {
                     write!(f, "{}::{}", trait_.name, type_alias_data.name)?;
-                    if parameters.len() > 0 {
+                    if self.parameters.len() > 0 {
                         write!(f, "<")?;
-                        f.write_joined(&*parameters.0, ", ")?;
+                        f.write_joined(&*self.parameters.0, ", ")?;
                         write!(f, ">")?;
                     }
                 } else {
-                    let projection_ty =
-                        ProjectionTy { associated_ty: *type_alias, parameters: parameters.clone() };
+                    let projection_ty = ProjectionTy {
+                        associated_ty: type_alias,
+                        parameters: self.parameters.clone(),
+                    };
 
                     projection_ty.hir_fmt(f)?;
                 }
             }
-            Ty::ForeignType(type_alias) => {
-                let type_alias = f.db.type_alias_data(*type_alias);
+            TypeCtor::ForeignType(type_alias) => {
+                let type_alias = f.db.type_alias_data(type_alias);
                 write!(f, "{}", type_alias.name)?;
+                if self.parameters.len() > 0 {
+                    write!(f, "<")?;
+                    f.write_joined(&*self.parameters.0, ", ")?;
+                    write!(f, ">")?;
+                }
             }
-            Ty::OpaqueType(opaque_ty_id, parameters) => {
+            TypeCtor::OpaqueType(opaque_ty_id) => {
                 match opaque_ty_id {
-                    &OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
+                    OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
                         let datas =
                             f.db.return_type_impl_traits(func).expect("impl trait id without data");
                         let data = (*datas)
                             .as_ref()
                             .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
-                        let bounds = data.subst(&parameters);
+                        let bounds = data.subst(&self.parameters);
                         write_bounds_like_dyn_trait_with_prefix("impl", &bounds.value, f)?;
                         // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution
                     }
                     OpaqueTyId::AsyncBlockTypeImplTrait(..) => {
                         write!(f, "impl Future<Output = ")?;
-                        parameters[0].hir_fmt(f)?;
+                        self.parameters[0].hir_fmt(f)?;
                         write!(f, ">")?;
                     }
                 }
             }
-            Ty::Closure(.., substs) => {
-                let sig = substs[0].callable_sig(f.db);
+            TypeCtor::Closure { .. } => {
+                let sig = self.parameters[0].callable_sig(f.db);
                 if let Some(sig) = sig {
                     if sig.params().is_empty() {
                         write!(f, "||")?;
@@ -518,6 +505,43 @@ impl HirDisplay for Ty {
                     write!(f, "{{closure}}")?;
                 }
             }
+        }
+        Ok(())
+    }
+}
+
+impl HirDisplay for ProjectionTy {
+    fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
+        if f.should_truncate() {
+            return write!(f, "{}", TYPE_HINT_TRUNCATION);
+        }
+
+        let trait_ = f.db.trait_data(self.trait_(f.db));
+        let first_parameter = self.parameters[0].into_displayable(
+            f.db,
+            f.max_size,
+            f.omit_verbose_types,
+            f.display_target,
+        );
+        write!(f, "<{} as {}", first_parameter, trait_.name)?;
+        if self.parameters.len() > 1 {
+            write!(f, "<")?;
+            f.write_joined(&self.parameters[1..], ", ")?;
+            write!(f, ">")?;
+        }
+        write!(f, ">::{}", f.db.type_alias_data(self.associated_ty).name)?;
+        Ok(())
+    }
+}
+
+impl HirDisplay for Ty {
+    fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
+        if f.should_truncate() {
+            return write!(f, "{}", TYPE_HINT_TRUNCATION);
+        }
+
+        match self {
+            Ty::Apply(a_ty) => a_ty.hir_fmt(f)?,
             Ty::Projection(p_ty) => p_ty.hir_fmt(f)?,
             Ty::Placeholder(id) => {
                 let generics = generics(f.db.upcast(), id.parent);
@@ -571,7 +595,7 @@ impl HirDisplay for Ty {
     }
 }
 
-impl HirDisplay for CallableSig {
+impl HirDisplay for FnSig {
     fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
         write!(f, "fn(")?;
         f.write_joined(self.params(), ", ")?;
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index a1769729ff..657f011d26 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -38,7 +38,7 @@ use syntax::SmolStr;
 use super::{
     primitive::{FloatTy, IntTy},
     traits::{Guidance, Obligation, ProjectionPredicate, Solution},
-    InEnvironment, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeWalk,
+    InEnvironment, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
 };
 use crate::{
     db::HirDatabase, infer::diagnostics::InferenceDiagnostic, lower::ImplTraitLoweringMode, Scalar,
@@ -46,6 +46,15 @@ use crate::{
 
 pub(crate) use unify::unify;
 
+macro_rules! ty_app {
+    ($ctor:pat, $param:pat) => {
+        crate::Ty::Apply(crate::ApplicationTy { ctor: $ctor, parameters: $param })
+    };
+    ($ctor:pat) => {
+        ty_app!($ctor, _)
+    };
+}
+
 mod unify;
 mod path;
 mod expr;
@@ -675,9 +684,9 @@ impl InferTy {
     fn fallback_value(self) -> Ty {
         match self {
             InferTy::TypeVar(..) => Ty::Unknown,
-            InferTy::IntVar(..) => Ty::Scalar(Scalar::Int(IntTy::I32)),
-            InferTy::FloatVar(..) => Ty::Scalar(Scalar::Float(FloatTy::F64)),
-            InferTy::MaybeNeverTypeVar(..) => Ty::Never,
+            InferTy::IntVar(..) => Ty::simple(TypeCtor::Scalar(Scalar::Int(IntTy::I32))),
+            InferTy::FloatVar(..) => Ty::simple(TypeCtor::Scalar(Scalar::Float(FloatTy::F64))),
+            InferTy::MaybeNeverTypeVar(..) => Ty::simple(TypeCtor::Never),
         }
     }
 }
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs
index 4cca359048..32c7c57cd6 100644
--- a/crates/hir_ty/src/infer/coerce.rs
+++ b/crates/hir_ty/src/infer/coerce.rs
@@ -7,7 +7,7 @@
 use hir_def::{lang_item::LangItemTarget, type_ref::Mutability};
 use test_utils::mark;
 
-use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty};
+use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty, TypeCtor};
 
 use super::{unify::TypeVarValue, InEnvironment, InferTy, InferenceContext};
 
@@ -33,7 +33,7 @@ impl<'a> InferenceContext<'a> {
         } else if self.coerce(ty2, ty1) {
             ty1.clone()
         } else {
-            if let (Ty::FnDef(..), Ty::FnDef(..)) = (ty1, ty2) {
+            if let (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnDef(_))) = (ty1, ty2) {
                 mark::hit!(coerce_fn_reification);
                 // Special case: two function types. Try to coerce both to
                 // pointers to have a chance at getting a match. See
@@ -53,12 +53,12 @@ impl<'a> InferenceContext<'a> {
     fn coerce_inner(&mut self, mut from_ty: Ty, to_ty: &Ty) -> bool {
         match (&from_ty, to_ty) {
             // Never type will make type variable to fallback to Never Type instead of Unknown.
-            (Ty::Never, Ty::Infer(InferTy::TypeVar(tv))) => {
+            (ty_app!(TypeCtor::Never), Ty::Infer(InferTy::TypeVar(tv))) => {
                 let var = self.table.new_maybe_never_type_var();
                 self.table.var_unification_table.union_value(*tv, TypeVarValue::Known(var));
                 return true;
             }
-            (Ty::Never, _) => return true,
+            (ty_app!(TypeCtor::Never), _) => return true,
 
             // Trivial cases, this should go after `never` check to
             // avoid infer result type to be never
@@ -71,33 +71,38 @@ impl<'a> InferenceContext<'a> {
 
         // Pointer weakening and function to pointer
         match (&mut from_ty, to_ty) {
-            // `*mut T` -> `*const T`
+            // `*mut T`, `&mut T, `&T`` -> `*const T`
             // `&mut T` -> `&T`
-            (Ty::RawPtr(m1, ..), Ty::RawPtr(m2 @ Mutability::Shared, ..))
-            | (Ty::Ref(m1, ..), Ty::Ref(m2 @ Mutability::Shared, ..)) => {
-                *m1 = *m2;
-            }
-            // `&T` -> `*const T`
-            // `&mut T` -> `*mut T`/`*const T`
-            (Ty::Ref(.., substs), &Ty::RawPtr(m2 @ Mutability::Shared, ..))
-            | (Ty::Ref(Mutability::Mut, substs), &Ty::RawPtr(m2, ..)) => {
-                from_ty = Ty::RawPtr(m2, substs.clone());
+            // `&mut T` -> `*mut T`
+            (ty_app!(c1@TypeCtor::RawPtr(_)), ty_app!(c2@TypeCtor::RawPtr(Mutability::Shared)))
+            | (ty_app!(c1@TypeCtor::Ref(_)), ty_app!(c2@TypeCtor::RawPtr(Mutability::Shared)))
+            | (ty_app!(c1@TypeCtor::Ref(_)), ty_app!(c2@TypeCtor::Ref(Mutability::Shared)))
+            | (ty_app!(c1@TypeCtor::Ref(Mutability::Mut)), ty_app!(c2@TypeCtor::RawPtr(_))) => {
+                *c1 = *c2;
             }
 
-            // Illegal mutability conversion
-            (Ty::RawPtr(Mutability::Shared, ..), Ty::RawPtr(Mutability::Mut, ..))
-            | (Ty::Ref(Mutability::Shared, ..), Ty::Ref(Mutability::Mut, ..)) => return false,
+            // Illegal mutablity conversion
+            (
+                ty_app!(TypeCtor::RawPtr(Mutability::Shared)),
+                ty_app!(TypeCtor::RawPtr(Mutability::Mut)),
+            )
+            | (
+                ty_app!(TypeCtor::Ref(Mutability::Shared)),
+                ty_app!(TypeCtor::Ref(Mutability::Mut)),
+            ) => return false,
 
             // `{function_type}` -> `fn()`
-            (Ty::FnDef(..), Ty::Function { .. }) => match from_ty.callable_sig(self.db) {
-                None => return false,
-                Some(sig) => {
-                    from_ty = Ty::fn_ptr(sig);
+            (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnPtr { .. })) => {
+                match from_ty.callable_sig(self.db) {
+                    None => return false,
+                    Some(sig) => {
+                        from_ty = Ty::fn_ptr(sig);
+                    }
                 }
-            },
+            }
 
-            (Ty::Closure(.., substs), Ty::Function { .. }) => {
-                from_ty = substs[0].clone();
+            (ty_app!(TypeCtor::Closure { .. }, params), ty_app!(TypeCtor::FnPtr { .. })) => {
+                from_ty = params[0].clone();
             }
 
             _ => {}
@@ -110,7 +115,9 @@ impl<'a> InferenceContext<'a> {
         // Auto Deref if cannot coerce
         match (&from_ty, to_ty) {
             // FIXME: DerefMut
-            (Ty::Ref(_, st1), Ty::Ref(_, st2)) => self.unify_autoderef_behind_ref(&st1[0], &st2[0]),
+            (ty_app!(TypeCtor::Ref(_), st1), ty_app!(TypeCtor::Ref(_), st2)) => {
+                self.unify_autoderef_behind_ref(&st1[0], &st2[0])
+            }
 
             // Otherwise, normal unify
             _ => self.unify(&from_ty, to_ty),
@@ -171,17 +178,17 @@ impl<'a> InferenceContext<'a> {
             },
         ) {
             let derefed_ty = canonicalized.decanonicalize_ty(derefed_ty.value);
-            let from_ty = self.resolve_ty_shallow(&derefed_ty);
-            // Stop when constructor matches.
-            if from_ty.equals_ctor(&to_ty) {
-                // It will not recurse to `coerce`.
-                return match (from_ty.substs(), to_ty.substs()) {
-                    (Some(st1), Some(st2)) => self.table.unify_substs(st1, st2, 0),
-                    (None, None) => true,
-                    _ => false,
-                };
-            } else if self.table.unify_inner_trivial(&derefed_ty, &to_ty, 0) {
-                return true;
+            match (&*self.resolve_ty_shallow(&derefed_ty), &*to_ty) {
+                // Stop when constructor matches.
+                (ty_app!(from_ctor, st1), ty_app!(to_ctor, st2)) if from_ctor == to_ctor => {
+                    // It will not recurse to `coerce`.
+                    return self.table.unify_substs(st1, st2, 0);
+                }
+                _ => {
+                    if self.table.unify_inner_trivial(&derefed_ty, &to_ty, 0) {
+                        return true;
+                    }
+                }
             }
         }
 
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 23d4ac8ef8..3fec0e4318 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -18,8 +18,8 @@ use crate::{
     primitive::{self, UintTy},
     traits::{FnTrait, InEnvironment},
     utils::{generics, variant_data, Generics},
-    Binders, CallableDefId, FnPointer, FnSig, InferTy, Mutability, Obligation, OpaqueTyId, Rawness,
-    Scalar, Substs, TraitRef, Ty,
+    ApplicationTy, Binders, CallableDefId, InferTy, Mutability, Obligation, OpaqueTyId, Rawness,
+    Scalar, Substs, TraitRef, Ty, TypeCtor,
 };
 
 use super::{
@@ -82,7 +82,10 @@ impl<'a> InferenceContext<'a> {
             arg_tys.push(arg);
         }
         let parameters = param_builder.build();
-        let arg_ty = Ty::Tuple(num_args, parameters);
+        let arg_ty = Ty::Apply(ApplicationTy {
+            ctor: TypeCtor::Tuple { cardinality: num_args as u16 },
+            parameters,
+        });
         let substs =
             Substs::build_for_generics(&generic_params).push(ty.clone()).push(arg_ty).build();
 
@@ -117,7 +120,10 @@ impl<'a> InferenceContext<'a> {
             Expr::Missing => Ty::Unknown,
             Expr::If { condition, then_branch, else_branch } => {
                 // if let is desugared to match, so this is always simple if
-                self.infer_expr(*condition, &Expectation::has_type(Ty::Scalar(Scalar::Bool)));
+                self.infer_expr(
+                    *condition,
+                    &Expectation::has_type(Ty::simple(TypeCtor::Scalar(Scalar::Bool))),
+                );
 
                 let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
                 let mut both_arms_diverge = Diverges::Always;
@@ -172,7 +178,7 @@ impl<'a> InferenceContext<'a> {
                 // existenail type AsyncBlockImplTrait<InnerType>: Future<Output = InnerType>
                 let inner_ty = self.infer_expr(*body, &Expectation::none());
                 let opaque_ty_id = OpaqueTyId::AsyncBlockTypeImplTrait(self.owner, *body);
-                Ty::OpaqueType(opaque_ty_id, Substs::single(inner_ty))
+                Ty::apply_one(TypeCtor::OpaqueType(opaque_ty_id), inner_ty)
             }
             Expr::Loop { body, label } => {
                 self.breakables.push(BreakableContext {
@@ -190,7 +196,7 @@ impl<'a> InferenceContext<'a> {
                 if ctxt.may_break {
                     ctxt.break_ty
                 } else {
-                    Ty::Never
+                    Ty::simple(TypeCtor::Never)
                 }
             }
             Expr::While { condition, body, label } => {
@@ -200,7 +206,10 @@ impl<'a> InferenceContext<'a> {
                     label: label.map(|label| self.body[label].name.clone()),
                 });
                 // while let is desugared to a match loop, so this is always simple while
-                self.infer_expr(*condition, &Expectation::has_type(Ty::Scalar(Scalar::Bool)));
+                self.infer_expr(
+                    *condition,
+                    &Expectation::has_type(Ty::simple(TypeCtor::Scalar(Scalar::Bool))),
+                );
                 self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
                 let _ctxt = self.breakables.pop().expect("breakable stack broken");
                 // the body may not run, so it diverging doesn't mean we diverge
@@ -247,12 +256,12 @@ impl<'a> InferenceContext<'a> {
                     None => self.table.new_type_var(),
                 };
                 sig_tys.push(ret_ty.clone());
-                let sig_ty = Ty::Function(FnPointer {
-                    num_args: sig_tys.len() - 1,
-                    sig: FnSig { variadic: false },
-                    substs: Substs(sig_tys.clone().into()),
-                });
-                let closure_ty = Ty::Closure(self.owner, tgt_expr, Substs::single(sig_ty));
+                let sig_ty = Ty::apply(
+                    TypeCtor::FnPtr { num_args: sig_tys.len() as u16 - 1, is_varargs: false },
+                    Substs(sig_tys.clone().into()),
+                );
+                let closure_ty =
+                    Ty::apply_one(TypeCtor::Closure { def: self.owner, expr: tgt_expr }, sig_ty);
 
                 // Eagerly try to relate the closure type with the expected
                 // type, otherwise we often won't have enough information to
@@ -303,8 +312,11 @@ impl<'a> InferenceContext<'a> {
             Expr::Match { expr, arms } => {
                 let input_ty = self.infer_expr(*expr, &Expectation::none());
 
-                let mut result_ty =
-                    if arms.is_empty() { Ty::Never } else { self.table.new_type_var() };
+                let mut result_ty = if arms.is_empty() {
+                    Ty::simple(TypeCtor::Never)
+                } else {
+                    self.table.new_type_var()
+                };
 
                 let matchee_diverges = self.diverges;
                 let mut all_arms_diverge = Diverges::Always;
@@ -315,7 +327,7 @@ impl<'a> InferenceContext<'a> {
                     if let Some(guard_expr) = arm.guard {
                         self.infer_expr(
                             guard_expr,
-                            &Expectation::has_type(Ty::Scalar(Scalar::Bool)),
+                            &Expectation::has_type(Ty::simple(TypeCtor::Scalar(Scalar::Bool))),
                         );
                     }
 
@@ -333,7 +345,7 @@ impl<'a> InferenceContext<'a> {
                 let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
                 self.infer_path(&resolver, p, tgt_expr.into()).unwrap_or(Ty::Unknown)
             }
-            Expr::Continue { .. } => Ty::Never,
+            Expr::Continue { .. } => Ty::simple(TypeCtor::Never),
             Expr::Break { expr, label } => {
                 let val_ty = if let Some(expr) = expr {
                     self.infer_expr(*expr, &Expectation::none())
@@ -358,7 +370,8 @@ impl<'a> InferenceContext<'a> {
                         expr: tgt_expr,
                     });
                 }
-                Ty::Never
+
+                Ty::simple(TypeCtor::Never)
             }
             Expr::Return { expr } => {
                 if let Some(expr) = expr {
@@ -367,14 +380,14 @@ impl<'a> InferenceContext<'a> {
                     let unit = Ty::unit();
                     self.coerce(&unit, &self.return_ty.clone());
                 }
-                Ty::Never
+                Ty::simple(TypeCtor::Never)
             }
             Expr::Yield { expr } => {
                 // FIXME: track yield type for coercion
                 if let Some(expr) = expr {
                     self.infer_expr(*expr, &Expectation::none());
                 }
-                Ty::Never
+                Ty::simple(TypeCtor::Never)
             }
             Expr::RecordLit { path, fields, spread } => {
                 let (ty, def_id) = self.resolve_variant(path.as_ref());
@@ -384,7 +397,7 @@ impl<'a> InferenceContext<'a> {
 
                 self.unify(&ty, &expected.ty);
 
-                let substs = ty.substs().cloned().unwrap_or_else(Substs::empty);
+                let substs = ty.substs().unwrap_or_else(Substs::empty);
                 let field_types = def_id.map(|it| self.db.field_types(it)).unwrap_or_default();
                 let variant_data = def_id.map(|it| variant_data(self.db.upcast(), it));
                 for (field_idx, field) in fields.iter().enumerate() {
@@ -423,23 +436,30 @@ impl<'a> InferenceContext<'a> {
                     },
                 )
                 .find_map(|derefed_ty| match canonicalized.decanonicalize_ty(derefed_ty.value) {
-                    Ty::Tuple(_, substs) => {
-                        name.as_tuple_index().and_then(|idx| substs.0.get(idx).cloned())
-                    }
-                    Ty::Adt(AdtId::StructId(s), parameters) => {
-                        self.db.struct_data(s).variant_data.field(name).map(|local_id| {
-                            let field = FieldId { parent: s.into(), local_id };
-                            self.write_field_resolution(tgt_expr, field);
-                            self.db.field_types(s.into())[field.local_id].clone().subst(&parameters)
-                        })
-                    }
-                    Ty::Adt(AdtId::UnionId(u), parameters) => {
-                        self.db.union_data(u).variant_data.field(name).map(|local_id| {
-                            let field = FieldId { parent: u.into(), local_id };
-                            self.write_field_resolution(tgt_expr, field);
-                            self.db.field_types(u.into())[field.local_id].clone().subst(&parameters)
-                        })
-                    }
+                    Ty::Apply(a_ty) => match a_ty.ctor {
+                        TypeCtor::Tuple { .. } => name
+                            .as_tuple_index()
+                            .and_then(|idx| a_ty.parameters.0.get(idx).cloned()),
+                        TypeCtor::Adt(AdtId::StructId(s)) => {
+                            self.db.struct_data(s).variant_data.field(name).map(|local_id| {
+                                let field = FieldId { parent: s.into(), local_id };
+                                self.write_field_resolution(tgt_expr, field);
+                                self.db.field_types(s.into())[field.local_id]
+                                    .clone()
+                                    .subst(&a_ty.parameters)
+                            })
+                        }
+                        TypeCtor::Adt(AdtId::UnionId(u)) => {
+                            self.db.union_data(u).variant_data.field(name).map(|local_id| {
+                                let field = FieldId { parent: u.into(), local_id };
+                                self.write_field_resolution(tgt_expr, field);
+                                self.db.field_types(u.into())[field.local_id]
+                                    .clone()
+                                    .subst(&a_ty.parameters)
+                            })
+                        }
+                        _ => None,
+                    },
                     _ => None,
                 })
                 .unwrap_or(Ty::Unknown);
@@ -477,24 +497,19 @@ impl<'a> InferenceContext<'a> {
                     Expectation::none()
                 };
                 let inner_ty = self.infer_expr_inner(*expr, &expectation);
-                match rawness {
-                    Rawness::RawPtr => Ty::RawPtr(*mutability, Substs::single(inner_ty)),
-                    Rawness::Ref => Ty::Ref(*mutability, Substs::single(inner_ty)),
-                }
+                let ty = match rawness {
+                    Rawness::RawPtr => TypeCtor::RawPtr(*mutability),
+                    Rawness::Ref => TypeCtor::Ref(*mutability),
+                };
+                Ty::apply_one(ty, inner_ty)
             }
             Expr::Box { expr } => {
                 let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
                 if let Some(box_) = self.resolve_boxed_box() {
-                    let mut sb = Substs::builder(generics(self.db.upcast(), box_.into()).len());
+                    let mut sb = Substs::build_for_type_ctor(self.db, TypeCtor::Adt(box_));
                     sb = sb.push(inner_ty);
-                    match self.db.generic_defaults(box_.into()).as_ref() {
-                        [_, alloc_ty, ..] if !alloc_ty.value.is_unknown() => {
-                            sb = sb.push(alloc_ty.value.clone());
-                        }
-                        _ => (),
-                    }
                     sb = sb.fill(repeat_with(|| self.table.new_type_var()));
-                    Ty::Adt(box_, sb.build())
+                    Ty::apply(TypeCtor::Adt(box_), sb.build())
                 } else {
                     Ty::Unknown
                 }
@@ -524,9 +539,14 @@ impl<'a> InferenceContext<'a> {
                     UnaryOp::Neg => {
                         match &inner_ty {
                             // Fast path for builtins
-                            Ty::Scalar(Scalar::Int(_))
-                            | Ty::Scalar(Scalar::Uint(_))
-                            | Ty::Scalar(Scalar::Float(_))
+                            Ty::Apply(ApplicationTy {
+                                ctor: TypeCtor::Scalar(Scalar::Int(_)),
+                                ..
+                            })
+                            | Ty::Apply(ApplicationTy {
+                                ctor: TypeCtor::Scalar(Scalar::Float(_)),
+                                ..
+                            })
                             | Ty::Infer(InferTy::IntVar(..))
                             | Ty::Infer(InferTy::FloatVar(..)) => inner_ty,
                             // Otherwise we resolve via the std::ops::Neg trait
@@ -537,9 +557,18 @@ impl<'a> InferenceContext<'a> {
                     UnaryOp::Not => {
                         match &inner_ty {
                             // Fast path for builtins
-                            Ty::Scalar(Scalar::Bool)
-                            | Ty::Scalar(Scalar::Int(_))
-                            | Ty::Scalar(Scalar::Uint(_))
+                            Ty::Apply(ApplicationTy {
+                                ctor: TypeCtor::Scalar(Scalar::Bool),
+                                ..
+                            })
+                            | Ty::Apply(ApplicationTy {
+                                ctor: TypeCtor::Scalar(Scalar::Int(_)),
+                                ..
+                            })
+                            | Ty::Apply(ApplicationTy {
+                                ctor: TypeCtor::Scalar(Scalar::Uint(_)),
+                                ..
+                            })
                             | Ty::Infer(InferTy::IntVar(..)) => inner_ty,
                             // Otherwise we resolve via the std::ops::Not trait
                             _ => self
@@ -551,7 +580,9 @@ impl<'a> InferenceContext<'a> {
             Expr::BinaryOp { lhs, rhs, op } => match op {
                 Some(op) => {
                     let lhs_expectation = match op {
-                        BinaryOp::LogicOp(..) => Expectation::has_type(Ty::Scalar(Scalar::Bool)),
+                        BinaryOp::LogicOp(..) => {
+                            Expectation::has_type(Ty::simple(TypeCtor::Scalar(Scalar::Bool)))
+                        }
                         _ => Expectation::none(),
                     };
                     let lhs_ty = self.infer_expr(*lhs, &lhs_expectation);
@@ -582,31 +613,31 @@ impl<'a> InferenceContext<'a> {
                 let rhs_ty = rhs.map(|e| self.infer_expr(e, &rhs_expect));
                 match (range_type, lhs_ty, rhs_ty) {
                     (RangeOp::Exclusive, None, None) => match self.resolve_range_full() {
-                        Some(adt) => Ty::Adt(adt, Substs::empty()),
+                        Some(adt) => Ty::simple(TypeCtor::Adt(adt)),
                         None => Ty::Unknown,
                     },
                     (RangeOp::Exclusive, None, Some(ty)) => match self.resolve_range_to() {
-                        Some(adt) => Ty::Adt(adt, Substs::single(ty)),
+                        Some(adt) => Ty::apply_one(TypeCtor::Adt(adt), ty),
                         None => Ty::Unknown,
                     },
                     (RangeOp::Inclusive, None, Some(ty)) => {
                         match self.resolve_range_to_inclusive() {
-                            Some(adt) => Ty::Adt(adt, Substs::single(ty)),
+                            Some(adt) => Ty::apply_one(TypeCtor::Adt(adt), ty),
                             None => Ty::Unknown,
                         }
                     }
                     (RangeOp::Exclusive, Some(_), Some(ty)) => match self.resolve_range() {
-                        Some(adt) => Ty::Adt(adt, Substs::single(ty)),
+                        Some(adt) => Ty::apply_one(TypeCtor::Adt(adt), ty),
                         None => Ty::Unknown,
                     },
                     (RangeOp::Inclusive, Some(_), Some(ty)) => {
                         match self.resolve_range_inclusive() {
-                            Some(adt) => Ty::Adt(adt, Substs::single(ty)),
+                            Some(adt) => Ty::apply_one(TypeCtor::Adt(adt), ty),
                             None => Ty::Unknown,
                         }
                     }
                     (RangeOp::Exclusive, Some(ty), None) => match self.resolve_range_from() {
-                        Some(adt) => Ty::Adt(adt, Substs::single(ty)),
+                        Some(adt) => Ty::apply_one(TypeCtor::Adt(adt), ty),
                         None => Ty::Unknown,
                     },
                     (RangeOp::Inclusive, _, None) => Ty::Unknown,
@@ -640,7 +671,7 @@ impl<'a> InferenceContext<'a> {
             }
             Expr::Tuple { exprs } => {
                 let mut tys = match &expected.ty {
-                    Ty::Tuple(_, substs) => substs
+                    ty_app!(TypeCtor::Tuple { .. }, st) => st
                         .iter()
                         .cloned()
                         .chain(repeat_with(|| self.table.new_type_var()))
@@ -653,11 +684,15 @@ impl<'a> InferenceContext<'a> {
                     self.infer_expr_coerce(*expr, &Expectation::has_type(ty.clone()));
                 }
 
-                Ty::Tuple(tys.len(), Substs(tys.into()))
+                Ty::apply(TypeCtor::Tuple { cardinality: tys.len() as u16 }, Substs(tys.into()))
             }
             Expr::Array(array) => {
                 let elem_ty = match &expected.ty {
-                    Ty::Array(st) | Ty::Slice(st) => st.as_single().clone(),
+                    // FIXME: remove when https://github.com/rust-lang/rust/issues/80501 is fixed
+                    #[allow(unreachable_patterns)]
+                    ty_app!(TypeCtor::Array, st) | ty_app!(TypeCtor::Slice, st) => {
+                        st.as_single().clone()
+                    }
                     _ => self.table.new_type_var(),
                 };
 
@@ -674,38 +709,42 @@ impl<'a> InferenceContext<'a> {
                         );
                         self.infer_expr(
                             *repeat,
-                            &Expectation::has_type(Ty::Scalar(Scalar::Uint(UintTy::Usize))),
+                            &Expectation::has_type(Ty::simple(TypeCtor::Scalar(Scalar::Uint(
+                                UintTy::Usize,
+                            )))),
                         );
                     }
                 }
 
-                Ty::Array(Substs::single(elem_ty))
+                Ty::apply_one(TypeCtor::Array, elem_ty)
             }
             Expr::Literal(lit) => match lit {
-                Literal::Bool(..) => Ty::Scalar(Scalar::Bool),
-                Literal::String(..) => Ty::Ref(Mutability::Shared, Substs::single(Ty::Str)),
+                Literal::Bool(..) => Ty::simple(TypeCtor::Scalar(Scalar::Bool)),
+                Literal::String(..) => {
+                    Ty::apply_one(TypeCtor::Ref(Mutability::Shared), Ty::simple(TypeCtor::Str))
+                }
                 Literal::ByteString(..) => {
-                    let byte_type = Ty::Scalar(Scalar::Uint(UintTy::U8));
-                    let array_type = Ty::Array(Substs::single(byte_type));
-                    Ty::Ref(Mutability::Shared, Substs::single(array_type))
+                    let byte_type = Ty::simple(TypeCtor::Scalar(Scalar::Uint(UintTy::U8)));
+                    let array_type = Ty::apply_one(TypeCtor::Array, byte_type);
+                    Ty::apply_one(TypeCtor::Ref(Mutability::Shared), array_type)
                 }
-                Literal::Char(..) => Ty::Scalar(Scalar::Char),
+                Literal::Char(..) => Ty::simple(TypeCtor::Scalar(Scalar::Char)),
                 Literal::Int(_v, ty) => match ty {
-                    Some(int_ty) => {
-                        Ty::Scalar(Scalar::Int(primitive::int_ty_from_builtin(*int_ty)))
-                    }
+                    Some(int_ty) => Ty::simple(TypeCtor::Scalar(Scalar::Int(
+                        primitive::int_ty_from_builtin(*int_ty),
+                    ))),
                     None => self.table.new_integer_var(),
                 },
                 Literal::Uint(_v, ty) => match ty {
-                    Some(int_ty) => {
-                        Ty::Scalar(Scalar::Uint(primitive::uint_ty_from_builtin(*int_ty)))
-                    }
+                    Some(int_ty) => Ty::simple(TypeCtor::Scalar(Scalar::Uint(
+                        primitive::uint_ty_from_builtin(*int_ty),
+                    ))),
                     None => self.table.new_integer_var(),
                 },
                 Literal::Float(_v, ty) => match ty {
-                    Some(float_ty) => {
-                        Ty::Scalar(Scalar::Float(primitive::float_ty_from_builtin(*float_ty)))
-                    }
+                    Some(float_ty) => Ty::simple(TypeCtor::Scalar(Scalar::Float(
+                        primitive::float_ty_from_builtin(*float_ty),
+                    ))),
                     None => self.table.new_float_var(),
                 },
             },
@@ -818,7 +857,7 @@ impl<'a> InferenceContext<'a> {
         // Apply autoref so the below unification works correctly
         // FIXME: return correct autorefs from lookup_method
         let actual_receiver_ty = match expected_receiver_ty.as_reference() {
-            Some((_, mutability)) => Ty::Ref(mutability, Substs::single(derefed_receiver_ty)),
+            Some((_, mutability)) => Ty::apply_one(TypeCtor::Ref(mutability), derefed_receiver_ty),
             _ => derefed_receiver_ty,
         };
         self.unify(&expected_receiver_ty, &actual_receiver_ty);
@@ -895,26 +934,30 @@ impl<'a> InferenceContext<'a> {
     }
 
     fn register_obligations_for_call(&mut self, callable_ty: &Ty) {
-        if let &Ty::FnDef(def, ref parameters) = callable_ty {
-            let generic_predicates = self.db.generic_predicates(def.into());
-            for predicate in generic_predicates.iter() {
-                let predicate = predicate.clone().subst(parameters);
-                if let Some(obligation) = Obligation::from_predicate(predicate) {
-                    self.obligations.push(obligation);
+        if let Ty::Apply(a_ty) = callable_ty {
+            if let TypeCtor::FnDef(def) = a_ty.ctor {
+                let generic_predicates = self.db.generic_predicates(def.into());
+                for predicate in generic_predicates.iter() {
+                    let predicate = predicate.clone().subst(&a_ty.parameters);
+                    if let Some(obligation) = Obligation::from_predicate(predicate) {
+                        self.obligations.push(obligation);
+                    }
                 }
-            }
-            // add obligation for trait implementation, if this is a trait method
-            match def {
-                CallableDefId::FunctionId(f) => {
-                    if let AssocContainerId::TraitId(trait_) = f.lookup(self.db.upcast()).container
-                    {
-                        // construct a TraitDef
-                        let substs =
-                            parameters.prefix(generics(self.db.upcast(), trait_.into()).len());
-                        self.obligations.push(Obligation::Trait(TraitRef { trait_, substs }));
+                // add obligation for trait implementation, if this is a trait method
+                match def {
+                    CallableDefId::FunctionId(f) => {
+                        if let AssocContainerId::TraitId(trait_) =
+                            f.lookup(self.db.upcast()).container
+                        {
+                            // construct a TraitDef
+                            let substs = a_ty
+                                .parameters
+                                .prefix(generics(self.db.upcast(), trait_.into()).len());
+                            self.obligations.push(Obligation::Trait(TraitRef { trait_, substs }));
+                        }
                     }
+                    CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => {}
                 }
-                CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => {}
             }
         }
     }
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs
index a318e47f3b..d974f805b5 100644
--- a/crates/hir_ty/src/infer/pat.rs
+++ b/crates/hir_ty/src/infer/pat.rs
@@ -13,7 +13,7 @@ use hir_expand::name::Name;
 use test_utils::mark;
 
 use super::{BindingMode, Expectation, InferenceContext};
-use crate::{utils::variant_data, Substs, Ty};
+use crate::{utils::variant_data, Substs, Ty, TypeCtor};
 
 impl<'a> InferenceContext<'a> {
     fn infer_tuple_struct_pat(
@@ -32,7 +32,7 @@ impl<'a> InferenceContext<'a> {
         }
         self.unify(&ty, expected);
 
-        let substs = ty.substs().cloned().unwrap_or_else(Substs::empty);
+        let substs = ty.substs().unwrap_or_else(Substs::empty);
 
         let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
         let (pre, post) = match ellipsis {
@@ -71,7 +71,7 @@ impl<'a> InferenceContext<'a> {
 
         self.unify(&ty, expected);
 
-        let substs = ty.substs().cloned().unwrap_or_else(Substs::empty);
+        let substs = ty.substs().unwrap_or_else(Substs::empty);
 
         let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
         for subpat in subpats {
@@ -138,7 +138,10 @@ impl<'a> InferenceContext<'a> {
                 inner_tys.extend(expectations_iter.by_ref().take(n_uncovered_patterns).cloned());
                 inner_tys.extend(post.iter().zip(expectations_iter).map(infer_pat));
 
-                Ty::Tuple(inner_tys.len(), Substs(inner_tys.into()))
+                Ty::apply(
+                    TypeCtor::Tuple { cardinality: inner_tys.len() as u16 },
+                    Substs(inner_tys.into()),
+                )
             }
             Pat::Or(ref pats) => {
                 if let Some((first_pat, rest)) = pats.split_first() {
@@ -162,7 +165,7 @@ impl<'a> InferenceContext<'a> {
                     _ => &Ty::Unknown,
                 };
                 let subty = self.infer_pat(*pat, expectation, default_bm);
-                Ty::Ref(*mutability, Substs::single(subty))
+                Ty::apply_one(TypeCtor::Ref(*mutability), subty)
             }
             Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat(
                 p.as_ref(),
@@ -195,7 +198,7 @@ impl<'a> InferenceContext<'a> {
 
                 let bound_ty = match mode {
                     BindingMode::Ref(mutability) => {
-                        Ty::Ref(mutability, Substs::single(inner_ty.clone()))
+                        Ty::apply_one(TypeCtor::Ref(mutability), inner_ty.clone())
                     }
                     BindingMode::Move => inner_ty.clone(),
                 };
@@ -204,17 +207,17 @@ impl<'a> InferenceContext<'a> {
                 return inner_ty;
             }
             Pat::Slice { prefix, slice, suffix } => {
-                let (container_ty, elem_ty): (fn(_) -> _, _) = match &expected {
-                    Ty::Array(st) => (Ty::Array, st.as_single().clone()),
-                    Ty::Slice(st) => (Ty::Slice, st.as_single().clone()),
-                    _ => (Ty::Slice, Ty::Unknown),
+                let (container_ty, elem_ty) = match &expected {
+                    ty_app!(TypeCtor::Array, st) => (TypeCtor::Array, st.as_single().clone()),
+                    ty_app!(TypeCtor::Slice, st) => (TypeCtor::Slice, st.as_single().clone()),
+                    _ => (TypeCtor::Slice, Ty::Unknown),
                 };
 
                 for pat_id in prefix.iter().chain(suffix) {
                     self.infer_pat(*pat_id, &elem_ty, default_bm);
                 }
 
-                let pat_ty = container_ty(Substs::single(elem_ty));
+                let pat_ty = Ty::apply_one(container_ty, elem_ty);
                 if let Some(slice_pat_id) = slice {
                     self.infer_pat(*slice_pat_id, &pat_ty, default_bm);
                 }
@@ -236,7 +239,7 @@ impl<'a> InferenceContext<'a> {
                     };
 
                     let inner_ty = self.infer_pat(*inner, inner_expected, default_bm);
-                    Ty::Adt(box_adt, Substs::single(inner_ty))
+                    Ty::apply_one(TypeCtor::Adt(box_adt), inner_ty)
                 }
                 None => Ty::Unknown,
             },
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs
index 2852ad5bf7..57eb8cede7 100644
--- a/crates/hir_ty/src/infer/unify.rs
+++ b/crates/hir_ty/src/infer/unify.rs
@@ -9,7 +9,7 @@ use test_utils::mark;
 use super::{InferenceContext, Obligation};
 use crate::{
     BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferTy, Scalar, Substs,
-    Ty, TyKind, TypeWalk,
+    Ty, TyKind, TypeCtor, TypeWalk,
 };
 
 impl<'a> InferenceContext<'a> {
@@ -257,14 +257,12 @@ impl InferenceTable {
         // try to resolve type vars first
         let ty1 = self.resolve_ty_shallow(ty1);
         let ty2 = self.resolve_ty_shallow(ty2);
-        if ty1.equals_ctor(&ty2) {
-            match (ty1.substs(), ty2.substs()) {
-                (Some(st1), Some(st2)) => self.unify_substs(st1, st2, depth + 1),
-                (None, None) => true,
-                _ => false,
+        match (&*ty1, &*ty2) {
+            (Ty::Apply(a_ty1), Ty::Apply(a_ty2)) if a_ty1.ctor == a_ty2.ctor => {
+                self.unify_substs(&a_ty1.parameters, &a_ty2.parameters, depth + 1)
             }
-        } else {
-            self.unify_inner_trivial(&ty1, &ty2, depth)
+
+            _ => self.unify_inner_trivial(&ty1, &ty2, depth),
         }
     }
 
@@ -302,12 +300,24 @@ impl InferenceTable {
             | (other, Ty::Infer(InferTy::TypeVar(tv)))
             | (Ty::Infer(InferTy::MaybeNeverTypeVar(tv)), other)
             | (other, Ty::Infer(InferTy::MaybeNeverTypeVar(tv)))
-            | (Ty::Infer(InferTy::IntVar(tv)), other @ Ty::Scalar(Scalar::Int(_)))
-            | (other @ Ty::Scalar(Scalar::Int(_)), Ty::Infer(InferTy::IntVar(tv)))
-            | (Ty::Infer(InferTy::IntVar(tv)), other @ Ty::Scalar(Scalar::Uint(_)))
-            | (other @ Ty::Scalar(Scalar::Uint(_)), Ty::Infer(InferTy::IntVar(tv)))
-            | (Ty::Infer(InferTy::FloatVar(tv)), other @ Ty::Scalar(Scalar::Float(_)))
-            | (other @ Ty::Scalar(Scalar::Float(_)), Ty::Infer(InferTy::FloatVar(tv))) => {
+            | (Ty::Infer(InferTy::IntVar(tv)), other @ ty_app!(TypeCtor::Scalar(Scalar::Int(_))))
+            | (other @ ty_app!(TypeCtor::Scalar(Scalar::Int(_))), Ty::Infer(InferTy::IntVar(tv)))
+            | (
+                Ty::Infer(InferTy::IntVar(tv)),
+                other @ ty_app!(TypeCtor::Scalar(Scalar::Uint(_))),
+            )
+            | (
+                other @ ty_app!(TypeCtor::Scalar(Scalar::Uint(_))),
+                Ty::Infer(InferTy::IntVar(tv)),
+            )
+            | (
+                Ty::Infer(InferTy::FloatVar(tv)),
+                other @ ty_app!(TypeCtor::Scalar(Scalar::Float(_))),
+            )
+            | (
+                other @ ty_app!(TypeCtor::Scalar(Scalar::Float(_))),
+                Ty::Infer(InferTy::FloatVar(tv)),
+            ) => {
                 // the type var is unknown since we tried to resolve it
                 self.var_unification_table.union_value(*tv, TypeVarValue::Known(other.clone()));
                 true
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index 1abb0440fd..676519594b 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -25,7 +25,7 @@ mod test_db;
 
 use std::{iter, mem, ops::Deref, sync::Arc};
 
-use base_db::salsa;
+use base_db::{salsa, CrateId};
 use hir_def::{
     builtin_type::BuiltinType,
     expr::ExprId,
@@ -57,6 +57,192 @@ pub enum Lifetime {
     Static,
 }
 
+/// A type constructor or type name: this might be something like the primitive
+/// type `bool`, a struct like `Vec`, or things like function pointers or
+/// tuples.
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
+pub enum TypeCtor {
+    /// a scalar type like `bool` or `u32`
+    Scalar(Scalar),
+
+    /// Structures, enumerations and unions.
+    Adt(AdtId),
+
+    /// The pointee of a string slice. Written as `str`.
+    Str,
+
+    /// The pointee of an array slice.  Written as `[T]`.
+    Slice,
+
+    /// An array with the given length. Written as `[T; n]`.
+    Array,
+
+    /// A raw pointer. Written as `*mut T` or `*const T`
+    RawPtr(Mutability),
+
+    /// A reference; a pointer with an associated lifetime. Written as
+    /// `&'a mut T` or `&'a T`.
+    Ref(Mutability),
+
+    /// The anonymous type of a function declaration/definition. Each
+    /// function has a unique type, which is output (for a function
+    /// named `foo` returning an `i32`) as `fn() -> i32 {foo}`.
+    ///
+    /// This includes tuple struct / enum variant constructors as well.
+    ///
+    /// For example the type of `bar` here:
+    ///
+    /// ```
+    /// fn foo() -> i32 { 1 }
+    /// let bar = foo; // bar: fn() -> i32 {foo}
+    /// ```
+    FnDef(CallableDefId),
+
+    /// A pointer to a function.  Written as `fn() -> i32`.
+    ///
+    /// For example the type of `bar` here:
+    ///
+    /// ```
+    /// fn foo() -> i32 { 1 }
+    /// let bar: fn() -> i32 = foo;
+    /// ```
+    // FIXME make this a Ty variant like in Chalk
+    FnPtr { num_args: u16, is_varargs: bool },
+
+    /// The never type `!`.
+    Never,
+
+    /// 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(TypeAliasId),
+
+    /// This represents a placeholder for an opaque type in situations where we
+    /// don't know the hidden type (i.e. currently almost always). This is
+    /// analogous to the `AssociatedType` type constructor.
+    /// It is also used as the type of async block, with one type parameter
+    /// representing the Future::Output type.
+    OpaqueType(OpaqueTyId),
+
+    /// Represents a foreign type declared in external blocks.
+    ForeignType(TypeAliasId),
+
+    /// The type of a specific closure.
+    ///
+    /// The closure signature is stored in a `FnPtr` type in the first type
+    /// parameter.
+    Closure { def: DefWithBodyId, expr: ExprId },
+}
+
+impl TypeCtor {
+    pub fn num_ty_params(self, db: &dyn HirDatabase) -> usize {
+        match self {
+            TypeCtor::Scalar(_)
+            | TypeCtor::Str
+            | TypeCtor::Never => 0,
+            TypeCtor::Slice
+            | TypeCtor::Array
+            | TypeCtor::RawPtr(_)
+            | TypeCtor::Ref(_)
+            | TypeCtor::Closure { .. } // 1 param representing the signature of the closure
+            => 1,
+            TypeCtor::Adt(adt) => {
+                let generic_params = generics(db.upcast(), adt.into());
+                generic_params.len()
+            }
+            TypeCtor::FnDef(callable) => {
+                let generic_params = generics(db.upcast(), callable.into());
+                generic_params.len()
+            }
+            TypeCtor::AssociatedType(type_alias) => {
+                let generic_params = generics(db.upcast(), type_alias.into());
+                generic_params.len()
+            }
+            TypeCtor::ForeignType(type_alias) => {
+                let generic_params = generics(db.upcast(), type_alias.into());
+                generic_params.len()
+            }
+            TypeCtor::OpaqueType(opaque_ty_id) => {
+                match opaque_ty_id {
+                    OpaqueTyId::ReturnTypeImplTrait(func, _) => {
+                        let generic_params = generics(db.upcast(), func.into());
+                        generic_params.len()
+                    }
+                    // 1 param representing Future::Output type.
+                    OpaqueTyId::AsyncBlockTypeImplTrait(..) => 1,
+                }
+            }
+            TypeCtor::FnPtr { num_args, is_varargs: _ } => num_args as usize + 1,
+            TypeCtor::Tuple { cardinality } => cardinality as usize,
+        }
+    }
+
+    pub fn krate(self, db: &dyn HirDatabase) -> Option<CrateId> {
+        match self {
+            TypeCtor::Scalar(_)
+            | TypeCtor::Str
+            | TypeCtor::Never
+            | TypeCtor::Slice
+            | TypeCtor::Array
+            | TypeCtor::RawPtr(_)
+            | TypeCtor::Ref(_)
+            | TypeCtor::FnPtr { .. }
+            | TypeCtor::Tuple { .. } => None,
+            // Closure's krate is irrelevant for coherence I would think?
+            TypeCtor::Closure { .. } => None,
+            TypeCtor::Adt(adt) => Some(adt.module(db.upcast()).krate()),
+            TypeCtor::FnDef(callable) => Some(callable.krate(db)),
+            TypeCtor::AssociatedType(type_alias) => {
+                Some(type_alias.lookup(db.upcast()).module(db.upcast()).krate())
+            }
+            TypeCtor::ForeignType(type_alias) => {
+                Some(type_alias.lookup(db.upcast()).module(db.upcast()).krate())
+            }
+            TypeCtor::OpaqueType(opaque_ty_id) => match opaque_ty_id {
+                OpaqueTyId::ReturnTypeImplTrait(func, _) => {
+                    Some(func.lookup(db.upcast()).module(db.upcast()).krate())
+                }
+                OpaqueTyId::AsyncBlockTypeImplTrait(def, _) => {
+                    Some(def.module(db.upcast()).krate())
+                }
+            },
+        }
+    }
+
+    pub fn as_generic_def(self) -> Option<GenericDefId> {
+        match self {
+            TypeCtor::Scalar(_)
+            | TypeCtor::Str
+            | TypeCtor::Never
+            | TypeCtor::Slice
+            | TypeCtor::Array
+            | TypeCtor::RawPtr(_)
+            | TypeCtor::Ref(_)
+            | TypeCtor::FnPtr { .. }
+            | TypeCtor::Tuple { .. }
+            | TypeCtor::Closure { .. } => None,
+            TypeCtor::Adt(adt) => Some(adt.into()),
+            TypeCtor::FnDef(callable) => Some(callable.into()),
+            TypeCtor::AssociatedType(type_alias) => Some(type_alias.into()),
+            TypeCtor::ForeignType(type_alias) => Some(type_alias.into()),
+            TypeCtor::OpaqueType(_impl_trait_id) => None,
+        }
+    }
+}
+
+/// A nominal type with (maybe 0) type parameters. This might be a primitive
+/// type like `bool`, a struct, tuple, function pointer, reference or
+/// several other things.
+#[derive(Clone, PartialEq, Eq, Debug, Hash)]
+pub struct ApplicationTy {
+    pub ctor: TypeCtor,
+    pub parameters: Substs,
+}
+
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 pub struct OpaqueTy {
     pub opaque_ty_id: OpaqueTyId,
@@ -99,18 +285,6 @@ impl TypeWalk for ProjectionTy {
     }
 }
 
-#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
-pub struct FnSig {
-    pub variadic: bool,
-}
-
-#[derive(Clone, PartialEq, Eq, Debug, Hash)]
-pub struct FnPointer {
-    pub num_args: usize,
-    pub sig: FnSig,
-    pub substs: Substs,
-}
-
 /// A type.
 ///
 /// See also the `TyKind` enum in rustc (librustc/ty/sty.rs), which represents
@@ -119,79 +293,10 @@ pub struct FnPointer {
 /// This should be cheap to clone.
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 pub enum Ty {
-    /// Structures, enumerations and unions.
-    Adt(AdtId, Substs),
-
-    /// 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(TypeAliasId, Substs),
-
-    /// a scalar type like `bool` or `u32`
-    Scalar(Scalar),
-
-    /// A tuple type.  For example, `(i32, bool)`.
-    Tuple(usize, Substs),
-
-    /// An array with the given length. Written as `[T; n]`.
-    Array(Substs),
-
-    /// The pointee of an array slice.  Written as `[T]`.
-    Slice(Substs),
-
-    /// A raw pointer. Written as `*mut T` or `*const T`
-    RawPtr(Mutability, Substs),
-
-    /// A reference; a pointer with an associated lifetime. Written as
-    /// `&'a mut T` or `&'a T`.
-    Ref(Mutability, Substs),
-
-    /// This represents a placeholder for an opaque type in situations where we
-    /// don't know the hidden type (i.e. currently almost always). This is
-    /// analogous to the `AssociatedType` type constructor.
-    /// It is also used as the type of async block, with one type parameter
-    /// representing the Future::Output type.
-    OpaqueType(OpaqueTyId, Substs),
-
-    /// The anonymous type of a function declaration/definition. Each
-    /// function has a unique type, which is output (for a function
-    /// named `foo` returning an `i32`) as `fn() -> i32 {foo}`.
-    ///
-    /// This includes tuple struct / enum variant constructors as well.
-    ///
-    /// For example the type of `bar` here:
-    ///
-    /// ```
-    /// fn foo() -> i32 { 1 }
-    /// let bar = foo; // bar: fn() -> i32 {foo}
-    /// ```
-    FnDef(CallableDefId, Substs),
-
-    /// The pointee of a string slice. Written as `str`.
-    Str,
-
-    /// The never type `!`.
-    Never,
-
-    /// The type of a specific closure.
-    ///
-    /// The closure signature is stored in a `FnPtr` type in the first type
-    /// parameter.
-    Closure(DefWithBodyId, ExprId, Substs),
-
-    /// Represents a foreign type declared in external blocks.
-    ForeignType(TypeAliasId),
-
-    /// A pointer to a function.  Written as `fn() -> i32`.
-    ///
-    /// For example the type of `bar` here:
-    ///
-    /// ```
-    /// fn foo() -> i32 { 1 }
-    /// let bar: fn() -> i32 = foo;
-    /// ```
-    Function(FnPointer),
+    /// A nominal type with (maybe 0) type parameters. This might be a primitive
+    /// type like `bool`, a struct, tuple, function pointer, reference or
+    /// several other things.
+    Apply(ApplicationTy),
 
     /// A "projection" type corresponds to an (unnormalized)
     /// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the
@@ -315,6 +420,10 @@ impl Substs {
         Substs::builder(generic_params.len())
     }
 
+    pub fn build_for_type_ctor(db: &dyn HirDatabase, type_ctor: TypeCtor) -> SubstsBuilder {
+        Substs::builder(type_ctor.num_ty_params(db))
+    }
+
     fn builder(param_count: usize) -> SubstsBuilder {
         SubstsBuilder { vec: Vec::with_capacity(param_count), param_count }
     }
@@ -546,29 +655,22 @@ pub enum TyKind {
 /// A function signature as seen by type inference: Several parameter types and
 /// one return type.
 #[derive(Clone, PartialEq, Eq, Debug)]
-pub struct CallableSig {
+pub struct FnSig {
     params_and_return: Arc<[Ty]>,
     is_varargs: bool,
 }
 
 /// A polymorphic function signature.
-pub type PolyFnSig = Binders<CallableSig>;
+pub type PolyFnSig = Binders<FnSig>;
 
-impl CallableSig {
-    pub fn from_params_and_return(mut params: Vec<Ty>, ret: Ty, is_varargs: bool) -> CallableSig {
+impl FnSig {
+    pub fn from_params_and_return(mut params: Vec<Ty>, ret: Ty, is_varargs: bool) -> FnSig {
         params.push(ret);
-        CallableSig { params_and_return: params.into(), is_varargs }
+        FnSig { params_and_return: params.into(), is_varargs }
     }
 
-    pub fn from_fn_ptr(fn_ptr: &FnPointer) -> CallableSig {
-        CallableSig {
-            params_and_return: Arc::clone(&fn_ptr.substs.0),
-            is_varargs: fn_ptr.sig.variadic,
-        }
-    }
-
-    pub fn from_substs(substs: &Substs) -> CallableSig {
-        CallableSig { params_and_return: Arc::clone(&substs.0), is_varargs: false }
+    pub fn from_fn_ptr_substs(substs: &Substs, is_varargs: bool) -> FnSig {
+        FnSig { params_and_return: Arc::clone(&substs.0), is_varargs }
     }
 
     pub fn params(&self) -> &[Ty] {
@@ -580,7 +682,7 @@ impl CallableSig {
     }
 }
 
-impl TypeWalk for CallableSig {
+impl TypeWalk for FnSig {
     fn walk(&self, f: &mut impl FnMut(&Ty)) {
         for t in self.params_and_return.iter() {
             t.walk(f);
@@ -599,42 +701,54 @@ impl TypeWalk for CallableSig {
 }
 
 impl Ty {
+    pub fn simple(ctor: TypeCtor) -> Ty {
+        Ty::Apply(ApplicationTy { ctor, parameters: Substs::empty() })
+    }
+    pub fn apply_one(ctor: TypeCtor, param: Ty) -> Ty {
+        Ty::Apply(ApplicationTy { ctor, parameters: Substs::single(param) })
+    }
+    pub fn apply(ctor: TypeCtor, parameters: Substs) -> Ty {
+        Ty::Apply(ApplicationTy { ctor, parameters })
+    }
     pub fn unit() -> Self {
-        Ty::Tuple(0, Substs::empty())
+        Ty::apply(TypeCtor::Tuple { cardinality: 0 }, Substs::empty())
     }
-
-    pub fn fn_ptr(sig: CallableSig) -> Self {
-        Ty::Function(FnPointer {
-            num_args: sig.params().len(),
-            sig: FnSig { variadic: sig.is_varargs },
-            substs: Substs(sig.params_and_return),
-        })
+    pub fn fn_ptr(sig: FnSig) -> Self {
+        Ty::apply(
+            TypeCtor::FnPtr { num_args: sig.params().len() as u16, is_varargs: sig.is_varargs },
+            Substs(sig.params_and_return),
+        )
     }
-
     pub fn builtin(builtin: BuiltinType) -> Self {
-        match builtin {
-            BuiltinType::Char => Ty::Scalar(Scalar::Char),
-            BuiltinType::Bool => Ty::Scalar(Scalar::Bool),
-            BuiltinType::Str => Ty::Str,
-            BuiltinType::Int(t) => Ty::Scalar(Scalar::Int(primitive::int_ty_from_builtin(t))),
-            BuiltinType::Uint(t) => Ty::Scalar(Scalar::Uint(primitive::uint_ty_from_builtin(t))),
-            BuiltinType::Float(t) => Ty::Scalar(Scalar::Float(primitive::float_ty_from_builtin(t))),
-        }
+        Ty::simple(match builtin {
+            BuiltinType::Char => TypeCtor::Scalar(Scalar::Char),
+            BuiltinType::Bool => TypeCtor::Scalar(Scalar::Bool),
+            BuiltinType::Str => TypeCtor::Str,
+            BuiltinType::Int(t) => TypeCtor::Scalar(Scalar::Int(primitive::int_ty_from_builtin(t))),
+            BuiltinType::Uint(t) => {
+                TypeCtor::Scalar(Scalar::Uint(primitive::uint_ty_from_builtin(t)))
+            }
+            BuiltinType::Float(t) => {
+                TypeCtor::Scalar(Scalar::Float(primitive::float_ty_from_builtin(t)))
+            }
+        })
     }
 
     pub fn as_reference(&self) -> Option<(&Ty, Mutability)> {
         match self {
-            Ty::Ref(mutability, parameters) => Some((parameters.as_single(), *mutability)),
+            Ty::Apply(ApplicationTy { ctor: TypeCtor::Ref(mutability), parameters }) => {
+                Some((parameters.as_single(), *mutability))
+            }
             _ => None,
         }
     }
 
     pub fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> {
         match self {
-            Ty::Ref(mutability, parameters) => {
+            Ty::Apply(ApplicationTy { ctor: TypeCtor::Ref(mutability), parameters }) => {
                 Some((parameters.as_single(), Rawness::Ref, *mutability))
             }
-            Ty::RawPtr(mutability, parameters) => {
+            Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(mutability), parameters }) => {
                 Some((parameters.as_single(), Rawness::RawPtr, *mutability))
             }
             _ => None,
@@ -644,7 +758,7 @@ impl Ty {
     pub fn strip_references(&self) -> &Ty {
         let mut t: &Ty = self;
 
-        while let Ty::Ref(_mutability, parameters) = t {
+        while let Ty::Apply(ApplicationTy { ctor: TypeCtor::Ref(_mutability), parameters }) = t {
             t = parameters.as_single();
         }
 
@@ -653,62 +767,30 @@ impl Ty {
 
     pub fn as_adt(&self) -> Option<(AdtId, &Substs)> {
         match self {
-            Ty::Adt(adt_def, parameters) => Some((*adt_def, parameters)),
+            Ty::Apply(ApplicationTy { ctor: TypeCtor::Adt(adt_def), parameters }) => {
+                Some((*adt_def, parameters))
+            }
             _ => None,
         }
     }
 
     pub fn as_tuple(&self) -> Option<&Substs> {
         match self {
-            Ty::Tuple(_, substs) => Some(substs),
-            _ => None,
-        }
-    }
-
-    pub fn as_generic_def(&self) -> Option<GenericDefId> {
-        match *self {
-            Ty::Adt(adt, ..) => Some(adt.into()),
-            Ty::FnDef(callable, ..) => Some(callable.into()),
-            Ty::AssociatedType(type_alias, ..) => Some(type_alias.into()),
-            Ty::ForeignType(type_alias, ..) => Some(type_alias.into()),
+            Ty::Apply(ApplicationTy { ctor: TypeCtor::Tuple { .. }, parameters }) => {
+                Some(parameters)
+            }
             _ => None,
         }
     }
 
     pub fn is_never(&self) -> bool {
-        matches!(self, Ty::Never)
+        matches!(self, Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }))
     }
 
     pub fn is_unknown(&self) -> bool {
         matches!(self, Ty::Unknown)
     }
 
-    pub fn equals_ctor(&self, other: &Ty) -> bool {
-        match (self, other) {
-            (Ty::Adt(adt, ..), Ty::Adt(adt2, ..)) => adt == adt2,
-            (Ty::Slice(_), Ty::Slice(_)) | (Ty::Array(_), Ty::Array(_)) => true,
-            (Ty::FnDef(def_id, ..), Ty::FnDef(def_id2, ..)) => def_id == def_id2,
-            (Ty::OpaqueType(ty_id, ..), Ty::OpaqueType(ty_id2, ..)) => ty_id == ty_id2,
-            (Ty::AssociatedType(ty_id, ..), Ty::AssociatedType(ty_id2, ..))
-            | (Ty::ForeignType(ty_id, ..), Ty::ForeignType(ty_id2, ..)) => ty_id == ty_id2,
-            (Ty::Closure(def, expr, _), Ty::Closure(def2, expr2, _)) => {
-                expr == expr2 && def == def2
-            }
-            (Ty::Ref(mutability, ..), Ty::Ref(mutability2, ..))
-            | (Ty::RawPtr(mutability, ..), Ty::RawPtr(mutability2, ..)) => {
-                mutability == mutability2
-            }
-            (
-                Ty::Function(FnPointer { num_args, sig, .. }),
-                Ty::Function(FnPointer { num_args: num_args2, sig: sig2, .. }),
-            ) => num_args == num_args2 && sig == sig2,
-            (Ty::Tuple(cardinality, _), Ty::Tuple(cardinality2, _)) => cardinality == cardinality2,
-            (Ty::Str, Ty::Str) | (Ty::Never, Ty::Never) => true,
-            (Ty::Scalar(scalar), Ty::Scalar(scalar2)) => scalar == scalar2,
-            _ => false,
-        }
-    }
-
     /// If this is a `dyn Trait` type, this returns the `Trait` part.
     pub fn dyn_trait_ref(&self) -> Option<&TraitRef> {
         match self {
@@ -727,30 +809,41 @@ impl Ty {
 
     fn builtin_deref(&self) -> Option<Ty> {
         match self {
-            Ty::Ref(.., parameters) => Some(Ty::clone(parameters.as_single())),
-            Ty::RawPtr(.., parameters) => Some(Ty::clone(parameters.as_single())),
+            Ty::Apply(a_ty) => match a_ty.ctor {
+                TypeCtor::Ref(..) => Some(Ty::clone(a_ty.parameters.as_single())),
+                TypeCtor::RawPtr(..) => Some(Ty::clone(a_ty.parameters.as_single())),
+                _ => None,
+            },
             _ => None,
         }
     }
 
     pub fn as_fn_def(&self) -> Option<FunctionId> {
         match self {
-            &Ty::FnDef(CallableDefId::FunctionId(func), ..) => Some(func),
+            &Ty::Apply(ApplicationTy {
+                ctor: TypeCtor::FnDef(CallableDefId::FunctionId(func)),
+                ..
+            }) => Some(func),
             _ => None,
         }
     }
 
-    pub fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig> {
+    pub fn callable_sig(&self, db: &dyn HirDatabase) -> Option<FnSig> {
         match self {
-            Ty::Function(fn_ptr) => Some(CallableSig::from_fn_ptr(fn_ptr)),
-            Ty::FnDef(def, parameters) => {
-                let sig = db.callable_item_signature(*def);
-                Some(sig.subst(&parameters))
-            }
-            Ty::Closure(.., substs) => {
-                let sig_param = &substs[0];
-                sig_param.callable_sig(db)
-            }
+            Ty::Apply(a_ty) => match a_ty.ctor {
+                TypeCtor::FnPtr { is_varargs, .. } => {
+                    Some(FnSig::from_fn_ptr_substs(&a_ty.parameters, is_varargs))
+                }
+                TypeCtor::FnDef(def) => {
+                    let sig = db.callable_item_signature(def);
+                    Some(sig.subst(&a_ty.parameters))
+                }
+                TypeCtor::Closure { .. } => {
+                    let sig_param = &a_ty.parameters[0];
+                    sig_param.callable_sig(db)
+                }
+                _ => None,
+            },
             _ => None,
         }
     }
@@ -759,66 +852,28 @@ impl Ty {
     /// the `Substs` for these type parameters with the given ones. (So e.g. if
     /// `self` is `Option<_>` and the substs contain `u32`, we'll have
     /// `Option<u32>` afterwards.)
-    pub fn apply_substs(mut self, new_substs: Substs) -> Ty {
-        match &mut self {
-            Ty::Adt(_, substs)
-            | Ty::Slice(substs)
-            | Ty::Array(substs)
-            | Ty::RawPtr(_, substs)
-            | Ty::Ref(_, substs)
-            | Ty::FnDef(_, substs)
-            | Ty::Function(FnPointer { substs, .. })
-            | Ty::Tuple(_, substs)
-            | Ty::OpaqueType(_, substs)
-            | Ty::AssociatedType(_, substs)
-            | Ty::Closure(.., substs) => {
-                assert_eq!(substs.len(), new_substs.len());
-                *substs = new_substs;
+    pub fn apply_substs(self, substs: Substs) -> Ty {
+        match self {
+            Ty::Apply(ApplicationTy { ctor, parameters: previous_substs }) => {
+                assert_eq!(previous_substs.len(), substs.len());
+                Ty::Apply(ApplicationTy { ctor, parameters: substs })
             }
-            _ => (),
+            _ => self,
         }
-        self
     }
 
     /// Returns the type parameters of this type if it has some (i.e. is an ADT
     /// or function); so if `self` is `Option<u32>`, this returns the `u32`.
-    pub fn substs(&self) -> Option<&Substs> {
+    pub fn substs(&self) -> Option<Substs> {
         match self {
-            Ty::Adt(_, substs)
-            | Ty::Slice(substs)
-            | Ty::Array(substs)
-            | Ty::RawPtr(_, substs)
-            | Ty::Ref(_, substs)
-            | Ty::FnDef(_, substs)
-            | Ty::Function(FnPointer { substs, .. })
-            | Ty::Tuple(_, substs)
-            | Ty::OpaqueType(_, substs)
-            | Ty::AssociatedType(_, substs)
-            | Ty::Closure(.., substs) => Some(substs),
-            _ => None,
-        }
-    }
-
-    pub fn substs_mut(&mut self) -> Option<&mut Substs> {
-        match self {
-            Ty::Adt(_, substs)
-            | Ty::Slice(substs)
-            | Ty::Array(substs)
-            | Ty::RawPtr(_, substs)
-            | Ty::Ref(_, substs)
-            | Ty::FnDef(_, substs)
-            | Ty::Function(FnPointer { substs, .. })
-            | Ty::Tuple(_, substs)
-            | Ty::OpaqueType(_, substs)
-            | Ty::AssociatedType(_, substs)
-            | Ty::Closure(.., substs) => Some(substs),
+            Ty::Apply(ApplicationTy { parameters, .. }) => Some(parameters.clone()),
             _ => None,
         }
     }
 
     pub fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<GenericPredicate>> {
         match self {
-            Ty::OpaqueType(opaque_ty_id, ..) => {
+            Ty::Apply(ApplicationTy { ctor: TypeCtor::OpaqueType(opaque_ty_id), .. }) => {
                 match opaque_ty_id {
                     OpaqueTyId::AsyncBlockTypeImplTrait(def, _expr) => {
                         let krate = def.module(db.upcast()).krate();
@@ -879,7 +934,7 @@ impl Ty {
 
     pub fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId> {
         match self {
-            Ty::AssociatedType(type_alias_id, ..) => {
+            Ty::Apply(ApplicationTy { ctor: TypeCtor::AssociatedType(type_alias_id), .. }) => {
                 match type_alias_id.lookup(db.upcast()).container {
                     AssocContainerId::TraitId(trait_id) => Some(trait_id),
                     _ => None,
@@ -994,6 +1049,11 @@ pub trait TypeWalk {
 impl TypeWalk for Ty {
     fn walk(&self, f: &mut impl FnMut(&Ty)) {
         match self {
+            Ty::Apply(a_ty) => {
+                for t in a_ty.parameters.iter() {
+                    t.walk(f);
+                }
+            }
             Ty::Projection(p_ty) => {
                 for t in p_ty.parameters.iter() {
                     t.walk(f);
@@ -1009,13 +1069,7 @@ impl TypeWalk for Ty {
                     t.walk(f);
                 }
             }
-            _ => {
-                if let Some(substs) = self.substs() {
-                    for t in substs.iter() {
-                        t.walk(f);
-                    }
-                }
-            }
+            Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
         }
         f(self);
     }
@@ -1026,6 +1080,9 @@ impl TypeWalk for Ty {
         binders: DebruijnIndex,
     ) {
         match self {
+            Ty::Apply(a_ty) => {
+                a_ty.parameters.walk_mut_binders(f, binders);
+            }
             Ty::Projection(p_ty) => {
                 p_ty.parameters.walk_mut_binders(f, binders);
             }
@@ -1037,11 +1094,7 @@ impl TypeWalk for Ty {
             Ty::Opaque(o_ty) => {
                 o_ty.parameters.walk_mut_binders(f, binders);
             }
-            _ => {
-                if let Some(substs) = self.substs_mut() {
-                    substs.walk_mut_binders(f, binders);
-                }
-            }
+            Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
         }
         f(self, binders);
     }
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index 44bd95a9ab..99b0ecf3b5 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -31,9 +31,9 @@ use crate::{
         all_super_trait_refs, associated_type_by_name_including_super_traits, generics,
         make_mut_slice, variant_data,
     },
-    Binders, BoundVar, CallableSig, DebruijnIndex, FnPointer, FnSig, GenericPredicate, OpaqueTy,
-    OpaqueTyId, PolyFnSig, ProjectionPredicate, ProjectionTy, ReturnTypeImplTrait,
-    ReturnTypeImplTraits, Substs, TraitEnvironment, TraitRef, Ty, TypeWalk,
+    Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, OpaqueTy, OpaqueTyId, PolyFnSig,
+    ProjectionPredicate, ProjectionTy, ReturnTypeImplTrait, ReturnTypeImplTraits, Substs,
+    TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
 };
 
 #[derive(Debug)]
@@ -145,10 +145,13 @@ impl Ty {
     pub fn from_hir_ext(ctx: &TyLoweringContext<'_>, type_ref: &TypeRef) -> (Self, Option<TypeNs>) {
         let mut res = None;
         let ty = match type_ref {
-            TypeRef::Never => Ty::Never,
+            TypeRef::Never => Ty::simple(TypeCtor::Never),
             TypeRef::Tuple(inner) => {
                 let inner_tys: Arc<[Ty]> = inner.iter().map(|tr| Ty::from_hir(ctx, tr)).collect();
-                Ty::Tuple(inner_tys.len(), Substs(inner_tys))
+                Ty::apply(
+                    TypeCtor::Tuple { cardinality: inner_tys.len() as u16 },
+                    Substs(inner_tys),
+                )
             }
             TypeRef::Path(path) => {
                 let (ty, res_) = Ty::from_hir_path(ctx, path);
@@ -157,28 +160,27 @@ impl Ty {
             }
             TypeRef::RawPtr(inner, mutability) => {
                 let inner_ty = Ty::from_hir(ctx, inner);
-                Ty::RawPtr(*mutability, Substs::single(inner_ty))
+                Ty::apply_one(TypeCtor::RawPtr(*mutability), inner_ty)
             }
             TypeRef::Array(inner) => {
                 let inner_ty = Ty::from_hir(ctx, inner);
-                Ty::Array(Substs::single(inner_ty))
+                Ty::apply_one(TypeCtor::Array, inner_ty)
             }
             TypeRef::Slice(inner) => {
                 let inner_ty = Ty::from_hir(ctx, inner);
-                Ty::Slice(Substs::single(inner_ty))
+                Ty::apply_one(TypeCtor::Slice, inner_ty)
             }
             TypeRef::Reference(inner, _, mutability) => {
                 let inner_ty = Ty::from_hir(ctx, inner);
-                Ty::Ref(*mutability, Substs::single(inner_ty))
+                Ty::apply_one(TypeCtor::Ref(*mutability), inner_ty)
             }
             TypeRef::Placeholder => Ty::Unknown,
             TypeRef::Fn(params, is_varargs) => {
-                let substs = Substs(params.iter().map(|tr| Ty::from_hir(ctx, tr)).collect());
-                Ty::Function(FnPointer {
-                    num_args: substs.len() - 1,
-                    sig: FnSig { variadic: *is_varargs },
-                    substs,
-                })
+                let sig = Substs(params.iter().map(|tr| Ty::from_hir(ctx, tr)).collect());
+                Ty::apply(
+                    TypeCtor::FnPtr { num_args: sig.len() as u16 - 1, is_varargs: *is_varargs },
+                    sig,
+                )
             }
             TypeRef::DynTrait(bounds) => {
                 let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0));
@@ -412,6 +414,7 @@ impl Ty {
             // FIXME: report error
             TypeNs::EnumVariantId(_) => return (Ty::Unknown, None),
         };
+
         Ty::from_type_relative_path(ctx, ty, Some(resolution), remaining_segments)
     }
 
@@ -1014,7 +1017,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
     let ret = Ty::from_hir(&ctx_ret, &data.ret_type);
     let generics = generics(db.upcast(), def.into());
     let num_binders = generics.len();
-    Binders::new(num_binders, CallableSig::from_params_and_return(params, ret, data.is_varargs))
+    Binders::new(num_binders, FnSig::from_params_and_return(params, ret, data.is_varargs))
 }
 
 /// Build the declared type of a function. This should not need to look at the
@@ -1022,7 +1025,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
 fn type_for_fn(db: &dyn HirDatabase, def: FunctionId) -> Binders<Ty> {
     let generics = generics(db.upcast(), def.into());
     let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST);
-    Binders::new(substs.len(), Ty::FnDef(def.into(), substs))
+    Binders::new(substs.len(), Ty::apply(TypeCtor::FnDef(def.into()), substs))
 }
 
 /// Build the declared type of a const.
@@ -1054,7 +1057,7 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS
     let params =
         fields.iter().map(|(_, field)| Ty::from_hir(&ctx, &field.type_ref)).collect::<Vec<_>>();
     let ret = type_for_adt(db, def.into());
-    Binders::new(ret.num_binders, CallableSig::from_params_and_return(params, ret.value, false))
+    Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value, false))
 }
 
 /// Build the type of a tuple struct constructor.
@@ -1065,7 +1068,7 @@ fn type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Binders<T
     }
     let generics = generics(db.upcast(), def.into());
     let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST);
-    Binders::new(substs.len(), Ty::FnDef(def.into(), substs))
+    Binders::new(substs.len(), Ty::apply(TypeCtor::FnDef(def.into()), substs))
 }
 
 fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) -> PolyFnSig {
@@ -1078,7 +1081,7 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId)
     let params =
         fields.iter().map(|(_, field)| Ty::from_hir(&ctx, &field.type_ref)).collect::<Vec<_>>();
     let ret = type_for_adt(db, def.parent.into());
-    Binders::new(ret.num_binders, CallableSig::from_params_and_return(params, ret.value, false))
+    Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value, false))
 }
 
 /// Build the type of a tuple enum variant constructor.
@@ -1090,13 +1093,13 @@ fn type_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) -
     }
     let generics = generics(db.upcast(), def.parent.into());
     let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST);
-    Binders::new(substs.len(), Ty::FnDef(def.into(), substs))
+    Binders::new(substs.len(), Ty::apply(TypeCtor::FnDef(def.into()), substs))
 }
 
 fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> {
     let generics = generics(db.upcast(), adt.into());
     let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST);
-    Binders::new(substs.len(), Ty::Adt(adt, substs))
+    Binders::new(substs.len(), Ty::apply(TypeCtor::Adt(adt), substs))
 }
 
 fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
@@ -1104,10 +1107,10 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
     let resolver = t.resolver(db.upcast());
     let ctx =
         TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
+    let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST);
     if db.type_alias_data(t).is_extern {
-        Binders::new(0, Ty::ForeignType(t))
+        Binders::new(substs.len(), Ty::apply(TypeCtor::ForeignType(t), substs))
     } else {
-        let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST);
         let type_ref = &db.type_alias_data(t).type_ref;
         let inner = Ty::from_hir(&ctx, type_ref.as_ref().unwrap_or(&TypeRef::Error));
         Binders::new(substs.len(), inner)
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index c8a0ad5f1f..66d8de9591 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -7,8 +7,8 @@ use std::{iter, sync::Arc};
 use arrayvec::ArrayVec;
 use base_db::CrateId;
 use hir_def::{
-    lang_item::LangItemTarget, type_ref::Mutability, AdtId, AssocContainerId, AssocItemId,
-    FunctionId, GenericDefId, HasModule, ImplId, Lookup, ModuleId, TraitId, TypeAliasId,
+    lang_item::LangItemTarget, type_ref::Mutability, AssocContainerId, AssocItemId, FunctionId,
+    GenericDefId, HasModule, ImplId, Lookup, ModuleId, TraitId,
 };
 use hir_expand::name::Name;
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -18,24 +18,15 @@ use crate::{
     db::HirDatabase,
     primitive::{self, FloatTy, IntTy, UintTy},
     utils::all_super_traits,
-    Canonical, DebruijnIndex, FnPointer, FnSig, InEnvironment, Scalar, Substs, TraitEnvironment,
-    TraitRef, Ty, TyKind, TypeWalk,
+    ApplicationTy, Canonical, DebruijnIndex, InEnvironment, Scalar, Substs, TraitEnvironment,
+    TraitRef, Ty, TyKind, TypeCtor, TypeWalk,
 };
 
 /// This is used as a key for indexing impls.
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub enum TyFingerprint {
-    Str,
-    Slice,
-    Array,
-    Never,
-    RawPtr(Mutability),
-    Scalar(Scalar),
-    Adt(AdtId),
+    Apply(TypeCtor),
     Dyn(TraitId),
-    Tuple(usize),
-    ForeignType(TypeAliasId),
-    FnPtr(usize, FnSig),
 }
 
 impl TyFingerprint {
@@ -43,42 +34,32 @@ impl TyFingerprint {
     /// have impls: if we have some `struct S`, we can have an `impl S`, but not
     /// `impl &S`. Hence, this will return `None` for reference types and such.
     pub(crate) fn for_impl(ty: &Ty) -> Option<TyFingerprint> {
-        let fp = match ty {
-            &Ty::Str => TyFingerprint::Str,
-            &Ty::Never => TyFingerprint::Never,
-            &Ty::Slice(..) => TyFingerprint::Slice,
-            &Ty::Array(..) => TyFingerprint::Array,
-            &Ty::Scalar(scalar) => TyFingerprint::Scalar(scalar),
-            &Ty::Adt(adt, _) => TyFingerprint::Adt(adt),
-            &Ty::Tuple(cardinality, _) => TyFingerprint::Tuple(cardinality),
-            &Ty::RawPtr(mutability, ..) => TyFingerprint::RawPtr(mutability),
-            &Ty::ForeignType(alias_id, ..) => TyFingerprint::ForeignType(alias_id),
-            &Ty::Function(FnPointer { num_args, sig, .. }) => TyFingerprint::FnPtr(num_args, sig),
-            Ty::Dyn(_) => ty.dyn_trait().map(|trait_| TyFingerprint::Dyn(trait_))?,
-            _ => return None,
-        };
-        Some(fp)
+        match ty {
+            Ty::Apply(a_ty) => Some(TyFingerprint::Apply(a_ty.ctor)),
+            Ty::Dyn(_) => ty.dyn_trait().map(|trait_| TyFingerprint::Dyn(trait_)),
+            _ => None,
+        }
     }
 }
 
 pub(crate) const ALL_INT_FPS: [TyFingerprint; 12] = [
-    TyFingerprint::Scalar(Scalar::Int(IntTy::I8)),
-    TyFingerprint::Scalar(Scalar::Int(IntTy::I16)),
-    TyFingerprint::Scalar(Scalar::Int(IntTy::I32)),
-    TyFingerprint::Scalar(Scalar::Int(IntTy::I64)),
-    TyFingerprint::Scalar(Scalar::Int(IntTy::I128)),
-    TyFingerprint::Scalar(Scalar::Int(IntTy::Isize)),
-    TyFingerprint::Scalar(Scalar::Uint(UintTy::U8)),
-    TyFingerprint::Scalar(Scalar::Uint(UintTy::U16)),
-    TyFingerprint::Scalar(Scalar::Uint(UintTy::U32)),
-    TyFingerprint::Scalar(Scalar::Uint(UintTy::U64)),
-    TyFingerprint::Scalar(Scalar::Uint(UintTy::U128)),
-    TyFingerprint::Scalar(Scalar::Uint(UintTy::Usize)),
+    TyFingerprint::Apply(TypeCtor::Scalar(Scalar::Int(IntTy::I8))),
+    TyFingerprint::Apply(TypeCtor::Scalar(Scalar::Int(IntTy::I16))),
+    TyFingerprint::Apply(TypeCtor::Scalar(Scalar::Int(IntTy::I32))),
+    TyFingerprint::Apply(TypeCtor::Scalar(Scalar::Int(IntTy::I64))),
+    TyFingerprint::Apply(TypeCtor::Scalar(Scalar::Int(IntTy::I128))),
+    TyFingerprint::Apply(TypeCtor::Scalar(Scalar::Int(IntTy::Isize))),
+    TyFingerprint::Apply(TypeCtor::Scalar(Scalar::Uint(UintTy::U8))),
+    TyFingerprint::Apply(TypeCtor::Scalar(Scalar::Uint(UintTy::U16))),
+    TyFingerprint::Apply(TypeCtor::Scalar(Scalar::Uint(UintTy::U32))),
+    TyFingerprint::Apply(TypeCtor::Scalar(Scalar::Uint(UintTy::U64))),
+    TyFingerprint::Apply(TypeCtor::Scalar(Scalar::Uint(UintTy::U128))),
+    TyFingerprint::Apply(TypeCtor::Scalar(Scalar::Uint(UintTy::Usize))),
 ];
 
 pub(crate) const ALL_FLOAT_FPS: [TyFingerprint; 2] = [
-    TyFingerprint::Scalar(Scalar::Float(FloatTy::F32)),
-    TyFingerprint::Scalar(Scalar::Float(FloatTy::F64)),
+    TyFingerprint::Apply(TypeCtor::Scalar(Scalar::Float(FloatTy::F32))),
+    TyFingerprint::Apply(TypeCtor::Scalar(Scalar::Float(FloatTy::F64))),
 ];
 
 /// Trait impls defined or available in some crate.
@@ -230,29 +211,32 @@ impl Ty {
         let mod_to_crate_ids = |module: ModuleId| Some(std::iter::once(module.krate()).collect());
 
         let lang_item_targets = match self {
-            Ty::Adt(def_id, _) => {
-                return mod_to_crate_ids(def_id.module(db.upcast()));
-            }
-            Ty::ForeignType(type_alias_id) => {
-                return mod_to_crate_ids(type_alias_id.lookup(db.upcast()).module(db.upcast()));
-            }
-            Ty::Scalar(Scalar::Bool) => lang_item_crate!("bool"),
-            Ty::Scalar(Scalar::Char) => lang_item_crate!("char"),
-            Ty::Scalar(Scalar::Float(f)) => match f {
-                // There are two lang items: one in libcore (fXX) and one in libstd (fXX_runtime)
-                FloatTy::F32 => lang_item_crate!("f32", "f32_runtime"),
-                FloatTy::F64 => lang_item_crate!("f64", "f64_runtime"),
+            Ty::Apply(a_ty) => match a_ty.ctor {
+                TypeCtor::Adt(def_id) => {
+                    return mod_to_crate_ids(def_id.module(db.upcast()));
+                }
+                TypeCtor::ForeignType(type_alias_id) => {
+                    return mod_to_crate_ids(type_alias_id.lookup(db.upcast()).module(db.upcast()));
+                }
+                TypeCtor::Scalar(Scalar::Bool) => lang_item_crate!("bool"),
+                TypeCtor::Scalar(Scalar::Char) => lang_item_crate!("char"),
+                TypeCtor::Scalar(Scalar::Float(f)) => match f {
+                    // There are two lang items: one in libcore (fXX) and one in libstd (fXX_runtime)
+                    FloatTy::F32 => lang_item_crate!("f32", "f32_runtime"),
+                    FloatTy::F64 => lang_item_crate!("f64", "f64_runtime"),
+                },
+                TypeCtor::Scalar(Scalar::Int(t)) => {
+                    lang_item_crate!(primitive::int_ty_to_string(t))
+                }
+                TypeCtor::Scalar(Scalar::Uint(t)) => {
+                    lang_item_crate!(primitive::uint_ty_to_string(t))
+                }
+                TypeCtor::Str => lang_item_crate!("str_alloc", "str"),
+                TypeCtor::Slice => lang_item_crate!("slice_alloc", "slice"),
+                TypeCtor::RawPtr(Mutability::Shared) => lang_item_crate!("const_ptr"),
+                TypeCtor::RawPtr(Mutability::Mut) => lang_item_crate!("mut_ptr"),
+                _ => return None,
             },
-            &Ty::Scalar(Scalar::Int(t)) => {
-                lang_item_crate!(primitive::int_ty_to_string(t))
-            }
-            &Ty::Scalar(Scalar::Uint(t)) => {
-                lang_item_crate!(primitive::uint_ty_to_string(t))
-            }
-            Ty::Str => lang_item_crate!("str_alloc", "str"),
-            Ty::Slice(_) => lang_item_crate!("slice_alloc", "slice"),
-            Ty::RawPtr(Mutability::Shared, _) => lang_item_crate!("const_ptr"),
-            Ty::RawPtr(Mutability::Mut, _) => lang_item_crate!("mut_ptr"),
             Ty::Dyn(_) => {
                 return self.dyn_trait().and_then(|trait_| {
                     mod_to_crate_ids(GenericDefId::TraitId(trait_).module(db.upcast()))
@@ -429,7 +413,7 @@ fn iterate_method_candidates_with_autoref(
     }
     let refed = Canonical {
         kinds: deref_chain[0].kinds.clone(),
-        value: Ty::Ref(Mutability::Shared, Substs::single(deref_chain[0].value.clone())),
+        value: Ty::apply_one(TypeCtor::Ref(Mutability::Shared), deref_chain[0].value.clone()),
     };
     if iterate_method_candidates_by_receiver(
         &refed,
@@ -445,7 +429,7 @@ fn iterate_method_candidates_with_autoref(
     }
     let ref_muted = Canonical {
         kinds: deref_chain[0].kinds.clone(),
-        value: Ty::Ref(Mutability::Mut, Substs::single(deref_chain[0].value.clone())),
+        value: Ty::apply_one(TypeCtor::Ref(Mutability::Mut), deref_chain[0].value.clone()),
     };
     if iterate_method_candidates_by_receiver(
         &ref_muted,
@@ -772,9 +756,11 @@ fn autoderef_method_receiver(
 ) -> Vec<Canonical<Ty>> {
     let mut deref_chain: Vec<_> = autoderef::autoderef(db, Some(krate), ty).collect();
     // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!)
-    if let Some(Ty::Array(parameters)) = deref_chain.last().map(|ty| &ty.value) {
+    if let Some(Ty::Apply(ApplicationTy { ctor: TypeCtor::Array, parameters })) =
+        deref_chain.last().map(|ty| &ty.value)
+    {
         let kinds = deref_chain.last().unwrap().kinds.clone();
-        let unsized_ty = Ty::Slice(parameters.clone());
+        let unsized_ty = Ty::apply(TypeCtor::Slice, parameters.clone());
         deref_chain.push(Canonical { value: unsized_ty, kinds })
     }
     deref_chain
diff --git a/crates/hir_ty/src/op.rs b/crates/hir_ty/src/op.rs
index 1c01a67ade..a4999c51d4 100644
--- a/crates/hir_ty/src/op.rs
+++ b/crates/hir_ty/src/op.rs
@@ -1,23 +1,30 @@
 //! Helper functions for binary operator type inference.
 use hir_def::expr::{ArithOp, BinaryOp, CmpOp};
 
-use crate::{InferTy, Scalar, Ty};
+use super::{InferTy, Ty, TypeCtor};
+use crate::{ApplicationTy, Scalar};
 
 pub(super) fn binary_op_return_ty(op: BinaryOp, lhs_ty: Ty, rhs_ty: Ty) -> Ty {
     match op {
-        BinaryOp::LogicOp(_) | BinaryOp::CmpOp(_) => Ty::Scalar(Scalar::Bool),
+        BinaryOp::LogicOp(_) | BinaryOp::CmpOp(_) => Ty::simple(TypeCtor::Scalar(Scalar::Bool)),
         BinaryOp::Assignment { .. } => Ty::unit(),
         BinaryOp::ArithOp(ArithOp::Shl) | BinaryOp::ArithOp(ArithOp::Shr) => match lhs_ty {
-            Ty::Scalar(Scalar::Int(_))
-            | Ty::Scalar(Scalar::Uint(_))
-            | Ty::Scalar(Scalar::Float(_)) => lhs_ty,
+            Ty::Apply(ApplicationTy { ctor, .. }) => match ctor {
+                TypeCtor::Scalar(Scalar::Int(_))
+                | TypeCtor::Scalar(Scalar::Uint(_))
+                | TypeCtor::Scalar(Scalar::Float(_)) => lhs_ty,
+                _ => Ty::Unknown,
+            },
             Ty::Infer(InferTy::IntVar(..)) | Ty::Infer(InferTy::FloatVar(..)) => lhs_ty,
             _ => Ty::Unknown,
         },
         BinaryOp::ArithOp(_) => match rhs_ty {
-            Ty::Scalar(Scalar::Int(_))
-            | Ty::Scalar(Scalar::Uint(_))
-            | Ty::Scalar(Scalar::Float(_)) => rhs_ty,
+            Ty::Apply(ApplicationTy { ctor, .. }) => match ctor {
+                TypeCtor::Scalar(Scalar::Int(_))
+                | TypeCtor::Scalar(Scalar::Uint(_))
+                | TypeCtor::Scalar(Scalar::Float(_)) => rhs_ty,
+                _ => Ty::Unknown,
+            },
             Ty::Infer(InferTy::IntVar(..)) | Ty::Infer(InferTy::FloatVar(..)) => rhs_ty,
             _ => Ty::Unknown,
         },
@@ -26,10 +33,13 @@ pub(super) fn binary_op_return_ty(op: BinaryOp, lhs_ty: Ty, rhs_ty: Ty) -> Ty {
 
 pub(super) fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty {
     match op {
-        BinaryOp::LogicOp(..) => Ty::Scalar(Scalar::Bool),
+        BinaryOp::LogicOp(..) => Ty::simple(TypeCtor::Scalar(Scalar::Bool)),
         BinaryOp::Assignment { op: None } => lhs_ty,
         BinaryOp::CmpOp(CmpOp::Eq { .. }) => match lhs_ty {
-            Ty::Scalar(_) | Ty::Str => lhs_ty,
+            Ty::Apply(ApplicationTy { ctor, .. }) => match ctor {
+                TypeCtor::Scalar(_) | TypeCtor::Str => lhs_ty,
+                _ => Ty::Unknown,
+            },
             Ty::Infer(InferTy::IntVar(..)) | Ty::Infer(InferTy::FloatVar(..)) => lhs_ty,
             _ => Ty::Unknown,
         },
@@ -37,9 +47,12 @@ pub(super) fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty {
         BinaryOp::CmpOp(CmpOp::Ord { .. })
         | BinaryOp::Assignment { op: Some(_) }
         | BinaryOp::ArithOp(_) => match lhs_ty {
-            Ty::Scalar(Scalar::Int(_))
-            | Ty::Scalar(Scalar::Uint(_))
-            | Ty::Scalar(Scalar::Float(_)) => lhs_ty,
+            Ty::Apply(ApplicationTy { ctor, .. }) => match ctor {
+                TypeCtor::Scalar(Scalar::Int(_))
+                | TypeCtor::Scalar(Scalar::Uint(_))
+                | TypeCtor::Scalar(Scalar::Float(_)) => lhs_ty,
+                _ => Ty::Unknown,
+            },
             Ty::Infer(InferTy::IntVar(..)) | Ty::Infer(InferTy::FloatVar(..)) => lhs_ty,
             _ => Ty::Unknown,
         },
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs
index 3f5f5091f9..d74c837370 100644
--- a/crates/hir_ty/src/traits/chalk.rs
+++ b/crates/hir_ty/src/traits/chalk.rs
@@ -19,8 +19,8 @@ use crate::{
     display::HirDisplay,
     method_resolution::{TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
     utils::generics,
-    BoundVar, CallableDefId, CallableSig, DebruijnIndex, GenericPredicate, ProjectionPredicate,
-    ProjectionTy, Substs, TraitRef, Ty,
+    BoundVar, CallableDefId, DebruijnIndex, FnSig, GenericPredicate, ProjectionPredicate,
+    ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
 };
 use mapping::{
     convert_where_clauses, generic_predicate_to_inline_bound, make_binders, TypeAliasAsAssocType,
@@ -286,8 +286,9 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
     ) -> chalk_ir::Binders<rust_ir::FnDefInputsAndOutputDatum<Interner>> {
         let sig_ty: Ty =
             from_chalk(self.db, substs.at(&Interner, 0).assert_ty_ref(&Interner).clone());
-        let sig = CallableSig::from_substs(
+        let sig = FnSig::from_fn_ptr_substs(
             &sig_ty.substs().expect("first closure param should be fn ptr"),
+            false,
         );
         let io = rust_ir::FnDefInputsAndOutputDatum {
             argument_types: sig.params().iter().map(|ty| ty.clone().to_chalk(self.db)).collect(),
@@ -488,11 +489,10 @@ pub(crate) fn struct_datum_query(
     struct_id: AdtId,
 ) -> Arc<StructDatum> {
     debug!("struct_datum {:?}", struct_id);
-    let adt_id = from_chalk(db, struct_id);
-    let type_ctor = Ty::Adt(adt_id, Substs::empty());
+    let type_ctor = TypeCtor::Adt(from_chalk(db, struct_id));
     debug!("struct {:?} = {:?}", struct_id, type_ctor);
-    let num_params = generics(db.upcast(), adt_id.into()).len();
-    let upstream = adt_id.module(db.upcast()).krate() != krate;
+    let num_params = type_ctor.num_ty_params(db);
+    let upstream = type_ctor.krate(db) != Some(krate);
     let where_clauses = type_ctor
         .as_generic_def()
         .map(|generic_def| {
diff --git a/crates/hir_ty/src/traits/chalk/mapping.rs b/crates/hir_ty/src/traits/chalk/mapping.rs
index 60d74e21ab..5a3cb79060 100644
--- a/crates/hir_ty/src/traits/chalk/mapping.rs
+++ b/crates/hir_ty/src/traits/chalk/mapping.rs
@@ -16,8 +16,9 @@ use crate::{
     db::HirDatabase,
     primitive::UintTy,
     traits::{Canonical, Obligation},
-    CallableDefId, FnPointer, FnSig, GenericPredicate, InEnvironment, OpaqueTy, OpaqueTyId,
+    ApplicationTy, CallableDefId, GenericPredicate, InEnvironment, OpaqueTy, OpaqueTyId,
     ProjectionPredicate, ProjectionTy, Scalar, Substs, TraitEnvironment, TraitRef, Ty, TyKind,
+    TypeCtor,
 };
 
 use super::interner::*;
@@ -27,67 +28,75 @@ impl ToChalk for Ty {
     type Chalk = chalk_ir::Ty<Interner>;
     fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Ty<Interner> {
         match self {
-            Ty::Ref(m, parameters) => ref_to_chalk(db, m, parameters),
-            Ty::Array(parameters) => array_to_chalk(db, parameters),
-            Ty::Function(FnPointer { sig: FnSig { variadic }, substs, .. }) => {
-                let substitution = chalk_ir::FnSubst(substs.to_chalk(db).shifted_in(&Interner));
-                chalk_ir::TyKind::Function(chalk_ir::FnPointer {
-                    num_binders: 0,
-                    sig: chalk_ir::FnSig { abi: (), safety: chalk_ir::Safety::Safe, variadic },
-                    substitution,
-                })
-                .intern(&Interner)
-            }
-            Ty::AssociatedType(type_alias, substs) => {
-                let assoc_type = TypeAliasAsAssocType(type_alias);
-                let assoc_type_id = assoc_type.to_chalk(db);
-                let substitution = substs.to_chalk(db);
-                chalk_ir::TyKind::AssociatedType(assoc_type_id, substitution).intern(&Interner)
-            }
+            Ty::Apply(apply_ty) => match apply_ty.ctor {
+                TypeCtor::Ref(m) => ref_to_chalk(db, m, apply_ty.parameters),
+                TypeCtor::Array => array_to_chalk(db, apply_ty.parameters),
+                TypeCtor::FnPtr { num_args: _, is_varargs } => {
+                    let substitution =
+                        chalk_ir::FnSubst(apply_ty.parameters.to_chalk(db).shifted_in(&Interner));
+                    chalk_ir::TyKind::Function(chalk_ir::FnPointer {
+                        num_binders: 0,
+                        sig: chalk_ir::FnSig {
+                            abi: (),
+                            safety: chalk_ir::Safety::Safe,
+                            variadic: is_varargs,
+                        },
+                        substitution,
+                    })
+                    .intern(&Interner)
+                }
+                TypeCtor::AssociatedType(type_alias) => {
+                    let assoc_type = TypeAliasAsAssocType(type_alias);
+                    let assoc_type_id = assoc_type.to_chalk(db);
+                    let substitution = apply_ty.parameters.to_chalk(db);
+                    chalk_ir::TyKind::AssociatedType(assoc_type_id, substitution).intern(&Interner)
+                }
 
-            Ty::OpaqueType(impl_trait_id, substs) => {
-                let id = impl_trait_id.to_chalk(db);
-                let substitution = substs.to_chalk(db);
-                chalk_ir::TyKind::OpaqueType(id, substitution).intern(&Interner)
-            }
+                TypeCtor::OpaqueType(impl_trait_id) => {
+                    let id = impl_trait_id.to_chalk(db);
+                    let substitution = apply_ty.parameters.to_chalk(db);
+                    chalk_ir::TyKind::OpaqueType(id, substitution).intern(&Interner)
+                }
 
-            Ty::ForeignType(type_alias) => {
-                let foreign_type = TypeAliasAsForeignType(type_alias);
-                let foreign_type_id = foreign_type.to_chalk(db);
-                chalk_ir::TyKind::Foreign(foreign_type_id).intern(&Interner)
-            }
+                TypeCtor::ForeignType(type_alias) => {
+                    let foreign_type = TypeAliasAsForeignType(type_alias);
+                    let foreign_type_id = foreign_type.to_chalk(db);
+                    chalk_ir::TyKind::Foreign(foreign_type_id).intern(&Interner)
+                }
 
-            Ty::Scalar(scalar) => chalk_ir::TyKind::Scalar(scalar).intern(&Interner),
+                TypeCtor::Scalar(scalar) => chalk_ir::TyKind::Scalar(scalar).intern(&Interner),
 
-            Ty::Tuple(cardinality, substs) => {
-                let substitution = substs.to_chalk(db);
-                chalk_ir::TyKind::Tuple(cardinality.into(), substitution).intern(&Interner)
-            }
-            Ty::RawPtr(mutability, substs) => {
-                let ty = substs[0].clone().to_chalk(db);
-                chalk_ir::TyKind::Raw(mutability.to_chalk(db), ty).intern(&Interner)
-            }
-            Ty::Slice(substs) => {
-                chalk_ir::TyKind::Slice(substs[0].clone().to_chalk(db)).intern(&Interner)
-            }
-            Ty::Str => chalk_ir::TyKind::Str.intern(&Interner),
-            Ty::FnDef(callable_def, substs) => {
-                let id = callable_def.to_chalk(db);
-                let substitution = substs.to_chalk(db);
-                chalk_ir::TyKind::FnDef(id, substitution).intern(&Interner)
-            }
-            Ty::Never => chalk_ir::TyKind::Never.intern(&Interner),
+                TypeCtor::Tuple { cardinality } => {
+                    let substitution = apply_ty.parameters.to_chalk(db);
+                    chalk_ir::TyKind::Tuple(cardinality.into(), substitution).intern(&Interner)
+                }
+                TypeCtor::RawPtr(mutability) => {
+                    let ty = apply_ty.parameters[0].clone().to_chalk(db);
+                    chalk_ir::TyKind::Raw(mutability.to_chalk(db), ty).intern(&Interner)
+                }
+                TypeCtor::Slice => {
+                    chalk_ir::TyKind::Slice(apply_ty.parameters[0].clone().to_chalk(db))
+                        .intern(&Interner)
+                }
+                TypeCtor::Str => chalk_ir::TyKind::Str.intern(&Interner),
+                TypeCtor::FnDef(callable_def) => {
+                    let id = callable_def.to_chalk(db);
+                    let substitution = apply_ty.parameters.to_chalk(db);
+                    chalk_ir::TyKind::FnDef(id, substitution).intern(&Interner)
+                }
+                TypeCtor::Never => chalk_ir::TyKind::Never.intern(&Interner),
 
-            Ty::Closure(def, expr, substs) => {
-                let closure_id = db.intern_closure((def, expr));
-                let substitution = substs.to_chalk(db);
-                chalk_ir::TyKind::Closure(closure_id.into(), substitution).intern(&Interner)
-            }
+                TypeCtor::Closure { def, expr } => {
+                    let closure_id = db.intern_closure((def, expr));
+                    let substitution = apply_ty.parameters.to_chalk(db);
+                    chalk_ir::TyKind::Closure(closure_id.into(), substitution).intern(&Interner)
+                }
 
-            Ty::Adt(adt_id, substs) => {
-                let substitution = substs.to_chalk(db);
-                chalk_ir::TyKind::Adt(chalk_ir::AdtId(adt_id), substitution).intern(&Interner)
-            }
+                TypeCtor::Adt(adt_id) => {
+                    let substitution = apply_ty.parameters.to_chalk(db);
+                    chalk_ir::TyKind::Adt(chalk_ir::AdtId(adt_id), substitution).intern(&Interner)
+                }
+            },
             Ty::Projection(proj_ty) => {
                 let associated_ty_id = TypeAliasAsAssocType(proj_ty.associated_ty).to_chalk(db);
                 let substitution = proj_ty.parameters.to_chalk(db);
@@ -134,7 +143,9 @@ impl ToChalk for Ty {
     fn from_chalk(db: &dyn HirDatabase, chalk: chalk_ir::Ty<Interner>) -> Self {
         match chalk.data(&Interner).kind.clone() {
             chalk_ir::TyKind::Error => Ty::Unknown,
-            chalk_ir::TyKind::Array(ty, _size) => Ty::Array(Substs::single(from_chalk(db, ty))),
+            chalk_ir::TyKind::Array(ty, _size) => {
+                Ty::apply(TypeCtor::Array, Substs::single(from_chalk(db, ty)))
+            }
             chalk_ir::TyKind::Placeholder(idx) => {
                 assert_eq!(idx.ui, UniverseIndex::ROOT);
                 let interned_id = crate::db::GlobalTypeParamId::from_intern_id(
@@ -160,14 +171,16 @@ impl ToChalk for Ty {
                 ..
             }) => {
                 assert_eq!(num_binders, 0);
-                let substs: Substs = from_chalk(
+                let parameters: Substs = from_chalk(
                     db,
                     substitution.0.shifted_out(&Interner).expect("fn ptr should have no binders"),
                 );
-                Ty::Function(FnPointer {
-                    num_args: (substs.len() - 1),
-                    sig: FnSig { variadic },
-                    substs,
+                Ty::Apply(ApplicationTy {
+                    ctor: TypeCtor::FnPtr {
+                        num_args: (parameters.len() - 1) as u16,
+                        is_varargs: variadic,
+                    },
+                    parameters,
                 })
             }
             chalk_ir::TyKind::BoundVar(idx) => Ty::Bound(idx),
@@ -183,49 +196,60 @@ impl ToChalk for Ty {
                 Ty::Dyn(predicates)
             }
 
-            chalk_ir::TyKind::Adt(struct_id, subst) => Ty::Adt(struct_id.0, from_chalk(db, subst)),
-            chalk_ir::TyKind::AssociatedType(type_id, subst) => Ty::AssociatedType(
-                from_chalk::<TypeAliasAsAssocType, _>(db, type_id).0,
-                from_chalk(db, subst),
+            chalk_ir::TyKind::Adt(struct_id, subst) => {
+                apply_ty_from_chalk(db, TypeCtor::Adt(struct_id.0), subst)
+            }
+            chalk_ir::TyKind::AssociatedType(type_id, subst) => apply_ty_from_chalk(
+                db,
+                TypeCtor::AssociatedType(from_chalk::<TypeAliasAsAssocType, _>(db, type_id).0),
+                subst,
             ),
-
             chalk_ir::TyKind::OpaqueType(opaque_type_id, subst) => {
-                Ty::OpaqueType(from_chalk(db, opaque_type_id), from_chalk(db, subst))
+                apply_ty_from_chalk(db, TypeCtor::OpaqueType(from_chalk(db, opaque_type_id)), subst)
             }
 
-            chalk_ir::TyKind::Scalar(scalar) => Ty::Scalar(scalar),
+            chalk_ir::TyKind::Scalar(scalar) => Ty::simple(TypeCtor::Scalar(scalar)),
             chalk_ir::TyKind::Tuple(cardinality, subst) => {
-                Ty::Tuple(cardinality, from_chalk(db, subst))
+                apply_ty_from_chalk(db, TypeCtor::Tuple { cardinality: cardinality as u16 }, subst)
             }
             chalk_ir::TyKind::Raw(mutability, ty) => {
-                Ty::RawPtr(from_chalk(db, mutability), Substs::single(from_chalk(db, ty)))
+                Ty::apply_one(TypeCtor::RawPtr(from_chalk(db, mutability)), from_chalk(db, ty))
             }
-            chalk_ir::TyKind::Slice(ty) => Ty::Slice(Substs::single(from_chalk(db, ty))),
+            chalk_ir::TyKind::Slice(ty) => Ty::apply_one(TypeCtor::Slice, from_chalk(db, ty)),
             chalk_ir::TyKind::Ref(mutability, _lifetime, ty) => {
-                Ty::Ref(from_chalk(db, mutability), Substs::single(from_chalk(db, ty)))
+                Ty::apply_one(TypeCtor::Ref(from_chalk(db, mutability)), from_chalk(db, ty))
             }
-            chalk_ir::TyKind::Str => Ty::Str,
-            chalk_ir::TyKind::Never => Ty::Never,
+            chalk_ir::TyKind::Str => Ty::simple(TypeCtor::Str),
+            chalk_ir::TyKind::Never => Ty::simple(TypeCtor::Never),
 
             chalk_ir::TyKind::FnDef(fn_def_id, subst) => {
-                Ty::FnDef(from_chalk(db, fn_def_id), from_chalk(db, subst))
+                let callable_def = from_chalk(db, fn_def_id);
+                apply_ty_from_chalk(db, TypeCtor::FnDef(callable_def), subst)
             }
 
             chalk_ir::TyKind::Closure(id, subst) => {
                 let id: crate::db::ClosureId = id.into();
                 let (def, expr) = db.lookup_intern_closure(id);
-                Ty::Closure(def, expr, from_chalk(db, subst))
+                apply_ty_from_chalk(db, TypeCtor::Closure { def, expr }, subst)
             }
 
-            chalk_ir::TyKind::Foreign(foreign_def_id) => {
-                Ty::ForeignType(from_chalk::<TypeAliasAsForeignType, _>(db, foreign_def_id).0)
-            }
+            chalk_ir::TyKind::Foreign(foreign_def_id) => Ty::simple(TypeCtor::ForeignType(
+                from_chalk::<TypeAliasAsForeignType, _>(db, foreign_def_id).0,
+            )),
             chalk_ir::TyKind::Generator(_, _) => unimplemented!(), // FIXME
             chalk_ir::TyKind::GeneratorWitness(_, _) => unimplemented!(), // FIXME
         }
     }
 }
 
+fn apply_ty_from_chalk(
+    db: &dyn HirDatabase,
+    ctor: TypeCtor,
+    subst: chalk_ir::Substitution<Interner>,
+) -> Ty {
+    Ty::Apply(ApplicationTy { ctor, parameters: from_chalk(db, subst) })
+}
+
 /// We currently don't model lifetimes, but Chalk does. So, we have to insert a
 /// fake lifetime here, because Chalks built-in logic may expect it to be there.
 fn ref_to_chalk(
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index fef70533d3..5d0449e567 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -39,15 +39,6 @@ pub struct Declaration {
     pub access: Option<ReferenceAccess>,
 }
 
-// Feature: Find All References
-//
-// Shows all references of the item at the cursor location
-//
-// |===
-// | Editor  | Shortcut
-//
-// | VS Code | kbd:[Shift+Alt+F12]
-// |===
 pub(crate) fn find_all_refs(
     sema: &Semantics<RootDatabase>,
     position: FilePosition,
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 22ddeeae3c..175ddd759c 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -59,15 +59,6 @@ pub(crate) fn prepare_rename(
     Ok(RangeInfo::new(range, ()))
 }
 
-// Feature: Rename
-//
-// Renames the item below the cursor and all of its references
-//
-// |===
-// | Editor  | Shortcut
-//
-// | VS Code | kbd:[F2]
-// |===
 pub(crate) fn rename(
     db: &RootDatabase,
     position: FilePosition,
diff --git a/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs b/crates/ide_assists/src/handlers/convert_for_to_iter_for_each.rs
similarity index 87%
rename from crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs
rename to crates/ide_assists/src/handlers/convert_for_to_iter_for_each.rs
index 27da28bc0a..9fddf889cf 100644
--- a/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs
+++ b/crates/ide_assists/src/handlers/convert_for_to_iter_for_each.rs
@@ -3,18 +3,17 @@ use hir::known;
 use ide_db::helpers::FamousDefs;
 use stdx::format_to;
 use syntax::{ast, AstNode};
-use test_utils::mark;
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
 
-// Assist: replace_for_loop_with_for_each
+// Assist: convert_for_to_iter_for_each
 //
 // Converts a for loop into a for_each loop on the Iterator.
 //
 // ```
 // fn main() {
 //     let x = vec![1, 2, 3];
-//     for$0 v in x {
+//     for $0v in x {
 //         let y = v * 2;
 //     }
 // }
@@ -28,19 +27,15 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
 //     });
 // }
 // ```
-pub(crate) fn replace_for_loop_with_for_each(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
+pub(crate) fn convert_for_to_iter_for_each(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let for_loop = ctx.find_node_at_offset::<ast::ForExpr>()?;
     let iterable = for_loop.iterable()?;
     let pat = for_loop.pat()?;
     let body = for_loop.loop_body()?;
-    if body.syntax().text_range().start() < ctx.offset() {
-        mark::hit!(not_available_in_body);
-        return None;
-    }
 
     acc.add(
-        AssistId("replace_for_loop_with_for_each", AssistKind::RefactorRewrite),
-        "Replace this for loop with `Iterator::for_each`",
+        AssistId("convert_for_to_iter_for_each", AssistKind::RefactorRewrite),
+        "Convert a for loop into an Iterator::for_each",
         for_loop.syntax().text_range(),
         |builder| {
             let mut buf = String::new();
@@ -150,13 +145,13 @@ pub struct NoIterMethod;
             FamousDefs::FIXTURE,
             EMPTY_ITER_FIXTURE
         );
-        check_assist(replace_for_loop_with_for_each, before, after);
+        check_assist(convert_for_to_iter_for_each, before, after);
     }
 
     #[test]
     fn test_not_for() {
         check_assist_not_applicable(
-            replace_for_loop_with_for_each,
+            convert_for_to_iter_for_each,
             r"
 let mut x = vec![1, 2, 3];
 x.iter_mut().$0for_each(|v| *v *= 2);
@@ -167,7 +162,7 @@ x.iter_mut().$0for_each(|v| *v *= 2);
     #[test]
     fn test_simple_for() {
         check_assist(
-            replace_for_loop_with_for_each,
+            convert_for_to_iter_for_each,
             r"
 fn main() {
     let x = vec![1, 2, 3];
@@ -185,21 +180,6 @@ fn main() {
         )
     }
 
-    #[test]
-    fn not_available_in_body() {
-        mark::check!(not_available_in_body);
-        check_assist_not_applicable(
-            replace_for_loop_with_for_each,
-            r"
-fn main() {
-    let x = vec![1, 2, 3];
-    for v in x {
-        $0v *= 2;
-    }
-}",
-        )
-    }
-
     #[test]
     fn test_for_borrowed() {
         check_assist_with_fixtures(
@@ -275,7 +255,7 @@ fn main() {
     #[test]
     fn test_for_borrowed_mut_behind_var() {
         check_assist(
-            replace_for_loop_with_for_each,
+            convert_for_to_iter_for_each,
             r"
 fn main() {
     let x = vec![1, 2, 3];
diff --git a/crates/ide_assists/src/handlers/replace_let_with_if_let.rs b/crates/ide_assists/src/handlers/replace_let_with_if_let.rs
index be7e724b56..5a27ada6b6 100644
--- a/crates/ide_assists/src/handlers/replace_let_with_if_let.rs
+++ b/crates/ide_assists/src/handlers/replace_let_with_if_let.rs
@@ -1,6 +1,5 @@
 use std::iter::once;
 
-use ide_db::ty_filter::TryEnum;
 use syntax::{
     ast::{
         self,
@@ -11,6 +10,7 @@ use syntax::{
 };
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
+use ide_db::ty_filter::TryEnum;
 
 // Assist: replace_let_with_if_let
 //
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index 53542d4334..4c067d4511 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -114,6 +114,7 @@ mod handlers {
     mod apply_demorgan;
     mod auto_import;
     mod change_visibility;
+    mod convert_for_to_iter_for_each;
     mod convert_integer_literal;
     mod early_return;
     mod expand_glob_import;
@@ -131,8 +132,8 @@ mod handlers {
     mod generate_enum_projection_method;
     mod generate_from_impl_for_enum;
     mod generate_function;
-    mod generate_getter_mut;
     mod generate_getter;
+    mod generate_getter_mut;
     mod generate_impl;
     mod generate_new;
     mod generate_setter;
@@ -155,7 +156,6 @@ mod handlers {
     mod reorder_fields;
     mod reorder_impl;
     mod replace_derive_with_manual_impl;
-    mod replace_for_loop_with_for_each;
     mod replace_if_let_with_match;
     mod replace_impl_trait_with_generic;
     mod replace_let_with_if_let;
@@ -177,9 +177,11 @@ mod handlers {
             apply_demorgan::apply_demorgan,
             auto_import::auto_import,
             change_visibility::change_visibility,
+            convert_for_to_iter_for_each::convert_for_to_iter_for_each,
             convert_integer_literal::convert_integer_literal,
             early_return::convert_to_guarded_return,
             expand_glob_import::expand_glob_import,
+            move_module_to_file::move_module_to_file,
             extract_struct_from_enum_variant::extract_struct_from_enum_variant,
             fill_match_arms::fill_match_arms,
             fix_visibility::fix_visibility,
@@ -189,12 +191,12 @@ mod handlers {
             generate_default_from_enum_variant::generate_default_from_enum_variant,
             generate_derive::generate_derive,
             generate_enum_is_method::generate_enum_is_method,
-            generate_enum_projection_method::generate_enum_as_method,
             generate_enum_projection_method::generate_enum_try_into_method,
+            generate_enum_projection_method::generate_enum_as_method,
             generate_from_impl_for_enum::generate_from_impl_for_enum,
             generate_function::generate_function,
-            generate_getter_mut::generate_getter_mut,
             generate_getter::generate_getter,
+            generate_getter_mut::generate_getter_mut,
             generate_impl::generate_impl,
             generate_new::generate_new,
             generate_setter::generate_setter,
@@ -208,7 +210,6 @@ mod handlers {
             move_bounds::move_bounds_to_where_clause,
             move_guard::move_arm_cond_to_match_guard,
             move_guard::move_guard_to_arm_body,
-            move_module_to_file::move_module_to_file,
             pull_assignment_up::pull_assignment_up,
             qualify_path::qualify_path,
             raw_string::add_hash,
@@ -220,7 +221,6 @@ mod handlers {
             reorder_fields::reorder_fields,
             reorder_impl::reorder_impl,
             replace_derive_with_manual_impl::replace_derive_with_manual_impl,
-            replace_for_loop_with_for_each::replace_for_loop_with_for_each,
             replace_if_let_with_match::replace_if_let_with_match,
             replace_if_let_with_match::replace_match_with_if_let,
             replace_impl_trait_with_generic::replace_impl_trait_with_generic,
diff --git a/crates/mbe/Cargo.toml b/crates/mbe/Cargo.toml
index 0abba35845..bbee2e32c1 100644
--- a/crates/mbe/Cargo.toml
+++ b/crates/mbe/Cargo.toml
@@ -19,6 +19,8 @@ parser = { path = "../parser", version = "0.0.0" }
 tt = { path = "../tt", version = "0.0.0" }
 test_utils = { path = "../test_utils", version = "0.0.0" }
 
-[dev-dependencies]
+# FIXME: Paper over a bug in cargo-worspaces which block publishing
+# https://github.com/pksunkara/cargo-workspaces/issues/39
+# [dev-dependencies]
 profile = { path = "../profile", version = "0.0.0" }
 
diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs
index 6d81be8804..0d0acd5897 100644
--- a/crates/mbe/src/benchmark.rs
+++ b/crates/mbe/src/benchmark.rs
@@ -177,7 +177,7 @@ fn invocation_fixtures(rules: &FxHashMap<String, MacroRules>) -> Vec<(String, tt
             let c = 1013904223;
             *seed = usize::wrapping_add(usize::wrapping_mul(*seed, a), c);
             return *seed;
-        }
+        };
         fn make_ident(ident: &str) -> tt::TokenTree {
             tt::Leaf::Ident(tt::Ident { id: tt::TokenId::unspecified(), text: SmolStr::new(ident) })
                 .into()
diff --git a/editors/code/README.md b/editors/code/README.md
index e7d7a06f0e..336695d9ff 100644
--- a/editors/code/README.md
+++ b/editors/code/README.md
@@ -2,32 +2,4 @@
 
 Provides support for rust-analyzer: novel LSP server for the Rust programming language.
 
-
-Features:
-
-* [code completion], [imports insertion]
-* [go to definition], [implementation], [type definition]
-* [find all references], [workspace symbol search], [rename]
-* [types and documentation on hover]
-* [inlay hints]
-* [semantic syntax highlighting]
-* a lot of [assist(code actions)]
-* apply suggestions from errors
-* ... and many more, checkout the [manual] to see them all
-
-[code completion]: https://rust-analyzer.github.io/manual.html#magic-completions
-[imports insertion]: https://rust-analyzer.github.io/manual.html#auto-import
-[go to definition]: https://rust-analyzer.github.io/manual.html#go-to-definition
-[implementation]: https://rust-analyzer.github.io/manual.html#go-to-implementation
-[type definition]: https://rust-analyzer.github.io/manual.html#go-to-type-definition
-[find all references]: https://rust-analyzer.github.io/manual.html#find-all-references
-[workspace symbol search]: https://rust-analyzer.github.io/manual.html#workspace-symbol
-[rename]: https://rust-analyzer.github.io/manual.html#rename
-[types and documentation on hover]: https://rust-analyzer.github.io/manual.html#hover
-[inlay hints]: https://rust-analyzer.github.io/manual.html#inlay-hints
-[semantic syntax highlighting]: https://rust-analyzer.github.io/manual.html#semantic-syntax-highlighting
-[assist(code actions)]: https://rust-analyzer.github.io/manual.html#assists-code-actions
-
-[manual]: https://rust-analyzer.github.io/manual.html
-
 See https://rust-analyzer.github.io/ for more information.


Test patch:
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index 2947857a5f..12ec4657b8 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -2491,58 +2491,3 @@ fn inner_use_enum_rename() {
         "#]],
     )
 }
-
-#[test]
-fn box_into_vec() {
-    check_infer(
-        r#"
-#[lang = "sized"]
-pub trait Sized {}
-
-#[lang = "unsize"]
-pub trait Unsize<T: ?Sized> {}
-
-#[lang = "coerce_unsized"]
-pub trait CoerceUnsized<T> {}
-
-pub unsafe trait Allocator {}
-
-pub struct Global;
-unsafe impl Allocator for Global {}
-
-#[lang = "owned_box"]
-#[fundamental]
-pub struct Box<T: ?Sized, A: Allocator = Global>;
-
-impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<Box<U, A>> for Box<T, A> {}
-
-pub struct Vec<T, A: Allocator = Global> {}
-
-#[lang = "slice"]
-impl<T> [T] {}
-
-#[lang = "slice_alloc"]
-impl<T> [T] {
-    pub fn into_vec<A: Allocator>(self: Box<Self, A>) -> Vec<T, A> {
-        unimplemented!()
-    }
-}
-
-fn test() {
-    let vec = <[_]>::into_vec(box [1i32]);
-}
-"#,
-        expect![[r#"
-            569..573 'self': Box<[T], A>
-            602..634 '{     ...     }': Vec<T, A>
-            612..628 'unimpl...ted!()': Vec<T, A>
-            648..694 '{     ...2]); }': ()
-            658..661 'vec': Vec<i32, Global>
-            664..679 '<[_]>::into_vec': fn into_vec<i32, Global>(Box<[i32], Global>) -> Vec<i32, Global>
-            664..691 '<[_]>:...1i32])': Vec<i32, Global>
-            680..690 'box [1i32]': Box<[i32; _], Global>
-            684..690 '[1i32]': [i32; _]
-            685..689 '1i32': i32
-        "#]],
-    )
-}
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index b7f6167607..384eb7eeea 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -190,8 +190,8 @@ fn assist_order_field_struct() {
     let mut assists = assists.iter();
 
     assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)");
-    assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method");
     assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method");
+    assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method");
     assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method");
     assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`");
 }
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index 4f007aa48b..7f6dbbccf2 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -192,6 +192,29 @@ pub(crate) fn frobnicate() {}
     )
 }
 
+#[test]
+fn doctest_convert_for_to_iter_for_each() {
+    check_doc_test(
+        "convert_for_to_iter_for_each",
+        r#####"
+fn main() {
+    let x = vec![1, 2, 3];
+    for $0v in x {
+        let y = v * 2;
+    }
+}
+"#####,
+        r#####"
+fn main() {
+    let x = vec![1, 2, 3];
+    x.into_iter().for_each(|v| {
+        let y = v * 2;
+    });
+}
+"#####,
+    )
+}
+
 #[test]
 fn doctest_convert_integer_literal() {
     check_doc_test(
@@ -1156,29 +1179,6 @@ impl Debug for S {
     )
 }
 
-#[test]
-fn doctest_replace_for_loop_with_for_each() {
-    check_doc_test(
-        "replace_for_loop_with_for_each",
-        r#####"
-fn main() {
-    let x = vec![1, 2, 3];
-    for$0 v in x {
-        let y = v * 2;
-    }
-}
-"#####,
-        r#####"
-fn main() {
-    let x = vec![1, 2, 3];
-    x.into_iter().for_each(|v| {
-        let y = v * 2;
-    });
-}
-"#####,
-    )
-}
-
 #[test]
 fn doctest_replace_if_let_with_match() {
     check_doc_test(
diff --git a/crates/rust-analyzer/tests/rust-analyzer/main.rs b/crates/rust-analyzer/tests/rust-analyzer/main.rs
index 7545b4a348..80bde29b9e 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/main.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/main.rs
@@ -54,9 +54,7 @@ version = "0.0.0"
 use std::collections::Spam;
 "#,
     )
-    .with_config(serde_json::json!({
-        "cargo": { "noSysroot": false }
-    }))
+    .with_sysroot(true)
     .server()
     .wait_until_workspace_is_loaded();
 
@@ -453,9 +451,7 @@ fn main() {{}}
 "#,
         librs, libs
     ))
-    .with_config(serde_json::json!({
-        "cargo": { "noSysroot": false }
-    }))
+    .with_sysroot(true)
     .server()
     .wait_until_workspace_is_loaded();
 
@@ -578,10 +574,7 @@ fn main() {
 "###,
     )
     .with_config(serde_json::json!({
-        "cargo": {
-            "loadOutDirsFromCheck": true,
-            "noSysroot": true,
-        }
+        "cargo": { "loadOutDirsFromCheck": true }
     }))
     .server()
     .wait_until_workspace_is_loaded();
@@ -724,10 +717,7 @@ pub fn foo(_input: TokenStream) -> TokenStream {
 "###,
     )
     .with_config(serde_json::json!({
-        "cargo": {
-            "loadOutDirsFromCheck": true,
-            "noSysroot": true,
-        },
+        "cargo": { "loadOutDirsFromCheck": true },
         "procMacro": {
             "enable": true,
             "server": PathBuf::from(env!("CARGO_BIN_EXE_rust-analyzer")),
diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs
index 6b774073d2..453ce4fa88 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/support.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs
@@ -20,6 +20,7 @@ use crate::testdir::TestDir;
 
 pub(crate) struct Project<'a> {
     fixture: &'a str,
+    with_sysroot: bool,
     tmp_dir: Option<TestDir>,
     roots: Vec<PathBuf>,
     config: serde_json::Value,
@@ -31,10 +32,8 @@ impl<'a> Project<'a> {
             fixture,
             tmp_dir: None,
             roots: vec![],
-            config: serde_json::json!({
-                // Loading standard library is costly, let's ignore it by default
-                "cargo": { "noSysroot": true }
-            }),
+            with_sysroot: false,
+            config: serde_json::Value::Null,
         }
     }
 
@@ -48,6 +47,11 @@ impl<'a> Project<'a> {
         self
     }
 
+    pub(crate) fn with_sysroot(mut self, yes: bool) -> Project<'a> {
+        self.with_sysroot = yes;
+        self
+    }
+
     pub(crate) fn with_config(mut self, config: serde_json::Value) -> Project<'a> {
         self.config = config;
         self