Change generic parameter/argument order

This commit "inverts" the order of generic parameters/arguments of an
item and its parent. This is to fulfill chalk's expectation on the
order of `Substitution` for generic associated types and it's one step
forward for their support (hopefully).

Although chalk doesn't put any constraint on the order of `Substitution`
for other items, it feels natural to get everything aligned rather than
special casing GATs.

One complication is that `TyBuilder` now demands its users to pass in
parent's `Substitution` upon construction unless it's obvious that the
the item has no parent (e.g. an ADT never has parent). All users
*should* already know the parent of the item in question, and without
this, it cannot be easily reasoned about whether we're pushing the
argument for the item or for its parent.

Quick comparison of how this commit changes `Substitution`:

```rust
trait Trait<TP, const CP: usize> {
  type Type<TC, const CC: usize> = ();
  fn f<TC, const CC: usize>() {}
}
```

- before this commit: `[Self, TP, CP, TC, CC]` for each trait item
- after this commit: `[TC, CC, Self, TP, CP]` for each trait item
This commit is contained in:
Ryo Yoshida 2022-10-02 21:13:21 +09:00
parent f8f5a5ea57
commit 4385d3dcd0
No known key found for this signature in database
GPG key ID: E25698A930586171
3 changed files with 141 additions and 87 deletions

View file

