Skip to content

Commit bcbfbe6

Browse files
committed
global param_env canonicalization cache
1 parent c2ec908 commit bcbfbe6

File tree

6 files changed

+152
-13
lines changed

6 files changed

+152
-13
lines changed

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

+104-6
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,35 @@ impl<'tcx> InferCtxt<'tcx> {
3535
/// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#canonicalizing-the-query
3636
pub fn canonicalize_query<V>(
3737
&self,
38-
value: V,
38+
value: ty::ParamEnvAnd<'tcx, V>,
3939
query_state: &mut OriginalQueryValues<'tcx>,
40-
) -> Canonical<'tcx, V>
40+
) -> Canonical<'tcx, ty::ParamEnvAnd<'tcx, V>>
4141
where
4242
V: TypeFoldable<TyCtxt<'tcx>>,
4343
{
44-
Canonicalizer::canonicalize(value, self, self.tcx, &CanonicalizeAllFreeRegions, query_state)
44+
let (param_env, value) = value.into_parts();
45+
let base = self.tcx.canonical_param_env_cache.get_or_insert(
46+
param_env,
47+
query_state,
48+
|query_state| {
49+
Canonicalizer::canonicalize(
50+
param_env,
51+
self,
52+
self.tcx,
53+
&CanonicalizeFreeRegionsOtherThanStatic,
54+
query_state,
55+
)
56+
},
57+
);
58+
Canonicalizer::canonicalize_continue(
59+
base,
60+
value,
61+
self,
62+
self.tcx,
63+
&CanonicalizeAllFreeRegions,
64+
query_state,
65+
)
66+
.unchecked_map(|(param_env, value)| param_env.and(value))
4567
}
4668

4769
/// Like [Self::canonicalize_query], but preserves distinct universes. For
@@ -126,19 +148,35 @@ impl<'tcx> InferCtxt<'tcx> {
126148
/// handling of `'static` regions (e.g. trait evaluation).
127149
pub fn canonicalize_query_keep_static<V>(
128150
&self,
129-
value: V,
151+
value: ty::ParamEnvAnd<'tcx, V>,
130152
query_state: &mut OriginalQueryValues<'tcx>,
131-
) -> Canonical<'tcx, V>
153+
) -> Canonical<'tcx, ty::ParamEnvAnd<'tcx, V>>
132154
where
133155
V: TypeFoldable<TyCtxt<'tcx>>,
134156
{
135-
Canonicalizer::canonicalize(
157+
let (param_env, value) = value.into_parts();
158+
let base = self.tcx.canonical_param_env_cache.get_or_insert(
159+
param_env,
160+
query_state,
161+
|query_state| {
162+
Canonicalizer::canonicalize(
163+
param_env,
164+
self,
165+
self.tcx,
166+
&CanonicalizeFreeRegionsOtherThanStatic,
167+
query_state,
168+
)
169+
},
170+
);
171+
Canonicalizer::canonicalize_continue(
172+
base,
136173
value,
137174
self,
138175
self.tcx,
139176
&CanonicalizeFreeRegionsOtherThanStatic,
140177
query_state,
141178
)
179+
.unchecked_map(|(param_env, value)| param_env.and(value))
142180
}
143181
}
144182

@@ -615,6 +653,66 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
615653
Canonical { max_universe, variables: canonical_variables, value: out_value }
616654
}
617655

