add a per-param-env cache to impls_bound

There used to be only a global cache, which led to uncached calls to
trait selection when there were type parameters.

I'm running a check that there are no adverse performance effects.

Fixes #37106 (drop elaboration times are now ~half of borrow checking,
so might still be worthy of optimization, but not critical).
This commit is contained in:
Ariel Ben-Yehuda 2016-10-13 23:39:50 +03:00
parent d34318dd53
commit a61d85b2fe
5 changed files with 41 additions and 27 deletions

View File

@ -336,7 +336,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
self.infcx.tcx
}
pub fn param_env(&self) -> &'cx ty::ParameterEnvironment<'tcx> {
pub fn param_env(&self) -> &'cx ty::ParameterEnvironment<'gcx> {
self.infcx.param_env()
}

View File

@ -34,7 +34,7 @@ use util::nodemap::FnvHashMap;
use serialize::{self, Encodable, Encoder};
use std::borrow::Cow;
use std::cell::Cell;
use std::cell::{Cell, RefCell};
use std::hash::{Hash, Hasher};
use std::iter;
use std::ops::Deref;
@ -1220,6 +1220,12 @@ pub struct ParameterEnvironment<'tcx> {
/// regions don't have this implicit scope and instead introduce
/// relationships in the environment.
pub free_id_outlive: CodeExtent,
/// A cache for `moves_by_default`.
pub is_copy_cache: RefCell<FnvHashMap<Ty<'tcx>, bool>>,
/// A cache for `type_is_sized`
pub is_sized_cache: RefCell<FnvHashMap<Ty<'tcx>, bool>>,
}
impl<'a, 'tcx> ParameterEnvironment<'tcx> {
@ -1232,6 +1238,8 @@ impl<'a, 'tcx> ParameterEnvironment<'tcx> {
implicit_region_bound: self.implicit_region_bound,
caller_bounds: caller_bounds,
free_id_outlive: self.free_id_outlive,
is_copy_cache: RefCell::new(FnvHashMap()),
is_sized_cache: RefCell::new(FnvHashMap()),
}
}
@ -2773,7 +2781,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
free_substs: Substs::empty(self),
caller_bounds: Vec::new(),
implicit_region_bound: self.mk_region(ty::ReEmpty),
free_id_outlive: free_id_outlive
free_id_outlive: free_id_outlive,
is_copy_cache: RefCell::new(FnvHashMap()),
is_sized_cache: RefCell::new(FnvHashMap()),
}
}
@ -2844,6 +2854,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
implicit_region_bound: tcx.mk_region(ty::ReScope(free_id_outlive)),
caller_bounds: predicates,
free_id_outlive: free_id_outlive,
is_copy_cache: RefCell::new(FnvHashMap()),
is_sized_cache: RefCell::new(FnvHashMap()),
};
let cause = traits::ObligationCause::misc(span, free_id_outlive.node_id(&self.region_maps));

View File

@ -901,23 +901,6 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ClosureUpvar<'tcx> {
}
}
impl<'tcx> TypeFoldable<'tcx> for ty::ParameterEnvironment<'tcx> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
ty::ParameterEnvironment {
free_substs: self.free_substs.fold_with(folder),
implicit_region_bound: self.implicit_region_bound.fold_with(folder),
caller_bounds: self.caller_bounds.fold_with(folder),
free_id_outlive: self.free_id_outlive,
}
}
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
self.free_substs.visit_with(visitor) ||
self.implicit_region_bound.visit_with(visitor) ||
self.caller_bounds.visit_with(visitor)
}
}
impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::error::ExpectedFound<T> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
ty::error::ExpectedFound {

View File

@ -20,9 +20,11 @@ use ty::{Disr, ParameterEnvironment};
use ty::fold::TypeVisitor;
use ty::layout::{Layout, LayoutError};
use ty::TypeVariants::*;
use util::nodemap::FnvHashMap;
use rustc_const_math::{ConstInt, ConstIsize, ConstUsize};
use std::cell::RefCell;
use std::cmp;
use std::hash::{Hash, Hasher};
use std::collections::hash_map::DefaultHasher;
@ -579,11 +581,24 @@ impl<'a, 'gcx, 'tcx, H: Hasher> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tc
impl<'a, 'tcx> ty::TyS<'tcx> {
fn impls_bound(&'tcx self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: &ParameterEnvironment<'tcx>,
bound: ty::BuiltinBound, span: Span) -> bool
bound: ty::BuiltinBound,
cache: &RefCell<FnvHashMap<Ty<'tcx>, bool>>,
span: Span) -> bool
{
tcx.infer_ctxt(None, Some(param_env.clone()), Reveal::ExactMatch).enter(|infcx| {
traits::type_known_to_meet_builtin_bound(&infcx, self, bound, span)
})
if self.has_param_types() || self.has_self_ty() {
if let Some(result) = cache.borrow().get(self) {
return *result;
}
}
let result =
tcx.infer_ctxt(None, Some(param_env.clone()), Reveal::ExactMatch)
.enter(|infcx| {
traits::type_known_to_meet_builtin_bound(&infcx, self, bound, span)
});
if self.has_param_types() || self.has_self_ty() {
cache.borrow_mut().insert(self, result);
}
return result;
}
// FIXME (@jroesch): I made this public to use it, not sure if should be private
@ -610,7 +625,9 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
TyArray(..) | TySlice(..) | TyTrait(..) | TyTuple(..) |
TyClosure(..) | TyAdt(..) | TyAnon(..) |
TyProjection(..) | TyParam(..) | TyInfer(..) | TyError => None
}.unwrap_or_else(|| !self.impls_bound(tcx, param_env, ty::BoundCopy, span));
}.unwrap_or_else(|| {
!self.impls_bound(tcx, param_env, ty::BoundCopy, &param_env.is_copy_cache, span)
});
if !self.has_param_types() && !self.has_self_ty() {
self.flags.set(self.flags.get() | if result {
@ -650,7 +667,9 @@ impl<'a, 'tcx> ty::TyS<'tcx> {
TyAdt(..) | TyProjection(..) | TyParam(..) |
TyInfer(..) | TyAnon(..) | TyError => None
}.unwrap_or_else(|| self.impls_bound(tcx, param_env, ty::BoundSized, span));
}.unwrap_or_else(|| {
self.impls_bound(tcx, param_env, ty::BoundSized, &param_env.is_sized_cache, span)
});
if !self.has_param_types() && !self.has_self_ty() {
self.flags.set(self.flags.get() | if result {

View File

@ -1458,7 +1458,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
}
pub fn param_env(&self) -> &ty::ParameterEnvironment<'tcx> {
pub fn param_env(&self) -> &ty::ParameterEnvironment<'gcx> {
&self.parameter_environment
}