Skip to content

Commit 41eb046

Browse files
Auto merge of #142316 - compiler-errors:cache-param-env, r=<try>
[perf] Cache the canonical *instantiation* of param-envs r? lcnr
2 parents 8ce2287 + 10849bc commit 41eb046

File tree

2 files changed

+162
-20
lines changed

2 files changed

+162
-20
lines changed

compiler/rustc_infer/src/infer/canonical/instantiate.rs

Lines changed: 156 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77
//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
88
99
use rustc_macros::extension;
10-
use rustc_middle::bug;
11-
use rustc_middle::ty::{self, FnMutDelegate, GenericArgKind, TyCtxt, TypeFoldable};
10+
use rustc_middle::ty::{
11+
self, DelayedMap, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable,
12+
TypeVisitableExt, TypeVisitor,
13+
};
14+
use rustc_type_ir::TypeVisitable;
1215

1316
use crate::infer::canonical::{Canonical, CanonicalVarValues};
1417

@@ -58,23 +61,156 @@ where
5861
T: TypeFoldable<TyCtxt<'tcx>>,
5962
{
6063
if var_values.var_values.is_empty() {
61-
value
62-
} else {
63-
let delegate = FnMutDelegate {
64-
regions: &mut |br: ty::BoundRegion| match var_values[br.var].kind() {
65-
GenericArgKind::Lifetime(l) => l,
66-
r => bug!("{:?} is a region but value is {:?}", br, r),
67-
},
68-
types: &mut |bound_ty: ty::BoundTy| match var_values[bound_ty.var].kind() {
69-
GenericArgKind::Type(ty) => ty,
70-
r => bug!("{:?} is a type but value is {:?}", bound_ty, r),
71-
},
72-
consts: &mut |bound_ct: ty::BoundVar| match var_values[bound_ct].kind() {
73-
GenericArgKind::Const(ct) => ct,
74-
c => bug!("{:?} is a const but value is {:?}", bound_ct, c),
75-
},
76-
};
77-
78-
tcx.replace_escaping_bound_vars_uncached(value, delegate)
64+
return value;
7965
}
66+
67+
value.fold_with(&mut BoundVarReplacer {
68+
tcx,
69+
current_index: ty::INNERMOST,
70+
var_values: var_values.var_values,
71+
cache: Default::default(),
72+
})
73+
}
74+
75+
/// Replaces the escaping bound vars (late bound regions or bound types) in a type.
76+
struct BoundVarReplacer<'tcx> {
77+
tcx: TyCtxt<'tcx>,
78+
79+
/// As with `RegionFolder`, represents the index of a binder *just outside*
80+
/// the ones we have visited.
81+
current_index: ty::DebruijnIndex,
82+
83+
var_values: ty::GenericArgsRef<'tcx>,
84+
85+
/// This cache only tracks the `DebruijnIndex` and assumes that it does not matter
86+
/// for the delegate how often its methods get used.
87+
cache: DelayedMap<(ty::DebruijnIndex, Ty<'tcx>), Ty<'tcx>>,
88+
}
89+
90+
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'tcx> {
91+
fn cx(&self) -> TyCtxt<'tcx> {
92+
self.tcx
93+
}
94+
95+
fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
96+
&mut self,
97+
t: ty::Binder<'tcx, T>,
98+
) -> ty::Binder<'tcx, T> {
99+
self.current_index.shift_in(1);
100+
let t = t.super_fold_with(self);
101+
self.current_index.shift_out(1);
102+
t
103+
}
104+
105+
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
106+
match *t.kind() {
107+
ty::Bound(debruijn, bound_ty) if debruijn == self.current_index => {
108+
self.var_values[bound_ty.var.as_usize()].expect_ty()
109+
}
110+
_ => {
111+
if !t.has_vars_bound_at_or_above(self.current_index) {
112+
t
113+
} else if let Some(&t) = self.cache.get(&(self.current_index, t)) {
114+
t
115+
} else {
116+
let res = t.super_fold_with(self);
117+
assert!(self.cache.insert((self.current_index, t), res));
118+
res
119+
}
120+
}
121+
}
122+
}
123+
124+
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
125+
match r.kind() {
126+
ty::ReBound(debruijn, br) if debruijn == self.current_index => {
127+
self.var_values[br.var.as_usize()].expect_region()
128+
}
129+
_ => r,
130+
}
131+
}
132+
133+
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
134+
match ct.kind() {
135+
ty::ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => {
136+
self.var_values[bound_const.as_usize()].expect_const()
137+
}
138+
_ => ct.super_fold_with(self),
139+
}
140+
}
141+
142+
fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
143+
if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
144+
}
145+
146+
fn fold_clauses(&mut self, c: ty::Clauses<'tcx>) -> ty::Clauses<'tcx> {
147+
if c.has_vars_bound_at_or_above(self.current_index) {
148+
if self.current_index == ty::INNERMOST {
149+
let index = *self
150+
.tcx
151+
.highest_var_in_clauses_cache
152+
.lock()
153+
.entry(c)
154+
.or_insert_with(|| highest_var_in_clauses(c));
155+
let c_args = &self.var_values[..=index];
156+
if let Some(c) = self.tcx.clauses_cache.lock().get(&(c, c_args)) {
157+
c
158+
} else {
159+
let folded = c.super_fold_with(self);
160+
self.tcx.clauses_cache.lock().insert((c, c_args), folded);
161+
folded
162+
}
163+
} else {
164+
c.super_fold_with(self)
165+
}
166+
} else {
167+
c
168+
}
169+
}
170+
}
171+
172+
fn highest_var_in_clauses<'tcx>(c: ty::Clauses<'tcx>) -> usize {
173+
struct HighestVarInClauses {
174+
max_var: usize,
175+
current_index: ty::DebruijnIndex,
176+
}
177+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HighestVarInClauses {
178+
fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
179+
&mut self,
180+
t: &ty::Binder<'tcx, T>,
181+
) -> Self::Result {
182+
self.current_index.shift_in(1);
183+
let t = t.super_visit_with(self);
184+
self.current_index.shift_out(1);
185+
t
186+
}
187+
fn visit_ty(&mut self, t: Ty<'tcx>) {
188+
if let ty::Bound(debruijn, bound_ty) = *t.kind()
189+
&& debruijn == self.current_index
190+
{
191+
self.max_var = self.max_var.max(bound_ty.var.as_usize());
192+
} else if t.has_vars_bound_at_or_above(self.current_index) {
193+
t.super_visit_with(self);
194+
}
195+
}
196+
fn visit_region(&mut self, r: ty::Region<'tcx>) {
197+
if let ty::ReBound(debruijn, bound_region) = r.kind()
198+
&& debruijn == self.current_index
199+
{
200+
self.max_var = self.max_var.max(bound_region.var.as_usize());
201+
}
202+
}
203+
fn visit_const(&mut self, ct: ty::Const<'tcx>) {
204+
if let ty::ConstKind::Bound(debruijn, bound_const) = ct.kind()
205+
&& debruijn == self.current_index
206+
{
207+
self.max_var = self.max_var.max(bound_const.as_usize());
208+
} else if ct.has_vars_bound_at_or_above(self.current_index) {
209+
ct.super_visit_with(self);
210+
}
211+
}
212+
}
213+
let mut visitor = HighestVarInClauses { max_var: 0, current_index: ty::INNERMOST };
214+
c.visit_with(&mut visitor);
215+
visitor.max_var
80216
}

compiler/rustc_middle/src/ty/context.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,6 +1460,10 @@ pub struct GlobalCtxt<'tcx> {
14601460

14611461
pub canonical_param_env_cache: CanonicalParamEnvCache<'tcx>,
14621462

1463+
pub highest_var_in_clauses_cache: Lock<FxHashMap<ty::Clauses<'tcx>, usize>>,
1464+
pub clauses_cache:
1465+
Lock<FxHashMap<(ty::Clauses<'tcx>, &'tcx [ty::GenericArg<'tcx>]), ty::Clauses<'tcx>>>,
1466+
14631467
/// Data layout specification for the current target.
14641468
pub data_layout: TargetDataLayout,
14651469

@@ -1707,6 +1711,8 @@ impl<'tcx> TyCtxt<'tcx> {
17071711
new_solver_evaluation_cache: Default::default(),
17081712
new_solver_canonical_param_env_cache: Default::default(),
17091713
canonical_param_env_cache: Default::default(),
1714+
highest_var_in_clauses_cache: Default::default(),
1715+
clauses_cache: Default::default(),
17101716
data_layout,
17111717
alloc_map: interpret::AllocMap::new(),
17121718
current_gcx,

0 commit comments

Comments
 (0)