Skip to content

Commit a6b3b01

Browse files
authoredOct 26, 2016
Auto merge of #37270 - Mark-Simulacrum:smallvec-optimized-arenas, r=eddyb
Add ArrayVec and AccumulateVec to reduce heap allocations during interning of slices Updates `mk_tup`, `mk_type_list`, and `mk_substs` to allow interning directly from iterators. The previous PR, #37220, changed some of the calls to pass a borrowed slice from `Vec` instead of directly passing the iterator, and these changes further optimize that to avoid the allocation entirely. This change yields 50% less malloc calls in [some cases](https://pastebin.mozilla.org/8921686). It also yields decent, though not amazing, performance improvements: ``` futures-rs-test 4.091s vs 4.021s --> 1.017x faster (variance: 1.004x, 1.004x) helloworld 0.219s vs 0.220s --> 0.993x faster (variance: 1.010x, 1.018x) html5ever-2016- 3.805s vs 3.736s --> 1.018x faster (variance: 1.003x, 1.009x) hyper.0.5.0 4.609s vs 4.571s --> 1.008x faster (variance: 1.015x, 1.017x) inflate-0.1.0 3.864s vs 3.883s --> 0.995x faster (variance: 1.232x, 1.005x) issue-32062-equ 0.309s vs 0.299s --> 1.033x faster (variance: 1.014x, 1.003x) issue-32278-big 1.614s vs 1.594s --> 1.013x faster (variance: 1.007x, 1.004x) jld-day15-parse 1.390s vs 1.326s --> 1.049x faster (variance: 1.006x, 1.009x) piston-image-0. 10.930s vs 10.675s --> 1.024x faster (variance: 1.006x, 1.010x) reddit-stress 2.302s vs 2.261s --> 1.019x faster (variance: 1.010x, 1.026x) regex.0.1.30 2.250s vs 2.240s --> 1.005x faster (variance: 1.087x, 1.011x) rust-encoding-0 1.895s vs 1.887s --> 1.005x faster (variance: 1.005x, 1.018x) syntex-0.42.2 29.045s vs 28.663s --> 1.013x faster (variance: 1.004x, 1.006x) syntex-0.42.2-i 13.925s vs 13.868s --> 1.004x faster (variance: 1.022x, 1.007x) ``` We implement a small-size optimized vector, intended to be used primarily for collection of presumed to be short iterators. This vector cannot be "upsized/reallocated" into a heap-allocated vector, since that would require (slow) branching logic, but during the initial collection from an iterator heap-allocation is possible. We make the new `AccumulateVec` and `ArrayVec` generic over implementors of the `Array` trait, of which there is currently one, `[T; 8]`. In the future, this is likely to expand to other values of N. Huge thanks to @nnethercote for collecting the performance and other statistics mentioned above.
2 parents 586a988 + 989eba7 commit a6b3b01

File tree

39 files changed

+361
-144
lines changed

39 files changed

+361
-144
lines changed
 

‎src/libarena/lib.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -157,13 +157,15 @@ impl<T> TypedArena<T> {
157157

158158
/// Allocates a slice of objects that are copy into the `TypedArena`, returning a mutable
159159
/// reference to it. Will panic if passed a zero-sized types.
160+
///
161+
/// Panics:
162+
/// - Zero-sized types
163+
/// - Zero-length slices
160164
#[inline]
161165
pub fn alloc_slice(&self, slice: &[T]) -> &mut [T]
162166
where T: Copy {
163167
assert!(mem::size_of::<T>() != 0);
164-
if slice.len() == 0 {
165-
return unsafe { slice::from_raw_parts_mut(heap::EMPTY as *mut T, 0) };
166-
}
168+
assert!(slice.len() != 0);
167169

168170
let available_capacity_bytes = self.end.get() as usize - self.ptr.get() as usize;
169171
let at_least_bytes = slice.len() * mem::size_of::<T>();

‎src/libcore/result.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,11 @@ impl<A, E, V: FromIterator<A>> FromIterator<Result<A, E>> for Result<V, E> {
10081008
None => None,
10091009
}
10101010
}
1011+
1012+
fn size_hint(&self) -> (usize, Option<usize>) {
1013+
let (_min, max) = self.iter.size_hint();
1014+
(0, max)
1015+
}
10111016
}
10121017

10131018
let mut adapter = Adapter { iter: iter.into_iter(), err: None };