@ -34,17 +34,32 @@ pub struct TyBuilder<D> {
data: D, data: D,
vec: SmallVec<[GenericArg; 2]>, vec: SmallVec<[GenericArg; 2]>,
param_kinds: SmallVec<[ParamKind; 2]>, param_kinds: SmallVec<[ParamKind; 2]>,
parent_subst: Substitution,
} }
impl<A> TyBuilder<A> { impl<A> TyBuilder<A> {
fn with_data<B>(self, data: B) -> TyBuilder<B> { 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> { impl<D> TyBuilder<D> {
fn new(data: D, param_kinds: SmallVec<[ParamKind; 2]>) -> TyBuilder<D> { fn new(
TyBuilder { data, vec: SmallVec::with_capacity(param_kinds.len()), param_kinds } 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) { fn build_internal(self) -> (D, Substitution) {
@ -52,13 +67,18 @@ impl<D> TyBuilder<D> {
for (a, e) in self.vec.iter().zip(self.param_kinds.iter()) { for (a, e) in self.vec.iter().zip(self.param_kinds.iter()) {
self.assert_match_kind(a, e); 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) (self.data, subst)
} }
pub fn push(mut self, arg: impl CastTo<GenericArg>) -> Self { pub fn push(mut self, arg: impl CastTo<GenericArg>) -> Self {
assert!(self.remaining() > 0);
let arg = arg.cast(Interner); let arg = arg.cast(Interner);
let expected_kind = &self.param_kinds[self.vec.len()]; let expected_kind = &self.param_kinds[self.vec.len()];
let arg_kind = match arg.data(Interner) { let arg_kind = match arg.data(Interner) {
chalk_ir::GenericArgData::Ty(_) => ParamKind::Type, chalk_ir::GenericArgData::Ty(_) => ParamKind::Type,
chalk_ir::GenericArgData::Lifetime(_) => panic!("Got lifetime in TyBuilder::push"), chalk_ir::GenericArgData::Lifetime(_) => panic!("Got lifetime in TyBuilder::push"),
@ -68,7 +88,9 @@ impl<D> TyBuilder<D> {
} }
}; };
assert_eq!(*expected_kind, arg_kind); assert_eq!(*expected_kind, arg_kind);
self.vec.push(arg); self.vec.push(arg);
self self
} }
@ -116,20 +138,6 @@ impl<D> TyBuilder<D> {
self 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) { fn assert_match_kind(&self, a: &chalk_ir::GenericArg<Interner>, e: &ParamKind) {
match (a.data(Interner), e) { match (a.data(Interner), e) {
(chalk_ir::GenericArgData::Ty(_), ParamKind::Type) (chalk_ir::GenericArgData::Ty(_), ParamKind::Type)
@ -178,53 +186,44 @@ impl TyBuilder<()> {
params.placeholder_subst(db) params.placeholder_subst(db)
} }
pub fn subst_for_def(db: &dyn HirDatabase, def: impl Into<GenericDefId>) -> TyBuilder<()> { pub fn subst_for_def(
let def = def.into(); db: &dyn HirDatabase,
let params = generics(db.upcast(), def); def: impl Into<GenericDefId>,
TyBuilder::new( parent_subst: Option<Substitution>,
(), ) -> TyBuilder<()> {
params let generics = generics(db.upcast(), def.into());
.iter() // FIXME: this assertion should hold but some adjustment around
.map(|(id, data)| match data { // `ValueTyDefId::EnumVariantId` is needed.
TypeOrConstParamData::TypeParamData(_) => ParamKind::Type, // assert!(generics.parent_generics().is_some() == parent_subst.is_some());
TypeOrConstParamData::ConstParamData(_) => { let params = generics
ParamKind::Const(db.const_param_ty(ConstParamId::from_unchecked(id))) .iter_self()
} .map(|(id, data)| match data {
}) TypeOrConstParamData::TypeParamData(_) => ParamKind::Type,
.collect(), 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`. /// Creates a `TyBuilder` to build `Substitution` for a generator defined in `parent`.
/// ///
/// A generator's substitution consists of: /// A generator's substitution consists of:
/// - generic parameters in scope on `parent`
/// - resume type of generator /// - resume type of generator
/// - yield type of generator ([`Generator::Yield`](std::ops::Generator::Yield)) /// - yield type of generator ([`Generator::Yield`](std::ops::Generator::Yield))
/// - return type of generator ([`Generator::Return`](std::ops::Generator::Return)) /// - return type of generator ([`Generator::Return`](std::ops::Generator::Return))
/// - generic parameters in scope on `parent`
/// in this order. /// in this order.
/// ///
/// This method prepopulates the builder with placeholder substitution of `parent`, so you /// This method prepopulates the builder with placeholder substitution of `parent`, so you
/// should only push exactly 3 `GenericArg`s before building. /// should only push exactly 3 `GenericArg`s before building.
pub fn subst_for_generator(db: &dyn HirDatabase, parent: DefWithBodyId) -> TyBuilder<()> { pub fn subst_for_generator(db: &dyn HirDatabase, parent: DefWithBodyId) -> TyBuilder<()> {
let parent_subst = match parent.as_generic_def_id() { let parent_subst =
Some(parent) => generics(db.upcast(), parent).placeholder_subst(db), parent.as_generic_def_id().map(|p| generics(db.upcast(), p).placeholder_subst(db));
// Static initializers *may* contain generators. // These represent resume type, yield type, and return type of generator.
None => Substitution::empty(Interner), let params = std::iter::repeat(ParamKind::Type).take(3).collect();
}; TyBuilder::new((), params, parent_subst)
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)
} }
pub fn build(self) -> Substitution { pub fn build(self) -> Substitution {
@ -235,7 +234,7 @@ impl TyBuilder<()> {
impl TyBuilder<hir_def::AdtId> { impl TyBuilder<hir_def::AdtId> {
pub fn adt(db: &dyn HirDatabase, def: hir_def::AdtId) -> 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( pub fn fill_with_defaults(
@ -243,7 +242,9 @@ impl TyBuilder<hir_def::AdtId> {
db: &dyn HirDatabase, db: &dyn HirDatabase,
mut fallback: impl FnMut() -> Ty, mut fallback: impl FnMut() -> Ty,
) -> Self { ) -> Self {
// Note that we're building ADT, so we never have parent generic parameters.
let defaults = db.generic_defaults(self.data.into()); 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()) { for default_ty in defaults.iter().skip(self.vec.len()) {
// NOTE(skip_binders): we only check if the arg type is error type. // NOTE(skip_binders): we only check if the arg type is error type.
if let Some(x) = default_ty.skip_binders().ty(Interner) { if let Some(x) = default_ty.skip_binders().ty(Interner) {
@ -251,9 +252,17 @@ impl TyBuilder<hir_def::AdtId> {
self.vec.push(fallback().cast(Interner)); self.vec.push(fallback().cast(Interner));
continue; continue;
} }
}; }
// each default can depend on the previous parameters // Each default can only depend on the previous parameters.
let subst_so_far = Substitution::from_iter(Interner, self.vec.clone()); // 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.vec.push(default_ty.clone().substitute(Interner, &subst_so_far).cast(Interner));
} }
self self
@ -268,7 +277,7 @@ impl TyBuilder<hir_def::AdtId> {
pub struct Tuple(usize); pub struct Tuple(usize);
impl TyBuilder<Tuple> { impl TyBuilder<Tuple> {
pub fn tuple(size: usize) -> 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 { pub fn build(self) -> Ty {
@ -279,7 +288,7 @@ impl TyBuilder<Tuple> {
impl TyBuilder<TraitId> { impl TyBuilder<TraitId> {
pub fn trait_ref(db: &dyn HirDatabase, def: TraitId) -> 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 { pub fn build(self) -> TraitRef {
@ -289,8 +298,12 @@ impl TyBuilder<TraitId> {
} }
impl TyBuilder<TypeAliasId> { impl TyBuilder<TypeAliasId> {
pub fn assoc_type_projection(db: &dyn HirDatabase, def: TypeAliasId) -> TyBuilder<TypeAliasId> { pub fn assoc_type_projection(
TyBuilder::subst_for_def(db, def).with_data(def) 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 { pub fn build(self) -> ProjectionTy {
@ -300,19 +313,6 @@ impl TyBuilder<TypeAliasId> {
} }
impl<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>> TyBuilder<Binders<T>> { 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 { pub fn build(self) -> T {
let (b, subst) = self.build_internal(); let (b, subst) = self.build_internal();
b.substitute(Interner, &subst) b.substitute(Interner, &subst)
@ -320,15 +320,41 @@ impl<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>> TyBuilder<Bin
} }
impl TyBuilder<Binders<Ty>> { impl TyBuilder<Binders<Ty>> {
pub fn def_ty(db: &dyn HirDatabase, def: TyDefId) -> TyBuilder<Binders<Ty>> { pub fn def_ty(
TyBuilder::subst_binders(db.ty(def)) 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>> { 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>> { pub fn value_ty(
TyBuilder::subst_binders(db.value_ty(def)) 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)
} }
} }

View file

@ -1641,6 +1641,19 @@ pub enum ValueTyDefId {
} }
impl_from!(FunctionId, StructId, UnionId, EnumVariantId, ConstId, StaticId for ValueTyDefId); impl_from!(FunctionId, StructId, UnionId, EnumVariantId, ConstId, StaticId for ValueTyDefId);
impl ValueTyDefId {
pub(crate) fn to_generic_def_id(self) -> Option<GenericDefId> {
match self {
Self::FunctionId(id) => Some(id.into()),
Self::StructId(id) => Some(id.into()),
Self::UnionId(id) => Some(id.into()),
Self::EnumVariantId(var) => Some(var.parent.into()),
Self::ConstId(id) => Some(id.into()),
Self::StaticId(_) => None,
}
}
}
/// Build the declared type of an item. This depends on the namespace; e.g. for /// Build the declared type of an item. This depends on the namespace; e.g. for
/// `struct Foo(usize)`, we have two types: The type of the struct itself, and /// `struct Foo(usize)`, we have two types: The type of the struct itself, and
/// the constructor function `(usize) -> Foo` which lives in the values /// the constructor function `(usize) -> Foo` which lives in the values

View file

@ -220,23 +220,30 @@ impl Generics {
}) })
} }
/// Iterator over types and const params of parent, then self. /// Iterator over types and const params of self, then parent.
pub(crate) fn iter<'a>( pub(crate) fn iter<'a>(
&'a self, &'a self,
) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a { ) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a {
let to_toc_id = |it: &'a Generics| { let to_toc_id = |it: &'a Generics| {
move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p) move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p)
}; };
self.parent_generics() self.params.iter().map(to_toc_id(self)).chain(self.iter_parent())
.into_iter() }
.flat_map(move |it| it.params.iter().map(to_toc_id(it)))
.chain(self.params.iter().map(to_toc_id(self))) /// Iterate over types and const params without parent params.
pub(crate) fn iter_self<'a>(
&'a self,
) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a {
let to_toc_id = |it: &'a Generics| {
move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p)
};
self.params.iter().map(to_toc_id(self))
} }
/// Iterator over types and const params of parent. /// Iterator over types and const params of parent.
pub(crate) fn iter_parent<'a>( pub(crate) fn iter_parent<'a>(
&'a self, &'a self,
) -> impl Iterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a { ) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a {
self.parent_generics().into_iter().flat_map(|it| { self.parent_generics().into_iter().flat_map(|it| {
let to_toc_id = let to_toc_id =
move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p); move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p);
@ -244,12 +251,18 @@ impl Generics {
}) })
} }
/// Returns total number of generic parameters in scope, including those from parent.
pub(crate) fn len(&self) -> usize { pub(crate) fn len(&self) -> usize {
let parent = self.parent_generics().map_or(0, Generics::len); let parent = self.parent_generics().map_or(0, Generics::len);
let child = self.params.type_or_consts.len(); let child = self.params.type_or_consts.len();
parent + child parent + child
} }
/// Returns numbers of generic parameters excluding those from parent.
pub(crate) fn len_self(&self) -> usize {
self.params.type_or_consts.len()
}
/// (parent total, self param, type param list, const param list, impl trait) /// (parent total, self param, type param list, const param list, impl trait)
pub(crate) fn provenance_split(&self) -> (usize, usize, usize, usize, usize) { pub(crate) fn provenance_split(&self) -> (usize, usize, usize, usize, usize) {
let ty_iter = || self.params.iter().filter_map(|x| x.1.type_param()); let ty_iter = || self.params.iter().filter_map(|x| x.1.type_param());
@ -274,10 +287,12 @@ impl Generics {
if param.parent == self.def { if param.parent == self.def {
let (idx, (_local_id, data)) = let (idx, (_local_id, data)) =
self.params.iter().enumerate().find(|(_, (idx, _))| *idx == param.local_id)?; self.params.iter().enumerate().find(|(_, (idx, _))| *idx == param.local_id)?;
let parent_len = self.parent_generics().map_or(0, Generics::len); Some((idx, data))
Some((parent_len + idx, data))
} else { } else {
self.parent_generics().and_then(|g| g.find_param(param)) self.parent_generics()
.and_then(|g| g.find_param(param))
// Remember that parent parameters come after parameters for self.
.map(|(idx, data)| (self.len_self() + idx, data))
} }
} }