Repository: rust-analyzer/rust-analyzer
Pull request #11688 (2022-03-12 11:55:40)

Add const generics
#7434 is not in this PR, so there is no evaluation of constants and no default const argument. But the rest (type inference and method resolution with chalk) should work.

Chalk is pedantic about kind of generic arguments, and will panic in case of mixing type and const arguments. I tried to add test that, but I'm not sure if it covers all cases.


bors[bot]: :v: HKalbasi can now approve this pull request. To approve and merge a pull request, simply reply with `bors r+`. More detailed instructions are available [here](https://bors.tech/documentation/getting-started/#reviewing-pull-requests).

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 1a17831dc1..5d084bcf1c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -171,9 +171,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "chalk-derive"
-version = "0.76.0"
+version = "0.79.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58c24b8052ea1e3adbb6f9ab7ba5fcc18b9d12591c042de4c833f709ce81e0e0"
+checksum = "0b14364774396379d5c488e73d88e0a6d2b51acd0dac9c8359e2f84c58cf3a16"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -183,9 +183,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-ir"
-version = "0.76.0"
+version = "0.79.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3cad5c3f1edd4b4a2c9bda24ae558ceb4f88336f88f944c2e35d0bfeb13c818"
+checksum = "cd571e8931d3075f562a2d460bfe3028a9c7b343876765cce95b6143a76b882e"
 dependencies = [
  "bitflags",
  "chalk-derive",
@@ -194,9 +194,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-recursive"
-version = "0.76.0"
+version = "0.79.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e68ba0c7219f34738b66c0c992438c644ca33f4d8a29da3d41604299c7eaf419"
+checksum = "54ceedab35607f4680d02de80f8be005af0ad5c1dcfec56cfd849d33da5fe736"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
@@ -207,9 +207,9 @@ dependencies = [
 
 [[package]]
 name = "chalk-solve"
-version = "0.76.0"
+version = "0.79.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94533188d3452bc72cbd5618d166f45fc7646b674ad3fe9667d557bc25236dee"
+checksum = "9e31bb853cf921365759346db05d833f969e330462432bf38c9c2be1e78a9abd"
 dependencies = [
  "chalk-derive",
  "chalk-ir",
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 6e3285fd4f..9edb6de9df 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -256,7 +256,7 @@ impl HirDisplay for TypeParam {
         }
 
         let bounds = f.db.generic_predicates_for_param(self.id.parent(), self.id.into(), None);
-        let substs = TyBuilder::type_params_subst(f.db, self.id.parent());
+        let substs = TyBuilder::placeholder_subst(f.db, self.id.parent());
         let predicates: Vec<_> =
             bounds.iter().cloned().map(|b| b.substitute(Interner, &substs)).collect();
         let krate = self.id.parent().krate(f.db).id;
@@ -292,8 +292,9 @@ impl HirDisplay for ConstParam {
 fn write_generic_params(def: GenericDefId, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
     let params = f.db.generic_params(def);
     if params.lifetimes.is_empty()
+        && params.type_or_consts.iter().all(|x| x.1.const_param().is_none())
         && params
-            .tocs
+            .type_or_consts
             .iter()
             .filter_map(|x| x.1.type_param())
             .all(|param| !matches!(param.provenance, TypeParamProvenance::TypeParamList))
@@ -315,7 +316,7 @@ fn write_generic_params(def: GenericDefId, f: &mut HirFormatter) -> Result<(), H
         delim(f)?;
         write!(f, "{}", lifetime.name)?;
     }
-    for (_, ty) in params.tocs.iter() {
+    for (_, ty) in params.type_or_consts.iter() {
         if let Some(name) = &ty.name() {
             match ty {
                 TypeOrConstParamData::TypeParamData(ty) => {
@@ -348,7 +349,9 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter) -> Result<(), Hir
     // unnamed type targets are displayed inline with the argument itself, e.g. `f: impl Y`.
     let is_unnamed_type_target = |target: &WherePredicateTypeTarget| match target {
         WherePredicateTypeTarget::TypeRef(_) => false,
-        WherePredicateTypeTarget::TypeOrConstParam(id) => params.tocs[*id].name().is_none(),
+        WherePredicateTypeTarget::TypeOrConstParam(id) => {
+            params.type_or_consts[*id].name().is_none()
+        }
     };
 
     let has_displayable_predicate = params
@@ -364,10 +367,12 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter) -> Result<(), Hir
 
     let write_target = |target: &WherePredicateTypeTarget, f: &mut HirFormatter| match target {
         WherePredicateTypeTarget::TypeRef(ty) => ty.hir_fmt(f),
-        WherePredicateTypeTarget::TypeOrConstParam(id) => match &params.tocs[*id].name() {
-            Some(name) => write!(f, "{}", name),
-            None => write!(f, "{{unnamed}}"),
-        },
+        WherePredicateTypeTarget::TypeOrConstParam(id) => {
+            match &params.type_or_consts[*id].name() {
+                Some(name) => write!(f, "{}", name),
+                None => write!(f, "{{unnamed}}"),
+            }
+        }
     };
 
     write!(f, "\nwhere")?;
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index b45689ec37..ee4ff0aebb 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -55,7 +55,9 @@ use hir_def::{
 use hir_expand::{name::name, MacroCallKind};
 use hir_ty::{
     autoderef,
-    consteval::{eval_const, ComputedExpr, ConstEvalCtx, ConstEvalError, ConstExt},
+    consteval::{
+        eval_const, unknown_const_as_generic, ComputedExpr, ConstEvalCtx, ConstEvalError, ConstExt,
+    },
     could_unify,
     diagnostics::BodyValidationDiagnostic,
     method_resolution::{self, TyFingerprint},
@@ -63,9 +65,9 @@ use hir_ty::{
     subst_prefix,
     traits::FnTrait,
     AliasEq, AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast,
-    DebruijnIndex, InEnvironment, Interner, QuantifiedWhereClause, Scalar, Solution, Substitution,
-    TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, TyVariableKind,
-    WhereClause,
+    DebruijnIndex, GenericArgData, InEnvironment, Interner, ParamKind, QuantifiedWhereClause,
+    Scalar, Solution, Substitution, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt,
+    TyKind, TyVariableKind, WhereClause,
 };
 use itertools::Itertools;
 use nameres::diagnostics::DefDiagnosticKind;
@@ -796,7 +798,7 @@ impl Field {
             VariantDef::Union(it) => it.id.into(),
             VariantDef::Variant(it) => it.parent.id.into(),
         };
-        let substs = TyBuilder::type_params_subst(db, generic_def_id);
+        let substs = TyBuilder::placeholder_subst(db, generic_def_id);
         let ty = db.field_types(var_id)[self.id].clone().substitute(Interner, &substs);
         Type::new(db, self.parent.module(db).id.krate(), var_id, ty)
     }
@@ -983,7 +985,10 @@ impl_from!(Struct, Union, Enum for Adt);
 impl Adt {
     pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
         let subst = db.generic_defaults(self.into());
-        subst.iter().any(|ty| ty.skip_binders().is_unknown())
+        subst.iter().any(|ty| match ty.skip_binders().data(Interner) {
+            GenericArgData::Ty(x) => x.is_unknown(),
+            _ => false,
+        })
     }
 
     /// Turns this ADT into a type. Any type parameters of the ADT will be
@@ -1680,7 +1685,10 @@ pub struct TypeAlias {
 impl TypeAlias {
     pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
         let subst = db.generic_defaults(self.id.into());
-        subst.iter().any(|ty| ty.skip_binders().is_unknown())
+        subst.iter().any(|ty| match ty.skip_binders().data(Interner) {
+            GenericArgData::Ty(x) => x.is_unknown(),
+            _ => false,
+        })
     }
 
     pub fn module(self, db: &dyn HirDatabase) -> Module {
@@ -2047,7 +2055,7 @@ impl_from!(
 impl GenericDef {
     pub fn params(self, db: &dyn HirDatabase) -> Vec<GenericParam> {
         let generics = db.generic_params(self.into());
-        let ty_params = generics.tocs.iter().map(|(local_id, _)| {
+        let ty_params = generics.type_or_consts.iter().map(|(local_id, _)| {
             let toc = TypeOrConstParam { id: TypeOrConstParamId { parent: self.into(), local_id } };
             match toc.split(db) {
                 Either::Left(x) => GenericParam::ConstParam(x),
@@ -2067,7 +2075,7 @@ impl GenericDef {
     pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeOrConstParam> {
         let generics = db.generic_params(self.into());
         generics
-            .tocs
+            .type_or_consts
             .iter()
             .map(|(local_id, _)| TypeOrConstParam {
                 id: TypeOrConstParamId { parent: self.into(), local_id },
@@ -2351,9 +2359,14 @@ impl TypeParam {
         let resolver = self.id.parent().resolver(db.upcast());
         let krate = self.id.parent().module(db.upcast()).krate();
         let ty = params.get(local_idx)?.clone();
-        let subst = TyBuilder::type_params_subst(db, self.id.parent());
+        let subst = TyBuilder::placeholder_subst(db, self.id.parent());
         let ty = ty.substitute(Interner, &subst_prefix(&subst, local_idx));
-        Some(Type::new_with_resolver_inner(db, krate, &resolver, ty))
+        match ty.data(Interner) {
+            GenericArgData::Ty(x) => {
+                Some(Type::new_with_resolver_inner(db, krate, &resolver, x.clone()))
+            }
+            _ => None,
+        }
     }
 }
 
@@ -2389,7 +2402,7 @@ impl ConstParam {
 
     pub fn name(self, db: &dyn HirDatabase) -> Name {
         let params = db.generic_params(self.id.parent());
-        match params.tocs[self.id.local_id()].name() {
+        match params.type_or_consts[self.id.local_id()].name() {
             Some(x) => x.clone(),
             None => {
                 never!();
@@ -2421,7 +2434,7 @@ pub struct TypeOrConstParam {
 impl TypeOrConstParam {
     pub fn name(self, db: &dyn HirDatabase) -> Name {
         let params = db.generic_params(self.id.parent);
-        match params.tocs[self.id.local_id].name() {
+        match params.type_or_consts[self.id.local_id].name() {
             Some(n) => n.clone(),
             _ => Name::missing(),
         }
@@ -2437,12 +2450,12 @@ impl TypeOrConstParam {
 
     pub fn split(self, db: &dyn HirDatabase) -> Either<ConstParam, TypeParam> {
         let params = db.generic_params(self.id.parent);
-        match &params.tocs[self.id.local_id] {
+        match &params.type_or_consts[self.id.local_id] {
             hir_def::generics::TypeOrConstParamData::TypeParamData(_) => {
-                Either::Right(TypeParam { id: self.id.into() })
+                Either::Right(TypeParam { id: TypeParamId::from_unchecked(self.id) })
             }
             hir_def::generics::TypeOrConstParamData::ConstParamData(_) => {
-                Either::Left(ConstParam { id: self.id.into() })
+                Either::Left(ConstParam { id: ConstParamId::from_unchecked(self.id) })
             }
         }
     }
@@ -2688,9 +2701,19 @@ impl Type {
     }
 
     pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool {
+        let mut it = args.iter().map(|t| t.ty.clone());
         let trait_ref = TyBuilder::trait_ref(db, trait_.id)
             .push(self.ty.clone())
-            .fill(args.iter().map(|t| t.ty.clone()))
+            .fill(|x| {
+                let r = it.next().unwrap();
+                match x {
+                    ParamKind::Type => GenericArgData::Ty(r).intern(Interner),
+                    ParamKind::Const(ty) => {
+                        // FIXME: this code is not covered in tests.
+                        unknown_const_as_generic(ty.clone())
+                    }
+                }
+            })
             .build();
 
         let goal = Canonical {
@@ -2707,9 +2730,18 @@ impl Type {
         args: &[Type],
         alias: TypeAlias,
     ) -> Option<Type> {
+        let mut args = args.iter();
         let projection = TyBuilder::assoc_type_projection(db, alias.id)
             .push(self.ty.clone())
-            .fill(args.iter().map(|t| t.ty.clone()))
+            .fill(|x| {
+                // FIXME: this code is not covered in tests.
+                match x {
+                    ParamKind::Type => {
+                        GenericArgData::Ty(args.next().unwrap().ty.clone()).intern(Interner)
+                    }
+                    ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
+                }
+            })
             .build();
         let goal = hir_ty::make_canonical(
             InEnvironment::new(
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 986ea0cf2a..4672e7db40 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -279,7 +279,7 @@ impl SourceToDefCtx<'_, '_> {
     pub(super) fn type_param_to_def(&mut self, src: InFile<ast::TypeParam>) -> Option<TypeParamId> {
         let container: ChildContainer = self.find_generic_param_container(src.syntax())?.into();
         let dyn_map = self.cache_for(container, src.file_id);
-        dyn_map[keys::TYPE_PARAM].get(&src.value).copied().map(|x| x.into())
+        dyn_map[keys::TYPE_PARAM].get(&src.value).copied().map(|x| TypeParamId::from_unchecked(x))
     }
 
     pub(super) fn lifetime_param_to_def(
@@ -297,7 +297,7 @@ impl SourceToDefCtx<'_, '_> {
     ) -> Option<ConstParamId> {
         let container: ChildContainer = self.find_generic_param_container(src.syntax())?.into();
         let dyn_map = self.cache_for(container, src.file_id);
-        dyn_map[keys::CONST_PARAM].get(&src.value).copied().map(|x| x.into())
+        dyn_map[keys::CONST_PARAM].get(&src.value).copied().map(|x| ConstParamId::from_unchecked(x))
     }
 
     pub(super) fn generic_param_to_def(
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs
index 6d7b98f3b1..04b77894ae 100644
--- a/crates/hir_def/src/generics.rs
+++ b/crates/hir_def/src/generics.rs
@@ -24,8 +24,8 @@ use crate::{
     keys,
     src::{HasChildSource, HasSource},
     type_ref::{LifetimeRef, TypeBound, TypeRef},
-    AdtId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId, LocalTypeOrConstParamId,
-    Lookup, TypeOrConstParamId,
+    AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId,
+    LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
 };
 
 /// Data about a generic type parameter (to a function, struct, impl, ...).
@@ -99,7 +99,7 @@ impl_from!(TypeParamData, ConstParamData for TypeOrConstParamData);
 /// Data about the generic parameters of a function, struct, impl, etc.
 #[derive(Clone, PartialEq, Eq, Debug, Default, Hash)]
 pub struct GenericParams {
-    pub tocs: Arena<TypeOrConstParamData>,
+    pub type_or_consts: Arena<TypeOrConstParamData>,
     pub lifetimes: Arena<LifetimeParamData>,
     pub where_predicates: Vec<WherePredicate>,
 }
@@ -138,13 +138,14 @@ impl GenericParams {
     pub fn type_iter<'a>(
         &'a self,
     ) -> impl Iterator<Item = (Idx<TypeOrConstParamData>, &TypeParamData)> {
-        self.tocs.iter().filter_map(|x| x.1.type_param().map(|y| (x.0, y)))
+        self.type_or_consts.iter().filter_map(|x| x.1.type_param().map(|y| (x.0, y)))
     }
 
-    pub fn toc_iter<'a>(
+    /// Iterator of type_or_consts field
+    pub fn iter<'a>(
         &'a self,
-    ) -> impl Iterator<Item = (Idx<TypeOrConstParamData>, &TypeOrConstParamData)> {
-        self.tocs.iter()
+    ) -> impl DoubleEndedIterator<Item = (Idx<TypeOrConstParamData>, &TypeOrConstParamData)> {
+        self.type_or_consts.iter()
     }
 
     pub(crate) fn generic_params_query(
@@ -251,7 +252,7 @@ impl GenericParams {
                         default,
                         provenance: TypeParamProvenance::TypeParamList,
                     };
-                    self.tocs.alloc(param.into());
+                    self.type_or_consts.alloc(param.into());
                     let type_ref = TypeRef::Path(name.into());
                     self.fill_bounds(lower_ctx, &type_param, Either::Left(type_ref));
                 }
@@ -261,7 +262,7 @@ impl GenericParams {
                         .ty()
                         .map_or(TypeRef::Error, |it| TypeRef::from_ast(lower_ctx, it));
                     let param = ConstParamData { name, ty: Interned::new(ty) };
-                    self.tocs.alloc(param.into());
+                    self.type_or_consts.alloc(param.into());
                 }
             }
         }
@@ -348,7 +349,7 @@ impl GenericParams {
                     default: None,
                     provenance: TypeParamProvenance::ArgumentImplTrait,
                 };
-                let param_id = self.tocs.alloc(param.into());
+                let param_id = self.type_or_consts.alloc(param.into());
                 for bound in bounds {
                     self.where_predicates.push(WherePredicate::TypeBound {
                         target: WherePredicateTypeTarget::TypeOrConstParam(param_id),
@@ -372,27 +373,34 @@ impl GenericParams {
     }
 
     pub(crate) fn shrink_to_fit(&mut self) {
-        let Self { lifetimes, tocs: types, where_predicates } = self;
+        let Self { lifetimes, type_or_consts: types, where_predicates } = self;
         lifetimes.shrink_to_fit();
         types.shrink_to_fit();
         where_predicates.shrink_to_fit();
     }
 
-    pub fn find_type_by_name(&self, name: &Name) -> Option<LocalTypeOrConstParamId> {
-        self.tocs
-            .iter()
-            .filter(|x| matches!(x.1, TypeOrConstParamData::TypeParamData(_)))
-            .find_map(|(id, p)| if p.name().as_ref() == Some(&name) { Some(id) } else { None })
+    pub fn find_type_by_name(&self, name: &Name, parent: GenericDefId) -> Option<TypeParamId> {
+        self.type_or_consts.iter().find_map(|(id, p)| {
+            if p.name().as_ref() == Some(&name) && p.type_param().is_some() {
+                Some(TypeParamId::from_unchecked(TypeOrConstParamId { local_id: id, parent }))
+            } else {
+                None
+            }
+        })
     }
 
-    pub fn find_type_or_const_by_name(&self, name: &Name) -> Option<LocalTypeOrConstParamId> {
-        self.tocs
-            .iter()
-            .find_map(|(id, p)| if p.name().as_ref() == Some(&name) { Some(id) } else { None })
+    pub fn find_const_by_name(&self, name: &Name, parent: GenericDefId) -> Option<ConstParamId> {
+        self.type_or_consts.iter().find_map(|(id, p)| {
+            if p.name().as_ref() == Some(&name) && p.const_param().is_some() {
+                Some(ConstParamId::from_unchecked(TypeOrConstParamId { local_id: id, parent }))
+            } else {
+                None
+            }
+        })
     }
 
     pub fn find_trait_self_param(&self) -> Option<LocalTypeOrConstParamId> {
-        self.tocs.iter().find_map(|(id, p)| {
+        self.type_or_consts.iter().find_map(|(id, p)| {
             if let TypeOrConstParamData::TypeParamData(p) = p {
                 if p.provenance == TypeParamProvenance::TraitSelf {
                     Some(id)
@@ -451,7 +459,7 @@ impl HasChildSource<LocalTypeOrConstParamId> for GenericDefId {
         db: &dyn DefDatabase,
     ) -> InFile<ArenaMap<LocalTypeOrConstParamId, Self::Value>> {
         let generic_params = db.generic_params(*self);
-        let mut idx_iter = generic_params.tocs.iter().map(|(idx, _)| idx);
+        let mut idx_iter = generic_params.type_or_consts.iter().map(|(idx, _)| idx);
 
         let (file_id, generic_params_list) = file_id_and_params_of(*self, db);
 
@@ -505,7 +513,7 @@ impl ChildBySource for GenericDefId {
         }
 
         let generic_params = db.generic_params(*self);
-        let mut toc_idx_iter = generic_params.tocs.iter().map(|(idx, _)| idx);
+        let mut toc_idx_iter = generic_params.type_or_consts.iter().map(|(idx, _)| idx);
         let lts_idx_iter = generic_params.lifetimes.iter().map(|(idx, _)| idx);
 
         // For traits the first type index is `Self`, skip it.
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index 516a16bcf0..379e03504b 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -582,7 +582,7 @@ impl<'a> Ctx<'a> {
             }
             GenericsOwner::Trait(trait_def) => {
                 // traits get the Self type as an implicit first type parameter
-                generics.tocs.alloc(
+                generics.type_or_consts.alloc(
                     TypeParamData {
                         name: Some(name![Self]),
                         default: None,
diff --git a/crates/hir_def/src/item_tree/pretty.rs b/crates/hir_def/src/item_tree/pretty.rs
index 34bea5bd45..b24ba61ea0 100644
--- a/crates/hir_def/src/item_tree/pretty.rs
+++ b/crates/hir_def/src/item_tree/pretty.rs
@@ -621,12 +621,13 @@ impl<'a> Printer<'a> {
     fn print_generic_arg(&mut self, arg: &GenericArg) {
         match arg {
             GenericArg::Type(ty) => self.print_type_ref(ty),
+            GenericArg::Const(c) => w!(self, "{}", c),
             GenericArg::Lifetime(lt) => w!(self, "{}", lt.name),
         }
     }
 
     fn print_generic_params(&mut self, params: &GenericParams) {
-        if params.tocs.is_empty() && params.lifetimes.is_empty() {
+        if params.type_or_consts.is_empty() && params.lifetimes.is_empty() {
             return;
         }
 
@@ -639,7 +640,7 @@ impl<'a> Printer<'a> {
             first = false;
             w!(self, "{}", lt.name);
         }
-        for (idx, x) in params.tocs.iter() {
+        for (idx, x) in params.type_or_consts.iter() {
             if !first {
                 w!(self, ", ");
             }
@@ -701,7 +702,7 @@ impl<'a> Printer<'a> {
                 match target {
                     WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty),
                     WherePredicateTypeTarget::TypeOrConstParam(id) => {
-                        match &params.tocs[*id].name() {
+                        match &params.type_or_consts[*id].name() {
                             Some(name) => w!(this, "{}", name),
                             None => w!(this, "_anon_{}", id.into_raw()),
                         }
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index d7292b0063..974a2ab8f2 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -343,11 +343,13 @@ impl TypeParamId {
     }
 }
 
-impl From<TypeOrConstParamId> for TypeParamId {
-    fn from(x: TypeOrConstParamId) -> Self {
+impl TypeParamId {
+    /// Caller should check if this toc id really belongs to a type
+    pub fn from_unchecked(x: TypeOrConstParamId) -> Self {
         Self(x)
     }
 }
+
 impl From<TypeParamId> for TypeOrConstParamId {
     fn from(x: TypeParamId) -> Self {
         x.0
@@ -367,11 +369,13 @@ impl ConstParamId {
     }
 }
 
-impl From<TypeOrConstParamId> for ConstParamId {
-    fn from(x: TypeOrConstParamId) -> Self {
+impl ConstParamId {
+    /// Caller should check if this toc id really belongs to a const
+    pub fn from_unchecked(x: TypeOrConstParamId) -> Self {
         Self(x)
     }
 }
+
 impl From<ConstParamId> for TypeOrConstParamId {
     fn from(x: ConstParamId) -> Self {
         x.0
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs
index aea1e3ec34..37c09a0984 100644
--- a/crates/hir_def/src/path.rs
+++ b/crates/hir_def/src/path.rs
@@ -6,7 +6,11 @@ use std::{
     iter,
 };
 
-use crate::{body::LowerCtx, intern::Interned, type_ref::LifetimeRef};
+use crate::{
+    body::LowerCtx,
+    intern::Interned,
+    type_ref::{ConstScalarOrPath, LifetimeRef},
+};
 use hir_expand::name::{name, Name};
 use syntax::ast;
 
@@ -78,6 +82,7 @@ pub struct AssociatedTypeBinding {
 pub enum GenericArg {
     Type(TypeRef),
     Lifetime(LifetimeRef),
+    Const(ConstScalarOrPath),
 }
 
 impl Path {
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs
index 550f12dabd..b6a24cd4ab 100644
--- a/crates/hir_def/src/path/lower.rs
+++ b/crates/hir_def/src/path/lower.rs
@@ -1,6 +1,6 @@
 //! Transforms syntax into `Path` objects, ideally with accounting for hygiene
 
-use crate::intern::Interned;
+use crate::{intern::Interned, type_ref::ConstScalarOrPath};
 
 use either::Either;
 use hir_expand::name::{name, AsName};
@@ -180,8 +180,10 @@ pub(super) fn lower_generic_args(
                     args.push(GenericArg::Lifetime(lifetime_ref))
                 }
             }
-            // constants are ignored for now.
-            ast::GenericArg::ConstArg(_) => (),
+            ast::GenericArg::ConstArg(arg) => {
+                let arg = ConstScalarOrPath::from_expr_opt(arg.expr());
+                args.push(GenericArg::Const(arg))
+            }
         }
     }
 
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs
index 3d47064e4f..72856a1bfe 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -189,14 +189,9 @@ impl Resolver {
                 Scope::GenericParams { .. } | Scope::ImplDefScope(_) if skip_to_mod => continue,
 
                 Scope::GenericParams { params, def } => {
-                    if let Some(local_id) = params.find_type_by_name(first_name) {
+                    if let Some(id) = params.find_type_by_name(first_name, *def) {
                         let idx = if path.segments().len() == 1 { None } else { Some(1) };
-                        return Some((
-                            TypeNs::GenericParam(
-                                TypeOrConstParamId { local_id, parent: *def }.into(),
-                            ),
-                            idx,
-                        ));
+                        return Some((TypeNs::GenericParam(id), idx));
                     }
                 }
                 Scope::ImplDefScope(impl_) => {
@@ -284,18 +279,14 @@ impl Resolver {
                 Scope::ExprScope(_) => continue,
 
                 Scope::GenericParams { params, def } if n_segments > 1 => {
-                    if let Some(local_id) = params.find_type_or_const_by_name(first_name) {
-                        let ty = TypeNs::GenericParam(
-                            TypeOrConstParamId { local_id, parent: *def }.into(),
-                        );
+                    if let Some(id) = params.find_type_by_name(first_name, *def) {
+                        let ty = TypeNs::GenericParam(id);
                         return Some(ResolveValueResult::Partial(ty, 1));
                     }
                 }
                 Scope::GenericParams { params, def } if n_segments == 1 => {
-                    if let Some(local_id) = params.find_type_or_const_by_name(first_name) {
-                        let val = ValueNs::GenericParam(
-                            TypeOrConstParamId { local_id, parent: *def }.into(),
-                        );
+                    if let Some(id) = params.find_const_by_name(first_name, *def) {
+                        let val = ValueNs::GenericParam(id);
                         return Some(ResolveValueResult::ValueNs(val));
                     }
                 }
@@ -518,18 +509,18 @@ impl Scope {
             }
             Scope::GenericParams { params, def: parent } => {
                 let parent = *parent;
-                for (local_id, param) in params.tocs.iter() {
+                for (local_id, param) in params.type_or_consts.iter() {
                     if let Some(name) = &param.name() {
                         let id = TypeOrConstParamId { parent, local_id };
-                        let data = &db.generic_params(parent).tocs[local_id];
+                        let data = &db.generic_params(parent).type_or_consts[local_id];
                         acc.add(
                             name,
                             ScopeDef::GenericParam(match data {
                                 TypeOrConstParamData::TypeParamData(_) => {
-                                    GenericParamId::TypeParamId(id.into())
+                                    GenericParamId::TypeParamId(TypeParamId::from_unchecked(id))
                                 }
                                 TypeOrConstParamData::ConstParamData(_) => {
-                                    GenericParamId::ConstParamId(id.into())
+                                    GenericParamId::ConstParamId(ConstParamId::from_unchecked(id))
                                 }
                             }),
                         );
diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs
index 027c410cdf..b9fadcfa8d 100644
--- a/crates/hir_def/src/type_ref.rs
+++ b/crates/hir_def/src/type_ref.rs
@@ -89,7 +89,7 @@ pub enum TypeRef {
     Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
     // FIXME: for full const generics, the latter element (length) here is going to have to be an
     // expression that is further lowered later in hir_ty.
-    Array(Box<TypeRef>, ConstScalar),
+    Array(Box<TypeRef>, ConstScalarOrPath),
     Slice(Box<TypeRef>),
     /// A fn pointer. Last element of the vector is the return type.
     Fn(Vec<(Option<Name>, TypeRef)>, bool /*varargs*/),
@@ -162,10 +162,7 @@ impl TypeRef {
                 // `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the
                 // `hir_ty` level, which would allow knowing the type of:
                 // let v: [u8; 2 + 2] = [0u8; 4];
-                let len = inner
-                    .expr()
-                    .map(ConstScalar::usize_from_literal_expr)
-                    .unwrap_or(ConstScalar::Unknown);
+                let len = ConstScalarOrPath::from_expr_opt(inner.expr());
 
                 TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
             }
@@ -278,7 +275,8 @@ impl TypeRef {
                             crate::path::GenericArg::Type(type_ref) => {
                                 go(type_ref, f);
                             }
-                            crate::path::GenericArg::Lifetime(_) => {}
+                            crate::path::GenericArg::Const(_)
+                            | crate::path::GenericArg::Lifetime(_) => {}
                         }
                     }
                     for binding in &args_and_bindings.bindings {
@@ -357,6 +355,60 @@ impl TypeBound {
     }
 }
 
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum ConstScalarOrPath {
+    Scalar(ConstScalar),
+    Path(Name),
+}
+
+impl std::fmt::Display for ConstScalarOrPath {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            ConstScalarOrPath::Scalar(s) => write!(f, "{}", s),
+            ConstScalarOrPath::Path(n) => write!(f, "{}", n),
+        }
+    }
+}
+
+impl ConstScalarOrPath {
+    pub(crate) fn from_expr_opt(expr: Option<ast::Expr>) -> Self {
+        match expr {
+            Some(x) => Self::from_expr(x),
+            None => Self::Scalar(ConstScalar::Unknown),
+        }
+    }
+
+    // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
+    // parse stage.
+    fn from_expr(expr: ast::Expr) -> Self {
+        match expr {
+            ast::Expr::PathExpr(p) => {
+                match p.path().and_then(|x| x.segment()).and_then(|x| x.name_ref()) {
+                    Some(x) => Self::Path(x.as_name()),
+                    None => Self::Scalar(ConstScalar::Unknown),
+                }
+            }
+            ast::Expr::Literal(lit) => {
+                let lkind = lit.kind();
+                match lkind {
+                    ast::LiteralKind::IntNumber(num)
+                        if num.suffix() == None || num.suffix() == Some("usize") =>
+                    {
+                        Self::Scalar(
+                            num.value()
+                                .and_then(|v| v.try_into().ok())
+                                .map(ConstScalar::Usize)
+                                .unwrap_or(ConstScalar::Unknown),
+                        )
+                    }
+                    _ => Self::Scalar(ConstScalar::Unknown),
+                }
+            }
+            _ => Self::Scalar(ConstScalar::Unknown),
+        }
+    }
+}
+
 /// A concrete constant value
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub enum ConstScalar {
@@ -389,25 +441,4 @@ impl ConstScalar {
             _ => None,
         }
     }
-
-    // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
-    // parse stage.
-    fn usize_from_literal_expr(expr: ast::Expr) -> ConstScalar {
-        match expr {
-            ast::Expr::Literal(lit) => {
-                let lkind = lit.kind();
-                match lkind {
-                    ast::LiteralKind::IntNumber(num)
-                        if num.suffix() == None || num.suffix() == Some("usize") =>
-                    {
-                        num.value().and_then(|v| v.try_into().ok())
-                    }
-                    _ => None,
-                }
-            }
-            _ => None,
-        }
-        .map(ConstScalar::Usize)
-        .unwrap_or(ConstScalar::Unknown)
-    }
 }
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index 850aaaa5a9..d2bfca53ad 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -18,9 +18,9 @@ ena = "0.14.0"
 tracing = "0.1"
 rustc-hash = "1.1.0"
 scoped-tls = "1"
-chalk-solve = { version = "0.76", default-features = false }
-chalk-ir = "0.76"
-chalk-recursive = { version = "0.76", default-features = false }
+chalk-solve = { version = "0.79", default-features = false }
+chalk-ir = "0.79"
+chalk-recursive = { version = "0.79", default-features = false }
 la-arena = { version = "0.3.0", path = "../../lib/arena" }
 once_cell = { version = "1.5.0" }
 typed-arena = "2.0.1"
diff --git a/crates/hir_ty/src/builder.rs b/crates/hir_ty/src/builder.rs
index 76e5efc052..c507c42f5b 100644
--- a/crates/hir_ty/src/builder.rs
+++ b/crates/hir_ty/src/builder.rs
@@ -8,67 +8,136 @@ use chalk_ir::{
     interner::HasInterner,
     AdtId, BoundVar, DebruijnIndex, Scalar,
 };
-use hir_def::{builtin_type::BuiltinType, GenericDefId, TraitId, TypeAliasId};
+use hir_def::{
+    builtin_type::BuiltinType, generics::TypeOrConstParamData, ConstParamId, GenericDefId, TraitId,
+    TypeAliasId,
+};
 use smallvec::SmallVec;
 
 use crate::{
-    db::HirDatabase, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders,
-    CallableSig, GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt,
-    TyKind, ValueTyDefId,
+    consteval::unknown_const_as_generic, db::HirDatabase, primitive, to_assoc_type_id,
+    to_chalk_trait_id, utils::generics, Binders, CallableSig, ConstData, ConstValue, GenericArg,
+    GenericArgData, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind,
+    ValueTyDefId,
 };
 
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum ParamKind {
+    Type,
+    Const(Ty),
+}
+
 /// This is a builder for `Ty` or anything that needs a `Substitution`.
 pub struct TyBuilder<D> {
     /// The `data` field is used to keep track of what we're building (e.g. an
     /// ADT, a `TraitRef`, ...).
     data: D,
     vec: SmallVec<[GenericArg; 2]>,
-    param_count: usize,
+    param_kinds: SmallVec<[ParamKind; 2]>,
+}
+
+impl<A> TyBuilder<A> {
+    fn with_data<B>(self, data: B) -> TyBuilder<B> {
+        TyBuilder { data, param_kinds: self.param_kinds, vec: self.vec }
+    }
 }
 
 impl<D> TyBuilder<D> {
-    fn new(data: D, param_count: usize) -> TyBuilder<D> {
-        TyBuilder { data, param_count, vec: SmallVec::with_capacity(param_count) }
+    fn new(data: D, param_kinds: SmallVec<[ParamKind; 2]>) -> TyBuilder<D> {
+        TyBuilder { data, vec: SmallVec::with_capacity(param_kinds.len()), param_kinds }
     }
 
     fn build_internal(self) -> (D, Substitution) {
-        assert_eq!(self.vec.len(), self.param_count);
+        assert_eq!(self.vec.len(), self.param_kinds.len());
+        for (a, e) in self.vec.iter().zip(self.param_kinds.iter()) {
+            self.assert_match_kind(a, e);
+        }
         let subst = Substitution::from_iter(Interner, self.vec);
         (self.data, subst)
     }
 
     pub fn push(mut self, arg: impl CastTo<GenericArg>) -> Self {
-        self.vec.push(arg.cast(Interner));
+        let arg = arg.cast(Interner);
+        let expected_kind = &self.param_kinds[self.vec.len()];
+        let arg_kind = match arg.data(Interner) {
+            chalk_ir::GenericArgData::Ty(_) => ParamKind::Type,
+            chalk_ir::GenericArgData::Lifetime(_) => panic!("Got lifetime in TyBuilder::push"),
+            chalk_ir::GenericArgData::Const(c) => {
+                let c = c.data(Interner);
+                ParamKind::Const(c.ty.clone())
+            }
+        };
+        assert_eq!(*expected_kind, arg_kind);
+        self.vec.push(arg);
         self
     }
 
     pub fn remaining(&self) -> usize {
-        self.param_count - self.vec.len()
+        self.param_kinds.len() - self.vec.len()
     }
 
     pub fn fill_with_bound_vars(self, debruijn: DebruijnIndex, starting_from: usize) -> Self {
-        self.fill(
-            (starting_from..)
-                .map(|idx| TyKind::BoundVar(BoundVar::new(debruijn, idx)).intern(Interner)),
-        )
+        // self.fill is inlined to make borrow checker happy
+        let mut this = self;
+        let other = this.param_kinds.iter().skip(this.vec.len());
+        let filler = (starting_from..).zip(other).map(|(idx, kind)| match kind {
+            ParamKind::Type => {
+                GenericArgData::Ty(TyKind::BoundVar(BoundVar::new(debruijn, idx)).intern(Interner))
+                    .intern(Interner)
+            }
+            ParamKind::Const(ty) => GenericArgData::Const(
+                ConstData {
+                    value: ConstValue::BoundVar(BoundVar::new(debruijn, idx)),
+                    ty: ty.clone(),
+                }
+                .intern(Interner),
+            )
+            .intern(Interner),
+        });
+        this.vec.extend(filler.take(this.remaining()).casted(Interner));
+        assert_eq!(this.remaining(), 0);
+        this
     }
 
     pub fn fill_with_unknown(self) -> Self {
-        self.fill(iter::repeat(TyKind::Error.intern(Interner)))
+        // self.fill is inlined to make borrow checker happy
+        let mut this = self;
+        let filler = this.param_kinds.iter().skip(this.vec.len()).map(|x| match x {
+            ParamKind::Type => GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner),
+            ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
+        });
+        this.vec.extend(filler.casted(Interner));
+        assert_eq!(this.remaining(), 0);
+        this
     }
 
-    pub fn fill(mut self, filler: impl Iterator<Item = impl CastTo<GenericArg>>) -> Self {
-        self.vec.extend(filler.take(self.remaining()).casted(Interner));
+    pub fn fill(mut self, filler: impl FnMut(&ParamKind) -> GenericArg) -> Self {
+        self.vec.extend(self.param_kinds.iter().skip(self.vec.len()).map(filler));
         assert_eq!(self.remaining(), 0);
         self
     }
 
     pub fn use_parent_substs(mut self, parent_substs: &Substitution) -> Self {
         assert!(self.vec.is_empty());
-        assert!(parent_substs.len(Interner) <= self.param_count);
-        self.vec.extend(parent_substs.iter(Interner).cloned());
+        assert!(parent_substs.len(Interner) <= self.param_kinds.len());
+        self.extend(parent_substs.iter(Interner).cloned());
         self
     }
+
+    fn extend(&mut self, it: impl Iterator<Item = GenericArg> + Clone) {
+        for x in it.clone().zip(self.param_kinds.iter().skip(self.vec.len())) {
+            self.assert_match_kind(&x.0, &x.1);
+        }
+        self.vec.extend(it);
+    }
+
+    fn assert_match_kind(&self, a: &chalk_ir::GenericArg<Interner>, e: &ParamKind) {
+        match (a.data(Interner), e) {
+            (chalk_ir::GenericArgData::Ty(_), ParamKind::Type)
+            | (chalk_ir::GenericArgData::Const(_), ParamKind::Const(_)) => (),
+            _ => panic!("Mismatched kinds: {:?}, {:?}, {:?}", a, self.vec, self.param_kinds),
+        }
+    }
 }
 
 impl TyBuilder<()> {
@@ -101,16 +170,26 @@ impl TyBuilder<()> {
         TyKind::Slice(argument).intern(Interner)
     }
 
-    pub fn type_params_subst(db: &dyn HirDatabase, def: impl Into<GenericDefId>) -> Substitution {
+    pub fn placeholder_subst(db: &dyn HirDatabase, def: impl Into<GenericDefId>) -> Substitution {
         let params = generics(db.upcast(), def.into());
-        params.type_params_subst(db)
+        params.placeholder_subst(db)
     }
 
     pub fn subst_for_def(db: &dyn HirDatabase, def: impl Into<GenericDefId>) -> TyBuilder<()> {
         let def = def.into();
         let params = generics(db.upcast(), def);
-        let param_count = params.len();
-        TyBuilder::new((), param_count)
+        TyBuilder::new(
+            (),
+            params
+                .iter()
+                .map(|(id, data)| match data {
+                    TypeOrConstParamData::TypeParamData(_) => ParamKind::Type,
+                    TypeOrConstParamData::ConstParamData(_) => {
+                        ParamKind::Const(db.const_param_ty(ConstParamId::from_unchecked(id)))
+                    }
+                })
+                .collect(),
+        )
     }
 
     pub fn build(self) -> Substitution {
@@ -120,10 +199,8 @@ impl TyBuilder<()> {
 }
 
 impl TyBuilder<hir_def::AdtId> {
-    pub fn adt(db: &dyn HirDatabase, adt: hir_def::AdtId) -> TyBuilder<hir_def::AdtId> {
-        let generics = generics(db.upcast(), adt.into());
-        let param_count = generics.len();
-        TyBuilder::new(adt, param_count)
+    pub fn adt(db: &dyn HirDatabase, def: hir_def::AdtId) -> TyBuilder<hir_def::AdtId> {
+        TyBuilder::subst_for_def(db, def).with_data(def)
     }
 
     pub fn fill_with_defaults(
@@ -133,14 +210,15 @@ impl TyBuilder<hir_def::AdtId> {
     ) -> Self {
         let defaults = db.generic_defaults(self.data.into());
         for default_ty in defaults.iter().skip(self.vec.len()) {
-            if default_ty.skip_binders().is_unknown() {
-                self.vec.push(fallback().cast(Interner));
-            } else {
-                // each default can depend on the previous parameters
-                let subst_so_far = Substitution::from_iter(Interner, self.vec.clone());
-                self.vec
-                    .push(default_ty.clone().substitute(Interner, &subst_so_far).cast(Interner));
-            }
+            if let GenericArgData::Ty(x) = default_ty.skip_binders().data(Interner) {
+                if x.is_unknown() {
+                    self.vec.push(fallback().cast(Interner));
+                    continue;
+                }
+            };
+            // each default can depend on the previous parameters
+            let subst_so_far = Substitution::from_iter(Interner, self.vec.clone());
+            self.vec.push(default_ty.clone().substitute(Interner, &subst_so_far).cast(Interner));
         }
         self
     }
@@ -154,7 +232,7 @@ impl TyBuilder<hir_def::AdtId> {
 pub struct Tuple(usize);
 impl TyBuilder<Tuple> {
     pub fn tuple(size: usize) -> TyBuilder<Tuple> {
-        TyBuilder::new(Tuple(size), size)
+        TyBuilder::new(Tuple(size), iter::repeat(ParamKind::Type).take(size).collect())
     }
 
     pub fn build(self) -> Ty {
@@ -164,10 +242,8 @@ impl TyBuilder<Tuple> {
 }
 
 impl TyBuilder<TraitId> {
-    pub fn trait_ref(db: &dyn HirDatabase, trait_id: TraitId) -> TyBuilder<TraitId> {
-        let generics = generics(db.upcast(), trait_id.into());
-        let param_count = generics.len();
-        TyBuilder::new(trait_id, param_count)
+    pub fn trait_ref(db: &dyn HirDatabase, def: TraitId) -> TyBuilder<TraitId> {
+        TyBuilder::subst_for_def(db, def).with_data(def)
     }
 
     pub fn build(self) -> TraitRef {
@@ -177,13 +253,8 @@ impl TyBuilder<TraitId> {
 }
 
 impl TyBuilder<TypeAliasId> {
-    pub fn assoc_type_projection(
-        db: &dyn HirDatabase,
-        type_alias: TypeAliasId,
-    ) -> TyBuilder<TypeAliasId> {
-        let generics = generics(db.upcast(), type_alias.into());
-        let param_count = generics.len();
-        TyBuilder::new(type_alias, param_count)
+    pub fn assoc_type_projection(db: &dyn HirDatabase, def: TypeAliasId) -> TyBuilder<TypeAliasId> {
+        TyBuilder::subst_for_def(db, def).with_data(def)
     }
 
     pub fn build(self) -> ProjectionTy {
@@ -194,8 +265,16 @@ impl TyBuilder<TypeAliasId> {
 
 impl<T: HasInterner<Interner = Interner> + Fold<Interner>> TyBuilder<Binders<T>> {
     fn subst_binders(b: Binders<T>) -> Self {
-        let param_count = b.binders.len(Interner);
-        TyBuilder::new(b, param_count)
+        let param_kinds = b
+            .binders
+            .iter(Interner)
+            .map(|x| match x {
+                chalk_ir::VariableKind::Ty(_) => ParamKind::Type,
+                chalk_ir::VariableKind::Lifetime => panic!("Got lifetime parameter"),
+                chalk_ir::VariableKind::Const(ty) => ParamKind::Const(ty.clone()),
+            })
+            .collect();
+        TyBuilder::new(b, param_kinds)
     }
 
     pub fn build(self) -> <T as Fold<Interner>>::Result {
diff --git a/crates/hir_ty/src/chalk_db.rs b/crates/hir_ty/src/chalk_db.rs
index 0a46aea950..1b67e5a86b 100644
--- a/crates/hir_ty/src/chalk_db.rs
+++ b/crates/hir_ty/src/chalk_db.rs
@@ -19,7 +19,8 @@ use hir_expand::name::name;
 use crate::{
     db::HirDatabase,
     display::HirDisplay,
-    from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, make_only_type_binders,
+    from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, make_binders,
+    make_single_type_binders,
     mapping::{from_chalk, ToChalk, TypeAliasAsValue},
     method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
     to_assoc_type_id, to_chalk_trait_id,
@@ -206,8 +207,8 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
                 let (datas, binders) = (*datas).as_ref().into_value_and_skipped_binders();
                 let data = &datas.impl_traits[idx as usize];
                 let bound = OpaqueTyDatumBound {
-                    bounds: make_only_type_binders(1, data.bounds.skip_binders().to_vec()),
-                    where_clauses: make_only_type_binders(0, vec![]),
+                    bounds: make_single_type_binders(data.bounds.skip_binders().to_vec()),
+                    where_clauses: chalk_ir::Binders::empty(Interner, vec![]),
                 };
                 chalk_ir::Binders::new(binders, bound)
             }
@@ -255,25 +256,22 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
                             .intern(Interner),
                     });
                     let bound = OpaqueTyDatumBound {
-                        bounds: make_only_type_binders(
-                            1,
-                            vec![
-                                crate::wrap_empty_binders(impl_bound),
-                                crate::wrap_empty_binders(proj_bound),
-                            ],
-                        ),
-                        where_clauses: make_only_type_binders(0, vec![]),
+                        bounds: make_single_type_binders(vec![
+                            crate::wrap_empty_binders(impl_bound),
+                            crate::wrap_empty_binders(proj_bound),
+                        ]),
+                        where_clauses: chalk_ir::Binders::empty(Interner, vec![]),
                     };
                     // The opaque type has 1 parameter.
-                    make_only_type_binders(1, bound)
+                    make_single_type_binders(bound)
                 } else {
                     // If failed to find Symbol’s value as variable is void: Future::Output, return empty bounds as fallback.
                     let bound = OpaqueTyDatumBound {
-                        bounds: make_only_type_binders(0, vec![]),
-                        where_clauses: make_only_type_binders(0, vec![]),
+                        bounds: chalk_ir::Binders::empty(Interner, vec![]),
+                        where_clauses: chalk_ir::Binders::empty(Interner, vec![]),
                     };
                     // The opaque type has 1 parameter.
-                    make_only_type_binders(1, bound)
+                    make_single_type_binders(bound)
                 }
             }
         };
@@ -310,7 +308,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
             argument_types: sig.params().to_vec(),
             return_type: sig.ret().clone(),
         };
-        make_only_type_binders(0, io.shifted_in(Interner))
+        chalk_ir::Binders::empty(Interner, io.shifted_in(Interner))
     }
     fn closure_upvars(
         &self,
@@ -318,7 +316,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
         _substs: &chalk_ir::Substitution<Interner>,
     ) -> chalk_ir::Binders<chalk_ir::Ty<Interner>> {
         let ty = TyBuilder::unit();
-        make_only_type_binders(0, ty)
+        chalk_ir::Binders::empty(Interner, ty)
     }
     fn closure_fn_substitution(
         &self,
@@ -407,7 +405,7 @@ pub(crate) fn associated_ty_data_query(
     // let bound_vars = generic_params.bound_vars_subst(DebruijnIndex::INNERMOST);
     let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast());
     let ctx = crate::TyLoweringContext::new(db, &resolver)
-        .with_type_param_mode(crate::lower::TypeParamLoweringMode::Variable);
+        .with_type_param_mode(crate::lower::ParamLoweringMode::Variable);
     let self_ty =
         TyKind::BoundVar(BoundVar::new(crate::DebruijnIndex::INNERMOST, 0)).intern(Interner);
     let mut bounds: Vec<_> = type_alias_data
@@ -440,7 +438,7 @@ pub(crate) fn associated_ty_data_query(
         trait_id: to_chalk_trait_id(trait_),
         id,
         name: type_alias,
-        binders: make_only_type_binders(generic_params.len(), bound_data),
+        binders: make_binders(db, &generic_params, bound_data),
     };
     Arc::new(datum)
 }
@@ -455,7 +453,7 @@ pub(crate) fn trait_datum_query(
     let trait_data = db.trait_data(trait_);
     debug!("trait {:?} = {:?}", trait_id, trait_data.name);
     let generic_params = generics(db.upcast(), trait_.into());
-    let bound_vars = generic_params.bound_vars_subst(DebruijnIndex::INNERMOST);
+    let bound_vars = generic_params.bound_vars_subst(db, DebruijnIndex::INNERMOST);
     let flags = rust_ir::TraitFlags {
         auto: trait_data.is_auto,
         upstream: trait_.lookup(db.upcast()).container.krate() != krate,
@@ -472,7 +470,7 @@ pub(crate) fn trait_datum_query(
         lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name));
     let trait_datum = TraitDatum {
         id: trait_id,
-        binders: make_only_type_binders(bound_vars.len(Interner), trait_datum_bound),
+        binders: make_binders(db, &generic_params, trait_datum_bound),
         flags,
         associated_ty_ids,
         well_known,
@@ -520,11 +518,11 @@ pub(crate) fn struct_datum_query(
 ) -> Arc<StructDatum> {
     debug!("struct_datum {:?}", struct_id);
     let chalk_ir::AdtId(adt_id) = struct_id;
-    let num_params = generics(db.upcast(), adt_id.into()).len();
+    let generic_params = generics(db.upcast(), adt_id.into());
     let upstream = adt_id.module(db.upcast()).krate() != krate;
     let where_clauses = {
         let generic_params = generics(db.upcast(), adt_id.into());
-        let bound_vars = generic_params.bound_vars_subst(DebruijnIndex::INNERMOST);
+        let bound_vars = generic_params.bound_vars_subst(db, DebruijnIndex::INNERMOST);
         convert_where_clauses(db, adt_id.into(), &bound_vars)
     };
     let flags = rust_ir::AdtFlags {
@@ -542,7 +540,7 @@ pub(crate) fn struct_datum_query(
         // FIXME set ADT kind
         kind: rust_ir::AdtKind::Struct,
         id: struct_id,
-        binders: make_only_type_binders(num_params, struct_datum_bound),
+        binders: make_binders(db, &generic_params, struct_datum_bound),
         flags,
     };
     Arc::new(struct_datum)
@@ -574,7 +572,7 @@ fn impl_def_datum(
     let impl_data = db.impl_data(impl_id);
 
     let generic_params = generics(db.upcast(), impl_id.into());
-    let bound_vars = generic_params.bound_vars_subst(DebruijnIndex::INNERMOST);
+    let bound_vars = generic_params.bound_vars_subst(db, DebruijnIndex::INNERMOST);
     let trait_ = trait_ref.hir_trait_id();
     let impl_type = if impl_id.lookup(db.upcast()).container.krate() == krate {
         rust_ir::ImplType::Local
@@ -611,7 +609,7 @@ fn impl_def_datum(
         .collect();
     debug!("impl_datum: {:?}", impl_datum_bound);
     let impl_datum = ImplDatum {
-        binders: make_only_type_binders(bound_vars.len(Interner), impl_datum_bound),
+        binders: make_binders(db, &generic_params, impl_datum_bound),
         impl_type,
         polarity,
         associated_ty_value_ids,
@@ -667,12 +665,12 @@ pub(crate) fn fn_def_datum_query(
     let callable_def: CallableDefId = from_chalk(db, fn_def_id);
     let generic_params = generics(db.upcast(), callable_def.into());
     let (sig, binders) = db.callable_item_signature(callable_def).into_value_and_skipped_binders();
-    let bound_vars = generic_params.bound_vars_subst(DebruijnIndex::INNERMOST);
+    let bound_vars = generic_params.bound_vars_subst(db, DebruijnIndex::INNERMOST);
     let where_clauses = convert_where_clauses(db, callable_def.into(), &bound_vars);
     let bound = rust_ir::FnDefDatumBound {
         // Note: Chalk doesn't actually use this information yet as far as I am aware, but we provide it anyway
-        inputs_and_output: make_only_type_binders(
-            0,
+        inputs_and_output: chalk_ir::Binders::empty(
+            Interner,
             rust_ir::FnDefInputsAndOutputDatum {
                 argument_types: sig.params().to_vec(),
                 return_type: sig.ret().clone(),
diff --git a/crates/hir_ty/src/chalk_ext.rs b/crates/hir_ty/src/chalk_ext.rs
index 8758f38bfc..b0885ab003 100644
--- a/crates/hir_ty/src/chalk_ext.rs
+++ b/crates/hir_ty/src/chalk_ext.rs
@@ -237,11 +237,11 @@ impl TyExt for Ty {
             TyKind::Placeholder(idx) => {
                 let id = from_placeholder_idx(db, *idx);
                 let generic_params = db.generic_params(id.parent);
-                let param_data = &generic_params.tocs[id.local_id];
+                let param_data = &generic_params.type_or_consts[id.local_id];
                 match param_data {
                     TypeOrConstParamData::TypeParamData(p) => match p.provenance {
                         hir_def::generics::TypeParamProvenance::ArgumentImplTrait => {
-                            let substs = TyBuilder::type_params_subst(db, id.parent);
+                            let substs = TyBuilder::placeholder_subst(db, id.parent);
                             let predicates = db
                                 .generic_predicates(id.parent)
                                 .iter()
diff --git a/crates/hir_ty/src/consteval.rs b/crates/hir_ty/src/consteval.rs
index 5cc474aca6..24296c6b7b 100644
--- a/crates/hir_ty/src/consteval.rs
+++ b/crates/hir_ty/src/consteval.rs
@@ -2,15 +2,25 @@
 
 use std::{collections::HashMap, convert::TryInto, fmt::Display};
 
-use chalk_ir::{IntTy, Scalar};
+use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData, IntTy, Scalar};
 use hir_def::{
     expr::{ArithOp, BinaryOp, Expr, Literal, Pat},
+    path::ModPath,
+    resolver::{Resolver, ValueNs},
     type_ref::ConstScalar,
 };
 use hir_expand::name::Name;
 use la_arena::{Arena, Idx};
+use stdx::never;
 
-use crate::{Const, ConstData, ConstValue, Interner, Ty, TyKind};
+use crate::{
+    db::HirDatabase,
+    infer::{Expectation, InferenceContext},
+    lower::ParamLoweringMode,
+    to_placeholder_idx,
+    utils::Generics,
+    Const, ConstData, ConstValue, GenericArg, Interner, Ty, TyKind,
+};
 
 /// Extension trait for [`Const`]
 pub trait ConstExt {
@@ -303,6 +313,57 @@ pub fn eval_usize(expr: Idx<Expr>, mut ctx: ConstEvalCtx<'_>) -> Option<u64> {
     None
 }
 
+pub(crate) fn path_to_const(
+    db: &dyn HirDatabase,
+    resolver: &Resolver,
+    path: &ModPath,
+    mode: ParamLoweringMode,
+    args_lazy: impl FnOnce() -> Generics,
+    debruijn: DebruijnIndex,
+) -> Option<Const> {
+    match resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) {
+        Some(ValueNs::GenericParam(p)) => {
+            let ty = db.const_param_ty(p);
+            let args = args_lazy();
+            let value = match mode {
+                ParamLoweringMode::Placeholder => {
+                    ConstValue::Placeholder(to_placeholder_idx(db, p.into()))
+                }
+                ParamLoweringMode::Variable => match args.param_idx(p.into()) {
+                    Some(x) => ConstValue::BoundVar(BoundVar::new(debruijn, x)),
+                    None => {
+                        never!(
+                            "Generic list doesn't contain this param: {:?}, {}, {:?}",
+                            args,
+                            path,
+                            p
+                        );
+                        return None;
+                    }
+                },
+            };
+            Some(ConstData { ty, value }.intern(Interner))
+        }
+        _ => None,
+    }
+}
+
+pub fn unknown_const(ty: Ty) -> Const {
+    ConstData {
+        ty,
+        value: ConstValue::Concrete(chalk_ir::ConcreteConst { interned: ConstScalar::Unknown }),
+    }
+    .intern(Interner)
+}
+
+pub fn unknown_const_usize() -> Const {
+    unknown_const(TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(Interner))
+}
+
+pub fn unknown_const_as_generic(ty: Ty) -> GenericArg {
+    GenericArgData::Const(unknown_const(ty)).intern(Interner)
+}
+
 /// Interns a possibly-unknown target usize
 pub fn usize_const(value: Option<u64>) -> Const {
     ConstData {
@@ -313,3 +374,27 @@ pub fn usize_const(value: Option<u64>) -> Const {
     }
     .intern(Interner)
 }
+
+pub(crate) fn eval_to_const(
+    expr: Idx<Expr>,
+    mode: ParamLoweringMode,
+    ctx: &mut InferenceContext,
+    args: impl FnOnce() -> Generics,
+    debruijn: DebruijnIndex,
+) -> Const {
+    if let Expr::Path(p) = &ctx.body.exprs[expr] {
+        let db = ctx.db;
+        let resolver = &ctx.resolver;
+        if let Some(c) = path_to_const(db, resolver, p.mod_path(), mode, args, debruijn) {
+            return c;
+        }
+    }
+    let body = ctx.body.clone();
+    let ctx = ConstEvalCtx {
+        exprs: &body.exprs,
+        pats: &body.pats,
+        local_data: HashMap::default(),
+        infer: &mut |x| ctx.infer_expr(x, &Expectation::None),
+    };
+    usize_const(eval_usize(expr, ctx))
+}
diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs
index e144dd43f4..599fd16dd0 100644
--- a/crates/hir_ty/src/db.rs
+++ b/crates/hir_ty/src/db.rs
@@ -13,7 +13,7 @@ use la_arena::ArenaMap;
 use crate::{
     chalk_db,
     method_resolution::{InherentImpls, TraitImpls},
-    Binders, CallableDefId, FnDefId, ImplTraitId, InferenceResult, Interner, PolyFnSig,
+    Binders, CallableDefId, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner, PolyFnSig,
     QuantifiedWhereClause, ReturnTypeImplTraits, TraitRef, Ty, TyDefId, ValueTyDefId,
 };
 use hir_expand::name::Name;
@@ -73,7 +73,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
 
     #[salsa::invoke(crate::lower::generic_defaults_query)]
     #[salsa::cycle(crate::lower::generic_defaults_recover)]
-    fn generic_defaults(&self, def: GenericDefId) -> Arc<[Binders<Ty>]>;
+    fn generic_defaults(&self, def: GenericDefId) -> Arc<[Binders<GenericArg>]>;
 
     #[salsa::invoke(InherentImpls::inherent_impls_in_crate_query)]
     fn inherent_impls_in_crate(&self, krate: CrateId) -> Arc<InherentImpls>;
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index d81aa34824..eb74809bfe 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -14,7 +14,7 @@ use hir_def::{
     intern::{Internable, Interned},
     item_scope::ItemInNs,
     path::{Path, PathKind},
-    type_ref::{TraitBoundModifier, TypeBound, TypeRef},
+    type_ref::{ConstScalar, TraitBoundModifier, TypeBound, TypeRef},
     visibility::Visibility,
     HasModule, ItemContainerId, Lookup, ModuleId, TraitId,
 };
@@ -28,10 +28,10 @@ use crate::{
     mapping::from_chalk,
     primitive, subst_prefix, to_assoc_type_id,
     utils::{self, generics},
-    AdtId, AliasEq, AliasTy, CallableDefId, CallableSig, Const, ConstValue, DomainGoal, GenericArg,
-    ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives, Mutability, OpaqueTy,
-    ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar, TraitRef, TraitRefExt, Ty, TyExt,
-    TyKind, WhereClause,
+    AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstValue, DomainGoal,
+    GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives, Mutability,
+    OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar, Substitution, TraitRef,
+    TraitRefExt, Ty, TyExt, TyKind, WhereClause,
 };
 
 pub struct HirFormatter<'a> {
@@ -316,11 +316,11 @@ impl HirDisplay for Const {
         let data = self.interned();
         match data.value {
             ConstValue::BoundVar(idx) => idx.hir_fmt(f),
-            ConstValue::InferenceVar(..) => write!(f, "_"),
+            ConstValue::InferenceVar(..) => write!(f, "#c#"),
             ConstValue::Placeholder(idx) => {
                 let id = from_placeholder_idx(f.db, idx);
                 let generics = generics(f.db.upcast(), id.parent);
-                let param_data = &generics.params.tocs[id.local_id];
+                let param_data = &generics.params.type_or_consts[id.local_id];
                 write!(f, "{}", param_data.name().unwrap())
             }
             ConstValue::Concrete(c) => write!(f, "{}", c.interned),
@@ -544,24 +544,37 @@ impl HirDisplay for Ty {
                         {
                             None => parameters.as_slice(Interner),
                             Some(default_parameters) => {
+                                fn should_show(
+                                    parameter: &GenericArg,
+                                    default_parameters: &[Binders<GenericArg>],
+                                    i: usize,
+                                    parameters: &Substitution,
+                                ) -> bool {
+                                    if parameter.ty(Interner).map(|x| x.kind(Interner))
+                                        == Some(&TyKind::Error)
+                                    {
+                                        return true;
+                                    }
+                                    if let Some(ConstValue::Concrete(c)) =
+                                        parameter.constant(Interner).map(|x| x.data(Interner).value)
+                                    {
+                                        if c.interned == ConstScalar::Unknown {
+                                            return true;
+                                        }
+                                    }
+                                    let default_parameter = match default_parameters.get(i) {
+                                        Some(x) => x,
+                                        None => return true,
+                                    };
+                                    let actual_default = default_parameter
+                                        .clone()
+                                        .substitute(Interner, &subst_prefix(parameters, i));
+                                    parameter != &actual_default
+                                }
                                 let mut default_from = 0;
                                 for (i, parameter) in parameters.iter(Interner).enumerate() {
-                                    match (
-                                        parameter.assert_ty_ref(Interner).kind(Interner),
-                                        default_parameters.get(i),
-                                    ) {
-                                        (&TyKind::Error, _) | (_, None) => {
-                                            default_from = i + 1;
-                                        }
-                                        (_, Some(default_parameter)) => {
-                                            let actual_default = default_parameter
-                                                .clone()
-                                                .substitute(Interner, &subst_prefix(parameters, i));
-                                            if parameter.assert_ty_ref(Interner) != &actual_default
-                                            {
-                                                default_from = i + 1;
-                                            }
-                                        }
+                                    if should_show(parameter, &default_parameters, i, parameters) {
+                                        default_from = i + 1;
                                     }
                                 }
                                 &parameters.as_slice(Interner)[0..default_from]
@@ -680,14 +693,14 @@ impl HirDisplay for Ty {
             TyKind::Placeholder(idx) => {
                 let id = from_placeholder_idx(f.db, *idx);
                 let generics = generics(f.db.upcast(), id.parent);
-                let param_data = &generics.params.tocs[id.local_id];
+                let param_data = &generics.params.type_or_consts[id.local_id];
                 match param_data {
                     TypeOrConstParamData::TypeParamData(p) => match p.provenance {
                         TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => {
                             write!(f, "{}", p.name.clone().unwrap_or_else(Name::missing))?
                         }
                         TypeParamProvenance::ArgumentImplTrait => {
-                            let substs = generics.type_params_subst(f.db);
+                            let substs = generics.placeholder_subst(f.db);
                             let bounds =
                                 f.db.generic_predicates(id.parent)
                                     .iter()
@@ -1281,6 +1294,7 @@ impl HirDisplay for hir_def::path::GenericArg {
     fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
         match self {
             hir_def::path::GenericArg::Type(ty) => ty.hir_fmt(f),
+            hir_def::path::GenericArg::Const(c) => write!(f, "{}", c),
             hir_def::path::GenericArg::Lifetime(lifetime) => write!(f, "{}", lifetime.name),
         }
     }
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 0c62f58940..4ee5391057 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -16,7 +16,7 @@
 use std::ops::Index;
 use std::sync::Arc;
 
-use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
+use chalk_ir::{cast::Cast, ConstValue, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
 use hir_def::{
     body::Body,
     data::{ConstData, FunctionData, StaticData},
@@ -29,14 +29,16 @@ use hir_def::{
     TraitId, TypeAliasId, VariantId,
 };
 use hir_expand::name::{name, Name};
+use itertools::Either;
 use la_arena::ArenaMap;
 use rustc_hash::FxHashMap;
 use stdx::impl_from;
 
 use crate::{
-    db::HirDatabase, fold_tys, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode,
-    to_assoc_type_id, AliasEq, AliasTy, DomainGoal, Goal, InEnvironment, Interner, ProjectionTy,
-    Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
+    builder::ParamKind, db::HirDatabase, fold_tys_and_consts, infer::coerce::CoerceMany,
+    lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Const, DomainGoal,
+    GenericArg, GenericArgData, Goal, InEnvironment, Interner, ProjectionTy, Substitution,
+    TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
 };
 
 // This lint has a false positive here. See the link below for details.
@@ -354,11 +356,11 @@ impl Index<PatId> for InferenceResult {
 
 /// The inference context contains all information needed during type inference.
 #[derive(Clone, Debug)]
-struct InferenceContext<'a> {
-    db: &'a dyn HirDatabase,
+pub(crate) struct InferenceContext<'a> {
+    pub(crate) db: &'a dyn HirDatabase,
     owner: DefWithBodyId,
-    body: Arc<Body>,
-    resolver: Resolver,
+    pub(crate) body: Arc<Body>,
+    pub(crate) resolver: Resolver,
     table: unify::InferenceTable<'a>,
     trait_env: Arc<TraitEnvironment>,
     result: InferenceResult,
@@ -488,6 +490,20 @@ impl<'a> InferenceContext<'a> {
         self.make_ty_with_mode(type_ref, ImplTraitLoweringMode::Disallowed)
     }
 
+    /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it.
+    fn insert_const_vars_shallow(&mut self, c: Const) -> Const {
+        let data = c.data(Interner);
+        match data.value {
+            ConstValue::Concrete(cc) => match cc.interned {
+                hir_def::type_ref::ConstScalar::Usize(_) => c,
+                hir_def::type_ref::ConstScalar::Unknown => {
+                    self.table.new_const_var(data.ty.clone())
+                }
+            },
+            _ => c,
+        }
+    }
+
     /// Replaces Ty::Unknown by a new type var, so we can maybe still infer it.
     fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
         match ty.kind(Interner) {
@@ -505,7 +521,14 @@ impl<'a> InferenceContext<'a> {
     }
 
     fn insert_type_vars(&mut self, ty: Ty) -> Ty {
-        fold_tys(ty, |ty, _| self.insert_type_vars_shallow(ty), DebruijnIndex::INNERMOST)
+        fold_tys_and_consts(
+            ty,
+            |x, _| match x {
+                Either::Left(ty) => Either::Left(self.insert_type_vars_shallow(ty)),
+                Either::Right(c) => Either::Right(self.insert_const_vars_shallow(c)),
+            },
+            DebruijnIndex::INNERMOST,
+        )
     }
 
     fn resolve_obligations_as_possible(&mut self) {
@@ -533,7 +556,7 @@ impl<'a> InferenceContext<'a> {
         &mut self,
         inner_ty: Ty,
         assoc_ty: Option<TypeAliasId>,
-        params: &[Ty],
+        params: &[GenericArg],
     ) -> Ty {
         match assoc_ty {
             Some(res_assoc_ty) => {
@@ -542,9 +565,10 @@ impl<'a> InferenceContext<'a> {
                     _ => panic!("resolve_associated_type called with non-associated type"),
                 };
                 let ty = self.table.new_type_var();
+                let mut param_iter = params.iter().cloned();
                 let trait_ref = TyBuilder::trait_ref(self.db, trait_)
                     .push(inner_ty)
-                    .fill(params.iter().cloned())
+                    .fill(|_| param_iter.next().unwrap())
                     .build();
                 let alias_eq = AliasEq {
                     alias: AliasTy::Projection(ProjectionTy {
@@ -627,13 +651,21 @@ impl<'a> InferenceContext<'a> {
             }
             TypeNs::SelfType(impl_id) => {
                 let generics = crate::utils::generics(self.db.upcast(), impl_id.into());
-                let substs = generics.type_params_subst(self.db);
+                let substs = generics.placeholder_subst(self.db);
                 let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
                 self.resolve_variant_on_alias(ty, unresolved, path)
             }
             TypeNs::TypeAliasId(it) => {
                 let ty = TyBuilder::def_ty(self.db, it.into())
-                    .fill(std::iter::repeat_with(|| self.table.new_type_var()))
+                    .fill(|x| match x {
+                        ParamKind::Type => {
+                            GenericArgData::Ty(self.table.new_type_var()).intern(Interner)
+                        }
+                        ParamKind::Const(ty) => {
+                            GenericArgData::Const(self.table.new_const_var(ty.clone()))
+                                .intern(Interner)
+                        }
+                    })
                     .build();
                 self.resolve_variant_on_alias(ty, unresolved, path)
             }
@@ -827,7 +859,7 @@ impl<'a> InferenceContext<'a> {
 /// When inferring an expression, we propagate downward whatever type hint we
 /// are able in the form of an `Expectation`.
 #[derive(Clone, PartialEq, Eq, Debug)]
-enum Expectation {
+pub(crate) enum Expectation {
     None,
     HasType(Ty),
     // Castable(Ty), // rustc has this, we currently just don't propagate an expectation for casts
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index c093f0e4b2..e78a6377e5 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -7,13 +7,15 @@ use std::{
     sync::Arc,
 };
 
-use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind};
+use chalk_ir::{
+    cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind,
+};
 use hir_def::{
     expr::{ArithOp, Array, BinaryOp, CmpOp, Expr, ExprId, Literal, Ordering, Statement, UnaryOp},
     generics::TypeOrConstParamData,
     path::{GenericArg, GenericArgs},
     resolver::resolver_for_expr,
-    FieldId, FunctionId, ItemContainerId, Lookup,
+    ConstParamId, FieldId, FunctionId, ItemContainerId, Lookup,
 };
 use hir_expand::name::{name, Name};
 use stdx::always;
@@ -23,7 +25,9 @@ use crate::{
     autoderef::{self, Autoderef},
     consteval,
     infer::coerce::CoerceMany,
-    lower::lower_to_chalk_mutability,
+    lower::{
+        const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode,
+    },
     mapping::from_chalk,
     method_resolution,
     primitive::{self, UintTy},
@@ -39,7 +43,7 @@ use super::{
 };
 
 impl<'a> InferenceContext<'a> {
-    pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
+    pub(crate) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
         let ty = self.infer_expr_inner(tgt_expr, expected);
         if self.resolve_ty_shallow(&ty).is_never() {
             // Any expression that produces a value of type `!` must have diverged
@@ -662,7 +666,7 @@ impl<'a> InferenceContext<'a> {
                     self.resolve_associated_type_with_params(
                         self_ty,
                         self.resolve_ops_index_output(),
-                        &[index_ty],
+                        &[GenericArgData::Ty(index_ty).intern(Interner)],
                     )
                 } else {
                     self.err_ty()
@@ -704,7 +708,7 @@ impl<'a> InferenceContext<'a> {
                             let cur_elem_ty = self.infer_expr_inner(expr, &expected);
                             coerce.coerce(self, Some(expr), &cur_elem_ty);
                         }
-                        Some(items.len() as u64)
+                        consteval::usize_const(Some(items.len() as u64))
                     }
                     &Array::Repeat { initializer, repeat } => {
                         self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty));
@@ -715,19 +719,22 @@ impl<'a> InferenceContext<'a> {
                             ),
                         );
 
-                        consteval::eval_usize(
-                            repeat,
-                            consteval::ConstEvalCtx {
-                                exprs: &body.exprs,
-                                pats: &body.pats,
-                                local_data: Default::default(),
-                                infer: &mut |x| self.infer_expr(x, &expected),
-                            },
-                        )
+                        if let Some(g_def) = self.owner.as_generic_def_id() {
+                            let generics = generics(self.db.upcast(), g_def);
+                            consteval::eval_to_const(
+                                repeat,
+                                ParamLoweringMode::Placeholder,
+                                self,
+                                || generics,
+                                DebruijnIndex::INNERMOST,
+                            )
+                        } else {
+                            consteval::usize_const(None)
+                        }
                     }
                 };
 
-                TyKind::Array(coerce.complete(), consteval::usize_const(len)).intern(Interner)
+                TyKind::Array(coerce.complete(), len).intern(Interner)
             }
             Expr::Literal(lit) => match lit {
                 Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(Interner),
@@ -1038,38 +1045,52 @@ impl<'a> InferenceContext<'a> {
         let total_len = parent_params + type_params + const_params + impl_trait_params;
         let mut substs = Vec::with_capacity(total_len);
         // Parent arguments are unknown
-        for (_id, param) in def_generics.iter_parent() {
+        for (id, param) in def_generics.iter_parent() {
             match param {
                 TypeOrConstParamData::TypeParamData(_) => {
-                    substs.push(self.table.new_type_var());
+                    substs.push(GenericArgData::Ty(self.table.new_type_var()).intern(Interner));
                 }
                 TypeOrConstParamData::ConstParamData(_) => {
-                    // FIXME: here we should do something else
-                    substs.push(self.table.new_type_var());
+                    let ty = self.db.const_param_ty(ConstParamId::from_unchecked(id));
+                    substs
+                        .push(GenericArgData::Const(self.table.new_const_var(ty)).intern(Interner));
                 }
             }
         }
-        // handle provided type arguments
+        // handle provided arguments
         if let Some(generic_args) = generic_args {
             // if args are provided, it should be all of them, but we can't rely on that
-            for arg in generic_args
+            for (arg, kind_id) in generic_args
                 .args
                 .iter()
-                .filter(|arg| matches!(arg, GenericArg::Type(_)))
-                .take(type_params)
+                .filter(|arg| !matches!(arg, GenericArg::Lifetime(_)))
+                .take(type_params + const_params)
+                .zip(def_generics.iter_id().skip(parent_params))
             {
-                match arg {
-                    GenericArg::Type(type_ref) => {
-                        let ty = self.make_ty(type_ref);
-                        substs.push(ty);
-                    }
-                    GenericArg::Lifetime(_) => {}
+                if let Some(g) = generic_arg_to_chalk(
+                    self.db,
+                    kind_id,
+                    arg,
+                    self,
+                    |this, type_ref| this.make_ty(type_ref),
+                    |this, c| {
+                        const_or_path_to_chalk(
+                            this.db,
+                            &this.resolver,
+                            c,
+                            ParamLoweringMode::Placeholder,
+                            || generics(this.db.upcast(), (&this.resolver).generic_def().unwrap()),
+                            DebruijnIndex::INNERMOST,
+                        )
+                    },
+                ) {
+                    substs.push(g);
                 }
             }
         };
         let supplied_params = substs.len();
         for _ in supplied_params..total_len {
-            substs.push(self.table.new_type_var());
+            substs.push(GenericArgData::Ty(self.table.new_type_var()).intern(Interner));
         }
         assert_eq!(substs.len(), total_len);
         Substitution::from_iter(Interner, substs)
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs
index 0d6c8f12d2..1d23654576 100644
--- a/crates/hir_ty/src/infer/path.rs
+++ b/crates/hir_ty/src/infer/path.rs
@@ -1,7 +1,5 @@
 //! Path expression resolution.
 
-use std::iter;
-
 use chalk_ir::cast::Cast;
 use hir_def::{
     path::{Path, PathSegment},
@@ -11,8 +9,8 @@ use hir_def::{
 use hir_expand::name::Name;
 
 use crate::{
-    method_resolution, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
-    ValueTyDefId,
+    builder::ParamKind, consteval, method_resolution, GenericArgData, Interner, Substitution,
+    TraitRefExt, Ty, TyBuilder, TyExt, TyKind, ValueTyDefId,
 };
 
 use super::{ExprOrPatId, InferenceContext, TraitRef};
@@ -82,7 +80,7 @@ impl<'a> InferenceContext<'a> {
             }
             ValueNs::ImplSelf(impl_id) => {
                 let generics = crate::utils::generics(self.db.upcast(), impl_id.into());
-                let substs = generics.type_params_subst(self.db);
+                let substs = generics.placeholder_subst(self.db);
                 let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
                 if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() {
                     let ty = self.db.value_ty(struct_id.into()).substitute(Interner, &substs);
@@ -98,9 +96,19 @@ impl<'a> InferenceContext<'a> {
         let parent_substs = self_subst.unwrap_or_else(|| Substitution::empty(Interner));
         let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
         let substs = ctx.substs_from_path(path, typable, true);
+        let mut it = substs.as_slice(Interner)[parent_substs.len(Interner)..].iter().cloned();
         let ty = TyBuilder::value_ty(self.db, typable)
             .use_parent_substs(&parent_substs)
-            .fill(substs.as_slice(Interner)[parent_substs.len(Interner)..].iter().cloned())
+            .fill(|x| {
+                it.next().unwrap_or_else(|| match x {
+                    ParamKind::Type => {
+                        GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
+                    }
+                    ParamKind::Const(_) => {
+                        GenericArgData::Const(consteval::usize_const(None)).intern(Interner)
+                    }
+                })
+            })
             .build();
         Some(ty)
     }
@@ -241,7 +249,15 @@ impl<'a> InferenceContext<'a> {
                 let substs = match container {
                     ItemContainerId::ImplId(impl_id) => {
                         let impl_substs = TyBuilder::subst_for_def(self.db, impl_id)
-                            .fill(iter::repeat_with(|| self.table.new_type_var()))
+                            .fill(|x| match x {
+                                ParamKind::Type => {
+                                    GenericArgData::Ty(self.table.new_type_var()).intern(Interner)
+                                }
+                                ParamKind::Const(ty) => {
+                                    GenericArgData::Const(self.table.new_const_var(ty.clone()))
+                                        .intern(Interner)
+                                }
+                            })
                             .build();
                         let impl_self_ty =
                             self.db.impl_self_ty(impl_id).substitute(Interner, &impl_substs);
@@ -252,7 +268,15 @@ impl<'a> InferenceContext<'a> {
                         // we're picking this method
                         let trait_ref = TyBuilder::trait_ref(self.db, trait_)
                             .push(ty.clone())
-                            .fill(std::iter::repeat_with(|| self.table.new_type_var()))
+                            .fill(|x| match x {
+                                ParamKind::Type => {
+                                    GenericArgData::Ty(self.table.new_type_var()).intern(Interner)
+                                }
+                                ParamKind::Const(ty) => {
+                                    GenericArgData::Const(self.table.new_const_var(ty.clone()))
+                                        .intern(Interner)
+                                }
+                            })
                             .build();
                         self.push_obligation(trait_ref.clone().cast(Interner));
                         Some(trait_ref.substitution)
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs
index 8a6c34b493..deb536e511 100644
--- a/crates/hir_ty/src/infer/unify.rs
+++ b/crates/hir_ty/src/infer/unify.rs
@@ -1,6 +1,6 @@
 //! Unification and canonicalization logic.
 
-use std::{fmt, iter, mem, sync::Arc};
+use std::{fmt, mem, sync::Arc};
 
 use chalk_ir::{
     cast::Cast, fold::Fold, interner::HasInterner, zip::Zip, FloatTy, IntTy, NoSolution,
@@ -9,13 +9,14 @@ use chalk_ir::{
 use chalk_solve::infer::ParameterEnaVariableExt;
 use ena::unify::UnifyKey;
 use hir_expand::name;
+use stdx::never;
 
 use super::{InferOk, InferResult, InferenceContext, TypeError};
 use crate::{
     db::HirDatabase, fold_tys, static_lifetime, traits::FnTrait, AliasEq, AliasTy, BoundVar,
-    Canonical, Const, DebruijnIndex, GenericArg, Goal, Guidance, InEnvironment, InferenceVar,
-    Interner, Lifetime, ProjectionTy, ProjectionTyExt, Scalar, Solution, Substitution,
-    TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
+    Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance, InEnvironment,
+    InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution,
+    Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
 };
 
 impl<'a> InferenceContext<'a> {
@@ -48,13 +49,13 @@ impl<T: HasInterner<Interner = Interner>> Canonicalized<T> {
         // the solution may contain new variables, which we need to convert to new inference vars
         let new_vars = Substitution::from_iter(
             Interner,
-            solution.binders.iter(Interner).map(|k| match k.kind {
+            solution.binders.iter(Interner).map(|k| match &k.kind {
                 VariableKind::Ty(TyVariableKind::General) => ctx.new_type_var().cast(Interner),
                 VariableKind::Ty(TyVariableKind::Integer) => ctx.new_integer_var().cast(Interner),
                 VariableKind::Ty(TyVariableKind::Float) => ctx.new_float_var().cast(Interner),
                 // Chalk can sometimes return new lifetime variables. We just use the static lifetime everywhere
                 VariableKind::Lifetime => static_lifetime().cast(Interner),
-                _ => panic!("const variable in solution"),
+                VariableKind::Const(ty) => ctx.new_const_var(ty.clone()).cast(Interner),
             }),
         );
         for (i, v) in solution.value.iter(Interner).enumerate() {
@@ -87,11 +88,17 @@ pub(crate) fn unify(
     let mut table = InferenceTable::new(db, env);
     let vars = Substitution::from_iter(
         Interner,
-        tys.binders
-            .iter(Interner)
-            // we always use type vars here because we want everything to
-            // fallback to Unknown in the end (kind of hacky, as below)
-            .map(|_| table.new_type_var()),
+        tys.binders.iter(Interner).map(|x| match &x.kind {
+            chalk_ir::VariableKind::Ty(_) => {
+                GenericArgData::Ty(table.new_type_var()).intern(Interner)
+            }
+            chalk_ir::VariableKind::Lifetime => {
+                GenericArgData::Ty(table.new_type_var()).intern(Interner)
+            } // FIXME: maybe wrong?
+            chalk_ir::VariableKind::Const(ty) => {
+                GenericArgData::Const(table.new_const_var(ty.clone())).intern(Interner)
+            }
+        }),
     );
     let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner);
     let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner);
@@ -117,8 +124,7 @@ pub(crate) fn unify(
     };
     Some(Substitution::from_iter(
         Interner,
-        vars.iter(Interner)
-            .map(|v| table.resolve_with_fallback(v.assert_ty_ref(Interner).clone(), &fallback)),
+        vars.iter(Interner).map(|v| table.resolve_with_fallback(v.clone(), &fallback)),
     ))
 }
 
@@ -552,11 +558,18 @@ impl<'a> InferenceTable<'a> {
 
         let mut arg_tys = vec![];
         let arg_ty = TyBuilder::tuple(num_args)
-            .fill(iter::repeat_with(|| {
-                let arg = self.new_type_var();
+            .fill(|x| {
+                let arg = match x {
+                    ParamKind::Type => self.new_type_var(),
+                    ParamKind::Const(ty) => {
+                        never!("Tuple with const parameter");
+                        return GenericArgData::Const(self.new_const_var(ty.clone()))
+                            .intern(Interner);
+                    }
+                };
                 arg_tys.push(arg.clone());
-                arg
-            }))
+                GenericArgData::Ty(arg).intern(Interner)
+            })
             .build();
 
         let projection = {
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index b57ad62068..d6a524d593 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -42,11 +42,13 @@ use hir_def::{
     type_ref::{ConstScalar, Rawness},
     TypeOrConstParamId,
 };
+use itertools::Either;
+use utils::Generics;
 
 use crate::{db::HirDatabase, utils::generics};
 
 pub use autoderef::autoderef;
-pub use builder::TyBuilder;
+pub use builder::{ParamKind, TyBuilder};
 pub use chalk_ext::*;
 pub use infer::{could_unify, InferenceDiagnostic, InferenceResult};
 pub use interner::Interner;
@@ -140,20 +142,58 @@ where
     Binders::empty(Interner, value.shifted_in_from(Interner, DebruijnIndex::ONE))
 }
 
-pub(crate) fn make_only_type_binders<T: HasInterner<Interner = Interner>>(
-    num_vars: usize,
+pub(crate) fn make_type_and_const_binders<T: HasInterner<Interner = Interner>>(
+    which_is_const: impl Iterator<Item = Option<Ty>>,
     value: T,
 ) -> Binders<T> {
     Binders::new(
         VariableKinds::from_iter(
             Interner,
-            std::iter::repeat(chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General))
-                .take(num_vars),
+            which_is_const.map(|x| {
+                if let Some(ty) = x {
+                    chalk_ir::VariableKind::Const(ty)
+                } else {
+                    chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
+                }
+            }),
         ),
         value,
     )
 }
 
+pub(crate) fn make_single_type_binders<T: HasInterner<Interner = Interner>>(
+    value: T,
+) -> Binders<T> {
+    Binders::new(
+        VariableKinds::from_iter(
+            Interner,
+            std::iter::once(chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)),
+        ),
+        value,
+    )
+}
+
+pub(crate) fn make_binders_with_count<T: HasInterner<Interner = Interner>>(
+    db: &dyn HirDatabase,
+    count: usize,
+    generics: &Generics,
+    value: T,
+) -> Binders<T> {
+    let it = generics.iter_id().take(count).map(|id| match id {
+        Either::Left(_) => None,
+        Either::Right(id) => Some(db.const_param_ty(id)),
+    });
+    crate::make_type_and_const_binders(it, value)
+}
+
+pub(crate) fn make_binders<T: HasInterner<Interner = Interner>>(
+    db: &dyn HirDatabase,
+    generics: &Generics,
+    value: T,
+) -> Binders<T> {
+    make_binders_with_count(db, usize::MAX, generics, value)
+}
+
 // FIXME: get rid of this
 pub fn make_canonical<T: HasInterner<Interner = Interner>>(
     value: T,
@@ -288,11 +328,17 @@ pub fn dummy_usize_const() -> Const {
 
 pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + Fold<Interner>>(
     t: T,
-    f: impl FnMut(BoundVar, DebruijnIndex) -> Ty,
+    for_ty: impl FnMut(BoundVar, DebruijnIndex) -> Ty,
+    for_const: impl FnMut(Ty, BoundVar, DebruijnIndex) -> Const,
 ) -> T::Result {
     use chalk_ir::{fold::Folder, Fallible};
-    struct FreeVarFolder<F>(F);
-    impl<'i, F: FnMut(BoundVar, DebruijnIndex) -> Ty + 'i> Folder<Interner> for FreeVarFolder<F> {
+    struct FreeVarFolder<F1, F2>(F1, F2);
+    impl<
+            'i,
+            F1: FnMut(BoundVar, DebruijnIndex) -> Ty + 'i,
+            F2: FnMut(Ty, BoundVar, DebruijnIndex) -> Const + 'i,
+        > Folder<Interner> for FreeVarFolder<F1, F2>
+    {
         type Error = NoSolution;
 
         fn as_dyn(&mut self) -> &mut dyn Folder<Interner, Error = Self::Error> {
@@ -310,13 +356,38 @@ pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + Fold<Interner
         ) -> Fallible<Ty> {
             Ok(self.0(bound_var, outer_binder))
         }
+
+        fn fold_free_var_const(
+            &mut self,
+            ty: Ty,
+            bound_var: BoundVar,
+            outer_binder: DebruijnIndex,
+        ) -> Fallible<Const> {
+            Ok(self.1(ty, bound_var, outer_binder))
+        }
     }
-    t.fold_with(&mut FreeVarFolder(f), DebruijnIndex::INNERMOST).expect("fold failed unexpectedly")
+    t.fold_with(&mut FreeVarFolder(for_ty, for_const), DebruijnIndex::INNERMOST)
+        .expect("fold failed unexpectedly")
 }
 
 pub(crate) fn fold_tys<T: HasInterner<Interner = Interner> + Fold<Interner>>(
     t: T,
-    f: impl FnMut(Ty, DebruijnIndex) -> Ty,
+    mut for_ty: impl FnMut(Ty, DebruijnIndex) -> Ty,
+    binders: DebruijnIndex,
+) -> T::Result {
+    fold_tys_and_consts(
+        t,
+        |x, d| match x {
+            Either::Left(x) => Either::Left(for_ty(x, d)),
+            Either::Right(x) => Either::Right(x),
+        },
+        binders,
+    )
+}
+
+pub(crate) fn fold_tys_and_consts<T: HasInterner<Interner = Interner> + Fold<Interner>>(
+    t: T,
+    f: impl FnMut(Either<Ty, Const>, DebruijnIndex) -> Either<Ty, Const>,
     binders: DebruijnIndex,
 ) -> T::Result {
     use chalk_ir::{
@@ -324,7 +395,9 @@ pub(crate) fn fold_tys<T: HasInterner<Interner = Interner> + Fold<Interner>>(
         Fallible,
     };
     struct TyFolder<F>(F);
-    impl<'i, F: FnMut(Ty, DebruijnIndex) -> Ty + 'i> Folder<Interner> for TyFolder<F> {
+    impl<'i, F: FnMut(Either<Ty, Const>, DebruijnIndex) -> Either<Ty, Const> + 'i> Folder<Interner>
+        for TyFolder<F>
+    {
         type Error = NoSolution;
 
         fn as_dyn(&mut self) -> &mut dyn Folder<Interner, Error = Self::Error> {
@@ -337,7 +410,11 @@ pub(crate) fn fold_tys<T: HasInterner<Interner = Interner> + Fold<Interner>>(
 
         fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Fallible<Ty> {
             let ty = ty.super_fold_with(self.as_dyn(), outer_binder)?;
-            Ok(self.0(ty, outer_binder))
+            Ok(self.0(Either::Left(ty), outer_binder).left().unwrap())
+        }
+
+        fn fold_const(&mut self, c: Const, outer_binder: DebruijnIndex) -> Fallible<Const> {
+            Ok(self.0(Either::Right(c), outer_binder).right().unwrap())
         }
     }
     t.fold_with(&mut TyFolder(f), binders).expect("fold failed unexpectedly")
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index 1d6affe9c7..9e69785ac2 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -9,9 +9,11 @@ use std::cell::{Cell, RefCell};
 use std::{iter, sync::Arc};
 
 use base_db::CrateId;
-use chalk_ir::{cast::Cast, fold::Shift, interner::HasInterner, Mutability, Safety};
+use chalk_ir::{cast::Cast, fold::Shift, Mutability, Safety};
 use hir_def::generics::TypeOrConstParamData;
 use hir_def::intern::Interned;
+use hir_def::path::{ModPath, PathKind};
+use hir_def::type_ref::ConstScalarOrPath;
 use hir_def::{
     adt::StructKind,
     body::{Expander, LowerCtx},
@@ -24,23 +26,25 @@ use hir_def::{
     ImplId, ItemContainerId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId,
     UnionId, VariantId,
 };
-use hir_def::{ConstParamId, TypeOrConstParamId};
+use hir_def::{ConstParamId, TypeOrConstParamId, TypeParamId};
 use hir_expand::{name::Name, ExpandResult};
+use itertools::Either;
 use la_arena::ArenaMap;
 use rustc_hash::FxHashSet;
 use smallvec::SmallVec;
 use stdx::{impl_from, never};
 use syntax::{ast, SmolStr};
 
-use crate::all_super_traits;
+use crate::consteval::{path_to_const, unknown_const_as_generic, unknown_const_usize, usize_const};
+use crate::method_resolution::fallback_bound_vars;
+use crate::utils::Generics;
+use crate::{all_super_traits, make_binders, Const, GenericArgData, ParamKind};
 use crate::{
     consteval,
     db::HirDatabase,
     mapping::ToChalk,
     static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
-    utils::{
-        all_super_trait_refs, associated_type_by_name_including_super_traits, generics, Generics,
-    },
+    utils::{all_super_trait_refs, associated_type_by_name_including_super_traits, generics},
     AliasEq, AliasTy, Binders, BoundVar, CallableSig, DebruijnIndex, DynTy, FnPointer, FnSig,
     FnSubst, ImplTraitId, Interner, PolyFnSig, ProjectionTy, QuantifiedWhereClause,
     QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits, Substitution,
@@ -56,7 +60,7 @@ pub struct TyLoweringContext<'a> {
     /// some type params should be represented as placeholders, and others
     /// should be converted to variables. I think in practice, this isn't
     /// possible currently, so this should be fine for now.
-    pub type_param_mode: TypeParamLoweringMode,
+    pub type_param_mode: ParamLoweringMode,
     pub impl_trait_mode: ImplTraitLoweringMode,
     impl_trait_counter: Cell<u16>,
     /// When turning `impl Trait` into opaque types, we have to collect the
@@ -77,7 +81,7 @@ impl<'a> TyLoweringContext<'a> {
     pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver) -> Self {
         let impl_trait_counter = Cell::new(0);
         let impl_trait_mode = ImplTraitLoweringMode::Disallowed;
-        let type_param_mode = TypeParamLoweringMode::Placeholder;
+        let type_param_mode = ParamLoweringMode::Placeholder;
         let in_binders = DebruijnIndex::INNERMOST;
         let opaque_type_data = RefCell::new(Vec::new());
         Self {
@@ -129,7 +133,7 @@ impl<'a> TyLoweringContext<'a> {
         Self { impl_trait_mode, ..self }
     }
 
-    pub fn with_type_param_mode(self, type_param_mode: TypeParamLoweringMode) -> Self {
+    pub fn with_type_param_mode(self, type_param_mode: ParamLoweringMode) -> Self {
         Self { type_param_mode, ..self }
     }
 }
@@ -155,7 +159,7 @@ pub enum ImplTraitLoweringMode {
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum TypeParamLoweringMode {
+pub enum ParamLoweringMode {
     Placeholder,
     Variable,
 }
@@ -165,6 +169,15 @@ impl<'a> TyLoweringContext<'a> {
         self.lower_ty_ext(type_ref).0
     }
 
+    fn generics(&self) -> Generics {
+        generics(
+            self.db.upcast(),
+            self.resolver
+                .generic_def()
+                .expect("there should be generics if there's a generic param"),
+        )
+    }
+
     pub fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option<TypeNs>) {
         let mut res = None;
         let ty = match type_ref {
@@ -185,8 +198,14 @@ impl<'a> TyLoweringContext<'a> {
             }
             TypeRef::Array(inner, len) => {
                 let inner_ty = self.lower_ty(inner);
-
-                let const_len = consteval::usize_const(len.as_usize());
+                let const_len = const_or_path_to_chalk(
+                    self.db,
+                    self.resolver,
+                    len,
+                    self.type_param_mode,
+                    || self.generics(),
+                    DebruijnIndex::INNERMOST,
+                );
 
                 TyKind::Array(inner_ty, const_len).intern(Interner)
             }
@@ -221,7 +240,7 @@ impl<'a> TyLoweringContext<'a> {
                         bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)),
                     )
                 });
-                let bounds = crate::make_only_type_binders(1, bounds);
+                let bounds = crate::make_single_type_binders(bounds);
                 TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner)
             }
             TypeRef::ImplTrait(bounds) => {
@@ -239,7 +258,7 @@ impl<'a> TyLoweringContext<'a> {
                         // place even if we encounter more opaque types while
                         // lowering the bounds
                         self.opaque_type_data.borrow_mut().push(ReturnTypeImplTrait {
-                            bounds: crate::make_only_type_binders(1, Vec::new()),
+                            bounds: crate::make_single_type_binders(Vec::new()),
                         });
                         // We don't want to lower the bounds inside the binders
                         // we're currently in, because they don't end up inside
@@ -259,7 +278,7 @@ impl<'a> TyLoweringContext<'a> {
                         let impl_trait_id = ImplTraitId::ReturnTypeImplTrait(func, idx);
                         let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
                         let generics = generics(self.db.upcast(), func.into());
-                        let parameters = generics.bound_vars_subst(self.in_binders);
+                        let parameters = generics.bound_vars_subst(self.db, self.in_binders);
                         TyKind::OpaqueType(opaque_ty_id, parameters).intern(Interner)
                     }
                     ImplTraitLoweringMode::Param => {
@@ -449,8 +468,7 @@ impl<'a> TyLoweringContext<'a> {
                             )
                         });
                         let dyn_ty = DynTy {
-                            bounds: crate::make_only_type_binders(
-                                1,
+                            bounds: crate::make_single_type_binders(
                                 QuantifiedWhereClauses::from_iter(
                                     Interner,
                                     Some(crate::wrap_empty_binders(WhereClause::Implemented(
@@ -475,10 +493,10 @@ impl<'a> TyLoweringContext<'a> {
                     self.resolver.generic_def().expect("generics in scope"),
                 );
                 match self.type_param_mode {
-                    TypeParamLoweringMode::Placeholder => {
+                    ParamLoweringMode::Placeholder => {
                         TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
                     }
-                    TypeParamLoweringMode::Variable => {
+                    ParamLoweringMode::Variable => {
                         let idx = generics.param_idx(param_id.into()).expect("matching generics");
                         TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
                     }
@@ -488,16 +506,20 @@ impl<'a> TyLoweringContext<'a> {
             TypeNs::SelfType(impl_id) => {
                 let generics = generics(self.db.upcast(), impl_id.into());
                 let substs = match self.type_param_mode {
-                    TypeParamLoweringMode::Placeholder => generics.type_params_subst(self.db),
-                    TypeParamLoweringMode::Variable => generics.bound_vars_subst(self.in_binders),
+                    ParamLoweringMode::Placeholder => generics.placeholder_subst(self.db),
+                    ParamLoweringMode::Variable => {
+                        generics.bound_vars_subst(self.db, self.in_binders)
+                    }
                 };
                 self.db.impl_self_ty(impl_id).substitute(Interner, &substs)
             }
             TypeNs::AdtSelfType(adt) => {
                 let generics = generics(self.db.upcast(), adt.into());
                 let substs = match self.type_param_mode {
-                    TypeParamLoweringMode::Placeholder => generics.type_params_subst(self.db),
-                    TypeParamLoweringMode::Variable => generics.bound_vars_subst(self.in_binders),
+                    ParamLoweringMode::Placeholder => generics.placeholder_subst(self.db),
+                    ParamLoweringMode::Variable => {
+                        generics.bound_vars_subst(self.db, self.in_binders)
+                    }
                 };
                 self.db.ty(adt.into()).substitute(Interner, &substs)
             }
@@ -549,7 +571,7 @@ impl<'a> TyLoweringContext<'a> {
             move |name, t, associated_ty| {
                 if name == segment.name {
                     let substs = match self.type_param_mode {
-                        TypeParamLoweringMode::Placeholder => {
+                        ParamLoweringMode::Placeholder => {
                             // if we're lowering to placeholders, we have to put
                             // them in now
                             let generics = generics(
@@ -558,10 +580,10 @@ impl<'a> TyLoweringContext<'a> {
                                     .generic_def()
                                     .expect("there should be generics if there's a generic param"),
                             );
-                            let s = generics.type_params_subst(self.db);
+                            let s = generics.placeholder_subst(self.db);
                             s.apply(t.substitution.clone(), Interner)
                         }
-                        TypeParamLoweringMode::Variable => t.substitution.clone(),
+                        ParamLoweringMode::Variable => t.substitution.clone(),
                     };
                     // We need to shift in the bound vars, since
                     // associated_type_shorthand_candidates does not do that
@@ -642,47 +664,75 @@ impl<'a> TyLoweringContext<'a> {
         explicit_self_ty: Option<Ty>,
     ) -> Substitution {
         let mut substs = Vec::new();
-        let def_generics = def_generic.map(|def| generics(self.db.upcast(), def));
-
+        let def_generics = if let Some(def) = def_generic {
+            generics(self.db.upcast(), def)
+        } else {
+            return Substitution::empty(Interner);
+        };
         let (parent_params, self_params, type_params, const_params, impl_trait_params) =
-            def_generics.map_or((0, 0, 0, 0, 0), |g| g.provenance_split());
+            def_generics.provenance_split();
         let total_len =
             parent_params + self_params + type_params + const_params + impl_trait_params;
 
-        substs.extend(iter::repeat(TyKind::Error.intern(Interner)).take(parent_params));
+        let ty_error = GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner);
+        let const_error = GenericArgData::Const(consteval::usize_const(None)).intern(Interner);
+
+        for (_, data) in def_generics.iter().take(parent_params) {
+            match data {
+                TypeOrConstParamData::TypeParamData(_) => substs.push(ty_error.clone()),
+                TypeOrConstParamData::ConstParamData(_) => substs.push(const_error.clone()),
+            }
+        }
 
         let fill_self_params = || {
             substs.extend(
                 explicit_self_ty
                     .into_iter()
-                    .chain(iter::repeat(TyKind::Error.intern(Interner)))
+                    .map(|x| GenericArgData::Ty(x).intern(Interner))
+                    .chain(iter::repeat(ty_error.clone()))
                     .take(self_params),
             )
         };
-        let mut had_explicit_type_args = false;
+        let mut had_explicit_args = false;
 
         if let Some(generic_args) = &segment.args_and_bindings {
             if !generic_args.has_self_type {
                 fill_self_params();
             }
-            let expected_num =
-                if generic_args.has_self_type { self_params + type_params } else { type_params };
+            let expected_num = if generic_args.has_self_type {
+                self_params + type_params + const_params
+            } else {
+                type_params + const_params
+            };
             let skip = if generic_args.has_self_type && self_params == 0 { 1 } else { 0 };
             // if args are provided, it should be all of them, but we can't rely on that
-            for arg in generic_args
+            for (arg, id) in generic_args
                 .args
                 .iter()
-                .filter(|arg| matches!(arg, GenericArg::Type(_)))
+                .filter(|arg| !matches!(arg, GenericArg::Lifetime(_)))
                 .skip(skip)
                 .take(expected_num)
+                .zip(def_generics.iter_id().skip(skip))
             {
-                match arg {
-                    GenericArg::Type(type_ref) => {
-                        had_explicit_type_args = true;
-                        let ty = self.lower_ty(type_ref);
-                        substs.push(ty);
-                    }
-                    GenericArg::Lifetime(_) => {}
+                if let Some(x) = generic_arg_to_chalk(
+                    self.db,
+                    id,
+                    arg,
+                    &mut (),
+                    |_, type_ref| self.lower_ty(type_ref),
+                    |_, c| {
+                        const_or_path_to_chalk(
+                            self.db,
+                            &self.resolver,
+                            c,
+                            self.type_param_mode,
+                            || self.generics(),
+                            DebruijnIndex::INNERMOST,
+                        )
+                    },
+                ) {
+                    had_explicit_args = true;
+                    substs.push(x);
                 }
             }
         } else {
@@ -692,7 +742,7 @@ impl<'a> TyLoweringContext<'a> {
         // handle defaults. In expression or pattern path segments without
         // explicitly specified type arguments, missing type arguments are inferred
         // (i.e. defaults aren't used).
-        if !infer_args || had_explicit_type_args {
+        if !infer_args || had_explicit_args {
             if let Some(def_generic) = def_generic {
                 let defaults = self.db.generic_defaults(def_generic);
                 assert_eq!(total_len, defaults.len());
@@ -707,8 +757,11 @@ impl<'a> TyLoweringContext<'a> {
 
         // add placeholders for args that were not provided
         // FIXME: emit diagnostics in contexts where this is not allowed
-        for _ in substs.len()..total_len {
-            substs.push(TyKind::Error.intern(Interner));
+        for (_, data) in def_generics.iter().skip(substs.len()) {
+            match data {
+                TypeOrConstParamData::TypeParamData(_) => substs.push(ty_error.clone()),
+                TypeOrConstParamData::ConstParamData(_) => substs.push(const_error.clone()),
+            }
         }
         assert_eq!(substs.len(), total_len);
 
@@ -775,8 +828,8 @@ impl<'a> TyLoweringContext<'a> {
                         };
                         let placeholder = to_placeholder_idx(self.db, param_id);
                         match self.type_param_mode {
-                            TypeParamLoweringMode::Placeholder => TyKind::Placeholder(placeholder),
-                            TypeParamLoweringMode::Variable => {
+                            ParamLoweringMode::Placeholder => TyKind::Placeholder(placeholder),
+                            ParamLoweringMode::Variable => {
                                 let idx = generics.param_idx(param_id).expect("matching generics");
                                 TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, idx))
                             }
@@ -919,8 +972,7 @@ impl<'a> TyLoweringContext<'a> {
             }
             predicates
         });
-
-        ReturnTypeImplTrait { bounds: crate::make_only_type_binders(1, predicates) }
+        ReturnTypeImplTrait { bounds: crate::make_single_type_binders(predicates) }
     }
 }
 
@@ -999,7 +1051,7 @@ fn named_associated_type_shorthand_candidates<R>(
             // Handle `Self::Type` referring to own associated type in trait definitions
             if let GenericDefId::TraitId(trait_id) = param_id.parent() {
                 let generics = generics(db.upcast(), trait_id.into());
-                if generics.params.tocs[param_id.local_id()].is_trait_self() {
+                if generics.params.type_or_consts[param_id.local_id()].is_trait_self() {
                     let trait_ref = TyBuilder::trait_ref(db, trait_id)
                         .fill_with_bound_vars(DebruijnIndex::INNERMOST, 0)
                         .build();
@@ -1026,9 +1078,9 @@ pub(crate) fn field_types_query(
     let generics = generics(db.upcast(), def);
     let mut res = ArenaMap::default();
     let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
+        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
     for (field_id, field_data) in var_data.fields().iter() {
-        res.insert(field_id, make_binders(&generics, ctx.lower_ty(&field_data.type_ref)))
+        res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(&field_data.type_ref)))
     }
     Arc::new(res)
 }
@@ -1049,7 +1101,7 @@ pub(crate) fn generic_predicates_for_param_query(
 ) -> Arc<[Binders<QuantifiedWhereClause>]> {
     let resolver = def.resolver(db.upcast());
     let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
+        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
     let generics = generics(db.upcast(), def);
     let mut predicates: Vec<_> = resolver
         .where_predicates_in_scope()
@@ -1097,14 +1149,16 @@ pub(crate) fn generic_predicates_for_param_query(
             }
             WherePredicate::Lifetime { .. } => false,
         })
-        .flat_map(|pred| ctx.lower_where_predicate(pred, true).map(|p| make_binders(&generics, p)))
+        .flat_map(|pred| {
+            ctx.lower_where_predicate(pred, true).map(|p| make_binders(db, &generics, p))
+        })
         .collect();
 
-    let subst = generics.bound_vars_subst(DebruijnIndex::INNERMOST);
+    let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
     let explicitly_unsized_tys = ctx.unsized_types.into_inner();
     let implicitly_sized_predicates =
         implicitly_sized_clauses(db, param_id.parent, &explicitly_unsized_tys, &subst, &resolver)
-            .map(|p| make_binders(&generics, crate::wrap_empty_binders(p)));
+            .map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p)));
     predicates.extend(implicitly_sized_predicates);
     predicates.into()
 }
@@ -1124,8 +1178,8 @@ pub(crate) fn trait_environment_query(
     def: GenericDefId,
 ) -> Arc<TraitEnvironment> {
     let resolver = def.resolver(db.upcast());
-    let ctx = TyLoweringContext::new(db, &resolver)
-        .with_type_param_mode(TypeParamLoweringMode::Placeholder);
+    let ctx =
+        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Placeholder);
     let mut traits_in_scope = Vec::new();
     let mut clauses = Vec::new();
     for pred in resolver.where_predicates_in_scope() {
@@ -1153,14 +1207,14 @@ pub(crate) fn trait_environment_query(
         // function default implementations (and speculative code
         // inside consts or type aliases)
         cov_mark::hit!(trait_self_implements_self);
-        let substs = TyBuilder::type_params_subst(db, trait_id);
+        let substs = TyBuilder::placeholder_subst(db, trait_id);
         let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution: substs };
         let pred = WhereClause::Implemented(trait_ref);
         let program_clause: chalk_ir::ProgramClause<Interner> = pred.cast(Interner);
         clauses.push(program_clause.into_from_env_clause(Interner));
     }
 
-    let subst = generics(db.upcast(), def).type_params_subst(db);
+    let subst = generics(db.upcast(), def).placeholder_subst(db);
     let explicitly_unsized_tys = ctx.unsized_types.into_inner();
     let implicitly_sized_clauses =
         implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver).map(|pred| {
@@ -1183,19 +1237,21 @@ pub(crate) fn generic_predicates_query(
 ) -> Arc<[Binders<QuantifiedWhereClause>]> {
     let resolver = def.resolver(db.upcast());
     let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
+        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
     let generics = generics(db.upcast(), def);
 
     let mut predicates = resolver
         .where_predicates_in_scope()
-        .flat_map(|pred| ctx.lower_where_predicate(pred, false).map(|p| make_binders(&generics, p)))
+        .flat_map(|pred| {
+            ctx.lower_where_predicate(pred, false).map(|p| make_binders(db, &generics, p))
+        })
         .collect::<Vec<_>>();
 
-    let subst = generics.bound_vars_subst(DebruijnIndex::INNERMOST);
+    let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
     let explicitly_unsized_tys = ctx.unsized_types.into_inner();
     let implicitly_sized_predicates =
         implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver)
-            .map(|p| make_binders(&generics, crate::wrap_empty_binders(p)));
+            .map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p)));
     predicates.extend(implicitly_sized_predicates);
     predicates.into()
 }
@@ -1234,40 +1290,36 @@ fn implicitly_sized_clauses<'a>(
 pub(crate) fn generic_defaults_query(
     db: &dyn HirDatabase,
     def: GenericDefId,
-) -> Arc<[Binders<Ty>]> {
+) -> Arc<[Binders<chalk_ir::GenericArg<Interner>>]> {
     let resolver = def.resolver(db.upcast());
     let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
+        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
     let generic_params = generics(db.upcast(), def);
 
     let defaults = generic_params
-        .toc_iter()
+        .iter()
         .enumerate()
-        .map(|(idx, (_, p))| {
+        .map(|(idx, (id, p))| {
             let p = match p {
                 TypeOrConstParamData::TypeParamData(p) => p,
                 TypeOrConstParamData::ConstParamData(_) => {
-                    // FIXME: here we should add const generic parameters
-                    let ty = TyKind::Error.intern(Interner);
-                    return crate::make_only_type_binders(idx, ty);
+                    // FIXME: implement const generic defaults
+                    let val = unknown_const_as_generic(
+                        db.const_param_ty(ConstParamId::from_unchecked(id)),
+                    );
+                    return crate::make_binders_with_count(db, idx, &generic_params, val);
                 }
             };
             let mut ty =
                 p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
 
             // Each default can only refer to previous parameters.
-            ty = crate::fold_free_vars(ty, |bound, binders| {
-                if bound.index >= idx && bound.debruijn == DebruijnIndex::INNERMOST {
-                    // type variable default referring to parameter coming
-                    // after it. This is forbidden (FIXME: report
-                    // diagnostic)
-                    TyKind::Error.intern(Interner)
-                } else {
-                    bound.shifted_in_from(binders).to_ty(Interner)
-                }
-            });
-
-            crate::make_only_type_binders(idx, ty)
+            // type variable default referring to parameter coming
+            // after it. This is forbidden (FIXME: report
+            // diagnostic)
+            ty = fallback_bound_vars(ty, idx);
+            let val = GenericArgData::Ty(ty).intern(Interner);
+            crate::make_binders_with_count(db, idx, &generic_params, val)
         })
         .collect();
 
@@ -1278,17 +1330,21 @@ pub(crate) fn generic_defaults_recover(
     db: &dyn HirDatabase,
     _cycle: &[String],
     def: &GenericDefId,
-) -> Arc<[Binders<Ty>]> {
+) -> Arc<[Binders<crate::GenericArg>]> {
     let generic_params = generics(db.upcast(), *def);
-
+    // FIXME: this code is not covered in tests.
     // we still need one default per parameter
     let defaults = generic_params
-        .toc_iter()
+        .iter_id()
         .enumerate()
-        .map(|(idx, _)| {
-            let ty = TyKind::Error.intern(Interner);
-
-            crate::make_only_type_binders(idx, ty)
+        .map(|(count, id)| {
+            let val = match id {
+                itertools::Either::Left(_) => {
+                    GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
+                }
+                itertools::Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)),
+            };
+            crate::make_binders_with_count(db, count, &generic_params, val)
         })
         .collect();
 
@@ -1300,26 +1356,27 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
     let resolver = def.resolver(db.upcast());
     let ctx_params = TyLoweringContext::new(db, &resolver)
         .with_impl_trait_mode(ImplTraitLoweringMode::Variable)
-        .with_type_param_mode(TypeParamLoweringMode::Variable);
+        .with_type_param_mode(ParamLoweringMode::Variable);
     let params = data.params.iter().map(|(_, tr)| ctx_params.lower_ty(tr)).collect::<Vec<_>>();
     let ctx_ret = TyLoweringContext::new(db, &resolver)
         .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
-        .with_type_param_mode(TypeParamLoweringMode::Variable);
+        .with_type_param_mode(ParamLoweringMode::Variable);
     let ret = ctx_ret.lower_ty(&data.ret_type);
     let generics = generics(db.upcast(), def.into());
     let mut sig = CallableSig::from_params_and_return(params, ret, data.is_varargs());
     if !data.legacy_const_generics_indices.is_empty() {
         sig.set_legacy_const_generics_indices(&data.legacy_const_generics_indices);
     }
-    make_binders(&generics, sig)
+    make_binders(db, &generics, sig)
 }
 
 /// Build the declared type of a function. This should not need to look at the
 /// function body.
 fn type_for_fn(db: &dyn HirDatabase, def: FunctionId) -> Binders<Ty> {
     let generics = generics(db.upcast(), def.into());
-    let substs = generics.bound_vars_subst(DebruijnIndex::INNERMOST);
+    let substs = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
     make_binders(
+        db,
         &generics,
         TyKind::FnDef(CallableDefId::FunctionId(def).to_chalk(db), substs).intern(Interner),
     )
@@ -1331,9 +1388,9 @@ fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders<Ty> {
     let generics = generics(db.upcast(), def.into());
     let resolver = def.resolver(db.upcast());
     let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
+        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
 
-    make_binders(&generics, ctx.lower_ty(&data.type_ref))
+    make_binders(db, &generics, ctx.lower_ty(&data.type_ref))
 }
 
 /// Build the declared type of a static.
@@ -1350,7 +1407,7 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS
     let fields = struct_data.variant_data.fields();
     let resolver = def.resolver(db.upcast());
     let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
+        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
     let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>();
     let (ret, binders) = type_for_adt(db, def.into()).into_value_and_skipped_binders();
     Binders::new(binders, CallableSig::from_params_and_return(params, ret, false))
@@ -1363,8 +1420,9 @@ fn type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Binders<T
         return type_for_adt(db, def.into());
     }
     let generics = generics(db.upcast(), def.into());
-    let substs = generics.bound_vars_subst(DebruijnIndex::INNERMOST);
+    let substs = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
     make_binders(
+        db,
         &generics,
         TyKind::FnDef(CallableDefId::StructId(def).to_chalk(db), substs).intern(Interner),
     )
@@ -1376,7 +1434,7 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId)
     let fields = var_data.variant_data.fields();
     let resolver = def.parent.resolver(db.upcast());
     let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
+        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
     let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>();
     let (ret, binders) = type_for_adt(db, def.parent.into()).into_value_and_skipped_binders();
     Binders::new(binders, CallableSig::from_params_and_return(params, ret, false))
@@ -1390,8 +1448,9 @@ fn type_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) -
         return type_for_adt(db, def.parent.into());
     }
     let generics = generics(db.upcast(), def.parent.into());
-    let substs = generics.bound_vars_subst(DebruijnIndex::INNERMOST);
+    let substs = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
     make_binders(
+        db,
         &generics,
         TyKind::FnDef(CallableDefId::EnumVariantId(def).to_chalk(db), substs).intern(Interner),
     )
@@ -1399,22 +1458,22 @@ fn type_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) -
 
 fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> {
     let generics = generics(db.upcast(), adt.into());
-    let b = TyBuilder::adt(db, adt);
-    let ty = b.fill_with_bound_vars(DebruijnIndex::INNERMOST, 0).build();
-    make_binders(&generics, ty)
+    let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
+    let ty = TyKind::Adt(crate::AdtId(adt), subst).intern(Interner);
+    make_binders(db, &generics, ty)
 }
 
 fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
     let generics = generics(db.upcast(), t.into());
     let resolver = t.resolver(db.upcast());
     let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
+        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
     if db.type_alias_data(t).is_extern {
         Binders::empty(Interner, TyKind::Foreign(crate::to_foreign_def_id(t)).intern(Interner))
     } else {
         let type_ref = &db.type_alias_data(t).type_ref;
         let inner = ctx.lower_ty(type_ref.as_deref().unwrap_or(&TypeRef::Error));
-        make_binders(&generics, inner)
+        make_binders(db, &generics, inner)
     }
 }
 
@@ -1485,7 +1544,7 @@ pub(crate) fn ty_recover(db: &dyn HirDatabase, _cycle: &[String], def: &TyDefId)
         TyDefId::AdtId(it) => generics(db.upcast(), it.into()),
         TyDefId::TypeAliasId(it) => generics(db.upcast(), it.into()),
     };
-    make_binders(&generics, TyKind::Error.intern(Interner))
+    make_binders(db, &generics, TyKind::Error.intern(Interner))
 }
 
 pub(crate) fn value_ty_query(db: &dyn HirDatabase, def: ValueTyDefId) -> Binders<Ty> {
@@ -1509,14 +1568,14 @@ pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binde
     ));
     let generics = generics(db.upcast(), impl_id.into());
     let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
-    make_binders(&generics, ctx.lower_ty(&impl_data.self_ty))
+        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+    make_binders(db, &generics, ctx.lower_ty(&impl_data.self_ty))
 }
 
 // returns None if def is a type arg
 pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty {
     let parent_data = db.generic_params(def.parent());
-    let data = &parent_data.tocs[def.local_id()];
+    let data = &parent_data.type_or_consts[def.local_id()];
     let resolver = def.parent().resolver(db.upcast());
     let ctx = TyLoweringContext::new(db, &resolver);
     match data {
@@ -1534,7 +1593,7 @@ pub(crate) fn impl_self_ty_recover(
     impl_id: &ImplId,
 ) -> Binders<Ty> {
     let generics = generics(db.upcast(), (*impl_id).into());
-    make_binders(&generics, TyKind::Error.intern(Interner))
+    make_binders(db, &generics, TyKind::Error.intern(Interner))
 }
 
 pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<Binders<TraitRef>> {
@@ -1546,7 +1605,7 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<
         impl_id, impl_loc, impl_data
     ));
     let ctx =
-        TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
+        TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
     let (self_ty, binders) = db.impl_self_ty(impl_id).into_value_and_skipped_binders();
     let target_trait = impl_data.target_trait.as_ref()?;
     Some(Binders::new(binders, ctx.lower_trait_ref(target_trait, Some(self_ty))?))
@@ -1561,7 +1620,7 @@ pub(crate) fn return_type_impl_traits(
     let resolver = def.resolver(db.upcast());
     let ctx_ret = TyLoweringContext::new(db, &resolver)
         .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
-        .with_type_param_mode(TypeParamLoweringMode::Variable);
+        .with_type_param_mode(ParamLoweringMode::Variable);
     let _ret = (&ctx_ret).lower_ty(&data.ret_type);
     let generics = generics(db.upcast(), def.into());
     let return_type_impl_traits =
@@ -1569,7 +1628,7 @@ pub(crate) fn return_type_impl_traits(
     if return_type_impl_traits.impl_traits.is_empty() {
         None
     } else {
-        Some(Arc::new(make_binders(&generics, return_type_impl_traits)))
+        Some(Arc::new(make_binders(db, &generics, return_type_impl_traits)))
     }
 }
 
@@ -1580,6 +1639,65 @@ pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mut
     }
 }
 
-fn make_binders<T: HasInterner<Interner = Interner>>(generics: &Generics, value: T) -> Binders<T> {
-    crate::make_only_type_binders(generics.len(), value)
+pub(crate) fn generic_arg_to_chalk<'a, T>(
+    db: &dyn HirDatabase,
+    kind_id: Either<TypeParamId, ConstParamId>,
+    arg: &'a GenericArg,
+    this: &mut T,
+    for_type: impl FnOnce(&mut T, &TypeRef) -> Ty + 'a,
+    for_const: impl FnOnce(&mut T, &ConstScalarOrPath) -> Const + 'a,
+) -> Option<crate::GenericArg> {
+    let kind = match kind_id {
+        Either::Left(_) => ParamKind::Type,
+        Either::Right(id) => {
+            let ty = db.const_param_ty(id);
+            ParamKind::Const(ty)
+        }
+    };
+    Some(match (arg, kind) {
+        (GenericArg::Type(type_ref), ParamKind::Type) => {
+            let ty = for_type(this, type_ref);
+            GenericArgData::Ty(ty).intern(Interner)
+        }
+        (GenericArg::Const(c), ParamKind::Const(_)) => {
+            GenericArgData::Const(for_const(this, c)).intern(Interner)
+        }
+        (GenericArg::Const(_), ParamKind::Type) => {
+            GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
+        }
+        (GenericArg::Type(t), ParamKind::Const(ty)) => {
+            // We want to recover simple idents, which parser detects them
+            // as types. Maybe here is not the best place to do it, but
+            // it works.
+            if let TypeRef::Path(p) = t {
+                let p = p.mod_path();
+                if p.kind == PathKind::Plain {
+                    if let [n] = p.segments() {
+                        let c = ConstScalarOrPath::Path(n.clone());
+                        return Some(GenericArgData::Const(for_const(this, &c)).intern(Interner));
+                    }
+                }
+            }
+            unknown_const_as_generic(ty)
+        }
+        (GenericArg::Lifetime(_), _) => return None,
+    })
+}
+
+pub(crate) fn const_or_path_to_chalk(
+    db: &dyn HirDatabase,
+    resolver: &Resolver,
+    value: &ConstScalarOrPath,
+    mode: ParamLoweringMode,
+    args: impl FnOnce() -> Generics,
+    debruijn: DebruijnIndex,
+) -> Const {
+    match value {
+        ConstScalarOrPath::Scalar(s) => usize_const(s.as_usize()),
+        ConstScalarOrPath::Path(n) => {
+            let path = ModPath::from_segments(PathKind::Plain, Some(n.clone()));
+            path_to_const(db, resolver, &path, mode, args, debruijn)
+                .unwrap_or_else(|| unknown_const_usize())
+        }
+    }
 }
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index 6564a3f4c7..06c834fbc8 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -6,7 +6,7 @@ use std::{iter, ops::ControlFlow, sync::Arc};
 
 use arrayvec::ArrayVec;
 use base_db::{CrateId, Edition};
-use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
+use chalk_ir::{cast::Cast, fold::Fold, interner::HasInterner, Mutability, UniverseIndex};
 use hir_def::{
     item_scope::ItemScope, lang_item::LangItemTarget, nameres::DefMap, AssocItemId, BlockId,
     ConstId, FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId,
@@ -25,8 +25,9 @@ use crate::{
     primitive::{self, FloatTy, IntTy, UintTy},
     static_lifetime,
     utils::all_super_traits,
-    AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, ForeignDefId, InEnvironment, Interner,
-    Scalar, Substitution, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
+    AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, ForeignDefId, GenericArgData,
+    InEnvironment, Interner, Scalar, Substitution, TraitEnvironment, TraitRefExt, Ty, TyBuilder,
+    TyExt, TyKind,
 };
 
 /// This is used as a key for indexing impls.
@@ -1087,13 +1088,14 @@ pub(crate) fn inherent_impl_substs(
         .build();
     let self_ty_with_vars = db.impl_self_ty(impl_id).substitute(Interner, &vars);
     let mut kinds = self_ty.binders.interned().to_vec();
-    kinds.extend(
-        iter::repeat(chalk_ir::WithKind::new(
-            chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General),
-            UniverseIndex::ROOT,
-        ))
-        .take(vars.len(Interner)),
-    );
+    kinds.extend(vars.iter(Interner).map(|x| {
+        let kind = match x.data(Interner) {
+            GenericArgData::Ty(_) => chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General),
+            GenericArgData::Const(c) => chalk_ir::VariableKind::Const(c.data(Interner).ty.clone()),
+            GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime,
+        };
+        chalk_ir::WithKind::new(kind, UniverseIndex::ROOT)
+    }));
     let tys = Canonical {
         binders: CanonicalVarKinds::from_iter(Interner, kinds),
         value: (self_ty_with_vars, self_ty.value.clone()),
@@ -1111,14 +1113,27 @@ pub(crate) fn inherent_impl_substs(
 
 /// This replaces any 'free' Bound vars in `s` (i.e. those with indices past
 /// num_vars_to_keep) by `TyKind::Unknown`.
-fn fallback_bound_vars(s: Substitution, num_vars_to_keep: usize) -> Substitution {
-    crate::fold_free_vars(s, |bound, binders| {
-        if bound.index >= num_vars_to_keep && bound.debruijn == DebruijnIndex::INNERMOST {
-            TyKind::Error.intern(Interner)
-        } else {
-            bound.shifted_in_from(binders).to_ty(Interner)
-        }
-    })
+pub(crate) fn fallback_bound_vars<T: Fold<Interner> + HasInterner<Interner = Interner>>(
+    s: T,
+    num_vars_to_keep: usize,
+) -> T::Result {
+    crate::fold_free_vars(
+        s,
+        |bound, binders| {
+            if bound.index >= num_vars_to_keep && bound.debruijn == DebruijnIndex::INNERMOST {
+                TyKind::Error.intern(Interner)
+            } else {
+                bound.shifted_in_from(binders).to_ty(Interner)
+            }
+        },
+        |ty, bound, binders| {
+            if bound.index >= num_vars_to_keep && bound.debruijn == DebruijnIndex::INNERMOST {
+                consteval::usize_const(None)
+            } else {
+                bound.shifted_in_from(binders).to_const(Interner, ty)
+            }
+        },
+    )
 }
 
 fn transform_receiver_ty(
@@ -1183,13 +1198,18 @@ fn generic_implements_goal(
         .push(self_ty.value.clone())
         .fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len())
         .build();
-    kinds.extend(
-        iter::repeat(chalk_ir::WithKind::new(
-            chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General),
-            UniverseIndex::ROOT,
-        ))
-        .take(trait_ref.substitution.len(Interner) - 1),
-    );
+    kinds.extend(trait_ref.substitution.iter(Interner).skip(1).map(|x| {
+        let vk = match x.data(Interner) {
+            chalk_ir::GenericArgData::Ty(_) => {
+                chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
+            }
+            chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime,
+            chalk_ir::GenericArgData::Const(c) => {
+                chalk_ir::VariableKind::Const(c.data(Interner).ty.clone())
+            }
+        };
+        chalk_ir::WithKind::new(vk, UniverseIndex::ROOT)
+    }));
     let obligation = trait_ref.cast(Interner);
     Canonical {
         binders: CanonicalVarKinds::from_iter(Interner, kinds),
diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs
index a6706aa81d..343e89eb9b 100644
--- a/crates/hir_ty/src/utils.rs
+++ b/crates/hir_ty/src/utils.rs
@@ -15,16 +15,18 @@ use hir_def::{
     path::Path,
     resolver::{HasResolver, TypeNs},
     type_ref::{TraitBoundModifier, TypeRef},
-    GenericDefId, ItemContainerId, Lookup, TraitId, TypeAliasId, TypeOrConstParamId,
+    ConstParamId, GenericDefId, ItemContainerId, Lookup, TraitId, TypeAliasId, TypeOrConstParamId,
+    TypeParamId,
 };
 use hir_expand::name::{name, Name};
+use itertools::Either;
 use rustc_hash::FxHashSet;
 use smallvec::{smallvec, SmallVec};
 use syntax::SmolStr;
 
 use crate::{
-    db::HirDatabase, ChalkTraitId, Interner, Substitution, TraitRef, TraitRefExt, TyKind,
-    WhereClause,
+    db::HirDatabase, ChalkTraitId, ConstData, ConstValue, GenericArgData, Interner, Substitution,
+    TraitRef, TraitRefExt, TyKind, WhereClause,
 };
 
 pub(crate) fn fn_traits(db: &dyn DefDatabase, krate: CrateId) -> impl Iterator<Item = TraitId> {
@@ -203,30 +205,43 @@ impl Generics {
             )
     }
 
-    pub(crate) fn toc_iter<'a>(
+    pub(crate) fn iter_id<'a>(
         &'a self,
-    ) -> impl Iterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a {
+    ) -> impl Iterator<Item = Either<TypeParamId, ConstParamId>> + 'a {
+        self.iter().map(|(id, data)| match data {
+            TypeOrConstParamData::TypeParamData(_) => Either::Left(TypeParamId::from_unchecked(id)),
+            TypeOrConstParamData::ConstParamData(_) => {
+                Either::Right(ConstParamId::from_unchecked(id))
+            }
+        })
+    }
+
+    /// Iterator over types and const params of parent, then self.
+    pub(crate) fn iter<'a>(
+        &'a self,
+    ) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a {
         self.parent_generics
             .as_ref()
             .into_iter()
             .flat_map(|it| {
                 it.params
-                    .toc_iter()
+                    .iter()
                     .map(move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p))
             })
             .chain(
-                self.params.toc_iter().map(move |(local_id, p)| {
+                self.params.iter().map(move |(local_id, p)| {
                     (TypeOrConstParamId { parent: self.def, local_id }, p)
                 }),
             )
     }
 
+    /// Iterator over types and const params of parent.
     pub(crate) fn iter_parent<'a>(
         &'a self,
     ) -> impl Iterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a {
         self.parent_generics.as_ref().into_iter().flat_map(|it| {
             it.params
-                .tocs
+                .type_or_consts
                 .iter()
                 .map(move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p))
         })
@@ -239,7 +254,7 @@ impl Generics {
     /// (total, parents, child)
     pub(crate) fn len_split(&self) -> (usize, usize, usize) {
         let parent = self.parent_generics.as_ref().map_or(0, |p| p.len());
-        let child = self.params.tocs.len();
+        let child = self.params.type_or_consts.len();
         (parent + child, parent, child)
     }
 
@@ -248,22 +263,20 @@ impl Generics {
         let parent = self.parent_generics.as_ref().map_or(0, |p| p.len());
         let self_params = self
             .params
-            .tocs
             .iter()
             .filter_map(|x| x.1.type_param())
             .filter(|p| p.provenance == TypeParamProvenance::TraitSelf)
             .count();
         let type_params = self
             .params
-            .tocs
+            .type_or_consts
             .iter()
             .filter_map(|x| x.1.type_param())
             .filter(|p| p.provenance == TypeParamProvenance::TypeParamList)
             .count();
-        let const_params = self.params.tocs.iter().filter_map(|x| x.1.const_param()).count();
+        let const_params = self.params.iter().filter_map(|x| x.1.const_param()).count();
         let impl_trait_params = self
             .params
-            .tocs
             .iter()
             .filter_map(|x| x.1.type_param())
             .filter(|p| p.provenance == TypeParamProvenance::ArgumentImplTrait)
@@ -279,7 +292,7 @@ impl Generics {
         if param.parent == self.def {
             let (idx, (_local_id, data)) = self
                 .params
-                .tocs
+                .type_or_consts
                 .iter()
                 .enumerate()
                 .find(|(_, (idx, _))| *idx == param.local_id)
@@ -292,21 +305,47 @@ impl Generics {
     }
 
     /// Returns a Substitution that replaces each parameter by a bound variable.
-    pub(crate) fn bound_vars_subst(&self, debruijn: DebruijnIndex) -> Substitution {
+    pub(crate) fn bound_vars_subst(
+        &self,
+        db: &dyn HirDatabase,
+        debruijn: DebruijnIndex,
+    ) -> Substitution {
         Substitution::from_iter(
             Interner,
-            self.toc_iter()
-                .enumerate()
-                .map(|(idx, _)| TyKind::BoundVar(BoundVar::new(debruijn, idx)).intern(Interner)),
+            self.iter_id().enumerate().map(|(idx, id)| match id {
+                Either::Left(_) => GenericArgData::Ty(
+                    TyKind::BoundVar(BoundVar::new(debruijn, idx)).intern(Interner),
+                )
+                .intern(Interner),
+                Either::Right(id) => GenericArgData::Const(
+                    ConstData {
+                        value: ConstValue::BoundVar(BoundVar::new(debruijn, idx)),
+                        ty: db.const_param_ty(id),
+                    }
+                    .intern(Interner),
+                )
+                .intern(Interner),
+            }),
         )
     }
 
     /// Returns a Substitution that replaces each parameter by itself (i.e. `Ty::Param`).
-    pub(crate) fn type_params_subst(&self, db: &dyn HirDatabase) -> Substitution {
+    pub(crate) fn placeholder_subst(&self, db: &dyn HirDatabase) -> Substitution {
         Substitution::from_iter(
             Interner,
-            self.toc_iter().map(|(id, _)| {
-                TyKind::Placeholder(crate::to_placeholder_idx(db, id)).intern(Interner)
+            self.iter_id().map(|id| match id {
+                Either::Left(id) => GenericArgData::Ty(
+                    TyKind::Placeholder(crate::to_placeholder_idx(db, id.into())).intern(Interner),
+                )
+                .intern(Interner),
+                Either::Right(id) => GenericArgData::Const(
+                    ConstData {
+                        value: ConstValue::Placeholder(crate::to_placeholder_idx(db, id.into())),
+                        ty: db.const_param_ty(id),
+                    }
+                    .intern(Interner),
+                )
+                .intern(Interner),
             }),
         )
     }


Test patch:
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index c118ae24cf..9700b388aa 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -1359,7 +1359,69 @@ impl<T> [T] {
 fn f() {
     let v = [1, 2].map::<_, usize>(|x| -> x * 2);
     v;
-  //^ [usize; _]
+  //^ [usize; 2]
+}
+    "#,
+    );
+}
+
+#[test]
+fn resolve_const_generic_method() {
+    check_types(
+        r#"
+struct Const<const N: usize>;
+
+#[lang = "array"]
+impl<T, const N: usize> [T; N] {
+    pub fn my_map<F, U, const X: usize>(self, f: F, c: Const<X>) -> [U; X]
+    where
+        F: FnMut(T) -> U,
+    { loop {} }
+}
+
+#[lang = "slice"]
+impl<T> [T] {
+    pub fn my_map<F, const X: usize, U>(self, f: F, c: Const<X>) -> &[U]
+    where
+        F: FnMut(T) -> U,
+    { loop {} }
+}
+
+fn f<const C: usize, P>() {
+    let v = [1, 2].my_map::<_, (), 12>(|x| -> x * 2, Const::<12>);
+    v;
+  //^ [(); 12]
+    let v = [1, 2].my_map::<_, P, C>(|x| -> x * 2, Const::<C>);
+    v;
+  //^ [P; C]
+}
+    "#,
+    );
+}
+
+#[test]
+fn const_generic_type_alias() {
+    check_types(
+        r#"
+struct Const<const N: usize>;
+type U2 = Const<2>;
+type U5 = Const<5>;
+
+impl U2 {
+    fn f(self) -> Const<12> {
+        loop {}
+    }
+}
+
+impl U5 {
+    fn f(self) -> Const<15> {
+        loop {}
+    }
+}
+
+fn f(x: U2) {
+    let y = x.f();
+      //^ Const<12>
 }
     "#,
     );
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index 020c5da680..4a65b927ba 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -1301,7 +1301,7 @@ impl<I: Iterator> IntoIterator for I {
 
 #[test]
 fn bug_11659() {
-    check_infer(
+    check_no_mismatches(
         r#"
 struct LinkArray<const N: usize, LD>(LD);
 fn f<const N: usize, LD>(x: LD) -> LinkArray<N, LD> {
@@ -1314,26 +1314,8 @@ fn test() {
     let y = LinkArray::<52, LinkArray<2, i32>>(x);
 }
         "#,
-        expect![[r#"
-        67..68 'x': LD
-        94..138 '{     ...   r }': LinkArray<{unknown}, LD>
-        104..105 'r': LinkArray<{unknown}, LD>
-        108..126 'LinkAr...N, LD>': LinkArray<{unknown}, LD>(LD) -> LinkArray<{unknown}, LD>
-        108..129 'LinkAr...LD>(x)': LinkArray<{unknown}, LD>
-        127..128 'x': LD
-        135..136 'r': LinkArray<{unknown}, LD>
-        150..232 '{     ...(x); }': ()
-        160..161 'x': LinkArray<{unknown}, {unknown}>
-        164..175 'f::<2, i32>': fn f<i32, i32>(i32) -> LinkArray<{unknown}, {unknown}>
-        164..178 'f::<2, i32>(5)': LinkArray<{unknown}, {unknown}>
-        176..177 '5': i32
-        188..189 'y': LinkArray<LinkArray<i32, {unknown}>, LinkArray<{unknown}, {unknown}>>
-        192..226 'LinkAr... i32>>': LinkArray<LinkArray<i32, {unknown}>, LinkArray<{unknown}, {unknown}>>(LinkArray<{unknown}, {unknown}>) -> LinkArray<LinkArray<i32, {unknown}>, LinkArray<{unknown}, {unknown}>>
-        192..229 'LinkAr...2>>(x)': LinkArray<LinkArray<i32, {unknown}>, LinkArray<{unknown}, {unknown}>>
-        227..228 'x': LinkArray<{unknown}, {unknown}>
-        "#]],
     );
-    check_infer(
+    check_no_mismatches(
         r#"
 struct LinkArray<LD, const N: usize>(LD);
 fn f<const N: usize, LD>(x: LD) -> LinkArray<LD, N> {
@@ -1346,23 +1328,106 @@ fn test() {
     let y = LinkArray::<LinkArray<i32, 2>, 52>(x);
 }
         "#,
-        expect![[r#"
-        67..68 'x': LD
-        94..138 '{     ...   r }': LinkArray<LD, {unknown}>
-        104..105 'r': LinkArray<LD, {unknown}>
-        108..126 'LinkAr...LD, N>': LinkArray<LD, {unknown}>(LD) -> LinkArray<LD, {unknown}>
-        108..129 'LinkAr... N>(x)': LinkArray<LD, {unknown}>
-        127..128 'x': LD
-        135..136 'r': LinkArray<LD, {unknown}>
-        150..232 '{     ...(x); }': ()
-        160..161 'x': LinkArray<i32, {unknown}>
-        164..175 'f::<i32, 2>': fn f<i32, i32>(i32) -> LinkArray<i32, {unknown}>
-        164..178 'f::<i32, 2>(5)': LinkArray<i32, {unknown}>
-        176..177 '5': i32
-        188..189 'y': LinkArray<LinkArray<i32, {unknown}>, {unknown}>
-        192..226 'LinkAr...>, 52>': LinkArray<LinkArray<i32, {unknown}>, {unknown}>(LinkArray<i32, {unknown}>) -> LinkArray<LinkArray<i32, {unknown}>, {unknown}>
-        192..229 'LinkAr...52>(x)': LinkArray<LinkArray<i32, {unknown}>, {unknown}>
-        227..228 'x': LinkArray<i32, {unknown}>
-        "#]],
     );
 }
+
+#[test]
+fn const_generic_error_tolerance() {
+    check_no_mismatches(
+        r#"
+#[lang = "sized"]
+pub trait Sized {}
+
+struct CT<const N: usize, T>(T);
+struct TC<T, const N: usize>(T);
+fn f<const N: usize, T>(x: T) -> (CT<N, T>, TC<T, N>) {
+    let l = CT::<N, T>(x);
+    let r = TC::<N, T>(x);
+    (l, r)
+}
+
+trait TR1<const N: usize>;
+trait TR2<const N: usize>;
+
+impl<const N: usize, T> TR1<N> for CT<N, T>;
+impl<const N: usize, T> TR1<5> for TC<T, N>;
+impl<const N: usize, T> TR2<N> for CT<T, N>;
+
+trait TR3<const N: usize> {
+    fn tr3(&self) -> &Self;
+}
+
+impl<const N: usize, T> TR3<5> for TC<T, N> {
+    fn tr3(&self) -> &Self {
+        self
+    }
+}
+
+impl<const N: usize, T> TR3<Item = 5> for TC<T, N> {}
+impl<const N: usize, T> TR3<T> for TC<T, N> {}
+
+fn impl_trait<const N: usize>(inp: impl TR1<N>) {}
+fn dyn_trait<const N: usize>(inp: &dyn TR2<N>) {}
+fn impl_trait_bad<'a, const N: usize>(inp: impl TR1<i32>) -> impl TR1<'a, i32> {}
+fn impl_trait_very_bad<const N: usize>(inp: impl TR1<Item = i32>) -> impl TR1<'a, Item = i32, 5, Foo = N> {}
+
+fn test() {
+    f::<2, i32>(5);
+    f::<2, 2>(5);
+    f(5);
+    f::<i32>(5);
+    CT::<52, CT<2, i32>>(x);
+    CT::<CT<2, i32>>(x);
+    impl_trait_bad(5);
+    impl_trait_bad(12);
+    TR3<5>::tr3();
+    TR3<{ 2+3 }>::tr3();
+    TC::<i32, 10>(5).tr3();
+    TC::<i32, 20>(5).tr3();
+    TC::<i32, i32>(5).tr3();
+    TC::<i32, { 7 + 3 }>(5).tr3();
+}
+        "#,
+    );
+}
+
+#[test]
+fn const_generic_impl_trait() {
+    check_no_mismatches(
+        r#"
+        //- minicore: from
+
+        struct Foo<T, const M: usize>;
+
+        trait Tr<T> {
+            fn f(T) -> Self;
+        }
+
+        impl<T, const M: usize> Tr<[T; M]> for Foo<T, M> {
+            fn f(_: [T; M]) -> Self {
+                Self
+            }
+        }
+
+        fn test() {
+            Foo::f([1, 2, 7, 10]);
+        }
+        "#,
+    );
+}
+
+#[test]
+fn nalgebra_factorial() {
+    check_no_mismatches(
+        r#"
+        const FACTORIAL: [u128; 4] = [1, 1, 2, 6];
+
+        fn factorial(n: usize) -> u128 {
+            match FACTORIAL.get(n) {
+                Some(f) => *f,
+                None => panic!("{}! is greater than u128::MAX", n),
+            }
+        }
+        "#,
+    )
+}
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index c11a70fa66..0d050f7461 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -1202,14 +1202,13 @@ fn infer_array() {
 
             let b = [a, ["b"]];
             let x: [u8; 0] = [];
-            // FIXME: requires const evaluation/taking type from rhs somehow
             let y: [u8; 2+2] = [1,2,3,4];
         }
         "#,
         expect![[r#"
             8..9 'x': &str
             17..18 'y': isize
-            27..395 '{     ...,4]; }': ()
+            27..326 '{     ...,4]; }': ()
             37..38 'a': [&str; 1]
             41..44 '[x]': [&str; 1]
             42..43 'x': &str
@@ -1259,12 +1258,12 @@ fn infer_array() {
             259..262 '"b"': &str
             274..275 'x': [u8; 0]
             287..289 '[]': [u8; 0]
-            368..369 'y': [u8; _]
-            383..392 '[1,2,3,4]': [u8; 4]
-            384..385 '1': u8
-            386..387 '2': u8
-            388..389 '3': u8
-            390..391 '4': u8
+            299..300 'y': [u8; 4]
+            314..323 '[1,2,3,4]': [u8; 4]
+            315..316 '1': u8
+            317..318 '2': u8
+            319..320 '3': u8
+            321..322 '4': u8
         "#]],
     );
 }
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 04d8b91e35..5daffb2c5e 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -3394,7 +3394,6 @@ fn main() {
     )
 }
 
-// FIXME: We should infer the length of the returned array :)
 #[test]
 fn const_generics() {
     check_infer(
@@ -3418,18 +3417,18 @@ fn main() {
 "#,
         expect![[r#"
             44..48 'self': &Self
-            151..155 'self': &[u8; _]
-            173..194 '{     ...     }': [u8; _]
-            183..188 '*self': [u8; _]
-            184..188 'self': &[u8; _]
+            151..155 'self': &[u8; L]
+            173..194 '{     ...     }': [u8; L]
+            183..188 '*self': [u8; L]
+            184..188 'self': &[u8; L]
             208..260 '{     ...g(); }': ()
             218..219 'v': [u8; 2]
             222..230 '[0u8; 2]': [u8; 2]
             223..226 '0u8': u8
             228..229 '2': usize
-            240..242 'v2': [u8; _]
+            240..242 'v2': [u8; 2]
             245..246 'v': [u8; 2]
-            245..257 'v.do_thing()': [u8; _]
+            245..257 'v.do_thing()': [u8; 2]
         "#]],
     )
 }
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index df27f935c8..96cd83b924 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -3361,6 +3361,27 @@ fn foo<T$0: Sized + ?Sized + Sized + Trait>() {}
     }
 }
 
+#[test]
+fn hover_const_generic_type_alias() {
+    check(
+        r#"
+struct Foo<const LEN: usize>;
+type Fo$0o2 = Foo<2>;
+"#,
+        expect![[r#"
+                *Foo2*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                type Foo2 = Foo<2>
+                ```
+            "#]],
+    );
+}
+
 #[test]
 fn hover_const_param() {
     check(