Skip to content

Commit

Permalink
Auto merge of #130821 - lcnr:nalgebra-hang-2, r=compiler-errors
Browse files Browse the repository at this point in the history
add caching to most type folders, rm region uniquification

Fixes the new minimization of the hang in nalgebra and nalgebra itself :3

this is a bit iffy, especially the cache in `TypeRelating`. I believe all the caches are correct, but it definitely adds some non-local complexity in places. The first commit removes region uniquification, reintroducing the ICE from rust-lang/trait-system-refactor-initiative#27. This does not affect coherence and I would like to fix this by introducing OR-region constraints

r? `@compiler-errors`
  • Loading branch information
bors committed Oct 2, 2024
2 parents 5384697 + 1a04a31 commit 18b1161
Show file tree
Hide file tree
Showing 12 changed files with 443 additions and 134 deletions.
7 changes: 6 additions & 1 deletion compiler/rustc_infer/src/infer/relate/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,15 @@ use crate::traits::{Obligation, PredicateObligation};
#[derive(Clone)]
pub struct CombineFields<'infcx, 'tcx> {
pub infcx: &'infcx InferCtxt<'tcx>,
// Immutable fields
pub trace: TypeTrace<'tcx>,
pub param_env: ty::ParamEnv<'tcx>,
pub goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
pub define_opaque_types: DefineOpaqueTypes,
// Mutable fields
//
// Adding any additional field likely requires
// changes to the cache of `TypeRelating`.
pub goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
}

impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
Expand Down
43 changes: 42 additions & 1 deletion compiler/rustc_infer/src/infer/relate/type_relating.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use rustc_middle::ty::relate::{
};
use rustc_middle::ty::{self, Ty, TyCtxt, TyVar};
use rustc_span::Span;
use rustc_type_ir::data_structures::DelayedSet;
use tracing::{debug, instrument};

use super::combine::CombineFields;
Expand All @@ -13,9 +14,38 @@ use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin};

/// Enforce that `a` is equal to or a subtype of `b`.
pub struct TypeRelating<'combine, 'a, 'tcx> {
// Immutable except for the `InferCtxt` and the
// resulting nested `goals`.
fields: &'combine mut CombineFields<'a, 'tcx>,

// Immutable field.
structurally_relate_aliases: StructurallyRelateAliases,
// Mutable field.
ambient_variance: ty::Variance,

/// The cache only tracks the `ambient_variance` as it's the
/// only field which is mutable and which meaningfully changes
/// the result when relating types.
///
/// The cache does not track whether the state of the
/// `InferCtxt` has been changed or whether we've added any
/// obligations to `self.fields.goals`. Whether a goal is added
/// once or multiple times is not really meaningful.
///
/// Changes in the inference state may delay some type inference to
/// the next fulfillment loop. Given that this loop is already
/// necessary, this is also not a meaningful change. Consider
/// the following three relations:
/// ```text
/// Vec<?0> sub Vec<?1>
/// ?0 eq u32
/// Vec<?0> sub Vec<?1>
/// ```
/// Without a cache, the second `Vec<?0> sub Vec<?1>` would eagerly
/// constrain `?1` to `u32`. When using the cache entry from the
/// first time we've related these types, this only happens when
/// later proving the `Subtype(?0, ?1)` goal from the first relation.
cache: DelayedSet<(ty::Variance, Ty<'tcx>, Ty<'tcx>)>,
}

impl<'combine, 'infcx, 'tcx> TypeRelating<'combine, 'infcx, 'tcx> {
Expand All @@ -24,7 +54,12 @@ impl<'combine, 'infcx, 'tcx> TypeRelating<'combine, 'infcx, 'tcx> {
structurally_relate_aliases: StructurallyRelateAliases,
ambient_variance: ty::Variance,
) -> TypeRelating<'combine, 'infcx, 'tcx> {
TypeRelating { fields: f, structurally_relate_aliases, ambient_variance }
TypeRelating {
fields: f,
structurally_relate_aliases,
ambient_variance,
cache: Default::default(),
}
}
}

