1
Fork 0

Add roll back infrastructure for opaque type caches

This commit is contained in:
Oli Scherer 2021-08-13 17:19:26 +00:00 committed by Oli Scherer
parent dca1e7aa5a
commit d49b0746f6
10 changed files with 113 additions and 37 deletions

View file

@ -66,17 +66,18 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> {
self.relate(a, b)
}
#[instrument(skip(self), level = "debug")]
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
debug!("{}.tys({:?}, {:?})", self.tag(), a, b);
if a == b {
return Ok(a);
}
trace!(a = ?a.kind(), b = ?b.kind());
let infcx = self.fields.infcx;
let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a);
let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b);
debug!("{}.tys: replacements ({:?}, {:?})", self.tag(), a, b);
trace!(a = ?a.kind(), b = ?b.kind(), "replacements");
match (a.kind(), b.kind()) {
(&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => {

View file

@ -5,7 +5,7 @@ pub use self::RegionVariableOrigin::*;
pub use self::SubregionOrigin::*;
pub use self::ValuePairs::*;
use self::opaque_types::OpaqueTypeMap;
use self::opaque_types::OpaqueTypeStorage;
pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog};
use crate::traits::{self, ObligationCause, PredicateObligations, TraitEngine};
@ -190,20 +190,10 @@ pub struct InferCtxtInner<'tcx> {
/// that all type inference variables have been bound and so forth.
region_obligations: Vec<(hir::HirId, RegionObligation<'tcx>)>,
/// Caches for opaque type inference.
pub opaque_type_storage: OpaqueTypeStorage<'tcx>,
undo_log: InferCtxtUndoLogs<'tcx>,
// Opaque types found in explicit return types and their
// associated fresh inference variable. Writeback resolves these
// variables to get the concrete type, which can be used to
// 'de-opaque' OpaqueTypeDecl outside of type inference.
pub opaque_types: OpaqueTypeMap<'tcx>,
/// A map from inference variables created from opaque
/// type instantiations (`ty::Infer`) to the actual opaque
/// type (`ty::Opaque`). Used during fallback to map unconstrained
/// opaque type inference variables to their corresponding
/// opaque type.
pub opaque_types_vars: FxHashMap<Ty<'tcx>, Ty<'tcx>>,
}
impl<'tcx> InferCtxtInner<'tcx> {
@ -217,8 +207,7 @@ impl<'tcx> InferCtxtInner<'tcx> {
float_unification_storage: ut::UnificationTableStorage::new(),
region_constraint_storage: Some(RegionConstraintStorage::new()),
region_obligations: vec![],
opaque_types: Default::default(),
opaque_types_vars: Default::default(),
opaque_type_storage: Default::default(),
}
}
@ -237,6 +226,11 @@ impl<'tcx> InferCtxtInner<'tcx> {
self.type_variable_storage.with_log(&mut self.undo_log)
}
#[inline]
fn opaque_types(&mut self) -> opaque_types::OpaqueTypeTable<'_, 'tcx> {
self.opaque_type_storage.with_log(&mut self.undo_log)
}
#[inline]
fn int_unification_table(
&mut self,

View file

@ -14,6 +14,10 @@ use std::ops::ControlFlow;
pub type OpaqueTypeMap<'tcx> = VecMap<OpaqueTypeKey<'tcx>, OpaqueTypeDecl<'tcx>>;
mod table;
pub use table::{OpaqueTypeStorage, OpaqueTypeTable};
/// Information about the opaque types whose values we
/// are inferring in this function (these are the `impl Trait` that
/// appear in the return type).
@ -352,6 +356,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
};
in_definition_scope.then_some(*origin)
}
pub fn opaque_types(&self) -> OpaqueTypeMap<'tcx> {
self.inner.borrow().opaque_type_storage.opaque_types()
}
}
// Visitor that requires that (almost) all regions in the type visited outlive
@ -513,7 +521,9 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> {
// Use the same type variable if the exact same opaque type appears more
// than once in the return type (e.g., if it's passed to a type alias).
if let Some(opaque_defn) = infcx.inner.borrow().opaque_types.get(&opaque_type_key) {
if let Some(opaque_defn) =
infcx.inner.borrow().opaque_type_storage.get_decl(&opaque_type_key)
{
debug!("re-using cached concrete type {:?}", opaque_defn.concrete_ty.kind());
return opaque_defn.concrete_ty;
}
@ -530,14 +540,10 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> {
// Foo, impl Bar)`.
let definition_span = self.value_span;
{
let mut infcx = self.infcx.inner.borrow_mut();
infcx.opaque_types.insert(
OpaqueTypeKey { def_id, substs },
OpaqueTypeDecl { opaque_type: ty, definition_span, concrete_ty: ty_var, origin },
);
infcx.opaque_types_vars.insert(ty_var, ty);
}
self.infcx.inner.borrow_mut().opaque_types().register(
OpaqueTypeKey { def_id, substs },
OpaqueTypeDecl { opaque_type: ty, definition_span, concrete_ty: ty_var, origin },
);
debug!("generated new type inference var {:?}", ty_var.kind());

View file

@ -0,0 +1,69 @@
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::undo_log::UndoLogs;
use rustc_middle::ty::{OpaqueTypeKey, Ty};
use crate::infer::InferCtxtUndoLogs;
use super::{OpaqueTypeDecl, OpaqueTypeMap};
#[derive(Default)]
pub struct OpaqueTypeStorage<'tcx> {
// Opaque types found in explicit return types and their
// associated fresh inference variable. Writeback resolves these
// variables to get the concrete type, which can be used to
// 'de-opaque' OpaqueTypeDecl, after typeck is done with all functions.
pub opaque_types: OpaqueTypeMap<'tcx>,
/// A map from inference variables created from opaque
/// type instantiations (`ty::Infer`) to the actual opaque
/// type (`ty::Opaque`). Used during fallback to map unconstrained
/// opaque type inference variables to their corresponding
/// opaque type.
pub opaque_types_vars: FxHashMap<Ty<'tcx>, Ty<'tcx>>,
}
impl<'tcx> OpaqueTypeStorage<'tcx> {
pub(crate) fn remove(&mut self, key: OpaqueTypeKey<'tcx>) {
match self.opaque_types.remove(&key) {
None => bug!("reverted opaque type inference that was never registered"),
Some(decl) => assert_ne!(self.opaque_types_vars.remove(decl.concrete_ty), None),
}
}
pub fn get_decl(&self, key: &OpaqueTypeKey<'tcx>) -> Option<&OpaqueTypeDecl<'tcx>> {
self.opaque_types.get(key)
}
pub fn get_opaque_type_for_infer_var(&self, key: Ty<'tcx>) -> Option<Ty<'tcx>> {
self.opaque_types_vars.get(key).copied()
}
pub fn opaque_types(&self) -> OpaqueTypeMap<'tcx> {
self.opaque_types.clone()
}
pub fn take_opaque_types(&mut self) -> OpaqueTypeMap<'tcx> {
std::mem::take(&mut self.opaque_types)
}
#[inline]
pub(crate) fn with_log<'a>(
&'a mut self,
undo_log: &'a mut InferCtxtUndoLogs<'tcx>,
) -> OpaqueTypeTable<'a, 'tcx> {
OpaqueTypeTable { storage: self, undo_log }
}
}
pub struct OpaqueTypeTable<'a, 'tcx> {
storage: &'a mut OpaqueTypeStorage<'tcx>,
undo_log: &'a mut InferCtxtUndoLogs<'tcx>,
}
impl<'a, 'tcx> OpaqueTypeTable<'a, 'tcx> {
pub fn register(&mut self, key: OpaqueTypeKey<'tcx>, decl: OpaqueTypeDecl<'tcx>) {
self.undo_log.push(key);
self.storage.opaque_types.insert(key, decl);
self.storage.opaque_types_vars.insert(decl.concrete_ty, decl.opaque_type);
}
}

View file

@ -4,7 +4,7 @@ use rustc_data_structures::snapshot_vec as sv;
use rustc_data_structures::undo_log::{Rollback, UndoLogs};
use rustc_data_structures::unify as ut;
use rustc_middle::infer::unify_key::RegionVidKey;
use rustc_middle::ty;
use rustc_middle::ty::{self, OpaqueTypeKey};
use crate::{
infer::{region_constraints, type_variable, InferCtxtInner},
@ -18,6 +18,7 @@ pub struct Snapshot<'tcx> {
/// Records the "undo" data for a single operation that affects some form of inference variable.
pub(crate) enum UndoLog<'tcx> {
OpaqueTypes(OpaqueTypeKey<'tcx>),
TypeVariables(type_variable::UndoLog<'tcx>),
ConstUnificationTable(sv::UndoLog<ut::Delegate<ty::ConstVid<'tcx>>>),
IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
@ -42,6 +43,7 @@ macro_rules! impl_from {
// Upcast from a single kind of "undoable action" to the general enum
impl_from! {
OpaqueTypes(OpaqueTypeKey<'tcx>),
RegionConstraintCollector(region_constraints::UndoLog<'tcx>),
TypeVariables(type_variable::UndoLog<'tcx>),
@ -64,6 +66,7 @@ impl_from! {
impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> {
fn reverse(&mut self, undo: UndoLog<'tcx>) {
match undo {
UndoLog::OpaqueTypes(key) => self.opaque_type_storage.remove(key),
UndoLog::TypeVariables(undo) => self.type_variable_storage.reverse(undo),
UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo),
UndoLog::IntUnificationTable(undo) => self.int_unification_storage.reverse(undo),