From b301b040f5781a9083348936369a01c37138756f Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Wed, 9 Mar 2022 22:20:24 +0330 Subject: [PATCH] Add const generics --- Cargo.lock | 16 +- crates/hir/src/display.rs | 21 +- crates/hir/src/lib.rs | 68 +++- crates/hir/src/semantics/source_to_def.rs | 4 +- crates/hir_def/src/generics.rs | 54 +-- crates/hir_def/src/item_tree/lower.rs | 2 +- crates/hir_def/src/item_tree/pretty.rs | 7 +- crates/hir_def/src/lib.rs | 12 +- crates/hir_def/src/path.rs | 7 +- crates/hir_def/src/path/lower.rs | 8 +- crates/hir_def/src/resolver.rs | 29 +- crates/hir_def/src/type_ref.rs | 85 +++-- crates/hir_ty/Cargo.toml | 6 +- crates/hir_ty/src/builder.rs | 177 +++++++--- crates/hir_ty/src/chalk_db.rs | 56 ++- crates/hir_ty/src/chalk_ext.rs | 4 +- crates/hir_ty/src/consteval.rs | 89 ++++- crates/hir_ty/src/db.rs | 4 +- crates/hir_ty/src/display.rs | 64 ++-- crates/hir_ty/src/infer.rs | 60 +++- crates/hir_ty/src/infer/expr.rs | 83 +++-- crates/hir_ty/src/infer/path.rs | 40 ++- crates/hir_ty/src/infer/unify.rs | 47 ++- crates/hir_ty/src/lib.rs | 101 +++++- crates/hir_ty/src/lower.rs | 350 +++++++++++++------ crates/hir_ty/src/method_resolution.rs | 70 ++-- crates/hir_ty/src/tests/method_resolution.rs | 64 +++- crates/hir_ty/src/tests/regression.rs | 141 ++++++-- crates/hir_ty/src/tests/simple.rs | 15 +- crates/hir_ty/src/tests/traits.rs | 13 +- crates/hir_ty/src/utils.rs | 81 +++-- crates/ide/src/hover/tests.rs | 21 ++ 32 files changed, 1272 insertions(+), 527 deletions(-) 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 ¶ms.tocs[*id].name() { - Some(name) => write!(f, "{}", name), - None => write!(f, "{{unnamed}}"), - }, + WherePredicateTypeTarget::TypeOrConstParam(id) => { + match ¶ms.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 { 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 { 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 { let params = db.generic_params(self.id.parent); - match ¶ms.tocs[self.id.local_id] { + match ¶ms.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 { + 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) -> Option { 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 { 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, + pub type_or_consts: Arena, pub lifetimes: Arena, pub where_predicates: Vec, } @@ -138,13 +138,14 @@ impl GenericParams { pub fn type_iter<'a>( &'a self, ) -> impl Iterator, &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, &TypeOrConstParamData)> { - self.tocs.iter() + ) -> impl DoubleEndedIterator, &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 { - 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 { + 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 { - 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 { + 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 { - 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 for GenericDefId { db: &dyn DefDatabase, ) -> InFile> { 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 ¶ms.tocs[*id].name() { + match ¶ms.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 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 for TypeOrConstParamId { fn from(x: TypeParamId) -> Self { x.0 @@ -367,11 +369,13 @@ impl ConstParamId { } } -impl From 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 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) = ¶m.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, Option, 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, ConstScalar), + Array(Box, ConstScalarOrPath), Slice(Box), /// A fn pointer. Last element of the vector is the return type. Fn(Vec<(Option, 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) -> 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 { /// 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 TyBuilder { + fn with_data(self, data: B) -> TyBuilder { + TyBuilder { data, param_kinds: self.param_kinds, vec: self.vec } + } } impl TyBuilder { - fn new(data: D, param_count: usize) -> TyBuilder { - TyBuilder { data, param_count, vec: SmallVec::with_capacity(param_count) } + fn new(data: D, param_kinds: SmallVec<[ParamKind; 2]>) -> TyBuilder { + 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) -> 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>) -> 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 + 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, 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) -> Substitution { + pub fn placeholder_subst(db: &dyn HirDatabase, def: impl Into) -> 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) -> 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 { - pub fn adt(db: &dyn HirDatabase, adt: hir_def::AdtId) -> TyBuilder { - 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 { + TyBuilder::subst_for_def(db, def).with_data(def) } pub fn fill_with_defaults( @@ -133,14 +210,15 @@ impl TyBuilder { ) -> 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 { pub struct Tuple(usize); impl TyBuilder { pub fn tuple(size: usize) -> TyBuilder { - 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 { } impl TyBuilder { - pub fn trait_ref(db: &dyn HirDatabase, trait_id: TraitId) -> TyBuilder { - 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 { + TyBuilder::subst_for_def(db, def).with_data(def) } pub fn build(self) -> TraitRef { @@ -177,13 +253,8 @@ impl TyBuilder { } impl TyBuilder { - pub fn assoc_type_projection( - db: &dyn HirDatabase, - type_alias: TypeAliasId, - ) -> TyBuilder { - 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 { + TyBuilder::subst_for_def(db, def).with_data(def) } pub fn build(self) -> ProjectionTy { @@ -194,8 +265,16 @@ impl TyBuilder { impl + Fold> TyBuilder> { fn subst_binders(b: Binders) -> 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) -> >::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 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 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 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 for ChalkContext<'a> { _substs: &chalk_ir::Substitution, ) -> chalk_ir::Binders> { 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 { 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, mut ctx: ConstEvalCtx<'_>) -> Option { None } +pub(crate) fn path_to_const( + db: &dyn HirDatabase, + resolver: &Resolver, + path: &ModPath, + mode: ParamLoweringMode, + args_lazy: impl FnOnce() -> Generics, + debruijn: DebruijnIndex, +) -> Option { + 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) -> Const { ConstData { @@ -313,3 +374,27 @@ pub fn usize_const(value: Option) -> Const { } .intern(Interner) } + +pub(crate) fn eval_to_const( + expr: Idx, + 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 { #[salsa::invoke(crate::lower::generic_defaults_query)] #[salsa::cycle(crate::lower::generic_defaults_recover)] - fn generic_defaults(&self, def: GenericDefId) -> Arc<[Binders]>; + fn generic_defaults(&self, def: GenericDefId) -> Arc<[Binders]>; #[salsa::invoke(InherentImpls::inherent_impls_in_crate_query)] fn inherent_impls_in_crate(&self, krate: CrateId) -> Arc; 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], + 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; } } ¶meters.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 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, - resolver: Resolver, + pub(crate) body: Arc, + pub(crate) resolver: Resolver, table: unify::InferenceTable<'a>, trait_env: Arc, 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, - 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> Canonicalized { // 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>( - num_vars: usize, +pub(crate) fn make_type_and_const_binders>( + which_is_const: impl Iterator>, value: T, ) -> Binders { 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>( + value: T, +) -> Binders { + 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>( + db: &dyn HirDatabase, + count: usize, + generics: &Generics, + value: T, +) -> Binders { + 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>( + db: &dyn HirDatabase, + generics: &Generics, + value: T, +) -> Binders { + make_binders_with_count(db, usize::MAX, generics, value) +} + // FIXME: get rid of this pub fn make_canonical>( value: T, @@ -288,11 +328,17 @@ pub fn dummy_usize_const() -> Const { pub(crate) fn fold_free_vars + Fold>( 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); - impl<'i, F: FnMut(BoundVar, DebruijnIndex) -> Ty + 'i> Folder for FreeVarFolder { + struct FreeVarFolder(F1, F2); + impl< + 'i, + F1: FnMut(BoundVar, DebruijnIndex) -> Ty + 'i, + F2: FnMut(Ty, BoundVar, DebruijnIndex) -> Const + 'i, + > Folder for FreeVarFolder + { type Error = NoSolution; fn as_dyn(&mut self) -> &mut dyn Folder { @@ -310,13 +356,38 @@ pub(crate) fn fold_free_vars + Fold Fallible { Ok(self.0(bound_var, outer_binder)) } + + fn fold_free_var_const( + &mut self, + ty: Ty, + bound_var: BoundVar, + outer_binder: DebruijnIndex, + ) -> Fallible { + 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 + Fold>( 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 + Fold>( + t: T, + f: impl FnMut(Either, DebruijnIndex) -> Either, binders: DebruijnIndex, ) -> T::Result { use chalk_ir::{ @@ -324,7 +395,9 @@ pub(crate) fn fold_tys + Fold>( Fallible, }; struct TyFolder(F); - impl<'i, F: FnMut(Ty, DebruijnIndex) -> Ty + 'i> Folder for TyFolder { + impl<'i, F: FnMut(Either, DebruijnIndex) -> Either + 'i> Folder + for TyFolder + { type Error = NoSolution; fn as_dyn(&mut self) -> &mut dyn Folder { @@ -337,7 +410,11 @@ pub(crate) fn fold_tys + Fold>( fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Fallible { 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 { + 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, /// 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) { 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, ) -> 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( // 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]> { 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 { 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 = 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]> { 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::>(); - 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]> { +) -> Arc<[Binders>]> { 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]> { +) -> Arc<[Binders]> { 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::>(); 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 { 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 { 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::>(); 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>(); 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 { 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 { 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 { @@ -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 { 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> { @@ -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>(generics: &Generics, value: T) -> Binders { - crate::make_only_type_binders(generics.len(), value) +pub(crate) fn generic_arg_to_chalk<'a, T>( + db: &dyn HirDatabase, + kind_id: Either, + 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 { + 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 + HasInterner>( + 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/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] { 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; + +#[lang = "array"] +impl [T; N] { + pub fn my_map(self, f: F, c: Const) -> [U; X] + where + F: FnMut(T) -> U, + { loop {} } +} + +#[lang = "slice"] +impl [T] { + pub fn my_map(self, f: F, c: Const) -> &[U] + where + F: FnMut(T) -> U, + { loop {} } +} + +fn f() { + 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::); + v; + //^ [P; C] +} + "#, + ); +} + +#[test] +fn const_generic_type_alias() { + check_types( + r#" +struct Const; +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 IntoIterator for I { #[test] fn bug_11659() { - check_infer( + check_no_mismatches( r#" struct LinkArray(LD); fn f(x: LD) -> LinkArray { @@ -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) -> LinkArray<{unknown}, {unknown}> - 164..178 'f::<2, i32>(5)': LinkArray<{unknown}, {unknown}> - 176..177 '5': i32 - 188..189 'y': LinkArray, LinkArray<{unknown}, {unknown}>> - 192..226 'LinkAr... i32>>': LinkArray, LinkArray<{unknown}, {unknown}>>(LinkArray<{unknown}, {unknown}>) -> LinkArray, LinkArray<{unknown}, {unknown}>> - 192..229 'LinkAr...2>>(x)': LinkArray, LinkArray<{unknown}, {unknown}>> - 227..228 'x': LinkArray<{unknown}, {unknown}> - "#]], ); - check_infer( + check_no_mismatches( r#" struct LinkArray(LD); fn f(x: LD) -> LinkArray { @@ -1346,23 +1328,106 @@ fn test() { let y = LinkArray::, 52>(x); } "#, - expect![[r#" - 67..68 'x': LD - 94..138 '{ ... r }': LinkArray - 104..105 'r': LinkArray - 108..126 'LinkAr...LD, N>': LinkArray(LD) -> LinkArray - 108..129 'LinkAr... N>(x)': LinkArray - 127..128 'x': LD - 135..136 'r': LinkArray - 150..232 '{ ...(x); }': () - 160..161 'x': LinkArray - 164..175 'f::': fn f(i32) -> LinkArray - 164..178 'f::(5)': LinkArray - 176..177 '5': i32 - 188..189 'y': LinkArray, {unknown}> - 192..226 'LinkAr...>, 52>': LinkArray, {unknown}>(LinkArray) -> LinkArray, {unknown}> - 192..229 'LinkAr...52>(x)': LinkArray, {unknown}> - 227..228 'x': LinkArray - "#]], ); } + +#[test] +fn const_generic_error_tolerance() { + check_no_mismatches( + r#" +#[lang = "sized"] +pub trait Sized {} + +struct CT(T); +struct TC(T); +fn f(x: T) -> (CT, TC) { + let l = CT::(x); + let r = TC::(x); + (l, r) +} + +trait TR1; +trait TR2; + +impl TR1 for CT; +impl TR1<5> for TC; +impl TR2 for CT; + +trait TR3 { + fn tr3(&self) -> &Self; +} + +impl TR3<5> for TC { + fn tr3(&self) -> &Self { + self + } +} + +impl TR3 for TC {} +impl TR3 for TC {} + +fn impl_trait(inp: impl TR1) {} +fn dyn_trait(inp: &dyn TR2) {} +fn impl_trait_bad<'a, const N: usize>(inp: impl TR1) -> impl TR1<'a, i32> {} +fn impl_trait_very_bad(inp: impl TR1) -> impl TR1<'a, Item = i32, 5, Foo = N> {} + +fn test() { + f::<2, i32>(5); + f::<2, 2>(5); + f(5); + f::(5); + CT::<52, CT<2, i32>>(x); + CT::>(x); + impl_trait_bad(5); + impl_trait_bad(12); + TR3<5>::tr3(); + TR3<{ 2+3 }>::tr3(); + TC::(5).tr3(); + TC::(5).tr3(); + TC::(5).tr3(); + TC::(5).tr3(); +} + "#, + ); +} + +#[test] +fn const_generic_impl_trait() { + check_no_mismatches( + r#" + //- minicore: from + + struct Foo; + + trait Tr { + fn f(T) -> Self; + } + + impl Tr<[T; M]> for Foo { + 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/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 { @@ -203,30 +205,43 @@ impl Generics { ) } - pub(crate) fn toc_iter<'a>( + pub(crate) fn iter_id<'a>( &'a self, - ) -> impl Iterator + 'a { + ) -> impl Iterator> + '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 + '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 + '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), }), ) } 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() {} } } +#[test] +fn hover_const_generic_type_alias() { + check( + r#" +struct Foo; +type Fo$0o2 = Foo<2>; +"#, + expect![[r#" + *Foo2* + + ```rust + test + ``` + + ```rust + type Foo2 = Foo<2> + ``` + "#]], + ); +} + #[test] fn hover_const_param() { check(