| //! `TyBuilder`, a helper for building instances of `Ty` and related types. |
| |
| use std::iter; |
| |
| use chalk_ir::{ |
| cast::{Cast, CastTo, Caster}, |
| fold::TypeFoldable, |
| interner::HasInterner, |
| AdtId, DebruijnIndex, Scalar, |
| }; |
| use hir_def::{ |
| builtin_type::BuiltinType, DefWithBodyId, GenericDefId, GenericParamId, TraitId, TypeAliasId, |
| }; |
| use smallvec::SmallVec; |
| |
| use crate::{ |
| consteval::unknown_const_as_generic, db::HirDatabase, error_lifetime, generics::generics, |
| infer::unify::InferenceTable, primitive, to_assoc_type_id, to_chalk_trait_id, Binders, |
| BoundVar, CallableSig, GenericArg, GenericArgData, Interner, ProjectionTy, Substitution, |
| TraitRef, Ty, TyDefId, TyExt, TyKind, |
| }; |
| |
| #[derive(Debug, Clone, PartialEq, Eq)] |
| pub enum ParamKind { |
| Type, |
| Lifetime, |
| Const(Ty), |
| } |
| |
| /// This is a builder for `Ty` or anything that needs a `Substitution`. |
| pub struct TyBuilder<D> { |
| /// The `data` field is used to keep track of what we're building (e.g. an |
| /// ADT, a `TraitRef`, ...). |
| data: D, |
| vec: SmallVec<[GenericArg; 2]>, |
| param_kinds: SmallVec<[ParamKind; 2]>, |
| parent_subst: Substitution, |
| } |
| |
| impl<A> TyBuilder<A> { |
| fn with_data<B>(self, data: B) -> TyBuilder<B> { |
| TyBuilder { |
| data, |
| vec: self.vec, |
| param_kinds: self.param_kinds, |
| parent_subst: self.parent_subst, |
| } |
| } |
| } |
| |
| impl<D> TyBuilder<D> { |
| fn new( |
| data: D, |
| param_kinds: SmallVec<[ParamKind; 2]>, |
| parent_subst: Option<Substitution>, |
| ) -> Self { |
| let parent_subst = parent_subst.unwrap_or_else(|| Substitution::empty(Interner)); |
| Self { data, vec: SmallVec::with_capacity(param_kinds.len()), param_kinds, parent_subst } |
| } |
| |
| fn new_empty(data: D) -> Self { |
| TyBuilder::new(data, SmallVec::new(), None) |
| } |
| |
| fn build_internal(self) -> (D, Substitution) { |
| assert_eq!( |
| self.vec.len(), |
| self.param_kinds.len(), |
| "{} args received, {} expected ({:?})", |
| self.vec.len(), |
| self.param_kinds.len(), |
| &self.param_kinds |
| ); |
| 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.into_iter().chain(self.parent_subst.iter(Interner).cloned()), |
| ); |
| (self.data, subst) |
| } |
| |
| pub fn build_into_subst(self) -> Substitution { |
| self.build_internal().1 |
| } |
| |
| pub fn push(mut self, arg: impl CastTo<GenericArg>) -> Self { |
| assert!(self.remaining() > 0); |
| let arg = arg.cast(Interner); |
| let expected_kind = &self.param_kinds[self.vec.len()]; |
| |
| let arg_kind = match arg.data(Interner) { |
| GenericArgData::Ty(_) => ParamKind::Type, |
| GenericArgData::Lifetime(_) => panic!("Got lifetime in TyBuilder::push"), |
| 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_kinds.len() - self.vec.len() |
| } |
| |
| pub fn fill_with_bound_vars(self, debruijn: DebruijnIndex, starting_from: usize) -> Self { |
| // self.fill is inlined to make borrow checker happy |
| let mut this = self; |
| let other = &this.param_kinds[this.vec.len()..]; |
| let filler = (starting_from..).zip(other).map(|(idx, kind)| match kind { |
| ParamKind::Type => BoundVar::new(debruijn, idx).to_ty(Interner).cast(Interner), |
| ParamKind::Const(ty) => { |
| BoundVar::new(debruijn, idx).to_const(Interner, ty.clone()).cast(Interner) |
| } |
| ParamKind::Lifetime => { |
| BoundVar::new(debruijn, idx).to_lifetime(Interner).cast(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 is inlined to make borrow checker happy |
| let mut this = self; |
| let filler = this.param_kinds[this.vec.len()..].iter().map(|x| match x { |
| ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner), |
| ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), |
| ParamKind::Lifetime => error_lifetime().cast(Interner), |
| }); |
| this.vec.extend(filler.casted(Interner)); |
| assert_eq!(this.remaining(), 0); |
| this |
| } |
| |
| #[tracing::instrument(skip_all)] |
| pub(crate) fn fill_with_inference_vars(self, table: &mut InferenceTable<'_>) -> Self { |
| self.fill(|x| match x { |
| ParamKind::Type => table.new_type_var().cast(Interner), |
| ParamKind::Const(ty) => table.new_const_var(ty.clone()).cast(Interner), |
| ParamKind::Lifetime => table.new_lifetime_var().cast(Interner), |
| }) |
| } |
| |
| pub fn fill(mut self, filler: impl FnMut(&ParamKind) -> GenericArg) -> Self { |
| self.vec.extend(self.param_kinds[self.vec.len()..].iter().map(filler)); |
| assert_eq!(self.remaining(), 0); |
| self |
| } |
| |
| fn assert_match_kind(&self, a: &chalk_ir::GenericArg<Interner>, e: &ParamKind) { |
| match (a.data(Interner), e) { |
| (GenericArgData::Ty(_), ParamKind::Type) |
| | (GenericArgData::Const(_), ParamKind::Const(_)) |
| | (GenericArgData::Lifetime(_), ParamKind::Lifetime) => (), |
| _ => panic!("Mismatched kinds: {a:?}, {:?}, {:?}", self.vec, self.param_kinds), |
| } |
| } |
| } |
| |
| impl TyBuilder<()> { |
| pub fn unit() -> Ty { |
| TyKind::Tuple(0, Substitution::empty(Interner)).intern(Interner) |
| } |
| |
| // FIXME: rustc's ty is dependent on the adt type, maybe we need to do that as well |
| pub fn discr_ty() -> Ty { |
| TyKind::Scalar(chalk_ir::Scalar::Int(chalk_ir::IntTy::I128)).intern(Interner) |
| } |
| |
| pub fn bool() -> Ty { |
| TyKind::Scalar(chalk_ir::Scalar::Bool).intern(Interner) |
| } |
| |
| pub fn usize() -> Ty { |
| TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(Interner) |
| } |
| |
| pub fn fn_ptr(sig: CallableSig) -> Ty { |
| TyKind::Function(sig.to_fn_ptr()).intern(Interner) |
| } |
| |
| pub fn builtin(builtin: BuiltinType) -> Ty { |
| match builtin { |
| BuiltinType::Char => TyKind::Scalar(Scalar::Char).intern(Interner), |
| BuiltinType::Bool => TyKind::Scalar(Scalar::Bool).intern(Interner), |
| BuiltinType::Str => TyKind::Str.intern(Interner), |
| BuiltinType::Int(t) => { |
| TyKind::Scalar(Scalar::Int(primitive::int_ty_from_builtin(t))).intern(Interner) |
| } |
| BuiltinType::Uint(t) => { |
| TyKind::Scalar(Scalar::Uint(primitive::uint_ty_from_builtin(t))).intern(Interner) |
| } |
| BuiltinType::Float(t) => { |
| TyKind::Scalar(Scalar::Float(primitive::float_ty_from_builtin(t))).intern(Interner) |
| } |
| } |
| } |
| |
| pub fn slice(argument: Ty) -> Ty { |
| TyKind::Slice(argument).intern(Interner) |
| } |
| |
| pub fn placeholder_subst(db: &dyn HirDatabase, def: impl Into<GenericDefId>) -> Substitution { |
| let params = generics(db.upcast(), def.into()); |
| params.placeholder_subst(db) |
| } |
| |
| pub fn unknown_subst(db: &dyn HirDatabase, def: impl Into<GenericDefId>) -> Substitution { |
| let params = generics(db.upcast(), def.into()); |
| Substitution::from_iter( |
| Interner, |
| params.iter_id().map(|id| match id { |
| GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner), |
| GenericParamId::ConstParamId(id) => { |
| unknown_const_as_generic(db.const_param_ty(id)).cast(Interner) |
| } |
| GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner), |
| }), |
| ) |
| } |
| |
| #[tracing::instrument(skip_all)] |
| pub fn subst_for_def( |
| db: &dyn HirDatabase, |
| def: impl Into<GenericDefId>, |
| parent_subst: Option<Substitution>, |
| ) -> TyBuilder<()> { |
| let generics = generics(db.upcast(), def.into()); |
| assert!(generics.parent_generics().is_some() == parent_subst.is_some()); |
| let params = generics |
| .iter_self() |
| .map(|(id, _data)| match id { |
| GenericParamId::TypeParamId(_) => ParamKind::Type, |
| GenericParamId::ConstParamId(id) => ParamKind::Const(db.const_param_ty(id)), |
| GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime, |
| }) |
| .collect(); |
| TyBuilder::new((), params, parent_subst) |
| } |
| |
| /// Creates a `TyBuilder` to build `Substitution` for a coroutine defined in `parent`. |
| /// |
| /// A coroutine's substitution consists of: |
| /// - resume type of coroutine |
| /// - yield type of coroutine ([`Coroutine::Yield`](std::ops::Coroutine::Yield)) |
| /// - return type of coroutine ([`Coroutine::Return`](std::ops::Coroutine::Return)) |
| /// - generic parameters in scope on `parent` |
| /// |
| /// in this order. |
| /// |
| /// This method prepopulates the builder with placeholder substitution of `parent`, so you |
| /// should only push exactly 3 `GenericArg`s before building. |
| pub fn subst_for_coroutine(db: &dyn HirDatabase, parent: DefWithBodyId) -> TyBuilder<()> { |
| let parent_subst = parent |
| .as_generic_def_id(db.upcast()) |
| .map(|p| generics(db.upcast(), p).placeholder_subst(db)); |
| // These represent resume type, yield type, and return type of coroutine. |
| let params = std::iter::repeat(ParamKind::Type).take(3).collect(); |
| TyBuilder::new((), params, parent_subst) |
| } |
| |
| pub fn subst_for_closure( |
| db: &dyn HirDatabase, |
| parent: DefWithBodyId, |
| sig_ty: Ty, |
| ) -> Substitution { |
| let sig_ty = sig_ty.cast(Interner); |
| let self_subst = iter::once(&sig_ty); |
| let Some(parent) = parent.as_generic_def_id(db.upcast()) else { |
| return Substitution::from_iter(Interner, self_subst); |
| }; |
| Substitution::from_iter( |
| Interner, |
| self_subst |
| .chain(generics(db.upcast(), parent).placeholder_subst(db).iter(Interner)) |
| .cloned() |
| .collect::<Vec<_>>(), |
| ) |
| } |
| |
| pub fn build(self) -> Substitution { |
| let ((), subst) = self.build_internal(); |
| subst |
| } |
| } |
| |
| impl TyBuilder<hir_def::AdtId> { |
| pub fn adt(db: &dyn HirDatabase, def: hir_def::AdtId) -> TyBuilder<hir_def::AdtId> { |
| TyBuilder::subst_for_def(db, def, None).with_data(def) |
| } |
| |
| pub fn fill_with_defaults( |
| mut self, |
| db: &dyn HirDatabase, |
| mut fallback: impl FnMut() -> Ty, |
| ) -> Self { |
| // Note that we're building ADT, so we never have parent generic parameters. |
| let defaults = db.generic_defaults(self.data.into()); |
| |
| for default_ty in &defaults[self.vec.len()..] { |
| // NOTE(skip_binders): we only check if the arg type is error type. |
| if let Some(x) = default_ty.skip_binders().ty(Interner) { |
| if x.is_unknown() { |
| self.vec.push(fallback().cast(Interner)); |
| continue; |
| } |
| } |
| // Each default can only depend on the previous parameters. |
| let subst_so_far = Substitution::from_iter( |
| Interner, |
| self.vec |
| .iter() |
| .cloned() |
| .chain(self.param_kinds[self.vec.len()..].iter().map(|it| match it { |
| ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner), |
| ParamKind::Lifetime => error_lifetime().cast(Interner), |
| ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), |
| })) |
| .take(self.param_kinds.len()), |
| ); |
| self.vec.push(default_ty.clone().substitute(Interner, &subst_so_far).cast(Interner)); |
| } |
| self |
| } |
| |
| pub fn build(self) -> Ty { |
| let (adt, subst) = self.build_internal(); |
| TyKind::Adt(AdtId(adt), subst).intern(Interner) |
| } |
| } |
| |
| pub struct Tuple(usize); |
| impl TyBuilder<Tuple> { |
| pub fn tuple(size: usize) -> TyBuilder<Tuple> { |
| TyBuilder::new(Tuple(size), iter::repeat(ParamKind::Type).take(size).collect(), None) |
| } |
| |
| pub fn build(self) -> Ty { |
| let (Tuple(size), subst) = self.build_internal(); |
| TyKind::Tuple(size, subst).intern(Interner) |
| } |
| |
| pub fn tuple_with<I>(elements: I) -> Ty |
| where |
| I: IntoIterator<Item = Ty>, |
| <I as IntoIterator>::IntoIter: ExactSizeIterator, |
| { |
| let elements = elements.into_iter(); |
| let len = elements.len(); |
| let mut b = |
| TyBuilder::new(Tuple(len), iter::repeat(ParamKind::Type).take(len).collect(), None); |
| for e in elements { |
| b = b.push(e); |
| } |
| b.build() |
| } |
| } |
| |
| impl TyBuilder<TraitId> { |
| pub fn trait_ref(db: &dyn HirDatabase, def: TraitId) -> TyBuilder<TraitId> { |
| TyBuilder::subst_for_def(db, def, None).with_data(def) |
| } |
| |
| pub fn build(self) -> TraitRef { |
| let (trait_id, substitution) = self.build_internal(); |
| TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution } |
| } |
| } |
| |
| impl TyBuilder<TypeAliasId> { |
| pub fn assoc_type_projection( |
| db: &dyn HirDatabase, |
| def: TypeAliasId, |
| parent_subst: Option<Substitution>, |
| ) -> TyBuilder<TypeAliasId> { |
| TyBuilder::subst_for_def(db, def, parent_subst).with_data(def) |
| } |
| |
| pub fn build(self) -> ProjectionTy { |
| let (type_alias, substitution) = self.build_internal(); |
| ProjectionTy { associated_ty_id: to_assoc_type_id(type_alias), substitution } |
| } |
| } |
| |
| impl<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>> TyBuilder<Binders<T>> { |
| pub fn build(self) -> T { |
| let (b, subst) = self.build_internal(); |
| b.substitute(Interner, &subst) |
| } |
| } |
| |
| impl TyBuilder<Binders<Ty>> { |
| pub fn def_ty( |
| db: &dyn HirDatabase, |
| def: TyDefId, |
| parent_subst: Option<Substitution>, |
| ) -> TyBuilder<Binders<Ty>> { |
| let poly_ty = db.ty(def); |
| let id: GenericDefId = match def { |
| TyDefId::BuiltinType(_) => { |
| assert!(parent_subst.is_none()); |
| return TyBuilder::new_empty(poly_ty); |
| } |
| TyDefId::AdtId(id) => id.into(), |
| TyDefId::TypeAliasId(id) => id.into(), |
| }; |
| TyBuilder::subst_for_def(db, id, parent_subst).with_data(poly_ty) |
| } |
| |
| pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder<Binders<Ty>> { |
| TyBuilder::subst_for_def(db, def, None).with_data(db.impl_self_ty(def)) |
| } |
| } |