Skip to content

internal: change generic parameter order #13335

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions crates/hir-ty/src/autoderef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,14 @@ fn deref_by_trait(table: &mut InferenceTable<'_>, ty: Ty) -> Option<Ty> {
let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?;

let projection = {
let b = TyBuilder::assoc_type_projection(db, target);
let b = TyBuilder::subst_for_def(db, deref_trait, None);
if b.remaining() != 1 {
// the Target type + Deref trait should only have one generic parameter,
// namely Deref's Self type
return None;
}
b.push(ty).build()
let deref_subst = b.push(ty).build();
TyBuilder::assoc_type_projection(db, target, Some(deref_subst)).build()
};

// Check that the type implements Deref at all
Expand Down
221 changes: 119 additions & 102 deletions crates/hir-ty/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use chalk_ir::{
cast::{Cast, CastTo, Caster},
fold::TypeFoldable,
interner::HasInterner,
AdtId, BoundVar, DebruijnIndex, Scalar,
AdtId, DebruijnIndex, Scalar,
};
use hir_def::{
builtin_type::BuiltinType, generics::TypeOrConstParamData, ConstParamId, DefWithBodyId,
Expand All @@ -16,9 +16,9 @@ use smallvec::SmallVec;

use crate::{
consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, 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,
to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig,
GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind,
ValueTyDefId,
};

#[derive(Debug, Clone, PartialEq, Eq)]
Expand All @@ -34,31 +34,51 @@ pub struct TyBuilder<D> {
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, param_kinds: self.param_kinds, vec: self.vec }
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]>) -> TyBuilder<D> {
TyBuilder { data, vec: SmallVec::with_capacity(param_kinds.len()), param_kinds }
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());
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);
let subst = Substitution::from_iter(
Interner,
self.vec.into_iter().chain(self.parent_subst.iter(Interner).cloned()),
);
(self.data, subst)
}

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) {
chalk_ir::GenericArgData::Ty(_) => ParamKind::Type,
chalk_ir::GenericArgData::Lifetime(_) => panic!("Got lifetime in TyBuilder::push"),
Expand All @@ -68,7 +88,9 @@ impl<D> TyBuilder<D> {
}
};
assert_eq!(*expected_kind, arg_kind);

self.vec.push(arg);

self
}