‎src/librustc/mir/tcx.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ impl<'tcx> Rvalue<'tcx> {
163163
let lhs_ty = lhs.ty(mir, tcx);
164164
let rhs_ty = rhs.ty(mir, tcx);
165165
let ty = op.ty(tcx, lhs_ty, rhs_ty);
166-
let ty = tcx.mk_tup(&[ty, tcx.types.bool]);
166+
let ty = tcx.intern_tup(&[ty, tcx.types.bool]);
167167
Some(ty)
168168
}
169169
&Rvalue::UnaryOp(_, ref operand) => {
@@ -184,7 +184,7 @@ impl<'tcx> Rvalue<'tcx> {
184184
}
185185
AggregateKind::Tuple => {
186186
Some(tcx.mk_tup(
187-
&ops.iter().map(|op| op.ty(mir, tcx)).collect::<Vec<_>>()
187+
ops.iter().map(|op| op.ty(mir, tcx))
188188
))
189189
}
190190
AggregateKind::Adt(def, _, substs, _) => {

‎src/librustc/traits/fulfill.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use dep_graph::DepGraph;
1212
use infer::{InferCtxt, InferOk};
1313
use ty::{self, Ty, TypeFoldable, ToPolyTraitRef, TyCtxt, ToPredicate};
14-
use ty::subst::{Substs, Subst};
14+
use ty::subst::Subst;
1515
use rustc_data_structures::obligation_forest::{ObligationForest, Error};
1616
use rustc_data_structures::obligation_forest::{ForestObligation, ObligationProcessor};
1717
use std::marker::PhantomData;
@@ -159,7 +159,7 @@ impl<'a, 'gcx, 'tcx> DeferredObligation<'tcx> {
159159
let concrete_ty = ty_scheme.ty.subst(tcx, substs);
160160
let predicate = ty::TraitRef {
161161
def_id: self.predicate.def_id(),
162-
substs: Substs::new_trait(tcx, concrete_ty, &[])
162+
substs: tcx.mk_substs_trait(concrete_ty, &[])
163163
}.to_predicate();
164164

165165
let original_obligation = Obligation::new(self.cause.clone(),

‎src/librustc/traits/select.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2596,7 +2596,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
25962596
k
25972597
}
25982598
});
2599-
let substs = Substs::new(tcx, params);
2599+
let substs = tcx.mk_substs(params);
26002600
for &ty in fields.split_last().unwrap().1 {
26012601
if ty.subst(tcx, substs).references_error() {
26022602
return Err(Unimplemented);
@@ -2616,7 +2616,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
26162616
k
26172617
}
26182618
});
2619-
let new_struct = tcx.mk_adt(def, Substs::new(tcx, params));
2619+
let new_struct = tcx.mk_adt(def, tcx.mk_substs(params));
26202620
let origin = TypeOrigin::Misc(obligation.cause.span);
26212621
let InferOk { obligations, .. } =
26222622
self.infcx.sub_types(false, origin, new_struct, target)

‎src/librustc/traits/util.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
380380
Ok(def_id) => {
381381
Ok(ty::TraitRef {
382382
def_id: def_id,
383-
substs: Substs::new_trait(self, param_ty, &[])
383+
substs: self.mk_substs_trait(param_ty, &[])
384384
})
385385
}
386386
Err(e) => {
@@ -400,7 +400,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
400400
{
401401
let trait_ref = ty::TraitRef {
402402
def_id: trait_def_id,
403-
substs: Substs::new_trait(self, param_ty, ty_params)
403+
substs: self.mk_substs_trait(param_ty, ty_params)
404404
};
405405
predicate_for_trait_ref(cause, trait_ref, recursion_depth)
406406
}
@@ -486,11 +486,11 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
486486
{
487487
let arguments_tuple = match tuple_arguments {
488488
TupleArgumentsFlag::No => sig.0.inputs[0],
489-
TupleArgumentsFlag::Yes => self.mk_tup(&sig.0.inputs),
489+
TupleArgumentsFlag::Yes => self.intern_tup(&sig.0.inputs[..]),
490490
};
491491
let trait_ref = ty::TraitRef {
492492
def_id: fn_trait_def_id,
493-
substs: Substs::new_trait(self, self_ty, &[arguments_tuple]),
493+
substs: self.mk_substs_trait(self_ty, &[arguments_tuple]),
494494
};
495495
ty::Binder((trait_ref, sig.0.output))
496496
}

‎src/librustc/ty/context.rs

+88-6
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use ty::maps;
3636
use util::common::MemoizationMap;
3737
use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet};
3838
use util::nodemap::{FnvHashMap, FnvHashSet};
39+
use rustc_data_structures::accumulate_vec::AccumulateVec;
3940

