From bac7628eae905b0c806788914c9b7ee9409983de Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 3 Feb 2023 09:00:56 +1100 Subject: [PATCH 1/5] Put a `ShallowResolver` within `OpportunisticVarResolver`. So one doesn't have to be constructed every time. --- compiler/rustc_infer/src/infer/resolve.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 65b90aa3d79d3..49cd9a9c3d0fb 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -16,26 +16,28 @@ use std::ops::ControlFlow; /// useful for printing messages etc but also required at various /// points for correctness. pub struct OpportunisticVarResolver<'a, 'tcx> { - infcx: &'a InferCtxt<'tcx>, + // The shallow resolver is used to resolve inference variables at every + // level of the type. + shallow_resolver: crate::infer::ShallowResolver<'a, 'tcx>, } impl<'a, 'tcx> OpportunisticVarResolver<'a, 'tcx> { #[inline] pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { - OpportunisticVarResolver { infcx } + OpportunisticVarResolver { shallow_resolver: crate::infer::ShallowResolver { infcx } } } } impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticVarResolver<'a, 'tcx> { fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { - self.infcx.tcx + TypeFolder::tcx(&self.shallow_resolver) } 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 { - let t = self.infcx.shallow_resolve(t); + let t = self.shallow_resolver.fold_ty(t); t.super_fold_with(self) } } @@ -44,7 +46,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticVarResolver<'a, 'tcx> { if !ct.has_non_region_infer() { ct // micro-optimize -- if there is nothing in this const that this fold affects... } else { - let ct = self.infcx.shallow_resolve(ct); + let ct = self.shallow_resolver.fold_const(ct); ct.super_fold_with(self) } } From f08a3371b01f6d6b01bf351f28bf0fbba2bd1b87 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 3 Feb 2023 10:15:24 +1100 Subject: [PATCH 2/5] Improve early bailout test in `resolve_vars_if_possible`. `!t.has_non_region_infer()` is the test used in `OpportunisticVarResolver`, and catches a few cases that `!t.needs_infer()` misses. --- compiler/rustc_infer/src/infer/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index f39170bb2916d..14af720fca1f9 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1389,8 +1389,8 @@ impl<'tcx> InferCtxt<'tcx> { where T: TypeFoldable<'tcx>, { - if !value.needs_infer() { - return value; // Avoid duplicated subst-folding. + if !value.has_non_region_infer() { + return value; } let mut r = resolve::OpportunisticVarResolver::new(self); value.fold_with(&mut r) From c2cf3f7b24b4667f72b12da547b57c02741f0615 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 3 Feb 2023 10:31:11 +1100 Subject: [PATCH 3/5] Inline `OpportunisticVarResolver::fold_ty`. --- compiler/rustc_infer/src/infer/resolve.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 49cd9a9c3d0fb..a39a40cf9abe2 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -33,6 +33,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticVarResolver<'a, 'tcx> { TypeFolder::tcx(&self.shallow_resolver) } + #[inline] 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... From fb8e6819aa59c215a2974454d0cbeca34830321a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 3 Feb 2023 12:36:44 +1100 Subject: [PATCH 4/5] Split and inline `ShallowResolver::fold_ty`. --- compiler/rustc_infer/src/infer/mod.rs | 57 ++++++++++++++++----------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 14af720fca1f9..8e0bcff8d0a89 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -30,7 +30,7 @@ use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef}; use rustc_middle::ty::visit::TypeVisitable; pub use rustc_middle::ty::IntVarValue; -use rustc_middle::ty::{self, GenericParamDefKind, InferConst, Ty, TyCtxt}; +use rustc_middle::ty::{self, GenericParamDefKind, InferConst, InferTy, Ty, TyCtxt}; use rustc_middle::ty::{ConstVid, FloatVid, IntVid, TyVid}; use rustc_span::symbol::Symbol; use rustc_span::Span; @@ -1870,9 +1870,33 @@ impl<'a, 'tcx> TypeFolder<'tcx> for ShallowResolver<'a, 'tcx> { /// If `ty` is a type variable of some kind, resolve it one level /// (but do not resolve types found in the result). If `typ` is /// not a type variable, just return it unmodified. + #[inline] fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match *ty.kind() { - ty::Infer(ty::TyVar(v)) => { + if let ty::Infer(v) = ty.kind() { self.fold_infer_ty(*v).unwrap_or(ty) } else { ty } + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.kind() { + self.infcx + .inner + .borrow_mut() + .const_unification_table() + .probe_value(vid) + .val + .known() + .unwrap_or(ct) + } else { + ct + } + } +} + +impl<'a, 'tcx> ShallowResolver<'a, 'tcx> { + // This is separate from `fold_ty` to keep that method small and inlinable. + #[inline(never)] + fn fold_infer_ty(&mut self, v: InferTy) -> Option> { + match v { + ty::TyVar(v) => { // Not entirely obvious: if `typ` is a type variable, // it can be resolved to an int/float variable, which // can then be recursively resolved, hence the @@ -1886,41 +1910,26 @@ impl<'a, 'tcx> TypeFolder<'tcx> for ShallowResolver<'a, 'tcx> { // Note: if these two lines are combined into one we get // dynamic borrow errors on `self.inner`. let known = self.infcx.inner.borrow_mut().type_variables().probe(v).known(); - known.map_or(ty, |t| self.fold_ty(t)) + known.map(|t| self.fold_ty(t)) } - ty::Infer(ty::IntVar(v)) => self + ty::IntVar(v) => self .infcx .inner .borrow_mut() .int_unification_table() .probe_value(v) - .map_or(ty, |v| v.to_type(self.infcx.tcx)), + .map(|v| v.to_type(self.infcx.tcx)), - ty::Infer(ty::FloatVar(v)) => self + ty::FloatVar(v) => self .infcx .inner .borrow_mut() .float_unification_table() .probe_value(v) - .map_or(ty, |v| v.to_type(self.infcx.tcx)), - - _ => ty, - } - } + .map(|v| v.to_type(self.infcx.tcx)), - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.kind() { - self.infcx - .inner - .borrow_mut() - .const_unification_table() - .probe_value(vid) - .val - .known() - .unwrap_or(ct) - } else { - ct + ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => None, } } } From 4aec1345aa5deabba4b63a71f5fb9bd2a3fde01b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 3 Feb 2023 11:36:32 +1100 Subject: [PATCH 5/5] Split and inline `TypeFreshener::fold_ty`. --- compiler/rustc_infer/src/infer/freshen.rs | 129 ++++++++++------------ 1 file changed, 61 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index 83d71edc2abd9..2355234637c40 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -140,79 +140,21 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { } } + #[inline] fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { if !t.needs_infer() && !t.has_erasable_regions() { - return t; - } - - let tcx = self.infcx.tcx; - - match *t.kind() { - ty::Infer(ty::TyVar(v)) => { - let opt_ty = self.infcx.inner.borrow_mut().type_variables().probe(v).known(); - self.freshen_ty(opt_ty, ty::TyVar(v), ty::FreshTy) - } + t + } else { + match *t.kind() { + ty::Infer(v) => self.fold_infer_ty(v).unwrap_or(t), - ty::Infer(ty::IntVar(v)) => self.freshen_ty( - self.infcx - .inner - .borrow_mut() - .int_unification_table() - .probe_value(v) - .map(|v| v.to_type(tcx)), - ty::IntVar(v), - ty::FreshIntTy, - ), + // This code is hot enough that a non-debug assertion here makes a noticeable + // difference on benchmarks like `wg-grammar`. + #[cfg(debug_assertions)] + ty::Placeholder(..) | ty::Bound(..) => bug!("unexpected type {:?}", t), - ty::Infer(ty::FloatVar(v)) => self.freshen_ty( - self.infcx - .inner - .borrow_mut() - .float_unification_table() - .probe_value(v) - .map(|v| v.to_type(tcx)), - ty::FloatVar(v), - ty::FreshFloatTy, - ), - - ty::Infer(ty::FreshTy(ct) | ty::FreshIntTy(ct) | ty::FreshFloatTy(ct)) => { - if ct >= self.ty_freshen_count { - bug!( - "Encountered a freshend type with id {} \ - but our counter is only at {}", - ct, - self.ty_freshen_count - ); - } - t + _ => t.super_fold_with(self), } - - ty::Generator(..) - | ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Adt(..) - | ty::Str - | ty::Error(_) - | ty::Array(..) - | ty::Slice(..) - | ty::RawPtr(..) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Dynamic(..) - | ty::Never - | ty::Tuple(..) - | ty::Alias(..) - | ty::Foreign(..) - | ty::Param(..) - | ty::Closure(..) - | ty::GeneratorWitnessMIR(..) - | ty::GeneratorWitness(..) => t.super_fold_with(self), - - ty::Placeholder(..) | ty::Bound(..) => bug!("unexpected type {:?}", t), } } @@ -253,3 +195,54 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { } } } + +impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { + // This is separate from `fold_ty` to keep that method small and inlinable. + #[inline(never)] + fn fold_infer_ty(&mut self, v: ty::InferTy) -> Option> { + match v { + ty::TyVar(v) => { + let opt_ty = self.infcx.inner.borrow_mut().type_variables().probe(v).known(); + Some(self.freshen_ty(opt_ty, ty::TyVar(v), ty::FreshTy)) + } + + ty::IntVar(v) => Some( + self.freshen_ty( + self.infcx + .inner + .borrow_mut() + .int_unification_table() + .probe_value(v) + .map(|v| v.to_type(self.infcx.tcx)), + ty::IntVar(v), + ty::FreshIntTy, + ), + ), + + ty::FloatVar(v) => Some( + self.freshen_ty( + self.infcx + .inner + .borrow_mut() + .float_unification_table() + .probe_value(v) + .map(|v| v.to_type(self.infcx.tcx)), + ty::FloatVar(v), + ty::FreshFloatTy, + ), + ), + + ty::FreshTy(ct) | ty::FreshIntTy(ct) | ty::FreshFloatTy(ct) => { + if ct >= self.ty_freshen_count { + bug!( + "Encountered a freshend type with id {} \ + but our counter is only at {}", + ct, + self.ty_freshen_count + ); + } + None + } + } + } +}