Skip to content

Commit c8237db

Browse files
committed
Clarify iterator interners.
There are two traits, `InternAs` and `InternIteratorElement`. I found them confusing to use, particularly this: ``` pub fn mk_tup<I: InternAs<Ty<'tcx>, Ty<'tcx>>>(self, iter: I) -> I::Output { iter.intern_with(|ts| self.intern_tup(ts)) } ``` where I thought there might have been two levels of interning going on (there isn't) due to the `intern_with`/`InternAs` + `intern_tup` naming. And then I found the actual traits and impls themselves *very* confusing. - `InternAs` has a single impl, for iterators, with four type variables. - `InternAs` is only implemented for iterators because it wouldn't really make sense to implement for any other type. And you can't really understand the trait without seeing that single impl, which is suspicious. - `InternAs` is basically just a wrapper for `InternIteratorElement` which does all the actual work. - Neither trait actually does any interning. They just have `Intern` in their name because they are used *by* interning code. - There are no comments. So this commit improves things. - It removes `InternAs` completely. This makes the `mk_*` function signatures slightly more verbose -- two trait bounds instead of one -- but much easier to read, because you only need to understand one trait instead of two. - It renames `InternIteratorElement` as `CollectAndApply`. Likewise, it renames its method `intern_with` as `collect_and_apply`. These names describe better what's going on: we collect the iterator elements into a slice and then apply a function to the slice. - It adds comments, making clear that all this is all there just to provide an optimized version of `f(&iter.collect::<Vec<_>>())`. It took me a couple of attempts to come up with this commit. My initial attempt kept `InternAs` around, but renamed things and added comments, and I wasn't happy with it. I think this version is much better. The resulting code is shorter, despite the addition of the comments.
1 parent 9d5cf0f commit c8237db

File tree

2 files changed

+87
-70
lines changed

2 files changed

+87
-70
lines changed

compiler/rustc_middle/src/ty/context.rs

+54-41
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ use rustc_target::abi::{Layout, LayoutS, TargetDataLayout, VariantIdx};
6767
use rustc_target::spec::abi;
6868
use rustc_type_ir::sty::TyKind::*;
6969
use rustc_type_ir::WithCachedTypeInfo;
70-
use rustc_type_ir::{DynKind, InternAs, InternIteratorElement, Interner, TypeFlags};
70+
use rustc_type_ir::{CollectAndApply, DynKind, Interner, TypeFlags};
7171

7272
use std::any::Any;
7373
use std::borrow::Borrow;
@@ -1835,8 +1835,12 @@ impl<'tcx> TyCtxt<'tcx> {
18351835
if ts.is_empty() { self.types.unit } else { self.mk_ty(Tuple(self.intern_type_list(&ts))) }
18361836
}
18371837

1838-
pub fn mk_tup<I: InternAs<Ty<'tcx>, Ty<'tcx>>>(self, iter: I) -> I::Output {
1839-
iter.intern_with(|ts| self.intern_tup(ts))
1838+
pub fn mk_tup<I, T>(self, iter: I) -> T::Output
1839+
where
1840+
I: Iterator<Item = T>,
1841+
T: CollectAndApply<Ty<'tcx>, Ty<'tcx>>,
1842+
{
1843+
T::collect_and_apply(iter, |ts| self.intern_tup(ts))
18401844
}
18411845

18421846
#[inline]
@@ -2157,11 +2161,12 @@ impl<'tcx> TyCtxt<'tcx> {
21572161
}
21582162
}
21592163

2160-
pub fn mk_const_list<I: InternAs<ty::Const<'tcx>, &'tcx List<ty::Const<'tcx>>>>(
2161-
self,
2162-
iter: I,
2163-
) -> I::Output {
2164-
iter.intern_with(|xs| self.intern_const_list(xs))
2164+
pub fn mk_const_list<I, T>(self, iter: I) -> T::Output
2165+
where
2166+
I: Iterator<Item = T>,
2167+
T: CollectAndApply<ty::Const<'tcx>, &'tcx List<ty::Const<'tcx>>>,
2168+
{
2169+
T::collect_and_apply(iter, |xs| self.intern_const_list(xs))
21652170
}
21662171

21672172
pub fn intern_const_list(self, cs: &[ty::Const<'tcx>]) -> &'tcx List<ty::Const<'tcx>> {
@@ -2220,48 +2225,57 @@ impl<'tcx> TyCtxt<'tcx> {
22202225
) -> T::Output
22212226
where
22222227
I: Iterator<Item = T>,
2223-
T: InternIteratorElement<Ty<'tcx>, ty::FnSig<'tcx>>,
2228+
T: CollectAndApply<Ty<'tcx>, ty::FnSig<'tcx>>,
22242229
{
2225-
inputs.chain(iter::once(output)).intern_with(|xs| ty::FnSig {
2230+
T::collect_and_apply(inputs.chain(iter::once(output)), |xs| ty::FnSig {
22262231
inputs_and_output: self.intern_type_list(xs),
22272232
c_variadic,
22282233
unsafety,
22292234
abi,
22302235
})
22312236
}
22322237

2233-
pub fn mk_poly_existential_predicates<
2234-
I: InternAs<PolyExistentialPredicate<'tcx>, &'tcx List<PolyExistentialPredicate<'tcx>>>,
2235-
>(
2236-
self,
2237-
iter: I,
2238-
) -> I::Output {
2239-
iter.intern_with(|xs| self.intern_poly_existential_predicates(xs))
2238+
pub fn mk_poly_existential_predicates<I, T>(self, iter: I) -> T::Output
2239+
where
2240+
I: Iterator<Item = T>,
2241+
T: CollectAndApply<
2242+
PolyExistentialPredicate<'tcx>,
2243+
&'tcx List<PolyExistentialPredicate<'tcx>>,
2244+
>,
2245+
{
2246+
T::collect_and_apply(iter, |xs| self.intern_poly_existential_predicates(xs))
22402247
}
22412248

2242-
pub fn mk_predicates<I: InternAs<Predicate<'tcx>, &'tcx List<Predicate<'tcx>>>>(
2243-
self,
2244-
iter: I,
2245-
) -> I::Output {
2246-
iter.intern_with(|xs| self.intern_predicates(xs))
2249+
pub fn mk_predicates<I, T>(self, iter: I) -> T::Output
2250+
where
2251+
I: Iterator<Item = T>,
2252+
T: CollectAndApply<Predicate<'tcx>, &'tcx List<Predicate<'tcx>>>,
2253+
{
2254+
T::collect_and_apply(iter, |xs| self.intern_predicates(xs))
22472255
}
22482256

2249-
pub fn mk_type_list<I: InternAs<Ty<'tcx>, &'tcx List<Ty<'tcx>>>>(self, iter: I) -> I::Output {
2250-
iter.intern_with(|xs| self.intern_type_list(xs))
2257+
pub fn mk_type_list<I, T>(self, iter: I) -> T::Output
2258+
where
2259+
I: Iterator<Item = T>,
2260+
T: CollectAndApply<Ty<'tcx>, &'tcx List<Ty<'tcx>>>,
2261+
{
2262+
T::collect_and_apply(iter, |xs| self.intern_type_list(xs))
22512263
}
22522264

2253-
pub fn mk_substs<I: InternAs<GenericArg<'tcx>, &'tcx List<GenericArg<'tcx>>>>(
2254-
self,
2255-
iter: I,
2256-
) -> I::Output {
2257-
iter.intern_with(|xs| self.intern_substs(xs))
2265+
pub fn mk_substs<I, T>(self, iter: I) -> T::Output
2266+
where
2267+
I: Iterator<Item = T>,
2268+
T: CollectAndApply<GenericArg<'tcx>, &'tcx List<GenericArg<'tcx>>>,
2269+
{
2270+
T::collect_and_apply(iter, |xs| self.intern_substs(xs))
22582271
}
22592272

2260-
pub fn mk_place_elems<I: InternAs<PlaceElem<'tcx>, &'tcx List<PlaceElem<'tcx>>>>(
2261-
self,
2262-
iter: I,
2263-
) -> I::Output {
2264-
iter.intern_with(|xs| self.intern_place_elems(xs))
2273+
pub fn mk_place_elems<I, T>(self, iter: I) -> T::Output
2274+
where
2275+
I: Iterator<Item = T>,
2276+
T: CollectAndApply<PlaceElem<'tcx>, &'tcx List<PlaceElem<'tcx>>>,
2277+
{
2278+
T::collect_and_apply(iter, |xs| self.intern_place_elems(xs))
22652279
}
22662280

22672281
pub fn mk_substs_trait(
@@ -2290,13 +2304,12 @@ impl<'tcx> TyCtxt<'tcx> {
22902304
ty::AliasTy { def_id, substs, _use_mk_alias_ty_instead: () }
22912305
}
22922306

2293-
pub fn mk_bound_variable_kinds<
2294-
I: InternAs<ty::BoundVariableKind, &'tcx List<ty::BoundVariableKind>>,
2295-
>(
2296-
self,
2297-
iter: I,
2298-
) -> I::Output {
2299-
iter.intern_with(|xs| self.intern_bound_variable_kinds(xs))
2307+
pub fn mk_bound_variable_kinds<I, T>(self, iter: I) -> T::Output
2308+
where
2309+
I: Iterator<Item = T>,
2310+
T: CollectAndApply<ty::BoundVariableKind, &'tcx List<ty::BoundVariableKind>>,
2311+
{
2312+
T::collect_and_apply(iter, |xs| self.intern_bound_variable_kinds(xs))
23002313
}
23012314

23022315
/// Emit a lint at `span` from a lint struct (some type that implements `DecorateLint`,

compiler/rustc_type_ir/src/lib.rs

+33-29
Original file line numberDiff line numberDiff line change
@@ -69,38 +69,37 @@ pub trait Interner: Sized {
6969
type PlaceholderRegion: Clone + Debug + Hash + Ord;
7070
}
7171

72-
pub trait InternAs<T: ?Sized, R> {
72+
/// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter`
73+
/// that produces `T` items. You could combine them with
74+
/// `f(&iter.collect::<Vec<_>>())`, but this requires allocating memory for the
75+
/// `Vec`.
76+
///
77+
/// This trait allows for faster implementations, intended for cases where the
78+
/// number of items produced by the iterator is small. There is a blanket impl
79+
/// for `T` items, but there is also a fallible impl for `Result<T, E>` items.
80+
pub trait CollectAndApply<T, R>: Sized {
7381
type Output;
74-
fn intern_with<F>(self, f: F) -> Self::Output
82+
83+
/// Produce a result of type `Self::Output` from `iter`. The result will
84+
/// typically be produced by applying `f` on the elements produced by
85+
/// `iter`, though this may not happen in some impls, e.g. if an error
86+
/// occured during iteration.
87+
fn collect_and_apply<I, F>(iter: I, f: F) -> Self::Output
7588
where
89+
I: Iterator<Item = Self>,
7690
F: FnOnce(&[T]) -> R;
7791
}
7892

79-
impl<I, T, R, E> InternAs<T, R> for I
80-
where
81-
E: InternIteratorElement<T, R>,
82-
I: Iterator<Item = E>,
83-
{
84-
type Output = E::Output;
85-
fn intern_with<F>(self, f: F) -> Self::Output
93+
/// The blanket impl that always collects all elements and applies `f`.
94+
impl<T, R> CollectAndApply<T, R> for T {
95+
type Output = R;
96+
97+
/// Equivalent to `f(&iter.collect::<Vec<_>>())`.
98+
fn collect_and_apply<I, F>(mut iter: I, f: F) -> R
8699
where
100+
I: Iterator<Item = T>,
87101
F: FnOnce(&[T]) -> R,
88102
{
89-
E::intern_with(self, f)
90-
}
91-
}
92-
93-
pub trait InternIteratorElement<T, R>: Sized {
94-
type Output;
95-
fn intern_with<I: Iterator<Item = Self>, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output;
96-
}
97-
98-
impl<T, R> InternIteratorElement<T, R> for T {
99-
type Output = R;
100-
fn intern_with<I: Iterator<Item = Self>, F: FnOnce(&[T]) -> R>(
101-
mut iter: I,
102-
f: F,
103-
) -> Self::Output {
104103
// This code is hot enough that it's worth specializing for the most
105104
// common length lists, to avoid the overhead of `SmallVec` creation.
106105
// Lengths 0, 1, and 2 typically account for ~95% of cases. If
@@ -127,12 +126,17 @@ impl<T, R> InternIteratorElement<T, R> for T {
127126
}
128127
}
129128

130-
impl<T, R, E> InternIteratorElement<T, R> for Result<T, E> {
129+
/// A fallible impl that will fail, without calling `f`, if there are any
130+
/// errors during collection.
131+
impl<T, R, E> CollectAndApply<T, R> for Result<T, E> {
131132
type Output = Result<R, E>;
132-
fn intern_with<I: Iterator<Item = Self>, F: FnOnce(&[T]) -> R>(
133-
mut iter: I,
134-
f: F,
135-
) -> Self::Output {
133+
134+
/// Equivalent to `Ok(f(&iter.collect::<Result<Vec<_>>>()?))`.
135+
fn collect_and_apply<I, F>(mut iter: I, f: F) -> Result<R, E>
136+
where
137+
I: Iterator<Item = Result<T, E>>,
138+
F: FnOnce(&[T]) -> R,
139+
{
136140
// This code is hot enough that it's worth specializing for the most
137141
// common length lists, to avoid the overhead of `SmallVec` creation.
138142
// Lengths 0, 1, and 2 typically account for ~95% of cases. If

0 commit comments

Comments
 (0)