Expand Down Expand Up @@ -78,6 +113,10 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, '_, 'tcx> {
let a = infcx.shallow_resolve(a);
let b = infcx.shallow_resolve(b);

if self.cache.contains(&(self.ambient_variance, a, b)) {
return Ok(a);
}

match (a.kind(), b.kind()) {
(&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => {
match self.ambient_variance {
Expand Down Expand Up @@ -160,6 +199,8 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, '_, 'tcx> {
}
}

assert!(self.cache.insert((self.ambient_variance, a, b)));

Ok(a)
}

Expand Down
14 changes: 11 additions & 3 deletions compiler/rustc_infer/src/infer/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use rustc_middle::bug;
use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFolder, TypeSuperFoldable};
use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::{self, Const, InferConst, Ty, TyCtxt, TypeFoldable};
use rustc_type_ir::data_structures::DelayedMap;

use super::{FixupError, FixupResult, InferCtxt};

Expand All @@ -15,12 +16,15 @@ use super::{FixupError, FixupResult, InferCtxt};
/// points for correctness.
pub struct OpportunisticVarResolver<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>,
/// We're able to use a cache here as the folder does
/// not have any mutable state.
cache: DelayedMap<Ty<'tcx>, Ty<'tcx>>,
}

impl<'a, 'tcx> OpportunisticVarResolver<'a, 'tcx> {
#[inline]
pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self {
OpportunisticVarResolver { infcx }
OpportunisticVarResolver { infcx, cache: Default::default() }
}
}

Expand All @@ -33,9 +37,13 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for OpportunisticVarResolver<'a, 'tcx> {
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
if !t.has_non_region_infer() {
t // micro-optimize -- if there is nothing in this type that this fold affects...
} else if let Some(&ty) = self.cache.get(&t) {
return ty;
} else {
let t = self.infcx.shallow_resolve(t);
t.super_fold_with(self)
let shallow = self.infcx.shallow_resolve(t);
let res = shallow.super_fold_with(self);
assert!(self.cache.insert(t, res));
res
}
}

Expand Down
28 changes: 25 additions & 3 deletions compiler/rustc_middle/src/ty/fold.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::def_id::DefId;
use rustc_type_ir::data_structures::DelayedMap;
pub use rustc_type_ir::fold::{
FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable, shift_region, shift_vars,
};
Expand Down Expand Up @@ -131,12 +132,20 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for RegionFolder<'a, 'tcx> {
///////////////////////////////////////////////////////////////////////////
// Bound vars replacer

/// A delegate used when instantiating bound vars.
///
/// Any implementation must make sure that each bound variable always
/// gets mapped to the same result. `BoundVarReplacer` caches by using
/// a `DelayedMap` which does not cache the first few types it encounters.
pub trait BoundVarReplacerDelegate<'tcx> {
fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx>;
fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx>;
fn replace_const(&mut self, bv: ty::BoundVar) -> ty::Const<'tcx>;
}

/// A simple delegate taking 3 mutable functions. The used functions must
/// always return the same result for each bound variable, no matter how
/// frequently they are called.
pub struct FnMutDelegate<'a, 'tcx> {
pub regions: &'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a),
pub types: &'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a),
Expand Down Expand Up @@ -164,11 +173,15 @@ struct BoundVarReplacer<'tcx, D> {
current_index: ty::DebruijnIndex,

delegate: D,

/// This cache only tracks the `DebruijnIndex` and assumes that it does not matter
/// for the delegate how often its methods get used.
cache: DelayedMap<(ty::DebruijnIndex, Ty<'tcx>), Ty<'tcx>>,
}

impl<'tcx, D: BoundVarReplacerDelegate<'tcx>> BoundVarReplacer<'tcx, D> {
fn new(tcx: TyCtxt<'tcx>, delegate: D) -> Self {
BoundVarReplacer { tcx, current_index: ty::INNERMOST, delegate }
BoundVarReplacer { tcx, current_index: ty::INNERMOST, delegate, cache: Default::default() }
}
}

Expand Down Expand Up @@ -197,8 +210,17 @@ where
debug_assert!(!ty.has_vars_bound_above(ty::INNERMOST));
ty::fold::shift_vars(self.tcx, ty, self.current_index.as_u32())
}
_ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self),
_ => t,
_ => {
if !t.has_vars_bound_at_or_above(self.current_index) {
t
} else if let Some(&t) = self.cache.get(&(self.current_index, t)) {
t
} else {
let res = t.super_fold_with(self);
assert!(self.cache.insert((self.current_index, t), res));
res
}
}
}
}

Expand Down
Loading

0 comments on commit 18b1161

Please sign in to comment.