|
21 | 21 | //!
|
22 | 22 | //! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
|
23 | 23 |
|
| 24 | +use rustc_data_structures::fx::FxHashMap; |
| 25 | +use rustc_data_structures::sync::Lock; |
24 | 26 | use rustc_macros::HashStable;
|
25 | 27 | use rustc_type_ir::Canonical as IrCanonical;
|
26 | 28 | use rustc_type_ir::CanonicalVarInfo as IrCanonicalVarInfo;
|
27 | 29 | pub use rustc_type_ir::{CanonicalTyVarKind, CanonicalVarKind};
|
28 | 30 | use smallvec::SmallVec;
|
| 31 | +use std::collections::hash_map::Entry; |
29 | 32 | use std::ops::Index;
|
30 | 33 |
|
31 | 34 | use crate::infer::MemberConstraint;
|
32 | 35 | use crate::mir::ConstraintCategory;
|
33 | 36 | use crate::ty::GenericArg;
|
34 |
| -use crate::ty::{self, BoundVar, List, Region, Ty, TyCtxt}; |
| 37 | +use crate::ty::{self, BoundVar, List, Region, Ty, TyCtxt, TypeFlags, TypeVisitableExt}; |
35 | 38 |
|
36 | 39 | pub type Canonical<'tcx, V> = IrCanonical<TyCtxt<'tcx>, V>;
|
37 | 40 |
|
@@ -291,3 +294,62 @@ impl<'tcx> Index<BoundVar> for CanonicalVarValues<'tcx> {
|
291 | 294 | &self.var_values[value.as_usize()]
|
292 | 295 | }
|
293 | 296 | }
|
| 297 | + |
| 298 | +#[derive(Default)] |
| 299 | +pub struct CanonicalParamEnvCache<'tcx> { |
| 300 | + map: Lock< |
| 301 | + FxHashMap< |
| 302 | + ty::ParamEnv<'tcx>, |
| 303 | + (Canonical<'tcx, ty::ParamEnv<'tcx>>, &'tcx [GenericArg<'tcx>]), |
| 304 | + >, |
| 305 | + >, |
| 306 | +} |
| 307 | + |
| 308 | +impl<'tcx> CanonicalParamEnvCache<'tcx> { |
| 309 | + /// Gets the cached canonical form of `key` or executes |
| 310 | + /// `canonicalize_op` and caches the result if not present. |
| 311 | + /// |
| 312 | + /// `canonicalize_op` is intentionally not allowed to be a closure to |
| 313 | + /// statically prevent it from capturing `InferCtxt` and resolving |
| 314 | + /// inference variables, which invalidates the cache. |
| 315 | + pub fn get_or_insert( |
| 316 | + &self, |
| 317 | + tcx: TyCtxt<'tcx>, |
| 318 | + key: ty::ParamEnv<'tcx>, |
| 319 | + state: &mut OriginalQueryValues<'tcx>, |
| 320 | + canonicalize_op: fn( |
| 321 | + TyCtxt<'tcx>, |
| 322 | + ty::ParamEnv<'tcx>, |
| 323 | + &mut OriginalQueryValues<'tcx>, |
| 324 | + ) -> Canonical<'tcx, ty::ParamEnv<'tcx>>, |
| 325 | + ) -> Canonical<'tcx, ty::ParamEnv<'tcx>> { |
| 326 | + if !key.has_type_flags( |
| 327 | + TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_FREE_REGIONS, |
| 328 | + ) { |
| 329 | + return Canonical { |
| 330 | + max_universe: ty::UniverseIndex::ROOT, |
| 331 | + variables: List::empty(), |
| 332 | + value: key, |
| 333 | + }; |
| 334 | + } |
| 335 | + |
| 336 | + assert_eq!(state.var_values.len(), 0); |
| 337 | + assert_eq!(state.universe_map.len(), 1); |
| 338 | + debug_assert_eq!(&*state.universe_map, &[ty::UniverseIndex::ROOT]); |
| 339 | + |
| 340 | + match self.map.borrow().entry(key) { |
| 341 | + Entry::Occupied(e) => { |
| 342 | + let (canonical, var_values) = e.get(); |
| 343 | + state.var_values.extend_from_slice(var_values); |
| 344 | + canonical.clone() |
| 345 | + } |
| 346 | + Entry::Vacant(e) => { |
| 347 | + let canonical = canonicalize_op(tcx, key, state); |
| 348 | + let OriginalQueryValues { var_values, universe_map } = state; |
| 349 | + assert_eq!(universe_map.len(), 1); |
| 350 | + e.insert((canonical.clone(), tcx.arena.alloc_slice(var_values))); |
| 351 | + canonical |
| 352 | + } |
| 353 | + } |
| 354 | + } |
| 355 | +} |
0 commit comments