656+
fn canonicalize_continue<U, V>(
657+
base: Canonical<'tcx, U>,
658+
value: V,
659+
infcx: &InferCtxt<'tcx>,
660+
tcx: TyCtxt<'tcx>,
661+
canonicalize_region_mode: &dyn CanonicalizeMode,
662+
query_state: &mut OriginalQueryValues<'tcx>,
663+
) -> Canonical<'tcx, (U, V)>
664+
where
665+
V: TypeFoldable<TyCtxt<'tcx>>,
666+
{
667+
let needs_canonical_flags = if canonicalize_region_mode.any() {
668+
TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_FREE_REGIONS
669+
} else {
670+
TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER
671+
};
672+
673+
// Fast path: nothing that needs to be canonicalized.
674+
if !value.has_type_flags(needs_canonical_flags) {
675+
return base.unchecked_map(|b| (b, value));
676+
}
677+
678+
let mut canonicalizer = Canonicalizer {
679+
infcx,
680+
tcx,
681+
canonicalize_mode: canonicalize_region_mode,
682+
needs_canonical_flags,
683+
variables: SmallVec::from_slice(base.variables),
684+
query_state,
685+
indices: FxHashMap::default(),
686+
binder_index: ty::INNERMOST,
687+
};
688+
if canonicalizer.query_state.var_values.spilled() {
689+
canonicalizer.indices = canonicalizer
690+
.query_state
691+
.var_values
692+
.iter()
693+
.enumerate()
694+
.map(|(i, &kind)| (kind, BoundVar::new(i)))
695+
.collect();
696+
}
697+
let out_value = value.fold_with(&mut canonicalizer);
698+
699+
// Once we have canonicalized `out_value`, it should not
700+
// contain anything that ties it to this inference context
701+
// anymore.
702+
debug_assert!(!out_value.has_infer() && !out_value.has_placeholders());
703+
704+
let canonical_variables =
705+
tcx.mk_canonical_var_infos(&canonicalizer.universe_canonicalized_variables());
706+
707+
let max_universe = canonical_variables
708+
.iter()
709+
.map(|cvar| cvar.universe())
710+
.max()
711+
.unwrap_or(ty::UniverseIndex::ROOT);
712+
713+
Canonical { max_universe, variables: canonical_variables, value: (base.value, out_value) }
714+
}
715+
618716
/// Creates a canonical variable replacing `kind` from the input,
619717
/// or returns an existing variable if `kind` has already been
620718
/// seen. `kind` is expected to be an unbound variable (or

