blob: 5c4eb8475bbc2470c72b02fd101ac7a608495bc7 [file] [log] [blame]
//! `TyBuilder`, a helper for building instances of `Ty` and related types.
use chalk_ir::{
DebruijnIndex, Scalar,
cast::{Cast, Caster},
};
use hir_def::{GenericDefId, GenericParamId, TraitId, builtin_type::BuiltinType};
use smallvec::SmallVec;
use crate::{
BoundVar, GenericArg, GenericArgData, Interner, Substitution, TraitRef, Ty, TyKind,
consteval::unknown_const_as_generic,
db::HirDatabase,
error_lifetime,
generics::generics,
infer::unify::InferenceTable,
next_solver::{
DbInterner, EarlyBinder,
mapping::{ChalkToNextSolver, NextSolverToChalk},
},
primitive, to_chalk_trait_id,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum ParamKind {
Type,
Lifetime,
Const(Ty),
}
/// This is a builder for `Ty` or anything that needs a `Substitution`.
pub(crate) 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 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.parent_subst.iter(Interner).cloned().chain(self.vec),
);
(self.data, subst)
}
pub(crate) fn remaining(&self) -> usize {
self.param_kinds.len() - self.vec.len()
}
pub(crate) 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
}
#[tracing::instrument(skip_all)]
pub(crate) fn fill_with_inference_vars(self, table: &mut InferenceTable<'_>) -> Self {
self.fill(|x| {
match x {
ParamKind::Type => crate::next_solver::GenericArg::Ty(table.next_ty_var()),
ParamKind::Const(_) => table.next_const_var().into(),
ParamKind::Lifetime => table.next_region_var().into(),
}
.to_chalk(table.interner())
})
}
pub(crate) 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(crate) fn usize() -> Ty {
TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(Interner)
}
pub(crate) 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(crate) fn unknown_subst(
db: &dyn HirDatabase,
def: impl Into<GenericDefId>,
) -> Substitution {
let interner = DbInterner::conjure();
let params = generics(db, 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_ns(id))
.to_chalk(interner)
.cast(Interner)
}
GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
}),
)
}
#[tracing::instrument(skip_all)]
pub(crate) fn subst_for_def(
db: &dyn HirDatabase,
def: impl Into<GenericDefId>,
parent_subst: Option<Substitution>,
) -> TyBuilder<()> {
let generics = generics(db, 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)
}
pub(crate) fn build(self) -> Substitution {
let ((), subst) = self.build_internal();
subst
}
}
impl TyBuilder<TraitId> {
pub(crate) fn trait_ref(db: &dyn HirDatabase, def: TraitId) -> TyBuilder<TraitId> {
TyBuilder::subst_for_def(db, def, None).with_data(def)
}
pub(crate) fn build(self) -> TraitRef {
let (trait_id, substitution) = self.build_internal();
TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution }
}
}
impl<'db, T: rustc_type_ir::TypeFoldable<DbInterner<'db>>> TyBuilder<EarlyBinder<'db, T>> {
pub(crate) fn build(self, interner: DbInterner<'db>) -> T {
let (b, subst) = self.build_internal();
let args: crate::next_solver::GenericArgs<'db> = subst.to_nextsolver(interner);
b.instantiate(interner, args)
}
}
impl<'db> TyBuilder<EarlyBinder<'db, crate::next_solver::Ty<'db>>> {
pub(crate) fn impl_self_ty(
db: &'db dyn HirDatabase,
def: hir_def::ImplId,
) -> TyBuilder<EarlyBinder<'db, crate::next_solver::Ty<'db>>> {
TyBuilder::subst_for_def(db, def, None).with_data(db.impl_self_ty(def))
}
}