Expand All @@ -79,20 +101,12 @@ impl<D> TyBuilder<D> {
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.iter().skip(this.vec.len());
let other = &this.param_kinds[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::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::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);
Expand All @@ -102,8 +116,8 @@ impl<D> TyBuilder<D> {
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.iter().skip(this.vec.len()).map(|x| match x {
ParamKind::Type => GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner),
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()),
});
this.vec.extend(filler.casted(Interner));
Expand All @@ -113,33 +127,17 @@ impl<D> TyBuilder<D> {

pub(crate) fn fill_with_inference_vars(self, table: &mut InferenceTable<'_>) -> Self {
self.fill(|x| match x {
ParamKind::Type => GenericArgData::Ty(table.new_type_var()).intern(Interner),
ParamKind::Const(ty) => {
GenericArgData::Const(table.new_const_var(ty.clone())).intern(Interner)
}
ParamKind::Type => table.new_type_var().cast(Interner),
ParamKind::Const(ty) => table.new_const_var(ty.clone()).cast(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));
self.vec.extend(self.param_kinds[self.vec.len()..].iter().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_kinds.len());
self.extend(parent_substs.iter(Interner).cloned());
self
}

fn extend(&mut self, it: impl Iterator<Item = GenericArg> + Clone) {
for x in it.clone().zip(self.param_kinds.iter().skip(self.vec.len())) {
self.assert_match_kind(&x.0, &x.1);
}
self.vec.extend(it);
}

fn assert_match_kind(&self, a: &chalk_ir::GenericArg<Interner>, e: &ParamKind) {
match (a.data(Interner), e) {
(chalk_ir::GenericArgData::Ty(_), ParamKind::Type)
Expand Down Expand Up @@ -188,53 +186,44 @@ impl TyBuilder<()> {
params.placeholder_subst(db)
}

pub fn subst_for_def(db: &dyn HirDatabase, def: impl Into<GenericDefId>) -> TyBuilder<()> {
let def = def.into();
let params = generics(db.upcast(), def);
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 subst_for_def(
db: &dyn HirDatabase,
def: impl Into<GenericDefId>,
parent_subst: Option<Substitution>,
) -> TyBuilder<()> {
let generics = generics(db.upcast(), def.into());
// FIXME: this assertion should hold but some adjustment around
// `ValueTyDefId::EnumVariantId` is needed.
// assert!(generics.parent_generics().is_some() == parent_subst.is_some());
let params = generics
.iter_self()
.map(|(id, data)| match data {
TypeOrConstParamData::TypeParamData(_) => ParamKind::Type,
TypeOrConstParamData::ConstParamData(_) => {
ParamKind::Const(db.const_param_ty(ConstParamId::from_unchecked(id)))
}
})
.collect();
TyBuilder::new((), params, parent_subst)
}

/// Creates a `TyBuilder` to build `Substitution` for a generator defined in `parent`.
///
/// A generator's substitution consists of:
/// - generic parameters in scope on `parent`
/// - resume type of generator
/// - yield type of generator ([`Generator::Yield`](std::ops::Generator::Yield))
/// - return type of generator ([`Generator::Return`](std::ops::Generator::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_generator(db: &dyn HirDatabase, parent: DefWithBodyId) -> TyBuilder<()> {
let parent_subst = match parent.as_generic_def_id() {
Some(parent) => generics(db.upcast(), parent).placeholder_subst(db),
// Static initializers *may* contain generators.
None => Substitution::empty(Interner),
};
let builder = TyBuilder::new(
(),
parent_subst
.iter(Interner)
.map(|arg| match arg.constant(Interner) {
Some(c) => ParamKind::Const(c.data(Interner).ty.clone()),
None => ParamKind::Type,
})
// These represent resume type, yield type, and return type of generator.
.chain(std::iter::repeat(ParamKind::Type).take(3))
.collect(),
);
builder.use_parent_substs(&parent_subst)
let parent_subst =
parent.as_generic_def_id().map(|p| generics(db.upcast(), p).placeholder_subst(db));
// These represent resume type, yield type, and return type of generator.
let params = std::iter::repeat(ParamKind::Type).take(3).collect();
TyBuilder::new((), params, parent_subst)
}

pub fn build(self) -> Substitution {
Expand All @@ -245,24 +234,35 @@ impl TyBuilder<()> {

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).with_data(def)
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());
let dummy_ty = TyKind::Error.intern(Interner).cast(Interner);
for default_ty in defaults.iter().skip(self.vec.len()) {
if let GenericArgData::Ty(x) = default_ty.skip_binders().data(Interner) {
// 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 depend on the previous parameters
let subst_so_far = Substitution::from_iter(Interner, self.vec.clone());
}
// Each default can only depend on the previous parameters.
// FIXME: we don't handle const generics here.
let subst_so_far = Substitution::from_iter(
Interner,
self.vec
.iter()
.cloned()
.chain(iter::repeat(dummy_ty.clone()))
.take(self.param_kinds.len()),
);
self.vec.push(default_ty.clone().substitute(Interner, &subst_so_far).cast(Interner));
}
self
Expand All @@ -277,7 +277,7 @@ impl TyBuilder<hir_def::AdtId> {
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())
TyBuilder::new(Tuple(size), iter::repeat(ParamKind::Type).take(size).collect(), None)
}

pub fn build(self) -> Ty {
Expand All @@ -288,7 +288,7 @@ impl TyBuilder<Tuple> {

impl TyBuilder<TraitId> {
pub fn trait_ref(db: &dyn HirDatabase, def: TraitId) -> TyBuilder<TraitId> {
TyBuilder::subst_for_def(db, def).with_data(def)
TyBuilder::subst_for_def(db, def, None).with_data(def)
}

pub fn build(self) -> TraitRef {
Expand All @@ -298,8 +298,12 @@ impl TyBuilder<TraitId> {
}

impl TyBuilder<TypeAliasId> {
pub fn assoc_type_projection(db: &dyn HirDatabase, def: TypeAliasId) -> TyBuilder<TypeAliasId> {
TyBuilder::subst_for_def(db, def).with_data(def)
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 {
Expand All @@ -309,35 +313,48 @@ impl TyBuilder<TypeAliasId> {
}

impl<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>> TyBuilder<Binders<T>> {
fn subst_binders(b: Binders<T>) -> Self {
let param_kinds = b
.binders
.iter(Interner)
.map(|x| match x {
chalk_ir::VariableKind::Ty(_) => ParamKind::Type,
chalk_ir::VariableKind::Lifetime => panic!("Got lifetime parameter"),
chalk_ir::VariableKind::Const(ty) => ParamKind::Const(ty.clone()),
})
.collect();
TyBuilder::new(b, param_kinds)
}

pub fn build(self) -> T {
let (b, subst) = self.build_internal();
b.substitute(Interner, &subst)
}
}

impl TyBuilder<Binders<Ty>> {
pub fn def_ty(db: &dyn HirDatabase, def: TyDefId) -> TyBuilder<Binders<Ty>> {
TyBuilder::subst_binders(db.ty(def))
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_binders(db.impl_self_ty(def))
TyBuilder::subst_for_def(db, def, None).with_data(db.impl_self_ty(def))
}

pub fn value_ty(db: &dyn HirDatabase, def: ValueTyDefId) -> TyBuilder<Binders<Ty>> {
TyBuilder::subst_binders(db.value_ty(def))
pub fn value_ty(
db: &dyn HirDatabase,
def: ValueTyDefId,
parent_subst: Option<Substitution>,
) -> TyBuilder<Binders<Ty>> {
let poly_value_ty = db.value_ty(def);
let id = match def.to_generic_def_id() {
Some(id) => id,
None => {
// static items
assert!(parent_subst.is_none());
return TyBuilder::new_empty(poly_value_ty);
}
};
TyBuilder::subst_for_def(db, id, parent_subst).with_data(poly_value_ty)
}
}
Loading