compiler/rustc_infer/src/infer/combine.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ impl<'tcx> InferCtxt<'tcx> {
171171
// two const param's types are able to be equal has to go through a canonical query with the actual logic
172172
// in `rustc_trait_selection`.
173173
let canonical = self.canonicalize_query(
174-
(relation.param_env(), a.ty(), b.ty()),
174+
relation.param_env().and((a.ty(), b.ty())),
175175
&mut OriginalQueryValues::default(),
176176
);
177177
self.tcx.check_tys_might_be_eq(canonical).map_err(|_| {

compiler/rustc_middle/src/infer/canonical.rs

+36
Original file line numberDiff line numberDiff line change
@@ -291,3 +291,39 @@ impl<'tcx> Index<BoundVar> for CanonicalVarValues<'tcx> {
291291
&self.var_values[value.as_usize()]
292292
}
293293
}
294+
295+
use rustc_data_structures::fx::FxHashMap;
296+
use rustc_data_structures::sync::Lock;
297+
298+
#[derive(Default)]
299+
pub struct CanonicalParamEnvCache<'tcx> {
300+
map: Lock<
301+
FxHashMap<
302+
ty::ParamEnv<'tcx>,
303+
(Canonical<'tcx, ty::ParamEnv<'tcx>>, OriginalQueryValues<'tcx>),
304+
>,
305+
>,
306+
}
307+
308+
impl<'tcx> CanonicalParamEnvCache<'tcx> {
309+
pub fn get_or_insert(
310+
&self,
311+
key: ty::ParamEnv<'tcx>,
312+
vals: &mut OriginalQueryValues<'tcx>,
313+
f: impl FnOnce(&mut OriginalQueryValues<'tcx>) -> Canonical<'tcx, ty::ParamEnv<'tcx>>,
314+
) -> Canonical<'tcx, ty::ParamEnv<'tcx>> {
315+
use std::collections::hash_map::Entry;
316+
match self.map.borrow().entry(key) {
317+
Entry::Occupied(e) => {
318+
let (canonical, vals2) = e.get();
319+
vals.clone_from(vals2);
320+
canonical.clone()
321+
}
322+
Entry::Vacant(e) => {
323+
let canonical = f(vals);
324+
e.insert((canonical.clone(), vals.clone()));
325+
canonical
326+
}
327+
}
328+
}
329+
}

compiler/rustc_middle/src/query/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -2177,7 +2177,9 @@ rustc_queries! {
21772177
/// Used in `super_combine_consts` to ICE if the type of the two consts are definitely not going to end up being
21782178
/// equal to eachother. This might return `Ok` even if the types are not equal, but will never return `Err` if
21792179
/// the types might be equal.
2180-
query check_tys_might_be_eq(arg: Canonical<'tcx, (ty::ParamEnv<'tcx>, Ty<'tcx>, Ty<'tcx>)>) -> Result<(), NoSolution> {
2180+
query check_tys_might_be_eq(
2181+
arg: Canonical<'tcx, ty::ParamEnvAnd<'tcx, (Ty<'tcx>, Ty<'tcx>)>>
2182+
) -> Result<(), NoSolution> {
21812183
desc { "check whether two const param are definitely not equal to eachother"}
21822184
}
21832185

compiler/rustc_middle/src/ty/context.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub mod tls;
66

77
use crate::arena::Arena;
88
use crate::dep_graph::{DepGraph, DepKindStruct};
9-
use crate::infer::canonical::{CanonicalVarInfo, CanonicalVarInfos};
9+
use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarInfo, CanonicalVarInfos};
1010
use crate::lint::struct_lint_level;
1111
use crate::metadata::ModChild;
1212
use crate::middle::codegen_fn_attrs::CodegenFnAttrs;
@@ -612,6 +612,8 @@ pub struct GlobalCtxt<'tcx> {
612612
pub new_solver_evaluation_cache: solve::EvaluationCache<'tcx>,
613613
pub new_solver_coherence_evaluation_cache: solve::EvaluationCache<'tcx>,
614614

615+
pub canonical_param_env_cache: CanonicalParamEnvCache<'tcx>,
616+
615617
/// Data layout specification for the current target.
616618
pub data_layout: TargetDataLayout,
617619

@@ -774,6 +776,7 @@ impl<'tcx> TyCtxt<'tcx> {
774776
evaluation_cache: Default::default(),
775777
new_solver_evaluation_cache: Default::default(),
776778
new_solver_coherence_evaluation_cache: Default::default(),
779+
canonical_param_env_cache: Default::default(),
777780
data_layout,
778781
alloc_map: Lock::new(interpret::AllocMap::new()),
779782
}

compiler/rustc_trait_selection/src/traits/misc.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_infer::infer::canonical::Canonical;
99
use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
1010
use rustc_infer::traits::query::NoSolution;
1111
use rustc_infer::{infer::outlives::env::OutlivesEnvironment, traits::FulfillmentError};
12-
use rustc_middle::ty::{self, AdtDef, GenericArg, List, ParamEnv, Ty, TyCtxt, TypeVisitableExt};
12+
use rustc_middle::ty::{self, AdtDef, GenericArg, List, Ty, TyCtxt, TypeVisitableExt};
1313
use rustc_span::DUMMY_SP;
1414

1515
use super::outlives_bounds::InferCtxtExt;
@@ -209,10 +209,10 @@ pub fn all_fields_implement_trait<'tcx>(
209209

210210
pub fn check_tys_might_be_eq<'tcx>(
211211
tcx: TyCtxt<'tcx>,
212-
canonical: Canonical<'tcx, (ParamEnv<'tcx>, Ty<'tcx>, Ty<'tcx>)>,
212+
canonical: Canonical<'tcx, ty::ParamEnvAnd<'tcx, (Ty<'tcx>, Ty<'tcx>)>>,
213213
) -> Result<(), NoSolution> {
214-
let (infcx, (param_env, ty_a, ty_b), _) =
215-
tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical);
214+
let (infcx, key, _) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical);
215+
let (param_env, (ty_a, ty_b)) = key.into_parts();
216216
let ocx = ObligationCtxt::new(&infcx);
217217

218218
let result = ocx.eq(&ObligationCause::dummy(), param_env, ty_a, ty_b);

0 commit comments

Comments
 (0)