4041
use arena::TypedArena;
4142
use std::borrow::Borrow;
@@ -44,6 +45,7 @@ use std::hash::{Hash, Hasher};
4445
use std::mem;
4546
use std::ops::Deref;
4647
use std::rc::Rc;
48+
use std::iter;
4749
use syntax::ast::{self, Name, NodeId};
4850
use syntax::attr;
4951
use syntax::parse::token::{self, keywords};
@@ -824,6 +826,9 @@ impl<'a, 'tcx> Lift<'tcx> for Ty<'a> {
824826
impl<'a, 'tcx> Lift<'tcx> for &'a Substs<'a> {
825827
type Lifted = &'tcx Substs<'tcx>;
826828
fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<&'tcx Substs<'tcx>> {
829+
if self.len() == 0 {
830+
return Some(Slice::empty());
831+
}
827832
if let Some(&Interned(substs)) = tcx.interners.substs.borrow().get(&self[..]) {
828833
if *self as *const _ == substs as *const _ {
829834
return Some(substs);
@@ -859,6 +864,9 @@ impl<'a, 'tcx> Lift<'tcx> for &'a Slice<Ty<'a>> {
859864
type Lifted = &'tcx Slice<Ty<'tcx>>;
860865
fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>)
861866
-> Option<&'tcx Slice<Ty<'tcx>>> {
867+
if self.len() == 0 {
868+
return Some(Slice::empty());
869+
}
862870
if let Some(&Interned(list)) = tcx.interners.type_list.borrow().get(&self[..]) {
863871
if *self as *const _ == list as *const _ {
864872
return Some(list);
@@ -1212,8 +1220,8 @@ macro_rules! slice_interners {
12121220
}
12131221

12141222
slice_interners!(
1215-
type_list: mk_type_list(Ty),
1216-
substs: mk_substs(Kind)
1223+
type_list: _intern_type_list(Ty),
1224+
substs: _intern_substs(Kind)
12171225
);
12181226

12191227
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
@@ -1318,12 +1326,16 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
13181326
self.mk_ty(TySlice(ty))
13191327
}
13201328

1321-
pub fn mk_tup(self, ts: &[Ty<'tcx>]) -> Ty<'tcx> {
1322-
self.mk_ty(TyTuple(self.mk_type_list(ts)))
1329+
pub fn intern_tup(self, ts: &[Ty<'tcx>]) -> Ty<'tcx> {
1330+
self.mk_ty(TyTuple(self.intern_type_list(ts)))
1331+
}
1332+
1333+
pub fn mk_tup<I: InternAs<[Ty<'tcx>], Ty<'tcx>>>(self, iter: I) -> I::Output {
1334+
iter.intern_with(|ts| self.mk_ty(TyTuple(self.intern_type_list(ts))))
13231335
}
13241336

13251337
pub fn mk_nil(self) -> Ty<'tcx> {
1326-
self.mk_tup(&[])
1338+
self.intern_tup(&[])
13271339
}
13281340

13291341
pub fn mk_diverging_default(self) -> Ty<'tcx> {
@@ -1369,7 +1381,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
13691381
-> Ty<'tcx> {
13701382
self.mk_closure_from_closure_substs(closure_id, ClosureSubsts {
13711383
func_substs: substs,
1372-
upvar_tys: self.mk_type_list(tys)
1384+
upvar_tys: self.intern_type_list(tys)
13731385
})
13741386
}
13751387

@@ -1414,6 +1426,40 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
14141426
self.mk_ty(TyAnon(def_id, substs))
14151427
}
14161428

1429+
pub fn intern_type_list(self, ts: &[Ty<'tcx>]) -> &'tcx Slice<Ty<'tcx>> {
1430+
if ts.len() == 0 {
1431+
Slice::empty()
1432+
} else {
1433+
self._intern_type_list(ts)
1434+
}
1435+
}
1436+
1437+
pub fn intern_substs(self, ts: &[Kind<'tcx>]) -> &'tcx Slice<Kind<'tcx>> {
1438+
if ts.len() == 0 {
1439+
Slice::empty()
1440+
} else {
1441+
self._intern_substs(ts)
1442+
}
1443+
}
1444+
1445+
pub fn mk_type_list<I: InternAs<[Ty<'tcx>],
1446+
&'tcx Slice<Ty<'tcx>>>>(self, iter: I) -> I::Output {
1447+
iter.intern_with(|xs| self.intern_type_list(xs))
1448+
}
1449+
1450+
pub fn mk_substs<I: InternAs<[Kind<'tcx>],
1451+
&'tcx Slice<Kind<'tcx>>>>(self, iter: I) -> I::Output {
1452+
iter.intern_with(|xs| self.intern_substs(xs))
1453+
}
1454+
1455+
pub fn mk_substs_trait(self,
1456+
s: Ty<'tcx>,
1457+
t: &[Ty<'tcx>])
1458+
-> &'tcx Substs<'tcx>
1459+
{
1460+
self.mk_substs(iter::once(s).chain(t.into_iter().cloned()).map(Kind::from))
1461+
}
1462+
14171463
pub fn trait_items(self, trait_did: DefId) -> Rc<Vec<ty::ImplOrTraitItem<'gcx>>> {
14181464
self.trait_items_cache.memoize(trait_did, || {
14191465
let def_ids = self.impl_or_trait_items(trait_did);
@@ -1432,3 +1478,39 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
14321478
})
14331479
}
14341480
}
1481+
1482+
pub trait InternAs<T: ?Sized, R> {
1483+
type Output;
1484+
fn intern_with<F>(self, F) -> Self::Output
1485+
where F: FnOnce(&T) -> R;
1486+
}
1487+
1488+
impl<I, T, R, E> InternAs<[T], R> for I
1489+
where E: InternIteratorElement<T, R>,
1490+
I: Iterator<Item=E> {
1491+
type Output = E::Output;
1492+
fn intern_with<F>(self, f: F) -> Self::Output
1493+
where F: FnOnce(&[T]) -> R {
1494+
E::intern_with(self, f)
1495+
}
1496+
}
1497+
1498+
pub trait InternIteratorElement<T, R>: Sized {
1499+
type Output;
1500+
fn intern_with<I: Iterator<Item=Self>, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output;
1501+
}
1502+
1503+
impl<T, R> InternIteratorElement<T, R> for T {
1504+
type Output = R;
1505+
fn intern_with<I: Iterator<Item=Self>, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output {
1506+
f(&iter.collect::<AccumulateVec<[_; 8]>>())
1507+
}
1508+
}
1509+
1510+
impl<T, R, E> InternIteratorElement<T, R> for Result<T, E> {
1511+
type Output = Result<R, E>;
1512+
fn intern_with<I: Iterator<Item=Self>, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output {
1513+
Ok(f(&iter.collect::<Result<AccumulateVec<[_; 8]>, _>>()?))
1514+
}
1515+
}
1516+

‎src/librustc/ty/mod.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ use std::ops::Deref;
4141
use std::rc::Rc;
4242
use std::slice;
4343
use std::vec::IntoIter;
44+
use std::mem;
4445
use syntax::ast::{self, Name, NodeId};
4546
use syntax::attr;
4647
use syntax::parse::token::{self, InternedString};
@@ -561,6 +562,14 @@ impl<'a, T> IntoIterator for &'a Slice<T> {
561562

562563
impl<'tcx> serialize::UseSpecializedDecodable for &'tcx Slice<Ty<'tcx>> {}
563564

565+
impl<T> Slice<T> {
566+
pub fn empty<'a>() -> &'a Slice<T> {
567+
unsafe {
568+
mem::transmute(slice::from_raw_parts(0x1 as *const T, 0))
569+
}
570+
}
571+
}
572+
564573
/// Upvars do not get their own node-id. Instead, we use the pair of
565574
/// the original var id (that is, the root variable that is referenced
566575
/// by the upvar) and the id of the closure expression.
@@ -1798,7 +1807,7 @@ impl<'a, 'tcx> AdtDefData<'tcx, 'tcx> {
17981807
_ if tys.references_error() => tcx.types.err,
17991808
0 => tcx.types.bool,
18001809
1 => tys[0],
1801-
_ => tcx.mk_tup(&tys)
1810+
_ => tcx.intern_tup(&tys[..])
18021811
};
18031812

18041813
match self.sized_constraint.get(dep_node()) {
@@ -1874,7 +1883,7 @@ impl<'a, 'tcx> AdtDefData<'tcx, 'tcx> {
18741883
};
18751884
let sized_predicate = Binder(TraitRef {
18761885
def_id: sized_trait,
1877-
substs: Substs::new_trait(tcx, ty, &[])
1886+
substs: tcx.mk_substs_trait(ty, &[])
18781887
}).to_predicate();
18791888
let predicates = tcx.lookup_predicates(self.did).predicates;
18801889
if predicates.into_iter().any(|p| p == sized_predicate) {
@@ -2125,7 +2134,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
21252134
pub fn node_id_item_substs(self, id: NodeId) -> ItemSubsts<'gcx> {
21262135
match self.tables.borrow().item_substs.get(&id) {
21272136
None => ItemSubsts {
2128-
substs: Substs::empty(self.global_tcx())
2137+
substs: self.global_tcx().intern_substs(&[])
21292138
},
21302139
Some(ts) => ts.clone(),
21312140
}
@@ -2797,7 +2806,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
27972806
// regions, so it shouldn't matter what we use for the free id
27982807
let free_id_outlive = self.region_maps.node_extent(ast::DUMMY_NODE_ID);
27992808
ty::ParameterEnvironment {
2800-
free_substs: Substs::empty(self),
2809+
free_substs: self.intern_substs(&[]),
28012810
caller_bounds: Vec::new(),
28022811
implicit_region_bound: self.mk_region(ty::ReEmpty),
28032812
free_id_outlive: free_id_outlive,

‎src/librustc/ty/relate.rs

+5-13
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,6 @@ pub trait TypeRelation<'a, 'gcx: 'a+'tcx, 'tcx: 'a> : Sized {
4949
Relate::relate(self, a, b)
5050
}
5151

52-
/// Relete elements of two slices pairwise.
53-
fn relate_zip<T: Relate<'tcx>>(&mut self, a: &[T], b: &[T]) -> RelateResult<'tcx, Vec<T>> {
54-
assert_eq!(a.len(), b.len());
55-
a.iter().zip(b).map(|(a, b)| self.relate(a, b)).collect()
56-
}
57-
5852
/// Switch variance for the purpose of relating `a` and `b`.
5953
fn relate_with_variance<T: Relate<'tcx>>(&mut self,
6054
variance: ty::Variance,
@@ -158,7 +152,7 @@ pub fn relate_substs<'a, 'gcx, 'tcx, R>(relation: &mut R,
158152
}
159153
});
160154

161-
Substs::maybe_new(tcx, params)
155+
Ok(tcx.mk_substs(params)?)
162156
}
163157

164158
impl<'tcx> Relate<'tcx> for &'tcx ty::BareFnTy<'tcx> {
@@ -489,10 +483,7 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R,
489483
(&ty::TyTuple(as_), &ty::TyTuple(bs)) =>
490484
{
491485
if as_.len() == bs.len() {
492-
let ts = as_.iter().zip(bs)
493-
.map(|(a, b)| relation.relate(a, b))
494-
.collect::<Result<Vec<_>, _>>()?;
495-
Ok(tcx.mk_tup(&ts))
486+
Ok(tcx.mk_tup(as_.iter().zip(bs).map(|(a, b)| relation.relate(a, b)))?)
496487
} else if !(as_.is_empty() || bs.is_empty()) {
497488
Err(TypeError::TupleSize(
498489
expected_found(relation, &as_.len(), &bs.len())))
@@ -544,10 +535,11 @@ impl<'tcx> Relate<'tcx> for ty::ClosureSubsts<'tcx> {
544535
where R: TypeRelation<'a, 'gcx, 'tcx>, 'gcx: 'a+'tcx, 'tcx: 'a
545536
{
546537
let substs = relate_substs(relation, None, a.func_substs, b.func_substs)?;
547-
let upvar_tys = relation.relate_zip(&a.upvar_tys, &b.upvar_tys)?;
538+
assert_eq!(a.upvar_tys.len(), b.upvar_tys.len());
548539
Ok(ty::ClosureSubsts {
549540
func_substs: substs,
550-
upvar_tys: relation.tcx().mk_type_list(&upvar_tys)
541+
upvar_tys: relation.tcx().mk_type_list(
542+
a.upvar_tys.iter().zip(b.upvar_tys).map(|(a, b)| relation.relate(a, b)))?
551543
})
552544
}
553545
}

‎src/librustc/ty/structural_impls.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use infer::type_variable;
1212
use ty::{self, Lift, Ty, TyCtxt};
1313
use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
14+
use rustc_data_structures::accumulate_vec::AccumulateVec;
1415

1516
use std::rc::Rc;
1617
use syntax::abi;
@@ -448,8 +449,8 @@ impl<'tcx> TypeFoldable<'tcx> for ty::TraitObject<'tcx> {
448449

449450
impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Slice<Ty<'tcx>> {
450451
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
451-
let tys = self.iter().map(|t| t.fold_with(folder)).collect::<Vec<_>>();
452-
folder.tcx().mk_type_list(&tys)
452+
let v = self.iter().map(|t| t.fold_with(folder)).collect::<AccumulateVec<[_; 8]>>();
453+
folder.tcx().intern_type_list(&v)
453454
}
454455

455456
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {

0 commit comments

Comments
 (0)
Please sign in to comment.